pidgin: 0f8154c9: ft: Allow the UI to overloadthe use of f...
darkrain42 at pidgin.im
darkrain42 at pidgin.im
Mon Aug 10 12:01:01 EDT 2009
-----------------------------------------------------------------
Revision: 0f8154c958deb1d89108a69a069d2c722d225db1
Ancestor: 2cb1f6d67d75d570e3c333f7d4055dd1ed953be4
Author: hanzz at soc.pidgin.im
Date: 2009-08-10T15:57:35
Branch: im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/0f8154c958deb1d89108a69a069d2c722d225db1
Modified files:
ChangeLog.API libpurple/ft.c libpurple/ft.h
ChangeLog:
ft: Allow the UI to overloadthe use of fread/fwrite. Closes #9844.
Patch from Jan "HanzZ" Kaluza with a few changes by darkrain42.
-------------- next part --------------
============================================================
--- ChangeLog.API a84a7848f57afcb793880cc4ae04c719e919dd37
+++ ChangeLog.API 016801c2b8833ac00e0e6f7dca877333abf0a59d
@@ -18,6 +18,9 @@ version 2.6.0 (??/??/2009):
* 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 @@ version 2.6.0 (??/??/2009):
* 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
============================================================
--- libpurple/ft.c 7562e3968f099ac5af95656c018c85a7806c2c99
+++ libpurple/ft.c fe00c06864711f5f577548ddeaa9f5661714da6b
@@ -482,13 +482,16 @@ purple_xfer_request_accepted(PurpleXfer
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 @@ purple_xfer_request_accepted(PurpleXfer
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 @@ purple_xfer_request_accepted(PurpleXfer
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 @@ transfer_cb(gpointer data, gint source,
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 @@ transfer_cb(gpointer data, gint source,
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 @@ transfer_cb(gpointer data, gint source,
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, PurpleI
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
}
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)
{
============================================================
--- libpurple/ft.h 3aa2704a0d52f381185179f08bd2dfab218e1d33
+++ libpurple/ft.h 9761ac804b3e5f1fdb3cf6fa43ddc4b966c5c07d
@@ -77,10 +77,50 @@ typedef struct
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 @@ struct _PurpleXfer
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(Purp
*/
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);
+
/*@}*/
/**************************************************************************/
More information about the Commits
mailing list