go-xmpp/message.go
Mickael Remond b05efea81d Quick prototype of message extension
This is a work-in-progress, needs refactor.
2019-06-04 18:47:44 +02:00

155 lines
3.4 KiB
Go

package xmpp // import "gosrc.io/xmpp"
import (
"encoding/xml"
"fmt"
"reflect"
)
// ============================================================================
// Message Packet
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"`
Extensions []MsgExtension `xml:",omitempty"`
}
func (Message) Name() string {
return "message"
}
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,
},
}
}
type messageDecoder struct{}
var message messageDecoder
func (messageDecoder) decode(p *xml.Decoder, se xml.StartElement) (Message, error) {
var packet Message
err := p.DecodeElement(&packet, &se)
return packet, err
}
// TODO: Support missing element (thread, extensions) by using proper marshaller
func (msg *Message) XMPPFormat() string {
return fmt.Sprintf("<message to='%s' type='chat' xml:lang='en'>"+
"<body>%s</body></message>",
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{})
}