From dade3504f039f3ff8b608fb54c181017422272aa Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Mon, 15 Jan 2018 12:28:34 +0100 Subject: [PATCH 1/4] 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: From c821267928a2b9adf974c9b8e758cd1f5a1f8c0a Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Mon, 15 Jan 2018 12:47:26 +0100 Subject: [PATCH 2/4] Do not repeat xmlns in attributes on parsing --- iq.go | 8 +++++++- iq_test.go | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/iq.go b/iq.go index a34a919..1b174f6 100644 --- a/iq.go +++ b/iq.go @@ -166,7 +166,13 @@ type Node struct { } func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - n.Attrs = start.Attr + // Assign "n.Attrs = start.Attr", without repeating xmlns in attributes + for _, attr := range start.Attr { + // Do not repeat xmlns + if attr.Name.Local != "xmlns" { + n.Attrs = append(n.Attrs, attr) + } + } type node Node return d.DecodeElement((*node)(n), &start) } diff --git a/iq_test.go b/iq_test.go index 1eba656..ab85370 100644 --- a/iq_test.go +++ b/iq_test.go @@ -37,6 +37,7 @@ func TestGenerateIq(t *testing.T) { }, Nodes: []Node{ {XMLName: xml.Name{ + Space: "http://jabber.org/protocol/disco#info", Local: "identity", }, Attrs: []xml.Attr{ From b3c11fb1515132d52c18c4c54c55aa1f74c9842f Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Mon, 15 Jan 2018 12:52:16 +0100 Subject: [PATCH 3/4] If codecov script is not available, do not try to upload --- test.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 14782ff..341553c 100755 --- a/test.sh +++ b/test.sh @@ -11,4 +11,6 @@ for d in $(go list ./... | grep -v vendor); do fi done -./codecov.sh \ No newline at end of file +if [ -f "./codecov.sh" ]; then + ./codecov.sh +fi From 20c2c449417abe413a6ccb976903bbf7b5bb01ce Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Mon, 15 Jan 2018 12:52:28 +0100 Subject: [PATCH 4/4] Fix broken tests --- cmd/xmpp_component/xmpp_component.go | 2 +- cmd/xmpp_jukebox/xmpp_jukebox.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/xmpp_component/xmpp_component.go b/cmd/xmpp_component/xmpp_component.go index a48426b..e2126d1 100644 --- a/cmd/xmpp_component/xmpp_component.go +++ b/cmd/xmpp_component/xmpp_component.go @@ -21,7 +21,7 @@ func main() { switch p := packet.(type) { case xmpp.IQ: - switch inner := p.Payload.(type) { + switch inner := p.Payload[0].(type) { case *xmpp.Node: fmt.Printf("%q\n", inner) diff --git a/cmd/xmpp_jukebox/xmpp_jukebox.go b/cmd/xmpp_jukebox/xmpp_jukebox.go index a67d1fe..2d7819f 100644 --- a/cmd/xmpp_jukebox/xmpp_jukebox.go +++ b/cmd/xmpp_jukebox/xmpp_jukebox.go @@ -63,7 +63,7 @@ func processMessage(client *xmpp.Client, p *mpg123.Player, packet *xmpp.Message) } func processIq(client *xmpp.Client, p *mpg123.Player, packet *xmpp.IQ) { - switch payload := packet.Payload.(type) { + switch payload := packet.Payload[0].(type) { // We support IOT Control IQ case *iot.ControlSet: var url string @@ -76,7 +76,7 @@ func processIq(client *xmpp.Client, p *mpg123.Player, packet *xmpp.IQ) { playSCURL(p, url) setResponse := new(iot.ControlSetResponse) - reply := xmpp.IQ{PacketAttrs: xmpp.PacketAttrs{To: packet.From, Type: "result", Id: packet.Id}, Payload: setResponse} + reply := xmpp.IQ{PacketAttrs: xmpp.PacketAttrs{To: packet.From, Type: "result", Id: packet.Id}, Payload: []xmpp.IQPayload{setResponse}} client.Send(reply.XMPPFormat()) // TODO add Soundclound artist / title retrieval sendUserTune(client, "Radiohead", "Spectre")