2018-01-01 09:12:33 -08:00
|
|
|
package xmpp // import "fluux.io/xmpp"
|
2016-01-06 07:51:12 -08:00
|
|
|
|
2016-02-15 02:08:54 -08:00
|
|
|
import (
|
|
|
|
"encoding/xml"
|
2016-02-15 07:33:27 -08:00
|
|
|
"fmt"
|
2016-01-06 07:51:12 -08:00
|
|
|
|
2018-01-01 09:35:47 -08:00
|
|
|
"fluux.io/xmpp/iot"
|
2016-02-15 02:08:54 -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
|
2016-02-15 06:22:51 -08:00
|
|
|
Payload IQPayload `xml:",omitempty"`
|
|
|
|
RawXML string `xml:",innerxml"`
|
2016-01-06 07:51:12 -08:00
|
|
|
// TODO We need to support detecting the IQ namespace / Query packet
|
|
|
|
// Error clientError
|
|
|
|
}
|
2016-02-15 06:22:51 -08:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-02-15 06:22:51 -08:00
|
|
|
type IQPayload interface {
|
|
|
|
IsIQPayload()
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2016-02-15 06:22:51 -08:00
|
|
|
iq.XMLName = start.Name
|
|
|
|
// 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
|
|
|
|
}
|
2016-02-15 06:22:51 -08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-15 07:33:27 -08:00
|
|
|
|
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 {
|
2016-02-15 07:33:27 -08:00
|
|
|
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)
|
|
|
|
}
|