summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2023-09-29 20:24:16 +0300
committerdefanor <defanor@uberspace.net>2023-09-29 20:24:16 +0300
commitccdb748c81abc9bb30b8989e27d22bbbb219f9a0 (patch)
tree295be2ea605c8acbe56116ea3ce119b99e3e0440
parent04e6fd5194481798bc30abc7a690664d5af36aeb (diff)
Add more checks, tests, and documentation
-rw-r--r--README3
-rw-r--r--src/Makefile.am4
-rw-r--r--src/rexmpp.c35
-rw-r--r--src/rexmpp.h2
-rw-r--r--src/rexmpp_base64.h25
-rw-r--r--src/rexmpp_console.c17
-rw-r--r--src/rexmpp_digest.c1
-rw-r--r--src/rexmpp_digest.h41
-rw-r--r--src/rexmpp_dns.c73
-rw-r--r--src/rexmpp_http_upload.c28
-rw-r--r--src/rexmpp_jid.c6
-rw-r--r--src/rexmpp_jingle.c22
-rw-r--r--src/rexmpp_openpgp.c5
-rw-r--r--src/rexmpp_random.c9
-rw-r--r--src/rexmpp_random.h7
-rw-r--r--src/rexmpp_tls.c24
-rw-r--r--src/rexmpp_xml.c8
-rw-r--r--src/rexmpp_xml.h5
-rw-r--r--src/rexmpp_xml_parser.c16
-rw-r--r--tests/Makefile.am25
-rw-r--r--tests/base64.c18
-rw-r--r--tests/send_to_self.c8
-rw-r--r--tests/xml_parse_and_print.c27
-rw-r--r--tests/xml_print_and_parse.c64
24 files changed, 385 insertions, 88 deletions
diff --git a/README b/README
index eb28c13..abeeef0 100644
--- a/README
+++ b/README
@@ -98,8 +98,7 @@ A rough roadmap:
[+] XEP-0176 v1.1: Jingle ICE-UDP Transport Method (uses libnice,
discovers TURN/STUN servers with XEP-0215: External Service
Discovery)
-[+] XEP-0320 v1.0: Use of DTLS-SRTP in Jingle Sessions (uses gnutls
- and libsrtp2)
+[+] XEP-0320 v1.0: Use of DTLS-SRTP in Jingle Sessions
[ ] XEP-0260: Jingle SOCKS5 Bytestreams Transport Method?
[ ] XEP-0391: Jingle Encrypted Transports?
[ ] XEP-0184: Message Delivery Receipts?
diff --git a/src/Makefile.am b/src/Makefile.am
index d027b1b..2dfc581 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -29,12 +29,12 @@ librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML2_CFLAGS) $(EXPAT_CFLAGS) \
$(GSASL_CFLAGS) $(UNBOUND_CFLAGS) $(CARES_CFLAGS) $(GPGME_CFLAGS) \
$(ICU_I18N_CFLAGS) $(LIBGCRYPT_CFLAGS) $(CURL_CFLAGS) \
$(NICE_CFLAGS) $(GLIB_CFLAGS) $(SRTP_CFLAGS) \
- $(PORTAUDIO_CFLAGS) $(OPUS_CFLAGS)
+ $(PORTAUDIO_CFLAGS) $(OPUS_CFLAGS) $(NETTLE_CFLAGS)
librexmpp_la_LIBADD = $(LIBXML2_LIBS) $(EXPAT_LIBS) \
$(GNUTLS_LIBS) $(LIBDANE_LIBS) $(OPENSSL_LIBS) \
$(GSASL_LIBS) $(UNBOUND_LIBS) $(CARES_LIBS) $(GPGME_LIBS) $(ICU_I18N_LIBS) \
$(LIBGCRYPT_LIBS) $(CURL_LIBS) $(NICE_LIBS) $(GLIB_LIBS) $(SRTP_LIBS) \
- $(PORTAUDIO_LIBS) $(OPUS_LIBS)
+ $(PORTAUDIO_LIBS) $(OPUS_LIBS) $(NETTLE_LIBS)
librexmpp_la_LDFLAGS = []
if USE_RUST
diff --git a/src/rexmpp.c b/src/rexmpp.c
index e19386d..e9d20c0 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -14,6 +14,7 @@
#include <arpa/nameser.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <assert.h>
#include "config.h"
@@ -401,6 +402,9 @@ rexmpp_disco_find_feature (rexmpp_t *s,
{
struct rexmpp_feature_search *search =
malloc(sizeof(struct rexmpp_feature_search));
+ if (search == NULL) {
+ return REXMPP_E_MALLOC;
+ }
search->max_requests = max_requests - 1;
search->found = 0;
search->pending = 1;
@@ -700,6 +704,7 @@ void rexmpp_cleanup (rexmpp_t *s) {
if (s->tcp_state == REXMPP_TCP_CONNECTING) {
int sock = rexmpp_tcp_conn_finish(&s->server_connection);
if (sock != -1) {
+ rexmpp_log(s, LOG_DEBUG, "TCP disconnected");
close(sock);
}
s->tcp_state = REXMPP_TCP_NONE;
@@ -855,15 +860,6 @@ const char *jid_bare_to_host (const char *jid_bare) {
return NULL;
}
-char *rexmpp_gen_id (rexmpp_t *s) {
- (void)s;
- char buf_raw[18], *buf_base64 = NULL;
- size_t buf_base64_len = 0;
- rexmpp_random_buf(buf_raw, 18);
- rexmpp_base64_to(buf_raw, 18, &buf_base64, &buf_base64_len);
- return buf_base64;
-}
-
rexmpp_xml_t *rexmpp_xml_set_delay (rexmpp_t *s, rexmpp_xml_t *node) {
if (rexmpp_xml_find_child (node, NULL, "delay")) {
return node;
@@ -1095,6 +1091,8 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
last = last->next;
}
if (i >= s->iq_queue_size && s->iq_queue_size > 0) {
+ assert(prev != NULL);
+ assert(last != NULL);
rexmpp_log(s, LOG_WARNING,
"The IQ queue limit is reached, giving up on the oldest IQ.");
prev->next = NULL;
@@ -1103,7 +1101,7 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
rexmpp_xml_t *iq_stanza =
rexmpp_xml_new_elem("iq", "jabber:client");
- rexmpp_xml_add_id(s, iq_stanza);
+ rexmpp_xml_add_id(iq_stanza);
rexmpp_xml_add_attr(iq_stanza, "type", type);
if (to != NULL) {
rexmpp_xml_add_attr(iq_stanza, "to", to);
@@ -1113,6 +1111,9 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
}
rexmpp_xml_add_child(iq_stanza, payload);
rexmpp_iq_t *iq = malloc(sizeof(rexmpp_iq_t));
+ if (iq == NULL) {
+ return REXMPP_E_MALLOC;
+ }
iq->request = rexmpp_xml_clone(iq_stanza);
iq->cb = cb;
iq->cb_data = cb_data;
@@ -1263,9 +1264,9 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
} else if (chunk_raw_len == 0) {
if (tls_was_active) {
s->tls_state = REXMPP_TLS_CLOSED;
- rexmpp_log(s, LOG_INFO, "TLS disconnected");
+ rexmpp_log(s, LOG_DEBUG, "TLS disconnected");
}
- rexmpp_log(s, LOG_INFO, "TCP disconnected");
+ rexmpp_log(s, LOG_DEBUG, "TCP disconnected");
rexmpp_cleanup(s);
if (s->stream_state == REXMPP_STREAM_READY ||
s->stream_state == REXMPP_STREAM_ERROR_RECONNECT) {
@@ -1619,7 +1620,7 @@ void rexmpp_stream_is_ready(rexmpp_t *s) {
}
rexmpp_xml_t *presence =
rexmpp_xml_new_elem("presence", "jabber:client");
- rexmpp_xml_add_id(s, presence);
+ rexmpp_xml_add_id(presence);
char *caps_hash = rexmpp_capabilities_hash(s, rexmpp_disco_info(s));
if (caps_hash != NULL) {
@@ -2042,7 +2043,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, rexmpp_xml_t *elem) {
strcmp(autojoin, "1") == 0) {
rexmpp_xml_t *presence =
rexmpp_xml_new_elem("presence", "jabber:client");
- rexmpp_xml_add_id(s, presence);
+ rexmpp_xml_add_id(presence);
rexmpp_xml_add_attr(presence, "from",
s->assigned_jid.full);
rexmpp_xml_t *nick =
@@ -2253,6 +2254,7 @@ void rexmpp_sax_start_elem_ns (rexmpp_t *s,
strcmp(namespace, "http://etherx.jabber.org/streams") == 0) {
rexmpp_log(s, LOG_DEBUG, "stream start");
s->stream_state = REXMPP_STREAM_NEGOTIATION;
+ rexmpp_xml_attribute_free_list(attributes);
return;
}
@@ -2326,7 +2328,7 @@ rexmpp_err_t rexmpp_stop (rexmpp_t *s) {
if (s->stream_state == REXMPP_STREAM_READY) {
rexmpp_xml_t *presence =
rexmpp_xml_new_elem("presence", "jabber:client");
- rexmpp_xml_add_id(s, presence);
+ rexmpp_xml_add_id(presence);
rexmpp_xml_add_attr(presence, "type", "unavailable");
rexmpp_send(s, presence);
}
@@ -2549,10 +2551,11 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
s->tls_state == REXMPP_TLS_CLOSING) {
rexmpp_tls_err_t err = rexmpp_tls_disconnect(s, s->tls);
if (err == REXMPP_TLS_SUCCESS) {
+ rexmpp_log(s, LOG_DEBUG, "TLS disconnected");
s->tls_state = REXMPP_TLS_INACTIVE;
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_CLOSED;
- } else {
+ } else if (err != REXMPP_TLS_E_AGAIN) {
s->tls_state = REXMPP_TLS_ERROR;
return REXMPP_E_TLS;
}
diff --git a/src/rexmpp.h b/src/rexmpp.h
index 8cc78ed..e9d833f 100644
--- a/src/rexmpp.h
+++ b/src/rexmpp.h
@@ -534,8 +534,6 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...);
*/
char *rexmpp_get_name (rexmpp_t *s, const char *jid_str);
-char *rexmpp_gen_id (rexmpp_t *s);
-
/**
@brief Finds a PEP event.
@param[in] s ::rexmpp
diff --git a/src/rexmpp_base64.h b/src/rexmpp_base64.h
index e681ed9..d11cd91 100644
--- a/src/rexmpp_base64.h
+++ b/src/rexmpp_base64.h
@@ -10,5 +10,26 @@
#include <stddef.h>
-int rexmpp_base64_to (const char *in, size_t in_len, char **out, size_t *out_len);
-int rexmpp_base64_from (const char *in, size_t in_len, char **out, size_t *out_len);
+/**
+ @brief Encodes data in Base64
+ @param[in] in Data to encode
+ @param[in] in_len Length of the input data
+ @param[out] out A pointer to the output buffer; its memory will be
+ allocated by the function, the caller receives ownership over it
+ @param[out] out_len Length of the produced Base64-encoded string
+ @returns 0 on success, a non-zero value otherwise
+*/
+int rexmpp_base64_to (const char *in, size_t in_len,
+ char **out, size_t *out_len);
+
+/**
+ @brief Decodes data from Base64
+ @param[in] in Data to decode
+ @param[in] in_len Length of the input data
+ @param[out] out A pointer to the output buffer; its memory will be
+ allocated by the function, the caller receives ownership over it
+ @param[out] out_len Length of the decoded string
+ @returns 0 on success, a non-zero value otherwise
+*/
+int rexmpp_base64_from (const char *in, size_t in_len,
+ char **out, size_t *out_len);
diff --git a/src/rexmpp_console.c b/src/rexmpp_console.c
index 2d2347f..64e5493 100644
--- a/src/rexmpp_console.c
+++ b/src/rexmpp_console.c
@@ -12,6 +12,7 @@
#include <string.h>
#include <stdlib.h>
+#include <stdarg.h>
#include "rexmpp.h"
#include "rexmpp_xml.h"
@@ -490,7 +491,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
msg_text = jid_str + strlen(jid_str) + 1;
rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
- rexmpp_xml_add_id(s, msg);
+ rexmpp_xml_add_id(msg);
rexmpp_xml_add_attr(msg, "to", jid.full);
rexmpp_xml_add_attr(msg, "type", "chat");
rexmpp_xml_t *body = rexmpp_xml_new_elem("body", NULL);
@@ -527,7 +528,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
free(b64);
rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
- rexmpp_xml_add_id(s, msg);
+ rexmpp_xml_add_id(msg);
rexmpp_xml_add_attr(msg, "to", jid.full);
rexmpp_xml_add_attr(msg, "type", "chat");
rexmpp_xml_add_child(msg, openpgp);
@@ -549,7 +550,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
msg_text = jid_str + strlen(jid_str) + 1;
rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
- rexmpp_xml_add_id(s, msg);
+ rexmpp_xml_add_id(msg);
rexmpp_xml_add_attr(msg, "to", jid.full);
rexmpp_xml_add_attr(msg, "type", "groupchat");
rexmpp_xml_t *body = rexmpp_xml_new_elem("body", NULL);
@@ -573,7 +574,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
snprintf(full_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
jid.bare, word);
presence = rexmpp_xml_new_elem("presence", "jabber:client");
- rexmpp_xml_add_id(s, presence);
+ rexmpp_xml_add_id(presence);
rexmpp_xml_add_attr(presence, "from", s->assigned_jid.full);
rexmpp_xml_add_attr(presence, "to", full_jid);
rexmpp_xml_t *x =
@@ -598,7 +599,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
snprintf(full_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
jid.bare, word);
presence = rexmpp_xml_new_elem("presence", "jabber:client");
- rexmpp_xml_add_id(s, presence);
+ rexmpp_xml_add_id(presence);
rexmpp_xml_add_attr(presence, "from", s->assigned_jid.full);
rexmpp_xml_add_attr(presence, "to", full_jid);
rexmpp_xml_add_attr(presence, "type", "unavailable");
@@ -683,7 +684,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
presence = rexmpp_xml_new_elem("presence", "jabber:client");
- rexmpp_xml_add_id(s, presence);
+ rexmpp_xml_add_id(presence);
rexmpp_xml_add_attr(presence, "to", word);
rexmpp_xml_add_attr(presence, "type", "subscribe");
rexmpp_send(s, presence);
@@ -694,7 +695,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
presence = rexmpp_xml_new_elem("presence", "jabber:client");
- rexmpp_xml_add_id(s, presence);
+ rexmpp_xml_add_id(presence);
rexmpp_xml_add_attr(presence, "to", word);
rexmpp_xml_add_attr(presence, "type", "subscribed");
rexmpp_send(s, presence);
@@ -705,7 +706,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
presence = rexmpp_xml_new_elem("presence", "jabber:client");
- rexmpp_xml_add_id(s, presence);
+ rexmpp_xml_add_id(presence);
rexmpp_xml_add_attr(presence, "to", word);
rexmpp_xml_add_attr(presence, "type", "unsubscribed");
rexmpp_send(s, presence);
diff --git a/src/rexmpp_digest.c b/src/rexmpp_digest.c
index e123636..c1dd436 100644
--- a/src/rexmpp_digest.c
+++ b/src/rexmpp_digest.c
@@ -87,7 +87,6 @@ int rexmpp_digest_init (rexmpp_digest_t *ctx, rexmpp_digest_algorithm algo) {
int rexmpp_digest_update (rexmpp_digest_t *ctx, const void *in, size_t len) {
#if defined(HAVE_GCRYPT)
- printf("wirte: %s %lu\n", (const char*)in, len);
gcry_md_write(*ctx, in, len);
#elif defined(HAVE_NETTLE)
ctx->nh->update(ctx->nh_ctx, len, in);
diff --git a/src/rexmpp_digest.h b/src/rexmpp_digest.h
index 77f198a..5001e12 100644
--- a/src/rexmpp_digest.h
+++ b/src/rexmpp_digest.h
@@ -31,14 +31,55 @@ typedef struct rexmpp_digest rexmpp_digest_t;
typedef EVP_MD_CTX* rexmpp_digest_t;
#endif
+/**
+ @brief Finds the digest length for a given algorithm.
+ @param[in] algo An algorithm.
+ @returns Digest length in bytes.
+*/
size_t rexmpp_digest_len (rexmpp_digest_algorithm algo);
+
+/**
+ @brief Computes a digest for a buffer.
+ @param[in] algo An algorithm.
+ @param[in] in Input data.
+ @param[in] in_len Input data length.
+ @param[out] out Output buffer.
+ @param[in] out_len Output buffer length.
+ @returns 0 on success, non-zero on failure.
+*/
int rexmpp_digest_buffer (rexmpp_digest_algorithm algo,
const void *in,
size_t in_len,
void *out,
size_t out_len);
+
+/**
+ @brief Initializes a digest context.
+ @param[out] ctx Pointer to an allocated ::rexmpp_digest_t context
+ to initialize.
+ @param[in] algo An algorithm to use.
+ @returns 0 on success, non-zero on failure.
+*/
int rexmpp_digest_init (rexmpp_digest_t *ctx, rexmpp_digest_algorithm algo);
+
+/**
+ @brief Updates a digest computation.
+ @param[in,out] ctx Context pointer.
+ @param[in] in Input data.
+ @param[in] len Length of the input buffer.
+ @returns 0 on success, non-zero on failure.
+*/
int rexmpp_digest_update (rexmpp_digest_t *ctx, const void *in, size_t len);
+
+/**
+ @brief Finishes a digest computation, freeing the context and
+ providing the output.
+ @param[in,out] ctx Context pointer.
+ @param[out] out A place to write the computed digest into, can be
+ NULL to just free the context.
+ @param[in] len Length of the allocated output buffer.
+ @returns 0 on success, non-zero on failure.
+*/
int rexmpp_digest_finish (rexmpp_digest_t *ctx, void *out, size_t len);
#endif
diff --git a/src/rexmpp_dns.c b/src/rexmpp_dns.c
index d13472e..d56aa10 100644
--- a/src/rexmpp_dns.c
+++ b/src/rexmpp_dns.c
@@ -85,17 +85,33 @@ void rexmpp_dns_result_free (rexmpp_dns_result_t *result) {
rexmpp_dns_result_t *result_from_hostent (struct hostent *hostinfo) {
rexmpp_dns_result_t *r = malloc(sizeof(rexmpp_dns_result_t));
+ if (r == NULL) {
+ return NULL;
+ }
r->secure = 0;
int i, size = 0;
while (hostinfo->h_addr_list[size] != NULL) {
size++;
}
r->data = malloc(sizeof(void *) * (size + 1));
+ if (r->data == NULL) {
+ free(r);
+ return NULL;
+ }
r->len = malloc(sizeof(int) * size);
+ if (r->len == NULL) {
+ free(r->data);
+ free(r);
+ return NULL;
+ }
for (i = 0; i < size; i++) {
r->len[i] = hostinfo->h_length;
r->data[i] = malloc(r->len[i]);
- memcpy(r->data[i], hostinfo->h_addr_list[i], hostinfo->h_length);
+ if (r->data[i] != NULL) {
+ memcpy(r->data[i], hostinfo->h_addr_list[i], hostinfo->h_length);
+ } else {
+ return r;
+ }
}
r->data[size] = NULL;
return r;
@@ -257,20 +273,54 @@ void rexmpp_dns_cb (void *ptr,
size++;
}
rexmpp_dns_result_t *res = malloc(sizeof(rexmpp_dns_result_t));
- res->data = malloc(sizeof(void *) * (size + 1));
+ if (res == NULL) {
+ rexmpp_log(s, LOG_DEBUG,
+ "Failed to allocate memory for a DNS resolution result");
+ ub_resolve_free(result);
+ d->cb(s, d->ptr, NULL);
+ free(d);
+ return;
+ }
+ res->data = calloc((size + 1), sizeof(void *));
+ if (res->data == NULL) {
+ rexmpp_log(s, LOG_DEBUG,
+ "Failed to allocate memory for a DNS resolution result's data");
+ free(res);
+ ub_resolve_free(result);
+ d->cb(s, d->ptr, NULL);
+ free(d);
+ return;
+ }
res->len = malloc(sizeof(int) * size);
+ if (res->len == NULL) {
+ rexmpp_log(s, LOG_DEBUG,
+ "Failed to allocate memory for a DNS resolution result's len");
+ free(res->data);
+ free(res);
+ ub_resolve_free(result);
+ d->cb(s, d->ptr, NULL);
+ free(d);
+ return;
+ }
for (i = 0; i < size; i++) {
if (result->qtype == 33) {
/* SRV */
res->len[i] = sizeof(rexmpp_dns_srv_t);
res->data[i] = malloc(res->len[i]);
+ if (res->data[i] == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to allocate memory for an SRV record");
+ d->cb(s, d->ptr, res);
+ rexmpp_dns_result_free(res);
+ free(d);
+ return;
+ }
int err = rexmpp_parse_srv(result->data[i], result->len[i],
(rexmpp_dns_srv_t*)res->data[i]);
if (err) {
rexmpp_log(s, LOG_WARNING, "Failed to parse an SRV record");
- res->data[i + 1] = NULL;
+ d->cb(s, d->ptr, res);
rexmpp_dns_result_free(res);
- d->cb(s, d->ptr, NULL);
free(d);
return;
}
@@ -278,7 +328,12 @@ void rexmpp_dns_cb (void *ptr,
/* Non-SRV, for now that's just A or AAAA */
res->len[i] = result->len[i];
res->data[i] = malloc(res->len[i]);
- memcpy(res->data[i], result->data[i], res->len[i]);
+ if (res->data[i] == NULL) {
+ rexmpp_log(s, LOG_WARNING,
+ "Failed to allocate memory for a DNS result's data record");
+ } else {
+ memcpy(res->data[i], result->data[i], res->len[i]);
+ }
}
}
res->data[size] = NULL;
@@ -352,6 +407,10 @@ int rexmpp_dns_resolve (rexmpp_t *s,
#if defined(USE_UNBOUND)
struct rexmpp_dns_query_cb_data *d =
malloc(sizeof(struct rexmpp_dns_query_cb_data));
+ if (d == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to allocate memory for a DNS query");
+ return 1;
+ }
d->s = s;
d->cb = callback;
d->ptr = ptr;
@@ -365,6 +424,10 @@ int rexmpp_dns_resolve (rexmpp_t *s,
#elif defined(USE_CARES)
struct rexmpp_dns_query_cb_data *d =
malloc(sizeof(struct rexmpp_dns_query_cb_data));
+ if (d == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to allocate memory for a DNS query");
+ return 1;
+ }
d->s = s;
d->cb = callback;
d->ptr = ptr;
diff --git a/src/rexmpp_http_upload.c b/src/rexmpp_http_upload.c
index 4d8c631..5d13f97 100644
--- a/src/rexmpp_http_upload.c
+++ b/src/rexmpp_http_upload.c
@@ -73,13 +73,19 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s,
if (header_name != NULL) {
const char *header_str = rexmpp_xml_text_child(header);
if (header_str != NULL) {
- size_t full_header_str_len = strlen(header_name) + 3 + strlen(header_str);
+ size_t full_header_str_len =
+ strlen(header_name) + 3 + strlen(header_str);
char *full_header_str = malloc(full_header_str_len);
- snprintf(full_header_str, full_header_str_len, "%s: %s",
- header_name, header_str);
- task->http_headers =
- curl_slist_append(task->http_headers, full_header_str);
- free(full_header_str);
+ if (full_header_str != NULL) {
+ snprintf(full_header_str, full_header_str_len, "%s: %s",
+ header_name, header_str);
+ task->http_headers =
+ curl_slist_append(task->http_headers, full_header_str);
+ free(full_header_str);
+ } else {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to allocate memory for a header");
+ }
}
}
header = rexmpp_xml_next_elem_sibling(header);
@@ -137,8 +143,18 @@ rexmpp_http_upload (rexmpp_t *s,
http_upload_cb cb,
void *cb_data)
{
+ if (fname == NULL) {
+ rexmpp_log(s, LOG_ERR, "No file name is provided");
+ fclose(fh);
+ return REXMPP_E_PARAM;
+ }
struct rexmpp_http_upload_task *task =
malloc(sizeof(struct rexmpp_http_upload_task));
+ if (task == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to allocate memory for an upload task");
+ fclose(fh);
+ return REXMPP_E_MALLOC;
+ }
task->fname = strdup(fname);
task->fsize = fsize;
task->fh = fh;
diff --git a/src/rexmpp_jid.c b/src/rexmpp_jid.c
index 522a492..f1c371c 100644
--- a/src/rexmpp_jid.c
+++ b/src/rexmpp_jid.c
@@ -64,8 +64,10 @@ int rexmpp_jid_parse (const char *str, struct rexmpp_jid *jid) {
jid->local[local_len] = '\0';
strncpy(jid->domain, domain, domain_len);
jid->domain[domain_len] = '\0';
- strncpy(jid->resource, resource, resource_len);
- jid->resource[resource_len] = '\0';
+ if (resource != NULL) {
+ strncpy(jid->resource, resource, resource_len);
+ jid->resource[resource_len] = '\0';
+ }
return 0;
}
diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c
index a147a5f..9e16d59 100644
--- a/src/rexmpp_jingle.c
+++ b/src/rexmpp_jingle.c
@@ -480,7 +480,7 @@ rexmpp_jingle_session_create (rexmpp_t *s,
#endif /* HAVE_POUS */
#endif /* ENABLE_CALLS */
if (! rexmpp_jingle_session_add(s, sess)) {
- rexmpp_jingle_session_destroy(sess);
+ /* The session is destroyed by rexmpp_jingle_session_add */
sess = NULL;
}
} else {
@@ -684,8 +684,8 @@ rexmpp_jingle_send_file (rexmpp_t *s,
len = fread(buf, 1, 4096, fh);
}
- char *sid = rexmpp_gen_id(s);
- char *ibb_sid = rexmpp_gen_id(s);
+ char *sid = rexmpp_random_id();
+ char *ibb_sid = rexmpp_random_id();
rexmpp_xml_t *jingle =
rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
@@ -1161,7 +1161,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
rexmpp_xml_add_attr(candidate, "component", buf);
rexmpp_xml_add_attr(candidate, "foundation", c->foundation);
rexmpp_xml_add_attr(candidate, "generation", "0");
- char *cid = rexmpp_gen_id(sess->s);
+ char *cid = rexmpp_random_id();
rexmpp_xml_add_attr(candidate, "id", cid);
free(cid);
nice_address_to_string(&c->addr, buf);
@@ -1767,7 +1767,7 @@ rexmpp_jingle_call (rexmpp_t *s,
const char *jid)
{
rexmpp_jingle_session_t *sess =
- rexmpp_jingle_session_create(s, strdup(jid), rexmpp_gen_id(s),
+ rexmpp_jingle_session_create(s, strdup(jid), rexmpp_random_id(),
REXMPP_JINGLE_SESSION_MEDIA, 1);
rexmpp_jingle_ice_agent_init(sess);
rexmpp_jingle_discover_turn(s, sess);
@@ -1896,11 +1896,13 @@ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) {
rexmpp_jingle_session_t *sess =
rexmpp_jingle_session_create(s, strdup(from_jid), strdup(sid),
REXMPP_JINGLE_SESSION_MEDIA, 0);
- sess->rtcp_mux =
- (rexmpp_xml_find_child(rtp_description,
- "urn:xmpp:jingle:apps:rtp:1",
- "rtcp-mux") != NULL);
- sess->initiate = rexmpp_xml_clone(jingle);
+ if (sess != NULL) {
+ sess->rtcp_mux =
+ (rexmpp_xml_find_child(rtp_description,
+ "urn:xmpp:jingle:apps:rtp:1",
+ "rtcp-mux") != NULL);
+ sess->initiate = rexmpp_xml_clone(jingle);
+ }
#endif /* ENABLE_CALLS */
} else if (file_description == NULL &&
rtp_description == NULL) {
diff --git a/src/rexmpp_openpgp.c b/src/rexmpp_openpgp.c
index 4ef7335..222ae6b 100644
--- a/src/rexmpp_openpgp.c
+++ b/src/rexmpp_openpgp.c
@@ -732,6 +732,11 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
/* Add keys for encryption. */
allocated = 8;
keys = malloc(sizeof(gpgme_key_t *) * allocated);
+ if (keys == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to allocate memory for keys");
+ rexmpp_xml_free(elem);
+ return NULL;
+ }
keys[0] = NULL;
rexmpp_openpgp_add_keys(s, s->initial_jid.bare, &keys, &nkeys, &allocated);
for (i = 0; recipients[i] != NULL; i++) {
diff --git a/src/rexmpp_random.c b/src/rexmpp_random.c
index 8efabd3..11e2b73 100644
--- a/src/rexmpp_random.c
+++ b/src/rexmpp_random.c
@@ -7,6 +7,7 @@
*/
#include "config.h"
+#include "rexmpp_base64.h"
#ifdef HAVE_GCRYPT
#include <gcrypt.h>
@@ -23,3 +24,11 @@ void rexmpp_random_buf (void *buf, size_t len) {
arc4random_buf(buf, len);
#endif
}
+
+char *rexmpp_random_id () {
+ char buf_raw[18], *buf_base64 = NULL;
+ size_t buf_base64_len = 0;
+ rexmpp_random_buf(buf_raw, 18);
+ rexmpp_base64_to(buf_raw, 18, &buf_base64, &buf_base64_len);
+ return buf_base64;
+}
diff --git a/src/rexmpp_random.h b/src/rexmpp_random.h
index 1b21001..b0f1978 100644
--- a/src/rexmpp_random.h
+++ b/src/rexmpp_random.h
@@ -19,4 +19,11 @@
*/
void rexmpp_random_buf (void *buf, size_t len);
+/**
+ @brief Generates a random ASCII identifier.
+ @returns A null-terminated string, which must be freed by the
+ caller.
+*/
+char *rexmpp_random_id ();
+
#endif
diff --git a/src/rexmpp_tls.c b/src/rexmpp_tls.c
index c8decc0..e483a2c 100644
--- a/src/rexmpp_tls.c
+++ b/src/rexmpp_tls.c
@@ -61,6 +61,10 @@ rexmpp_tls_err_t rexmpp_process_openssl_ret (rexmpp_t *s,
rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls) {
rexmpp_tls_t *tls_ctx = malloc(sizeof(rexmpp_tls_t));
+ if (tls_ctx == NULL) {
+ rexmpp_log(s, LOG_CRIT, "Failed to allocate memory for a TLS context");
+ return NULL;
+ }
#if defined(USE_GNUTLS)
(void)dtls;
int err;
@@ -71,6 +75,7 @@ rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls) {
if (err) {
rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s",
gnutls_strerror(err));
+ free(tls_ctx);
return NULL;
}
if (! dtls) {
@@ -79,6 +84,7 @@ rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls) {
if (err < 0) {
rexmpp_log(s, LOG_CRIT, "Certificates loading error: %s",
gnutls_strerror(err));
+ free(tls_ctx);
return NULL;
}
@@ -91,6 +97,7 @@ rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls) {
: TLS_method());
if (tls_ctx->openssl_ctx == NULL) {
rexmpp_log(s, LOG_CRIT, "OpenSSL context creation error");
+ free(tls_ctx);
return NULL;
}
SSL_CTX_set_verify(tls_ctx->openssl_ctx, SSL_VERIFY_PEER, NULL);
@@ -99,6 +106,7 @@ rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls) {
"Failed to set default verify paths for OpenSSL context");
SSL_CTX_free(tls_ctx->openssl_ctx);
tls_ctx->openssl_ctx = NULL;
+ free(tls_ctx);
return NULL;
}
#else
@@ -149,7 +157,7 @@ void rexmpp_tls_session_free (rexmpp_tls_t *tls_ctx) {
}
tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE;
#else
- (void)s;
+ (void)tls_ctx;
#endif
}
@@ -504,6 +512,8 @@ rexmpp_tls_disconnect (rexmpp_t *s, rexmpp_tls_t *tls_ctx) {
int ret = gnutls_bye(tls_ctx->gnutls_session, GNUTLS_SHUT_RDWR);
if (ret == GNUTLS_E_SUCCESS) {
return REXMPP_TLS_SUCCESS;
+ } else if (ret == GNUTLS_E_AGAIN) {
+ return REXMPP_TLS_E_AGAIN;
} else {
rexmpp_log(s, LOG_WARNING, "Failed to close TLS connection: %s",
gnutls_strerror(ret));
@@ -519,6 +529,7 @@ rexmpp_tls_disconnect (rexmpp_t *s, rexmpp_tls_t *tls_ctx) {
"rexmpp_tls_disconnect", ret);
}
#else
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
@@ -558,8 +569,8 @@ rexmpp_tls_srtp_get_keys (rexmpp_t *s,
(void)tls_ctx;
(void)key_len;
(void)salt_len;
- (void)client_key_wsalt;
- (void)server_key_wsalt;
+ (void)key_mat;
+ rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return -1;
#endif
}
@@ -598,6 +609,7 @@ rexmpp_tls_send (rexmpp_t *s,
(void)data;
(void)data_size;
(void)written;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
@@ -635,6 +647,7 @@ rexmpp_tls_recv (rexmpp_t *s,
(void)data;
(void)data_size;
(void)received;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
@@ -721,6 +734,7 @@ rexmpp_tls_set_x509_key_file (rexmpp_t *s,
#else
(void)cert_file;
(void)key_file;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
@@ -751,6 +765,7 @@ rexmpp_tls_set_x509_trust_file (rexmpp_t *s,
return REXMPP_TLS_SUCCESS;
#else
(void)trust_file;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
@@ -798,12 +813,13 @@ int rexmpp_tls_peer_fp (rexmpp_t *s,
fp_str[*fp_size * 3 - 1] = 0;
return 0;
#else
- (void)s;
(void)tls_ctx;
(void)algo_str;
(void)raw_fp;
(void)fp_str;
(void)fp_size;
+ rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
+ return -1;
#endif
}
diff --git a/src/rexmpp_xml.c b/src/rexmpp_xml.c
index 08e11dd..96b283d 100644
--- a/src/rexmpp_xml.c
+++ b/src/rexmpp_xml.c
@@ -12,6 +12,7 @@
#include "rexmpp.h"
#include "rexmpp_utf8.h"
#include "rexmpp_xml.h"
+#include "rexmpp_random.h"
#ifndef USE_RUST
void rexmpp_xml_qname_free (rexmpp_xml_qname_t *qname) {
@@ -449,10 +450,9 @@ char *rexmpp_xml_serialize (const rexmpp_xml_t *node, int pretty) {
}
rexmpp_xml_t *
-rexmpp_xml_add_id (rexmpp_t *s,
- rexmpp_xml_t *node)
+rexmpp_xml_add_id (rexmpp_xml_t *node)
{
- char *buf = rexmpp_gen_id(s);
+ char *buf = rexmpp_random_id();
if (buf == NULL) {
return NULL;
}
@@ -557,10 +557,12 @@ rexmpp_xml_t *rexmpp_xml_read_fd (FILE *fd) {
size_t init_len = 0;
ssize_t buf_len = 0;
do {
+ buf = NULL;
buf_len = getline(&buf, &init_len, fd);
if (buf_len > 0) {
rexmpp_xml_parser_feed(parser, buf, buf_len, 0);
}
+ free(buf);
} while (buf_len > 0 &&
! (builder.root != NULL && builder.current == NULL) );
diff --git a/src/rexmpp_xml.h b/src/rexmpp_xml.h
index d5a9af1..8f6d974 100644
--- a/src/rexmpp_xml.h
+++ b/src/rexmpp_xml.h
@@ -9,6 +9,8 @@
#ifndef REXMPP_XML_H
#define REXMPP_XML_H
+#include <stdio.h>
+
typedef struct rexmpp_xml_qname rexmpp_xml_qname_t;
typedef struct rexmpp_xml_attribute rexmpp_xml_attr_t;
typedef struct rexmpp_xml_node rexmpp_xml_t;
@@ -139,8 +141,7 @@ int rexmpp_xml_add_attr_ns (rexmpp_xml_t *node,
composition.
*/
rexmpp_xml_t *
-rexmpp_xml_add_id (rexmpp_t *s,
- rexmpp_xml_t *node);
+rexmpp_xml_add_id (rexmpp_xml_t *node);
/**
@brief A helper function for XML serialisation.
diff --git a/src/rexmpp_xml_parser.c b/src/rexmpp_xml_parser.c
index e00c70c..3f485d7 100644
--- a/src/rexmpp_xml_parser.c
+++ b/src/rexmpp_xml_parser.c
@@ -41,13 +41,15 @@ void rexmpp_xml_sax_elem_start (rexmpp_xml_parser_ctx_t ctx,
for (i = nb_attributes - 1; i >= 0; i--) {
size_t attr_len = attributes[i * 5 + 4] - attributes[i * 5 + 3];
char *attr_val = malloc(attr_len + 1);
- attr_val[attr_len] = '\0';
- strncpy(attr_val, attributes[i * 5 + 3], attr_len);
- rexmpp_xml_attr_t *attr =
- rexmpp_xml_attr_new(attributes[i * 5], NULL, attr_val);
- free(attr_val);
- attr->next = attrs;
- attrs = attr;
+ if (attr_val != NULL) {
+ attr_val[attr_len] = '\0';
+ strncpy(attr_val, attributes[i * 5 + 3], attr_len);
+ rexmpp_xml_attr_t *attr =
+ rexmpp_xml_attr_new(attributes[i * 5], NULL, attr_val);
+ free(attr_val);
+ attr->next = attrs;
+ attrs = attr;
+ }
}
ctx->handlers->elem_start(ctx->user_data, localname, URI, attrs);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 22290a5..93c0b82 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,19 +1,24 @@
AM_CFLAGS = -Werror -Wall -Wextra -pedantic -std=gnu99
-COMMON_FLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) \
+COMMON_FLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) $(EXPAT_CFLAGS) \
$(GNUTLS_CFLAGS) $(LIBDANE_CFLAGS) $(OPENSSL_CFLAGS) \
$(GSASL_CFLAGS) $(UNBOUND_CFLAGS) $(CARES_CFLAGS) $(GPGME_CFLAGS) \
$(ICU_I18N_CFLAGS) $(LIBGCRYPT_CFLAGS) $(CURL_CFLAGS) \
$(NICE_CFLAGS) $(GLIB_CFLAGS) $(SRTP_CFLAGS) \
- -I$(top_srcdir)/src
-COMMON_LIBS = $(LIBXML_LIBS) \
- $(GNUTLS_LIBS) $(LIBDANE_LIBS) $(OPENSSL_LIBS) \
- $(GSASL_LIBS) $(UNBOUND_LIBS) $(CARES_LIBS) $(GPGME_LIBS) $(ICU_I18N_LIBS) \
- $(LIBGCRYPT_LIBS) $(CURL_LIBS) $(NICE_LIBS) $(GLIB_LIBS) $(SRTP_LIBS) \
- -L$(top_builddir)/src -lrexmpp
+ $(PORTAUDIO_CFLAGS) $(OPUS_CFLAGS) $(NETTLE_CFLAGS)
+COMMON_LDADD = -L$(top_builddir)/src -lrexmpp
send_to_self_CFLAGS = $(COMMON_FLAGS)
-send_to_self_LDADD = $(COMMON_LIBS)
+xml_parse_and_print_CFLAGS = $(COMMON_FLAGS)
+xml_print_and_parse_CFLAGS = $(COMMON_FLAGS)
+base64_CFLAGS = $(COMMON_FLAGS)
-check_PROGRAMS = send_to_self
-TESTS = send_to_self
+send_to_self_LDADD = $(COMMON_LDADD)
+xml_parse_and_print_LDADD = $(COMMON_LDADD)
+xml_print_and_parse_LDADD = $(COMMON_LDADD)
+base64_LDADD = $(COMMON_LDADD)
+
+check_PROGRAMS = send_to_self \
+ xml_parse_and_print xml_print_and_parse base64
+TESTS = send_to_self \
+ xml_parse_and_print xml_print_and_parse base64
diff --git a/tests/base64.c b/tests/base64.c
new file mode 100644
index 0000000..b69ba0e
--- /dev/null
+++ b/tests/base64.c
@@ -0,0 +1,18 @@
+#include <string.h>
+#include "rexmpp_base64.h"
+
+int main () {
+ char *original_plain = "test string";
+ char *original_base64 = "dGVzdCBzdHJpbmc=";
+ char *encoded, *decoded;
+ size_t encoded_len, decoded_len;
+ if (rexmpp_base64_to(original_plain, strlen(original_plain),
+ &encoded, &encoded_len)) {
+ return -1;
+ }
+ if (rexmpp_base64_from(original_base64, strlen(original_base64),
+ &decoded, &decoded_len)) {
+ return -1;
+ }
+ return strcmp(original_plain, decoded) || strcmp(original_base64, encoded);
+}
diff --git a/tests/send_to_self.c b/tests/send_to_self.c
index b5d6d7e..2fcb80f 100644
--- a/tests/send_to_self.c
+++ b/tests/send_to_self.c
@@ -16,7 +16,6 @@ that it's the expected message.
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
-#include <gsasl.h>
#include <rexmpp.h>
#include <rexmpp_sasl.h>
@@ -89,9 +88,7 @@ int my_xml_out_cb (rexmpp_t *s, rexmpp_xml_t *node) {
return 0;
}
-int main (int argc, char **argv) {
- (void)argc;
- (void)argv;
+int main () {
jid = getenv("JID");
pass = getenv("PASS");
char *tls_policy = getenv("TLS_POLICY");
@@ -143,8 +140,7 @@ int main (int argc, char **argv) {
if (stage == TEST_CONNECTING && s.stream_state == REXMPP_STREAM_READY) {
rexmpp_xml_t *msg =
- rexmpp_xml_add_id(&s,
- rexmpp_xml_new_elem("message", "jabber:client"));
+ rexmpp_xml_add_id(rexmpp_xml_new_elem("message", "jabber:client"));
rexmpp_xml_add_attr(msg, "to", jid);
rexmpp_xml_add_attr(msg, "type", "chat");
rexmpp_xml_t *body = rexmpp_xml_new_elem("body", NULL);
diff --git a/tests/xml_parse_and_print.c b/tests/xml_parse_and_print.c
new file mode 100644
index 0000000..ad6bbca
--- /dev/null
+++ b/tests/xml_parse_and_print.c
@@ -0,0 +1,27 @@
+#include <string.h>
+#include <stdlib.h>
+#include "rexmpp_xml.h"
+
+int main () {
+ int ret = 0;
+
+ char *str = "<foo bar=\"baz\">"
+ "<qux xmlns=\"urn:test\">a b c d</qux>"
+ "<quux e=\"f\" g=\"h\"/>"
+ "</foo>";
+ rexmpp_xml_t *xml = rexmpp_xml_parse (str, strlen(str));
+
+ if (xml == NULL) {
+ ret = -1;
+ } else {
+ char *str_new = rexmpp_xml_serialize (xml, 0);
+ if (str_new == NULL) {
+ ret = -2;
+ } else {
+ ret = strcmp(str, str_new);
+ free(str_new);
+ }
+ rexmpp_xml_free(xml);
+ }
+ return ret;
+}
diff --git a/tests/xml_print_and_parse.c b/tests/xml_print_and_parse.c
new file mode 100644
index 0000000..9bd7a3f
--- /dev/null
+++ b/tests/xml_print_and_parse.c
@@ -0,0 +1,64 @@
+#include <string.h>
+#include <stdlib.h>
+#include "rexmpp_xml.h"
+
+int main () {
+ int ret = 0;
+
+ rexmpp_xml_attr_t
+ foo_attributes = { .qname = {"bar", NULL},
+ .value = "baz",
+ .next = NULL },
+ quux_attributes_g = { .qname = {"g", NULL},
+ .value = "h",
+ .next = NULL },
+ quux_attributes =
+ { .qname = {"e", NULL},
+ .value = "f",
+ .next = &quux_attributes_g };
+ rexmpp_xml_t
+ quux = { .type = REXMPP_XML_ELEMENT,
+ .alt.elem =
+ { .qname = {"quux", NULL},
+ .attributes = &quux_attributes,
+ .children = NULL
+ },
+ .next = NULL
+ },
+ qux_text = { .type = REXMPP_XML_TEXT,
+ .alt.text = "a b c d",
+ .next = NULL },
+ qux = { .type = REXMPP_XML_ELEMENT,
+ .alt.elem =
+ { .qname = {"qux", "urn:dummy"},
+ .attributes = NULL,
+ .children = &qux_text
+ },
+ .next = &quux
+ },
+ xml =
+ { .type = REXMPP_XML_ELEMENT,
+ .alt.elem =
+ { .qname = {"foo", NULL},
+ .attributes = &foo_attributes,
+ .children = &qux
+ },
+ .next = NULL
+ };
+
+ char *str_new = rexmpp_xml_serialize (&xml, 0);
+ if (str_new == NULL) {
+ ret = -1;
+ } else {
+ rexmpp_xml_t *xml_new = rexmpp_xml_parse (str_new, strlen(str_new));
+ if (xml_new == NULL) {
+ ret = -2;
+ } else {
+ /* Compare the XML structures. */
+ ret = (rexmpp_xml_eq(&xml, xml_new) == 0);
+ rexmpp_xml_free(xml_new);
+ }
+ free(str_new);
+ }
+ return ret;
+}