Ticket #1281: 0001-purple-fix-file-transfer-memory-management-3.4.2.patch

File 0001-purple-fix-file-transfer-memory-management-3.4.2.patch, 7.1 KB (added by dx, at 2017-01-30T18:14:06Z)
  • protocols/purple/bpurple.h

    From 3721b17878437aa1d826c804be154cb512c227c4 Mon Sep 17 00:00:00 2001
    From: dequis <dx@dxzone.com.ar>
    Date: Sun, 13 Nov 2016 17:00:04 -0300
    Subject: [PATCH] purple: fix file transfer memory management
    
    This means cancelling transfers on logout to avoid crashes, keeping
    track of timeouts, reffing and unreffing the xfers, listening to the
    callbacks from UI and purple more carefully and using the correct
    functions to free the correct things at the correct moments.
    
    Originally intended to fix a crash triggered when the dcc stall timeout
    kicks in after the account is offline, which is apparently very frequent
    with telegram (it sends file transfers while fetching history, and
    randomly disconnects a while later).
    
    Trying to fix that meant opening a can of worms, but after three days of
    work on this bug I'm pretty sure I've finished dealing with the
    resulting mess and tested all the typical edge cases.
    ---
     protocols/purple/bpurple.h |  1 +
     protocols/purple/ft.c      | 79 +++++++++++++++++++++++++++++++++++++++-------
     protocols/purple/purple.c  |  6 ++++
     3 files changed, 75 insertions(+), 11 deletions(-)
    
    diff --git a/protocols/purple/bpurple.h b/protocols/purple/bpurple.h
    index 39677b86..ade22d9c 100644
    a b struct purple_data 
    1212
    1313    GHashTable *input_requests;
    1414    guint next_request_id;
     15    GSList *filetransfers;
    1516};
    1617
    1718#endif /* !BPURPLE_H */
  • protocols/purple/ft.c

    diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c
    index 320fc887..81fee8d0 100644
    a b struct prpl_xfer_data { 
    4141        int fd;
    4242        char *fn, *handle;
    4343        gboolean ui_wants_data;
     44        int timeout;
    4445};
    4546
    4647static file_transfer_t *next_ft;
    static void prpl_xfer_canceled(struct file_transfer *ft, char *reason) 
    6364{
    6465        struct prpl_xfer_data *px = ft->data;
    6566
    66         purple_xfer_request_denied(px->xfer);
     67        if (px->xfer) {
     68                if (!purple_xfer_is_completed(px->xfer) && !purple_xfer_is_canceled(px->xfer)) {
     69                        purple_xfer_cancel_local(px->xfer);
     70                }
     71                px->xfer->ui_data = NULL;
     72                purple_xfer_unref(px->xfer);
     73                px->xfer = NULL;
     74        }
     75}
     76
     77static void prpl_xfer_free(struct file_transfer *ft)
     78{
     79        struct prpl_xfer_data *px = ft->data;
     80        struct purple_data *pd = px->ic->proto_data;
     81
     82        pd->filetransfers = g_slist_remove(pd->filetransfers, px);
     83
     84        if (px->xfer) {
     85                px->xfer->ui_data = NULL;
     86                purple_xfer_unref(px->xfer);
     87        }
     88
     89        if (px->timeout) {
     90                b_event_remove(px->timeout);
     91        }
     92
     93        g_free(px->fn);
     94        g_free(px->handle);
     95        if (px->fd >= 0) {
     96                close(px->fd);
     97        }
     98        g_free(px);
    6799}
    68100
    69101static void prplcb_xfer_new(PurpleXfer *xfer)
    70102{
     103        purple_xfer_ref(xfer);
     104
    71105        if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
    72106                struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
     107                struct purple_data *pd;
    73108
    74109                xfer->ui_data = px;
    75110                px->xfer = xfer;
    static void prplcb_xfer_new(PurpleXfer *xfer) 
    77112                px->fd = -1;
    78113                px->ic = purple_ic_by_pa(xfer->account);
    79114
     115                pd = px->ic->proto_data;
     116                pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
     117
    80118                purple_xfer_set_local_filename(xfer, px->fn);
    81119
    82120                /* Sadly the xfer struct is still empty ATM so come back after
    static gboolean prplcb_xfer_new_send_cb(gpointer data, gint fd, b_input_conditio 
    111149
    112150        px->ft->accept = prpl_xfer_accept;
    113151        px->ft->canceled = prpl_xfer_canceled;
     152        px->ft->free = prpl_xfer_free;
    114153        px->ft->write_request = prpl_xfer_write_request;
    115154
    116155        return FALSE;
    static gboolean prpl_xfer_write_request(struct file_transfer *ft) 
    163202}
    164203
    165204
    166 /* Generic (IM<>UI): */
    167205static void prplcb_xfer_destroy(PurpleXfer *xfer)
    168206{
    169207        struct prpl_xfer_data *px = xfer->ui_data;
    170208
    171         g_free(px->fn);
    172         g_free(px->handle);
    173         if (px->fd >= 0) {
    174                 close(px->fd);
     209        if (px) {
     210                px->xfer = NULL;
    175211        }
    176         g_free(px);
    177212}
    178213
    179214static void prplcb_xfer_progress(PurpleXfer *xfer, double percent)
    static void prplcb_xfer_cancel_remote(PurpleXfer *xfer) 
    223258{
    224259        struct prpl_xfer_data *px = xfer->ui_data;
    225260
    226         if (px->ft) {
     261        if (px && px->ft) {
    227262                imcb_file_canceled(px->ic, px->ft, "Canceled by remote end");
    228         } else {
     263        } else if (px) {
    229264                /* px->ft == NULL for sends, because of the two stages. :-/ */
    230265                imcb_error(px->ic, "File transfer cancelled by remote end");
    231266        }
    static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condi 
    239274void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle)
    240275{
    241276        struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
     277        struct purple_data *pd;
    242278        char *dir, *basename;
    243279
    244280        ft->data = px;
    245281        px->ft = ft;
     282        px->ft->free = prpl_xfer_free;
    246283
    247284        dir = g_strdup("/tmp/bitlbee-purple-ft.XXXXXX");
    248285        if (!mkdtemp(dir)) {
    void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char 
    271308        px->ic = ic;
    272309        px->handle = g_strdup(handle);
    273310
     311        pd = px->ic->proto_data;
     312        pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
     313
    274314        imcb_log(ic,
    275315                 "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait...");
    276316
    277         b_timeout_add(0, purple_transfer_request_cb, ft);
     317        px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
    278318}
    279319
    280320static void purple_transfer_forward(struct file_transfer *ft)
    static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condi 
    294334        file_transfer_t *ft = data;
    295335        struct prpl_xfer_data *px = ft->data;
    296336
     337        px->timeout = 0;
     338
    297339        if (ft->write == NULL) {
    298340                ft->write = prpl_xfer_write;
    299341                imcb_file_recv_start(px->ic, ft);
    static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned 
    321363                imcb_file_finished(px->ic, ft);
    322364                px->ft = NULL;
    323365        } else {
    324                 b_timeout_add(0, purple_transfer_request_cb, ft);
     366                px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
    325367        }
    326368
    327369        return TRUE;
    328370}
    329371
     372void purple_transfer_cancel_all(struct im_connection *ic)
     373{
     374        struct purple_data *pd = ic->proto_data;
     375
     376        while (pd->filetransfers) {
     377                struct prpl_xfer_data *px = pd->filetransfers->data;
     378
     379                if (px->ft) {
     380                        imcb_file_canceled(ic, px->ft, "Logging out");
     381                }
     382
     383                pd->filetransfers = g_slist_remove(pd->filetransfers, px);
     384        }
     385}
     386
    330387
    331388
    332389PurpleXferUiOps bee_xfer_uiops =
  • protocols/purple/purple.c

    diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c
    index d0e8ec40..b72f5137 100644
    a b static char *set_eval_display_name(set_t *set, char *value); 
    4242void purple_request_input_callback(guint id, struct im_connection *ic,
    4343                                   const char *message, const char *who);
    4444
     45void purple_transfer_cancel_all(struct im_connection *ic);
     46
    4547/* purple_request_input specific stuff */
    4648typedef void (*ri_callback_t)(gpointer, const gchar *);
    4749
    static void purple_logout(struct im_connection *ic) 
    365367                imcb_chat_free(ic->groupchats->data);
    366368        }
    367369
     370        if (pd->filetransfers) {
     371                purple_transfer_cancel_all(ic);
     372        }
     373
    368374        purple_account_set_enabled(pd->account, "BitlBee", FALSE);
    369375        purple_connections = g_slist_remove(purple_connections, ic);
    370376        purple_accounts_remove(pd->account);