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 '<': /* "<" */
250 : case '>': /* ">" */
251 0 : len += 4;
252 0 : break;
253 0 : case '&': /* "&" */
254 0 : len += 5;
255 0 : break;
256 0 : case '"':
257 0 : len += 6; /*""" */
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, "<");
270 0 : dst += 4;
271 0 : break;
272 : case '>':
273 0 : strcpy(dst, ">");
274 0 : dst += 4;
275 0 : break;
276 : case '&':
277 0 : strcpy(dst, "&");
278 0 : dst += 5;
279 0 : break;
280 : case '"':
281 0 : strcpy(dst, """);
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 : }
|