diff options
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | emacs/xml_interface.c | 159 | ||||
-rw-r--r-- | examples/basic.c | 13 | ||||
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/rexmpp.c | 790 | ||||
-rw-r--r-- | src/rexmpp.h | 112 | ||||
-rw-r--r-- | src/rexmpp_console.c | 412 | ||||
-rw-r--r-- | src/rexmpp_console.h | 4 | ||||
-rw-r--r-- | src/rexmpp_http_upload.c | 49 | ||||
-rw-r--r-- | src/rexmpp_jingle.c | 575 | ||||
-rw-r--r-- | src/rexmpp_jingle.h | 8 | ||||
-rw-r--r-- | src/rexmpp_openpgp.c | 262 | ||||
-rw-r--r-- | src/rexmpp_openpgp.h | 10 | ||||
-rw-r--r-- | src/rexmpp_pubsub.c | 51 | ||||
-rw-r--r-- | src/rexmpp_pubsub.h | 4 | ||||
-rw-r--r-- | src/rexmpp_roster.c | 76 | ||||
-rw-r--r-- | src/rexmpp_roster.h | 14 | ||||
-rw-r--r-- | src/rexmpp_xml.c | 592 | ||||
-rw-r--r-- | src/rexmpp_xml.h | 232 |
19 files changed, 1941 insertions, 1431 deletions
@@ -53,8 +53,8 @@ A rough roadmap: [.] Texinfo manual. [.] Proper JID handling (RFC 7622). [+] Abstraction of the used XML, SASL, TLS, and DNS libraries, and - optional usage of alternative ones. Though left libxml2 for now: - could reuse existing libxml2 bindings that way. + optional usage of alternative ones. Though only supporting libxml2 + for XML parsing and printing for now. [.] Automated testing. [.] Alternative module implementations in Rust. diff --git a/emacs/xml_interface.c b/emacs/xml_interface.c index 2f4ebba..3dabf96 100644 --- a/emacs/xml_interface.c +++ b/emacs/xml_interface.c @@ -20,20 +20,22 @@ and EOF ones, to simplify reading with libxml2. #include <syslog.h> #include <gnutls/gnutls.h> #include <rexmpp.h> +#include <rexmpp_xml.h> #include <rexmpp_openpgp.h> +#include <rexmpp_http_upload.h> -void print_xml (xmlNodePtr node) { +void print_xml (rexmpp_xml_t *node) { char *s = rexmpp_xml_serialize(node); printf("%s%c\n", s, '\0'); free(s); } -xmlNodePtr read_xml () { - xmlNodePtr elem = NULL; +rexmpp_xml_t *read_xml () { + rexmpp_xml_t *elem = NULL; xmlDocPtr doc = xmlReadFd(STDIN_FILENO, "", "utf-8", 0); if (doc != NULL) { - elem = xmlCopyNode(xmlDocGetRootElement(doc), 1); + elem = rexmpp_xml_from_libxml2(xmlDocGetRootElement(doc)); xmlFreeDoc(doc); return elem; } @@ -41,70 +43,72 @@ xmlNodePtr read_xml () { } -char *request (rexmpp_t *s, xmlNodePtr payload) +char *request (rexmpp_t *s, rexmpp_xml_t *payload) { - xmlNodePtr req = rexmpp_xml_add_id(s, xmlNewNode(NULL, "request")); - xmlAddChild(req, payload); + rexmpp_xml_t *req = rexmpp_xml_new_elem("request", NULL); + rexmpp_xml_add_id(s, req); + rexmpp_xml_add_child(req, payload); print_xml(req); - char *id = xmlGetProp(req, "id"); - xmlFreeNode(req); + char *id = strdup(rexmpp_xml_find_attr_val(req, "id")); + rexmpp_xml_free(req); return id; } -xmlNodePtr read_response (rexmpp_t *s, const char *id) { - xmlNodePtr elem = read_xml(); +void req_process (rexmpp_t *s, + rexmpp_xml_t *elem); + +rexmpp_xml_t *read_response (rexmpp_t *s, const char *id) { + rexmpp_xml_t *elem = read_xml(); if (elem != NULL) { if (rexmpp_xml_match(elem, NULL, "response")) { - char *resp_id = xmlGetProp(elem, "id"); + const char *resp_id = rexmpp_xml_find_attr_val(elem, "id"); if (resp_id != NULL) { - int matches = (strcmp(resp_id, id) == 0); - free(resp_id); - if (matches) { + if (strcmp(resp_id, id) == 0) { return elem; } else { /* Just fail for now, to avoid deadlocks. Though this shouldn't happen. */ - xmlFreeNode(elem); + rexmpp_xml_free(elem); rexmpp_log(s, LOG_ERR, "Unexpected response ID received."); return NULL; } } } req_process(s, elem); - xmlFreeNode(elem); + rexmpp_xml_free(elem); } return read_response(s, id); } -xmlNodePtr req_block (rexmpp_t *s, xmlNodePtr req) { +rexmpp_xml_t *req_block (rexmpp_t *s, rexmpp_xml_t *req) { char *id = request(s, req); - xmlNodePtr resp = read_response(s, id); + rexmpp_xml_t *resp = read_response(s, id); free(id); return resp; } void respond_xml (rexmpp_t *s, const char *id, - xmlNodePtr payload) { - xmlNodePtr response = xmlNewNode(NULL, "response"); - xmlNewProp(response, "id", id); + rexmpp_xml_t *payload) { + rexmpp_xml_t *response = rexmpp_xml_new_elem("response", NULL); + rexmpp_xml_add_attr(response, "id", id); if (payload != NULL) { - xmlAddChild(response, payload); + rexmpp_xml_add_child(response, payload); } print_xml(response); - xmlFreeNode(response); + rexmpp_xml_free(response); } void respond_text (rexmpp_t *s, const char *id, const char *buf) { - xmlNodePtr response = xmlNewNode(NULL, "response"); - xmlNewProp(response, "id", id); + rexmpp_xml_t *response = rexmpp_xml_new_elem("response", NULL); + rexmpp_xml_add_attr(response, "id", id); if (buf != NULL) { - xmlNodeAddContent(response, buf); + rexmpp_xml_add_text(response, buf); } print_xml(response); - xmlFreeNode(response); + rexmpp_xml_free(response); } void on_http_upload (rexmpp_t *s, void *cb_data, const char *url) { @@ -114,84 +118,83 @@ void on_http_upload (rexmpp_t *s, void *cb_data, const char *url) { } void req_process (rexmpp_t *s, - xmlNodePtr elem) + rexmpp_xml_t *elem) { - char *id = xmlGetProp(elem, "id"); + const char *id = rexmpp_xml_find_attr_val(elem, "id"); if (id == NULL) { return; } rexmpp_err_t err; char buf[64]; - xmlNodePtr child = xmlFirstElementChild(elem); + rexmpp_xml_t *child = rexmpp_xml_first_elem_child(elem); if (rexmpp_xml_match(child, NULL, "stop")) { snprintf(buf, 64, "%d", rexmpp_stop(s)); respond_text(s, id, buf); } else if (rexmpp_xml_match(child, NULL, "console")) { - char *in = xmlNodeGetContent(child); + char *in = strdup(rexmpp_xml_text_child(child)); rexmpp_console_feed(s, in, strlen(in)); free(in); respond_text(s, id, NULL); } else if (rexmpp_xml_match(child, NULL, "send")) { - if (xmlFirstElementChild(child)) { - xmlNodePtr stanza = xmlCopyNode(xmlFirstElementChild(child), 1); + if (rexmpp_xml_first_elem_child(child)) { + rexmpp_xml_t *stanza = + rexmpp_xml_clone(rexmpp_xml_first_elem_child(child)); snprintf(buf, 64, "%d", rexmpp_send(s, stanza)); respond_text(s, id, buf); } } else if (rexmpp_xml_match(child, NULL, "openpgp-decrypt-message")) { int valid; - xmlNodePtr plaintext = - rexmpp_openpgp_decrypt_verify_message(s, xmlFirstElementChild(child), + rexmpp_xml_t *plaintext = + rexmpp_openpgp_decrypt_verify_message(s, rexmpp_xml_first_elem_child(child), &valid); /* todo: wrap into another element, with the 'valid' attribute */ respond_xml(s, id, plaintext); } else if (rexmpp_xml_match(child, NULL, "openpgp-payload")) { enum rexmpp_ox_mode mode = REXMPP_OX_CRYPT; - char *mode_str = xmlGetProp(child, "mode"); + const char *mode_str = rexmpp_xml_find_attr_val(child, "mode"); if (strcmp(mode_str, "sign") == 0) { mode = REXMPP_OX_SIGN; } else if (strcmp(mode_str, "signcrypt") == 0) { mode = REXMPP_OX_SIGNCRYPT; } - free(mode_str); - xmlNodePtr payload_xml = - xmlFirstElementChild(rexmpp_xml_find_child(child, NULL, "payload")); + rexmpp_xml_t *payload_xml = + rexmpp_xml_first_elem_child(rexmpp_xml_find_child(child, NULL, "payload")); - char **recipients[16]; + char *recipients[16]; int recipients_num = 0; - xmlNodePtr plchild; - for (plchild = xmlFirstElementChild(child); + rexmpp_xml_t *plchild; + for (plchild = rexmpp_xml_first_elem_child(child); plchild != NULL && recipients_num < 15; plchild = plchild->next) { if (rexmpp_xml_match(plchild, NULL, "to")) { - recipients[recipients_num] = xmlNodeGetContent(plchild); + recipients[recipients_num] = strdup(rexmpp_xml_text_child(plchild)); recipients_num++; } } recipients[recipients_num] = NULL; char *payload_str = - rexmpp_openpgp_payload(s, xmlCopyNode(payload_xml, 1), recipients, NULL, mode); + rexmpp_openpgp_payload(s, rexmpp_xml_clone(payload_xml), + (const char **)recipients, NULL, mode); for (recipients_num = 0; recipients[recipients_num] != NULL; recipients_num++) { free(recipients[recipients_num]); } respond_text(s, id, payload_str); free(payload_str); } else if (rexmpp_xml_match(child, NULL, "get-name")) { - char *jid = xmlNodeGetContent(child); + const char *jid = rexmpp_xml_text_child(child); if (jid != NULL) { char *name = rexmpp_get_name(s, jid); if (name != NULL) { respond_text(s, id, name); free(name); } - free(jid); } } else if (rexmpp_xml_match(child, NULL, "http-upload")) { - char *in = xmlNodeGetContent(child); + char *in = strdup(rexmpp_xml_text_child(child)); rexmpp_http_upload_path(s, NULL, in, NULL, on_http_upload, strdup(id)); free(in); } - free(id); return; } @@ -210,22 +213,23 @@ void my_logger (rexmpp_t *s, int priority, const char *fmt, va_list args) { case LOG_INFO: priority_str = "info"; break; case LOG_DEBUG: priority_str = "debug"; break; } - xmlNodePtr node = xmlNewNode(NULL, "log"); - xmlNewProp(node, "priority", priority_str); - xmlNodeAddContent(node, buf); + rexmpp_xml_t *node = rexmpp_xml_new_elem("log", NULL); + rexmpp_xml_add_attr(node, "priority", priority_str); + rexmpp_xml_add_text(node, buf); free(buf); print_xml(node); - xmlFreeNode(node); + rexmpp_xml_free(node); } -void my_console_print_cb (rexmpp_t *s, const char *fmt, va_list args) { +int my_console_print_cb (rexmpp_t *s, const char *fmt, va_list args) { char *buf = malloc(1024 * 20); vsnprintf(buf, 1024 * 20, fmt, args); - xmlNodePtr node = xmlNewNode(NULL, "console"); - xmlNodeAddContent(node, buf); + rexmpp_xml_t *node = rexmpp_xml_new_elem("console", NULL); + rexmpp_xml_add_text(node, buf); free(buf); print_xml(node); - xmlFreeNode(node); + rexmpp_xml_free(node); + return 0; } int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) { @@ -239,53 +243,50 @@ int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) { case REXMPP_SASL_PROP_AUTHID: prop_str = "authid"; break; default: return -1; } - xmlNodePtr req = xmlNewNode(NULL, "sasl"); - xmlNewProp(req, "property", prop_str); - xmlNodePtr rep = req_block(s, req); + rexmpp_xml_t *req = rexmpp_xml_new_elem("sasl", NULL); + rexmpp_xml_add_attr(req, "property", prop_str); + rexmpp_xml_t *rep = req_block(s, req); if (rep == NULL) { return -1; } - char *val = xmlNodeGetContent(rep); - xmlFreeNode(rep); + const char *val = rexmpp_xml_text_child(rep); if (val == NULL) { return -1; } rexmpp_sasl_property_set (s, prop, val); - free(val); + rexmpp_xml_free(rep); return GSASL_OK; } -int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) { - xmlNodePtr req = xmlNewNode(NULL, "xml-in"); - xmlAddChild(req, xmlCopyNode(node, 1)); - xmlNodePtr rep = req_block(s, req); +int my_xml_in_cb (rexmpp_t *s, rexmpp_xml_t *node) { + rexmpp_xml_t *req = rexmpp_xml_new_elem("xml-in", NULL); + rexmpp_xml_add_child(req, rexmpp_xml_clone(node)); + rexmpp_xml_t *rep = req_block(s, req); if (rep == NULL) { return 0; } - char *val = xmlNodeGetContent(rep); - xmlFreeNode(rep); + const char *val = rexmpp_xml_text_child(rep); if (val == NULL) { return 0; } int n = atoi(val); - free(val); + rexmpp_xml_free(rep); return n; } -int my_xml_out_cb (rexmpp_t *s, xmlNodePtr node) { - xmlNodePtr req = xmlNewNode(NULL, "xml-out"); - xmlAddChild(req, xmlCopyNode(node, 1)); - xmlNodePtr rep = req_block(s, req); +int my_xml_out_cb (rexmpp_t *s, rexmpp_xml_t *node) { + rexmpp_xml_t *req = rexmpp_xml_new_elem("xml-out", NULL); + rexmpp_xml_add_child(req, rexmpp_xml_clone(node)); + rexmpp_xml_t *rep = req_block(s, req); if (rep == NULL) { return 0; } - char *val = xmlNodeGetContent(rep); - xmlFreeNode(rep); + const char *val = rexmpp_xml_text_child(rep); if (val == NULL) { return 0; } int n = atoi(val); - free(val); + rexmpp_xml_free(rep); return n; } @@ -338,10 +339,10 @@ int main (int argc, char **argv) { do { /* Check if we have some user input. */ if (n > 0 && FD_ISSET(STDIN_FILENO, &read_fds)) { - xmlNodePtr elem = read_xml(); + rexmpp_xml_t *elem = read_xml(); if (elem != NULL) { req_process(&s, elem); - xmlFreeNode(elem); + rexmpp_xml_free(elem); } } diff --git a/examples/basic.c b/examples/basic.c index 982106a..66c54f6 100644 --- a/examples/basic.c +++ b/examples/basic.c @@ -13,6 +13,7 @@ #include <gsasl.h> #include <time.h> #include <rexmpp.h> +#include <rexmpp_xml.h> #include <rexmpp_sasl.h> int log_level = 8; @@ -66,7 +67,7 @@ int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) { } /* An XML in callback, printing what was received. */ -int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) { +int my_xml_in_cb (rexmpp_t *s, rexmpp_xml_t *node) { char *xml_buf = rexmpp_xml_serialize(node); printf("recv: %s\n", xml_buf); free(xml_buf); @@ -74,7 +75,7 @@ int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) { } /* An XML out callback, printing what is about to be sent. */ -int my_xml_out_cb (rexmpp_t *s, xmlNodePtr node) { +int my_xml_out_cb (rexmpp_t *s, rexmpp_xml_t *node) { char *xml_buf = rexmpp_xml_serialize(node); printf("send: %s\n", xml_buf); free(xml_buf); @@ -178,9 +179,11 @@ int main (int argc, char **argv) { /* Raw XML input. */ xmlDocPtr doc = xmlReadMemory(input, input_len, "", "utf-8", 0); if (doc != NULL) { - xmlNodePtr node = xmlDocGetRootElement(doc); - if (node != NULL) { - xmlUnlinkNode(node); + xmlNodePtr node_libxml2 = xmlDocGetRootElement(doc); + if (node_libxml2 != NULL) { + xmlUnlinkNode(node_libxml2); + rexmpp_xml_t *node = rexmpp_xml_from_libxml2(node_libxml2); + xmlFreeNode(node_libxml2); rexmpp_send(&s, node); } else { puts("No root node"); diff --git a/src/Makefile.am b/src/Makefile.am index de0a68f..0955d81 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,12 +21,13 @@ librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \ rexmpp_http_upload.h rexmpp_http_upload.c \ rexmpp_jingle.h rexmpp_jingle.c \ rexmpp_base64.h rexmpp_base64.c \ - rexmpp_sasl.h rexmpp_sasl.c + rexmpp_sasl.h rexmpp_sasl.c \ + rexmpp_xml.h rexmpp_xml.c include_HEADERS = config.h rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h \ rexmpp_dns.h rexmpp_tls.h rexmpp_jid.h rexmpp_openpgp.h rexmpp_console.h \ rexmpp_pubsub.h rexmpp_http_upload.h rexmpp_jingle.h rexmpp_base64.h \ - rexmpp_sasl.h + rexmpp_sasl.h rexmpp_xml.h librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) \ $(GNUTLS_CFLAGS) $(LIBDANE_CFLAGS) $(OPENSSL_CFLAGS) \ $(GSASL_CFLAGS) $(UNBOUND_CFLAGS) $(CARES_CFLAGS) $(GPGME_CFLAGS) \ diff --git a/src/rexmpp.c b/src/rexmpp.c index c29574e..80d7cc2 100644 --- a/src/rexmpp.c +++ b/src/rexmpp.c @@ -29,6 +29,7 @@ #endif #include "rexmpp.h" +#include "rexmpp_xml.h" #include "rexmpp_tcp.h" #include "rexmpp_socks.h" #include "rexmpp_roster.h" @@ -114,21 +115,21 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...) } } -char *rexmpp_capabilities_string (rexmpp_t *s, xmlNodePtr info) { +char *rexmpp_capabilities_string (rexmpp_t *s, rexmpp_xml_t *info) { /* Assuming the info is sorted already. Would be better to sort it here (todo). */ - xmlNodePtr cur; + rexmpp_xml_t *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) { + if (strcmp(cur->alt.elem.qname.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"); + const char *category = rexmpp_xml_find_attr_val(cur, "category"); + const char *type = rexmpp_xml_find_attr_val(cur, "type"); + const char *lang = rexmpp_xml_find_attr_val(cur, "xml:lang"); + const char *name = rexmpp_xml_find_attr_val(cur, "name"); /* Calculate the length needed. */ if (category != NULL) { @@ -177,22 +178,8 @@ char *rexmpp_capabilities_string (rexmpp_t *s, xmlNodePtr info) { } str[str_len] = '<'; str_len++; - - /* Free the values. */ - if (category != NULL) { - free(category); - } - if (type != NULL) { - free(type); - } - if (lang != NULL) { - free(lang); - } - if (name != NULL) { - free(name); - } - } else if (strcmp(cur->name, "feature") == 0) { - char *var = xmlGetProp(cur, "var"); + } else if (strcmp(cur->alt.elem.qname.name, "feature") == 0) { + const char *var = rexmpp_xml_find_attr_val(cur, "var"); int cur_len = 2 + strlen(var); if (cur_len > buf_len - str_len) { while (cur_len > buf_len - str_len) { @@ -204,10 +191,9 @@ char *rexmpp_capabilities_string (rexmpp_t *s, xmlNodePtr info) { 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'; @@ -215,7 +201,7 @@ char *rexmpp_capabilities_string (rexmpp_t *s, xmlNodePtr info) { } char *rexmpp_capabilities_hash (rexmpp_t *s, - xmlNodePtr info) + rexmpp_xml_t *info) { char *out = NULL; size_t out_len = 0; @@ -233,38 +219,35 @@ char *rexmpp_capabilities_hash (rexmpp_t *s, return out; } -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 +265,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) { + char *name = strdup(rexmpp_xml_find_attr_val(roster_item, "name")); if (name != NULL) { return 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 +309,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); } } } @@ -440,8 +408,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 +417,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; @@ -601,37 +569,37 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, 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; + + 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); + + pl_type = rexmpp_xml_new_elem("payload-type", "urn:xmpp:jingle:apps:rtp:1"); + rexmpp_xml_add_attr(pl_type, "id", "96"); + rexmpp_xml_add_attr(pl_type, "name", "speex"); + rexmpp_xml_add_attr(pl_type, "clockrate", "32000"); + 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", "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."); @@ -744,11 +712,11 @@ 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) { @@ -778,12 +746,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); } @@ -803,7 +771,7 @@ void rexmpp_done (rexmpp_t *s) { rexmpp_dns_ctx_deinit(s); xmlFreeParserCtxt(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) { @@ -811,15 +779,15 @@ 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) { @@ -827,11 +795,11 @@ void rexmpp_done (rexmpp_t *s) { s->roster_ver = NULL; } 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) { @@ -841,7 +809,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; } } @@ -894,87 +862,7 @@ char *rexmpp_gen_id (rexmpp_t *s) { 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; } @@ -983,42 +871,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; @@ -1074,15 +935,15 @@ rexmpp_err_t rexmpp_send_continue (rexmpp_t *s) free(s->send_buffer); s->send_buffer = NULL; if (s->send_queue != NULL) { - xmlNodePtr node = s->send_queue; + rexmpp_xml_t *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); + s->send_queue = s->send_queue->next; + rexmpp_xml_free(node); } else { return REXMPP_SUCCESS; } @@ -1122,19 +983,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; } @@ -1145,20 +1006,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) { @@ -1170,7 +1032,7 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node) unsigned char *buf = rexmpp_xml_serialize(node); 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; } @@ -1178,11 +1040,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; } @@ -1193,28 +1055,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); } @@ -1222,7 +1081,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) { @@ -1239,18 +1098,19 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s, 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(s, 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); + iq->request = rexmpp_xml_clone(iq_stanza); iq->cb = cb; iq->cb_data = cb_data; iq->next = s->active_iq; @@ -1260,12 +1120,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; @@ -1274,12 +1134,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; @@ -1294,24 +1154,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); } @@ -1329,20 +1187,18 @@ 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; @@ -1387,12 +1243,14 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) { elem != NULL && (err == REXMPP_SUCCESS || err == REXMPP_E_AGAIN); elem = elem->next) { - if (s->xml_in_cb != NULL && s->xml_in_cb(s, elem) != 0) { + rexmpp_xml_t *elem_tmp = rexmpp_xml_from_libxml2(elem); + if (s->xml_in_cb != NULL && s->xml_in_cb(s, elem_tmp) != 0) { rexmpp_log(s, LOG_WARNING, "Message processing was cancelled by xml_in_cb."); } else { - err = rexmpp_process_element(s, elem); + err = rexmpp_process_element(s, elem_tmp); } + rexmpp_xml_free(elem_tmp); } xmlFreeNodeList(s->input_queue); s->input_queue = NULL; @@ -1632,10 +1490,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; @@ -1652,12 +1510,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, @@ -1666,8 +1523,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 { @@ -1686,8 +1543,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; @@ -1704,8 +1561,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; @@ -1717,15 +1574,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); @@ -1748,27 +1605,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(s, 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); } @@ -1776,8 +1637,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; @@ -1788,24 +1649,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; @@ -1820,12 +1679,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, @@ -1835,22 +1694,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 @@ -1858,8 +1718,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; } @@ -1873,7 +1733,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; } @@ -1883,18 +1743,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); @@ -1913,10 +1772,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; @@ -1926,10 +1785,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; } @@ -1938,7 +1797,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; } } @@ -1948,18 +1808,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; @@ -1981,29 +1841,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 && @@ -2012,8 +1863,8 @@ 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 = strdup(rexmpp_xml_find_attr_val(query, "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) { @@ -2025,9 +1876,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 && @@ -2036,12 +1887,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, @@ -2052,20 +1904,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. */ @@ -2074,49 +1923,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); } } } @@ -2124,28 +1968,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) { @@ -2153,12 +1996,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; @@ -2170,56 +2013,54 @@ 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 *presence = + rexmpp_xml_new_elem("presence", "jabber:client"); + rexmpp_xml_add_id(s, presence); + rexmpp_xml_add_attr(presence, "from", + s->assigned_jid.full); + rexmpp_xml_t *nick = rexmpp_xml_find_child(conference, "urn:xmpp:bookmarks:1", "nick"); - char *nick_str; + const char *nick_str; if (nick != NULL) { - nick_str = xmlNodeGetContent(nick); + nick_str = rexmpp_xml_text_child(nick); } else { - nick_str = strdup(s->initial_jid.local); + 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); + rexmpp_xml_add_attr(presence, "to", jid); free(jid); - xmlNodePtr x = xmlNewNode(NULL, "x"); - xmlNewNs(x, "http://jabber.org/protocol/muc", NULL); - xmlAddChild(presence, x); + rexmpp_xml_t *x = + rexmpp_xml_new_elem("x", + "http://jabber.org/protocol/muc"); + rexmpp_xml_add_child(presence, x); rexmpp_send(s, presence); } - free(item_id); - free(autojoin); } } - free(node); } } } @@ -2264,23 +2105,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"); @@ -2303,20 +2142,19 @@ 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); + s->stream_id = strdup(rexmpp_xml_find_attr_val(elem, "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) { @@ -2328,8 +2166,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); @@ -2350,7 +2188,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"); @@ -2475,12 +2313,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(s, 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) { @@ -2642,8 +2484,8 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { s->last_network_activity + s->ping_delay <= time(NULL)) { 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 { diff --git a/src/rexmpp.h b/src/rexmpp.h index 13b6c08..94f2df2 100644 --- a/src/rexmpp.h +++ b/src/rexmpp.h @@ -187,6 +187,7 @@ enum tls_pol { typedef enum rexmpp_err rexmpp_err_t; +#include "rexmpp_xml.h" #include "rexmpp_tcp.h" #include "rexmpp_socks.h" #include "rexmpp_dns.h" @@ -207,8 +208,8 @@ typedef enum rexmpp_err rexmpp_err_t; */ typedef void (*rexmpp_iq_callback_t) (rexmpp_t *s, void *cb_data, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success); typedef struct rexmpp_iq rexmpp_iq_t; @@ -217,7 +218,7 @@ typedef struct rexmpp_iq rexmpp_iq_t; struct rexmpp_iq { /** @brief The sent request. */ - xmlNodePtr request; + rexmpp_xml_t *request; /** @brief A callback to call on reply. */ rexmpp_iq_callback_t cb; /** @brief User-supplied data, to pass to a callback function. */ @@ -228,9 +229,9 @@ struct rexmpp_iq typedef void (*log_function_t) (rexmpp_t *s, int priority, const char *format, va_list args); typedef int (*sasl_property_cb_t) (rexmpp_t *s, rexmpp_sasl_property prop); -typedef int (*xml_in_cb_t) (rexmpp_t *s, xmlNodePtr node); -typedef int (*xml_out_cb_t) (rexmpp_t *s, xmlNodePtr node); -typedef void (*roster_modify_cb_t) (rexmpp_t *s, xmlNodePtr item); +typedef int (*xml_in_cb_t) (rexmpp_t *s, rexmpp_xml_t *node); +typedef int (*xml_out_cb_t) (rexmpp_t *s, rexmpp_xml_t *node); +typedef void (*roster_modify_cb_t) (rexmpp_t *s, rexmpp_xml_t *item); typedef int (*console_print_cb_t) (rexmpp_t *s, const char *format, va_list args); /** @brief Complete connection state */ @@ -298,23 +299,23 @@ struct rexmpp /* Stream-related state. */ struct rexmpp_jid assigned_jid; - xmlNodePtr stream_features; - xmlNodePtr roster_items; + rexmpp_xml_t *stream_features; + rexmpp_xml_t *roster_items; char *roster_ver; - xmlNodePtr roster_presence; - xmlNodePtr roster_events; + rexmpp_xml_t *roster_presence; + rexmpp_xml_t *roster_events; /* Other dynamic data. */ - xmlNodePtr disco_info; + rexmpp_xml_t *disco_info; /* Includes Jingle RTP session candidates; rexmpp prioritizes the ones listed earlier on incoming calls. */ - xmlNodePtr jingle_rtp_description; + rexmpp_xml_t *jingle_rtp_description; /* IQs we're waiting for responses to. */ rexmpp_iq_t *active_iq; /* Cached IQ requests and responses. */ - xmlNodePtr iq_cache; + rexmpp_xml_t *iq_cache; /* Jingle context. */ rexmpp_jingle_ctx_t jingle; @@ -323,7 +324,7 @@ struct rexmpp unsigned int reconnect_number; time_t reconnect_seconds; struct timespec next_reconnect_time; - xmlNodePtr stanza_queue; + rexmpp_xml_t *stanza_queue; uint32_t stanzas_out_count; uint32_t stanzas_out_acknowledged; uint32_t stanzas_in_count; @@ -365,7 +366,7 @@ struct rexmpp ssize_t send_buffer_sent; /* A queue of XML elements to send. */ - xmlNodePtr send_queue; + rexmpp_xml_t *send_queue; /* XML parser context, and current element pointer for building XML nodes with a SAX2 parser interface. */ @@ -431,7 +432,7 @@ rexmpp_err_t rexmpp_stop (rexmpp_t *s); @param[in] node An XML element to send. The library assumes ownership of the element, so it must not be freed by the caller. */ -rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node); +rexmpp_err_t rexmpp_send (rexmpp_t *s, rexmpp_xml_t *node); /** @brief Prepare and send a new info/query request. @@ -450,7 +451,7 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, xmlNodePtr node); rexmpp_err_t rexmpp_iq_new (rexmpp_t *s, const char *type, const char *to, - xmlNodePtr payload, + rexmpp_xml_t *payload, rexmpp_iq_callback_t cb, void *cb_data); @@ -462,7 +463,7 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s, rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s, const char *type, const char *to, - xmlNodePtr payload, + rexmpp_xml_t *payload, rexmpp_iq_callback_t cb, void *cb_data, int fresh); @@ -471,9 +472,9 @@ rexmpp_err_t rexmpp_cached_iq_new (rexmpp_t *s, @brief Reply to an IQ. */ void rexmpp_iq_reply (rexmpp_t *s, - xmlNodePtr req, + rexmpp_xml_t *req, const char *type, - xmlNodePtr payload); + rexmpp_xml_t *payload); /** @brief Determines the maximum time to wait before the next @@ -502,35 +503,6 @@ struct timespec *rexmpp_timeout (rexmpp_t *s, int rexmpp_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds); /** - @brief Compose an 'error' element. -*/ -xmlNodePtr rexmpp_xml_error (const char *type, const char *condition); - -/** - @brief A helper function for XML parsing. - @param[in] str A string to parse. - @param[in] str_len String length. - @returns Parsed XML, or NULL on failure. -*/ -xmlNodePtr rexmpp_xml_parse (const char *str, int str_len); - -/** - @brief A helper function for XML serialisation. - @param[in] node An XML node. - @returns A string (must be freed by the caller). -*/ -char *rexmpp_xml_serialize (xmlNodePtr node); - -/** - @brief Adds an "id" attribute to an XML stanza. - @param[in,out] s ::rexmpp - @param[in] node A pointer to an XML stanza. - @returns The same pointer as on input, for more convenient - composition. -*/ -xmlNodePtr rexmpp_xml_add_id (rexmpp_t *s, xmlNodePtr node); - -/** @brief The logging function. @param[in] s ::rexmpp @param[in] priority A syslog priority. @@ -548,40 +520,6 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...); */ char *rexmpp_get_name (rexmpp_t *s, const char *jid_str); -/** - @brief Compares two XML elements. -*/ -int rexmpp_xml_eq (xmlNodePtr n1, xmlNodePtr n2); - -/** - @brief Matches an XML node against a namespace and an element name. - @param[in] node An XML node to match. - @param[in] namespace An XML namespace. Can be NULL (matches - anything), and it is assumed that the default namespace is - "jabber:client" (so if it is "jabber:client" and an element doesn't - have a namespace defined, this function counts that as a match). - @param[in] name Element name. Can be NULL (matches anything). - @returns 1 on a successful match, 0 otherwise. -*/ -int rexmpp_xml_match (xmlNodePtr node, - const char *namespace, - const char *name); - -/** - @brief Finds a child element of an XML node, which matches the - given namespace and name. - @param[in] node The node containing child nodes. - @param[in] namespace The namespace to look for. - @param[in] name The element name to look for. - @returns A pointer to the first matching child node, or NULL if no - matching child elements found. -*/ -xmlNodePtr rexmpp_xml_find_child (xmlNodePtr node, - const char *namespace, - const char *name); - -xmlNodePtr rexmpp_xml_new_node (const char *name, const char *namespace); - char *rexmpp_gen_id (rexmpp_t *s); /** @@ -593,10 +531,10 @@ char *rexmpp_gen_id (rexmpp_t *s); @returns A pointer to the message announcing an event, or NULL on failure. */ -xmlNodePtr rexmpp_find_event (rexmpp_t *s, - const char *from, - const char *node, - xmlNodePtr *prev_event); +rexmpp_xml_t *rexmpp_find_event (rexmpp_t *s, + const char *from, + const char *node, + rexmpp_xml_t **prev_event); void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len); diff --git a/src/rexmpp_console.c b/src/rexmpp_console.c index b863672..ce325aa 100644 --- a/src/rexmpp_console.c +++ b/src/rexmpp_console.c @@ -13,6 +13,7 @@ #include <string.h> #include "rexmpp.h" +#include "rexmpp_xml.h" #include "rexmpp_openpgp.h" #include "rexmpp_http_upload.h" #include "rexmpp_jingle.h" @@ -30,61 +31,60 @@ void rexmpp_console_printf (rexmpp_t *s, const char *format, ...) } } -char *rexmpp_console_message_string (rexmpp_t *s, xmlNodePtr node) { - char *ret = NULL; - xmlNodePtr openpgp = +const char *rexmpp_console_message_string (rexmpp_t *s, rexmpp_xml_t *node) { + const char *ret = NULL; + rexmpp_xml_t *openpgp = rexmpp_xml_find_child(node, "urn:xmpp:openpgp:0", "openpgp"); if (openpgp != NULL) { int valid; - xmlNodePtr elem = rexmpp_openpgp_decrypt_verify_message(s, node, &valid); + rexmpp_xml_t *elem = rexmpp_openpgp_decrypt_verify_message(s, node, &valid); if (! valid) { rexmpp_console_printf(s, "An invalid OpenPGP message!\n"); } if (elem != NULL) { - xmlNodePtr payload = + rexmpp_xml_t *payload = rexmpp_xml_find_child(elem, "urn:xmpp:openpgp:0", "payload"); if (payload != NULL) { - xmlNodePtr pl_body = + rexmpp_xml_t *pl_body = rexmpp_xml_find_child(payload, "jabber:client", "body"); if (pl_body != NULL) { - ret = xmlNodeGetContent(pl_body); + ret = rexmpp_xml_text_child(pl_body); } } - xmlFreeNode(elem); + rexmpp_xml_free(elem); } } if (ret == NULL) { - xmlNodePtr body = rexmpp_xml_find_child(node, "jabber:client", "body"); - ret = xmlNodeGetContent(body); + rexmpp_xml_t *body = + rexmpp_xml_find_child(node, "jabber:client", "body"); + ret = rexmpp_xml_text_child(body); } return ret; } -void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node) { +void rexmpp_console_on_send (rexmpp_t *s, rexmpp_xml_t *node) { if (rexmpp_xml_match(node, "jabber:client", "message")) { - char *to = xmlGetProp(node, "to"); + const char *to = rexmpp_xml_find_attr_val(node, "to"); if (to != NULL) { /* "from" should be set for verification. */ - char *from = xmlGetProp(node, "from"); - xmlAttrPtr fromProp = NULL; - if (from == NULL) { - fromProp = xmlNewProp(node, "from", to); + int added_from = 0; + if (rexmpp_xml_find_attr_val(node, "from") == NULL) { + rexmpp_xml_add_attr(node, "from", to); + added_from = 1; } - char *str = rexmpp_console_message_string(s, node); - if (fromProp != NULL) { - xmlRemoveProp(fromProp); + const char *str = rexmpp_console_message_string(s, node); + if (added_from) { + rexmpp_xml_remove_attr(node, "from"); } if (str != NULL) { rexmpp_console_printf(s, "You tell %s: %s\n", to, str); - free(str); } - free(to); } } if (rexmpp_xml_match(node, "jabber:client", "presence")) { - char *presence_type = xmlGetProp(node, "type"); - char *presence_to = xmlGetProp(node, "to"); + const char *presence_type = rexmpp_xml_find_attr_val(node, "type"); + const char *presence_to = rexmpp_xml_find_attr_val(node, "to"); if (presence_to == NULL) { rexmpp_console_printf(s, "Becoming %s\n", (presence_type == NULL) ? @@ -106,72 +106,58 @@ void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node) { "Denying %s's presence subscription request.\n", presence_to); } - free(presence_to); - } - if (presence_type != NULL) { - free(presence_type); } } } -void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node) { +void rexmpp_console_on_recv (rexmpp_t *s, rexmpp_xml_t *node) { if (rexmpp_xml_match(node, "jabber:client", "message")) { - xmlNodePtr sent = rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "sent"); + rexmpp_xml_t *sent = rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "sent"); if (sent != NULL) { - xmlNodePtr fwd = + rexmpp_xml_t *fwd = rexmpp_xml_find_child(sent, "urn:xmpp:forward:0", "forwarded"); if (fwd != NULL) { - xmlNodePtr msg = + rexmpp_xml_t *msg = rexmpp_xml_find_child(fwd, "jabber:client", "message"); if (msg != NULL) { - char *to = xmlGetProp(msg, "to"); - char *str = rexmpp_console_message_string(s, msg); + const char *to = rexmpp_xml_find_attr_val(msg, "to"); + const char *str = rexmpp_console_message_string(s, msg); if (str != NULL) { rexmpp_console_printf(s, "You tell %s: %s\n", to, str); - free(str); - } - if (to != NULL) { - free(to); } } } } - xmlNodePtr received = + rexmpp_xml_t *received = rexmpp_xml_find_child(node, "urn:xmpp:carbons:2", "received"); if (received != NULL) { - xmlNodePtr fwd = + rexmpp_xml_t *fwd = rexmpp_xml_find_child(received, "urn:xmpp:forward:0", "forwarded"); if (fwd != NULL) { - xmlNodePtr msg = + rexmpp_xml_t *msg = rexmpp_xml_find_child(fwd, "jabber:client", "message"); if (msg != NULL) { - char *from = xmlGetProp(msg, "from"); - char *str = rexmpp_console_message_string(s, msg); + const char *from = rexmpp_xml_find_attr_val(msg, "from"); + const char *str = rexmpp_console_message_string(s, msg); if (str != NULL) { rexmpp_console_printf(s, "%s tells you: %s\n", from, str); - free(str); - } - if (from != NULL) { - free(from); } } } } - char *from = xmlGetProp(node, "from"); + const char *from = rexmpp_xml_find_attr_val(node, "from"); if (from != NULL) { - char *str = rexmpp_console_message_string(s, node); + const char *str = rexmpp_console_message_string(s, node); if (str != NULL) { rexmpp_console_printf(s, "%s tells you: %s\n", from, str); - free(str); } - free(from); } } if (rexmpp_xml_match(node, "jabber:client", "presence")) { - char *presence_type = xmlGetProp(node, "type"); - char *from = xmlGetProp(node, "from"); + const char *presence_type = rexmpp_xml_find_attr_val(node, "type"); + const char *from = rexmpp_xml_find_attr_val(node, "from"); if (presence_type != NULL && ! strcmp(presence_type, "subscribe")) { rexmpp_console_printf(s, "%s requests a presence subscription\n", from); } else if (presence_type != NULL && ! strcmp(presence_type, "subscribed")) { @@ -183,74 +169,64 @@ void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node) { (presence_type == NULL) ? "available" : presence_type); - xmlNodePtr show = rexmpp_xml_find_child(node, "jabber:client", "show"); + rexmpp_xml_t *show = + rexmpp_xml_find_child(node, "jabber:client", "show"); if (show != NULL) { - char *show_str = xmlNodeGetContent(show); - rexmpp_console_printf(s, " (%s)", show_str); - free(show_str); - show_str = NULL; + rexmpp_console_printf(s, " (%s)", + rexmpp_xml_text_child(show)); } - xmlNodePtr status = rexmpp_xml_find_child(node, "jabber:client", "status"); + rexmpp_xml_t *status = + rexmpp_xml_find_child(node, "jabber:client", "status"); if (status != NULL) { - char *status_str = xmlNodeGetContent(status); - rexmpp_console_printf(s, ": %s", status_str); - free(status_str); - status_str = NULL; + rexmpp_console_printf(s, ": %s", + rexmpp_xml_text_child(status)); } rexmpp_console_printf(s, "\n"); } - if (presence_type != NULL) { - free(presence_type); - } - if (from != NULL) { - free(from); - } } } void rexmpp_console_roster_deleted (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; (void)response; - xmlNodePtr item = + rexmpp_xml_t *item = rexmpp_xml_find_child(rexmpp_xml_find_child(req, "jabber:iq:roster", "query"), "jabber:iq:roster", "item"); - char *jid = xmlGetProp(item, "jid"); + const char *jid = rexmpp_xml_find_attr_val(item, "jid"); if (success) { rexmpp_console_printf(s, "Deleted %s from the roster.\n", jid); } else { rexmpp_console_printf(s, "Failed to delete %s from the roster.\n", jid); } - free(jid); } void rexmpp_console_roster_added (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; (void)response; - xmlNodePtr item = + rexmpp_xml_t *item = rexmpp_xml_find_child(rexmpp_xml_find_child(req, "jabber:iq:roster", "query"), "jabber:iq:roster", "item"); - char *jid = xmlGetProp(item, "jid"); + const char *jid = rexmpp_xml_find_attr_val(item, "jid"); if (success) { rexmpp_console_printf(s, "Added %s into the roster.\n", jid); } else { rexmpp_console_printf(s, "Failed to add %s into the roster.\n", jid); } - free(jid); } void rexmpp_console_on_run (rexmpp_t *s, rexmpp_err_t result) { @@ -272,8 +248,8 @@ void rexmpp_console_on_upload (rexmpp_t *s, void *cb_data, const char *url) { void rexmpp_console_disco_info (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -282,57 +258,44 @@ void rexmpp_console_disco_info (rexmpp_t *s, rexmpp_console_printf(s, "Failed to discover info.\n"); return; } - xmlNodePtr query = + rexmpp_xml_t *query = rexmpp_xml_find_child(response, "http://jabber.org/protocol/disco#info", "query"); if (query == NULL) { rexmpp_console_printf(s, "No disco#info query in response.\n"); return; } - char *from = xmlGetProp(response, "from"); + const char *from = rexmpp_xml_find_attr_val(response, "from"); if (from == NULL) { rexmpp_console_printf(s, "No 'from' property in response.\n"); return; } rexmpp_console_printf(s, "Discovered info for %s:\n", from); - xmlNodePtr child = xmlFirstElementChild(query); + rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query); while (child != NULL) { if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#info", "feature")) { - char *var = xmlGetProp(child, "var"); + const char *var = rexmpp_xml_find_attr_val(child, "var"); rexmpp_console_printf(s, "- feature var %s\n", var); - if (var != NULL) { - free(var); - } } else if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#info", "identity")) { - char *category = xmlGetProp(child, "category"); - char *type = xmlGetProp(child, "type"); - char *name = xmlGetProp(child, "name"); + const char *category = rexmpp_xml_find_attr_val(child, "category"); + const char *type = rexmpp_xml_find_attr_val(child, "type"); + const char *name = rexmpp_xml_find_attr_val(child, "name"); rexmpp_console_printf(s, "- identity name %s, type %s, category %s\n", name, type, category); - if (category != NULL) { - free(category); - } - if (type != NULL) { - free(type); - } - if (name != NULL) { - free(name); - } } else { rexmpp_console_printf(s, "Encountered an unknown disco#info element.\n"); } - child = child->next; + child = rexmpp_xml_next_elem_sibling(child); } rexmpp_console_printf(s, "(end of discovered info for %s)\n", from); - free(from); } void rexmpp_console_disco_items (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -341,49 +304,39 @@ void rexmpp_console_disco_items (rexmpp_t *s, rexmpp_console_printf(s, "Failed to discover items.\n"); return; } - xmlNodePtr query = + rexmpp_xml_t *query = rexmpp_xml_find_child(response, "http://jabber.org/protocol/disco#items", "query"); if (query == NULL) { rexmpp_console_printf(s, "No disco#items query in response.\n"); return; } - char *from = xmlGetProp(response, "from"); + const char *from = rexmpp_xml_find_attr_val(response, "from"); if (from == NULL) { rexmpp_console_printf(s, "No 'from' property in response.\n"); return; } rexmpp_console_printf(s, "Discovered items for %s:\n", from); - xmlNodePtr child = xmlFirstElementChild(query); + rexmpp_xml_t *child = rexmpp_xml_first_elem_child(query); while (child != NULL) { if (rexmpp_xml_match(child, "http://jabber.org/protocol/disco#items", "item")) { - char *jid = xmlGetProp(child, "jid"); - char *name = xmlGetProp(child, "name"); - char *node = xmlGetProp(child, "node"); - rexmpp_console_printf(s, "- item jid %s, name %s, node %s\n", jid, name); - if (jid != NULL) { - free(jid); - } - if (name != NULL) { - free(name); - } - if (node != NULL) { - free(node); - } + const char *jid = rexmpp_xml_find_attr_val(child, "jid"); + const char *name = rexmpp_xml_find_attr_val(child, "name"); + const char *node = rexmpp_xml_find_attr_val(child, "node"); + rexmpp_console_printf(s, "- item jid %s, name %s, node %s\n", jid, name, node); } else { rexmpp_console_printf(s, "Encountered an unknown disco#items element.\n"); } - child = child->next; + child = rexmpp_xml_next_elem_sibling(child); } rexmpp_console_printf(s, "(end of discovered items for %s)\n", from); - free(from); } void rexmpp_console_pubsub_node_deleted (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -398,31 +351,29 @@ void rexmpp_console_pubsub_node_deleted (rexmpp_t *s, void rexmpp_console_blocklist (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; (void)req; if (success) { - xmlNodePtr bl = rexmpp_xml_find_child(response, "urn:xmpp:blocking", "blocklist"); + rexmpp_xml_t *bl = + rexmpp_xml_find_child(response, "urn:xmpp:blocking", "blocklist"); if (bl == NULL) { rexmpp_console_printf(s, "No blocklist element in the response.\n"); return; } rexmpp_console_printf(s, "Block list:"); - xmlNodePtr child = xmlFirstElementChild(bl); + rexmpp_xml_t *child = rexmpp_xml_first_elem_child(bl); while (child != NULL) { if (rexmpp_xml_match(child, "urn:xmpp:blocking", "item")) { - char *jid = xmlGetProp(child, "jid"); + const char *jid = rexmpp_xml_find_attr_val(child, "jid"); rexmpp_console_printf(s, " %s", jid); - if (jid != NULL) { - free(jid); - } } else { rexmpp_console_printf(s, "Encountered an unknown blocklist child element.\n"); } - child = child->next; + child = rexmpp_xml_next_elem_sibling(child); } rexmpp_console_printf(s, "\n"); } else { @@ -432,8 +383,8 @@ void rexmpp_console_blocklist (rexmpp_t *s, void rexmpp_console_blocklist_blocked (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -448,8 +399,8 @@ void rexmpp_console_blocklist_blocked (rexmpp_t *s, void rexmpp_console_blocklist_unblocked (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -466,7 +417,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { /* todo: buffering */ (void)str_len; /* Unused for now (todo). */ char *words_save_ptr; - xmlNodePtr presence; + rexmpp_xml_t *presence; char *word, *jid_str, *msg_text; struct rexmpp_jid jid; word = strtok_r(str, " ", &words_save_ptr); @@ -536,11 +487,14 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { return; } msg_text = jid_str + strlen(jid_str) + 1; - xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message")); - xmlNewNs(msg, "jabber:client", NULL); - xmlNewProp(msg, "to", jid.full); - xmlNewProp(msg, "type", "chat"); - xmlNewTextChild(msg, NULL, "body", msg_text); + + rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client"); + rexmpp_xml_add_id(s, msg); + rexmpp_xml_add_attr(msg, "to", jid.full); + rexmpp_xml_add_attr(msg, "type", "chat"); + rexmpp_xml_t *body = rexmpp_xml_new_elem("body", NULL); + rexmpp_xml_add_text(body, msg_text); + rexmpp_xml_add_child(msg, body); rexmpp_send(s, msg); } @@ -552,9 +506,9 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { return; } msg_text = jid_str + strlen(jid_str) + 1; - xmlNodePtr body = xmlNewNode(NULL, "body"); - xmlNewNs(body, "jabber:client", NULL); - xmlNodeAddContent(body, msg_text); + rexmpp_xml_t *body = + rexmpp_xml_new_elem("body", "jabber:client"); + rexmpp_xml_add_text(body, msg_text); const char *rcpt[2]; rcpt[0] = jid.full; rcpt[1] = NULL; @@ -566,21 +520,20 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { } else if (strcmp(word, "crypt") == 0) { b64 = rexmpp_openpgp_payload(s, body, rcpt, NULL, REXMPP_OX_CRYPT); } - xmlNodePtr openpgp = xmlNewNode(NULL, "openpgp"); - openpgp->ns = xmlNewNs(openpgp, "urn:xmpp:openpgp:0", NULL); - xmlNodeAddContent(openpgp, b64); + rexmpp_xml_t *openpgp = + rexmpp_xml_new_elem("openpgp", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_text(openpgp, b64); free(b64); - xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message")); - xmlNewNs(msg, "jabber:client", NULL); - xmlNewProp(msg, "to", jid.full); - xmlNewProp(msg, "type", "chat"); - xmlAddChild(msg, openpgp); + rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client"); + rexmpp_xml_add_id(s, msg); + rexmpp_xml_add_attr(msg, "to", jid.full); + rexmpp_xml_add_attr(msg, "type", "chat"); + rexmpp_xml_add_child(msg, openpgp); - body = xmlNewNode(NULL, "body"); - xmlNewNs(body, "jabber:client", NULL); - xmlNodeAddContent(body, "This is a secret message."); - xmlAddChild(msg, body); + body = rexmpp_xml_new_elem("body", "jabber:client"); + rexmpp_xml_add_text(body, "This is a secret message."); + rexmpp_xml_add_child(msg, body); rexmpp_send(s, msg); } @@ -593,11 +546,14 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { return; } msg_text = jid_str + strlen(jid_str) + 1; - xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message")); - xmlNewNs(msg, "jabber:client", NULL); - xmlNewProp(msg, "to", jid.full); - xmlNewProp(msg, "type", "groupchat"); - xmlNewTextChild(msg, NULL, "body", msg_text); + + rexmpp_xml_t *msg = rexmpp_xml_new_elem("message", "jabber:client"); + rexmpp_xml_add_id(s, msg); + rexmpp_xml_add_attr(msg, "to", jid.full); + rexmpp_xml_add_attr(msg, "type", "groupchat"); + rexmpp_xml_t *body = rexmpp_xml_new_elem("body", NULL); + rexmpp_xml_add_text(body, msg_text); + rexmpp_xml_add_child(msg, body); rexmpp_send(s, msg); } if (! strcmp(word, "join")) { @@ -615,12 +571,13 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { char *full_jid = malloc(strlen(jid.bare) + strlen(word) + 2); snprintf(full_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s", jid.bare, word); - presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence")); - xmlNewProp(presence, "from", s->assigned_jid.full); - xmlNewProp(presence, "to", full_jid); - xmlNodePtr x = xmlNewNode(NULL, "x"); - xmlNewNs(x, "http://jabber.org/protocol/muc", NULL); - xmlAddChild(presence, x); + presence = rexmpp_xml_new_elem("presence", "jabber:client"); + rexmpp_xml_add_id(s, presence); + rexmpp_xml_add_attr(presence, "from", s->assigned_jid.full); + rexmpp_xml_add_attr(presence, "to", full_jid); + rexmpp_xml_t *x = + rexmpp_xml_new_elem("x", "http://jabber.org/protocol/muc"); + rexmpp_xml_add_child(presence, x); rexmpp_send(s, presence); free(full_jid); } @@ -639,10 +596,11 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { char *full_jid = malloc(strlen(jid.bare) + strlen(word) + 2); snprintf(full_jid, strlen(jid_str) + strlen(word) + 2, "%s/%s", jid.bare, word); - presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence")); - xmlNewProp(presence, "from", s->assigned_jid.full); - xmlNewProp(presence, "to", full_jid); - xmlNewProp(presence, "type", "unavailable"); + presence = rexmpp_xml_new_elem("presence", "jabber:client"); + rexmpp_xml_add_id(s, presence); + rexmpp_xml_add_attr(presence, "from", s->assigned_jid.full); + rexmpp_xml_add_attr(presence, "to", full_jid); + rexmpp_xml_add_attr(presence, "type", "unavailable"); rexmpp_send(s, presence); free(full_jid); } @@ -654,25 +612,26 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { return; } if (! strcmp(word, "list")) { - xmlNodePtr item; + rexmpp_xml_t *item; for (item = s->roster_items; item != NULL; - item = xmlNextElementSibling(item)) { - char *item_jid = xmlGetProp(item, "jid"); - char *item_ask = xmlGetProp(item, "ask"); - char *item_subscription = xmlGetProp(item, "subscription"); + item = item->next) { + const char *item_jid = rexmpp_xml_find_attr_val(item, "jid"); + const char *item_ask = rexmpp_xml_find_attr_val(item, "ask"); + const char *item_subscription = + rexmpp_xml_find_attr_val(item, "subscription"); char *item_presence = "unavailable"; if (s->track_roster_presence) { for (presence = s->roster_presence; presence != NULL; - presence = xmlNextElementSibling(presence)) { - char *presence_from = xmlGetProp(presence, "from"); + presence = presence->next) { + const char *presence_from = + rexmpp_xml_find_attr_val(presence, "from"); if (presence_from != NULL) { rexmpp_jid_parse(presence_from, &jid); if (! strcmp(jid.bare, item_jid)) { item_presence = "available"; } - free(presence_from); } } } @@ -681,28 +640,19 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { "presence = %s\n", item_jid, item_subscription, item_ask, item_presence); - if (item_jid != NULL) { - free(item_jid); - } - if (item_ask != NULL) { - free(item_ask); - } - if (item_subscription != NULL) { - free(item_subscription); - } } } else if (! strcmp(word, "delete")) { word = strtok_r(NULL, " ", &words_save_ptr); if (word == NULL) { return; } - xmlNodePtr delete_item = xmlNewNode(NULL, "item"); - delete_item->ns = xmlNewNs(delete_item, "jabber:iq:roster", NULL); - xmlNewProp(delete_item, "jid", word); - xmlNewProp(delete_item, "subscription", "remove"); - xmlNodePtr delete_query = xmlNewNode(NULL, "query"); - delete_query->ns = xmlNewNs(delete_query, "jabber:iq:roster", NULL); - xmlAddChild(delete_query, delete_item); + rexmpp_xml_t *delete_item = + rexmpp_xml_new_elem("item", "jabber:iq:roster"); + rexmpp_xml_add_attr(delete_item, "jid", word); + rexmpp_xml_add_attr(delete_item, "subscription", "remove"); + rexmpp_xml_t *delete_query = + rexmpp_xml_new_elem("query", "jabber:iq:roster"); + rexmpp_xml_add_child(delete_query, delete_item); rexmpp_iq_new(s, "set", NULL, delete_query, rexmpp_console_roster_deleted, NULL); } else if (! strcmp(word, "add")) { @@ -710,13 +660,13 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { if (word == NULL) { return; } - xmlNodePtr delete_item = xmlNewNode(NULL, "item"); - delete_item->ns = xmlNewNs(delete_item, "jabber:iq:roster", NULL); - xmlNewProp(delete_item, "jid", word); - xmlNodePtr delete_query = xmlNewNode(NULL, "query"); - delete_query->ns = xmlNewNs(delete_query, "jabber:iq:roster", NULL); - xmlAddChild(delete_query, delete_item); - rexmpp_iq_new(s, "set", NULL, delete_query, + rexmpp_xml_t *add_item = + rexmpp_xml_new_elem("item", "jabber:iq:roster"); + rexmpp_xml_add_attr(add_item, "jid", word); + rexmpp_xml_t *add_query = + rexmpp_xml_new_elem("query", "jabber:iq:roster"); + rexmpp_xml_add_child(add_query, add_item); + rexmpp_iq_new(s, "set", NULL, add_query, rexmpp_console_roster_added, NULL); } } @@ -731,9 +681,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { if (word == NULL) { return; } - presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence")); - xmlNewProp(presence, "to", word); - xmlNewProp(presence, "type", "subscribe"); + presence = rexmpp_xml_new_elem("presence", "jabber:client"); + rexmpp_xml_add_id(s, presence); + rexmpp_xml_add_attr(presence, "to", word); + rexmpp_xml_add_attr(presence, "type", "subscribe"); rexmpp_send(s, presence); } if (! strcmp(word, "approve")) { @@ -741,9 +692,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { if (word == NULL) { return; } - presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence")); - xmlNewProp(presence, "to", word); - xmlNewProp(presence, "type", "subscribed"); + presence = rexmpp_xml_new_elem("presence", "jabber:client"); + rexmpp_xml_add_id(s, presence); + rexmpp_xml_add_attr(presence, "to", word); + rexmpp_xml_add_attr(presence, "type", "subscribed"); rexmpp_send(s, presence); } if (! strcmp(word, "deny")) { @@ -751,9 +703,10 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { if (word == NULL) { return; } - presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence")); - xmlNewProp(presence, "to", word); - xmlNewProp(presence, "type", "unsubscribed"); + presence = rexmpp_xml_new_elem("presence", "jabber:client"); + rexmpp_xml_add_id(s, presence); + rexmpp_xml_add_attr(presence, "to", word); + rexmpp_xml_add_attr(presence, "type", "unsubscribed"); rexmpp_send(s, presence); } } @@ -773,7 +726,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { char *sid = strtok_r(NULL, " ", &words_save_ptr); if (sid != NULL) { rexmpp_jingle_session_terminate(s, sid, - rexmpp_xml_new_node("success", + rexmpp_xml_new_elem("success", "urn:xmpp:jingle:1"), NULL); } @@ -781,7 +734,7 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { char *sid = strtok_r(NULL, " ", &words_save_ptr); if (sid != NULL) { rexmpp_jingle_session_terminate(s, sid, - rexmpp_xml_new_node("decline", + rexmpp_xml_new_elem("decline", "urn:xmpp:jingle:1"), NULL); } @@ -824,8 +777,8 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { if (jid == NULL) { return; } - xmlNodePtr query = - rexmpp_xml_new_node("query", + rexmpp_xml_t *query = + rexmpp_xml_new_elem("query", "http://jabber.org/protocol/disco#info"); rexmpp_iq_new(s, "get", jid, query, rexmpp_console_disco_info, NULL); } @@ -834,8 +787,8 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { if (jid == NULL) { return; } - xmlNodePtr query = - rexmpp_xml_new_node("query", + rexmpp_xml_t *query = + rexmpp_xml_new_elem("query", "http://jabber.org/protocol/disco#items"); rexmpp_iq_new(s, "get", jid, query, rexmpp_console_disco_items, NULL); } @@ -865,27 +818,32 @@ void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len) { if (! strcmp(word, "blocklist")) { word = strtok_r(NULL, " ", &words_save_ptr); if (word == NULL) { - xmlNodePtr bl = rexmpp_xml_new_node("blocklist", "urn:xmpp:blocking"); + rexmpp_xml_t *bl = + rexmpp_xml_new_elem("blocklist", "urn:xmpp:blocking"); rexmpp_iq_new(s, "get", NULL, bl, rexmpp_console_blocklist, NULL); } else if (! strcmp(word, "block")) { char *jid = strtok_r(NULL, " ", &words_save_ptr); if (jid == NULL) { return; } - xmlNodePtr bl = rexmpp_xml_new_node("block", "urn:xmpp:blocking"); - xmlNodePtr item = rexmpp_xml_new_node("item", "urn:xmpp:blocking"); - xmlNewProp(item, "jid", jid); - xmlAddChild(bl, item); + rexmpp_xml_t *bl = + rexmpp_xml_new_elem("block", "urn:xmpp:blocking"); + rexmpp_xml_t *item = + rexmpp_xml_new_elem("item", "urn:xmpp:blocking"); + rexmpp_xml_add_attr(item, "jid", jid); + rexmpp_xml_add_child(bl, item); rexmpp_iq_new(s, "set", NULL, bl, rexmpp_console_blocklist_blocked, NULL); } else if (! strcmp(word, "unblock")) { char *jid = strtok_r(NULL, " ", &words_save_ptr); if (jid == NULL) { return; } - xmlNodePtr bl = rexmpp_xml_new_node("unblock", "urn:xmpp:blocking"); - xmlNodePtr item = rexmpp_xml_new_node("item", "urn:xmpp:blocking"); - xmlNewProp(item, "jid", jid); - xmlAddChild(bl, item); + rexmpp_xml_t *bl = + rexmpp_xml_new_elem("unblock", "urn:xmpp:blocking"); + rexmpp_xml_t *item = + rexmpp_xml_new_elem("item", "urn:xmpp:blocking"); + rexmpp_xml_add_attr(item, "jid", jid); + rexmpp_xml_add_child(bl, item); rexmpp_iq_new(s, "set", NULL, bl, rexmpp_console_blocklist_unblocked, NULL); } } diff --git a/src/rexmpp_console.h b/src/rexmpp_console.h index 85e3d75..bb2aed7 100644 --- a/src/rexmpp_console.h +++ b/src/rexmpp_console.h @@ -11,8 +11,8 @@ #include "rexmpp.h" -void rexmpp_console_on_send (rexmpp_t *s, xmlNodePtr node); -void rexmpp_console_on_recv (rexmpp_t *s, xmlNodePtr node); +void rexmpp_console_on_send (rexmpp_t *s, rexmpp_xml_t *node); +void rexmpp_console_on_recv (rexmpp_t *s, rexmpp_xml_t *node); void rexmpp_console_on_run (rexmpp_t *s, rexmpp_err_t result); void rexmpp_console_feed (rexmpp_t *s, char *str, ssize_t str_len); diff --git a/src/rexmpp_http_upload.c b/src/rexmpp_http_upload.c index 62d371b..2552771 100644 --- a/src/rexmpp_http_upload.c +++ b/src/rexmpp_http_upload.c @@ -18,6 +18,7 @@ #endif #include "rexmpp.h" +#include "rexmpp_xml.h" #include "rexmpp_http_upload.h" @@ -43,21 +44,21 @@ void rexmpp_upload_task_finish (struct rexmpp_http_upload_task *task) { void rexmpp_http_upload_slot_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)request; struct rexmpp_http_upload_task *task = ptr; if (success) { - xmlNodePtr slot = rexmpp_xml_find_child(response, "urn:xmpp:http:upload:0", "slot"); - xmlNodePtr put = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "put"); - xmlNodePtr get = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "get"); + rexmpp_xml_t *slot = rexmpp_xml_find_child(response, "urn:xmpp:http:upload:0", "slot"); + rexmpp_xml_t *put = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "put"); + rexmpp_xml_t *get = rexmpp_xml_find_child(slot, "urn:xmpp:http:upload:0", "get"); if (put != NULL && get != NULL) { - char *put_url = xmlGetProp(put, "url"); - char *get_url = xmlGetProp(get, "url"); + const char *put_url = rexmpp_xml_find_attr_val(put, "url"); + const char *get_url = rexmpp_xml_find_attr_val(get, "url"); if (put_url != NULL && get_url != NULL) { - task->get_url = get_url; + task->get_url = strdup(get_url); CURL *ce = curl_easy_init(); curl_easy_setopt(ce, CURLOPT_PRIVATE, task); @@ -65,11 +66,11 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s, curl_easy_setopt(ce, CURLOPT_READDATA, task->fh); curl_easy_setopt(ce, CURLOPT_URL, put_url); - xmlNodePtr header = xmlFirstElementChild(put); + rexmpp_xml_t *header = rexmpp_xml_first_elem_child(put); while (header) { - char *header_name = xmlGetProp(header, "name"); + const char *header_name = rexmpp_xml_find_attr_val(header, "name"); if (header_name != NULL) { - char *header_str = xmlNodeGetContent(header); + const char *header_str = rexmpp_xml_text_child(header); if (header_str != NULL) { size_t full_header_str_len = strlen(header_name) + 3 + strlen(header_str); char *full_header_str = malloc(full_header_str_len); @@ -78,11 +79,9 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s, task->http_headers = curl_slist_append(task->http_headers, full_header_str); free(full_header_str); - free(header_str); } - free(header_name); } - header = header->next; + header = rexmpp_xml_next_elem_sibling(header); } curl_easy_setopt(ce, CURLOPT_HTTPHEADER, task->http_headers); @@ -91,12 +90,6 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s, return; } else { rexmpp_log(s, LOG_ERR, "Unexpected structure for a HTTP file upload slot."); - if (get_url != NULL) { - free(get_url); - } - } - if (put_url != NULL) { - free(put_url); } } else { rexmpp_log(s, LOG_ERR, "Unexpected structure for a HTTP file upload slot."); @@ -109,8 +102,8 @@ void rexmpp_http_upload_slot_cb (rexmpp_t *s, void rexmpp_http_upload_feature_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)response; @@ -120,17 +113,17 @@ void rexmpp_http_upload_feature_cb (rexmpp_t *s, rexmpp_upload_task_finish(task); return; } - xmlNodePtr req = rexmpp_xml_new_node("request", "urn:xmpp:http:upload:0"); - xmlNewProp(req, "filename", task->fname); + rexmpp_xml_t *req = + rexmpp_xml_new_elem("request", "urn:xmpp:http:upload:0"); + rexmpp_xml_add_attr(req, "filename", task->fname); char buf[11]; snprintf(buf, 11, "%u", task->fsize); - xmlNewProp(req, "size", buf); + rexmpp_xml_add_attr(req, "size", buf); if (task->content_type) { - xmlNewProp(req, "content-type", task->content_type); + rexmpp_xml_add_attr(req, "content-type", task->content_type); } - char *to = xmlGetProp(request, "to"); + const char *to = rexmpp_xml_find_attr_val(request, "to"); rexmpp_iq_new(s, "get", to, req, rexmpp_http_upload_slot_cb, task); - free(to); } rexmpp_err_t diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c index 8499dc8..573c193 100644 --- a/src/rexmpp_jingle.c +++ b/src/rexmpp_jingle.c @@ -43,6 +43,7 @@ A/V calls over ICE-UDP + DTLS-SRTP: #endif #include "rexmpp.h" +#include "rexmpp_xml.h" #include "rexmpp_jingle.h" #include "rexmpp_base64.h" @@ -71,15 +72,15 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) { free(session->sid); } if (session->initiate != NULL) { - xmlFreeNodeList(session->initiate); + rexmpp_xml_free_list(session->initiate); } if (session->accept != NULL) { - xmlFreeNodeList(session->accept); + rexmpp_xml_free_list(session->accept); } if (session->ibb_fh != NULL) { fclose(session->ibb_fh); } - #ifdef ENABLE_CALLS +#ifdef ENABLE_CALLS if (session->type == REXMPP_JINGLE_SESSION_MEDIA) { int i; for (i = 0; i < 2; i++) { @@ -126,7 +127,7 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) { session->turn_password = NULL; } } - #endif +#endif free(session); } @@ -267,8 +268,8 @@ void rexmpp_jingle_stop (rexmpp_t *s) { void rexmpp_jingle_accept_file_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)request; @@ -292,15 +293,16 @@ rexmpp_jingle_accept_file (rexmpp_t *s, path, strerror(errno)); return REXMPP_E_OTHER; } - xmlNodePtr jingle = session->initiate; - xmlNodePtr content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content"); + rexmpp_xml_t *jingle = session->initiate; + rexmpp_xml_t *content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content"); - xmlNodePtr new_jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1"); - xmlNewProp(new_jingle, "action", "session-accept"); - xmlNewProp(new_jingle, "responder", s->assigned_jid.full); - xmlNewProp(new_jingle, "sid", session->sid); - xmlAddChild(new_jingle, xmlCopyNode(content, 1)); - session->accept = xmlCopyNode(new_jingle, 1); + rexmpp_xml_t *new_jingle = + rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1"); + rexmpp_xml_add_attr(new_jingle, "action", "session-accept"); + rexmpp_xml_add_attr(new_jingle, "responder", s->assigned_jid.full); + rexmpp_xml_add_attr(new_jingle, "sid", session->sid); + rexmpp_xml_add_child(new_jingle, rexmpp_xml_clone(content)); + session->accept = rexmpp_xml_clone(new_jingle); return rexmpp_iq_new(s, "set", session->jid, new_jingle, rexmpp_jingle_accept_file_cb, strdup(session->sid)); } @@ -319,8 +321,8 @@ rexmpp_jingle_accept_file_by_id (rexmpp_t *s, void rexmpp_jingle_session_terminate_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)request; @@ -336,24 +338,27 @@ void rexmpp_jingle_session_terminate_cb (rexmpp_t *s, rexmpp_err_t rexmpp_jingle_session_terminate (rexmpp_t *s, const char *sid, - xmlNodePtr reason_node, + rexmpp_xml_t *reason_node, const char *reason_text) { rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid); if (session == NULL) { return REXMPP_E_OTHER; } - xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1"); - xmlNewProp(jingle, "action", "session-terminate"); - xmlNewProp(jingle, "sid", sid); - xmlNodePtr reason = rexmpp_xml_new_node("reason", "urn:xmpp:jingle:1"); + rexmpp_xml_t *jingle = + rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1"); + rexmpp_xml_add_attr(jingle, "action", "session-terminate"); + rexmpp_xml_add_attr(jingle, "sid", sid); + rexmpp_xml_t *reason = + rexmpp_xml_new_elem("reason", "urn:xmpp:jingle:1"); if (reason_text != NULL) { - xmlNodePtr text = rexmpp_xml_new_node("text", "urn:xmpp:jingle:1"); - xmlNodeAddContent(text, reason_text); - xmlAddChild(reason, text); + rexmpp_xml_t *text = + rexmpp_xml_new_elem("text", "urn:xmpp:jingle:1"); + rexmpp_xml_add_text(text, reason_text); + rexmpp_xml_add_child(reason, text); } - xmlAddChild(reason, reason_node); - xmlAddChild(jingle, reason); + rexmpp_xml_add_child(reason, reason_node); + rexmpp_xml_add_child(jingle, reason); rexmpp_err_t ret = rexmpp_iq_new(s, "set", session->jid, jingle, rexmpp_jingle_session_terminate_cb, strdup(sid)); @@ -363,8 +368,8 @@ rexmpp_jingle_session_terminate (rexmpp_t *s, void rexmpp_jingle_send_file_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)request; @@ -416,31 +421,33 @@ rexmpp_jingle_send_file (rexmpp_t *s, char *sid = rexmpp_gen_id(s); char *ibb_sid = rexmpp_gen_id(s); - xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1"); - xmlNewProp(jingle, "action", "session-initiate"); - xmlNewProp(jingle, "sid", sid); - xmlNewProp(jingle, "initiator", s->assigned_jid.full); - - xmlNodePtr content = rexmpp_xml_new_node("content", "urn:xmpp:jingle:1"); - xmlNewProp(content, "creator", "initiator"); - xmlNewProp(content, "name", "IBB file"); - xmlAddChild(jingle, content); - - xmlNodePtr transport = - rexmpp_xml_new_node("transport", "urn:xmpp:jingle:transports:ibb:1"); - xmlNewProp(transport, "block-size", "4096"); - xmlNewProp(transport, "sid", ibb_sid); - xmlAddChild(content, transport); - xmlNodePtr description = - rexmpp_xml_new_node("description", "urn:xmpp:jingle:apps:file-transfer:5"); - xmlAddChild(content, description); - xmlNodePtr file = - rexmpp_xml_new_node("file", "urn:xmpp:jingle:apps:file-transfer:5"); - xmlAddChild(description, file); - xmlNodePtr file_name = - rexmpp_xml_new_node("name", "urn:xmpp:jingle:apps:file-transfer:5"); - xmlNodeAddContent(file_name, basename(path)); - xmlAddChild(file, file_name); + rexmpp_xml_t *jingle = + rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1"); + rexmpp_xml_add_attr(jingle, "action", "session-initiate"); + rexmpp_xml_add_attr(jingle, "sid", sid); + rexmpp_xml_add_attr(jingle, "initiator", s->assigned_jid.full); + + rexmpp_xml_t *content = + rexmpp_xml_new_elem("content", "urn:xmpp:jingle:1"); + rexmpp_xml_add_attr(content, "creator", "initiator"); + rexmpp_xml_add_attr(content, "name", "IBB file"); + rexmpp_xml_add_child(jingle, content); + + rexmpp_xml_t *transport = + rexmpp_xml_new_elem("transport", "urn:xmpp:jingle:transports:ibb:1"); + rexmpp_xml_add_attr(transport, "block-size", "4096"); + rexmpp_xml_add_attr(transport, "sid", ibb_sid); + rexmpp_xml_add_child(content, transport); + rexmpp_xml_t *description = + rexmpp_xml_new_elem("description", "urn:xmpp:jingle:apps:file-transfer:5"); + rexmpp_xml_add_child(content, description); + rexmpp_xml_t *file = + rexmpp_xml_new_elem("file", "urn:xmpp:jingle:apps:file-transfer:5"); + rexmpp_xml_add_child(description, file); + rexmpp_xml_t *file_name = + rexmpp_xml_new_elem("name", "urn:xmpp:jingle:apps:file-transfer:5"); + rexmpp_xml_add_text(file_name, basename(path)); + rexmpp_xml_add_child(file, file_name); char *hash_base64 = NULL; size_t hash_base64_len = 0; @@ -448,11 +455,12 @@ rexmpp_jingle_send_file (rexmpp_t *s, gcry_md_get_algo_dlen(GCRY_MD_SHA256), &hash_base64, &hash_base64_len); - xmlNodePtr file_hash = rexmpp_xml_new_node("hash", "urn:xmpp:hashes:2"); - xmlNewProp(file_hash, "algo", "sha-256"); - xmlNodeAddContent(file_hash, hash_base64); + rexmpp_xml_t *file_hash = + rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2"); + rexmpp_xml_add_attr(file_hash, "algo", "sha-256"); + rexmpp_xml_add_text(file_hash, hash_base64); free(hash_base64); - xmlAddChild(file, file_hash); + rexmpp_xml_add_child(file, file_hash); hash_base64 = NULL; hash_base64_len = 0; @@ -460,26 +468,26 @@ rexmpp_jingle_send_file (rexmpp_t *s, gcry_md_get_algo_dlen(GCRY_MD_SHA3_256), &hash_base64, &hash_base64_len); - file_hash = rexmpp_xml_new_node("hash", "urn:xmpp:hashes:2"); - xmlNewProp(file_hash, "algo", "sha3-256"); - xmlNodeAddContent(file_hash, hash_base64); + file_hash = rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2"); + rexmpp_xml_add_attr(file_hash, "algo", "sha3-256"); + rexmpp_xml_add_text(file_hash, hash_base64); free(hash_base64); - xmlAddChild(file, file_hash); + rexmpp_xml_add_child(file, file_hash); gcry_md_close(hd); long fsize = ftell(fh); fseek(fh, 0, SEEK_SET); snprintf(buf, 11, "%ld", fsize); - xmlNodePtr file_size = - rexmpp_xml_new_node("size", "urn:xmpp:jingle:apps:file-transfer:5"); - xmlNodeAddContent(file_size, buf); - xmlAddChild(file, file_size); + rexmpp_xml_t *file_size = + rexmpp_xml_new_elem("size", "urn:xmpp:jingle:apps:file-transfer:5"); + rexmpp_xml_add_text(file_size, buf); + rexmpp_xml_add_child(file, file_size); rexmpp_jingle_session_t *sess = rexmpp_jingle_session_create(s, strdup(jid), sid, REXMPP_JINGLE_SESSION_FILE, 1); if (sess != NULL) { - sess->initiate = xmlCopyNode(jingle, 1); + sess->initiate = rexmpp_xml_clone(jingle); sess->ibb_sid = ibb_sid; sess->ibb_fh = fh; return rexmpp_iq_new(s, "set", sess->jid, jingle, @@ -492,8 +500,8 @@ rexmpp_jingle_send_file (rexmpp_t *s, void rexmpp_jingle_ibb_close_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)request; @@ -509,8 +517,8 @@ void rexmpp_jingle_ibb_close_cb (rexmpp_t *s, void rexmpp_jingle_ibb_send_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)request; @@ -529,8 +537,9 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s, return; } if (feof(session->ibb_fh)) { - xmlNodePtr close = rexmpp_xml_new_node("close", "http://jabber.org/protocol/ibb"); - xmlNewProp(close, "sid", session->ibb_sid); + rexmpp_xml_t *close = + rexmpp_xml_new_elem("close", "http://jabber.org/protocol/ibb"); + rexmpp_xml_add_attr(close, "sid", session->ibb_sid); rexmpp_iq_new(s, "set", session->jid, close, rexmpp_jingle_ibb_close_cb, sid); return; @@ -538,15 +547,16 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s, char buf[4096]; size_t len = fread(buf, 1, 4096, session->ibb_fh); if (len > 0) { - xmlNodePtr data = rexmpp_xml_new_node("data", "http://jabber.org/protocol/ibb"); - xmlNewProp(data, "sid", session->ibb_sid); + rexmpp_xml_t *data = + rexmpp_xml_new_elem("data", "http://jabber.org/protocol/ibb"); + rexmpp_xml_add_attr(data, "sid", session->ibb_sid); char *out = NULL; size_t out_len = 0; rexmpp_base64_to(buf, len, &out, &out_len); - xmlNodeAddContent(data, out); + rexmpp_xml_add_text(data, out); free(out); snprintf(buf, 11, "%u", session->ibb_seq); - xmlNewProp(data, "seq", buf); + rexmpp_xml_add_attr(data, "seq", buf); session->ibb_seq++; rexmpp_iq_new(s, "set", session->jid, data, rexmpp_jingle_ibb_send_cb, sid); @@ -554,7 +564,7 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s, } else { rexmpp_log(s, LOG_ERR, "Failed to read from a file: %s ", strerror(errno)); rexmpp_jingle_session_terminate(s, sid, - rexmpp_xml_new_node("media-error", + rexmpp_xml_new_elem("media-error", "urn:xmpp:jingle:1"), "File reading error"); } @@ -565,8 +575,8 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s, #ifdef ENABLE_CALLS void rexmpp_jingle_call_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)request; @@ -581,31 +591,29 @@ void rexmpp_jingle_call_cb (rexmpp_t *s, void rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess, - xmlNodePtr transport) + rexmpp_xml_t *transport) { if (sess->ice_agent == NULL) { /* Must be an incoming call; just add candidates to session-initiate's transport. */ - xmlNodePtr old_transport = + rexmpp_xml_t *old_transport = rexmpp_xml_find_child(rexmpp_xml_find_child(sess->initiate, "urn:xmpp:jingle:1", "content"), "urn:xmpp:jingle:transports:ice-udp:1", "transport"); - xmlNodePtr candidate = xmlFirstElementChild(transport); + rexmpp_xml_t *candidate = rexmpp_xml_first_elem_child(transport); while (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1", "candidate")) { - xmlAddChild(old_transport, xmlCopyNode(candidate, 1)); - candidate = candidate->next; + rexmpp_xml_add_child(old_transport, rexmpp_xml_clone(candidate)); + candidate = rexmpp_xml_next_elem_sibling(candidate); } return; } - char *ufrag = xmlGetProp(transport, "ufrag"); - char *password = xmlGetProp(transport, "pwd"); + const char *ufrag = rexmpp_xml_find_attr_val(transport, "ufrag"); + const char *password = rexmpp_xml_find_attr_val(transport, "pwd"); nice_agent_set_remote_credentials(sess->ice_agent, sess->ice_stream_id, ufrag, password); - free(ufrag); - free(password); int component_id; @@ -614,12 +622,12 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess, nice_agent_get_remote_candidates(sess->ice_agent, sess->ice_stream_id, component_id); - xmlNodePtr candidate = xmlFirstElementChild(transport); + rexmpp_xml_t *candidate = rexmpp_xml_first_elem_child(transport); while (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1", "candidate")) { - char *component = xmlGetProp(candidate, "component"); + const char *component = rexmpp_xml_find_attr_val(candidate, "component"); if (component[0] == component_id + '0') { - char *type_str = xmlGetProp(candidate, "type"); + const char *type_str = rexmpp_xml_find_attr_val(candidate, "type"); int type_n = NICE_CANDIDATE_TYPE_HOST; if (strcmp(type_str, "host") == 0) { type_n = NICE_CANDIDATE_TYPE_HOST; @@ -630,38 +638,32 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess, } else if (strcmp(type_str, "relay") == 0) { type_n = NICE_CANDIDATE_TYPE_RELAYED; } - free(type_str); NiceCandidate *c = nice_candidate_new(type_n); c->component_id = component_id; c->stream_id = sess->ice_stream_id; - char *foundation = xmlGetProp(candidate, "foundation"); + const char *foundation = rexmpp_xml_find_attr_val(candidate, "foundation"); strncpy(c->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION - 1); c->foundation[NICE_CANDIDATE_MAX_FOUNDATION - 1] = 0; - free(foundation); c->transport = NICE_CANDIDATE_TRANSPORT_UDP; - char *priority = xmlGetProp(candidate, "priority"); + const char *priority = rexmpp_xml_find_attr_val(candidate, "priority"); c->priority = atoi(priority); - free(priority); - char *ip = xmlGetProp(candidate, "ip"); + const char *ip = rexmpp_xml_find_attr_val(candidate, "ip"); if (! nice_address_set_from_string(&c->addr, ip)) { rexmpp_log(sess->s, LOG_ERR, "Failed to parse an ICE-UDP candidate's address: %s", ip); } - free(ip); - char *port = xmlGetProp(candidate, "port"); + const char *port = rexmpp_xml_find_attr_val(candidate, "port"); nice_address_set_port(&c->addr, atoi(port)); - free(port); remote_candidates = g_slist_prepend(remote_candidates, c); } - free(component); - candidate = candidate->next; + candidate = rexmpp_xml_next_elem_sibling(candidate); } if (remote_candidates != NULL) { nice_agent_set_remote_candidates(sess->ice_agent, sess->ice_stream_id, @@ -674,7 +676,7 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess, /* Checks whether we are in the active (client) role for DTLS, based on either "session-initiate" or "session-accept" message. */ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate) { - xmlNodePtr fingerprint = + rexmpp_xml_t *fingerprint = rexmpp_xml_find_child (rexmpp_xml_find_child (rexmpp_xml_find_child @@ -687,7 +689,7 @@ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate in_initiate ? "initiate" : "accept"); return 0; } - char *fingerprint_setup = xmlGetProp(fingerprint, "setup"); + const char *fingerprint_setup = rexmpp_xml_find_attr_val(fingerprint, "setup"); if (fingerprint_setup == NULL) { rexmpp_log(sess->s, LOG_ERR, "No 'setup' attribute for a fingerprint element"); return 0; @@ -706,15 +708,14 @@ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate active = (strcmp(fingerprint_setup, "active") == 0); } } - free(fingerprint_setup); return active; } void rexmpp_transport_info_call_cb (rexmpp_t *s, void *ptr, - xmlNodePtr request, - xmlNodePtr response, + rexmpp_xml_t *request, + rexmpp_xml_t *response, int success) { (void)request; @@ -762,90 +763,87 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent, } gnutls_free(cert_list); - xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1"); - xmlNewProp(jingle, "sid", sess->sid); + rexmpp_xml_t *jingle = rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1"); + rexmpp_xml_add_attr(jingle, "sid", sess->sid); - xmlNodePtr content = rexmpp_xml_new_node("content", "urn:xmpp:jingle:1"); - xmlNewProp(content, "creator", "initiator"); - xmlNewProp(content, "senders", "both"); - xmlNodePtr description; + rexmpp_xml_t *content = rexmpp_xml_new_elem("content", "urn:xmpp:jingle:1"); + rexmpp_xml_add_attr(content, "creator", "initiator"); + rexmpp_xml_add_attr(content, "senders", "both"); + rexmpp_xml_t *description; if (sess->initiator) { - xmlNewProp(jingle, "action", "session-initiate"); - xmlNewProp(jingle, "initiator", sess->s->assigned_jid.full); - xmlNewProp(content, "name", "call"); + rexmpp_xml_add_attr(jingle, "action", "session-initiate"); + rexmpp_xml_add_attr(jingle, "initiator", sess->s->assigned_jid.full); + rexmpp_xml_add_attr(content, "name", "call"); /* https://datatracker.ietf.org/doc/html/rfc4568 */ - xmlNodePtr encryption = - rexmpp_xml_new_node("encryption", "urn:xmpp:jingle:apps:rtp:1"); - xmlNewProp(encryption, "required", "true"); - xmlAddChild(content, encryption); - xmlNodePtr crypto = rexmpp_xml_new_node("crypto", "urn:xmpp:jingle:apps:rtp:1"); - xmlNewProp(crypto, "crypto-suite", "AES_CM_128_HMAC_SHA1_80"); - xmlNewProp(crypto, "tag", "1"); - xmlAddChild(encryption, crypto); - - description = xmlCopyNode(sess->s->jingle_rtp_description, 1); + rexmpp_xml_t *encryption = + rexmpp_xml_new_elem("encryption", "urn:xmpp:jingle:apps:rtp:1"); + rexmpp_xml_add_attr(encryption, "required", "true"); + rexmpp_xml_add_child(content, encryption); + rexmpp_xml_t *crypto = + rexmpp_xml_new_elem("crypto", "urn:xmpp:jingle:apps:rtp:1"); + rexmpp_xml_add_attr(crypto, "crypto-suite", "AES_CM_128_HMAC_SHA1_80"); + rexmpp_xml_add_attr(crypto, "tag", "1"); + rexmpp_xml_add_child(encryption, crypto); + + description = rexmpp_xml_clone(sess->s->jingle_rtp_description); } else { - xmlNodePtr init_jingle = sess->initiate; - xmlNodePtr init_content = + rexmpp_xml_t *init_jingle = sess->initiate; + rexmpp_xml_t *init_content = rexmpp_xml_find_child(init_jingle, "urn:xmpp:jingle:1", "content"); - char *init_content_name = xmlGetProp(init_content, "name"); + const char *init_content_name = rexmpp_xml_find_attr_val(init_content, "name"); if (init_content_name != NULL) { - xmlNewProp(content, "name", init_content_name); - free(init_content_name); + rexmpp_xml_add_attr(content, "name", init_content_name); } else { rexmpp_log(sess->s, LOG_ERR, "Empty content name for Jingle session %s with %s", sess->sid, sess->jid); } - xmlNewProp(jingle, "action", "session-accept"); - xmlNewProp(jingle, "initiator", sess->jid); - xmlNewProp(jingle, "responder", sess->s->assigned_jid.full); + rexmpp_xml_add_attr(jingle, "action", "session-accept"); + rexmpp_xml_add_attr(jingle, "initiator", sess->jid); + rexmpp_xml_add_attr(jingle, "responder", sess->s->assigned_jid.full); - description = xmlCopyNode(sess->s->jingle_rtp_description, 2); + description = rexmpp_xml_clone(sess->s->jingle_rtp_description); /* Find the first matching payload-type and add that */ - xmlNodePtr pl_type = - xmlFirstElementChild(sess->s->jingle_rtp_description); - xmlNodePtr selected_pl = NULL; + rexmpp_xml_t *pl_type = + rexmpp_xml_first_elem_child(sess->s->jingle_rtp_description); + rexmpp_xml_t *selected_pl = NULL; while (pl_type != NULL && selected_pl == NULL) { if (rexmpp_xml_match(pl_type, "urn:xmpp:jingle:apps:rtp:1", "payload-type")) { - char *pl_id = xmlGetProp(pl_type, "id"); + const char *pl_id = rexmpp_xml_find_attr_val(pl_type, "id"); if (pl_id != NULL) { int pl_id_num = atoi(pl_id); - xmlNodePtr proposed_pl_type = - xmlFirstElementChild + rexmpp_xml_t *proposed_pl_type = + rexmpp_xml_first_elem_child (rexmpp_xml_find_child (rexmpp_xml_find_child(sess->initiate, "urn:xmpp:jingle:1", "content"), "urn:xmpp:jingle:apps:rtp:1", "description")); while (proposed_pl_type != NULL && selected_pl == NULL) { if (rexmpp_xml_match(proposed_pl_type, "urn:xmpp:jingle:apps:rtp:1", "payload-type")) { - char *proposed_pl_id = xmlGetProp(proposed_pl_type, "id"); + const char *proposed_pl_id = rexmpp_xml_find_attr_val(proposed_pl_type, "id"); if (proposed_pl_id != NULL) { int proposed_pl_id_num = atoi(proposed_pl_id); if (pl_id_num < 96 && pl_id_num == proposed_pl_id_num) { selected_pl = pl_type; } else { - char *pl_name = xmlGetProp(pl_type, "name"); + const char *pl_name = rexmpp_xml_find_attr_val(pl_type, "name"); if (pl_name != NULL) { - char *proposed_pl_name = xmlGetProp(proposed_pl_type, "name"); + const char *proposed_pl_name = + rexmpp_xml_find_attr_val(proposed_pl_type, "name"); if (proposed_pl_name != NULL) { if (strcmp(pl_name, proposed_pl_name) == 0) { /* todo: compare clock rates, numbers of channels, parameters */ selected_pl = pl_type; } - free(proposed_pl_name); } - free(pl_name); } } - free(proposed_pl_id); } } - proposed_pl_type = proposed_pl_type->next; + proposed_pl_type = rexmpp_xml_next_elem_sibling(proposed_pl_type); } - free(pl_id); } else { rexmpp_log(sess->s, LOG_ERR, "No 'id' specified for a pyaload-type element."); @@ -854,7 +852,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent, pl_type = pl_type->next; } if (selected_pl != NULL) { - xmlAddChild(description, xmlCopyNode(selected_pl, 1)); + rexmpp_xml_add_child(description, rexmpp_xml_clone(selected_pl)); } else { rexmpp_log(sess->s, LOG_ERR, "No suitable payload type found"); /* todo: fail if it's NULL, though it shouldn't happen, since @@ -862,69 +860,70 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent, } } - xmlAddChild(jingle, content); - xmlAddChild(content, description); + rexmpp_xml_add_child(jingle, content); + rexmpp_xml_add_child(content, description); if (sess->rtcp_mux) { - xmlNodePtr rtcp_mux = - rexmpp_xml_new_node("rtcp-mux", "urn:xmpp:jingle:apps:rtp:1"); - xmlAddChild(description, rtcp_mux); + rexmpp_xml_t *rtcp_mux = + rexmpp_xml_new_elem("rtcp-mux", "urn:xmpp:jingle:apps:rtp:1"); + rexmpp_xml_add_child(description, rtcp_mux); } - xmlNodePtr transport = - rexmpp_xml_new_node("transport", "urn:xmpp:jingle:transports:ice-udp:1"); + rexmpp_xml_t *transport = + rexmpp_xml_new_elem("transport", "urn:xmpp:jingle:transports:ice-udp:1"); gchar *ufrag = NULL; gchar *password = NULL; nice_agent_get_local_credentials(agent, stream_id, &ufrag, &password); - xmlNewProp(transport, "ufrag", ufrag); - xmlNewProp(transport, "pwd", password); + rexmpp_xml_add_attr(transport, "ufrag", ufrag); + rexmpp_xml_add_attr(transport, "pwd", password); g_free(ufrag); g_free(password); - xmlAddChild(content, transport); + rexmpp_xml_add_child(content, transport); int component_id; - xmlNodePtr postponed_candidates = NULL; + rexmpp_xml_t *postponed_candidates = NULL; for (component_id = 1; component_id <= (sess->rtcp_mux ? 1 : 2); component_id++) { GSList *candidates = nice_agent_get_local_candidates(agent, stream_id, component_id); GSList *cand_cur = candidates; int cand_num = 0; while (cand_cur != NULL) { - xmlNodePtr candidate = - rexmpp_xml_new_node("candidate", "urn:xmpp:jingle:transports:ice-udp:1"); + rexmpp_xml_t *candidate = + rexmpp_xml_new_elem("candidate", "urn:xmpp:jingle:transports:ice-udp:1"); char buf[INET6_ADDRSTRLEN]; NiceCandidate *c = (NiceCandidate *)cand_cur->data; snprintf(buf, 11, "%u", component_id); - xmlNewProp(candidate, "component", buf); - xmlNewProp(candidate, "foundation", c->foundation); - xmlNewProp(candidate, "generation", "0"); + rexmpp_xml_add_attr(candidate, "component", buf); + rexmpp_xml_add_attr(candidate, "foundation", c->foundation); + rexmpp_xml_add_attr(candidate, "generation", "0"); char *cid = rexmpp_gen_id(sess->s); - xmlNewProp(candidate, "id", cid); + rexmpp_xml_add_attr(candidate, "id", cid); free(cid); nice_address_to_string(&c->addr, buf); - xmlNewProp(candidate, "ip", buf); + rexmpp_xml_add_attr(candidate, "ip", buf); snprintf(buf, 11, "%u", nice_address_get_port(&c->addr)); - xmlNewProp(candidate, "port", buf); - xmlNewProp(candidate, "network", "0"); - xmlNewProp(candidate, "protocol", "udp"); + rexmpp_xml_add_attr(candidate, "port", buf); + rexmpp_xml_add_attr(candidate, "network", "0"); + rexmpp_xml_add_attr(candidate, "protocol", "udp"); snprintf(buf, 11, "%u", c->priority); - xmlNewProp(candidate, "priority", buf); + rexmpp_xml_add_attr(candidate, "priority", buf); char *nice_type[] = {"host", "srflx", "prflx", "relay"}; if (c->type < 4) { - xmlNewProp(candidate, "type", nice_type[c->type]); + rexmpp_xml_add_attr(candidate, "type", nice_type[c->type]); } /* Can't send too many candidates, since stanza sizes are usually - limited, and then it breaks the stream. Limiting to 10 per - component, sending the rest later, via transport-info. */ + limited, and then it breaks the stream. Limiting to 10 per + component, sending the rest later, via transport-info. */ if (cand_num < 10) { - xmlAddChild(transport, candidate); + rexmpp_xml_add_child(transport, candidate); } else { - xmlNodePtr jingle_ti = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1"); - xmlNewProp(jingle_ti, "sid", sess->sid); - xmlNewProp(jingle_ti, "action", "transport-info"); - xmlNodePtr content_copy = xmlCopyNode(content, 2); - xmlNodePtr transport_copy = xmlCopyNode(transport, 2); - xmlAddChild(jingle_ti, content_copy); - xmlAddChild(content_copy, transport_copy); - xmlAddChild(transport_copy, candidate); + rexmpp_xml_t *jingle_ti = + rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1"); + rexmpp_xml_add_attr(jingle_ti, "sid", sess->sid); + rexmpp_xml_add_attr(jingle_ti, "action", "transport-info"); + rexmpp_xml_t *content_copy = rexmpp_xml_clone(content); + rexmpp_xml_t *transport_copy = rexmpp_xml_clone(transport); + rexmpp_xml_add_child(jingle_ti, content_copy); + rexmpp_xml_add_child(content_copy, transport_copy); + rexmpp_xml_add_child(transport_copy, candidate); jingle_ti->next = postponed_candidates; postponed_candidates = jingle_ti; } @@ -936,24 +935,24 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent, } } - xmlNodePtr fingerprint = - rexmpp_xml_new_node("fingerprint", "urn:xmpp:jingle:apps:dtls:0"); - xmlNewProp(fingerprint, "hash", "sha-256"); + rexmpp_xml_t *fingerprint = + rexmpp_xml_new_elem("fingerprint", "urn:xmpp:jingle:apps:dtls:0"); + rexmpp_xml_add_attr(fingerprint, "hash", "sha-256"); if (sess->initiator) { - xmlNewProp(fingerprint, "setup", "actpass"); + rexmpp_xml_add_attr(fingerprint, "setup", "actpass"); } else if (rexmpp_jingle_dtls_is_active(sess, 1)) { - xmlNewProp(fingerprint, "setup", "active"); + rexmpp_xml_add_attr(fingerprint, "setup", "active"); } else { - xmlNewProp(fingerprint, "setup", "passive"); + rexmpp_xml_add_attr(fingerprint, "setup", "passive"); } - xmlNodeAddContent(fingerprint, fp_str); - xmlAddChild(transport, fingerprint); + rexmpp_xml_add_text(fingerprint, fp_str); + rexmpp_xml_add_child(transport, fingerprint); if (sess->initiator) { - sess->initiate = xmlCopyNode(jingle, 1); + sess->initiate = rexmpp_xml_clone(jingle); } else { - sess->accept = xmlCopyNode(jingle, 1); + sess->accept = rexmpp_xml_clone(jingle); } rexmpp_iq_new(sess->s, "set", sess->jid, jingle, @@ -962,7 +961,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent, /* Now send transport-info messages with candidates that didn't fit initially. */ while (postponed_candidates != NULL) { - xmlNodePtr pc_next = postponed_candidates->next; + rexmpp_xml_t *pc_next = postponed_candidates->next; postponed_candidates->next = NULL; rexmpp_iq_new(sess->s, "set", sess->jid, postponed_candidates, rexmpp_transport_info_call_cb, strdup(sess->sid)); @@ -1060,7 +1059,7 @@ rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess, recvfrom(fd, &c, 1, MSG_PEEK, (struct sockaddr *) &cli_addr, &cli_addr_size); if (ret > 0) { - return 1; + return 1; } return 0; @@ -1306,25 +1305,25 @@ void rexmpp_jingle_stun_dns_cb (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *res void rexmpp_jingle_turn_cb (rexmpp_t *s, void *sess_ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)req; rexmpp_jingle_session_t *sess = sess_ptr; if (success) { /* use credentials */ - xmlNodePtr services = xmlFirstElementChild(response); + rexmpp_xml_t *services = rexmpp_xml_first_elem_child(response); if (rexmpp_xml_match(services, "urn:xmpp:extdisco:2", "services")) { - xmlNodePtr service = xmlFirstElementChild(services); + rexmpp_xml_t *service = rexmpp_xml_first_elem_child(services); while (service != NULL) { if (rexmpp_xml_match(service, "urn:xmpp:extdisco:2", "service")) { - char *type = xmlGetProp(service, "type"); - char *transport = xmlGetProp(service, "transport"); - char *host = xmlGetProp(service, "host"); - char *port = xmlGetProp(service, "port"); - char *username = xmlGetProp(service, "username"); - char *password = xmlGetProp(service, "password"); + const char *type = rexmpp_xml_find_attr_val(service, "type"); + const char *transport = rexmpp_xml_find_attr_val(service, "transport"); + const char *host = rexmpp_xml_find_attr_val(service, "host"); + const char *port = rexmpp_xml_find_attr_val(service, "port"); + const char *username = rexmpp_xml_find_attr_val(service, "username"); + const char *password = rexmpp_xml_find_attr_val(service, "password"); if (sess->stun_host == NULL && type != NULL && transport != NULL && host != NULL && port != NULL && @@ -1344,27 +1343,8 @@ void rexmpp_jingle_turn_cb (rexmpp_t *s, sess->turn_password = strdup(password); rexmpp_log(s, LOG_DEBUG, "Setting TURN server to %s:%s", host, port); } - - if (type != NULL) { - free(type); - } - if (transport != NULL) { - free(transport); - } - if (host != NULL) { - free(host); - } - if (port != NULL) { - free(port); - } - if (username != NULL) { - free(username); - } - if (password != NULL) { - free(password); - } } - service = service->next; + service = rexmpp_xml_next_elem_sibling(service); } if (sess->stun_host != NULL) { /* Resolve, then resolve STUN host, then connect. */ @@ -1392,25 +1372,24 @@ void rexmpp_jingle_turn_cb (rexmpp_t *s, void rexmpp_jingle_discover_turn_cb (rexmpp_t *s, void *sess_ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)req; - char *response_from = xmlGetProp(response, "from"); + const char *response_from = rexmpp_xml_find_attr_val(response, "from"); rexmpp_jingle_session_t *sess = sess_ptr; if (success) { - xmlNodePtr services = rexmpp_xml_new_node("services", "urn:xmpp:extdisco:2"); - xmlNewProp(services, "type", "turn"); - rexmpp_iq_new(s, "get", response_from, services, rexmpp_jingle_turn_cb, sess_ptr); + rexmpp_xml_t *services = + rexmpp_xml_new_elem("services", "urn:xmpp:extdisco:2"); + rexmpp_xml_add_attr(services, "type", "turn"); + rexmpp_iq_new(s, "get", response_from, services, + rexmpp_jingle_turn_cb, sess_ptr); } else { rexmpp_log(s, LOG_DEBUG, "No external service discovery, trying to connect without STUN/TURN"); nice_agent_gather_candidates(sess->ice_agent, sess->ice_stream_id); } - if (response_from != NULL) { - free(response_from); - } } void rexmpp_jingle_discover_turn (rexmpp_t *s, rexmpp_jingle_session_t *sess) { @@ -1446,11 +1425,11 @@ rexmpp_jingle_call_accept (rexmpp_t *s, rexmpp_jingle_ice_agent_init(sess); rexmpp_jingle_bind_sockets(sess, rtp_port_in, rtp_port_out); - xmlNodePtr content = + rexmpp_xml_t *content = rexmpp_xml_find_child(sess->initiate, "urn:xmpp:jingle:1", "content"); - xmlNodePtr ice_udp_transport = + rexmpp_xml_t * ice_udp_transport = rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1", "transport"); @@ -1458,7 +1437,7 @@ rexmpp_jingle_call_accept (rexmpp_t *s, rexmpp_log(s, LOG_ERR, "No ICE-UDP transport defined for session %s", sid); rexmpp_jingle_session_terminate (s, sid, - rexmpp_xml_new_node("unsupported-transports", "urn:xmpp:jingle:1"), + rexmpp_xml_new_elem("unsupported-transports", "urn:xmpp:jingle:1"), "No ICE-UDP transport defined"); return REXMPP_E_OTHER; } @@ -1495,42 +1474,44 @@ rexmpp_jingle_call_accept (rexmpp_t *s, } #endif -int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) { +int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { int handled = 0; if (! s->enable_jingle) { return handled; } - xmlNodePtr jingle = rexmpp_xml_find_child(elem, "urn:xmpp:jingle:1", "jingle"); + rexmpp_xml_t *jingle = + rexmpp_xml_find_child(elem, "urn:xmpp:jingle:1", "jingle"); if (jingle != NULL) { handled = 1; - char *action = xmlGetProp(jingle, "action"); - char *sid = xmlGetProp(jingle, "sid"); - char *from_jid = xmlGetProp(elem, "from"); + const char *action = rexmpp_xml_find_attr_val(jingle, "action"); + const char *sid = rexmpp_xml_find_attr_val(jingle, "sid"); + const char *from_jid = rexmpp_xml_find_attr_val(elem, "from"); if (action != NULL && sid != NULL && from_jid != NULL) { if (strcmp(action, "session-initiate") == 0) { /* todo: could be more than one content element, handle that */ - xmlNodePtr content = + rexmpp_xml_t *content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content"); if (content == NULL) { - rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request")); + rexmpp_iq_reply(s, elem, "error", + rexmpp_xml_error("cancel", "bad-request")); } else { rexmpp_iq_reply(s, elem, "result", NULL); - xmlNodePtr file_description = + rexmpp_xml_t *file_description = rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:file-transfer:5", "description"); - xmlNodePtr ibb_transport = + rexmpp_xml_t *ibb_transport = rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ibb:1", "transport"); - xmlNodePtr ice_udp_transport = + rexmpp_xml_t *ice_udp_transport = rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1", "transport"); - xmlNodePtr rtp_description = + rexmpp_xml_t *rtp_description = rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:rtp:1", "description"); if (file_description != NULL && ibb_transport != NULL) { - char *ibb_sid = xmlGetProp(ibb_transport, "sid"); + char *ibb_sid = strdup(rexmpp_xml_find_attr_val(ibb_transport, "sid")); if (ibb_sid != NULL) { rexmpp_log(s, LOG_DEBUG, "Jingle session-initiate from %s, sid %s, ibb sid %s", @@ -1539,11 +1520,11 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) { rexmpp_jingle_session_create(s, strdup(from_jid), strdup(sid), REXMPP_JINGLE_SESSION_FILE, 0); if (sess != NULL) { - sess->initiate = xmlCopyNode(jingle, 1); + sess->initiate = rexmpp_xml_clone(jingle); sess->ibb_sid = ibb_sid; } else { rexmpp_jingle_session_terminate(s, sid, - rexmpp_xml_new_node("failed-transport", + rexmpp_xml_new_elem("failed-transport", "urn:xmpp:jingle:1"), NULL); } @@ -1551,7 +1532,7 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) { rexmpp_log(s, LOG_ERR, "Jingle IBB transport doesn't have a sid attribute"); rexmpp_jingle_session_terminate (s, sid, - rexmpp_xml_new_node("unsupported-transports", + rexmpp_xml_new_elem("unsupported-transports", "urn:xmpp:jingle:1"), NULL); } @@ -1566,19 +1547,19 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) { (rexmpp_xml_find_child(rtp_description, "urn:xmpp:jingle:apps:rtp:1", "rtcp-mux") != NULL); - sess->initiate = xmlCopyNode(jingle, 1); + sess->initiate = rexmpp_xml_clone(jingle); #endif } else if (file_description == NULL && rtp_description == NULL) { rexmpp_jingle_session_terminate (s, sid, - rexmpp_xml_new_node("unsupported-applications", + rexmpp_xml_new_elem("unsupported-applications", "urn:xmpp:jingle:1"), NULL); } else if (ibb_transport == NULL && ice_udp_transport == NULL) { rexmpp_jingle_session_terminate(s, sid, - rexmpp_xml_new_node("unsupported-transports", + rexmpp_xml_new_elem("unsupported-transports", "urn:xmpp:jingle:1"), NULL); } else { @@ -1593,26 +1574,26 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) { rexmpp_iq_reply(s, elem, "result", NULL); rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid); if (session != NULL) { - session->accept = xmlCopyNode(jingle, 1); - xmlNodePtr content = + session->accept = rexmpp_xml_clone(jingle); + rexmpp_xml_t *content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content"); - xmlNodePtr file_description = + rexmpp_xml_t *file_description = rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:file-transfer:5", "description"); - xmlNodePtr ibb_transport = + rexmpp_xml_t *ibb_transport = rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ibb:1", "transport"); if (ibb_transport != NULL && file_description != NULL) { - xmlNodePtr open = - rexmpp_xml_new_node("open", "http://jabber.org/protocol/ibb"); - xmlNewProp(open, "sid", session->ibb_sid); - xmlNewProp(open, "block-size", "4096"); - xmlNewProp(open, "stanza", "iq"); + rexmpp_xml_t *open = + rexmpp_xml_new_elem("open", "http://jabber.org/protocol/ibb"); + rexmpp_xml_add_attr(open, "sid", session->ibb_sid); + rexmpp_xml_add_attr(open, "block-size", "4096"); + rexmpp_xml_add_attr(open, "stanza", "iq"); rexmpp_iq_new(s, "set", session->jid, open, rexmpp_jingle_ibb_send_cb, strdup(sid)); } else { #ifdef ENABLE_CALLS - xmlNodePtr ice_udp_transport = + rexmpp_xml_t *ice_udp_transport = rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1", "transport"); if (ice_udp_transport != NULL) { @@ -1626,9 +1607,9 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) { rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid); if (session != NULL) { #ifdef ENABLE_CALLS - xmlNodePtr content = + rexmpp_xml_t *content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content"); - xmlNodePtr ice_udp_transport = + rexmpp_xml_t *ice_udp_transport = rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1", "transport"); if (ice_udp_transport != NULL) { @@ -1638,62 +1619,54 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) { } } else { rexmpp_log(s, LOG_WARNING, "Unknown Jingle action: %s", action); - rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request")); + rexmpp_iq_reply(s, elem, "error", + rexmpp_xml_error("cancel", "bad-request")); } } else { rexmpp_log(s, LOG_WARNING, "Received a malformed Jingle element"); - rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request")); - } - if (action != NULL) { - free(action); - } - if (sid != NULL) { - free(sid); - } - if (from_jid != NULL) { - free(from_jid); + rexmpp_iq_reply(s, elem, "error", + rexmpp_xml_error("cancel", "bad-request")); } } /* XEP-0261: Jingle In-Band Bytestreams Transport Method */ - xmlNodePtr ibb_open = + rexmpp_xml_t *ibb_open = rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "open"); if (ibb_open != NULL) { handled = 1; /* no-op, though could check sid here. */ rexmpp_iq_reply(s, elem, "result", NULL); } - xmlNodePtr ibb_close = + rexmpp_xml_t *ibb_close = rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "close"); if (ibb_close != NULL) { handled = 1; rexmpp_iq_reply(s, elem, "result", NULL); - char *sid = xmlGetProp(ibb_close, "sid"); + const char *sid = rexmpp_xml_find_attr_val(ibb_close, "sid"); if (sid != NULL) { rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_ibb_sid(s, sid); if (session != NULL) { rexmpp_jingle_session_terminate (s, session->sid, - rexmpp_xml_new_node("success", "urn:xmpp:jingle:1"), NULL); + rexmpp_xml_new_elem("success", "urn:xmpp:jingle:1"), NULL); } - free(sid); } } - xmlNodePtr ibb_data = + rexmpp_xml_t *ibb_data = rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "data"); if (ibb_data != NULL) { handled = 1; - char *sid = xmlGetProp(ibb_data, "sid"); + const char *sid = rexmpp_xml_find_attr_val(ibb_data, "sid"); if (sid != NULL) { rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_ibb_sid(s, sid); if (session != NULL && session->ibb_fh != NULL) { - char *data = NULL, *data_base64 = xmlNodeGetContent(ibb_data); + char *data = NULL; + const char *data_base64 = rexmpp_xml_text_child(ibb_data); if (data_base64 != NULL) { size_t data_len = 0; int base64_err = rexmpp_base64_from(data_base64, strlen(data_base64), - &data, &data_len); - free(data_base64); + &data, &data_len); if (base64_err != 0) { rexmpp_log(s, LOG_ERR, "Base-64 decoding failure"); } else { @@ -1706,7 +1679,6 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) { } } } - free(sid); } /* todo: report errors */ rexmpp_iq_reply(s, elem, "result", NULL); @@ -1872,13 +1844,13 @@ rexmpp_jingle_run (rexmpp_t *s, cert_list_size); rexmpp_jingle_session_terminate (s, sess->sid, - rexmpp_xml_new_node("security-error", "urn:xmpp:jingle:1"), + rexmpp_xml_new_elem("security-error", "urn:xmpp:jingle:1"), "Unexpected certificate list size; expected exactly 1."); } else { - xmlNodePtr jingle = comp->session->initiator + rexmpp_xml_t *jingle = comp->session->initiator ? comp->session->accept : comp->session->initiate; - xmlNodePtr fingerprint = + rexmpp_xml_t *fingerprint = rexmpp_xml_find_child (rexmpp_xml_find_child (rexmpp_xml_find_child @@ -1892,16 +1864,16 @@ rexmpp_jingle_run (rexmpp_t *s, "No fingerprint in the peer's Jingle element"); rexmpp_jingle_session_terminate (s, sess->sid, - rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"), + rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), "No fingerprint element"); } else { - char *hash_str = xmlGetProp(fingerprint, "hash"); + const char *hash_str = rexmpp_xml_find_attr_val(fingerprint, "hash"); if (hash_str == NULL) { rexmpp_log(comp->s, LOG_ERR, "No hash attribute in the peer's fingerprint element"); rexmpp_jingle_session_terminate (s, sess->sid, - rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"), + rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), "No hash attribute in the fingerprint element"); break; } else { @@ -1922,13 +1894,12 @@ rexmpp_jingle_run (rexmpp_t *s, } else if (strcmp(hash_str, "md5") == 0) { algo = GNUTLS_DIG_MD5; } - free(hash_str); if (algo == GNUTLS_DIG_UNKNOWN) { rexmpp_log(comp->s, LOG_ERR, "Unknown hash algorithm in the peer's fingerprint"); rexmpp_jingle_session_terminate (s, sess->sid, - rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"), + rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), "Unknown hash algorithm for a DTLS certificate fingerprint"); break; } else { @@ -1942,19 +1913,19 @@ rexmpp_jingle_run (rexmpp_t *s, } fp_str[fp_size * 3 - 1] = 0; - char *fingerprint_cont = xmlNodeGetContent(fingerprint); + const char *fingerprint_cont = + rexmpp_xml_text_child(fingerprint); /* Fingerprint string should be uppercase, but allowing any case for now, while Dino uses lowercase. */ int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str); - free(fingerprint_cont); if (fingerprint_mismatch) { rexmpp_log(comp->s, LOG_ERR, "Peer's fingerprint mismatch: expected %s, calculated %s", fingerprint_cont, fp_str); rexmpp_jingle_session_terminate (s, sess->sid, - rexmpp_xml_new_node("security-error", "urn:xmpp:jingle:1"), + rexmpp_xml_new_elem("security-error", "urn:xmpp:jingle:1"), "DTLS certificate fingerprint mismatch"); break; } else { @@ -2019,7 +1990,7 @@ rexmpp_jingle_run (rexmpp_t *s, if (comp->component_id == 1) { rexmpp_jingle_session_terminate (s, sess->sid, - rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"), + rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), "DTLS connection error"); break; } diff --git a/src/rexmpp_jingle.h b/src/rexmpp_jingle.h index d3e11eb..eb0a0d3 100644 --- a/src/rexmpp_jingle.h +++ b/src/rexmpp_jingle.h @@ -24,7 +24,7 @@ #include "rexmpp.h" /** @brief Processes incoming Jingle IQs. */ -int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem); +int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem); /** @brief Destroys Jingle sessions. */ void rexmpp_jingle_stop (rexmpp_t *s); @@ -45,7 +45,7 @@ rexmpp_jingle_send_file (rexmpp_t *s, rexmpp_err_t rexmpp_jingle_session_terminate (rexmpp_t *s, const char *sid, - xmlNodePtr reason_node, + rexmpp_xml_t *reason_node, const char *reason_text); typedef struct rexmpp_jingle_component rexmpp_jingle_component_t; @@ -79,8 +79,8 @@ struct rexmpp_jingle_component { struct rexmpp_jingle_session { char *jid; char *sid; - xmlNodePtr initiate; - xmlNodePtr accept; + rexmpp_xml_t *initiate; + rexmpp_xml_t *accept; rexmpp_jingle_session_t *next; /* Sessions are commonly passed to callbacks by external libraries, so it's convenient to have the corresponding rexmpp_t accessible diff --git a/src/rexmpp_openpgp.c b/src/rexmpp_openpgp.c index 13cee44..85ebc68 100644 --- a/src/rexmpp_openpgp.c +++ b/src/rexmpp_openpgp.c @@ -47,10 +47,10 @@ Possible future improvements: #ifdef HAVE_GPGME #include <gpgme.h> #endif -#include <libxml/tree.h> #include <gcrypt.h> #include "rexmpp.h" +#include "rexmpp_xml.h" #include "rexmpp_openpgp.h" #include "rexmpp_jid.h" #include "rexmpp_pubsub.h" @@ -61,8 +61,8 @@ Possible future improvements: void rexmpp_pgp_fp_reply (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -71,33 +71,33 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s, rexmpp_log(s, LOG_WARNING, "Failed to retrieve an OpenpPGP key"); return; } - xmlNodePtr pubsub = + rexmpp_xml_t *pubsub = rexmpp_xml_find_child(response, "http://jabber.org/protocol/pubsub", "pubsub"); if (pubsub == NULL) { rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: not a pubsub response"); return; } - xmlNodePtr items = - rexmpp_xml_find_child(pubsub, "http://jabber.org/protocol/pubsub", - "items"); + rexmpp_xml_t *items = + rexmpp_xml_find_child(pubsub, "http://jabber.org/protocol/pubsub", + "items"); if (items == NULL) { rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no items in pubsub element"); return; } - xmlNodePtr item = + rexmpp_xml_t *item = rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub", "item"); if (item == NULL) { rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no item in items"); return; } - xmlNodePtr pubkey = + rexmpp_xml_t *pubkey = rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "pubkey"); if (pubkey == NULL) { rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no pubkey in item"); return; } - xmlNodePtr data = + rexmpp_xml_t *data = rexmpp_xml_find_child(pubkey, "urn:xmpp:openpgp:0", "data"); if (data == NULL) { rexmpp_log(s, LOG_ERR, "OpenPGP key retrieval: no data in pubkey"); @@ -106,10 +106,9 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s, char *key_raw = NULL; size_t key_raw_len = 0; - char *key_base64 = xmlNodeGetContent(data); + const char *key_base64 = rexmpp_xml_text_child(data); int base64_err = rexmpp_base64_from(key_base64, strlen(key_base64), &key_raw, &key_raw_len); - free(key_base64); if (base64_err != 0) { rexmpp_log(s, LOG_ERR, "Base-64 key decoding failure"); return; @@ -140,18 +139,19 @@ void rexmpp_pgp_fp_reply (rexmpp_t *s, rexmpp_err_t rexmpp_openpgp_check_keys (rexmpp_t *s, const char *jid, - xmlNodePtr items) + rexmpp_xml_t *items) { - xmlNodePtr item = + rexmpp_xml_t *item = rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub#event", "item"); - xmlNodePtr list = - rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "public-keys-list"); - xmlNodePtr metadata; - for (metadata = xmlFirstElementChild(list); + rexmpp_xml_t *list = + rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "public-keys-list"); + rexmpp_xml_t *metadata; + for (metadata = rexmpp_xml_first_elem_child(list); metadata != NULL; - metadata = xmlNextElementSibling(metadata)) { - char *fingerprint = xmlGetProp(metadata, "v4-fingerprint"); + metadata = rexmpp_xml_next_elem_sibling(metadata)) { + const char *fingerprint = + rexmpp_xml_find_attr_val(metadata, "v4-fingerprint"); gpgme_key_t key; gpgme_error_t err; err = gpgme_get_key(s->pgp_ctx, fingerprint, &key, 0); @@ -162,97 +162,95 @@ rexmpp_openpgp_check_keys (rexmpp_t *s, rexmpp_log(s, LOG_DEBUG, "Unknown OpenPGP key fingerprint for %s: %s", jid, fingerprint); - xmlNodePtr fp_req = xmlNewNode(NULL, "pubsub"); - xmlNewNs(fp_req, "http://jabber.org/protocol/pubsub", NULL); - xmlNodePtr fp_req_items = xmlNewNode(NULL, "items"); - xmlNewProp(fp_req_items, "max_items", "1"); + rexmpp_xml_t *fp_req = + rexmpp_xml_new_elem("pubsub", "http://jabber.org/protocol/pubsub"); + rexmpp_xml_t *fp_req_items = + rexmpp_xml_new_elem("items", NULL); + rexmpp_xml_add_attr(fp_req_items, "max_items", "1"); char key_node[72]; snprintf(key_node, 72, "urn:xmpp:openpgp:0:public-keys:%s", fingerprint); - xmlNewProp(fp_req_items, "node", key_node); - xmlAddChild(fp_req, fp_req_items); + rexmpp_xml_add_attr(fp_req_items, "node", key_node); + rexmpp_xml_add_child(fp_req, fp_req_items); rexmpp_iq_new(s, "get", jid, fp_req, rexmpp_pgp_fp_reply, NULL); } else if (gpg_err_code(err) != GPG_ERR_NO_ERROR) { rexmpp_log(s, LOG_WARNING, "OpenPGP error when looking for a key: %s", gpgme_strerror(err)); } - free(fingerprint); } return REXMPP_SUCCESS; } -xmlNodePtr rexmpp_published_fingerprints (rexmpp_t *s, const char *jid) { - xmlNodePtr published = +rexmpp_xml_t *rexmpp_published_fingerprints (rexmpp_t *s, const char *jid) { + rexmpp_xml_t *published = rexmpp_find_event(s, jid, "urn:xmpp:openpgp:0:public-keys", NULL); if (published == NULL) { return NULL; } - xmlNodePtr event = + rexmpp_xml_t *event = rexmpp_xml_find_child(published, "http://jabber.org/protocol/pubsub#event", "event"); - xmlNodePtr items = + rexmpp_xml_t *items = rexmpp_xml_find_child(event, "http://jabber.org/protocol/pubsub#event", "items"); - xmlNodePtr item = + rexmpp_xml_t *item = rexmpp_xml_find_child(items, "http://jabber.org/protocol/pubsub#event", "item"); - xmlNodePtr list = + rexmpp_xml_t *list = rexmpp_xml_find_child(item, "urn:xmpp:openpgp:0", "public-keys-list"); - xmlNodePtr published_fps = xmlFirstElementChild(list); + rexmpp_xml_t *published_fps = list->alt.elem.children; return published_fps; } int rexmpp_openpgp_key_is_published (rexmpp_t *s, const char *fp) { - xmlNodePtr metadata; + rexmpp_xml_t *metadata; for (metadata = rexmpp_published_fingerprints(s, s->assigned_jid.bare); metadata != NULL; - metadata = xmlNextElementSibling(metadata)) { + metadata = metadata->next) { if (! rexmpp_xml_match(metadata, "urn:xmpp:openpgp:0", "pubkey-metadata")) { continue; } - char *fingerprint = xmlGetProp(metadata, "v4-fingerprint"); + const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint"); if (fingerprint == NULL) { rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata"); continue; } - int matches = (strcmp(fingerprint, fp) == 0); - free(fingerprint); - if (matches) { + if (strcmp(fingerprint, fp) == 0) { return 1; } } return 0; } -xmlNodePtr +rexmpp_xml_t * rexmpp_openpgp_remove_key_from_list (rexmpp_t *s, const char *fp) { - xmlNodePtr fps = - xmlCopyNodeList(rexmpp_published_fingerprints(s, s->assigned_jid.bare)); - xmlNodePtr metadata, prev = NULL; + rexmpp_xml_t *fps = + rexmpp_xml_clone_list(rexmpp_published_fingerprints(s, s->assigned_jid.bare)); + rexmpp_xml_t *metadata, *prev = NULL; for (metadata = fps; metadata != NULL; - prev = metadata, metadata = xmlNextElementSibling(metadata)) { + prev = metadata, metadata = rexmpp_xml_next_elem_sibling(metadata)) { if (! rexmpp_xml_match(metadata, "urn:xmpp:openpgp:0", "pubkey-metadata")) { continue; } - char *fingerprint = xmlGetProp(metadata, "v4-fingerprint"); + const char *fingerprint = + rexmpp_xml_find_attr_val(metadata, "v4-fingerprint"); if (fingerprint == NULL) { rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata"); continue; } int matches = (strcmp(fingerprint, fp) == 0); - free(fingerprint); if (matches) { if (prev != NULL) { prev->next = metadata->next; } else { fps = metadata->next; } - xmlFreeNode(metadata); + rexmpp_xml_free(metadata); return fps; } } @@ -261,8 +259,8 @@ rexmpp_openpgp_remove_key_from_list (rexmpp_t *s, void rexmpp_pgp_key_publish_list_iq (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -275,18 +273,18 @@ void rexmpp_pgp_key_publish_list_iq (rexmpp_t *s, rexmpp_log(s, LOG_INFO, "Published an OpenpPGP key list"); } -void rexmpp_pgp_key_fp_list_upload (rexmpp_t *s, xmlNodePtr metadata) { - xmlNodePtr keylist = xmlNewNode(NULL, "public-keys-list"); - xmlNewNs(keylist, "urn:xmpp:openpgp:0", NULL); - xmlAddChild(keylist, metadata); +void rexmpp_pgp_key_fp_list_upload (rexmpp_t *s, rexmpp_xml_t *metadata) { + rexmpp_xml_t *keylist = + rexmpp_xml_new_elem("public-keys-list", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_child(keylist, metadata); rexmpp_pubsub_item_publish(s, NULL, "urn:xmpp:openpgp:0:public-keys", NULL, keylist, rexmpp_pgp_key_publish_list_iq, NULL); } void rexmpp_pgp_key_delete_iq (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -295,18 +293,17 @@ void rexmpp_pgp_key_delete_iq (rexmpp_t *s, rexmpp_log(s, LOG_WARNING, "Failed to delete an OpenpPGP key"); return; } - xmlNodePtr pubsub = xmlFirstElementChild(req); - xmlNodePtr publish = xmlFirstElementChild(pubsub); - char *node = xmlGetProp(publish, "node"); - char *fingerprint = node + 31; + rexmpp_xml_t *pubsub = req->alt.elem.children; + rexmpp_xml_t *publish = pubsub->alt.elem.children;; + const char *node = rexmpp_xml_find_attr_val(publish, "node"); + const char *fingerprint = node + 31; rexmpp_log(s, LOG_INFO, "Removed OpenpPGP key %s", fingerprint); - free(node); } void rexmpp_pgp_key_publish_iq (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -316,10 +313,10 @@ void rexmpp_pgp_key_publish_iq (rexmpp_t *s, return; } rexmpp_log(s, LOG_INFO, "Uploaded an OpenpPGP key"); - xmlNodePtr pubsub = xmlFirstElementChild(req); - xmlNodePtr publish = xmlFirstElementChild(pubsub); - char *node = xmlGetProp(publish, "node"); - char *fingerprint = node + 31; + rexmpp_xml_t *pubsub = req->alt.elem.children; + rexmpp_xml_t *publish = pubsub->alt.elem.children;; + const char *node = rexmpp_xml_find_attr_val(publish, "node"); + const char *fingerprint = node + 31; char time_str[42]; time_t t = time(NULL); @@ -327,22 +324,20 @@ void rexmpp_pgp_key_publish_iq (rexmpp_t *s, gmtime_r(&t, &utc_time); strftime(time_str, 42, "%FT%TZ", &utc_time); - xmlNodePtr metadata = xmlNewNode(NULL, "pubkey-metadata"); - xmlNewNs(metadata, "urn:xmpp:openpgp:0", NULL); - xmlNewProp(metadata, "date", time_str); - xmlNewProp(metadata, "v4-fingerprint", fingerprint); - - free(node); + rexmpp_xml_t *metadata = + rexmpp_xml_new_elem("pubkey-metadata", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_attr(metadata, "date", time_str); + rexmpp_xml_add_attr(metadata, "v4-fingerprint", fingerprint); - xmlNodePtr fps = rexmpp_openpgp_remove_key_from_list(s, fingerprint); + rexmpp_xml_t *fps = rexmpp_openpgp_remove_key_from_list(s, fingerprint); if (fps != NULL) { - metadata->next = xmlCopyNodeList(fps); + metadata->next = fps; } rexmpp_pgp_key_fp_list_upload(s, metadata); } void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp) { - xmlNodePtr new_fp_list = rexmpp_openpgp_remove_key_from_list(s, fp); + rexmpp_xml_t *new_fp_list = rexmpp_openpgp_remove_key_from_list(s, fp); if (new_fp_list != NULL) { rexmpp_pgp_key_fp_list_upload(s, new_fp_list); } @@ -382,14 +377,14 @@ rexmpp_err_t rexmpp_openpgp_publish_key (rexmpp_t *s, const char *fp) { key_raw = gpgme_data_release_and_get_mem(key_dh, &key_raw_len); rexmpp_base64_to(key_raw, key_raw_len, &key_base64, &key_base64_len); free(key_raw); - xmlNodePtr data = xmlNewNode(NULL, "data"); - xmlNewNs(data, "urn:xmpp:openpgp:0", NULL); - xmlNodeAddContent(data, key_base64); + rexmpp_xml_t *data = + rexmpp_xml_new_elem("data", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_text(data, key_base64); free(key_base64); - xmlNodePtr pubkey = xmlNewNode(NULL, "pubkey"); - xmlNewNs(pubkey, "urn:xmpp:openpgp:0", NULL); - xmlAddChild(pubkey, data); + rexmpp_xml_t *pubkey = + rexmpp_xml_new_elem("pubkey", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_child(pubkey, data); char time_str[42]; time_t t = time(NULL); @@ -421,9 +416,9 @@ int rexmpp_openpgp_fingerprint_matches (const char *f1, const char *f2) { return 1; } -xmlNodePtr +rexmpp_xml_t * rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, - xmlNodePtr message, + rexmpp_xml_t *message, int *valid) { gpgme_error_t err; @@ -433,14 +428,13 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, rexmpp_log(s, LOG_ERR, "Not a message element"); return NULL; } - char *from_str = xmlGetProp(message, "from"); + const char *from_str = rexmpp_xml_find_attr_val(message, "from"); if (from_str == NULL) { rexmpp_log(s, LOG_ERR, "No 'from' attribute"); return NULL; } rexmpp_jid_parse(from_str, &from); - free(from_str); - char *to_str = xmlGetProp(message, "to"); + const char *to_str = rexmpp_xml_find_attr_val(message, "to"); if (to_str == NULL) { if (strcmp(from.bare, s->assigned_jid.bare) != 0) { rexmpp_log(s, LOG_ERR, "No 'to' attribute"); @@ -449,18 +443,16 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, rexmpp_jid_parse(from.full, &to); } else { rexmpp_jid_parse(to_str, &to); - free(to_str); } - xmlNodePtr openpgp = + rexmpp_xml_t *openpgp = rexmpp_xml_find_child(message, "urn:xmpp:openpgp:0", "openpgp"); if (openpgp == NULL) { rexmpp_log(s, LOG_ERR, "No 'openpgp' child element"); return NULL; } - char *cipher_str = xmlNodeGetContent(openpgp); - xmlNodePtr plain = + const char *cipher_str = rexmpp_xml_text_child(openpgp); + rexmpp_xml_t *plain = rexmpp_openpgp_decrypt_verify(s, cipher_str); - free(cipher_str); if (plain == NULL) { return NULL; } @@ -476,23 +468,20 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, return plain; } - xmlNodePtr child; + rexmpp_xml_t *child; int found = 0; - for (child = xmlFirstElementChild(plain); + for (child = rexmpp_xml_first_elem_child(plain); child != NULL && ! found; - child = xmlNextElementSibling(child)) + child = rexmpp_xml_next_elem_sibling(child)) { if (rexmpp_xml_match(child, "urn:xmpp:openpgp:0", "to")) { - char *to_jid = xmlGetProp(child, "jid"); + const char *to_jid = rexmpp_xml_find_attr_val(child, "jid"); if (to_jid == NULL) { rexmpp_log(s, LOG_WARNING, "Found a 'to' element without a 'jid' attribute"); } else if (strcmp(to_jid, to.bare) == 0) { found = 1; } - if (to_jid != NULL) { - free(to_jid); - } } } if (! found) { @@ -520,11 +509,11 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, } found = 0; - xmlNodePtr metadata; + rexmpp_xml_t *metadata; for (metadata = rexmpp_published_fingerprints(s, from.bare); metadata != NULL && ! found; - metadata = xmlNextElementSibling(metadata)) { - char *fingerprint = xmlGetProp(metadata, "v4-fingerprint"); + metadata = metadata->next) { + const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint"); if (fingerprint == NULL) { rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata"); continue; @@ -532,7 +521,6 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, if (rexmpp_openpgp_fingerprint_matches(fingerprint, sig->fpr)) { found = 1; } - free(fingerprint); } if (! found) { rexmpp_log(s, LOG_ERR, "No %s's known key matches that of the signature", @@ -570,7 +558,7 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, return plain; } -xmlNodePtr +rexmpp_xml_t * rexmpp_openpgp_decrypt_verify (rexmpp_t *s, const char *cipher_base64) { @@ -600,7 +588,7 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s, rexmpp_log(s, LOG_ERR, "Failed to release and get memory"); return NULL; } - xmlNodePtr elem = rexmpp_xml_parse(plain, plain_len); + rexmpp_xml_t *elem = rexmpp_xml_parse(plain, plain_len); if(elem == NULL) { rexmpp_log(s, LOG_ERR, "Failed to parse an XML document"); } @@ -615,11 +603,11 @@ void rexmpp_openpgp_add_keys (rexmpp_t *s, int *allocated) { gpgme_error_t err; - xmlNodePtr metadata; + rexmpp_xml_t *metadata; for (metadata = rexmpp_published_fingerprints(s, jid); metadata != NULL; - metadata = xmlNextElementSibling(metadata)) { - char *fingerprint = xmlGetProp(metadata, "v4-fingerprint"); + metadata = metadata->next) { + const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint"); if (fingerprint == NULL) { rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata"); continue; @@ -643,19 +631,18 @@ void rexmpp_openpgp_add_keys (rexmpp_t *s, rexmpp_log(s, LOG_ERR, "Failed to read key %s: %s", fingerprint, gpgme_strerror(err)); } - free(fingerprint); } } void rexmpp_openpgp_set_signers (rexmpp_t *s) { gpgme_error_t err; - xmlNodePtr metadata; + rexmpp_xml_t *metadata; gpgme_key_t sec_key; gpgme_signers_clear(s->pgp_ctx); for (metadata = rexmpp_published_fingerprints(s, s->initial_jid.bare); metadata != NULL; - metadata = xmlNextElementSibling(metadata)) { - char *fingerprint = xmlGetProp(metadata, "v4-fingerprint"); + metadata = metadata->next) { + const char *fingerprint = rexmpp_xml_find_attr_val(metadata, "v4-fingerprint"); if (fingerprint == NULL) { rexmpp_log(s, LOG_WARNING, "No fingerprint found in pubkey-metadata"); continue; @@ -670,12 +657,11 @@ void rexmpp_openpgp_set_signers (rexmpp_t *s) { rexmpp_log(s, LOG_ERR, "Failed to read key %s: %s", fingerprint, gpgme_strerror(err)); } - free(fingerprint); } } char *rexmpp_openpgp_payload (rexmpp_t *s, - xmlNodePtr payload, + rexmpp_xml_t *payload, const char **recipients, const char **signers, enum rexmpp_ox_mode mode) @@ -693,8 +679,8 @@ char *rexmpp_openpgp_payload (rexmpp_t *s, } else if (mode == REXMPP_OX_CRYPT) { elem_name = "crypt"; } - xmlNodePtr elem = xmlNewNode(NULL, elem_name); - xmlNewNs(elem, "urn:xmpp:openpgp:0", NULL); + rexmpp_xml_t *elem = + rexmpp_xml_new_elem(elem_name, "urn:xmpp:openpgp:0"); if (mode == REXMPP_OX_SIGN || mode == REXMPP_OX_SIGNCRYPT) { if (signers == NULL) { @@ -717,10 +703,10 @@ char *rexmpp_openpgp_payload (rexmpp_t *s, /* Add all the recipients. */ for (i = 0; recipients[i] != NULL; i++) { - xmlNodePtr to = xmlNewNode(NULL, "to"); - xmlNewNs(to, "urn:xmpp:openpgp:0", NULL); - xmlNewProp(to, "jid", recipients[i]); - xmlAddChild(elem, to); + rexmpp_xml_t *to = + rexmpp_xml_new_elem("to", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_attr(to, "jid", recipients[i]); + rexmpp_xml_add_child(elem, to); } } @@ -730,16 +716,16 @@ char *rexmpp_openpgp_payload (rexmpp_t *s, struct tm utc_time; gmtime_r(&t, &utc_time); strftime(time_str, 42, "%FT%TZ", &utc_time); - xmlNodePtr time = xmlNewNode(NULL, "time"); - xmlNewNs(time, "urn:xmpp:openpgp:0", NULL); - xmlNewProp(time, "stamp", time_str); - xmlAddChild(elem, time); + rexmpp_xml_t *time = + rexmpp_xml_new_elem("time", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_attr(time, "stamp", time_str); + rexmpp_xml_add_child(elem, time); /* Add the payload. */ - xmlNodePtr pl = xmlNewNode(NULL, "payload"); - xmlNewNs(pl, "urn:xmpp:openpgp:0", NULL); - xmlAddChild(pl, payload); - xmlAddChild(elem, pl); + rexmpp_xml_t *pl = + rexmpp_xml_new_elem("payload", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_child(pl, payload); + rexmpp_xml_add_child(elem, pl); if (mode == REXMPP_OX_CRYPT || mode == REXMPP_OX_SIGNCRYPT) { /* Add keys for encryption. */ @@ -758,16 +744,16 @@ char *rexmpp_openpgp_payload (rexmpp_t *s, gcry_create_nonce(rand, rand_len); rexmpp_base64_to(rand, rand_len, &rand_str, &rand_str_len); - xmlNodePtr rpad = xmlNewNode(NULL, "rpad"); - xmlNewNs(rpad, "urn:xmpp:openpgp:0", NULL); - xmlNodeAddContent(rpad, rand_str); + rexmpp_xml_t *rpad = + rexmpp_xml_new_elem("rpad", "urn:xmpp:openpgp:0"); + rexmpp_xml_add_text(rpad, rand_str); free(rand_str); - xmlAddChild(elem, rpad); + rexmpp_xml_add_child(elem, rpad); } /* Serialize the resulting XML. */ char *plaintext = rexmpp_xml_serialize(elem); - xmlFreeNode(elem); + rexmpp_xml_free(elem); /* Encrypt, base64-encode. */ gpgme_data_t cipher_dh, plain_dh; @@ -832,7 +818,7 @@ rexmpp_err_t gpgme_not_supported(rexmpp_t *s) { rexmpp_err_t rexmpp_openpgp_check_keys (rexmpp_t *s, const char *jid, - xmlNodePtr items) { + rexmpp_xml_t *items) { (void)jid; (void)items; return gpgme_not_supported(s); @@ -848,7 +834,7 @@ void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp) { gpgme_not_supported(s); } -xmlNodePtr +rexmpp_xml_t * rexmpp_openpgp_decrypt_verify (rexmpp_t *s, const char *cipher_base64) { (void)cipher_base64; @@ -856,9 +842,9 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s, return NULL; } -xmlNodePtr +rexmpp_xml_t * rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, - xmlNodePtr message, + rexmpp_xml_t *message, int *valid) { (void)message; (void)valid; @@ -867,7 +853,7 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, } char *rexmpp_openpgp_payload (rexmpp_t *s, - xmlNodePtr payload, + rexmpp_xml_t *payload, const char **recipients, const char **signers, enum rexmpp_ox_mode mode) { diff --git a/src/rexmpp_openpgp.h b/src/rexmpp_openpgp.h index 7470347..2132930 100644 --- a/src/rexmpp_openpgp.h +++ b/src/rexmpp_openpgp.h @@ -29,7 +29,7 @@ enum rexmpp_ox_mode { rexmpp_err_t rexmpp_openpgp_check_keys (rexmpp_t *s, const char *jid, - xmlNodePtr items); + rexmpp_xml_t *items); /** @brief Publishes a key via PEP/pubsub. @@ -52,7 +52,7 @@ void rexmpp_openpgp_retract_key (rexmpp_t *s, const char *fp); @param[in] cipher_base64 An OpenPGP ciphertext. @returns A plaintext message body. */ -xmlNodePtr +rexmpp_xml_t * rexmpp_openpgp_decrypt_verify (rexmpp_t *s, const char *cipher_base64); @@ -65,9 +65,9 @@ rexmpp_openpgp_decrypt_verify (rexmpp_t *s, valid. @returns A decrypted message body. */ -xmlNodePtr +rexmpp_xml_t * rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, - xmlNodePtr message, + rexmpp_xml_t *message, int *valid); /** @@ -83,7 +83,7 @@ rexmpp_openpgp_decrypt_verify_message (rexmpp_t *s, @returns An encoded <openpgp> payload. */ char *rexmpp_openpgp_payload (rexmpp_t *s, - xmlNodePtr payload, + rexmpp_xml_t *payload, const char **recipients, const char **signers, enum rexmpp_ox_mode mode); diff --git a/src/rexmpp_pubsub.c b/src/rexmpp_pubsub.c index 145a352..58cb060 100644 --- a/src/rexmpp_pubsub.c +++ b/src/rexmpp_pubsub.c @@ -7,25 +7,22 @@ */ #include "rexmpp.h" +#include "rexmpp_xml.h" void rexmpp_pubsub_iq (rexmpp_t *s, const char *iq_type, const char *pubsub_namespace, const char *service_jid, - xmlNodePtr payload, + rexmpp_xml_t *payload, rexmpp_iq_callback_t callback, void *cb_data) { - xmlNodePtr pubsub = xmlNewNode(NULL, "pubsub"); if (pubsub_namespace == NULL) { - xmlNewNs(pubsub, "http://jabber.org/protocol/pubsub", NULL); - } else { - xmlNewNs(pubsub, pubsub_namespace, NULL); + pubsub_namespace = "http://jabber.org/protocol/pubsub"; } - - xmlAddChild(pubsub, payload); - + rexmpp_xml_t *pubsub = rexmpp_xml_new_elem("pubsub", pubsub_namespace); + rexmpp_xml_add_child(pubsub, payload); rexmpp_iq_new(s, iq_type, service_jid, pubsub, callback, cb_data); } @@ -34,21 +31,21 @@ rexmpp_pubsub_item_publish (rexmpp_t *s, const char *service_jid, const char *node, const char *item_id, - xmlNodePtr payload, + rexmpp_xml_t *payload, rexmpp_iq_callback_t callback, void *cb_data) { - xmlNodePtr item = xmlNewNode(NULL, "item"); - xmlNewNs(item, "http://jabber.org/protocol/pubsub", NULL); + rexmpp_xml_t *item = + rexmpp_xml_new_elem("item", "http://jabber.org/protocol/pubsub"); if (item_id != NULL) { - xmlNewProp(item, "id", item_id); + rexmpp_xml_add_attr(item, "id", item_id); } - xmlAddChild(item, payload); + rexmpp_xml_add_child(item, payload); - xmlNodePtr publish = xmlNewNode(NULL, "publish"); - xmlNewNs(publish, "http://jabber.org/protocol/pubsub", NULL); - xmlNewProp(publish, "node", node); - xmlAddChild(publish, item); + rexmpp_xml_t *publish = + rexmpp_xml_new_elem("publish", "http://jabber.org/protocol/pubsub"); + rexmpp_xml_add_attr(publish, "node", node); + rexmpp_xml_add_child(publish, item); rexmpp_pubsub_iq(s, "set", NULL, service_jid, publish, callback, cb_data); } @@ -61,16 +58,16 @@ rexmpp_pubsub_item_retract (rexmpp_t *s, rexmpp_iq_callback_t callback, void *cb_data) { - xmlNodePtr item = xmlNewNode(NULL, "item"); - xmlNewNs(item, "http://jabber.org/protocol/pubsub", NULL); + rexmpp_xml_t *item = + rexmpp_xml_new_elem("item", "http://jabber.org/protocol/pubsub"); if (item_id != NULL) { - xmlNewProp(item, "id", item_id); + rexmpp_xml_add_attr(item, "id", item_id); } - xmlNodePtr retract = xmlNewNode(NULL, "retract"); - xmlNewNs(retract, "http://jabber.org/protocol/pubsub", NULL); - xmlNewProp(retract, "node", node); - xmlAddChild(retract, item); + rexmpp_xml_t *retract = + rexmpp_xml_new_elem("retract", "http://jabber.org/protocol/pubsub"); + rexmpp_xml_add_attr(retract, "node", node); + rexmpp_xml_add_child(retract, item); rexmpp_pubsub_iq(s, "set", NULL, service_jid, retract, callback, cb_data); } @@ -82,9 +79,9 @@ rexmpp_pubsub_node_delete (rexmpp_t *s, rexmpp_iq_callback_t callback, void *cb_data) { - xmlNodePtr delete = xmlNewNode(NULL, "delete"); - xmlNewNs(delete, "http://jabber.org/protocol/pubsub#owner", NULL); - xmlNewProp(delete, "node", node); + rexmpp_xml_t *delete = + rexmpp_xml_new_elem("delete", "http://jabber.org/protocol/pubsub#owner"); + rexmpp_xml_add_attr(delete, "node", node); rexmpp_pubsub_iq(s, "set", "http://jabber.org/protocol/pubsub#owner", service_jid, delete, callback, cb_data); diff --git a/src/rexmpp_pubsub.h b/src/rexmpp_pubsub.h index b5a7c3e..86675d3 100644 --- a/src/rexmpp_pubsub.h +++ b/src/rexmpp_pubsub.h @@ -11,7 +11,7 @@ rexmpp_pubsub_iq (rexmpp_t *s, const char *iq_type, const char *pubsub_namespace, const char *service_jid, - xmlNodePtr payload, + rexmpp_xml_t *payload, rexmpp_iq_callback_t callback, void *cb_data); @@ -20,7 +20,7 @@ rexmpp_pubsub_item_publish (rexmpp_t *s, const char *service_jid, const char *node, const char *item_id, - xmlNodePtr payload, + rexmpp_xml_t *payload, rexmpp_iq_callback_t callback, void *cb_data); diff --git a/src/rexmpp_roster.c b/src/rexmpp_roster.c index 63a52b9..db09ff0 100644 --- a/src/rexmpp_roster.c +++ b/src/rexmpp_roster.c @@ -7,24 +7,25 @@ */ #include "rexmpp.h" +#include "rexmpp_xml.h" #include <syslog.h> #include <string.h> #include <libxml/tree.h> #include <libxml/xmlsave.h> -xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s, - const char *jid, - xmlNodePtr *prev_item) +rexmpp_xml_t * +rexmpp_roster_find_item (rexmpp_t *s, + const char *jid, + rexmpp_xml_t **prev_item) { - xmlNodePtr prev = NULL, cur = s->roster_items; + rexmpp_xml_t *prev = NULL, *cur = s->roster_items; while (cur != NULL) { - char *cur_jid = xmlGetProp(cur, "jid"); + const char *cur_jid = rexmpp_xml_find_attr_val(cur, "jid"); if (cur_jid == NULL) { rexmpp_log(s, LOG_ALERT, "No jid found in a roster item."); return NULL; } int match = (strcmp(cur_jid, jid) == 0); - free(cur_jid); if (match) { if (prev_item != NULL) { *prev_item = prev; @@ -37,17 +38,17 @@ xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s, return NULL; } -rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item) { +rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, rexmpp_xml_t *item) { rexmpp_err_t ret = REXMPP_SUCCESS; if (! rexmpp_xml_match(item, "jabber:iq:roster", "item")) { rexmpp_log(s, LOG_ERR, "No roster item."); return REXMPP_E_PARAM; } - char *subscription = xmlGetProp(item, "subscription"); - char *jid = xmlGetProp(item, "jid"); + const char *subscription = rexmpp_xml_find_attr_val(item, "subscription"); + const char *jid = rexmpp_xml_find_attr_val(item, "jid"); if (subscription != NULL && strcmp(subscription, "remove") == 0) { /* Delete the item. */ - xmlNodePtr prev, cur; + rexmpp_xml_t *prev, *cur; cur = rexmpp_roster_find_item(s, jid, &prev); if (cur != NULL) { if (prev != NULL) { @@ -55,13 +56,13 @@ rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item) { } else { s->roster_items = cur->next; } - xmlFreeNode(cur); + rexmpp_xml_free(cur); } else { ret = REXMPP_E_ROSTER_ITEM_NOT_FOUND; } } else { /* Add or modify the item. */ - xmlNodePtr cur, prev; + rexmpp_xml_t *cur, *prev; cur = rexmpp_roster_find_item(s, jid, &prev); /* Remove the item if it was in the roster before. */ if (cur != NULL) { @@ -70,37 +71,33 @@ rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item) { } else { s->roster_items = cur->next; } - xmlFreeNode(cur); + rexmpp_xml_free(cur); } /* Add the new item. */ - xmlNodePtr new_item = xmlCopyNode(item, 1); + rexmpp_xml_t *new_item = rexmpp_xml_clone(item); new_item->next = s->roster_items; s->roster_items = new_item; } - free(jid); - if (subscription != NULL) { - free(subscription); - } if (s->roster_modify_cb != NULL) { s->roster_modify_cb(s, item); } return ret; } -void rexmpp_roster_set (rexmpp_t *s, xmlNodePtr query) { +void rexmpp_roster_set (rexmpp_t *s, rexmpp_xml_t *query) { if (s->roster_items != NULL) { - xmlFreeNodeList(s->roster_items); + rexmpp_xml_free_list(s->roster_items); } if (s->roster_ver != NULL) { free(s->roster_ver); } - s->roster_ver = xmlGetProp(query, "ver"); - s->roster_items = xmlCopyNodeList(xmlFirstElementChild(query)); + s->roster_ver = strdup(rexmpp_xml_find_attr_val(query, "ver")); + s->roster_items = rexmpp_xml_clone_list(query->alt.elem.children); if (s->roster_modify_cb != NULL) { - xmlNodePtr item; - for (item = xmlFirstElementChild(query); + rexmpp_xml_t *item; + for (item = query->alt.elem.children; item != NULL; - item = xmlNextElementSibling(item)) + item = item->next) { s->roster_modify_cb(s, item); } @@ -112,10 +109,11 @@ void rexmpp_roster_cache_read (rexmpp_t *s) { rexmpp_log(s, LOG_WARNING, "No roster cache file path is set."); return; } - xmlDocPtr doc = xmlReadFile(s->roster_cache_file, "utf-8", XML_PARSE_NONET); - xmlNodePtr query = xmlDocGetRootElement(doc); - rexmpp_roster_set(s, query); - xmlFreeDoc(doc); + rexmpp_xml_t *query = rexmpp_xml_read_file(s->roster_cache_file); + if (query != NULL) { + rexmpp_roster_set(s, query); + rexmpp_xml_free(query); + } } void rexmpp_roster_cache_write (rexmpp_t *s) { @@ -123,24 +121,22 @@ void rexmpp_roster_cache_write (rexmpp_t *s) { rexmpp_log(s, LOG_WARNING, "No roster cache file path is set."); return; } - xmlDocPtr doc = xmlNewDoc("1.0"); - xmlNodePtr query = xmlNewDocNode(doc, NULL, "query", NULL); - xmlDocSetRootElement(doc, query); - xmlNewNs(query, "jabber:iq:roster", NULL); + + rexmpp_xml_t *query = rexmpp_xml_new_elem("query", "jabber:iq:roster"); if (s->roster_ver != NULL) { - xmlNewProp(query, "ver", s->roster_ver); + rexmpp_xml_add_attr(query, "ver", s->roster_ver); } if (s->roster_items != NULL) { - xmlAddChild(query, xmlDocCopyNodeList(doc, s->roster_items)); + rexmpp_xml_add_child(query, rexmpp_xml_clone_list(s->roster_items)); } - xmlSaveFileEnc(s->roster_cache_file, doc, "utf-8"); - xmlFreeDoc(doc); + rexmpp_xml_write_file(s->roster_cache_file, query); + rexmpp_xml_free(query); } void rexmpp_iq_roster_get (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success) { (void)ptr; @@ -149,7 +145,7 @@ void rexmpp_iq_roster_get (rexmpp_t *s, rexmpp_log(s, LOG_ERR, "Roster loading failed."); return; } - xmlNodePtr query = xmlFirstElementChild(response); + rexmpp_xml_t *query = response->alt.elem.children; if (! rexmpp_xml_match(query, "jabber:iq:roster", "query")) { rexmpp_log(s, LOG_DEBUG, "No roster query in reply."); return; diff --git a/src/rexmpp_roster.h b/src/rexmpp_roster.h index 1fa9183..f613a7b 100644 --- a/src/rexmpp_roster.h +++ b/src/rexmpp_roster.h @@ -7,15 +7,15 @@ */ -xmlNodePtr rexmpp_roster_find_item (rexmpp_t *s, - const char *jid, - xmlNodePtr *prev_item); -rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, xmlNodePtr item); -void rexmpp_roster_set (rexmpp_t *s, xmlNodePtr query); +rexmpp_xml_t *rexmpp_roster_find_item (rexmpp_t *s, + const char *jid, + rexmpp_xml_t **prev_item); +rexmpp_err_t rexmpp_modify_roster (rexmpp_t *s, rexmpp_xml_t *item); +void rexmpp_roster_set (rexmpp_t *s, rexmpp_xml_t *query); void rexmpp_roster_cache_read (rexmpp_t *s); void rexmpp_roster_cache_write (rexmpp_t *s); void rexmpp_iq_roster_get (rexmpp_t *s, void *ptr, - xmlNodePtr req, - xmlNodePtr response, + rexmpp_xml_t *req, + rexmpp_xml_t *response, int success); diff --git a/src/rexmpp_xml.c b/src/rexmpp_xml.c new file mode 100644 index 0000000..8c91d32 --- /dev/null +++ b/src/rexmpp_xml.c @@ -0,0 +1,592 @@ +/** + @file rexmpp_xml.c + @brief XML structures and functions for rexmpp + @author defanor <defanor@uberspace.net> + @date 2023 + @copyright MIT license. +*/ + +#include <string.h> +#include <libxml/tree.h> +#include <libxml/xmlsave.h> +#include "rexmpp.h" +#include "rexmpp_xml.h" + +void rexmpp_xml_qname_free (rexmpp_xml_qname_t *qname) { + if (qname->name != NULL) { + free(qname->name); + qname->name = NULL; + } + if (qname->namespace != NULL) { + free(qname->namespace); + qname->namespace = NULL; + } +} + +void rexmpp_xml_attribute_free (rexmpp_xml_attr_t *attr) { + if (attr == NULL) { + return; + } + rexmpp_xml_qname_free(&(attr->qname)); + if (attr->value != NULL) { + free(attr->value); + attr->value = NULL; + } + free(attr); +} + +void rexmpp_xml_attribute_free_list (rexmpp_xml_attr_t *attr) { + rexmpp_xml_attr_t *next = attr; + while (attr != NULL) { + next = attr->next; + rexmpp_xml_attribute_free(attr); + attr = next; + } +} + +void rexmpp_xml_free (rexmpp_xml_t *node) { + if (node == NULL) { + return; + } + if (node->type == REXMPP_XML_TEXT) { + if (node->alt.text != NULL) { + free(node->alt.text); + node->alt.text = NULL; + } + } if (node->type == REXMPP_XML_ELEMENT) { + rexmpp_xml_qname_free(&(node->alt.elem.qname)); + rexmpp_xml_attribute_free_list(node->alt.elem.attributes); + rexmpp_xml_free_list(node->alt.elem.children); + } + free(node); +} + +void rexmpp_xml_free_list (rexmpp_xml_t *node) { + rexmpp_xml_t *next = node; + while (node != NULL) { + next = node->next; + rexmpp_xml_free(node); + node = next; + } +} + +rexmpp_xml_t *rexmpp_xml_clone (rexmpp_xml_t *node) { + if (node == NULL) { + return NULL; + } + + if (node->type == REXMPP_XML_TEXT) { + return rexmpp_xml_new_text(node->alt.text); + } else if (node->type == REXMPP_XML_ELEMENT) { + rexmpp_xml_t *ret = + rexmpp_xml_new_elem(node->alt.elem.qname.name, + node->alt.elem.qname.namespace); + rexmpp_xml_attr_t **next_attr = &(ret->alt.elem.attributes); + rexmpp_xml_attr_t *old_attr; + for (old_attr = node->alt.elem.attributes; + old_attr != NULL; + old_attr = old_attr->next) + { + rexmpp_xml_attr_t *new_attr = + rexmpp_xml_attr_new(old_attr->qname.name, + old_attr->qname.namespace, + old_attr->value); + *next_attr = new_attr; + next_attr = &(new_attr->next); + } + + ret->alt.elem.children = + rexmpp_xml_clone_list(node->alt.elem.children); + return ret; + } + return NULL; +} + +rexmpp_xml_t *rexmpp_xml_clone_list (rexmpp_xml_t *node) { + rexmpp_xml_t *first, *last; + if (node == NULL) { + return NULL; + } + first = rexmpp_xml_clone(node); + for (last = first, node = node->next; + node != NULL; + last = last->next, node = node->next) + { + last->next = rexmpp_xml_clone(node); + } + return first; +} + + +rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from) { + if (from == NULL) { + return NULL; + } + + rexmpp_xml_t *to = NULL; + if (from->type == XML_ELEMENT_NODE) { + to = malloc(sizeof(rexmpp_xml_t)); + + /* Type */ + to->type = REXMPP_XML_ELEMENT; + + /* Name and namespace */ + to->alt.elem.qname.name = strdup(from->name); + if (from->nsDef != NULL && from->nsDef->href != NULL) { + to->alt.elem.qname.namespace = strdup(from->nsDef->href); + } else { + to->alt.elem.qname.namespace = NULL; + } + + /* Attributes */ + to->alt.elem.attributes = NULL; + struct _xmlAttr *from_attr; + rexmpp_xml_attr_t **to_next_attr = &(to->alt.elem.attributes); + for (from_attr = from->properties; + from_attr != NULL; + from_attr = from_attr->next) + { + rexmpp_xml_attr_t *to_attr = + malloc(sizeof(rexmpp_xml_attr_t)); + to_attr->qname.name = strdup(from_attr->name); + to_attr->qname.namespace = NULL; + if (from_attr->ns != NULL && from_attr->ns->href != NULL) { + to_attr->qname.namespace = strdup(from_attr->ns->href); + to_attr->value = + xmlGetNsProp(from, to_attr->qname.name, to_attr->qname.namespace); + } else { + to_attr->value = xmlGetProp(from, to_attr->qname.name); + } + to_attr->next = NULL; + + *to_next_attr = to_attr; + to_next_attr = &(to_attr->next); + } + + /* Children */ + to->alt.elem.children = NULL; + xmlNodePtr from_child; + rexmpp_xml_t **to_next_child = &(to->alt.elem.children); + for (from_child = from->children; + from_child != NULL; + from_child = from_child->next) + { + rexmpp_xml_t *next_child = rexmpp_xml_from_libxml2(from_child); + if (next_child != NULL) { + *to_next_child = next_child; + to_next_child = &(next_child->next); + } + } + + /* Next */ + to->next = NULL; + + } else if (from->type == XML_TEXT_NODE) { + to = malloc(sizeof(rexmpp_xml_t)); + to->type = REXMPP_XML_TEXT; + to->alt.text = xmlNodeGetContent(from); + to->next = NULL; + } + return to; +} + +rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from) { + if (from == NULL) { + return NULL; + } + rexmpp_xml_t *to = rexmpp_xml_from_libxml2(from); + if (from->next != NULL) { + to->next = rexmpp_xml_from_libxml2_list(from->next); + } + return to; +} + +xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from) { + if (from == NULL) { + return NULL; + } + + if (from->type == REXMPP_XML_TEXT) { + xmlNodePtr to = xmlNewText(from->alt.text); + to->next = rexmpp_xml_to_libxml2(from->next); + return to; + } + + /* Name and namespace */ + xmlNodePtr to = xmlNewNode(NULL, from->alt.elem.qname.name); + if (from->alt.elem.qname.namespace != NULL) { + xmlNewNs(to, from->alt.elem.qname.namespace, NULL); + } + + /* Attributes */ + rexmpp_xml_attr_t *attr = from->alt.elem.attributes; + while (attr != NULL) { + /* TODO: Would be nice to take namespaces into account, though + they are currently not used for attributes. */ + xmlNewProp(to, attr->qname.name, attr->value); + attr = attr->next; + } + + /* Children */ + rexmpp_xml_t *child = from->alt.elem.children; + while (child != NULL) { + xmlAddChild(to, rexmpp_xml_to_libxml2(child)); + child = child->next; + } + return to; +} + +xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from) { + xmlNodePtr to = rexmpp_xml_to_libxml2(from); + if (from->next != NULL) { + xmlAddNextSibling(to, rexmpp_xml_to_libxml2_list(from->next)); + } + return to; +} + +rexmpp_xml_t *rexmpp_xml_new_text (const char *str) { + rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t)); + node->type = REXMPP_XML_TEXT; + node->alt.text = strdup(str); + node->next = NULL; + return node; +} + +void rexmpp_xml_add_child (rexmpp_xml_t *node, + rexmpp_xml_t *child) +{ + rexmpp_xml_t **last_ptr = &(node->alt.elem.children); + while (*last_ptr != NULL) { + last_ptr = &((*last_ptr)->next); + } + *last_ptr = child; +} + +int rexmpp_xml_add_text (rexmpp_xml_t *node, + const char *str) +{ + rexmpp_xml_t *text_node = rexmpp_xml_new_text(str); + if (text_node != NULL) { + rexmpp_xml_add_child(node, text_node); + return 0; + } + return -1; +} + + +rexmpp_xml_t *rexmpp_xml_new_elem (const char *name, + const char *namespace) +{ + rexmpp_xml_t *node = malloc(sizeof(rexmpp_xml_t)); + node->type = REXMPP_XML_ELEMENT; + node->alt.elem.qname.name = strdup(name); + if (namespace != NULL) { + node->alt.elem.qname.namespace = strdup(namespace); + } else { + node->alt.elem.qname.namespace = NULL; + } + node->alt.elem.attributes = NULL; + node->alt.elem.children = NULL; + node->next = NULL; + return node; +} + +rexmpp_xml_attr_t *rexmpp_xml_attr_new (const char *name, + const char *namespace, + const char *value) +{ + rexmpp_xml_attr_t *attr = malloc(sizeof(rexmpp_xml_attr_t)); + attr->qname.name = strdup(name); + if (namespace != NULL) { + attr->qname.namespace = strdup(namespace); + } else { + attr->qname.namespace = NULL; + } + attr->value = strdup(value); + attr->next = NULL; + return attr; +} + +int rexmpp_xml_add_attr_ns (rexmpp_xml_t *node, + const char *name, + const char *namespace, + const char *value) +{ + if (node == NULL || node->type != REXMPP_XML_ELEMENT) { + return -1; + } + rexmpp_xml_attr_t *attr = + rexmpp_xml_attr_new(name, namespace, value); + attr->next = node->alt.elem.attributes; + node->alt.elem.attributes = attr; + return 0; +} + +int rexmpp_xml_remove_attr_ns (rexmpp_xml_t *node, + const char *name, + const char *namespace) { + if (node == NULL || node->type != REXMPP_XML_ELEMENT) { + return -1; + } + + rexmpp_xml_attr_t **attr, *next_attr; + for (attr = &(node->alt.elem.attributes); *attr != NULL; attr = &((*attr)->next)) { + if (rexmpp_xml_attr_match(*attr, namespace, name)) { + next_attr = (*attr)->next; + rexmpp_xml_attribute_free(*attr); + *attr = next_attr; + return 0; + } + } + return 1; +} + +int rexmpp_xml_add_attr (rexmpp_xml_t *node, + const char *name, + const char *value) +{ + return rexmpp_xml_add_attr_ns(node, name, NULL, value); +} + +int rexmpp_xml_remove_attr (rexmpp_xml_t *node, + const char *name) { + return rexmpp_xml_remove_attr_ns(node, name, NULL); +} + +rexmpp_xml_t * +rexmpp_xml_add_id (rexmpp_t *s, + rexmpp_xml_t *node) +{ + char *buf = rexmpp_gen_id(s); + if (buf == NULL) { + return NULL; + } + rexmpp_xml_add_attr(node, "id", buf); + free(buf); + return node; +} + +char *rexmpp_xml_serialize (rexmpp_xml_t *node) { + xmlNodePtr node_libxml2 = rexmpp_xml_to_libxml2(node); + xmlBufferPtr buf = xmlBufferCreate(); + xmlSaveCtxtPtr ctx = xmlSaveToBuffer(buf, "utf-8", 0); + xmlSaveTree(ctx, node_libxml2); + xmlSaveFlush(ctx); + xmlSaveClose(ctx); + unsigned char *out = xmlBufferDetach(buf); + xmlBufferFree(buf); + xmlFreeNode(node_libxml2); + return out; +} + +xmlNodePtr rexmpp_xml_parse_libxml2 (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; +} + +rexmpp_xml_t *rexmpp_xml_parse (const char *str, int str_len) { + xmlNodePtr node_lxml2 = rexmpp_xml_parse_libxml2(str, str_len); + if (node_lxml2 != NULL) { + rexmpp_xml_t *node = rexmpp_xml_from_libxml2(node_lxml2); + xmlFreeNode(node_lxml2); + return node; + } + return NULL; +} + +rexmpp_xml_t *rexmpp_xml_read_file (const char *path) { + xmlDocPtr doc = xmlReadFile(path, "utf-8", XML_PARSE_NONET); + xmlNodePtr lxml2 = xmlDocGetRootElement(doc); + rexmpp_xml_t *ret = rexmpp_xml_from_libxml2(lxml2); + xmlFreeDoc(doc); + return ret; +} + +int rexmpp_xml_write_file (const char *path, rexmpp_xml_t* node) { + xmlDocPtr doc = xmlNewDoc("1.0"); + xmlNodePtr node_lxml2 = rexmpp_xml_to_libxml2(node); + xmlDocSetRootElement(doc, node_lxml2); + xmlSaveFileEnc(path, doc, "utf-8"); + xmlFreeDoc(doc); + return 0; +} + +unsigned int rexmpp_xml_siblings_count (rexmpp_xml_t *node) { + unsigned int i = 0; + for (i = 0; node != NULL; i++) { + node = node->next; + } + return i; +} + +int rexmpp_xml_match (rexmpp_xml_t *node, + const char *namespace, + const char *name) +{ + if (node == NULL) { + return 0; + } + if (node->type != REXMPP_XML_ELEMENT) { + return 0; + } + if (name != NULL) { + if (strcmp(name, node->alt.elem.qname.name) != 0) { + return 0; + } + } + if (namespace != NULL) { + if (node->alt.elem.qname.namespace == NULL && + strcmp(namespace, "jabber:client") != 0) { + return 0; + } else if (node->alt.elem.qname.namespace != NULL) { + if (strcmp(namespace, node->alt.elem.qname.namespace) != 0) { + return 0; + } + } + } + return 1; +} + +int rexmpp_xml_attr_match (rexmpp_xml_attr_t *attr, + const char *namespace, + const char *name) +{ + if (attr == NULL) { + return 0; + } + if (name != NULL) { + if (strcmp(name, attr->qname.name) != 0) { + return 0; + } + } + if (namespace != NULL) { + if (attr->qname.namespace == NULL && + strcmp(namespace, "jabber:client") != 0) { + return 0; + } else if (strcmp(namespace, attr->qname.namespace) != 0) { + return 0; + } + } + return 1; +} + +int rexmpp_xml_is_stanza (rexmpp_xml_t *node) { + return rexmpp_xml_match(node, "jabber:client", "message") || + rexmpp_xml_match(node, "jabber:client", "iq") || + rexmpp_xml_match(node, "jabber:client", "presence"); +} + +rexmpp_xml_t *rexmpp_xml_error (const char *type, const char *condition) { + rexmpp_xml_t * error = rexmpp_xml_new_elem("error", NULL); + rexmpp_xml_add_attr(error, "type", type); + rexmpp_xml_t * cond = + rexmpp_xml_new_elem(condition, "urn:ietf:params:xml:ns:xmpp-stanzas"); + rexmpp_xml_add_child(error, cond); + return error; +} + +rexmpp_xml_attr_t * +rexmpp_xml_find_attr (rexmpp_xml_t *node, + const char *name, + const char *namespace) +{ + if (node == NULL || node->type != REXMPP_XML_ELEMENT) { + return NULL; + } + rexmpp_xml_attr_t *attr; + for (attr = node->alt.elem.attributes; attr != NULL; attr = attr->next) { + if (rexmpp_xml_attr_match(attr, namespace, name)) { + return attr; + } + } + return NULL; +} + +const char *rexmpp_xml_find_attr_val_ns (rexmpp_xml_t *node, + const char *name, + const char *namespace) { + rexmpp_xml_attr_t *attr = rexmpp_xml_find_attr(node, name, namespace); + if (attr != NULL) { + return attr->value; + } + return NULL; +} + +const char *rexmpp_xml_find_attr_val (rexmpp_xml_t *node, + const char *name) { + return rexmpp_xml_find_attr_val_ns(node, name, NULL); +} + +rexmpp_xml_t * +rexmpp_xml_find_child (rexmpp_xml_t *node, + const char *namespace, + const char *name) +{ + if (node == NULL || node->type != REXMPP_XML_ELEMENT) { + return NULL; + } + rexmpp_xml_t *child; + for (child = node->alt.elem.children; child != NULL; child = child->next) { + if (rexmpp_xml_match(child, namespace, name)) { + return child; + } + } + return NULL; +} + +int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2) { + /* Just serialize and compare strings for now: awkward, but + simple. */ + char *n1str = rexmpp_xml_serialize(n1); + char *n2str = rexmpp_xml_serialize(n2); + int eq = (strcmp(n1str, n2str) == 0); + free(n1str); + free(n2str); + return eq; +} + +rexmpp_xml_t *rexmpp_xml_children (rexmpp_xml_t *node) { + if (node != NULL && node->type == REXMPP_XML_ELEMENT) { + return node->alt.elem.children; + } + return NULL; +} + +rexmpp_xml_t *rexmpp_xml_first_elem_child (rexmpp_xml_t *node) { + rexmpp_xml_t *child; + for (child = rexmpp_xml_children(node); child != NULL; child = child->next) { + if (child->type == REXMPP_XML_ELEMENT) { + return child; + } + } + return NULL; +} + +rexmpp_xml_t *rexmpp_xml_next_elem_sibling (rexmpp_xml_t *node) { + if (node == NULL) { + return NULL; + } + rexmpp_xml_t *sibling; + for (sibling = node->next; sibling != NULL; sibling = sibling->next) { + if (sibling->type == REXMPP_XML_ELEMENT) { + return sibling; + } + } + return NULL; +} + +const char *rexmpp_xml_text (rexmpp_xml_t *node) { + if (node != NULL && node->type == REXMPP_XML_TEXT) { + return node->alt.text; + } + return NULL; +} + +const char *rexmpp_xml_text_child (rexmpp_xml_t *node) { + return rexmpp_xml_text(rexmpp_xml_children(node)); +} diff --git a/src/rexmpp_xml.h b/src/rexmpp_xml.h new file mode 100644 index 0000000..33f61f2 --- /dev/null +++ b/src/rexmpp_xml.h @@ -0,0 +1,232 @@ +/** + @file rexmpp_xml.h + @brief XML structures and functions for rexmpp + @author defanor <defanor@uberspace.net> + @date 2023 + @copyright MIT license. +*/ + +#ifndef REXMPP_XML_H +#define REXMPP_XML_H + +#include <libxml/tree.h> + +typedef struct rexmpp_xml_qname rexmpp_xml_qname_t; +typedef struct rexmpp_xml_attribute rexmpp_xml_attr_t; +typedef struct rexmpp_xml_node rexmpp_xml_t; + +struct rexmpp_xml_qname { + char *name; + char *namespace; +}; + +struct rexmpp_xml_attribute { + rexmpp_xml_qname_t qname; + char *value; + rexmpp_xml_attr_t *next; +}; + +enum rexmpp_xml_node_type { + REXMPP_XML_ELEMENT, + REXMPP_XML_TEXT +}; + +typedef enum rexmpp_xml_node_type rexmpp_xml_node_type_t; + +struct rexmpp_xml_node { + rexmpp_xml_node_type_t type; + union { + struct { + rexmpp_xml_qname_t qname; + rexmpp_xml_attr_t *attributes; + rexmpp_xml_t *children; + } elem; + char *text; + } alt; + rexmpp_xml_t *next; +}; + + +/** + @brief Frees a single XML node. Does not free its siblings. +*/ +void rexmpp_xml_free (rexmpp_xml_t *node); + +/** + @brief Frees an XML node and its siblings. +*/ +void rexmpp_xml_free_list (rexmpp_xml_t *node); + +/** + @brief Clones a single XML node, without its siblings. +*/ +rexmpp_xml_t *rexmpp_xml_clone (rexmpp_xml_t *node); + +/** + @brief Clones an XML node, together with its siblings. +*/ +rexmpp_xml_t *rexmpp_xml_clone_list (rexmpp_xml_t *node); + +/** + @brief Creates a single ::rexmpp_xml_t XML node out of libxml2's + xmlNode, without siblings. +*/ +rexmpp_xml_t *rexmpp_xml_from_libxml2 (xmlNodePtr from); + +/** + @brief Creates a ::rexmpp_xml_t XML node out of libxml2's xmlNode, + with siblings. +*/ +rexmpp_xml_t *rexmpp_xml_from_libxml2_list (xmlNodePtr from); + +xmlNodePtr rexmpp_xml_to_libxml2 (rexmpp_xml_t *from); + +xmlNodePtr rexmpp_xml_to_libxml2_list (rexmpp_xml_t *from); + +/** + @brief Creates a textual ::rexmpp_xml_t XML node (with type = + ::REXMPP_XML_TEXT). +*/ +rexmpp_xml_t *rexmpp_xml_new_text (const char *str); + +/** + @brief Creates an element ::rexmpp_xml_t XML node (with type = + ::REXMPP_XML_ELEMENT). +*/ +rexmpp_xml_t *rexmpp_xml_new_elem (const char *name, + const char *namespace); + +/** + @brief Adds a child node. +*/ +void rexmpp_xml_add_child (rexmpp_xml_t *node, + rexmpp_xml_t *child); + +/** + @brief Creates a text node, and adds it as a child. +*/ +int rexmpp_xml_add_text (rexmpp_xml_t *node, + const char *str); + +rexmpp_xml_attr_t *rexmpp_xml_attr_new (const char *name, + const char *namespace, + const char *value); + +int rexmpp_xml_add_attr (rexmpp_xml_t *node, + const char *name, + const char *value); + +int rexmpp_xml_remove_attr_ns (rexmpp_xml_t *node, + const char *name, + const char *namespace); + +int rexmpp_xml_remove_attr (rexmpp_xml_t *node, + const char *name); + +int rexmpp_xml_add_attr_ns (rexmpp_xml_t *node, + const char *name, + const char *namespace, + const char *value); + +/** + @brief Adds an "id" attribute to an XML stanza. + @param[in,out] s ::rexmpp + @param[in] node A pointer to an XML stanza. + @returns The same pointer as on input, for more convenient + composition. +*/ +rexmpp_xml_t * +rexmpp_xml_add_id (rexmpp_t *s, + rexmpp_xml_t *node); + +/** + @brief A helper function for XML serialisation. + @param[in] node An XML node. + @returns A string (must be freed by the caller). +*/ +char *rexmpp_xml_serialize (rexmpp_xml_t *node); + +/** + @brief Count the number of siblings after a given node. +*/ +unsigned int rexmpp_xml_siblings_count (rexmpp_xml_t *node); + +/** + @brief Compares the node's name and namespace to given ones. +*/ +int rexmpp_xml_match (rexmpp_xml_t *node, + const char *namespace, + const char *name); + +int rexmpp_xml_is_stanza (rexmpp_xml_t *node); + +/** + @brief Compose an 'error' element. +*/ +rexmpp_xml_t *rexmpp_xml_error (const char *type, const char *condition); + +/** + @brief Matches an XML node against a namespace and an element name. + @param[in] node An XML node to match. + @param[in] namespace An XML namespace. Can be NULL (matches + anything), and it is assumed that the default namespace is + "jabber:client" (so if it is "jabber:client" and an element doesn't + have a namespace defined, this function counts that as a match). + @param[in] name Element name. Can be NULL (matches anything). + @returns 1 on a successful match, 0 otherwise. +*/ +int rexmpp_xml_attr_match (rexmpp_xml_attr_t *attr, + const char *namespace, + const char *name); + +rexmpp_xml_attr_t *rexmpp_xml_find_attr (rexmpp_xml_t *node, + const char *name, + const char *namespace); + +const char *rexmpp_xml_find_attr_val_ns (rexmpp_xml_t *node, + const char *name, + const char *namespace); + +const char *rexmpp_xml_find_attr_val (rexmpp_xml_t *node, + const char *name); + +/** + @brief Finds a child element of an XML node, which matches the + given namespace and name. + @param[in] node The node containing child nodes. + @param[in] namespace The namespace to look for. + @param[in] name The element name to look for. + @returns A pointer to the first matching child node, or NULL if no + matching child elements found. +*/ +rexmpp_xml_t *rexmpp_xml_find_child (rexmpp_xml_t *node, + const char *namespace, + const char *name); + +rexmpp_xml_t *rexmpp_xml_children (rexmpp_xml_t *node); + +const char *rexmpp_xml_text (rexmpp_xml_t *node); + +const char *rexmpp_xml_text_child (rexmpp_xml_t *node); + +rexmpp_xml_t *rexmpp_xml_first_elem_child (rexmpp_xml_t *node); + +rexmpp_xml_t *rexmpp_xml_next_elem_sibling (rexmpp_xml_t *node); + +/** + @brief Compares two XML elements. +*/ +int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2); + +/** + @brief A helper function for XML parsing. + @param[in] str A string to parse. + @param[in] str_len String length. + @returns Parsed XML, or NULL on failure. +*/ +rexmpp_xml_t *rexmpp_xml_parse (const char *str, int str_len); + +rexmpp_xml_t *rexmpp_xml_read_file (const char *path); +int rexmpp_xml_write_file (const char *path, rexmpp_xml_t* node); + +#endif |