util.stanza

The stanza module provides several methods for building and manipulating stanza objects.

Utility functions

Stanza construction

All of these functions create and return a new stanza object, on which you can call various methods, and which you may convert to a string of XML at any time with tostring(mystanza).

stanza(name, attr)

Creates a new stanza, with the specified name and attributes (attributes are standard key→value tables).

message(attr, body)

Helper for creating message stanzas, equivalent to stanza("message", attr), or stanza("message", attr):body(body) with the second argument.

presence(attr)

Helper for creating presence stanzas, equivalent to stanza("presence", attr).

iq(attr)

Helper for creating iq stanzas, equivalent to stanza("iq", attr).

reply(stanza)

Creates a 'reply' stanza. Replies are the same kind of stanza, with the to/from switched accordingly, and with the same id. If the stanza is an iq stanza, the reply's 'type' attribute will be 'result', otherwise it will be the same type as the original stanza.

error_reply(stanza, error_type, error_condition, error_text, error_by)

Constructs and returns an appropriate error reply to the given stanza. As reply() above, but the 'type' attribute is always 'error'.

error_type must be one of: 'auth', 'cancel', 'continue', 'modify', 'wait' (see descriptions in RFC 6120)

error_condition must be a valid XMPP error condition, as in RFC 6120 Defined Error Conditions.

error_text is optional, and may be set to a human-readable text description of the error.

error_by can be set to specify the JID that originates the error when this differs from the to of the original stanza.

clone(stanza)

Return a clone of the given stanza, useful to hold on to copies of stanzas that may be modified by other plugins in Prosody.

Serialization

These functions are for preparing stanzas for storage, not for converted them to XML. For that see tostring(stanza).

preserialize(stanza)

Returns a version of the stanza that is suitable for serialization, e.g. through our storage API or util.serialization. You must call deserialize() below on the stanza before using it, when you read it back.

deserialize(stanza)

Make a full stanza object out of a previously serialized stanza.

Stanza objects

Properties

name

The name of the top tag of this object.

attr

A table of attributes for this object.

Numeric indexes

Every stanza object is itself an array of its child nodes, with strings for text nodes and stanza objects for child tags. Iterating over the children should be done using the children() method.

tags

An array that contains only the child tags of this stanza ( i.e. excluding text nodes). Searching for specific tags should be done using the get_child() method. Iterating over the tags should be done with the childtags() method.

Putting these all together, a simple example:

   print("You have received a"..mystanza.name.."stanza from"..mystanza.attr.from..".")
   print("It contains "..#mystanza.." child nodes, "..#mystanza.tags.." of which are tags.");

Methods

Stanza construction

These functions are used for adding content to stanza objects. For a full example of building a stanza from scratch, see Building a stanza.

stanza:tag(name, attr)

Inserts a child tag with the specified name and attributes to the stanza.

Calls can be chained, for example:

local my_message = stanza.message({to = "myfriend@example.com", type = "chat" })
                   :tag("body"):text("Hello there!")
print(my_message)

would produce:

<message to='myfriend@example.com' type='chat'>
     <body>Hello there!</body>
</message>

stanza:text(text)

Inserts a text node.

stanza:text_tag(name, text, attr)

Convenience method equivalent to stanza:tag(name, attr):text(text):up()

Available starting with 0.12.0.

stanza:up()

Moves "up" in the node tree, so that the next child node may be added as a sibling instead of child node.

stanza.message():tag("subject"):text("hi")
  :tag("body"):text("hello there")
--> <message><subject>hi<body>hello there</body></subject></message>
 
stanza.message():tag("subject"):text("hi"):up()
  :tag("body"):text("hello there")
--> <message><subject>hi</subject><body>hello there</body></message>

stanza:body(text)

Convenience function for inserting a body tag with the specified text content to the stanza. Equivalent to stanza:tag("body"):text(text):up()

So, in the above example, the stanza could also be constructed like this:

    stanza.message({ to = "myfriend@example.com", type = "chat" }):body("Hello there!")

stanza:add_child(child)

Adds a child element (created with stanza() for example) to the current position in the stanza. See stanza:add_direct_child() to add to the element you call the method on, rather than at the current position under it.

stanza:reset()

Moves the 'context' position of the stanza object back to the top, so that stanza:tag() and other methods will work on the topmost element of the stanza. To return only a single level up, see stanza:up().

stanza:add_direct_child(child)

Similar to stanza:add_child(), but adds a child to the stanza element directly, ignoring (and not changing) the current position (so that :tag() and other methods will continue from where they were).

stanza:add_error(error_type, condition, error_message, error_by)

Adds a <error type="{error_type}" by="{error_by}><{condition} xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/><text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">{error_message}</text></error> child element at the current cursor position.

error_type may be a string or a table. If it is a string, its value is used directly for the type attribute. If it is a table, it should be the only argument and it is assumed to originate from util.error. The other arguments are sourced from error_type as follows:

  • error_type.type is used as error_type
  • error_type.condition is used as condition
  • error_type.text is used as error_message
  • error_type.context.by (if present and a string) overrides error_by
  • error_type.extra.uri (if present and a string) is used as a text inside the <gone/> element if condition is equal to gone.
  • error_type.extra.tag (if present and a stanza) is added as a sibling child besides the condition and the text.

If error_type.extra.namespace and error_type.extra.condition are both set and error_type.extra.tag is not set, then a sibling element is generated using the condition as local name and the given namespace.

If error_by (after the above substitutions) is nil or equal to the stanza’s from attribute, the by attribute is omitted.

If error_message (after the above substitutions) is nil, the <text/> child is omitted.

stanza:query(xmlns)

Convenience function for inserting a query tag with the specified xmlns to the stanza.

stanza.iq({ type = "get", to = "example.com" }):query("jabber:iq:version")

Iteration

stanza:children()

Returns an iterator for all the object's immediate children, which are either of type "string" (text nodes) or "table" (child tags, also valid stanza objects).

The iterator can be used with Lua's generic 'for' loop like so:

   for childnode in mystanza:children() do
      print(childnode)
   end

stanza:childtags(name, xmlns)

Like :children() also returns an iterator, but one that only iterates over child tags, and not text nodes. Arguments work like with get_child() below.

Querying stanzas

stanza:get_child(name, xmlns)

Retrieves the first child tag that matches the given name and xmlns. If xmlns is nil or not given then it defaults to the current stanza object's xmlns.

   message = stanza.message({ xmlns = "jabber:client" })
         :tag("body"):text("Hello world"):up()
         :tag("delay", { xmlns = "urn:xmpp:delay", stamp = "2002-09-10T23:08:25Z" })
   print("Body is:", message:get_child("body"):get_text())
   print("Timestamp is:", message:get_child("delay", "urn:xmpp:delay").attr.stamp)

Returns nil if no matching tags are found.

stanza:child_with_name(name)

Returns the first child with the specified name. Warning: this method ignores namespaces, and may be deprecated in future versions. It is recommended to use stanza:get_child() instead.

stanza:child_with_ns(namespace)

Returns the first child with the specified namespace.

stanza:get_text()

Returns the text context of the current stanza.

stanza:get_child_text(name, xmlns)

Returns the text content of the first child tag matching name and xmlns (works like get_child() above), or nil if there's no such child.

stanza:get_child_with_attr()

Added in 0.12

stanza:get_child_with_attr(name, xmlns, attr_name, attr_value, normalize)

Returns a the first child tag with a matching attribute.

For example, given a message like:

<message type="chat">
  <delay xmlns="urn:xmpp:delay" from="capulet.com" stamp="2002-09-10T23:08:25Z"/>
  <delay xmlns="urn:xmpp:delay" from="montague.net" stamp="2002-09-10T23:08:29Z"/>
</message>

Then the second <delay> element can be reached by:

local montague_delay = message:get_child_with_attr("delay", "urn:xmpp:delay", "from", "montague.net");

The normalize argument takes a callback used to normalize the attribute value for comparison, e.g. to enable case insensitive comparison or similar.

stanza:get_child_attr(name, xmlns, attr_name)

Added in trunk

Retrieves a single attribute value from the first child tag that matches the given name and xmlns.

stanza:get_error()

Extracts and returns three values from an error stanza; the error type (e.g. 'auth'), the error condition (e.g. 'service-unavailable') and the error text (if any).

stanza:find(path)

This is a stanza query method similar to XPath, but a lot more simplistic. It only supports extracting a single child, attribute or text node. Its main purpose is to replace long chains of stanza:get_child(name, xmlns), without worrying about missing nodes causing errors.

The path is built of /-separated segments each representing an element, selected by either an XML namespace or an element name or both. The XML namespace is wrapped in { }. The final component can be prefixed with an @ character to return an attribute, or be a single # character to return the text content of the matching element.

Examples:

stanza:find"{urn:xmpp:delay}delay@stamp" -- The timestamp of a XEP-0203 element.
 
stanza:find"{http://jabber.org/protocol/disco#info}query/identity" -- <identity/> element of a info query.
 
stanza:find"{http://jabber.org/protocol/pubsub#event}event/items/item/{http://www.w3.org/2005/Atom}entry/title#"
 --The title text of an Atom entry in a PubSub event.

Modifying stanzas

stanza:maptags(callback)

Iterate over each immediate child of the stanza, and pass it to callback. The callback function must either:

  • return the input element to leave it unmodified
  • return a new element to replace the input element
  • return nil to remove the input element from its parent

XML strings

tostring(stanza)

Return the stanza as a string of XML.

stanza:top_tag()

Returns the top tag of the stanza as a string.

stanza:pretty_print()

Like tostring(stanza), but uses ANSI colour escape sequences to highlight the stanza.

stanza:pretty_top_tag()

As above, but returns only the top tag.

stanza:indent()

Returns a clone of the stanza with whitespace inserted between tags to produce indentation, making the structure easier to see for humans. Any existing whitespace only text nodes are replaced.

Added in 0.12

Examples

Building a stanza

This example XML (taken from XEP-0092):

<iq
    type='result'
    to='romeo@montague.net/orchard'
    from='juliet@capulet.com/balcony'
    id='version_1'>
  <query xmlns='jabber:iq:version'>
    <name>Prosody</name>
    <version>0.8.2</version>
    <os>Ubuntu</os>
  </query>
</iq>

Would be expressed in Lua as:

stanza.iq({ type = "result", to = "romeo@montague.net/orchard", from = "juliet@capulet.com/balcony", id = "version_1" })
    :tag("query", { xmlns = "jabber:iq:version" })
        :tag("name"):text("Prosody"):up()
        :tag("version"):text("0.8.2"):up()
        :tag("os"):text("Ubuntu"):up()

We also have a small command-line Lua script for converting any given XML to its Lua counterpart. It reads in XML and prints out the equivalent Lua representation. You can download it here, and simply run it with 'lua stanzaconv.lua'.