package stanza import ( "encoding/xml" "github.com/google/uuid" ) /* TODO support ability to put Raw payload inside IQ */ // ============================================================================ // IQ Packet // IQ implements RFC 6120 - A.5 Client Namespace (a part) type IQ struct { // Info/Query XMLName xml.Name `xml:"iq"` // MUST have a ID Attrs // We can only have one payload on IQ: // "An IQ stanza of type "get" or "set" MUST contain exactly one // child element, which specifies the semantics of the particular // request." Payload IQPayload `xml:",omitempty"` Error Err `xml:"error,omitempty"` // Any is used to decode unknown payload as a generic structure Any *Node `xml:",any"` } type IQPayload interface { Namespace() string } func NewIQ(a Attrs) IQ { // TODO ensure that type is set, as it is required if a.Id == "" { if id, err := uuid.NewRandom(); err == nil { a.Id = id.String() } } return IQ{ XMLName: xml.Name{Local: "iq"}, Attrs: a, } } func (iq IQ) MakeError(xerror Err) IQ { from := iq.From to := iq.To iq.Type = "error" iq.From = to iq.To = from iq.Error = xerror return iq } 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 func (iq *IQ) 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 == "type" { iq.Type = StanzaType(attr.Value) } if attr.Name.Local == "to" { iq.To = attr.Value } if attr.Name.Local == "from" { iq.From = attr.Value } } // decode inner elements for { t, err := d.Token() if err != nil { return err } switch tt := t.(type) { case xml.StartElement: if tt.Name.Local == "error" { var xmppError Err err = d.DecodeElement(&xmppError, &tt) if err != nil { return err } iq.Error = xmppError continue } if iqExt := TypeRegistry.GetIQExtension(tt.Name); iqExt != nil { // Decode payload extension err = d.DecodeElement(iqExt, &tt) if err != nil { return err } iq.Payload = iqExt continue } // TODO: If unknown decode as generic node node := new(Node) err = d.DecodeElement(node, &tt) if err != nil { return err } iq.Any = node case xml.EndElement: if tt == start.End() { return nil } } } }