about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDmitry Podgorny <pasis.ua@gmail.com>2019-06-01 20:21:46 +0300
committerDmitry Podgorny <pasis.ua@gmail.com>2019-06-01 20:21:46 +0300
commitc2d3c3e41664bbe49863f4b653b66ce2d7ba2788 (patch)
treec483be2ce153188f32ccf48c6460eba96ed1a94b
parentbfa4a2ef1fdee2ae6ae017e8a97ac968509c3ca5 (diff)
downloadprofani-tty-c2d3c3e41664bbe49863f4b653b66ce2d7ba2788.tar.gz
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.
-rw-r--r--src/xmpp/connection.c39
1 files 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