From 78ac47158bb248b5719511af9136783974f79b1a Mon Sep 17 00:00:00 2001 From: Jakub Adam Date: Mon, 17 May 2010 22:30:24 +0200 Subject: [PATCH] Fix #3001523: Cancelling a long pending file transfer crashes Pidgin After some time of inactivity, Communicator sends SIP BYE causing SIPE to deallocate corresponding sip_dialog. When file transfer was afterwards canceled by user, code attempted to access the freed structure, leading to segfault. Terminating dialog now cancels all pending file transfers with that particular contact. Accept/decline screen window is replaced with a message that remote peer cancelled the transfer, not allowing user to make invalid action anymore. --- src/core/sipe-core-private.h | 3 -- src/core/sipe-dialog.c | 6 ++++ src/core/sipe-dialog.h | 1 + src/core/sipe-ft.c | 67 ++++++++++++++++++-------------------------- src/core/sipe-ft.h | 18 +++++------- src/core/sipe.c | 25 +++++++++-------- src/purple/purple-ft.c | 12 ++++++++ 7 files changed, 67 insertions(+), 65 deletions(-) diff --git a/src/core/sipe-core-private.h b/src/core/sipe-core-private.h index ee618d69..8373fd9d 100644 --- a/src/core/sipe-core-private.h +++ b/src/core/sipe-core-private.h @@ -55,9 +55,6 @@ struct sipe_core_private { /* Buddies */ GHashTable *buddies; - /* File Transfer */ - GHashTable *filetransfers; - /* Scheduling system */ GSList *timeouts; diff --git a/src/core/sipe-dialog.c b/src/core/sipe-dialog.c index 01857239..5ea79e5a 100644 --- a/src/core/sipe-dialog.c +++ b/src/core/sipe-dialog.c @@ -32,6 +32,7 @@ #include "sipe-dialog.h" #include "sipe-session.h" #include "sipe-utils.h" +#include "sipe-ft.h" void sipe_dialog_free(struct sip_dialog *dialog) { @@ -55,6 +56,11 @@ void sipe_dialog_free(struct sip_dialog *dialog) g_free(data); } + while (dialog->filetransfers) { + struct sipe_file_transfer *ft = dialog->filetransfers->data; + sipe_core_ft_deallocate(ft); + } + g_free(dialog->callid); g_free(dialog->ourtag); g_free(dialog->theirtag); diff --git a/src/core/sipe-dialog.h b/src/core/sipe-dialog.h index 8083f559..e4ab02e5 100644 --- a/src/core/sipe-dialog.h +++ b/src/core/sipe-dialog.h @@ -54,6 +54,7 @@ struct sip_dialog { GSList *routes; gchar *request; GSList *supported; /* counterparty capabilities */ + GSList *filetransfers; int cseq; /** corresponds to Session-Expires SIP header value */ int expires; diff --git a/src/core/sipe-ft.c b/src/core/sipe-ft.c index 6f222035..3bea8fc1 100644 --- a/src/core/sipe-ft.c +++ b/src/core/sipe-ft.c @@ -259,9 +259,10 @@ void sipe_ft_deallocate(struct sipe_file_transfer *ft) void sipe_core_ft_deallocate(struct sipe_file_transfer *ft) { struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE; + struct sip_dialog *dialog = ft_private->dialog; - g_hash_table_remove(ft_private->sipe_private->filetransfers, - ft_private->invitation_cookie); + dialog->filetransfers = g_slist_remove(dialog->filetransfers, ft_private); + sipe_ft_deallocate(ft); } void sipe_core_ft_cancel(struct sipe_file_transfer *ft) @@ -470,27 +471,24 @@ void sipe_core_ft_outgoing_init(struct sipe_file_transfer *ft, filename, size); - struct sip_session *session = sipe_session_find_or_add_im(sipe_private, - who); - - g_hash_table_insert(sipe_private->filetransfers, - g_strdup(ft_private->invitation_cookie), - ft_private); + struct sip_session *session = sipe_session_find_or_add_im(sipe_private, who); // Queue the message sipe_session_enqueue_message(session, body, "text/x-msmsgsinvite"); dialog = sipe_dialog_find(session, who); if (dialog && !dialog->outgoing_invite) { - ft_private->dialog = dialog; sipe_im_process_queue(sipe_private, session); } else if (!dialog || !dialog->outgoing_invite) { // Need to send the INVITE to get the outgoing dialog setup sipe_invite(sipe_private, session, who, body, "text/x-msmsgsinvite", NULL, FALSE); + dialog = sipe_dialog_find(session, who); } - g_free(body); + dialog->filetransfers = g_slist_append(dialog->filetransfers, ft_private); + ft_private->dialog = dialog; + g_free(body); } void sipe_core_ft_outgoing_start(struct sipe_file_transfer *ft, @@ -757,23 +755,12 @@ gssize sipe_core_ft_write(struct sipe_file_transfer *ft, } void sipe_ft_incoming_transfer(struct sipe_core_private *sipe_private, - struct sipmsg *msg, + struct sip_dialog *dialog, const GSList *body) { struct sipe_file_transfer_private *ft_private; - const gchar *callid = sipmsg_find_header(msg, "Call-ID"); - gchar *from = parse_from(sipmsg_find_header(msg, "From")); - struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, - callid, - from); gsize file_size; - g_free(from); - if (!session) { - SIPE_DEBUG_ERROR_NOFORMAT("sipe_ft_incoming_transfer: can't find session for remote party"); - return; - } - ft_private = g_new0(struct sipe_file_transfer_private, 1); ft_private->sipe_private = sipe_private; @@ -782,39 +769,40 @@ void sipe_ft_incoming_transfer(struct sipe_core_private *sipe_private, ft_private->invitation_cookie = g_strdup(sipe_utils_nameval_find(body, "Invitation-Cookie")); - ft_private->dialog = sipe_dialog_find(session, session->with); + ft_private->dialog = dialog; file_size = g_ascii_strtoull(sipe_utils_nameval_find(body, "Application-FileSize"), NULL, 10); sipe_backend_ft_incoming(SIPE_CORE_PUBLIC, SIPE_FILE_TRANSFER_PUBLIC, - session->with, + dialog->with, sipe_utils_nameval_find(body, "Application-File"), file_size); if (ft_private->public.backend_private != NULL) { - g_hash_table_insert(sipe_private->filetransfers, - g_strdup(ft_private->invitation_cookie), - ft_private); + ft_private->dialog->filetransfers = g_slist_append(ft_private->dialog->filetransfers, ft_private); } else { sipe_ft_deallocate(SIPE_FILE_TRANSFER_PUBLIC); } } -static struct sipe_file_transfer_private *sipe_find_ft(struct sipe_core_private *sipe_private, - const GSList *body) +static struct sipe_file_transfer_private * +sipe_find_ft(const struct sip_dialog *dialog, const gchar *inv_cookie) { - return g_hash_table_lookup(sipe_private->filetransfers, - sipe_utils_nameval_find(body, - "Invitation-Cookie")); + GSList *ftlist = dialog->filetransfers; + for (; ftlist != NULL; ftlist = ftlist->next) { + struct sipe_file_transfer_private *ft_private = ftlist->data; + if (sipe_strequal(ft_private->invitation_cookie, inv_cookie)) + return ft_private; + } + return NULL; } -void sipe_ft_incoming_accept(struct sipe_core_private *sipe_private, - const GSList *body) +void sipe_ft_incoming_accept(struct sip_dialog *dialog, const GSList *body) { - struct sipe_file_transfer_private *ft_private = sipe_find_ft(sipe_private, - body); + const gchar *inv_cookie = sipe_utils_nameval_find(body, "Invitation-Cookie"); + struct sipe_file_transfer_private *ft_private = sipe_find_ft(dialog, inv_cookie); if (ft_private) { const gchar *ip = sipe_utils_nameval_find(body, "IP-Address"); @@ -879,11 +867,10 @@ void sipe_ft_incoming_accept(struct sipe_core_private *sipe_private, } } -void sipe_ft_incoming_cancel(struct sipe_core_private *sipe_private, - const GSList *body) +void sipe_ft_incoming_cancel(struct sip_dialog *dialog, const GSList *body) { - struct sipe_file_transfer_private *ft_private = sipe_find_ft(sipe_private, - body); + const gchar *inv_cookie = sipe_utils_nameval_find(body, "Invitation-Cookie"); + struct sipe_file_transfer_private *ft_private = sipe_find_ft(dialog, inv_cookie); if (ft_private) sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER_PUBLIC); diff --git a/src/core/sipe-ft.h b/src/core/sipe-ft.h index 0ef02a40..d36efa25 100644 --- a/src/core/sipe-ft.h +++ b/src/core/sipe-ft.h @@ -23,7 +23,6 @@ */ /* Forward declarations */ -struct sipmsg; struct sipe_core_private; struct sipe_file_transfer; @@ -31,7 +30,7 @@ struct sipe_file_transfer; /** * Deallocate file transfer data structure */ -void sipe_ft_deallocate(struct sipe_file_transfer *ft); +void sipe_core_ft_deallocate(struct sipe_file_transfer *ft); /** * Called when remote peer wants to send a file. @@ -40,11 +39,11 @@ void sipe_ft_deallocate(struct sipe_file_transfer *ft); * purple_xfer_request(). * * @param sipe_private Sipe core private data - * @param msg SIP message + * @param dialog SIP dialog used for the file transfer * @param body parsed SIP message body as name-value pairs */ void sipe_ft_incoming_transfer(struct sipe_core_private *sipe_private, - struct sipmsg *msg, + struct sip_dialog *dialog, const GSList *body); /** @@ -53,23 +52,20 @@ void sipe_ft_incoming_transfer(struct sipe_core_private *sipe_private, * This message is sent during the negotiation phase when parameters of the * transfer like IP address or TCP port are going to be set up. * - * @param sipe_private Sipe core private data + * @param dialog SIP dialog used for the file transfer * @param body parsed SIP message body as name-value pairs */ -void sipe_ft_incoming_accept(struct sipe_core_private *sipe_private, - const GSList *body); +void sipe_ft_incoming_accept(struct sip_dialog *dialog, const GSList *body); /** * Called when remote peer cancels ongoing file transfer. * * Function dispatches the request to libpurple * - * @param sipe_private Sipe core private data - * @param body SIP message body + * @param dialog SIP dialog used for the file transfer * @param body parsed SIP message body as name-value pairs */ -void sipe_ft_incoming_cancel(struct sipe_core_private *sipe_private, - const GSList *body); +void sipe_ft_incoming_cancel(struct sip_dialog *dialog, const GSList *body); /** * Parses file transfer message body and creates a list with name-value pairs diff --git a/src/core/sipe.c b/src/core/sipe.c index 375cf067..c17e1b68 100644 --- a/src/core/sipe.c +++ b/src/core/sipe.c @@ -3597,7 +3597,7 @@ process_message_response(struct sipe_core_private *sipe_private, message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite")) { GSList *parsed_body = sipe_ft_parse_msg_body(msg->body); - sipe_ft_incoming_cancel(sipe_private, parsed_body); + sipe_ft_incoming_cancel(dialog, parsed_body); sipe_utils_nameval_free(parsed_body); } @@ -3869,7 +3869,7 @@ process_invite_response(struct sipe_core_private *sipe_private, message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite")) { GSList *parsed_body = sipe_ft_parse_msg_body(message->body); - sipe_ft_incoming_cancel(sipe_private, parsed_body); + sipe_ft_incoming_cancel(dialog, parsed_body); sipe_utils_nameval_free(parsed_body); } @@ -4472,7 +4472,7 @@ static void do_reauthenticate_cb(struct sipe_core_private *sipe_private, static gboolean sipe_process_incoming_x_msmsgsinvite(struct sipe_core_private *sipe_private, - struct sipmsg *msg, + struct sip_dialog *dialog, GSList *parsed_body) { gboolean found = FALSE; @@ -4481,13 +4481,13 @@ sipe_process_incoming_x_msmsgsinvite(struct sipe_core_private *sipe_private, const gchar *invitation_command = sipe_utils_nameval_find(parsed_body, "Invitation-Command"); if (sipe_strequal(invitation_command, "INVITE")) { - sipe_ft_incoming_transfer(sipe_private, msg, parsed_body); + sipe_ft_incoming_transfer(sipe_private, dialog, parsed_body); found = TRUE; } else if (sipe_strequal(invitation_command, "CANCEL")) { - sipe_ft_incoming_cancel(sipe_private, parsed_body); + sipe_ft_incoming_cancel(dialog, parsed_body); found = TRUE; } else if (sipe_strequal(invitation_command, "ACCEPT")) { - sipe_ft_incoming_accept(sipe_private, parsed_body); + sipe_ft_incoming_accept(dialog, parsed_body); found = TRUE; } } @@ -4568,8 +4568,13 @@ static void process_incoming_message(struct sipe_core_private *sipe_private, send_sip_response(sipe_private, msg, 200, "OK", NULL); found = TRUE; } else if (g_str_has_prefix(contenttype, "text/x-msmsgsinvite")) { + const gchar *callid = sipmsg_find_header(msg, "Call-ID"); + struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, + callid, + from); + struct sip_dialog *dialog = sipe_dialog_find(session, from); GSList *body = sipe_ft_parse_msg_body(msg->body); - found = sipe_process_incoming_x_msmsgsinvite(sipe_private, msg, body); + found = sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, body); sipe_utils_nameval_free(body); if (found) { send_sip_response(sipe_private, msg, 200, "OK", NULL); @@ -4844,11 +4849,12 @@ static void process_incoming_invite(struct sipe_core_private *sipe_private, gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL); if (tmp) { gsize len; + struct sip_dialog *dialog = sipe_dialog_find(session, from); gchar *body = (gchar *) g_base64_decode(tmp, &len); GSList *parsed_body = sipe_ft_parse_msg_body(body); - sipe_process_incoming_x_msmsgsinvite(sipe_private, msg, parsed_body); + sipe_process_incoming_x_msmsgsinvite(sipe_private, dialog, parsed_body); sipe_utils_nameval_free(parsed_body); sipmsg_add_header(msg, "Supported", "ms-text-format"); /* accepts received message */ } @@ -7750,8 +7756,6 @@ struct sipe_core_public *sipe_core_allocate(const gchar *signin_name, g_free, (GDestroyNotify)g_hash_table_destroy); sip->subscriptions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)sipe_subscription_free); - sipe_private->filetransfers = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, (GDestroyNotify)sipe_ft_deallocate); sip->status = g_strdup(SIPE_STATUS_ID_UNKNOWN); return((struct sipe_core_public *)sipe_private); @@ -7866,7 +7870,6 @@ void sipe_core_deallocate(struct sipe_core_public *sipe_public) g_hash_table_destroy(sip->our_publications); g_hash_table_destroy(sip->user_state_publications); g_hash_table_destroy(sip->subscriptions); - g_hash_table_destroy(sipe_private->filetransfers); if (sip->groups) { GSList *entry = sip->groups; diff --git a/src/purple/purple-ft.c b/src/purple/purple-ft.c index 85d9df46..a91f3407 100644 --- a/src/purple/purple-ft.c +++ b/src/purple/purple-ft.c @@ -86,6 +86,8 @@ const gchar *sipe_backend_ft_get_error(SIPE_UNUSED_PARAMETER struct sipe_file_tr void sipe_backend_ft_deallocate(struct sipe_file_transfer *ft) { struct sipe_backend_file_transfer *backend_ft = ft->backend_private; + PurpleXfer *xfer = backend_ft->xfer; + PurpleXferStatusType status = purple_xfer_get_status(xfer); if (backend_ft->listenfd >= 0) { SIPE_DEBUG_INFO("sipe_ft_free_xfer_struct: closing listening socket %d", @@ -94,6 +96,16 @@ void sipe_backend_ft_deallocate(struct sipe_file_transfer *ft) } if (backend_ft->listener) purple_network_listen_cancel(backend_ft->listener); + + // If file transfer is not finished, cancel it + if ( status != PURPLE_XFER_STATUS_DONE + && status != PURPLE_XFER_STATUS_CANCEL_LOCAL + && status != PURPLE_XFER_STATUS_CANCEL_REMOTE) { + purple_xfer_set_cancel_recv_fnc(xfer, NULL); + purple_xfer_set_cancel_send_fnc(xfer, NULL); + purple_xfer_cancel_remote(xfer); + } + g_free(backend_ft); } -- 2.11.4.GIT