diff options
Diffstat (limited to 'src/xmpp')
-rw-r--r-- | src/xmpp/avatar.c | 2 | ||||
-rw-r--r-- | src/xmpp/connection.c | 110 | ||||
-rw-r--r-- | src/xmpp/omemo.c | 2 | ||||
-rw-r--r-- | src/xmpp/ox.c | 2 | ||||
-rw-r--r-- | src/xmpp/ox.h | 10 | ||||
-rw-r--r-- | src/xmpp/session.c | 53 | ||||
-rw-r--r-- | src/xmpp/session.h | 2 | ||||
-rw-r--r-- | src/xmpp/stanza.c | 2 | ||||
-rw-r--r-- | src/xmpp/stanza.h | 2 | ||||
-rw-r--r-- | src/xmpp/xmpp.h | 5 |
10 files changed, 165 insertions, 25 deletions
diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 89d34fbc..346e85ba 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -79,7 +79,7 @@ avatar_pep_subscribe(void) message_pubsub_event_handler_add(STANZA_NS_USER_AVATAR_METADATA, _avatar_metadata_handler, NULL, NULL); message_pubsub_event_handler_add(STANZA_NS_USER_AVATAR_DATA, _avatar_metadata_handler, NULL, NULL); - //caps_add_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); + // caps_add_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); if (looking_for) { g_hash_table_destroy(looking_for); diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index 516006a9..c04f9df2 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -872,6 +872,106 @@ connection_set_priority(const int priority) conn.priority = priority; } +#if defined(LIBXMPP_VERSION_MAJOR) && defined(LIBXMPP_VERSION_MINOR) \ + && ((LIBXMPP_VERSION_MAJOR > 0) || (LIBXMPP_VERSION_MINOR >= 12)) +static xmpp_stanza_t* +_get_soh_error(xmpp_stanza_t* error_stanza) +{ + return xmpp_stanza_get_child_by_path(error_stanza, + XMPP_STANZA_NAME_IN_NS("error", STANZA_NS_STREAMS), + XMPP_STANZA_NAME_IN_NS("see-other-host", STANZA_NS_XMPP_STREAMS), + NULL); +} +#else +static xmpp_stanza_t* +_get_soh_error(xmpp_stanza_t* error_stanza) +{ + const char* name = xmpp_stanza_get_name(error_stanza); + const char* ns = xmpp_stanza_get_ns(error_stanza); + if (!name || !ns || strcmp(name, "error") || strcmp(ns, STANZA_NS_STREAMS)) { + log_debug("_get_soh_error: could not find error stanza"); + return NULL; + } + return xmpp_stanza_get_child_by_name_and_ns(error_stanza, "see-other-host", STANZA_NS_XMPP_STREAMS); +} +#endif + +#if GLIB_CHECK_VERSION(2, 66, 0) +static gboolean +_split_url(const char* alturi, gchar** host, gint* port) +{ + /* Construct a valid URI with `schema://` as `g_uri_split_network()` + * requires this to be there. + */ + const char* xmpp = "xmpp://"; + char* xmpp_uri = malloc(strlen(xmpp) + strlen(alturi) + 1); + if (!xmpp_uri) { + log_debug("_get_other_host: malloc failed \"%s\"", alturi); + return false; + } + memcpy(xmpp_uri, xmpp, strlen(xmpp)); + memcpy(xmpp_uri + strlen(xmpp), alturi, strlen(alturi) + 1); + gboolean ret = g_uri_split_network(xmpp_uri, 0, NULL, host, port, NULL); + free(xmpp_uri); + /* fix-up `port` as g_uri_split_network() sets port to `-1` if it's missing + * in the passed-in URI, but libstrophe expects a "missing port" + * to be passed as `0` (which then results in connecting to the standard port). + */ + if (*port == -1) + *port = 0; + return ret; +} +#else +/* poor-mans URL splitting */ +static gboolean +_split_url(const char* alturi, gchar** host, gint* port) +{ + ptrdiff_t hostlen; + /* search ':' from start and end + * if `first` matches `last` it's a `hostname:port` combination + * if `first` is different than `last` it's `[ip:v6]:port` + */ + char* first = strchr(alturi, ':'); + char* last = strrchr(alturi, ':'); + if (first && first == last) { + hostlen = last - alturi; + if (!strtoi_range(last + 1, port, 1, 65535, NULL)) + return FALSE; + } else { + hostlen = strlen(alturi) + 1; + *port = 0; + } + gchar* buf = g_malloc(hostlen); + if (!buf) + return FALSE; + memcpy(buf, alturi, hostlen); + buf[hostlen - 1] = '\0'; + *host = buf; + return TRUE; +} +#endif + +static bool +_get_other_host(xmpp_stanza_t* error_stanza, gchar** host, int* port) +{ + xmpp_stanza_t* soh_error = _get_soh_error(error_stanza); + if (!soh_error || !xmpp_stanza_get_children(soh_error)) { + log_debug("_get_other_host: stream-error contains no see-other-host"); + return false; + } + const char* alturi = xmpp_stanza_get_text_ptr(xmpp_stanza_get_children(soh_error)); + if (!alturi) { + log_debug("_get_other_host: see-other-host contains no text"); + return false; + } + + if (!_split_url(alturi, host, port)) { + log_debug("_get_other_host: Could not split \"%s\"", alturi); + return false; + } + return true; +} + static void _connection_handler(xmpp_conn_t* const xmpp_conn, const xmpp_conn_event_t status, const int error, xmpp_stream_error_t* const stream_error, void* const userdata) @@ -924,6 +1024,14 @@ _connection_handler(xmpp_conn_t* const xmpp_conn, const xmpp_conn_event_t status // login attempt failed } else if (conn.conn_status != JABBER_DISCONNECTING) { + gchar* host; + int port; + if (stream_error && stream_error->stanza && _get_other_host(stream_error->stanza, &host, &port)) { + session_reconnect(host, port); + log_debug("Connection handler: Forcing a re-connect to \"%s\"", host); + conn.conn_status = JABBER_RECONNECT; + return; + } log_debug("Connection handler: Login failed"); session_login_failed(); } @@ -1074,7 +1182,7 @@ _random_bytes_close(void) static void _compute_identifier(const char* barejid) { - //in case of reconnect (lost connection) + // in case of reconnect (lost connection) free(prof_identifier); prof_identifier = g_compute_hmac_for_string(G_CHECKSUM_SHA256, diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c index 26cd6b37..c4d82e42 100644 --- a/src/xmpp/omemo.c +++ b/src/xmpp/omemo.c @@ -437,7 +437,7 @@ omemo_receive_message(xmpp_stanza_t* const stanza, gboolean* trusted) keys = g_list_append(keys, key); continue; - skip: +skip: free(key); } diff --git a/src/xmpp/ox.c b/src/xmpp/ox.c index b8a0a41e..c4efa5d6 100644 --- a/src/xmpp/ox.c +++ b/src/xmpp/ox.c @@ -222,7 +222,7 @@ ox_request_public_key(const char* const jid, const char* const fingerprint) </item> </publish> </pubsub> -</iq> +</iq> </pre> * */ diff --git a/src/xmpp/ox.h b/src/xmpp/ox.h index 143cce95..06acd119 100644 --- a/src/xmpp/ox.h +++ b/src/xmpp/ox.h @@ -2,7 +2,7 @@ * ox.h * vim: expandtab:ts=4:sts=4:sw=4 * - * Copyright (C) 2020 Stefan Kropp <stefan@debxwoody.de> + * Copyright (C) 2020 Stefan Kropp <stefan@debxwoody.de> * * This file is part of Profanity. * @@ -35,10 +35,10 @@ /*! * \page OX OX Implementation - * + * * \section OX XEP-0373: OpenPGP for XMPP * XEP-0373: OpenPGP for XMPP (OX) is the implementation of OpenPGP for XMPP - * replace the XEP-0027. + * replace the XEP-0027. * * https://xmpp.org/extensions/xep-0373.html */ @@ -48,10 +48,10 @@ * * Reads the public key from the given file. Checks the key-information and * pushes the key on PEP. - * + * * https://xmpp.org/extensions/xep-0373.html#announcing-pubkey * - * \param filename name of the file with the public key + * \param filename name of the file with the public key * \return TRUE: success; FALSE: failed */ diff --git a/src/xmpp/session.c b/src/xmpp/session.c index 4a19e211..ce3c557a 100644 --- a/src/xmpp/session.c +++ b/src/xmpp/session.c @@ -98,7 +98,7 @@ static char* saved_status; static void _session_reconnect(void); -static void _session_free_saved_account(void); +static void _session_free_internals(void); static void _session_free_saved_details(void); void @@ -117,8 +117,7 @@ session_connect_with_account(const ProfAccount* const account) log_info("Connecting using account: %s", account->name); - _session_free_saved_account(); - _session_free_saved_details(); + _session_free_internals(); // save account name and password for reconnect saved_account.name = strdup(account->name); @@ -152,8 +151,7 @@ session_connect_with_details(const char* const jid, const char* const passwd, co assert(jid != NULL); assert(passwd != NULL); - _session_free_saved_account(); - _session_free_saved_details(); + _session_free_internals(); // save details for reconnect, remember name for account creating on success saved_details.name = strdup(jid); @@ -240,8 +238,7 @@ session_disconnect(void) void session_shutdown(void) { - _session_free_saved_account(); - _session_free_saved_details(); + _session_free_internals(); chat_sessions_clear(); presence_clear_sub_requests(); @@ -275,6 +272,9 @@ session_process_events(void) } } break; + case JABBER_RECONNECT: + _session_reconnect(); + break; default: break; } @@ -371,8 +371,7 @@ session_login_failed(void) if (reconnect_timer == NULL) { log_debug("Connection handler: No reconnect timer"); sv_ev_failed_login(); - _session_free_saved_account(); - _session_free_saved_details(); + _session_free_internals(); } else { log_debug("Connection handler: Restarting reconnect timer"); if (prefs_get_reconnect() != 0) { @@ -394,8 +393,7 @@ session_lost_connection(void) assert(reconnect_timer == NULL); reconnect_timer = g_timer_new(); } else { - _session_free_saved_account(); - _session_free_saved_details(); + _session_free_internals(); } } @@ -537,6 +535,24 @@ session_check_autoaway(void) g_free(mode); } +static struct +{ + gchar* altdomain; + unsigned short altport; +} reconnect; + +/* This takes ownership of `altdomain`, i.e. the caller must not + * free the value after calling this function. + */ +void +session_reconnect(gchar* altdomain, unsigned short altport) +{ + reconnect.altdomain = altdomain; + reconnect.altport = altport; + assert(reconnect_timer == NULL); + reconnect_timer = g_timer_new(); +} + static void _session_reconnect(void) { @@ -553,19 +569,30 @@ _session_reconnect(void) } else { jid = strdup(account->jid); } + const char* server; + unsigned short port; + if (reconnect.altdomain) { + server = reconnect.altdomain; + port = reconnect.altport; + } else { + server = account->server; + port = account->port; + } log_debug("Attempting reconnect with account %s", account->name); - connection_connect(jid, saved_account.passwd, account->server, account->port, account->tls_policy, account->auth_policy); + connection_connect(jid, saved_account.passwd, server, port, account->tls_policy, account->auth_policy); free(jid); account_free(account); g_timer_start(reconnect_timer); } static void -_session_free_saved_account(void) +_session_free_internals(void) { FREE_SET_NULL(saved_account.name); FREE_SET_NULL(saved_account.passwd); + GFREE_SET_NULL(reconnect.altdomain); + _session_free_saved_details(); } static void diff --git a/src/xmpp/session.h b/src/xmpp/session.h index 62ee9b10..d8565fa4 100644 --- a/src/xmpp/session.h +++ b/src/xmpp/session.h @@ -46,4 +46,6 @@ void session_autoping_fail(void); void session_init_activity(void); void session_check_autoaway(void); +void session_reconnect(gchar* altdomain, unsigned short altport); + #endif diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index d28ed3d2..bfb782da 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2605,7 +2605,7 @@ stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const s char* id = connection_create_stanza_id(); xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); free(id); - //xmpp_stanza_set_to(iq, jid); + // xmpp_stanza_set_to(iq, jid); xmpp_stanza_t* query = xmpp_stanza_new(ctx); xmpp_stanza_set_name(query, STANZA_NAME_QUERY); diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 9e957749..07d1f395 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -245,6 +245,8 @@ #define STANZA_NS_REPORTING "urn:xmpp:reporting:1" #define STANZA_NS_MOOD "http://jabber.org/protocol/mood" #define STANZA_NS_MOOD_NOTIFY "http://jabber.org/protocol/mood+notify" +#define STANZA_NS_STREAMS "http://etherx.jabber.org/streams" +#define STANZA_NS_XMPP_STREAMS "urn:ietf:params:xml:ns:xmpp-streams" #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo" diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 8a2d2eb2..6aaa0cb9 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -72,7 +72,8 @@ typedef enum { JABBER_DISCONNECTING, JABBER_DISCONNECTED, JABBER_RAW_CONNECTING, - JABBER_RAW_CONNECTED + JABBER_RAW_CONNECTED, + JABBER_RECONNECT } jabber_conn_status_t; typedef enum { @@ -99,7 +100,7 @@ typedef struct bookmark_t char* password; char* name; gboolean autojoin; - int ext_gajim_minimize; //0 - non existent, 1 - true, 2 - false + int ext_gajim_minimize; // 0 - non existent, 1 - true, 2 - false } Bookmark; typedef struct disco_identity_t |