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

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

    From 69fcb047df6bbbe447738430c055b592a9efc285 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 d1484054..0cc9150a 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 
    244279void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle)
    245280{
    246281        struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
     282        struct purple_data *pd;
    247283        char *dir, *basename;
    248284
    249285        ft->data = px;
    250286        px->ft = ft;
     287        px->ft->free = prpl_xfer_free;
    251288
    252289        dir = g_strdup("/tmp/bitlbee-purple-ft.XXXXXX");
    253290        if (!mkdtemp(dir)) {
    void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char 
    276313        px->ic = ic;
    277314        px->handle = g_strdup(handle);
    278315
     316        pd = px->ic->proto_data;
     317        pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
     318
    279319        imcb_log(ic,
    280320                 "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait...");
    281321
    282         b_timeout_add(0, purple_transfer_request_cb, ft);
     322        px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
    283323}
    284324
    285325static void purple_transfer_forward(struct file_transfer *ft)
    static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condi 
    299339        file_transfer_t *ft = data;
    300340        struct prpl_xfer_data *px = ft->data;
    301341
     342        px->timeout = 0;
     343
    302344        if (ft->write == NULL) {
    303345                ft->write = prpl_xfer_write;
    304346                imcb_file_recv_start(px->ic, ft);
    static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned 
    326368                imcb_file_finished(px->ic, ft);
    327369                px->ft = NULL;
    328370        } else {
    329                 b_timeout_add(0, purple_transfer_request_cb, ft);
     371                px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
    330372        }
    331373
    332374        return TRUE;
    333375}
    334376
     377void purple_transfer_cancel_all(struct im_connection *ic)
     378{
     379        struct purple_data *pd = ic->proto_data;
     380
     381        while (pd->filetransfers) {
     382                struct prpl_xfer_data *px = pd->filetransfers->data;
     383
     384                if (px->ft) {
     385                        imcb_file_canceled(ic, px->ft, "Logging out");
     386                }
     387
     388                pd->filetransfers = g_slist_remove(pd->filetransfers, px);
     389        }
     390}
     391
    335392
    336393
    337394PurpleXferUiOps bee_xfer_uiops =
  • protocols/purple/purple.c

    diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c
    index b00d3078..812c397b 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) 
    348350                return;
    349351        }
    350352
     353        if (pd->filetransfers) {
     354                purple_transfer_cancel_all(ic);
     355        }
     356
    351357        purple_account_set_enabled(pd->account, "BitlBee", FALSE);
    352358        purple_connections = g_slist_remove(purple_connections, ic);
    353359        purple_accounts_remove(pd->account);