/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); + } +}