/pidgin/main: df9f5de00ea2: PurpleSocket: fix a crash

Tomasz Wasilczyk <[email protected]>
Newsgroups gmane.comp.gnome.gaim.cvs
Message-ID <[email protected]>
Changeset: df9f5de00ea2c33924e95d907f60c8ea32efdb05
Author:	 Tomasz Wasilczyk <[email protected]>
Date:	 2014-05-20 11:57 +0200
Branch:	 default
URL: https://hg.pidgin.im/pidgin/main/rev/df9f5de00ea2

Description:

PurpleSocket: fix a crash

diffstat:

 libpurple/connection.c             |  11 +++++
 libpurple/connection.h             |  11 +++++
 libpurple/core.c                   |   2 +
 libpurple/internal.h               |  25 +++++++++++
 libpurple/protocols/gg/tcpsocket.c |   2 +
 libpurple/purple-socket.c          |  81 ++++++++++++++++++++++++++++++++++---
 6 files changed, 125 insertions(+), 7 deletions(-)

diffs (240 lines):

diff --git a/libpurple/connection.c b/libpurple/connection.c
--- a/libpurple/connection.c
+++ b/libpurple/connection.c
@@ -313,6 +313,16 @@ purple_connection_get_flags(const Purple
 	return priv->flags;
 }
 
+gboolean
+purple_connection_is_disconnecting(const PurpleConnection *gc)
+{
+	PurpleConnectionPrivate *priv = PURPLE_CONNECTION_GET_PRIVATE(gc);
+
+	g_return_val_if_fail(priv != NULL, TRUE);
+
+	return priv->is_finalizing;
+}
+
 PurpleAccount *
 purple_connection_get_account(const PurpleConnection *gc)
 {
@@ -795,6 +805,7 @@ purple_connection_finalize(GObject *obje
 	}
 
 	purple_http_conn_cancel_all(gc);
+	_purple_socket_cancel_with_connection(gc);
 	purple_proxy_connect_cancel_with_handle(gc);
 
 	connections = g_list_remove(connections, gc);
diff --git a/libpurple/connection.h b/libpurple/connection.h
--- a/libpurple/connection.h
+++ b/libpurple/connection.h
@@ -377,6 +377,17 @@ PurpleConnectionFlags purple_connection_
 	(purple_connection_get_state(gc) == PURPLE_CONNECTION_CONNECTED)
 
 /**
+ * purple_connection_is_disconnecting:
+ * @param gc The connection.
+ *
+ * Checks, if connection is in disconnecting state.
+ *
+ * Returns: %TRUE, if the account is disconnecting.
+ */
+gboolean
+purple_connection_is_disconnecting(const PurpleConnection *gc);
+
+/**
  * purple_connection_get_account:
  * @gc: The connection.
  *
diff --git a/libpurple/core.c b/libpurple/core.c
--- a/libpurple/core.c
+++ b/libpurple/core.c
@@ -196,6 +196,7 @@ purple_core_init(const char *ui)
 	purple_log_init();
 	purple_network_init();
 	purple_pounces_init();
+	_purple_socket_init();
 	purple_proxy_init();
 	purple_dnsquery_init();
 	purple_sound_init();
@@ -277,6 +278,7 @@ purple_core_quit(void)
 	purple_theme_manager_uninit();
 	purple_xfers_uninit();
 	purple_proxy_uninit();
+	_purple_socket_uninit();
 	purple_dnsquery_uninit();
 	_purple_image_store_uninit();
 	purple_network_uninit();
diff --git a/libpurple/internal.h b/libpurple/internal.h
--- a/libpurple/internal.h
+++ b/libpurple/internal.h
@@ -365,4 +365,29 @@ gboolean
 int
 _purple_fstat(int fd, GStatBuf *st);
 
+/**
+ * _purple_socket_cancel_with_connection:
+ * @gc The connection.
+ *
+ * Cancels all #PurpleSocket instances bound with @gc.
+ */
+void
+_purple_socket_cancel_with_connection(PurpleConnection *gc);
+
+/**
+ * _purple_socket_init: (skip)
+ *
+ * Initializes the #PurpleSocket subsystem.
+ */
+void
+_purple_socket_init(void);
+
+/**
+ * _purple_socket_uninit: (skip)
+ *
+ * Uninitializes the #PurpleSocket subsystem.
+ */
+void
+_purple_socket_uninit(void);
+
 #endif /* _PURPLE_INTERNAL_H_ */
diff --git a/libpurple/protocols/gg/tcpsocket.c b/libpurple/protocols/gg/tcpsocket.c
--- a/libpurple/protocols/gg/tcpsocket.c
+++ b/libpurple/protocols/gg/tcpsocket.c
@@ -65,6 +65,8 @@ ggp_tcpsocket_connect(void *_gc, const c
 	PurpleConnection *gc = _gc;
 	PurpleSocket *ps;
 
+	g_return_val_if_fail(!purple_connection_is_disconnecting(gc), NULL);
+
 	g_return_val_if_fail(host != NULL, NULL);
 	g_return_val_if_fail(is_async, NULL);
 
diff --git a/libpurple/purple-socket.c b/libpurple/purple-socket.c
--- a/libpurple/purple-socket.c
+++ b/libpurple/purple-socket.c
@@ -53,6 +53,43 @@ struct _PurpleSocket
 	gpointer cb_data;
 };
 
+static GHashTable *handles = NULL;
+
+static void
+handle_add(PurpleSocket *ps)
+{
+	PurpleConnection *gc = ps->gc;
+	GSList *l;
+
+	l = g_hash_table_lookup(handles, gc);
+	l = g_slist_prepend(l, ps);
+	g_hash_table_insert(handles, gc, l);
+}
+
+static void
+handle_remove(PurpleSocket *ps)
+{
+	PurpleConnection *gc = ps->gc;
+	GSList *l;
+
+	l = g_hash_table_lookup(handles, gc);
+	l = g_slist_remove(l, ps);
+	g_hash_table_insert(handles, gc, l);
+}
+
+void
+_purple_socket_init(void)
+{
+	handles = g_hash_table_new(g_direct_hash, g_direct_equal);
+}
+
+void
+_purple_socket_uninit(void)
+{
+	g_hash_table_destroy(handles);
+	handles = NULL;
+}
+
 PurpleSocket *
 purple_socket_new(PurpleConnection *gc)
 {
@@ -63,6 +100,8 @@ purple_socket_new(PurpleConnection *gc)
 	ps->port = -1;
 	ps->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
 
+	handle_add(ps);
+
 	return ps;
 }
 
@@ -197,6 +236,12 @@ purple_socket_connect(PurpleSocket *ps, 
 
 	g_return_val_if_fail(ps != NULL, FALSE);
 
+	if (ps->gc && purple_connection_is_disconnecting(ps->gc)) {
+		purple_debug_error("socket", "connection is being destroyed");
+		ps->state = PURPLE_SOCKET_STATE_ERROR;
+		return FALSE;
+	}
+
 	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
 		return FALSE;
 	ps->state = PURPLE_SOCKET_STATE_CONNECTING;
@@ -321,14 +366,9 @@ purple_socket_get_data(PurpleSocket *ps,
 	return g_hash_table_lookup(ps->data, key);
 }
 
-void
-purple_socket_destroy(PurpleSocket *ps)
+static void
+purple_socket_cancel(PurpleSocket *ps)
 {
-	if (ps == NULL)
-		return;
-
-	g_free(ps->host);
-
 	if (ps->inpa > 0)
 		purple_input_remove(ps->inpa);
 	ps->inpa = 0;
@@ -337,13 +377,40 @@ purple_socket_destroy(PurpleSocket *ps)
 		purple_ssl_close(ps->tls_connection);
 		ps->fd = -1;
 	}
+	ps->tls_connection = NULL;
 
 	if (ps->raw_connection != NULL)
 		purple_proxy_connect_cancel(ps->raw_connection);
+	ps->raw_connection = NULL;
 
 	if (ps->fd > 0)
 		close(ps->fd);
+	ps->fd = 0;
+}
 
+void
+purple_socket_destroy(PurpleSocket *ps)
+{
+	if (ps == NULL)
+		return;
+
+	handle_remove(ps);
+
+	purple_socket_cancel(ps);
+
+	g_free(ps->host);
 	g_hash_table_destroy(ps->data);
 	g_free(ps);
 }
+
+void
+_purple_socket_cancel_with_connection(PurpleConnection *gc)
+{
+	GSList *it;
+
+	it = g_hash_table_lookup(handles, gc);
+	for (; it; it = g_slist_next(it)) {
+		PurpleSocket *ps = it->data;
+		purple_socket_cancel(ps);
+	}
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.