LCOV - code coverage report
Current view: top level - src - stanza.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 50.7 % 677 343
Test Date: 2024-08-20 10:03:45 Functions: 66.0 % 53 35

            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            0 :             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              : 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           58 : static void _render_update(
     295              :     int *written, int length, int lastwrite, size_t *left, char **ptr)
     296              : {
     297           74 :     *written += lastwrite;
     298              : 
     299           58 :     if (*written >= length) {
     300              :         *left = 0;
     301              :         *ptr = NULL;
     302              :     } else {
     303           44 :         *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              :         _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              : 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 2.0-1