Documentation ¶
Overview ¶
Package stanza contains functionality for dealing with XMPP stanzas and stanza level errors.
Stanzas (Message, Presence, and IQ) are the basic building blocks of an XMPP stream. Messages are used to send data that is fire-and-forget such as chat messages. Presence is a publish-subscribe mechanism and is used to broadcast availability on the network (sometimes called "status" in chat, eg. online, offline, or away). IQ (Info/Query) is a request response mechanism for data that requires a response (eg. fetching an avatar or a list of client features).
There are two APIs for creating stanzas in this package, a token based XML stream API where the final stanza can be read from an xml.TokenReader, and a struct based API that relies on embedding structs in this package into the users own types. Stanzas created using either API are not guaranteed to be valid or enforce specific stanza semantics.
Custom Stanzas ¶
The stanza types in this package aren't very useful by themselves. To transmit meaningful data our stanzas must contain a payload. To add a payload with the struct based API we use composition to create a new struct where the payload is represented by additional fields. For example, XEP-0199: XMPP Ping defines an IQ stanza with a payload named "ping" qualified by the "urn:xmpp:ping" namespace. To implement this in our own code we might create a Ping struct similar to the following:
// PingIQ is an IQ stanza with an XEP-0199: XMPP Ping payload. type PingIQ struct { stanza.IQ Ping struct{} `xml:"urn:xmpp:ping ping"` }
For details on marshaling and the use of the xml tag, refer to the encoding/xml package.
We could also create a similar stanza with the token stream API:
// PingIQ returns an xml.TokenReader that outputs a new IQ stanza with a // ping payload. func PingIQ(id string, to jid.JID) xml.TokenReader { start := xml.StartElement{Name: xml.Name{Space: "urn:xmpp:ping", Local: "ping"}} return stanza.IQ{ ID: id, To: to, Type: stanza.GetIQ, }.Wrap(start) }
Example (Stream) ¶
package main import ( "encoding/xml" "log" "os" "mellium.im/xmlstream" "mellium.im/xmpp/jid" "mellium.im/xmpp/stanza" ) // WrapPingIQ returns an xml.TokenReader that outputs a new IQ stanza with // a ping payload. func WrapPingIQ(to jid.JID) xml.TokenReader { start := xml.StartElement{Name: xml.Name{Local: "ping", Space: "urn:xmpp:ping"}} return stanza.IQ{To: to, Type: stanza.GetIQ}.Wrap(xmlstream.Wrap(nil, start)) } func main() { j := jid.MustParse("feste@example.net/siJo4eeT") e := xml.NewEncoder(os.Stdout) e.Indent("", "\t") ping := WrapPingIQ(j) if _, err := xmlstream.Copy(e, ping); err != nil { log.Fatal(err) } if err := e.Flush(); err != nil { log.Fatal(err) } }
Output: <iq type="get" to="feste@example.net/siJo4eeT"> <ping xmlns="urn:xmpp:ping"></ping> </iq>
Example (Struct) ¶
package main import ( "encoding/xml" "log" "os" "mellium.im/xmpp/jid" "mellium.im/xmpp/stanza" ) // PingIQ represents a ping from XEP-0199: XMPP Ping. type PingIQ struct { stanza.IQ Ping struct{} `xml:"urn:xmpp:ping ping"` } func main() { e := xml.NewEncoder(os.Stdout) e.Indent("", "\t") j := jid.MustParse("feste@example.net/siJo4eeT") err := e.Encode(PingIQ{ IQ: stanza.IQ{ Type: stanza.GetIQ, To: j, }, }) if err != nil { log.Fatal(err) } }
Output: <iq id="" to="feste@example.net/siJo4eeT" from="" type="get"> <ping xmlns="urn:xmpp:ping"></ping> </iq>
Index ¶
- Constants
- func AddID(by jid.JID) xmlstream.Transformer
- func AddOriginID(r xml.TokenReader) xml.TokenReader
- func Is(name xml.Name) bool
- type Condition
- type Error
- func (se Error) Error() string
- func (se Error) Is(target error) bool
- func (se Error) MarshalXML(e *xml.Encoder, _ xml.StartElement) error
- func (se Error) TokenReader() xml.TokenReader
- func (se *Error) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
- func (se Error) Wrap(payload xml.TokenReader) xml.TokenReader
- func (se Error) WriteXML(w xmlstream.TokenWriter) (n int, err error)
- type ErrorType
- type ID
- type IQ
- type IQType
- type Message
- type MessageType
- type OriginID
- type Presence
- type PresenceType
Examples ¶
Constants ¶
const (
// The namespace for unique and stable stanza and origin IDs.
NSSid = "urn:xmpp:sid:0"
)
Namespaces used by this package, provided as a convenience.
Variables ¶
This section is empty.
Functions ¶
func AddID ¶ added in v0.19.0
func AddID(by jid.JID) xmlstream.Transformer
AddID returns an transformer that adds a random stanza ID to any stanzas that does not already have one.
func AddOriginID ¶ added in v0.19.0
func AddOriginID(r xml.TokenReader) xml.TokenReader
AddOriginID is an xmlstream.Transformer that adds an origin ID to any stanzas found in the input stream.
Types ¶
type Condition ¶
type Condition string
Condition represents a more specific stanza error condition that can be encapsulated by an <error/> element.
const ( // The sender has sent a stanza containing XML that does not conform to // the appropriate schema or that cannot be processed (e.g., an IQ // stanza that includes an unrecognized value of the 'type' attribute // or an element that is qualified by a recognized namespace but that // violates the defined syntax for the element); the associated error // type SHOULD be "modify". BadRequest Condition = "bad-request" // Access cannot be granted because an existing resource exists with the // same name or address; the associated error type SHOULD be "cancel". Conflict Condition = "conflict" // The feature represented in the XML stanza is not implemented by the // intended recipient or an intermediate server and therefore the stanza // cannot be processed (e.g., the entity understands the namespace but // does not recognize the element name); the associated error type // SHOULD be "cancel" or "modify". FeatureNotImplemented Condition = "feature-not-implemented" // The requesting entity does not possess the necessary permissions to // perform an action that only certain authorized roles or individuals // are allowed to complete (i.e., it typically relates to authorization // rather than authentication); the associated error type SHOULD be // "auth". Forbidden Condition = "forbidden" // The recipient or server can no longer be contacted at this address, // typically on a permanent basis (as opposed to the <redirect/> error // condition, which is used for temporary addressing failures); the // associated error type SHOULD be "cancel" and the error stanza SHOULD // include a new address (if available) as the XML character data of the // <gone/> element (which MUST be a Uniform Resource Identifier [URI] or // Internationalized Resource Identifier [IRI] at which the entity can // be contacted, typically an XMPP IRI as specified in RFC 5122). Gone Condition = "gone" // The server has experienced a misconfiguration or other internal error // that prevents it from processing the stanza; the associated error // type SHOULD be "cancel". InternalServerError Condition = "internal-server-error" // The addressed JID or item requested cannot be found; the associated // error type SHOULD be "cancel". // // Security Warning: An application MUST NOT return this error if // doing so would provide information about the intended recipient's // network availability to an entity that is not authorized to know // such information (for a more detailed discussion of presence // authorization, refer to the discussion of presence subscriptions // in RFC 6121); instead it MUST return a ServiceUnavailable // stanza error. ItemNotFound Condition = "item-not-found" // The sending entity has provided (e.g., during resource binding) or // communicated (e.g., in the 'to' address of a stanza) an XMPP address // that violates the rules of the mellium.im/xmpp/jid package; the // associated error type SHOULD be "modify". // // Implementation Note: Enforcement of the format for XMPP localparts // is primarily the responsibility of the service at which the // associated account or entity is located (e.g., the example.com // service is responsible for returning <jid-malformed/> errors // related to all JIDs of the form <localpart@example.com>), whereas // enforcement of the format for XMPP domainparts is primarily the // responsibility of the service that seeks to route a stanza to the // service identified by that domainpart (e.g., the example.org // service is responsible for returning <jid-malformed/> errors // related to stanzas that users of that service have to tried send // to JIDs of the form <localpart@example.com>). However, any entity // that detects a malformed JID MAY return this error. JIDMalformed Condition = "jid-malformed" // The recipient or server understands the request but cannot process it // because the request does not meet criteria defined by the recipient // or server (e.g., a request to subscribe to information that does not // simultaneously include configuration parameters needed by the // recipient); the associated error type SHOULD be "modify". NotAcceptable Condition = "not-acceptable" // The recipient or server does not allow any entity to perform the // action (e.g., sending to entities at a blacklisted domain); the // associated error type SHOULD be "cancel". NotAllowed Condition = "not-allowed" // The sender needs to provide credentials before being allowed to // perform the action, or has provided improper credentials (the name // "not-authorized", which was borrowed from the "401 Unauthorized" // error of HTTP, might lead the reader to think that this condition // relates to authorization, but instead it is typically used in // relation to authentication); the associated error type SHOULD be // "auth". NotAuthorized Condition = "not-authorized" // The entity has violated some local service policy (e.g., a message // contains words that are prohibited by the service) and the server MAY // choose to specify the policy in the <text/> element or in an // application-specific condition element; the associated error type // SHOULD be "modify" or "wait" depending on the policy being violated. PolicyViolation Condition = "policy-violation" // maintenance, etc.; the associated error type SHOULD be "wait". // // Security Warning: An application MUST NOT return this error if // doing so would provide information about the intended recipient's // network availability to an entity that is not authorized to know // such information (for a more detailed discussion of presence // authorization, refer to the discussion of presence subscriptions // in RFC 6121); instead it MUST return a ServiceUnavailable stanza // error. RecipientUnavailable Condition = "recipient-unavailable" // The recipient or server is redirecting requests for this information // to another entity, typically in a temporary fashion (as opposed to // the <gone/> error condition, which is used for permanent addressing // failures); the associated error type SHOULD be "modify" and the error // stanza SHOULD contain the alternate address in the XML character data // of the <redirect/> element (which MUST be a URI or IRI with which the // sender can communicate, typically an XMPP IRI as specified in // RFC 5122). // // Security Warning: An application receiving a stanza-level redirect // SHOULD warn a human user of the redirection attempt and request // approval before proceeding to communicate with the entity whose // address is contained in the XML character data of the <redirect/> // element, because that entity might have a different identity or // might enforce different security policies. The end-to-end // authentication or signing of XMPP stanzas could help to mitigate // this risk, since it would enable the sender to determine if the // entity to which it has been redirected has the same identity as // the entity it originally attempted to contact. An application MAY // have a policy of following redirects only if it has authenticated // the receiving entity. In addition, an application SHOULD abort // the communication attempt after a certain number of successive // redirects (e.g., at least 2 but no more than 5). Redirect Condition = "redirect" // The requesting entity is not authorized to access the requested // service because prior registration is necessary (examples of prior // registration include members-only rooms in XMPP multi-user chat // [XEP-0045] and gateways to non-XMPP instant messaging services, which // traditionally required registration in order to use the gateway // [XEP-0100]); the associated error type SHOULD be "auth". RegistrationRequired Condition = "registration-required" // A remote server or service specified as part or all of the JID of the // intended recipient does not exist or cannot be resolved (e.g., there // is no _xmpp-server._tcp DNS SRV record, the A or AAAA fallback // resolution fails, or A/AAAA lookups succeed but there is no response // on the IANA-registered port 5269); the associated error type SHOULD // be "cancel". RemoteServerNotFound Condition = "remote-server-not-found" // A remote server or service specified as part or all of the JID of the // intended recipient (or needed to fulfill a request) was resolved but // communications could not be established within a reasonable amount of // time (e.g., an XML stream cannot be established at the resolved IP // address and port, or an XML stream can be established but stream // negotiation fails because of problems with TLS, SASL, Server // Dialback, etc.); the associated error type SHOULD be "wait" (unless // the error is of a more permanent nature, e.g., the remote server is // found but it cannot be authenticated or it violates security // policies). RemoteServerTimeout Condition = "remote-server-timeout" // The server or recipient is busy or lacks the system resources // necessary to service the request; the associated error type SHOULD be // "wait". ResourceConstraint Condition = "resource-constraint" // service; the associated error type SHOULD be "cancel". // // Security Warning: An application MUST return a ServiceUnavailable // stanza error instead of ItemNotFound or RecipientUnavailable if // if sending one of the latter errors would provide information about // the intended recipient's network availability to an entity that is // not authorized to know such information (for a more detailed discussion // of presence authorization, refer to RFC 6121). ServiceUnavailable Condition = "service-unavailable" // The requesting entity is not authorized to access the requested // service because a prior subscription is necessary (examples of prior // subscription include authorization to receive presence information as // defined in RFC 6121 and opt-in data feeds for XMPP publish-subscribe // as defined in [XEP-0060]); the associated error type SHOULD be // "auth". SubscriptionRequired Condition = "subscription-required" // The error condition is not one of those defined by the other // conditions in this list; any error type can be associated with this // condition, and it SHOULD NOT be used except in conjunction with an // application-specific condition. UndefinedCondition Condition = "undefined-condition" // The recipient or server understood the request but was not expecting // it at this time (e.g., the request was out of order); the associated // error type SHOULD be "wait" or "modify". UnexpectedRequest Condition = "unexpected-request" )
A list of stanza error conditions defined in RFC 6120 §8.3.3
type Error ¶
type Error struct { XMLName xml.Name By jid.JID Type ErrorType Condition Condition Text map[string]string }
Error is an implementation of error intended to be marshalable and unmarshalable as XML.
Text is a map of language tags to human readable representations of the error in a given language. Normally there will just be one with an empty language (eg. "": "Some error"). The keys are not validated to make sure they comply with BCP 47.
func (Error) Is ¶ added in v0.19.0
Is will be used by errors.Is when comparing errors. It compares the condition and type fields. If either is empty it is treated as a wildcard. If both are empty the comparison is true if err is of type Error.
For more information see the errors package.
func (Error) MarshalXML ¶
MarshalXML satisfies the xml.Marshaler interface for Error.
Example ¶
package main import ( "encoding/xml" "os" "mellium.im/xmpp/jid" "mellium.im/xmpp/stanza" ) func main() { e := xml.NewEncoder(os.Stdout) e.Indent("", "\t") err := e.Encode(stanza.Error{ By: jid.MustParse("me@example.com"), Type: stanza.Cancel, Condition: stanza.BadRequest, Text: map[string]string{ "en": "Malformed XML in request", }, }) if err != nil { panic(err) } }
Output: <error type="cancel" by="me@example.com"> <bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"></bad-request> <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" xml:lang="en">Malformed XML in request</text> </error>
func (Error) TokenReader ¶ added in v0.3.0
func (se Error) TokenReader() xml.TokenReader
TokenReader satisfies the xmlstream.Marshaler interface for Error.
func (*Error) UnmarshalXML ¶
UnmarshalXML satisfies the xml.Unmarshaler interface for StanzaError.
Example ¶
package main import ( "encoding/xml" "fmt" "strings" "mellium.im/xmpp/stanza" ) func main() { d := xml.NewDecoder(strings.NewReader(` <error type="cancel" by="me@example.com"> <bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"></bad-request> <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Malformed XML</text> </error>`)) se := stanza.Error{} err := d.Decode(&se) if err != nil { panic(err) } fmt.Printf("%s: %s", se, se.Text[""]) }
Output: bad-request: Malformed XML
func (Error) Wrap ¶ added in v0.19.0
func (se Error) Wrap(payload xml.TokenReader) xml.TokenReader
Wrap wraps the payload in an error.
type ErrorType ¶
type ErrorType string
ErrorType is the type of an stanza error payloads. It should normally be one of the constants defined in this package.
const ( // Cancel indicates that the error cannot be remedied and the operation should // not be retried. Cancel ErrorType = "cancel" // Auth indicates that an operation should be retried after providing // credentials. Auth ErrorType = "auth" // Continue indicates that the operation can proceed (the condition was only a // warning). Continue ErrorType = "continue" // Modify indicates that the operation can be retried after changing the data // sent. Modify ErrorType = "modify" // Wait is indicates that an error is temporary and may be retried. Wait ErrorType = "wait" )
type ID ¶ added in v0.19.0
type ID struct { XMLName xml.Name `xml:"urn:xmpp:sid:0 stanza-id"` ID string `xml:"id,attr"` By jid.JID `xml:"by,attr"` }
ID is a unique and stable stanza ID.
func (ID) TokenReader ¶ added in v0.19.0
func (id ID) TokenReader() xml.TokenReader
TokenReader implements xmlstream.Marshaler.
type IQ ¶
type IQ struct { XMLName xml.Name `xml:"iq"` ID string `xml:"id,attr"` To jid.JID `xml:"to,attr,omitempty"` From jid.JID `xml:"from,attr,omitempty"` Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` Type IQType `xml:"type,attr"` }
IQ ("Information Query") is used as a general request response mechanism. IQ's are one-to-one, provide get and set semantics, and always require a response in the form of a result or an error.
func NewIQ ¶ added in v0.15.0
func NewIQ(start xml.StartElement) (IQ, error)
NewIQ unmarshals an XML token into a IQ.
func UnmarshalIQError ¶ added in v0.19.0
func UnmarshalIQError(r xml.TokenReader, start xml.StartElement) (IQ, error)
UnmarshalIQError converts the provided XML token into an IQ. If the type of the IQ is "error" it unmarshals the entire payload and returns the error along with the original IQ.
func (IQ) Error ¶ added in v0.18.0
func (iq IQ) Error(err Error) xml.TokenReader
Error returns a token reader that wraps the first element from payload in an IQ stanza with the to and from attributes switched and the type set to ErrorIQ.
func (IQ) Result ¶ added in v0.15.0
func (iq IQ) Result(payload xml.TokenReader) xml.TokenReader
Result returns a token reader that wraps the first element from payload in an IQ stanza with the to and from attributes switched and the type set to ResultIQ.
func (IQ) StartElement ¶ added in v0.15.0
func (iq IQ) StartElement() xml.StartElement
StartElement converts the IQ into an XML token.
func (IQ) Wrap ¶ added in v0.15.0
func (iq IQ) Wrap(payload xml.TokenReader) xml.TokenReader
Wrap wraps the payload in a stanza.
The resulting IQ may not contain an id or from attribute and thus may not be valid without further processing.
type IQType ¶
type IQType string
IQType is the type of an IQ stanza. It should normally be one of the constants defined in this package.
const ( // GetIQ is used to query another entity for information. GetIQ IQType = "get" // SetIQ is used to provide data to another entity, set new values, and // replace existing values. SetIQ IQType = "set" // ResultIQ is sent in response to a successful get or set IQ. ResultIQ IQType = "result" // ErrorIQ is sent to report that an error occurred during the delivery or // processing of a get or set IQ. ErrorIQ IQType = "error" )
func (IQType) MarshalText ¶ added in v0.14.0
MarshalText ensures that the zero value for IQType is marshaled to XML as a valid IQ get request. It satisfies the encoding.TextMarshaler interface for IQType.
type Message ¶
type Message struct { XMLName xml.Name `xml:"message"` ID string `xml:"id,attr,omitempty"` To jid.JID `xml:"to,attr,omitempty"` From jid.JID `xml:"from,attr,omitempty"` Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` Type MessageType `xml:"type,attr,omitempty"` }
Message is an XMPP stanza that contains a payload for direct one-to-one communication with another network entity. It is often used for sending chat messages to an individual or group chat server, or for notifications and alerts that don't require a response.
func NewMessage ¶ added in v0.15.0
func NewMessage(start xml.StartElement) (Message, error)
NewMessage unmarshals an XML token into a Message.
func (Message) StartElement ¶ added in v0.15.0
func (msg Message) StartElement() xml.StartElement
StartElement converts the Message into an XML token.
func (Message) Wrap ¶ added in v0.15.0
func (msg Message) Wrap(payload xml.TokenReader) xml.TokenReader
Wrap wraps the payload in a stanza.
type MessageType ¶
type MessageType string
MessageType is the type of a message stanza. It should normally be one of the constants defined in this package.
const ( // NormalMessage is a standalone message that is sent outside the context of a // one-to-one conversation or groupchat, and to which it is expected that the // recipient will reply. Typically a receiving client will present a message // of type "normal" in an interface that enables the recipient to reply, but // without a conversation history. NormalMessage MessageType = "normal" // ChatMessage represents a message sent in the context of a one-to-one chat // session. Typically an interactive client will present a message of type // "chat" in an interface that enables one-to-one chat between the two // parties, including an appropriate conversation history. ChatMessage MessageType = "chat" // ErrorMessage is generated by an entity that experiences an error when // processing a message received from another entity. ErrorMessage MessageType = "error" // GroupChatMessage is sent in the context of a multi-user chat environment. // Typically a receiving client will present a message of type "groupchat" in // an interface that enables many-to-many chat between the parties, including // a roster of parties in the chatroom and an appropriate conversation // history. GroupChatMessage MessageType = "groupchat" // HeadlineMessage provides an alert, a notification, or other transient // information to which no reply is expected (e.g., news headlines, sports // updates, near-real-time market data, or syndicated content). Because no // reply to the message is expected, typically a receiving client will present // a message of type "headline" in an interface that appropriately // differentiates the message from standalone messages, chat messages, and // groupchat messages (e.g., by not providing the recipient with the ability // to reply). HeadlineMessage MessageType = "headline" )
type OriginID ¶ added in v0.19.0
type OriginID struct { XMLName xml.Name `xml:"urn:xmpp:sid:0 origin-id"` ID string `xml:"id,attr"` }
OriginID is a unique and stable stanza ID generated by an originating entity that may want to hide its identity.
func (OriginID) TokenReader ¶ added in v0.19.0
func (id OriginID) TokenReader() xml.TokenReader
TokenReader implements xmlstream.Marshaler.
type Presence ¶
type Presence struct { XMLName xml.Name `xml:"presence"` ID string `xml:"id,attr"` To jid.JID `xml:"to,attr"` From jid.JID `xml:"from,attr"` Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` Type PresenceType `xml:"type,attr,omitempty"` }
Presence is an XMPP stanza that is used as an indication that an entity is available for communication. It is used to set a status message, broadcast availability, and advertise entity capabilities. It can be directed (one-to-one), or used as a broadcast mechanism (one-to-many).
func NewPresence ¶ added in v0.15.0
func NewPresence(start xml.StartElement) (Presence, error)
NewPresence unmarshals an XML token into a Presence.
func (Presence) StartElement ¶ added in v0.15.0
func (p Presence) StartElement() xml.StartElement
StartElement converts the Presence into an XML token.
func (Presence) Wrap ¶ added in v0.15.0
func (p Presence) Wrap(payload xml.TokenReader) xml.TokenReader
Wrap wraps the payload in a stanza.
If to is the zero value for jid.JID, no to attribute is set on the resulting presence.
type PresenceType ¶
type PresenceType string
PresenceType is the type of a presence stanza. It should normally be one of the constants defined in this package.
const ( // AvailablePresence is a special case that signals that the entity is // available for communication. AvailablePresence PresenceType = "" // ErrorPresence indicates that an error has occurred regarding processing of // a previously sent presence stanza; if the presence stanza is of type // "error", it MUST include an <error/> child element ErrorPresence PresenceType = "error" // ProbePresence is a request for an entity's current presence. It should // generally only be generated and sent by servers on behalf of a user. ProbePresence PresenceType = "probe" // SubscribePresence is sent when the sender wishes to subscribe to the // recipient's presence. SubscribePresence PresenceType = "subscribe" // SubscribedPresence indicates that the sender has allowed the recipient to // receive future presence broadcasts. SubscribedPresence PresenceType = "subscribed" // communication. UnavailablePresence PresenceType = "unavailable" // UnsubscribePresence indicates that the sender is unsubscribing from the // receiver's presence. UnsubscribePresence PresenceType = "unsubscribe" // UnsubscribedPresence indicates that the subscription request has been // denied, or a previously granted subscription has been revoked. UnsubscribedPresence PresenceType = "unsubscribed" )