diff options
author | defanor <defanor@uberspace.net> | 2020-11-17 15:07:05 +0300 |
---|---|---|
committer | defanor <defanor@uberspace.net> | 2020-11-17 15:18:22 +0300 |
commit | 48542332c351d21706e61abbd9cb50ce02afac2d (patch) | |
tree | 836aa3365f3de12a14bdd7b5ab4188f09eec8815 | |
parent | 0748958c63052a6c4e0fb20a172ae793fdf8fa6c (diff) |
Add initial JID parsing
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | examples/basic.c | 12 | ||||
-rw-r--r-- | examples/weechat.c | 80 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/rexmpp.c | 72 | ||||
-rw-r--r-- | src/rexmpp.h | 6 | ||||
-rw-r--r-- | src/rexmpp_jid.c | 57 | ||||
-rw-r--r-- | src/rexmpp_jid.h | 24 |
8 files changed, 150 insertions, 109 deletions
@@ -46,7 +46,7 @@ A rough roadmap: [.] Doxygen documentation. [.] Texinfo manual. -[ ] Proper JID handling (RFC 7622). +[.] Proper JID handling (RFC 7622). [ ] Abstraction of the used XML, SASL, TLS, and DNS libraries, and optional usage of alternative ones. Though maybe shouldn't abstract out XML functions and structures: could reuse existing diff --git a/examples/basic.c b/examples/basic.c index 88a1ca4..09a98c4 100644 --- a/examples/basic.c +++ b/examples/basic.c @@ -37,16 +37,8 @@ int my_sasl_property_cb (rexmpp_t *s, Gsasl_property prop) { return GSASL_OK; } if (prop == GSASL_AUTHID) { - char *domainpart = strchr(s->initial_jid, '@'); - if (domainpart != NULL) { - int localpart_len = domainpart - s->initial_jid; - char *localpart = malloc(localpart_len + 1); - localpart[localpart_len] = 0; - strncpy(localpart, s->initial_jid, localpart_len); - gsasl_property_set (s->sasl_session, GSASL_AUTHID, localpart); - free(localpart); - return GSASL_OK; - } + gsasl_property_set (s->sasl_session, GSASL_AUTHID, s->initial_jid.local); + return GSASL_OK; } printf("unhandled gsasl property: %d\n", prop); return GSASL_NO_CALLBACK; diff --git a/examples/weechat.c b/examples/weechat.c index dc6c957..46b9093 100644 --- a/examples/weechat.c +++ b/examples/weechat.c @@ -28,6 +28,7 @@ #include "weechat-plugin.h" #include "rexmpp.h" #include "rexmpp_roster.h" +#include "rexmpp_jid.h" WEECHAT_PLUGIN_NAME("rexmpp"); WEECHAT_PLUGIN_DESCRIPTION("XMPP plugin using librexmpp"); @@ -80,16 +81,8 @@ int my_sasl_property_cb (rexmpp_t *s, Gsasl_property prop) { return GSASL_OK; } if (prop == GSASL_AUTHID) { - char *domainpart = strchr(s->initial_jid, '@'); - if (domainpart != NULL) { - int localpart_len = domainpart - s->initial_jid; - char *localpart = malloc(localpart_len + 1); - localpart[localpart_len] = 0; - strncpy(localpart, s->initial_jid, localpart_len); - gsasl_property_set (s->sasl_session, GSASL_AUTHID, localpart); - free(localpart); - return GSASL_OK; - } + gsasl_property_set (s->sasl_session, GSASL_AUTHID, s->initial_jid.local); + return GSASL_OK; } weechat_printf(wr->server_buffer, "unhandled gsasl property: %d\n", prop); return GSASL_NO_CALLBACK; @@ -137,7 +130,7 @@ int muc_close_cb (const void *ptr, void *data, struct weechat_rexmpp_muc *wrm = (void*)ptr; rexmpp_t *s = &wrm->wr->rexmpp_state; xmlNodePtr presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence")); - xmlNewProp(presence, "from", s->assigned_jid); + xmlNewProp(presence, "from", s->assigned_jid.full); xmlNewProp(presence, "to", wrm->jid); xmlNewProp(presence, "type", "unavailable"); rexmpp_send(s, presence); @@ -152,27 +145,21 @@ int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) { /* free(xml_buf); */ if (rexmpp_xml_match(node, "jabber:client", "message")) { char *from = xmlGetProp(node, "from"); - char *display_name = from; - int i, resource_removed = 0; - for (i = 0; i < strlen(from); i++) { - if (from[i] == '/') { - from[i] = 0; - display_name = from + i + 1; - resource_removed = 1; - break; - } - } if (from != NULL) { - struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", from); + struct rexmpp_jid from_jid; + rexmpp_jid_parse(from, &from_jid); + xmlFree(from); + char *display_name = from_jid.full; + if (from_jid.resource[0]) { + display_name = from_jid.resource; + } + struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", from_jid.bare); if (buf == NULL) { - buf = weechat_buffer_new (from, + buf = weechat_buffer_new (from_jid.bare, &query_input_cb, wr, NULL, &query_close_cb, wr, NULL); weechat_buffer_set(buf, "nicklist", "1"); } - if (resource_removed) { - from[i] = '/'; /* restore */ - } xmlNodePtr body = rexmpp_xml_find_child(node, "jabber:client", "body"); if (body != NULL) { xmlChar *str = xmlNodeGetContent(body); @@ -183,40 +170,33 @@ int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) { xmlFree(str); } } - xmlFree(from); } } if (rexmpp_xml_match(node, "jabber:client", "presence")) { char *presence_type = xmlGetProp(node, "type"); - char *jid = xmlGetProp(node, "from"); - char *full_jid = strdup(jid); - int i; - char *resource = ""; - for (i = 0; i < strlen(jid); i++) { - if (jid[i] == '/') { - jid[i] = 0; - resource = jid + i + 1; - break; - } - } + char *from = xmlGetProp(node, "from"); + struct rexmpp_jid from_jid; + rexmpp_jid_parse(from, &from_jid); + xmlFree(from); + if (rexmpp_xml_find_child(node, "http://jabber.org/protocol/muc#user", "x")) { /* Update MUC nicklist */ - struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", jid); + struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", from_jid.bare); if (buf != NULL) { if (presence_type != NULL && strcmp(presence_type, "unavailable") == 0) { struct t_gui_nick *nick = - weechat_nicklist_search_nick(buf, NULL, resource); + weechat_nicklist_search_nick(buf, NULL, from_jid.resource); if (nick != NULL) { weechat_nicklist_remove_nick(buf, nick); } } else { - weechat_nicklist_add_nick(buf, NULL, resource, + weechat_nicklist_add_nick(buf, NULL, from_jid.resource, "bar_fg", "", "lightgreen", 1); } } - } else if (rexmpp_roster_find_item(s, jid, NULL) != NULL) { + } else if (rexmpp_roster_find_item(s, from_jid.bare, NULL) != NULL) { /* A roster item. */ - struct t_gui_nick *nick = weechat_nicklist_search_nick(wr->server_buffer, NULL, jid); + struct t_gui_nick *nick = weechat_nicklist_search_nick(wr->server_buffer, NULL, from_jid.bare); if (presence_type == NULL) { /* An "available" presence: just ensure that it's shown as online. */ @@ -228,25 +208,23 @@ int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) { just went offline). */ xmlNodePtr cur; int found = 0; + struct rexmpp_jid cur_from_jid; for (cur = s->roster_presence; cur != NULL; cur = xmlNextElementSibling(cur)) { char *cur_from = xmlGetProp(cur, "from"); - if (strcmp(cur_from, full_jid) != 0 && - strncmp(cur_from, jid, strlen(jid)) == 0 && - strlen(cur_from) > strlen(jid) && - cur_from[strlen(jid)] == '/') { + rexmpp_jid_parse(cur_from, &cur_from_jid); + xmlFree(cur_from); + if (strcmp(cur_from_jid.bare, from_jid.bare) == 0 && + strcmp(cur_from_jid.resource, from_jid.resource) != 0) { found = 1; } - free(cur_from); } if (! found) { weechat_nicklist_nick_set(wr->server_buffer, nick, "prefix", ""); } } } - free(jid); - free(full_jid); if (presence_type != NULL) { free(presence_type); } @@ -295,7 +273,7 @@ my_input_cb (const void *ptr, void *data, } else if (input_data[0] == 'j' && input_data[1] == ' ') { char *jid = strdup(input_data + 2); xmlNodePtr presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence")); - xmlNewProp(presence, "from", s->assigned_jid); + xmlNewProp(presence, "from", s->assigned_jid.full); xmlNewProp(presence, "to", jid); xmlNodePtr x = xmlNewNode(NULL, "x"); xmlNewNs(x, "http://jabber.org/protocol/muc", NULL); diff --git a/src/Makefile.am b/src/Makefile.am index 3a977cd..e66a276 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,8 +12,10 @@ librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \ rexmpp_tcp.h rexmpp_tcp.c \ rexmpp_socks.h rexmpp_socks.c \ rexmpp.h rexmpp.c \ - rexmpp_dns.h rexmpp_dns.c -include_HEADERS = rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h rexmpp_dns.h + rexmpp_dns.h rexmpp_dns.c \ + rexmpp_jid.h rexmpp_jid.c +include_HEADERS = rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h \ + rexmpp_dns.h rexmpp_jid.h librexmpp_la_CFLAGS = $(AM_CFLAGS) $(LIBXML_CFLAGS) $(GNUTLS_CFLAGS) \ $(LIBDANE_CFLAGS) $(GSASL_CFLAGS) $(UNBOUND_CFLAGS) librexmpp_la_LIBADD = $(LIBXML_LIBS) $(GNUTLS_LIBS) $(LIBDANE_LIBS) \ diff --git a/src/rexmpp.c b/src/rexmpp.c index f6bf7c3..b0d8d94 100644 --- a/src/rexmpp.c +++ b/src/rexmpp.c @@ -28,6 +28,7 @@ #include "rexmpp_socks.h" #include "rexmpp_roster.h" #include "rexmpp_dns.h" +#include "rexmpp_jid.h" void rexmpp_sax_start_elem_ns (rexmpp_t *s, const char *localname, @@ -268,8 +269,8 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid) s->reconnect_number = 0; s->next_reconnect_time.tv_sec = 0; s->next_reconnect_time.tv_usec = 0; - s->initial_jid = NULL; - s->assigned_jid = NULL; + s->initial_jid.full[0] = '\0'; + s->assigned_jid.full[0] = '\0'; s->stanza_queue_size = 1024; s->send_queue_size = 1024; s->iq_queue_size = 1024; @@ -287,7 +288,10 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, const char *jid) return REXMPP_E_JID; } - s->initial_jid = strdup(jid); + if (rexmpp_jid_parse(jid, &(s->initial_jid))) { + rexmpp_log(s, LOG_CRIT, "Failed to parse the initial JID."); + return REXMPP_E_JID; + } s->xml_parser = xmlCreatePushParserCtxt(&sax, s, "", 0, NULL); @@ -422,10 +426,6 @@ void rexmpp_done (rexmpp_t *s) { s->resolver_ctx = NULL; } xmlFreeParserCtxt(s->xml_parser); - if (s->initial_jid != NULL) { - free(s->initial_jid); - s->initial_jid = NULL; - } if (s->stream_id != NULL) { free(s->stream_id); s->stream_id = NULL; @@ -565,8 +565,8 @@ xmlNodePtr rexmpp_xml_set_delay (rexmpp_t *s, xmlNodePtr node) { strftime(buf, 42, "%FT%T%z", local_time); xmlNodePtr delay = xmlNewChild(node, NULL, "delay", NULL); xmlNewProp(delay, "stamp", buf); - if (s != NULL && s->assigned_jid != NULL) { - xmlNewProp(delay, "from", s->assigned_jid); + if (s != NULL && s->assigned_jid.full[0]) { + xmlNewProp(delay, "from", s->assigned_jid.full); } return node; } @@ -777,8 +777,8 @@ void rexmpp_iq_reply (rexmpp_t *s, xmlNewProp(iq_stanza, "to", to); free(to); } - if (s->assigned_jid != NULL) { - xmlNewProp(iq_stanza, "from", s->assigned_jid); + if (s->assigned_jid.full[0]) { + xmlNewProp(iq_stanza, "from", s->assigned_jid.full); } if (payload != NULL) { xmlAddChild(iq_stanza, payload); @@ -815,8 +815,8 @@ rexmpp_err_t rexmpp_iq_new (rexmpp_t *s, if (to != NULL) { xmlNewProp(iq_stanza, "to", to); } - if (s->assigned_jid != NULL) { - xmlNewProp(iq_stanza, "from", s->assigned_jid); + if (s->assigned_jid.full[0]) { + xmlNewProp(iq_stanza, "from", s->assigned_jid.full); } xmlAddChild(iq_stanza, payload); rexmpp_iq_t *iq = malloc(sizeof(rexmpp_iq_t)); @@ -935,7 +935,7 @@ rexmpp_err_t rexmpp_stream_open (rexmpp_t *s) { "<stream:stream to='%s' version='1.0' " "xml:lang='en' xmlns='jabber:client' " "xmlns:stream='http://etherx.jabber.org/streams'>", - jid_bare_to_host(s->initial_jid)); + s->initial_jid.domain); s->stream_state = REXMPP_STREAM_OPENING; return rexmpp_send_raw(s, buf, strlen(buf)); } @@ -1066,7 +1066,7 @@ rexmpp_err_t rexmpp_tls_handshake (rexmpp_t *s) { } ret = gnutls_certificate_verify_peers3(s->gnutls_session, - jid_bare_to_host(s->initial_jid), + s->initial_jid.domain, &status); if (ret || status) { s->tls_state = REXMPP_TLS_ERROR; @@ -1131,8 +1131,8 @@ rexmpp_err_t rexmpp_tls_start (rexmpp_t *s) { gnutls_session_set_ptr(s->gnutls_session, s); gnutls_alpn_set_protocols(s->gnutls_session, &xmpp_client_protocol, 1, 0); gnutls_server_name_set(s->gnutls_session, GNUTLS_NAME_DNS, - jid_bare_to_host(s->initial_jid), - strlen(jid_bare_to_host(s->initial_jid))); + s->initial_jid.domain, + strlen(s->initial_jid.domain)); gnutls_set_default_priority(s->gnutls_session); gnutls_credentials_set(s->gnutls_session, GNUTLS_CRD_CERTIFICATE, s->gnutls_cred); @@ -1247,7 +1247,7 @@ void rexmpp_srv_cb (void *s_ptr, if (s->server_srv == NULL && s->server_srv_tls == NULL) { /* Failed to resolve anything: a fallback. */ - s->server_host = jid_bare_to_host(s->initial_jid); + s->server_host = s->initial_jid.domain; s->server_port = 5222; rexmpp_start_connecting(s); } else { @@ -1375,7 +1375,7 @@ void rexmpp_stream_is_ready(rexmpp_t *s) { if (s->enable_service_discovery) { xmlNodePtr disco_query = xmlNewNode(NULL, "query"); xmlNewNs(disco_query, "http://jabber.org/protocol/disco#info", NULL); - rexmpp_iq_new(s, "get", jid_bare_to_host(s->initial_jid), + rexmpp_iq_new(s, "get", s->initial_jid.domain, disco_query, rexmpp_iq_discovery_info); } if (s->manage_roster) { @@ -1420,8 +1420,7 @@ void rexmpp_bound (rexmpp_t *s, xmlNodePtr req, xmlNodePtr response, int success xmlNodePtr jid = xmlFirstElementChild(child); if (rexmpp_xml_match(jid, "urn:ietf:params:xml:ns:xmpp-bind", "jid")) { rexmpp_log(s, LOG_INFO, "jid: %s", xmlNodeGetContent(jid)); - s->assigned_jid = malloc(strlen(xmlNodeGetContent(jid)) + 1); - strcpy(s->assigned_jid, xmlNodeGetContent(jid)); + rexmpp_jid_parse(xmlNodeGetContent(jid), &(s->assigned_jid)); } if (s->stream_id == NULL && (child = rexmpp_xml_find_child(s->stream_features, "urn:xmpp:sm:3", @@ -1523,7 +1522,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) { if (from == NULL) { from_server = 1; } else { - if (strcmp(from, jid_bare_to_host(s->assigned_jid)) == 0) { + if (strcmp(from, s->initial_jid.domain) == 0) { from_server = 1; } free(from); @@ -1597,21 +1596,11 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) { s->track_roster_presence) { char *from = xmlGetProp(elem, "from"); if (from != NULL) { - size_t i; - int resource_removed = 0; - for (i = 0; i < strlen(from); i++) { - if (from[i] == '/') { - from[i] = '\0'; - resource_removed = i; - break; - } - } - if (rexmpp_roster_find_item(s, from, NULL) != 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. */ - if (resource_removed) { - /* Restore full JID. */ - from[resource_removed] = '/'; - } char *type = xmlGetProp(elem, "type"); xmlNodePtr cur, prev; if (type == NULL || strcmp(type, "unavailable") == 0) { @@ -1622,7 +1611,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) { cur != NULL; prev = cur, cur = xmlNextElementSibling(cur)) { char *cur_from = xmlGetProp(cur, "from"); - if (strcmp(cur_from, from) == 0) { + if (strcmp(cur_from, from_jid.full) == 0) { if (prev == NULL) { s->roster_presence = cur->next; } else { @@ -1643,7 +1632,6 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) { free(type); } } - free(from); } } @@ -2023,12 +2011,12 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { if (s->manual_host == NULL) { /* Start by querying SRV records. */ rexmpp_log(s, LOG_DEBUG, "start (or reconnect)"); - size_t srv_query_buf_len = strlen(jid_bare_to_host(s->initial_jid)) + + size_t srv_query_buf_len = strlen(s->initial_jid.domain) + strlen("_xmpps-client._tcp..") + 1; char *srv_query = malloc(srv_query_buf_len); snprintf(srv_query, srv_query_buf_len, - "_xmpps-client._tcp.%s.", jid_bare_to_host(s->initial_jid)); + "_xmpps-client._tcp.%s.", s->initial_jid.domain); int err; err = ub_resolve_async(s->resolver_ctx, srv_query, 33, 1, s, rexmpp_srv_cb, NULL); @@ -2037,7 +2025,7 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { srv_query, ub_strerror(err)); } snprintf(srv_query, srv_query_buf_len, - "_xmpp-client._tcp.%s.", jid_bare_to_host(s->initial_jid)); + "_xmpp-client._tcp.%s.", s->initial_jid.domain); err = ub_resolve_async(s->resolver_ctx, srv_query, 33, 1, s, rexmpp_srv_cb, NULL); if (err) { @@ -2104,7 +2092,7 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { s->ping_requested = 1; xmlNodePtr ping_cmd = xmlNewNode(NULL, "ping"); xmlNewNs(ping_cmd, "urn:xmpp:ping", NULL); - rexmpp_iq_new(s, "get", jid_bare_to_host(s->initial_jid), + rexmpp_iq_new(s, "get", s->initial_jid.domain, ping_cmd, rexmpp_pong); } else { rexmpp_log(s, LOG_WARNING, "Ping timeout, reconnecting."); diff --git a/src/rexmpp.h b/src/rexmpp.h index 38cf4b2..8b5632a 100644 --- a/src/rexmpp.h +++ b/src/rexmpp.h @@ -17,7 +17,7 @@ #include "rexmpp_tcp.h" #include "rexmpp_socks.h" #include "rexmpp_dns.h" - +#include "rexmpp_jid.h" typedef struct rexmpp rexmpp_t; @@ -221,7 +221,7 @@ struct rexmpp enum carbons_st carbons_state; /* Basic configuration. */ - char *initial_jid; + struct rexmpp_jid initial_jid; /* Manual host/port configuration. */ const char *manual_host; @@ -255,7 +255,7 @@ struct rexmpp roster_modify_cb_t roster_modify_cb; /* Stream-related state. */ - char *assigned_jid; + struct rexmpp_jid assigned_jid; xmlNodePtr stream_features; xmlNodePtr roster_items; char *roster_ver; diff --git a/src/rexmpp_jid.c b/src/rexmpp_jid.c new file mode 100644 index 0000000..44e2754 --- /dev/null +++ b/src/rexmpp_jid.c @@ -0,0 +1,57 @@ +/** + @file rexmpp_jid.c + @brief JID parsing and manipulation + @author defanor <defanor@uberspace.net> + @date 2020 + @copyright MIT license. +*/ + +#include <stddef.h> +#include <string.h> +#include "rexmpp_jid.h" + +int rexmpp_jid_parse (const char *str, struct rexmpp_jid *jid) { + const char *resource = NULL, *domain = NULL; + size_t i; + size_t resource_len = 0, local_len = 0; + size_t domain_len, bare_len, full_len = strlen(str); + domain_len = full_len; + bare_len = full_len; + + /* Find the separators. */ + for (i = 0; i < full_len; i++) { + if (local_len == 0 && str[i] == '@') { + local_len = i; + domain_len -= local_len + 1; + domain = str + i + 1; + } + if (str[i] == '/') { + resource_len = full_len - i - 1; + domain_len -= resource_len + 1; + bare_len -= resource_len + 1; + resource = str + i + 1; + break; + } + } + + /* Check all the lengths. */ + if (full_len > 3071 || bare_len > 2047 || + local_len > 1023 || resource_len > 1023 || + domain_len > 1023 || domain_len < 1) { + return -1; + } + + /* Copy all the parts. */ + strncpy(jid->full, str, full_len); + jid->full[full_len] = '\0'; + strncpy(jid->bare, str, bare_len); + jid->bare[bare_len] = '\0'; + strncpy(jid->local, str, local_len); + jid->local[local_len] = '\0'; + strncpy(jid->domain, domain, domain_len); + jid->domain[domain_len] = '\0'; + strncpy(jid->resource, resource, resource_len); + jid->resource[resource_len] = '\0'; + + return 0; +} diff --git a/src/rexmpp_jid.h b/src/rexmpp_jid.h new file mode 100644 index 0000000..8613e3d --- /dev/null +++ b/src/rexmpp_jid.h @@ -0,0 +1,24 @@ +/** + @file rexmpp_jid.h + @brief JID parsing and manipulation + @author defanor <defanor@uberspace.net> + @date 2020 + @copyright MIT license. +*/ + +#ifndef REXMPP_JID_H +#define REXMPP_JID_H + +/** @brief A redundant structure for easy access to JID parts and + avoidance of dynamic allocations. */ +struct rexmpp_jid { + char local[1024]; + char domain[1024]; + char resource[1024]; + char bare[2048]; + char full[3072]; +}; + +int rexmpp_jid_parse (const char *str, struct rexmpp_jid *jid); + +#endif |