2018-12-26 09:50:01 -08:00
|
|
|
package xmpp // import "gosrc.io/xmpp"
|
2016-01-06 07:51:12 -08:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/xml"
|
|
|
|
"fmt"
|
2019-05-31 14:35:04 -07:00
|
|
|
"reflect"
|
2016-01-06 07:51:12 -08:00
|
|
|
)
|
|
|
|
|
2018-01-13 09:50:17 -08:00
|
|
|
// ============================================================================
|
|
|
|
// Message Packet
|
|
|
|
|
|
|
|
type Message struct {
|
|
|
|
XMLName xml.Name `xml:"message"`
|
2018-01-13 08:54:07 -08:00
|
|
|
PacketAttrs
|
2019-05-31 14:35:04 -07:00
|
|
|
Subject string `xml:"subject,omitempty"`
|
|
|
|
Body string `xml:"body,omitempty"`
|
|
|
|
Thread string `xml:"thread,omitempty"`
|
|
|
|
Error Err `xml:"error,omitempty"`
|
|
|
|
Extensions []MsgExtension `xml:",omitempty"`
|
2016-01-06 07:51:12 -08:00
|
|
|
}
|
|
|
|
|
2018-01-13 09:50:17 -08:00
|
|
|
func (Message) Name() string {
|
|
|
|
return "message"
|
|
|
|
}
|
|
|
|
|
2018-01-24 00:38:02 -08:00
|
|
|
func NewMessage(msgtype, from, to, id, lang string) Message {
|
|
|
|
return Message{
|
|
|
|
XMLName: xml.Name{Local: "message"},
|
|
|
|
PacketAttrs: PacketAttrs{
|
|
|
|
Id: id,
|
|
|
|
From: from,
|
|
|
|
To: to,
|
|
|
|
Type: msgtype,
|
|
|
|
Lang: lang,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-13 09:50:17 -08:00
|
|
|
type messageDecoder struct{}
|
|
|
|
|
|
|
|
var message messageDecoder
|
2016-01-06 07:51:12 -08:00
|
|
|
|
2018-01-13 09:50:17 -08:00
|
|
|
func (messageDecoder) decode(p *xml.Decoder, se xml.StartElement) (Message, error) {
|
|
|
|
var packet Message
|
|
|
|
err := p.DecodeElement(&packet, &se)
|
|
|
|
return packet, err
|
|
|
|
}
|
|
|
|
|
2019-05-31 14:35:04 -07:00
|
|
|
// TODO: Support missing element (thread, extensions) by using proper marshaller
|
2018-01-13 09:50:17 -08:00
|
|
|
func (msg *Message) XMPPFormat() string {
|
2016-01-06 07:51:12 -08:00
|
|
|
return fmt.Sprintf("<message to='%s' type='chat' xml:lang='en'>"+
|
|
|
|
"<body>%s</body></message>",
|
2018-01-13 09:50:17 -08:00
|
|
|
msg.To,
|
|
|
|
xmlEscape(msg.Body))
|
2016-01-06 07:51:12 -08:00
|
|
|
}
|
2019-05-31 14:35:04 -07:00
|
|
|
|
|
|
|
// UnmarshalXML implements custom parsing for IQs
|
|
|
|
func (msg *Message) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
|
|
msg.XMLName = start.Name
|
|
|
|
|
|
|
|
// Extract packet attributes
|
|
|
|
for _, attr := range start.Attr {
|
|
|
|
if attr.Name.Local == "id" {
|
|
|
|
msg.Id = attr.Value
|
|
|
|
}
|
|
|
|
if attr.Name.Local == "type" {
|
|
|
|
msg.Type = attr.Value
|
|
|
|
}
|
|
|
|
if attr.Name.Local == "to" {
|
|
|
|
msg.To = attr.Value
|
|
|
|
}
|
|
|
|
if attr.Name.Local == "from" {
|
|
|
|
msg.From = attr.Value
|
|
|
|
}
|
|
|
|
if attr.Name.Local == "lang" {
|
|
|
|
msg.Lang = attr.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode inner elements
|
|
|
|
for {
|
|
|
|
t, err := d.Token()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch tt := t.(type) {
|
|
|
|
|
|
|
|
case xml.StartElement:
|
|
|
|
var elt interface{}
|
|
|
|
elementType := tt.Name.Space
|
|
|
|
|
|
|
|
if extensionType := msgTypeRegistry[elementType]; extensionType != nil {
|
|
|
|
val := reflect.New(extensionType)
|
|
|
|
elt = val.Interface()
|
|
|
|
if msgExt, ok := elt.(MsgExtension); ok {
|
|
|
|
err = d.DecodeElement(elt, &tt)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
msg.Extensions = append(msg.Extensions, msgExt)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Decode default message elements
|
|
|
|
var err error
|
|
|
|
switch tt.Name.Local {
|
|
|
|
case "body":
|
|
|
|
err = d.DecodeElement(&msg.Body, &tt)
|
|
|
|
case "thread":
|
|
|
|
err = d.DecodeElement(&msg.Thread, &tt)
|
|
|
|
case "subject":
|
|
|
|
err = d.DecodeElement(&msg.Subject, &tt)
|
|
|
|
case "error":
|
|
|
|
err = d.DecodeElement(&msg.Error, &tt)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case xml.EndElement:
|
|
|
|
if tt == start.End() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// Message extensions
|
|
|
|
// Provide ability to add support to XMPP extension tags on messages
|
|
|
|
|
|
|
|
type MsgExtension interface {
|
|
|
|
IsMsgExtension()
|
|
|
|
}
|
|
|
|
|
|
|
|
// XEP-0184
|
|
|
|
type Receipt struct {
|
|
|
|
// xmlns: urn:xmpp:receipts
|
|
|
|
XMLName xml.Name
|
|
|
|
Id string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*Receipt) IsMsgExtension() {}
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// TODO: Make it configurable at to be able to easily add new XMPP extensions
|
|
|
|
// in separate modules
|
|
|
|
|
|
|
|
var msgTypeRegistry = make(map[string]reflect.Type)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
msgTypeRegistry["urn:xmpp:receipts"] = reflect.TypeOf(Receipt{})
|
|
|
|
}
|