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