diff options
Diffstat (limited to 'src/rexmpp.c')
-rw-r--r-- | src/rexmpp.c | 1444 |
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 |