summaryrefslogtreecommitdiff
path: root/src/rexmpp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rexmpp.c')
-rw-r--r--src/rexmpp.c1444
1 files changed, 726 insertions, 718 deletions
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