From c2d3c3e41664bbe49863f4b653b66ce2d7ba2788 Mon Sep 17 00:00:00 2001 From: Dmitry Podgorny Date: Sat, 1 Jun 2019 20:21:46 +0300 Subject: [PATCH] xmpp/connection: fix #1103 When connection is lost, profanity tries to disconnect what leads to an infinite loop. The loop occurs, because connection_disconnet() runs xmpp_run_once() separately and waits for XMPP_CONN_DISCONNECT event. But it doesn't happen, because the connection object is disconnected. As solution, don't disconnect after XMPP_CONN_DISCONNECT is received. Also, don't free libstrophe objects while the event loops executes, because the event loop continues using objects after callbacks quit. --- src/xmpp/connection.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index afcd8199..62b56666 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -57,7 +57,9 @@ typedef struct prof_conn_t { xmpp_log_t *xmpp_log; xmpp_ctx_t *xmpp_ctx; xmpp_conn_t *xmpp_conn; + gboolean xmpp_in_event_loop; jabber_conn_status_t conn_status; + xmpp_conn_event_t conn_last_event; char *presence_message; int priority; char *domain; @@ -85,7 +87,9 @@ connection_init(void) xmpp_initialize(); conn.xmpp_conn = NULL; conn.xmpp_ctx = NULL; + conn.xmpp_in_event_loop = FALSE; conn.conn_status = JABBER_DISCONNECTED; + conn.conn_last_event = XMPP_CONN_DISCONNECT; conn.presence_message = NULL; conn.domain = NULL; conn.features_by_jid = NULL; @@ -96,7 +100,9 @@ connection_init(void) void connection_check_events(void) { + conn.xmpp_in_event_loop = TRUE; xmpp_run_once(conn.xmpp_ctx, 10); + conn.xmpp_in_event_loop = FALSE; } void @@ -196,21 +202,30 @@ connection_connect(const char *const jid, const char *const passwd, const char * void connection_disconnect(void) { - conn.conn_status = JABBER_DISCONNECTING; - xmpp_disconnect(conn.xmpp_conn); + // don't disconnect already disconnected connection, + // or we get infinite loop otherwise + if (conn.conn_last_event == XMPP_CONN_CONNECT) { + conn.conn_status = JABBER_DISCONNECTING; + xmpp_disconnect(conn.xmpp_conn); - while (conn.conn_status == JABBER_DISCONNECTING) { - session_process_events(); + while (conn.conn_status == JABBER_DISCONNECTING) { + session_process_events(); + } + } else { + conn.conn_status = JABBER_DISCONNECTED; } - if (conn.xmpp_conn) { - xmpp_conn_release(conn.xmpp_conn); - conn.xmpp_conn = NULL; - } + // can't free libstrophe objects while we're in the event loop + if (!conn.xmpp_in_event_loop) { + if (conn.xmpp_conn) { + xmpp_conn_release(conn.xmpp_conn); + conn.xmpp_conn = NULL; + } - if (conn.xmpp_ctx) { - xmpp_ctx_free(conn.xmpp_ctx); - conn.xmpp_ctx = NULL; + if (conn.xmpp_ctx) { + xmpp_ctx_free(conn.xmpp_ctx); + conn.xmpp_ctx = NULL; + } } } @@ -470,6 +485,8 @@ 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) { + conn.conn_last_event = status; + switch (status) { // login success