The library provides comprehensive representation of XMPP elements as well as tools to work with them. Every such element is represented by an Erlang record. Most of the library's code is auto generated and thus considered to be bug free and efficient.
The approach is very similar to ASN.1, Google Protocol Buffers or Apache Thrift: an XML element is transformed into internal language structure (an Erlang record in our case) - the process known as "decoding". During decoding, validation is also performed, thus well-typed structures are generated, potentially decreasing bugs related to handcrafted parsing. A reverse process known as "encoding" is applied for transforming an Erlang record into an XML element.
The library should be used along with fast_xml
library, because it is only able to decode from and encode to structures
generated by that library (that is, xmlel()
elements).
Table of Contents:
The library is considered as production ready and has been used in
ejabberd XMPP server since version 16.12.
However, the API is quite unstable so far and incompatibilities may be introduced
from release to release. The stable API will be denoted by 2.x
tag in the future.
You need at least Erlang OTP 19.0.
As usual, the following commands are used to obtain and compile the library:
$ git clone https://github.com/processone/xmpp.git
$ cd xmpp
$ make
The full API is documented in doc/API.md
Before calling any function from the library, xmpp
application should be
started.
Although there are several header files which a developer might find useful to look into, they should not be included directly in the code. Only include/xmpp.hrl file should be included, because it already includes all needed headers and also defines some useful macros. So the typical code should look like:
%% file: foo.erl
-module(foo).
-include_lib("xmpp/include/xmpp.hrl").
...
start() ->
application:start(xmpp),
...
All XMPP elements (records) are defined in include/xmpp_codec.hrl
file. For convenience, every record has the corresponding type spec.
There is also predefined xmpp_element()
type which is a container for all
defined record types: so sometimes we will refer to an arbitrary XMPP element as
xmpp_element()
in the rest of this document. These records are generated
automatically by
XML generator
from specification file
specs/xmpp_codec.spec.
The specification file contains information about XML elements
defined within XMPP related namespace.
TODO: writing specs for new elements will be documented later. For now you can learn by example: pick up a spec of any element which is very close to the one you want to add a spec for and use it as a pattern
WARNING: you should not include
xmpp_codec.hrl
in your erlang code. Includexmpp.hrl
instead.xmpp_codec.hrl
should be only used to consult definitions of existing XMPP elements.
Once an xmlel()
element is obtained (either using fxml_stream:parse_element/1
function or by any other means), it can be decoded using either decode/1
or decode/3 functions. The result will be an XMPP element. Note
that decoding might fail if there is no known XMPP element for the provided
xmlel()
element, or xmlel()
is invalid, so you should call these functions
inside try ... catch
. Exceptions returned during decoding can be formatted using
format_error/1 or io_format_error/1
functions.
Example:
handle(#iq{type = get, sub_els = [El]} = IQ) ->
try xmpp:decode(El) of
Pkt -> handle_iq_child_element(Pkt)
catch _:{xmpp_codec, Reason} ->
Txt = xmpp:format_error(Reason),
io:format("iq has malformed child element: ~s", [Txt]),
handle_iq_with_malformed_child_element(IQ)
end.
encode/1 and encode/2 functions can be used for reverse
transformation, i.e. to encode an XMPP element into xmlel()
element.
Encoding would never fail as long as provided XMPP element is valid.
Amongst all XMPP elements defined in the library, the most notable ones
are stanzas: message()
, presence()
and iq()
elements. A large part of xmpp
module deals with stanzas.
Records of all stanzas have several common fields:
id
, type
, lang
, from
, to
, sub_els
and meta
. Although it's
acceptable to manipulate with these fields directly (which is useful in
pattern matching, for example), xmpp
module provides several functions
to work with them:
id
: get_id/1 and set_id/2 can be usedtype
: get_type/1 and set_type/2 can be usedlang
: get_lang/1 and set_lang/2 can be usedfrom
andto
: get_from/1, get_to/1, set_from/2, set_to/2 and set_from_to/3 can be usedsub_els
: get_els/1 and set_els/2 can be used; additional functions to work with subtags directly are: set_subtag/2, get_subtag/2, get_subtags/2, try_subtag/2, try_subtags/2, remove_subtag/2, has_subtag/2, and append_subtags/2.meta
: this field is intended for attaching arbitrary Erlang terms to stanzas; the field is represented by amap()
and can be manipulated using standard maps module, but also using the following functions: get_meta/1, get_meta/2, get_meta/3, set_meta/2, put_meta/3, update_meta/3 and del_meta/2
For creating iq()
replies the following functions exist:
make_iq_result/1 and make_iq_result/2.
These two functions are iq()
specific and create iq()
elements of type
result
only. To create an error response from an arbitrary stanza
(including iq()
) make_error/2 function can be used.
A series of functions is provided by xmpp
module for constructing
stanza_error()
or stream_error()
elements. To construct stanza_error()
elements the functions with err_
prefix can be used, such as
err_bad_request/0 or
err_internal_server_error/2.
To construct stream_error()
elements the functions with serr_
prefix
can be used, such as serr_not_well_formed/0 or
serr_invalid_from/2.
The text element is represented by #text{}
record (of text()
type).
Some record fields, such as #message.body
or #presence.status
,
contain a list of text elements (i.e. [text()]
).
To avoid writting a lot of extracting code the following functions can be used
to manipulate with text()
elements: get_text/1,
get_text/2, mk_text/1 and mk_text/2.
For pretty printing of XMPP elements (for example, for dumping elements in the log in a more readable form), pp/1 function can be used.
There are many predefined macros for XMPP specific XML namespaces defined
in include/ns.hrl such as ?NS_CLIENT
or ?NS_ROSTER
.
WARNING: you should not include
ns.hrl
in your erlang code. Includexmpp.hrl
instead. Consult this file only to obtain a macro name for the required namespace.
There is also get_ns/1 function which can be used
to obtain a namespace of xmpp_element()
or from xmlel()
element.
An XMPP address (aka Jabber ID or JID) can be represented using two types:
jid()
: a JID is represented by a record#jid{}
. This type is used to represent JIDs in XMPP elements.ljid()
: a JID is represented by a tuple{User, Server, Resource}
whereUser
,Server
andResource
are stringprepped version of a nodepart, namepart and resourcepart of a JID respectively. This representation is useful for JIDs comparison and when a JID should be used as a key (in a Mnesia database, ETS table, etc.)
Both types and the record are defined in include/jid.hrl.
WARNING: you should not include
jid.hrl
in your erlang code. Includexmpp.hrl
instead.
Functions for working with JIDs are provided by jid module.
The library uses a callback function in order to perform language translation. The translation is applied when constructing text or error elements. To set the callback one can use set_tr_callback/1 function. By default, no translation is performed.
XMPP elements from the following documents are supported:
- RFC 6120: XMPP Core
- RFC 6121: XMPP Instant Messaging and Presence
- XEP-0004: Data Forms
- XEP-0012: Last Activity
- XEP-0013: Flexible Offline Message Retrieval
- XEP-0016: Privacy Lists
- XEP-0022: Message Events
- XEP-0023: Message Expiration
- XEP-0030: Service Discovery
- XEP-0033: Extended Stanza Addressing
- XEP-0039: Statistics Gathering
- XEP-0045: Multi-User Chat
- XEP-0047: In-Band Bytestreams
- XEP-0048: Bookmarks
- XEP-0049: Private XML Storage
- XEP-0050: Ad-Hoc Commands
- XEP-0054: vcard-temp
- XEP-0055: Jabber Search
- XEP-0059: Result Set Management
- XEP-0060: Publish-Subscribe
- XEP-0065: SOCKS5 Bytestreams
- XEP-0066: Out of Band Data
- XEP-0077: In-Band Registration
- XEP-0078: Non-SASL Authentication
- XEP-0084: User Avatar
- XEP-0085: Chat State Notifications
- XEP-0092: Software Version
- XEP-0114: Jabber Component Protocol
- XEP-0115: Entity Capabilities
- XEP-0131: Stanza Headers and Internet Metadata
- XEP-0138: Stream Compression
- XEP-0153: vCard-Based Avatars
- XEP-0158: CAPTCHA Forms
- XEP-0166: Jingle
- XEP-0172: User Nickname
- XEP-0184: Message Delivery Receipts
- XEP-0191: Blocking Command
- XEP-0198: Stream Management
- XEP-0199: XMPP Ping
- XEP-0202: Entity Time
- XEP-0203: Delayed Delivery
- XEP-0215: External Service Discovery
- XEP-0220: Server Dialback
- XEP-0221: Data Forms Media Element
- XEP-0231: Bits of Binary
- XEP-0234: Jingle File Transfer
- XEP-0249: Direct MUC Invitations
- XEP-0260: Jingle SOCKS5 Bytestreams Transport Method
- XEP-0261: Jingle In-Band Bytestreams Transport Method
- XEP-0264: Jingle Content Thumbnails
- XEP-0279: Server IP Check
- XEP-0280: Message Carbons
- XEP-0297: Stanza Forwarding
- XEP-0300: Use of Cryptographic Hash Functions in XMPP
- XEP-0313: Message Archive Management
- XEP-0319: Last User Interaction in Presence
- XEP-0328: JID Prep
- XEP-0333: Chat Markers
- XEP-0334: Message Processing Hints
- XEP-0352: Client State Indication
- XEP-0355: Namespace Delegation
- XEP-0356: Privileged Entity
- XEP-0357: Push Notifications
- XEP-0359: Unique and Stable Stanza IDs
- XEP-0363: HTTP File Upload
- XEP-0369: Mediated Information eXchange (MIX)
- XEP-0377: Spam Reporting
- XEP-0405: MIX: Participant Server Requirements
- XEP-0417: E2E Authentication in XMPP: Certificate Issuance and Revocation
- XEP-0430: Inbox
- draft-cridland-xmpp-session-01: XMPP Session Establishment
As well as some proprietary extensions from ProcessOne