2019-06-26 08:14:52 -07:00
|
|
|
package stanza
|
2016-01-06 07:51:12 -08:00
|
|
|
|
2016-02-15 02:08:54 -08:00
|
|
|
import (
|
|
|
|
"encoding/xml"
|
2019-11-28 08:15:15 -08:00
|
|
|
"strings"
|
2019-10-28 13:21:35 -07:00
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2016-02-15 02:08:54 -08:00
|
|
|
)
|
|
|
|
|
2018-01-15 03:28:34 -08:00
|
|
|
/*
|
2018-02-13 13:07:15 -08:00
|
|
|
TODO support ability to put Raw payload inside IQ
|
2018-01-15 03:28:34 -08:00
|
|
|
*/
|
|
|
|
|
2018-01-13 09:50:17 -08:00
|
|
|
// ============================================================================
|
|
|
|
// IQ Packet
|
|
|
|
|
2019-06-22 02:13:33 -07:00
|
|
|
// IQ implements RFC 6120 - A.5 Client Namespace (a part)
|
2018-01-13 09:50:17 -08:00
|
|
|
type IQ struct { // Info/Query
|
|
|
|
XMLName xml.Name `xml:"iq"`
|
2019-06-22 02:13:33 -07:00
|
|
|
// MUST have a ID
|
|
|
|
Attrs
|
2019-06-19 01:21:57 -07:00
|
|
|
// We can only have one payload on IQ:
|
2019-06-13 08:22:39 -07:00
|
|
|
// "An IQ stanza of type "get" or "set" MUST contain exactly one
|
|
|
|
// child element, which specifies the semantics of the particular
|
|
|
|
// request."
|
2019-06-19 01:21:57 -07:00
|
|
|
Payload IQPayload `xml:",omitempty"`
|
2019-11-28 08:15:15 -08:00
|
|
|
Error *Err `xml:"error,omitempty"`
|
2019-09-27 08:23:38 -07:00
|
|
|
// Any is used to decode unknown payload as a generic structure
|
2019-06-24 03:15:29 -07:00
|
|
|
Any *Node `xml:",any"`
|
2016-01-06 07:51:12 -08:00
|
|
|
}
|
2016-02-15 06:22:51 -08:00
|
|
|
|
2019-06-22 02:13:33 -07:00
|
|
|
type IQPayload interface {
|
|
|
|
Namespace() string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewIQ(a Attrs) IQ {
|
|
|
|
// TODO ensure that type is set, as it is required
|
2019-10-28 13:21:35 -07:00
|
|
|
if a.Id == "" {
|
|
|
|
if id, err := uuid.NewRandom(); err == nil {
|
|
|
|
a.Id = id.String()
|
|
|
|
}
|
|
|
|
}
|
2018-01-15 03:28:34 -08:00
|
|
|
return IQ{
|
|
|
|
XMLName: xml.Name{Local: "iq"},
|
2019-06-22 02:13:33 -07:00
|
|
|
Attrs: a,
|
2018-01-15 03:28:34 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-20 09:56:07 -08:00
|
|
|
func (iq IQ) MakeError(xerror Err) IQ {
|
|
|
|
from := iq.From
|
|
|
|
to := iq.To
|
|
|
|
|
|
|
|
iq.Type = "error"
|
|
|
|
iq.From = to
|
|
|
|
iq.To = from
|
2019-11-28 08:15:15 -08:00
|
|
|
iq.Error = &xerror
|
2018-01-20 09:56:07 -08:00
|
|
|
|
|
|
|
return iq
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// 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
|
2018-01-18 08:03:54 -08:00
|
|
|
|
2016-02-15 06:22:51 -08:00
|
|
|
// 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" {
|
2019-06-22 02:13:33 -07:00
|
|
|
iq.Type = StanzaType(attr.Value)
|
2016-02-15 09:33:51 -08:00
|
|
|
}
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode inner elements
|
|
|
|
for {
|
|
|
|
t, err := d.Token()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch tt := t.(type) {
|
|
|
|
case xml.StartElement:
|
2019-06-19 04:46:02 -07:00
|
|
|
if tt.Name.Local == "error" {
|
|
|
|
var xmppError Err
|
|
|
|
err = d.DecodeElement(&xmppError, &tt)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-02-15 06:22:51 -08:00
|
|
|
}
|
2019-11-28 08:15:15 -08:00
|
|
|
iq.Error = &xmppError
|
2019-06-19 04:46:02 -07:00
|
|
|
continue
|
2016-02-15 06:22:51 -08:00
|
|
|
}
|
2019-06-19 04:46:02 -07:00
|
|
|
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
|
|
|
|
}
|
2019-06-22 02:13:33 -07:00
|
|
|
// TODO: If unknown decode as generic node
|
2019-06-24 03:15:29 -07:00
|
|
|
node := new(Node)
|
|
|
|
err = d.DecodeElement(node, &tt)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
iq.Any = node
|
2016-02-15 06:22:51 -08:00
|
|
|
case xml.EndElement:
|
|
|
|
if tt == start.End() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-28 08:15:15 -08:00
|
|
|
|
|
|
|
// Following RFC-3920 for IQs
|
|
|
|
func (iq *IQ) IsValid() bool {
|
|
|
|
// ID is required
|
|
|
|
if len(strings.TrimSpace(iq.Id)) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type is required
|
|
|
|
if iq.Type.IsEmpty() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type get and set must contain one and only one child element that specifies the semantics
|
|
|
|
if iq.Type == IQTypeGet || iq.Type == IQTypeSet {
|
|
|
|
if iq.Payload == nil && iq.Any == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A result must include zero or one child element
|
|
|
|
if iq.Type == IQTypeResult {
|
|
|
|
if iq.Payload != nil && iq.Any != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Error type must contain an "error" child element
|
|
|
|
if iq.Type == IQTypeError {
|
|
|
|
if iq.Error == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|