Mercurial > pidgin
changeset 27872:4a4e9d309fc0
ft: Allow the UI to overloadthe use of fread/fwrite. Closes #9844.
Patch from Jan "HanzZ" Kaluza with a few changes by darkrain42.
committer: Paul Aurich <paul@darkrain42.org>
author | hanzz@soc.pidgin.im |
---|---|
date | Mon, 10 Aug 2009 15:57:35 +0000 |
parents | 2075b5cb608e |
children | 5a38fab8c199 |
files | ChangeLog.API libpurple/ft.c libpurple/ft.h |
diffstat | 3 files changed, 150 insertions(+), 35 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog.API Mon Aug 10 04:40:54 2009 +0000 +++ b/ChangeLog.API Mon Aug 10 15:57:35 2009 +0000 @@ -18,6 +18,9 @@ * Three Blist UI ops used to overload libpurple's built-in saving of the buddy list to blist.xml. If a UI implements these, it probably wants to add the buddies itself and not call purple_blist_load. + * Three File Transfer UI ops used to overload libpurple's use of fread + and fwrite for saving a file locally. These allow a UI to stream a + file through a socket without buffering the file on the local disk. * Jabber plugin signals (see jabber-signals.dox) * purple_account_remove_setting * purple_buddy_destroy @@ -65,6 +68,7 @@ * purple_strequal * purple_utf8_strip_unprintables * purple_util_fetch_url_request_len_with_account + * purple_xfer_ui_ready * xmlnode_from_file * xmlnode_get_parent * xmlnode_set_attrib_full
--- a/libpurple/ft.c Mon Aug 10 04:40:54 2009 +0000 +++ b/libpurple/ft.c Mon Aug 10 15:57:35 2009 +0000 @@ -482,13 +482,16 @@ if (type == PURPLE_XFER_SEND) { /* Sending a file */ /* Check the filename. */ + PurpleXferUiOps *ui_ops; + ui_ops = purple_xfer_get_ui_ops(xfer); + #ifdef _WIN32 if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) #else if (g_strrstr(filename, "../")) #endif { - char *utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); + utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8); purple_xfer_error(type, account, xfer->who, msg); @@ -499,15 +502,20 @@ return; } - if (g_stat(filename, &st) == -1) { - purple_xfer_show_file_error(xfer, filename); - purple_xfer_unref(xfer); - return; + if (ui_ops == NULL || (ui_ops->read == NULL && ui_ops->write == NULL)) { + if (g_stat(filename, &st) == -1) { + purple_xfer_show_file_error(xfer, filename); + purple_xfer_unref(xfer); + return; + } + + purple_xfer_set_local_filename(xfer, filename); + purple_xfer_set_size(xfer, st.st_size); + } else { + utf8 = g_strdup(filename); + purple_xfer_set_local_filename(xfer, filename); } - purple_xfer_set_local_filename(xfer, filename); - purple_xfer_set_size(xfer, st.st_size); - base = g_path_get_basename(filename); utf8 = g_filename_to_utf8(base, -1, NULL, NULL, NULL); g_free(base); @@ -516,7 +524,6 @@ msg = g_strdup_printf(_("Offering to send %s to %s"), utf8, buddy ? purple_buddy_get_alias(buddy) : xfer->who); g_free(utf8); - purple_xfer_conversation_write(xfer, msg, FALSE); g_free(msg); } @@ -946,10 +953,17 @@ guchar *buffer = NULL; gssize r = 0; + ui_ops = purple_xfer_get_ui_ops(xfer); + if (condition & PURPLE_INPUT_READ) { r = purple_xfer_read(xfer, &buffer); if (r > 0) { - const size_t wc = fwrite(buffer, 1, r, xfer->dest_fp); + size_t wc; + if (ui_ops && ui_ops->write) + wc = ui_ops->write(xfer, buffer, r); + else + wc = fwrite(buffer, 1, r, xfer->dest_fp); + if (wc != r) { purple_debug_error("filetransfer", "Unable to write whole buffer.\n"); purple_xfer_cancel_local(xfer); @@ -975,26 +989,53 @@ return; } - buffer = g_malloc0(s); + if (ui_ops && ui_ops->read) { + gssize tmp = ui_ops->read(xfer, &buffer, s); + if (tmp == 0) { + /* + * UI isn't ready to send data. It will call + * purple_xfer_ui_ready when ready, which sets back up this + * watcher. + */ + if (xfer->watcher != 0) { + purple_timeout_remove(xfer->watcher); + xfer->watcher = 0; + } - result = fread(buffer, 1, s, xfer->dest_fp); - if (result != s) { - purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); - purple_xfer_cancel_remote(xfer); - g_free(buffer); - return; + return; + } else if (tmp < 0) { + purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); + purple_xfer_cancel_local(xfer); + return; + } + + result = tmp; + } else { + buffer = g_malloc0(s); + result = fread(buffer, 1, s, xfer->dest_fp); + if (result != s) { + purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); + purple_xfer_cancel_local(xfer); + g_free(buffer); + return; + } } /* Write as much as we're allowed to. */ - r = purple_xfer_write(xfer, buffer, s); + r = purple_xfer_write(xfer, buffer, result); if (r == -1) { purple_xfer_cancel_remote(xfer); g_free(buffer); return; - } else if (r < s) { - /* We have to seek back in the file now. */ - fseek(xfer->dest_fp, r - s, SEEK_CUR); + } else if (r < result) { + if (ui_ops == NULL || (ui_ops->read == NULL && ui_ops->write == NULL)) { + /* We have to seek back in the file now. */ + fseek(xfer->dest_fp, r - s, SEEK_CUR); + } + else { + ui_ops->data_not_sent(xfer, buffer + r, result - r); + } } else { /* * We managed to write the entire buffer. This means our @@ -1016,8 +1057,6 @@ g_free(buffer); - ui_ops = purple_xfer_get_ui_ops(xfer); - if (ui_ops != NULL && ui_ops->update_progress != NULL) ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer)); @@ -1031,18 +1070,21 @@ begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond) { PurpleXferType type = purple_xfer_get_type(xfer); + PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer); - xfer->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer), - type == PURPLE_XFER_RECEIVE ? "wb" : "rb"); + if (ui_ops == NULL || (ui_ops->read == NULL && ui_ops->write == NULL)) { + xfer->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer), + type == PURPLE_XFER_RECEIVE ? "wb" : "rb"); - if (xfer->dest_fp == NULL) { - purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer)); - purple_xfer_cancel_local(xfer); - return; + if (xfer->dest_fp == NULL) { + purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer)); + purple_xfer_cancel_local(xfer); + return; + } + + fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET); } - fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET); - if (xfer->fd) xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer); @@ -1068,6 +1110,26 @@ } void +purple_xfer_ui_ready(PurpleXfer *xfer) +{ + PurpleInputCondition cond; + PurpleXferType type; + + g_return_if_fail(xfer != NULL); + + type = purple_xfer_get_type(xfer); + if (type == PURPLE_XFER_SEND) + cond = PURPLE_INPUT_WRITE; + else /* if (type == PURPLE_XFER_RECEIVE) */ + cond = PURPLE_INPUT_READ; + + if (xfer->watcher == 0) + xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer); + + transfer_cb(xfer, 0, cond); +} + +void purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, unsigned int port) {
--- a/libpurple/ft.h Mon Aug 10 04:40:54 2009 +0000 +++ b/libpurple/ft.h Mon Aug 10 15:57:35 2009 +0000 @@ -77,10 +77,50 @@ void (*cancel_local)(PurpleXfer *xfer); void (*cancel_remote)(PurpleXfer *xfer); + /** + * UI op to write data received from the prpl. The UI must deal with the + * entire buffer and return size, or it is treated as an error. + * + * @param xfer The file transfer structure + * @param buffer The buffer to write + * @param size The size of the buffer + * + * @return size if the write was successful, or a value between 0 and + * size on error. + * @since 2.6.0 + */ + gssize (*write)(PurpleXfer *xfer, const guchar *buffer, gssize size); + + /** + * UI op to read data to send to the prpl for a file transfer. + * + * @param xfer The file transfer structure + * @param buffer A pointer to a buffer. The UI must allocate this buffer. + * libpurple will free the data. + * @param size The maximum amount of data to put in the buffer. + * + * @returns The amount of data in the buffer, 0 if nothing is available, + * and a negative value if an error occurred and the transfer + * should be cancelled (libpurple will cancel). + * @since 2.6.0 + */ + gssize (*read)(PurpleXfer *xfer, guchar **buffer, gssize size); + + /** + * Op to notify the UI that not all the data read in was written. The UI + * should re-enqueue this data and return it the next time read is called. + * + * This MUST be implemented if read and write are implemented. + * + * @param xfer The file transfer structure + * @param buffer A pointer to the beginning of the unwritten data. + * @param size The amount of unwritten data. + * + * @since 2.6.0 + */ + void (*data_not_sent)(PurpleXfer *xfer, const guchar *buffer, gsize size); + void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); } PurpleXferUiOps; /** @@ -132,7 +172,6 @@ gssize (*read)(guchar **buffer, PurpleXfer *xfer); gssize (*write)(const guchar *buffer, size_t size, PurpleXfer *xfer); void (*ack)(PurpleXfer *xfer, const guchar *buffer, size_t size); - } ops; PurpleXferUiOps *ui_ops; /**< UI-specific operations. */ @@ -617,6 +656,16 @@ */ void purple_xfer_conversation_write(PurpleXfer *xfer, char *message, gboolean is_error); +/** + * Allows the UI to signal it's ready to send/receive data (depending on + * the direction of the file transfer. + * + * @param xfer The file transfer for which data is ready. + * + * @since 2.6.0 + */ +void purple_xfer_ui_ready(PurpleXfer *xfer); + /*@}*/ /**************************************************************************/