diff --git a/message.go b/message.go
index a9e108e..af815e1 100644
--- a/message.go
+++ b/message.go
@@ -3,6 +3,7 @@ package xmpp // import "gosrc.io/xmpp"
import (
"encoding/xml"
"fmt"
+ "reflect"
)
// ============================================================================
@@ -11,10 +12,11 @@ import (
type Message struct {
XMLName xml.Name `xml:"message"`
PacketAttrs
- Subject string `xml:"subject,omitempty"`
- Body string `xml:"body,omitempty"`
- Thread string `xml:"thread,omitempty"`
- Error Err `xml:"error,omitempty"`
+ Subject string `xml:"subject,omitempty"`
+ Body string `xml:"body,omitempty"`
+ Thread string `xml:"thread,omitempty"`
+ Error Err `xml:"error,omitempty"`
+ Extensions []MsgExtension `xml:",omitempty"`
}
func (Message) Name() string {
@@ -44,9 +46,109 @@ func (messageDecoder) decode(p *xml.Decoder, se xml.StartElement) (Message, erro
return packet, err
}
+// TODO: Support missing element (thread, extensions) by using proper marshaller
func (msg *Message) XMPPFormat() string {
return fmt.Sprintf(""+
"%s",
msg.To,
xmlEscape(msg.Body))
}
+
+// 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{})
+}
diff --git a/message_test.go b/message_test.go
index 21ee882..39738ae 100644
--- a/message_test.go
+++ b/message_test.go
@@ -27,3 +27,57 @@ func TestGenerateMessage(t *testing.T) {
t.Errorf("non matching items\n%s", cmp.Diff(parsedMessage, message))
}
}
+
+func TestDecodeError(t *testing.T) {
+ str := `
+
+
+
+`
+
+ parsedMessage := xmpp.Message{}
+ if err := xml.Unmarshal([]byte(str), &parsedMessage); err != nil {
+ t.Errorf("message error stanza unmarshall error: %v", err)
+ return
+ }
+ if parsedMessage.Error.Type != "cancel" {
+ t.Errorf("incorrect error type: %s", parsedMessage.Error.Type)
+ }
+}
+
+func TestDecodeXEP0184(t *testing.T) {
+ str := `
+ My lord, dispatch; read o'er these articles.
+
+`
+ parsedMessage := xmpp.Message{}
+ if err := xml.Unmarshal([]byte(str), &parsedMessage); err != nil {
+ t.Errorf("message receipt unmarshall error: %v", err)
+ return
+ }
+
+ if parsedMessage.Body != "My lord, dispatch; read o'er these articles." {
+ t.Errorf("Unexpected body: '%s'", parsedMessage.Body)
+ }
+
+ if len(parsedMessage.Extensions) < 1 {
+ t.Errorf("no extension found on parsed message")
+ return
+ }
+
+ switch ext := parsedMessage.Extensions[0].(type) {
+ case *xmpp.Receipt:
+ if ext.XMLName.Local != "request" {
+ t.Errorf("unexpected extension: %s:%s", ext.XMLName.Space, ext.XMLName.Local)
+ }
+ default:
+ t.Errorf("could not find receipt extension")
+ }
+
+}