changeset 28222:6fca316de1e2

merged with im.pidgin.pidgin
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Wed, 12 Aug 2009 02:05:32 +0900
parents 766f7905314a (current diff) e502112a03e7 (diff)
children 90d108cda54e
files libpurple/protocols/jabber/si.c
diffstat 8 files changed, 183 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog.API	Mon Aug 10 20:36:54 2009 +0900
+++ b/ChangeLog.API	Wed Aug 12 02:05:32 2009 +0900
@@ -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
@@ -95,6 +99,10 @@
 		* status is set before emitting signals in purple_xfer_set_status.
 		* Creating multiple distinct chats with the same name (i.e. "MSN Chat")
 		  is deprecated and will be removed in libpurple 3.0.0.
+		* purple_xfer_start now accepts -1 as the fd parameter if the protocol
+		  plugin will administer the transfer itself. 0 is still accepted for
+		  backward compatibility since older versions of libpurple will not
+		  accept -1.
 
 		Deprecated:
 		* buddy-added and buddy-removed blist signals
--- a/libpurple/ft.c	Mon Aug 10 20:36:54 2009 +0900
+++ b/libpurple/ft.c	Wed Aug 12 02:05:32 2009 +0900
@@ -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,19 +1070,22 @@
 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)
+	if (xfer->fd != -1)
 		xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer);
 
 	xfer->start_time = time(NULL);
@@ -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->fd != -1)
+		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)
 {
@@ -1081,6 +1143,13 @@
 
 	purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_STARTED);
 
+	/*
+	 * FIXME 3.0.0 -- there's too much broken code depending on fd == 0
+	 * meaning "don't use a real fd"
+	 */
+	if (fd == 0)
+		fd = -1;
+
 	if (type == PURPLE_XFER_RECEIVE) {
 		cond = PURPLE_INPUT_READ;
 
@@ -1127,7 +1196,7 @@
 		xfer->watcher = 0;
 	}
 
-	if (xfer->fd != 0)
+	if (xfer->fd != -1)
 		close(xfer->fd);
 
 	if (xfer->dest_fp != NULL) {
@@ -1190,7 +1259,7 @@
 		xfer->watcher = 0;
 	}
 
-	if (xfer->fd != 0)
+	if (xfer->fd != -1)
 		close(xfer->fd);
 
 	if (xfer->dest_fp != NULL) {
@@ -1255,7 +1324,7 @@
 		xfer->watcher = 0;
 	}
 
-	if (xfer->fd != 0)
+	if (xfer->fd != -1)
 		close(xfer->fd);
 
 	if (xfer->dest_fp != NULL) {
--- a/libpurple/ft.h	Mon Aug 10 20:36:54 2009 +0900
+++ b/libpurple/ft.h	Wed Aug 12 02:05:32 2009 +0900
@@ -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. */
@@ -548,6 +587,12 @@
  * file receive transfer. On send, @a fd must be specified, and
  * @a ip and @a port are ignored.
  *
+ * Prior to libpurple 2.6.0, passing '0' to @a fd was special-cased to
+ * allow the protocol plugin to facilitate the file transfer itself. As of
+ * 2.6.0, this is supported (for backward compatibility), but will be
+ * removed in libpurple 3.0.0. If a prpl detects that the running libpurple
+ * is running 2.6.0 or higher, it should use the invalid fd '-1'.
+ *
  * @param xfer The file transfer.
  * @param fd   The file descriptor for the socket.
  * @param ip   The IP address to connect to.
@@ -617,6 +662,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);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/protocols/jabber/buddy.c	Mon Aug 10 20:36:54 2009 +0900
+++ b/libpurple/protocols/jabber/buddy.c	Wed Aug 12 02:05:32 2009 +0900
@@ -513,6 +513,7 @@
 
 		xmlnode_insert_data(binval, enc, -1);
 		g_free(enc);
+		purple_imgstore_unref(img);
 	} else if (vc_node) {
 		xmlnode *photo;
 		/* TODO: Remove all PHOTO children? (see above note) */
--- a/libpurple/protocols/jabber/iq.c	Mon Aug 10 20:36:54 2009 +0900
+++ b/libpurple/protocols/jabber/iq.c	Wed Aug 12 02:05:32 2009 +0900
@@ -282,11 +282,6 @@
 	id = xmlnode_get_attrib(packet, "id");
 	iq_type = xmlnode_get_attrib(packet, "type");
 
-	signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin,
-			"jabber-receiving-iq", js->gc, iq_type, id, from, packet));
-	if (signal_return)
-		return;
-
 	/*
 	 * child will be either the first tag child or NULL if there is no child.
 	 * Historically, we used just the 'query' subchild, but newer XEPs use
@@ -345,6 +340,11 @@
 		return;
 	}
 
+	signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin,
+			"jabber-receiving-iq", js->gc, iq_type, id, from, packet));
+	if (signal_return)
+		return;
+
 	/* First, lets see if a special callback got registered */
 	if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) {
 		if((jcd = g_hash_table_lookup(js->iq_callbacks, id))) {
--- a/libpurple/protocols/jabber/si.c	Mon Aug 10 20:36:54 2009 +0900
+++ b/libpurple/protocols/jabber/si.c	Wed Aug 12 02:05:32 2009 +0900
@@ -1072,7 +1072,7 @@
 			jsx->ibb_session = sess;
 
 			/* start the transfer */
-			purple_xfer_start(xfer, 0, NULL, 0);
+			purple_xfer_start(xfer, -1, NULL, 0);
 			return TRUE;
 		} else {
 			/* failed to create IBB session */
@@ -1155,7 +1155,7 @@
 			return;
 		}
 
-		purple_xfer_start(xfer, 0, NULL, 0);
+		purple_xfer_start(xfer, -1, NULL, 0);
 		purple_xfer_set_bytes_sent(xfer, 0);
 		purple_xfer_update_progress(xfer);
 		jabber_si_xfer_ibb_send_data(sess);
--- a/libpurple/protocols/msn/slplink.c	Mon Aug 10 20:36:54 2009 +0900
+++ b/libpurple/protocols/msn/slplink.c	Wed Aug 12 02:05:32 2009 +0900
@@ -456,7 +456,7 @@
 	slpmsg->info = "SLP FILE";
 
 	xfer = (PurpleXfer *)slpcall->xfer;
-	purple_xfer_start(slpcall->xfer, 0, NULL, 0);
+	purple_xfer_start(slpcall->xfer, -1, NULL, 0);
 	slpmsg->fp = xfer->dest_fp;
 	if (g_stat(purple_xfer_get_local_filename(xfer), &st) == 0)
 		slpmsg->size = st.st_size;
@@ -537,7 +537,7 @@
 					if (xfer != NULL)
 					{
 						purple_xfer_ref(xfer);
-						purple_xfer_start(xfer,	0, NULL, 0);
+						purple_xfer_start(xfer,	-1, NULL, 0);
 
 						if (xfer->data == NULL) {
 							purple_xfer_unref(xfer);
--- a/libpurple/protocols/msnp9/slplink.c	Mon Aug 10 20:36:54 2009 +0900
+++ b/libpurple/protocols/msnp9/slplink.c	Wed Aug 12 02:05:32 2009 +0900
@@ -496,7 +496,7 @@
 	slpmsg->info = "SLP FILE";
 #endif
 	xfer = (PurpleXfer *)slpcall->xfer;
-	purple_xfer_start(slpcall->xfer, 0, NULL, 0);
+	purple_xfer_start(slpcall->xfer, -1, NULL, 0);
 	slpmsg->fp = xfer->dest_fp;
 	if (g_stat(purple_xfer_get_local_filename(xfer), &st) == 0)
 		slpmsg->size = st.st_size;
@@ -561,7 +561,7 @@
 					if (xfer != NULL)
 					{
 						purple_xfer_ref(xfer);
-						purple_xfer_start(xfer,	0, NULL, 0);
+						purple_xfer_start(xfer,	-1, NULL, 0);
 
 						if (xfer->data == NULL) {
 							purple_xfer_unref(xfer);