mirror of
https://github.com/strophe/libstrophe.git
synced 2026-07-21 04:56:21 +00:00
It has been pointed out that the wording of the license of this library is not entirely clear. The term "dual licensing" usually refers to a licence choice of two licenses "LICENSE1 _or_ LICENSE2. Instead the license of this library claimed "LICENSE1 _and_ LICENSE2". After an internal discussion with @metajack and @pasis it was made clear that the initial idea was to dual license the library in the usual way. This was also made clear by jack on the ML in the past [0]. As of jack, these licensing terms originated from jquery, which also used the 'and' version in the past and has since been corrected [1]. This patch changes the license terms to 'MIT or GPLv3' and also adds SPDX headers [2]. [0] https://groups.google.com/g/libstrophe/c/JkFgr601JQc [1] https://stackoverflow.com/q/2758409 [2] https://spdx.org Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
1685 lines
46 KiB
C
1685 lines
46 KiB
C
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
|
/* stanza.c
|
|
** strophe XMPP client library -- XMPP stanza object and utilities
|
|
**
|
|
** Copyright (C) 2005-2009 Collecta, Inc.
|
|
**
|
|
** This software is provided AS-IS with no warranty, either express
|
|
** or implied.
|
|
**
|
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
|
*/
|
|
|
|
/** @file
|
|
* Stanza creation and manipulation.
|
|
*/
|
|
|
|
/** @defgroup Stanza Stanza creation and manipulation
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "strophe.h"
|
|
#include "common.h"
|
|
#include "hash.h"
|
|
#include "parser.h"
|
|
|
|
/** Create a stanza object.
|
|
* This function allocates and initializes a blank stanza object.
|
|
* The stanza will have a reference count of one, so the caller does not
|
|
* need to clone it.
|
|
*
|
|
* @param ctx a Strophe context object
|
|
*
|
|
* @return a stanza object
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx)
|
|
{
|
|
xmpp_stanza_t *stanza;
|
|
|
|
stanza = strophe_alloc(ctx, sizeof(xmpp_stanza_t));
|
|
if (stanza != NULL) {
|
|
stanza->ref = 1;
|
|
stanza->ctx = ctx;
|
|
stanza->type = XMPP_STANZA_UNKNOWN;
|
|
stanza->prev = NULL;
|
|
stanza->next = NULL;
|
|
stanza->children = NULL;
|
|
stanza->parent = NULL;
|
|
stanza->data = NULL;
|
|
stanza->attributes = NULL;
|
|
}
|
|
|
|
return stanza;
|
|
}
|
|
|
|
/** Clone a stanza object.
|
|
* This function increments the reference count of the stanza object.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return the stanza object with it's reference count incremented
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *stanza)
|
|
{
|
|
stanza->ref++;
|
|
|
|
return stanza;
|
|
}
|
|
|
|
/*
|
|
* Copy the attributes of stanza src into stanza dst. Return -1 on error.
|
|
*/
|
|
static int _stanza_copy_attributes(xmpp_stanza_t *dst, const xmpp_stanza_t *src)
|
|
{
|
|
hash_iterator_t *iter;
|
|
const char *key;
|
|
const char *val;
|
|
int rc = XMPP_EOK;
|
|
|
|
iter = hash_iter_new(src->attributes);
|
|
if (!iter)
|
|
rc = XMPP_EMEM;
|
|
|
|
while (rc == XMPP_EOK && (key = hash_iter_next(iter))) {
|
|
val = hash_get(src->attributes, key);
|
|
if (!val)
|
|
rc = XMPP_EINT;
|
|
if (rc == XMPP_EOK)
|
|
rc = xmpp_stanza_set_attribute(dst, key, val);
|
|
}
|
|
hash_iter_release(iter);
|
|
|
|
if (rc != XMPP_EOK && dst->attributes) {
|
|
hash_release(dst->attributes);
|
|
dst->attributes = NULL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/** Copy a stanza and its children.
|
|
* This function copies a stanza along with all its children and returns
|
|
* the new stanza and children with a reference count of 1. The returned
|
|
* stanza will have no parent and no siblings. This function is useful
|
|
* for extracting a child stanza for inclusion in another tree.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a new Strophe stanza object
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *stanza)
|
|
{
|
|
xmpp_stanza_t *copy, *child, *copychild, *tail;
|
|
|
|
copy = xmpp_stanza_new(stanza->ctx);
|
|
if (!copy)
|
|
goto copy_error;
|
|
|
|
copy->type = stanza->type;
|
|
|
|
if (stanza->data) {
|
|
copy->data = strophe_strdup(stanza->ctx, stanza->data);
|
|
if (!copy->data)
|
|
goto copy_error;
|
|
}
|
|
|
|
if (stanza->attributes) {
|
|
if (_stanza_copy_attributes(copy, stanza) == -1)
|
|
goto copy_error;
|
|
}
|
|
|
|
tail = copy->children;
|
|
for (child = stanza->children; child; child = child->next) {
|
|
copychild = xmpp_stanza_copy(child);
|
|
if (!copychild)
|
|
goto copy_error;
|
|
copychild->parent = copy;
|
|
|
|
if (tail) {
|
|
copychild->prev = tail;
|
|
tail->next = copychild;
|
|
} else
|
|
copy->children = copychild;
|
|
tail = copychild;
|
|
}
|
|
|
|
return copy;
|
|
|
|
copy_error:
|
|
/* release all the hitherto allocated memory */
|
|
if (copy)
|
|
xmpp_stanza_release(copy);
|
|
return NULL;
|
|
}
|
|
|
|
/** Release a stanza object and all of its children.
|
|
* This function releases a stanza object and potentially all of its
|
|
* children, which may cause the object(s) to be freed.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return TRUE if the object was freed and FALSE otherwise
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_release(xmpp_stanza_t *stanza)
|
|
{
|
|
int released = 0;
|
|
xmpp_stanza_t *child, *tchild;
|
|
|
|
/* release stanza */
|
|
if (stanza->ref > 1)
|
|
stanza->ref--;
|
|
else {
|
|
/* release all children */
|
|
child = stanza->children;
|
|
while (child) {
|
|
tchild = child;
|
|
child = child->next;
|
|
tchild->next = NULL;
|
|
xmpp_stanza_release(tchild);
|
|
}
|
|
|
|
if (stanza->attributes)
|
|
hash_release(stanza->attributes);
|
|
if (stanza->data)
|
|
strophe_free(stanza->ctx, stanza->data);
|
|
strophe_free(stanza->ctx, stanza);
|
|
released = 1;
|
|
}
|
|
|
|
return released;
|
|
}
|
|
|
|
/** Get the strophe context that the stanza is associated with.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a Strophe context
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *stanza)
|
|
{
|
|
return stanza->ctx;
|
|
}
|
|
|
|
/** Determine if a stanza is a text node.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return TRUE if the stanza is a text node, FALSE otherwise
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_is_text(xmpp_stanza_t *stanza)
|
|
{
|
|
return (stanza && stanza->type == XMPP_STANZA_TEXT);
|
|
}
|
|
|
|
/** Determine if a stanza is a tag node.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return TRUE if the stanza is a tag node, FALSE otherwise
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_is_tag(xmpp_stanza_t *stanza)
|
|
{
|
|
return (stanza && stanza->type == XMPP_STANZA_TAG);
|
|
}
|
|
|
|
/* Escape a string with for use in a XML text node or attribute. Assumes that
|
|
* the input string is encoded in UTF-8. On success, returns a pointer to a
|
|
* buffer with the resulting data which must be xmpp_free()'d by the caller.
|
|
* On failure, returns NULL.
|
|
*/
|
|
|
|
static char *_escape_xml(xmpp_ctx_t *ctx, char *text)
|
|
{
|
|
size_t len = 0;
|
|
char *src;
|
|
char *dst;
|
|
char *buf;
|
|
for (src = text; *src != '\0'; src++) {
|
|
switch (*src) {
|
|
case '<': /* "<" */
|
|
case '>': /* ">" */
|
|
len += 4;
|
|
break;
|
|
case '&': /* "&" */
|
|
len += 5;
|
|
break;
|
|
case '"':
|
|
len += 6; /*""" */
|
|
break;
|
|
default:
|
|
len++;
|
|
}
|
|
}
|
|
if ((buf = strophe_alloc(ctx, (len + 1) * sizeof(char))) == NULL)
|
|
return NULL; /* Error */
|
|
dst = buf;
|
|
for (src = text; *src != '\0'; src++) {
|
|
switch (*src) {
|
|
case '<':
|
|
strcpy(dst, "<");
|
|
dst += 4;
|
|
break;
|
|
case '>':
|
|
strcpy(dst, ">");
|
|
dst += 4;
|
|
break;
|
|
case '&':
|
|
strcpy(dst, "&");
|
|
dst += 5;
|
|
break;
|
|
case '"':
|
|
strcpy(dst, """);
|
|
dst += 6;
|
|
break;
|
|
default:
|
|
*dst = *src;
|
|
dst++;
|
|
}
|
|
}
|
|
*dst = '\0';
|
|
return buf;
|
|
}
|
|
|
|
/* small helper function */
|
|
static void _render_update(
|
|
int *written, int length, int lastwrite, size_t *left, char **ptr)
|
|
{
|
|
*written += lastwrite;
|
|
|
|
if (*written >= length) {
|
|
*left = 0;
|
|
*ptr = NULL;
|
|
} else {
|
|
*left -= lastwrite;
|
|
*ptr = &(*ptr)[lastwrite];
|
|
}
|
|
}
|
|
|
|
/* always returns number of bytes written or that would have been
|
|
* written if the buffer was large enough
|
|
* return values < 0 indicate some error occurred,
|
|
* and return values > buflen indicate buffer was not large enough
|
|
*/
|
|
static int
|
|
_render_stanza_recursive(xmpp_stanza_t *stanza, char *buf, size_t buflen)
|
|
{
|
|
char *ptr = buf;
|
|
size_t left = buflen;
|
|
int ret, written;
|
|
xmpp_stanza_t *child;
|
|
hash_iterator_t *iter;
|
|
const char *key;
|
|
char *tmp;
|
|
|
|
written = 0;
|
|
|
|
if (stanza->type == XMPP_STANZA_UNKNOWN)
|
|
return XMPP_EINVOP;
|
|
|
|
if (stanza->type == XMPP_STANZA_TEXT) {
|
|
if (!stanza->data)
|
|
return XMPP_EINVOP;
|
|
|
|
tmp = _escape_xml(stanza->ctx, stanza->data);
|
|
if (tmp == NULL)
|
|
return XMPP_EMEM;
|
|
ret = strophe_snprintf(ptr, left, "%s", tmp);
|
|
strophe_free(stanza->ctx, tmp);
|
|
if (ret < 0)
|
|
return XMPP_EMEM;
|
|
_render_update(&written, buflen, ret, &left, &ptr);
|
|
} else { /* stanza->type == XMPP_STANZA_TAG */
|
|
if (!stanza->data)
|
|
return XMPP_EINVOP;
|
|
|
|
/* write beginning of tag and attributes */
|
|
ret = strophe_snprintf(ptr, left, "<%s", stanza->data);
|
|
if (ret < 0)
|
|
return XMPP_EMEM;
|
|
_render_update(&written, buflen, ret, &left, &ptr);
|
|
|
|
if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) {
|
|
iter = hash_iter_new(stanza->attributes);
|
|
while ((key = hash_iter_next(iter))) {
|
|
if (!strcmp(key, "xmlns")) {
|
|
/* don't output namespace if parent stanza is the same */
|
|
if (stanza->parent && stanza->parent->attributes &&
|
|
hash_get(stanza->parent->attributes, key) &&
|
|
!strcmp(
|
|
(char *)hash_get(stanza->attributes, key),
|
|
(char *)hash_get(stanza->parent->attributes, key)))
|
|
continue;
|
|
/* or if this is the stream namespace */
|
|
if (!stanza->parent &&
|
|
!strcmp((char *)hash_get(stanza->attributes, key),
|
|
XMPP_NS_CLIENT))
|
|
continue;
|
|
}
|
|
tmp = _escape_xml(stanza->ctx,
|
|
(char *)hash_get(stanza->attributes, key));
|
|
if (tmp == NULL) {
|
|
hash_iter_release(iter);
|
|
return XMPP_EMEM;
|
|
}
|
|
ret = strophe_snprintf(ptr, left, " %s=\"%s\"", key, tmp);
|
|
strophe_free(stanza->ctx, tmp);
|
|
if (ret < 0) {
|
|
hash_iter_release(iter);
|
|
return XMPP_EMEM;
|
|
}
|
|
_render_update(&written, buflen, ret, &left, &ptr);
|
|
}
|
|
hash_iter_release(iter);
|
|
}
|
|
|
|
if (!stanza->children) {
|
|
/* write end if singleton tag */
|
|
ret = strophe_snprintf(ptr, left, "/>");
|
|
if (ret < 0)
|
|
return XMPP_EMEM;
|
|
_render_update(&written, buflen, ret, &left, &ptr);
|
|
} else {
|
|
/* this stanza has child stanzas */
|
|
|
|
/* write end of start tag */
|
|
ret = strophe_snprintf(ptr, left, ">");
|
|
if (ret < 0)
|
|
return XMPP_EMEM;
|
|
_render_update(&written, buflen, ret, &left, &ptr);
|
|
|
|
/* iterate and recurse over child stanzas */
|
|
child = stanza->children;
|
|
while (child) {
|
|
ret = _render_stanza_recursive(child, ptr, left);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
_render_update(&written, buflen, ret, &left, &ptr);
|
|
|
|
child = child->next;
|
|
}
|
|
|
|
/* write end tag */
|
|
ret = strophe_snprintf(ptr, left, "</%s>", stanza->data);
|
|
if (ret < 0)
|
|
return XMPP_EMEM;
|
|
|
|
_render_update(&written, buflen, ret, &left, &ptr);
|
|
}
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
/** Render a stanza object to text.
|
|
* This function renders a given stanza object, along with its
|
|
* children, to text. The text is returned in an allocated,
|
|
* null-terminated buffer. It starts by allocating a 1024 byte buffer
|
|
* and reallocates more memory if that is not large enough.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param buf a reference to a string pointer
|
|
* @param buflen a reference to a size_t
|
|
*
|
|
* @return 0 on success (XMPP_EOK), and a number less than 0 on failure
|
|
* (XMPP_EMEM, XMPP_EINVOP)
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_to_text(xmpp_stanza_t *stanza, char **buf, size_t *buflen)
|
|
{
|
|
char *buffer, *tmp;
|
|
size_t length;
|
|
int ret;
|
|
|
|
/* allocate a default sized buffer and attempt to render */
|
|
length = 1024;
|
|
buffer = strophe_alloc(stanza->ctx, length);
|
|
if (!buffer) {
|
|
*buf = NULL;
|
|
*buflen = 0;
|
|
return XMPP_EMEM;
|
|
}
|
|
|
|
ret = _render_stanza_recursive(stanza, buffer, length);
|
|
if (ret < 0) {
|
|
strophe_free(stanza->ctx, buffer);
|
|
*buf = NULL;
|
|
*buflen = 0;
|
|
return ret;
|
|
}
|
|
|
|
if ((size_t)ret > length - 1) {
|
|
tmp = strophe_realloc(stanza->ctx, buffer, ret + 1);
|
|
if (!tmp) {
|
|
strophe_free(stanza->ctx, buffer);
|
|
*buf = NULL;
|
|
*buflen = 0;
|
|
return XMPP_EMEM;
|
|
}
|
|
length = ret + 1;
|
|
buffer = tmp;
|
|
|
|
ret = _render_stanza_recursive(stanza, buffer, length);
|
|
if ((size_t)ret > length - 1) {
|
|
strophe_free(stanza->ctx, buffer);
|
|
*buf = NULL;
|
|
*buflen = 0;
|
|
return XMPP_EMEM;
|
|
}
|
|
}
|
|
|
|
buffer[length - 1] = 0;
|
|
|
|
*buf = buffer;
|
|
*buflen = ret;
|
|
|
|
return XMPP_EOK;
|
|
}
|
|
|
|
/** Set the name of a stanza.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param name a string with the name of the stanza
|
|
*
|
|
* @return XMPP_EOK on success, a number less than 0 on failure (XMPP_EMEM,
|
|
* XMPP_EINVOP)
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *name)
|
|
{
|
|
if (stanza->type == XMPP_STANZA_TEXT)
|
|
return XMPP_EINVOP;
|
|
|
|
if (stanza->data)
|
|
strophe_free(stanza->ctx, stanza->data);
|
|
|
|
stanza->type = XMPP_STANZA_TAG;
|
|
stanza->data = strophe_strdup(stanza->ctx, name);
|
|
|
|
return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
|
|
}
|
|
|
|
/** Get the stanza name.
|
|
* This function returns a pointer to the stanza name. If the caller needs
|
|
* to store this data, it must make a copy.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a string with the stanza name
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
const char *xmpp_stanza_get_name(xmpp_stanza_t *stanza)
|
|
{
|
|
if (stanza->type == XMPP_STANZA_TEXT)
|
|
return NULL;
|
|
return stanza->data;
|
|
}
|
|
|
|
/** Count the attributes in a stanza object.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return the number of attributes for the stanza object
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *stanza)
|
|
{
|
|
if (stanza->attributes == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return hash_num_keys(stanza->attributes);
|
|
}
|
|
|
|
/** Get all attributes for a stanza object.
|
|
* This function populates the array with attributes from the stanza. The
|
|
* attr array will be in the format: attr[i] = attribute name,
|
|
* attr[i+1] = attribute value.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param attr the string array to populate
|
|
* @param attrlen the size of the array
|
|
*
|
|
* @return the number of slots used in the array, which will be 2 times the
|
|
* number of attributes in the stanza
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_get_attributes(xmpp_stanza_t *stanza,
|
|
const char **attr,
|
|
int attrlen)
|
|
{
|
|
hash_iterator_t *iter;
|
|
const char *key;
|
|
int num = 0;
|
|
|
|
if (stanza->attributes == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
iter = hash_iter_new(stanza->attributes);
|
|
while ((key = hash_iter_next(iter)) != NULL && attrlen) {
|
|
attr[num++] = key;
|
|
attrlen--;
|
|
if (attrlen == 0) {
|
|
hash_iter_release(iter);
|
|
return num;
|
|
}
|
|
attr[num++] = hash_get(stanza->attributes, key);
|
|
attrlen--;
|
|
if (attrlen == 0) {
|
|
hash_iter_release(iter);
|
|
return num;
|
|
}
|
|
}
|
|
|
|
hash_iter_release(iter);
|
|
return num;
|
|
}
|
|
|
|
/** Set an attribute for a stanza object.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param key a string with the attribute name
|
|
* @param value a string with the attribute value
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_attribute(xmpp_stanza_t *stanza,
|
|
const char *key,
|
|
const char *value)
|
|
{
|
|
char *val;
|
|
int rc;
|
|
|
|
if (stanza->type != XMPP_STANZA_TAG)
|
|
return XMPP_EINVOP;
|
|
|
|
if (!stanza->attributes) {
|
|
stanza->attributes = hash_new(stanza->ctx, 8, strophe_free);
|
|
if (!stanza->attributes)
|
|
return XMPP_EMEM;
|
|
}
|
|
|
|
val = strophe_strdup(stanza->ctx, value);
|
|
if (!val) {
|
|
return XMPP_EMEM;
|
|
}
|
|
|
|
rc = hash_add(stanza->attributes, key, val);
|
|
if (rc < 0) {
|
|
strophe_free(stanza->ctx, val);
|
|
return XMPP_EMEM;
|
|
}
|
|
|
|
return XMPP_EOK;
|
|
}
|
|
|
|
/** Set the stanza namespace.
|
|
* This is a convenience function equivalent to calling:
|
|
* xmpp_stanza_set_attribute(stanza, "xmlns", ns);
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param ns a string with the namespace
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_ns(xmpp_stanza_t *stanza, const char *ns)
|
|
{
|
|
return xmpp_stanza_set_attribute(stanza, "xmlns", ns);
|
|
}
|
|
|
|
/** Add a child stanza to a stanza object.
|
|
* If do_clone is TRUE, user keeps reference to the child stanza and must call
|
|
* xmpp_stanza_release() to release the reference. If do_clone is FALSE, user
|
|
* transfers ownership and must not neither call xmpp_stanza_release() for
|
|
* the child stanza nor use it.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param child the child stanza object
|
|
* @param do_clone TRUE to increase ref count of child (default for
|
|
* xmpp_stanza_add_child())
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza,
|
|
xmpp_stanza_t *child,
|
|
int do_clone)
|
|
{
|
|
xmpp_stanza_t *s;
|
|
|
|
if (do_clone) {
|
|
/* get a reference to the child */
|
|
xmpp_stanza_clone(child);
|
|
}
|
|
|
|
child->parent = stanza;
|
|
|
|
if (!stanza->children)
|
|
stanza->children = child;
|
|
else {
|
|
s = stanza->children;
|
|
while (s->next)
|
|
s = s->next;
|
|
s->next = child;
|
|
child->prev = s;
|
|
}
|
|
|
|
return XMPP_EOK;
|
|
}
|
|
|
|
/** Add a child stanza to a stanza object.
|
|
* This function clones the child and appends it to the stanza object's
|
|
* children.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param child the child stanza object
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child)
|
|
{
|
|
return xmpp_stanza_add_child_ex(stanza, child, 1);
|
|
}
|
|
|
|
/** Set the text data for a text stanza.
|
|
* This function copies the text given and sets the stanza object's text to
|
|
* it. Attempting to use this function on a stanza that has a name will
|
|
* fail with XMPP_EINVOP. This function takes the text as a null-terminated
|
|
* string.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param text a string with the text
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than zero on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *text)
|
|
{
|
|
if (stanza->type == XMPP_STANZA_TAG)
|
|
return XMPP_EINVOP;
|
|
|
|
stanza->type = XMPP_STANZA_TEXT;
|
|
|
|
if (stanza->data)
|
|
strophe_free(stanza->ctx, stanza->data);
|
|
stanza->data = strophe_strdup(stanza->ctx, text);
|
|
|
|
return stanza->data == NULL ? XMPP_EMEM : XMPP_EOK;
|
|
}
|
|
|
|
/** Set the text data for a text stanza.
|
|
* This function copies the text given and sets the stanza object's text to
|
|
* it. Attempting to use this function on a stanza that has a name will
|
|
* fail with XMPP_EINVOP. This function takes the text as buffer and a length
|
|
* as opposed to a null-terminated string.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param text a buffer with the text
|
|
* @param size the length of the text
|
|
*
|
|
* @return XMPP_EOK (0) on success and a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
|
const char *text,
|
|
size_t size)
|
|
{
|
|
if (stanza->type == XMPP_STANZA_TAG)
|
|
return XMPP_EINVOP;
|
|
|
|
stanza->type = XMPP_STANZA_TEXT;
|
|
|
|
if (stanza->data)
|
|
strophe_free(stanza->ctx, stanza->data);
|
|
stanza->data = strophe_alloc(stanza->ctx, size + 1);
|
|
if (!stanza->data)
|
|
return XMPP_EMEM;
|
|
|
|
memcpy(stanza->data, text, size);
|
|
stanza->data[size] = 0;
|
|
|
|
return XMPP_EOK;
|
|
}
|
|
|
|
/** Get the 'id' attribute of the stanza object.
|
|
* This is a convenience function equivalent to:
|
|
* xmpp_stanza_get_attribute(stanza, "id");
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a string with the 'id' attribute value
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
const char *xmpp_stanza_get_id(xmpp_stanza_t *stanza)
|
|
{
|
|
return xmpp_stanza_get_attribute(stanza, "id");
|
|
}
|
|
|
|
/** Get the namespace attribute of the stanza object.
|
|
* This is a convenience function equivalent to:
|
|
* xmpp_stanza_get_attribute(stanza, "xmlns");
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a string with the 'xmlns' attribute value
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
const char *xmpp_stanza_get_ns(xmpp_stanza_t *stanza)
|
|
{
|
|
return xmpp_stanza_get_attribute(stanza, "xmlns");
|
|
}
|
|
|
|
/** Get the 'type' attribute of the stanza object.
|
|
* This is a convenience function equivalent to:
|
|
* xmpp_stanza_get_attribute(stanza, "type");
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a string with the 'type' attribute value
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
const char *xmpp_stanza_get_type(xmpp_stanza_t *stanza)
|
|
{
|
|
return xmpp_stanza_get_attribute(stanza, "type");
|
|
}
|
|
|
|
/** Get the 'to' attribute of the stanza object.
|
|
* This is a convenience function equivalent to:
|
|
* xmpp_stanza_get_attribute(stanza, "to");
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a string with the 'to' attribute value
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
const char *xmpp_stanza_get_to(xmpp_stanza_t *stanza)
|
|
{
|
|
return xmpp_stanza_get_attribute(stanza, "to");
|
|
}
|
|
|
|
/** Get the 'from' attribute of the stanza object.
|
|
* This is a convenience function equivalent to:
|
|
* xmpp_stanza_get_attribute(stanza, "from");
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a string with the 'from' attribute value
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
const char *xmpp_stanza_get_from(xmpp_stanza_t *stanza)
|
|
{
|
|
return xmpp_stanza_get_attribute(stanza, "from");
|
|
}
|
|
|
|
/** Get the first child of stanza following a path-like list of names.
|
|
* This function searches the children and their children that match
|
|
* the given path.
|
|
*
|
|
* * "name" - Search 'name'
|
|
*
|
|
* * "name[@ns='foo']" - Search 'name' which is in the namespace 'foo'
|
|
*
|
|
* The Syntax to pass namespaces is inspired by the XPATH way of passing
|
|
* attributes.
|
|
*
|
|
* The namespace syntax only supports single quotes `'`.
|
|
*
|
|
* The \ref XMPP_STANZA_NAME_IN_NS macro is provided as a helper for names
|
|
* in namespaces.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param ... a var-args list that must be terminated by a NULL entry
|
|
*
|
|
* @return the matching child stanza object or NULL if no match was found
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_get_child_by_path(xmpp_stanza_t *stanza, ...)
|
|
{
|
|
xmpp_stanza_t *child = NULL;
|
|
char *p, *tok, *attr, *saveattr, *ns = NULL;
|
|
const char *xmlns;
|
|
va_list ap;
|
|
|
|
va_start(ap, stanza);
|
|
|
|
while ((p = va_arg(ap, char *)) != NULL) {
|
|
tok = strophe_strdup(stanza->ctx, p);
|
|
if (!tok) {
|
|
child = NULL;
|
|
break;
|
|
}
|
|
saveattr = ns = NULL;
|
|
attr = strophe_strtok_r(tok, "[", &saveattr);
|
|
if (attr) {
|
|
attr = strophe_strtok_r(NULL, "]", &saveattr);
|
|
if (attr) {
|
|
if (!strncmp(attr, "@ns='", 5)) {
|
|
ns = attr + 5;
|
|
strophe_strtok_r(ns, "'", &saveattr);
|
|
}
|
|
}
|
|
}
|
|
if (!child) {
|
|
if (strcmp(xmpp_stanza_get_name(stanza), tok))
|
|
goto error_out;
|
|
|
|
if (ns) {
|
|
xmlns = xmpp_stanza_get_ns(stanza);
|
|
if (!xmlns || strcmp(xmlns, ns))
|
|
goto error_out;
|
|
}
|
|
child = stanza;
|
|
} else {
|
|
if (!ns)
|
|
child = xmpp_stanza_get_child_by_name(child, tok);
|
|
else
|
|
child = xmpp_stanza_get_child_by_name_and_ns(child, tok, ns);
|
|
}
|
|
error_out:
|
|
strophe_free(stanza->ctx, tok);
|
|
if (!child)
|
|
break;
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
return p == NULL ? child : NULL;
|
|
}
|
|
|
|
/** Get the first child of stanza with name.
|
|
* This function searches all the immediate children of stanza for a child
|
|
* stanza that matches the name. The first matching child is returned.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param name a string with the name to match
|
|
*
|
|
* @return the matching child stanza object or NULL if no match was found
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *stanza,
|
|
const char *name)
|
|
{
|
|
xmpp_stanza_t *child;
|
|
|
|
for (child = stanza->children; child; child = child->next) {
|
|
if (child->type == XMPP_STANZA_TAG &&
|
|
(strcmp(name, xmpp_stanza_get_name(child)) == 0))
|
|
break;
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
/** Get the first child of a stanza with a given namespace.
|
|
* This function searches all the immediate children of a stanza for a child
|
|
* stanza that matches the namespace provided. The first matching child
|
|
* is returned.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param ns a string with the namespace to match
|
|
*
|
|
* @return the matching child stanza object or NULL if no match was found
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *stanza,
|
|
const char *ns)
|
|
{
|
|
xmpp_stanza_t *child;
|
|
const char *child_ns;
|
|
|
|
for (child = stanza->children; child; child = child->next) {
|
|
child_ns = xmpp_stanza_get_ns(child);
|
|
if (child_ns && strcmp(ns, child_ns) == 0)
|
|
break;
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
/** Get the first child of stanza with name and a given namespace.
|
|
* This function searches all the immediate children of stanza for a child
|
|
* stanza that matches the name and namespace provided.
|
|
* The first matching child is returned.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param name a string with the name to match
|
|
* @param ns a string with the namespace to match
|
|
*
|
|
* @return the matching child stanza object or NULL if no match was found
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *stanza,
|
|
const char *name,
|
|
const char *ns)
|
|
{
|
|
xmpp_stanza_t *child;
|
|
const char *child_ns;
|
|
|
|
for (child = stanza->children; child; child = child->next) {
|
|
if (child->type == XMPP_STANZA_TAG &&
|
|
(strcmp(name, xmpp_stanza_get_name(child)) == 0)) {
|
|
child_ns = xmpp_stanza_get_ns(child);
|
|
if (child_ns && strcmp(ns, child_ns) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
/** Get the list of children.
|
|
* This function returns the first child of the stanza object. The rest
|
|
* of the children can be obtained by calling xmpp_stanza_get_next() to
|
|
* iterate over the siblings.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return the first child stanza or NULL if there are no children
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *stanza)
|
|
{
|
|
return stanza->children;
|
|
}
|
|
|
|
/** Get the next sibling of a stanza.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return the next sibling stanza or NULL if there are no more siblings
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *stanza)
|
|
{
|
|
return stanza->next;
|
|
}
|
|
|
|
/** Get the text data for a text stanza.
|
|
* This function copies the text data from a stanza and returns the new
|
|
* allocated string. The caller is responsible for freeing this string
|
|
* with xmpp_free().
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return an allocated string with the text data
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
char *xmpp_stanza_get_text(xmpp_stanza_t *stanza)
|
|
{
|
|
size_t len, clen;
|
|
xmpp_stanza_t *child;
|
|
char *text;
|
|
|
|
if (stanza->type == XMPP_STANZA_TEXT) {
|
|
if (stanza->data)
|
|
return strophe_strdup(stanza->ctx, stanza->data);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
len = 0;
|
|
for (child = stanza->children; child; child = child->next)
|
|
if (child->type == XMPP_STANZA_TEXT)
|
|
len += strlen(child->data);
|
|
|
|
if (len == 0)
|
|
return NULL;
|
|
|
|
text = (char *)strophe_alloc(stanza->ctx, len + 1);
|
|
if (!text)
|
|
return NULL;
|
|
|
|
len = 0;
|
|
for (child = stanza->children; child; child = child->next)
|
|
if (child->type == XMPP_STANZA_TEXT) {
|
|
clen = strlen(child->data);
|
|
memcpy(&text[len], child->data, clen);
|
|
len += clen;
|
|
}
|
|
|
|
text[len] = 0;
|
|
|
|
return text;
|
|
}
|
|
|
|
/** Get the text data pointer for a text stanza.
|
|
* This function copies returns the raw pointer to the text data in the
|
|
* stanza. This should only be used in very special cases where the
|
|
* caller needs to translate the datatype as this will save a double
|
|
* allocation. The caller should not hold onto this pointer, and is
|
|
* responsible for allocating a copy if it needs one.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return an string pointer to the data or NULL
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *stanza)
|
|
{
|
|
if (stanza->type == XMPP_STANZA_TEXT)
|
|
return stanza->data;
|
|
return NULL;
|
|
}
|
|
|
|
/** Set the 'id' attribute of a stanza.
|
|
*
|
|
* This is a convenience function for:
|
|
* xmpp_stanza_set_attribute(stanza, 'id', id);
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param id a string containing the 'id' value
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_id(xmpp_stanza_t *stanza, const char *id)
|
|
{
|
|
return xmpp_stanza_set_attribute(stanza, "id", id);
|
|
}
|
|
|
|
/** Set the 'type' attribute of a stanza.
|
|
* This is a convenience function for:
|
|
* xmpp_stanza_set_attribute(stanza, 'type', type);
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param type a string containing the 'type' value
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_type(xmpp_stanza_t *stanza, const char *type)
|
|
{
|
|
return xmpp_stanza_set_attribute(stanza, "type", type);
|
|
}
|
|
|
|
/** Set the 'to' attribute of a stanza.
|
|
*
|
|
* This is a convenience function for:
|
|
* xmpp_stanza_set_attribute(stanza, 'to', to);
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param to a string containing the 'to' value
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_to(xmpp_stanza_t *stanza, const char *to)
|
|
{
|
|
return xmpp_stanza_set_attribute(stanza, "to", to);
|
|
}
|
|
|
|
/** Set the 'from' attribute of a stanza.
|
|
*
|
|
* This is a convenience function for:
|
|
* xmpp_stanza_set_attribute(stanza, 'from', from);
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param from a string containing the 'from' value
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_set_from(xmpp_stanza_t *stanza, const char *from)
|
|
{
|
|
return xmpp_stanza_set_attribute(stanza, "from", from);
|
|
}
|
|
|
|
/** Get an attribute from a stanza.
|
|
* This function returns a pointer to the attribute value. If the caller
|
|
* wishes to save this value it must make its own copy.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param name a string containing attribute name
|
|
*
|
|
* @return a string with the attribute value or NULL on an error
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *stanza, const char *name)
|
|
{
|
|
if (stanza->type != XMPP_STANZA_TAG)
|
|
return NULL;
|
|
|
|
if (!stanza->attributes)
|
|
return NULL;
|
|
|
|
return hash_get(stanza->attributes, name);
|
|
}
|
|
|
|
/** Delete an attribute from a stanza.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param name a string containing attribute name
|
|
*
|
|
* @return XMPP_EOK (0) on success or a number less than 0 on failure
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_stanza_del_attribute(xmpp_stanza_t *stanza, const char *name)
|
|
{
|
|
if (stanza->type != XMPP_STANZA_TAG)
|
|
return -1;
|
|
|
|
if (!stanza->attributes)
|
|
return -1;
|
|
|
|
return hash_drop(stanza->attributes, name);
|
|
}
|
|
|
|
/** Create a stanza object in reply to another.
|
|
* This function makes a copy of a stanza object with the attribute "to" set
|
|
* its original "from".
|
|
* The stanza will have a reference count of one, so the caller does not
|
|
* need to clone it.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
*
|
|
* @return a new Strophe stanza object
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *stanza)
|
|
{
|
|
xmpp_stanza_t *copy = NULL;
|
|
const char *from;
|
|
int rc;
|
|
|
|
from = xmpp_stanza_get_from(stanza);
|
|
if (!from)
|
|
goto copy_error;
|
|
|
|
copy = xmpp_stanza_new(stanza->ctx);
|
|
if (!copy)
|
|
goto copy_error;
|
|
|
|
copy->type = stanza->type;
|
|
|
|
if (stanza->data) {
|
|
copy->data = strophe_strdup(stanza->ctx, stanza->data);
|
|
if (!copy->data)
|
|
goto copy_error;
|
|
}
|
|
|
|
if (stanza->attributes) {
|
|
if (_stanza_copy_attributes(copy, stanza) < 0)
|
|
goto copy_error;
|
|
}
|
|
|
|
xmpp_stanza_del_attribute(copy, "to");
|
|
xmpp_stanza_del_attribute(copy, "from");
|
|
xmpp_stanza_del_attribute(copy, "xmlns");
|
|
rc = xmpp_stanza_set_to(copy, from);
|
|
if (rc != XMPP_EOK)
|
|
goto copy_error;
|
|
|
|
return copy;
|
|
|
|
copy_error:
|
|
if (copy)
|
|
xmpp_stanza_release(copy);
|
|
return NULL;
|
|
}
|
|
|
|
/** Create an error stanza in reply to the provided stanza.
|
|
*
|
|
* Check https://tools.ietf.org/html/rfc6120#section-8.3 for details.
|
|
*
|
|
* @param stanza a Strophe stanza object
|
|
* @param error_type type attribute in the `<error/>` child element
|
|
* @param condition the defined-condition (e.g. "item-not-found")
|
|
* @param text optional description, may be NULL
|
|
*
|
|
* @return a new Strophe stanza object
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *stanza,
|
|
const char *error_type,
|
|
const char *condition,
|
|
const char *text)
|
|
{
|
|
xmpp_ctx_t *ctx = stanza->ctx;
|
|
xmpp_stanza_t *reply = NULL;
|
|
xmpp_stanza_t *error = NULL;
|
|
xmpp_stanza_t *item = NULL;
|
|
xmpp_stanza_t *text_stanza = NULL;
|
|
const char *to;
|
|
|
|
if (!error_type || !condition)
|
|
goto quit_err;
|
|
|
|
reply = xmpp_stanza_reply(stanza);
|
|
if (!reply)
|
|
goto quit_err;
|
|
if (xmpp_stanza_set_type(reply, "error") != XMPP_EOK)
|
|
goto quit_err;
|
|
to = xmpp_stanza_get_to(stanza);
|
|
if (to)
|
|
if (xmpp_stanza_set_from(reply, to) != XMPP_EOK)
|
|
goto quit_err;
|
|
|
|
error = xmpp_stanza_new(ctx);
|
|
if (!error)
|
|
goto quit_err;
|
|
if (xmpp_stanza_set_name(error, "error") != XMPP_EOK)
|
|
goto quit_err;
|
|
if (xmpp_stanza_set_type(error, error_type) != XMPP_EOK)
|
|
goto quit_err;
|
|
if (xmpp_stanza_add_child(reply, error) != XMPP_EOK)
|
|
goto quit_err;
|
|
xmpp_stanza_release(error);
|
|
|
|
item = xmpp_stanza_new(ctx);
|
|
if (!item)
|
|
goto quit_err;
|
|
if (xmpp_stanza_set_name(item, condition) != XMPP_EOK)
|
|
goto quit_err;
|
|
if (xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF) != XMPP_EOK)
|
|
goto quit_err;
|
|
if (xmpp_stanza_add_child(error, item) != XMPP_EOK)
|
|
goto quit_err;
|
|
xmpp_stanza_release(item);
|
|
|
|
if (text) {
|
|
item = xmpp_stanza_new(ctx);
|
|
if (!item)
|
|
goto quit_err;
|
|
if (xmpp_stanza_set_name(item, "text") != XMPP_EOK)
|
|
goto quit_err;
|
|
if (xmpp_stanza_set_ns(item, XMPP_NS_STANZAS_IETF) != XMPP_EOK)
|
|
goto quit_err;
|
|
if (xmpp_stanza_add_child(error, item) != XMPP_EOK)
|
|
goto quit_err;
|
|
xmpp_stanza_release(item);
|
|
text_stanza = xmpp_stanza_new(ctx);
|
|
if (!text_stanza)
|
|
goto quit_err;
|
|
if (xmpp_stanza_set_text(text_stanza, text) != XMPP_EOK)
|
|
goto quit_err;
|
|
if (xmpp_stanza_add_child(item, text_stanza) != XMPP_EOK)
|
|
goto quit_err;
|
|
xmpp_stanza_release(text_stanza);
|
|
}
|
|
|
|
return reply;
|
|
|
|
quit_err:
|
|
if (reply)
|
|
xmpp_stanza_release(reply);
|
|
if (error)
|
|
xmpp_stanza_release(error);
|
|
if (item)
|
|
xmpp_stanza_release(item);
|
|
if (text_stanza)
|
|
xmpp_stanza_release(text_stanza);
|
|
return NULL;
|
|
}
|
|
|
|
static xmpp_stanza_t *_stanza_new_with_attrs(xmpp_ctx_t *ctx,
|
|
const char *name,
|
|
const char *type,
|
|
const char *id,
|
|
const char *to)
|
|
{
|
|
xmpp_stanza_t *stanza = xmpp_stanza_new(ctx);
|
|
int ret;
|
|
|
|
if (stanza) {
|
|
ret = xmpp_stanza_set_name(stanza, name);
|
|
if (ret == XMPP_EOK && type)
|
|
ret = xmpp_stanza_set_type(stanza, type);
|
|
if (ret == XMPP_EOK && id)
|
|
ret = xmpp_stanza_set_id(stanza, id);
|
|
if (ret == XMPP_EOK && to)
|
|
ret = xmpp_stanza_set_to(stanza, to);
|
|
if (ret != XMPP_EOK) {
|
|
xmpp_stanza_release(stanza);
|
|
stanza = NULL;
|
|
}
|
|
}
|
|
return stanza;
|
|
}
|
|
|
|
/** Create a `<message/>` stanza object with given attributes.
|
|
* Attributes are optional and may be NULL.
|
|
*
|
|
* @param ctx a Strophe context object
|
|
* @param type attribute 'type'
|
|
* @param to attribute 'to'
|
|
* @param id attribute 'id'
|
|
*
|
|
* @return a new Strophe stanza object
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
|
|
const char *type,
|
|
const char *to,
|
|
const char *id)
|
|
{
|
|
return _stanza_new_with_attrs(ctx, "message", type, id, to);
|
|
}
|
|
|
|
/** Get text from `<body/>` child element.
|
|
* This function returns new allocated string. The caller is responsible
|
|
* for freeing this string with xmpp_free().
|
|
*
|
|
* @param msg well formed `<message/>` stanza
|
|
*
|
|
* @return allocated string or NULL on failure (no `<body/>` element or
|
|
* memory allocation error)
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
char *xmpp_message_get_body(xmpp_stanza_t *msg)
|
|
{
|
|
xmpp_stanza_t *body;
|
|
const char *name;
|
|
char *text = NULL;
|
|
|
|
name = xmpp_stanza_get_name(msg);
|
|
body = xmpp_stanza_get_child_by_name(msg, "body");
|
|
if (name && strcmp(name, "message") == 0 && body) {
|
|
text = xmpp_stanza_get_text(body);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
/** Add `<body/>` child element to a `<message/>` stanza with the given text.
|
|
*
|
|
* @param msg a `<message>` stanza object without `<body/>` child element.
|
|
* @param text The text that shall be placed in the body.
|
|
*
|
|
* @return 0 on success (XMPP_EOK), and a number less than 0 on failure
|
|
* (XMPP_EMEM, XMPP_EINVOP)
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *text)
|
|
{
|
|
xmpp_ctx_t *ctx = msg->ctx;
|
|
xmpp_stanza_t *body;
|
|
xmpp_stanza_t *text_stanza;
|
|
const char *name;
|
|
int ret;
|
|
|
|
/* check that msg is a `<message/>` stanza and doesn't contain `<body/>` */
|
|
name = xmpp_stanza_get_name(msg);
|
|
body = xmpp_stanza_get_child_by_name(msg, "body");
|
|
if (!name || strcmp(name, "message") != 0 || body)
|
|
return XMPP_EINVOP;
|
|
|
|
body = xmpp_stanza_new(ctx);
|
|
text_stanza = xmpp_stanza_new(ctx);
|
|
|
|
ret = body && text_stanza ? XMPP_EOK : XMPP_EMEM;
|
|
if (ret == XMPP_EOK)
|
|
ret = xmpp_stanza_set_name(body, "body");
|
|
if (ret == XMPP_EOK)
|
|
ret = xmpp_stanza_set_text(text_stanza, text);
|
|
if (ret == XMPP_EOK)
|
|
ret = xmpp_stanza_add_child(body, text_stanza);
|
|
if (ret == XMPP_EOK)
|
|
ret = xmpp_stanza_add_child(msg, body);
|
|
|
|
if (text_stanza)
|
|
xmpp_stanza_release(text_stanza);
|
|
if (body)
|
|
xmpp_stanza_release(body);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/** Create an `<iq/>` stanza object with given attributes.
|
|
* Attributes are optional and may be NULL.
|
|
*
|
|
* @param ctx a Strophe context object
|
|
* @param type attribute 'type'
|
|
* @param id attribute 'id'
|
|
*
|
|
* @return a new Strophe stanza object
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char *type, const char *id)
|
|
{
|
|
return _stanza_new_with_attrs(ctx, "iq", type, id, NULL);
|
|
}
|
|
|
|
/** Create a `<presence/>` stanza object.
|
|
*
|
|
* @param ctx a Strophe context object
|
|
*
|
|
* @return a new Strophe stanza object
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx)
|
|
{
|
|
return _stanza_new_with_attrs(ctx, "presence", NULL, NULL, NULL);
|
|
}
|
|
|
|
/** Create an <stream:error/> stanza object with given type and error text.
|
|
* The error text is optional and may be NULL.
|
|
*
|
|
* @param ctx a Strophe context object
|
|
* @param type enum of strophe_error_type_t
|
|
* @param text content of a 'text'
|
|
*
|
|
* @return a new Strophe stanza object
|
|
*
|
|
* @todo Handle errors in this function
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *
|
|
xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text)
|
|
{
|
|
xmpp_stanza_t *error =
|
|
_stanza_new_with_attrs(ctx, "stream:error", NULL, NULL, NULL);
|
|
xmpp_stanza_t *error_type = xmpp_stanza_new(ctx);
|
|
|
|
switch (type) {
|
|
case XMPP_SE_BAD_FORMAT:
|
|
xmpp_stanza_set_name(error_type, "bad-format");
|
|
break;
|
|
case XMPP_SE_BAD_NS_PREFIX:
|
|
xmpp_stanza_set_name(error_type, "bad-namespace-prefix");
|
|
break;
|
|
case XMPP_SE_CONFLICT:
|
|
xmpp_stanza_set_name(error_type, "conflict");
|
|
break;
|
|
case XMPP_SE_CONN_TIMEOUT:
|
|
xmpp_stanza_set_name(error_type, "connection-timeout");
|
|
break;
|
|
case XMPP_SE_HOST_GONE:
|
|
xmpp_stanza_set_name(error_type, "host-gone");
|
|
break;
|
|
case XMPP_SE_HOST_UNKNOWN:
|
|
xmpp_stanza_set_name(error_type, "host-unknown");
|
|
break;
|
|
case XMPP_SE_IMPROPER_ADDR:
|
|
xmpp_stanza_set_name(error_type, "improper-addressing");
|
|
break;
|
|
case XMPP_SE_INTERNAL_SERVER_ERROR:
|
|
xmpp_stanza_set_name(error_type, "internal-server-error");
|
|
break;
|
|
case XMPP_SE_INVALID_FROM:
|
|
xmpp_stanza_set_name(error_type, "invalid-from");
|
|
break;
|
|
case XMPP_SE_INVALID_ID:
|
|
xmpp_stanza_set_name(error_type, "invalid-id");
|
|
break;
|
|
case XMPP_SE_INVALID_NS:
|
|
xmpp_stanza_set_name(error_type, "invalid-namespace");
|
|
break;
|
|
case XMPP_SE_INVALID_XML:
|
|
xmpp_stanza_set_name(error_type, "invalid-xml");
|
|
break;
|
|
case XMPP_SE_NOT_AUTHORIZED:
|
|
xmpp_stanza_set_name(error_type, "not-authorized");
|
|
break;
|
|
case XMPP_SE_POLICY_VIOLATION:
|
|
xmpp_stanza_set_name(error_type, "policy-violation");
|
|
break;
|
|
case XMPP_SE_REMOTE_CONN_FAILED:
|
|
xmpp_stanza_set_name(error_type, "remote-connection-failed");
|
|
break;
|
|
case XMPP_SE_RESOURCE_CONSTRAINT:
|
|
xmpp_stanza_set_name(error_type, "resource-constraint");
|
|
break;
|
|
case XMPP_SE_RESTRICTED_XML:
|
|
xmpp_stanza_set_name(error_type, "restricted-xml");
|
|
break;
|
|
case XMPP_SE_SEE_OTHER_HOST:
|
|
xmpp_stanza_set_name(error_type, "see-other-host");
|
|
break;
|
|
case XMPP_SE_SYSTEM_SHUTDOWN:
|
|
xmpp_stanza_set_name(error_type, "system-shutdown");
|
|
break;
|
|
case XMPP_SE_UNDEFINED_CONDITION:
|
|
xmpp_stanza_set_name(error_type, "undefined-condition");
|
|
break;
|
|
case XMPP_SE_UNSUPPORTED_ENCODING:
|
|
xmpp_stanza_set_name(error_type, "unsupported-encoding");
|
|
break;
|
|
case XMPP_SE_UNSUPPORTED_STANZA_TYPE:
|
|
xmpp_stanza_set_name(error_type, "unsupported-stanza-type");
|
|
break;
|
|
case XMPP_SE_UNSUPPORTED_VERSION:
|
|
xmpp_stanza_set_name(error_type, "unsupported-version");
|
|
break;
|
|
case XMPP_SE_XML_NOT_WELL_FORMED:
|
|
xmpp_stanza_set_name(error_type, "xml-not-well-formed");
|
|
break;
|
|
default:
|
|
xmpp_stanza_set_name(error_type, "internal-server-error");
|
|
break;
|
|
}
|
|
|
|
xmpp_stanza_set_ns(error_type, XMPP_NS_STREAMS_IETF);
|
|
xmpp_stanza_add_child_ex(error, error_type, 0);
|
|
|
|
if (text) {
|
|
xmpp_stanza_t *error_text = xmpp_stanza_new(ctx);
|
|
xmpp_stanza_t *content = xmpp_stanza_new(ctx);
|
|
|
|
xmpp_stanza_set_name(error_text, "text");
|
|
xmpp_stanza_set_ns(error_text, XMPP_NS_STREAMS_IETF);
|
|
|
|
xmpp_stanza_set_text(content, text);
|
|
xmpp_stanza_add_child_ex(error_text, content, 0);
|
|
|
|
xmpp_stanza_add_child_ex(error, error_text, 0);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static void _stub_stream_start(char *name, char **attrs, void *userdata)
|
|
{
|
|
UNUSED(name);
|
|
UNUSED(attrs);
|
|
UNUSED(userdata);
|
|
}
|
|
|
|
static void _stub_stream_end(char *name, void *userdata)
|
|
{
|
|
UNUSED(name);
|
|
UNUSED(userdata);
|
|
}
|
|
|
|
static void _stream_stanza(xmpp_stanza_t *stanza, void *userdata)
|
|
{
|
|
xmpp_stanza_t **dest = userdata;
|
|
if (*dest == NULL) {
|
|
stanza = xmpp_stanza_clone(stanza);
|
|
*dest = stanza;
|
|
}
|
|
}
|
|
|
|
/** Create a stanza object from the string.
|
|
* This function allocates and initializes a stanza object which represents
|
|
* stanza located in the string.
|
|
* The stanza will have a reference count of one, so the caller does not
|
|
* need to clone it.
|
|
*
|
|
* @param ctx a Strophe context object
|
|
* @param str stanza in NULL terminated string representation
|
|
*
|
|
* @return a stanza object or NULL on an error
|
|
*
|
|
* @ingroup Stanza
|
|
*/
|
|
xmpp_stanza_t *xmpp_stanza_new_from_string(xmpp_ctx_t *ctx, const char *str)
|
|
{
|
|
xmpp_stanza_t *stanza = NULL;
|
|
parser_t *parser;
|
|
int ret;
|
|
|
|
static const char *start = "<stream>";
|
|
static const char *end = "</stream>";
|
|
|
|
parser = parser_new(ctx, _stub_stream_start, _stub_stream_end,
|
|
_stream_stanza, &stanza);
|
|
if (parser) {
|
|
ret = parser_feed(parser, (char *)start, strlen(start)) &&
|
|
parser_feed(parser, (char *)str, strlen(str)) &&
|
|
parser_feed(parser, (char *)end, strlen(end));
|
|
parser_free(parser);
|
|
if (!ret && stanza) {
|
|
xmpp_stanza_release(stanza);
|
|
stanza = NULL;
|
|
}
|
|
}
|
|
return stanza;
|
|
}
|