summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Cargo.toml17
-rw-r--r--src/Makefile.am50
-rw-r--r--src/rexmpp.c1444
-rw-r--r--src/rexmpp.h255
-rw-r--r--src/rexmpp.rs280
-rw-r--r--src/rexmpp_base64.h25
-rw-r--r--src/rexmpp_console.c645
-rw-r--r--src/rexmpp_console.h4
-rw-r--r--src/rexmpp_digest.c125
-rw-r--r--src/rexmpp_digest.h85
-rw-r--r--src/rexmpp_dns.c137
-rw-r--r--src/rexmpp_dns.h29
-rw-r--r--src/rexmpp_dns.rs62
-rw-r--r--src/rexmpp_http_upload.c79
-rw-r--r--src/rexmpp_jid.c6
-rw-r--r--src/rexmpp_jid.rs17
-rw-r--r--src/rexmpp_jingle.c1956
-rw-r--r--src/rexmpp_jingle.h74
-rw-r--r--src/rexmpp_openpgp.c287
-rw-r--r--src/rexmpp_openpgp.h10
-rw-r--r--src/rexmpp_pubsub.c51
-rw-r--r--src/rexmpp_pubsub.h4
-rw-r--r--src/rexmpp_random.c34
-rw-r--r--src/rexmpp_random.h29
-rw-r--r--src/rexmpp_random.rs5
-rw-r--r--src/rexmpp_roster.c96
-rw-r--r--src/rexmpp_roster.h14
-rw-r--r--src/rexmpp_rust.rs8
-rw-r--r--src/rexmpp_sasl.c82
-rw-r--r--src/rexmpp_socks.rs180
-rw-r--r--src/rexmpp_tcp.c87
-rw-r--r--src/rexmpp_tcp.h17
-rw-r--r--src/rexmpp_tcp.rs469
-rw-r--r--src/rexmpp_tls.c788
-rw-r--r--src/rexmpp_tls.h85
-rw-r--r--src/rexmpp_utf8.h93
-rw-r--r--src/rexmpp_xml.c805
-rw-r--r--src/rexmpp_xml.h269
-rw-r--r--src/rexmpp_xml.rs1040
-rw-r--r--src/rexmpp_xml_parser.c323
-rw-r--r--src/rexmpp_xml_parser.h106
-rw-r--r--src/rexmpp_xml_parser.rs145
42 files changed, 7956 insertions, 2361 deletions
diff --git a/src/Cargo.toml b/src/Cargo.toml
new file mode 100644
index 0000000..65a0ff4
--- /dev/null
+++ b/src/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "rexmpp_rust"
+version = "0.1.0"
+authors = ["defanor <defanor@uberspace.net>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+name = "rexmpp_rust"
+crate-type = ["staticlib"]
+path = "rexmpp_rust.rs"
+
+[dependencies]
+libc = "0.2"
+errno = "0.3"
+rxml = "0.9"
diff --git a/src/Makefile.am b/src/Makefile.am
index 9fff4c8..2dfc581 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,18 +1,8 @@
-AM_CFLAGS = -Werror -Wall -Wextra -pedantic -std=gnu99 \
- -Wno-pointer-sign
-
-# -Wno-pointer-sign is used to suppress libxml2-related warnings.
-# Since we only care about UTF-8, and in almost all cases just its
-# ASCII subset (comparing or setting fixed namespaces, element names,
-# etc), it shouldn't matter. Later it would be nice to abstract XML
-# manipulations anyway, to allow libexpat as an alternative.
-
+AM_CFLAGS = -Werror -Wall -Wextra -pedantic -std=gnu99
lib_LTLIBRARIES = librexmpp.la
librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \
- rexmpp_tcp.h rexmpp_tcp.c \
- rexmpp_socks.h rexmpp_socks.c \
rexmpp.h rexmpp.c \
rexmpp_dns.h rexmpp_dns.c \
rexmpp_tls.h rexmpp_tls.c \
@@ -23,17 +13,43 @@ librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \
rexmpp_http_upload.h rexmpp_http_upload.c \
rexmpp_jingle.h rexmpp_jingle.c \
rexmpp_base64.h rexmpp_base64.c \
- rexmpp_sasl.h rexmpp_sasl.c
+ rexmpp_sasl.h rexmpp_sasl.c \
+ rexmpp_xml.h rexmpp_xml.c \
+ rexmpp_utf8.h \
+ rexmpp_random.h rexmpp_random.c \
+ rexmpp_digest.h rexmpp_digest.c
+
include_HEADERS = config.h rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h \
rexmpp_dns.h rexmpp_tls.h rexmpp_jid.h rexmpp_openpgp.h rexmpp_console.h \
rexmpp_pubsub.h rexmpp_http_upload.h rexmpp_jingle.h rexmpp_base64.h \
- rexmpp_sasl.h
-librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) \
+ rexmpp_sasl.h rexmpp_xml.h rexmpp_utf8.h rexmpp_xml_parser.h \
+ rexmpp_random.h rexmpp_digest.h
+librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML2_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)
-librexmpp_la_LIBADD = $(LIBXML_LIBS) \
+ $(NICE_CFLAGS) $(GLIB_CFLAGS) $(SRTP_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)
+ $(LIBGCRYPT_LIBS) $(CURL_LIBS) $(NICE_LIBS) $(GLIB_LIBS) $(SRTP_LIBS) \
+ $(PORTAUDIO_LIBS) $(OPUS_LIBS) $(NETTLE_LIBS)
+librexmpp_la_LDFLAGS = []
+
+if USE_RUST
+target_debug_librexmpp_rust_a_SOURCES = \
+ rexmpp_rust.rs rexmpp.rs rexmpp_jid.rs rexmpp_dns.rs rexmpp_tcp.rs \
+ rexmpp_socks.rs rexmpp_xml.rs rexmpp_xml_parser.rs
+noinst_LIBRARIES = target/debug/librexmpp_rust.a
+librexmpp_la_LIBADD += target/debug/librexmpp_rust.a
+librexmpp_la_LDFLAGS += -L. -lpthread -ldl
+
+target/debug/librexmpp_rust.a: $(target_debug_librexmpp_rust_a_SOURCES)
+ $(CARGO) build
+
+else
+librexmpp_la_SOURCES += rexmpp_tcp.h rexmpp_tcp.c \
+ rexmpp_socks.h rexmpp_socks.c \
+ rexmpp_xml_parser.h rexmpp_xml_parser.c
+endif
diff --git a/src/rexmpp.c b/src/rexmpp.c
index d7e1364..24b9965 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -14,13 +14,17 @@
#include <arpa/nameser.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <assert.h>
+#include <stdbool.h>
#include "config.h"
+#ifdef HAVE_GCRYPT
#include <gcrypt.h>
-#include <libxml/tree.h>
-#include <libxml/xmlsave.h>
+#endif
+#ifdef USE_UNBOUND
#include <unbound.h>
+#endif
#ifdef HAVE_GPGME
#include <gpgme.h>
#endif
@@ -29,6 +33,7 @@
#endif
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_tcp.h"
#include "rexmpp_socks.h"
#include "rexmpp_roster.h"
@@ -40,6 +45,8 @@
#include "rexmpp_jingle.h"
#include "rexmpp_base64.h"
#include "rexmpp_sasl.h"
+#include "rexmpp_random.h"
+#include "rexmpp_digest.h"
struct rexmpp_iq_cacher {
rexmpp_iq_callback_t cb;
@@ -88,21 +95,13 @@ const char *rexmpp_strerror (rexmpp_err_t error) {
}
void rexmpp_sax_start_elem_ns (rexmpp_t *s,
- const char *localname,
- const char *prefix,
- const char *URI,
- int nb_namespaces,
- const char **namespaces,
- int nb_attributes,
- int nb_defaulted,
- const char **attributes);
-
-void rexmpp_sax_end_elem_ns(rexmpp_t *s,
- const char *localname,
- const char *prefix,
- const char *URI);
-
-void rexmpp_sax_characters (rexmpp_t *s, const char * ch, int len);
+ const char *name,
+ const char *namespace,
+ rexmpp_xml_attr_t *attributes);
+
+void rexmpp_sax_end_elem_ns(rexmpp_t *s);
+
+void rexmpp_sax_characters (rexmpp_t *s, const char * ch, size_t len);
void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...)
{
@@ -114,157 +113,225 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...)
}
}
-char *rexmpp_capabilities_string (rexmpp_t *s, xmlNodePtr info) {
- /* Assuming the info is sorted already. Would be better to sort it
- here (todo). */
- xmlNodePtr cur;
- int buf_len = 1024, str_len = 0;
- char *str = malloc(buf_len);
- for (cur = info; cur; cur = cur->next) {
- if (strcmp(cur->name, "identity") == 0) {
- int cur_len = 5; /* ///< for an empty identity */
-
- /* Collect the properties we'll need. */
- char *category = xmlGetProp(cur, "category");
- char *type = xmlGetProp(cur, "type");
- char *lang = xmlGetProp(cur, "xml:lang");
- char *name = xmlGetProp(cur, "name");
-
- /* Calculate the length needed. */
- if (category != NULL) {
- cur_len += strlen(category);
- }
- if (type != NULL) {
- cur_len += strlen(type);
- }
- if (lang != NULL) {
- cur_len += strlen(lang);
- }
- if (name != NULL) {
- cur_len += strlen(name);
- }
+rexmpp_err_t rexmpp_muc_ping_set (rexmpp_t *s,
+ const char *occupant_jid,
+ const char *password,
+ unsigned int delay)
+{
+ /* At first try to edit an existing record. */
+ rexmpp_muc_ping_t *mp = s->muc_ping;
+ while (mp != NULL) {
+ if (strcmp(mp->jid, occupant_jid) == 0) {
+ mp->delay = delay;
+ return REXMPP_SUCCESS;
+ }
+ mp = mp->next;
+ }
- /* Reallocate the buffer if necessary. */
- if (cur_len > buf_len - str_len) {
- while (cur_len > buf_len - str_len) {
- buf_len *= 2;
- }
- str = realloc(str, buf_len);
- }
+ /* No existing self-ping record for this occupant JID; create a new
+ one. */
+ mp = malloc(sizeof(rexmpp_muc_ping_t));
+ if (mp == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to allocate memory for a MUC self-ping record: %s",
+ strerror(errno));
+ return REXMPP_E_MALLOC;
+ }
+ mp->jid = strdup(occupant_jid);
+ if (mp->jid == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to duplicate JID string for a MUC self-ping record: %s",
+ strerror(errno));
+ free(mp);
+ return REXMPP_E_OTHER;
+ }
+ if (password != NULL) {
+ mp->password = strdup(password);
+ } else {
+ mp->password = NULL;
+ }
- /* Fill the data. */
- if (category != NULL) {
- strcpy(str + str_len, category);
- str_len += strlen(category);
- }
- str[str_len] = '/';
- str_len++;
- if (type != NULL) {
- strcpy(str + str_len, type);
- str_len += strlen(type);
- }
- str[str_len] = '/';
- str_len++;
- if (lang != NULL) {
- strcpy(str + str_len, lang);
- str_len += strlen(lang);
- }
- str[str_len] = '/';
- str_len++;
- if (name != NULL) {
- strcpy(str + str_len, name);
- str_len += strlen(name);
- }
- str[str_len] = '<';
- str_len++;
+ mp->delay = delay;
+ mp->requested = 0;
+ mp->last_activity.tv_sec = 0;
+ mp->last_activity.tv_nsec = 0;
+ mp->next = s->muc_ping;
+ s->muc_ping = mp;
+ return REXMPP_SUCCESS;
+}
- /* Free the values. */
- if (category != NULL) {
- free(category);
+rexmpp_err_t rexmpp_muc_ping_remove (rexmpp_t *s, const char *occupant_jid) {
+ rexmpp_muc_ping_t **mp = &(s->muc_ping);
+ while (*mp != NULL) {
+ if (strcmp(occupant_jid, (*mp)->jid) == 0) {
+ rexmpp_muc_ping_t *found = *mp;
+ *mp = found->next;
+ free(found->jid);
+ if (found->password != NULL) {
+ free(found->password);
}
- if (type != NULL) {
- free(type);
- }
- if (lang != NULL) {
- free(lang);
- }
- if (name != NULL) {
- free(name);
+ free(found);
+ return REXMPP_SUCCESS;
+ }
+ mp = &((*mp)->next);
+ }
+ rexmpp_log(s, LOG_WARNING,
+ "Removal of MUC self-ping record for JID %s is requested, "
+ "but no such record is found",
+ occupant_jid);
+ return REXMPP_E_OTHER;
+}
+
+rexmpp_err_t rexmpp_muc_join (rexmpp_t *s,
+ const char *occupant_jid,
+ const char *password,
+ unsigned int ping_delay)
+{
+ rexmpp_xml_t *presence =
+ rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(presence, "to", occupant_jid);
+ rexmpp_xml_t *x =
+ rexmpp_xml_new_elem("x", "http://jabber.org/protocol/muc");
+ rexmpp_xml_add_child(presence, x);
+ rexmpp_err_t ret = rexmpp_send(s, presence);
+ if (ping_delay > 0) {
+ rexmpp_muc_ping_set(s, occupant_jid, password, ping_delay);
+ }
+ return ret;
+}
+
+rexmpp_err_t rexmpp_muc_leave (rexmpp_t *s, const char *occupant_jid) {
+ rexmpp_muc_ping_remove(s, occupant_jid);
+ rexmpp_xml_t *presence =
+ rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(presence, "to", occupant_jid);
+ rexmpp_xml_add_attr(presence, "type", "unavailable");
+ return rexmpp_send(s, presence);
+}
+
+void rexmpp_muc_pong (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ rexmpp_muc_ping_t *mp = ptr;
+ clock_gettime(CLOCK_MONOTONIC, &(mp->last_activity));
+ mp->requested = 0;
+ if (! success) {
+ const char *jid = rexmpp_xml_find_attr_val(req, "to");
+ rexmpp_xml_t *error = rexmpp_xml_first_elem_child(response);
+ if (error == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "MUC self-ping failure for %s, and no error element received",
+ jid);
+ rexmpp_muc_ping_remove(s, jid);
+ } else {
+ rexmpp_log(s, LOG_WARNING, "MUC self-ping failure for %s: %s",
+ jid, error->alt.elem.qname.name);
+ if (rexmpp_xml_match(error, NULL, "service-unavailable") ||
+ rexmpp_xml_match(error, NULL, "feature-not-implemented") ||
+ rexmpp_xml_match(error, NULL, "remote-server-not-found") ||
+ rexmpp_xml_match(error, NULL, "remote-server-timeout")) {
+ rexmpp_log(s, LOG_WARNING, "Giving up on pinging it");
+ rexmpp_muc_ping_remove(s, jid);
+ } else if (rexmpp_xml_match(error, NULL, "item-not-found")) {
+ /* Ignore and keep pinging? */
+ } else {
+ /* Some other error, re-join. */
+ rexmpp_muc_join(s, jid, NULL, 0);
}
- } else if (strcmp(cur->name, "feature") == 0) {
- char *var = xmlGetProp(cur, "var");
- int cur_len = 2 + strlen(var);
- if (cur_len > buf_len - str_len) {
- while (cur_len > buf_len - str_len) {
- buf_len *= 2;
+ }
+ }
+}
+
+char *rexmpp_capabilities_hash (rexmpp_t *s,
+ rexmpp_xml_t *info)
+{
+ /* Assuming the info is sorted already. Might be useful to sort it
+ here. */
+ rexmpp_digest_t digest_ctx;
+ if (rexmpp_digest_init(&digest_ctx, REXMPP_DIGEST_SHA1)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a digest object");
+ return NULL;
+ }
+
+ rexmpp_xml_t *cur;
+ for (cur = info; cur; cur = cur->next) {
+ if (strcmp(cur->alt.elem.qname.name, "identity") == 0) {
+ const char *identity_strings[4] =
+ { rexmpp_xml_find_attr_val(cur, "category"),
+ rexmpp_xml_find_attr_val(cur, "type"),
+ rexmpp_xml_find_attr_val(cur, "xml:lang"),
+ rexmpp_xml_find_attr_val(cur, "name")
+ };
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (identity_strings[i] != NULL) {
+ rexmpp_digest_update(&digest_ctx,
+ identity_strings[i],
+ strlen(identity_strings[i]));
}
- str = realloc(str, buf_len);
+ rexmpp_digest_update(&digest_ctx, (i < 3) ? "/" : "<", 1);
+ }
+ } else if (strcmp(cur->alt.elem.qname.name, "feature") == 0) {
+ const char *var = rexmpp_xml_find_attr_val(cur, "var");
+ if (var != NULL) {
+ rexmpp_digest_update(&digest_ctx, var, strlen(var));
+ rexmpp_digest_update(&digest_ctx, "<", 1);
+ } else {
+ rexmpp_log(s, LOG_ERR, "Found an empty feature var");
}
- strcpy(str + str_len, var);
- str_len += strlen(var);
- str[str_len] = '<';
- str_len++;
- free(var);
} else {
rexmpp_log(s, LOG_ERR,
- "Unsupported node type in disco info: %s", cur->name);
+ "Unsupported node type in disco info: %s",
+ cur->alt.elem.qname.name);
}
}
- str[str_len] = '\0';
- return str;
-}
-char *rexmpp_capabilities_hash (rexmpp_t *s,
- xmlNodePtr info)
-{
- char *out = NULL;
- size_t out_len = 0;
- char *str = rexmpp_capabilities_string(s, info);
- if (str != NULL) {
- unsigned int sha1_len = gcry_md_get_algo_dlen(GCRY_MD_SHA1);
- char *sha1 = malloc(sha1_len);
- if (sha1 != NULL) {
- gcry_md_hash_buffer(GCRY_MD_SHA1, sha1, str, strlen(str));
- rexmpp_base64_to(sha1, sha1_len, &out, &out_len);
- free(sha1);
- }
- free(str);
- }
- return out;
+ size_t digest_len = rexmpp_digest_len(REXMPP_DIGEST_SHA1);
+ char *digest_buf = malloc(digest_len);
+ rexmpp_digest_finish(&digest_ctx, digest_buf, digest_len);
+ char *digest_base64 = NULL;
+ size_t digest_base64_len = 0;
+ rexmpp_base64_to(digest_buf, digest_len, &digest_base64, &digest_base64_len);
+ free(digest_buf);
+ return digest_base64;
}
-xmlNodePtr rexmpp_find_event (rexmpp_t *s,
- const char *from,
- const char *node,
- xmlNodePtr *prev_event)
+rexmpp_xml_t *rexmpp_find_event (rexmpp_t *s,
+ const char *from,
+ const char *node,
+ rexmpp_xml_t **prev_event)
{
- xmlNodePtr prev, cur;
+ rexmpp_xml_t *prev, *cur;
for (prev = NULL, cur = s->roster_events;
cur != NULL;
- prev = cur, cur = xmlNextElementSibling(cur)) {
- char *cur_from = xmlGetProp(cur, "from");
+ prev = cur, cur = cur->next) {
+ const char *cur_from = rexmpp_xml_find_attr_val(cur, "from");
if (cur_from == NULL) {
continue;
}
- xmlNodePtr cur_event =
+ rexmpp_xml_t *cur_event =
rexmpp_xml_find_child(cur,
"http://jabber.org/protocol/pubsub#event",
"event");
- xmlNodePtr cur_items =
+ rexmpp_xml_t *cur_items =
rexmpp_xml_find_child(cur_event,
"http://jabber.org/protocol/pubsub#event",
"items");
if (cur_items == NULL) {
- free(cur_from);
continue;
}
- char *cur_node = xmlGetProp(cur_items, "node");
+ const char *cur_node = rexmpp_xml_find_attr_val(cur_items, "node");
if (cur_node == NULL) {
continue;
}
int match = (strcmp(cur_from, from) == 0 && strcmp(cur_node, node) == 0);
- free(cur_node);
- free(cur_from);
if (match) {
if (prev_event != NULL) {
*prev_event = prev;
@@ -282,36 +349,39 @@ char *rexmpp_get_name (rexmpp_t *s, const char *jid_str) {
return NULL;
}
if (s->manage_roster) {
- xmlNodePtr roster_item = rexmpp_roster_find_item(s, jid.bare, NULL);
- if (roster_item) {
- char *name = xmlGetProp(roster_item, "name");
+ rexmpp_xml_t *roster_item = rexmpp_roster_find_item(s, jid.bare, NULL);
+ if (roster_item != NULL) {
+ const char *name = rexmpp_xml_find_attr_val(roster_item, "name");
if (name != NULL) {
- return name;
+ return strdup(name);
}
}
if (s->track_roster_events) {
- xmlNodePtr elem =
+ rexmpp_xml_t *elem =
rexmpp_find_event(s, jid.bare, "http://jabber.org/protocol/nick", NULL);
if (elem != NULL) {
- xmlNodePtr event =
+ rexmpp_xml_t *event =
rexmpp_xml_find_child(elem,
"http://jabber.org/protocol/pubsub#event",
"event");
- xmlNodePtr items =
+ rexmpp_xml_t *items =
rexmpp_xml_find_child(event,
"http://jabber.org/protocol/pubsub#event",
"items");
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(items,
"http://jabber.org/protocol/pubsub#event",
"item");
if (item != NULL) {
- xmlNodePtr nick =
+ rexmpp_xml_t *nick =
rexmpp_xml_find_child(item,
"http://jabber.org/protocol/nick",
"nick");
- if (nick != NULL) {
- return xmlNodeGetContent(nick);
+ if (nick != NULL &&
+ nick->type == REXMPP_XML_ELEMENT &&
+ nick->alt.elem.children != NULL &&
+ nick->alt.elem.children->type == REXMPP_XML_TEXT) {
+ return strdup(rexmpp_xml_text_child(nick));
}
}
}
@@ -323,93 +393,75 @@ char *rexmpp_get_name (rexmpp_t *s, const char *jid_str) {
return strdup(jid.bare);
}
-xmlNodePtr rexmpp_xml_feature (const char *var) {
- xmlNodePtr feature = xmlNewNode(NULL, "feature");
- xmlNewProp(feature, "var", var);
+rexmpp_xml_t *rexmpp_xml_feature (const char *var) {
+ rexmpp_xml_t *feature = rexmpp_xml_new_elem("feature", NULL);
+ rexmpp_xml_add_attr(feature, "var", var);
return feature;
}
-xmlNodePtr rexmpp_xml_new_node (const char *name, const char *namespace) {
- xmlNodePtr node = xmlNewNode(NULL, name);
- xmlNewNs(node, namespace, NULL);
- return node;
-}
-
-xmlNodePtr rexmpp_xml_error (const char *type, const char *condition) {
- xmlNodePtr error = xmlNewNode(NULL, "error");
- xmlNewProp(error, "type", type);
- xmlNodePtr cond = xmlNewNode(NULL, condition);
- xmlNewNs(cond, "urn:ietf:params:xml:ns:xmpp-stanzas", NULL);
- xmlAddChild(error, cond);
- return error;
-}
-
void rexmpp_disco_find_feature_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
struct rexmpp_feature_search *search = ptr;
if (! success) {
- char *to = xmlGetProp(request, "to");
- xmlNodePtr query = xmlFirstElementChild(request);
- rexmpp_log(s, LOG_ERR, "Failed to query %s for %s.", to, query->nsDef->href);
- free(to);
+ const char *to = rexmpp_xml_find_attr_val(request, "to");
+ rexmpp_xml_t *query = request->alt.elem.children;
+ rexmpp_log(s, LOG_ERR, "Failed to query %s for %s.", to, query->alt.elem.qname.namespace);
} else if (! search->found) {
- xmlNodePtr query = xmlFirstElementChild(response);
+ rexmpp_xml_t *query = rexmpp_xml_first_elem_child(response);
if (rexmpp_xml_match(query, "http://jabber.org/protocol/disco#info",
"query")) {
- xmlNodePtr child = xmlFirstElementChild(query);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query);
while (child != NULL && (! search->found)) {
if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#info",
"feature")) {
- char *var = xmlGetProp(child, "var");
+ const char *var = rexmpp_xml_find_attr_val(child, "var");
if (var != NULL) {
if (strcmp(var, search->feature_var) == 0) {
search->cb(s, search->cb_data, request, response, success);
search->found = 1;
}
- free(var);
}
}
- child = child->next;
+ child = rexmpp_xml_next_elem_sibling(child);
}
if ((! search->found) && (search->max_requests > 0)) {
/* Still not found, request items */
- char *jid = xmlGetProp(request, "to");
+ const char *jid = rexmpp_xml_find_attr_val(request, "to");
if (jid != NULL) {
search->pending++;
search->max_requests--;
- xmlNodePtr query =
- rexmpp_xml_new_node("query",
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query",
"http://jabber.org/protocol/disco#items");
rexmpp_cached_iq_new(s, "get", jid, query,
rexmpp_disco_find_feature_cb,
search, search->fresh);
- free(jid);
}
}
} else if (rexmpp_xml_match(query,
"http://jabber.org/protocol/disco#items",
"query")) {
- xmlNodePtr child = xmlFirstElementChild(query);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query);
while (child != NULL && (search->max_requests > 0)) {
if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#items",
"item")) {
- char *jid = xmlGetProp(child, "jid");
+ const char *jid = rexmpp_xml_find_attr_val(child, "jid");
if (jid != NULL) {
search->pending++;
search->max_requests--;
- xmlNodePtr query =
- rexmpp_xml_new_node("query",
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query",
"http://jabber.org/protocol/disco#info");
rexmpp_cached_iq_new(s, "get", jid, query,
rexmpp_disco_find_feature_cb,
search, search->fresh);
}
}
- child = child->next;
+ child = rexmpp_xml_next_elem_sibling(child);
}
}
}
@@ -433,6 +485,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;
@@ -440,8 +495,8 @@ rexmpp_disco_find_feature (rexmpp_t *s,
search->cb_data = cb_data;
search->fresh = fresh;
search->feature_var = feature_var;
- xmlNodePtr query =
- rexmpp_xml_new_node("query", "http://jabber.org/protocol/disco#info");
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query", "http://jabber.org/protocol/disco#info");
if (jid == NULL) {
jid = s->initial_jid.domain;
}
@@ -449,18 +504,18 @@ rexmpp_disco_find_feature (rexmpp_t *s,
rexmpp_disco_find_feature_cb, search, fresh);
}
-xmlNodePtr rexmpp_disco_info (rexmpp_t *s) {
+rexmpp_xml_t *rexmpp_disco_info (rexmpp_t *s) {
if (s->disco_info != NULL) {
return s->disco_info;
}
- xmlNodePtr prev = NULL, cur;
+ rexmpp_xml_t *prev = NULL, *cur;
/* There must be at least one identity, so filling in somewhat
sensible defaults. A basic client may leave them be, while an
advanced one would adjust and/or extend them. */
- s->disco_info = xmlNewNode(NULL, "identity");
- xmlNewProp(s->disco_info, "category", "client");
- xmlNewProp(s->disco_info, "type", s->client_type);
- xmlNewProp(s->disco_info, "name", s->client_name);
+ s->disco_info = rexmpp_xml_new_elem("identity", NULL);
+ rexmpp_xml_add_attr(s->disco_info, "category", "client");
+ rexmpp_xml_add_attr(s->disco_info, "type", s->client_type);
+ rexmpp_xml_add_attr(s->disco_info, "name", s->client_name);
prev = s->disco_info;
cur = rexmpp_xml_feature("http://jabber.org/protocol/disco#info");
prev->next = cur;
@@ -511,17 +566,17 @@ xmlNodePtr rexmpp_disco_info (rexmpp_t *s) {
return s->disco_info;
}
+struct rexmpp_xml_parser_handlers sax = {
+ (rexmpp_xml_parser_element_start)rexmpp_sax_start_elem_ns,
+ (rexmpp_xml_parser_element_end)rexmpp_sax_end_elem_ns,
+ (rexmpp_xml_parser_characters)rexmpp_sax_characters
+};
+
rexmpp_err_t rexmpp_init (rexmpp_t *s,
const char *jid,
log_function_t log_func)
{
int err;
- xmlSAXHandler sax = {
- .initialized = XML_SAX2_MAGIC,
- .characters = (charactersSAXFunc)rexmpp_sax_characters,
- .startElementNs = (startElementNsSAX2Func)rexmpp_sax_start_elem_ns,
- .endElementNs = (endElementNsSAX2Func)rexmpp_sax_end_elem_ns,
- };
s->tcp_state = REXMPP_TCP_NONE;
s->resolver_state = REXMPP_RESOLVER_NONE;
@@ -532,29 +587,30 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->carbons_state = REXMPP_CARBONS_INACTIVE;
s->manual_host = NULL;
s->manual_port = 5222;
- s->manual_direct_tls = 0;
+ s->manual_direct_tls = false;
s->disco_node = "rexmpp";
s->socks_host = NULL;
s->server_host = NULL;
- s->enable_carbons = 1;
- s->manage_roster = 1;
+ s->enable_carbons = true;
+ s->manage_roster = true;
s->roster_cache_file = NULL;
- s->track_roster_presence = 1;
- s->track_roster_events = 1;
- s->nick_notifications = 1;
+ s->track_roster_presence = true;
+ s->track_roster_events = true;
+ s->nick_notifications = true;
#ifdef HAVE_GPGME
- s->retrieve_openpgp_keys = 1;
+ s->retrieve_openpgp_keys = true;
#else
- s->retrieve_openpgp_keys = 0;
+ s->retrieve_openpgp_keys = false;
#endif
- s->autojoin_bookmarked_mucs = 1;
+ s->autojoin_bookmarked_mucs = true;
s->tls_policy = REXMPP_TLS_REQUIRE;
- s->enable_jingle = 1;
+ s->enable_jingle = true;
s->client_name = PACKAGE_NAME;
s->client_type = "console";
s->client_version = PACKAGE_VERSION;
s->local_address = NULL;
- s->jingle_prefer_rtcp_mux = 1;
+ s->jingle_prefer_rtcp_mux = true;
+ s->muc_ping_default_delay = 600;
s->send_buffer = NULL;
s->send_queue = NULL;
s->server_srv = NULL;
@@ -562,6 +618,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->server_srv_tls = NULL;
s->server_srv_tls_cur = -1;
s->server_socket = -1;
+ s->server_socket_dns_secure = false;
s->current_element_root = NULL;
s->current_element = NULL;
s->input_queue = NULL;
@@ -577,7 +634,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->iq_cache = NULL;
s->reconnect_number = 0;
s->next_reconnect_time.tv_sec = 0;
- s->next_reconnect_time.tv_usec = 0;
+ s->next_reconnect_time.tv_nsec = 0;
s->initial_jid.full[0] = '\0';
s->assigned_jid.full[0] = '\0';
s->stanza_queue_size = 1024;
@@ -585,52 +642,50 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->iq_queue_size = 1024;
s->iq_cache_size = 1024;
s->max_jingle_sessions = 1024;
+ s->x509_cert_file = NULL;
+ s->x509_key_file = NULL;
+ s->x509_trust_file = NULL;
s->log_function = log_func;
s->sasl_property_cb = NULL;
s->xml_in_cb = NULL;
s->xml_out_cb = NULL;
s->roster_modify_cb = NULL;
s->console_print_cb = NULL;
+ s->socket_cb = NULL;
s->ping_delay = 600;
- s->ping_requested = 0;
- s->last_network_activity = 0;
+ s->ping_requested = false;
+ s->last_network_activity.tv_sec = 0;
+ s->last_network_activity.tv_nsec = 0;
+ s->muc_ping = NULL;
s->disco_info = NULL;
- /* The default description. Since the players and streamers are
- external for now, this may be adjusted by an application or a
- user. */
s->jingle_rtp_description =
- rexmpp_xml_new_node("description", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(s->jingle_rtp_description, "media", "audio");
- xmlNodePtr pl_type;
-
- pl_type = rexmpp_xml_new_node("payload-type", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(pl_type, "id", "97");
- xmlNewProp(pl_type, "name", "opus");
- xmlNewProp(pl_type, "clockrate", "48000");
- xmlNewProp(pl_type, "channels", "2");
- xmlAddChild(s->jingle_rtp_description, pl_type);
-
- pl_type = rexmpp_xml_new_node("payload-type", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(pl_type, "id", "96");
- xmlNewProp(pl_type, "name", "speex");
- xmlNewProp(pl_type, "clockrate", "32000");
- xmlNewProp(pl_type, "channels", "1");
- xmlAddChild(s->jingle_rtp_description, pl_type);
-
- pl_type = rexmpp_xml_new_node("payload-type", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(pl_type, "id", "0");
- xmlNewProp(pl_type, "name", "PCMU");
- xmlNewProp(pl_type, "clockrate", "8000");
- xmlNewProp(pl_type, "channels", "1");
- xmlAddChild(s->jingle_rtp_description, pl_type);
-
- pl_type = rexmpp_xml_new_node("payload-type", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(pl_type, "id", "8");
- xmlNewProp(pl_type, "name", "PCMA");
- xmlNewProp(pl_type, "clockrate", "8000");
- xmlNewProp(pl_type, "channels", "1");
- xmlAddChild(s->jingle_rtp_description, pl_type);
+ rexmpp_xml_new_elem("description", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(s->jingle_rtp_description, "media", "audio");
+ rexmpp_xml_t *pl_type;
+
+#ifdef HAVE_OPUS
+ pl_type = rexmpp_xml_new_elem("payload-type", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(pl_type, "id", "97");
+ rexmpp_xml_add_attr(pl_type, "name", "opus");
+ rexmpp_xml_add_attr(pl_type, "clockrate", "48000");
+ rexmpp_xml_add_attr(pl_type, "channels", "2");
+ rexmpp_xml_add_child(s->jingle_rtp_description, pl_type);
+#endif
+
+ pl_type = rexmpp_xml_new_elem("payload-type", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(pl_type, "id", "0");
+ rexmpp_xml_add_attr(pl_type, "name", "PCMU");
+ rexmpp_xml_add_attr(pl_type, "clockrate", "8000");
+ rexmpp_xml_add_attr(pl_type, "channels", "1");
+ rexmpp_xml_add_child(s->jingle_rtp_description, pl_type);
+
+ pl_type = rexmpp_xml_new_elem("payload-type", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(pl_type, "id", "8");
+ rexmpp_xml_add_attr(pl_type, "name", "PCMA");
+ rexmpp_xml_add_attr(pl_type, "clockrate", "8000");
+ rexmpp_xml_add_attr(pl_type, "channels", "1");
+ rexmpp_xml_add_child(s->jingle_rtp_description, pl_type);
if (jid == NULL) {
rexmpp_log(s, LOG_CRIT, "No initial JID is provided.");
@@ -646,6 +701,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
return REXMPP_E_JID;
}
+#ifdef HAVE_GCRYPT
if (! gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
rexmpp_log(s, LOG_DEBUG, "Initializing libgcrypt");
if (gcry_check_version(NULL) == NULL) {
@@ -654,8 +710,9 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
}
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
+#endif
- s->xml_parser = xmlCreatePushParserCtxt(&sax, s, "", 0, NULL);
+ s->xml_parser = rexmpp_xml_parser_new(&sax, s);
if (s->xml_parser == NULL) {
rexmpp_log(s, LOG_CRIT, "Failed to create an XML parser context.");
@@ -663,13 +720,13 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
}
if (rexmpp_dns_ctx_init(s)) {
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
return REXMPP_E_DNS;
}
if (rexmpp_tls_init(s)) {
rexmpp_dns_ctx_deinit(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
return REXMPP_E_TLS;
}
@@ -677,7 +734,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
if (err) {
rexmpp_tls_deinit(s);
rexmpp_dns_ctx_deinit(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
return REXMPP_E_SASL;
}
@@ -685,7 +742,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
rexmpp_sasl_ctx_deinit(s);
rexmpp_tls_deinit(s);
rexmpp_dns_ctx_deinit(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
}
#ifdef HAVE_GPGME
@@ -698,9 +755,11 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
rexmpp_tls_deinit(s);
rexmpp_dns_ctx_deinit(s);
rexmpp_jingle_stop(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
return REXMPP_E_PGP;
}
+#else
+ s->pgp_ctx = NULL;
#endif
#ifdef HAVE_CURL
if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
@@ -711,6 +770,8 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
rexmpp_log(s, LOG_CRIT, "Failed to initialize curl_multi");
/* todo: free other structures and fail */
}
+#else
+ s->curl_multi = NULL;
#endif
return REXMPP_SUCCESS;
@@ -729,6 +790,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;
@@ -743,20 +805,20 @@ void rexmpp_cleanup (rexmpp_t *s) {
s->send_buffer = NULL;
}
if (s->stream_features != NULL) {
- xmlFreeNode(s->stream_features);
+ rexmpp_xml_free(s->stream_features);
s->stream_features = NULL;
}
if (s->send_queue != NULL) {
- xmlFreeNodeList(s->send_queue);
+ rexmpp_xml_free_list(s->send_queue);
s->send_queue = NULL;
}
if (s->current_element_root != NULL) {
- xmlFreeNode(s->current_element_root);
+ rexmpp_xml_free_list(s->current_element_root);
s->current_element_root = NULL;
s->current_element = NULL;
}
if (s->input_queue != NULL) {
- xmlFreeNodeList(s->input_queue);
+ rexmpp_xml_free_list(s->input_queue);
s->input_queue = NULL;
s->input_queue_last = NULL;
}
@@ -777,12 +839,12 @@ void rexmpp_cleanup (rexmpp_t *s) {
void rexmpp_iq_finish (rexmpp_t *s,
rexmpp_iq_t *iq,
int success,
- xmlNodePtr response)
+ rexmpp_xml_t *response)
{
if (iq->cb != NULL) {
iq->cb(s, iq->cb_data, iq->request, response, success);
}
- xmlFreeNode(iq->request);
+ rexmpp_xml_free(iq->request);
free(iq);
}
@@ -800,9 +862,9 @@ void rexmpp_done (rexmpp_t *s) {
rexmpp_sasl_ctx_deinit(s);
rexmpp_tls_deinit(s);
rexmpp_dns_ctx_deinit(s);
- xmlFreeParserCtxt(s->xml_parser);
+ rexmpp_xml_parser_free(s->xml_parser);
if (s->jingle_rtp_description != NULL) {
- xmlFreeNode(s->jingle_rtp_description);
+ rexmpp_xml_free(s->jingle_rtp_description);
s->jingle_rtp_description = NULL;
}
if (s->stream_id != NULL) {
@@ -810,27 +872,33 @@ void rexmpp_done (rexmpp_t *s) {
s->stream_id = NULL;
}
if (s->roster_items != NULL) {
- xmlFreeNodeList(s->roster_items);
+ rexmpp_xml_free_list(s->roster_items);
s->roster_items = NULL;
}
if (s->roster_presence != NULL) {
- xmlFreeNodeList(s->roster_presence);
+ rexmpp_xml_free_list(s->roster_presence);
s->roster_presence = NULL;
}
if (s->roster_events != NULL) {
- xmlFreeNodeList(s->roster_events);
+ rexmpp_xml_free_list(s->roster_events);
s->roster_events = NULL;
}
if (s->roster_ver != NULL) {
free(s->roster_ver);
s->roster_ver = NULL;
}
+ while (s->muc_ping != NULL) {
+ rexmpp_muc_ping_t *mp_next = s->muc_ping->next;
+ free(s->muc_ping->jid);
+ free(s->muc_ping);
+ s->muc_ping = mp_next;
+ }
if (s->disco_info != NULL) {
- xmlFreeNodeList(s->disco_info);
+ rexmpp_xml_free_list(s->disco_info);
s->disco_info = NULL;
}
if (s->stanza_queue != NULL) {
- xmlFreeNodeList(s->stanza_queue);
+ rexmpp_xml_free_list(s->stanza_queue);
s->stanza_queue = NULL;
}
while (s->active_iq != NULL) {
@@ -840,7 +908,7 @@ void rexmpp_done (rexmpp_t *s) {
rexmpp_iq_finish(s, iq, 0, NULL);
}
if (s->iq_cache != NULL) {
- xmlFreeNodeList(s->iq_cache);
+ rexmpp_xml_free_list(s->iq_cache);
s->iq_cache = NULL;
}
}
@@ -853,7 +921,7 @@ void rexmpp_schedule_reconnect (rexmpp_t *s) {
return;
}
if (s->reconnect_number == 0) {
- gcry_create_nonce((char*)&s->reconnect_seconds, sizeof(time_t));
+ rexmpp_random_buf((char*)&s->reconnect_seconds, sizeof(time_t));
if (s->reconnect_seconds < 0) {
s->reconnect_seconds = - s->reconnect_seconds;
}
@@ -866,7 +934,7 @@ void rexmpp_schedule_reconnect (rexmpp_t *s) {
if (seconds > 3600) {
seconds = 3600;
}
- gettimeofday(&(s->next_reconnect_time), NULL);
+ clock_gettime(CLOCK_MONOTONIC, &(s->next_reconnect_time));
s->next_reconnect_time.tv_sec += seconds;
rexmpp_log(s, LOG_DEBUG, "Scheduled reconnect number %d, in %d seconds",
s->reconnect_number,
@@ -884,96 +952,7 @@ 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;
- gcry_create_nonce(buf_raw, 18);
- rexmpp_base64_to(buf_raw, 18, &buf_base64, &buf_base64_len);
- return buf_base64;
-}
-
-xmlNodePtr rexmpp_xml_add_id (rexmpp_t *s, xmlNodePtr node) {
- char *buf = rexmpp_gen_id(s);
- if (buf == NULL) {
- return NULL;
- }
- xmlNewProp(node, "id", buf);
- free(buf);
- return node;
-}
-
-unsigned int rexmpp_xml_siblings_count (xmlNodePtr node) {
- unsigned int i;
- for (i = 0; node != NULL; i++) {
- node = xmlNextElementSibling(node);
- }
- return i;
-}
-
-int rexmpp_xml_match (xmlNodePtr node,
- const char *namespace,
- const char *name)
-{
- if (node == NULL) {
- return 0;
- }
- if (name != NULL) {
- if (strcmp(name, node->name) != 0) {
- return 0;
- }
- }
- if (namespace != NULL) {
- if (node->nsDef == NULL || node->nsDef->href == NULL) {
- if (strcmp(namespace, "jabber:client") != 0) {
- return 0;
- }
- } else {
- if (node->nsDef) {
- if (strcmp(namespace, node->nsDef->href) != 0) {
- return 0;
- }
- } else {
- if (namespace != NULL) {
- return 0;
- }
- }
- }
- }
- return 1;
-}
-
-int rexmpp_xml_eq (xmlNodePtr n1, xmlNodePtr n2) {
- /* Just serialize and compare strings for now: awkward, but
- simple. */
- char *n1str = rexmpp_xml_serialize(n1);
- char *n2str = rexmpp_xml_serialize(n2);
- int eq = (strcmp(n1str, n2str) == 0);
- free(n1str);
- free(n2str);
- return eq;
-}
-
-xmlNodePtr rexmpp_xml_find_child (xmlNodePtr node,
- const char *namespace,
- const char *name)
-{
- if (node == NULL) {
- return NULL;
- }
- xmlNodePtr child;
- for (child = xmlFirstElementChild(node);
- child != NULL;
- child = xmlNextElementSibling(child))
- {
- if (rexmpp_xml_match(child, namespace, name)) {
- return child;
- }
- }
- return NULL;
-}
-
-xmlNodePtr rexmpp_xml_set_delay (rexmpp_t *s, xmlNodePtr node) {
+rexmpp_xml_t *rexmpp_xml_set_delay (rexmpp_t *s, rexmpp_xml_t *node) {
if (rexmpp_xml_find_child (node, NULL, "delay")) {
return node;
}
@@ -982,42 +961,15 @@ xmlNodePtr rexmpp_xml_set_delay (rexmpp_t *s, xmlNodePtr node) {
struct tm utc_time;
gmtime_r(&t, &utc_time);
strftime(buf, 42, "%FT%TZ", &utc_time);
- xmlNodePtr delay = xmlNewChild(node, NULL, "delay", NULL);
- xmlNewProp(delay, "stamp", buf);
+ rexmpp_xml_t *delay = rexmpp_xml_new_elem("delay", NULL);
+ rexmpp_xml_add_child(node, delay);
+ rexmpp_xml_add_attr(delay, "stamp", buf);
if (s != NULL && s->assigned_jid.full[0]) {
- xmlNewProp(delay, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(delay, "from", s->assigned_jid.full);
}
return node;
}
-xmlNodePtr rexmpp_xml_parse (const char *str, int str_len) {
- xmlNodePtr elem = NULL;
- xmlDocPtr doc = xmlReadMemory(str, str_len, "", "utf-8", XML_PARSE_NONET);
- if (doc != NULL) {
- elem = xmlCopyNode(xmlDocGetRootElement(doc), 1);
- xmlFreeDoc(doc);
- }
- return elem;
-}
-
-char *rexmpp_xml_serialize (xmlNodePtr node) {
- xmlBufferPtr buf = xmlBufferCreate();
- xmlSaveCtxtPtr ctx = xmlSaveToBuffer(buf, "utf-8", 0);
- xmlSaveTree(ctx, node);
- xmlSaveFlush(ctx);
- xmlSaveClose(ctx);
- unsigned char *out = xmlBufferDetach(buf);
- xmlBufferFree(buf);
- return out;
-}
-
-int rexmpp_xml_is_stanza (xmlNodePtr node) {
- return rexmpp_xml_match(node, "jabber:client", "message") ||
- rexmpp_xml_match(node, "jabber:client", "iq") ||
- rexmpp_xml_match(node, "jabber:client", "presence");
-}
-
-
rexmpp_err_t rexmpp_send_start (rexmpp_t *s, const void *data, size_t data_len)
{
int sasl_err;
@@ -1052,61 +1004,64 @@ rexmpp_err_t rexmpp_send_continue (rexmpp_t *s)
}
ssize_t ret;
rexmpp_tls_err_t err;
- int tls_was_active;
while (1) {
- tls_was_active = (s->tls_state == REXMPP_TLS_ACTIVE);
- if (tls_was_active) {
+ if (s->tls_state == REXMPP_TLS_ACTIVE) {
err = rexmpp_tls_send (s,
+ s->tls,
s->send_buffer,
s->send_buffer_len,
&ret);
- } else {
- ret = send (s->server_socket,
- s->send_buffer + s->send_buffer_sent,
- s->send_buffer_len - s->send_buffer_sent,
- 0);
- }
- if (ret > 0) {
- s->last_network_activity = time(NULL);
- s->send_buffer_sent += ret;
- if (s->send_buffer_sent == s->send_buffer_len) {
- free(s->send_buffer);
- s->send_buffer = NULL;
- if (s->send_queue != NULL) {
- xmlNodePtr node = s->send_queue;
- unsigned char *buf = rexmpp_xml_serialize(node);
- ret = rexmpp_send_start(s, buf, strlen(buf));
- free(buf);
- if (ret != REXMPP_SUCCESS) {
- return ret;
- }
- s->send_queue = xmlNextElementSibling(s->send_queue);
- xmlFreeNode(node);
- } else {
- return REXMPP_SUCCESS;
- }
- }
- } else {
- if (tls_was_active) {
+ if (ret <= 0) {
if (err != REXMPP_TLS_E_AGAIN) {
s->tls_state = REXMPP_TLS_ERROR;
/* Assume a TCP error for now as well. */
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_ERROR;
rexmpp_schedule_reconnect(s);
- return REXMPP_E_AGAIN;
}
- } else {
+ /* Returning E_AGAIN for now, since the error is potentially
+ recoverable after the scheduled reconnect. */
+ return REXMPP_E_AGAIN;
+ }
+ } else {
+ ret = send (s->server_socket,
+ s->send_buffer + s->send_buffer_sent,
+ s->send_buffer_len - s->send_buffer_sent,
+ 0);
+ if (ret <= 0) {
if (errno != EAGAIN) {
rexmpp_log(s, LOG_ERR, "TCP send error: %s", strerror(errno));
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_ERROR;
rexmpp_schedule_reconnect(s);
- return REXMPP_E_AGAIN;
}
+ /* E_AGAIN, similarly to the TLS case. */
+ return REXMPP_E_AGAIN;
}
- return REXMPP_E_AGAIN;
}
+
+ assert(ret > 0);
+
+ clock_gettime(CLOCK_MONOTONIC, &(s->last_network_activity));
+ s->send_buffer_sent += ret;
+ if (s->send_buffer_sent == s->send_buffer_len) {
+ free(s->send_buffer);
+ s->send_buffer = NULL;
+ if (s->send_queue != NULL) {
+ rexmpp_xml_t *node = s->send_queue;
+ char *buf = rexmpp_xml_serialize(node, 0);
+ ret = rexmpp_send_start(s, buf, strlen(buf));
+ free(buf);
+ if (ret != REXMPP_SUCCESS) {
+ return ret;
+ }
+ s->send_queue = s->send_queue->next;
+ rexmpp_xml_free(node);
+ } else {
+ return REXMPP_SUCCESS;
+ }
+ }
+
}
}
@@ -1121,19 +1076,19 @@ rexmpp_err_t rexmpp_send_raw (rexmpp_t *s, const void *data, size_t data_len)
rexmpp_err_t rexmpp_sm_send_req (rexmpp_t *s);
-rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
+rexmpp_err_t rexmpp_send (rexmpp_t *s, rexmpp_xml_t *node)
{
int need_ack = 0;
int ret;
if (s->xml_out_cb != NULL && s->xml_out_cb(s, node) == 1) {
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
rexmpp_log(s, LOG_WARNING, "Message sending was cancelled by xml_out_cb.");
return REXMPP_E_CANCELLED;
}
if (rexmpp_xml_siblings_count(s->send_queue) >= s->send_queue_size) {
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
rexmpp_log(s, LOG_ERR, "The send queue is full, not sending.");
return REXMPP_E_SEND_QUEUE_FULL;
}
@@ -1144,20 +1099,21 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
if (s->sm_state == REXMPP_SM_ACTIVE) {
if (s->stanzas_out_count >=
s->stanza_queue_size + s->stanzas_out_acknowledged) {
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
rexmpp_log(s, LOG_ERR, "The stanza queue is full, not sending.");
return REXMPP_E_STANZA_QUEUE_FULL;
}
need_ack = 1;
- xmlNodePtr queued_stanza = rexmpp_xml_set_delay(s, xmlCopyNode(node, 1));
+ rexmpp_xml_t *queued_stanza =
+ rexmpp_xml_set_delay(s, rexmpp_xml_clone(node));
if (s->stanza_queue == NULL) {
s->stanza_queue = queued_stanza;
} else {
- xmlNodePtr last = s->stanza_queue;
- while (xmlNextElementSibling(last) != NULL) {
- last = xmlNextElementSibling(last);
+ rexmpp_xml_t *last = s->stanza_queue;
+ while (last->next != NULL) {
+ last = last->next;
}
- xmlAddNextSibling(last, queued_stanza);
+ last->next = queued_stanza;
}
}
if (s->sm_state != REXMPP_SM_INACTIVE) {
@@ -1166,10 +1122,10 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
}
if (s->send_buffer == NULL) {
- unsigned char *buf = rexmpp_xml_serialize(node);
+ char *buf = rexmpp_xml_serialize(node, 0);
ret = rexmpp_send_raw(s, buf, strlen(buf));
free(buf);
- xmlFreeNode(node);
+ rexmpp_xml_free(node);
if (ret != REXMPP_SUCCESS && ret != REXMPP_E_AGAIN) {
return ret;
}
@@ -1177,11 +1133,11 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
if (s->send_queue == NULL) {
s->send_queue = node;
} else {
- xmlNodePtr last = s->send_queue;
- while (xmlNextElementSibling(last) != NULL) {
- last = xmlNextElementSibling(last);
+ rexmpp_xml_t *last = s->send_queue;
+ while (last->next != NULL) {
+ last = last->next;
}
- xmlAddNextSibling(last, node);
+ last->next = node;
}
ret = REXMPP_E_AGAIN;
}
@@ -1192,28 +1148,25 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node)
}
void rexmpp_iq_reply (rexmpp_t *s,
- xmlNodePtr req,
+ rexmpp_xml_t *req,
const char *type,
- xmlNodePtr payload)
+ rexmpp_xml_t *payload)
{
- xmlNodePtr iq_stanza = xmlNewNode(NULL, "iq");
- xmlNewNs(iq_stanza, "jabber:client", NULL);
- xmlNewProp(iq_stanza, "type", type);
- char *id = xmlGetProp(req, "id");
+ rexmpp_xml_t *iq_stanza = rexmpp_xml_new_elem("iq", "jabber:client");
+ rexmpp_xml_add_attr(iq_stanza, "type", type);
+ const char *id = rexmpp_xml_find_attr_val(req, "id");
if (id != NULL) {
- xmlNewProp(iq_stanza, "id", id);
- free(id);
+ rexmpp_xml_add_attr(iq_stanza, "id", id);
}
- char *to = xmlGetProp(req, "from");
+ const char *to = rexmpp_xml_find_attr_val(req, "from");
if (to != NULL) {
- xmlNewProp(iq_stanza, "to", to);
- free(to);
+ rexmpp_xml_add_attr(iq_stanza, "to", to);
}
if (s->assigned_jid.full[0]) {
- xmlNewProp(iq_stanza, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(iq_stanza, "from", s->assigned_jid.full);
}
if (payload != NULL) {
- xmlAddChild(iq_stanza, payload);
+ rexmpp_xml_add_child(iq_stanza, payload);
}
rexmpp_send(s, iq_stanza);
}
@@ -1221,7 +1174,7 @@ void rexmpp_iq_reply (rexmpp_t *s,
rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
const char *type,
const char *to,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t cb,
void *cb_data)
{
@@ -1232,24 +1185,30 @@ 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;
rexmpp_iq_finish(s, last, 0, NULL);
}
- xmlNodePtr iq_stanza = rexmpp_xml_add_id(s, xmlNewNode(NULL, "iq"));
- xmlNewNs(iq_stanza, "jabber:client", NULL);
- xmlNewProp(iq_stanza, "type", type);
+ rexmpp_xml_t *iq_stanza =
+ rexmpp_xml_new_elem("iq", "jabber:client");
+ rexmpp_xml_add_id(iq_stanza);
+ rexmpp_xml_add_attr(iq_stanza, "type", type);
if (to != NULL) {
- xmlNewProp(iq_stanza, "to", to);
+ rexmpp_xml_add_attr(iq_stanza, "to", to);
}
if (s->assigned_jid.full[0]) {
- xmlNewProp(iq_stanza, "from", s->assigned_jid.full);
+ rexmpp_xml_add_attr(iq_stanza, "from", s->assigned_jid.full);
}
- xmlAddChild(iq_stanza, payload);
+ rexmpp_xml_add_child(iq_stanza, payload);
rexmpp_iq_t *iq = malloc(sizeof(rexmpp_iq_t));
- iq->request = xmlCopyNode(iq_stanza, 1);
+ if (iq == NULL) {
+ return REXMPP_E_MALLOC;
+ }
+ iq->request = rexmpp_xml_clone(iq_stanza);
iq->cb = cb;
iq->cb_data = cb_data;
iq->next = s->active_iq;
@@ -1259,12 +1218,12 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
void rexmpp_iq_cache_cb (rexmpp_t *s,
void *cb_data,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
if (success && response != NULL) {
- xmlNodePtr prev_last = NULL, last = NULL, ciq = s->iq_cache;
+ rexmpp_xml_t *prev_last = NULL, *last = NULL, *ciq = s->iq_cache;
uint32_t size = 0;
while (ciq != NULL && ciq->next != NULL) {
prev_last = last;
@@ -1273,12 +1232,12 @@ void rexmpp_iq_cache_cb (rexmpp_t *s,
ciq = ciq->next->next;
}
if (size >= s->iq_queue_size && prev_last != NULL) {
- xmlFreeNode(last->next);
- xmlFreeNode(last);
+ rexmpp_xml_free(last->next);
+ rexmpp_xml_free(last);
prev_last->next->next = NULL;
}
- xmlNodePtr req = xmlCopyNode(request, 1);
- xmlNodePtr resp = xmlCopyNode(response, 1);
+ rexmpp_xml_t *req = rexmpp_xml_clone(request);
+ rexmpp_xml_t *resp = rexmpp_xml_clone(response);
req->next = resp;
resp->next = s->iq_cache;
s->iq_cache = req;
@@ -1293,24 +1252,22 @@ void rexmpp_iq_cache_cb (rexmpp_t *s,
rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
const char *type,
const char *to,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t cb,
void *cb_data,
int fresh)
{
if (! fresh) {
- xmlNodePtr ciq = s->iq_cache;
+ rexmpp_xml_t *ciq = s->iq_cache;
while (ciq != NULL && ciq->next != NULL) {
- xmlNodePtr ciq_pl = xmlFirstElementChild(ciq);
- char *ciq_type = xmlGetProp(ciq, "type");
- char *ciq_to = xmlGetProp(ciq, "to");
+ rexmpp_xml_t *ciq_pl = ciq->alt.elem.children;
+ const char *ciq_type = rexmpp_xml_find_attr_val(ciq, "type");
+ const char *ciq_to = rexmpp_xml_find_attr_val(ciq, "to");
int matches = (rexmpp_xml_eq(ciq_pl, payload) &&
strcmp(ciq_type, type) == 0 &&
strcmp(ciq_to, to) == 0);
- free(ciq_to);
- free(ciq_type);
if (matches) {
- xmlFreeNode(payload);
+ rexmpp_xml_free(payload);
if (cb != NULL) {
cb(s, cb_data, ciq, ciq->next, 1);
}
@@ -1328,24 +1285,23 @@ rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
rexmpp_err_t rexmpp_sm_ack (rexmpp_t *s) {
char buf[11];
- xmlNodePtr ack = xmlNewNode(NULL, "a");
- xmlNewNs(ack, "urn:xmpp:sm:3", NULL);
+ rexmpp_xml_t *ack = rexmpp_xml_new_elem("a", "urn:xmpp:sm:3");
snprintf(buf, 11, "%u", s->stanzas_in_count);
- xmlNewProp(ack, "h", buf);
+ rexmpp_xml_add_attr(ack, "h", buf);
return rexmpp_send(s, ack);
}
rexmpp_err_t rexmpp_sm_send_req (rexmpp_t *s) {
- xmlNodePtr ack = xmlNewNode(NULL, "r");
- xmlNewNs(ack, "urn:xmpp:sm:3", NULL);
- return rexmpp_send(s, ack);
+ rexmpp_xml_t *req = rexmpp_xml_new_elem("r", "urn:xmpp:sm:3");
+ return rexmpp_send(s, req);
}
-rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem);
+rexmpp_err_t rexmpp_process_element (rexmpp_t *s, rexmpp_xml_t *elem);
rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
char chunk_raw[4096], *chunk;
- ssize_t chunk_raw_len, chunk_len;
+ size_t chunk_len;
+ ssize_t chunk_raw_len;
int sasl_err;
rexmpp_tls_err_t recv_err;
rexmpp_err_t err = REXMPP_SUCCESS;
@@ -1355,12 +1311,12 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
do {
tls_was_active = (s->tls_state == REXMPP_TLS_ACTIVE);
if (tls_was_active) {
- recv_err = rexmpp_tls_recv(s, chunk_raw, 4096, &chunk_raw_len);
+ recv_err = rexmpp_tls_recv(s, s->tls, chunk_raw, 4096, &chunk_raw_len);
} else {
chunk_raw_len = recv(s->server_socket, chunk_raw, 4096, 0);
}
if (chunk_raw_len > 0) {
- s->last_network_activity = time(NULL);
+ clock_gettime(CLOCK_MONOTONIC, &(s->last_network_activity));
if (s->sasl_state == REXMPP_SASL_ACTIVE) {
sasl_err = rexmpp_sasl_decode(s, chunk_raw, chunk_raw_len,
&chunk, &chunk_len);
@@ -1372,13 +1328,13 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
chunk = chunk_raw;
chunk_len = chunk_raw_len;
}
- xmlParseChunk(s->xml_parser, chunk, chunk_len, 0);
+ rexmpp_xml_parser_feed(s->xml_parser, chunk, chunk_len, 0);
if (chunk != chunk_raw && chunk != NULL) {
free(chunk);
}
chunk = NULL;
- xmlNodePtr elem;
+ rexmpp_xml_t *elem;
for (elem = s->input_queue;
/* Skipping everything after an error. Might be better to
process it anyway, but it could lead to more errors if
@@ -1393,7 +1349,7 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
err = rexmpp_process_element(s, elem);
}
}
- xmlFreeNodeList(s->input_queue);
+ rexmpp_xml_free_list(s->input_queue);
s->input_queue = NULL;
s->input_queue_last = NULL;
if (err != REXMPP_SUCCESS && err != REXMPP_E_AGAIN) {
@@ -1402,9 +1358,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) {
@@ -1537,7 +1493,7 @@ rexmpp_process_tls_conn_err (rexmpp_t *s,
return rexmpp_stream_open(s);
} else {
/* A STARTTLS connection, restart the stream. */
- xmlCtxtResetPush(s->xml_parser, "", 0, "", "utf-8");
+ s->xml_parser = rexmpp_xml_parser_reset(s->xml_parser);
return rexmpp_stream_open(s);
}
} else {
@@ -1552,7 +1508,7 @@ rexmpp_err_t rexmpp_connected_to_server (rexmpp_t *s) {
"Connected to the server, the used address record was %s",
s->server_socket_dns_secure ? "secure" : "not secure");
s->reconnect_number = 0;
- xmlCtxtResetPush(s->xml_parser, "", 0, "", "utf-8");
+ s->xml_parser = rexmpp_xml_parser_reset(s->xml_parser);
if (s->tls_state == REXMPP_TLS_AWAITING_DIRECT) {
return rexmpp_process_tls_conn_err(s, rexmpp_tls_connect(s));
} else {
@@ -1631,10 +1587,10 @@ void rexmpp_srv_cb (rexmpp_t *s,
rexmpp_err_t rexmpp_resend_stanzas (rexmpp_t *s) {
uint32_t i, count;
rexmpp_err_t ret = REXMPP_SUCCESS;
- xmlNodePtr sq;
+ rexmpp_xml_t *sq;
count = s->stanzas_out_count - s->stanzas_out_acknowledged;
for (i = 0; i < count && s->stanza_queue != NULL; i++) {
- sq = xmlNextElementSibling(s->stanza_queue);
+ sq = s->stanza_queue->next;
ret = rexmpp_send(s, s->stanza_queue);
if (ret > REXMPP_E_AGAIN) {
return ret;
@@ -1651,12 +1607,11 @@ rexmpp_err_t rexmpp_resend_stanzas (rexmpp_t *s) {
return ret;
}
-void rexmpp_sm_handle_ack (rexmpp_t *s, xmlNodePtr elem) {
- char *h = xmlGetProp(elem, "h");
+void rexmpp_sm_handle_ack (rexmpp_t *s, rexmpp_xml_t *elem) {
+ const char *h = rexmpp_xml_find_attr_val(elem, "h");
if (h != NULL) {
uint32_t prev_ack = s->stanzas_out_acknowledged;
s->stanzas_out_acknowledged = strtoul(h, NULL, 10);
- xmlFree(h);
rexmpp_log(s, LOG_DEBUG,
"server acknowledged %u out of %u sent stanzas",
s->stanzas_out_acknowledged,
@@ -1665,8 +1620,8 @@ void rexmpp_sm_handle_ack (rexmpp_t *s, xmlNodePtr elem) {
if (prev_ack <= s->stanzas_out_acknowledged) {
uint32_t i;
for (i = prev_ack; i < s->stanzas_out_acknowledged; i++) {
- xmlNodePtr sq = xmlNextElementSibling(s->stanza_queue);
- xmlFreeNode(s->stanza_queue);
+ rexmpp_xml_t *sq = s->stanza_queue->next;
+ rexmpp_xml_free(s->stanza_queue);
s->stanza_queue = sq;
}
} else {
@@ -1685,8 +1640,8 @@ void rexmpp_sm_handle_ack (rexmpp_t *s, xmlNodePtr elem) {
void rexmpp_carbons_enabled (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -1703,8 +1658,8 @@ void rexmpp_carbons_enabled (rexmpp_t *s,
void rexmpp_pong (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -1716,15 +1671,15 @@ void rexmpp_pong (rexmpp_t *s,
void rexmpp_disco_carbons_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success) {
(void)ptr;
(void)req;
(void)response;
if (success) {
- xmlNodePtr carbons_enable =
- rexmpp_xml_new_node("enable", "urn:xmpp:carbons:2");
+ rexmpp_xml_t *carbons_enable =
+ rexmpp_xml_new_elem("enable", "urn:xmpp:carbons:2");
s->carbons_state = REXMPP_CARBONS_NEGOTIATION;
rexmpp_iq_new(s, "set", NULL, carbons_enable,
rexmpp_carbons_enabled, NULL);
@@ -1747,27 +1702,31 @@ void rexmpp_stream_is_ready(rexmpp_t *s) {
if (s->roster_cache_file != NULL) {
rexmpp_roster_cache_read(s);
}
- xmlNodePtr roster_query = xmlNewNode(NULL, "query");
- xmlNewNs(roster_query, "jabber:iq:roster", NULL);
+ rexmpp_xml_t *roster_query =
+ rexmpp_xml_new_elem("query", "jabber:iq:roster");
if (s->roster_ver != NULL) {
- xmlNewProp(roster_query, "ver", s->roster_ver);
+ rexmpp_xml_add_attr(roster_query, "ver", s->roster_ver);
} else {
- xmlNewProp(roster_query, "ver", "");
+ rexmpp_xml_add_attr(roster_query, "ver", "");
}
rexmpp_iq_new(s, "get", NULL,
roster_query, rexmpp_iq_roster_get, NULL);
}
- xmlNodePtr presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
+ rexmpp_xml_t *presence =
+ rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+
char *caps_hash = rexmpp_capabilities_hash(s, rexmpp_disco_info(s));
if (caps_hash != NULL) {
- xmlNodePtr c = xmlNewNode(NULL, "c");
- xmlNewNs(c, "http://jabber.org/protocol/caps", NULL);
- xmlNewProp(c, "hash", "sha-1");
- xmlNewProp(c, "node", s->disco_node);
- xmlNewProp(c, "ver", caps_hash);
- xmlAddChild(presence, c);
+ rexmpp_xml_t *c =
+ rexmpp_xml_new_elem("c", "http://jabber.org/protocol/caps");
+ rexmpp_xml_add_attr(c, "hash", "sha-1");
+ rexmpp_xml_add_attr(c, "node", s->disco_node);
+ rexmpp_xml_add_attr(c, "ver", caps_hash);
+ rexmpp_xml_add_child(presence, c);
free(caps_hash);
}
+
rexmpp_send(s, presence);
}
@@ -1775,8 +1734,8 @@ void rexmpp_stream_is_ready(rexmpp_t *s) {
https://tools.ietf.org/html/rfc6120#section-7 */
void rexmpp_bound (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -1787,24 +1746,22 @@ void rexmpp_bound (rexmpp_t *s,
return;
}
/* todo: handle errors */
- xmlNodePtr child = xmlFirstElementChild(response);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(response);
if (rexmpp_xml_match(child, "urn:ietf:params:xml:ns:xmpp-bind", "bind")) {
- xmlNodePtr jid = xmlFirstElementChild(child);
+ rexmpp_xml_t *jid = rexmpp_xml_first_elem_child(child);
if (rexmpp_xml_match(jid, "urn:ietf:params:xml:ns:xmpp-bind", "jid")) {
- char *jid_str = xmlNodeGetContent(jid);
+ const char *jid_str = rexmpp_xml_text_child(jid);
rexmpp_log(s, LOG_INFO, "jid: %s", jid_str);
rexmpp_jid_parse(jid_str, &(s->assigned_jid));
- free(jid_str);
}
if (s->stream_id == NULL &&
- (child = rexmpp_xml_find_child(s->stream_features, "urn:xmpp:sm:3",
- "sm"))) {
+ (rexmpp_xml_find_child(s->stream_features, "urn:xmpp:sm:3",
+ "sm") != NULL)) {
/* Try to resume a stream. */
s->sm_state = REXMPP_SM_NEGOTIATION;
s->stream_state = REXMPP_STREAM_SM_FULL;
- xmlNodePtr sm_enable = xmlNewNode(NULL, "enable");
- xmlNewNs(sm_enable, "urn:xmpp:sm:3", NULL);
- xmlNewProp(sm_enable, "resume", "true");
+ rexmpp_xml_t *sm_enable =
+ rexmpp_xml_new_elem("enable", "urn:xmpp:sm:3");
rexmpp_send(s, sm_enable);
s->stanzas_out_count = 0;
s->stanzas_out_acknowledged = 0;
@@ -1819,12 +1776,12 @@ void rexmpp_bound (rexmpp_t *s,
rexmpp_err_t rexmpp_stream_bind (rexmpp_t *s) {
/* Issue a bind request. */
s->stream_state = REXMPP_STREAM_BIND;
- xmlNodePtr bind_cmd = xmlNewNode(NULL, "bind");
- xmlNewNs(bind_cmd, "urn:ietf:params:xml:ns:xmpp-bind", NULL);
+ rexmpp_xml_t *bind_cmd =
+ rexmpp_xml_new_elem("bind", "urn:ietf:params:xml:ns:xmpp-bind");
return rexmpp_iq_new(s, "set", NULL, bind_cmd, rexmpp_bound, NULL);
}
-rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
+rexmpp_err_t rexmpp_process_element (rexmpp_t *s, rexmpp_xml_t *elem) {
rexmpp_console_on_recv(s, elem);
/* Stream negotiation,
@@ -1834,22 +1791,23 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
/* Remember features. */
if (s->stream_features != NULL) {
- xmlFreeNode(s->stream_features);
+ rexmpp_xml_free(s->stream_features);
}
- s->stream_features = xmlCopyNode(elem, 1);
+ s->stream_features = rexmpp_xml_clone(elem);
/* TODO: check for required features properly here. Currently
assuming that STARTTLS, SASL, and BIND (with an exception for
SM) are always required if they are present. */
- xmlNodePtr starttls =
+ rexmpp_xml_t *starttls =
rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-tls",
"starttls");
- xmlNodePtr sasl =
+ rexmpp_xml_t *sasl =
rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
"mechanisms");
- xmlNodePtr bind =
+ rexmpp_xml_t *bind =
rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-bind", "bind");
- xmlNodePtr sm = rexmpp_xml_find_child(elem, "urn:xmpp:sm:3", "sm");
+ rexmpp_xml_t *sm =
+ rexmpp_xml_find_child(elem, "urn:xmpp:sm:3", "sm");
if (starttls != NULL) {
/* Go for TLS, unless we're both trying to avoid it, and have
@@ -1857,8 +1815,8 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (! (s->tls_policy == REXMPP_TLS_AVOID &&
(sasl != NULL || bind != NULL || sm != NULL))) {
s->stream_state = REXMPP_STREAM_STARTTLS;
- xmlNodePtr starttls_cmd = xmlNewNode(NULL, "starttls");
- xmlNewNs(starttls_cmd, "urn:ietf:params:xml:ns:xmpp-tls", NULL);
+ rexmpp_xml_t *starttls_cmd =
+ rexmpp_xml_new_elem("starttls", "urn:ietf:params:xml:ns:xmpp-tls");
rexmpp_send(s, starttls_cmd);
return REXMPP_SUCCESS;
}
@@ -1872,7 +1830,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
}
/* Nothing to negotiate. */
- if (xmlFirstElementChild(elem) == NULL) {
+ if (rexmpp_xml_first_elem_child(elem) == NULL) {
rexmpp_stream_is_ready(s);
return REXMPP_SUCCESS;
}
@@ -1882,18 +1840,17 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
s->sasl_state = REXMPP_SASL_NEGOTIATION;
char mech_list[2048]; /* todo: perhaps grow it dynamically */
mech_list[0] = '\0';
- xmlNodePtr mechanism;
- for (mechanism = xmlFirstElementChild(sasl);
+ rexmpp_xml_t *mechanism;
+ for (mechanism = rexmpp_xml_first_elem_child(sasl);
mechanism != NULL;
- mechanism = xmlNextElementSibling(mechanism)) {
+ mechanism = rexmpp_xml_next_elem_sibling(mechanism)) {
if (rexmpp_xml_match(mechanism, "urn:ietf:params:xml:ns:xmpp-sasl",
"mechanism")) {
- char *mech_str = xmlNodeGetContent(mechanism);
+ const char *mech_str = rexmpp_xml_text_child(mechanism);
snprintf(mech_list + strlen(mech_list),
2048 - strlen(mech_list),
"%s ",
mech_str);
- free(mech_str);
}
}
const char *mech = rexmpp_sasl_suggest_mechanism(s, mech_list);
@@ -1912,10 +1869,10 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
s->sasl_state = REXMPP_SASL_ERROR;
return REXMPP_E_SASL;
}
- xmlNodePtr auth_cmd = xmlNewNode(NULL, "auth");
- xmlNewProp(auth_cmd, "mechanism", mech);
- xmlNewNs(auth_cmd, "urn:ietf:params:xml:ns:xmpp-sasl", NULL);
- xmlNodeAddContent(auth_cmd, sasl_buf);
+ rexmpp_xml_t *auth_cmd =
+ rexmpp_xml_new_elem("auth", "urn:ietf:params:xml:ns:xmpp-sasl");
+ rexmpp_xml_add_attr(auth_cmd, "mechanism", mech);
+ rexmpp_xml_add_text(auth_cmd, sasl_buf);
free(sasl_buf);
rexmpp_send(s, auth_cmd);
return REXMPP_SUCCESS;
@@ -1925,10 +1882,10 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
s->stream_state = REXMPP_STREAM_SM_RESUME;
char buf[11];
snprintf(buf, 11, "%u", s->stanzas_in_count);
- xmlNodePtr sm_resume = xmlNewNode(NULL, "resume");
- xmlNewNs(sm_resume, "urn:xmpp:sm:3", NULL);
- xmlNewProp(sm_resume, "previd", s->stream_id);
- xmlNewProp(sm_resume, "h", buf);
+ rexmpp_xml_t *sm_resume =
+ rexmpp_xml_new_elem("resume", "urn:xmpp:sm:3");
+ rexmpp_xml_add_attr(sm_resume, "previd", s->stream_id);
+ rexmpp_xml_add_attr(sm_resume, "h", buf);
rexmpp_send(s, sm_resume);
return REXMPP_SUCCESS;
}
@@ -1937,7 +1894,8 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
return rexmpp_stream_bind(s);
}
} else {
- rexmpp_log(s, LOG_ERR, "Expected stream features, received %s", elem->name);
+ rexmpp_log(s, LOG_ERR, "Expected stream features, received %s",
+ elem->alt.elem.qname.name);
return REXMPP_E_STREAM;
}
}
@@ -1947,18 +1905,18 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
should cancel further processing by the library (so we can send
errors for unhandled IQs here). */
if (rexmpp_xml_match(elem, "jabber:client", "iq")) {
- char *type = xmlGetProp(elem, "type");
+ const char *type = rexmpp_xml_find_attr_val(elem, "type");
/* IQ responses. */
if (strcmp(type, "result") == 0 || strcmp(type, "error") == 0) {
- char *id = xmlGetProp(elem, "id");
+ const char *id = rexmpp_xml_find_attr_val(elem, "id");
rexmpp_iq_t *req = s->active_iq, *prev_req = NULL;
int found = 0;
while (req != NULL && found == 0) {
- char *req_id = xmlGetProp(req->request, "id");
- char *req_to = xmlGetProp(req->request, "to");
- char *rep_from = xmlGetProp(elem, "from");
+ const char *req_id = rexmpp_xml_find_attr_val(req->request, "id");
+ const char *req_to = rexmpp_xml_find_attr_val(req->request, "to");
+ const char *rep_from = rexmpp_xml_find_attr_val(elem, "from");
rexmpp_iq_t *req_next = req->next;
- int id_matches = (strcmp(id, req_id) == 0);
+ int id_matches = (req_id != NULL) && (strcmp(id, req_id) == 0);
int jid_matches = 0;
if (rep_from == NULL) {
jid_matches = 1;
@@ -1980,29 +1938,20 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
/* Finish and free the IQ request structure. */
rexmpp_iq_finish(s, req, success, elem);
}
- if (req_to != NULL) {
- free(req_to);
- }
- if (rep_from != NULL) {
- free(rep_from);
- }
- free(req_id);
prev_req = req;
req = req_next;
}
- free(id);
} else if (! rexmpp_jingle_iq(s, elem)) {
if (strcmp(type, "set") == 0) {
- xmlNodePtr query = xmlFirstElementChild(elem);
+ rexmpp_xml_t *query = rexmpp_xml_first_elem_child(elem);
int from_server = 0;
- char *from = xmlGetProp(elem, "from");
+ const char *from = rexmpp_xml_find_attr_val(elem, "from");
if (from == NULL) {
from_server = 1;
} else {
if (strcmp(from, s->initial_jid.domain) == 0) {
from_server = 1;
}
- free(from);
}
if (from_server &&
s->manage_roster &&
@@ -2011,8 +1960,12 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (s->roster_ver != NULL) {
free(s->roster_ver);
}
- s->roster_ver = xmlGetProp(query, "ver");
- rexmpp_modify_roster(s, xmlFirstElementChild(query));
+ s->roster_ver = NULL;
+ const char *roster_ver = rexmpp_xml_find_attr_val(query, "ver");
+ if (roster_ver != NULL) {
+ s->roster_ver = strdup(roster_ver);
+ }
+ rexmpp_modify_roster(s, rexmpp_xml_first_elem_child(query));
/* todo: check for errors */
rexmpp_iq_reply(s, elem, "result", NULL);
if (s->roster_cache_file != NULL) {
@@ -2024,9 +1977,9 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_xml_error("cancel", "service-unavailable"));
}
} else if (strcmp(type, "get") == 0) {
- xmlNodePtr query = xmlFirstElementChild(elem);
+ rexmpp_xml_t *query = rexmpp_xml_first_elem_child(elem);
if (rexmpp_xml_match(query, "http://jabber.org/protocol/disco#info", "query")) {
- char *node = xmlGetProp(query, "node");
+ const char *node = rexmpp_xml_find_attr_val(query, "node");
char *caps_hash = rexmpp_capabilities_hash(s, rexmpp_disco_info(s));
if (node == NULL ||
(caps_hash != NULL &&
@@ -2035,12 +1988,13 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
strncmp(node, s->disco_node, strlen(s->disco_node)) == 0 &&
node[strlen(s->disco_node)] == '#' &&
strcmp(node + strlen(s->disco_node) + 1, caps_hash) == 0)) {
- xmlNodePtr result = xmlNewNode(NULL, "query");
- xmlNewNs(result, "http://jabber.org/protocol/disco#info", NULL);
+ rexmpp_xml_t *result =
+ rexmpp_xml_new_elem("query", "http://jabber.org/protocol/disco#info");
if (node != NULL) {
- xmlNewProp(result, "node", node);
+ rexmpp_xml_add_attr(result, "node", node);
}
- xmlAddChild(result, xmlCopyNodeList(rexmpp_disco_info(s)));
+ rexmpp_xml_add_child(result,
+ rexmpp_xml_clone_list(rexmpp_disco_info(s)));
rexmpp_iq_reply(s, elem, "result", result);
} else {
rexmpp_log(s, LOG_WARNING,
@@ -2051,20 +2005,17 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (caps_hash != NULL) {
free(caps_hash);
}
- if (node != NULL) {
- free(node);
- }
} else if (rexmpp_xml_match(query, "urn:xmpp:ping", "ping")) {
rexmpp_iq_reply(s, elem, "result", NULL);
} else if (rexmpp_xml_match(query, "jabber:iq:version", "query")) {
- xmlNodePtr reply = xmlNewNode(NULL, "query");
- xmlNewNs(reply, "jabber:iq:version", NULL);
- xmlNodePtr name = xmlNewNode(NULL, "name");
- xmlNodeAddContent(name, s->client_name);
- xmlAddChild(reply, name);
- xmlNodePtr version = xmlNewNode(NULL, "version");
- xmlNodeAddContent(version, s->client_version);
- xmlAddChild(reply, version);
+ rexmpp_xml_t *reply =
+ rexmpp_xml_new_elem("query", "jabber:iq:version");
+ rexmpp_xml_t *name = rexmpp_xml_new_elem("name", NULL);
+ rexmpp_xml_add_text(name, s->client_name);
+ rexmpp_xml_add_child(reply, name);
+ rexmpp_xml_t *version = rexmpp_xml_new_elem("version", NULL);
+ rexmpp_xml_add_text(version, s->client_version);
+ rexmpp_xml_add_child(reply, version);
rexmpp_iq_reply(s, elem, "result", reply);
} else {
/* An unknown request. */
@@ -2073,49 +2024,44 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
}
}
}
- free(type);
}
/* Incoming presence information. */
if (rexmpp_xml_match(elem, "jabber:client", "presence") &&
s->manage_roster &&
s->track_roster_presence) {
- char *from = xmlGetProp(elem, "from");
+ const char *from = rexmpp_xml_find_attr_val(elem, "from");
if (from != NULL) {
struct rexmpp_jid from_jid;
rexmpp_jid_parse(from, &from_jid);
- xmlFree(from);
if (rexmpp_roster_find_item(s, from_jid.bare, NULL) != NULL) {
/* The bare JID is in the roster. */
- char *type = xmlGetProp(elem, "type");
- xmlNodePtr cur, prev;
+ const char *type = rexmpp_xml_find_attr_val(elem, "type");
+ rexmpp_xml_t *cur, *prev;
if (type == NULL || strcmp(type, "unavailable") == 0) {
/* Either a new "available" presence or an "unavailable"
one: remove the previously stored presence for this
JID. */
for (prev = NULL, cur = s->roster_presence;
cur != NULL;
- prev = cur, cur = xmlNextElementSibling(cur)) {
- char *cur_from = xmlGetProp(cur, "from");
+ prev = cur, cur = cur->next) {
+ const char *cur_from = rexmpp_xml_find_attr_val(cur, "from");
if (strcmp(cur_from, from_jid.full) == 0) {
if (prev == NULL) {
s->roster_presence = cur->next;
} else {
prev->next = cur->next;
}
- xmlFreeNode(cur);
- cur = NULL;
+ rexmpp_xml_free(cur);
+ break;
}
- free(cur_from);
}
}
if (type == NULL) {
/* An "available" presence: add it. */
- xmlNodePtr presence = xmlCopyNode(elem, 1);
+ rexmpp_xml_t *presence = rexmpp_xml_clone(elem);
presence->next = s->roster_presence;
s->roster_presence = presence;
- } else {
- free(type);
}
}
}
@@ -2123,28 +2069,27 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
/* Incoming messages. */
if (rexmpp_xml_match(elem, "jabber:client", "message")) {
- char *from = xmlGetProp(elem, "from");
+ const char *from = rexmpp_xml_find_attr_val(elem, "from");
if (from != NULL) {
struct rexmpp_jid from_jid;
rexmpp_jid_parse(from, &from_jid);
- xmlFree(from);
if (rexmpp_roster_find_item(s, from_jid.bare, NULL) != NULL ||
strcmp(from_jid.bare, s->assigned_jid.bare) == 0) {
- xmlNodePtr event =
+ rexmpp_xml_t *event =
rexmpp_xml_find_child(elem,
"http://jabber.org/protocol/pubsub#event",
"event");
if (event != NULL && s->manage_roster && s->track_roster_events) {
- xmlNodePtr items =
+ rexmpp_xml_t *items =
rexmpp_xml_find_child(event,
"http://jabber.org/protocol/pubsub#event",
"items");
if (items != NULL) {
- char *node = xmlGetProp(items, "node");
+ const char *node = rexmpp_xml_find_attr_val(items, "node");
if (node != NULL) {
/* Remove the previously stored items for the same sender
and node, if any. */
- xmlNodePtr prev, cur;
+ rexmpp_xml_t *prev, *cur;
cur = rexmpp_find_event(s, from_jid.bare, node, &prev);
if (cur) {
if (prev == NULL) {
@@ -2152,12 +2097,12 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
} else {
prev->next = cur->next;
}
- xmlFreeNode(cur);
+ rexmpp_xml_free(cur);
cur = NULL;
}
/* Add the new message. */
- xmlNodePtr message = xmlCopyNode(elem, 1);
+ rexmpp_xml_t *message = rexmpp_xml_clone(elem);
message->next = s->roster_events;
s->roster_events = message;
@@ -2169,56 +2114,50 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (s->autojoin_bookmarked_mucs &&
strcmp(node, "urn:xmpp:bookmarks:1") == 0 &&
strcmp(from_jid.bare, s->assigned_jid.bare) == 0) {
- xmlNodePtr item;
- for (item = xmlFirstElementChild(items);
+ rexmpp_xml_t *item;
+ for (item = rexmpp_xml_first_elem_child(items);
item != NULL;
- item = xmlNextElementSibling(item)) {
- xmlNodePtr conference =
+ item = rexmpp_xml_next_elem_sibling(item)) {
+ rexmpp_xml_t *conference =
rexmpp_xml_find_child(item,
"urn:xmpp:bookmarks:1",
"conference");
if (conference == NULL) {
continue;
}
- char *item_id = xmlGetProp(item, "id");
+ const char *item_id = rexmpp_xml_find_attr_val(item, "id");
if (item_id == NULL) {
continue;
}
- char *autojoin = xmlGetProp(conference, "autojoin");
+ const char *autojoin =
+ rexmpp_xml_find_attr_val(conference, "autojoin");
if (autojoin == NULL) {
- free(item_id);
continue;
}
if (strcmp(autojoin, "true") == 0 ||
strcmp(autojoin, "1") == 0) {
- xmlNodePtr presence =
- rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "from", s->assigned_jid.full);
- xmlNodePtr nick =
+ rexmpp_xml_t *nick =
rexmpp_xml_find_child(conference,
"urn:xmpp:bookmarks:1",
"nick");
- char *nick_str;
+ const char *nick_str = NULL;
if (nick != NULL) {
- nick_str = xmlNodeGetContent(nick);
- } else {
- nick_str = strdup(s->initial_jid.local);
+ nick_str = rexmpp_xml_text_child(nick);
+ }
+ if (nick_str == NULL) {
+ nick_str = s->initial_jid.local;
}
- char *jid = malloc(strlen(item_id) + strlen(nick_str) + 2);
- sprintf(jid, "%s/%s", item_id, nick_str);
- free(nick_str);
- xmlNewProp(presence, "to", jid);
- free(jid);
- xmlNodePtr x = xmlNewNode(NULL, "x");
- xmlNewNs(x, "http://jabber.org/protocol/muc", NULL);
- xmlAddChild(presence, x);
- rexmpp_send(s, presence);
+ char *occupant_jid =
+ malloc(strlen(item_id) + strlen(nick_str) + 2);
+ sprintf(occupant_jid, "%s/%s", item_id, nick_str);
+ const char *password =
+ rexmpp_xml_find_attr_val(conference, "password");
+ rexmpp_muc_join(s, occupant_jid, password,
+ s->muc_ping_default_delay);
+ free(occupant_jid);
}
- free(item_id);
- free(autojoin);
}
}
- free(node);
}
}
}
@@ -2263,23 +2202,21 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
int sasl_err;
if (rexmpp_xml_match(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
"challenge")) {
- char *challenge = xmlNodeGetContent(elem);
+ const char *challenge = rexmpp_xml_text_child(elem);
sasl_err = rexmpp_sasl_step64 (s, challenge, (char**)&sasl_buf);
- free(challenge);
if (sasl_err) {
s->sasl_state = REXMPP_SASL_ERROR;
return REXMPP_E_SASL;
}
- xmlNodePtr response = xmlNewNode(NULL, "response");
- xmlNewNs(response, "urn:ietf:params:xml:ns:xmpp-sasl", NULL);
- xmlNodeAddContent(response, sasl_buf);
+ rexmpp_xml_t *response =
+ rexmpp_xml_new_elem("response", "urn:ietf:params:xml:ns:xmpp-sasl");
+ rexmpp_xml_add_text(response, sasl_buf);
free(sasl_buf);
- return rexmpp_send(s, response);
+ rexmpp_send(s, response);
} else if (rexmpp_xml_match(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
"success")) {
- char *success = xmlNodeGetContent(elem);
+ const char *success = rexmpp_xml_text_child(elem);
sasl_err = rexmpp_sasl_step64 (s, success, (char**)&sasl_buf);
- free(success);
free(sasl_buf);
if (! sasl_err) {
rexmpp_log(s, LOG_DEBUG, "SASL success");
@@ -2288,7 +2225,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
return REXMPP_E_SASL;
}
s->sasl_state = REXMPP_SASL_ACTIVE;
- xmlCtxtResetPush(s->xml_parser, "", 0, "", "utf-8");
+ s->xml_parser = rexmpp_xml_parser_reset(s->xml_parser);
return rexmpp_stream_open(s);
} else if (rexmpp_xml_match(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
"failure")) {
@@ -2302,20 +2239,23 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (s->stream_state == REXMPP_STREAM_SM_FULL) {
if (rexmpp_xml_match(elem, "urn:xmpp:sm:3", "enabled")) {
s->sm_state = REXMPP_SM_ACTIVE;
- char *resume = xmlGetProp(elem, "resume");
+ const char *resume = rexmpp_xml_find_attr_val(elem, "resume");
if (resume != NULL) {
if (s->stream_id != NULL) {
free(s->stream_id);
}
- s->stream_id = xmlGetProp(elem, "id");
- xmlFree(resume);
+ const char *stream_id = rexmpp_xml_find_attr_val(elem, "id");
+ s->stream_id = NULL;
+ if (stream_id != NULL) {
+ s->stream_id = strdup(stream_id);
+ }
}
rexmpp_stream_is_ready(s);
} else if (rexmpp_xml_match(elem, "urn:xmpp:sm:3", "failed")) {
s->stream_state = REXMPP_STREAM_SM_ACKS;
s->sm_state = REXMPP_SM_NEGOTIATION;
- xmlNodePtr sm_enable = xmlNewNode(NULL, "enable");
- xmlNewNs(sm_enable, "urn:xmpp:sm:3", NULL);
+ rexmpp_xml_t *sm_enable =
+ rexmpp_xml_new_elem("enable", "urn:xmpp:sm:3");
rexmpp_send(s, sm_enable);
}
} else if (s->stream_state == REXMPP_STREAM_SM_ACKS) {
@@ -2327,8 +2267,8 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
}
} else if (rexmpp_xml_match(elem, "urn:xmpp:sm:3", "failed")) {
s->sm_state = REXMPP_SM_INACTIVE;
- xmlNodePtr sm_enable = xmlNewNode(NULL, "enable");
- xmlNewNs(sm_enable, "urn:xmpp:sm:3", NULL);
+ rexmpp_xml_t *sm_enable =
+ rexmpp_xml_new_elem("enable", "urn:xmpp:sm:3");
rexmpp_send(s, sm_enable);
}
rexmpp_stream_is_ready(s);
@@ -2349,7 +2289,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
s->active_iq = next;
rexmpp_iq_finish(s, iq, 0, NULL);
}
- xmlNodePtr child =
+ rexmpp_xml_t *child =
rexmpp_xml_find_child(s->stream_features,
"urn:ietf:params:xml:ns:xmpp-bind",
"bind");
@@ -2371,69 +2311,69 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
}
-void rexmpp_sax_characters (rexmpp_t *s, const char *ch, int len)
+/* These SAX handlers are similar to those in rexmpp_xml.c, might be
+ nice to reuse them. */
+void rexmpp_sax_characters (rexmpp_t *s, const char *ch, size_t len)
{
if (s->current_element != NULL) {
- xmlNodeAddContentLen(s->current_element, ch, len);
+ rexmpp_xml_t *last_node = s->current_element->alt.elem.children;
+ if (last_node != NULL && last_node->type == REXMPP_XML_TEXT) {
+ /* The last child is textual as well, just extend it */
+ size_t last_len = strlen(last_node->alt.text);
+ char *new_alt_text = realloc(last_node->alt.text, last_len + len + 1);
+ if (new_alt_text == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to reallocate the XML element text buffer: %s",
+ strerror(errno));
+ return;
+ }
+ last_node->alt.text = new_alt_text;
+ strncpy(last_node->alt.text + last_len, ch, len);
+ last_node->alt.text[last_len + len] = '\0';
+ } else {
+ rexmpp_xml_t *text_node = rexmpp_xml_new_text_len(ch, len);
+ if (text_node != NULL) {
+ text_node->next = s->current_element->alt.elem.children;
+ s->current_element->alt.elem.children = text_node;
+ }
+ }
}
}
void rexmpp_sax_start_elem_ns (rexmpp_t *s,
- const char *localname,
- const char *prefix,
- const char *URI,
- int nb_namespaces,
- const char **namespaces,
- int nb_attributes,
- int nb_defaulted,
- const char **attributes)
+ const char *name,
+ const char *namespace,
+ rexmpp_xml_attr_t *attributes)
{
- /* Not checking namespaces beyond URI. */
- (void)nb_namespaces;
- (void)namespaces;
- (void)nb_defaulted;
-
- int i;
if (s->stream_state == REXMPP_STREAM_OPENING &&
- strcmp(localname, "stream") == 0 &&
- strcmp(URI, "http://etherx.jabber.org/streams") == 0) {
+ s->current_element == NULL &&
+ strcmp(name, "stream") == 0 &&
+ 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;
}
if (s->stream_state != REXMPP_STREAM_OPENING) {
if (s->current_element == NULL) {
- s->current_element = xmlNewNode(NULL, localname);
+ s->current_element = rexmpp_xml_new_elem(name, namespace);
s->current_element_root = s->current_element;
} else {
- xmlNodePtr node = xmlNewNode(NULL, localname);
- xmlAddChild(s->current_element, node);
+ rexmpp_xml_t *node = rexmpp_xml_new_elem(name, namespace);
+ node->next = s->current_element->alt.elem.children;
+ s->current_element->alt.elem.children = node;
s->current_element = node;
}
- xmlNsPtr ns = xmlNewNs(s->current_element, URI, prefix);
- s->current_element->ns = ns;
- for (i = 0; i < nb_attributes; 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);
- xmlNewProp(s->current_element, attributes[i * 5], attr_val);
- free(attr_val);
- }
+ s->current_element->alt.elem.attributes = attributes;
}
}
-void rexmpp_sax_end_elem_ns (rexmpp_t *s,
- const char *localname,
- const char *prefix,
- const char *URI)
+void rexmpp_sax_end_elem_ns (rexmpp_t *s)
{
- (void)prefix; /* Not interested in prefix here. */
if ((s->stream_state == REXMPP_STREAM_CLOSING ||
s->stream_state == REXMPP_STREAM_ERROR) &&
- strcmp(localname, "stream") == 0 &&
- strcmp(URI, "http://etherx.jabber.org/streams") == 0) {
+ s->current_element == NULL) {
rexmpp_log(s, LOG_DEBUG, "stream end");
if (s->sasl_state == REXMPP_SASL_ACTIVE) {
rexmpp_sasl_ctx_cleanup(s);
@@ -2453,8 +2393,16 @@ void rexmpp_sax_end_elem_ns (rexmpp_t *s,
}
if (s->current_element != s->current_element_root) {
- s->current_element = s->current_element->parent;
+ /* Find the parent, set it as current element. */
+ rexmpp_xml_t *parent = s->current_element_root;
+ while (parent->alt.elem.children != s->current_element) {
+ parent = parent->alt.elem.children;
+ }
+ s->current_element = parent;
} else {
+ /* Done parsing this element; reverse all the lists of children
+ and queue it. */
+ rexmpp_xml_reverse_children(s->current_element);
if (s->input_queue == NULL) {
s->input_queue = s->current_element;
s->input_queue_last = s->current_element;
@@ -2474,12 +2422,16 @@ rexmpp_err_t rexmpp_close (rexmpp_t *s) {
}
rexmpp_err_t rexmpp_stop (rexmpp_t *s) {
- s->stream_state = REXMPP_STREAM_CLOSE_REQUESTED;
if (s->stream_state == REXMPP_STREAM_READY) {
- xmlNodePtr presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "type", "unavailable");
+ rexmpp_xml_t *presence =
+ rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "type", "unavailable");
rexmpp_send(s, presence);
}
+
+ s->stream_state = REXMPP_STREAM_CLOSE_REQUESTED;
+
if (s->sm_state == REXMPP_SM_ACTIVE) {
int ret = rexmpp_sm_ack(s);
if (ret > REXMPP_E_AGAIN) {
@@ -2494,8 +2446,8 @@ rexmpp_err_t rexmpp_stop (rexmpp_t *s) {
}
rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
- struct timeval now;
- if (gettimeofday(&now, NULL) != 0) {
+ struct timespec now;
+ if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
rexmpp_log(s, LOG_ERR, "Failed to get time: %s", strerror(errno));
return REXMPP_E_OTHER;
}
@@ -2636,16 +2588,52 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
}
+ /* MUC self-pinging. */
+ if (s->tcp_state == REXMPP_TCP_CONNECTED &&
+ s->stream_state == REXMPP_STREAM_READY)
+ {
+ rexmpp_muc_ping_t *mp = s->muc_ping;
+ while (mp != NULL) {
+ if (mp->last_activity.tv_sec + mp->delay <= now.tv_sec) {
+ if (mp->requested == 0) {
+ clock_gettime(CLOCK_MONOTONIC, &(mp->last_activity));
+ mp->requested = 1;
+ rexmpp_xml_t *ping_cmd =
+ rexmpp_xml_new_elem("ping", "urn:xmpp:ping");
+ rexmpp_iq_new(s, "get", mp->jid,
+ ping_cmd, rexmpp_muc_pong, mp);
+ } else {
+ /* Requested already, and delay time passed again, without
+ a reply (not even an error). Warn the user, remove this
+ MUC. */
+ char *occupant_jid_to_remove = mp->jid;
+ rexmpp_log(s, LOG_WARNING,
+ "No MUC self-ping reply for %s, "
+ "disabling self-ping for it",
+ mp->jid);
+ mp = mp->next;
+ rexmpp_muc_ping_remove(s, occupant_jid_to_remove);
+ continue;
+ }
+ }
+ mp = mp->next;
+ }
+ }
+
/* Pinging the server. */
if (s->tcp_state == REXMPP_TCP_CONNECTED &&
- s->last_network_activity + s->ping_delay <= time(NULL)) {
+ s->stream_state == REXMPP_STREAM_READY &&
+ s->last_network_activity.tv_sec + s->ping_delay <= now.tv_sec) {
if (s->ping_requested == 0) {
s->ping_requested = 1;
- xmlNodePtr ping_cmd = xmlNewNode(NULL, "ping");
- xmlNewNs(ping_cmd, "urn:xmpp:ping", NULL);
+ rexmpp_xml_t *ping_cmd =
+ rexmpp_xml_new_elem("ping", "urn:xmpp:ping");
rexmpp_iq_new(s, "get", s->initial_jid.domain,
ping_cmd, rexmpp_pong, NULL);
} else {
+ /* Last network activity is updated on sending as well as on
+ receiving, so this will not be triggered right after sending
+ the request. */
rexmpp_log(s, LOG_WARNING, "Ping timeout, reconnecting.");
rexmpp_cleanup(s);
rexmpp_schedule_reconnect(s);
@@ -2694,12 +2682,13 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
if (s->tcp_state == REXMPP_TCP_CONNECTED &&
s->stream_state == REXMPP_STREAM_CLOSED &&
s->tls_state == REXMPP_TLS_CLOSING) {
- rexmpp_tls_err_t err = rexmpp_tls_disconnect(s);
+ 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;
}
@@ -2771,11 +2760,11 @@ int rexmpp_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
return max_fd;
}
-struct timeval *rexmpp_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv)
+struct timespec *rexmpp_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv)
{
- struct timeval *ret = max_tv;
+ struct timespec *ret = max_tv;
if (s->resolver_state != REXMPP_RESOLVER_NONE &&
s->resolver_state != REXMPP_RESOLVER_READY) {
@@ -2786,36 +2775,55 @@ struct timeval *rexmpp_timeout (rexmpp_t *s,
ret = rexmpp_jingle_timeout(s, ret, tv);
- struct timeval now;
- gettimeofday(&now, NULL);
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
if (s->reconnect_number > 0 &&
s->next_reconnect_time.tv_sec > now.tv_sec &&
(ret == NULL ||
s->next_reconnect_time.tv_sec - now.tv_sec < ret->tv_sec)) {
tv->tv_sec = s->next_reconnect_time.tv_sec - now.tv_sec;
- tv->tv_usec = 0;
+ tv->tv_nsec = 0;
ret = tv;
}
if (s->tcp_state == REXMPP_TCP_CONNECTED &&
- s->last_network_activity + s->ping_delay > now.tv_sec) {
- time_t next_ping = s->last_network_activity + s->ping_delay - now.tv_sec;
+ s->stream_state == REXMPP_STREAM_READY) {
+ rexmpp_muc_ping_t *mp = s->muc_ping;
+ while (mp != NULL) {
+ if (mp->last_activity.tv_sec + mp->delay > now.tv_sec) {
+ time_t next_ping =
+ mp->last_activity.tv_sec + mp->delay - now.tv_sec;
+ if (ret == NULL || next_ping < ret->tv_sec) {
+ tv->tv_sec = next_ping;
+ tv->tv_nsec = 0;
+ ret = tv;
+ }
+ }
+ mp = mp->next;
+ }
+ }
+
+ if (s->tcp_state == REXMPP_TCP_CONNECTED &&
+ s->stream_state == REXMPP_STREAM_READY &&
+ s->last_network_activity.tv_sec + s->ping_delay > now.tv_sec) {
+ time_t next_ping =
+ s->last_network_activity.tv_sec + s->ping_delay - now.tv_sec;
if (ret == NULL || next_ping < ret->tv_sec) {
tv->tv_sec = next_ping;
- tv->tv_usec = 0;
+ tv->tv_nsec = 0;
ret = tv;
}
}
#ifdef HAVE_CURL
- long curl_timeout;
+ long curl_timeout; /* in milliseconds */
curl_multi_timeout(s->curl_multi, &curl_timeout);
if (curl_timeout >= 0 &&
(curl_timeout / 1000 < ret->tv_sec ||
(curl_timeout / 1000 == ret->tv_sec &&
- (curl_timeout % 1000) * 1000 < ret->tv_usec))) {
+ (curl_timeout % 1000) * 1000000 < ret->tv_nsec))) {
tv->tv_sec = curl_timeout / 1000;
- tv->tv_usec = (curl_timeout % 1000) * 1000;
+ tv->tv_nsec = (curl_timeout % 1000) * 1000000;
ret = tv;
}
#endif
diff --git a/src/rexmpp.h b/src/rexmpp.h
index c6d4428..0aa6252 100644
--- a/src/rexmpp.h
+++ b/src/rexmpp.h
@@ -10,10 +10,10 @@
#define REXMPP_H
#include <stdint.h>
+#include <stdbool.h>
#include "config.h"
-#include <libxml/tree.h>
#ifdef HAVE_GPGME
#include <gpgme.h>
#endif
@@ -187,6 +187,8 @@ enum tls_pol {
typedef enum rexmpp_err rexmpp_err_t;
+#include "rexmpp_xml.h"
+#include "rexmpp_xml_parser.h"
#include "rexmpp_tcp.h"
#include "rexmpp_socks.h"
#include "rexmpp_dns.h"
@@ -207,8 +209,8 @@ typedef enum rexmpp_err rexmpp_err_t;
*/
typedef void (*rexmpp_iq_callback_t) (rexmpp_t *s,
void *cb_data,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success);
typedef struct rexmpp_iq rexmpp_iq_t;
@@ -217,7 +219,7 @@ typedef struct rexmpp_iq rexmpp_iq_t;
struct rexmpp_iq
{
/** @brief The sent request. */
- xmlNodePtr request;
+ rexmpp_xml_t *request;
/** @brief A callback to call on reply. */
rexmpp_iq_callback_t cb;
/** @brief User-supplied data, to pass to a callback function. */
@@ -226,12 +228,31 @@ struct rexmpp_iq
rexmpp_iq_t *next;
};
+typedef struct rexmpp_muc_ping rexmpp_muc_ping_t;
+
+/** @brief MUC self-ping data. */
+struct rexmpp_muc_ping
+{
+ /** @brief Own occupant JID to ping. */
+ char *jid;
+ /** @brief Optional password to rejoin with. */
+ char *password;
+ /** @brief Ping delay, in seconds. */
+ unsigned int delay;
+ /** @brief Whether a ping is requested (pending) already. */
+ int requested;
+ /** @brief When the MUC was active. */
+ struct timespec last_activity;
+ rexmpp_muc_ping_t *next;
+};
+
typedef void (*log_function_t) (rexmpp_t *s, int priority, const char *format, va_list args);
typedef int (*sasl_property_cb_t) (rexmpp_t *s, rexmpp_sasl_property prop);
-typedef int (*xml_in_cb_t) (rexmpp_t *s, xmlNodePtr node);
-typedef int (*xml_out_cb_t) (rexmpp_t *s, xmlNodePtr node);
-typedef void (*roster_modify_cb_t) (rexmpp_t *s, xmlNodePtr item);
+typedef int (*xml_in_cb_t) (rexmpp_t *s, rexmpp_xml_t *node);
+typedef int (*xml_out_cb_t) (rexmpp_t *s, rexmpp_xml_t *node);
+typedef void (*roster_modify_cb_t) (rexmpp_t *s, rexmpp_xml_t *item);
typedef int (*console_print_cb_t) (rexmpp_t *s, const char *format, va_list args);
+typedef void (*socket_cb_t) (rexmpp_t *s, int socket);
/** @brief Complete connection state */
struct rexmpp
@@ -253,7 +274,7 @@ struct rexmpp
/* Manual host/port configuration. */
const char *manual_host;
uint16_t manual_port;
- int manual_direct_tls;
+ bool manual_direct_tls;
/* Miscellaneous settings */
const char *disco_node;
@@ -263,21 +284,23 @@ struct rexmpp
uint16_t socks_port;
/* Various knobs (these are used instead of loadable modules). */
- int enable_carbons; /* XEP-0280 */
- int manage_roster;
+ bool enable_carbons; /* XEP-0280 */
+ bool manage_roster;
const char *roster_cache_file;
- int track_roster_presence;
- int track_roster_events; /* XEP-0163 */
- int nick_notifications; /* XEP-0172 */
- int retrieve_openpgp_keys; /* XEP-0373 */
- int autojoin_bookmarked_mucs; /* XEP-0402 */
+ bool track_roster_presence;
+ bool track_roster_events; /* XEP-0163 */
+ bool nick_notifications; /* XEP-0172 */
+ bool retrieve_openpgp_keys; /* XEP-0373 */
+ bool autojoin_bookmarked_mucs; /* XEP-0402 */
enum tls_pol tls_policy;
- int enable_jingle;
+ bool enable_jingle;
const char *client_name; /* XEP-0030, XEP-0092 */
const char *client_type; /* XEP-0030 */
const char *client_version; /* XEP-0092 */
const char *local_address; /* For ICE, XEP-0176 */
- int jingle_prefer_rtcp_mux;
+ bool jingle_prefer_rtcp_mux;
+ /* A delay in seconds, to use for MUC self-ping by default */
+ unsigned int muc_ping_default_delay;
/* Resource limits. */
uint32_t stanza_queue_size;
@@ -286,6 +309,12 @@ struct rexmpp
uint32_t iq_cache_size;
uint32_t max_jingle_sessions;
+ /* X.509 settings: for TLS and DTLS, to use for SASL EXTERNAL
+ authentication and DTLS-SRTP on Jingle calls. */
+ const char *x509_key_file;
+ const char *x509_cert_file;
+ const char *x509_trust_file;
+
/* Callbacks. */
log_function_t log_function;
sasl_property_cb_t sasl_property_cb;
@@ -293,44 +322,48 @@ struct rexmpp
xml_out_cb_t xml_out_cb;
roster_modify_cb_t roster_modify_cb;
console_print_cb_t console_print_cb;
+ socket_cb_t socket_cb;
/* Stream-related state. */
struct rexmpp_jid assigned_jid;
- xmlNodePtr stream_features;
- xmlNodePtr roster_items;
+ rexmpp_xml_t *stream_features;
+ rexmpp_xml_t *roster_items;
char *roster_ver;
- xmlNodePtr roster_presence;
- xmlNodePtr roster_events;
+ rexmpp_xml_t *roster_presence;
+ rexmpp_xml_t *roster_events;
/* Other dynamic data. */
- xmlNodePtr disco_info;
+ rexmpp_xml_t *disco_info;
/* Includes Jingle RTP session candidates; rexmpp prioritizes the
ones listed earlier on incoming calls. */
- xmlNodePtr jingle_rtp_description;
+ rexmpp_xml_t *jingle_rtp_description;
/* IQs we're waiting for responses to. */
rexmpp_iq_t *active_iq;
/* Cached IQ requests and responses. */
- xmlNodePtr iq_cache;
+ rexmpp_xml_t *iq_cache;
/* Jingle context. */
- rexmpp_jingle_ctx_t jingle;
+ rexmpp_jingle_ctx_t *jingle;
/* Connection and stream management. */
unsigned int reconnect_number;
time_t reconnect_seconds;
- struct timeval next_reconnect_time;
- xmlNodePtr stanza_queue;
+ struct timespec next_reconnect_time;
+ rexmpp_xml_t *stanza_queue;
uint32_t stanzas_out_count;
uint32_t stanzas_out_acknowledged;
uint32_t stanzas_in_count;
char *stream_id;
/* Server ping configuration and state. */
- int ping_delay;
- int ping_requested;
- time_t last_network_activity;
+ unsigned int ping_delay;
+ bool ping_requested;
+ struct timespec last_network_activity;
+
+ /* MUC self-ping */
+ rexmpp_muc_ping_t *muc_ping;
/* DNS-related structures. */
rexmpp_dns_ctx_t resolver;
@@ -348,7 +381,7 @@ struct rexmpp
int server_socket;
/* Whether the address it's connected to was verified with
DNSSEC. */
- int server_socket_dns_secure;
+ bool server_socket_dns_secure;
/* A structure used to establish a TCP connection. */
rexmpp_tcp_conn_t server_connection;
@@ -359,34 +392,42 @@ struct rexmpp
NULL if there is anything in the send queue). Not appending data
to it, see send_queue for queuing. */
char *send_buffer;
- ssize_t send_buffer_len;
- ssize_t send_buffer_sent;
+ size_t send_buffer_len;
+ size_t send_buffer_sent;
/* A queue of XML elements to send. */
- xmlNodePtr send_queue;
+ rexmpp_xml_t *send_queue;
+
+ /* An input queue of parsed XML structures. */
+ rexmpp_xml_t *input_queue;
+ rexmpp_xml_t *input_queue_last;
/* XML parser context, and current element pointer for building
XML nodes with a SAX2 parser interface. */
- xmlParserCtxtPtr xml_parser;
- xmlNodePtr current_element_root;
- xmlNodePtr current_element;
- xmlNodePtr input_queue;
- xmlNodePtr input_queue_last;
+ rexmpp_xml_parser_ctx_t xml_parser;
+
+ /* The children are stored in reverse order during building. */
+ rexmpp_xml_t *current_element_root;
+ rexmpp_xml_t *current_element;
/* TLS structures. */
- rexmpp_tls_t tls;
+ rexmpp_tls_t *tls;
/* SASL structures. */
- rexmpp_sasl_ctx_t sasl;
+ rexmpp_sasl_ctx_t *sasl;
/* OpenPGP structures */
#ifdef HAVE_GPGME
gpgme_ctx_t pgp_ctx;
+#else
+ void *pgp_ctx;
#endif
/* curl structures */
#ifdef HAVE_CURL
CURLM *curl_multi;
+#else
+ void *curl_multi;
#endif
};
@@ -429,7 +470,7 @@ rexmpp_err_t rexmpp_stop (rexmpp_t *s);
@param[in] node An XML element to send. The library assumes
ownership of the element, so it must not be freed by the caller.
*/
-rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node);
+rexmpp_err_t rexmpp_send (rexmpp_t *s, rexmpp_xml_t *node);
/**
@brief Prepare and send a new info/query request.
@@ -448,7 +489,7 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node);
rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
const char *type,
const char *to,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t cb,
void *cb_data);
@@ -460,7 +501,7 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s,
rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
const char *type,
const char *to,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t cb,
void *cb_data,
int fresh);
@@ -469,9 +510,9 @@ rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s,
@brief Reply to an IQ.
*/
void rexmpp_iq_reply (rexmpp_t *s,
- xmlNodePtr req,
+ rexmpp_xml_t *req,
const char *type,
- xmlNodePtr payload);
+ rexmpp_xml_t *payload);
/**
@brief Determines the maximum time to wait before the next
@@ -479,13 +520,13 @@ void rexmpp_iq_reply (rexmpp_t *s,
@param[in] s ::rexmpp
@param[in] max_tv An existing timeout (can be NULL), to return if
there's no more urgent timeouts.
- @param[in,out] tv An allocated timeval structure, to store the time
- in.
+ @param[in,out] tv An allocated timespec structure, to store the
+ time in.
@returns A pointer to either max_tv or tv.
*/
-struct timeval *rexmpp_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv);
+struct timespec *rexmpp_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv);
/**
@brief Sets file descriptors to watch.
@@ -500,35 +541,6 @@ struct timeval *rexmpp_timeout (rexmpp_t *s,
int rexmpp_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
/**
- @brief Compose an 'error' element.
-*/
-xmlNodePtr rexmpp_xml_error (const char *type, const char *condition);
-
-/**
- @brief A helper function for XML parsing.
- @param[in] str A string to parse.
- @param[in] str_len String length.
- @returns Parsed XML, or NULL on failure.
-*/
-xmlNodePtr rexmpp_xml_parse (const char *str, int str_len);
-
-/**
- @brief A helper function for XML serialisation.
- @param[in] node An XML node.
- @returns A string (must be freed by the caller).
-*/
-char *rexmpp_xml_serialize (xmlNodePtr node);
-
-/**
- @brief Adds an "id" attribute to an XML stanza.
- @param[in,out] s ::rexmpp
- @param[in] node A pointer to an XML stanza.
- @returns The same pointer as on input, for more convenient
- composition.
-*/
-xmlNodePtr rexmpp_xml_add_id (rexmpp_t *s, xmlNodePtr node);
-
-/**
@brief The logging function.
@param[in] s ::rexmpp
@param[in] priority A syslog priority.
@@ -547,42 +559,6 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...);
char *rexmpp_get_name (rexmpp_t *s, const char *jid_str);
/**
- @brief Compares two XML elements.
-*/
-int rexmpp_xml_eq (xmlNodePtr n1, xmlNodePtr n2);
-
-/**
- @brief Matches an XML node against a namespace and an element name.
- @param[in] node An XML node to match.
- @param[in] namespace An XML namespace. Can be NULL (matches
- anything), and it is assumed that the default namespace is
- "jabber:client" (so if it is "jabber:client" and an element doesn't
- have a namespace defined, this function counts that as a match).
- @param[in] name Element name. Can be NULL (matches anything).
- @returns 1 on a successful match, 0 otherwise.
-*/
-int rexmpp_xml_match (xmlNodePtr node,
- const char *namespace,
- const char *name);
-
-/**
- @brief Finds a child element of an XML node, which matches the
- given namespace and name.
- @param[in] node The node containing child nodes.
- @param[in] namespace The namespace to look for.
- @param[in] name The element name to look for.
- @returns A pointer to the first matching child node, or NULL if no
- matching child elements found.
-*/
-xmlNodePtr rexmpp_xml_find_child (xmlNodePtr node,
- const char *namespace,
- const char *name);
-
-xmlNodePtr rexmpp_xml_new_node (const char *name, const char *namespace);
-
-char *rexmpp_gen_id (rexmpp_t *s);
-
-/**
@brief Finds a PEP event.
@param[in] s ::rexmpp
@param[in] from JID.
@@ -591,10 +567,10 @@ char *rexmpp_gen_id (rexmpp_t *s);
@returns A pointer to the message announcing an event, or NULL on
failure.
*/
-xmlNodePtr rexmpp_find_event (rexmpp_t *s,
- const char *from,
- const char *node,
- xmlNodePtr *prev_event);
+rexmpp_xml_t *rexmpp_find_event (rexmpp_t *s,
+ const char *from,
+ const char *node,
+ rexmpp_xml_t **prev_event);
void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len);
@@ -632,4 +608,43 @@ rexmpp_disco_find_feature (rexmpp_t *s,
int fresh,
int max_requests);
+/**
+ @brief Add a MUC JID to self-ping
+ @param[in,out] s ::rexmpp
+ @param[in] jid Own occupant JID to ping
+ @param[in] password Optional password to rejoin with
+ @param[in] delay How often to ping, in seconds
+*/
+rexmpp_err_t rexmpp_muc_ping_set (rexmpp_t *s,
+ const char *occupant_jid,
+ const char *password,
+ unsigned int delay);
+
+/**
+ @brief Remove a MUC JID to self-ping
+ @param[in,out] s ::rexmpp
+ @param[in] jid Own occupant JID
+*/
+rexmpp_err_t rexmpp_muc_ping_remove (rexmpp_t *s,
+ const char *occupant_jid);
+
+/**
+ @brief Join a MUC, optionally setting self-ping
+ @param[in,out] s ::rexmpp
+ @param[in] occupant_jid Occupant JID
+ @param[in] password Optional password
+ @param[in] ping_delay MUC self-ping delay, 0 to not set it
+*/
+rexmpp_err_t rexmpp_muc_join (rexmpp_t *s,
+ const char *occupant_jid,
+ const char *password,
+ unsigned int ping_delay);
+
+/**
+ @brief Leave a MUC, stop self-pinging it
+ @param[in,out] s ::rexmpp
+ @param[in] occupant_jid Occupant JID
+*/
+rexmpp_err_t rexmpp_muc_leave (rexmpp_t *s, const char *occupant_jid);
+
#endif
diff --git a/src/rexmpp.rs b/src/rexmpp.rs
new file mode 100644
index 0000000..250da00
--- /dev/null
+++ b/src/rexmpp.rs
@@ -0,0 +1,280 @@
+extern crate libc;
+use std::os::raw::{c_char, c_int, c_void, c_uint};
+use libc::{time_t, timespec};
+
+use super::{rexmpp_jid, rexmpp_xml, rexmpp_dns, rexmpp_tcp, rexmpp_socks};
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum ResolverState {
+ Ready,
+ SRV,
+ SRV2,
+ Failure
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum TCPState {
+ None,
+ Connecting,
+ SOCKS,
+ Connected,
+ Closed,
+ ConnectionFailure,
+ Error
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum StreamState {
+ None,
+ Opening,
+ StartTLS,
+ SASL,
+ Bind,
+ SMFull,
+ SMAcks,
+ SMResume,
+ Ready,
+ CloseRequested,
+ Closing,
+ Closed,
+ Error,
+ ErrorReconnect
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum TLSState {
+ Inactive,
+ AwaitingDirect,
+ Handshake,
+ Active,
+ Closing,
+ Closed,
+ Error
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum SASLState {
+ Inactive,
+ Negotiation,
+ Active,
+ Error
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum SMState {
+ Inactive,
+ Negotiation,
+ Active
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum CarbonsState {
+ Inactive,
+ Negotiation,
+ Active
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum TLSPolicy {
+ Require,
+ Prefer,
+ Avoid
+}
+
+type IQCallback = unsafe extern "C"
+fn (s: *mut Rexmpp, cb_data: *mut c_void,
+ request: *mut rexmpp_xml::RexmppXML, response: *mut rexmpp_xml::RexmppXML,
+ success: c_int) -> ();
+
+type SocketCallback = unsafe extern "C"
+fn (s: *mut Rexmpp, socket: c_int) -> ();
+
+#[repr(C)]
+pub struct RexmppIQ {
+ pub requset: *mut rexmpp_xml::RexmppXML,
+ pub cb: IQCallback,
+ pub cb_data: *const c_void,
+ pub next: *mut RexmppIQ
+}
+
+#[repr(C)]
+pub struct RexmppMUCPing {
+ pub jid: *mut c_char,
+ pub password: *mut c_char,
+ pub delay: c_uint,
+ pub requested: bool,
+ pub last_activity: timespec,
+ pub next: *mut RexmppMUCPing
+}
+
+#[repr(C)]
+pub struct Rexmpp {
+ pub resolver_state: ResolverState,
+ pub tcp_state: TCPState,
+ pub stream_state: StreamState,
+ pub tls_state: TLSState,
+ pub sasl_state: SASLState,
+ pub sm_state: SMState,
+ pub carbons_state: CarbonsState,
+
+ // Basic configuration
+ pub initial_jid: rexmpp_jid::RexmppJID,
+
+ // Manual host/port configuration
+ pub manual_host: *const c_char,
+ pub manual_port: u16,
+ pub manual_direct_tls: bool,
+
+ // Miscellaneous settings
+ pub disco_node: *const c_char,
+
+ // SOCKS settings
+ pub socks_host: *const c_char,
+ pub socks_port: u16,
+
+ // Various knobs (these are used instead of loadable modules)
+ pub enable_carbons: bool, // XEP-0280
+ pub manage_roster: bool,
+ pub roster_cache_file: *const c_char,
+ pub track_roster_presence: bool,
+ pub track_roster_events: bool, // XEP-0163
+ pub nick_notifications: bool, // XEP-0172
+ pub retrieve_openpgp_keys: bool, // XEP-0373
+ pub autojoin_bookmarked_mucs: bool, // XEP-0402
+ pub tls_policy: TLSPolicy,
+ pub enable_jingle: bool,
+ pub client_name: *const c_char, // XEP-0030, XEP-0092
+ pub client_type: *const c_char, // XEP-0030
+ pub client_version: *const c_char, // XEP-0092
+ pub local_address: *const c_char, // For ICE, XEP-0176
+ pub jingle_prefer_rtcp_mux: bool,
+ // A delay in seconds, to use for MUC self-ping by default
+ pub muc_ping_default_delay: c_uint,
+ // Resource limits
+ pub stanza_queue_size: u32,
+ pub send_queue_size: u32,
+ pub iq_queue_size: u32,
+ pub iq_cache_size: u32,
+ pub max_jingle_sessions: u32,
+
+ // X.509 settings (for TLS and DTLS)
+ pub x509_key_file: *const c_char,
+ pub x509_cert_file: *const c_char,
+ pub x509_trust_file: *const c_char,
+
+ // Callbacks
+
+ // c_variadic is experimental and cannot be used on the stable
+ // release channel, so skipping the log function callback.
+ pub log_function: *const c_void,
+ // Actually skipping proper definitions of others for now as well
+ // (TODO: add them).
+ pub sasl_property_cb: *const c_void,
+ pub xml_in_cb: *const c_void,
+ pub xml_out_cb: *const c_void,
+ pub roster_modify_cb: *const c_void,
+ pub console_print_cb: *const c_void,
+ pub socket_cb: Option<SocketCallback>,
+
+ // Stream-related state
+ pub assigned_jid: rexmpp_jid::RexmppJID,
+ pub stream_features: *mut rexmpp_xml::RexmppXML,
+ pub roster_items: *mut rexmpp_xml::RexmppXML,
+ pub roster_ver: *mut c_char,
+ pub roster_presence: *mut rexmpp_xml::RexmppXML,
+ pub roster_events: *mut rexmpp_xml::RexmppXML,
+
+ // Other dynamic data
+ pub disco_info: *mut rexmpp_xml::RexmppXML,
+ // Includes Jingle RTP session candidates; rexmpp prioritizes the
+ // ones listed earlier on incoming calls
+ pub jingle_rtp_description: *mut rexmpp_xml::RexmppXML,
+
+ // IQs we're waiting for responses to
+ pub active_iq: *mut RexmppIQ,
+ pub iq_cache: *mut rexmpp_xml::RexmppXML,
+
+ // Jingle context
+ pub jingle: *const c_void, // TODO
+
+ // Connection and stream management
+ pub reconnect_number: c_uint,
+ pub reconnect_seconds: time_t,
+ pub next_reconnect_time: timespec,
+ pub stanza_queue: *mut rexmpp_xml::RexmppXML,
+ pub stanzas_out_count: u32,
+ pub stanzas_out_acknowledged: u32,
+ pub stanzas_in_count: u32,
+ pub stream_id: *mut c_char,
+
+ // Server ping configuration and state
+ pub ping_delay: c_uint,
+ pub ping_requested: bool,
+ pub last_network_activity: timespec,
+
+ // MUC self-ping
+ pub muc_ping: *mut RexmppMUCPing,
+
+ // DNS-related structures
+ pub resolver: *mut c_void,
+ pub server_srv: *mut rexmpp_dns::RexmppDNSResult,
+ pub server_srv_cur: c_int,
+ pub server_srv_tls: *mut rexmpp_dns::RexmppDNSResult,
+ pub server_srv_tls_cur: c_int,
+ pub server_active_srv: *mut rexmpp_dns::RexmppDNSSRV,
+
+ // The XMPP server we are connecting to
+ pub server_host: *const c_char,
+ pub server_port: u16,
+
+ // The primary socket used for communication with the server
+ pub server_socket: c_int,
+ // Whether the address it's connected to was verified with DNSSEC
+ pub server_socket_dns_secure: bool,
+
+ // A structure used to establish a TCP connection
+ pub server_connection: rexmpp_tcp::RexmppTCPConnection,
+ pub server_socks_conn: rexmpp_socks::RexmppSocks,
+
+ // Send buffer. NULL if there is nothing to send (and must not be
+ // NULL if there is anything in the send queue). Not appending
+ // data to it, see send_queue for queuing.
+ pub send_buffer: *mut c_char,
+ pub send_buffer_len: isize,
+ pub send_buffer_sent: isize,
+
+ // A queue of XML elements to send
+ pub send_queue: *mut rexmpp_xml::RexmppXML,
+
+ // An input queue of parsed XML structures
+ pub input_queue: *mut rexmpp_xml::RexmppXML,
+ pub input_queue_last: *mut rexmpp_xml::RexmppXML,
+
+ // XML parser context, and current element pointer for building
+ // XML nodes with a SAX2 parser interface
+ pub xml_parser: *mut c_void,
+
+ // The children are stored in reverse order during building
+ pub current_element_root: *mut rexmpp_xml::RexmppXML,
+ pub current_element: *mut rexmpp_xml::RexmppXML,
+
+ // TLS structures
+ pub tls: *mut c_void,
+
+ // SASL structures
+ pub sasl: *mut c_void,
+
+ // OpenPGP structures
+ pub pgp_ctx: *mut c_void,
+
+ // curl structures
+ pub curl_multi: *mut c_void
+}
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 f2d748f..a6f859a 100644
--- a/src/rexmpp_console.c
+++ b/src/rexmpp_console.c
@@ -11,11 +11,15 @@
*/
#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_openpgp.h"
#include "rexmpp_http_upload.h"
#include "rexmpp_jingle.h"
+#include "rexmpp_pubsub.h"
#include "rexmpp_console.h"
@@ -29,61 +33,60 @@ void rexmpp_console_printf (rexmpp_t *s, const char *format, ...)
}
}
-char *rexmpp_console_message_string (rexmpp_t *s, xmlNodePtr node) {
- char *ret = NULL;
- xmlNodePtr openpgp =
+const char *rexmpp_console_message_string (rexmpp_t *s, rexmpp_xml_t *node) {
+ const char *ret = NULL;
+ rexmpp_xml_t *openpgp =
rexmpp_xml_find_child(node, "urn:xmpp:openpgp:0", "openpgp");
if (openpgp != NULL) {
int valid;
- xmlNodePtr elem = rexmpp_openpgp_decrypt_verify_message(s, node, &valid);
+ rexmpp_xml_t *elem = rexmpp_openpgp_decrypt_verify_message(s, node, &valid);
if (! valid) {
rexmpp_console_printf(s, "An invalid OpenPGP message!\n");
}
if (elem != NULL) {
- xmlNodePtr payload =
+ rexmpp_xml_t *payload =
rexmpp_xml_find_child(elem, "urn:xmpp:openpgp:0", "payload");
if (payload != NULL) {
- xmlNodePtr pl_body =
+ rexmpp_xml_t *pl_body =
rexmpp_xml_find_child(payload, "jabber:client", "body");
if (pl_body != NULL) {
- ret = xmlNodeGetContent(pl_body);
+ ret = rexmpp_xml_text_child(pl_body);
}
}
- xmlFreeNode(elem);
+ rexmpp_xml_free(elem);
}
}
if (ret == NULL) {
- xmlNodePtr body = rexmpp_xml_find_child(node, "jabber:client", "body");
- ret = xmlNodeGetContent(body);
+ rexmpp_xml_t *body =
+ rexmpp_xml_find_child(node, "jabber:client", "body");
+ ret = rexmpp_xml_text_child(body);
}
return ret;
}
-void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node) {
+void rexmpp_console_on_send (rexmpp_t *s, rexmpp_xml_t *node) {
if (rexmpp_xml_match(node, "jabber:client", "message")) {
- char *to = xmlGetProp(node, "to");
+ const char *to = rexmpp_xml_find_attr_val(node, "to");
if (to != NULL) {
/* "from" should be set for verification. */
- char *from = xmlGetProp(node, "from");
- xmlAttrPtr fromProp = NULL;
- if (from == NULL) {
- fromProp = xmlNewProp(node, "from", to);
+ int added_from = 0;
+ if (rexmpp_xml_find_attr_val(node, "from") == NULL) {
+ rexmpp_xml_add_attr(node, "from", to);
+ added_from = 1;
}
- char *str = rexmpp_console_message_string(s, node);
- if (fromProp != NULL) {
- xmlRemoveProp(fromProp);
+ const char *str = rexmpp_console_message_string(s, node);
+ if (added_from) {
+ rexmpp_xml_remove_attr(node, "from");
}
if (str != NULL) {
rexmpp_console_printf(s, "You tell %s: %s\n", to, str);
- free(str);
}
- free(to);
}
}
if (rexmpp_xml_match(node, "jabber:client", "presence")) {
- char *presence_type = xmlGetProp(node, "type");
- char *presence_to = xmlGetProp(node, "to");
+ const char *presence_type = rexmpp_xml_find_attr_val(node, "type");
+ const char *presence_to = rexmpp_xml_find_attr_val(node, "to");
if (presence_to == NULL) {
rexmpp_console_printf(s, "Becoming %s\n",
(presence_type == NULL) ?
@@ -105,72 +108,58 @@ void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node) {
"Denying %s's presence subscription request.\n",
presence_to);
}
- free(presence_to);
- }
- if (presence_type != NULL) {
- free(presence_type);
}
}
}
-void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node) {
+void rexmpp_console_on_recv (rexmpp_t *s, rexmpp_xml_t *node) {
if (rexmpp_xml_match(node, "jabber:client", "message")) {
- xmlNodePtr sent = rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "sent");
+ rexmpp_xml_t *sent = rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "sent");
if (sent != NULL) {
- xmlNodePtr fwd =
+ rexmpp_xml_t *fwd =
rexmpp_xml_find_child(sent, "urn:xmpp:forward:0", "forwarded");
if (fwd != NULL) {
- xmlNodePtr msg =
+ rexmpp_xml_t *msg =
rexmpp_xml_find_child(fwd, "jabber:client", "message");
if (msg != NULL) {
- char *to = xmlGetProp(msg, "to");
- char *str = rexmpp_console_message_string(s, msg);
+ const char *to = rexmpp_xml_find_attr_val(msg, "to");
+ const char *str = rexmpp_console_message_string(s, msg);
if (str != NULL) {
rexmpp_console_printf(s, "You tell %s: %s\n", to, str);
- free(str);
- }
- if (to != NULL) {
- free(to);
}
}
}
}
- xmlNodePtr received =
+ rexmpp_xml_t *received =
rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "received");
if (received != NULL) {
- xmlNodePtr fwd =
+ rexmpp_xml_t *fwd =
rexmpp_xml_find_child(received, "urn:xmpp:forward:0", "forwarded");
if (fwd != NULL) {
- xmlNodePtr msg =
+ rexmpp_xml_t *msg =
rexmpp_xml_find_child(fwd, "jabber:client", "message");
if (msg != NULL) {
- char *from = xmlGetProp(msg, "from");
- char *str = rexmpp_console_message_string(s, msg);
+ const char *from = rexmpp_xml_find_attr_val(msg, "from");
+ const char *str = rexmpp_console_message_string(s, msg);
if (str != NULL) {
rexmpp_console_printf(s, "%s tells you: %s\n", from, str);
- free(str);
- }
- if (from != NULL) {
- free(from);
}
}
}
}
- char *from = xmlGetProp(node, "from");
+ const char *from = rexmpp_xml_find_attr_val(node, "from");
if (from != NULL) {
- char *str = rexmpp_console_message_string(s, node);
+ const char *str = rexmpp_console_message_string(s, node);
if (str != NULL) {
rexmpp_console_printf(s, "%s tells you: %s\n", from, str);
- free(str);
}
- free(from);
}
}
if (rexmpp_xml_match(node, "jabber:client", "presence")) {
- char *presence_type = xmlGetProp(node, "type");
- char *from = xmlGetProp(node, "from");
+ const char *presence_type = rexmpp_xml_find_attr_val(node, "type");
+ const char *from = rexmpp_xml_find_attr_val(node, "from");
if (presence_type != NULL && ! strcmp(presence_type, "subscribe")) {
rexmpp_console_printf(s, "%s requests a presence subscription\n", from);
} else if (presence_type != NULL && ! strcmp(presence_type, "subscribed")) {
@@ -182,74 +171,64 @@ void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node) {
(presence_type == NULL) ?
"available" :
presence_type);
- xmlNodePtr show = rexmpp_xml_find_child(node, "jabber:client", "show");
+ rexmpp_xml_t *show =
+ rexmpp_xml_find_child(node, "jabber:client", "show");
if (show != NULL) {
- char *show_str = xmlNodeGetContent(show);
- rexmpp_console_printf(s, " (%s)", show_str);
- free(show_str);
- show_str = NULL;
+ rexmpp_console_printf(s, " (%s)",
+ rexmpp_xml_text_child(show));
}
- xmlNodePtr status = rexmpp_xml_find_child(node, "jabber:client", "status");
+ rexmpp_xml_t *status =
+ rexmpp_xml_find_child(node, "jabber:client", "status");
if (status != NULL) {
- char *status_str = xmlNodeGetContent(status);
- rexmpp_console_printf(s, ": %s", status_str);
- free(status_str);
- status_str = NULL;
+ rexmpp_console_printf(s, ": %s",
+ rexmpp_xml_text_child(status));
}
rexmpp_console_printf(s, "\n");
}
- if (presence_type != NULL) {
- free(presence_type);
- }
- if (from != NULL) {
- free(from);
- }
}
}
void rexmpp_console_roster_deleted (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
(void)response;
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(rexmpp_xml_find_child(req,
"jabber:iq:roster",
"query"),
"jabber:iq:roster", "item");
- char *jid = xmlGetProp(item, "jid");
+ const char *jid = rexmpp_xml_find_attr_val(item, "jid");
if (success) {
rexmpp_console_printf(s, "Deleted %s from the roster.\n", jid);
} else {
rexmpp_console_printf(s, "Failed to delete %s from the roster.\n", jid);
}
- free(jid);
}
void rexmpp_console_roster_added (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
(void)response;
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(rexmpp_xml_find_child(req,
"jabber:iq:roster",
"query"),
"jabber:iq:roster", "item");
- char *jid = xmlGetProp(item, "jid");
+ const char *jid = rexmpp_xml_find_attr_val(item, "jid");
if (success) {
rexmpp_console_printf(s, "Added %s into the roster.\n", jid);
} else {
rexmpp_console_printf(s, "Failed to add %s into the roster.\n", jid);
}
- free(jid);
}
void rexmpp_console_on_run (rexmpp_t *s, rexmpp_err_t result) {
@@ -269,11 +248,210 @@ void rexmpp_console_on_upload (rexmpp_t *s, void *cb_data, const char *url) {
free(fpath);
}
+void rexmpp_console_disco_info (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ if (! success) {
+ rexmpp_console_printf(s, "Failed to discover info.\n");
+ return;
+ }
+ rexmpp_xml_t *query =
+ rexmpp_xml_find_child(response, "http://jabber.org/protocol/disco#info",
+ "query");
+ if (query == NULL) {
+ rexmpp_console_printf(s, "No disco#info query in response.\n");
+ return;
+ }
+ const char *from = rexmpp_xml_find_attr_val(response, "from");
+ if (from == NULL) {
+ rexmpp_console_printf(s, "No 'from' property in response.\n");
+ return;
+ }
+ rexmpp_console_printf(s, "Discovered info for %s:\n", from);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query);
+ while (child != NULL) {
+ if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#info",
+ "feature")) {
+ const char *var = rexmpp_xml_find_attr_val(child, "var");
+ rexmpp_console_printf(s, "- feature var %s\n", var);
+ } else if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#info",
+ "identity")) {
+ const char *category = rexmpp_xml_find_attr_val(child, "category");
+ const char *type = rexmpp_xml_find_attr_val(child, "type");
+ const char *name = rexmpp_xml_find_attr_val(child, "name");
+ rexmpp_console_printf(s, "- identity name %s, type %s, category %s\n",
+ name, type, category);
+ } else {
+ rexmpp_console_printf(s, "Encountered an unknown disco#info element.\n");
+ }
+ child = rexmpp_xml_next_elem_sibling(child);
+ }
+ rexmpp_console_printf(s, "(end of discovered info for %s)\n", from);
+}
+
+void rexmpp_console_disco_items (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ if (! success) {
+ rexmpp_console_printf(s, "Failed to discover items.\n");
+ return;
+ }
+ rexmpp_xml_t *query =
+ rexmpp_xml_find_child(response, "http://jabber.org/protocol/disco#items",
+ "query");
+ if (query == NULL) {
+ rexmpp_console_printf(s, "No disco#items query in response.\n");
+ return;
+ }
+ const char *from = rexmpp_xml_find_attr_val(response, "from");
+ if (from == NULL) {
+ rexmpp_console_printf(s, "No 'from' property in response.\n");
+ return;
+ }
+ rexmpp_console_printf(s, "Discovered items for %s:\n", from);
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query);
+ while (child != NULL) {
+ if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#items",
+ "item")) {
+ const char *jid = rexmpp_xml_find_attr_val(child, "jid");
+ const char *name = rexmpp_xml_find_attr_val(child, "name");
+ const char *node = rexmpp_xml_find_attr_val(child, "node");
+ rexmpp_console_printf(s, "- item jid %s, name %s, node %s\n", jid, name, node);
+ } else {
+ rexmpp_console_printf(s, "Encountered an unknown disco#items element.\n");
+ }
+ child = rexmpp_xml_next_elem_sibling(child);
+ }
+ rexmpp_console_printf(s, "(end of discovered items for %s)\n", from);
+}
+
+void rexmpp_console_pubsub_node_deleted (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Deleted the pubsub node.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to delete the pubsub node.\n");
+ }
+}
+
+void rexmpp_bookmark_added (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Added a bookmark.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to add a bookmark.\n");
+ }
+}
+
+void rexmpp_bookmark_removed (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Removed a bookmark.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to remove a bookmark.\n");
+ }
+}
+
+void rexmpp_console_blocklist (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ if (success) {
+ rexmpp_xml_t *bl =
+ rexmpp_xml_find_child(response, "urn:xmpp:blocking", "blocklist");
+ if (bl == NULL) {
+ rexmpp_console_printf(s, "No blocklist element in the response.\n");
+ return;
+ }
+ rexmpp_console_printf(s, "Block list:");
+ rexmpp_xml_t *child = rexmpp_xml_first_elem_child(bl);
+ while (child != NULL) {
+ if (rexmpp_xml_match(child, "urn:xmpp:blocking", "item")) {
+ const char *jid = rexmpp_xml_find_attr_val(child, "jid");
+ rexmpp_console_printf(s, " %s", jid);
+ } else {
+ rexmpp_console_printf(s, "Encountered an unknown blocklist child element.\n");
+ }
+ child = rexmpp_xml_next_elem_sibling(child);
+ }
+ rexmpp_console_printf(s, "\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to retrieve block list.\n");
+ }
+}
+
+void rexmpp_console_blocklist_blocked (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Blocklisted successfully.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to blocklist.\n");
+ }
+}
+
+void rexmpp_console_blocklist_unblocked (rexmpp_t *s,
+ void *ptr,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
+ int success)
+{
+ (void)ptr;
+ (void)req;
+ (void)response;
+ if (success) {
+ rexmpp_console_printf(s, "Un-blocklisted successfully.\n");
+ } else {
+ rexmpp_console_printf(s, "Failed to un-blocklist.\n");
+ }
+}
+
void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
/* todo: buffering */
(void)str_len; /* Unused for now (todo). */
char *words_save_ptr;
- xmlNodePtr presence;
+ rexmpp_xml_t *presence;
char *word, *jid_str, *msg_text;
struct rexmpp_jid jid;
word = strtok_r(str, " ", &words_save_ptr);
@@ -294,6 +472,8 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
"muc join <conference> [as] <nick>\n"
"muc leave <conference> [as] <nick>\n"
"muc tell <conference> <message>\n"
+ "bookmark add <conference> [autojoin] [nick] [password]\n"
+ "bookmark remove <conference>\n"
"roster list\n"
"roster add <jid>\n"
"roster delete <jid>\n"
@@ -305,8 +485,14 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
"jingle decline <sid>\n"
"jingle accept-file <sid> <file path>\n"
"jingle send-file <jid> <file path>\n"
- "jingle accept-call <sid> <in port> <out port>\n"
- "jingle call <jid> <in port> <out port>\n"
+ "jingle accept-call <sid>\n"
+ "jingle call <jid>\n"
+ "disco info <jid>\n"
+ "disco items <jid>\n"
+ "pubsub node delete <service_jid> <node>\n"
+ "blocklist\n"
+ "blocklist block <jid>\n"
+ "blocklist unblock <jid>\n"
;
if (! strcmp(word, "help")) {
@@ -337,11 +523,14 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
msg_text = jid_str + strlen(jid_str) + 1;
- xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
- xmlNewNs(msg, "jabber:client", NULL);
- xmlNewProp(msg, "to", jid.full);
- xmlNewProp(msg, "type", "chat");
- xmlNewTextChild(msg, NULL, "body", msg_text);
+
+ rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
+ 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);
+ rexmpp_xml_add_text(body, msg_text);
+ rexmpp_xml_add_child(msg, body);
rexmpp_send(s, msg);
}
@@ -353,9 +542,9 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
msg_text = jid_str + strlen(jid_str) + 1;
- xmlNodePtr body = xmlNewNode(NULL, "body");
- xmlNewNs(body, "jabber:client", NULL);
- xmlNodeAddContent(body, msg_text);
+ rexmpp_xml_t *body =
+ rexmpp_xml_new_elem("body", "jabber:client");
+ rexmpp_xml_add_text(body, msg_text);
const char *rcpt[2];
rcpt[0] = jid.full;
rcpt[1] = NULL;
@@ -367,21 +556,20 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
} else if (strcmp(word, "crypt") == 0) {
b64 = rexmpp_openpgp_payload(s, body, rcpt, NULL, REXMPP_OX_CRYPT);
}
- xmlNodePtr openpgp = xmlNewNode(NULL, "openpgp");
- openpgp->ns = xmlNewNs(openpgp, "urn:xmpp:openpgp:0", NULL);
- xmlNodeAddContent(openpgp, b64);
+ rexmpp_xml_t *openpgp =
+ rexmpp_xml_new_elem("openpgp", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_text(openpgp, b64);
free(b64);
- xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
- xmlNewNs(msg, "jabber:client", NULL);
- xmlNewProp(msg, "to", jid.full);
- xmlNewProp(msg, "type", "chat");
- xmlAddChild(msg, openpgp);
+ rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
+ 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);
- body = xmlNewNode(NULL, "body");
- xmlNewNs(body, "jabber:client", NULL);
- xmlNodeAddContent(body, "This is a secret message.");
- xmlAddChild(msg, body);
+ body = rexmpp_xml_new_elem("body", "jabber:client");
+ rexmpp_xml_add_text(body, "This is a secret message.");
+ rexmpp_xml_add_child(msg, body);
rexmpp_send(s, msg);
}
@@ -394,11 +582,14 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
msg_text = jid_str + strlen(jid_str) + 1;
- xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
- xmlNewNs(msg, "jabber:client", NULL);
- xmlNewProp(msg, "to", jid.full);
- xmlNewProp(msg, "type", "groupchat");
- xmlNewTextChild(msg, NULL, "body", msg_text);
+
+ rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client");
+ 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);
+ rexmpp_xml_add_text(body, msg_text);
+ rexmpp_xml_add_child(msg, body);
rexmpp_send(s, msg);
}
if (! strcmp(word, "join")) {
@@ -407,23 +598,20 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
if (! strcmp(word, "as")) {
word = strtok_r(NULL, " ", &words_save_ptr);
}
if (word == NULL) {
return;
}
- char *full_jid = malloc(strlen(jid.bare) + strlen(word) + 2);
- snprintf(full_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
+ char *occupant_jid = malloc(strlen(jid.bare) + strlen(word) + 2);
+ snprintf(occupant_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
jid.bare, word);
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "from", s->assigned_jid.full);
- xmlNewProp(presence, "to", full_jid);
- xmlNodePtr x = xmlNewNode(NULL, "x");
- xmlNewNs(x, "http://jabber.org/protocol/muc", NULL);
- xmlAddChild(presence, x);
- rexmpp_send(s, presence);
- free(full_jid);
+ rexmpp_muc_join(s, occupant_jid, NULL, s->muc_ping_default_delay);
+ free(occupant_jid);
}
if (! strcmp(word, "leave")) {
jid_str = strtok_r(NULL, " ", &words_save_ptr);
@@ -431,21 +619,58 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
if (! strcmp(word, "as")) {
word = strtok_r(NULL, " ", &words_save_ptr);
}
if (word == NULL) {
return;
}
- char *full_jid = malloc(strlen(jid.bare) + strlen(word) + 2);
- snprintf(full_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
+ char *occupant_jid = malloc(strlen(jid.bare) + strlen(word) + 2);
+ snprintf(occupant_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s",
jid.bare, word);
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "from", s->assigned_jid.full);
- xmlNewProp(presence, "to", full_jid);
- xmlNewProp(presence, "type", "unavailable");
- rexmpp_send(s, presence);
- free(full_jid);
+ rexmpp_muc_leave(s, occupant_jid);
+ free(occupant_jid);
+ }
+ }
+
+ if (! strcmp(word, "bookmark")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (! strcmp(word, "add")) {
+ char *muc_jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (muc_jid == NULL) {
+ return;
+ }
+ char *autojoin = strtok_r(NULL, " ", &words_save_ptr);
+ char *nick_str = strtok_r(NULL, " ", &words_save_ptr);
+ char *password = strtok_r(NULL, " ", &words_save_ptr);
+
+ rexmpp_xml_t *conference =
+ rexmpp_xml_new_elem("conference", "urn:xmpp:bookmarks:1");
+ if (autojoin != NULL) {
+ rexmpp_xml_add_attr(conference, "autojoin", autojoin);
+ }
+ if (password != NULL) {
+ rexmpp_xml_add_attr(conference, "password", password);
+ }
+ if (nick_str != NULL) {
+ rexmpp_xml_t *nick =
+ rexmpp_xml_new_elem("nick", "urn:xmpp:bookmarks:1");
+ rexmpp_xml_add_text(nick, nick_str);
+ rexmpp_xml_add_child(conference, nick);
+ }
+ rexmpp_pubsub_item_publish(s, NULL, "urn:xmpp:bookmarks:1", muc_jid,
+ conference, rexmpp_bookmark_added, NULL);
+ }
+ if (! strcmp(word, "remove")) {
+ char *muc_jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (muc_jid == NULL) {
+ return;
+ }
+ rexmpp_pubsub_item_retract(s, NULL, "urn:xmpp:bookmarks:1", muc_jid,
+ rexmpp_bookmark_removed, NULL);
}
}
@@ -455,25 +680,26 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
return;
}
if (! strcmp(word, "list")) {
- xmlNodePtr item;
+ rexmpp_xml_t *item;
for (item = s->roster_items;
item != NULL;
- item = xmlNextElementSibling(item)) {
- char *item_jid = xmlGetProp(item, "jid");
- char *item_ask = xmlGetProp(item, "ask");
- char *item_subscription = xmlGetProp(item, "subscription");
+ item = item->next) {
+ const char *item_jid = rexmpp_xml_find_attr_val(item, "jid");
+ const char *item_ask = rexmpp_xml_find_attr_val(item, "ask");
+ const char *item_subscription =
+ rexmpp_xml_find_attr_val(item, "subscription");
char *item_presence = "unavailable";
if (s->track_roster_presence) {
for (presence = s->roster_presence;
presence != NULL;
- presence = xmlNextElementSibling(presence)) {
- char *presence_from = xmlGetProp(presence, "from");
+ presence = presence->next) {
+ const char *presence_from =
+ rexmpp_xml_find_attr_val(presence, "from");
if (presence_from != NULL) {
rexmpp_jid_parse(presence_from, &jid);
if (! strcmp(jid.bare, item_jid)) {
item_presence = "available";
}
- free(presence_from);
}
}
}
@@ -482,28 +708,19 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
"presence = %s\n",
item_jid, item_subscription, item_ask,
item_presence);
- if (item_jid != NULL) {
- free(item_jid);
- }
- if (item_ask != NULL) {
- free(item_ask);
- }
- if (item_subscription != NULL) {
- free(item_subscription);
- }
}
} else if (! strcmp(word, "delete")) {
word = strtok_r(NULL, " ", &words_save_ptr);
if (word == NULL) {
return;
}
- xmlNodePtr delete_item = xmlNewNode(NULL, "item");
- delete_item->ns = xmlNewNs(delete_item, "jabber:iq:roster", NULL);
- xmlNewProp(delete_item, "jid", word);
- xmlNewProp(delete_item, "subscription", "remove");
- xmlNodePtr delete_query = xmlNewNode(NULL, "query");
- delete_query->ns = xmlNewNs(delete_query, "jabber:iq:roster", NULL);
- xmlAddChild(delete_query, delete_item);
+ rexmpp_xml_t *delete_item =
+ rexmpp_xml_new_elem("item", "jabber:iq:roster");
+ rexmpp_xml_add_attr(delete_item, "jid", word);
+ rexmpp_xml_add_attr(delete_item, "subscription", "remove");
+ rexmpp_xml_t *delete_query =
+ rexmpp_xml_new_elem("query", "jabber:iq:roster");
+ rexmpp_xml_add_child(delete_query, delete_item);
rexmpp_iq_new(s, "set", NULL, delete_query,
rexmpp_console_roster_deleted, NULL);
} else if (! strcmp(word, "add")) {
@@ -511,13 +728,13 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
if (word == NULL) {
return;
}
- xmlNodePtr delete_item = xmlNewNode(NULL, "item");
- delete_item->ns = xmlNewNs(delete_item, "jabber:iq:roster", NULL);
- xmlNewProp(delete_item, "jid", word);
- xmlNodePtr delete_query = xmlNewNode(NULL, "query");
- delete_query->ns = xmlNewNs(delete_query, "jabber:iq:roster", NULL);
- xmlAddChild(delete_query, delete_item);
- rexmpp_iq_new(s, "set", NULL, delete_query,
+ rexmpp_xml_t *add_item =
+ rexmpp_xml_new_elem("item", "jabber:iq:roster");
+ rexmpp_xml_add_attr(add_item, "jid", word);
+ rexmpp_xml_t *add_query =
+ rexmpp_xml_new_elem("query", "jabber:iq:roster");
+ rexmpp_xml_add_child(add_query, add_item);
+ rexmpp_iq_new(s, "set", NULL, add_query,
rexmpp_console_roster_added, NULL);
}
}
@@ -532,9 +749,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
if (word == NULL) {
return;
}
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "to", word);
- xmlNewProp(presence, "type", "subscribe");
+ presence = rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "to", word);
+ rexmpp_xml_add_attr(presence, "type", "subscribe");
rexmpp_send(s, presence);
}
if (! strcmp(word, "approve")) {
@@ -542,9 +760,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
if (word == NULL) {
return;
}
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "to", word);
- xmlNewProp(presence, "type", "subscribed");
+ presence = rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "to", word);
+ rexmpp_xml_add_attr(presence, "type", "subscribed");
rexmpp_send(s, presence);
}
if (! strcmp(word, "deny")) {
@@ -552,9 +771,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
if (word == NULL) {
return;
}
- presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "to", word);
- xmlNewProp(presence, "type", "unsubscribed");
+ presence = rexmpp_xml_new_elem("presence", "jabber:client");
+ rexmpp_xml_add_id(presence);
+ rexmpp_xml_add_attr(presence, "to", word);
+ rexmpp_xml_add_attr(presence, "type", "unsubscribed");
rexmpp_send(s, presence);
}
}
@@ -574,7 +794,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
char *sid = strtok_r(NULL, " ", &words_save_ptr);
if (sid != NULL) {
rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("success",
+ rexmpp_xml_new_elem("success",
"urn:xmpp:jingle:1"),
NULL);
}
@@ -582,7 +802,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
char *sid = strtok_r(NULL, " ", &words_save_ptr);
if (sid != NULL) {
rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("decline",
+ rexmpp_xml_new_elem("decline",
"urn:xmpp:jingle:1"),
NULL);
}
@@ -600,18 +820,95 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) {
}
} else if (! strcmp(word, "accept-call")) {
char *sid = strtok_r(NULL, " ", &words_save_ptr);
- char *port_in = strtok_r(NULL, " ", &words_save_ptr);
- char *port_out = strtok_r(NULL, " ", &words_save_ptr);
- if (sid != NULL && port_in != NULL && port_out != NULL) {
- rexmpp_jingle_call_accept(s, sid, atoi(port_in), atoi(port_out));
+ if (sid != NULL) {
+ rexmpp_jingle_call_accept(s, sid);
}
} else if (! strcmp(word, "call")) {
char *jid = strtok_r(NULL, " ", &words_save_ptr);
- char *port_in = strtok_r(NULL, " ", &words_save_ptr);
- char *port_out = strtok_r(NULL, " ", &words_save_ptr);
- if (jid != NULL && port_in != NULL && port_out != NULL) {
- rexmpp_jingle_call(s, jid, atoi(port_in), atoi(port_out));
+ if (jid != NULL) {
+ rexmpp_jingle_call(s, jid);
+ }
+ }
+ }
+
+ if (! strcmp(word, "disco")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
+ if (! strcmp(word, "info")) {
+ char *jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (jid == NULL) {
+ return;
+ }
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query",
+ "http://jabber.org/protocol/disco#info");
+ rexmpp_iq_new(s, "get", jid, query, rexmpp_console_disco_info, NULL);
+ }
+ if (! strcmp(word, "items")) {
+ char *jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (jid == NULL) {
+ return;
+ }
+ rexmpp_xml_t *query =
+ rexmpp_xml_new_elem("query",
+ "http://jabber.org/protocol/disco#items");
+ rexmpp_iq_new(s, "get", jid, query, rexmpp_console_disco_items, NULL);
+ }
+ }
+
+ if (! strcmp(word, "pubsub")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
+ if (! strcmp(word, "node")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ return;
+ }
+ if (! strcmp(word, "delete")) {
+ char *service_jid = strtok_r(NULL, " ", &words_save_ptr);
+ char *node = strtok_r(NULL, " ", &words_save_ptr);
+ if (service_jid == NULL || node == NULL) {
+ return;
+ }
+ rexmpp_pubsub_node_delete(s, service_jid, node, rexmpp_console_pubsub_node_deleted, NULL);
+ }
+ }
+ }
+
+ if (! strcmp(word, "blocklist")) {
+ word = strtok_r(NULL, " ", &words_save_ptr);
+ if (word == NULL) {
+ rexmpp_xml_t *bl =
+ rexmpp_xml_new_elem("blocklist", "urn:xmpp:blocking");
+ rexmpp_iq_new(s, "get", NULL, bl, rexmpp_console_blocklist, NULL);
+ } else if (! strcmp(word, "block")) {
+ char *jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (jid == NULL) {
+ return;
+ }
+ rexmpp_xml_t *bl =
+ rexmpp_xml_new_elem("block", "urn:xmpp:blocking");
+ rexmpp_xml_t *item =
+ rexmpp_xml_new_elem("item", "urn:xmpp:blocking");
+ rexmpp_xml_add_attr(item, "jid", jid);
+ rexmpp_xml_add_child(bl, item);
+ rexmpp_iq_new(s, "set", NULL, bl, rexmpp_console_blocklist_blocked, NULL);
+ } else if (! strcmp(word, "unblock")) {
+ char *jid = strtok_r(NULL, " ", &words_save_ptr);
+ if (jid == NULL) {
+ return;
}
+ rexmpp_xml_t *bl =
+ rexmpp_xml_new_elem("unblock", "urn:xmpp:blocking");
+ rexmpp_xml_t *item =
+ rexmpp_xml_new_elem("item", "urn:xmpp:blocking");
+ rexmpp_xml_add_attr(item, "jid", jid);
+ rexmpp_xml_add_child(bl, item);
+ rexmpp_iq_new(s, "set", NULL, bl, rexmpp_console_blocklist_unblocked, NULL);
}
}
}
diff --git a/src/rexmpp_console.h b/src/rexmpp_console.h
index 85e3d75..bb2aed7 100644
--- a/src/rexmpp_console.h
+++ b/src/rexmpp_console.h
@@ -11,8 +11,8 @@
#include "rexmpp.h"
-void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node);
-void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node);
+void rexmpp_console_on_send (rexmpp_t *s, rexmpp_xml_t *node);
+void rexmpp_console_on_recv (rexmpp_t *s, rexmpp_xml_t *node);
void rexmpp_console_on_run (rexmpp_t *s, rexmpp_err_t result);
void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len);
diff --git a/src/rexmpp_digest.c b/src/rexmpp_digest.c
new file mode 100644
index 0000000..c1dd436
--- /dev/null
+++ b/src/rexmpp_digest.c
@@ -0,0 +1,125 @@
+/**
+ @file rexmpp_digest.c
+ @brief Cryptographic functions
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include "config.h"
+#include "rexmpp_digest.h"
+
+#if defined(HAVE_GCRYPT)
+#include <gcrypt.h>
+#elif defined(HAVE_NETTLE)
+#include <nettle/nettle-meta.h>
+#include <stdlib.h>
+#elif defined(HAVE_OPENSSL)
+#include <openssl/evp.h>
+#endif
+
+size_t rexmpp_digest_len (rexmpp_digest_algorithm algo) {
+ switch (algo) {
+ case REXMPP_DIGEST_SHA1: return 20;
+ case REXMPP_DIGEST_SHA256: return 32;
+ case REXMPP_DIGEST_SHA3_256: return 32;
+ default: return 0;
+ }
+}
+
+int rexmpp_digest_buffer (rexmpp_digest_algorithm algo,
+ const void *in,
+ size_t in_len,
+ void *out,
+ size_t out_len)
+{
+ rexmpp_digest_t ctx;
+ int err = rexmpp_digest_init(&ctx, algo);
+ if (err) {
+ return err;
+ }
+ err = rexmpp_digest_update(&ctx, in, in_len);
+ if (err) {
+ return err;
+ }
+ return rexmpp_digest_finish(&ctx, out, out_len);
+}
+
+int rexmpp_digest_init (rexmpp_digest_t *ctx, rexmpp_digest_algorithm algo) {
+#if defined(HAVE_GCRYPT)
+ int gcry_algo = GCRY_MD_NONE;
+ switch (algo) {
+ case REXMPP_DIGEST_SHA1: gcry_algo = GCRY_MD_SHA1; break;
+ case REXMPP_DIGEST_SHA256: gcry_algo = GCRY_MD_SHA256; break;
+ case REXMPP_DIGEST_SHA3_256: gcry_algo = GCRY_MD_SHA3_256; break;
+ default: return -1;
+ }
+ gcry_error_t err = gcry_md_open(ctx, gcry_algo, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ return -1;
+ }
+#elif defined(HAVE_NETTLE)
+ ctx->nh = NULL;
+ switch (algo) {
+ case REXMPP_DIGEST_SHA1: ctx->nh = &nettle_sha1; break;
+ case REXMPP_DIGEST_SHA256: ctx->nh = &nettle_sha256; break;
+ case REXMPP_DIGEST_SHA3_256: ctx->nh = &nettle_sha3_256; break;
+ default: return -1;
+ }
+ ctx->nh_ctx = malloc(ctx->nh->context_size);
+ ctx->nh->init(ctx->nh_ctx);
+#elif defined(HAVE_OPENSSL)
+ const EVP_MD *md = NULL;
+ switch (algo) {
+ case REXMPP_DIGEST_SHA1: md = EVP_sha1(); break;
+ case REXMPP_DIGEST_SHA256: md = EVP_sha256(); break;
+ case REXMPP_DIGEST_SHA3_256: md = EVP_sha3_256(); break;
+ default: return -1;
+ }
+ *ctx = EVP_MD_CTX_new();
+ if (! EVP_DigestInit(*ctx, md)) {
+ EVP_MD_CTX_free(*ctx);
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int rexmpp_digest_update (rexmpp_digest_t *ctx, const void *in, size_t len) {
+#if defined(HAVE_GCRYPT)
+ gcry_md_write(*ctx, in, len);
+#elif defined(HAVE_NETTLE)
+ ctx->nh->update(ctx->nh_ctx, len, in);
+#elif defined(HAVE_OPENSSL)
+ if (! EVP_DigestUpdate(*ctx, in, len)) {
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int rexmpp_digest_finish (rexmpp_digest_t *ctx, void *out, size_t len) {
+ int ret = 0;
+#if defined(HAVE_GCRYPT)
+ if (out != NULL) {
+ unsigned char *result = gcry_md_read(*ctx, 0);
+ if (result != NULL) {
+ memcpy(out, result, len);
+ } else {
+ ret = -1;
+ }
+ }
+ gcry_md_close(*ctx);
+#elif defined(HAVE_NETTLE)
+ ctx->nh->digest(ctx->nh_ctx, len, out);
+ free(ctx->nh_ctx);
+#elif defined(HAVE_OPENSSL)
+ (void)len;
+ if (! EVP_DigestFinal_ex(*ctx, out, NULL)) {
+ ret = -1;
+ }
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+#endif
+ return ret;
+}
diff --git a/src/rexmpp_digest.h b/src/rexmpp_digest.h
new file mode 100644
index 0000000..5001e12
--- /dev/null
+++ b/src/rexmpp_digest.h
@@ -0,0 +1,85 @@
+/**
+ @file rexmpp_digest.h
+ @brief Cryptographic functions
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_DIGEST_H
+#define REXMPP_DIGEST_H
+
+typedef enum {
+ REXMPP_DIGEST_SHA1,
+ REXMPP_DIGEST_SHA256,
+ REXMPP_DIGEST_SHA3_256
+} rexmpp_digest_algorithm;
+
+
+#if defined(HAVE_GCRYPT)
+#include <gcrypt.h>
+typedef gcry_md_hd_t rexmpp_digest_t;
+#elif defined(HAVE_NETTLE)
+#include <nettle/nettle-meta.h>
+struct rexmpp_digest {
+ const struct nettle_hash *nh;
+ void *nh_ctx;
+};
+typedef struct rexmpp_digest rexmpp_digest_t;
+#elif defined(HAVE_OPENSSL)
+#include <openssl/evp.h>
+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 5c3f4f6..d56aa10 100644
--- a/src/rexmpp_dns.c
+++ b/src/rexmpp_dns.c
@@ -7,6 +7,7 @@
*/
#include <memory.h>
+#include <stdlib.h>
#include <syslog.h>
#include "config.h"
@@ -64,6 +65,7 @@ int rexmpp_parse_srv (char *in, int in_len, struct rexmpp_dns_srv *out) {
}
+#ifndef USE_RUST
void rexmpp_dns_result_free (rexmpp_dns_result_t *result) {
if (result->data != NULL) {
int i;
@@ -79,20 +81,37 @@ void rexmpp_dns_result_free (rexmpp_dns_result_t *result) {
}
free(result);
}
+#endif
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;
@@ -102,22 +121,22 @@ rexmpp_dns_result_t *result_from_hostent (struct hostent *hostinfo) {
int rexmpp_dns_ctx_init (rexmpp_t *s) {
#if defined(USE_UNBOUND)
int err;
- s->resolver.ctx = ub_ctx_create();
- if (s->resolver.ctx == NULL) {
+ s->resolver = ub_ctx_create();
+ if (s->resolver == NULL) {
rexmpp_log(s, LOG_CRIT, "Failed to create resolver context");
return 1;
}
- err = ub_ctx_resolvconf(s->resolver.ctx, NULL);
+ err = ub_ctx_resolvconf(s->resolver, NULL);
if (err != 0) {
rexmpp_log(s, LOG_WARNING, "Failed to read resolv.conf: %s",
ub_strerror(err));
}
- err = ub_ctx_hosts(s->resolver.ctx, NULL);
+ err = ub_ctx_hosts(s->resolver, NULL);
if (err != 0) {
rexmpp_log(s, LOG_WARNING, "Failed to read hosts file: %s",
ub_strerror(err));
}
- err = ub_ctx_add_ta_file(s->resolver.ctx, DNSSEC_TRUST_ANCHOR_FILE);
+ err = ub_ctx_add_ta_file(s->resolver, DNSSEC_TRUST_ANCHOR_FILE);
if (err != 0) {
rexmpp_log(s, LOG_WARNING, "Failed to set root key file for DNSSEC: %s",
ub_strerror(err));
@@ -130,7 +149,7 @@ int rexmpp_dns_ctx_init (rexmpp_t *s) {
ares_strerror(err));
return 1;
}
- err = ares_init(&(s->resolver.channel));
+ err = ares_init(&(s->resolver));
if (err) {
rexmpp_log(s, LOG_CRIT, "ares channel initialisation error: %s",
ares_strerror(err));
@@ -139,7 +158,7 @@ int rexmpp_dns_ctx_init (rexmpp_t *s) {
}
return 0;
#else
- (void)s;
+ s->resolver = NULL;
return 0;
#endif
}
@@ -151,12 +170,15 @@ void rexmpp_dns_ctx_cleanup (rexmpp_t *s) {
void rexmpp_dns_ctx_deinit (rexmpp_t *s) {
#if defined(USE_UNBOUND)
- if (s->resolver.ctx != NULL) {
- ub_ctx_delete(s->resolver.ctx);
- s->resolver.ctx = NULL;
+ if (s->resolver != NULL) {
+ ub_ctx_delete(s->resolver);
+ s->resolver = NULL;
}
#elif defined(USE_CARES)
- ares_destroy(s->resolver.channel);
+ if (s->resolver != NULL) {
+ ares_destroy(s->resolver);
+ s->resolver = NULL;
+ }
ares_library_cleanup();
#else
(void)s;
@@ -166,13 +188,13 @@ void rexmpp_dns_ctx_deinit (rexmpp_t *s) {
int rexmpp_dns_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#if defined(USE_UNBOUND)
(void)write_fds;
- int max_fd = ub_fd(s->resolver.ctx) + 1;
+ int max_fd = ub_fd(s->resolver) + 1;
if (max_fd != 0) {
FD_SET(max_fd - 1, read_fds);
}
return max_fd;
#elif defined(USE_CARES)
- return ares_fds(s->resolver.channel, read_fds, write_fds);
+ return ares_fds(s->resolver, read_fds, write_fds);
#else
(void)s;
(void)read_fds;
@@ -181,16 +203,30 @@ int rexmpp_dns_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#endif
}
-struct timeval * rexmpp_dns_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv)
+struct timespec * rexmpp_dns_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv)
{
#if defined(USE_UNBOUND)
(void)s;
(void)tv;
return max_tv;
#elif defined(USE_CARES)
- return ares_timeout(s->resolver.channel, max_tv, tv);
+ struct timeval tv_ms;
+ struct timeval *max_tv_ms = NULL;
+ if (max_tv != NULL) {
+ max_tv_ms = &tv_ms;
+ tv_ms.tv_sec = tv->tv_sec;
+ tv_ms.tv_usec = tv->tv_nsec / 1000;
+ }
+ struct timeval *ret_ms = ares_timeout(s->resolver, max_tv_ms, &tv_ms);
+ if (ret_ms == max_tv_ms) {
+ return max_tv;
+ } else {
+ tv->tv_sec = tv_ms.tv_sec;
+ tv->tv_nsec = tv_ms.tv_usec * 1000;
+ return tv;
+ }
#else
(void)s;
(void)max_tv;
@@ -237,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;
}
@@ -258,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;
@@ -332,10 +407,14 @@ 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;
- int err = ub_resolve_async(s->resolver.ctx, query, rrtype, rrclass,
+ int err = ub_resolve_async(s->resolver, query, rrtype, rrclass,
d, rexmpp_dns_cb, NULL);
if (err) {
rexmpp_log(s, LOG_ERR, "Failed to query %s: %s",
@@ -345,10 +424,14 @@ 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;
- ares_query(s->resolver.channel, query, rrclass, rrtype, rexmpp_dns_cb, d);
+ ares_query(s->resolver, query, rrclass, rrtype, rexmpp_dns_cb, d);
#else
if (rrclass == 1) {
if (rrtype == 1 || rrtype == 28) {
@@ -381,8 +464,8 @@ int rexmpp_dns_process (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#if defined(USE_UNBOUND)
(void)read_fds;
(void)write_fds;
- if (ub_poll(s->resolver.ctx)) {
- int err = ub_process(s->resolver.ctx);
+ if (ub_poll(s->resolver)) {
+ int err = ub_process(s->resolver);
if (err != 0) {
rexmpp_log(s, LOG_ERR, "DNS query processing error: %s",
ub_strerror(err));
@@ -391,7 +474,7 @@ int rexmpp_dns_process (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
return 0;
#elif defined(USE_CARES)
- ares_process(s->resolver.channel, read_fds, write_fds);
+ ares_process(s->resolver, read_fds, write_fds);
return 0;
#else
(void)s;
diff --git a/src/rexmpp_dns.h b/src/rexmpp_dns.h
index 6641238..7abd2ef 100644
--- a/src/rexmpp_dns.h
+++ b/src/rexmpp_dns.h
@@ -12,6 +12,7 @@
#define REXMPP_DNS_H
#include <stdint.h>
+#include <stdbool.h>
#include "config.h"
#include "rexmpp.h"
@@ -21,21 +22,21 @@
*/
#if defined(USE_UNBOUND)
#include <unbound.h>
-struct rexmpp_dns_ctx {
- struct ub_ctx *ctx;
-};
+typedef struct ub_ctx* rexmpp_dns_ctx_t;
+/* struct rexmpp_dns_ctx { */
+/* struct ub_ctx *ctx; */
+/* }; */
#elif defined(USE_CARES)
#include <ares.h>
-struct rexmpp_dns_ctx {
- ares_channel channel;
-};
+typedef ares_channel rexmpp_dns_ctx_t;
+/* struct rexmpp_dns_ctx { */
+/* ares_channel channel; */
+/* }; */
#else
-struct rexmpp_dns_ctx {
- int dummy;
-};
+typedef void* rexmpp_dns_ctx_t;
#endif
-typedef struct rexmpp_dns_ctx rexmpp_dns_ctx_t;
+/* typedef struct rexmpp_dns_ctx rexmpp_dns_ctx_t; */
struct rexmpp_dns_srv {
uint16_t priority;
@@ -58,7 +59,7 @@ struct rexmpp_dns_result {
int *len;
/** @brief Whether the result was retrieved securely (that is,
verified with DNSSEC). */
- int secure;
+ bool secure;
};
typedef struct rexmpp_dns_result rexmpp_dns_result_t;
@@ -103,9 +104,9 @@ int rexmpp_dns_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
/**
@brief Reports timeouts.
*/
-struct timeval * rexmpp_dns_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv);
+struct timespec * rexmpp_dns_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv);
typedef void (*dns_query_cb_t) (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *result);
diff --git a/src/rexmpp_dns.rs b/src/rexmpp_dns.rs
new file mode 100644
index 0000000..1489835
--- /dev/null
+++ b/src/rexmpp_dns.rs
@@ -0,0 +1,62 @@
+use std::os::raw::{c_int, c_void, c_char};
+use libc::*;
+use std::ptr;
+
+use super::rexmpp;
+
+type DNSQueryCB = unsafe extern "C"
+fn (s: *mut rexmpp::Rexmpp, ptr: *mut c_void, result: *mut RexmppDNSResult) -> ();
+
+extern {
+ pub fn rexmpp_dns_resolve (s: *mut rexmpp::Rexmpp,
+ query: *const c_char,
+ rrtype: c_int,
+ rrclass: c_int,
+ ptr: *mut c_void,
+ callback: DNSQueryCB) -> c_int;
+ pub fn rexmpp_dns_process (s: *mut rexmpp::Rexmpp,
+ read_fds: *mut fd_set,
+ write_fds: *mut fd_set) -> c_int;
+ pub fn rexmpp_dns_fds (s: *mut rexmpp::Rexmpp,
+ read_fds: *mut fd_set,
+ write_fds: *mut fd_set) -> c_int;
+ pub fn rexmpp_dns_timeout (s: *mut rexmpp::Rexmpp,
+ max_tv: *mut timespec,
+ tv: *mut timespec) -> *mut timespec;
+}
+
+#[repr(C)]
+pub struct RexmppDNSResult {
+ pub data: *mut *mut c_void,
+ pub len: *mut c_int,
+ pub secure: bool
+}
+
+#[repr(C)]
+pub struct RexmppDNSSRV {
+ pub priority: u16,
+ pub weight: u16,
+ pub port: u16,
+ pub target: [c_char; 256]
+}
+
+
+#[no_mangle]
+pub unsafe extern "C"
+fn rexmpp_dns_result_free (result: *mut RexmppDNSResult) {
+ if (*result).data != ptr::null_mut() {
+ let mut i = 0;
+ let data_ptr: *mut *mut c_void = (*result).data;
+ while *(data_ptr.offset(i)) != ptr::null_mut() {
+ free(*(data_ptr.offset(i)));
+ i += 1;
+ }
+ free((*result).data as *mut c_void);
+ (*result).data = ptr::null_mut();
+ }
+ if (*result).len != ptr::null_mut() {
+ free((*result).len as *mut c_void);
+ (*result).len = ptr::null_mut();
+ }
+ free(result as *mut c_void);
+}
diff --git a/src/rexmpp_http_upload.c b/src/rexmpp_http_upload.c
index 62d371b..18a3302 100644
--- a/src/rexmpp_http_upload.c
+++ b/src/rexmpp_http_upload.c
@@ -10,6 +10,7 @@
#include <string.h>
#include <libgen.h>
#include <errno.h>
+#include <stdlib.h>
#include "config.h"
@@ -18,6 +19,7 @@
#endif
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_http_upload.h"
@@ -43,46 +45,51 @@ void rexmpp_upload_task_finish (struct rexmpp_http_upload_task *task) {
void rexmpp_http_upload_slot_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
struct rexmpp_http_upload_task *task = ptr;
if (success) {
- xmlNodePtr slot = rexmpp_xml_find_child(response, "urn:xmpp:http:upload:0", "slot");
- xmlNodePtr put = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "put");
- xmlNodePtr get = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "get");
+ rexmpp_xml_t *slot = rexmpp_xml_find_child(response, "urn:xmpp:http:upload:0", "slot");
+ rexmpp_xml_t *put = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "put");
+ rexmpp_xml_t *get = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "get");
if (put != NULL && get != NULL) {
- char *put_url = xmlGetProp(put, "url");
- char *get_url = xmlGetProp(get, "url");
+ const char *put_url = rexmpp_xml_find_attr_val(put, "url");
+ const char *get_url = rexmpp_xml_find_attr_val(get, "url");
if (put_url != NULL && get_url != NULL) {
- task->get_url = get_url;
+ task->get_url = strdup(get_url);
CURL *ce = curl_easy_init();
curl_easy_setopt(ce, CURLOPT_PRIVATE, task);
curl_easy_setopt(ce, CURLOPT_UPLOAD, 1);
curl_easy_setopt(ce, CURLOPT_READDATA, task->fh);
curl_easy_setopt(ce, CURLOPT_URL, put_url);
+ curl_easy_setopt(ce, CURLOPT_INFILESIZE, task->fsize);
- xmlNodePtr header = xmlFirstElementChild(put);
+ rexmpp_xml_t *header = rexmpp_xml_first_elem_child(put);
while (header) {
- char *header_name = xmlGetProp(header, "name");
+ const char *header_name = rexmpp_xml_find_attr_val(header, "name");
if (header_name != NULL) {
- char *header_str = xmlNodeGetContent(header);
+ 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);
- free(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");
+ }
}
- free(header_name);
}
- header = header->next;
+ header = rexmpp_xml_next_elem_sibling(header);
}
curl_easy_setopt(ce, CURLOPT_HTTPHEADER, task->http_headers);
@@ -91,12 +98,6 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s,
return;
} else {
rexmpp_log(s, LOG_ERR, "Unexpected structure for a HTTP file upload slot.");
- if (get_url != NULL) {
- free(get_url);
- }
- }
- if (put_url != NULL) {
- free(put_url);
}
} else {
rexmpp_log(s, LOG_ERR, "Unexpected structure for a HTTP file upload slot.");
@@ -109,8 +110,8 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s,
void rexmpp_http_upload_feature_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)response;
@@ -120,17 +121,17 @@ void rexmpp_http_upload_feature_cb (rexmpp_t *s,
rexmpp_upload_task_finish(task);
return;
}
- xmlNodePtr req = rexmpp_xml_new_node("request", "urn:xmpp:http:upload:0");
- xmlNewProp(req, "filename", task->fname);
+ rexmpp_xml_t *req =
+ rexmpp_xml_new_elem("request", "urn:xmpp:http:upload:0");
+ rexmpp_xml_add_attr(req, "filename", task->fname);
char buf[11];
snprintf(buf, 11, "%u", task->fsize);
- xmlNewProp(req, "size", buf);
+ rexmpp_xml_add_attr(req, "size", buf);
if (task->content_type) {
- xmlNewProp(req, "content-type", task->content_type);
+ rexmpp_xml_add_attr(req, "content-type", task->content_type);
}
- char *to = xmlGetProp(request, "to");
+ const char *to = rexmpp_xml_find_attr_val(request, "to");
rexmpp_iq_new(s, "get", to, req, rexmpp_http_upload_slot_cb, task);
- free(to);
}
rexmpp_err_t
@@ -143,8 +144,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_jid.rs b/src/rexmpp_jid.rs
new file mode 100644
index 0000000..7730b2f
--- /dev/null
+++ b/src/rexmpp_jid.rs
@@ -0,0 +1,17 @@
+use std::os::raw::{c_char};
+
+#[repr(C)]
+pub struct RexmppJID {
+ local: [c_char; 1024],
+ domain: [c_char; 1024],
+ resource: [c_char; 1024],
+ bare: [c_char; 2048],
+ full: [c_char; 3072]
+}
+
+// #[no_mangle]
+// extern "C"
+// fn rexmpp_jid_parse (str: *const c_char, jid : &mut RexmppJID) -> c_int
+// {
+
+// }
diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c
index 3a72a95..2b84445 100644
--- a/src/rexmpp_jingle.c
+++ b/src/rexmpp_jingle.c
@@ -27,24 +27,251 @@ A/V calls over ICE-UDP + DTLS-SRTP:
#include <syslog.h>
#include <errno.h>
#include <libgen.h>
-#include <gcrypt.h>
#include "config.h"
#ifdef ENABLE_CALLS
+#include <inttypes.h>
#include <glib.h>
#include <gio/gnetworking.h>
#include <nice.h>
#include <agent.h>
-#include <gnutls/dtls.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
#include <srtp2/srtp.h>
+#include <math.h>
+#include "portaudio.h"
+#ifdef HAVE_OPUS
+#include <opus.h>
+#endif
#endif
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_jingle.h"
#include "rexmpp_base64.h"
+#include "rexmpp_random.h"
+#include "rexmpp_tls.h"
+#include "rexmpp_digest.h"
+
+
+/* https://en.wikipedia.org/wiki/G.711 */
+uint8_t rexmpp_pcma_encode (int16_t x) {
+ uint8_t sign = 0x80;
+ uint8_t pos;
+ uint8_t val = 0;
+ if (x < 0) {
+ x = -x;
+ sign = 0;
+ }
+ x >>= 3;
+ for (pos = 11; pos > 5 && ! (x & (1 << pos)); pos--);
+ val = (x >> (pos - 4)) & 0xf;
+ return (sign | ((pos - 4) << 4) | val) ^ 0x55;
+}
+
+int16_t rexmpp_pcma_decode (uint8_t x) {
+ x ^= 0x55;
+ int16_t sign = -1;
+ uint8_t shift = (x >> 4) & 7;
+ int16_t val = x & 0xf;
+ if (x & 0x80) {
+ x ^= 0x80;
+ sign = 1;
+ }
+ val = (val << 1) | 1;
+ if (shift > 0) {
+ val = (val | 0x20) << (shift - 1);
+ }
+ val <<= 3;
+ return sign * val;
+}
+
+uint8_t rexmpp_pcmu_encode (int16_t x) {
+ uint8_t sign = 0;
+ uint8_t pos;
+ uint8_t val = 0;
+ if (x < 0) {
+ x = -x;
+ sign = 0x80;
+ }
+ x >>= 2;
+ for (pos = 12; pos > 5 && ! (x & (1 << pos)); pos--);
+ val = (x >> (pos - 4)) & 0xf;
+ return (sign | ((pos - 4) << 4) | val) ^ 0xff;
+}
+
+int16_t rexmpp_pcmu_decode (uint8_t x) {
+ x ^= 0xff;
+ int16_t sign = 1;
+ uint8_t shift = (x >> 4) & 7;
+ int16_t val = x & 0xf;
+ if (x & 0x80) {
+ x ^= 0x80;
+ sign = -1;
+ }
+ val = (val << 1) | 1;
+ if (shift > 0) {
+ val = (val | 0x20) << (shift - 1);
+ }
+ val <<= 2;
+ return sign * val;
+}
+
+rexmpp_xml_t *
+rexmpp_jingle_session_payload_by_id (rexmpp_jingle_session_t *sess,
+ int payload_type_id)
+{
+ if (sess->accept == NULL) {
+ return NULL;
+ }
+ rexmpp_xml_t *descr_child =
+ rexmpp_xml_first_elem_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (sess->accept, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:apps:rtp:1", "description"));
+ while (descr_child != NULL) {
+ if (rexmpp_xml_match(descr_child, "urn:xmpp:jingle:apps:rtp:1",
+ "payload-type"))
+ {
+ const char *pl_id = rexmpp_xml_find_attr_val(descr_child, "id");
+ if (pl_id != NULL && atoi(pl_id) == payload_type_id) {
+ return descr_child;
+ }
+ }
+ descr_child = rexmpp_xml_next_elem_sibling(descr_child);
+ }
+ return NULL;
+}
+
+#ifdef ENABLE_CALLS
+int rexmpp_jingle_session_configure_audio (rexmpp_jingle_session_t *sess) {
+ if (sess->accept == NULL) {
+ return 0;
+ }
+ rexmpp_xml_t *descr_child =
+ rexmpp_xml_first_elem_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child(sess->accept, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:apps:rtp:1", "description"));
+ while (descr_child != NULL) {
+ if (rexmpp_xml_match(descr_child, "urn:xmpp:jingle:apps:rtp:1",
+ "payload-type"))
+ {
+ const char *pl_id = rexmpp_xml_find_attr_val(descr_child, "id");
+ if (pl_id != NULL) {
+ if (atoi(pl_id) == 0) {
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to PCMU (0) for Jingle session %s",
+ sess->sid);
+ sess->codec = REXMPP_CODEC_PCMU;
+ sess->payload_type = 0;
+ return sess->payload_type;
+ }
+ if (atoi(pl_id) == 8) {
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to PCMA (8) for Jingle session %s",
+ sess->sid);
+ sess->codec = REXMPP_CODEC_PCMA;
+ sess->payload_type = 8;
+ return sess->payload_type;
+ }
+#ifdef HAVE_OPUS
+ const char *pl_name = rexmpp_xml_find_attr_val(descr_child, "name");
+ if ((pl_name != NULL) && (strcmp(pl_name, "opus") == 0)) {
+ sess->codec = REXMPP_CODEC_OPUS;
+ sess->payload_type = atoi(pl_id);
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to Opus (%u) for Jingle session %s",
+ sess->payload_type, sess->sid);
+ return sess->payload_type;
+ }
+#endif /* HAVE_OPUS */
+ }
+ }
+ descr_child = rexmpp_xml_next_elem_sibling(descr_child);
+ }
+ return 0;
+}
+
+
+static int rexmpp_pa_callback (const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData)
+{
+ (void)timeInfo;
+ (void)statusFlags;
+ struct pa_buffers *data = (struct pa_buffers*)userData;
+ int16_t *out = (int16_t*)output;
+ int16_t *in = (int16_t*)input;
+ unsigned int i;
+ for (i = 0; i < frameCount; i++) {
+ if (in != NULL) {
+ data->capture.buf[data->capture.write_pos] = in[i];
+ data->capture.write_pos++;
+ data->capture.write_pos %= PA_BUF_SIZE;
+ }
+
+ if (data->playback.read_pos != data->playback.write_pos) {
+ out[i] = data->playback.buf[data->playback.read_pos];
+ data->playback.read_pos++;
+ data->playback.read_pos %= PA_BUF_SIZE;
+ } else {
+ out[i] = 0;
+ }
+ }
+ return 0;
+}
+
+void
+rexmpp_jingle_run_audio (rexmpp_jingle_session_t *sess) {
+ rexmpp_random_buf(&(sess->rtp_seq_num), sizeof(uint16_t));
+ sess->rtp_last_seq_num = 0xffff;
+ rexmpp_random_buf(&(sess->rtp_timestamp), sizeof(uint32_t));
+ rexmpp_random_buf(&(sess->rtp_ssrc), sizeof(uint32_t));
+
+ int rate = 8000;
+ int channels = 1;
+#ifdef HAVE_OPUS
+ if (sess->codec == REXMPP_CODEC_OPUS) {
+ rate = 48000;
+ channels = 2;
+ int opus_error;
+ sess->opus_enc =
+ opus_encoder_create(rate, channels, OPUS_APPLICATION_VOIP, &opus_error);
+ sess->opus_dec =
+ opus_decoder_create(rate, channels, &opus_error);
+ }
+#endif /* HAVE_OPUS */
+
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "Setting up an audio stream: SSRC %" PRIx32
+ ", %d Hz, %d channels",
+ sess->rtp_ssrc, rate, channels);
+
+ PaError err = Pa_OpenDefaultStream (&(sess->pa_stream),
+ channels,
+ channels,
+ paInt16,
+ /* paFloat32, */
+ rate,
+ /* 480, */
+ paFramesPerBufferUnspecified,
+ rexmpp_pa_callback,
+ &(sess->ring_buffers));
+ if (err != paNoError) {
+ rexmpp_log(sess->s, LOG_ERR, "Failed to open a PA stream: %s",
+ Pa_GetErrorText(err));
+ }
+ err = Pa_StartStream(sess->pa_stream);
+ if (err != paNoError) {
+ rexmpp_log(sess->s, LOG_ERR, "Failed to start a PA stream: %s",
+ Pa_GetErrorText(err));
+ }
+}
+#endif /* ENABLE_CALLS */
rexmpp_jingle_session_t *
@@ -52,7 +279,7 @@ rexmpp_jingle_session_by_id (rexmpp_t *s, const char *sid) {
if (sid == NULL) {
return NULL;
}
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
if (strcmp(cur->sid, sid) == 0) {
return cur;
@@ -71,17 +298,36 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
free(session->sid);
}
if (session->initiate != NULL) {
- xmlFreeNodeList(session->initiate);
+ rexmpp_xml_free_list(session->initiate);
}
if (session->accept != NULL) {
- xmlFreeNodeList(session->accept);
+ rexmpp_xml_free_list(session->accept);
}
if (session->ibb_fh != NULL) {
fclose(session->ibb_fh);
}
- #ifdef ENABLE_CALLS
+#ifdef ENABLE_CALLS
if (session->type == REXMPP_JINGLE_SESSION_MEDIA) {
int i;
+ if (session->pa_stream != NULL) {
+ PaError err = Pa_StopStream(session->pa_stream);
+ if (err != paNoError) {
+ rexmpp_log(session->s, LOG_ERR,
+ "Failed to close a PortAudio stream: %s",
+ Pa_GetErrorText(err));
+ }
+ session->pa_stream = NULL;
+ }
+#ifdef HAVE_OPUS
+ if (session->opus_enc != NULL) {
+ opus_encoder_destroy(session->opus_enc);
+ session->opus_enc = NULL;
+ }
+ if (session->opus_dec != NULL) {
+ opus_decoder_destroy(session->opus_dec);
+ session->opus_dec = NULL;
+ }
+#endif /* HAVE_OPUS */
for (i = 0; i < 2; i++) {
rexmpp_jingle_component_t *comp = &session->component[i];
if (comp->dtls_state == REXMPP_TLS_ACTIVE ||
@@ -90,22 +336,29 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
/* SRTP structures are allocated upon a TLS connection, so
using the TLS state to find when they should be
deallocated. */
- srtp_dealloc(comp->srtp_in);
- srtp_dealloc(comp->srtp_out);
+ if (comp->srtp_in != NULL) {
+ srtp_dealloc(comp->srtp_in);
+ }
+ if (comp->srtp_out != NULL) {
+ srtp_dealloc(comp->srtp_out);
+ }
}
if (comp->dtls_state == REXMPP_TLS_HANDSHAKE ||
comp->dtls_state == REXMPP_TLS_ACTIVE ||
comp->dtls_state == REXMPP_TLS_CLOSING ||
comp->dtls_state == REXMPP_TLS_CLOSED) {
- gnutls_deinit(comp->dtls_session);
+ if (comp->dtls_state == REXMPP_TLS_HANDSHAKE ||
+ comp->dtls_state == REXMPP_TLS_ACTIVE) {
+ rexmpp_tls_disconnect(comp->s, comp->dtls);
+ }
+ rexmpp_tls_session_free(comp->dtls);
+ rexmpp_tls_ctx_free(comp->dtls);
+ comp->dtls = NULL;
comp->dtls_state = REXMPP_TLS_INACTIVE;
}
- if (comp->udp_socket != -1) {
- close(comp->udp_socket);
- comp->udp_socket = -1;
- }
}
if (session->ice_agent != NULL) {
+ nice_agent_close_async(session->ice_agent, NULL, NULL);
g_object_unref(session->ice_agent);
session->ice_agent = NULL;
}
@@ -126,7 +379,7 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
session->turn_password = NULL;
}
}
- #endif
+#endif /* ENABLE_CALLS */
free(session);
}
@@ -135,11 +388,11 @@ void rexmpp_jingle_session_delete (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
return;
}
rexmpp_log(s, LOG_DEBUG, "Removing Jingle session %s", sess->sid);
- rexmpp_jingle_session_t *cur = s->jingle.sessions, *prev = NULL;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions, *prev = NULL;
while (cur != NULL) {
if (sess == cur) {
if (prev == NULL) {
- s->jingle.sessions = cur->next;
+ s->jingle->sessions = cur->next;
} else {
prev->next = cur->next;
}
@@ -157,7 +410,7 @@ void rexmpp_jingle_session_delete_by_id (rexmpp_t *s, const char *sid) {
int rexmpp_jingle_session_add (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
uint32_t sessions_num = 0;
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
sessions_num++;
cur = cur->next;
@@ -168,8 +421,8 @@ int rexmpp_jingle_session_add (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
return 0;
}
rexmpp_log(s, LOG_DEBUG, "Adding Jingle session %s", sess->sid);
- sess->next = s->jingle.sessions;
- s->jingle.sessions = sess;
+ sess->next = s->jingle->sessions;
+ s->jingle->sessions = sess;
return 1;
}
@@ -184,45 +437,61 @@ rexmpp_jingle_session_create (rexmpp_t *s,
{
rexmpp_jingle_session_t *sess = malloc(sizeof(rexmpp_jingle_session_t));
if (sess != NULL) {
- sess->s = s;
sess->jid = jid;
sess->sid = sid;
- sess->type = type;
- sess->initiator = initiator;
sess->initiate = NULL;
sess->accept = NULL;
+ sess->s = s;
+ sess->initiator = initiator;
+ sess->type = type;
sess->ibb_fh = NULL;
sess->ibb_sid = NULL;
sess->ibb_seq = 0;
#ifdef ENABLE_CALLS
+ sess->stun_host = NULL;
+ sess->stun_port = 0;
+ sess->turn_host = NULL;
+ sess->turn_port = 0;
+ sess->turn_username = NULL;
+ sess->turn_password = NULL;
+
int i;
for (i = 0; i < 2; i++) {
sess->component[i].component_id = i + 1;
sess->component[i].session = sess;
sess->component[i].s = s;
sess->component[i].dtls_state = REXMPP_TLS_INACTIVE;
- sess->component[i].dtls_buf_len = 0;
- sess->component[i].udp_socket = -1;
+ sess->component[i].dtls = rexmpp_tls_ctx_new(s, 1);
+ sess->component[i].srtp_out = NULL;
+ sess->component[i].srtp_in = NULL;
}
- sess->ice_agent = NULL;
+
sess->rtcp_mux = s->jingle_prefer_rtcp_mux;
+ sess->ice_agent = NULL;
- sess->stun_host = NULL;
- sess->stun_port = 0;
- sess->turn_host = NULL;
- sess->turn_port = 0;
- sess->turn_username = NULL;
- sess->turn_password = NULL;
- /* rexmpp_jingle_ice_agent_init(sess); */
-#endif
+ sess->pa_stream = NULL;
+
+ sess->ring_buffers.capture.read_pos = 0;
+ sess->ring_buffers.capture.write_pos = 0;
+ sess->ring_buffers.playback.read_pos = 0;
+ sess->ring_buffers.playback.write_pos = 0;
+ sess->codec = REXMPP_CODEC_UNDEFINED;
+ sess->payload_type = 0;
+
+#ifdef HAVE_OPUS
+ sess->opus_enc = NULL;
+ sess->opus_dec = NULL;
+#endif /* HAVE_POUS */
+#endif /* ENABLE_CALLS */
if (! rexmpp_jingle_session_add(s, sess)) {
- rexmpp_jingle_session_destroy(sess);
- sess = NULL;
+ /* The session is destroyed by rexmpp_jingle_session_add */
+ return NULL;
}
+ return sess;
} else {
rexmpp_log(s, LOG_ERR, "Failed to allocate memory for a Jingle session");
+ return NULL;
}
- return sess;
}
rexmpp_jingle_session_t *
@@ -230,7 +499,7 @@ rexmpp_jingle_session_by_ibb_sid (rexmpp_t *s, const char *ibb_sid) {
if (ibb_sid == NULL) {
return NULL;
}
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
if (cur->type == REXMPP_JINGLE_SESSION_FILE &&
strcmp(cur->ibb_sid, ibb_sid) == 0) {
@@ -244,31 +513,37 @@ rexmpp_jingle_session_by_ibb_sid (rexmpp_t *s, const char *ibb_sid) {
}
int rexmpp_jingle_init (rexmpp_t *s) {
- s->jingle.sessions = NULL;
+ s->jingle = malloc(sizeof(struct rexmpp_jingle_ctx));
+ s->jingle->sessions = NULL;
#ifdef ENABLE_CALLS
g_networking_init();
srtp_init();
- s->jingle.gloop = g_main_loop_new(NULL, FALSE);
-#endif
+ s->jingle->gloop = g_main_loop_new(NULL, FALSE);
+ Pa_Initialize();
+ nice_debug_enable(1);
+#endif /* ENABLE_CALLS */
return 0;
}
void rexmpp_jingle_stop (rexmpp_t *s) {
- while (s->jingle.sessions != NULL) {
- rexmpp_jingle_session_delete(s, s->jingle.sessions);
+ while (s->jingle->sessions != NULL) {
+ rexmpp_jingle_session_delete(s, s->jingle->sessions);
}
#ifdef ENABLE_CALLS
- g_main_loop_quit(s->jingle.gloop);
- s->jingle.gloop = NULL;
+ g_main_loop_quit(s->jingle->gloop);
+ s->jingle->gloop = NULL;
srtp_shutdown();
-#endif
+ Pa_Terminate();
+#endif /* ENABLE_CALLS */
+ free(s->jingle);
+ s->jingle = NULL;
}
void rexmpp_jingle_accept_file_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -292,15 +567,16 @@ rexmpp_jingle_accept_file (rexmpp_t *s,
path, strerror(errno));
return REXMPP_E_OTHER;
}
- xmlNodePtr jingle = session->initiate;
- xmlNodePtr content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
+ rexmpp_xml_t *jingle = session->initiate;
+ rexmpp_xml_t *content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr new_jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(new_jingle, "action", "session-accept");
- xmlNewProp(new_jingle, "responder", s->assigned_jid.full);
- xmlNewProp(new_jingle, "sid", session->sid);
- xmlAddChild(new_jingle, xmlCopyNode(content, 1));
- session->accept = xmlCopyNode(new_jingle, 1);
+ rexmpp_xml_t *new_jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(new_jingle, "action", "session-accept");
+ rexmpp_xml_add_attr(new_jingle, "responder", s->assigned_jid.full);
+ rexmpp_xml_add_attr(new_jingle, "sid", session->sid);
+ rexmpp_xml_add_child(new_jingle, rexmpp_xml_clone(content));
+ session->accept = rexmpp_xml_clone(new_jingle);
return rexmpp_iq_new(s, "set", session->jid, new_jingle,
rexmpp_jingle_accept_file_cb, strdup(session->sid));
}
@@ -319,8 +595,8 @@ rexmpp_jingle_accept_file_by_id (rexmpp_t *s,
void rexmpp_jingle_session_terminate_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -336,24 +612,27 @@ void rexmpp_jingle_session_terminate_cb (rexmpp_t *s,
rexmpp_err_t
rexmpp_jingle_session_terminate (rexmpp_t *s,
const char *sid,
- xmlNodePtr reason_node,
+ rexmpp_xml_t *reason_node,
const char *reason_text)
{
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session == NULL) {
return REXMPP_E_OTHER;
}
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "action", "session-terminate");
- xmlNewProp(jingle, "sid", sid);
- xmlNodePtr reason = rexmpp_xml_new_node("reason", "urn:xmpp:jingle:1");
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "action", "session-terminate");
+ rexmpp_xml_add_attr(jingle, "sid", sid);
+ rexmpp_xml_t *reason =
+ rexmpp_xml_new_elem("reason", "urn:xmpp:jingle:1");
if (reason_text != NULL) {
- xmlNodePtr text = rexmpp_xml_new_node("text", "urn:xmpp:jingle:1");
- xmlNodeAddContent(text, reason_text);
- xmlAddChild(reason, text);
+ rexmpp_xml_t *text =
+ rexmpp_xml_new_elem("text", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_text(text, reason_text);
+ rexmpp_xml_add_child(reason, text);
}
- xmlAddChild(reason, reason_node);
- xmlAddChild(jingle, reason);
+ rexmpp_xml_add_child(reason, reason_node);
+ rexmpp_xml_add_child(jingle, reason);
rexmpp_err_t ret = rexmpp_iq_new(s, "set", session->jid, jingle,
rexmpp_jingle_session_terminate_cb,
strdup(sid));
@@ -363,8 +642,8 @@ rexmpp_jingle_session_terminate (rexmpp_t *s,
void rexmpp_jingle_send_file_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -391,95 +670,99 @@ rexmpp_jingle_send_file (rexmpp_t *s,
}
char buf[4096];
- gcry_md_hd_t hd;
- gcry_error_t err = gcry_md_open(&hd, GCRY_MD_SHA256, 0);
- if (err != GPG_ERR_NO_ERROR) {
- rexmpp_log(s, LOG_ERR, "Failed to create a MD object: %s",
- gcry_strerror(err));
+ rexmpp_digest_t sha256, sha3_256;
+ if (rexmpp_digest_init(&sha256, REXMPP_DIGEST_SHA256)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a SHA-256 digest object");
fclose(fh);
return REXMPP_E_OTHER;
}
- err = gcry_md_enable(hd, GCRY_MD_SHA3_256);
- if (err != GPG_ERR_NO_ERROR) {
- rexmpp_log(s, LOG_ERR, "Failed to add sha3-256 to the MD object: %s",
- gcry_strerror(err));
+ if (rexmpp_digest_init(&sha3_256, REXMPP_DIGEST_SHA3_256)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a SHA-3-256 digest object");
+ rexmpp_digest_finish(&sha256, NULL, 0);
fclose(fh);
return REXMPP_E_OTHER;
}
size_t len = fread(buf, 1, 4096, fh);
while (len > 0) {
- gcry_md_write(hd, buf, len);
+ rexmpp_digest_update(&sha256, buf, len);
+ rexmpp_digest_update(&sha3_256, buf, len);
len = fread(buf, 1, 4096, fh);
}
- gcry_md_final(hd);
-
- char *sid = rexmpp_gen_id(s);
- char *ibb_sid = rexmpp_gen_id(s);
-
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "action", "session-initiate");
- xmlNewProp(jingle, "sid", sid);
- xmlNewProp(jingle, "initiator", s->assigned_jid.full);
-
- xmlNodePtr content = rexmpp_xml_new_node("content", "urn:xmpp:jingle:1");
- xmlNewProp(content, "creator", "initiator");
- xmlNewProp(content, "name", "IBB file");
- xmlAddChild(jingle, content);
-
- xmlNodePtr transport =
- rexmpp_xml_new_node("transport", "urn:xmpp:jingle:transports:ibb:1");
- xmlNewProp(transport, "block-size", "4096");
- xmlNewProp(transport, "sid", ibb_sid);
- xmlAddChild(content, transport);
- xmlNodePtr description =
- rexmpp_xml_new_node("description", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlAddChild(content, description);
- xmlNodePtr file =
- rexmpp_xml_new_node("file", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlAddChild(description, file);
- xmlNodePtr file_name =
- rexmpp_xml_new_node("name", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlNodeAddContent(file_name, basename(path));
- xmlAddChild(file, file_name);
+ char *sid = rexmpp_random_id();
+ char *ibb_sid = rexmpp_random_id();
+
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "action", "session-initiate");
+ rexmpp_xml_add_attr(jingle, "sid", sid);
+ rexmpp_xml_add_attr(jingle, "initiator", s->assigned_jid.full);
+
+ rexmpp_xml_t *content =
+ rexmpp_xml_new_elem("content", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(content, "creator", "initiator");
+ rexmpp_xml_add_attr(content, "name", "IBB file");
+ rexmpp_xml_add_child(jingle, content);
+
+ rexmpp_xml_t *transport =
+ rexmpp_xml_new_elem("transport", "urn:xmpp:jingle:transports:ibb:1");
+ rexmpp_xml_add_attr(transport, "block-size", "4096");
+ rexmpp_xml_add_attr(transport, "sid", ibb_sid);
+ rexmpp_xml_add_child(content, transport);
+ rexmpp_xml_t *description =
+ rexmpp_xml_new_elem("description", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_child(content, description);
+ rexmpp_xml_t *file =
+ rexmpp_xml_new_elem("file", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_child(description, file);
+ rexmpp_xml_t *file_name =
+ rexmpp_xml_new_elem("name", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_text(file_name, basename(path));
+ rexmpp_xml_add_child(file, file_name);
+
+ char hash[32];
char *hash_base64 = NULL;
size_t hash_base64_len = 0;
- rexmpp_base64_to(gcry_md_read(hd, GCRY_MD_SHA256),
- gcry_md_get_algo_dlen(GCRY_MD_SHA256),
+
+ rexmpp_digest_finish(&sha256, hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA256));
+ rexmpp_base64_to(hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA256),
&hash_base64,
&hash_base64_len);
- xmlNodePtr file_hash = rexmpp_xml_new_node("hash", "urn:xmpp:hashes:2");
- xmlNewProp(file_hash, "algo", "sha-256");
- xmlNodeAddContent(file_hash, hash_base64);
+ rexmpp_xml_t *file_hash =
+ rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2");
+ rexmpp_xml_add_attr(file_hash, "algo", "sha-256");
+ rexmpp_xml_add_text(file_hash, hash_base64);
free(hash_base64);
- xmlAddChild(file, file_hash);
+ rexmpp_xml_add_child(file, file_hash);
hash_base64 = NULL;
hash_base64_len = 0;
- rexmpp_base64_to(gcry_md_read(hd, GCRY_MD_SHA3_256),
- gcry_md_get_algo_dlen(GCRY_MD_SHA3_256),
+ rexmpp_digest_finish(&sha3_256, hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA3_256));
+ rexmpp_base64_to(hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA3_256),
&hash_base64,
&hash_base64_len);
- file_hash = rexmpp_xml_new_node("hash", "urn:xmpp:hashes:2");
- xmlNewProp(file_hash, "algo", "sha3-256");
- xmlNodeAddContent(file_hash, hash_base64);
+ file_hash = rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2");
+ rexmpp_xml_add_attr(file_hash, "algo", "sha3-256");
+ rexmpp_xml_add_text(file_hash, hash_base64);
free(hash_base64);
- xmlAddChild(file, file_hash);
-
- gcry_md_close(hd);
+ rexmpp_xml_add_child(file, file_hash);
long fsize = ftell(fh);
fseek(fh, 0, SEEK_SET);
snprintf(buf, 11, "%ld", fsize);
- xmlNodePtr file_size =
- rexmpp_xml_new_node("size", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlNodeAddContent(file_size, buf);
- xmlAddChild(file, file_size);
+ rexmpp_xml_t *file_size =
+ rexmpp_xml_new_elem("size", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_text(file_size, buf);
+ rexmpp_xml_add_child(file, file_size);
rexmpp_jingle_session_t *sess =
rexmpp_jingle_session_create(s, strdup(jid), sid, REXMPP_JINGLE_SESSION_FILE, 1);
if (sess != NULL) {
- sess->initiate = xmlCopyNode(jingle, 1);
+ sess->initiate = rexmpp_xml_clone(jingle);
sess->ibb_sid = ibb_sid;
sess->ibb_fh = fh;
return rexmpp_iq_new(s, "set", sess->jid, jingle,
@@ -492,8 +775,8 @@ rexmpp_jingle_send_file (rexmpp_t *s,
void rexmpp_jingle_ibb_close_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -509,8 +792,8 @@ void rexmpp_jingle_ibb_close_cb (rexmpp_t *s,
void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -529,8 +812,9 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
return;
}
if (feof(session->ibb_fh)) {
- xmlNodePtr close = rexmpp_xml_new_node("close", "http://jabber.org/protocol/ibb");
- xmlNewProp(close, "sid", session->ibb_sid);
+ rexmpp_xml_t *close =
+ rexmpp_xml_new_elem("close", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(close, "sid", session->ibb_sid);
rexmpp_iq_new(s, "set", session->jid, close,
rexmpp_jingle_ibb_close_cb, sid);
return;
@@ -538,15 +822,16 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
char buf[4096];
size_t len = fread(buf, 1, 4096, session->ibb_fh);
if (len > 0) {
- xmlNodePtr data = rexmpp_xml_new_node("data", "http://jabber.org/protocol/ibb");
- xmlNewProp(data, "sid", session->ibb_sid);
+ rexmpp_xml_t *data =
+ rexmpp_xml_new_elem("data", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(data, "sid", session->ibb_sid);
char *out = NULL;
size_t out_len = 0;
rexmpp_base64_to(buf, len, &out, &out_len);
- xmlNodeAddContent(data, out);
+ rexmpp_xml_add_text(data, out);
free(out);
snprintf(buf, 11, "%u", session->ibb_seq);
- xmlNewProp(data, "seq", buf);
+ rexmpp_xml_add_attr(data, "seq", buf);
session->ibb_seq++;
rexmpp_iq_new(s, "set", session->jid, data,
rexmpp_jingle_ibb_send_cb, sid);
@@ -554,7 +839,7 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
} else {
rexmpp_log(s, LOG_ERR, "Failed to read from a file: %s ", strerror(errno));
rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("media-error",
+ rexmpp_xml_new_elem("media-error",
"urn:xmpp:jingle:1"),
"File reading error");
}
@@ -565,8 +850,8 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
#ifdef ENABLE_CALLS
void rexmpp_jingle_call_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -581,31 +866,29 @@ void rexmpp_jingle_call_cb (rexmpp_t *s,
void
rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
- xmlNodePtr transport)
+ rexmpp_xml_t *transport)
{
if (sess->ice_agent == NULL) {
/* Must be an incoming call; just add candidates to
session-initiate's transport. */
- xmlNodePtr old_transport =
+ rexmpp_xml_t *old_transport =
rexmpp_xml_find_child(rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1",
"content"),
"urn:xmpp:jingle:transports:ice-udp:1",
"transport");
- xmlNodePtr candidate = xmlFirstElementChild(transport);
+ rexmpp_xml_t *candidate = rexmpp_xml_first_elem_child(transport);
while (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
"candidate")) {
- xmlAddChild(old_transport, xmlCopyNode(candidate, 1));
- candidate = candidate->next;
+ rexmpp_xml_add_child(old_transport, rexmpp_xml_clone(candidate));
+ candidate = rexmpp_xml_next_elem_sibling(candidate);
}
return;
}
- char *ufrag = xmlGetProp(transport, "ufrag");
- char *password = xmlGetProp(transport, "pwd");
+ const char *ufrag = rexmpp_xml_find_attr_val(transport, "ufrag");
+ const char *password = rexmpp_xml_find_attr_val(transport, "pwd");
nice_agent_set_remote_credentials(sess->ice_agent, sess->ice_stream_id,
ufrag, password);
- free(ufrag);
- free(password);
int component_id;
@@ -614,59 +897,63 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
nice_agent_get_remote_candidates(sess->ice_agent,
sess->ice_stream_id,
component_id);
- xmlNodePtr candidate = xmlFirstElementChild(transport);
- while (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
- "candidate")) {
- char *component = xmlGetProp(candidate, "component");
- if (component[0] == component_id + '0') {
- char *type_str = xmlGetProp(candidate, "type");
- int type_n = NICE_CANDIDATE_TYPE_HOST;
- if (strcmp(type_str, "host") == 0) {
- type_n = NICE_CANDIDATE_TYPE_HOST;
- } else if (strcmp(type_str, "srflx") == 0) {
- type_n = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
- } else if (strcmp(type_str, "prflx") == 0) {
- type_n = NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
- } else if (strcmp(type_str, "relay") == 0) {
- type_n = NICE_CANDIDATE_TYPE_RELAYED;
- }
- free(type_str);
- NiceCandidate *c = nice_candidate_new(type_n);
- c->component_id = component_id;
- c->stream_id = sess->ice_stream_id;
+ rexmpp_xml_t *candidate = rexmpp_xml_first_elem_child(transport);
+ while (candidate != NULL) {
+ if (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
+ "candidate")) {
+ const char *component = rexmpp_xml_find_attr_val(candidate, "component");
+ if (component[0] == component_id + '0') {
+ const char *type_str = rexmpp_xml_find_attr_val(candidate, "type");
+ int type_n = NICE_CANDIDATE_TYPE_HOST;
+ if (strcmp(type_str, "host") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_HOST;
+ } else if (strcmp(type_str, "srflx") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
+ } else if (strcmp(type_str, "prflx") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
+ } else if (strcmp(type_str, "relay") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_RELAYED;
+ }
+ NiceCandidate *c = nice_candidate_new(type_n);
+ c->component_id = component_id;
+ c->stream_id = sess->ice_stream_id;
- char *foundation = xmlGetProp(candidate, "foundation");
- strncpy(c->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION - 1);
- c->foundation[NICE_CANDIDATE_MAX_FOUNDATION - 1] = 0;
- free(foundation);
+ const char *foundation = rexmpp_xml_find_attr_val(candidate, "foundation");
+ strncpy(c->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION - 1);
+ c->foundation[NICE_CANDIDATE_MAX_FOUNDATION - 1] = 0;
- c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
+ c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
- char *priority = xmlGetProp(candidate, "priority");
- c->priority = atoi(priority);
- free(priority);
+ const char *priority = rexmpp_xml_find_attr_val(candidate, "priority");
+ c->priority = atoi(priority);
- char *ip = xmlGetProp(candidate, "ip");
- if (! nice_address_set_from_string(&c->addr, ip)) {
- rexmpp_log(sess->s, LOG_ERR,
- "Failed to parse an ICE-UDP candidate's address: %s",
- ip);
- }
- free(ip);
+ const char *ip = rexmpp_xml_find_attr_val(candidate, "ip");
+ if (! nice_address_set_from_string(&c->addr, ip)) {
+ rexmpp_log(sess->s, LOG_ERR,
+ "Failed to parse an ICE-UDP candidate's address: %s",
+ ip);
+ }
- char *port = xmlGetProp(candidate, "port");
- nice_address_set_port(&c->addr, atoi(port));
- free(port);
+ const char *port = rexmpp_xml_find_attr_val(candidate, "port");
+ nice_address_set_port(&c->addr, atoi(port));
- remote_candidates = g_slist_prepend(remote_candidates, c);
+ remote_candidates = g_slist_prepend(remote_candidates, c);
+ }
}
- free(component);
- candidate = candidate->next;
+ candidate = rexmpp_xml_next_elem_sibling(candidate);
}
if (remote_candidates != NULL) {
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "Setting %d remote candidates for component %d",
+ g_slist_length(remote_candidates),
+ component_id);
nice_agent_set_remote_candidates(sess->ice_agent, sess->ice_stream_id,
component_id, remote_candidates);
g_slist_free_full(remote_candidates, (GDestroyNotify)&nice_candidate_free);
+ } else {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "No remote candidates for component %d",
+ component_id);
}
}
}
@@ -674,7 +961,7 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
/* Checks whether we are in the active (client) role for DTLS, based
on either "session-initiate" or "session-accept" message. */
int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate) {
- xmlNodePtr fingerprint =
+ rexmpp_xml_t *fingerprint =
rexmpp_xml_find_child
(rexmpp_xml_find_child
(rexmpp_xml_find_child
@@ -687,7 +974,7 @@ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate
in_initiate ? "initiate" : "accept");
return 0;
}
- char *fingerprint_setup = xmlGetProp(fingerprint, "setup");
+ const char *fingerprint_setup = rexmpp_xml_find_attr_val(fingerprint, "setup");
if (fingerprint_setup == NULL) {
rexmpp_log(sess->s, LOG_ERR, "No 'setup' attribute for a fingerprint element");
return 0;
@@ -706,15 +993,14 @@ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate
active = (strcmp(fingerprint_setup, "active") == 0);
}
}
- free(fingerprint_setup);
return active;
}
void rexmpp_transport_info_call_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -735,126 +1021,110 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
{
rexmpp_jingle_session_t *sess = data;
- gnutls_x509_crt_t *cert_list;
- int cert_list_size = 0;
- /* We'll need a certificate a bit later, but checking it before
- allocating other things. */
- int err = gnutls_certificate_get_x509_crt(sess->s->jingle.dtls_cred, 0,
- &cert_list, &cert_list_size);
- if (err) {
- rexmpp_log(sess->s, LOG_ERR,
- "Failed to read own certificate list: %s",
- gnutls_strerror(err));
- return;
- }
+ rexmpp_log(sess->s, LOG_DEBUG, "ICE agent candidate gathering is done");
- char fp[32], fp_str[97];
+ /* We'll need a fingerprint a bit later, but checking it before
+ allocating other things. */
+ char fp[32], fp_str[32 * 3 + 1];
size_t fp_size = 32;
- gnutls_x509_crt_get_fingerprint(cert_list[0], GNUTLS_DIG_SHA256, fp, &fp_size);
- int i;
- for (i = 0; i < 32; i++) {
- snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF);
- }
- fp_str[95] = 0;
-
- for (i = 0; i < cert_list_size; i++) {
- gnutls_x509_crt_deinit(cert_list[i]);
+ if (rexmpp_tls_my_fp(sess->s, fp, fp_str, &fp_size)) {
+ return;
}
- gnutls_free(cert_list);
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "sid", sess->sid);
+ rexmpp_xml_t *jingle = rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "sid", sess->sid);
- xmlNodePtr content = rexmpp_xml_new_node("content", "urn:xmpp:jingle:1");
- xmlNewProp(content, "creator", "initiator");
- xmlNewProp(content, "senders", "both");
- xmlNodePtr description;
+ rexmpp_xml_t *content = rexmpp_xml_new_elem("content", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(content, "creator", "initiator");
+ rexmpp_xml_add_attr(content, "senders", "both");
+ rexmpp_xml_t *description;
if (sess->initiator) {
- xmlNewProp(jingle, "action", "session-initiate");
- xmlNewProp(jingle, "initiator", sess->s->assigned_jid.full);
- xmlNewProp(content, "name", "call");
+ /* We are the intiator: send the predefined options. */
+ rexmpp_xml_add_attr(jingle, "action", "session-initiate");
+ rexmpp_xml_add_attr(jingle, "initiator", sess->s->assigned_jid.full);
+ rexmpp_xml_add_attr(content, "name", "call");
/* https://datatracker.ietf.org/doc/html/rfc4568 */
- xmlNodePtr encryption =
- rexmpp_xml_new_node("encryption", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(encryption, "required", "true");
- xmlAddChild(content, encryption);
- xmlNodePtr crypto = rexmpp_xml_new_node("crypto", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(crypto, "crypto-suite", "AES_CM_128_HMAC_SHA1_80");
- xmlNewProp(crypto, "tag", "1");
- xmlAddChild(encryption, crypto);
-
- description = xmlCopyNode(sess->s->jingle_rtp_description, 1);
+ rexmpp_xml_t *encryption =
+ rexmpp_xml_new_elem("encryption", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(encryption, "required", "true");
+ rexmpp_xml_add_child(content, encryption);
+ rexmpp_xml_t *crypto =
+ rexmpp_xml_new_elem("crypto", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(crypto, "crypto-suite", "AES_CM_128_HMAC_SHA1_80");
+ rexmpp_xml_add_attr(crypto, "tag", "1");
+ rexmpp_xml_add_child(encryption, crypto);
+
+ description = rexmpp_xml_clone(sess->s->jingle_rtp_description);
} else {
- xmlNodePtr init_jingle = sess->initiate;
- xmlNodePtr init_content =
+ /* Accepting the call, will have to compare payload type options
+ to supported and preferred ones, and pick some from those. */
+ rexmpp_xml_t *init_jingle = sess->initiate;
+ rexmpp_xml_t *init_content =
rexmpp_xml_find_child(init_jingle, "urn:xmpp:jingle:1", "content");
- char *init_content_name = xmlGetProp(init_content, "name");
+ const char *init_content_name = rexmpp_xml_find_attr_val(init_content, "name");
if (init_content_name != NULL) {
- xmlNewProp(content, "name", init_content_name);
- free(init_content_name);
+ rexmpp_xml_add_attr(content, "name", init_content_name);
} else {
rexmpp_log(sess->s, LOG_ERR,
"Empty content name for Jingle session %s with %s",
sess->sid, sess->jid);
}
- xmlNewProp(jingle, "action", "session-accept");
- xmlNewProp(jingle, "initiator", sess->jid);
- xmlNewProp(jingle, "responder", sess->s->assigned_jid.full);
+ rexmpp_xml_add_attr(jingle, "action", "session-accept");
+ rexmpp_xml_add_attr(jingle, "initiator", sess->jid);
+ rexmpp_xml_add_attr(jingle, "responder", sess->s->assigned_jid.full);
- description = xmlCopyNode(sess->s->jingle_rtp_description, 2);
+ description = rexmpp_xml_clone(sess->s->jingle_rtp_description);
/* Find the first matching payload-type and add that */
- xmlNodePtr pl_type =
- xmlFirstElementChild(sess->s->jingle_rtp_description);
- xmlNodePtr selected_pl = NULL;
- while (pl_type != NULL && selected_pl == NULL) {
+ rexmpp_xml_t *pl_type =
+ rexmpp_xml_first_elem_child(sess->s->jingle_rtp_description);
+ rexmpp_xml_t *selected_pl = NULL;
+ while (pl_type != NULL) {
if (rexmpp_xml_match(pl_type, "urn:xmpp:jingle:apps:rtp:1", "payload-type")) {
- char *pl_id = xmlGetProp(pl_type, "id");
+ const char *pl_id = rexmpp_xml_find_attr_val(pl_type, "id");
if (pl_id != NULL) {
int pl_id_num = atoi(pl_id);
- xmlNodePtr proposed_pl_type =
- xmlFirstElementChild
+ rexmpp_xml_t *proposed_pl_type =
+ rexmpp_xml_first_elem_child
(rexmpp_xml_find_child
(rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1", "content"),
"urn:xmpp:jingle:apps:rtp:1", "description"));
while (proposed_pl_type != NULL && selected_pl == NULL) {
if (rexmpp_xml_match(proposed_pl_type, "urn:xmpp:jingle:apps:rtp:1", "payload-type")) {
- char *proposed_pl_id = xmlGetProp(proposed_pl_type, "id");
+ const char *proposed_pl_id = rexmpp_xml_find_attr_val(proposed_pl_type, "id");
if (proposed_pl_id != NULL) {
int proposed_pl_id_num = atoi(proposed_pl_id);
if (pl_id_num < 96 && pl_id_num == proposed_pl_id_num) {
selected_pl = pl_type;
} else {
- char *pl_name = xmlGetProp(pl_type, "name");
+ const char *pl_name =
+ rexmpp_xml_find_attr_val(pl_type, "name");
if (pl_name != NULL) {
- char *proposed_pl_name = xmlGetProp(proposed_pl_type, "name");
+ const char *proposed_pl_name =
+ rexmpp_xml_find_attr_val(proposed_pl_type, "name");
if (proposed_pl_name != NULL) {
if (strcmp(pl_name, proposed_pl_name) == 0) {
/* todo: compare clock rates, numbers of
channels, parameters */
selected_pl = pl_type;
}
- free(proposed_pl_name);
}
- free(pl_name);
}
}
- free(proposed_pl_id);
}
}
- proposed_pl_type = proposed_pl_type->next;
+ proposed_pl_type = rexmpp_xml_next_elem_sibling(proposed_pl_type);
}
- free(pl_id);
} else {
rexmpp_log(sess->s, LOG_ERR,
- "No 'id' specified for a pyaload-type element.");
+ "No 'id' specified for a payload-type element.");
}
}
pl_type = pl_type->next;
}
if (selected_pl != NULL) {
- xmlAddChild(description, xmlCopyNode(selected_pl, 1));
+ rexmpp_xml_add_child(description, rexmpp_xml_clone(selected_pl));
} else {
rexmpp_log(sess->s, LOG_ERR, "No suitable payload type found");
/* todo: fail if it's NULL, though it shouldn't happen, since
@@ -862,69 +1132,70 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
}
- xmlAddChild(jingle, content);
- xmlAddChild(content, description);
+ rexmpp_xml_add_child(jingle, content);
+ rexmpp_xml_add_child(content, description);
if (sess->rtcp_mux) {
- xmlNodePtr rtcp_mux =
- rexmpp_xml_new_node("rtcp-mux", "urn:xmpp:jingle:apps:rtp:1");
- xmlAddChild(description, rtcp_mux);
+ rexmpp_xml_t *rtcp_mux =
+ rexmpp_xml_new_elem("rtcp-mux", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_child(description, rtcp_mux);
}
- xmlNodePtr transport =
- rexmpp_xml_new_node("transport", "urn:xmpp:jingle:transports:ice-udp:1");
+ rexmpp_xml_t *transport =
+ rexmpp_xml_new_elem("transport", "urn:xmpp:jingle:transports:ice-udp:1");
gchar *ufrag = NULL;
gchar *password = NULL;
nice_agent_get_local_credentials(agent, stream_id, &ufrag, &password);
- xmlNewProp(transport, "ufrag", ufrag);
- xmlNewProp(transport, "pwd", password);
+ rexmpp_xml_add_attr(transport, "ufrag", ufrag);
+ rexmpp_xml_add_attr(transport, "pwd", password);
g_free(ufrag);
g_free(password);
- xmlAddChild(content, transport);
+ rexmpp_xml_add_child(content, transport);
int component_id;
- xmlNodePtr postponed_candidates = NULL;
+ rexmpp_xml_t *postponed_candidates = NULL;
for (component_id = 1; component_id <= (sess->rtcp_mux ? 1 : 2); component_id++) {
GSList *candidates = nice_agent_get_local_candidates(agent, stream_id, component_id);
GSList *cand_cur = candidates;
int cand_num = 0;
while (cand_cur != NULL) {
- xmlNodePtr candidate =
- rexmpp_xml_new_node("candidate", "urn:xmpp:jingle:transports:ice-udp:1");
+ rexmpp_xml_t *candidate =
+ rexmpp_xml_new_elem("candidate", "urn:xmpp:jingle:transports:ice-udp:1");
char buf[INET6_ADDRSTRLEN];
NiceCandidate *c = (NiceCandidate *)cand_cur->data;
snprintf(buf, 11, "%u", component_id);
- xmlNewProp(candidate, "component", buf);
- xmlNewProp(candidate, "foundation", c->foundation);
- xmlNewProp(candidate, "generation", "0");
- char *cid = rexmpp_gen_id(sess->s);
- xmlNewProp(candidate, "id", cid);
+ 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_random_id();
+ rexmpp_xml_add_attr(candidate, "id", cid);
free(cid);
nice_address_to_string(&c->addr, buf);
- xmlNewProp(candidate, "ip", buf);
+ rexmpp_xml_add_attr(candidate, "ip", buf);
snprintf(buf, 11, "%u", nice_address_get_port(&c->addr));
- xmlNewProp(candidate, "port", buf);
- xmlNewProp(candidate, "network", "0");
- xmlNewProp(candidate, "protocol", "udp");
+ rexmpp_xml_add_attr(candidate, "port", buf);
+ rexmpp_xml_add_attr(candidate, "network", "0");
+ rexmpp_xml_add_attr(candidate, "protocol", "udp");
snprintf(buf, 11, "%u", c->priority);
- xmlNewProp(candidate, "priority", buf);
+ rexmpp_xml_add_attr(candidate, "priority", buf);
char *nice_type[] = {"host", "srflx", "prflx", "relay"};
if (c->type < 4) {
- xmlNewProp(candidate, "type", nice_type[c->type]);
+ rexmpp_xml_add_attr(candidate, "type", nice_type[c->type]);
}
/* Can't send too many candidates, since stanza sizes are usually
- limited, and then it breaks the stream. Limiting to 10 per
- component, sending the rest later, via transport-info. */
+ limited, and then it breaks the stream. Limiting to 10 per
+ component, sending the rest later, via transport-info. */
if (cand_num < 10) {
- xmlAddChild(transport, candidate);
+ rexmpp_xml_add_child(transport, candidate);
} else {
- xmlNodePtr jingle_ti = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle_ti, "sid", sess->sid);
- xmlNewProp(jingle_ti, "action", "transport-info");
- xmlNodePtr content_copy = xmlCopyNode(content, 2);
- xmlNodePtr transport_copy = xmlCopyNode(transport, 2);
- xmlAddChild(jingle_ti, content_copy);
- xmlAddChild(content_copy, transport_copy);
- xmlAddChild(transport_copy, candidate);
+ rexmpp_xml_t *jingle_ti =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle_ti, "sid", sess->sid);
+ rexmpp_xml_add_attr(jingle_ti, "action", "transport-info");
+ rexmpp_xml_t *content_copy = rexmpp_xml_clone(content);
+ rexmpp_xml_t *transport_copy = rexmpp_xml_clone(transport);
+ rexmpp_xml_add_child(jingle_ti, content_copy);
+ rexmpp_xml_add_child(content_copy, transport_copy);
+ rexmpp_xml_add_child(transport_copy, candidate);
jingle_ti->next = postponed_candidates;
postponed_candidates = jingle_ti;
}
@@ -936,24 +1207,26 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
}
- xmlNodePtr fingerprint =
- rexmpp_xml_new_node("fingerprint", "urn:xmpp:jingle:apps:dtls:0");
- xmlNewProp(fingerprint, "hash", "sha-256");
+ rexmpp_xml_t *fingerprint =
+ rexmpp_xml_new_elem("fingerprint", "urn:xmpp:jingle:apps:dtls:0");
+ rexmpp_xml_add_attr(fingerprint, "hash", "sha-256");
if (sess->initiator) {
- xmlNewProp(fingerprint, "setup", "actpass");
+ rexmpp_xml_add_attr(fingerprint, "setup", "actpass");
} else if (rexmpp_jingle_dtls_is_active(sess, 1)) {
- xmlNewProp(fingerprint, "setup", "active");
+ rexmpp_xml_add_attr(fingerprint, "setup", "active");
} else {
- xmlNewProp(fingerprint, "setup", "passive");
+ rexmpp_xml_add_attr(fingerprint, "setup", "passive");
}
- xmlNodeAddContent(fingerprint, fp_str);
- xmlAddChild(transport, fingerprint);
+ rexmpp_xml_add_text(fingerprint, fp_str);
+ rexmpp_xml_add_child(transport, fingerprint);
if (sess->initiator) {
- sess->initiate = xmlCopyNode(jingle, 1);
+ sess->initiate = rexmpp_xml_clone(jingle);
} else {
- sess->accept = xmlCopyNode(jingle, 1);
+ sess->accept = rexmpp_xml_clone(jingle);
+ rexmpp_jingle_session_configure_audio(sess);
+ rexmpp_jingle_run_audio(sess);
}
rexmpp_iq_new(sess->s, "set", sess->jid, jingle,
@@ -962,7 +1235,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
/* Now send transport-info messages with candidates that didn't fit
initially. */
while (postponed_candidates != NULL) {
- xmlNodePtr pc_next = postponed_candidates->next;
+ rexmpp_xml_t *pc_next = postponed_candidates->next;
postponed_candidates->next = NULL;
rexmpp_iq_new(sess->s, "set", sess->jid, postponed_candidates,
rexmpp_transport_info_call_cb, strdup(sess->sid));
@@ -971,7 +1244,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
ssize_t
-rexmpp_jingle_dtls_push_func (gnutls_transport_ptr_t p, const void *data, size_t size)
+rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size)
{
rexmpp_jingle_component_t *comp = p;
rexmpp_jingle_session_t *sess = comp->session;
@@ -979,58 +1252,21 @@ rexmpp_jingle_dtls_push_func (gnutls_transport_ptr_t p, const void *data, size_t
comp->component_id, size, data);
}
-ssize_t rexmpp_jingle_dtls_generic_pull_func (rexmpp_jingle_session_t *sess,
- char *tls_buf,
- size_t *tls_buf_len,
- gnutls_session_t tls_session,
- void *data,
- size_t size)
-{
- (void)sess;
- size_t ret = -1;
- if (*tls_buf_len > 0) {
- if (size >= *tls_buf_len) {
- memcpy(data, tls_buf, *tls_buf_len);
- ret = *tls_buf_len;
- *tls_buf_len = 0;
- } else {
- if (size > DTLS_SRTP_BUF_SIZE) {
- size = DTLS_SRTP_BUF_SIZE;
- }
- memcpy(data, tls_buf, size);
- memmove(tls_buf, tls_buf + size, DTLS_SRTP_BUF_SIZE - size);
- ret = size;
- *tls_buf_len = *tls_buf_len - size;
- }
- } else {
- gnutls_transport_set_errno(tls_session, EAGAIN);
- ret = -1;
- }
-
- return ret;
+rexmpp_tls_t *rexmpp_jingle_component_dtls(void *p) {
+ rexmpp_jingle_component_t *comp = p;
+ return comp->dtls;
}
-ssize_t
-rexmpp_jingle_dtls_pull_func (gnutls_transport_ptr_t p,
- void *data,
- size_t size)
+/* The timeout is always zero for DTLS. */
+int rexmpp_jingle_dtls_pull_timeout_func (void *p,
+ unsigned int ms)
{
rexmpp_jingle_component_t *comp = p;
rexmpp_jingle_session_t *sess = comp->session;
- return
- rexmpp_jingle_dtls_generic_pull_func(sess,
- comp->dtls_buf,
- &comp->dtls_buf_len,
- comp->dtls_session,
- data,
- size);
-}
+ /* if (comp->dtls->dtls_buf_len > 0) { */
+ /* return comp->dtls->dtls_buf_len; */
+ /* } */
-int
-rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess,
- unsigned int ms,
- guint component_id)
-{
fd_set rfds;
struct timeval tv;
@@ -1040,38 +1276,72 @@ rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess,
char c;
FD_ZERO(&rfds);
-
- GSocket *sock =
- nice_agent_get_selected_socket(sess->ice_agent,
- sess->ice_stream_id, component_id);
- int fd = g_socket_get_fd(sock);
- FD_SET(fd, &rfds);
+ int fd, nfds = -1;
+
+ GPtrArray *sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, comp->component_id);
+ guint i;
+ for (i = 0; i < sockets->len; i++) {
+ fd = g_socket_get_fd(sockets->pdata[i]);
+ FD_SET(fd, &rfds);
+ if (fd > nfds) {
+ nfds = fd;
+ }
+ }
+ g_ptr_array_unref(sockets);
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
- ret = select(fd + 1, &rfds, NULL, NULL, &tv);
- if (ret <= 0) {
+ ret = select(nfds + 1, &rfds, NULL, NULL, &tv);
+ if (ret < 0) {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "DTLS pull function: select failed: %s",
+ strerror(errno));
return ret;
}
cli_addr_size = sizeof(cli_addr);
- ret =
- recvfrom(fd, &c, 1, MSG_PEEK,
- (struct sockaddr *) &cli_addr, &cli_addr_size);
- if (ret > 0) {
- return 1;
+ ret = 0;
+ sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, comp->component_id);
+ for (i = 0; i < sockets->len; i++) {
+ int err =
+ recvfrom(g_socket_get_fd(sockets->pdata[i]), &c, 1, MSG_PEEK,
+ (struct sockaddr *) &cli_addr, &cli_addr_size);
+ if (err == -1) {
+ /* ENOTCONN and EAGAIN are common here, but report other
+ errors. */
+ if (errno != ENOTCONN && errno != EAGAIN) {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "DTLS pull function: failed to peek a socket: %s",
+ strerror(errno));
+ }
+ } else {
+ ret += err;
+ }
}
+ g_ptr_array_unref(sockets);
+ if (ret > 0) {
+ return ret;
+ }
return 0;
}
-int rexmpp_jingle_dtls_pull_timeout_func (gnutls_transport_ptr_t p,
- unsigned int ms)
-{
- rexmpp_jingle_component_t *comp = p;
- return rexmpp_jingle_dtls_generic_pull_timeout_func(comp->session, ms,
- comp->component_id);
+const char *rexmpp_ice_component_state_text(int state) {
+ switch (state) {
+ case NICE_COMPONENT_STATE_DISCONNECTED: return "disconnected";
+ case NICE_COMPONENT_STATE_GATHERING: return "gathering";
+ case NICE_COMPONENT_STATE_CONNECTING: return "connecting";
+ case NICE_COMPONENT_STATE_CONNECTED: return "connected";
+ case NICE_COMPONENT_STATE_READY: return "ready";
+ case NICE_COMPONENT_STATE_FAILED: return "failed";
+ case NICE_COMPONENT_STATE_LAST: return "last";
+ default: return "unknown";
+ }
}
void
@@ -1083,6 +1353,9 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
{
rexmpp_jingle_session_t *sess = data;
(void)agent;
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "ICE agent component %d state for stream %d changed to %s",
+ component_id, stream_id, rexmpp_ice_component_state_text(state));
if (component_id < 1 || component_id > 2) {
rexmpp_log(sess->s, LOG_CRIT, "Unexpected ICE component_id: %d",
component_id);
@@ -1101,30 +1374,13 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
return;
}
- int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
-
- gnutls_session_t *tls_session = &sess->component[component_id - 1].dtls_session;
- gnutls_init(tls_session,
- (active_role ? GNUTLS_CLIENT : GNUTLS_SERVER) |
- GNUTLS_DATAGRAM |
- GNUTLS_NONBLOCK);
- if (! active_role) {
- gnutls_certificate_server_set_request(*tls_session, GNUTLS_CERT_REQUEST);
- }
- gnutls_set_default_priority(*tls_session);
- gnutls_credentials_set(*tls_session, GNUTLS_CRD_CERTIFICATE,
- sess->s->jingle.dtls_cred);
-
- gnutls_transport_set_ptr(*tls_session, &sess->component[component_id - 1]);
- gnutls_transport_set_push_function(*tls_session, rexmpp_jingle_dtls_push_func);
- gnutls_transport_set_pull_function(*tls_session, rexmpp_jingle_dtls_pull_func);
- gnutls_transport_set_pull_timeout_function(*tls_session,
- rexmpp_jingle_dtls_pull_timeout_func);
+ rexmpp_jingle_component_t *comp = &(sess->component[component_id - 1]);
+ rexmpp_dtls_connect(sess->s,
+ comp->dtls,
+ comp,
+ rexmpp_jingle_dtls_is_active(sess, 0));
sess->component[component_id - 1].dtls_state = REXMPP_TLS_HANDSHAKE;
- /* todo: use the profile/crypto-suite from <crypto/> element */
- gnutls_srtp_set_profile(*tls_session, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80);
- gnutls_handshake(*tls_session);
-
+ rexmpp_tls_handshake(sess->s, comp->dtls);
} else if (state == NICE_COMPONENT_STATE_FAILED) {
rexmpp_log(sess->s, LOG_ERR,
"ICE connection failed for Jingle session %s, ICE stream %d, component %d",
@@ -1136,19 +1392,20 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
void
rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id,
- guint len, gchar *buf, gpointer data)
+ guint len, gchar *gbuf, gpointer data)
{
/* Demultiplexing here for DTLS and SRTP:
https://datatracker.ietf.org/doc/html/rfc5764#section-5.1.2 */
(void)agent;
(void)stream_id;
(void)component_id;
+ uint8_t *buf = (uint8_t *)gbuf;
rexmpp_jingle_component_t *comp = data;
if (len == 0) {
rexmpp_log(comp->s, LOG_WARNING, "Received an empty ICE message");
return;
}
- if (127 < (uint8_t)buf[0] && (uint8_t)buf[0] < 192) {
+ if (127 < buf[0] && buf[0] < 192) {
int err;
srtp_ctx_t *srtp_in;
if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
@@ -1162,43 +1419,178 @@ rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id
"Received an SRTP packet while DTLS is inactive");
return;
}
- uint16_t port_out = comp->udp_port_out;
+ if (srtp_in == NULL) {
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Received an SRTP packet while SRTP is not set up");
+ return;
+ }
if (component_id == 1) {
- err = srtp_unprotect(srtp_in, buf, &len);
+ err = srtp_unprotect(srtp_in, buf, (int*)&len);
if (err == srtp_err_status_auth_fail && comp->session->rtcp_mux) {
/* Try to demultiplex. Maybe there's a better way to do it,
but this will do for now. */
- err = srtp_unprotect_rtcp(srtp_in, buf, &len);
- port_out = comp->session->component[0].udp_port_out;
+ err = srtp_unprotect_rtcp(srtp_in, buf, (int*)&len);
}
} else {
- err = srtp_unprotect_rtcp(srtp_in, buf, &len);
+ err = srtp_unprotect_rtcp(srtp_in, buf, (int*)&len);
}
if (err) {
rexmpp_log(comp->s, LOG_ERR, "SRT(C)P unprotect error %d on component %d",
err, component_id);
} else {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- addr.sin_port = htons(port_out);
- sendto(comp->udp_socket, buf, len, 0,
- (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
+ /* TODO: apparently sometimes there is more than one RTCP packet
+ in the decrypted data; parse them all. */
+ uint8_t version = (buf[0] & 0xc0) >> 6;
+ if (version == 2 && len >= 12) {
+ size_t i;
+ uint8_t payload_type = buf[1] & 0x7f;
+ uint8_t csrc_count = buf[0] & 0xF;
+ uint16_t seq_num = ((uint16_t)buf[2] << 8) | buf[3];
+ int data_start_pos = 12 + csrc_count * 4;
+
+ /* Exclude possible RTCP packets here, RFC 5761 */
+ if (component_id == 1
+ && (payload_type < 64 || payload_type > 80)) {
+ if ((seq_num > comp->session->rtp_last_seq_num)
+ || (comp->session->rtp_last_seq_num > 65500)) {
+ if ((comp->session->rtp_last_seq_num <= 65500)
+ && (seq_num - comp->session->rtp_last_seq_num > 1)) {
+ rexmpp_log(comp->s, LOG_NOTICE,
+ "%d RTP packets lost",
+ seq_num - comp->session->rtp_last_seq_num - 1);
+ }
+ struct ring_buf *playback = &(comp->session->ring_buffers.playback);
+
+ /* Skip the RTP header. */
+ if (payload_type == 0) {
+ /* PCMU */
+ for (i = data_start_pos; i < len; i++) {
+ playback->buf[playback->write_pos] = rexmpp_pcmu_decode(buf[i]);
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ } else if (payload_type == 8) {
+ /* PCMA */
+ for (i = data_start_pos; i < len; i++) {
+ playback->buf[playback->write_pos] = rexmpp_pcma_decode(buf[i]);
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ }
+#ifdef HAVE_OPUS
+ else if (payload_type == comp->session->payload_type
+ && comp->session->codec == REXMPP_CODEC_OPUS) {
+ /* The same payload type as for output, which is Opus */
+ opus_int16 decoded[5760 * 2];
+ int decoded_len;
+ decoded_len =
+ opus_decode(comp->session->opus_dec,
+ (const unsigned char *)buf + data_start_pos,
+ len - data_start_pos,
+ decoded,
+ 5760,
+ 0);
+ int j;
+ for (j = 0; j < decoded_len; j++) {
+ playback->buf[playback->write_pos] = decoded[j];
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ }
+#endif /* HAVE_OPUS */
+ else {
+ /* Some other payload type, possibly with a dynamic ID */
+ rexmpp_xml_t *payload =
+ rexmpp_jingle_session_payload_by_id(comp->session,
+ payload_type);
+ const char *pl_name = rexmpp_xml_find_attr_val(payload, "name");
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Unhandled payload type %d, '%s'",
+ payload_type, pl_name);
+ }
+ comp->session->rtp_last_seq_num = seq_num;
+ } else {
+ rexmpp_log(comp->s, LOG_NOTICE,
+ "Out of order RTP packets received: %d after %d",
+ seq_num, comp->session->rtp_last_seq_num);
+ }
+ } else {
+ uint8_t packet_type = buf[1];
+ unsigned int pos = 0;
+ unsigned int rtcp_len = (buf[2] << 8) | buf[3];
+ if (packet_type == 200 && len >= 28) {
+ uint32_t rtcp_ssrc =
+ (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ uint64_t rtcp_ntp_timestamp = 0;
+ for (i = 8; i < 16; i++) {
+ rtcp_ntp_timestamp <<= 8;
+ rtcp_ntp_timestamp |= buf[i];
+ }
+ uint32_t rtcp_rtp_timestamp =
+ (buf[16] << 24) | (buf[17] << 16) | (buf[18] << 8) | buf[19];
+ uint32_t rtcp_packet_count =
+ (buf[20] << 24) | (buf[21] << 16) | (buf[22] << 8) | buf[23];
+ uint32_t rtcp_octet_count =
+ (buf[24] << 24) | (buf[25] << 16) | (buf[26] << 8) | buf[27];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP SR received: SSRC % " PRIx32 ", NTP TS %" PRIu64
+ ", RTP TS %" PRIu32 ", PC %" PRIu32 ", OC %" PRIu32,
+ rtcp_ssrc, rtcp_ntp_timestamp, rtcp_rtp_timestamp,
+ rtcp_packet_count, rtcp_octet_count);
+ pos = 28;
+ } else if (packet_type == 201 && len >= 8) {
+ uint32_t rtcp_ssrc =
+ (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP RR received: SSRC %x",
+ rtcp_ssrc);
+ pos = 8;
+ } else {
+ rexmpp_log(comp->s, LOG_DEBUG, "RTCP packet received, PT %d",
+ packet_type);
+ }
+
+ while ((pos > 0)
+ && ((pos + 24) <= len)
+ && ((pos + 24) <= (rtcp_len + 1) * 4)) {
+ uint32_t rtcp_ssrc = (buf[pos] << 24) | (buf[pos + 1] << 16)
+ | (buf[pos + 2] << 8) | buf[pos + 3];
+ uint8_t fraction_lost = buf[pos + 4];
+ uint32_t packets_lost =
+ (buf[pos + 5] << 24) | (buf[pos + 6] << 16) | buf[pos + 7];
+ uint32_t rtcp_ehsn = (buf[pos + 8] << 24) | (buf[pos + 9] << 16)
+ | (buf[pos + 10] << 8) | buf[pos + 11];
+ uint32_t rtcp_jitter = (buf[pos + 12] << 24) | (buf[pos + 13] << 16)
+ | (buf[pos + 14] << 8) | buf[pos + 15];
+ uint32_t rtcp_lsr = (buf[pos + 16] << 24) | (buf[pos + 17] << 16)
+ | (buf[pos + 18] << 8) | buf[pos + 19];
+ uint32_t rtcp_dlsr = (buf[pos + 20] << 24) | (buf[pos + 21] << 16)
+ | (buf[pos + 22] << 8) | buf[pos + 23];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP report block: SSRC % " PRIx32 ", lost %" PRIu32
+ "%, %" PRIu8 " packets, "
+ "highest seq num %" PRIu32
+ ", jitter %" PRIu32 ", LSR %" PRIu32 ", DLSR %" PRIu32,
+ rtcp_ssrc, fraction_lost, packets_lost,
+ rtcp_ehsn, rtcp_jitter, rtcp_lsr, rtcp_dlsr);
+ pos += 24;
+ }
+ }
+ } else {
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Unhandled RT(C)P packet: version %d, length %d",
+ version, len);
+ }
}
} else {
- if (comp->dtls_buf_len + len < DTLS_SRTP_BUF_SIZE) {
- memcpy(comp->dtls_buf + comp->dtls_buf_len, buf, len);
- comp->dtls_buf_len += len;
- } else {
- rexmpp_log(comp->s, LOG_WARNING, "Dropping a DTLS packet");
- }
+ rexmpp_dtls_feed(comp->s, comp->dtls, buf, len);
}
}
int
rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
{
- sess->ice_agent = nice_agent_new(g_main_loop_get_context (sess->s->jingle.gloop),
+ sess->ice_agent = nice_agent_new(g_main_loop_get_context (sess->s->jingle->gloop),
NICE_COMPATIBILITY_RFC5245);
if (sess->s->local_address != NULL) {
NiceAddress *address = nice_address_new();
@@ -1223,7 +1615,7 @@ rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
int i;
for (i = 0; i < (sess->rtcp_mux ? 1 : 2); i++) {
nice_agent_attach_recv(sess->ice_agent, sess->ice_stream_id, i + 1,
- g_main_loop_get_context (sess->s->jingle.gloop),
+ g_main_loop_get_context (sess->s->jingle->gloop),
rexmpp_jingle_ice_recv_cb,
&sess->component[i]);
}
@@ -1231,29 +1623,6 @@ rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
return 1;
}
-void
-rexmpp_jingle_bind_sockets (rexmpp_jingle_session_t *sess,
- uint16_t rtp_port_in, uint16_t rtp_port_out)
-{
- sess->component[0].udp_port_in = rtp_port_in;
- sess->component[0].udp_port_out = rtp_port_out;
- sess->component[1].udp_port_in = rtp_port_in + 1;
- sess->component[1].udp_port_out = rtp_port_out + 1;
- int i;
- for (i = 0; i < 2; i++) {
- sess->component[i].udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- addr.sin_port = htons(sess->component[i].udp_port_in);
- if (bind (sess->component[i].udp_socket,
- (struct sockaddr*)&addr, sizeof(struct sockaddr_in))) {
- rexmpp_log(sess->s, LOG_ERR, "Failed to bind a UDP socket on port %u",
- sess->component[i].udp_port_in);
- }
- }
-}
-
void rexmpp_jingle_turn_dns_cb (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *result) {
rexmpp_jingle_session_t *sess = ptr;
if (result != NULL && result->data != NULL) {
@@ -1306,25 +1675,25 @@ void rexmpp_jingle_stun_dns_cb (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *res
void rexmpp_jingle_turn_cb (rexmpp_t *s,
void *sess_ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)req;
rexmpp_jingle_session_t *sess = sess_ptr;
if (success) {
/* use credentials */
- xmlNodePtr services = xmlFirstElementChild(response);
+ rexmpp_xml_t *services = rexmpp_xml_first_elem_child(response);
if (rexmpp_xml_match(services, "urn:xmpp:extdisco:2", "services")) {
- xmlNodePtr service = xmlFirstElementChild(services);
+ rexmpp_xml_t *service = rexmpp_xml_first_elem_child(services);
while (service != NULL) {
if (rexmpp_xml_match(service, "urn:xmpp:extdisco:2", "service")) {
- char *type = xmlGetProp(service, "type");
- char *transport = xmlGetProp(service, "transport");
- char *host = xmlGetProp(service, "host");
- char *port = xmlGetProp(service, "port");
- char *username = xmlGetProp(service, "username");
- char *password = xmlGetProp(service, "password");
+ const char *type = rexmpp_xml_find_attr_val(service, "type");
+ const char *transport = rexmpp_xml_find_attr_val(service, "transport");
+ const char *host = rexmpp_xml_find_attr_val(service, "host");
+ const char *port = rexmpp_xml_find_attr_val(service, "port");
+ const char *username = rexmpp_xml_find_attr_val(service, "username");
+ const char *password = rexmpp_xml_find_attr_val(service, "password");
if (sess->stun_host == NULL &&
type != NULL && transport != NULL && host != NULL && port != NULL &&
@@ -1344,27 +1713,8 @@ void rexmpp_jingle_turn_cb (rexmpp_t *s,
sess->turn_password = strdup(password);
rexmpp_log(s, LOG_DEBUG, "Setting TURN server to %s:%s", host, port);
}
-
- if (type != NULL) {
- free(type);
- }
- if (transport != NULL) {
- free(transport);
- }
- if (host != NULL) {
- free(host);
- }
- if (port != NULL) {
- free(port);
- }
- if (username != NULL) {
- free(username);
- }
- if (password != NULL) {
- free(password);
- }
}
- service = service->next;
+ service = rexmpp_xml_next_elem_sibling(service);
}
if (sess->stun_host != NULL) {
/* Resolve, then resolve STUN host, then connect. */
@@ -1392,25 +1742,24 @@ void rexmpp_jingle_turn_cb (rexmpp_t *s,
void rexmpp_jingle_discover_turn_cb (rexmpp_t *s,
void *sess_ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)req;
- char *response_from = xmlGetProp(response, "from");
+ const char *response_from = rexmpp_xml_find_attr_val(response, "from");
rexmpp_jingle_session_t *sess = sess_ptr;
if (success) {
- xmlNodePtr services = rexmpp_xml_new_node("services", "urn:xmpp:extdisco:2");
- xmlNewProp(services, "type", "turn");
- rexmpp_iq_new(s, "get", response_from, services, rexmpp_jingle_turn_cb, sess_ptr);
+ rexmpp_xml_t *services =
+ rexmpp_xml_new_elem("services", "urn:xmpp:extdisco:2");
+ rexmpp_xml_add_attr(services, "type", "turn");
+ rexmpp_iq_new(s, "get", response_from, services,
+ rexmpp_jingle_turn_cb, sess_ptr);
} else {
rexmpp_log(s, LOG_DEBUG,
"No external service discovery, trying to connect without STUN/TURN");
nice_agent_gather_candidates(sess->ice_agent, sess->ice_stream_id);
}
- if (response_from != NULL) {
- free(response_from);
- }
}
void rexmpp_jingle_discover_turn (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
@@ -1420,37 +1769,36 @@ void rexmpp_jingle_discover_turn (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
rexmpp_err_t
rexmpp_jingle_call (rexmpp_t *s,
- const char *jid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ 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_bind_sockets(sess, rtp_port_in, rtp_port_out);
- rexmpp_jingle_discover_turn(s, sess);
- return REXMPP_SUCCESS;
+ if (sess != NULL) {
+ rexmpp_jingle_ice_agent_init(sess);
+ rexmpp_jingle_discover_turn(s, sess);
+ return REXMPP_SUCCESS;
+ } else {
+ rexmpp_log(s, LOG_ERR, "Failed to create a Jingle session for a call");
+ return REXMPP_E_OTHER;
+ }
}
rexmpp_err_t
rexmpp_jingle_call_accept (rexmpp_t *s,
- const char *sid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *sid)
{
rexmpp_jingle_session_t *sess = rexmpp_jingle_session_by_id(s, sid);
if (sess == NULL) {
return REXMPP_E_OTHER;
}
rexmpp_jingle_ice_agent_init(sess);
- rexmpp_jingle_bind_sockets(sess, rtp_port_in, rtp_port_out);
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1",
"content");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t * ice_udp_transport =
rexmpp_xml_find_child(content,
"urn:xmpp:jingle:transports:ice-udp:1",
"transport");
@@ -1458,7 +1806,7 @@ rexmpp_jingle_call_accept (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "No ICE-UDP transport defined for session %s", sid);
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-transports", "urn:xmpp:jingle:1"),
+ rexmpp_xml_new_elem("unsupported-transports", "urn:xmpp:jingle:1"),
"No ICE-UDP transport defined");
return REXMPP_E_OTHER;
}
@@ -1466,71 +1814,74 @@ rexmpp_jingle_call_accept (rexmpp_t *s,
rexmpp_jingle_discover_turn(s, sess);
return REXMPP_SUCCESS;
}
-#else
+#else /* ENABLE_CALLS */
+
+ssize_t
+rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size)
+{
+ (void)p;
+ (void)data;
+ (void)size;
+ return -1;
+}
rexmpp_err_t
rexmpp_jingle_call (rexmpp_t *s,
- const char *jid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *jid)
{
(void)jid;
- (void)rtp_port_in;
- (void)rtp_port_out;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without support for media calls");
return REXMPP_E_OTHER;
}
rexmpp_err_t
rexmpp_jingle_call_accept (rexmpp_t *s,
- const char *sid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *sid)
{
(void)sid;
- (void)rtp_port_in;
- (void)rtp_port_out;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without support for media calls");
return REXMPP_E_OTHER;
}
-#endif
+#endif /* ENABLE_CALLS */
-int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
+int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) {
int handled = 0;
if (! s->enable_jingle) {
return handled;
}
- xmlNodePtr jingle = rexmpp_xml_find_child(elem, "urn:xmpp:jingle:1", "jingle");
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_find_child(elem, "urn:xmpp:jingle:1", "jingle");
if (jingle != NULL) {
handled = 1;
- char *action = xmlGetProp(jingle, "action");
- char *sid = xmlGetProp(jingle, "sid");
- char *from_jid = xmlGetProp(elem, "from");
+ const char *action = rexmpp_xml_find_attr_val(jingle, "action");
+ const char *sid = rexmpp_xml_find_attr_val(jingle, "sid");
+ const char *from_jid = rexmpp_xml_find_attr_val(elem, "from");
if (action != NULL && sid != NULL && from_jid != NULL) {
if (strcmp(action, "session-initiate") == 0) {
/* todo: could be more than one content element, handle that */
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
if (content == NULL) {
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
} else {
rexmpp_iq_reply(s, elem, "result", NULL);
- xmlNodePtr file_description =
+ rexmpp_xml_t *file_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:file-transfer:5",
"description");
- xmlNodePtr ibb_transport =
+ rexmpp_xml_t *ibb_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ibb:1",
"transport");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
- xmlNodePtr rtp_description =
+ rexmpp_xml_t *rtp_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:rtp:1",
"description");
if (file_description != NULL && ibb_transport != NULL) {
- char *ibb_sid = xmlGetProp(ibb_transport, "sid");
+ const char *ibb_sid = rexmpp_xml_find_attr_val(ibb_transport, "sid");
if (ibb_sid != NULL) {
rexmpp_log(s, LOG_DEBUG,
"Jingle session-initiate from %s, sid %s, ibb sid %s",
@@ -1539,19 +1890,21 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_jingle_session_create(s, strdup(from_jid), strdup(sid),
REXMPP_JINGLE_SESSION_FILE, 0);
if (sess != NULL) {
- sess->initiate = xmlCopyNode(jingle, 1);
- sess->ibb_sid = ibb_sid;
+ sess->initiate = rexmpp_xml_clone(jingle);
+ sess->ibb_sid = strdup(ibb_sid);
} else {
- rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("failed-transport",
- "urn:xmpp:jingle:1"),
- NULL);
+ rexmpp_jingle_session_terminate
+ (s, sid,
+ rexmpp_xml_new_elem("failed-transport",
+ "urn:xmpp:jingle:1"),
+ NULL);
}
} else {
- rexmpp_log(s, LOG_ERR, "Jingle IBB transport doesn't have a sid attribute");
+ rexmpp_log(s, LOG_ERR,
+ "Jingle IBB transport doesn't have a sid attribute");
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-transports",
+ rexmpp_xml_new_elem("unsupported-transports",
"urn:xmpp:jingle:1"),
NULL);
}
@@ -1562,25 +1915,28 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr 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 = xmlCopyNode(jingle, 1);
-#endif
+ 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) {
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-applications",
+ rexmpp_xml_new_elem("unsupported-applications",
"urn:xmpp:jingle:1"),
NULL);
} else if (ibb_transport == NULL &&
ice_udp_transport == NULL) {
- rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("unsupported-transports",
- "urn:xmpp:jingle:1"),
- NULL);
+ rexmpp_jingle_session_terminate
+ (s, sid,
+ rexmpp_xml_new_elem("unsupported-transports",
+ "urn:xmpp:jingle:1"),
+ NULL);
} else {
/* todo: some other error */
}
@@ -1593,107 +1949,106 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_iq_reply(s, elem, "result", NULL);
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session != NULL) {
- session->accept = xmlCopyNode(jingle, 1);
- xmlNodePtr content =
+ session->accept = rexmpp_xml_clone(jingle);
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr file_description =
+ rexmpp_xml_t *file_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:file-transfer:5",
"description");
- xmlNodePtr ibb_transport =
+ rexmpp_xml_t *ibb_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ibb:1",
"transport");
if (ibb_transport != NULL && file_description != NULL) {
- xmlNodePtr open =
- rexmpp_xml_new_node("open", "http://jabber.org/protocol/ibb");
- xmlNewProp(open, "sid", session->ibb_sid);
- xmlNewProp(open, "block-size", "4096");
- xmlNewProp(open, "stanza", "iq");
+ rexmpp_xml_t *open =
+ rexmpp_xml_new_elem("open", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(open, "sid", session->ibb_sid);
+ rexmpp_xml_add_attr(open, "block-size", "4096");
+ rexmpp_xml_add_attr(open, "stanza", "iq");
rexmpp_iq_new(s, "set", session->jid, open,
rexmpp_jingle_ibb_send_cb, strdup(sid));
} else {
#ifdef ENABLE_CALLS
- xmlNodePtr ice_udp_transport =
+ rexmpp_jingle_session_configure_audio(session);
+ rexmpp_jingle_run_audio(session);
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
if (ice_udp_transport != NULL) {
rexmpp_jingle_ice_udp_add_remote(session, ice_udp_transport);
+ } else {
+ rexmpp_log(s, LOG_WARNING,
+ "ICE-UDP transport is unset in session-accept");
}
-#endif
+#endif /* ENABLE_CALLS */
}
+ } else {
+ rexmpp_log(s, LOG_WARNING, "Jingle session %s is not found", sid);
}
} else if (strcmp(action, "transport-info") == 0) {
rexmpp_iq_reply(s, elem, "result", NULL);
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session != NULL) {
#ifdef ENABLE_CALLS
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
if (ice_udp_transport != NULL) {
rexmpp_jingle_ice_udp_add_remote(session, ice_udp_transport);
}
-#endif
+#endif /* ENABLE_CALLS */
}
} else {
rexmpp_log(s, LOG_WARNING, "Unknown Jingle action: %s", action);
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
}
} else {
rexmpp_log(s, LOG_WARNING, "Received a malformed Jingle element");
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
- }
- if (action != NULL) {
- free(action);
- }
- if (sid != NULL) {
- free(sid);
- }
- if (from_jid != NULL) {
- free(from_jid);
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
}
}
/* XEP-0261: Jingle In-Band Bytestreams Transport Method */
- xmlNodePtr ibb_open =
+ rexmpp_xml_t *ibb_open =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "open");
if (ibb_open != NULL) {
handled = 1;
/* no-op, though could check sid here. */
rexmpp_iq_reply(s, elem, "result", NULL);
}
- xmlNodePtr ibb_close =
+ rexmpp_xml_t *ibb_close =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "close");
if (ibb_close != NULL) {
handled = 1;
rexmpp_iq_reply(s, elem, "result", NULL);
- char *sid = xmlGetProp(ibb_close, "sid");
+ const char *sid = rexmpp_xml_find_attr_val(ibb_close, "sid");
if (sid != NULL) {
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_ibb_sid(s, sid);
if (session != NULL) {
rexmpp_jingle_session_terminate
(s, session->sid,
- rexmpp_xml_new_node("success", "urn:xmpp:jingle:1"), NULL);
+ rexmpp_xml_new_elem("success", "urn:xmpp:jingle:1"), NULL);
}
- free(sid);
}
}
- xmlNodePtr ibb_data =
+ rexmpp_xml_t *ibb_data =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "data");
if (ibb_data != NULL) {
handled = 1;
- char *sid = xmlGetProp(ibb_data, "sid");
+ const char *sid = rexmpp_xml_find_attr_val(ibb_data, "sid");
if (sid != NULL) {
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_ibb_sid(s, sid);
if (session != NULL && session->ibb_fh != NULL) {
- char *data = NULL, *data_base64 = xmlNodeGetContent(ibb_data);
+ char *data = NULL;
+ const char *data_base64 = rexmpp_xml_text_child(ibb_data);
if (data_base64 != NULL) {
size_t data_len = 0;
int base64_err = rexmpp_base64_from(data_base64, strlen(data_base64),
- &data, &data_len);
- free(data_base64);
+ &data, &data_len);
if (base64_err != 0) {
rexmpp_log(s, LOG_ERR, "Base-64 decoding failure");
} else {
@@ -1706,7 +2061,6 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
}
}
}
- free(sid);
}
/* todo: report errors */
rexmpp_iq_reply(s, elem, "result", NULL);
@@ -1719,7 +2073,7 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#ifdef ENABLE_CALLS
gint poll_timeout;
GPollFD poll_fds[10];
- GMainContext* gctx = g_main_loop_get_context(s->jingle.gloop);
+ GMainContext* gctx = g_main_loop_get_context(s->jingle->gloop);
if (g_main_context_acquire(gctx)) {
gint poll_fds_n = g_main_context_query(gctx,
G_PRIORITY_HIGH,
@@ -1741,29 +2095,23 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
rexmpp_jingle_session_t *sess;
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
for (i = 0; i < 2; i++) {
if (sess->component[i].dtls_state != REXMPP_TLS_INACTIVE &&
sess->component[i].dtls_state != REXMPP_TLS_CLOSED &&
sess->component[i].dtls_state != REXMPP_TLS_ERROR) {
- GSocket *sock =
- nice_agent_get_selected_socket(sess->ice_agent,
- sess->ice_stream_id,
- i + 1);
- if (sock != NULL) {
- int fd = g_socket_get_fd(sock);
- g_object_unref(sock);
+ GPtrArray *sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, i + 1);
+ guint i;
+ for (i = 0; i < sockets->len; i++) {
+ int fd = g_socket_get_fd(sockets->pdata[i]);
FD_SET(fd, read_fds);
if (fd > nfds) {
nfds = fd;
}
}
- if (sess->component[i].udp_socket != -1) {
- FD_SET(sess->component[i].udp_socket, read_fds);
- if (sess->component[i].udp_socket > nfds) {
- nfds = sess->component[i].udp_socket;
- }
- }
+ g_ptr_array_unref(sockets);
}
}
}
@@ -1771,21 +2119,21 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
rexmpp_log(s, LOG_ERR,
"Failed to acquire GMainContext in rexmpp_jingle_fds");
}
-#else
+#else /* ENABLE_CALLS */
(void)s;
(void)read_fds;
(void)write_fds;
-#endif
+#endif /* ENABLE_CALLS */
return (nfds + 1);
}
-struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv) {
+struct timespec * rexmpp_jingle_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv) {
#ifdef ENABLE_CALLS
gint poll_timeout;
GPollFD poll_fds[10];
- GMainContext* gctx = g_main_loop_get_context(s->jingle.gloop);
+ GMainContext* gctx = g_main_loop_get_context(s->jingle->gloop);
if (g_main_context_acquire(gctx)) {
g_main_context_query(gctx,
G_PRIORITY_HIGH,
@@ -1795,28 +2143,34 @@ struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
g_main_context_release(gctx);
rexmpp_jingle_session_t *sess;
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
int i;
for (i = 0; i < 2; i++) {
if (sess->component[i].dtls_state != REXMPP_TLS_INACTIVE &&
sess->component[i].dtls_state != REXMPP_TLS_CLOSED &&
sess->component[i].dtls_state != REXMPP_TLS_ERROR) {
- int tms = gnutls_dtls_get_timeout(sess->component[i].dtls_session);
+ int tms = rexmpp_dtls_timeout(sess->s, sess->component[i].dtls);
if (tms > 0 && (poll_timeout < 0 || tms < poll_timeout)) {
poll_timeout = tms;
}
+ /* Set poll timeout to at most 5 ms if there are connected
+ components, for timely transmission of Jingle data. */
+ if (sess->component[i].dtls_state == REXMPP_TLS_ACTIVE &&
+ (poll_timeout < 0 || poll_timeout > 5)) {
+ poll_timeout = 5;
+ }
}
}
}
if (poll_timeout >= 0) {
int sec = poll_timeout / 1000;
- int usec = (poll_timeout % 1000) * 1000;
+ int nsec = (poll_timeout % 1000) * 1000000;
if (max_tv == NULL ||
(max_tv->tv_sec > sec ||
- (max_tv->tv_sec == sec && max_tv->tv_usec > usec))) {
+ (max_tv->tv_sec == sec && max_tv->tv_nsec > nsec))) {
tv->tv_sec = sec;
- tv->tv_usec = usec;
+ tv->tv_nsec = nsec;
max_tv = tv;
}
}
@@ -1824,10 +2178,10 @@ struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
rexmpp_log(s, LOG_ERR,
"Failed to acquire GMainContext in rexmpp_jingle_timeout");
}
-#else
+#else /* ENABLE_CALLS */
(void)s;
(void)tv;
-#endif
+#endif /* ENABLE_CALLS */
return max_tv;
}
@@ -1837,232 +2191,282 @@ rexmpp_jingle_run (rexmpp_t *s,
fd_set *write_fds)
{
(void)write_fds;
+ (void)read_fds;
#ifdef ENABLE_CALLS
rexmpp_jingle_session_t *sess;
- int key_mat_size;
- char key_mat[4096];
int err;
- gnutls_datum_t client_key, client_salt, server_key, server_salt;
- char client_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2],
- server_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2];
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ unsigned char key_mat[2 * (SRTP_AES_ICM_128_KEY_LEN_WSALT)],
+ client_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT],
+ server_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT];
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
char input[4096 + SRTP_MAX_TRAILER_LEN];
int input_len;
int comp_id;
+
for (comp_id = 0; comp_id < 2; comp_id++) {
rexmpp_jingle_component_t *comp = &sess->component[comp_id];
if (comp->dtls_state == REXMPP_TLS_HANDSHAKE) {
- int ret = gnutls_handshake(comp->dtls_session);
- if (ret == 0) {
+ int ret = rexmpp_tls_handshake(s, comp->dtls);
+ if (ret == REXMPP_TLS_SUCCESS) {
rexmpp_log(s, LOG_DEBUG,
"DTLS connected for Jingle session %s, component %d",
sess->sid, comp->component_id);
comp->dtls_state = REXMPP_TLS_ACTIVE;
- /* Verify the peer's fingerprint */
-
- unsigned int cert_list_size = 0;
- const gnutls_datum_t *cert_list;
- cert_list =
- gnutls_certificate_get_peers(comp->dtls_session, &cert_list_size);
- if (cert_list_size != 1) {
+ rexmpp_xml_t *jingle = comp->session->initiator
+ ? comp->session->accept
+ : comp->session->initiate;
+ rexmpp_xml_t *fingerprint =
+ rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (jingle, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:transports:ice-udp:1", "transport"),
+ "urn:xmpp:jingle:apps:dtls:0", "fingerprint");
+ if (fingerprint == NULL) {
+ /* todo: might be neater to check it upon receiving the
+ stanzas, instead of checking it here */
rexmpp_log(comp->s, LOG_ERR,
- "Unexpected peer certificate list size: %d",
- cert_list_size);
+ "No fingerprint in the peer's Jingle element");
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("security-error", "urn:xmpp:jingle:1"),
- "Unexpected certificate list size; expected exactly 1.");
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "No fingerprint element");
+ return REXMPP_E_TLS;
} else {
- xmlNodePtr jingle = comp->session->initiator
- ? comp->session->accept
- : comp->session->initiate;
- xmlNodePtr fingerprint =
- rexmpp_xml_find_child
- (rexmpp_xml_find_child
- (rexmpp_xml_find_child
- (jingle, "urn:xmpp:jingle:1", "content"),
- "urn:xmpp:jingle:transports:ice-udp:1", "transport"),
- "urn:xmpp:jingle:apps:dtls:0", "fingerprint");
- if (fingerprint == NULL) {
- /* todo: might be neater to check it upon receiving the
- stanzas, instead of checking it here */
+ const char *hash_str = rexmpp_xml_find_attr_val(fingerprint, "hash");
+ if (hash_str == NULL) {
rexmpp_log(comp->s, LOG_ERR,
- "No fingerprint in the peer's Jingle element");
+ "No hash attribute in the peer's fingerprint element");
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "No fingerprint element");
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "No hash attribute in the fingerprint element");
+ return REXMPP_E_TLS;
} else {
- char *hash_str = xmlGetProp(fingerprint, "hash");
- if (hash_str == NULL) {
- rexmpp_log(comp->s, LOG_ERR,
- "No hash attribute in the peer's fingerprint element");
- rexmpp_jingle_session_terminate
- (s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "No hash attribute in the fingerprint element");
- break;
- } else {
- gnutls_digest_algorithm_t algo = GNUTLS_DIG_UNKNOWN;
- /* gnutls_digest_get_id uses different names, so
- checking manually here. These are SDP options,
- <https://datatracker.ietf.org/doc/html/rfc4572#page-8>. */
- if (strcmp(hash_str, "sha-1") == 0) {
- algo = GNUTLS_DIG_SHA1;
- } else if (strcmp(hash_str, "sha-224") == 0) {
- algo = GNUTLS_DIG_SHA224;
- } else if (strcmp(hash_str, "sha-256") == 0) {
- algo = GNUTLS_DIG_SHA256;
- } else if (strcmp(hash_str, "sha-384") == 0) {
- algo = GNUTLS_DIG_SHA384;
- } else if (strcmp(hash_str, "sha-512") == 0) {
- algo = GNUTLS_DIG_SHA512;
- } else if (strcmp(hash_str, "md5") == 0) {
- algo = GNUTLS_DIG_MD5;
- }
- free(hash_str);
- if (algo == GNUTLS_DIG_UNKNOWN) {
+ char fp[64], fp_str[64 * 3];
+ size_t fp_size = 64;
+ if (rexmpp_tls_peer_fp(comp->s, comp->dtls, hash_str,
+ fp, fp_str, &fp_size))
+ {
+ rexmpp_jingle_session_terminate
+ (s, sess->sid,
+ rexmpp_xml_new_elem("connectivity-error",
+ "urn:xmpp:jingle:1"),
+ "Failed to obtain the DTLS certificate fingerprint");
+ return REXMPP_E_TLS;
+ } else {
+ const char *fingerprint_cont =
+ rexmpp_xml_text_child(fingerprint);
+ /* Fingerprint string should be uppercase, but
+ allowing any case for now, while Dino uses
+ lowercase. */
+ int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str);
+ if (fingerprint_mismatch) {
rexmpp_log(comp->s, LOG_ERR,
- "Unknown hash algorithm in the peer's fingerprint");
+ "Peer's fingerprint mismatch: expected %s,"
+ " calculated %s",
+ fingerprint_cont, fp_str);
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "Unknown hash algorithm for a DTLS certificate fingerprint");
- break;
+ rexmpp_xml_new_elem("security-error", "urn:xmpp:jingle:1"),
+ "DTLS certificate fingerprint mismatch");
+ return REXMPP_E_TLS;
} else {
-
- char fp[64], fp_str[64 * 3];
- size_t fp_size = 64;
- gnutls_fingerprint(algo, cert_list, fp, &fp_size);
- size_t i;
- for (i = 0; i < fp_size; i++) {
- snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF);
+ /* The fingerprint is fine, proceed to SRTP. */
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "Peer's fingerprint: %s", fp_str);
+
+ rexmpp_tls_srtp_get_keys(s, comp->dtls,
+ SRTP_AES_128_KEY_LEN, SRTP_SALT_LEN,
+ key_mat);
+ /* client key */
+ memcpy(client_sess_key, key_mat, SRTP_AES_128_KEY_LEN);
+ /* server key */
+ memcpy(server_sess_key, key_mat + SRTP_AES_128_KEY_LEN,
+ SRTP_AES_128_KEY_LEN);
+ /* client salt */
+ memcpy(client_sess_key + SRTP_AES_128_KEY_LEN,
+ key_mat + SRTP_AES_128_KEY_LEN * 2,
+ SRTP_SALT_LEN);
+ /* server salt */
+ memcpy(server_sess_key + SRTP_AES_128_KEY_LEN,
+ key_mat + SRTP_AES_128_KEY_LEN * 2 + SRTP_SALT_LEN,
+ SRTP_SALT_LEN);
+
+ int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
+
+ srtp_policy_t inbound;
+ memset(&inbound, 0x0, sizeof(srtp_policy_t));
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
+ inbound.ssrc.type = ssrc_any_inbound;
+ inbound.key = active_role ? server_sess_key : client_sess_key;
+ inbound.window_size = 1024;
+ inbound.allow_repeat_tx = 1;
+ inbound.next = NULL;
+ err = srtp_create(&(comp->srtp_in), &inbound);
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to create srtp_in");
}
- fp_str[fp_size * 3 - 1] = 0;
-
- char *fingerprint_cont = xmlNodeGetContent(fingerprint);
- /* Fingerprint string should be uppercase, but
- allowing any case for now, while Dino uses
- lowercase. */
- int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str);
- free(fingerprint_cont);
- if (fingerprint_mismatch) {
- rexmpp_log(comp->s, LOG_ERR,
- "Peer's fingerprint mismatch: expected %s, calculated %s",
- fingerprint_cont, fp_str);
- rexmpp_jingle_session_terminate
- (s, sess->sid,
- rexmpp_xml_new_node("security-error", "urn:xmpp:jingle:1"),
- "DTLS certificate fingerprint mismatch");
- break;
- } else {
- /* The fingerprint is fine, proceed to SRTP. */
- rexmpp_log(comp->s, LOG_DEBUG, "Peer's fingerprint: %s", fp_str);
-
- key_mat_size =
- gnutls_srtp_get_keys(comp->dtls_session, key_mat,
- SRTP_AES_ICM_128_KEY_LEN_WSALT * 2,
- &client_key, &client_salt,
- &server_key, &server_salt);
- rexmpp_log(s, LOG_DEBUG, "SRTP key material size: %d",
- key_mat_size);
- memcpy(client_sess_key, client_key.data,
- SRTP_AES_128_KEY_LEN);
- memcpy(client_sess_key + SRTP_AES_128_KEY_LEN,
- client_salt.data, SRTP_SALT_LEN);
-
- memcpy(server_sess_key, server_key.data,
- SRTP_AES_128_KEY_LEN);
- memcpy(server_sess_key + SRTP_AES_128_KEY_LEN,
- server_salt.data, SRTP_SALT_LEN);
-
- int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
-
- srtp_policy_t inbound;
- memset(&inbound, 0x0, sizeof(srtp_policy_t));
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
- inbound.ssrc.type = ssrc_any_inbound;
- inbound.key = active_role ? server_sess_key : client_sess_key;
- inbound.window_size = 1024;
- inbound.allow_repeat_tx = 1;
- inbound.next = NULL;
- err = srtp_create(&(comp->srtp_in), &inbound);
- if (err) {
- rexmpp_log(s, LOG_ERR, "Failed to create srtp_in");
- }
- srtp_policy_t outbound;
- memset(&outbound, 0x0, sizeof(srtp_policy_t));
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
- outbound.ssrc.type = ssrc_any_outbound;
- outbound.key = active_role ? client_sess_key : server_sess_key;
- outbound.window_size = 1024;
- outbound.allow_repeat_tx = 1;
- outbound.next = NULL;
- err = srtp_create(&(comp->srtp_out), &outbound);
- if (err) {
- rexmpp_log(s, LOG_ERR, "Failed to create srtp_out");
- }
+ srtp_policy_t outbound;
+ memset(&outbound, 0x0, sizeof(srtp_policy_t));
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
+ outbound.ssrc.type = ssrc_any_outbound;
+ outbound.key = active_role ? client_sess_key : server_sess_key;
+ outbound.window_size = 1024;
+ outbound.allow_repeat_tx = 1;
+ outbound.next = NULL;
+ err = srtp_create(&(comp->srtp_out), &outbound);
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to create srtp_out");
}
}
}
}
}
- } else if (ret != GNUTLS_E_AGAIN) {
- rexmpp_log(s, LOG_ERR, "DTLS error for session %s, component %d: %s",
- sess->sid, comp->component_id, gnutls_strerror(ret));
+ } else if (ret != REXMPP_TLS_E_AGAIN) {
+ rexmpp_log(s, LOG_ERR, "DTLS error for session %s, component %d",
+ sess->sid, comp->component_id);
comp->dtls_state = REXMPP_TLS_ERROR;
if (comp->component_id == 1) {
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
"DTLS connection error");
- break;
+ return REXMPP_E_TLS;
}
}
}
- /* Handle outbound packets */
- srtp_ctx_t *srtp_out;
+ /* Check on the DTLS session, too. */
if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
- srtp_out = comp->srtp_out;
- } else if ((comp->dtls_state == REXMPP_TLS_ERROR || comp->session->rtcp_mux) &&
- comp->session->component[0].dtls_state == REXMPP_TLS_ACTIVE) {
- /* Try to reuse the first component's session. */
- srtp_out = comp->session->component[0].srtp_out;
- } else {
- break;
+ ssize_t received;
+ rexmpp_tls_err_t err =
+ rexmpp_tls_recv(s, comp->dtls, input, 4096, &received);
+ if (err != REXMPP_TLS_SUCCESS && err != REXMPP_TLS_E_AGAIN) {
+ rexmpp_log(s, LOG_ERR,
+ "Error on rexmpp_tls_recv (component id %d), "
+ "terminating Jingle session %s",
+ comp->component_id, sess->sid);
+ rexmpp_jingle_session_terminate
+ (s, sess->sid,
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "TLS reading error");
+ return REXMPP_E_TLS;
+ }
}
+ }
- if (FD_ISSET(comp->udp_socket, read_fds)) {
- input_len = recv(comp->udp_socket, input, 4096, 0);
- if (comp->component_id == 1) {
- err = srtp_protect(srtp_out, input, &input_len);
- } else {
- err = srtp_protect_rtcp(srtp_out, input, &input_len);
+ /* Send captured audio frames for established sessions. */
+ if ((sess->component[0].dtls_state == REXMPP_TLS_ACTIVE)
+ && (sess->component[0].srtp_out != NULL)
+ && (sess->codec != REXMPP_CODEC_UNDEFINED)) {
+ struct ring_buf *capture = &(sess->ring_buffers.capture);
+ while (capture->write_pos != capture->read_pos) {
+ if (sess->codec == REXMPP_CODEC_PCMU) {
+ for (input_len = 12;
+ input_len < 4096 && capture->write_pos != capture->read_pos;
+ input_len++)
+ {
+ input[input_len] =
+ rexmpp_pcmu_encode(capture->buf[capture->read_pos]);
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ } else if (sess->codec == REXMPP_CODEC_PCMA) {
+ for (input_len = 12;
+ input_len < 4096 && capture->write_pos != capture->read_pos;
+ input_len++)
+ {
+ input[input_len] =
+ rexmpp_pcma_encode(capture->buf[capture->read_pos]);
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ }
+#ifdef HAVE_OPUS
+ else if (sess->codec == REXMPP_CODEC_OPUS) {
+ unsigned int samples_available;
+ if (capture->write_pos > capture->read_pos) {
+ samples_available = capture->write_pos - capture->read_pos;
+ } else {
+ samples_available =
+ (PA_BUF_SIZE - capture->read_pos) + capture->write_pos;
+ }
+ unsigned int frame_size = 480;
+ if (samples_available >= frame_size * 2) {
+ opus_int16 pcm[4096];
+ /* Prepare a regular buffer */
+ unsigned int i;
+ for (i = 0;
+ (i < frame_size * 2) &&
+ (capture->write_pos != capture->read_pos);
+ i++)
+ {
+ pcm[i] = capture->buf[capture->read_pos];
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ /* Encode it */
+ int encoded_len;
+ encoded_len = opus_encode(sess->opus_enc,
+ pcm,
+ frame_size,
+ (unsigned char*)input + 12,
+ 4096);
+ if (encoded_len < 0) {
+ rexmpp_log(s, LOG_ERR, "Failed to encode an Opus frame");
+ break;
+ }
+ input_len = 12 + encoded_len;
+ } else {
+ break;
+ }
}
+#endif /* HAVE_OPUS */
+
+ /* Setup an RTP header */
+ uint32_t hl, nl;
+ hl = (2 << 30) /* version */
+ | (0 << 29) /* padding */
+ | (0 << 28) /* extension */
+ | (0 << 24) /* CSRC count */
+ | (0 << 23) /* marker */
+ | (sess->payload_type << 16) /* paylaod type, RFC 3551 */
+ | sess->rtp_seq_num;
+ sess->rtp_seq_num++;
+ nl = htonl(hl);
+ memcpy(input, &nl, sizeof(uint32_t));
+ nl = htonl(sess->rtp_timestamp);
+ memcpy(input + 4, &nl, sizeof(uint32_t));
+ nl = htonl(sess->rtp_ssrc);
+ memcpy(input + 8, &nl, sizeof(uint32_t));
+ /* The RTP header is ready */
+
+ srtp_ctx_t *srtp_out = sess->component[0].srtp_out;
+ err = srtp_protect(srtp_out, input, &input_len);
if (err) {
- rexmpp_log(s, LOG_ERR, "SRT(C)P protect error %d\n", err);
+ rexmpp_log(s, LOG_ERR, "SRTP protect error %d", err);
} else {
nice_agent_send(sess->ice_agent, sess->ice_stream_id,
- sess->rtcp_mux ? 1 : comp->component_id,
+ /* sess->rtcp_mux ? 1 : comp->component_id, */
+ 1,
input_len, input);
}
}
- /* Check on the DTLS session too. */
- if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
- input_len = gnutls_record_recv(comp->dtls_session, input, 4096);
- }
}
}
- g_main_context_iteration(g_main_loop_get_context(s->jingle.gloop), 0);
-#else
+ g_main_context_iteration(g_main_loop_get_context(s->jingle->gloop), 0);
+#else /* ENABLE_CALLS */
(void)s;
(void)read_fds;
-#endif
+#endif /* ENABLE_CALLS */
return REXMPP_SUCCESS;
}
diff --git a/src/rexmpp_jingle.h b/src/rexmpp_jingle.h
index cd23efb..189614a 100644
--- a/src/rexmpp_jingle.h
+++ b/src/rexmpp_jingle.h
@@ -16,15 +16,20 @@
#ifdef ENABLE_CALLS
#include <glib.h>
#include <agent.h>
-#include <gnutls/gnutls.h>
#include <srtp2/srtp.h>
-#define DTLS_SRTP_BUF_SIZE 0x4000
+#include "portaudio.h"
+#ifdef HAVE_OPUS
+#include <opus/opus.h>
+#endif
+#define PA_BUF_SIZE 0x4000
#endif
#include "rexmpp.h"
+#include "rexmpp_tls.h"
+
/** @brief Processes incoming Jingle IQs. */
-int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem);
+int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem);
/** @brief Destroys Jingle sessions. */
void rexmpp_jingle_stop (rexmpp_t *s);
@@ -45,7 +50,7 @@ rexmpp_jingle_send_file (rexmpp_t *s,
rexmpp_err_t
rexmpp_jingle_session_terminate (rexmpp_t *s,
const char *sid,
- xmlNodePtr reason_node,
+ rexmpp_xml_t *reason_node,
const char *reason_text);
typedef struct rexmpp_jingle_component rexmpp_jingle_component_t;
@@ -57,6 +62,13 @@ enum rexmpp_jingle_session_type {
REXMPP_JINGLE_SESSION_MEDIA
};
+enum rexmpp_codec {
+ REXMPP_CODEC_UNDEFINED,
+ REXMPP_CODEC_PCMU,
+ REXMPP_CODEC_PCMA,
+ REXMPP_CODEC_OPUS
+};
+
#ifdef ENABLE_CALLS
/* A structure used for callbacks, to pass rexmpp_t,
rexmpp_jingle_session_t, and the component ID. */
@@ -64,23 +76,31 @@ struct rexmpp_jingle_component {
rexmpp_t *s;
rexmpp_jingle_session_t *session;
int component_id;
- gnutls_session_t dtls_session;
- char dtls_buf[DTLS_SRTP_BUF_SIZE];
+ rexmpp_tls_t *dtls;
enum tls_st dtls_state;
- size_t dtls_buf_len;
srtp_t srtp_in;
srtp_t srtp_out;
- uint16_t udp_port_in;
- uint16_t udp_port_out;
- int udp_socket;
+};
+
+struct ring_buf
+{
+ int16_t buf[PA_BUF_SIZE];
+ unsigned int write_pos;
+ unsigned int read_pos;
+};
+
+struct pa_buffers
+{
+ struct ring_buf capture;
+ struct ring_buf playback;
};
#endif
struct rexmpp_jingle_session {
char *jid;
char *sid;
- xmlNodePtr initiate;
- xmlNodePtr accept;
+ rexmpp_xml_t *initiate;
+ rexmpp_xml_t *accept;
rexmpp_jingle_session_t *next;
/* Sessions are commonly passed to callbacks by external libraries,
so it's convenient to have the corresponding rexmpp_t accessible
@@ -107,13 +127,25 @@ struct rexmpp_jingle_session {
int rtcp_mux;
NiceAgent *ice_agent;
int ice_stream_id;
-#endif
+ PaStream *pa_stream;
+ /* The default codec and payload type for this stream. */
+ enum rexmpp_codec codec;
+ uint8_t payload_type;
+ struct pa_buffers ring_buffers;
+ uint16_t rtp_seq_num;
+ uint16_t rtp_last_seq_num;
+ uint32_t rtp_timestamp;
+ uint32_t rtp_ssrc;
+#ifdef HAVE_OPUS
+ OpusEncoder *opus_enc;
+ OpusDecoder *opus_dec;
+#endif /* HAVE_POUS */
+#endif /* ENABLE_CALLS */
};
struct rexmpp_jingle_ctx {
#ifdef ENABLE_CALLS
GMainLoop* gloop;
- gnutls_certificate_credentials_t dtls_cred;
#endif
rexmpp_jingle_session_t *sessions;
};
@@ -121,20 +153,16 @@ struct rexmpp_jingle_ctx {
int rexmpp_jingle_init (rexmpp_t *s);
rexmpp_err_t rexmpp_jingle_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
-struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv);
+struct timespec * rexmpp_jingle_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv);
int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
rexmpp_err_t
rexmpp_jingle_call (rexmpp_t *s,
- const char *jid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out);
+ const char *jid);
rexmpp_err_t
rexmpp_jingle_call_accept (rexmpp_t *s,
- const char *sid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out);
+ const char *sid);
#endif
diff --git a/src/rexmpp_openpgp.c b/src/rexmpp_openpgp.c
index 13cee44..72fd38b 100644
--- a/src/rexmpp_openpgp.c
+++ b/src/rexmpp_openpgp.c
@@ -47,22 +47,24 @@ Possible future improvements:
#ifdef HAVE_GPGME
#include <gpgme.h>
#endif
-#include <libxml/tree.h>
#include <gcrypt.h>
+#include <errno.h>
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_openpgp.h"
#include "rexmpp_jid.h"
#include "rexmpp_pubsub.h"
#include "rexmpp_base64.h"
+#include "rexmpp_random.h"
#ifdef HAVE_GPGME
void rexmpp_pgp_fp_reply (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -71,33 +73,33 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s,
rexmpp_log(s, LOG_WARNING, "Failed to retrieve an OpenpPGP key");
return;
}
- xmlNodePtr pubsub =
+ rexmpp_xml_t *pubsub =
rexmpp_xml_find_child(response, "http://jabber.org/protocol/pubsub",
"pubsub");
if (pubsub == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: not a pubsub response");
return;
}
- xmlNodePtr items =
- rexmpp_xml_find_child(pubsub, "http://jabber.org/protocol/pubsub",
- "items");
+ rexmpp_xml_t *items =
+ rexmpp_xml_find_child(pubsub, "http://jabber.org/protocol/pubsub",
+ "items");
if (items == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no items in pubsub element");
return;
}
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub", "item");
if (item == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no item in items");
return;
}
- xmlNodePtr pubkey =
+ rexmpp_xml_t *pubkey =
rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "pubkey");
if (pubkey == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no pubkey in item");
return;
}
- xmlNodePtr data =
+ rexmpp_xml_t *data =
rexmpp_xml_find_child(pubkey, "urn:xmpp:openpgp:0", "data");
if (data == NULL) {
rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no data in pubkey");
@@ -106,10 +108,9 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s,
char *key_raw = NULL;
size_t key_raw_len = 0;
- char *key_base64 = xmlNodeGetContent(data);
+ const char *key_base64 = rexmpp_xml_text_child(data);
int base64_err =
rexmpp_base64_from(key_base64, strlen(key_base64), &key_raw, &key_raw_len);
- free(key_base64);
if (base64_err != 0) {
rexmpp_log(s, LOG_ERR, "Base-64 key decoding failure");
return;
@@ -140,18 +141,19 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s,
rexmpp_err_t
rexmpp_openpgp_check_keys (rexmpp_t *s,
const char *jid,
- xmlNodePtr items)
+ rexmpp_xml_t *items)
{
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub#event",
"item");
- xmlNodePtr list =
- rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "public-keys-list");
- xmlNodePtr metadata;
- for (metadata = xmlFirstElementChild(list);
+ rexmpp_xml_t *list =
+ rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "public-keys-list");
+ rexmpp_xml_t *metadata;
+ for (metadata = rexmpp_xml_first_elem_child(list);
metadata != NULL;
- metadata = xmlNextElementSibling(metadata)) {
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ metadata = rexmpp_xml_next_elem_sibling(metadata)) {
+ const char *fingerprint =
+ rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
gpgme_key_t key;
gpgme_error_t err;
err = gpgme_get_key(s->pgp_ctx, fingerprint, &key, 0);
@@ -162,97 +164,95 @@ rexmpp_openpgp_check_keys (rexmpp_t *s,
rexmpp_log(s, LOG_DEBUG,
"Unknown OpenPGP key fingerprint for %s: %s",
jid, fingerprint);
- xmlNodePtr fp_req = xmlNewNode(NULL, "pubsub");
- xmlNewNs(fp_req, "http://jabber.org/protocol/pubsub", NULL);
- xmlNodePtr fp_req_items = xmlNewNode(NULL, "items");
- xmlNewProp(fp_req_items, "max_items", "1");
+ rexmpp_xml_t *fp_req =
+ rexmpp_xml_new_elem("pubsub", "http://jabber.org/protocol/pubsub");
+ rexmpp_xml_t *fp_req_items =
+ rexmpp_xml_new_elem("items", NULL);
+ rexmpp_xml_add_attr(fp_req_items, "max_items", "1");
char key_node[72];
snprintf(key_node, 72, "urn:xmpp:openpgp:0:public-keys:%s", fingerprint);
- xmlNewProp(fp_req_items, "node", key_node);
- xmlAddChild(fp_req, fp_req_items);
+ rexmpp_xml_add_attr(fp_req_items, "node", key_node);
+ rexmpp_xml_add_child(fp_req, fp_req_items);
rexmpp_iq_new(s, "get", jid, fp_req, rexmpp_pgp_fp_reply, NULL);
} else if (gpg_err_code(err) != GPG_ERR_NO_ERROR) {
rexmpp_log(s, LOG_WARNING,
"OpenPGP error when looking for a key: %s",
gpgme_strerror(err));
}
- free(fingerprint);
}
return REXMPP_SUCCESS;
}
-xmlNodePtr rexmpp_published_fingerprints (rexmpp_t *s, const char *jid) {
- xmlNodePtr published =
+rexmpp_xml_t *rexmpp_published_fingerprints (rexmpp_t *s, const char *jid) {
+ rexmpp_xml_t *published =
rexmpp_find_event(s, jid, "urn:xmpp:openpgp:0:public-keys", NULL);
if (published == NULL) {
return NULL;
}
- xmlNodePtr event =
+ rexmpp_xml_t *event =
rexmpp_xml_find_child(published, "http://jabber.org/protocol/pubsub#event",
"event");
- xmlNodePtr items =
+ rexmpp_xml_t *items =
rexmpp_xml_find_child(event, "http://jabber.org/protocol/pubsub#event",
"items");
- xmlNodePtr item =
+ rexmpp_xml_t *item =
rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub#event",
"item");
- xmlNodePtr list =
+ rexmpp_xml_t *list =
rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0",
"public-keys-list");
- xmlNodePtr published_fps = xmlFirstElementChild(list);
+ rexmpp_xml_t *published_fps = list->alt.elem.children;
return published_fps;
}
int rexmpp_openpgp_key_is_published (rexmpp_t *s, const char *fp) {
- xmlNodePtr metadata;
+ rexmpp_xml_t *metadata;
for (metadata = rexmpp_published_fingerprints(s, s->assigned_jid.bare);
metadata != NULL;
- metadata = xmlNextElementSibling(metadata)) {
+ metadata = metadata->next) {
if (! rexmpp_xml_match(metadata, "urn:xmpp:openpgp:0", "pubkey-metadata")) {
continue;
}
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
}
- int matches = (strcmp(fingerprint, fp) == 0);
- free(fingerprint);
- if (matches) {
+ if (strcmp(fingerprint, fp) == 0) {
return 1;
}
}
return 0;
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_remove_key_from_list (rexmpp_t *s,
const char *fp)
{
- xmlNodePtr fps =
- xmlCopyNodeList(rexmpp_published_fingerprints(s, s->assigned_jid.bare));
- xmlNodePtr metadata, prev = NULL;
+ rexmpp_xml_t *fps =
+ rexmpp_xml_clone_list(rexmpp_published_fingerprints(s, s->assigned_jid.bare));
+ rexmpp_xml_t *metadata, *prev = NULL;
for (metadata = fps;
metadata != NULL;
- prev = metadata, metadata = xmlNextElementSibling(metadata)) {
+ prev = metadata, metadata = rexmpp_xml_next_elem_sibling(metadata)) {
if (! rexmpp_xml_match(metadata, "urn:xmpp:openpgp:0", "pubkey-metadata")) {
continue;
}
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ const char *fingerprint =
+ rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
}
int matches = (strcmp(fingerprint, fp) == 0);
- free(fingerprint);
if (matches) {
if (prev != NULL) {
prev->next = metadata->next;
} else {
fps = metadata->next;
}
- xmlFreeNode(metadata);
+ rexmpp_xml_free(metadata);
return fps;
}
}
@@ -261,8 +261,8 @@ rexmpp_openpgp_remove_key_from_list (rexmpp_t *s,
void rexmpp_pgp_key_publish_list_iq (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -275,18 +275,18 @@ void rexmpp_pgp_key_publish_list_iq (rexmpp_t *s,
rexmpp_log(s, LOG_INFO, "Published an OpenpPGP key list");
}
-void rexmpp_pgp_key_fp_list_upload (rexmpp_t *s, xmlNodePtr metadata) {
- xmlNodePtr keylist = xmlNewNode(NULL, "public-keys-list");
- xmlNewNs(keylist, "urn:xmpp:openpgp:0", NULL);
- xmlAddChild(keylist, metadata);
+void rexmpp_pgp_key_fp_list_upload (rexmpp_t *s, rexmpp_xml_t *metadata) {
+ rexmpp_xml_t *keylist =
+ rexmpp_xml_new_elem("public-keys-list", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_child(keylist, metadata);
rexmpp_pubsub_item_publish(s, NULL, "urn:xmpp:openpgp:0:public-keys",
NULL, keylist, rexmpp_pgp_key_publish_list_iq, NULL);
}
void rexmpp_pgp_key_delete_iq (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -295,18 +295,17 @@ void rexmpp_pgp_key_delete_iq (rexmpp_t *s,
rexmpp_log(s, LOG_WARNING, "Failed to delete an OpenpPGP key");
return;
}
- xmlNodePtr pubsub = xmlFirstElementChild(req);
- xmlNodePtr publish = xmlFirstElementChild(pubsub);
- char *node = xmlGetProp(publish, "node");
- char *fingerprint = node + 31;
+ rexmpp_xml_t *pubsub = req->alt.elem.children;
+ rexmpp_xml_t *publish = pubsub->alt.elem.children;;
+ const char *node = rexmpp_xml_find_attr_val(publish, "node");
+ const char *fingerprint = node + 31;
rexmpp_log(s, LOG_INFO, "Removed OpenpPGP key %s", fingerprint);
- free(node);
}
void rexmpp_pgp_key_publish_iq (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -316,10 +315,10 @@ void rexmpp_pgp_key_publish_iq (rexmpp_t *s,
return;
}
rexmpp_log(s, LOG_INFO, "Uploaded an OpenpPGP key");
- xmlNodePtr pubsub = xmlFirstElementChild(req);
- xmlNodePtr publish = xmlFirstElementChild(pubsub);
- char *node = xmlGetProp(publish, "node");
- char *fingerprint = node + 31;
+ rexmpp_xml_t *pubsub = req->alt.elem.children;
+ rexmpp_xml_t *publish = pubsub->alt.elem.children;;
+ const char *node = rexmpp_xml_find_attr_val(publish, "node");
+ const char *fingerprint = node + 31;
char time_str[42];
time_t t = time(NULL);
@@ -327,22 +326,20 @@ void rexmpp_pgp_key_publish_iq (rexmpp_t *s,
gmtime_r(&t, &utc_time);
strftime(time_str, 42, "%FT%TZ", &utc_time);
- xmlNodePtr metadata = xmlNewNode(NULL, "pubkey-metadata");
- xmlNewNs(metadata, "urn:xmpp:openpgp:0", NULL);
- xmlNewProp(metadata, "date", time_str);
- xmlNewProp(metadata, "v4-fingerprint", fingerprint);
+ rexmpp_xml_t *metadata =
+ rexmpp_xml_new_elem("pubkey-metadata", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_attr(metadata, "date", time_str);
+ rexmpp_xml_add_attr(metadata, "v4-fingerprint", fingerprint);
- free(node);
-
- xmlNodePtr fps = rexmpp_openpgp_remove_key_from_list(s, fingerprint);
+ rexmpp_xml_t *fps = rexmpp_openpgp_remove_key_from_list(s, fingerprint);
if (fps != NULL) {
- metadata->next = xmlCopyNodeList(fps);
+ metadata->next = fps;
}
rexmpp_pgp_key_fp_list_upload(s, metadata);
}
void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp) {
- xmlNodePtr new_fp_list = rexmpp_openpgp_remove_key_from_list(s, fp);
+ rexmpp_xml_t *new_fp_list = rexmpp_openpgp_remove_key_from_list(s, fp);
if (new_fp_list != NULL) {
rexmpp_pgp_key_fp_list_upload(s, new_fp_list);
}
@@ -382,14 +379,14 @@ rexmpp_err_t rexmpp_openpgp_publish_key (rexmpp_t *s, const char *fp) {
key_raw = gpgme_data_release_and_get_mem(key_dh, &key_raw_len);
rexmpp_base64_to(key_raw, key_raw_len, &key_base64, &key_base64_len);
free(key_raw);
- xmlNodePtr data = xmlNewNode(NULL, "data");
- xmlNewNs(data, "urn:xmpp:openpgp:0", NULL);
- xmlNodeAddContent(data, key_base64);
+ rexmpp_xml_t *data =
+ rexmpp_xml_new_elem("data", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_text(data, key_base64);
free(key_base64);
- xmlNodePtr pubkey = xmlNewNode(NULL, "pubkey");
- xmlNewNs(pubkey, "urn:xmpp:openpgp:0", NULL);
- xmlAddChild(pubkey, data);
+ rexmpp_xml_t *pubkey =
+ rexmpp_xml_new_elem("pubkey", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_child(pubkey, data);
char time_str[42];
time_t t = time(NULL);
@@ -421,9 +418,9 @@ int rexmpp_openpgp_fingerprint_matches (const char *f1, const char *f2) {
return 1;
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
- xmlNodePtr message,
+ rexmpp_xml_t *message,
int *valid)
{
gpgme_error_t err;
@@ -433,14 +430,13 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "Not a message element");
return NULL;
}
- char *from_str = xmlGetProp(message, "from");
+ const char *from_str = rexmpp_xml_find_attr_val(message, "from");
if (from_str == NULL) {
rexmpp_log(s, LOG_ERR, "No 'from' attribute");
return NULL;
}
rexmpp_jid_parse(from_str, &from);
- free(from_str);
- char *to_str = xmlGetProp(message, "to");
+ const char *to_str = rexmpp_xml_find_attr_val(message, "to");
if (to_str == NULL) {
if (strcmp(from.bare, s->assigned_jid.bare) != 0) {
rexmpp_log(s, LOG_ERR, "No 'to' attribute");
@@ -449,18 +445,16 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
rexmpp_jid_parse(from.full, &to);
} else {
rexmpp_jid_parse(to_str, &to);
- free(to_str);
}
- xmlNodePtr openpgp =
+ rexmpp_xml_t *openpgp =
rexmpp_xml_find_child(message, "urn:xmpp:openpgp:0", "openpgp");
if (openpgp == NULL) {
rexmpp_log(s, LOG_ERR, "No 'openpgp' child element");
return NULL;
}
- char *cipher_str = xmlNodeGetContent(openpgp);
- xmlNodePtr plain =
+ const char *cipher_str = rexmpp_xml_text_child(openpgp);
+ rexmpp_xml_t *plain =
rexmpp_openpgp_decrypt_verify(s, cipher_str);
- free(cipher_str);
if (plain == NULL) {
return NULL;
}
@@ -476,23 +470,20 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
return plain;
}
- xmlNodePtr child;
+ rexmpp_xml_t *child;
int found = 0;
- for (child = xmlFirstElementChild(plain);
+ for (child = rexmpp_xml_first_elem_child(plain);
child != NULL && ! found;
- child = xmlNextElementSibling(child))
+ child = rexmpp_xml_next_elem_sibling(child))
{
if (rexmpp_xml_match(child, "urn:xmpp:openpgp:0", "to")) {
- char *to_jid = xmlGetProp(child, "jid");
+ const char *to_jid = rexmpp_xml_find_attr_val(child, "jid");
if (to_jid == NULL) {
rexmpp_log(s, LOG_WARNING,
"Found a 'to' element without a 'jid' attribute");
} else if (strcmp(to_jid, to.bare) == 0) {
found = 1;
}
- if (to_jid != NULL) {
- free(to_jid);
- }
}
}
if (! found) {
@@ -520,11 +511,11 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
}
found = 0;
- xmlNodePtr metadata;
+ rexmpp_xml_t *metadata;
for (metadata = rexmpp_published_fingerprints(s, from.bare);
metadata != NULL && ! found;
- metadata = xmlNextElementSibling(metadata)) {
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ metadata = metadata->next) {
+ const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
@@ -532,7 +523,6 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
if (rexmpp_openpgp_fingerprint_matches(fingerprint, sig->fpr)) {
found = 1;
}
- free(fingerprint);
}
if (! found) {
rexmpp_log(s, LOG_ERR, "No %s's known key matches that of the signature",
@@ -570,7 +560,7 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
return plain;
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
const char *cipher_base64)
{
@@ -600,7 +590,7 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "Failed to release and get memory");
return NULL;
}
- xmlNodePtr elem = rexmpp_xml_parse(plain, plain_len);
+ rexmpp_xml_t *elem = rexmpp_xml_parse(plain, plain_len);
if(elem == NULL) {
rexmpp_log(s, LOG_ERR, "Failed to parse an XML document");
}
@@ -615,11 +605,11 @@ void rexmpp_openpgp_add_keys (rexmpp_t *s,
int *allocated)
{
gpgme_error_t err;
- xmlNodePtr metadata;
+ rexmpp_xml_t *metadata;
for (metadata = rexmpp_published_fingerprints(s, jid);
metadata != NULL;
- metadata = xmlNextElementSibling(metadata)) {
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ metadata = metadata->next) {
+ const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
@@ -630,7 +620,15 @@ void rexmpp_openpgp_add_keys (rexmpp_t *s,
*nkeys = *nkeys + 1;
if (*nkeys == *allocated) {
*allocated = *allocated * 2;
- *keys = realloc(*keys, sizeof(gpgme_key_t *) * *allocated);
+ gpgme_key_t *new_keys =
+ realloc(*keys, sizeof(gpgme_key_t *) * *allocated);
+ if (new_keys == NULL) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to reallocate the OpenPGP keys array: %s",
+ strerror(errno));
+ continue;
+ }
+ *keys = new_keys;
}
} else {
gpgme_key_unref((*keys)[*nkeys]);
@@ -643,19 +641,18 @@ void rexmpp_openpgp_add_keys (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "Failed to read key %s: %s",
fingerprint, gpgme_strerror(err));
}
- free(fingerprint);
}
}
void rexmpp_openpgp_set_signers (rexmpp_t *s) {
gpgme_error_t err;
- xmlNodePtr metadata;
+ rexmpp_xml_t *metadata;
gpgme_key_t sec_key;
gpgme_signers_clear(s->pgp_ctx);
for (metadata = rexmpp_published_fingerprints(s, s->initial_jid.bare);
metadata != NULL;
- metadata = xmlNextElementSibling(metadata)) {
- char *fingerprint = xmlGetProp(metadata, "v4-fingerprint");
+ metadata = metadata->next) {
+ const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint");
if (fingerprint == NULL) {
rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata");
continue;
@@ -670,12 +667,11 @@ void rexmpp_openpgp_set_signers (rexmpp_t *s) {
rexmpp_log(s, LOG_ERR, "Failed to read key %s: %s",
fingerprint, gpgme_strerror(err));
}
- free(fingerprint);
}
}
char *rexmpp_openpgp_payload (rexmpp_t *s,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
const char **recipients,
const char **signers,
enum rexmpp_ox_mode mode)
@@ -693,8 +689,8 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
} else if (mode == REXMPP_OX_CRYPT) {
elem_name = "crypt";
}
- xmlNodePtr elem = xmlNewNode(NULL, elem_name);
- xmlNewNs(elem, "urn:xmpp:openpgp:0", NULL);
+ rexmpp_xml_t *elem =
+ rexmpp_xml_new_elem(elem_name, "urn:xmpp:openpgp:0");
if (mode == REXMPP_OX_SIGN || mode == REXMPP_OX_SIGNCRYPT) {
if (signers == NULL) {
@@ -717,10 +713,10 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
/* Add all the recipients. */
for (i = 0; recipients[i] != NULL; i++) {
- xmlNodePtr to = xmlNewNode(NULL, "to");
- xmlNewNs(to, "urn:xmpp:openpgp:0", NULL);
- xmlNewProp(to, "jid", recipients[i]);
- xmlAddChild(elem, to);
+ rexmpp_xml_t *to =
+ rexmpp_xml_new_elem("to", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_attr(to, "jid", recipients[i]);
+ rexmpp_xml_add_child(elem, to);
}
}
@@ -730,21 +726,26 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
struct tm utc_time;
gmtime_r(&t, &utc_time);
strftime(time_str, 42, "%FT%TZ", &utc_time);
- xmlNodePtr time = xmlNewNode(NULL, "time");
- xmlNewNs(time, "urn:xmpp:openpgp:0", NULL);
- xmlNewProp(time, "stamp", time_str);
- xmlAddChild(elem, time);
+ rexmpp_xml_t *time =
+ rexmpp_xml_new_elem("time", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_attr(time, "stamp", time_str);
+ rexmpp_xml_add_child(elem, time);
/* Add the payload. */
- xmlNodePtr pl = xmlNewNode(NULL, "payload");
- xmlNewNs(pl, "urn:xmpp:openpgp:0", NULL);
- xmlAddChild(pl, payload);
- xmlAddChild(elem, pl);
+ rexmpp_xml_t *pl =
+ rexmpp_xml_new_elem("payload", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_child(pl, payload);
+ rexmpp_xml_add_child(elem, pl);
if (mode == REXMPP_OX_CRYPT || mode == REXMPP_OX_SIGNCRYPT) {
/* 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++) {
@@ -753,21 +754,21 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
/* A random-length random-content padding. */
char *rand_str, rand[256];
- gcry_create_nonce(rand, 1);
+ rexmpp_random_buf(rand, 1);
size_t rand_str_len = 0, rand_len = (unsigned char)rand[0] % (255 - 16) + 16;
- gcry_create_nonce(rand, rand_len);
+ rexmpp_random_buf(rand, rand_len);
rexmpp_base64_to(rand, rand_len, &rand_str, &rand_str_len);
- xmlNodePtr rpad = xmlNewNode(NULL, "rpad");
- xmlNewNs(rpad, "urn:xmpp:openpgp:0", NULL);
- xmlNodeAddContent(rpad, rand_str);
+ rexmpp_xml_t *rpad =
+ rexmpp_xml_new_elem("rpad", "urn:xmpp:openpgp:0");
+ rexmpp_xml_add_text(rpad, rand_str);
free(rand_str);
- xmlAddChild(elem, rpad);
+ rexmpp_xml_add_child(elem, rpad);
}
/* Serialize the resulting XML. */
- char *plaintext = rexmpp_xml_serialize(elem);
- xmlFreeNode(elem);
+ char *plaintext = rexmpp_xml_serialize(elem, 0);
+ rexmpp_xml_free(elem);
/* Encrypt, base64-encode. */
gpgme_data_t cipher_dh, plain_dh;
@@ -832,7 +833,7 @@ rexmpp_err_t gpgme_not_supported(rexmpp_t *s) {
rexmpp_err_t
rexmpp_openpgp_check_keys (rexmpp_t *s,
const char *jid,
- xmlNodePtr items) {
+ rexmpp_xml_t *items) {
(void)jid;
(void)items;
return gpgme_not_supported(s);
@@ -848,7 +849,7 @@ void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp) {
gpgme_not_supported(s);
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
const char *cipher_base64) {
(void)cipher_base64;
@@ -856,9 +857,9 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
return NULL;
}
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
- xmlNodePtr message,
+ rexmpp_xml_t *message,
int *valid) {
(void)message;
(void)valid;
@@ -867,14 +868,14 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
}
char *rexmpp_openpgp_payload (rexmpp_t *s,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
const char **recipients,
const char **signers,
enum rexmpp_ox_mode mode) {
(void)recipients;
(void)signers;
(void)mode;
- xmlFreeNode(payload);
+ rexmpp_xml_free(payload);
gpgme_not_supported(s);
return NULL;
}
diff --git a/src/rexmpp_openpgp.h b/src/rexmpp_openpgp.h
index 7470347..2132930 100644
--- a/src/rexmpp_openpgp.h
+++ b/src/rexmpp_openpgp.h
@@ -29,7 +29,7 @@ enum rexmpp_ox_mode {
rexmpp_err_t
rexmpp_openpgp_check_keys (rexmpp_t *s,
const char *jid,
- xmlNodePtr items);
+ rexmpp_xml_t *items);
/**
@brief Publishes a key via PEP/pubsub.
@@ -52,7 +52,7 @@ void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp);
@param[in] cipher_base64 An OpenPGP ciphertext.
@returns A plaintext message body.
*/
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
const char *cipher_base64);
@@ -65,9 +65,9 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s,
valid.
@returns A decrypted message body.
*/
-xmlNodePtr
+rexmpp_xml_t *
rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
- xmlNodePtr message,
+ rexmpp_xml_t *message,
int *valid);
/**
@@ -83,7 +83,7 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s,
@returns An encoded <openpgp> payload.
*/
char *rexmpp_openpgp_payload (rexmpp_t *s,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
const char **recipients,
const char **signers,
enum rexmpp_ox_mode mode);
diff --git a/src/rexmpp_pubsub.c b/src/rexmpp_pubsub.c
index 145a352..58cb060 100644
--- a/src/rexmpp_pubsub.c
+++ b/src/rexmpp_pubsub.c
@@ -7,25 +7,22 @@
*/
#include "rexmpp.h"
+#include "rexmpp_xml.h"
void
rexmpp_pubsub_iq (rexmpp_t *s,
const char *iq_type,
const char *pubsub_namespace,
const char *service_jid,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t callback,
void *cb_data)
{
- xmlNodePtr pubsub = xmlNewNode(NULL, "pubsub");
if (pubsub_namespace == NULL) {
- xmlNewNs(pubsub, "http://jabber.org/protocol/pubsub", NULL);
- } else {
- xmlNewNs(pubsub, pubsub_namespace, NULL);
+ pubsub_namespace = "http://jabber.org/protocol/pubsub";
}
-
- xmlAddChild(pubsub, payload);
-
+ rexmpp_xml_t *pubsub = rexmpp_xml_new_elem("pubsub", pubsub_namespace);
+ rexmpp_xml_add_child(pubsub, payload);
rexmpp_iq_new(s, iq_type, service_jid, pubsub, callback, cb_data);
}
@@ -34,21 +31,21 @@ rexmpp_pubsub_item_publish (rexmpp_t *s,
const char *service_jid,
const char *node,
const char *item_id,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t callback,
void *cb_data)
{
- xmlNodePtr item = xmlNewNode(NULL, "item");
- xmlNewNs(item, "http://jabber.org/protocol/pubsub", NULL);
+ rexmpp_xml_t *item =
+ rexmpp_xml_new_elem("item", "http://jabber.org/protocol/pubsub");
if (item_id != NULL) {
- xmlNewProp(item, "id", item_id);
+ rexmpp_xml_add_attr(item, "id", item_id);
}
- xmlAddChild(item, payload);
+ rexmpp_xml_add_child(item, payload);
- xmlNodePtr publish = xmlNewNode(NULL, "publish");
- xmlNewNs(publish, "http://jabber.org/protocol/pubsub", NULL);
- xmlNewProp(publish, "node", node);
- xmlAddChild(publish, item);
+ rexmpp_xml_t *publish =
+ rexmpp_xml_new_elem("publish", "http://jabber.org/protocol/pubsub");
+ rexmpp_xml_add_attr(publish, "node", node);
+ rexmpp_xml_add_child(publish, item);
rexmpp_pubsub_iq(s, "set", NULL, service_jid, publish, callback, cb_data);
}
@@ -61,16 +58,16 @@ rexmpp_pubsub_item_retract (rexmpp_t *s,
rexmpp_iq_callback_t callback,
void *cb_data)
{
- xmlNodePtr item = xmlNewNode(NULL, "item");
- xmlNewNs(item, "http://jabber.org/protocol/pubsub", NULL);
+ rexmpp_xml_t *item =
+ rexmpp_xml_new_elem("item", "http://jabber.org/protocol/pubsub");
if (item_id != NULL) {
- xmlNewProp(item, "id", item_id);
+ rexmpp_xml_add_attr(item, "id", item_id);
}
- xmlNodePtr retract = xmlNewNode(NULL, "retract");
- xmlNewNs(retract, "http://jabber.org/protocol/pubsub", NULL);
- xmlNewProp(retract, "node", node);
- xmlAddChild(retract, item);
+ rexmpp_xml_t *retract =
+ rexmpp_xml_new_elem("retract", "http://jabber.org/protocol/pubsub");
+ rexmpp_xml_add_attr(retract, "node", node);
+ rexmpp_xml_add_child(retract, item);
rexmpp_pubsub_iq(s, "set", NULL, service_jid, retract, callback, cb_data);
}
@@ -82,9 +79,9 @@ rexmpp_pubsub_node_delete (rexmpp_t *s,
rexmpp_iq_callback_t callback,
void *cb_data)
{
- xmlNodePtr delete = xmlNewNode(NULL, "delete");
- xmlNewNs(delete, "http://jabber.org/protocol/pubsub#owner", NULL);
- xmlNewProp(delete, "node", node);
+ rexmpp_xml_t *delete =
+ rexmpp_xml_new_elem("delete", "http://jabber.org/protocol/pubsub#owner");
+ rexmpp_xml_add_attr(delete, "node", node);
rexmpp_pubsub_iq(s, "set", "http://jabber.org/protocol/pubsub#owner",
service_jid, delete, callback, cb_data);
diff --git a/src/rexmpp_pubsub.h b/src/rexmpp_pubsub.h
index b5a7c3e..86675d3 100644
--- a/src/rexmpp_pubsub.h
+++ b/src/rexmpp_pubsub.h
@@ -11,7 +11,7 @@ rexmpp_pubsub_iq (rexmpp_t *s,
const char *iq_type,
const char *pubsub_namespace,
const char *service_jid,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t callback,
void *cb_data);
@@ -20,7 +20,7 @@ rexmpp_pubsub_item_publish (rexmpp_t *s,
const char *service_jid,
const char *node,
const char *item_id,
- xmlNodePtr payload,
+ rexmpp_xml_t *payload,
rexmpp_iq_callback_t callback,
void *cb_data);
diff --git a/src/rexmpp_random.c b/src/rexmpp_random.c
new file mode 100644
index 0000000..11e2b73
--- /dev/null
+++ b/src/rexmpp_random.c
@@ -0,0 +1,34 @@
+/**
+ @file rexmpp_random.c
+ @brief Random generation
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include "config.h"
+#include "rexmpp_base64.h"
+
+#ifdef HAVE_GCRYPT
+#include <gcrypt.h>
+#else
+#define _GNU_SOURCE
+#include <stdlib.h>
+#endif
+
+
+void rexmpp_random_buf (void *buf, size_t len) {
+#ifdef HAVE_GCRYPT
+ gcry_create_nonce(buf, len);
+#else
+ 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
new file mode 100644
index 0000000..b0f1978
--- /dev/null
+++ b/src/rexmpp_random.h
@@ -0,0 +1,29 @@
+/**
+ @file rexmpp_random.h
+ @brief Random generation
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_RANDOM_H
+#define REXMPP_RANDOM_H
+
+/**
+ @brief Fills a buffer with cryptographically-secure random data.
+ @param[out] buf A buffer to write into.
+ @param[in] len The number of bytes to fill.
+
+ Uses arc4random_buf or gcry_create_nonce, depending on what is
+ available.
+*/
+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_random.rs b/src/rexmpp_random.rs
new file mode 100644
index 0000000..dbabc71
--- /dev/null
+++ b/src/rexmpp_random.rs
@@ -0,0 +1,5 @@
+use std::os::raw::{c_char};
+
+extern {
+ pub fn rexmpp_random_id () -> *mut c_char;
+}
diff --git a/src/rexmpp_roster.c b/src/rexmpp_roster.c
index 63a52b9..3deb5b7 100644
--- a/src/rexmpp_roster.c
+++ b/src/rexmpp_roster.c
@@ -7,24 +7,24 @@
*/
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include <syslog.h>
#include <string.h>
-#include <libxml/tree.h>
-#include <libxml/xmlsave.h>
+#include <stdlib.h>
-xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s,
- const char *jid,
- xmlNodePtr *prev_item)
+rexmpp_xml_t *
+rexmpp_roster_find_item (rexmpp_t *s,
+ const char *jid,
+ rexmpp_xml_t **prev_item)
{
- xmlNodePtr prev = NULL, cur = s->roster_items;
+ rexmpp_xml_t *prev = NULL, *cur = s->roster_items;
while (cur != NULL) {
- char *cur_jid = xmlGetProp(cur, "jid");
+ const char *cur_jid = rexmpp_xml_find_attr_val(cur, "jid");
if (cur_jid == NULL) {
rexmpp_log(s, LOG_ALERT, "No jid found in a roster item.");
return NULL;
}
int match = (strcmp(cur_jid, jid) == 0);
- free(cur_jid);
if (match) {
if (prev_item != NULL) {
*prev_item = prev;
@@ -32,75 +32,75 @@ xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s,
return cur;
}
prev = cur;
- cur = cur->next;
+ cur = rexmpp_xml_next_elem_sibling(cur);
}
return NULL;
}
-rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item) {
+rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, rexmpp_xml_t *item) {
rexmpp_err_t ret = REXMPP_SUCCESS;
if (! rexmpp_xml_match(item, "jabber:iq:roster", "item")) {
rexmpp_log(s, LOG_ERR, "No roster item.");
return REXMPP_E_PARAM;
}
- char *subscription = xmlGetProp(item, "subscription");
- char *jid = xmlGetProp(item, "jid");
+ const char *subscription = rexmpp_xml_find_attr_val(item, "subscription");
+ const char *jid = rexmpp_xml_find_attr_val(item, "jid");
if (subscription != NULL && strcmp(subscription, "remove") == 0) {
/* Delete the item. */
- xmlNodePtr prev, cur;
+ rexmpp_xml_t *prev, *cur;
cur = rexmpp_roster_find_item(s, jid, &prev);
if (cur != NULL) {
if (prev != NULL) {
- prev->next = cur->next;
+ prev->next = rexmpp_xml_next_elem_sibling(cur);
} else {
- s->roster_items = cur->next;
+ s->roster_items = rexmpp_xml_next_elem_sibling(cur);
}
- xmlFreeNode(cur);
+ rexmpp_xml_free(cur);
} else {
ret = REXMPP_E_ROSTER_ITEM_NOT_FOUND;
}
} else {
/* Add or modify the item. */
- xmlNodePtr cur, prev;
+ rexmpp_xml_t *cur, *prev;
cur = rexmpp_roster_find_item(s, jid, &prev);
/* Remove the item if it was in the roster before. */
if (cur != NULL) {
if (prev != NULL) {
- prev->next = cur->next;
+ prev->next = rexmpp_xml_next_elem_sibling(cur);
} else {
- s->roster_items = cur->next;
+ s->roster_items = rexmpp_xml_next_elem_sibling(cur);
}
- xmlFreeNode(cur);
+ rexmpp_xml_free(cur);
}
/* Add the new item. */
- xmlNodePtr new_item = xmlCopyNode(item, 1);
+ rexmpp_xml_t *new_item = rexmpp_xml_clone(item);
new_item->next = s->roster_items;
s->roster_items = new_item;
}
- free(jid);
- if (subscription != NULL) {
- free(subscription);
- }
if (s->roster_modify_cb != NULL) {
s->roster_modify_cb(s, item);
}
return ret;
}
-void rexmpp_roster_set (rexmpp_t *s, xmlNodePtr query) {
+void rexmpp_roster_set (rexmpp_t *s, rexmpp_xml_t *query) {
if (s->roster_items != NULL) {
- xmlFreeNodeList(s->roster_items);
+ rexmpp_xml_free_list(s->roster_items);
}
if (s->roster_ver != NULL) {
free(s->roster_ver);
}
- s->roster_ver = xmlGetProp(query, "ver");
- s->roster_items = xmlCopyNodeList(xmlFirstElementChild(query));
+ const char *roster_ver = rexmpp_xml_find_attr_val(query, "ver");
+ s->roster_ver = NULL;
+ if (roster_ver != NULL) {
+ s->roster_ver = strdup(roster_ver);
+ }
+ s->roster_items = rexmpp_xml_clone_list(rexmpp_xml_first_elem_child(query));
if (s->roster_modify_cb != NULL) {
- xmlNodePtr item;
- for (item = xmlFirstElementChild(query);
+ rexmpp_xml_t *item;
+ for (item = rexmpp_xml_first_elem_child(query);
item != NULL;
- item = xmlNextElementSibling(item))
+ item = rexmpp_xml_next_elem_sibling(item))
{
s->roster_modify_cb(s, item);
}
@@ -112,10 +112,11 @@ void rexmpp_roster_cache_read (rexmpp_t *s) {
rexmpp_log(s, LOG_WARNING, "No roster cache file path is set.");
return;
}
- xmlDocPtr doc = xmlReadFile(s->roster_cache_file, "utf-8", XML_PARSE_NONET);
- xmlNodePtr query = xmlDocGetRootElement(doc);
- rexmpp_roster_set(s, query);
- xmlFreeDoc(doc);
+ rexmpp_xml_t *query = rexmpp_xml_read_file(s->roster_cache_file);
+ if (query != NULL) {
+ rexmpp_roster_set(s, query);
+ rexmpp_xml_free(query);
+ }
}
void rexmpp_roster_cache_write (rexmpp_t *s) {
@@ -123,24 +124,22 @@ void rexmpp_roster_cache_write (rexmpp_t *s) {
rexmpp_log(s, LOG_WARNING, "No roster cache file path is set.");
return;
}
- xmlDocPtr doc = xmlNewDoc("1.0");
- xmlNodePtr query = xmlNewDocNode(doc, NULL, "query", NULL);
- xmlDocSetRootElement(doc, query);
- xmlNewNs(query, "jabber:iq:roster", NULL);
+
+ rexmpp_xml_t *query = rexmpp_xml_new_elem("query", "jabber:iq:roster");
if (s->roster_ver != NULL) {
- xmlNewProp(query, "ver", s->roster_ver);
+ rexmpp_xml_add_attr(query, "ver", s->roster_ver);
}
if (s->roster_items != NULL) {
- xmlAddChild(query, xmlDocCopyNodeList(doc, s->roster_items));
+ rexmpp_xml_add_child(query, rexmpp_xml_clone_list(s->roster_items));
}
- xmlSaveFileEnc(s->roster_cache_file, doc, "utf-8");
- xmlFreeDoc(doc);
+ rexmpp_xml_write_file(s->roster_cache_file, query);
+ rexmpp_xml_free(query);
}
void rexmpp_iq_roster_get (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)ptr;
@@ -149,8 +148,9 @@ void rexmpp_iq_roster_get (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "Roster loading failed.");
return;
}
- xmlNodePtr query = xmlFirstElementChild(response);
- if (! rexmpp_xml_match(query, "jabber:iq:roster", "query")) {
+ rexmpp_xml_t *query =
+ rexmpp_xml_find_child(response, "jabber:iq:roster", "query");
+ if (query == NULL) {
rexmpp_log(s, LOG_DEBUG, "No roster query in reply.");
return;
}
diff --git a/src/rexmpp_roster.h b/src/rexmpp_roster.h
index 1fa9183..f613a7b 100644
--- a/src/rexmpp_roster.h
+++ b/src/rexmpp_roster.h
@@ -7,15 +7,15 @@
*/
-xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s,
- const char *jid,
- xmlNodePtr *prev_item);
-rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item);
-void rexmpp_roster_set (rexmpp_t *s, xmlNodePtr query);
+rexmpp_xml_t *rexmpp_roster_find_item (rexmpp_t *s,
+ const char *jid,
+ rexmpp_xml_t **prev_item);
+rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, rexmpp_xml_t *item);
+void rexmpp_roster_set (rexmpp_t *s, rexmpp_xml_t *query);
void rexmpp_roster_cache_read (rexmpp_t *s);
void rexmpp_roster_cache_write (rexmpp_t *s);
void rexmpp_iq_roster_get (rexmpp_t *s,
void *ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success);
diff --git a/src/rexmpp_rust.rs b/src/rexmpp_rust.rs
new file mode 100644
index 0000000..ba80598
--- /dev/null
+++ b/src/rexmpp_rust.rs
@@ -0,0 +1,8 @@
+mod rexmpp_jid;
+mod rexmpp_xml;
+mod rexmpp_xml_parser;
+mod rexmpp_dns;
+mod rexmpp_tcp;
+mod rexmpp_socks;
+mod rexmpp_random;
+mod rexmpp;
diff --git a/src/rexmpp_sasl.c b/src/rexmpp_sasl.c
index 7dd16ba..20c4ba0 100644
--- a/src/rexmpp_sasl.c
+++ b/src/rexmpp_sasl.c
@@ -8,6 +8,7 @@
*/
#include <syslog.h>
+#include <stdlib.h>
#include "config.h"
#include "rexmpp.h"
@@ -31,28 +32,33 @@ int rexmpp_sasl_cb (Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop) {
}
int rexmpp_sasl_ctx_init (rexmpp_t *s) {
- int err = gsasl_init(&(s->sasl.ctx));
+ s->sasl = malloc(sizeof(struct rexmpp_sasl_ctx));
+ int err = gsasl_init(&(s->sasl->ctx));
if (err != GSASL_OK) {
rexmpp_log(s, LOG_CRIT, "gsasl initialisation error: %s",
gsasl_strerror(err));
return -1;
}
- gsasl_callback_hook_set(s->sasl.ctx, s);
- gsasl_callback_set(s->sasl.ctx, rexmpp_sasl_cb);
+ gsasl_callback_hook_set(s->sasl->ctx, s);
+ gsasl_callback_set(s->sasl->ctx, rexmpp_sasl_cb);
return 0;
}
void rexmpp_sasl_ctx_deinit (rexmpp_t *s) {
- gsasl_done(s->sasl.ctx);
+ gsasl_done(s->sasl->ctx);
+ if (s->sasl != NULL) {
+ free(s->sasl);
+ s->sasl = NULL;
+ }
}
void rexmpp_sasl_ctx_cleanup (rexmpp_t *s) {
- gsasl_finish(s->sasl.session);
- s->sasl.session = NULL;
+ gsasl_finish(s->sasl->session);
+ s->sasl->session = NULL;
}
int rexmpp_sasl_encode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) {
- int sasl_err = gsasl_encode (s->sasl.session, in, in_len, out, out_len);
+ int sasl_err = gsasl_encode (s->sasl->session, in, in_len, out, out_len);
if (sasl_err != GSASL_OK) {
rexmpp_log(s, LOG_ERR, "SASL encoding error: %s", gsasl_strerror(sasl_err));
return -1;
@@ -61,7 +67,7 @@ int rexmpp_sasl_encode (rexmpp_t *s, const char *in, size_t in_len, char **out,
}
int rexmpp_sasl_decode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) {
- int sasl_err = gsasl_decode(s->sasl.session, in, in_len, out, out_len);
+ int sasl_err = gsasl_decode(s->sasl->session, in, in_len, out, out_len);
if (sasl_err != GSASL_OK) {
rexmpp_log(s, LOG_ERR, "SASL decoding error: %s", gsasl_strerror(sasl_err));
return -1;
@@ -70,15 +76,15 @@ int rexmpp_sasl_decode (rexmpp_t *s, const char *in, size_t in_len, char **out,
}
const char *rexmpp_sasl_suggest_mechanism (rexmpp_t *s, const char *mech_list) {
- return gsasl_client_suggest_mechanism(s->sasl.ctx, mech_list);
+ return gsasl_client_suggest_mechanism(s->sasl->ctx, mech_list);
}
void rexmpp_sasl_property_set (rexmpp_t *s, rexmpp_sasl_property prop, const char *data) {
- gsasl_property_set (s->sasl.session, (Gsasl_property)prop, data);
+ gsasl_property_set (s->sasl->session, (Gsasl_property)prop, data);
}
int rexmpp_sasl_start (rexmpp_t *s, const char *mech) {
- int sasl_err = gsasl_client_start(s->sasl.ctx, mech, &(s->sasl.session));
+ int sasl_err = gsasl_client_start(s->sasl->ctx, mech, &(s->sasl->session));
if (sasl_err != GSASL_OK) {
rexmpp_log(s, LOG_CRIT, "Failed to initialise SASL session: %s",
gsasl_strerror(sasl_err));
@@ -88,7 +94,7 @@ int rexmpp_sasl_start (rexmpp_t *s, const char *mech) {
}
int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) {
- int sasl_err = gsasl_step64 (s->sasl.session, b64_in, b64_out);
+ int sasl_err = gsasl_step64 (s->sasl->session, b64_in, b64_out);
if (sasl_err != GSASL_OK) {
if (sasl_err == GSASL_NEEDS_MORE) {
rexmpp_log(s, LOG_DEBUG, "SASL needs more data");
@@ -106,26 +112,30 @@ int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) {
#include <memory.h>
int rexmpp_sasl_ctx_init (rexmpp_t *s) {
- s->sasl.mech = REXMPP_SASL_MECH_UNKNOWN;
- s->sasl.authid = NULL;
- s->sasl.password = NULL;
+ s->sasl = malloc(sizeof(struct rexmpp_sasl_ctx));
+ s->sasl->mech = REXMPP_SASL_MECH_UNKNOWN;
+ s->sasl->authid = NULL;
+ s->sasl->password = NULL;
return 0;
}
void rexmpp_sasl_ctx_cleanup (rexmpp_t *s) {
- s->sasl.mech = REXMPP_SASL_MECH_UNKNOWN;
- if (s->sasl.authid != NULL) {
- free(s->sasl.authid);
- s->sasl.authid = NULL;
+ s->sasl->mech = REXMPP_SASL_MECH_UNKNOWN;
+ if (s->sasl->authid != NULL) {
+ free(s->sasl->authid);
+ s->sasl->authid = NULL;
}
- if (s->sasl.password != NULL) {
- free(s->sasl.password);
- s->sasl.password = NULL;
+ if (s->sasl->password != NULL) {
+ free(s->sasl->password);
+ s->sasl->password = NULL;
}
}
void rexmpp_sasl_ctx_deinit (rexmpp_t *s) {
- (void)s;
+ if (s->sasl != NULL) {
+ free(s->sasl);
+ s->sasl = NULL;
+ }
}
int rexmpp_sasl_encode (rexmpp_t *s, const char *in, size_t in_len, char **out, size_t *out_len) {
@@ -186,7 +196,7 @@ const char *rexmpp_sasl_suggest_mechanism (rexmpp_t *s, const char *mech_list) {
int rexmpp_sasl_start (rexmpp_t *s, const char *mech) {
rexmpp_sasl_mechanism m = rexmpp_sasl_mech_read(mech);
if (m != REXMPP_SASL_MECH_UNKNOWN) {
- s->sasl.mech = m;
+ s->sasl->mech = m;
return 0;
}
return -1;
@@ -194,15 +204,15 @@ int rexmpp_sasl_start (rexmpp_t *s, const char *mech) {
const char *rexmpp_sasl_get_prop (rexmpp_t *s, rexmpp_sasl_property prop) {
if (prop == REXMPP_SASL_PROP_AUTHID) {
- if (s->sasl.authid == NULL) {
+ if (s->sasl->authid == NULL) {
s->sasl_property_cb(s, prop);
}
- return s->sasl.authid;
+ return s->sasl->authid;
} else if (prop == REXMPP_SASL_PROP_PASSWORD) {
- if (s->sasl.password == NULL) {
+ if (s->sasl->password == NULL) {
s->sasl_property_cb(s, prop);
}
- return s->sasl.password;
+ return s->sasl->password;
}
return NULL;
}
@@ -210,7 +220,7 @@ const char *rexmpp_sasl_get_prop (rexmpp_t *s, rexmpp_sasl_property prop) {
int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) {
(void)s;
(void)b64_in;
- if (s->sasl.mech == REXMPP_SASL_MECH_PLAIN) {
+ if (s->sasl->mech == REXMPP_SASL_MECH_PLAIN) {
/* RFC 4616 */
const char *authid = rexmpp_sasl_get_prop(s, REXMPP_SASL_PROP_AUTHID);
const char *password = rexmpp_sasl_get_prop(s, REXMPP_SASL_PROP_PASSWORD);
@@ -226,7 +236,7 @@ int rexmpp_sasl_step64 (rexmpp_t *s, const char *b64_in, char **b64_out) {
free(auth);
return 0;
}
- } else if (s->sasl.mech == REXMPP_SASL_MECH_EXTERNAL) {
+ } else if (s->sasl->mech == REXMPP_SASL_MECH_EXTERNAL) {
*b64_out = strdup("");
return 0;
}
@@ -237,15 +247,15 @@ void rexmpp_sasl_property_set (rexmpp_t *s, rexmpp_sasl_property prop, const cha
(void)s;
(void)data;
if (prop == REXMPP_SASL_PROP_AUTHID) {
- if (s->sasl.authid != NULL) {
- free(s->sasl.authid);
+ if (s->sasl->authid != NULL) {
+ free(s->sasl->authid);
}
- s->sasl.authid = strdup(data);
+ s->sasl->authid = strdup(data);
} else if (prop == REXMPP_SASL_PROP_PASSWORD) {
- if (s->sasl.password != NULL) {
- free(s->sasl.password);
+ if (s->sasl->password != NULL) {
+ free(s->sasl->password);
}
- s->sasl.password = strdup(data);
+ s->sasl->password = strdup(data);
}
}
diff --git a/src/rexmpp_socks.rs b/src/rexmpp_socks.rs
new file mode 100644
index 0000000..1828de1
--- /dev/null
+++ b/src/rexmpp_socks.rs
@@ -0,0 +1,180 @@
+// For rustc and libstd-rust version 1.48
+
+use std::os::raw::{c_int, c_char};
+use std::ffi::CStr;
+use std::net::TcpStream;
+use std::os::unix::io::{FromRawFd, IntoRawFd};
+use std::io::Write;
+use std::io::Read;
+use std::io::ErrorKind;
+use std::convert::TryFrom;
+
+const REXMPP_SOCKS_BUF_LEN: usize = 300;
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksIOState {
+ Writing,
+ Reading
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksStage {
+ Auth,
+ Cmd,
+ Done
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksErr {
+ Connected,
+ EAgain,
+ ETCP,
+ EReply,
+ EVersion,
+ ESocks,
+ EHost
+}
+
+#[repr(C)]
+pub struct RexmppSocks {
+ fd: c_int,
+ host: *const c_char,
+ port: u16,
+ stage: SocksStage,
+ io_state: SocksIOState,
+ socks_error: c_int,
+ buf: [u8; REXMPP_SOCKS_BUF_LEN],
+ buf_len: usize,
+ buf_sent: usize
+}
+
+#[no_mangle]
+extern "C" fn rexmpp_socks_proceed (s : &mut RexmppSocks) -> SocksErr {
+ if s.io_state == SocksIOState::Writing {
+ let mut stream : TcpStream = unsafe { TcpStream::from_raw_fd(s.fd) };
+ let ret = stream.write(&s.buf[s.buf_sent .. s.buf_len]);
+ // Make sure the connection is not closed by TcpStream.
+ TcpStream::into_raw_fd(stream);
+ match ret {
+ Ok(sent) => {
+ s.buf_sent += sent;
+ if s.buf_len == s.buf_sent {
+ s.buf_len = 0;
+ s.io_state = SocksIOState::Reading;
+ }
+ }
+ Err(error) => match error.kind() {
+ ErrorKind::WouldBlock => return SocksErr::EAgain,
+ _ => return SocksErr::ETCP
+ }
+ }
+ } else if s.io_state == SocksIOState::Reading {
+ let mut stream : TcpStream = unsafe { TcpStream::from_raw_fd(s.fd) };
+ let ret = stream.read(&mut s.buf[s.buf_len ..]);
+ // Make sure the connection is not closed by TcpStream.
+ TcpStream::into_raw_fd(stream);
+ match ret {
+ Ok(received) => {
+ s.buf_len += received;
+ if s.buf[0] != 5 {
+ return SocksErr::EVersion;
+ }
+ if s.buf_len >= 2 {
+ s.socks_error = s.buf[1].into();
+ }
+ if s.stage == SocksStage::Auth {
+ if s.buf_len > 2 {
+ return SocksErr::EReply;
+ }
+ if s.buf_len == 2 {
+ if s.socks_error != 0 {
+ return SocksErr::ESocks;
+ }
+ s.buf[0] = 5; // SOCKS version 5
+ s.buf[1] = 1; // Connect
+ s.buf[2] = 0; // Reserved
+ s.buf[3] = 3; // Domain name (todo: IP addresses)
+ let host_cstr : &CStr =
+ unsafe { CStr::from_ptr(s.host) };
+ let host_len = host_cstr.to_bytes().len();
+ match u8::try_from(host_len) {
+ Ok(u) => { s.buf[4] = u }
+ Err(_) => return SocksErr::EHost
+ }
+ s.buf[5 .. 5 + host_len].
+ copy_from_slice(&host_cstr.to_bytes());
+ s.buf[5 + host_len .. 7 + host_len].
+ copy_from_slice(&s.port.to_be_bytes());
+ s.buf_len = 7 + host_len;
+ s.buf_sent = 0;
+ s.stage = SocksStage::Cmd;
+ s.io_state = SocksIOState::Writing;
+ return rexmpp_socks_proceed(s);
+ }
+ } else if s.stage == SocksStage::Cmd {
+ if s.buf_len >= 5 {
+ let mut full_len : usize = 6;
+ match s.buf[3] {
+ // IPv4
+ 1 => full_len += 4,
+ // Domain name
+ 3 => full_len += usize::from(s.buf[4]) + 1,
+ // IPv6
+ 4 => full_len += 16,
+ _ => return SocksErr::EReply
+ }
+ if s.buf_len > full_len {
+ return SocksErr::EReply;
+ }
+ if s.buf_len == full_len {
+ if s.socks_error != 0 {
+ return SocksErr::ESocks;
+ }
+ // We are done
+ s.stage = SocksStage::Done;
+ return SocksErr::Connected;
+ }
+ }
+ }
+ }
+ Err(error) => match error.kind() {
+ ErrorKind::WouldBlock => return SocksErr::EAgain,
+ _ => return SocksErr::ETCP
+ }
+ }
+ }
+ return SocksErr::EAgain
+}
+
+#[no_mangle]
+extern "C" fn rexmpp_socks_init (
+ s : &mut RexmppSocks,
+ fd: c_int,
+ host: *const c_char,
+ port: u16
+)
+ -> SocksErr
+{
+ s.fd = fd;
+ s.host = host;
+ s.port = port;
+ s.socks_error = 0;
+
+ let host_cstr : &CStr = unsafe { CStr::from_ptr(host) };
+ if host_cstr.to_bytes().len() > 255 {
+ return SocksErr::EHost;
+ }
+
+ // Request authentication
+ s.stage = SocksStage::Auth;
+ s.io_state = SocksIOState::Writing;
+ s.buf[0] = 5; // SOCKS version 5
+ s.buf[1] = 1; // 1 supported method
+ s.buf[2] = 0; // No authentication required
+ s.buf_len = 3;
+ s.buf_sent = 0;
+ return rexmpp_socks_proceed(s);
+}
diff --git a/src/rexmpp_tcp.c b/src/rexmpp_tcp.c
index 3022182..c6a53a5 100644
--- a/src/rexmpp_tcp.c
+++ b/src/rexmpp_tcp.c
@@ -50,10 +50,10 @@ void rexmpp_tcp_dns_a_cb (rexmpp_t *s,
conn->addr_cur_v4 = -1;
if (conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) {
/* Wait for 50 ms for IPv6. */
- gettimeofday(&(conn->next_connection_time), NULL);
- conn->next_connection_time.tv_usec += REXMPP_TCP_IPV6_DELAY_MS * 1000;
- if (conn->next_connection_time.tv_usec >= 1000000) {
- conn->next_connection_time.tv_usec -= 1000000;
+ clock_gettime(CLOCK_MONOTONIC, &(conn->next_connection_time));
+ conn->next_connection_time.tv_nsec += REXMPP_TCP_IPV6_DELAY_MS * 1000000;
+ if (conn->next_connection_time.tv_nsec >= 1000000000) {
+ conn->next_connection_time.tv_nsec -= 1000000000;
conn->next_connection_time.tv_sec++;
}
}
@@ -99,6 +99,21 @@ rexmpp_tcp_connected (rexmpp_tcp_conn_t *conn, int fd) {
return REXMPP_CONN_DONE;
}
+int rexmpp_tcp_socket(rexmpp_t *s, int domain) {
+ int sock = socket(domain, SOCK_STREAM, 0);
+
+ /* Make it non-blocking */
+ int flags = fcntl(sock, F_GETFL, 0);
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+
+ /* Call the socket creation callback, if provided */
+ if (s->socket_cb != NULL) {
+ s->socket_cb(s, sock);
+ }
+
+ return sock;
+}
+
rexmpp_tcp_conn_error_t
rexmpp_tcp_conn_init (rexmpp_t *s,
rexmpp_tcp_conn_t *conn,
@@ -116,20 +131,16 @@ rexmpp_tcp_conn_init (rexmpp_t *s,
conn->fd = -1;
conn->dns_secure = 0;
conn->next_connection_time.tv_sec = 0;
- conn->next_connection_time.tv_usec = 0;
+ conn->next_connection_time.tv_nsec = 0;
conn->resolution_v4 = REXMPP_CONN_RESOLUTION_INACTIVE;
conn->resolution_v6 = REXMPP_CONN_RESOLUTION_INACTIVE;
struct sockaddr_in addr_v4;
- int flags;
- if (inet_pton(AF_INET, host, &addr_v4)) {
+ if (inet_pton(AF_INET, host, &(addr_v4.sin_addr))) {
addr_v4.sin_family = AF_INET;
addr_v4.sin_port = htons(port);
- conn->sockets[conn->connection_attempts] =
- socket(AF_INET, SOCK_STREAM, 0);
- flags = fcntl(conn->sockets[conn->connection_attempts], F_GETFL, 0);
- fcntl(conn->sockets[conn->connection_attempts], F_SETFL, flags | O_NONBLOCK);
+ conn->sockets[conn->connection_attempts] = rexmpp_tcp_socket(s, AF_INET);
if (connect(conn->sockets[conn->connection_attempts],
(struct sockaddr*)&addr_v4,
sizeof(addr_v4))) {
@@ -144,15 +155,12 @@ rexmpp_tcp_conn_init (rexmpp_t *s,
return REXMPP_CONN_IN_PROGRESS;
}
struct sockaddr_in6 addr_v6;
- if (inet_pton(AF_INET6, host, &addr_v6)) {
+ if (inet_pton(AF_INET6, host, &(addr_v6.sin6_addr))) {
addr_v6.sin6_family = AF_INET6;
addr_v6.sin6_port = htons(port);
addr_v6.sin6_flowinfo = 0;
addr_v6.sin6_scope_id = 0;
- conn->sockets[conn->connection_attempts] =
- socket(AF_INET6, SOCK_STREAM, 0);
- flags = fcntl(conn->sockets[conn->connection_attempts], F_GETFL, 0);
- fcntl(conn->sockets[conn->connection_attempts], F_SETFL, flags | O_NONBLOCK);
+ conn->sockets[conn->connection_attempts] = rexmpp_tcp_socket(s, AF_INET6);
if (connect(conn->sockets[conn->connection_attempts],
(struct sockaddr*)&addr_v6,
sizeof(addr_v6))) {
@@ -201,7 +209,7 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s,
fd_set *write_fds)
{
(void)read_fds; /* Not checking any read FDs at the moment. */
- struct timeval now;
+ struct timespec now;
int i;
/* Check for successful connections. */
@@ -241,10 +249,10 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s,
if (conn->connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS &&
(rexmpp_tcp_conn_ipv4_available(conn) ||
rexmpp_tcp_conn_ipv6_available(conn))) {
- gettimeofday(&now, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > conn->next_connection_time.tv_sec ||
(now.tv_sec == conn->next_connection_time.tv_sec &&
- now.tv_usec >= conn->next_connection_time.tv_usec)) {
+ now.tv_nsec >= conn->next_connection_time.tv_nsec)) {
/* Time to attempt a new connection. */
int use_ipv6 = 0;
if (rexmpp_tcp_conn_ipv4_available(conn) &&
@@ -295,16 +303,13 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s,
addrlen = sizeof(addr_v4);
}
- conn->sockets[conn->connection_attempts] =
- socket(domain, SOCK_STREAM, 0);
- int flags = fcntl(conn->sockets[conn->connection_attempts], F_GETFL, 0);
- fcntl(conn->sockets[conn->connection_attempts], F_SETFL, flags | O_NONBLOCK);
+ conn->sockets[conn->connection_attempts] = rexmpp_tcp_socket(s, domain);
if (connect(conn->sockets[conn->connection_attempts], addr, addrlen)) {
if (errno == EINPROGRESS) {
- gettimeofday(&(conn->next_connection_time), NULL);
- conn->next_connection_time.tv_usec += REXMPP_TCP_CONN_DELAY_MS * 1000;
- if (conn->next_connection_time.tv_usec >= 1000000) {
- conn->next_connection_time.tv_usec -= 1000000;
+ clock_gettime(CLOCK_MONOTONIC, &(conn->next_connection_time));
+ conn->next_connection_time.tv_nsec += REXMPP_TCP_CONN_DELAY_MS * 1000000;
+ if (conn->next_connection_time.tv_nsec >= 1000000000) {
+ conn->next_connection_time.tv_nsec -= 1000000000;
conn->next_connection_time.tv_sec++;
}
conn->connection_attempts++;
@@ -333,14 +338,14 @@ rexmpp_tcp_conn_proceed (rexmpp_t *s,
}
}
- gettimeofday(&now, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &now);
if (active_connections ||
conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING ||
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING ||
(conn->next_connection_time.tv_sec > now.tv_sec ||
(conn->next_connection_time.tv_sec == now.tv_sec &&
- conn->next_connection_time.tv_usec > now.tv_usec))) {
+ conn->next_connection_time.tv_nsec > now.tv_nsec))) {
return REXMPP_CONN_IN_PROGRESS;
} else {
return REXMPP_CONN_FAILURE;
@@ -368,13 +373,13 @@ int rexmpp_tcp_conn_fds (rexmpp_t *s,
return max_fd;
}
-struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s,
- rexmpp_tcp_conn_t *conn,
- struct timeval *max_tv,
- struct timeval *tv)
+struct timespec *rexmpp_tcp_conn_timeout (rexmpp_t *s,
+ rexmpp_tcp_conn_t *conn,
+ struct timespec *max_tv,
+ struct timespec *tv)
{
- struct timeval now;
- struct timeval *ret = max_tv;
+ struct timespec now;
+ struct timespec *ret = max_tv;
if (conn->resolution_v4 == REXMPP_CONN_RESOLUTION_WAITING ||
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_WAITING) {
ret = rexmpp_dns_timeout(s, max_tv, tv);
@@ -383,20 +388,20 @@ struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s,
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_SUCCESS ||
(conn->resolution_v4 == REXMPP_CONN_RESOLUTION_INACTIVE &&
conn->resolution_v6 == REXMPP_CONN_RESOLUTION_INACTIVE)) {
- gettimeofday(&now, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec < conn->next_connection_time.tv_sec ||
(now.tv_sec == conn->next_connection_time.tv_sec &&
- now.tv_usec <= conn->next_connection_time.tv_usec)) {
+ now.tv_nsec <= conn->next_connection_time.tv_nsec)) {
if (ret == NULL ||
ret->tv_sec > conn->next_connection_time.tv_sec - now.tv_sec ||
(ret->tv_sec == conn->next_connection_time.tv_sec - now.tv_sec &&
- ret->tv_usec > conn->next_connection_time.tv_usec - now.tv_usec)) {
+ ret->tv_nsec > conn->next_connection_time.tv_nsec - now.tv_nsec)) {
ret = tv;
tv->tv_sec = conn->next_connection_time.tv_sec - now.tv_sec;
- if (conn->next_connection_time.tv_usec > now.tv_usec) {
- tv->tv_usec = conn->next_connection_time.tv_usec - now.tv_usec;
+ if (conn->next_connection_time.tv_nsec > now.tv_nsec) {
+ tv->tv_nsec = conn->next_connection_time.tv_nsec - now.tv_nsec;
} else {
- tv->tv_usec = conn->next_connection_time.tv_usec + 1000000 - now.tv_usec;
+ tv->tv_nsec = conn->next_connection_time.tv_nsec + 1000000000 - now.tv_nsec;
tv->tv_sec--;
}
}
diff --git a/src/rexmpp_tcp.h b/src/rexmpp_tcp.h
index 5a296cc..8ee32a0 100644
--- a/src/rexmpp_tcp.h
+++ b/src/rexmpp_tcp.h
@@ -20,6 +20,7 @@
#define REXMPP_TCP_H
#include <sys/time.h>
+#include <stdbool.h>
#include "rexmpp.h"
#include "rexmpp_dns.h"
@@ -98,13 +99,13 @@ struct rexmpp_tcp_connection {
/** @brief The number of connection attempts so far. */
int connection_attempts;
- /** @brief Next scheduled connection time. */
- struct timeval next_connection_time;
+ /** @brief Next scheduled connection time (monotonic). */
+ struct timespec next_connection_time;
/** @brief File descriptor of a connected socket. */
int fd;
/** @brief Whether the A or AAAA records used to establish the final
connection were verified with DNSSEC. */
- int dns_secure;
+ bool dns_secure;
};
/**
@@ -175,13 +176,13 @@ int rexmpp_tcp_conn_fds (rexmpp_t *s,
@param[in] s ::rexmpp
@param[in] conn An active connection structure.
@param[in] max_tv An existing maximum timeout.
- @param[out] tv A timeval structure to store a new timeout in.
+ @param[out] tv A timespec structure to store a new timeout in.
@returns A pointer to either max_tv or tv, depending on which one
is smaller.
*/
-struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s,
- rexmpp_tcp_conn_t *conn,
- struct timeval *max_tv,
- struct timeval *tv);
+struct timespec *rexmpp_tcp_conn_timeout (rexmpp_t *s,
+ rexmpp_tcp_conn_t *conn,
+ struct timespec *max_tv,
+ struct timespec *tv);
#endif
diff --git a/src/rexmpp_tcp.rs b/src/rexmpp_tcp.rs
new file mode 100644
index 0000000..56c204c
--- /dev/null
+++ b/src/rexmpp_tcp.rs
@@ -0,0 +1,469 @@
+use std::os::raw::{c_int, c_char};
+use libc::*;
+use std::ptr::{null_mut,null};
+use std::mem;
+use errno::{errno};
+
+use super::{rexmpp_dns, rexmpp};
+
+
+#[link(name = "libc")]
+extern {
+ fn inet_pton (af: c_int, src: *const c_char, dst: *mut c_void) -> c_int;
+}
+
+
+const REXMPP_TCP_MAX_CONNECTION_ATTEMPTS: usize = 20;
+const REXMPP_TCP_IPV6_DELAY_MS: i64 = 50;
+const REXMPP_TCP_CONN_DELAY_MS: i64 = 250;
+
+#[derive(PartialEq, Copy, Clone)]
+#[repr(C)]
+pub enum ResolutionStatus {
+ Inactive,
+ Waiting,
+ Success,
+ Failure
+}
+
+#[derive(PartialEq, Copy, Clone)]
+#[repr(C)]
+pub enum ConnectionError {
+ Done,
+ ResolverError,
+ InProgress,
+ Failure,
+ Error
+}
+
+#[repr(C)]
+pub struct RexmppTCPConnection {
+ pub host: *const c_char,
+ pub port: u16,
+ pub resolution_v4: ResolutionStatus,
+ pub resolver_status_v4: c_int,
+ pub resolved_v4: *mut rexmpp_dns::RexmppDNSResult,
+ pub addr_cur_v4: c_int,
+ pub resolution_v6: ResolutionStatus,
+ pub resolver_status_v6: c_int,
+ pub resolved_v6: *mut rexmpp_dns::RexmppDNSResult,
+ pub addr_cur_v6: c_int,
+ pub sockets: [c_int; REXMPP_TCP_MAX_CONNECTION_ATTEMPTS],
+ pub connection_attempts: c_int,
+ pub next_connection_time: timespec,
+ pub fd: c_int,
+ pub dns_secure: bool
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_dns_aaaa_cb (_s: *mut rexmpp::Rexmpp,
+ ptr: *mut c_void,
+ result: *mut rexmpp_dns::RexmppDNSResult)
+ -> () {
+ let conn = ptr as *mut RexmppTCPConnection;
+ (*conn).resolved_v6 = result;
+ if result != null_mut() {
+ (*conn).resolution_v6 = ResolutionStatus::Success;
+ (*conn).addr_cur_v6 = -1;
+ } else {
+ (*conn).resolution_v6 = ResolutionStatus::Failure;
+ }
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_dns_a_cb (_s: *mut rexmpp::Rexmpp,
+ ptr: *mut c_void,
+ result: *mut rexmpp_dns::RexmppDNSResult)
+ -> () {
+ let conn = ptr as *mut RexmppTCPConnection;
+ (*conn).resolved_v4 = result;
+ if result != null_mut() {
+ (*conn).resolution_v4 = ResolutionStatus::Success;
+ (*conn).addr_cur_v4 = -1;
+ if (*conn).resolution_v6 == ResolutionStatus::Waiting {
+ // Wait a bit (usually 50 ms) for IPv6
+ clock_gettime(CLOCK_MONOTONIC, &mut (*conn).next_connection_time);
+ (*conn).next_connection_time.tv_nsec += REXMPP_TCP_IPV6_DELAY_MS * 1000000;
+ if (*conn).next_connection_time.tv_nsec >= 1000000000 {
+ (*conn).next_connection_time.tv_nsec -= 1000000000;
+ (*conn).next_connection_time.tv_sec += 1;
+ }
+ }
+ } else {
+ (*conn).resolution_v4 = ResolutionStatus::Failure;
+ }
+}
+
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_cleanup (conn: *mut RexmppTCPConnection) -> () {
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ if (*conn).sockets[i] != -1 && (*conn).sockets[i] != (*conn).fd {
+ close((*conn).sockets[i]);
+ (*conn).sockets[i] = -1;
+ }
+ }
+ if (*conn).resolution_v4 != ResolutionStatus::Inactive {
+ (*conn).resolution_v4 = ResolutionStatus::Inactive;
+ (*conn).resolution_v6 = ResolutionStatus::Inactive;
+ }
+ if (*conn).resolved_v4 != null_mut() {
+ rexmpp_dns::rexmpp_dns_result_free((*conn).resolved_v4);
+ (*conn).resolved_v4 = null_mut();
+ }
+ if (*conn).resolved_v6 != null_mut() {
+ rexmpp_dns::rexmpp_dns_result_free((*conn).resolved_v6);
+ (*conn).resolved_v6 = null_mut();
+ }
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_connected (conn: *mut RexmppTCPConnection, fd: c_int)
+ -> ConnectionError {
+ let mut sa_ptr = mem::MaybeUninit::<sockaddr>::uninit();
+ let mut sa_len : socklen_t = mem::size_of::<sockaddr>() as u32;
+ getsockname(fd, sa_ptr.as_mut_ptr(), &mut sa_len);
+ let sa = sa_ptr.assume_init();
+ if sa.sa_family == (AF_INET as u16)
+ && (*conn).resolved_v4 != null_mut() {
+ (*conn).dns_secure = (*(*conn).resolved_v4).secure;
+ }
+ else if sa.sa_family == (AF_INET6 as u16)
+ && (*conn).resolved_v6 != null_mut() {
+ (*conn).dns_secure = (*(*conn).resolved_v6).secure;
+ }
+ (*conn).fd = fd;
+ rexmpp_tcp_cleanup(conn);
+ return ConnectionError::Done;
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_socket (s: *mut rexmpp::Rexmpp, domain: c_int) -> c_int {
+ let sock: c_int = socket(domain, SOCK_STREAM, 0);
+
+ // Make it non-blocking
+ let flags: c_int = fcntl(sock, F_GETFL, 0);
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+
+ // Call the socket creation callback, if provided
+ ((*s).socket_cb).map(|cb| cb(s, sock));
+
+ return sock;
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_init (s: *mut rexmpp::Rexmpp,
+ conn: *mut RexmppTCPConnection,
+ host: *const c_char,
+ port: u16)
+ -> ConnectionError {
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ (*conn).sockets[i] = -1;
+ }
+ (*conn).connection_attempts = 0;
+ (*conn).port = port;
+ (*conn).resolved_v4 = null_mut();
+ (*conn).resolved_v6 = null_mut();
+ (*conn).fd = -1;
+ (*conn).dns_secure = false;
+ (*conn).next_connection_time.tv_sec = 0;
+ (*conn).next_connection_time.tv_nsec = 0;
+
+ (*conn).resolution_v4 = ResolutionStatus::Inactive;
+ (*conn).resolution_v6 = ResolutionStatus::Inactive;
+
+ let mut addr_v4 = mem::MaybeUninit::<sockaddr_in>::uninit();
+ if inet_pton(AF_INET, host,
+ &mut ((*addr_v4.as_mut_ptr()).sin_addr)
+ as *mut in_addr as *mut c_void) == 1 {
+ (*addr_v4.as_mut_ptr()).sin_family = AF_INET as u16;
+ (*addr_v4.as_mut_ptr()).sin_port = port.to_be();
+ (*conn).sockets[(*conn).connection_attempts as usize] =
+ rexmpp_tcp_socket(s, AF_INET);
+ if connect((*conn).sockets[(*conn).connection_attempts as usize],
+ addr_v4.as_mut_ptr() as *mut sockaddr,
+ mem::size_of::<sockaddr_in>() as u32) != 0 {
+ if errno().0 != EINPROGRESS {
+ return ConnectionError::Error;
+ }
+ } else {
+ return rexmpp_tcp_connected(conn,
+ (*conn).sockets[(*conn).connection_attempts as usize]);
+ }
+ (*conn).connection_attempts += 1;
+ return ConnectionError::InProgress;
+ }
+
+ let mut addr_v6 = mem::MaybeUninit::<sockaddr_in6>::uninit();
+ if inet_pton(AF_INET6, host,
+ &mut ((*addr_v6.as_mut_ptr()).sin6_addr)
+ as *mut in6_addr as *mut c_void) == 1 {
+ (*addr_v6.as_mut_ptr()).sin6_family = AF_INET as u16;
+ (*addr_v6.as_mut_ptr()).sin6_port = port.to_be();
+ (*addr_v6.as_mut_ptr()).sin6_flowinfo = 0;
+ (*addr_v6.as_mut_ptr()).sin6_scope_id = 0;
+ (*conn).sockets[(*conn).connection_attempts as usize] =
+ rexmpp_tcp_socket(s, AF_INET6);
+ if connect((*conn).sockets[(*conn).connection_attempts as usize],
+ addr_v6.as_mut_ptr() as *mut sockaddr,
+ mem::size_of::<sockaddr_in6>() as u32) != 0 {
+ if errno().0 != EINPROGRESS {
+ return ConnectionError::Error;
+ }
+ } else {
+ return rexmpp_tcp_connected(conn,
+ (*conn).sockets[(*conn).connection_attempts as usize]);
+ }
+ (*conn).connection_attempts += 1;
+ return ConnectionError::InProgress;
+ }
+ (*conn).resolution_v4 = ResolutionStatus::Waiting;
+ (*conn).resolution_v6 = ResolutionStatus::Waiting;
+
+ rexmpp_dns::rexmpp_dns_resolve(s, host, 28, 1,
+ conn as *mut c_void,
+ rexmpp_tcp_dns_aaaa_cb);
+ rexmpp_dns::rexmpp_dns_resolve(s, host, 1, 1,
+ conn as *mut c_void,
+ rexmpp_tcp_dns_a_cb);
+ return ConnectionError::InProgress;
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_finish (conn: *mut RexmppTCPConnection) -> c_int {
+ rexmpp_tcp_cleanup(conn);
+ return (*conn).fd;
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_ipv4_available (conn: *mut RexmppTCPConnection) -> bool {
+ (*conn).resolution_v4 == ResolutionStatus::Success
+ && (*conn).resolved_v4 != null_mut()
+ && *(*(*conn).resolved_v4).data
+ .offset(((*conn).addr_cur_v4 + 1) as isize) != null_mut()
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_ipv6_available (conn: *mut RexmppTCPConnection) -> bool {
+ (*conn).resolution_v6 == ResolutionStatus::Success
+ && (*conn).resolved_v6 != null_mut()
+ && *(*(*conn).resolved_v6).data
+ .offset(((*conn).addr_cur_v6 + 1) as isize) != null_mut()
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_proceed (s: *mut rexmpp::Rexmpp,
+ conn: *mut RexmppTCPConnection,
+ read_fds: *mut fd_set,
+ write_fds: *mut fd_set) -> ConnectionError {
+ // Check for successful connections.
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ if (*conn).sockets[i] != -1 && FD_ISSET((*conn).sockets[i], write_fds) {
+ let mut err: c_int = 0;
+ let mut err_len: socklen_t = mem::size_of::<c_int>() as u32;
+ if getsockopt((*conn).sockets[i], SOL_SOCKET, SO_ERROR,
+ &mut err as *mut c_int as *mut c_void,
+ &mut err_len) < 0 {
+ return ConnectionError::Error;
+ } else {
+ if err == 0 {
+ return rexmpp_tcp_connected(conn, (*conn).sockets[i]);
+ } else if err != EINPROGRESS {
+ close((*conn).sockets[i]);
+ (*conn).sockets[i] = -1;
+ }
+ }
+ }
+ }
+
+ // Name resolution
+ if (*conn).resolution_v4 == ResolutionStatus::Waiting
+ || (*conn).resolution_v6 == ResolutionStatus::Waiting {
+ rexmpp_dns::rexmpp_dns_process(s, read_fds, write_fds);
+ }
+ if (*conn).resolution_v4 == ResolutionStatus::Failure
+ && (*conn).resolution_v6 == ResolutionStatus::Failure {
+ // Failed to resolve anything
+ return ConnectionError::Failure;
+ }
+
+ // New connections
+ let mut repeat: bool;
+ let mut now = mem::MaybeUninit::<timespec>::uninit();
+ let now_ptr = now.as_mut_ptr();
+ loop {
+ repeat = false;
+ if (*conn).connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS as i32
+ && (rexmpp_tcp_conn_ipv4_available(conn)
+ || rexmpp_tcp_conn_ipv6_available(conn)) {
+ clock_gettime(CLOCK_MONOTONIC, now_ptr);
+ if (*now_ptr).tv_sec > (*conn).next_connection_time.tv_sec
+ || ((*now_ptr).tv_sec == (*conn).next_connection_time.tv_sec
+ && (*now_ptr).tv_nsec >= (*conn).next_connection_time.tv_nsec) {
+ // Time to attempt a new connection
+ let mut use_ipv6 = false;
+ if rexmpp_tcp_conn_ipv4_available(conn) &&
+ rexmpp_tcp_conn_ipv6_available(conn) {
+ if (*conn).addr_cur_v4 >= (*conn).addr_cur_v6 {
+ use_ipv6 = true;
+ }
+ } else if rexmpp_tcp_conn_ipv6_available(conn) {
+ use_ipv6 = true;
+ }
+
+ let addr: *mut sockaddr;
+ let addrlen: socklen_t;
+ let domain: c_int;
+ if use_ipv6 {
+ let mut addr_v6: sockaddr_in6 = mem::zeroed();
+ (*conn).addr_cur_v6 += 1;
+ let len = (mem::size_of::<in6_addr>() as i32)
+ .min(*(*(*conn).resolved_v6).len.offset((*conn).addr_cur_v6 as isize));
+ memcpy(&mut addr_v6.sin6_addr as *mut in6_addr as *mut c_void,
+ *(*(*conn).resolved_v6).data.offset((*conn).addr_cur_v6 as isize),
+ len as usize);
+ addr_v6.sin6_family = AF_INET6 as u16;
+ addr_v6.sin6_port = (*conn).port.to_be();
+ addr_v6.sin6_flowinfo = 0;
+ addr_v6.sin6_scope_id = 0;
+ domain = AF_INET6;
+ addr = &mut addr_v6 as *mut sockaddr_in6 as *mut sockaddr;
+ addrlen = mem::size_of::<sockaddr_in6>() as u32;
+ } else {
+ let mut addr_v4: sockaddr_in = mem::zeroed();
+ (*conn).addr_cur_v4 += 1;
+ let len = (mem::size_of::<in_addr>() as i32)
+ .min(*(*(*conn).resolved_v4).len.offset((*conn).addr_cur_v4 as isize));
+ memcpy(&mut addr_v4.sin_addr as *mut in_addr as *mut c_void,
+ *(*(*conn).resolved_v4).data.offset((*conn).addr_cur_v4 as isize),
+ len as usize);
+ addr_v4.sin_family = AF_INET as u16;
+ addr_v4.sin_port = (*conn).port.to_be();
+ domain = AF_INET;
+ addr = &mut addr_v4 as *mut sockaddr_in as *mut sockaddr;
+ addrlen = mem::size_of::<sockaddr_in>() as u32;
+ }
+ (*conn).sockets[(*conn).connection_attempts as usize] =
+ rexmpp_tcp_socket(s, domain);
+ if connect((*conn).sockets[(*conn).connection_attempts as usize],
+ addr, addrlen) != 0 {
+ if errno().0 == EINPROGRESS {
+ clock_gettime(CLOCK_MONOTONIC, &mut (*conn).next_connection_time);
+ (*conn).next_connection_time.tv_nsec +=
+ REXMPP_TCP_CONN_DELAY_MS * 1000000;
+ if (*conn).next_connection_time.tv_nsec >= 1000000000 {
+ (*conn).next_connection_time.tv_nsec -= 1000000000;
+ (*conn).next_connection_time.tv_sec += 1;
+ }
+ (*conn).connection_attempts += 1;
+ } else {
+ close((*conn).sockets[(*conn).connection_attempts as usize]);
+ (*conn).sockets[(*conn).connection_attempts as usize] = -1;
+ if (*conn).connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS as i32
+ && (rexmpp_tcp_conn_ipv4_available(conn) ||
+ rexmpp_tcp_conn_ipv6_available(conn)) {
+ repeat = true;
+ }
+ }
+ } else {
+ return rexmpp_tcp_connected(conn,
+ (*conn).sockets[(*conn).connection_attempts as usize]);
+ }
+ }
+ }
+ if ! repeat {
+ break;
+ }
+ }
+
+ let mut active_connections = false;
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ if (*conn).sockets[i] != -1 {
+ active_connections = true;
+ break;
+ }
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, now_ptr);
+
+ if active_connections
+ || (*conn).resolution_v4 == ResolutionStatus::Waiting
+ || (*conn).resolution_v6 == ResolutionStatus::Waiting
+ || ((*conn).next_connection_time.tv_sec > (*now_ptr).tv_sec
+ || ((*conn).next_connection_time.tv_sec == (*now_ptr).tv_sec
+ && (*conn).next_connection_time.tv_nsec > (*now_ptr).tv_nsec)) {
+ ConnectionError::InProgress
+ } else {
+ ConnectionError::Failure
+ }
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_fds (s: *mut rexmpp::Rexmpp,
+ conn: *mut RexmppTCPConnection,
+ read_fds: *mut fd_set,
+ write_fds: *mut fd_set) -> c_int {
+ let mut max_fd: c_int = 0;
+ if (*conn).resolution_v4 == ResolutionStatus::Waiting
+ || (*conn).resolution_v6 == ResolutionStatus::Waiting {
+ max_fd = rexmpp_dns::rexmpp_dns_fds(s, read_fds, write_fds);
+ }
+ for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) {
+ if (*conn).sockets[i] != -1 {
+ FD_SET((*conn).sockets[i], write_fds);
+ if max_fd < (*conn).sockets[i] + 1 {
+ max_fd = (*conn).sockets[i] + 1;
+ }
+ }
+ }
+ max_fd
+}
+
+#[no_mangle]
+unsafe extern "C"
+fn rexmpp_tcp_conn_timeout (s: *mut rexmpp::Rexmpp,
+ conn: *mut RexmppTCPConnection,
+ max_tv: *mut timespec,
+ tv: *mut timespec) -> *mut timespec {
+ let mut now: timespec = mem::zeroed();
+ let mut ret: *mut timespec = max_tv;
+ if (*conn).resolution_v4 == ResolutionStatus::Waiting
+ || (*conn).resolution_v6 == ResolutionStatus::Waiting {
+ ret = rexmpp_dns::rexmpp_dns_timeout(s, max_tv, tv);
+ }
+ if (*conn).resolution_v4 == ResolutionStatus::Success
+ || (*conn).resolution_v6 == ResolutionStatus::Success
+ || ((*conn).resolution_v4 == ResolutionStatus::Inactive
+ && (*conn).resolution_v4 == ResolutionStatus::Inactive) {
+ clock_gettime(CLOCK_MONOTONIC, &mut now);
+ if now.tv_sec < (*conn).next_connection_time.tv_sec
+ || (now.tv_sec == (*conn).next_connection_time.tv_sec
+ && now.tv_nsec <= (*conn).next_connection_time.tv_nsec) {
+ if ret == null_mut()
+ || (*ret).tv_sec > (*conn).next_connection_time.tv_sec - now.tv_sec
+ || ((*ret).tv_sec == (*conn).next_connection_time.tv_sec - now.tv_sec
+ && (*ret).tv_nsec > (*conn).next_connection_time.tv_nsec - now.tv_sec) {
+ ret = tv;
+ (*tv).tv_sec = (*conn).next_connection_time.tv_sec - now.tv_sec;
+ if (*conn).next_connection_time.tv_nsec > now.tv_nsec {
+ (*tv).tv_nsec = (*conn).next_connection_time.tv_nsec - now.tv_nsec;
+ } else {
+ (*tv).tv_nsec = (*conn).next_connection_time.tv_nsec + 1000000000 - now.tv_nsec;
+ (*tv).tv_sec -= 1;
+ }
+ }
+ }
+ }
+ ret
+}
diff --git a/src/rexmpp_tls.c b/src/rexmpp_tls.c
index 7919556..29bbc96 100644
--- a/src/rexmpp_tls.c
+++ b/src/rexmpp_tls.c
@@ -8,6 +8,7 @@
#include <syslog.h>
#include <string.h>
+#include <stdlib.h>
#include "config.h"
@@ -16,160 +17,387 @@
#include <gnutls/crypto.h>
#include <gnutls/x509.h>
#include <gnutls/dane.h>
+#include <gnutls/dtls.h>
#elif defined(USE_OPENSSL)
#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
#endif
#include "rexmpp.h"
+#include "rexmpp_digest.h"
#include "rexmpp_tls.h"
+#ifdef ENABLE_CALLS
+rexmpp_tls_t *rexmpp_jingle_component_dtls(void *p);
+ssize_t
+rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size);
+int rexmpp_jingle_dtls_pull_timeout_func (void *p,
+ unsigned int ms);
+#endif
+
#if defined(USE_OPENSSL)
-rexmpp_tls_err_t rexmpp_process_openssl_ret (rexmpp_t *s, int ret) {
- int err = SSL_get_error(s->tls.openssl_conn, ret);
- s->tls.openssl_direction = REXMPP_OPENSSL_NONE;
+rexmpp_tls_err_t rexmpp_process_openssl_ret (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *func,
+ int ret)
+{
+ int err = SSL_get_error(tls_ctx->openssl_conn, ret);
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE;
if (ret == 1) {
return REXMPP_TLS_SUCCESS;
} else if (err == SSL_ERROR_WANT_READ) {
- s->tls.openssl_direction = REXMPP_OPENSSL_READ;
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_READ;
return REXMPP_TLS_E_AGAIN;
} else if (err == SSL_ERROR_WANT_WRITE) {
- s->tls.openssl_direction = REXMPP_OPENSSL_WRITE;
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_WRITE;
return REXMPP_TLS_E_AGAIN;
} else {
- rexmpp_log(s, LOG_ERR, "OpenSSL error %d", err);
+ rexmpp_log(s, LOG_ERR, "OpenSSL error %d (ret %d) in %s",
+ err, ret, func);
+ ERR_print_errors_fp(stderr);
return REXMPP_TLS_E_OTHER;
}
}
#endif
-int rexmpp_tls_init (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)
int err;
- s->tls.tls_session_data = NULL;
- s->tls.tls_session_data_size = 0;
+ tls_ctx->tls_session_data = NULL;
+ tls_ctx->tls_session_data_size = 0;
- err = gnutls_certificate_allocate_credentials(&(s->tls.gnutls_cred));
+ err = gnutls_certificate_allocate_credentials(&(tls_ctx->gnutls_cred));
if (err) {
rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s",
gnutls_strerror(err));
- return 1;
+ free(tls_ctx);
+ return NULL;
+ }
+ if (! dtls) {
+ err = gnutls_certificate_set_x509_system_trust(tls_ctx->gnutls_cred);
}
- err = gnutls_certificate_set_x509_system_trust(s->tls.gnutls_cred);
if (err < 0) {
rexmpp_log(s, LOG_CRIT, "Certificates loading error: %s",
gnutls_strerror(err));
- return 1;
+ free(tls_ctx);
+ return NULL;
}
-#ifdef ENABLE_CALLS
- err = gnutls_certificate_allocate_credentials(&(s->jingle.dtls_cred));
- if (err) {
- gnutls_certificate_free_credentials(s->tls.gnutls_cred);
- rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s",
- gnutls_strerror(err));
- return 1;
- }
-#endif
- return 0;
+
+ tls_ctx->dtls_buf_len = 0;
#elif defined(USE_OPENSSL)
- SSL_library_init();
- SSL_load_error_strings();
- s->tls.openssl_direction = REXMPP_OPENSSL_NONE;
- s->tls.openssl_conn = NULL;
- s->tls.openssl_ctx = SSL_CTX_new(TLS_client_method());
- if (s->tls.openssl_ctx == NULL) {
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE;
+ tls_ctx->openssl_conn = NULL;
+ tls_ctx->openssl_ctx = SSL_CTX_new(dtls
+ ? DTLS_method()
+ : TLS_method());
+ if (tls_ctx->openssl_ctx == NULL) {
rexmpp_log(s, LOG_CRIT, "OpenSSL context creation error");
- return 1;
+ free(tls_ctx);
+ return NULL;
}
- SSL_CTX_set_verify(s->tls.openssl_ctx, SSL_VERIFY_PEER, NULL);
- if (SSL_CTX_set_default_verify_paths(s->tls.openssl_ctx) == 0) {
- rexmpp_log(s, LOG_CRIT, "Failed to set default verify paths for OpenSSL context");
- SSL_CTX_free(s->tls.openssl_ctx);
- s->tls.openssl_ctx = NULL;
- return 1;
+ SSL_CTX_set_verify(tls_ctx->openssl_ctx, SSL_VERIFY_PEER, NULL);
+ if (SSL_CTX_set_default_verify_paths(tls_ctx->openssl_ctx) == 0) {
+ rexmpp_log(s, LOG_CRIT,
+ "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;
}
- return 0;
#else
(void)s;
- return 0;
+ (void)dtls;
#endif
+ return tls_ctx;
}
+void rexmpp_tls_ctx_free (rexmpp_tls_t *tls_ctx) {
+#if defined(USE_GNUTLS)
+ gnutls_certificate_free_credentials(tls_ctx->gnutls_cred);
+ if (tls_ctx->tls_session_data != NULL) {
+ free(tls_ctx->tls_session_data);
+ tls_ctx->tls_session_data = NULL;
+ }
+#elif defined(USE_OPENSSL)
+ if (tls_ctx->openssl_ctx != NULL) {
+ SSL_CTX_free(tls_ctx->openssl_ctx);
+ }
+ tls_ctx->openssl_ctx = NULL;
+#endif
+ free(tls_ctx);
+}
-void rexmpp_tls_cleanup (rexmpp_t *s) {
- if (s->tls_state != REXMPP_TLS_INACTIVE &&
- s->tls_state != REXMPP_TLS_AWAITING_DIRECT) {
+int rexmpp_tls_init (rexmpp_t *s) {
+#if defined(USE_OPENSSL)
+ SSL_library_init();
+ SSL_load_error_strings();
+#endif
+ s->tls = rexmpp_tls_ctx_new(s, 0);
+ return (s->tls == NULL);
+}
+
+void rexmpp_tls_session_free (rexmpp_tls_t *tls_ctx) {
#if defined(USE_GNUTLS)
- gnutls_deinit(s->tls.gnutls_session);
+ gnutls_deinit(tls_ctx->gnutls_session);
#elif defined(USE_OPENSSL)
- if (s->tls.openssl_conn != NULL) {
- SSL_free(s->tls.openssl_conn);
- s->tls.openssl_conn = NULL;
+ if (tls_ctx->openssl_conn != NULL) {
+ SSL_free(tls_ctx->openssl_conn);
+ tls_ctx->openssl_conn = NULL;
+ /* bio_conn is freed implicitly by SSL_free. */
+ tls_ctx->bio_conn = NULL;
+ }
+ if (tls_ctx->bio_io != NULL) {
+ BIO_free(tls_ctx->bio_io);
+ tls_ctx->bio_io = NULL;
}
- s->tls.openssl_direction = REXMPP_OPENSSL_NONE;
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE;
#else
- (void)s;
+ (void)tls_ctx;
#endif
+}
+
+void rexmpp_tls_cleanup (rexmpp_t *s) {
+ if (s->tls_state != REXMPP_TLS_INACTIVE &&
+ s->tls_state != REXMPP_TLS_AWAITING_DIRECT) {
+ rexmpp_tls_session_free(s->tls);
}
}
void rexmpp_tls_deinit (rexmpp_t *s) {
+ if (s->tls != NULL) {
+ rexmpp_tls_ctx_free(s->tls);
+ s->tls = NULL;
+ }
+}
+
+#ifdef ENABLE_CALLS
#if defined(USE_GNUTLS)
- gnutls_certificate_free_credentials(s->tls.gnutls_cred);
- if (s->tls.tls_session_data != NULL) {
- free(s->tls.tls_session_data);
- s->tls.tls_session_data = NULL;
+ssize_t
+rexmpp_dtls_jingle_pull_func_gnutls (gnutls_transport_ptr_t p,
+ void *data,
+ size_t size)
+{
+ rexmpp_tls_t *tls_ctx = rexmpp_jingle_component_dtls(p);
+ ssize_t received;
+
+ char *tls_buf = tls_ctx->dtls_buf;
+ size_t *tls_buf_len = &(tls_ctx->dtls_buf_len);
+
+ rexmpp_tls_err_t ret = REXMPP_TLS_SUCCESS;
+ if (*tls_buf_len > 0) {
+ if (size >= *tls_buf_len) {
+ memcpy(data, tls_buf, *tls_buf_len);
+ received = *tls_buf_len;
+ *tls_buf_len = 0;
+ } else {
+ if (size > DTLS_SRTP_BUF_SIZE) {
+ size = DTLS_SRTP_BUF_SIZE;
+ }
+ memcpy(data, tls_buf, size);
+ memmove(tls_buf, tls_buf + size, DTLS_SRTP_BUF_SIZE - size);
+ received = size;
+ *tls_buf_len = *tls_buf_len - size;
+ }
+ } else {
+ ret = REXMPP_TLS_E_AGAIN;
+ }
+
+ if (ret == REXMPP_TLS_SUCCESS) {
+ return received;
+ } else if (ret == REXMPP_TLS_E_AGAIN) {
+ gnutls_transport_set_errno(tls_ctx->gnutls_session, EAGAIN);
}
+ return -1;
+}
+#endif
+
+#if defined(USE_OPENSSL)
+long rexmpp_dtls_openssl_bio_cb(BIO *b, int oper, const char *argp,
+ size_t len, int argi,
+ long argl, int ret, size_t *processed) {
+ (void)argi;
+ (void)argl;
+ (void)processed;
+ if (oper == BIO_CB_WRITE) {
+ rexmpp_jingle_dtls_push_func(BIO_get_callback_arg(b), argp, len);
+ }
+ return ret;
+}
+#endif
+#endif
+
+#if defined(USE_OPENSSL)
+int rexmpp_openssl_verify_accept_all (int preverify_ok,
+ X509_STORE_CTX *x509_ctx)
+{
+ (void)preverify_ok;
+ (void)x509_ctx;
+ return 1;
+}
+#endif
+
#ifdef ENABLE_CALLS
- gnutls_certificate_free_credentials(s->jingle.dtls_cred);
+rexmpp_tls_err_t
+rexmpp_dtls_connect (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *user_data,
+ int client) {
+#if defined(USE_GNUTLS)
+ gnutls_session_t *tls_session = &(tls_ctx->gnutls_session);
+ gnutls_init(tls_session,
+ (client ? GNUTLS_CLIENT : GNUTLS_SERVER) |
+ GNUTLS_DATAGRAM |
+ GNUTLS_NONBLOCK);
+ if (! client) {
+ gnutls_certificate_server_set_request(*tls_session, GNUTLS_CERT_REQUIRE);
+ }
+ gnutls_set_default_priority(*tls_session);
+ rexmpp_tls_set_x509_key_file(s, tls_ctx, NULL, NULL);
+ gnutls_credentials_set(*tls_session, GNUTLS_CRD_CERTIFICATE,
+ tls_ctx->gnutls_cred);
+
+ gnutls_transport_set_ptr(*tls_session, user_data);
+ gnutls_transport_set_push_function
+ (*tls_session, rexmpp_jingle_dtls_push_func);
+ gnutls_transport_set_pull_function
+ (*tls_session, rexmpp_dtls_jingle_pull_func_gnutls);
+ gnutls_transport_set_pull_timeout_function
+ (*tls_session, rexmpp_jingle_dtls_pull_timeout_func);
+ /* todo: use the profile/crypto-suite from <crypto/> element */
+ gnutls_srtp_set_profile(*tls_session, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80);
+ return REXMPP_TLS_SUCCESS;
+#elif defined(USE_OPENSSL)
+ (void)client;
+ int err;
+ /* Setup credentials */
+ rexmpp_tls_set_x509_key_file(s, tls_ctx, NULL, NULL);
+ /* Create a connection. */
+ tls_ctx->openssl_conn = SSL_new(tls_ctx->openssl_ctx);
+ SSL_set_verify(tls_ctx->openssl_conn,
+ SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ rexmpp_openssl_verify_accept_all);
+ /* Set a BIO */
+ BIO_new_bio_pair(&(tls_ctx->bio_conn), 4096, &(tls_ctx->bio_io), 4096);
+ BIO_up_ref(tls_ctx->bio_conn);
+ SSL_set0_rbio(tls_ctx->openssl_conn, tls_ctx->bio_conn);
+ SSL_set0_wbio(tls_ctx->openssl_conn, tls_ctx->bio_conn);
+ /* Set a callback to track writes */
+ BIO_set_callback_ex(tls_ctx->bio_conn, rexmpp_dtls_openssl_bio_cb);
+ BIO_set_callback_arg(tls_ctx->bio_conn, user_data);
+ BIO_set_ssl(tls_ctx->bio_conn, tls_ctx->openssl_conn, BIO_NOCLOSE);
+ /* Enable SRTP (TODO: support different profiles) */
+ err = SSL_set_tlsext_use_srtp(tls_ctx->openssl_conn,
+ "SRTP_AES128_CM_SHA1_80");
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to setup SRTP for the DTLS connection");
+ return REXMPP_TLS_E_OTHER;
+ }
+ if (client) {
+ err = SSL_connect(tls_ctx->openssl_conn);
+ } else {
+ err = SSL_accept(tls_ctx->openssl_conn);
+ }
+ return rexmpp_process_openssl_ret(s, tls_ctx, "rexmpp_dtls_connect", err);
+#else
+ (void)s;
+ (void)tls_ctx;
+ (void)user_data;
+ (void)client;
+ return REXMPP_TLS_E_OTHER;
#endif
+}
+
+void rexmpp_dtls_feed(rexmpp_t *s, rexmpp_tls_t *tls_ctx, uint8_t *buf, size_t len) {
+#if defined(USE_GNUTLS)
+ if (tls_ctx->dtls_buf_len + len < DTLS_SRTP_BUF_SIZE) {
+ memcpy(tls_ctx->dtls_buf + tls_ctx->dtls_buf_len, buf, len);
+ tls_ctx->dtls_buf_len += len;
+ } else {
+ rexmpp_log(s, LOG_WARNING, "Dropping a DTLS packet");
+ }
#elif defined(USE_OPENSSL)
- if (s->tls.openssl_ctx != NULL) {
- SSL_CTX_free(s->tls.openssl_ctx);
+ (void)s;
+ BIO_write(tls_ctx->bio_io, buf, len);
+#else
+ (void)s;
+ (void)tls_ctx;
+ (void)buf;
+ (void)len;
+#endif
+}
+#endif
+
+rexmpp_tls_err_t rexmpp_tls_handshake (rexmpp_t *s, rexmpp_tls_t *tls_ctx) {
+#if defined(USE_GNUTLS)
+ int ret = gnutls_handshake(tls_ctx->gnutls_session);
+ if (ret == 0) {
+ return REXMPP_TLS_SUCCESS;
+ } else if (ret == GNUTLS_E_AGAIN) {
+ return REXMPP_TLS_E_AGAIN;
+ } else {
+ rexmpp_log(s, LOG_ERR, "Error during a TLS handshake: %s",
+ gnutls_strerror(ret));
+ return REXMPP_TLS_E_OTHER;
}
- s->tls.openssl_ctx = NULL;
+#elif defined(USE_OPENSSL)
+ return rexmpp_process_openssl_ret(s, tls_ctx, "rexmpp_tls_handshake",
+ SSL_do_handshake(tls_ctx->openssl_conn));
#else
(void)s;
+ (void)tls_ctx;
+ return REXMPP_TLS_E_OTHER;
#endif
}
rexmpp_tls_err_t
rexmpp_tls_connect (rexmpp_t *s) {
+ if (s->x509_key_file != NULL && s->x509_cert_file != NULL) {
+ rexmpp_tls_set_x509_key_file(s, s->tls, NULL, NULL);
+ }
+
#if defined(USE_GNUTLS)
if (s->tls_state != REXMPP_TLS_HANDSHAKE) {
- gnutls_datum_t xmpp_client_protocol = {"xmpp-client", strlen("xmpp-client")};
+ gnutls_datum_t xmpp_client_protocol =
+ {(unsigned char*)"xmpp-client", strlen("xmpp-client")};
rexmpp_log(s, LOG_DEBUG, "starting TLS");
- gnutls_init(&s->tls.gnutls_session, GNUTLS_CLIENT);
- gnutls_session_set_ptr(s->tls.gnutls_session, s);
- gnutls_alpn_set_protocols(s->tls.gnutls_session, &xmpp_client_protocol, 1, 0);
- gnutls_server_name_set(s->tls.gnutls_session, GNUTLS_NAME_DNS,
+ gnutls_init(&s->tls->gnutls_session, GNUTLS_CLIENT);
+ gnutls_session_set_ptr(s->tls->gnutls_session, s);
+ gnutls_alpn_set_protocols(s->tls->gnutls_session, &xmpp_client_protocol, 1, 0);
+ gnutls_server_name_set(s->tls->gnutls_session, GNUTLS_NAME_DNS,
s->initial_jid.domain,
strlen(s->initial_jid.domain));
- gnutls_set_default_priority(s->tls.gnutls_session);
- gnutls_credentials_set(s->tls.gnutls_session, GNUTLS_CRD_CERTIFICATE,
- s->tls.gnutls_cred);
- gnutls_transport_set_int(s->tls.gnutls_session, s->server_socket);
- gnutls_handshake_set_timeout(s->tls.gnutls_session,
+ gnutls_set_default_priority(s->tls->gnutls_session);
+ gnutls_credentials_set(s->tls->gnutls_session, GNUTLS_CRD_CERTIFICATE,
+ s->tls->gnutls_cred);
+ gnutls_transport_set_int(s->tls->gnutls_session, s->server_socket);
+ gnutls_handshake_set_timeout(s->tls->gnutls_session,
GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
- if (s->tls.tls_session_data != NULL) {
- int ret = gnutls_session_set_data(s->tls.gnutls_session,
- s->tls.tls_session_data,
- s->tls.tls_session_data_size);
+ if (s->tls->tls_session_data != NULL) {
+ int ret = gnutls_session_set_data(s->tls->gnutls_session,
+ s->tls->tls_session_data,
+ s->tls->tls_session_data_size);
if (ret != GNUTLS_E_SUCCESS) {
rexmpp_log(s, LOG_WARNING, "Failed to set TLS session data: %s",
gnutls_strerror(ret));
- free(s->tls.tls_session_data);
- s->tls.tls_session_data = NULL;
- s->tls.tls_session_data_size = 0;
+ free(s->tls->tls_session_data);
+ s->tls->tls_session_data = NULL;
+ s->tls->tls_session_data_size = 0;
}
}
}
- int ret = gnutls_handshake(s->tls.gnutls_session);
+ int ret = gnutls_handshake(s->tls->gnutls_session);
if (ret == GNUTLS_E_AGAIN) {
rexmpp_log(s, LOG_DEBUG, "Waiting for TLS handshake to complete");
return REXMPP_TLS_E_AGAIN;
} else if (ret == 0) {
- int status;
+ unsigned int status;
int srv_is_secure = 0;
if (s->stream_state == REXMPP_STREAM_NONE &&
@@ -190,7 +418,7 @@ rexmpp_tls_connect (rexmpp_t *s) {
service/source host
(<https://tools.ietf.org/html/rfc7712#section-5.1>,
<https://tools.ietf.org/html/rfc7673#section-6>). */
- ret = dane_verify_session_crt(NULL, s->tls.gnutls_session, s->server_host,
+ ret = dane_verify_session_crt(NULL, s->tls->gnutls_session, s->server_host,
"tcp", s->server_port, 0, 0, &status);
if (ret) {
rexmpp_log(s, LOG_WARNING, "DANE verification error: %s",
@@ -212,7 +440,7 @@ rexmpp_tls_connect (rexmpp_t *s) {
}
}
- ret = gnutls_certificate_verify_peers3(s->tls.gnutls_session,
+ ret = gnutls_certificate_verify_peers3(s->tls->gnutls_session,
s->initial_jid.domain,
&status);
if (ret || status) {
@@ -224,23 +452,23 @@ rexmpp_tls_connect (rexmpp_t *s) {
} else {
rexmpp_log(s, LOG_ERR, "Untrusted certificate");
}
- gnutls_bye(s->tls.gnutls_session, GNUTLS_SHUT_RDWR);
+ gnutls_bye(s->tls->gnutls_session, GNUTLS_SHUT_RDWR);
return REXMPP_TLS_E_OTHER;
}
- if (gnutls_session_is_resumed(s->tls.gnutls_session)) {
+ if (gnutls_session_is_resumed(s->tls->gnutls_session)) {
rexmpp_log(s, LOG_INFO, "TLS session is resumed");
} else {
- if (s->tls.tls_session_data != NULL) {
+ if (s->tls->tls_session_data != NULL) {
rexmpp_log(s, LOG_DEBUG, "TLS session is not resumed");
- free(s->tls.tls_session_data);
- s->tls.tls_session_data = NULL;
+ free(s->tls->tls_session_data);
+ s->tls->tls_session_data = NULL;
}
- gnutls_session_get_data(s->tls.gnutls_session, NULL,
- &s->tls.tls_session_data_size);
- s->tls.tls_session_data = malloc(s->tls.tls_session_data_size);
- ret = gnutls_session_get_data(s->tls.gnutls_session, s->tls.tls_session_data,
- &s->tls.tls_session_data_size);
+ gnutls_session_get_data(s->tls->gnutls_session, NULL,
+ &s->tls->tls_session_data_size);
+ s->tls->tls_session_data = malloc(s->tls->tls_session_data_size);
+ ret = gnutls_session_get_data(s->tls->gnutls_session, s->tls->tls_session_data,
+ &s->tls->tls_session_data_size);
if (ret != GNUTLS_E_SUCCESS) {
rexmpp_log(s, LOG_ERR, "Failed to get TLS session data: %s",
gnutls_strerror(ret));
@@ -256,26 +484,27 @@ rexmpp_tls_connect (rexmpp_t *s) {
}
#elif defined(USE_OPENSSL)
if (s->tls_state != REXMPP_TLS_HANDSHAKE) {
- s->tls.openssl_conn = SSL_new(s->tls.openssl_ctx);
- if (s->tls.openssl_conn == NULL) {
+ s->tls->openssl_conn = SSL_new(s->tls->openssl_ctx);
+ if (s->tls->openssl_conn == NULL) {
rexmpp_log(s, LOG_ERR, "Failed to create an OpenSSL connection object");
return REXMPP_TLS_E_OTHER;
}
- if (SSL_set_fd(s->tls.openssl_conn, s->server_socket) == 0) {
+ if (SSL_set_fd(s->tls->openssl_conn, s->server_socket) == 0) {
rexmpp_log(s, LOG_ERR, "Failed to set a file descriptor for OpenSSL connection");
return REXMPP_TLS_E_OTHER;
}
- if (SSL_set1_host(s->tls.openssl_conn, s->initial_jid.domain) == 0) {
+ if (SSL_set1_host(s->tls->openssl_conn, s->initial_jid.domain) == 0) {
rexmpp_log(s, LOG_ERR, "Failed to set a hostname for OpenSSL connection");
return REXMPP_TLS_E_OTHER;
}
/* For SNI */
- if (SSL_set_tlsext_host_name(s->tls.openssl_conn, s->initial_jid.domain) == 0) {
+ if (SSL_set_tlsext_host_name(s->tls->openssl_conn, s->initial_jid.domain) == 0) {
rexmpp_log(s, LOG_ERR, "Failed to set a tlsext hostname for OpenSSL connection");
return REXMPP_TLS_E_OTHER;
}
}
- return rexmpp_process_openssl_ret(s, SSL_connect(s->tls.openssl_conn));
+ return rexmpp_process_openssl_ret(s, s->tls, "rexmpp_tls_connect",
+ SSL_connect(s->tls->openssl_conn));
#else
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
@@ -283,36 +512,86 @@ rexmpp_tls_connect (rexmpp_t *s) {
}
rexmpp_tls_err_t
-rexmpp_tls_disconnect (rexmpp_t *s) {
+rexmpp_tls_disconnect (rexmpp_t *s, rexmpp_tls_t *tls_ctx) {
#if defined(USE_GNUTLS)
- int ret = gnutls_bye(s->tls.gnutls_session, GNUTLS_SHUT_RDWR);
+ 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));
return REXMPP_TLS_E_OTHER;
}
#elif defined(USE_OPENSSL)
- int ret = SSL_shutdown(s->tls.openssl_conn);
+ int ret = SSL_shutdown(tls_ctx->openssl_conn);
if (ret == 0) {
- s->tls.openssl_direction = REXMPP_OPENSSL_READ;
+ tls_ctx->openssl_direction = REXMPP_OPENSSL_READ;
return REXMPP_TLS_E_AGAIN;
} else {
- return rexmpp_process_openssl_ret(s, ret);
+ return rexmpp_process_openssl_ret(s, 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
}
-rexmpp_tls_err_t
-rexmpp_tls_send (rexmpp_t *s, void *data, size_t data_size, ssize_t *written)
+#ifdef ENABLE_CALLS
+int
+rexmpp_tls_srtp_get_keys (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ size_t key_len,
+ size_t salt_len,
+ unsigned char *key_mat)
{
#if defined(USE_GNUTLS)
+ int key_mat_size;
+ key_mat_size =
+ gnutls_srtp_get_keys(tls_ctx->gnutls_session,
+ key_mat, (key_len + salt_len) * 2,
+ NULL, NULL, NULL, NULL);
+ if (key_mat_size == GNUTLS_E_SHORT_MEMORY_BUFFER ||
+ key_mat_size < 0) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to retrieve DTLS key material for SRTP: %s",
+ gnutls_strerror(key_mat_size));
+ }
+ return 0;
+#elif defined(USE_OPENSSL)
+ /* https://www.rfc-editor.org/rfc/rfc5764.html */
+ const char *extractor = "EXTRACTOR-dtls_srtp";
+ int err = SSL_export_keying_material(tls_ctx->openssl_conn,
+ key_mat, 2 * (key_len + salt_len),
+ extractor, strlen(extractor),
+ NULL, 0, 0);
+ return rexmpp_process_openssl_ret(s, tls_ctx,
+ "rexmpp_tls_srtp_get_keys", err);
+#else
+ (void)s;
+ (void)tls_ctx;
+ (void)key_len;
+ (void)salt_len;
+ (void)key_mat;
+ rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
+ return -1;
+#endif
+}
+#endif
+
+rexmpp_tls_err_t
+rexmpp_tls_send (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *data,
+ size_t data_size,
+ ssize_t *written)
+{
*written = -1;
- ssize_t ret = gnutls_record_send(s->tls.gnutls_session,
+#if defined(USE_GNUTLS)
+ ssize_t ret = gnutls_record_send(tls_ctx->gnutls_session,
data,
data_size);
if (ret >= 0) {
@@ -325,27 +604,32 @@ rexmpp_tls_send (rexmpp_t *s, void *data, size_t data_size, ssize_t *written)
return REXMPP_TLS_E_OTHER;
}
#elif defined(USE_OPENSSL)
- *written = -1;
- int ret = SSL_write_ex(s->tls.openssl_conn, data, data_size, written);
+ int ret = SSL_write_ex(tls_ctx->openssl_conn, data, data_size,
+ (size_t*)written);
if (ret > 0) {
return REXMPP_TLS_SUCCESS;
} else {
- return rexmpp_process_openssl_ret(s, ret);
+ return rexmpp_process_openssl_ret(s, tls_ctx, "rexmpp_tls_send", ret);
}
#else
(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
}
rexmpp_tls_err_t
-rexmpp_tls_recv (rexmpp_t *s, void *data, size_t data_size, ssize_t *received) {
+rexmpp_tls_recv (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *data,
+ size_t data_size,
+ ssize_t *received)
+{
#if defined(USE_GNUTLS)
*received = -1;
- ssize_t ret = gnutls_record_recv(s->tls.gnutls_session, data, data_size);
+ ssize_t ret = gnutls_record_recv(tls_ctx->gnutls_session, data, data_size);
if (ret >= 0) {
*received = ret;
return REXMPP_TLS_SUCCESS;
@@ -357,35 +641,49 @@ rexmpp_tls_recv (rexmpp_t *s, void *data, size_t data_size, ssize_t *received) {
}
#elif defined(USE_OPENSSL)
*received = -1;
- int ret = SSL_read_ex(s->tls.openssl_conn, data, data_size, received);
+ int ret = SSL_read_ex(tls_ctx->openssl_conn, data, data_size,
+ (size_t*)received);
if (ret > 0) {
return REXMPP_TLS_SUCCESS;
} else {
- return rexmpp_process_openssl_ret(s, ret);
+ return rexmpp_process_openssl_ret(s, tls_ctx, "rexmpp_tls_recv", ret);
}
#else
(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
}
+#ifdef ENABLE_CALLS
+unsigned int rexmpp_dtls_timeout (rexmpp_t *s, rexmpp_tls_t *tls_ctx) {
+ (void)s;
+#if defined(USE_GNUTLS)
+ return gnutls_dtls_get_timeout(tls_ctx->gnutls_session);
+#else
+ (void)tls_ctx;
+ return -1;
+#endif
+}
+#endif
+
int rexmpp_tls_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#if defined(USE_GNUTLS)
- if (gnutls_record_get_direction(s->tls.gnutls_session) == 0) {
+ if (gnutls_record_get_direction(s->tls->gnutls_session) == 0) {
FD_SET(s->server_socket, read_fds);
} else {
FD_SET(s->server_socket, write_fds);
}
return s->server_socket + 1;
#elif defined(USE_OPENSSL)
- if (s->tls.openssl_direction == REXMPP_OPENSSL_READ) {
+ if (s->tls->openssl_direction == REXMPP_OPENSSL_READ) {
FD_SET(s->server_socket, read_fds);
return s->server_socket + 1;
}
- if (s->tls.openssl_direction == REXMPP_OPENSSL_WRITE) {
+ if (s->tls->openssl_direction == REXMPP_OPENSSL_WRITE) {
FD_SET(s->server_socket, write_fds);
return s->server_socket + 1;
}
@@ -400,20 +698,25 @@ int rexmpp_tls_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
rexmpp_tls_err_t
rexmpp_tls_set_x509_key_file (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
const char *cert_file,
const char *key_file)
{
+ if (cert_file == NULL) {
+ cert_file = s->x509_cert_file;
+ }
+ if (key_file == NULL) {
+ key_file = s->x509_key_file;
+ }
+ if (cert_file == NULL || key_file == NULL) {
+ rexmpp_log(s, LOG_ERR, "No certificate or key file defined");
+ return REXMPP_TLS_E_OTHER;
+ }
#if defined(USE_GNUTLS)
- int ret = gnutls_certificate_set_x509_key_file(s->tls.gnutls_cred,
+ int ret = gnutls_certificate_set_x509_key_file(tls_ctx->gnutls_cred,
cert_file,
key_file,
- GNUTLS_X509_FMT_PEM);
-#ifdef ENABLE_CALLS
- gnutls_certificate_set_x509_key_file(s->jingle.dtls_cred,
- cert_file,
- key_file,
- GNUTLS_X509_FMT_PEM);
-#endif
+ GNUTLS_X509_FMT_DER);
if (ret == 0) {
return REXMPP_TLS_SUCCESS;
} else {
@@ -422,15 +725,15 @@ rexmpp_tls_set_x509_key_file (rexmpp_t *s,
return REXMPP_TLS_E_OTHER;
}
#elif defined(USE_OPENSSL)
- if (SSL_CTX_use_certificate_file(s->tls.openssl_ctx,
+ if (SSL_CTX_use_certificate_file(tls_ctx->openssl_ctx,
cert_file,
- SSL_FILETYPE_PEM) != 1) {
+ SSL_FILETYPE_ASN1) != 1) {
rexmpp_log(s, LOG_ERR, "Failed to set a certificate file");
return REXMPP_TLS_E_OTHER;
}
- if (SSL_CTX_use_PrivateKey_file(s->tls.openssl_ctx,
+ if (SSL_CTX_use_PrivateKey_file(tls_ctx->openssl_ctx,
key_file,
- SSL_FILETYPE_PEM) != 1) {
+ SSL_FILETYPE_ASN1) != 1) {
rexmpp_log(s, LOG_ERR, "Failed to set a key file");
return REXMPP_TLS_E_OTHER;
}
@@ -438,6 +741,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
@@ -445,22 +749,248 @@ rexmpp_tls_set_x509_key_file (rexmpp_t *s,
rexmpp_tls_err_t
rexmpp_tls_set_x509_trust_file (rexmpp_t *s,
- const char *cert_file)
+ rexmpp_tls_t *tls_ctx,
+ const char *trust_file)
{
+ if (trust_file == NULL) {
+ trust_file = s->x509_trust_file;
+ }
+ if (trust_file == NULL) {
+ rexmpp_log(s, LOG_ERR, "No trust file is defined");
+ return REXMPP_TLS_E_OTHER;
+ }
#if defined(USE_GNUTLS)
- gnutls_certificate_set_x509_trust_file(s->tls.gnutls_cred,
- cert_file,
- GNUTLS_X509_FMT_PEM);
+ gnutls_certificate_set_x509_trust_file(tls_ctx->gnutls_cred,
+ trust_file,
+ GNUTLS_X509_FMT_DER);
return REXMPP_TLS_SUCCESS;
#elif defined(USE_OPENSSL)
- if (SSL_CTX_load_verify_locations(s->tls.openssl_ctx, cert_file, NULL) != 1) {
+ if (SSL_CTX_load_verify_locations(tls_ctx->openssl_ctx, trust_file, NULL) != 1) {
rexmpp_log(s, LOG_ERR, "Failed to set a trusted certificate file");
return REXMPP_TLS_E_OTHER;
}
return REXMPP_TLS_SUCCESS;
#else
- (void)cert_file;
+ (void)trust_file;
+ (void)tls_ctx;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support");
return REXMPP_TLS_E_OTHER;
#endif
}
+
+
+int rexmpp_tls_peer_fp (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *algo_str,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+#if defined(USE_GNUTLS)
+ unsigned int cert_list_size = 0;
+ const gnutls_datum_t *cert_list;
+ cert_list =
+ gnutls_certificate_get_peers(tls_ctx->gnutls_session, &cert_list_size);
+ if (cert_list_size != 1) {
+ rexmpp_log(s, LOG_ERR,
+ "Unexpected peer certificate list size: %d",
+ cert_list_size);
+ return -1;
+ }
+ return rexmpp_x509_raw_cert_fp(s, algo_str, cert_list,
+ raw_fp, fp_str, fp_size);
+#elif defined(USE_OPENSSL)
+ if (strcmp(algo_str, "sha-256") != 0) {
+ rexmpp_log(s, LOG_ERR,
+ "Unsupported hash function algorithm: %s", algo_str);
+ return -1;
+ }
+ X509 *peer_cert = SSL_get0_peer_certificate(tls_ctx->openssl_conn);
+ if (peer_cert == NULL) {
+ rexmpp_log(s, LOG_ERR, "No peer certificate found");
+ return -1;
+ }
+ unsigned int len;
+ X509_digest(peer_cert, EVP_sha256(), (unsigned char*)raw_fp, &len);
+ *fp_size = len;
+ size_t i;
+ for (i = 0; i < *fp_size; i++) {
+ snprintf(fp_str + i * 3, 4, "%02X:", raw_fp[i] & 0xFF);
+ }
+ fp_str[*fp_size * 3 - 1] = 0;
+ return 0;
+#else
+ (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
+}
+
+/* TODO: handle different algorithms, and maybe apply this to
+ arbitrary files. */
+int rexmpp_tls_my_fp (rexmpp_t *s,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+ rexmpp_digest_t digest_ctx;
+ if (rexmpp_digest_init(&digest_ctx, REXMPP_DIGEST_SHA256)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a digest object");
+ return -1;
+ }
+
+ if (s->x509_cert_file == NULL) {
+ rexmpp_log(s, LOG_WARNING, "No X.509 certificate file defined");
+ return -1;
+ }
+ FILE *fh = fopen(s->x509_cert_file, "r");
+ if (fh == NULL) {
+ rexmpp_log(s, LOG_ERR, "Failed to open the X.509 certificate file");
+ return -1;
+ }
+ unsigned char *buf[4096];
+ size_t len = fread(buf, 1, 4096, fh);
+ while (len > 0) {
+ rexmpp_digest_update(&digest_ctx, buf, len);
+ len = fread(buf, 1, 4096, fh);
+ }
+ fclose(fh);
+
+ *fp_size = rexmpp_digest_len(REXMPP_DIGEST_SHA256);
+ rexmpp_digest_finish(&digest_ctx, raw_fp, *fp_size);
+
+ size_t i;
+ for (i = 0; i < (*fp_size); i++) {
+ snprintf(fp_str + i * 3, 4, "%02X:", raw_fp[i] & 0xFF);
+ }
+ fp_str[(*fp_size) * 3 - 1] = 0;
+ return 0;
+}
+
+int rexmpp_tls_session_fp (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *algo_str,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+#if defined(USE_GNUTLS)
+ gnutls_x509_crt_t *cert_list;
+ unsigned int cert_list_size = 0;
+ int err =
+ gnutls_certificate_get_x509_crt(tls_ctx->gnutls_cred,
+ 0, &cert_list, &cert_list_size);
+ if (err) {
+ rexmpp_log(s, LOG_ERR,
+ "Failed to read own certificate list: %s",
+ gnutls_strerror(err));
+ return -1;
+ }
+
+ err = rexmpp_x509_cert_fp(s, algo_str, cert_list[0], raw_fp, fp_str, fp_size);
+
+ size_t i;
+ for (i = 0; i < cert_list_size; i++) {
+ gnutls_x509_crt_deinit(cert_list[i]);
+ }
+ gnutls_free(cert_list);
+ return err;
+#else
+ (void)s;
+ (void)tls_ctx;
+ (void)algo_str;
+ (void)raw_fp;
+ (void)fp_str;
+ (void)fp_size;
+ return -1;
+#endif
+}
+
+int rexmpp_x509_cert_fp (rexmpp_t *s,
+ const char *algo_str,
+ void *cert,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+#if defined(USE_GNUTLS)
+ gnutls_datum_t raw_cert;
+ int err = gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_DER, &raw_cert);
+ if (err != GNUTLS_E_SUCCESS) {
+ rexmpp_log(s, LOG_ERR, "Failed to export a certificate: %s",
+ gnutls_strerror(err));
+ return err;
+ }
+ err = rexmpp_x509_raw_cert_fp(s, algo_str, &raw_cert, raw_fp, fp_str, fp_size);
+ gnutls_free(raw_cert.data);
+ return err;
+#else
+ (void)s;
+ (void)algo_str;
+ (void)cert;
+ (void)raw_fp;
+ (void)fp_str;
+ (void)fp_size;
+ return -1;
+#endif
+}
+
+int rexmpp_x509_raw_cert_fp (rexmpp_t *s,
+ const char *algo_str,
+ const void *raw_cert,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size)
+{
+#if defined(USE_GNUTLS)
+ const gnutls_datum_t *cert = (const gnutls_datum_t*)raw_cert;
+ gnutls_digest_algorithm_t algo = GNUTLS_DIG_UNKNOWN;
+ /* gnutls_digest_get_id uses different names, so
+ checking manually here. These are SDP options,
+ <https://datatracker.ietf.org/doc/html/rfc4572#page-8>. */
+ if (strcmp(algo_str, "sha-1") == 0) {
+ algo = GNUTLS_DIG_SHA1;
+ } else if (strcmp(algo_str, "sha-224") == 0) {
+ algo = GNUTLS_DIG_SHA224;
+ } else if (strcmp(algo_str, "sha-256") == 0) {
+ algo = GNUTLS_DIG_SHA256;
+ } else if (strcmp(algo_str, "sha-384") == 0) {
+ algo = GNUTLS_DIG_SHA384;
+ } else if (strcmp(algo_str, "sha-512") == 0) {
+ algo = GNUTLS_DIG_SHA512;
+ } else if (strcmp(algo_str, "md5") == 0) {
+ algo = GNUTLS_DIG_MD5;
+ }
+ if (algo == GNUTLS_DIG_UNKNOWN) {
+ rexmpp_log(s, LOG_ERR, "Unknown hash algorithm: %s", algo_str);
+ return -1;
+ }
+
+ int err = gnutls_fingerprint(algo, cert, raw_fp, fp_size);
+ if (err != GNUTLS_E_SUCCESS) {
+ rexmpp_log(s, LOG_ERR, "Failed to calculate a fingerprint: %s",
+ gnutls_strerror(err));
+ return -1;
+ }
+ if (fp_str != NULL) {
+ size_t i;
+ for (i = 0; i < (*fp_size); i++) {
+ snprintf(fp_str + i * 3, 4, "%02X:", raw_fp[i] & 0xFF);
+ }
+ fp_str[(*fp_size) * 3 - 1] = 0;
+ }
+ return 0;
+#else
+ (void)s;
+ (void)algo_str;
+ (void)raw_cert;
+ (void)raw_fp;
+ (void)fp_str;
+ (void)fp_size;
+ return -1;
+#endif
+}
diff --git a/src/rexmpp_tls.h b/src/rexmpp_tls.h
index 24ba042..ff4bca6 100644
--- a/src/rexmpp_tls.h
+++ b/src/rexmpp_tls.h
@@ -20,6 +20,8 @@ to write logs and read other values (including server socket).
#include "rexmpp.h"
#include "config.h"
+#define DTLS_SRTP_BUF_SIZE 0x4000
+
typedef struct rexmpp_tls rexmpp_tls_t;
/**
@@ -43,6 +45,8 @@ struct rexmpp_tls {
size_t tls_session_data_size;
gnutls_session_t gnutls_session;
gnutls_certificate_credentials_t gnutls_cred;
+ char dtls_buf[DTLS_SRTP_BUF_SIZE];
+ size_t dtls_buf_len;
};
#elif defined(USE_OPENSSL)
#include <openssl/ssl.h>
@@ -54,6 +58,8 @@ enum rexmpp_openssl_direction {
struct rexmpp_tls {
SSL_CTX *openssl_ctx;
SSL *openssl_conn;
+ BIO *bio_conn;
+ BIO *bio_io;
enum rexmpp_openssl_direction openssl_direction;
};
#else
@@ -66,26 +72,93 @@ int rexmpp_tls_init(rexmpp_t *s);
void rexmpp_tls_cleanup(rexmpp_t *s);
void rexmpp_tls_deinit(rexmpp_t *s);
-rexmpp_tls_err_t rexmpp_tls_connect(rexmpp_t *s);
-rexmpp_tls_err_t rexmpp_tls_disconnect(rexmpp_t *s);
+rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls);
+void rexmpp_tls_ctx_free (rexmpp_tls_t *tls_ctx);
+
+void rexmpp_tls_session_free (rexmpp_tls_t *tls_ctx);
-rexmpp_tls_err_t rexmpp_tls_send(rexmpp_t *s, void *data, size_t data_size, ssize_t *written);
-rexmpp_tls_err_t rexmpp_tls_recv(rexmpp_t *s, void *data, size_t data_size, ssize_t *received);
+rexmpp_tls_err_t rexmpp_tls_connect (rexmpp_t *s);
+rexmpp_tls_err_t rexmpp_tls_handshake (rexmpp_t *s, rexmpp_tls_t *tls_ctx);
+rexmpp_tls_err_t rexmpp_tls_disconnect (rexmpp_t *s, rexmpp_tls_t *tls_ctx);
+#ifdef ENABLE_CALLS
+rexmpp_tls_err_t
+rexmpp_dtls_connect (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *user_data,
+ int client);
+void rexmpp_dtls_feed(rexmpp_t *s, rexmpp_tls_t *tls_ctx, uint8_t *buf, size_t len);
+
+int
+rexmpp_tls_srtp_get_keys (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ size_t key_len,
+ size_t salt_len,
+ unsigned char *key_mat);
+#endif
+rexmpp_tls_err_t
+rexmpp_tls_send (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *data,
+ size_t data_size,
+ ssize_t *written);
+rexmpp_tls_err_t
+rexmpp_tls_recv (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ void *data,
+ size_t data_size,
+ ssize_t *received);
+#ifdef ENABLE_CALLS
+unsigned int rexmpp_dtls_timeout (rexmpp_t *s, rexmpp_tls_t *tls_ctx);
+#endif
int rexmpp_tls_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds);
/**
- @brief Sets credentials for both client authentication to the
- server (SASL EXTERNAL) and DTLS connections in Jingle sessions.
+ @brief Sets credentials for a given TLS context: either provided
+ ones or defined for the whole ::rexmpp structure.
*/
rexmpp_tls_err_t
rexmpp_tls_set_x509_key_file (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
const char *cert_file,
const char *key_file);
rexmpp_tls_err_t
rexmpp_tls_set_x509_trust_file (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
const char *cert_file);
+int rexmpp_tls_peer_fp (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *algo_str,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
+
+int rexmpp_tls_my_fp (rexmpp_t *s,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
+
+int rexmpp_tls_session_fp (rexmpp_t *s,
+ rexmpp_tls_t *tls_ctx,
+ const char *algo_str,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
+
+int rexmpp_x509_cert_fp (rexmpp_t *s,
+ const char *algo_str,
+ void *cert,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
+
+int rexmpp_x509_raw_cert_fp (rexmpp_t *s,
+ const char *algo_str,
+ const void *raw_cert,
+ char *raw_fp,
+ char *fp_str,
+ size_t *fp_size);
#endif
diff --git a/src/rexmpp_utf8.h b/src/rexmpp_utf8.h
new file mode 100644
index 0000000..4096aac
--- /dev/null
+++ b/src/rexmpp_utf8.h
@@ -0,0 +1,93 @@
+/**
+ @file rexmpp_utf8.h
+ @brief UTF-8 helper functions
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_UTF8_H
+#define REXMPP_UTF8_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef HAVE_ICU
+
+#include <unicode/utf8.h>
+#define REXMPP_U8_NEXT U8_NEXT
+
+#else
+
+#define REXMPP_U8_NEXT(str, pos, len, out) \
+ rexmpp_utf8_next(str, &pos, len, &out);
+
+/**
+ @brief Similar to libicu's U8_NEXT macros: reads a single UTF-8
+ code point, advances the position.
+ @param[in] str A string to read.
+ @param[in,out] pos Byte position within the string. Advanced by the
+ number of bytes read to produce a code point, not advanced on
+ failure.
+ @param[in] len String length.
+ @param[in,out] out A pointer to the location for writing the code
+ point.
+ @returns 0 on failure, 1 on success.
+*/
+inline static
+void rexmpp_utf8_next (const uint8_t *str,
+ size_t *pos,
+ size_t len,
+ int32_t *out)
+{
+ if (*pos >= len) {
+ *out = -1;
+ return;
+ }
+
+ if ((str[*pos] & 0x80) == 0
+ && *pos + 1 <= len)
+ /* U+0000 to U+007F: 0xxxxxxx */
+ {
+ *out = str[*pos];
+ *pos = *pos + 1;
+ } else if ((str[*pos] & 0xe0) == 0xc0
+ && *pos + 2 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80)
+ /* U+0080 to U+07FF: 110xxxxx 10xxxxxx */
+ {
+ *out = (((int32_t)(str[*pos] & 0x1f) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f));
+ *pos = *pos + 2;
+ } else if ((str[*pos] & 0xf0) == 0xe0
+ && *pos + 3 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80
+ && (str[*pos + 2] & 0xc0) == 0x80)
+ /* U+0800 to U+FFFF: 1110xxxx 10xxxxxx 10xxxxxx */
+ {
+ *out = (((((int32_t)(str[*pos] & 0xf) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f)) << 6)
+ | ((int32_t)str[*pos + 2] & 0x3f));
+ *pos = *pos + 3;
+ } else if ((str[*pos] & 0xf8) == 0xf0
+ && *pos + 4 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80
+ && (str[*pos + 2] & 0xc0) == 0x80
+ && (str[*pos + 3] & 0xc0) == 0x80)
+ /* U+10000 to U+10FFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ {
+ *out = (((((((int32_t)(str[*pos] & 7) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f)) << 6)
+ | (((int32_t)str[*pos + 2] & 0x3f))) << 6)
+ | ((int32_t)str[*pos + 3] & 0x3f));
+ *pos = *pos + 4;
+ } else
+ /* Invalid UTF-8 */
+ {
+ *out = -1;
+ }
+}
+
+#endif /* HAVE_ICU */
+
+#endif /* REXMPP_UTF8_H */
diff --git a/src/rexmpp_xml.c b/src/rexmpp_xml.c
new file mode 100644
index 0000000..8b106ab
--- /dev/null
+++ b/src/rexmpp_xml.c
@@ -0,0 +1,805 @@
+/**
+ @file rexmpp_xml.c
+ @brief XML structures and functions for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#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) {
+ if (qname->name != NULL) {
+ free(qname->name);
+ qname->name = NULL;
+ }
+ if (qname->namespace != NULL) {
+ free(qname->namespace);
+ qname->namespace = NULL;
+ }
+}
+
+void rexmpp_xml_attribute_free (rexmpp_xml_attr_t *attr) {
+ if (attr == NULL) {
+ return;
+ }
+ rexmpp_xml_qname_free(&(attr->qname));
+ if (attr->value != NULL) {
+ free(attr->value);
+ attr->value = NULL;
+ }
+ free(attr);
+}
+
+void rexmpp_xml_attribute_free_list (rexmpp_xml_attr_t *attr) {
+ rexmpp_xml_attr_t *next = attr;
+ while (attr != NULL) {
+ next = attr->next;
+ rexmpp_xml_attribute_free(attr);
+ attr = next;
+ }
+}
+
+void rexmpp_xml_free (rexmpp_xml_t *node) {
+ if (node == NULL) {
+ return;
+ }
+ if (node->type == REXMPP_XML_TEXT) {
+ if (node->alt.text != NULL) {
+ free(node->alt.text);
+ node->alt.text = NULL;
+ }
+ } if (node->type == REXMPP_XML_ELEMENT) {
+ rexmpp_xml_qname_free(&(node->alt.elem.qname));
+ rexmpp_xml_attribute_free_list(node->alt.elem.attributes);
+ rexmpp_xml_free_list(node->alt.elem.children);
+ }
+ free(node);
+}
+
+void rexmpp_xml_free_list (rexmpp_xml_t *node) {
+ rexmpp_xml_t *next = node;
+ while (node != NULL) {
+ next = node->next;
+ rexmpp_xml_free(node);
+ node = next;
+ }
+}
+
+rexmpp_xml_t *rexmpp_xml_clone (rexmpp_xml_t *node) {
+ if (node == NULL) {
+ return NULL;
+ }
+
+ if (node->type == REXMPP_XML_TEXT) {
+ return rexmpp_xml_new_text(node->alt.text);
+ } else if (node->type == REXMPP_XML_ELEMENT) {
+ rexmpp_xml_t *ret =
+ rexmpp_xml_new_elem(node->alt.elem.qname.name,
+ node->alt.elem.qname.namespace);
+ rexmpp_xml_attr_t **next_attr = &(ret->alt.elem.attributes);
+ rexmpp_xml_attr_t *old_attr;
+ for (old_attr = node->alt.elem.attributes;
+ old_attr != NULL;
+ old_attr = old_attr->next)
+ {
+ rexmpp_xml_attr_t *new_attr =
+ rexmpp_xml_attr_new(old_attr->qname.name,
+ old_attr->qname.namespace,
+ old_attr->value);
+ *next_attr = new_attr;
+ next_attr = &(new_attr->next);
+ }
+
+ ret->alt.elem.children =
+ rexmpp_xml_clone_list(node->alt.elem.children);
+ return ret;
+ }
+ return NULL;
+}
+
+rexmpp_xml_t *rexmpp_xml_clone_list (rexmpp_xml_t *node) {
+ rexmpp_xml_t *first, *last;
+ if (node == NULL) {
+ return NULL;
+ }
+ first = rexmpp_xml_clone(node);
+ for (last = first, node = node->next;
+ node != NULL;
+ last = last->next, node = node->next)
+ {
+ last->next = rexmpp_xml_clone(node);
+ }
+ return first;
+}
+
+rexmpp_xml_t *rexmpp_xml_new_text (const char *str) {
+ rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t));
+ node->type = REXMPP_XML_TEXT;
+ node->alt.text = strdup(str);
+ node->next = NULL;
+ return node;
+}
+
+rexmpp_xml_t *rexmpp_xml_new_text_len (const char *str, size_t len) {
+ rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t));
+ node->type = REXMPP_XML_TEXT;
+ node->alt.text = strndup(str, len);
+ node->next = NULL;
+ return node;
+}
+
+void rexmpp_xml_add_child (rexmpp_xml_t *node,
+ rexmpp_xml_t *child)
+{
+ rexmpp_xml_t **last_ptr = &(node->alt.elem.children);
+ while (*last_ptr != NULL) {
+ last_ptr = &((*last_ptr)->next);
+ }
+ *last_ptr = child;
+}
+
+int rexmpp_xml_add_text (rexmpp_xml_t *node,
+ const char *str)
+{
+ rexmpp_xml_t *text_node = rexmpp_xml_new_text(str);
+ if (text_node != NULL) {
+ rexmpp_xml_add_child(node, text_node);
+ return 0;
+ }
+ return -1;
+}
+
+int rexmpp_xml_add_text_len (rexmpp_xml_t *node,
+ const char *str,
+ size_t len)
+{
+ rexmpp_xml_t *text_node = rexmpp_xml_new_text_len(str, len);
+ if (text_node != NULL) {
+ rexmpp_xml_add_child(node, text_node);
+ return 0;
+ }
+ return -1;
+}
+
+rexmpp_xml_t *rexmpp_xml_new_elem (const char *name,
+ const char *namespace)
+{
+ rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t));
+ node->type = REXMPP_XML_ELEMENT;
+ node->alt.elem.qname.name = strdup(name);
+ if (namespace != NULL) {
+ node->alt.elem.qname.namespace = strdup(namespace);
+ } else {
+ node->alt.elem.qname.namespace = NULL;
+ }
+ node->alt.elem.attributes = NULL;
+ node->alt.elem.children = NULL;
+ node->next = NULL;
+ return node;
+}
+
+rexmpp_xml_attr_t *rexmpp_xml_attr_new (const char *name,
+ const char *namespace,
+ const char *value)
+{
+ rexmpp_xml_attr_t *attr = malloc(sizeof(rexmpp_xml_attr_t));
+ attr->qname.name = strdup(name);
+ if (namespace != NULL) {
+ attr->qname.namespace = strdup(namespace);
+ } else {
+ attr->qname.namespace = NULL;
+ }
+ attr->value = strdup(value);
+ attr->next = NULL;
+ return attr;
+}
+
+int rexmpp_xml_add_attr_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace,
+ const char *value)
+{
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return -1;
+ }
+ rexmpp_xml_attr_t *attr =
+ rexmpp_xml_attr_new(name, namespace, value);
+ attr->next = node->alt.elem.attributes;
+ node->alt.elem.attributes = attr;
+ return 0;
+}
+
+int rexmpp_xml_remove_attr_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace) {
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return -1;
+ }
+
+ rexmpp_xml_attr_t **attr, *next_attr;
+ for (attr = &(node->alt.elem.attributes); *attr != NULL; attr = &((*attr)->next)) {
+ if (rexmpp_xml_attr_match(*attr, namespace, name)) {
+ next_attr = (*attr)->next;
+ rexmpp_xml_attribute_free(*attr);
+ *attr = next_attr;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int rexmpp_xml_add_attr (rexmpp_xml_t *node,
+ const char *name,
+ const char *value)
+{
+ return rexmpp_xml_add_attr_ns(node, name, NULL, value);
+}
+
+int rexmpp_xml_remove_attr (rexmpp_xml_t *node,
+ const char *name) {
+ return rexmpp_xml_remove_attr_ns(node, name, NULL);
+}
+
+/* Adds a character, grows the string as needed. */
+static inline char *rexmpp_str_putc (char *str, size_t *len, char c) {
+ char *ret = str;
+ if ((*len) % 1024 == 0) {
+ ret = realloc(str, (*len) + 1024);
+ if (ret == NULL) {
+ /* A failure to realloc. */
+ if (str != NULL) {
+ free(str);
+ }
+ return NULL;
+ }
+ }
+ ret[*len] = c;
+ *len = (*len) + 1;
+ return ret;
+}
+
+static inline
+char *rexmpp_str_putc_escaped (char *str, size_t *len, char c) {
+ char *ret = str;
+ char buf[7];
+ char *esc = buf;
+ size_t i = 0;
+ size_t esc_len;
+ if (c == '<') {
+ esc = "&lt;";
+ } else if (c == '>') {
+ esc = "&gt;";
+ } else if (c == '&') {
+ esc = "&amp;";
+ } else if (c == '\'') {
+ esc = "&apos;";
+ } else if (c == '"') {
+ esc = "&quot;";
+ } else {
+ snprintf(esc, 7, "&#%u;", c);
+ }
+ esc_len = strlen(esc);
+ while (i < esc_len) {
+ ret = rexmpp_str_putc(ret, len, esc[i]);
+ i++;
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print_name (char *str, size_t *len, const char *name) {
+ char *ret = str;
+ size_t name_len = strlen(name);
+ size_t i = 0;
+ int32_t c = 0; /* matches ICU's UChar32 */
+ size_t prev_i = 0, j;
+ do {
+ REXMPP_U8_NEXT(name, i, name_len, c);
+ if (c >= 0) {
+ if (c == ':'
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= 0xC0 && c <= 0xD6)
+ || (c >= 0xD8 && c <= 0xF6)
+ || (c >= 0xF8 && c <= 0x2FF)
+ || (c >= 0x370 && c <= 0x37D)
+ || (c >= 0x37F && c <= 0x1FFF)
+ || (c >= 0x200C && c <= 0x200D)
+ || (c >= 0x2070 && c <= 0x218F)
+ || (c >= 0x2C00 && c <= 0x2FEF)
+ || (c >= 0x3001 && c <= 0xD7FF)
+ || (c >= 0xF900 && c <= 0xFDCF)
+ || (c >= 0xFDF0 && c <= 0xFFF0)
+ || (c >= 0x10000 && c <= 0xEFFFF)
+ || ((i > 0) &&
+ (c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || c == 0xB7
+ || (c >= 0x0300 && c <= 0x036F)
+ || (c >= 0x203F && c <= 0x2040))))
+ {
+ /* Print the allowed characters. */
+ for (j = prev_i; j < i; j++) {
+ ret = rexmpp_str_putc(ret, len, name[j]);
+ }
+ }
+ } else {
+ /* Skip invalid characters. */
+ i++;
+ }
+ prev_i = i;
+ } while (i < name_len);
+ return ret;
+}
+
+char *rexmpp_xml_print_text (char *str, size_t *len, const char *text) {
+ char *ret = str;
+ size_t i = 0;
+ size_t text_len = strlen(text);
+ while (i < text_len && ret != NULL) {
+ char c = text[i];
+ if (strchr("<&>'\"", c)) {
+ /* Escape the few special characters. */
+ ret = rexmpp_str_putc_escaped(ret, len, c);
+ } else {
+ /* Write others as is. */
+ ret = rexmpp_str_putc(ret, len, c);
+ }
+ i++;
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print_raw (char *str, size_t *len, const char *text) {
+ char *ret = str;
+ size_t i = 0;
+ size_t text_len = strlen(text);
+ while (i < text_len && ret != NULL) {
+ char c = text[i];
+ ret = rexmpp_str_putc(ret, len, c);
+ i++;
+ }
+ return ret;
+}
+
+static inline char *rexmpp_xml_print_indent (char *str,
+ size_t *len,
+ int indent) {
+ if (indent <= 0) {
+ return str;
+ }
+ int i;
+ char *ret = str;
+ for (i = 0; i < indent * 2; i++) {
+ ret = rexmpp_str_putc(ret, len, ' ');
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print (char *str,
+ size_t *len,
+ const rexmpp_xml_t *node,
+ int indent) {
+ char *ret = str;
+ if (node->type == REXMPP_XML_TEXT) {
+ ret = rexmpp_xml_print_text(ret, len, node->alt.text);
+ } else if (node->type == REXMPP_XML_ELEMENT) {
+ if (indent > 0) {
+ ret = rexmpp_str_putc(ret, len, '\n');
+ ret = rexmpp_xml_print_indent(ret, len, indent);
+ }
+ ret = rexmpp_str_putc(ret, len, '<');
+ ret = rexmpp_xml_print_name(ret, len, node->alt.elem.qname.name);
+ if (node->alt.elem.qname.namespace != NULL) {
+ ret = rexmpp_xml_print_raw(ret, len, " xmlns=\"");
+ ret = rexmpp_xml_print_text(ret, len, node->alt.elem.qname.namespace);
+ ret = rexmpp_str_putc(ret, len, '"');
+ }
+ if (node->alt.elem.attributes != NULL) {
+ rexmpp_xml_attr_t *attr;
+ for (attr = node->alt.elem.attributes; attr != NULL; attr = attr->next) {
+ ret = rexmpp_str_putc(ret, len, ' ');
+ /* Ignoring namespaces here for now. */
+ ret = rexmpp_xml_print_name(ret, len, attr->qname.name);
+ ret = rexmpp_xml_print_raw(ret, len, "=\"");
+ ret = rexmpp_xml_print_text(ret, len, attr->value);
+ ret = rexmpp_str_putc(ret, len, '"');
+ }
+ }
+ if (node->alt.elem.children == NULL) {
+ ret = rexmpp_xml_print_raw(ret, len, "/>");
+ } else {
+ ret = rexmpp_str_putc(ret, len, '>');
+ rexmpp_xml_t *child;
+ int last_child_is_textual = 0;
+ for (child = rexmpp_xml_children(node);
+ child != NULL;
+ child = child->next)
+ {
+ ret = rexmpp_xml_print(ret, len, child,
+ indent > -1 ? indent + 1 : -1);
+ last_child_is_textual = child->type == REXMPP_XML_TEXT;
+ }
+ if (indent >= 0 && ! last_child_is_textual) {
+ ret = rexmpp_str_putc(ret, len, '\n');
+ ret = rexmpp_xml_print_indent(ret, len, indent);
+ }
+ ret = rexmpp_xml_print_raw(ret, len, "</");
+ ret = rexmpp_xml_print_name(ret, len, node->alt.elem.qname.name);
+ ret = rexmpp_str_putc(ret, len, '>');
+ }
+ }
+ return ret;
+}
+
+char *rexmpp_xml_serialize (const rexmpp_xml_t *node, int pretty) {
+ size_t s_len = 0;
+ char *s = NULL;
+ s = rexmpp_xml_print(s, &s_len, node, pretty ? 0 : -1);
+ s = rexmpp_str_putc(s, &s_len, '\0');
+ return s;
+}
+
+rexmpp_xml_t *
+rexmpp_xml_add_id (rexmpp_xml_t *node)
+{
+ char *buf = rexmpp_random_id();
+ if (buf == NULL) {
+ return NULL;
+ }
+ rexmpp_xml_add_attr(node, "id", buf);
+ free(buf);
+ return node;
+}
+#endif
+
+/* These SAX handlers are similar to those from rexmpp.c, and perhaps
+ can be reused. */
+void rexmpp_xml_parse_sax_characters (struct rexmpp_xml_builder *builder,
+ const char *ch,
+ size_t len)
+{
+ if (builder->current != NULL) {
+ rexmpp_xml_t *last_node = builder->current->alt.elem.children;
+ if (last_node != NULL && last_node->type == REXMPP_XML_TEXT) {
+ /* The last child is textual as well, just extend it */
+ size_t last_len = strlen(last_node->alt.text);
+ char *new_alt_text = realloc(last_node->alt.text, last_len + len + 1);
+ if (new_alt_text == NULL) {
+ /* TODO: Would be nice a report an error here. */
+ return;
+ }
+ last_node->alt.text = new_alt_text;
+ strncpy(last_node->alt.text + last_len, ch, len);
+ last_node->alt.text[last_len + len] = '\0';
+ } else {
+ rexmpp_xml_t *text_node = rexmpp_xml_new_text_len(ch, len);
+ if (text_node != NULL) {
+ text_node->next = builder->current->alt.elem.children;
+ builder->current->alt.elem.children = text_node;
+ }
+ }
+ }
+}
+
+void rexmpp_xml_parse_sax_start_elem_ns (struct rexmpp_xml_builder *builder,
+ const char *name,
+ const char *namespace,
+ rexmpp_xml_attr_t *attributes)
+{
+ if (builder->current == NULL && builder->root == NULL) {
+ /* Just started */
+ builder->current = rexmpp_xml_new_elem(name, namespace);
+ builder->root = builder->current;
+ } else if (builder->current != NULL) {
+ /* Parsing is in progress */
+ rexmpp_xml_t *node = rexmpp_xml_new_elem(name, namespace);
+ node->next = builder->current->alt.elem.children;
+ builder->current->alt.elem.children = node;
+ builder->current = node;
+ } else {
+ /* The parsind is over, but we are receiving these events
+ still. Just free the attribute lists, ignore the rest. */
+ rexmpp_xml_attribute_free_list(attributes);
+ }
+ builder->current->alt.elem.attributes = attributes;
+}
+
+void rexmpp_xml_parse_sax_end_elem_ns (struct rexmpp_xml_builder *builder)
+{
+ if (builder->current != builder->root) {
+ /* Find the parent, set it as current element. */
+ rexmpp_xml_t *parent = builder->root;
+ while (parent->alt.elem.children != builder->current) {
+ parent = parent->alt.elem.children;
+ }
+ builder->current = parent;
+ } else {
+ /* Done parsing this element; reverse all the lists of children. */
+ builder->current = NULL;
+ rexmpp_xml_reverse_children(builder->root);
+ }
+}
+
+struct rexmpp_xml_parser_handlers builder_sax = {
+ (rexmpp_xml_parser_element_start)rexmpp_xml_parse_sax_start_elem_ns,
+ (rexmpp_xml_parser_element_end)rexmpp_xml_parse_sax_end_elem_ns,
+ (rexmpp_xml_parser_characters)rexmpp_xml_parse_sax_characters
+};
+
+rexmpp_xml_t *rexmpp_xml_parse (const char *str, int str_len) {
+ struct rexmpp_xml_builder builder = { NULL, NULL };
+ rexmpp_xml_parser_ctx_t parser =
+ rexmpp_xml_parser_new(&builder_sax, &builder);
+ rexmpp_xml_parser_feed(parser, str, str_len, 1);
+ rexmpp_xml_parser_free(parser);
+ if (builder.current != NULL) {
+ /* The parsing is not complete. */
+ rexmpp_xml_free(builder.root);
+ return NULL;
+ }
+ return builder.root;
+}
+
+rexmpp_xml_t *rexmpp_xml_read_fd (int fd) {
+ struct rexmpp_xml_builder builder = { NULL, NULL };
+ rexmpp_xml_parser_ctx_t parser =
+ rexmpp_xml_parser_new(&builder_sax, &builder);
+ if (parser == NULL) {
+ return NULL;
+ }
+
+ char buf[4096];
+ ssize_t buf_len = 0;
+ do {
+ buf_len = read(fd, buf, 4096);
+ if (buf_len > 0) {
+ rexmpp_xml_parser_feed(parser, buf, buf_len, 0);
+ }
+ } while (buf_len > 0 &&
+ ! (builder.root != NULL && builder.current == NULL) );
+
+ rexmpp_xml_parser_free(parser);
+
+ if (builder.current != NULL) {
+ /* The parsing is not complete. */
+ rexmpp_xml_free(builder.root);
+ return NULL;
+ }
+
+ return builder.root;
+}
+
+rexmpp_xml_t *rexmpp_xml_read_file (const char *path) {
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ return NULL;
+ }
+ rexmpp_xml_t *node = rexmpp_xml_read_fd(fd);
+ close(fd);
+ return node;
+}
+
+#ifndef USE_RUST
+int rexmpp_xml_write_file (const char *path, rexmpp_xml_t* node) {
+ FILE *fd = fopen(path, "w");
+ if (fd == NULL) {
+ return -1;
+ }
+ char *serialized = rexmpp_xml_serialize(node, 1);
+ fputs("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", fd);
+ fputs(serialized, fd);
+ fclose(fd);
+ return 0;
+}
+
+unsigned int rexmpp_xml_siblings_count (rexmpp_xml_t *node) {
+ unsigned int i = 0;
+ for (i = 0; node != NULL; i++) {
+ node = node->next;
+ }
+ return i;
+}
+
+int rexmpp_xml_match (rexmpp_xml_t *node,
+ const char *namespace,
+ const char *name)
+{
+ if (node == NULL) {
+ return 0;
+ }
+ if (node->type != REXMPP_XML_ELEMENT) {
+ return 0;
+ }
+ if (name != NULL) {
+ if (strcmp(name, node->alt.elem.qname.name) != 0) {
+ return 0;
+ }
+ }
+ if (namespace != NULL) {
+ if (node->alt.elem.qname.namespace == NULL &&
+ strcmp(namespace, "jabber:client") != 0) {
+ return 0;
+ } else if (node->alt.elem.qname.namespace != NULL) {
+ if (strcmp(namespace, node->alt.elem.qname.namespace) != 0) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+int rexmpp_xml_attr_match (rexmpp_xml_attr_t *attr,
+ const char *namespace,
+ const char *name)
+{
+ if (attr == NULL) {
+ return 0;
+ }
+ if (name != NULL) {
+ if (strcmp(name, attr->qname.name) != 0) {
+ return 0;
+ }
+ }
+ if (namespace != NULL) {
+ if (attr->qname.namespace == NULL &&
+ strcmp(namespace, "jabber:client") != 0) {
+ return 0;
+ } else if (strcmp(namespace, attr->qname.namespace) != 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int rexmpp_xml_is_stanza (rexmpp_xml_t *node) {
+ return rexmpp_xml_match(node, "jabber:client", "message") ||
+ rexmpp_xml_match(node, "jabber:client", "iq") ||
+ rexmpp_xml_match(node, "jabber:client", "presence");
+}
+
+rexmpp_xml_t *rexmpp_xml_error (const char *type, const char *condition) {
+ rexmpp_xml_t * error = rexmpp_xml_new_elem("error", NULL);
+ rexmpp_xml_add_attr(error, "type", type);
+ rexmpp_xml_t * cond =
+ rexmpp_xml_new_elem(condition, "urn:ietf:params:xml:ns:xmpp-stanzas");
+ rexmpp_xml_add_child(error, cond);
+ return error;
+}
+
+rexmpp_xml_attr_t *
+rexmpp_xml_find_attr (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace)
+{
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return NULL;
+ }
+ rexmpp_xml_attr_t *attr;
+ for (attr = node->alt.elem.attributes; attr != NULL; attr = attr->next) {
+ if (rexmpp_xml_attr_match(attr, namespace, name)) {
+ return attr;
+ }
+ }
+ return NULL;
+}
+
+const char *rexmpp_xml_find_attr_val_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace) {
+ rexmpp_xml_attr_t *attr = rexmpp_xml_find_attr(node, name, namespace);
+ if (attr != NULL) {
+ return attr->value;
+ }
+ return NULL;
+}
+
+const char *rexmpp_xml_find_attr_val (rexmpp_xml_t *node,
+ const char *name) {
+ return rexmpp_xml_find_attr_val_ns(node, name, NULL);
+}
+
+rexmpp_xml_t *
+rexmpp_xml_find_child (rexmpp_xml_t *node,
+ const char *namespace,
+ const char *name)
+{
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return NULL;
+ }
+ rexmpp_xml_t *child;
+ for (child = node->alt.elem.children; child != NULL; child = child->next) {
+ if (rexmpp_xml_match(child, namespace, name)) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2) {
+ /* Just serialize and compare strings for now: awkward, but
+ simple. */
+ char *n1str = rexmpp_xml_serialize(n1, 0);
+ char *n2str = rexmpp_xml_serialize(n2, 0);
+ int eq = (strcmp(n1str, n2str) == 0);
+ free(n1str);
+ free(n2str);
+ return eq;
+}
+
+rexmpp_xml_t *rexmpp_xml_children (const rexmpp_xml_t *node) {
+ if (node != NULL && node->type == REXMPP_XML_ELEMENT) {
+ return node->alt.elem.children;
+ }
+ return NULL;
+}
+
+rexmpp_xml_t *rexmpp_xml_first_elem_child (rexmpp_xml_t *node) {
+ rexmpp_xml_t *child;
+ for (child = rexmpp_xml_children(node); child != NULL; child = child->next) {
+ if (child->type == REXMPP_XML_ELEMENT) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+rexmpp_xml_t *rexmpp_xml_next_elem_sibling (rexmpp_xml_t *node) {
+ if (node == NULL) {
+ return NULL;
+ }
+ rexmpp_xml_t *sibling;
+ for (sibling = node->next; sibling != NULL; sibling = sibling->next) {
+ if (sibling->type == REXMPP_XML_ELEMENT) {
+ return sibling;
+ }
+ }
+ return NULL;
+}
+
+char *rexmpp_xml_text (rexmpp_xml_t *node) {
+ if (node != NULL && node->type == REXMPP_XML_TEXT) {
+ return node->alt.text;
+ }
+ return NULL;
+}
+
+char *rexmpp_xml_text_child (rexmpp_xml_t *node) {
+ return rexmpp_xml_text(rexmpp_xml_children(node));
+}
+
+rexmpp_xml_t *rexmpp_xml_reverse_list (rexmpp_xml_t *node) {
+ rexmpp_xml_t *next, *prev = NULL;
+ while (node != NULL) {
+ next = node->next;
+ node->next = prev;
+ prev = node;
+ node = next;
+ }
+ return prev;
+}
+
+void rexmpp_xml_reverse_children (rexmpp_xml_t *node) {
+ if (node == NULL || node->type != REXMPP_XML_ELEMENT) {
+ return;
+ }
+ node->alt.elem.children = rexmpp_xml_reverse_list(node->alt.elem.children);
+ rexmpp_xml_t *cur;
+ for (cur = node->alt.elem.children; cur != NULL; cur = cur->next) {
+ if (cur->type == REXMPP_XML_ELEMENT && cur->alt.elem.children != NULL) {
+ rexmpp_xml_reverse_children(cur);
+ }
+ }
+}
+
+#endif
diff --git a/src/rexmpp_xml.h b/src/rexmpp_xml.h
new file mode 100644
index 0000000..38142ae
--- /dev/null
+++ b/src/rexmpp_xml.h
@@ -0,0 +1,269 @@
+/**
+ @file rexmpp_xml.h
+ @brief XML structures and functions for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#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;
+
+struct rexmpp_xml_qname {
+ char *name;
+ char *namespace;
+};
+
+struct rexmpp_xml_attribute {
+ rexmpp_xml_qname_t qname;
+ char *value;
+ rexmpp_xml_attr_t *next;
+};
+
+enum rexmpp_xml_node_type {
+ REXMPP_XML_ELEMENT,
+ REXMPP_XML_TEXT
+};
+
+typedef enum rexmpp_xml_node_type rexmpp_xml_node_type_t;
+
+struct rexmpp_xml_node {
+ rexmpp_xml_node_type_t type;
+ union {
+ struct {
+ rexmpp_xml_qname_t qname;
+ rexmpp_xml_attr_t *attributes;
+ rexmpp_xml_t *children;
+ } elem;
+ char *text;
+ } alt;
+ rexmpp_xml_t *next;
+};
+
+struct rexmpp_xml_builder {
+ rexmpp_xml_t *current;
+ rexmpp_xml_t *root;
+};
+
+void rexmpp_xml_qname_free (rexmpp_xml_qname_t *qname);
+void rexmpp_xml_attribute_free (rexmpp_xml_attr_t *attr);
+void rexmpp_xml_attribute_free_list (rexmpp_xml_attr_t *attr);
+
+/**
+ @brief Frees a single XML node. Does not free its siblings.
+*/
+void rexmpp_xml_free (rexmpp_xml_t *node);
+
+/**
+ @brief Frees an XML node and its siblings.
+*/
+void rexmpp_xml_free_list (rexmpp_xml_t *node);
+
+/**
+ @brief Clones a single XML node, without its siblings.
+*/
+rexmpp_xml_t *rexmpp_xml_clone (rexmpp_xml_t *node);
+
+/**
+ @brief Clones an XML node, together with its siblings.
+*/
+rexmpp_xml_t *rexmpp_xml_clone_list (rexmpp_xml_t *node);
+
+/**
+ @brief Creates a textual ::rexmpp_xml_t XML node (with type =
+ ::REXMPP_XML_TEXT).
+*/
+rexmpp_xml_t *rexmpp_xml_new_text (const char *str);
+
+/**
+ @brief Creates a textual ::rexmpp_xml_t XML node (with type =
+ ::REXMPP_XML_TEXT).
+*/
+rexmpp_xml_t *rexmpp_xml_new_text_len (const char *str, size_t len);
+
+/**
+ @brief Creates an element ::rexmpp_xml_t XML node (with type =
+ ::REXMPP_XML_ELEMENT).
+*/
+rexmpp_xml_t *rexmpp_xml_new_elem (const char *name,
+ const char *namespace);
+
+/**
+ @brief Adds a child node.
+*/
+void rexmpp_xml_add_child (rexmpp_xml_t *node,
+ rexmpp_xml_t *child);
+
+/**
+ @brief Creates a text node, and adds it as a child.
+*/
+int rexmpp_xml_add_text (rexmpp_xml_t *node,
+ const char *str);
+
+/**
+ @brief Creates a text node, and adds it as a child.
+*/
+int rexmpp_xml_add_text_len (rexmpp_xml_t *node,
+ const char *str,
+ size_t len);
+
+rexmpp_xml_attr_t *rexmpp_xml_attr_new (const char *name,
+ const char *namespace,
+ const char *value);
+
+int rexmpp_xml_add_attr (rexmpp_xml_t *node,
+ const char *name,
+ const char *value);
+
+int rexmpp_xml_remove_attr_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace);
+
+int rexmpp_xml_remove_attr (rexmpp_xml_t *node,
+ const char *name);
+
+int rexmpp_xml_add_attr_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace,
+ const char *value);
+
+/**
+ @brief Adds an "id" attribute to an XML stanza.
+ @param[in,out] s ::rexmpp
+ @param[in] node A pointer to an XML stanza.
+ @returns The same pointer as on input, for more convenient
+ composition.
+*/
+rexmpp_xml_t *
+rexmpp_xml_add_id (rexmpp_xml_t *node);
+
+/**
+ @brief A helper function for XML serialisation.
+ @param[in] node An XML node.
+ @returns A string (must be freed by the caller).
+*/
+char *rexmpp_xml_serialize (const rexmpp_xml_t *node, int pretty);
+
+/**
+ @brief Count the number of siblings after a given node.
+*/
+unsigned int rexmpp_xml_siblings_count (rexmpp_xml_t *node);
+
+/**
+ @brief Compares the node's name and namespace to given ones.
+*/
+int rexmpp_xml_match (rexmpp_xml_t *node,
+ const char *namespace,
+ const char *name);
+
+int rexmpp_xml_is_stanza (rexmpp_xml_t *node);
+
+/**
+ @brief Compose an 'error' element.
+*/
+rexmpp_xml_t *rexmpp_xml_error (const char *type, const char *condition);
+
+/**
+ @brief Matches an XML node against a namespace and an element name.
+ @param[in] node An XML node to match.
+ @param[in] namespace An XML namespace. Can be NULL (matches
+ anything), and it is assumed that the default namespace is
+ "jabber:client" (so if it is "jabber:client" and an element doesn't
+ have a namespace defined, this function counts that as a match).
+ @param[in] name Element name. Can be NULL (matches anything).
+ @returns 1 on a successful match, 0 otherwise.
+*/
+int rexmpp_xml_attr_match (rexmpp_xml_attr_t *attr,
+ const char *namespace,
+ const char *name);
+
+rexmpp_xml_attr_t *rexmpp_xml_find_attr (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace);
+
+const char *rexmpp_xml_find_attr_val_ns (rexmpp_xml_t *node,
+ const char *name,
+ const char *namespace);
+
+const char *rexmpp_xml_find_attr_val (rexmpp_xml_t *node,
+ const char *name);
+
+/**
+ @brief Finds a child element of an XML node, which matches the
+ given namespace and name.
+ @param[in] node The node containing child nodes.
+ @param[in] namespace The namespace to look for.
+ @param[in] name The element name to look for.
+ @returns A pointer to the first matching child node, or NULL if no
+ matching child elements found.
+*/
+rexmpp_xml_t *rexmpp_xml_find_child (rexmpp_xml_t *node,
+ const char *namespace,
+ const char *name);
+
+rexmpp_xml_t *rexmpp_xml_children (const rexmpp_xml_t *node);
+
+char *rexmpp_xml_text (rexmpp_xml_t *node);
+
+char *rexmpp_xml_text_child (rexmpp_xml_t *node);
+
+rexmpp_xml_t *rexmpp_xml_first_elem_child (rexmpp_xml_t *node);
+
+rexmpp_xml_t *rexmpp_xml_next_elem_sibling (rexmpp_xml_t *node);
+
+/**
+ @brief Compares two XML elements.
+*/
+int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2);
+
+/**
+ @brief A helper function for XML parsing.
+ @param[in] str A string to parse.
+ @param[in] str_len String length.
+ @returns Parsed XML, or NULL on failure.
+*/
+rexmpp_xml_t *rexmpp_xml_parse (const char *str, int str_len);
+
+/**
+ @brief Reads XML from a file stream, reading the stream line by
+ line.
+ @param[in] fd A file descriptor
+ @returns Parsed XML, or NULL on failure.
+*/
+rexmpp_xml_t *rexmpp_xml_read_fd (int fd);
+
+/**
+ @brief Reads XML from a file
+ @param[in] path A file path
+ @returns Parsed XML, or NULL on failure.
+*/
+rexmpp_xml_t *rexmpp_xml_read_file (const char *path);
+
+/**
+ @brief Writes XML into a file
+ @param[in] path A file path
+ @param[in] node XML to write
+ @returns 0 on success, -1 on failure.
+*/
+int rexmpp_xml_write_file (const char *path, rexmpp_xml_t* node);
+
+/**
+ @brief Reverses a linked list of XML nodes
+ @param[in,out] node The head of the list to reverse
+ @returns The new head of the list
+*/
+rexmpp_xml_t *rexmpp_xml_reverse_list (rexmpp_xml_t *node);
+
+/**
+ @brief Recursively reverses children of an XML element
+ @param[in,out] node The root XML element
+*/
+void rexmpp_xml_reverse_children (rexmpp_xml_t *node);
+
+#endif
diff --git a/src/rexmpp_xml.rs b/src/rexmpp_xml.rs
new file mode 100644
index 0000000..f0d292a
--- /dev/null
+++ b/src/rexmpp_xml.rs
@@ -0,0 +1,1040 @@
+extern crate libc;
+use libc::{strdup, strndup, free, strcmp};
+use std::os::raw::{c_char, c_int, c_void, c_uint};
+use std::ptr;
+use std::ffi::{CStr, CString};
+use std::clone::Clone;
+use std::fs::File;
+use std::io::Write;
+
+use super::{rexmpp};
+use super::{rexmpp_random};
+
+// extern {
+// fn rexmpp_xml_parse (str: *mut c_char, str_len: c_int) -> *mut RexmppXML;
+// }
+
+#[repr(C)]
+pub struct RexmppXMLQName {
+ pub name: *mut c_char,
+ pub namespace: *mut c_char
+}
+
+impl Copy for RexmppXMLQName { }
+
+impl Clone for RexmppXMLQName {
+ fn clone(&self) -> RexmppXMLQName {
+ RexmppXMLQName {
+ name: unsafe { strdup(self.name) },
+ namespace: if self.namespace != ptr::null_mut() {
+ unsafe { strdup(self.namespace) }
+ } else {
+ ptr::null_mut()
+ }
+ }
+ }
+}
+
+#[repr(C)]
+pub struct RexmppXMLAttribute {
+ pub qname: RexmppXMLQName,
+ pub value: *mut c_char,
+ pub next: *mut RexmppXMLAttribute
+}
+
+impl Copy for RexmppXMLAttribute { }
+
+impl Clone for RexmppXMLAttribute {
+ fn clone(&self) -> RexmppXMLAttribute {
+ RexmppXMLAttribute {
+ qname: Clone::clone(&self.qname),
+ value: unsafe { strdup(self.value) },
+ next: ptr::null_mut()
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+#[derive(PartialEq)]
+#[repr(C)]
+pub enum NodeType {
+ Element,
+ Text
+}
+
+#[repr(C)]
+pub struct RexmppXMLAltElem {
+ pub qname: RexmppXMLQName,
+ pub attributes: *mut RexmppXMLAttribute,
+ pub children: *mut RexmppXML
+}
+
+impl Copy for RexmppXMLAltElem { }
+
+impl Clone for RexmppXMLAltElem {
+ fn clone(&self) -> RexmppXMLAltElem {
+ unsafe {
+ let mut ret = RexmppXMLAltElem {
+ qname: Clone::clone(&self.qname),
+ attributes: ptr::null_mut(),
+ children: ptr::null_mut()
+ };
+ let mut old_attr_ptr = self.attributes;
+ let mut next_attr_ptr_ptr : *mut *mut RexmppXMLAttribute = &mut ret.attributes;
+ loop {
+ match old_attr_ptr.as_mut() {
+ None => break,
+ Some(old_attr) => {
+ let new_attr_ptr = rexmpp_xml_attr_new(old_attr.qname.name,
+ old_attr.qname.namespace,
+ old_attr.value);
+ next_attr_ptr_ptr.write(new_attr_ptr);
+ next_attr_ptr_ptr = &mut ((*new_attr_ptr).next);
+ old_attr_ptr = old_attr.next;
+ }
+ }
+ }
+ ret.children = rexmpp_xml_clone_list(self.children);
+ return ret;
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub enum RexmppXMLAlt {
+ Elem(RexmppXMLAltElem),
+ Text(*mut c_char)
+}
+
+#[repr(C)]
+pub struct RexmppXML {
+ pub alt: RexmppXMLAlt,
+ pub next: *mut RexmppXML
+}
+
+impl Copy for RexmppXML { }
+
+impl Clone for RexmppXML {
+ fn clone(&self) -> RexmppXML {
+ RexmppXML {
+ alt: match self.alt {
+ RexmppXMLAlt::Text(text) =>
+ RexmppXMLAlt::Text(unsafe { strdup(text) }),
+ RexmppXMLAlt::Elem(e) =>
+ RexmppXMLAlt::Elem(Clone::clone(& e))
+ },
+ next: ptr::null_mut()
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_qname_free (qname_ptr: *mut RexmppXMLQName) {
+ match unsafe { qname_ptr.as_mut() } {
+ None => return,
+ Some(qname) => {
+ if qname.name != ptr::null_mut() {
+ unsafe { free(qname.name as *mut c_void) };
+ qname.name = ptr::null_mut();
+ }
+ if qname.namespace != ptr::null_mut() {
+ unsafe { free(qname.namespace as *mut c_void) };
+ qname.namespace = ptr::null_mut();
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_attribute_free (attr_ptr: *mut RexmppXMLAttribute) {
+ if attr_ptr == ptr::null_mut() {
+ return;
+ }
+ let mut attr : RexmppXMLAttribute = unsafe { *Box::from_raw(attr_ptr) };
+ rexmpp_xml_qname_free(&mut (attr.qname));
+ if attr.value != ptr::null_mut() {
+ unsafe { free(attr.value as *mut c_void) }
+ attr.value = ptr::null_mut();
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_attribute_free_list (mut attr_ptr: *mut RexmppXMLAttribute) {
+ let mut next;
+ while attr_ptr != ptr::null_mut() {
+ next = unsafe { (*attr_ptr).next };
+ rexmpp_xml_attribute_free(attr_ptr);
+ attr_ptr = next;
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_free (node_ptr: *mut RexmppXML) {
+ if node_ptr == ptr::null_mut() {
+ return;
+ }
+ let mut node : RexmppXML = unsafe { *Box::from_raw(node_ptr) };
+ unsafe {
+ match node.alt {
+ RexmppXMLAlt::Text(mut t) => {
+ free(t as *mut c_void);
+ t = ptr::null_mut();
+ },
+ RexmppXMLAlt::Elem(mut element) => {
+ rexmpp_xml_qname_free(&mut (element.qname));
+ rexmpp_xml_attribute_free_list(element.attributes);
+ rexmpp_xml_free_list(element.children);
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_free_list (mut node_ptr: *mut RexmppXML) {
+ let mut next;
+ while node_ptr != ptr::null_mut() {
+ next = unsafe { (*node_ptr).next };
+ rexmpp_xml_free(node_ptr);
+ node_ptr = next;
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_clone (node_ptr: *mut RexmppXML) -> *mut RexmppXML {
+ if node_ptr == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ return Box::into_raw(Box::new(Clone::clone(& unsafe { *node_ptr })));
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_clone_list (mut node_ptr: *mut RexmppXML) -> *mut RexmppXML {
+ if node_ptr == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ let first_ptr = rexmpp_xml_clone(node_ptr);
+ let mut last_ptr = first_ptr;
+ node_ptr = unsafe { (*node_ptr).next };
+ while node_ptr != ptr::null_mut() {
+ unsafe { (*last_ptr).next = rexmpp_xml_clone(node_ptr) };
+ last_ptr = unsafe { (*last_ptr).next };
+ node_ptr = unsafe { (*node_ptr).next };
+ }
+ return first_ptr;
+}
+
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_new_text (str: *const c_char) -> *mut RexmppXML {
+ let node = RexmppXML {
+ alt: RexmppXMLAlt::Text( unsafe { strdup(str) } ),
+ next: ptr::null_mut()
+ };
+ let b = Box::new(node);
+ return Box::into_raw(b);
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_new_text_len (str: *const c_char, len: usize) -> *mut RexmppXML {
+ let node = RexmppXML {
+ alt: RexmppXMLAlt::Text( unsafe { strndup(str, len) } ),
+ next: ptr::null_mut()
+ };
+ let b = Box::new(node);
+ return Box::into_raw(b);
+}
+
+#[no_mangle]
+pub extern "C" fn rexmpp_xml_add_child (node: *mut RexmppXML,
+ child: *mut RexmppXML) -> () {
+ // It is important to wrap everything in "unsafe" here; somehow
+ // the enum fields are not mutated otherwise.
+ unsafe {
+ match (*node).alt {
+ RexmppXMLAlt::Elem(ref mut elem) => {
+ let mut last_ptr : &mut *mut RexmppXML =
+ &mut ((*elem).children);
+ while *last_ptr != ptr::null_mut() {
+ last_ptr = &mut ((*(* last_ptr)).next);
+ }
+ *last_ptr = child;
+ },
+ _ => ()
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn rexmpp_xml_add_text (node: *mut RexmppXML,
+ str: *const c_char) -> c_int {
+ let text_node : *mut RexmppXML = rexmpp_xml_new_text(str);
+ if text_node != ptr::null_mut() {
+ rexmpp_xml_add_child(node, text_node);
+ return 1;
+ }
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rexmpp_xml_add_text_len (node: *mut RexmppXML,
+ str: *const c_char,
+ len: usize) -> c_int {
+ let text_node : *mut RexmppXML = rexmpp_xml_new_text_len(str, len);
+ if text_node != ptr::null_mut() {
+ rexmpp_xml_add_child(node, text_node);
+ return 1;
+ }
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_new_elem (name: *const c_char,
+ namespace: *const c_char) -> *mut RexmppXML {
+ let node = RexmppXML {
+ alt: RexmppXMLAlt::Elem (
+ RexmppXMLAltElem {
+ qname: RexmppXMLQName {
+ name: unsafe { strdup(name) },
+ namespace: if namespace == ptr::null_mut() {
+ ptr::null_mut()
+ } else {
+ unsafe { strdup(namespace) }
+ }
+ },
+ attributes: ptr::null_mut(),
+ children: ptr::null_mut()
+ }
+ ),
+ next: ptr::null_mut()
+ };
+ let b = Box::new(node);
+ return Box::into_raw(b);
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_attr_new (name: *const c_char,
+ namespace: *const c_char,
+ value: *const c_char) -> *mut RexmppXMLAttribute {
+ let node = RexmppXMLAttribute {
+ qname: RexmppXMLQName {
+ name: unsafe { strdup(name) },
+ namespace: if namespace == ptr::null_mut() {
+ ptr::null_mut()
+ } else {
+ unsafe { strdup(namespace) }
+ }
+ },
+ value: unsafe { strdup(value) },
+ next: ptr::null_mut()
+ };
+ return Box::into_raw(Box::new(node));
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_add_attr_ns (node: *mut RexmppXML,
+ name: *const c_char,
+ namespace: *const c_char,
+ value: *const c_char) -> c_int {
+ if node == ptr::null_mut() {
+ return -1;
+ }
+ // Wrapping everything into "unsafe", otherwise enum fields are
+ // not mutated.
+ unsafe {
+ match(*node).alt {
+ RexmppXMLAlt::Elem(ref mut elem) => {
+ let attr = rexmpp_xml_attr_new(name, namespace, value);
+ (*attr).next = (*elem).attributes;
+ (*elem).attributes = attr;
+ 0
+ },
+ _ => -1
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_remove_attr_ns (node: *mut RexmppXML,
+ name: *const c_char,
+ namespace: *const c_char) -> c_int {
+ if node == ptr::null_mut() {
+ return -1;
+ }
+ // Wrapping everything into "unsafe", otherwise enum fields are
+ // not mutated.
+ unsafe {
+ match (*node).alt {
+ RexmppXMLAlt::Elem(ref mut elem) => {
+ let mut attr_ptr_ptr: *mut *mut RexmppXMLAttribute =
+ &mut (*elem).attributes;
+ while *attr_ptr_ptr != ptr::null_mut() {
+ if rexmpp_xml_attr_match(*attr_ptr_ptr,
+ namespace, name) > 0 {
+ let next_attr_ptr : *mut RexmppXMLAttribute =
+ (**attr_ptr_ptr).next;
+ rexmpp_xml_attribute_free(*attr_ptr_ptr);
+ *attr_ptr_ptr = next_attr_ptr;
+ return 0;
+ }
+ attr_ptr_ptr = &mut (**attr_ptr_ptr).next;
+ }
+ 1
+ },
+ _ => -1,
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_add_attr (node: *mut RexmppXML,
+ name: *const c_char,
+ value: *const c_char) -> c_int {
+ rexmpp_xml_add_attr_ns(node, name, ptr::null_mut(), value)
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_remove_attr (node: *mut RexmppXML,
+ name: *const c_char) -> c_int {
+ rexmpp_xml_remove_attr_ns(node, name, ptr::null_mut())
+}
+
+
+fn rexmpp_xml_push_escaped (c: char, s: &mut String) {
+ if c == '<' {
+ s.push_str("&lt;")
+ } else if c == '>' {
+ s.push_str("&gt;")
+ } else if c == '&' {
+ s.push_str("&amp;")
+ } else if c == '\'' {
+ s.push_str("&apos;")
+ } else if c == '"' {
+ s.push_str("&quot;")
+ } else {
+ s.push_str(format!("&#{};", u32::from(c)).as_str());
+ };
+}
+
+fn rexmpp_xml_print_text (c: char, s: &mut String) {
+ if "<&>'\"".chars().any(|sc| sc == c) {
+ rexmpp_xml_push_escaped(c, s);
+ } else {
+ s.push(c);
+ }
+}
+
+fn rexmpp_xml_print_name (i: usize, c: char, s: &mut String) {
+ if c == ':'
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= '\u{C0}' && c <= '\u{D6}')
+ || (c >= '\u{D8}' && c <= '\u{F6}')
+ || (c >= '\u{F8}' && c <= '\u{2FF}')
+ || (c >= '\u{370}' && c <= '\u{37D}')
+ || (c >= '\u{37F}' && c <= '\u{1FFF}')
+ || (c >= '\u{200C}' && c <= '\u{200D}')
+ || (c >= '\u{2070}' && c <= '\u{218F}')
+ || (c >= '\u{2C00}' && c <= '\u{2FEF}')
+ || (c >= '\u{3001}' && c <= '\u{D7FF}')
+ || (c >= '\u{F900}' && c <= '\u{FDCF}')
+ || (c >= '\u{FDF0}' && c <= '\u{FFF0}')
+ || (c >= '\u{10000}' && c <= '\u{EFFFF}')
+ || ((i > 0) &&
+ (c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || c == '\u{B7}'
+ || (c >= '\u{0300}' && c <= '\u{036F}')
+ || (c >= '\u{203F}' && c <= '\u{2040}')))
+ {
+ // Print the allowed characters.
+ s.push(c);
+ }
+}
+
+fn rexmpp_xml_print_indent (indent: i32, s: &mut String) {
+ let mut i = 0;
+ while i < indent {
+ s.push_str(" ");
+ i = i + 1;
+ }
+}
+
+fn rexmpp_xml_print (node_ptr: *const RexmppXML,
+ ret: &mut String,
+ indent: i32)
+ -> ()
+{
+ unsafe {
+ let node : RexmppXML = *node_ptr;
+ match node {
+ RexmppXML { alt : RexmppXMLAlt::Text (text_ptr),
+ next: _} => {
+ let text_cstr : &CStr = CStr::from_ptr(text_ptr);
+ let text_str : String =
+ String::from_utf8_lossy(text_cstr.to_bytes())
+ .to_string();
+ // let mut escaped = String::with_capacity(text_str.capacity());
+ text_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ },
+ RexmppXML { alt : RexmppXMLAlt::Elem (element),
+ next: _} => {
+ // let mut ret = String::with_capacity(1024);
+ let name_cstr : &CStr =
+ CStr::from_ptr(element.qname.name);
+ let name_str : String =
+ String::from_utf8_lossy(name_cstr.to_bytes())
+ .to_string();
+ if indent > 0 {
+ ret.push('\n');
+ rexmpp_xml_print_indent(indent, ret);
+ }
+ ret.push('<');
+ name_str.char_indices().
+ for_each(|(i, c)| rexmpp_xml_print_name(i, c, ret));
+ if element.qname.namespace != ptr::null_mut() {
+ let namespace_cstr : &CStr =
+ CStr::from_ptr(element.qname.namespace);
+ let namespace_str : String =
+ String::from_utf8_lossy(namespace_cstr.to_bytes())
+ .to_string();
+ ret.push_str(" xmlns=\"");
+ namespace_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ ret.push('"');
+ }
+ if element.attributes != ptr::null_mut() {
+ let mut attr_ptr : *mut RexmppXMLAttribute =
+ element.attributes;
+ while attr_ptr != ptr::null_mut() {
+ let attr : RexmppXMLAttribute = *attr_ptr;
+ let attr_name_cstr =
+ CStr::from_ptr(attr.qname.name);
+ let attr_name_str =
+ String::from_utf8_lossy(attr_name_cstr.to_bytes())
+ .to_string();
+ // TODO: handle attribute namespaces someday.
+ let attr_value_cstr =
+ CStr::from_ptr(attr.value);
+ let attr_value_str =
+ String::from_utf8_lossy(attr_value_cstr.to_bytes())
+ .to_string();
+ ret.push(' ');
+ attr_name_str.char_indices().
+ for_each(|(i, c)|
+ rexmpp_xml_print_name(i, c, ret));
+ ret.push_str("=\"");
+ attr_value_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ ret.push('"');
+ attr_ptr = (*attr_ptr).next;
+ }
+ }
+ if element.children == ptr::null_mut() {
+ ret.push_str("/>");
+ } else {
+ ret.push('>');
+ let mut child = rexmpp_xml_children(node_ptr);
+ let mut last_child_is_textual = false;
+ while child != ptr::null_mut() {
+ rexmpp_xml_print(child, ret,
+ if indent > -1 { indent + 1 }
+ else { -1 } );
+ last_child_is_textual =
+ matches!((*child).alt, RexmppXMLAlt::Text(_));
+ child = (*child).next;
+ }
+ if indent > 0 && ! last_child_is_textual {
+ ret.push('\n');
+ rexmpp_xml_print_indent(indent, ret);
+ }
+ ret.push_str("</");
+ name_str.char_indices().
+ for_each(|(i, c)|
+ rexmpp_xml_print_name(i, c, ret));
+ ret.push('>');
+ }
+ }
+ }
+ }
+}
+
+fn rexmpp_xml_serialize_str (node_ptr: *const RexmppXML,
+ pretty: bool)
+ -> String
+{
+ let mut out = String::with_capacity(4096);
+ rexmpp_xml_print(node_ptr, &mut out, if pretty { 0 } else { -1 });
+ return out;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_serialize (node_ptr: *const RexmppXML,
+ pretty: bool)
+ -> *mut c_char
+{
+ let out = rexmpp_xml_serialize_str(node_ptr, pretty);
+ match CString::new(out) {
+ Ok(cstr) => unsafe { strdup(cstr.as_ptr()) },
+ Err(_) => ptr::null_mut()
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_add_id (node: *mut RexmppXML)
+ -> *mut RexmppXML
+{
+ match CString::new("id") {
+ Err(_) => return ptr::null_mut(),
+ Ok(id_cstr) => {
+ let buf = unsafe { rexmpp_random::rexmpp_random_id() };
+ if buf == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ rexmpp_xml_add_attr(node, id_cstr.as_ptr(), buf);
+ unsafe { free(buf as *mut c_void) };
+ return node;
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_write_file (path: *const c_char,
+ node: *const RexmppXML)
+ -> c_int
+{
+ let path_cstr : &CStr = unsafe { CStr::from_ptr(path) };
+ let path_str : String =
+ String::from_utf8_lossy(path_cstr.to_bytes())
+ .to_string();
+ match File::create(path_str) {
+ Ok(mut fd) => {
+ fd.write_all(b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ fd.write_all(rexmpp_xml_serialize_str(node, false).as_bytes());
+ },
+ Err(_) => { return -1; }
+ }
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_siblings_count (mut node: *const RexmppXML) -> c_uint {
+ let mut i : c_uint = 0;
+ while node != ptr::null() {
+ node = unsafe { (*node).next };
+ i = i + 1;
+ }
+ return i;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_match (node: *const RexmppXML,
+ namespace: *const c_char,
+ name: *const c_char) -> c_int {
+ if node == ptr::null_mut() {
+ return 0;
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Text(_) => return 0,
+ RexmppXMLAlt::Elem(elem) => {
+ if name != ptr::null_mut() {
+ let name_cstr : &CStr = unsafe { CStr::from_ptr(name) };
+ let elem_name_cstr : &CStr =
+ unsafe { CStr::from_ptr(elem.qname.name) };
+ if name_cstr != elem_name_cstr {
+ return 0;
+ }
+ }
+ if namespace != ptr::null_mut() {
+ let namespace_cstr : &CStr = unsafe { CStr::from_ptr(namespace) };
+ if unsafe { elem.qname.namespace } == ptr::null_mut() {
+ match CStr::to_str(namespace_cstr) {
+ Ok(namespace_str) => if namespace_str == "jabber:client" {
+ return 1;
+ },
+ Err(_) => return 0
+ }
+ return 0;
+ }
+ let elem_namespace_cstr : &CStr =
+ unsafe { CStr::from_ptr(elem.qname.namespace) };
+ if namespace_cstr != elem_namespace_cstr {
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_attr_match (attr: *const RexmppXMLAttribute,
+ namespace: *const c_char,
+ name: *const c_char) -> c_int {
+ if attr == ptr::null() {
+ return 0;
+ }
+ if name != ptr::null() {
+ let name_cstr : &CStr = unsafe { CStr::from_ptr(name) };
+ let attr_name_cstr : &CStr = unsafe { CStr::from_ptr((*attr).qname.name) };
+ if name_cstr != attr_name_cstr {
+ return 0;
+ }
+ }
+ if namespace != ptr::null() {
+ let namespace_cstr : &CStr = unsafe { CStr::from_ptr(namespace) };
+ if unsafe { (*attr).qname.namespace } == ptr::null_mut() {
+ match CStr::to_str(namespace_cstr) {
+ Ok(namespace_str) => if namespace_str != "jabber:client" {
+ return 0;
+ },
+ Err(_) => return 0
+ }
+ } else {
+ let attr_namespace_cstr : &CStr =
+ unsafe { CStr::from_ptr((*attr).qname.namespace) };
+ if namespace_cstr != attr_namespace_cstr {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_is_stanza (node: *const RexmppXML) -> c_int {
+ if rexmpp_xml_match(node,
+ CString::new("jabber:client").expect("CString::new failed").as_ptr(),
+ CString::new("message").expect("CString::new failed").as_ptr()) == 1
+ || rexmpp_xml_match(node,
+ CString::new("jabber:client").expect("CString::new failed").as_ptr(),
+ CString::new("iq").expect("CString::new failed").as_ptr()) == 1
+ || rexmpp_xml_match(node,
+ CString::new("jabber:client").expect("CString::new failed").as_ptr(),
+ CString::new("presence").expect("CString::new failed").as_ptr()) == 1
+ {
+ return 1;
+ }
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_error (error_type: *const c_char, condition: *const c_char)
+ -> *mut RexmppXML {
+ let error : *mut RexmppXML =
+ rexmpp_xml_new_elem(CString::new("error")
+ .expect("CString::new failed")
+ .as_ptr(),
+ ptr::null_mut());
+ rexmpp_xml_add_attr(error,
+ CString::new("type")
+ .expect("CString::new failed")
+ .as_ptr(),
+ error_type);
+ let cond =
+ rexmpp_xml_new_elem(condition,
+ CString::new("urn:ietf:params:xml:ns:xmpp-stanzas")
+ .expect("CString::new failed")
+ .as_ptr());
+ rexmpp_xml_add_child(error, cond);
+ return error;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_find_attr (node: *mut RexmppXML,
+ name: *const c_char,
+ namespace: *const c_char)
+ -> *mut RexmppXMLAttribute {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Elem(elem) => {
+ let mut attr_ptr : *mut RexmppXMLAttribute =
+ unsafe { elem.attributes };
+ while attr_ptr != ptr::null_mut() {
+ if rexmpp_xml_attr_match(attr_ptr, namespace, name) > 0 {
+ return attr_ptr;
+ }
+ unsafe { attr_ptr = (*attr_ptr).next };
+ }
+ return ptr::null_mut();
+ },
+ _ => return ptr::null_mut(),
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_find_attr_val_ns (node: *mut RexmppXML,
+ name: *const c_char,
+ namespace: *const c_char)
+ -> *const c_char {
+ let attr : *mut RexmppXMLAttribute =
+ rexmpp_xml_find_attr(node, name, namespace);
+ if attr != ptr::null_mut() {
+ return unsafe { (*attr).value };
+ }
+ return ptr::null_mut();
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_find_attr_val (node: *mut RexmppXML,
+ name: *const c_char)
+ -> *const c_char {
+ rexmpp_xml_find_attr_val_ns(node, name, ptr::null_mut())
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_find_child (node: *mut RexmppXML,
+ namespace: *const c_char,
+ name: *const c_char)
+ -> *mut RexmppXML {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Elem(elem) => {
+ let mut child: *mut RexmppXML = unsafe { elem.children };
+ while child != ptr::null_mut() {
+ if rexmpp_xml_match(child, namespace, name) > 0 {
+ return child;
+ }
+ unsafe { child = (*child).next };
+ }
+ ptr::null_mut()
+ },
+ _ => ptr::null_mut()
+ }
+}
+
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_eq (n1: *const RexmppXML, n2: *const RexmppXML) -> bool {
+ if n1 == n2 {
+ return true;
+ }
+ if n1 == ptr::null_mut() || n1 == ptr::null_mut() {
+ return false;
+ }
+ unsafe {
+ match (*n1, *n2) {
+ (RexmppXML { alt : RexmppXMLAlt::Text (text1),
+ next: _ },
+ RexmppXML { alt : RexmppXMLAlt::Text (text2),
+ next: _ }
+ ) => strcmp(text1, text2) == 0,
+ (RexmppXML
+ { alt : RexmppXMLAlt::Elem
+ ( RexmppXMLAltElem {
+ qname: RexmppXMLQName {
+ name: name1,
+ namespace: namespace1
+ },
+ attributes: attributes1,
+ children: children1
+ } ),
+ next: _},
+ RexmppXML
+ { alt : RexmppXMLAlt::Elem
+ ( RexmppXMLAltElem {
+ qname: RexmppXMLQName {
+ name: name2,
+ namespace: namespace2
+ },
+ attributes: attributes2,
+ children: children2
+ } ),
+ next: _}
+ ) => {
+ // Compare names
+ if strcmp(name1, name2) != 0
+ { return false; }
+ // Compare namespaces
+ if namespace1 != namespace2 &&
+ (namespace1 == ptr::null_mut() ||
+ namespace2 == ptr::null_mut() ||
+ strcmp(namespace1, namespace2) != 0)
+ { return false; }
+ // Compare attributes
+ let mut attr1 = attributes1;
+ let mut attr2 = attributes2;
+ while ! (attr1 == ptr::null_mut() && attr2 == ptr::null_mut()) {
+ if attr1 == ptr::null_mut() {
+ return false;
+ }
+ if attr2 == ptr::null_mut() {
+ return false;
+ }
+ if strcmp((*attr1).qname.name, (*attr2).qname.name) != 0 {
+ return false;
+ }
+ if strcmp((*attr1).value, (*attr2).value) != 0 {
+ return false;
+ }
+ attr1 = (*attr1).next;
+ attr2 = (*attr2).next;
+ }
+ // Compare children
+ let mut child1 = children1;
+ let mut child2 = children2;
+ while ! (child1 == ptr::null_mut() && child2 == ptr::null_mut())
+ {
+ if child1 == ptr::null_mut() {
+ return false;
+ }
+ if child2 == ptr::null_mut() {
+ return false;
+ }
+ if ! rexmpp_xml_eq(child1, child2) {
+ return false;
+ }
+ child1 = (*child1).next;
+ child2 = (*child2).next;
+ }
+ true
+ }
+ _ => false
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_children (node: *const RexmppXML)
+ -> *mut RexmppXML {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Elem(elem) => elem.children,
+ _ => ptr::null_mut()
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_first_elem_child (node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ let mut child: *mut RexmppXML = rexmpp_xml_children(node);
+ while child != ptr::null_mut() {
+ if matches!(unsafe { (*child).alt }, RexmppXMLAlt::Elem(_)) {
+ return child;
+ }
+ unsafe { child = (*child).next };
+ }
+ return ptr::null_mut();
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_next_elem_sibling (node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ let mut sibling: *mut RexmppXML = unsafe { (*node).next };
+ while sibling != ptr::null_mut() {
+ if matches!(unsafe { (*sibling).alt }, RexmppXMLAlt::Elem(_)) {
+ return sibling;
+ }
+ unsafe { sibling = (*sibling).next };
+ }
+ return ptr::null_mut();
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_text (node: *mut RexmppXML)
+ -> *mut c_char {
+ if node == ptr::null_mut() {
+ return ptr::null_mut();
+ }
+ match unsafe { (*node).alt } {
+ RexmppXMLAlt::Text(text) => text,
+ _ => ptr::null_mut()
+ }
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_text_child (node: *mut RexmppXML)
+ -> *mut c_char {
+ rexmpp_xml_text(rexmpp_xml_children(node))
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_reverse_list (mut node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ let mut next;
+ let mut prev = ptr::null_mut();
+ while node != ptr::null_mut() {
+ unsafe {
+ next = (*node).next;
+ (*node).next = prev;
+ prev = node;
+ node = next;
+ }
+ }
+ return prev;
+}
+
+#[no_mangle]
+pub extern "C"
+fn rexmpp_xml_reverse_children (node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ unsafe {
+ if node == ptr::null_mut() {
+ return node;
+ }
+ match (*node).alt {
+ RexmppXMLAlt::Elem(ref mut elem) => {
+ (*elem).children = rexmpp_xml_reverse_list((*elem).children);
+ let mut cur = node;
+ while cur != ptr::null_mut() {
+ match (*cur).alt {
+ RexmppXMLAlt::Elem(ref mut cur_elem) => {
+ (*cur_elem).children =
+ rexmpp_xml_reverse_children((*cur_elem).children);
+ },
+ _ => ()
+ }
+ cur = (*cur).next;
+ }
+ },
+ _ => ()
+ }
+ }
+ return node;
+}
diff --git a/src/rexmpp_xml_parser.c b/src/rexmpp_xml_parser.c
new file mode 100644
index 0000000..3f485d7
--- /dev/null
+++ b/src/rexmpp_xml_parser.c
@@ -0,0 +1,323 @@
+/**
+ @file rexmpp_xml_parser.c
+ @brief XML parsing for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#include <string.h>
+
+#include "rexmpp.h"
+#include "rexmpp_xml.h"
+#include "rexmpp_xml_parser.h"
+#include "config.h"
+
+#if defined(USE_LIBXML2)
+
+void rexmpp_xml_sax_characters (rexmpp_xml_parser_ctx_t ctx,
+ const char *ch,
+ int len)
+{
+ ctx->handlers->text(ctx->user_data, ch, len);
+}
+
+void rexmpp_xml_sax_elem_start (rexmpp_xml_parser_ctx_t ctx,
+ const char *localname,
+ const char *prefix,
+ const char *URI,
+ int nb_namespaces,
+ const char **namespaces,
+ int nb_attributes,
+ int nb_defaulted,
+ const char **attributes)
+{
+ (void)prefix;
+ (void)nb_namespaces;
+ (void)namespaces;
+ (void)nb_defaulted;
+ rexmpp_xml_attr_t *attrs = NULL;
+ int i;
+ 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);
+ 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);
+}
+
+void rexmpp_xml_sax_elem_end (rexmpp_xml_parser_ctx_t ctx,
+ const char *localname,
+ const char *prefix,
+ const char *URI)
+{
+ (void)localname;
+ (void)prefix;
+ (void)URI;
+ ctx->handlers->elem_end(ctx->user_data);
+}
+
+xmlSAXHandler rexmpp_xml_parser_sax = {
+ .initialized = XML_SAX2_MAGIC,
+ .characters = (charactersSAXFunc)rexmpp_xml_sax_characters,
+ .startElementNs = (startElementNsSAX2Func)rexmpp_xml_sax_elem_start,
+ .endElementNs = (endElementNsSAX2Func)rexmpp_xml_sax_elem_end,
+};
+
+
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+
+/* rexmpp_xml_t *to = NULL; */
+/* if (from->type == XML_ELEMENT_NODE) { */
+/* to = malloc(sizeof(rexmpp_xml_t)); */
+
+/* /\* Type *\/ */
+/* to->type = REXMPP_XML_ELEMENT; */
+
+/* /\* Name and namespace *\/ */
+/* to->alt.elem.qname.name = strdup(from->name); */
+/* if (from->nsDef != NULL && from->nsDef->href != NULL) { */
+/* to->alt.elem.qname.namespace = strdup(from->nsDef->href); */
+/* } else { */
+/* to->alt.elem.qname.namespace = NULL; */
+/* } */
+
+/* /\* Attributes *\/ */
+/* to->alt.elem.attributes = NULL; */
+/* struct _xmlAttr *from_attr; */
+/* rexmpp_xml_attr_t **to_next_attr = &(to->alt.elem.attributes); */
+/* for (from_attr = from->properties; */
+/* from_attr != NULL; */
+/* from_attr = from_attr->next) */
+/* { */
+/* rexmpp_xml_attr_t *to_attr = */
+/* malloc(sizeof(rexmpp_xml_attr_t)); */
+/* to_attr->qname.name = strdup(from_attr->name); */
+/* to_attr->qname.namespace = NULL; */
+/* if (from_attr->ns != NULL && from_attr->ns->href != NULL) { */
+/* to_attr->qname.namespace = strdup(from_attr->ns->href); */
+/* to_attr->value = */
+/* xmlGetNsProp(from, to_attr->qname.name, to_attr->qname.namespace); */
+/* } else { */
+/* to_attr->value = xmlGetProp(from, to_attr->qname.name); */
+/* } */
+/* to_attr->next = NULL; */
+
+/* *to_next_attr = to_attr; */
+/* to_next_attr = &(to_attr->next); */
+/* } */
+
+/* /\* Children *\/ */
+/* to->alt.elem.children = NULL; */
+/* xmlNodePtr from_child; */
+/* rexmpp_xml_t **to_next_child = &(to->alt.elem.children); */
+/* for (from_child = from->children; */
+/* from_child != NULL; */
+/* from_child = from_child->next) */
+/* { */
+/* rexmpp_xml_t *next_child = rexmpp_xml_from_libxml2(from_child); */
+/* if (next_child != NULL) { */
+/* *to_next_child = next_child; */
+/* to_next_child = &(next_child->next); */
+/* } */
+/* } */
+
+/* /\* Next *\/ */
+/* to->next = NULL; */
+
+/* } else if (from->type == XML_TEXT_NODE) { */
+/* to = malloc(sizeof(rexmpp_xml_t)); */
+/* to->type = REXMPP_XML_TEXT; */
+/* to->alt.text = xmlNodeGetContent(from); */
+/* to->next = NULL; */
+/* } */
+/* return to; */
+/* } */
+
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+/* rexmpp_xml_t *to = rexmpp_xml_from_libxml2(from); */
+/* if (from->next != NULL) { */
+/* to->next = rexmpp_xml_from_libxml2_list(from->next); */
+/* } */
+/* return to; */
+/* } */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from) { */
+/* if (from == NULL) { */
+/* return NULL; */
+/* } */
+
+/* if (from->type == REXMPP_XML_TEXT) { */
+/* xmlNodePtr to = xmlNewText(from->alt.text); */
+/* to->next = rexmpp_xml_to_libxml2(from->next); */
+/* return to; */
+/* } */
+
+/* /\* Name and namespace *\/ */
+/* xmlNodePtr to = xmlNewNode(NULL, from->alt.elem.qname.name); */
+/* if (from->alt.elem.qname.namespace != NULL) { */
+/* xmlNewNs(to, from->alt.elem.qname.namespace, NULL); */
+/* } */
+
+/* /\* Attributes *\/ */
+/* rexmpp_xml_attr_t *attr = from->alt.elem.attributes; */
+/* while (attr != NULL) { */
+/* /\* TODO: Would be nice to take namespaces into account, though */
+/* they are currently not used for attributes. *\/ */
+/* xmlNewProp(to, attr->qname.name, attr->value); */
+/* attr = attr->next; */
+/* } */
+
+/* /\* Children *\/ */
+/* rexmpp_xml_t *child = from->alt.elem.children; */
+/* while (child != NULL) { */
+/* xmlAddChild(to, rexmpp_xml_to_libxml2(child)); */
+/* child = child->next; */
+/* } */
+/* return to; */
+/* } */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from) { */
+/* xmlNodePtr to = rexmpp_xml_to_libxml2(from); */
+/* if (from->next != NULL) { */
+/* xmlAddNextSibling(to, rexmpp_xml_to_libxml2_list(from->next)); */
+/* } */
+/* return to; */
+/* } */
+
+#elif defined(USE_EXPAT)
+
+void XMLCALL
+rexmpp_xml_sax_elem_start (rexmpp_xml_parser_ctx_t ctx,
+ const char *el,
+ const char **attributes)
+{
+ char *buf = strdup(el);
+ char *name = NULL, *namespace = buf;
+ size_t i;
+ for (i = 0; i < strlen(namespace); i++) {
+ if (namespace[i] == '\xff') {
+ name = namespace + i + 1;
+ namespace[i] = '\0';
+ }
+ }
+ if (name == NULL) {
+ name = namespace;
+ namespace = NULL;
+ }
+ rexmpp_xml_attr_t *attrs = NULL;
+ for (i = 0; attributes[i] != NULL; i += 2) {
+ rexmpp_xml_attr_t *attr =
+ rexmpp_xml_attr_new(attributes[i], NULL, attributes[i + 1]);
+ attr->next = attrs;
+ attrs = attr;
+ }
+
+ ctx->handlers->elem_start(ctx->user_data, name, namespace, attrs);
+ free(buf);
+}
+
+void XMLCALL
+rexmpp_xml_sax_elem_end(rexmpp_xml_parser_ctx_t ctx,
+ const XML_Char *name)
+{
+ (void)name;
+ ctx->handlers->elem_end(ctx->user_data);
+}
+
+void XMLCALL
+rexmpp_xml_sax_characters (rexmpp_xml_parser_ctx_t ctx,
+ const XML_Char *ch,
+ int len)
+{
+ ctx->handlers->text(ctx->user_data, ch, len);
+}
+
+#endif
+
+
+
+rexmpp_xml_parser_ctx_t
+rexmpp_xml_parser_new (rexmpp_xml_parser_handlers_t handlers,
+ void *data)
+{
+ rexmpp_xml_parser_ctx_t ctx = malloc(sizeof(struct rexmpp_xml_parser_ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+#if defined(USE_LIBXML2)
+ xmlParserCtxtPtr p =
+ xmlCreatePushParserCtxt(&rexmpp_xml_parser_sax, ctx, "", 0, NULL);
+#elif defined(USE_EXPAT)
+ XML_Parser p = XML_ParserCreateNS("utf-8", '\xff');
+ XML_SetUserData(p, ctx);
+ XML_SetStartElementHandler(p, (XML_StartElementHandler)
+ rexmpp_xml_sax_elem_start);
+ XML_SetEndElementHandler(p, (XML_EndElementHandler)
+ rexmpp_xml_sax_elem_end);
+ XML_SetCharacterDataHandler(p, (XML_CharacterDataHandler)
+ rexmpp_xml_sax_characters);
+#endif
+ if (p == NULL) {
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->xml_parser = p;
+ ctx->handlers = handlers;
+ ctx->user_data = data;
+ return ctx;
+}
+
+void rexmpp_xml_parser_free (rexmpp_xml_parser_ctx_t ctx) {
+#if defined(USE_LIBXML2)
+ xmlFreeParserCtxt(ctx->xml_parser);
+#elif defined(USE_EXPAT)
+ XML_ParserFree(ctx->xml_parser);
+#endif
+ free(ctx);
+}
+
+rexmpp_xml_parser_ctx_t rexmpp_xml_parser_reset (rexmpp_xml_parser_ctx_t ctx) {
+#if defined(USE_LIBXML2)
+ xmlCtxtResetPush(ctx->xml_parser, "", 0, "", "utf-8");
+#elif defined(USE_EXPAT)
+ XML_ParserReset(ctx->xml_parser, "utf-8");
+ XML_SetUserData(ctx->xml_parser, ctx);
+ XML_SetStartElementHandler(ctx->xml_parser, (XML_StartElementHandler)
+ rexmpp_xml_sax_elem_start);
+ XML_SetEndElementHandler(ctx->xml_parser, (XML_EndElementHandler)
+ rexmpp_xml_sax_elem_end);
+ XML_SetCharacterDataHandler(ctx->xml_parser, (XML_CharacterDataHandler)
+ rexmpp_xml_sax_characters);
+#endif
+ return ctx;
+}
+
+void
+rexmpp_xml_parser_feed (rexmpp_xml_parser_ctx_t ctx,
+ const char *chunk,
+ size_t len,
+ int final)
+{
+#if defined(USE_LIBXML2)
+ xmlParseChunk(ctx->xml_parser, chunk, len, final);
+#elif defined(USE_EXPAT)
+ XML_Parse(ctx->xml_parser, chunk, len, final);
+#endif
+}
diff --git a/src/rexmpp_xml_parser.h b/src/rexmpp_xml_parser.h
new file mode 100644
index 0000000..66627ab
--- /dev/null
+++ b/src/rexmpp_xml_parser.h
@@ -0,0 +1,106 @@
+/**
+ @file rexmpp_xml_parser.h
+ @brief XML parsing for rexmpp
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_XML_PARSER_H
+#define REXMPP_XML_PARSER_H
+
+
+#if defined(USE_LIBXML2)
+ #include <libxml/tree.h>
+#elif defined(USE_EXPAT)
+ #include <expat.h>
+#endif
+
+#include "config.h"
+
+typedef void (*rexmpp_xml_parser_element_start) (void *data,
+ const char *name,
+ const char *namespace,
+ rexmpp_xml_attr_t *attributes);
+typedef void (*rexmpp_xml_parser_element_end) (void *data);
+typedef void (*rexmpp_xml_parser_characters) (void *data,
+ const char *ch,
+ size_t len);
+
+struct rexmpp_xml_parser_handlers {
+ rexmpp_xml_parser_element_start elem_start;
+ rexmpp_xml_parser_element_end elem_end;
+ rexmpp_xml_parser_characters text;
+};
+
+
+typedef struct rexmpp_xml_parser_ctx* rexmpp_xml_parser_ctx_t;
+typedef struct rexmpp_xml_parser_handlers* rexmpp_xml_parser_handlers_t;
+
+struct rexmpp_xml_parser_ctx {
+#if defined(USE_LIBXML2)
+ xmlParserCtxtPtr xml_parser;
+#elif defined(USE_EXPAT)
+ XML_Parser xml_parser;
+#else
+ void *xml_parser;
+#endif
+ rexmpp_xml_parser_handlers_t handlers;
+ void *user_data;
+};
+
+/**
+ @brief Allocates a new XML parser context
+ @param[in] handlers SAX-like parser event handlers
+ @param[in] data User-provided data to pass to the handlers
+ @returns A parser context pointer, or NULL on failure.
+*/
+rexmpp_xml_parser_ctx_t
+rexmpp_xml_parser_new (rexmpp_xml_parser_handlers_t handlers,
+ void *data);
+
+/**
+ @brief Frees an XML parser context
+ @param[in] ctx An XML parser context
+*/
+void rexmpp_xml_parser_free (rexmpp_xml_parser_ctx_t ctx);
+
+/**
+ @brief Feeds data to parse into an XML parser
+ @param[in] ctx An XML parser context
+ @param[in] chunk A chunk of data to parse
+ @param[in] len Length of the data chunk
+*/
+void
+rexmpp_xml_parser_feed (rexmpp_xml_parser_ctx_t ctx,
+ const char *chunk,
+ size_t len,
+ int final);
+
+/**
+ @brief Resets a parser context
+ @param[in] ctx An XML parser context
+ @returns A new pointer, since it may change during a reset
+*/
+rexmpp_xml_parser_ctx_t rexmpp_xml_parser_reset (rexmpp_xml_parser_ctx_t ctx);
+
+
+/* #if defined(USE_LIBXML2) */
+/* /\** */
+/* @brief Creates a single ::rexmpp_xml_t XML node out of libxml2's */
+/* xmlNode, without siblings. */
+/* *\/ */
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from); */
+
+/* /\** */
+/* @brief Creates a ::rexmpp_xml_t XML node out of libxml2's xmlNode, */
+/* with siblings. */
+/* *\/ */
+/* rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from); */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from); */
+
+/* xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from); */
+/* #endif */
+
+#endif
diff --git a/src/rexmpp_xml_parser.rs b/src/rexmpp_xml_parser.rs
new file mode 100644
index 0000000..037c2f2
--- /dev/null
+++ b/src/rexmpp_xml_parser.rs
@@ -0,0 +1,145 @@
+extern crate libc;
+extern crate rxml;
+use libc::{free, strndup};
+use std::ptr;
+use std::os::raw::{c_char, c_void};
+use std::ffi::{CStr, CString};
+use std::slice;
+use rxml::{FeedParser, Error, ResolvedEvent, XmlVersion, EventRead, CData};
+use std::io;
+use super::{rexmpp_xml};
+
+type RexmppXMLParserElementStart = unsafe extern "C"
+fn (data: *mut c_void,
+ name: *const c_char,
+ namespace: *const c_char,
+ attributes: *mut rexmpp_xml::RexmppXMLAttribute) -> ();
+
+type RexmppXMLParserElementEnd = unsafe extern "C"
+fn (data: *mut c_void) -> ();
+
+type RexmppXMLParserCharacters = unsafe extern "C"
+fn (data: *mut c_void,
+ ch: *const c_char,
+ len: usize) -> ();
+
+#[repr(C)]
+struct RexmppXMLParserHandlers {
+ elem_start: RexmppXMLParserElementStart,
+ elem_end: RexmppXMLParserElementEnd,
+ text: RexmppXMLParserCharacters
+}
+
+#[repr(C)]
+struct RexmppXMLParserCtx {
+ xml_parser: *mut FeedParser,
+ handlers: *mut RexmppXMLParserHandlers,
+ user_data: *mut c_void
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_parser_new (handlers: *mut RexmppXMLParserHandlers,
+ data: *mut c_void)
+ -> *mut RexmppXMLParserCtx
+{
+ let mut fp = FeedParser::default();
+ let ctx = RexmppXMLParserCtx {
+ xml_parser: Box::into_raw(Box::new(fp)),
+ handlers: handlers,
+ user_data: data
+ };
+ Box::into_raw(Box::new(ctx))
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_parser_free (ctx: *mut RexmppXMLParserCtx) {
+ unsafe { free(ctx as *mut c_void) };
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_parser_feed (ctx: *mut RexmppXMLParserCtx,
+ chunk: *const c_char,
+ len: usize,
+ is_final: bool)
+{
+ unsafe {
+ // todo: maybe duplicate the string, since apparently a
+ // mutable one is expected by the parser.
+ let mut buf : &[u8] = slice::from_raw_parts(chunk as *mut u8, len);
+ let user_data_ptr = (*ctx).user_data;
+ let handlers = (*ctx).handlers;
+ (*((*ctx).xml_parser)).parse_all(&mut buf, is_final, |ev| {
+ match ev {
+ ResolvedEvent::StartElement(_, (namespace, name), attrs) =>
+ {
+ let name_str = name.to_string();
+ let ns_opt_cstr : Option<CString> = match namespace {
+ None => None,
+ Some(ns_arc_name) => {
+ match CString::new(ns_arc_name.to_string()) {
+ Ok(cstr) => Some(cstr),
+ Err(_) => None
+ }
+ }
+ };
+ match CString::new(name_str) {
+ Ok(name_cstr) => {
+ let name_cstr_ptr = name_cstr.as_ptr();
+ let namespace_cstr_ptr =
+ match ns_opt_cstr {
+ None => ptr::null_mut(),
+ // "ref" is important to use here,
+ // otherwise the pointer will be
+ // wrong.
+ Some(ref ns_cstr) => ns_cstr.as_ptr()
+ };
+ let mut attributes = ptr::null_mut();
+ for ((_, attr_name), attr_val) in attrs.iter() {
+ match (CString::new(attr_name.to_string()),
+ CString::new(attr_val.to_string())) {
+ (Ok(attr_name_cstr), Ok(attr_val_cstr)) => {
+ let attr =
+ rexmpp_xml::rexmpp_xml_attr_new
+ (attr_name_cstr.as_ptr(),
+ ptr::null_mut(),
+ attr_val_cstr.as_ptr());
+ (*attr).next = attributes;
+ attributes = attr;
+ },
+ _ => ()
+ }
+ }
+ ((*handlers).elem_start)
+ (user_data_ptr,
+ name_cstr_ptr,
+ namespace_cstr_ptr,
+ attributes);
+ },
+ Err(_) => ()
+ }
+ },
+ ResolvedEvent::EndElement(_) =>
+ ((*handlers).elem_end)(user_data_ptr),
+ ResolvedEvent::Text(_, cd) =>
+ ((*handlers).text)(
+ user_data_ptr,
+ cd.as_ptr() as *const i8,
+ cd.len()
+ ),
+ _ => ()
+ }
+ });
+ }
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_parser_reset (ctx_raw: *mut RexmppXMLParserCtx)
+ -> *mut RexmppXMLParserCtx
+{
+ let ctx = unsafe { Box::from_raw(ctx_raw) };
+ rexmpp_xml_parser_new((*ctx).handlers, (*ctx).user_data)
+}