LCOV - code coverage report
Current view: top level - src - stanza.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 344 679 50.7 %
Date: 2024-02-03 00:00:00 Functions: 35 53 66.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
       2             : /* stanza.c
       3             : ** strophe XMPP client library -- XMPP stanza object and utilities
       4             : **
       5             : ** Copyright (C) 2005-2009 Collecta, Inc.
       6             : **
       7             : **  This software is provided AS-IS with no warranty, either express
       8             : **  or implied.
       9             : **
      10             : **  This program is dual licensed under the MIT or GPLv3 licenses.
      11             : */
      12             : 
      13             : /** @file
      14             :  *  Stanza creation and manipulation.
      15             :  */
      16             : 
      17             : /** @defgroup Stanza Stanza creation and manipulation
      18             :  */
      19             : 
      20             : #include <stdio.h>
      21             : #include <string.h>
      22             : 
      23             : #include "strophe.h"
      24             : #include "common.h"
      25             : #include "hash.h"
      26             : #include "parser.h"
      27             : 
      28             : /** Create a stanza object.
      29             :  *  This function allocates and initializes a blank stanza object.
      30             :  *  The stanza will have a reference count of one, so the caller does not
      31             :  *  need to clone it.
      32             :  *
      33             :  *  @param ctx a Strophe context object
      34             :  *
      35             :  *  @return a stanza object
      36             :  *
      37             :  *  @ingroup Stanza
      38             :  */
      39          40 : xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
      40             : {
      41          40 :     xmpp_stanza_t *stanza;
      42             : 
      43          40 :     stanza = strophe_alloc(ctx, sizeof(xmpp_stanza_t));
      44          40 :     if (stanza != NULL) {
      45          40 :         memset(stanza, 0, sizeof(xmpp_stanza_t));
      46          40 :         stanza->ref = 1;
      47          40 :         stanza->ctx = ctx;
      48          40 :         stanza->type = XMPP_STANZA_UNKNOWN;
      49             :     }
      50             : 
      51          40 :     return stanza;
      52             : }
      53             : 
      54             : /** Clone a stanza object.
      55             :  *  This function increments the reference count of the stanza object.
      56             :  *
      57             :  *  @param stanza a Strophe stanza object
      58             :  *
      59             :  *  @return the stanza object with it's reference count incremented
      60             :  *
      61             :  *  @ingroup Stanza
      62             :  */
      63           7 : xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *stanza)
      64             : {
      65           7 :     stanza->ref++;
      66             : 
      67           7 :     return stanza;
      68             : }
      69             : 
      70             : /*
      71             :  * Copy the attributes of stanza src into stanza dst. Return -1 on error.
      72             :  */
      73           1 : static int _stanza_copy_attributes(xmpp_stanza_t *dst, const xmpp_stanza_t *src)
      74             : {
      75           1 :     hash_iterator_t *iter;
      76           1 :     const char *key;
      77           1 :     const char *val;
      78           1 :     int rc = XMPP_EOK;
      79             : 
      80           1 :     iter = hash_iter_new(src->attributes);
      81           1 :     if (!iter)
      82           0 :         rc = XMPP_EMEM;
      83             : 
      84           5 :     while (rc == XMPP_EOK && (key = hash_iter_next(iter))) {
      85           4 :         val = hash_get(src->attributes, key);
      86           4 :         if (!val)
      87             :             rc = XMPP_EINT;
      88           4 :         if (rc == XMPP_EOK)
      89           4 :             rc = xmpp_stanza_set_attribute(dst, key, val);
      90             :     }
      91           1 :     hash_iter_release(iter);
      92             : 
      93           1 :     if (rc != XMPP_EOK && dst->attributes) {
      94           0 :         hash_release(dst->attributes);
      95           0 :         dst->attributes = NULL;
      96             :     }
      97           1 :     return rc;
      98             : }
      99             : 
     100             : /** Copy a stanza and its children.
     101             :  *  This function copies a stanza along with all its children and returns
     102             :  *  the new stanza and children with a reference count of 1.  The returned
     103             :  *  stanza will have no parent and no siblings.  This function is useful
     104             :  *  for extracting a child stanza for inclusion in another tree.
     105             :  *
     106             :  *  @param stanza a Strophe stanza object
     107             :  *
     108             :  *  @return a new Strophe stanza object
     109             :  *
     110             :  *  @ingroup Stanza
     111             :  */
     112           0 : xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *stanza)
     113             : {
     114           0 :     xmpp_stanza_t *copy, *child, *copychild, *tail;
     115             : 
     116           0 :     copy = xmpp_stanza_new(stanza->ctx);
     117           0 :     if (!copy)
     118           0 :         goto copy_error;
     119             : 
     120           0 :     copy->type = stanza->type;
     121             : 
     122           0 :     if (stanza->data) {
     123           0 :         copy->data = strophe_strdup(stanza->ctx, stanza->data);
     124           0 :         if (!copy->data)
     125           0 :             goto copy_error;
     126             :     }
     127             : 
     128           0 :     if (stanza->attributes) {
     129           0 :         if (_stanza_copy_attributes(copy, stanza) == -1)
     130           0 :             goto copy_error;
     131             :     }
     132             : 
     133           0 :     tail = copy->children;
     134           0 :     for (child = stanza->children; child; child = child->next) {
     135           0 :         copychild = xmpp_stanza_copy(child);
     136           0 :         if (!copychild)
     137           0 :             goto copy_error;
     138           0 :         copychild->parent = copy;
     139             : 
     140           0 :         if (tail) {
     141           0 :             copychild->prev = tail;
     142           0 :             tail->next = copychild;
     143             :         } else
     144           0 :             copy->children = copychild;
     145           0 :         tail = copychild;
     146             :     }
     147             : 
     148             :     return copy;
     149             : 
     150           0 : copy_error:
     151             :     /* release all the hitherto allocated memory */
     152           0 :     if (copy)
     153           0 :         xmpp_stanza_release(copy);
     154             :     return NULL;
     155             : }
     156             : 
     157             : /** Release a stanza object and all of its children.
     158             :  *  This function releases a stanza object and potentially all of its
     159             :  *  children, which may cause the object(s) to be freed.
     160             :  *
     161             :  *  @param stanza a Strophe stanza object
     162             :  *
     163             :  *  @return TRUE if the object was freed and FALSE otherwise
     164             :  *
     165             :  *  @ingroup Stanza
     166             :  */
     167          47 : int xmpp_stanza_release(xmpp_stanza_t *stanza)
     168             : {
     169          47 :     int released = 0;
     170          47 :     xmpp_stanza_t *child, *tchild;
     171             : 
     172             :     /* release stanza */
     173          47 :     if (stanza->ref > 1)
     174           7 :         stanza->ref--;
     175             :     else {
     176             :         /* release all children */
     177          40 :         child = stanza->children;
     178          71 :         while (child) {
     179          31 :             tchild = child;
     180          31 :             child = child->next;
     181          31 :             tchild->next = NULL;
     182          31 :             xmpp_stanza_release(tchild);
     183             :         }
     184             : 
     185          40 :         if (stanza->attributes)
     186          30 :             hash_release(stanza->attributes);
     187          40 :         if (stanza->data)
     188          36 :             strophe_free(stanza->ctx, stanza->data);
     189          40 :         strophe_free(stanza->ctx, stanza);
     190          40 :         released = 1;
     191             :     }
     192             : 
     193          47 :     return released;
     194             : }
     195             : 
     196             : /** Get the strophe context that the stanza is associated with.
     197             :  *
     198             :  *  @param stanza a Strophe stanza object
     199             :  *
     200             :  *  @return a Strophe context
     201             :  *
     202             :  *  @ingroup Stanza
     203             :  */
     204           0 : xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *stanza)
     205             : {
     206           0 :     return stanza->ctx;
     207             : }
     208             : 
     209             : /** Determine if a stanza is a text node.
     210             :  *
     211             :  *  @param stanza a Strophe stanza object
     212             :  *
     213             :  *  @return TRUE if the stanza is a text node, FALSE otherwise
     214             :  *
     215             :  *  @ingroup Stanza
     216             :  */
     217           0 : int xmpp_stanza_is_text(xmpp_stanza_t *stanza)
     218             : {
     219           0 :     return (stanza && stanza->type == XMPP_STANZA_TEXT);
     220             : }
     221             : 
     222             : /** Determine if a stanza is a tag node.
     223             :  *
     224             :  *  @param stanza a Strophe stanza object
     225             :  *
     226             :  *  @return TRUE if the stanza is a tag node, FALSE otherwise
     227             :  *
     228             :  *  @ingroup Stanza
     229             :  */
     230           0 : int xmpp_stanza_is_tag(xmpp_stanza_t *stanza)
     231             : {
     232           0 :     return (stanza && stanza->type == XMPP_STANZA_TAG);
     233             : }
     234             : 
     235             : /* Escape a string with for use in a XML text node or attribute. Assumes that
     236             :  * the input string is encoded in UTF-8. On success, returns a pointer to a
     237             :  * buffer with the resulting data which must be xmpp_free()'d by the caller.
     238             :  * On failure, returns NULL.
     239             :  */
     240             : 
     241          12 : static char *_escape_xml(xmpp_ctx_t *ctx, char *text)
     242             : {
     243          12 :     size_t len = 0;
     244          12 :     char *src;
     245          12 :     char *dst;
     246          12 :     char *buf;
     247         217 :     for (src = text; *src != '\0'; src++) {
     248         205 :         switch (*src) {
     249           0 :         case '<': /* "&lt;" */
     250             :         case '>': /* "&gt;" */
     251           0 :             len += 4;
     252           0 :             break;
     253           0 :         case '&': /* "&amp;" */
     254           0 :             len += 5;
     255           0 :             break;
     256           0 :         case '"':
     257           0 :             len += 6; /*"&quot;" */
     258           0 :             break;
     259         205 :         default:
     260         205 :             len++;
     261             :         }
     262             :     }
     263          12 :     if ((buf = strophe_alloc(ctx, (len + 1) * sizeof(char))) == NULL)
     264             :         return NULL; /* Error */
     265             :     dst = buf;
     266         217 :     for (src = text; *src != '\0'; src++) {
     267         205 :         switch (*src) {
     268             :         case '<':
     269           0 :             strcpy(dst, "&lt;");
     270           0 :             dst += 4;
     271           0 :             break;
     272             :         case '>':
     273           0 :             strcpy(dst, "&gt;");
     274           0 :             dst += 4;
     275           0 :             break;
     276             :         case '&':
     277           0 :             strcpy(dst, "&amp;");
     278           0 :             dst += 5;
     279           0 :             break;
     280             :         case '"':
     281           0 :             strcpy(dst, "&quot;");
     282           0 :             dst += 6;
     283           0 :             break;
     284         205 :         default:
     285         205 :             *dst = *src;
     286         205 :             dst++;
     287             :         }
     288             :     }
     289          12 :     *dst = '\0';
     290          12 :     return buf;
     291             : }
     292             : 
     293             : /* small helper function */
     294          60 : static void _render_update(
     295             :     int *written, int length, int lastwrite, size_t *left, char **ptr)
     296             : {
     297          60 :     *written += lastwrite;
     298             : 
     299          60 :     if (*written >= length) {
     300             :         *left = 0;
     301             :         *ptr = NULL;
     302             :     } else {
     303          60 :         *left -= lastwrite;
     304          44 :         *ptr = &(*ptr)[lastwrite];
     305             :     }
     306             : }
     307             : 
     308             : /* always returns number of bytes written or that would have been
     309             :  * written if the buffer was large enough
     310             :  * return values < 0 indicate some error occurred,
     311             :  * and return values > buflen indicate buffer was not large enough
     312             :  */
     313             : static int
     314          16 : _render_stanza_recursive(xmpp_stanza_t *stanza, char *buf, size_t buflen)
     315             : {
     316          16 :     char *ptr = buf;
     317          16 :     size_t left = buflen;
     318          16 :     int ret, written;
     319          16 :     xmpp_stanza_t *child;
     320          16 :     hash_iterator_t *iter;
     321          16 :     const char *key;
     322          16 :     char *tmp;
     323             : 
     324          16 :     written = 0;
     325             : 
     326          16 :     if (stanza->type == XMPP_STANZA_UNKNOWN)
     327          16 :         return XMPP_EINVOP;
     328             : 
     329          16 :     if (stanza->type == XMPP_STANZA_TEXT) {
     330           2 :         if (!stanza->data)
     331             :             return XMPP_EINVOP;
     332             : 
     333           2 :         tmp = _escape_xml(stanza->ctx, stanza->data);
     334           2 :         if (tmp == NULL)
     335             :             return XMPP_EMEM;
     336           2 :         ret = strophe_snprintf(ptr, left, "%s", tmp);
     337           2 :         strophe_free(stanza->ctx, tmp);
     338           2 :         if (ret < 0)
     339             :             return XMPP_EMEM;
     340           2 :         _render_update(&written, buflen, ret, &left, &ptr);
     341             :     } else { /* stanza->type == XMPP_STANZA_TAG */
     342          14 :         if (!stanza->data)
     343             :             return XMPP_EINVOP;
     344             : 
     345             :         /* write beginning of tag and attributes */
     346          14 :         ret = strophe_snprintf(ptr, left, "<%s", stanza->data);
     347          14 :         if (ret < 0)
     348             :             return XMPP_EMEM;
     349          14 :         _render_update(&written, buflen, ret, &left, &ptr);
     350             : 
     351          14 :         if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) {
     352          14 :             iter = hash_iter_new(stanza->attributes);
     353          32 :             while ((key = hash_iter_next(iter))) {
     354          18 :                 if (!strcmp(key, "xmlns")) {
     355             :                     /* don't output namespace if parent stanza is the same */
     356          24 :                     if (stanza->parent && stanza->parent->attributes &&
     357          11 :                         hash_get(stanza->parent->attributes, key) &&
     358          10 :                         !strcmp(
     359          10 :                             (char *)hash_get(stanza->attributes, key),
     360          10 :                             (char *)hash_get(stanza->parent->attributes, key)))
     361           8 :                         continue;
     362             :                     /* or if this is the stream namespace */
     363           5 :                     if (!stanza->parent &&
     364           2 :                         !strcmp((char *)hash_get(stanza->attributes, key),
     365             :                                 XMPP_NS_CLIENT))
     366           0 :                         continue;
     367             :                 }
     368          10 :                 tmp = _escape_xml(stanza->ctx,
     369          10 :                                   (char *)hash_get(stanza->attributes, key));
     370          10 :                 if (tmp == NULL) {
     371           0 :                     hash_iter_release(iter);
     372           0 :                     return XMPP_EMEM;
     373             :                 }
     374          10 :                 ret = strophe_snprintf(ptr, left, " %s=\"%s\"", key, tmp);
     375          10 :                 strophe_free(stanza->ctx, tmp);
     376          10 :                 if (ret < 0) {
     377           0 :                     hash_iter_release(iter);
     378           0 :                     return XMPP_EMEM;
     379             :                 }
     380          42 :                 _render_update(&written, buflen, ret, &left, &ptr);
     381             :             }
     382          14 :             hash_iter_release(iter);
     383             :         }
     384             : 
     385          14 :         if (!stanza->children) {
     386             :             /* write end if singleton tag */
     387           7 :             ret = strophe_snprintf(ptr, left, "/>");
     388           7 :             if (ret < 0)
     389             :                 return XMPP_EMEM;
     390           7 :             _render_update(&written, buflen, ret, &left, &ptr);
     391             :         } else {
     392             :             /* this stanza has child stanzas */
     393             : 
     394             :             /* write end of start tag */
     395           7 :             ret = strophe_snprintf(ptr, left, ">");
     396           7 :             if (ret < 0)
     397             :                 return XMPP_EMEM;
     398           7 :             _render_update(&written, buflen, ret, &left, &ptr);
     399             : 
     400             :             /* iterate and recurse over child stanzas */
     401           7 :             child = stanza->children;
     402          20 :             while (child) {
     403          13 :                 ret = _render_stanza_recursive(child, ptr, left);
     404          13 :                 if (ret < 0)
     405           0 :                     return ret;
     406             : 
     407          13 :                 _render_update(&written, buflen, ret, &left, &ptr);
     408             : 
     409          13 :                 child = child->next;
     410             :             }
     411             : 
     412             :             /* write end tag */
     413           7 :             ret = strophe_snprintf(ptr, left, "</%s>", stanza->data);
     414           7 :             if (ret < 0)
     415             :                 return XMPP_EMEM;
     416             : 
     417           7 :             _render_update(&written, buflen, ret, &left, &ptr);
     418             :         }
     419             :     }
     420             : 
     421             :     return written;
     422             : }
     423             : 
     424             : /** Render a stanza object to text.
     425             :  *  This function renders a given stanza object, along with its
     426             :  *  children, to text.  The text is returned in an allocated,
     427             :  *  null-terminated buffer.  It starts by allocating a 1024 byte buffer
     428             :  *  and reallocates more memory if that is not large enough.
     429             :  *
     430             :  *  @param stanza a Strophe stanza object
     431             :  *  @param buf a reference to a string pointer
     432             :  *  @param buflen a reference to a size_t
     433             :  *
     434             :  *  @return 0 on success (XMPP_EOK), and a number less than 0 on failure
     435             :  *      (XMPP_EMEM, XMPP_EINVOP)
     436             :  *
     437             :  *  @ingroup Stanza
     438             :  */
     439           3 : int xmpp_stanza_to_text(xmpp_stanza_t *stanza, char **buf, size_t *buflen)
     440             : {
     441           3 :     char *buffer, *tmp;
     442           3 :     size_t length;
     443           3 :     int ret;
     444             : 
     445             :     /* allocate a default sized buffer and attempt to render */
     446           3 :     length = 1024;
     447           3 :     buffer = strophe_alloc(stanza->ctx, length);
     448           3 :     if (!buffer) {
     449           0 :         *buf = NULL;
     450           0 :         *buflen = 0;
     451           0 :         return XMPP_EMEM;
     452             :     }
     453             : 
     454           3 :     ret = _render_stanza_recursive(stanza, buffer, length);
     455           3 :     if (ret < 0) {
     456           0 :         strophe_free(stanza->ctx, buffer);
     457           0 :         *buf = NULL;
     458           0 :         *buflen = 0;
     459           0 :         return ret;
     460             :     }
     461             : 
     462           3 :     if ((size_t)ret > length - 1) {
     463           0 :         tmp = strophe_realloc(stanza->ctx, buffer, ret + 1);
     464           0 :         if (!tmp) {
     465           0 :             strophe_free(stanza->ctx, buffer);
     466           0 :             *buf = NULL;
     467           0 :             *buflen = 0;
     468           0 :             return XMPP_EMEM;
     469             :         }
     470           0 :         length = ret + 1;
     471           0 :         buffer = tmp;
     472             : 
     473           0 :         ret = _render_stanza_recursive(stanza, buffer, length);
     474           0 :         if ((size_t)ret > length - 1) {
     475           0 :             strophe_free(stanza->ctx, buffer);
     476           0 :             *buf = NULL;
     477           0 :             *buflen = 0;
     478           0 :             return XMPP_EMEM;
     479             :         }
     480             :     }
     481             : 
     482           3 :     buffer[length - 1] = 0;
     483             : 
     484           3 :     *buf = buffer;
     485           3 :     *buflen = ret;
     486             : 
     487           3 :     return XMPP_EOK;
     488             : }
     489             : 
     490             : /** Set the name of a stanza.
     491             :  *
     492             :  *  @param stanza a Strophe stanza object
     493             :  *  @param name a string with the name of the stanza
     494             :  *
     495             :  *  @return XMPP_EOK on success, a number less than 0 on failure (XMPP_EMEM,
     496             :  *      XMPP_EINVOP)
     497             :  *
     498             :  *  @ingroup Stanza
     499             :  */
     500          31 : int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *name)
     501             : {
     502          31 :     if (stanza->type == XMPP_STANZA_TEXT)
     503             :         return XMPP_EINVOP;
     504             : 
     505          31 :     if (stanza->data)
     506           0 :         strophe_free(stanza->ctx, stanza->data);
     507             : 
     508          31 :     stanza->type = XMPP_STANZA_TAG;
     509          31 :     stanza->data = strophe_strdup(stanza->ctx, name);
     510             : 
     511          31 :     return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
     512             : }
     513             : 
     514             : /** Get the stanza name.
     515             :  *  This function returns a pointer to the stanza name.  If the caller needs
     516             :  *  to store this data, it must make a copy.
     517             :  *
     518             :  *  @param stanza a Strophe stanza object
     519             :  *
     520             :  *  @return a string with the stanza name
     521             :  *
     522             :  *  @ingroup Stanza
     523             :  */
     524          19 : const char *xmpp_stanza_get_name(xmpp_stanza_t *stanza)
     525             : {
     526          19 :     if (stanza->type == XMPP_STANZA_TEXT)
     527             :         return NULL;
     528          19 :     return stanza->data;
     529             : }
     530             : 
     531             : /** Count the attributes in a stanza object.
     532             :  *
     533             :  *  @param stanza a Strophe stanza object
     534             :  *
     535             :  *  @return the number of attributes for the stanza object
     536             :  *
     537             :  *  @ingroup Stanza
     538             :  */
     539           0 : int xmpp_stanza_get_attribute_count(xmpp_stanza_t *stanza)
     540             : {
     541           0 :     if (stanza->attributes == NULL) {
     542             :         return 0;
     543             :     }
     544             : 
     545           0 :     return hash_num_keys(stanza->attributes);
     546             : }
     547             : 
     548             : /** Get all attributes for a stanza object.
     549             :  *  This function populates the array with attributes from the stanza.  The
     550             :  *  attr array will be in the format:  attr[i] = attribute name,
     551             :  *  attr[i+1] = attribute value.
     552             :  *
     553             :  *  @param stanza a Strophe stanza object
     554             :  *  @param attr the string array to populate
     555             :  *  @param attrlen the size of the array
     556             :  *
     557             :  *  @return the number of slots used in the array, which will be 2 times the
     558             :  *      number of attributes in the stanza
     559             :  *
     560             :  *  @ingroup Stanza
     561             :  */
     562           1 : int xmpp_stanza_get_attributes(xmpp_stanza_t *stanza,
     563             :                                const char **attr,
     564             :                                int attrlen)
     565             : {
     566           1 :     hash_iterator_t *iter;
     567           1 :     const char *key;
     568           1 :     int num = 0;
     569             : 
     570           1 :     if (stanza->attributes == NULL) {
     571             :         return 0;
     572             :     }
     573             : 
     574           1 :     iter = hash_iter_new(stanza->attributes);
     575           5 :     while ((key = hash_iter_next(iter)) != NULL && attrlen) {
     576           4 :         attr[num++] = key;
     577           4 :         attrlen--;
     578           4 :         if (attrlen == 0) {
     579           0 :             hash_iter_release(iter);
     580           0 :             return num;
     581             :         }
     582           4 :         attr[num++] = hash_get(stanza->attributes, key);
     583           4 :         attrlen--;
     584           4 :         if (attrlen == 0) {
     585           0 :             hash_iter_release(iter);
     586           0 :             return num;
     587             :         }
     588             :     }
     589             : 
     590           1 :     hash_iter_release(iter);
     591           1 :     return num;
     592             : }
     593             : 
     594             : /** Set an attribute for a stanza object.
     595             :  *
     596             :  *  @param stanza a Strophe stanza object
     597             :  *  @param key a string with the attribute name
     598             :  *  @param value a string with the attribute value
     599             :  *
     600             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
     601             :  *
     602             :  *  @ingroup Stanza
     603             :  */
     604          48 : int xmpp_stanza_set_attribute(xmpp_stanza_t *stanza,
     605             :                               const char *key,
     606             :                               const char *value)
     607             : {
     608          48 :     char *val;
     609          48 :     int rc;
     610             : 
     611          48 :     if (stanza->type != XMPP_STANZA_TAG)
     612             :         return XMPP_EINVOP;
     613             : 
     614          48 :     if (!stanza->attributes) {
     615          30 :         stanza->attributes = hash_new(stanza->ctx, 8, strophe_free);
     616          30 :         if (!stanza->attributes)
     617             :             return XMPP_EMEM;
     618             :     }
     619             : 
     620          48 :     val = strophe_strdup(stanza->ctx, value);
     621          48 :     if (!val) {
     622             :         return XMPP_EMEM;
     623             :     }
     624             : 
     625          48 :     rc = hash_add(stanza->attributes, key, val);
     626          48 :     if (rc < 0) {
     627           0 :         strophe_free(stanza->ctx, val);
     628           0 :         return XMPP_EMEM;
     629             :     }
     630             : 
     631             :     return XMPP_EOK;
     632             : }
     633             : 
     634             : /** Set the stanza namespace.
     635             :  *  This is a convenience function equivalent to calling:
     636             :  *  xmpp_stanza_set_attribute(stanza, "xmlns", ns);
     637             :  *
     638             :  *  @param stanza a Strophe stanza object
     639             :  *  @param ns a string with the namespace
     640             :  *
     641             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
     642             :  *
     643             :  *  @ingroup Stanza
     644             :  */
     645          26 : int xmpp_stanza_set_ns(xmpp_stanza_t *stanza, const char *ns)
     646             : {
     647          26 :     return xmpp_stanza_set_attribute(stanza, "xmlns", ns);
     648             : }
     649             : 
     650             : /** Add a child stanza to a stanza object.
     651             :  *  If do_clone is TRUE, user keeps reference to the child stanza and must call
     652             :  *  xmpp_stanza_release() to release the reference. If do_clone is FALSE, user
     653             :  *  transfers ownership and must not neither call xmpp_stanza_release() for
     654             :  *  the child stanza nor use it.
     655             :  *
     656             :  *  @param stanza a Strophe stanza object
     657             :  *  @param child the child stanza object
     658             :  *  @param do_clone TRUE to increase ref count of child (default for
     659             :  *                  xmpp_stanza_add_child())
     660             :  *
     661             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
     662             :  *
     663             :  *  @ingroup Stanza
     664             :  */
     665          31 : int xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza,
     666             :                              xmpp_stanza_t *child,
     667             :                              int do_clone)
     668             : {
     669          31 :     xmpp_stanza_t *s;
     670             : 
     671          31 :     if (do_clone) {
     672             :         /* get a reference to the child */
     673           3 :         xmpp_stanza_clone(child);
     674             :     }
     675             : 
     676          31 :     child->parent = stanza;
     677             : 
     678          31 :     if (!stanza->children)
     679          21 :         stanza->children = child;
     680             :     else {
     681             :         s = stanza->children;
     682          19 :         while (s->next)
     683             :             s = s->next;
     684          10 :         s->next = child;
     685          10 :         child->prev = s;
     686             :     }
     687             : 
     688          31 :     return XMPP_EOK;
     689             : }
     690             : 
     691             : /** Add a child stanza to a stanza object.
     692             :  *  This function clones the child and appends it to the stanza object's
     693             :  *  children.
     694             :  *
     695             :  *  @param stanza a Strophe stanza object
     696             :  *  @param child the child stanza object
     697             :  *
     698             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
     699             :  *
     700             :  *  @ingroup Stanza
     701             :  */
     702           3 : int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
     703             : {
     704           3 :     return xmpp_stanza_add_child_ex(stanza, child, 1);
     705             : }
     706             : 
     707             : /** Set the text data for a text stanza.
     708             :  *  This function copies the text given and sets the stanza object's text to
     709             :  *  it.  Attempting to use this function on a stanza that has a name will
     710             :  *  fail with XMPP_EINVOP.  This function takes the text as a null-terminated
     711             :  *  string.
     712             :  *
     713             :  *  @param stanza a Strophe stanza object
     714             :  *  @param text a string with the text
     715             :  *
     716             :  *  @return XMPP_EOK (0) on success or a number less than zero on failure
     717             :  *
     718             :  *  @ingroup Stanza
     719             :  */
     720           4 : int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *text)
     721             : {
     722           4 :     if (stanza->type == XMPP_STANZA_TAG)
     723             :         return XMPP_EINVOP;
     724             : 
     725           4 :     stanza->type = XMPP_STANZA_TEXT;
     726             : 
     727           4 :     if (stanza->data)
     728           0 :         strophe_free(stanza->ctx, stanza->data);
     729           4 :     stanza->data = strophe_strdup(stanza->ctx, text);
     730             : 
     731           4 :     return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
     732             : }
     733             : 
     734             : /** Set the text data for a text stanza.
     735             :  *  This function copies the text given and sets the stanza object's text to
     736             :  *  it.  Attempting to use this function on a stanza that has a name will
     737             :  *  fail with XMPP_EINVOP.  This function takes the text as buffer and a length
     738             :  *  as opposed to a null-terminated string.
     739             :  *
     740             :  *  @param stanza a Strophe stanza object
     741             :  *  @param text a buffer with the text
     742             :  *  @param size the length of the text
     743             :  *
     744             :  *  @return XMPP_EOK (0) on success and a number less than 0 on failure
     745             :  *
     746             :  *  @ingroup Stanza
     747             :  */
     748           0 : int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
     749             :                                    const char *text,
     750             :                                    size_t size)
     751             : {
     752           0 :     if (stanza->type == XMPP_STANZA_TAG)
     753             :         return XMPP_EINVOP;
     754             : 
     755           0 :     stanza->type = XMPP_STANZA_TEXT;
     756             : 
     757           0 :     if (stanza->data)
     758           0 :         strophe_free(stanza->ctx, stanza->data);
     759           0 :     stanza->data = strophe_alloc(stanza->ctx, size + 1);
     760           0 :     if (!stanza->data)
     761             :         return XMPP_EMEM;
     762             : 
     763           0 :     memcpy(stanza->data, text, size);
     764           0 :     stanza->data[size] = 0;
     765             : 
     766           0 :     return XMPP_EOK;
     767             : }
     768             : 
     769             : /** Get the 'id' attribute of the stanza object.
     770             :  *  This is a convenience function equivalent to:
     771             :  *  xmpp_stanza_get_attribute(stanza, "id");
     772             :  *
     773             :  *  @param stanza a Strophe stanza object
     774             :  *
     775             :  *  @return a string with the 'id' attribute value
     776             :  *
     777             :  *  @ingroup Stanza
     778             :  */
     779           2 : const char *xmpp_stanza_get_id(xmpp_stanza_t *stanza)
     780             : {
     781           2 :     return xmpp_stanza_get_attribute(stanza, "id");
     782             : }
     783             : 
     784             : /** Get the namespace attribute of the stanza object.
     785             :  *  This is a convenience function equivalent to:
     786             :  *  xmpp_stanza_get_attribute(stanza, "xmlns");
     787             :  *
     788             :  *  @param stanza a Strophe stanza object
     789             :  *
     790             :  *  @return a string with the 'xmlns' attribute value
     791             :  *
     792             :  *  @ingroup Stanza
     793             :  */
     794           4 : const char *xmpp_stanza_get_ns(xmpp_stanza_t *stanza)
     795             : {
     796           4 :     return xmpp_stanza_get_attribute(stanza, "xmlns");
     797             : }
     798             : 
     799             : /** Get the 'type' attribute of the stanza object.
     800             :  *  This is a convenience function equivalent to:
     801             :  *  xmpp_stanza_get_attribute(stanza, "type");
     802             :  *
     803             :  *  @param stanza a Strophe stanza object
     804             :  *
     805             :  *  @return a string with the 'type' attribute value
     806             :  *
     807             :  *  @ingroup Stanza
     808             :  */
     809           2 : const char *xmpp_stanza_get_type(xmpp_stanza_t *stanza)
     810             : {
     811           2 :     return xmpp_stanza_get_attribute(stanza, "type");
     812             : }
     813             : 
     814             : /** Get the 'to' attribute of the stanza object.
     815             :  *  This is a convenience function equivalent to:
     816             :  *  xmpp_stanza_get_attribute(stanza, "to");
     817             :  *
     818             :  *  @param stanza a Strophe stanza object
     819             :  *
     820             :  *  @return a string with the 'to' attribute value
     821             :  *
     822             :  *  @ingroup Stanza
     823             :  */
     824           3 : const char *xmpp_stanza_get_to(xmpp_stanza_t *stanza)
     825             : {
     826           3 :     return xmpp_stanza_get_attribute(stanza, "to");
     827             : }
     828             : 
     829             : /** Get the 'from' attribute of the stanza object.
     830             :  *  This is a convenience function equivalent to:
     831             :  *  xmpp_stanza_get_attribute(stanza, "from");
     832             :  *
     833             :  *  @param stanza a Strophe stanza object
     834             :  *
     835             :  *  @return a string with the 'from' attribute value
     836             :  *
     837             :  *  @ingroup Stanza
     838             :  */
     839           3 : const char *xmpp_stanza_get_from(xmpp_stanza_t *stanza)
     840             : {
     841           3 :     return xmpp_stanza_get_attribute(stanza, "from");
     842             : }
     843             : 
     844             : /** Get the first child of stanza following a path-like list of names.
     845             :  *  This function searches the children and their children that match
     846             :  *  the given path.
     847             :  *
     848             :  *  * "name" - Search 'name'
     849             :  *
     850             :  *  * "name[@ns='foo']" - Search 'name' which is in the namespace 'foo'
     851             :  *
     852             :  *  The Syntax to pass namespaces is inspired by the XPATH way of passing
     853             :  *  attributes.
     854             :  *
     855             :  *  The namespace syntax only supports single quotes `'`.
     856             :  *
     857             :  *  The \ref XMPP_STANZA_NAME_IN_NS macro is provided as a helper for names
     858             :  *  in namespaces.
     859             :  *
     860             :  *  @param stanza a Strophe stanza object
     861             :  *  @param ... a var-args list that must be terminated by a NULL entry
     862             :  *
     863             :  *  @return the matching child stanza object or NULL if no match was found
     864             :  *
     865             :  *  @ingroup Stanza
     866             :  */
     867           6 : xmpp_stanza_t *xmpp_stanza_get_child_by_path(xmpp_stanza_t *stanza, ...)
     868             : {
     869           6 :     xmpp_stanza_t *child = NULL;
     870           6 :     char *p, *tok, *attr, *saveattr, *ns = NULL;
     871           6 :     const char *xmlns;
     872           6 :     va_list ap;
     873             : 
     874           6 :     va_start(ap, stanza);
     875             : 
     876          17 :     while ((p = va_arg(ap, char *)) != NULL) {
     877          14 :         tok = strophe_strdup(stanza->ctx, p);
     878          14 :         if (!tok) {
     879             :             child = NULL;
     880             :             break;
     881             :         }
     882          14 :         saveattr = ns = NULL;
     883          14 :         attr = strophe_strtok_r(tok, "[", &saveattr);
     884          14 :         if (attr) {
     885          14 :             attr = strophe_strtok_r(NULL, "]", &saveattr);
     886          14 :             if (attr) {
     887           4 :                 if (!strncmp(attr, "@ns='", 5)) {
     888           4 :                     ns = attr + 5;
     889           4 :                     strophe_strtok_r(ns, "'", &saveattr);
     890             :                 }
     891             :             }
     892             :         }
     893          14 :         if (!child) {
     894           5 :             if (strcmp(xmpp_stanza_get_name(stanza), tok))
     895           1 :                 goto error_out;
     896             : 
     897           4 :             if (ns) {
     898           1 :                 xmlns = xmpp_stanza_get_ns(stanza);
     899           1 :                 if (!xmlns || strcmp(xmlns, ns))
     900           1 :                     goto error_out;
     901             :             }
     902             :             child = stanza;
     903             :         } else {
     904           9 :             if (!ns)
     905           6 :                 child = xmpp_stanza_get_child_by_name(child, tok);
     906             :             else
     907           3 :                 child = xmpp_stanza_get_child_by_name_and_ns(child, tok, ns);
     908             :         }
     909          14 : error_out:
     910          14 :         strophe_free(stanza->ctx, tok);
     911          14 :         if (!child)
     912             :             break;
     913             :     }
     914             : 
     915           6 :     va_end(ap);
     916             : 
     917           6 :     return p == NULL ? child : NULL;
     918             : }
     919             : 
     920             : /** Get the first child of stanza with name.
     921             :  *  This function searches all the immediate children of stanza for a child
     922             :  *  stanza that matches the name.  The first matching child is returned.
     923             :  *
     924             :  *  @param stanza a Strophe stanza object
     925             :  *  @param name a string with the name to match
     926             :  *
     927             :  *  @return the matching child stanza object or NULL if no match was found
     928             :  *
     929             :  *  @ingroup Stanza
     930             :  */
     931           7 : xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *stanza,
     932             :                                              const char *name)
     933             : {
     934           7 :     xmpp_stanza_t *child;
     935             : 
     936           7 :     for (child = stanza->children; child; child = child->next) {
     937           7 :         if (child->type == XMPP_STANZA_TAG &&
     938           7 :             (strcmp(name, xmpp_stanza_get_name(child)) == 0))
     939             :             break;
     940             :     }
     941             : 
     942           7 :     return child;
     943             : }
     944             : 
     945             : /** Get the first child of a stanza with a given namespace.
     946             :  *  This function searches all the immediate children of a stanza for a child
     947             :  *  stanza that matches the namespace provided.  The first matching child
     948             :  *  is returned.
     949             :  *
     950             :  *  @param stanza a Strophe stanza object
     951             :  *  @param ns a string with the namespace to match
     952             :  *
     953             :  *  @return the matching child stanza object or NULL if no match was found
     954             :  *
     955             :  *  @ingroup Stanza
     956             :  */
     957           0 : xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *stanza,
     958             :                                            const char *ns)
     959             : {
     960           0 :     xmpp_stanza_t *child;
     961           0 :     const char *child_ns;
     962             : 
     963           0 :     for (child = stanza->children; child; child = child->next) {
     964           0 :         child_ns = xmpp_stanza_get_ns(child);
     965           0 :         if (child_ns && strcmp(ns, child_ns) == 0)
     966             :             break;
     967             :     }
     968             : 
     969           0 :     return child;
     970             : }
     971             : 
     972             : /** Get the first child of stanza with name and a given namespace.
     973             :  *  This function searches all the immediate children of stanza for a child
     974             :  *  stanza that matches the name and namespace provided.
     975             :  *  The first matching child is returned.
     976             :  *
     977             :  *  @param stanza a Strophe stanza object
     978             :  *  @param name a string with the name to match
     979             :  *  @param ns a string with the namespace to match
     980             :  *
     981             :  *  @return the matching child stanza object or NULL if no match was found
     982             :  *
     983             :  *  @ingroup Stanza
     984             :  */
     985           3 : xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *stanza,
     986             :                                                     const char *name,
     987             :                                                     const char *ns)
     988             : {
     989           3 :     xmpp_stanza_t *child;
     990           3 :     const char *child_ns;
     991             : 
     992           4 :     for (child = stanza->children; child; child = child->next) {
     993           3 :         if (child->type == XMPP_STANZA_TAG &&
     994           3 :             (strcmp(name, xmpp_stanza_get_name(child)) == 0)) {
     995           3 :             child_ns = xmpp_stanza_get_ns(child);
     996           3 :             if (child_ns && strcmp(ns, child_ns) == 0) {
     997             :                 break;
     998             :             }
     999             :         }
    1000             :     }
    1001             : 
    1002           3 :     return child;
    1003             : }
    1004             : 
    1005             : /** Get the list of children.
    1006             :  *  This function returns the first child of the stanza object.  The rest
    1007             :  *  of the children can be obtained by calling xmpp_stanza_get_next() to
    1008             :  *  iterate over the siblings.
    1009             :  *
    1010             :  *  @param stanza a Strophe stanza object
    1011             :  *
    1012             :  *  @return the first child stanza or NULL if there are no children
    1013             :  *
    1014             :  *  @ingroup Stanza
    1015             :  */
    1016           6 : xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *stanza)
    1017             : {
    1018           6 :     return stanza->children;
    1019             : }
    1020             : 
    1021             : /** Get the next sibling of a stanza.
    1022             :  *
    1023             :  *  @param stanza a Strophe stanza object
    1024             :  *
    1025             :  *  @return the next sibling stanza or NULL if there are no more siblings
    1026             :  *
    1027             :  *  @ingroup Stanza
    1028             :  */
    1029           0 : xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *stanza)
    1030             : {
    1031           0 :     return stanza->next;
    1032             : }
    1033             : 
    1034             : /** Get the text data for a text stanza.
    1035             :  *  This function copies the text data from a stanza and returns the new
    1036             :  *  allocated string.  The caller is responsible for freeing this string
    1037             :  *  with xmpp_free().
    1038             :  *
    1039             :  *  @param stanza a Strophe stanza object
    1040             :  *
    1041             :  *  @return an allocated string with the text data
    1042             :  *
    1043             :  *  @ingroup Stanza
    1044             :  */
    1045           0 : char *xmpp_stanza_get_text(xmpp_stanza_t *stanza)
    1046             : {
    1047           0 :     size_t len, clen;
    1048           0 :     xmpp_stanza_t *child;
    1049           0 :     char *text;
    1050             : 
    1051           0 :     if (stanza->type == XMPP_STANZA_TEXT) {
    1052           0 :         if (stanza->data)
    1053           0 :             return strophe_strdup(stanza->ctx, stanza->data);
    1054             :         else
    1055             :             return NULL;
    1056             :     }
    1057             : 
    1058           0 :     len = 0;
    1059           0 :     for (child = stanza->children; child; child = child->next)
    1060           0 :         if (child->type == XMPP_STANZA_TEXT)
    1061           0 :             len += strlen(child->data);
    1062             : 
    1063           0 :     if (len == 0)
    1064             :         return NULL;
    1065             : 
    1066           0 :     text = (char *)strophe_alloc(stanza->ctx, len + 1);
    1067           0 :     if (!text)
    1068             :         return NULL;
    1069             : 
    1070           0 :     len = 0;
    1071           0 :     for (child = stanza->children; child; child = child->next)
    1072           0 :         if (child->type == XMPP_STANZA_TEXT) {
    1073           0 :             clen = strlen(child->data);
    1074           0 :             memcpy(&text[len], child->data, clen);
    1075           0 :             len += clen;
    1076             :         }
    1077             : 
    1078           0 :     text[len] = 0;
    1079             : 
    1080           0 :     return text;
    1081             : }
    1082             : 
    1083             : /** Get the text data pointer for a text stanza.
    1084             :  *  This function copies returns the raw pointer to the text data in the
    1085             :  *  stanza.  This should only be used in very special cases where the
    1086             :  *  caller needs to translate the datatype as this will save a double
    1087             :  *  allocation.  The caller should not hold onto this pointer, and is
    1088             :  *  responsible for allocating a copy if it needs one.
    1089             :  *
    1090             :  *  @param stanza a Strophe stanza object
    1091             :  *
    1092             :  *  @return an string pointer to the data or NULL
    1093             :  *
    1094             :  *  @ingroup Stanza
    1095             :  */
    1096           0 : const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *stanza)
    1097             : {
    1098           0 :     if (stanza->type == XMPP_STANZA_TEXT)
    1099           0 :         return stanza->data;
    1100             :     return NULL;
    1101             : }
    1102             : 
    1103             : /** Set the 'id' attribute of a stanza.
    1104             :  *
    1105             :  *  This is a convenience function for:
    1106             :  *  xmpp_stanza_set_attribute(stanza, 'id', id);
    1107             :  *
    1108             :  *  @param stanza a Strophe stanza object
    1109             :  *  @param id a string containing the 'id' value
    1110             :  *
    1111             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
    1112             :  *
    1113             :  *  @ingroup Stanza
    1114             :  */
    1115           0 : int xmpp_stanza_set_id(xmpp_stanza_t *stanza, const char *id)
    1116             : {
    1117           0 :     return xmpp_stanza_set_attribute(stanza, "id", id);
    1118             : }
    1119             : 
    1120             : /** Set the 'type' attribute of a stanza.
    1121             :  *  This is a convenience function for:
    1122             :  *  xmpp_stanza_set_attribute(stanza, 'type', type);
    1123             :  *
    1124             :  *  @param stanza a Strophe stanza object
    1125             :  *  @param type a string containing the 'type' value
    1126             :  *
    1127             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
    1128             :  *
    1129             :  *  @ingroup Stanza
    1130             :  */
    1131           2 : int xmpp_stanza_set_type(xmpp_stanza_t *stanza, const char *type)
    1132             : {
    1133           2 :     return xmpp_stanza_set_attribute(stanza, "type", type);
    1134             : }
    1135             : 
    1136             : /** Set the 'to' attribute of a stanza.
    1137             :  *
    1138             :  *  This is a convenience function for:
    1139             :  *  xmpp_stanza_set_attribute(stanza, 'to', to);
    1140             :  *
    1141             :  *  @param stanza a Strophe stanza object
    1142             :  *  @param to a string containing the 'to' value
    1143             :  *
    1144             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
    1145             :  *
    1146             :  *  @ingroup Stanza
    1147             :  */
    1148           1 : int xmpp_stanza_set_to(xmpp_stanza_t *stanza, const char *to)
    1149             : {
    1150           1 :     return xmpp_stanza_set_attribute(stanza, "to", to);
    1151             : }
    1152             : 
    1153             : /** Set the 'from' attribute of a stanza.
    1154             :  *
    1155             :  *  This is a convenience function for:
    1156             :  *  xmpp_stanza_set_attribute(stanza, 'from', from);
    1157             :  *
    1158             :  *  @param stanza a Strophe stanza object
    1159             :  *  @param from a string containing the 'from' value
    1160             :  *
    1161             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
    1162             :  *
    1163             :  *  @ingroup Stanza
    1164             :  */
    1165           1 : int xmpp_stanza_set_from(xmpp_stanza_t *stanza, const char *from)
    1166             : {
    1167           1 :     return xmpp_stanza_set_attribute(stanza, "from", from);
    1168             : }
    1169             : 
    1170             : /** Get an attribute from a stanza.
    1171             :  *  This function returns a pointer to the attribute value.  If the caller
    1172             :  *  wishes to save this value it must make its own copy.
    1173             :  *
    1174             :  *  @param stanza a Strophe stanza object
    1175             :  *  @param name a string containing attribute name
    1176             :  *
    1177             :  *  @return a string with the attribute value or NULL on an error
    1178             :  *
    1179             :  *  @ingroup Stanza
    1180             :  */
    1181          14 : const char *xmpp_stanza_get_attribute(xmpp_stanza_t *stanza, const char *name)
    1182             : {
    1183          14 :     if (stanza->type != XMPP_STANZA_TAG)
    1184             :         return NULL;
    1185             : 
    1186          14 :     if (!stanza->attributes)
    1187             :         return NULL;
    1188             : 
    1189          14 :     return hash_get(stanza->attributes, name);
    1190             : }
    1191             : 
    1192             : /** Delete an attribute from a stanza.
    1193             :  *
    1194             :  *  @param stanza a Strophe stanza object
    1195             :  *  @param name a string containing attribute name
    1196             :  *
    1197             :  *  @return XMPP_EOK (0) on success or a number less than 0 on failure
    1198             :  *
    1199             :  *  @ingroup Stanza
    1200             :  */
    1201           3 : int xmpp_stanza_del_attribute(xmpp_stanza_t *stanza, const char *name)
    1202             : {
    1203           3 :     if (stanza->type != XMPP_STANZA_TAG)
    1204             :         return -1;
    1205             : 
    1206           3 :     if (!stanza->attributes)
    1207             :         return -1;
    1208             : 
    1209           3 :     return hash_drop(stanza->attributes, name);
    1210             : }
    1211             : 
    1212             : /** Create a stanza object in reply to another.
    1213             :  *  This function makes a copy of a stanza object with the attribute "to" set
    1214             :  *  its original "from".
    1215             :  *  The stanza will have a reference count of one, so the caller does not
    1216             :  *  need to clone it.
    1217             :  *
    1218             :  *  @param stanza a Strophe stanza object
    1219             :  *
    1220             :  *  @return a new Strophe stanza object
    1221             :  *
    1222             :  *  @ingroup Stanza
    1223             :  */
    1224           1 : xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *stanza)
    1225             : {
    1226           1 :     xmpp_stanza_t *copy = NULL;
    1227           1 :     const char *from;
    1228           1 :     int rc;
    1229             : 
    1230           1 :     from = xmpp_stanza_get_from(stanza);
    1231           1 :     if (!from)
    1232           0 :         goto copy_error;
    1233             : 
    1234           1 :     copy = xmpp_stanza_new(stanza->ctx);
    1235           1 :     if (!copy)
    1236           0 :         goto copy_error;
    1237             : 
    1238           1 :     copy->type = stanza->type;
    1239             : 
    1240           1 :     if (stanza->data) {
    1241           1 :         copy->data = strophe_strdup(stanza->ctx, stanza->data);
    1242           1 :         if (!copy->data)
    1243           0 :             goto copy_error;
    1244             :     }
    1245             : 
    1246           1 :     if (stanza->attributes) {
    1247           1 :         if (_stanza_copy_attributes(copy, stanza) < 0)
    1248           0 :             goto copy_error;
    1249             :     }
    1250             : 
    1251           1 :     xmpp_stanza_del_attribute(copy, "to");
    1252           1 :     xmpp_stanza_del_attribute(copy, "from");
    1253           1 :     xmpp_stanza_del_attribute(copy, "xmlns");
    1254           1 :     rc = xmpp_stanza_set_to(copy, from);
    1255           1 :     if (rc != XMPP_EOK)
    1256           0 :         goto copy_error;
    1257             : 
    1258             :     return copy;
    1259             : 
    1260           0 : copy_error:
    1261           0 :     if (copy)
    1262           0 :         xmpp_stanza_release(copy);
    1263             :     return NULL;
    1264             : }
    1265             : 
    1266             : /** Create an error stanza in reply to the provided stanza.
    1267             :  *
    1268             :  *  Check https://tools.ietf.org/html/rfc6120#section-8.3 for details.
    1269             :  *
    1270             :  *  @param stanza a Strophe stanza object
    1271             :  *  @param error_type type attribute in the `<error/>` child element
    1272             :  *  @param condition the defined-condition (e.g. "item-not-found")
    1273             :  *  @param text optional description, may be NULL
    1274             :  *
    1275             :  *  @return a new Strophe stanza object
    1276             :  *
    1277             :  *  @ingroup Stanza
    1278             :  */
    1279           1 : xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *stanza,
    1280             :                                        const char *error_type,
    1281             :                                        const char *condition,
    1282             :                                        const char *text)
    1283             : {
    1284           1 :     xmpp_ctx_t *ctx = stanza->ctx;
    1285           1 :     xmpp_stanza_t *reply = NULL;
    1286           1 :     xmpp_stanza_t *error = NULL;
    1287           1 :     xmpp_stanza_t *item = NULL;
    1288           1 :     xmpp_stanza_t *text_stanza = NULL;
    1289           1 :     const char *to;
    1290             : 
    1291           1 :     if (!error_type || !condition)
    1292           0 :         goto quit_err;
    1293             : 
    1294           1 :     reply = xmpp_stanza_reply(stanza);
    1295           1 :     if (!reply)
    1296           0 :         goto quit_err;
    1297           1 :     if (xmpp_stanza_set_type(reply, "error") != XMPP_EOK)
    1298           0 :         goto quit_err;
    1299           1 :     to = xmpp_stanza_get_to(stanza);
    1300           1 :     if (to)
    1301           1 :         if (xmpp_stanza_set_from(reply, to) != XMPP_EOK)
    1302           0 :             goto quit_err;
    1303             : 
    1304           1 :     error = xmpp_stanza_new(ctx);
    1305           1 :     if (!error)
    1306           0 :         goto quit_err;
    1307           1 :     if (xmpp_stanza_set_name(error, "error") != XMPP_EOK)
    1308           0 :         goto quit_err;
    1309           1 :     if (xmpp_stanza_set_type(error, error_type) != XMPP_EOK)
    1310           0 :         goto quit_err;
    1311           1 :     if (xmpp_stanza_add_child(reply, error) != XMPP_EOK)
    1312           0 :         goto quit_err;
    1313           1 :     xmpp_stanza_release(error);
    1314             : 
    1315           1 :     item = xmpp_stanza_new(ctx);
    1316           1 :     if (!item)
    1317           0 :         goto quit_err;
    1318           1 :     if (xmpp_stanza_set_name(item, condition) != XMPP_EOK)
    1319           0 :         goto quit_err;
    1320           1 :     if (xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF) != XMPP_EOK)
    1321           0 :         goto quit_err;
    1322           1 :     if (xmpp_stanza_add_child(error, item) != XMPP_EOK)
    1323           0 :         goto quit_err;
    1324           1 :     xmpp_stanza_release(item);
    1325             : 
    1326           1 :     if (text) {
    1327           0 :         item = xmpp_stanza_new(ctx);
    1328           0 :         if (!item)
    1329           0 :             goto quit_err;
    1330           0 :         if (xmpp_stanza_set_name(item, "text") != XMPP_EOK)
    1331           0 :             goto quit_err;
    1332           0 :         if (xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF) != XMPP_EOK)
    1333           0 :             goto quit_err;
    1334           0 :         if (xmpp_stanza_add_child(error, item) != XMPP_EOK)
    1335           0 :             goto quit_err;
    1336           0 :         xmpp_stanza_release(item);
    1337           0 :         text_stanza = xmpp_stanza_new(ctx);
    1338           0 :         if (!text_stanza)
    1339           0 :             goto quit_err;
    1340           0 :         if (xmpp_stanza_set_text(text_stanza, text) != XMPP_EOK)
    1341           0 :             goto quit_err;
    1342           0 :         if (xmpp_stanza_add_child(item, text_stanza) != XMPP_EOK)
    1343           0 :             goto quit_err;
    1344           0 :         xmpp_stanza_release(text_stanza);
    1345             :     }
    1346             : 
    1347             :     return reply;
    1348             : 
    1349           0 : quit_err:
    1350           0 :     if (reply)
    1351           0 :         xmpp_stanza_release(reply);
    1352           0 :     if (error)
    1353           0 :         xmpp_stanza_release(error);
    1354           0 :     if (item)
    1355           0 :         xmpp_stanza_release(item);
    1356           0 :     if (text_stanza)
    1357           0 :         xmpp_stanza_release(text_stanza);
    1358             :     return NULL;
    1359             : }
    1360             : 
    1361           0 : static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
    1362             :                                              const char *name,
    1363             :                                              const char *type,
    1364             :                                              const char *id,
    1365             :                                              const char *to)
    1366             : {
    1367           0 :     xmpp_stanza_t *stanza = xmpp_stanza_new(ctx);
    1368           0 :     int ret;
    1369             : 
    1370           0 :     if (stanza) {
    1371           0 :         ret = xmpp_stanza_set_name(stanza, name);
    1372           0 :         if (ret == XMPP_EOK && type)
    1373           0 :             ret = xmpp_stanza_set_type(stanza, type);
    1374           0 :         if (ret == XMPP_EOK && id)
    1375           0 :             ret = xmpp_stanza_set_id(stanza, id);
    1376           0 :         if (ret == XMPP_EOK && to)
    1377           0 :             ret = xmpp_stanza_set_to(stanza, to);
    1378           0 :         if (ret != XMPP_EOK) {
    1379           0 :             xmpp_stanza_release(stanza);
    1380           0 :             stanza = NULL;
    1381             :         }
    1382             :     }
    1383           0 :     return stanza;
    1384             : }
    1385             : 
    1386             : /** Create a `<message/>` stanza object with given attributes.
    1387             :  *  Attributes are optional and may be NULL.
    1388             :  *
    1389             :  *  @param ctx a Strophe context object
    1390             :  *  @param type attribute 'type'
    1391             :  *  @param to attribute 'to'
    1392             :  *  @param id attribute 'id'
    1393             :  *
    1394             :  *  @return a new Strophe stanza object
    1395             :  *
    1396             :  *  @ingroup Stanza
    1397             :  */
    1398           0 : xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
    1399             :                                 const char *type,
    1400             :                                 const char *to,
    1401             :                                 const char *id)
    1402             : {
    1403           0 :     return _stanza_new_with_attrs(ctx, "message", type, id, to);
    1404             : }
    1405             : 
    1406             : /** Get text from `<body/>` child element.
    1407             :  *  This function returns new allocated string. The caller is responsible
    1408             :  *  for freeing this string with xmpp_free().
    1409             :  *
    1410             :  *  @param msg well formed `<message/>` stanza
    1411             :  *
    1412             :  *  @return allocated string or NULL on failure (no `<body/>` element or
    1413             :  *      memory allocation error)
    1414             :  *
    1415             :  *  @ingroup Stanza
    1416             :  */
    1417           0 : char *xmpp_message_get_body(xmpp_stanza_t *msg)
    1418             : {
    1419           0 :     xmpp_stanza_t *body;
    1420           0 :     const char *name;
    1421           0 :     char *text = NULL;
    1422             : 
    1423           0 :     name = xmpp_stanza_get_name(msg);
    1424           0 :     body = xmpp_stanza_get_child_by_name(msg, "body");
    1425           0 :     if (name && strcmp(name, "message") == 0 && body) {
    1426           0 :         text = xmpp_stanza_get_text(body);
    1427             :     }
    1428           0 :     return text;
    1429             : }
    1430             : 
    1431             : /** Add `<body/>` child element to a `<message/>` stanza with the given text.
    1432             :  *
    1433             :  *  @param msg a `<message>` stanza object without `<body/>` child element.
    1434             :  *  @param text The text that shall be placed in the body.
    1435             :  *
    1436             :  *  @return 0 on success (XMPP_EOK), and a number less than 0 on failure
    1437             :  *      (XMPP_EMEM, XMPP_EINVOP)
    1438             :  *
    1439             :  *  @ingroup Stanza
    1440             :  */
    1441           0 : int xmpp_message_set_body(xmpp_stanza_t *msg, const char *text)
    1442             : {
    1443           0 :     xmpp_ctx_t *ctx = msg->ctx;
    1444           0 :     xmpp_stanza_t *body;
    1445           0 :     xmpp_stanza_t *text_stanza;
    1446           0 :     const char *name;
    1447           0 :     int ret;
    1448             : 
    1449             :     /* check that msg is a `<message/>` stanza and doesn't contain `<body/>` */
    1450           0 :     name = xmpp_stanza_get_name(msg);
    1451           0 :     body = xmpp_stanza_get_child_by_name(msg, "body");
    1452           0 :     if (!name || strcmp(name, "message") != 0 || body)
    1453             :         return XMPP_EINVOP;
    1454             : 
    1455           0 :     body = xmpp_stanza_new(ctx);
    1456           0 :     text_stanza = xmpp_stanza_new(ctx);
    1457             : 
    1458           0 :     ret = body && text_stanza ? XMPP_EOK : XMPP_EMEM;
    1459           0 :     if (ret == XMPP_EOK)
    1460           0 :         ret = xmpp_stanza_set_name(body, "body");
    1461           0 :     if (ret == XMPP_EOK)
    1462           0 :         ret = xmpp_stanza_set_text(text_stanza, text);
    1463           0 :     if (ret == XMPP_EOK)
    1464           0 :         ret = xmpp_stanza_add_child(body, text_stanza);
    1465           0 :     if (ret == XMPP_EOK)
    1466           0 :         ret = xmpp_stanza_add_child(msg, body);
    1467             : 
    1468           0 :     if (text_stanza)
    1469           0 :         xmpp_stanza_release(text_stanza);
    1470           0 :     if (body)
    1471           0 :         xmpp_stanza_release(body);
    1472             : 
    1473             :     return ret;
    1474             : }
    1475             : 
    1476             : /** Create an `<iq/>` stanza object with given attributes.
    1477             :  *  Attributes are optional and may be NULL.
    1478             :  *
    1479             :  *  @param ctx a Strophe context object
    1480             :  *  @param type attribute 'type'
    1481             :  *  @param id attribute 'id'
    1482             :  *
    1483             :  *  @return a new Strophe stanza object
    1484             :  *
    1485             :  *  @ingroup Stanza
    1486             :  */
    1487           0 : xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char *type, const char *id)
    1488             : {
    1489           0 :     return _stanza_new_with_attrs(ctx, "iq", type, id, NULL);
    1490             : }
    1491             : 
    1492             : /** Create a `<presence/>` stanza object.
    1493             :  *
    1494             :  *  @param ctx a Strophe context object
    1495             :  *
    1496             :  *  @return a new Strophe stanza object
    1497             :  *
    1498             :  *  @ingroup Stanza
    1499             :  */
    1500           0 : xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx)
    1501             : {
    1502           0 :     return _stanza_new_with_attrs(ctx, "presence", NULL, NULL, NULL);
    1503             : }
    1504             : 
    1505             : /** Create an <stream:error/> stanza object with given type and error text.
    1506             :  *  The error text is optional and may be NULL.
    1507             :  *
    1508             :  *  @param ctx a Strophe context object
    1509             :  *  @param type enum of strophe_error_type_t
    1510             :  *  @param text content of a 'text'
    1511             :  *
    1512             :  *  @return a new Strophe stanza object
    1513             :  *
    1514             :  *  @todo Handle errors in this function
    1515             :  *
    1516             :  *  @ingroup Stanza
    1517             :  */
    1518             : xmpp_stanza_t *
    1519           0 : xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text)
    1520             : {
    1521           0 :     xmpp_stanza_t *error =
    1522           0 :         _stanza_new_with_attrs(ctx, "stream:error", NULL, NULL, NULL);
    1523           0 :     xmpp_stanza_t *error_type = xmpp_stanza_new(ctx);
    1524             : 
    1525           0 :     switch (type) {
    1526           0 :     case XMPP_SE_BAD_FORMAT:
    1527           0 :         xmpp_stanza_set_name(error_type, "bad-format");
    1528           0 :         break;
    1529           0 :     case XMPP_SE_BAD_NS_PREFIX:
    1530           0 :         xmpp_stanza_set_name(error_type, "bad-namespace-prefix");
    1531           0 :         break;
    1532           0 :     case XMPP_SE_CONFLICT:
    1533           0 :         xmpp_stanza_set_name(error_type, "conflict");
    1534           0 :         break;
    1535           0 :     case XMPP_SE_CONN_TIMEOUT:
    1536           0 :         xmpp_stanza_set_name(error_type, "connection-timeout");
    1537           0 :         break;
    1538           0 :     case XMPP_SE_HOST_GONE:
    1539           0 :         xmpp_stanza_set_name(error_type, "host-gone");
    1540           0 :         break;
    1541           0 :     case XMPP_SE_HOST_UNKNOWN:
    1542           0 :         xmpp_stanza_set_name(error_type, "host-unknown");
    1543           0 :         break;
    1544           0 :     case XMPP_SE_IMPROPER_ADDR:
    1545           0 :         xmpp_stanza_set_name(error_type, "improper-addressing");
    1546           0 :         break;
    1547           0 :     case XMPP_SE_INTERNAL_SERVER_ERROR:
    1548           0 :         xmpp_stanza_set_name(error_type, "internal-server-error");
    1549           0 :         break;
    1550           0 :     case XMPP_SE_INVALID_FROM:
    1551           0 :         xmpp_stanza_set_name(error_type, "invalid-from");
    1552           0 :         break;
    1553           0 :     case XMPP_SE_INVALID_ID:
    1554           0 :         xmpp_stanza_set_name(error_type, "invalid-id");
    1555           0 :         break;
    1556           0 :     case XMPP_SE_INVALID_NS:
    1557           0 :         xmpp_stanza_set_name(error_type, "invalid-namespace");
    1558           0 :         break;
    1559           0 :     case XMPP_SE_INVALID_XML:
    1560           0 :         xmpp_stanza_set_name(error_type, "invalid-xml");
    1561           0 :         break;
    1562           0 :     case XMPP_SE_NOT_AUTHORIZED:
    1563           0 :         xmpp_stanza_set_name(error_type, "not-authorized");
    1564           0 :         break;
    1565           0 :     case XMPP_SE_POLICY_VIOLATION:
    1566           0 :         xmpp_stanza_set_name(error_type, "policy-violation");
    1567           0 :         break;
    1568           0 :     case XMPP_SE_REMOTE_CONN_FAILED:
    1569           0 :         xmpp_stanza_set_name(error_type, "remote-connection-failed");
    1570           0 :         break;
    1571           0 :     case XMPP_SE_RESOURCE_CONSTRAINT:
    1572           0 :         xmpp_stanza_set_name(error_type, "resource-constraint");
    1573           0 :         break;
    1574           0 :     case XMPP_SE_RESTRICTED_XML:
    1575           0 :         xmpp_stanza_set_name(error_type, "restricted-xml");
    1576           0 :         break;
    1577           0 :     case XMPP_SE_SEE_OTHER_HOST:
    1578           0 :         xmpp_stanza_set_name(error_type, "see-other-host");
    1579           0 :         break;
    1580           0 :     case XMPP_SE_SYSTEM_SHUTDOWN:
    1581           0 :         xmpp_stanza_set_name(error_type, "system-shutdown");
    1582           0 :         break;
    1583           0 :     case XMPP_SE_UNDEFINED_CONDITION:
    1584           0 :         xmpp_stanza_set_name(error_type, "undefined-condition");
    1585           0 :         break;
    1586           0 :     case XMPP_SE_UNSUPPORTED_ENCODING:
    1587           0 :         xmpp_stanza_set_name(error_type, "unsupported-encoding");
    1588           0 :         break;
    1589           0 :     case XMPP_SE_UNSUPPORTED_STANZA_TYPE:
    1590           0 :         xmpp_stanza_set_name(error_type, "unsupported-stanza-type");
    1591           0 :         break;
    1592           0 :     case XMPP_SE_UNSUPPORTED_VERSION:
    1593           0 :         xmpp_stanza_set_name(error_type, "unsupported-version");
    1594           0 :         break;
    1595           0 :     case XMPP_SE_XML_NOT_WELL_FORMED:
    1596           0 :         xmpp_stanza_set_name(error_type, "xml-not-well-formed");
    1597           0 :         break;
    1598           0 :     default:
    1599           0 :         xmpp_stanza_set_name(error_type, "internal-server-error");
    1600           0 :         break;
    1601             :     }
    1602             : 
    1603           0 :     xmpp_stanza_set_ns(error_type, XMPP_NS_STREAMS_IETF);
    1604           0 :     xmpp_stanza_add_child_ex(error, error_type, 0);
    1605             : 
    1606           0 :     if (text) {
    1607           0 :         xmpp_stanza_t *error_text = xmpp_stanza_new(ctx);
    1608           0 :         xmpp_stanza_t *content = xmpp_stanza_new(ctx);
    1609             : 
    1610           0 :         xmpp_stanza_set_name(error_text, "text");
    1611           0 :         xmpp_stanza_set_ns(error_text, XMPP_NS_STREAMS_IETF);
    1612             : 
    1613           0 :         xmpp_stanza_set_text(content, text);
    1614           0 :         xmpp_stanza_add_child_ex(error_text, content, 0);
    1615             : 
    1616           0 :         xmpp_stanza_add_child_ex(error, error_text, 0);
    1617             :     }
    1618             : 
    1619           0 :     return error;
    1620             : }
    1621             : 
    1622           5 : static void _stub_stream_start(char *name, char **attrs, void *userdata)
    1623             : {
    1624           5 :     UNUSED(name);
    1625           5 :     UNUSED(attrs);
    1626           5 :     UNUSED(userdata);
    1627           5 : }
    1628             : 
    1629           4 : static void _stub_stream_end(char *name, void *userdata)
    1630             : {
    1631           4 :     UNUSED(name);
    1632           4 :     UNUSED(userdata);
    1633           4 : }
    1634             : 
    1635           5 : static void _stream_stanza(xmpp_stanza_t *stanza, void *userdata)
    1636             : {
    1637           5 :     xmpp_stanza_t **dest = userdata;
    1638           5 :     if (*dest == NULL) {
    1639           4 :         stanza = xmpp_stanza_clone(stanza);
    1640           4 :         *dest = stanza;
    1641             :     }
    1642           5 : }
    1643             : 
    1644             : /** Create a stanza object from the string.
    1645             :  *  This function allocates and initializes a stanza object which represents
    1646             :  *  stanza located in the string.
    1647             :  *  The stanza will have a reference count of one, so the caller does not
    1648             :  *  need to clone it.
    1649             :  *
    1650             :  *  @param ctx a Strophe context object
    1651             :  *  @param str stanza in NULL terminated string representation
    1652             :  *
    1653             :  *  @return a stanza object or NULL on an error
    1654             :  *
    1655             :  *  @ingroup Stanza
    1656             :  */
    1657           5 : xmpp_stanza_t *xmpp_stanza_new_from_string(xmpp_ctx_t *ctx, const char *str)
    1658             : {
    1659           5 :     xmpp_stanza_t *stanza = NULL;
    1660           5 :     parser_t *parser;
    1661           5 :     int ret;
    1662             : 
    1663           5 :     static const char *start = "<stream>";
    1664           5 :     static const char *end = "</stream>";
    1665             : 
    1666           5 :     parser = parser_new(ctx, _stub_stream_start, _stub_stream_end,
    1667             :                         _stream_stanza, &stanza);
    1668           5 :     if (parser) {
    1669          10 :         ret = parser_feed(parser, (char *)start, strlen(start)) &&
    1670          10 :               parser_feed(parser, (char *)str, strlen(str)) &&
    1671           5 :               parser_feed(parser, (char *)end, strlen(end));
    1672           5 :         parser_free(parser);
    1673           5 :         if (!ret && stanza) {
    1674           0 :             xmpp_stanza_release(stanza);
    1675           0 :             stanza = NULL;
    1676             :         }
    1677             :     }
    1678           5 :     return stanza;
    1679             : }

Generated by: LCOV version 1.14