go-xmpp/iq.go

257 lines
5.6 KiB
Go
Raw Normal View History

2018-01-01 09:12:33 -08:00
package xmpp // import "fluux.io/xmpp"
import (
"encoding/xml"
"fmt"
2018-01-16 13:33:21 -08:00
"reflect"
2018-01-01 09:35:47 -08:00
"fluux.io/xmpp/iot"
)
2018-01-15 03:28:34 -08:00
/*
TODO I would like to be able to write
2018-01-16 13:33:21 -08:00
NewIQ(Id, From, To, Type, Lang).AddPayload(IQPayload)
Payload would be:
2018-01-15 03:28:34 -08:00
2018-01-16 13:33:21 -08:00
payload := Node{
Ns: "http://jabber.org/protocol/disco#info",
Tag: "identity",
Attrs: map[string]string{
"category":"gateway",
"type": "skype",
"name": "Test Gateway",
},
Nodes: []Node{},
}
AddPayload(Ns, Tag, Attrs)
ex:
NewIQ("get", "test@localhost", "admin@localhost", "en")
.AddPayload("http://jabber.org/protocol/disco#info",
"identity",
map[string]string{
"category":"gateway",
"type": "skype",
"name": "Test Gateway",
})
NewNode(Ns, Tag, Attrs)
NewNodeWithChildren(Ns, Tag, Attrs, Nodes)
Attr {
K string
V string
}
xmpp.Elt.DiscoInfo("identity", "gateway", "skype", "Test Gateway")
xmppElt.DiscoInfo.identity("
import xmpp/node/discoinfo
discoinfo.Identity("gateway", "skype", "Test Gateway")
[]Attr{{"category", "gateway"}
TODO support ability to put Raw payload
2018-01-15 03:28:34 -08:00
*/
2018-01-13 09:50:17 -08:00
// ============================================================================
// IQ Packet
type IQ struct { // Info/Query
XMLName xml.Name `xml:"iq"`
2018-01-13 08:54:07 -08:00
PacketAttrs
2018-01-15 03:28:34 -08:00
Payload []IQPayload `xml:",omitempty"`
RawXML string `xml:",innerxml"`
// Error clientError
}
2018-01-15 03:28:34 -08:00
func NewIQ(iqtype, from, to, id, lang string) IQ {
return IQ{
XMLName: xml.Name{Local: "iq"},
PacketAttrs: PacketAttrs{
Id: id,
From: from,
To: to,
Type: iqtype,
Lang: lang,
},
}
}
func (iq *IQ) AddPayload(payload IQPayload) {
iq.Payload = append(iq.Payload, payload)
}
2018-01-13 09:50:17 -08:00
func (IQ) Name() string {
return "iq"
}
type iqDecoder struct{}
var iq iqDecoder
func (iqDecoder) decode(p *xml.Decoder, se xml.StartElement) (IQ, error) {
var packet IQ
err := p.DecodeElement(&packet, &se)
return packet, err
}
// UnmarshalXML implements custom parsing for IQs
2018-01-13 09:50:17 -08:00
func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
iq.XMLName = start.Name
2018-01-16 13:33:21 -08:00
fmt.Println("IQ Name", iq.XMLName)
// Extract IQ attributes
for _, attr := range start.Attr {
if attr.Name.Local == "id" {
iq.Id = attr.Value
}
2016-02-15 09:33:51 -08:00
if attr.Name.Local == "type" {
iq.Type = attr.Value
}
if attr.Name.Local == "to" {
iq.To = attr.Value
}
if attr.Name.Local == "from" {
iq.From = attr.Value
}
if attr.Name.Local == "lang" {
iq.Lang = attr.Value
}
}
// decode inner elements
2018-01-16 13:33:21 -08:00
level := 0
for {
t, err := d.Token()
if err != nil {
return err
}
switch tt := t.(type) {
case xml.StartElement:
2018-01-16 13:33:21 -08:00
level++
if level <= 1 {
var elt interface{}
payloadType := tt.Name.Space + " " + tt.Name.Local
if payloadType := typeRegistry[payloadType]; payloadType != nil {
val := reflect.New(payloadType)
elt = val.Interface()
} else {
elt = new(Node)
}
if iqPl, ok := elt.(IQPayload); ok {
err = d.DecodeElement(elt, &tt)
if err != nil {
return err
}
iq.Payload = append(iq.Payload, iqPl) // []IQPayload{iqPl}
}
}
case xml.EndElement:
2018-01-16 13:33:21 -08:00
level--
if tt == start.End() {
return nil
}
}
}
}
2016-02-15 09:33:51 -08:00
// XMPPFormat returns the string representation of the XMPP packet.
// TODO: Should I simply rely on xml.Marshal ?
2018-01-13 09:50:17 -08:00
func (iq *IQ) XMPPFormat() string {
if iq.Payload != nil {
var payload []byte
var err error
if payload, err = xml.Marshal(iq.Payload); err != nil {
return fmt.Sprintf("<iq to='%s' type='%s' id='%s' xml:lang='en'>"+
"</iq>",
iq.To, iq.Type, iq.Id)
}
return fmt.Sprintf("<iq to='%s' type='%s' id='%s' xml:lang='en'>"+
"%s</iq>",
iq.To, iq.Type, iq.Id, payload)
}
return fmt.Sprintf("<iq to='%s' type='%s' id='%s' xml:lang='en'>"+
"%s</iq>",
iq.To, iq.Type, iq.Id,
iq.RawXML)
}
2018-01-13 10:14:26 -08:00
// ============================================================================
2018-01-15 03:28:34 -08:00
// Generic IQ Payload
type IQPayload interface {
IsIQPayload()
}
2018-01-13 10:14:26 -08:00
2018-01-13 10:27:46 -08:00
type Node struct {
XMLName xml.Name
2018-01-15 03:28:34 -08:00
Attrs []xml.Attr `xml:"-"`
// Content []byte `xml:",innerxml"`
Nodes []Node `xml:",any"`
}
2018-01-16 13:33:21 -08:00
type Attr struct {
K string
V string
}
2018-01-15 03:28:34 -08:00
func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
2018-01-16 13:33:21 -08:00
// Assign "n.Attrs = start.Attr", without repeating xmlns in attributes:
for _, attr := range start.Attr {
2018-01-16 13:33:21 -08:00
// Do not repeat xmlns, it is already in XMLName
if attr.Name.Local != "xmlns" {
n.Attrs = append(n.Attrs, attr)
}
}
2018-01-15 03:28:34 -08:00
type node Node
return d.DecodeElement((*node)(n), &start)
}
func (n *Node) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
start.Attr = n.Attrs
start.Name = n.XMLName
err = e.EncodeToken(start)
e.EncodeElement(n.Nodes, xml.StartElement{Name: n.XMLName})
return e.EncodeToken(xml.EndElement{Name: start.Name})
2018-01-13 10:14:26 -08:00
}
2018-01-13 10:27:46 -08:00
func (*Node) IsIQPayload() {}
2018-01-16 13:33:21 -08:00
// ============================================================================
// Disco
type DiscoInfo struct {
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#info query"`
Identity Identity
}
func (*DiscoInfo) IsIQPayload() {}
type Identity struct {
XMLName xml.Name `xml:"identity"`
Name string `xml:"name,attr"`
Category string `xml:"category,attr"`
Type string `xml:"type,attr"`
}
// ============================================================================
var typeRegistry = make(map[string]reflect.Type)
func init() {
typeRegistry["http://jabber.org/protocol/disco#info query"] = reflect.TypeOf(DiscoInfo{})
typeRegistry["urn:ietf:params:xml:ns:xmpp-bind bind"] = reflect.TypeOf(BindBind{})
typeRegistry["urn:xmpp:iot:control set"] = reflect.TypeOf(iot.ControlSet{})
}