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

Tomasz Wasilczyk twasilczyk at pidgin.im
Tue May 20 05:57:29 EDT 2014


Changeset: df9f5de00ea2c33924e95d907f60c8ea32efdb05
Author:	 Tomasz Wasilczyk <twasilczyk at pidgin.im>
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);
+	}
+}



More information about the Commits mailing list