@ -3,127 +3,20 @@ package xmpp
import (
"encoding/xml"
"fmt"
"strconv"
)
/ *
TODO support ability to put Raw payload inside IQ
* /
// ============================================================================
// XMPP Errors
// Err is an XMPP stanza payload that is used to report error on message,
// presence or iq stanza.
// It is intended to be added in the payload of the erroneous stanza.
type Err struct {
XMLName xml . Name ` xml:"error" `
Code int ` xml:"code,attr,omitempty" `
Type string ` xml:"type,attr,omitempty" `
Reason string
Text string ` xml:"urn:ietf:params:xml:ns:xmpp-stanzas text,omitempty" `
}
func ( x * Err ) Namespace ( ) string {
return x . XMLName . Space
}
// UnmarshalXML implements custom parsing for IQs
func ( x * Err ) UnmarshalXML ( d * xml . Decoder , start xml . StartElement ) error {
x . XMLName = start . Name
// Extract attributes
for _ , attr := range start . Attr {
if attr . Name . Local == "type" {
x . Type = attr . Value
}
if attr . Name . Local == "code" {
if code , err := strconv . Atoi ( attr . Value ) ; err == nil {
x . Code = code
}
}
}
// Check subelements to extract error text and reason (from local namespace).
for {
t , err := d . Token ( )
if err != nil {
return err
}
switch tt := t . ( type ) {
case xml . StartElement :
elt := new ( Node )
err = d . DecodeElement ( elt , & tt )
if err != nil {
return err
}
textName := xml . Name { Space : "urn:ietf:params:xml:ns:xmpp-stanzas" , Local : "text" }
if elt . XMLName == textName {
x . Text = string ( elt . Content )
} else if elt . XMLName . Space == "urn:ietf:params:xml:ns:xmpp-stanzas" {
x . Reason = elt . XMLName . Local
}
case xml . EndElement :
if tt == start . End ( ) {
return nil
}
}
}
}
func ( x Err ) MarshalXML ( e * xml . Encoder , start xml . StartElement ) ( err error ) {
if x . Code == 0 {
return nil
}
// Encode start element and attributes
start . Name = xml . Name { Local : "error" }
code := xml . Attr {
Name : xml . Name { Local : "code" } ,
Value : strconv . Itoa ( x . Code ) ,
}
start . Attr = append ( start . Attr , code )
if len ( x . Type ) > 0 {
typ := xml . Attr {
Name : xml . Name { Local : "type" } ,
Value : x . Type ,
}
start . Attr = append ( start . Attr , typ )
}
err = e . EncodeToken ( start )
// SubTags
// Reason
if x . Reason != "" {
reason := xml . Name { Space : "urn:ietf:params:xml:ns:xmpp-stanzas" , Local : x . Reason }
e . EncodeToken ( xml . StartElement { Name : reason } )
e . EncodeToken ( xml . EndElement { Name : reason } )
}
// Text
if x . Text != "" {
text := xml . Name { Space : "urn:ietf:params:xml:ns:xmpp-stanzas" , Local : "text" }
e . EncodeToken ( xml . StartElement { Name : text } )
e . EncodeToken ( xml . CharData ( x . Text ) )
e . EncodeToken ( xml . EndElement { Name : text } )
}
return e . EncodeToken ( xml . EndElement { Name : start . Name } )
}
// ============================================================================
// IQ Packet
// IQ implements RFC 6120 - A.5 Client Namespace (a part)
type IQ struct { // Info/Query
XMLName xml . Name ` xml:"iq" `
PacketAttrs
// 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
@ -133,16 +26,16 @@ type IQ struct { // Info/Query
RawXML string ` xml:",innerxml" `
}
func NewIQ ( iqtype , from , to , id , lang string ) IQ {
type IQPayload interface {
Namespace ( ) string
}
func NewIQ ( a Attrs ) IQ {
// TODO generate IQ ID if not set
// TODO ensure that type is set, as it is required
return IQ {
XMLName : xml . Name { Local : "iq" } ,
PacketAttrs : PacketAttrs {
Id : id ,
From : from ,
To : to ,
Type : iqtype ,
Lang : lang ,
} ,
Attrs : a ,
}
}
@ -182,7 +75,7 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
iq . Id = attr . Value
}
if attr . Name . Local == "type" {
iq . Type = attr . Value
iq . Type = StanzaType ( attr . Value )
}
if attr . Name . Local == "to" {
iq . To = attr . Value
@ -190,9 +83,6 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if attr . Name . Local == "from" {
iq . From = attr . Value
}
if attr . Name . Local == "lang" {
iq . Lang = attr . Value
}
}
// decode inner elements
@ -223,6 +113,7 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
iq . Payload = iqExt
continue
}
// TODO: If unknown decode as generic node
return fmt . Errorf ( "unexpected element in iq: %s %s" , tt . Name . Space , tt . Name . Local )
case xml . EndElement :
if tt == start . End ( ) {
@ -233,11 +124,7 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
}
// ============================================================================
// Generic IQ Payload
type IQPayload interface {
Namespace ( ) string
}
// Generic / unknown content
// Node is a generic structure to represent XML data. It is used to parse
// unreferenced or custom stanza payload.