LCOV - code coverage report
Current view: top level - src - stanza.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 348 682 51.0 %
Date: 2023-08-10 00:00:00 Functions: 35 53 66.0 %

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

Generated by: LCOV version 1.14