diff --git a/xmpp/auth.go b/xmpp/auth.go index e8c0e42..2874cae 100644 --- a/xmpp/auth.go +++ b/xmpp/auth.go @@ -66,8 +66,11 @@ type saslFailure struct { type bindBind struct { XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"` - Resource string - Jid string + Resource string `xml:"resource,omitempty"` + Jid string `xml:"jid,omitempty"` +} + +func (*bindBind) IsIQPayload() { } // Session is obsolete in RFC 6121. diff --git a/xmpp/iot/control.go b/xmpp/iot/control.go index 19a5e4d..16908cc 100644 --- a/xmpp/iot/control.go +++ b/xmpp/iot/control.go @@ -2,16 +2,24 @@ package iot import "encoding/xml" +/* type Control struct { ControlSet ControlSet `xml:",omitempty"` ControlGetForm ControlGetForm `xml:",omitempty"` } +func (*Control) IQPayload() { +} +*/ + type ControlSet struct { XMLName xml.Name `xml:"urn:xmpp:iot:control set"` Fields []ControlField `xml:",any"` } +func (*ControlSet) IsIQPayload() { +} + type ControlGetForm struct { XMLName xml.Name `xml:"urn:xmpp:iot:control getForm"` } diff --git a/xmpp/iq.go b/xmpp/iq.go index 10e2ab5..1ecabf5 100644 --- a/xmpp/iq.go +++ b/xmpp/iq.go @@ -10,9 +10,66 @@ import ( type ClientIQ struct { XMLName xml.Name `xml:"jabber:client iq"` Packet - Bind bindBind `xml:",omitempty"` - iot.Control - RawXML string `xml:",innerxml"` + Payload IQPayload `xml:",omitempty"` + RawXML string `xml:",innerxml"` // TODO We need to support detecting the IQ namespace / Query packet // Error clientError } + +type IQPayload interface { + IsIQPayload() +} + +// UnmarshalXML implements custom parsing for IQs +func (iq *ClientIQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + iq.XMLName = start.Name + // Extract IQ attributes + for _, attr := range start.Attr { + if attr.Name.Local == "id" { + iq.Id = 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 + for { + t, err := d.Token() + if err != nil { + return err + } + + var p IQPayload + switch tt := t.(type) { + + case xml.StartElement: + switch tt.Name.Space + " " + tt.Name.Local { + case "urn:ietf:params:xml:ns:xmpp-bind bind": + p = new(bindBind) + case "urn:xmpp:iot:control set": + p = new(iot.ControlSet) + // TODO: Add a default Type that passes RawXML + } + if p != nil { + err = d.DecodeElement(p, &tt) + if err != nil { + return err + } + iq.Payload = p + p = nil + } + + case xml.EndElement: + if tt == start.End() { + return nil + } + } + } +} diff --git a/xmpp/session.go b/xmpp/session.go index 2704e22..6a27fc8 100644 --- a/xmpp/session.go +++ b/xmpp/session.go @@ -154,11 +154,18 @@ func (s *Session) bind(o Options) { } var iq ClientIQ - if s.err = s.decoder.Decode(&iq); s.err != nil || &iq.Bind == nil { - s.err = errors.New("iq bind result missing: " + s.err.Error()) + if s.err = s.decoder.Decode(&iq); s.err != nil { + s.err = errors.New("error decoding iq bind result: " + s.err.Error()) return } - s.BindJid = iq.Bind.Jid // our local id (with possibly randomly generated resource + + switch payload := iq.Payload.(type) { + case *bindBind: + s.BindJid = payload.Jid // our local id (with possibly randomly generated resource + default: + s.err = errors.New("iq bind result missing") + } + return }