Documentation ¶
Overview ¶
Package for implementing XMPP clients and components.
The package is built around the concept of an XML stream - a pair of XML documents written to and read from a TCP connection. Top-level elements in the document form the messages processed by either end of the connection.
An XML stream is then configured for an XMPP conversation, as either a client (chat, etc) or component (a sort of server plugin).
Create a client:
jid, err := xmpp.ParseJID("alice@wonderland.lit/some-resource") addr, err := xmpp.HomeServerAddrs(jid) stream, err := xmpp.NewStream(addr[0], nil) X, err := xmpp.NewClientXMPP(stream, jid, "password", nil)
Create a component:
jid, err := xmpp.ParseJID("rabbithole.wonderland.lit") stream, err := xmpp.NewStream("localhost:5347", nil) X, err := xmpp.NewComponentXMPP(stream, jid, "secret")
Outgoing XMPP stanzas are sent to the XMPP instance's Out channel, e.g. a client typically announces its presence on the XMPP network as soon as it's connected:
X.Out <- xmpp.Presence{}
Incoming messages are handled by consuming the XMPP instance's In channel. The channel is sent all XMPP stanzas as well as terminating error (io.EOF for clean shutdown or any other error for something unexpected). The channel is also closed after an error.
XMPP defines four types of stanza: <error/>, <iq/>, <message/> and <presence/> represented by Error, IQ, Message (shown below) and Presence structs respectively.
for i := range X.In { switch v := i.(type) { case error: log.Printf("error : %v\n", v) case *xmpp.Message: log.Printf("msg : %s says %s\n", v.From, v.Body) default: log.Printf("%T : %v\n", v, v) } }
Note: A "bound" JID is negotatiated during XMPP setup and may be different to the JID passed to the New(Client|Component)XMPP() call. Always use the XMPP instance's JID attribute in any stanzas.
Index ¶
- Constants
- Variables
- func HomeServerAddrs(jid JID) (addr []string, err error)
- func SessionID() string
- func UUID4() string
- type Active
- type AdHocCommand
- type AdHocField
- type AdHocFieldOption
- type AdHocNote
- type AdHocXForm
- type ClientConfig
- type Composing
- type Confirm
- type Disco
- type DiscoFeature
- type DiscoIdentity
- type DiscoInfo
- type DiscoItem
- type DiscoItems
- type Error
- type ErrorCondition
- type FilterID
- type Gone
- type IQ
- type Inactive
- type JID
- type Matcher
- type MatcherFunc
- type Message
- type MessageBody
- type Paused
- type Ping
- type Presence
- type RegisterQuery
- type RegisterRegistered
- type RegisterRemove
- type RemoteRosterManagerQuery
- type RosterItem
- type RosterQuery
- type SoftwareVersion
- type Stream
- func (stream *Stream) Decode(v interface{}, start *xml.StartElement) error
- func (stream *Stream) Next() (*xml.StartElement, error)
- func (stream *Stream) Send(v interface{}) error
- func (stream *Stream) SendEnd(end *xml.EndElement) error
- func (stream *Stream) SendStart(start *xml.StartElement) (*xml.StartElement, error)
- func (stream *Stream) Skip() error
- func (stream *Stream) UpgradeTLS(config *tls.Config) error
- type StreamConfig
- type VCard
- type XMPP
- Bugs
Constants ¶
const ( NodeAdHocCommand = "http://jabber.org/protocol/commands" ActionAdHocExecute = "execute" ActionAdHocNext = "next" ActionAdHocCancel = "cancel" StatusAdHocExecute = "executing" StatusAdHocCompleted = "completed" StatusAdHocCanceled = "canceled" TypeAdHocForm = "form" TypeAdHocResult = "result" TypeAdHocSubmit = "submit" TypeAdHocListSingle = "list-single" TypeAdHocListMulti = "list-multi" TypeAdHocNoteInfo = "info" TypeAdHocNoteWarning = "warn" TypeAdHocNoteError = "error" TypeAdHocFieldListMulti = "list-multi" TypeAdHocFieldListSingle = "list-single" TypeAdHocFieldTextSingle = "text-single" TypeAdHocFieldJidSingle = "jid-single" TypeAdHocFieldTextPrivate = "text-private" )
const ( NSDiscoInfo = "http://jabber.org/protocol/disco#info" NSDiscoItems = "http://jabber.org/protocol/disco#items" )
const ( NSRemoteRosterManager = "urn:xmpp:tmp:roster-management:0" RemoteRosterManagerTypeRequest = "request" RemoteRosterManagerTypeAllowed = "allowed" RemoteRosterManagerTypeRejected = "rejected" )
const ( NSRoster = "jabber:iq:roster" RosterSubscriptionBoth = "both" RosterSubscriptionFrom = "from" RosterSubscriptionTo = "to" RosterSubscriptionRemove = "remove" )
const ( IQTypeGet = "get" IQTypeSet = "set" IQTypeResult = "result" IQTypeError = "error" MessageTypeNormal = "normal" MessageTypeChat = "chat" MessageTypeError = "error" )
const (
// Standard port for XMPP clients to connect to.
ClientPort = 5222
)
const (
NSChatStatesNotification = "http://jabber.org/protocol/chatstates"
)
const (
NSHTTPAuth = "http://jabber.org/protocol/http-auth"
)
const (
NSJabberClient = "jabber:iq:version"
)
const (
NSPing = "urn:xmpp:ping"
)
const (
NSRegister = "jabber:iq:register"
)
const (
NSVCardTemp = "vcard-temp"
)
Variables ¶
var ( ErrorFeatureNotImplemented = ErrorCondition{nsErrorStanzas, "feature-not-implemented"} ErrorRemoteServerNotFound = ErrorCondition{nsErrorStanzas, "remote-server-not-found"} ErrorNotAuthorized = ErrorCondition{nsErrorStanzas, "not-authorized"} ErrorConflict = ErrorCondition{nsErrorStanzas, "conflict"} ErrorNotAcceptable = ErrorCondition{nsErrorStanzas, "not-acceptable"} ErrorForbidden = ErrorCondition{nsErrorStanzas, "forbidden"} )
Stanza errors.
var DiscoPayloadMatcher = MatcherFunc( func(v interface{}) bool { iq, ok := v.(*IQ) if !ok { return false } ns := strings.Split(iq.PayloadName().Space, "#")[0] return ns == discoNamespacePrefix }, )
Matcher instance to match <iq/> stanzas with a disco payload.
Functions ¶
func HomeServerAddrs ¶
Perform a DNS SRV lookup and return an ordered list of "host:port" TCP addresses for the JID's home server. If no SRV records are found then assume the JID's domain is also the home server.
Types ¶
type Active ¶
type AdHocCommand ¶
type AdHocCommand struct { XMLName xml.Name `xml:"http://jabber.org/protocol/commands command"` Node string `xml:"node,attr"` Action string `xml:"action,attr"` SessionID string `xml:"sessionid,attr"` Status string `xml:"status,attr"` XForm AdHocXForm `xml:"x"` Note AdHocNote `xml:"note,omitempty"` }
type AdHocField ¶
type AdHocField struct { Var string `xml:"var,attr"` Label string `xml:"label,attr"` Type string `xml:"type,attr"` Options []AdHocFieldOption `xml:"option"` Value string `xml:"value,omitempty"` }
type AdHocNote ¶
type AdHocXForm ¶
type ClientConfig ¶
type ClientConfig struct { // Don't upgrade the connection to TLS, even if the server supports it. If // the server *requires* TLS then this option is ignored. NoTLS bool // Skip verification of the server's certificate chain. Probably only // useful during development. InsecureSkipVerify bool }
Config structure used to create a new XMPP client connection.
type Composing ¶
type Confirm ¶
type Confirm struct { XMLName xml.Name `xml:"http://jabber.org/protocol/http-auth confirm"` ID string `xml:"id,attr"` Method string `xml:"method,attr"` URL string `xml:"url,attr"` }
XEP-0070: Verifying HTTP Requests via XMPP
type Disco ¶
type Disco struct {
XMPP *XMPP
}
Service Discovery (XEP-0030) protocol. "Wraps" XMPP instance to provide a more convenient API for Disco clients.
func (*Disco) Info ¶
Request information about the service identified by 'to'.
func (*Disco) Items ¶
func (disco *Disco) Items(to, from, node string) (*DiscoItems, error)
Request items in the service identified by 'to'.
type DiscoIdentity ¶
type DiscoIdentity struct { Category string `xml:"category,attr"` Type string `xml:"type,attr"` Name string `xml:"name,attr"` }
Identity
type DiscoInfo ¶
type DiscoInfo struct { XMLName xml.Name `xml:"http://jabber.org/protocol/disco#info query"` Node string `xml:"node,attr"` Identity []DiscoIdentity `xml:"identity"` Feature []DiscoFeature `xml:"feature"` }
IQ get/result payload for "info" requests.
type DiscoItem ¶
type DiscoItem struct { JID string `xml:"jid,attr"` Node string `xml:"node,attr"` Name string `xml:"name,attr"` }
Item.
type DiscoItems ¶
type DiscoItems struct { XMLName xml.Name `xml:"http://jabber.org/protocol/disco#items query"` Node string `xml:"node,attr"` Item []DiscoItem `xml:"item"` }
IQ get/result payload for "items" requests.
type Error ¶
type Error struct { XMLName xml.Name `xml:"error"` Code string `xml:"code,attr,omitempty"` Type string `xml:"type,attr"` Payload string `xml:",innerxml"` }
XMPP <error/>. May occur as a top-level stanza or embedded in another stanza, e.g. an <iq type="error"/>.
func NewError ¶
func NewError(errorType string, condition ErrorCondition, text string) *Error
Create a new Error instance using the args as the payload.
func NewErrorWithCode ¶
func NewErrorWithCode(code, errorType string, condition ErrorCondition, text string) *Error
func (Error) Condition ¶
func (e Error) Condition() ErrorCondition
Return the error condition from the payload.
type FilterID ¶
type FilterID int64
Uniquely identifies a stream filter. Used to remove a filter that's no longer needed.
type Gone ¶
type IQ ¶
type IQ struct { XMLName xml.Name `xml:"iq"` ID string `xml:"id,attr"` Type string `xml:"type,attr"` To string `xml:"to,attr,omitempty"` From string `xml:"from,attr,omitempty"` Payload string `xml:",innerxml"` Error *Error `xml:"error"` }
XMPP <iq/> stanza.
func (*IQ) PayloadDecode ¶
Decode the payload (an XML string) into the given value. See xml.Unmarshal for how the value is decoded.
func (*IQ) PayloadEncode ¶
Encode the value to an XML string and set as the payload. See xml.Marshal for how the value is encoded.
func (*IQ) PayloadName ¶
Return the name of the payload element.
type Inactive ¶
type JID ¶
type JID struct { // Node/local component e.g. the alice of alice@example.com/foo. Node string // Domain component, e.g. the example.com of alice@example.com/foo for a // client or the whole JID of a component. Domain string // Resource component, e.g. the foo of alice@example.com/foo. Resource string }
Jabber Identifier - uniquely identifies an individual entity in a XMPP/Jabber network.
type Matcher ¶
type Matcher interface { // Return true if the stanza, v, matches. Match(v interface{}) (match bool) }
Interface used to test if a stanza matches some application-defined conditions.
type MatcherFunc ¶
type MatcherFunc func(v interface{}) bool
Adapter to allow a plain func to be used as a Matcher.
func (MatcherFunc) Match ¶
func (fn MatcherFunc) Match(v interface{}) bool
Implement Matcher by calling the adapted func.
type Message ¶
type Message struct { XMLName xml.Name `xml:"message"` ID string `xml:"id,attr,omitempty"` Type string `xml:"type,attr,omitempty"` To string `xml:"to,attr,omitempty"` From string `xml:"from,attr,omitempty"` Subject string `xml:"subject,omitempty"` Body []MessageBody `xml:"body,omitempty"` Thread string `xml:"thread,omitempty"` Error *Error `xml:"error"` Lang string `xml:"xml:lang,attr,omitempty"` Confirm *Confirm `xml:"confirm"` // XEP-0070 Active *Active `xml:"active"` // XEP-0085 Composing *Composing `xml:"composing"` // XEP-0085 Paused *Paused `xml:"paused"` // XEP-0085 Inactive *Inactive `xml:"inactive"` // XEP-0085 Gone *Gone `xml:"gone"` // XEP-0085 }
XMPP <message/> stanza.
type MessageBody ¶
type Paused ¶
type Presence ¶
type Presence struct { XMLName xml.Name `xml:"presence"` ID string `xml:"id,attr,omitempty"` Type string `xml:"type,attr,omitempty"` To string `xml:"to,attr,omitempty"` From string `xml:"from,attr,omitempty"` Show string `xml:"show"` // away, chat, dnd, xa Status string `xml:"status"` // sb []clientText Photo string `xml:"photo,omitempty"` // Avatar Nick string `xml:"nick,omitempty"` // Nickname }
XMPP <presence/> stanza.
type RegisterQuery ¶
type RegisterQuery struct { XMLName xml.Name `xml:"jabber:iq:register query"` Instructions string `xml:"instructions"` Username string `xml:"username"` Password string `xml:"password"` XForm AdHocXForm `xml:"x"` Registered *RegisterRegistered `xmp:"registered"` Remove *RegisterRemove `xmp:"remove"` }
type RemoteRosterManagerQuery ¶
type RosterItem ¶
type RosterQuery ¶
type RosterQuery struct { XMLName xml.Name `xml:"jabber:iq:roster query"` Items []RosterItem `xml:"item"` }
type SoftwareVersion ¶
type SoftwareVersion struct { XMLName xml.Name `xml:"jabber:iq:version query"` Name string `xml:"name,omitempty"` Version string `xml:"version,omitempty"` OS string `xml:"os,omitempty"` }
XEP-0092 Software Version
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
func NewStream ¶
func NewStream(addr string, config *StreamConfig) (*Stream, error)
Create a XML stream connection. A Stream is used by an XMPP instance to handle sending and receiving XML data over the net connection.
func (*Stream) Decode ¶
func (stream *Stream) Decode(v interface{}, start *xml.StartElement) error
Decode a stanza. If start is not nil, the stanza for the start element that's already been consumed is read. A nil start will read the next stanza in the stream. See xml.Decoder.DecodeElement for decoding rules.
func (*Stream) Next ¶
func (stream *Stream) Next() (*xml.StartElement, error)
Find start of next stanza. Bad things are very likely to happen if a call to Next() is successful but you don't actually decode or skip the element.
func (*Stream) Send ¶
Send a stanza. Used to write a complete, top-level element.
func (*Stream) SendEnd ¶
func (stream *Stream) SendEnd(end *xml.EndElement) error
Send the end element that closes the stream.
func (*Stream) SendStart ¶
func (stream *Stream) SendStart(start *xml.StartElement) (*xml.StartElement, error)
Send the element's start tag. Typically used to open the stream's document.
func (*Stream) Skip ¶
Skip reads tokens until it reaches the end element of the most recent start element that has already been read.
type StreamConfig ¶
type StreamConfig struct { // Log all sent and received stanzas. // Enabling this option causes stanzas to be buffered in memory before they // are either sent to the server or delivered to the application. It also // causes incoming stanzas to be XML-parsed a second time. LogStanzas bool // The dommain connection for certificate validation. ConnectionDomain string }
Stream configuration.
type XMPP ¶
type XMPP struct { // JID associated with the stream. Note: this may be negotiated with the // server during setup and so must be used for all messages. JID JID // Channel of incoming messages. Values will be one of IQ, Message, // Presence, Error or error. Will be closed at the end when the stream is // closed or the stream's net connection dies. In chan interface{} // Channel of outgoing messages. Messages must be able to be marshaled by // the standard xml package, however you should try to send one of IQ, // Message or Presence. Out chan interface{} // contains filtered or unexported fields }
Handles XMPP conversations over a Stream. Use NewClientXMPP or NewComponentXMPP to create and configure a XMPP instance. Close the conversation by closing the Out channel, the In channel will be closed when the remote server closes its stream.
func NewClientXMPP ¶
Create a client XMPP over the stream.
func NewComponentXMPP ¶
Create a component XMPP connection over the stream.
func (*XMPP) AddFilter ¶
Add a filter that routes matching stanzas to the returned channel. A FilterID is also returned and can be pased to RemoveFilter to remove the filter again.
func (*XMPP) RemoveFilter ¶
Remove a filter previously added with AddFilter.
Notes ¶
Bugs ¶
authentication incorrectly reports, "No supported SASL mechanism found", for authentication attemtps that fail due to invalid credentials.
ParseJID should fail for incorrectly formatted JIDs.
Filter channels are not closed when the stream is closed.