/* * Copy the attributes of stanza src into stanza dst. Return -1 on error. */ static int _stanza_copy_attributes(xmpp_stanza_t * dst, const xmpp_stanza_t * const src) { hash_iterator_t *iter = NULL; const char *key; void *val; dst->attributes = hash_new(src->ctx, 8, xmpp_free); if (!dst->attributes) return -1; iter = hash_iter_new(src->attributes); if (!iter) goto error; while ((key = hash_iter_next(iter))) { val = xmpp_strdup(src->ctx, (char *)hash_get(src->attributes, key)); if (!val) goto error; if (hash_add(dst->attributes, key, val)) { xmpp_free(src->ctx, val); goto error; } } hash_iter_release(iter); return 0; error: if (iter != NULL) hash_iter_release(iter); hash_release(dst->attributes); return -1; }
/** Get all attributes for a stanza object. * This function populates the array with attributes from the stanza. The * attr array will be in the format: attr[i] = attribute name, * attr[i+1] = attribute value. * * @param stanza a Strophe stanza object * @param attr the string array to populate * @param attrlen the size of the array * * @return the number of slots used in the array, which will be 2 times the * number of attributes in the stanza * * @ingroup Stanza */ int xmpp_stanza_get_attributes(xmpp_stanza_t * const stanza, const char **attr, int attrlen) { hash_iterator_t *iter; const char *key; int num = 0; if (stanza->attributes == NULL) { return 0; } iter = hash_iter_new(stanza->attributes); while ((key = hash_iter_next(iter)) != NULL && attrlen) { attr[num++] = key; attrlen--; if (attrlen == 0) { hash_iter_release(iter); return num; } attr[num++] = hash_get(stanza->attributes, key); attrlen--; if (attrlen == 0) { hash_iter_release(iter); return num; } } hash_iter_release(iter); return num; }
/* * Copy the attributes of stanza src into stanza dst. Return -1 on error. */ static int _stanza_copy_attributes(xmpp_stanza_t * dst, const xmpp_stanza_t * const src) { hash_iterator_t *iter; const char *key; const char *val; int rc = XMPP_EOK; iter = hash_iter_new(src->attributes); if (!iter) rc = XMPP_EMEM; while (rc == XMPP_EOK && (key = hash_iter_next(iter))) { val = hash_get(src->attributes, key); if (!val) rc = XMPP_EINT; if (rc == XMPP_EOK) rc = xmpp_stanza_set_attribute(dst, key, val); } hash_iter_release(iter); if (rc != XMPP_EOK && dst->attributes) { hash_release(dst->attributes); dst->attributes = NULL; } return rc; }
/** Copy a stanza and its children. * This function copies a stanza along with all its children and returns * the new stanza and children with a reference count of 1. The returned * stanza will have no parent and no siblings. This function is useful * for extracting a child stanza for inclusion in another tree. * * @param stanza a Strophe stanza object * * @return a new Strophe stanza object * * @ingroup Stanza */ xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t * const stanza) { xmpp_stanza_t *copy, *child, *copychild, *tail; hash_iterator_t *iter; const char *key; void *val; copy = xmpp_stanza_new(stanza->ctx); if (!copy) goto copy_error; copy->type = stanza->type; if (stanza->data) { copy->data = xmpp_strdup(stanza->ctx, stanza->data); if (!copy->data) goto copy_error; } if (stanza->attributes) { copy->attributes = hash_new(stanza->ctx, 8, xmpp_free); if (!copy->attributes) goto copy_error; iter = hash_iter_new(stanza->attributes); if (!iter) { printf("DEBUG HERE\n"); goto copy_error; } while ((key = hash_iter_next(iter))) { val = xmpp_strdup(stanza->ctx, (char *)hash_get(stanza->attributes, key)); if (!val) goto copy_error; if (hash_add(copy->attributes, key, val)) goto copy_error; } hash_iter_release(iter); } tail = copy->children; for (child = stanza->children; child; child = child->next) { copychild = xmpp_stanza_copy(child); if (!copychild) goto copy_error; copychild->parent = copy; if (tail) { copychild->prev = tail; tail->next = copychild; } else copy->children = copychild; tail = copychild; } return copy; copy_error: /* release all the hitherto allocated memory */ if (copy) xmpp_stanza_release(copy); return NULL; }
/** Release a Strophe connection object. * Decrement the reference count by one for a connection, freeing the * connection object if the count reaches 0. * * @param conn a Strophe connection object * * @return TRUE if the connection object was freed and FALSE otherwise * * @ingroup Connections */ int xmpp_conn_release(xmpp_conn_t * const conn) { xmpp_ctx_t *ctx; xmpp_connlist_t *item, *prev; xmpp_handlist_t *hlitem, *thli; hash_iterator_t *iter; const char *key; int released = 0; if (conn->ref > 1) conn->ref--; else { ctx = conn->ctx; /* remove connection from context's connlist */ if (ctx->connlist->conn == conn) { item = ctx->connlist; ctx->connlist = item->next; xmpp_free(ctx, item); } else { prev = NULL; item = ctx->connlist; while (item && item->conn != conn) { prev = item; item = item->next; } if (!item) { xmpp_error(ctx, "xmpp", "Connection not in context's list\n"); } else { prev->next = item->next; xmpp_free(ctx, item); } } /* free handler stuff * note that userdata is the responsibility of the client * and the handler pointers don't need to be freed since they * are pointers to functions */ hlitem = conn->timed_handlers; while (hlitem) { thli = hlitem; hlitem = hlitem->next; xmpp_free(ctx, thli); } /* id handlers * we have to traverse the hash table freeing list elements * then release the hash table */ iter = hash_iter_new(conn->id_handlers); while ((key = hash_iter_next(iter))) { hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key); while (hlitem) { thli = hlitem; hlitem = hlitem->next; xmpp_free(conn->ctx, thli->id); xmpp_free(conn->ctx, thli); } } hash_iter_release(iter); hash_release(conn->id_handlers); hlitem = conn->handlers; while (hlitem) { thli = hlitem; hlitem = hlitem->next; if (thli->ns) xmpp_free(ctx, thli->ns); if (thli->name) xmpp_free(ctx, thli->name); if (thli->type) xmpp_free(ctx, thli->type); xmpp_free(ctx, thli); } if (conn->stream_error) { xmpp_stanza_release(conn->stream_error->stanza); if (conn->stream_error->text) xmpp_free(ctx, conn->stream_error->text); xmpp_free(ctx, conn->stream_error); } parser_free(conn->parser); if (conn->domain) xmpp_free(ctx, conn->domain); if (conn->jid) xmpp_free(ctx, conn->jid); if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid); if (conn->pass) xmpp_free(ctx, conn->pass); if (conn->stream_id) xmpp_free(ctx, conn->stream_id); if (conn->lang) xmpp_free(ctx, conn->lang); xmpp_free(ctx, conn); released = 1; } return released; }
/* always returns number of bytes written or that would have been * written if the buffer was large enough * return values < 0 indicate some error occurred, * and return values > buflen indicate buffer was not large enough */ static int _render_stanza_recursive(xmpp_stanza_t *stanza, char * const buf, size_t const buflen) { char *ptr = buf; size_t left = buflen; int ret, written; xmpp_stanza_t *child; hash_iterator_t *iter; const char *key; char *tmp; written = 0; if (stanza->type == XMPP_STANZA_UNKNOWN) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TEXT) { if (!stanza->data) return XMPP_EINVOP; tmp = _escape_xml(stanza->ctx, stanza->data); if (tmp == NULL) return XMPP_EMEM; ret = xmpp_snprintf(ptr, left, "%s", tmp); xmpp_free(stanza->ctx, tmp); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* stanza->type == XMPP_STANZA_TAG */ if (!stanza->data) return XMPP_EINVOP; /* write beginning of tag and attributes */ ret = xmpp_snprintf(ptr, left, "<%s", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) { iter = hash_iter_new(stanza->attributes); while ((key = hash_iter_next(iter))) { if (!strcmp(key, "xmlns")) { /* don't output namespace if parent stanza is the same */ if (stanza->parent && stanza->parent->attributes && hash_get(stanza->parent->attributes, key) && !strcmp((char*)hash_get(stanza->attributes, key), (char*)hash_get(stanza->parent->attributes, key))) continue; /* or if this is the stream namespace */ if (!stanza->parent && !strcmp((char*)hash_get(stanza->attributes, key), XMPP_NS_CLIENT)) continue; } tmp = _escape_xml(stanza->ctx, (char *)hash_get(stanza->attributes, key)); if (tmp == NULL) return XMPP_EMEM; ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp); xmpp_free(stanza->ctx, tmp); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } hash_iter_release(iter); } if (!stanza->children) { /* write end if singleton tag */ ret = xmpp_snprintf(ptr, left, "/>"); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* this stanza has child stanzas */ /* write end of start tag */ ret = xmpp_snprintf(ptr, left, ">"); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); /* iterate and recurse over child stanzas */ child = stanza->children; while (child) { ret = _render_stanza_recursive(child, ptr, left); if (ret < 0) return ret; _render_update(&written, buflen, ret, &left, &ptr); child = child->next; } /* write end tag */ ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } } return written; }
/* always returns number of bytes written or that would have been * written if the buffer was large enough * return values < 0 indicate some error occured, * and return values > buflen indicate buffer was not large enough */ static int _render_stanza_recursive(xmpp_stanza_t *stanza, char * const buf, size_t const buflen) { char *ptr = buf; size_t left = buflen; int ret, written; xmpp_stanza_t *child; hash_iterator_t *iter; const char *key; assert(stanza); written = 0; if (stanza->type == XMPP_STANZA_UNKNOWN) return XMPP_EINVOP; if (stanza->type == XMPP_STANZA_TEXT) { if (!stanza->data) return XMPP_EINVOP; assert(stanza->data); ret = xmpp_snprintf(ptr, left, "%s", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* stanza->type == XMPP_STANZA_TAG */ if (!stanza->data) return XMPP_EINVOP; /* write begining of tag and attributes */ assert(stanza->data); ret = xmpp_snprintf(ptr, left, "<%s", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) { iter = hash_iter_new(stanza->attributes); while ((key = hash_iter_next(iter))) { assert(hash_get(stanza->attributes, key)); ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, (char *)hash_get(stanza->attributes, key)); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } hash_iter_release(iter); } if (!stanza->children) { /* write end if singleton tag */ ret = xmpp_snprintf(ptr, left, "/>"); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* this stanza has child stanzas */ /* write end of start tag */ ret = xmpp_snprintf(ptr, left, ">"); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); /* iterate and recurse over child stanzas */ child = stanza->children; while (child) { ret = _render_stanza_recursive(child, ptr, left); if (ret < 0) return ret; _render_update(&written, buflen, ret, &left, &ptr); child = child->next; } /* write end tag */ assert(stanza->data); ret = xmpp_snprintf(ptr, left, "</%s>", stanza->data); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } } return written; }
int main(int argc, char **argv) { xmpp_ctx_t *ctx; hash_t *table, *clone; hash_iterator_t *iter; unsigned int seed; const char *key; char *result; int err = 0; int i; /* initialize random numbers */ if (argc > 2) { /* use a seed from the command line */ seed = (unsigned int)atoi(argv[1]); } else { seed = (unsigned int)clock(); } /* using random seed 'seed' */ srand(seed); /* allocate a default context */ ctx = xmpp_ctx_new(NULL, NULL); if (ctx == NULL) { /* ctx allocation failed! */ return -1; } /* allocate a hash table */ table = hash_new(ctx, TABLESIZE, NULL); if (table == NULL) { /* table allocation failed! */ return 1; } /* test insertion */ for (i = 0; i < nkeys; i++) { err = hash_add(table, keys[i], (void*)values[i]); if (err) return err; } /* test key count */ if (hash_num_keys(table) != nkeys) { /* wrong number of keys in table! */ return 1; } /* test cloning */ clone = hash_clone(table); /* test lookup */ for (i = 0; i < nkeys; i++) { result = hash_get(clone, keys[i]); if (result == NULL) { /* lookup failed! */ return 1; } if (strcmp(values[i], result)) { /* lookup returned incorrect value! */ return 1; } } /* test key iterator */ iter = hash_iter_new(clone); if (iter == NULL) { /* iterator allocation failed! */ return 1; } for (i = 0; i < nkeys; i++) { key = hash_iter_next(iter); printf("key: '%s'\n", key); } key = hash_iter_next(iter); if (key != NULL) { /* extra keys returned! */ return 1; } key = hash_iter_next(iter); if (key != NULL) { /* extra keys returned! */ return 1; } hash_iter_release(iter); /* release the hash table */ hash_release(table); /* test drops */ hash_drop(clone, keys[2]); if (hash_get(clone, keys[2]) != NULL) return 1; hash_drop(clone, keys[1]); hash_drop(clone, keys[4]); if (hash_get(clone, keys[4]) != NULL) return 1; if (hash_get(clone, keys[1]) != NULL) return 1; /* keys 0,3 should still be available */ if (hash_get(clone, keys[0]) == NULL) return 1; if (hash_get(clone, keys[3]) == NULL) return 1; /* release our clone */ hash_release(clone); /* release our library context */ xmpp_ctx_free(ctx); return err; }
/** Release a Strophe connection object. * Decrement the reference count by one for a connection, freeing the * connection object if the count reaches 0. * * @param conn a Strophe connection object * * @return TRUE if the connection object was freed and FALSE otherwise * * @ingroup Connections */ int xmpp_conn_release(xmpp_conn_t * const conn) { xmpp_ctx_t *ctx; list_t *item; xmpp_handler_t *temp; xmpp_handlist_t *hlitem, *thli; hash_iterator_t *iter; const char *key; if (conn->ref > 1) { conn->ref--; return 0; } ctx = conn->ctx; /* remove connection from context's connlist */ item = list_pop_by_data(ctx->connlist, (void *)conn); if (item) xmpp_free(ctx, item); else xmpp_error(ctx, "xmpp", "Connection not in context's list\n"); /* free handler stuff * note that userdata is the responsibility of the client * and the handler pointers don't need to be freed since they * are pointers to functions */ while ((item = list_shift(conn->timed_handlers))) { xmpp_free(ctx, item->data); xmpp_free(ctx, item); } list_destroy(conn->timed_handlers); /* id handlers * we have to traverse the hash table freeing list elements * then release the hash table */ iter = hash_iter_new(conn->id_handlers); while ((key = hash_iter_next(iter))) { hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key); while (hlitem) { thli = hlitem; hlitem = hlitem->next; xmpp_free(ctx, thli->id); xmpp_free(ctx, thli); } } hash_iter_release(iter); hash_release(conn->id_handlers); while ((item = list_shift(conn->handlers))) { temp = (xmpp_handler_t *)item->data; if (temp->ns) xmpp_free(ctx, temp->ns); if (temp->name) xmpp_free(ctx, temp->name); if (temp->type) xmpp_free(ctx, temp->type); xmpp_free(ctx, temp); xmpp_free(ctx, item); } list_destroy(conn->handlers); if (conn->stream_error) { xmpp_stanza_release(conn->stream_error->stanza); if (conn->stream_error->text) xmpp_free(ctx, conn->stream_error->text); xmpp_free(ctx, conn->stream_error); } parser_free(conn->parser); /* free send_queue */ list_destroy(conn->send_queue); if (conn->domain) xmpp_free(ctx, conn->domain); if (conn->jid) xmpp_free(ctx, conn->jid); if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid); if (conn->pass) xmpp_free(ctx, conn->pass); if (conn->stream_id) xmpp_free(ctx, conn->stream_id); if (conn->lang) xmpp_free(ctx, conn->lang); if (conn->tls) tls_free(conn->tls); xmpp_free(ctx, conn); return 1; }