xmpp

package
v0.0.0-...-efce8db Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2017 License: MIT Imports: 13 Imported by: 4

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

View Source
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"
)
View Source
const (
	NSDiscoInfo  = "http://jabber.org/protocol/disco#info"
	NSDiscoItems = "http://jabber.org/protocol/disco#items"
)
View Source
const (
	NSRemoteRosterManager = "urn:xmpp:tmp:roster-management:0"

	RemoteRosterManagerTypeRequest  = "request"
	RemoteRosterManagerTypeAllowed  = "allowed"
	RemoteRosterManagerTypeRejected = "rejected"
)
View Source
const (
	NSRoster = "jabber:iq:roster"

	RosterSubscriptionBoth   = "both"
	RosterSubscriptionFrom   = "from"
	RosterSubscriptionTo     = "to"
	RosterSubscriptionRemove = "remove"
)
View Source
const (
	IQTypeGet    = "get"
	IQTypeSet    = "set"
	IQTypeResult = "result"
	IQTypeError  = "error"

	MessageTypeNormal = "normal"
	MessageTypeChat   = "chat"
	MessageTypeError  = "error"
)
View Source
const (
	// Standard port for XMPP clients to connect to.
	ClientPort = 5222
)
View Source
const (
	NSChatStatesNotification = "http://jabber.org/protocol/chatstates"
)
View Source
const (
	NSHTTPAuth = "http://jabber.org/protocol/http-auth"
)
View Source
const (
	NSJabberClient = "jabber:iq:version"
)
View Source
const (
	NSPing = "urn:xmpp:ping"
)
View Source
const (
	NSRegister = "jabber:iq:register"
)
View Source
const (
	NSVCardTemp = "vcard-temp"
)

Variables

View Source
var (
	ErrorFeatureNotImplemented = ErrorCondition{nsErrorStanzas, "feature-not-implemented"}
	ErrorRemoteServerNotFound  = ErrorCondition{nsErrorStanzas, "remote-server-not-found"}
	ErrorServiceUnavailable    = ErrorCondition{nsErrorStanzas, "service-unavailable"}
	ErrorNotAuthorized         = ErrorCondition{nsErrorStanzas, "not-authorized"}
	ErrorConflict              = ErrorCondition{nsErrorStanzas, "conflict"}
	ErrorNotAcceptable         = ErrorCondition{nsErrorStanzas, "not-acceptable"}
	ErrorForbidden             = ErrorCondition{nsErrorStanzas, "forbidden"}
)

Stanza errors.

View Source
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

func HomeServerAddrs(jid JID) (addr []string, err error)

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.

func SessionID

func SessionID() string

func UUID4

func UUID4() string

Generate a UUID4.

Types

type Active

type Active struct {
	XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates 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 AdHocFieldOption

type AdHocFieldOption struct {
	Value string `xml:"value"`
}

type AdHocNote

type AdHocNote struct {
	Type  string `xml:"type,attr"`
	Value string `xml:",innerxml"`
}

type AdHocXForm

type AdHocXForm struct {
	XMLName      xml.Name     `xml:"jabber:x:data x"`
	Type         string       `xml:"type,attr"`
	Title        string       `xml:"title"`
	Instructions string       `xml:"instructions"`
	Fields       []AdHocField `xml:"field"`
}

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 Composing struct {
	XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates 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

func (disco *Disco) Info(to, from string) (*DiscoInfo, error)

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 DiscoFeature

type DiscoFeature struct {
	Var string `xml:"var,attr"`
}

Feature

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.

func (Error) Error

func (e Error) Error() string

func (Error) Text

func (e Error) Text() string

Return the error text from the payload, or "" if not present.

type ErrorCondition

type ErrorCondition xml.Name

Error condition.

type FilterID

type FilterID int64

Uniquely identifies a stream filter. Used to remove a filter that's no longer needed.

func (FilterID) Error

func (fid FilterID) Error() string

Implements the error interface for a FilterID.

type Gone

type Gone struct {
	XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates 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

func (iq *IQ) PayloadDecode(v interface{}) error

Decode the payload (an XML string) into the given value. See xml.Unmarshal for how the value is decoded.

func (*IQ) PayloadEncode

func (iq *IQ) PayloadEncode(v interface{}) error

Encode the value to an XML string and set as the payload. See xml.Marshal for how the value is encoded.

func (*IQ) PayloadName

func (iq *IQ) PayloadName() (name xml.Name)

Return the name of the payload element.

func (*IQ) Response

func (iq *IQ) Response(iqType string) *IQ

Create a response IQ. The ID is kept, To and From are reversed, Type is set to the given value.

type Inactive

type Inactive struct {
	XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates 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.

func ParseJID

func ParseJID(s string) (jid JID, err error)

Parse a string into a JID structure.

func (JID) Bare

func (jid JID) Bare() string

Return the "bare" JID, i.e. no resource component.

func (JID) Full

func (jid JID) Full() string

Return the full JID as a string.

func (JID) String

func (jid JID) String() string

Return full JID as a string.

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.

func IQResult

func IQResult(id string) Matcher

Matcher to identify a <iq id="..." type="result" /> stanza with the given id.

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 MessageBody struct {
	Lang  string `xml:"xml:lang,attr,omitempty"`
	Value string `xml:",chardata"`
}

type Paused

type Paused struct {
	XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates paused"`
}

type Ping

type Ping struct {
	XMLName xml.Name `xml:"urn:xmpp:ping ping"`
}

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 RegisterRegistered

type RegisterRegistered struct {
	XMLName xml.Name `xml:"registered"`
}

type RegisterRemove

type RegisterRemove struct {
	XMLName xml.Name `xml:"remove"`
}

type RemoteRosterManagerQuery

type RemoteRosterManagerQuery struct {
	XMLName xml.Name `xml:"urn:xmpp:tmp:roster-management:0 query"`
	Reason  string   `xml:"reason,attr,omitempty"`
	Type    string   `xml:"type,attr"`
}

type RosterItem

type RosterItem struct {
	JID          string   `xml:"jid,attr"`
	Name         string   `xml:"name,attr,omitempty"`
	Subscription string   `xml:"subscription,attr"`
	Groupes      []string `xml:"group"`
}

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

func (stream *Stream) Send(v interface{}) error

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

func (stream *Stream) Skip() error

Skip reads tokens until it reaches the end element of the most recent start element that has already been read.

func (*Stream) UpgradeTLS

func (stream *Stream) UpgradeTLS(config *tls.Config) error

Upgrade the stream's underlying net connection to TLS.

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 VCard

type VCard struct {
	XMLName xml.Name `xml:"vcard-temp vCard"`
}

XEP-0054 vCard

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

func NewClientXMPP(stream *Stream, jid JID, password string, config *ClientConfig) (*XMPP, error)

Create a client XMPP over the stream.

func NewComponentXMPP

func NewComponentXMPP(stream *Stream, jid JID, secret string) (*XMPP, error)

Create a component XMPP connection over the stream.

func (*XMPP) AddFilter

func (x *XMPP) AddFilter(m Matcher) (FilterID, chan interface{})

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) Close

func (x *XMPP) Close()

func (*XMPP) RemoveFilter

func (x *XMPP) RemoveFilter(id FilterID) error

Remove a filter previously added with AddFilter.

func (*XMPP) SendRecv

func (x *XMPP) SendRecv(iq *IQ) (*IQ, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL