From dade3504f039f3ff8b608fb54c181017422272aa Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Mon, 15 Jan 2018 12:28:34 +0100 Subject: [PATCH] Improve generic IQ parsing --- client_test.go | 3 +- cmd/xmpp_component/xmpp_component.go | 8 +++ iq.go | 77 ++++++++++++++++++++++++---- iq_test.go | 35 +++++++++++++ session.go | 3 +- 5 files changed, 113 insertions(+), 13 deletions(-) diff --git a/client_test.go b/client_test.go index 2746041..381d5af 100644 --- a/client_test.go +++ b/client_test.go @@ -170,7 +170,8 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) { return } - switch iq.Payload.(type) { + // TODO Check all elements + switch iq.Payload[0].(type) { case *bindBind: result := ` diff --git a/cmd/xmpp_component/xmpp_component.go b/cmd/xmpp_component/xmpp_component.go index 3303c47..a48426b 100644 --- a/cmd/xmpp_component/xmpp_component.go +++ b/cmd/xmpp_component/xmpp_component.go @@ -1,6 +1,7 @@ package main import ( + "encoding/xml" "fmt" "fluux.io/xmpp" @@ -22,6 +23,13 @@ func main() { case xmpp.IQ: switch inner := p.Payload.(type) { case *xmpp.Node: + fmt.Printf("%q\n", inner) + + data, err := xml.Marshal(inner) + if err != nil { + fmt.Println("cannot marshall payload") + } + fmt.Println("data=", string(data)) component.processIQ(p.Type, p.Id, p.From, inner) default: fmt.Println("default") diff --git a/iq.go b/iq.go index 63667f1..a34a919 100644 --- a/iq.go +++ b/iq.go @@ -7,18 +7,57 @@ import ( "fluux.io/xmpp/iot" ) +/* +TODO I would like to be able to write + + newIQ(Id, From, To, Type, Lang).AddPayload(IQPayload) + + xmpp.IQ{ + XMLName: xml.Name{ + Space: "", + Local: "", + }, + PacketAttrs: xmpp.PacketAttrs{ + Id: "", + From: "", + To: "", + Type: "", + Lang: "", + }, + Payload: nil, + RawXML: "", + } + +*/ + // ============================================================================ // IQ Packet type IQ struct { // Info/Query XMLName xml.Name `xml:"iq"` PacketAttrs - Payload IQPayload `xml:",omitempty"` - RawXML string `xml:",innerxml"` - // TODO We need to support detecting the IQ namespace / Query packet + Payload []IQPayload `xml:",omitempty"` + RawXML string `xml:",innerxml"` // Error clientError } +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) +} + func (IQ) Name() string { return "iq" } @@ -33,10 +72,6 @@ func (iqDecoder) decode(p *xml.Decoder, se xml.StartElement) (IQ, error) { return packet, err } -type IQPayload interface { - IsIQPayload() -} - // UnmarshalXML implements custom parsing for IQs func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { iq.XMLName = start.Name @@ -83,7 +118,7 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { if err != nil { return err } - iq.Payload = p + iq.Payload = []IQPayload{p} p = nil } @@ -117,12 +152,32 @@ func (iq *IQ) XMPPFormat() string { } // ============================================================================ -// Genery IQ Node +// Generic IQ Payload + +type IQPayload interface { + IsIQPayload() +} type Node struct { XMLName xml.Name - Content []byte `xml:",innerxml"` - Nodes []Node `xml:",any"` + Attrs []xml.Attr `xml:"-"` + // Content []byte `xml:",innerxml"` + Nodes []Node `xml:",any"` +} + +func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + n.Attrs = start.Attr + 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}) } func (*Node) IsIQPayload() {} diff --git a/iq_test.go b/iq_test.go index 9042ebc..1eba656 100644 --- a/iq_test.go +++ b/iq_test.go @@ -27,3 +27,38 @@ func TestUnmarshalIqs(t *testing.T) { } } } + +func TestGenerateIq(t *testing.T) { + iq := NewIQ("get", "admin@localhost", "test@localhost", "1", "en") + payload := Node{ + XMLName: xml.Name{ + Space: "http://jabber.org/protocol/disco#info", + Local: "query", + }, + Nodes: []Node{ + {XMLName: xml.Name{ + Local: "identity", + }, + Attrs: []xml.Attr{ + {Name: xml.Name{Local: "category"}, Value: "gateway"}, + {Name: xml.Name{Local: "type"}, Value: "skype"}, + {Name: xml.Name{Local: "name"}, Value: "Test Gateway"}, + }, + Nodes: nil, + }}, + } + iq.AddPayload(&payload) + data, err := xml.Marshal(iq) + if err != nil { + t.Errorf("cannot marshal xml structure") + } + + var parsedIQ = new(IQ) + if err = xml.Unmarshal(data, parsedIQ); err != nil { + t.Errorf("Unmarshal(%s) returned error", data) + } + + if !reflect.DeepEqual(parsedIQ.Payload[0], iq.Payload[0]) { + t.Errorf("expecting result %+v = %+v", parsedIQ.Payload[0], iq.Payload[0]) + } +} diff --git a/session.go b/session.go index dc47ffe..c4afa58 100644 --- a/session.go +++ b/session.go @@ -163,7 +163,8 @@ func (s *Session) bind(o Options) { return } - switch payload := iq.Payload.(type) { + // TODO Check all elements + switch payload := iq.Payload[0].(type) { case *bindBind: s.BindJid = payload.Jid // our local id (with possibly randomly generated resource default: