changeset 29518:51c5cbc0d325

Kill off the use of fseek for non-seekable streams in file transfers. Fixes #9841. committer: John Bailey <rekkanoryo@rekkanoryo.org>
author foufou33@gmail.com
date Mon, 12 Oct 2009 18:41:51 +0000
parents bb873040d7de
children 18667ce1f55d
files libpurple/ft.c
diffstat 1 files changed, 50 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/ft.c	Mon Oct 12 17:45:33 2009 +0000
+++ b/libpurple/ft.c	Mon Oct 12 18:41:51 2009 +0000
@@ -57,6 +57,7 @@
 		PURPLE_XFER_READY_UI   = 0x1,
 		PURPLE_XFER_READY_PRPL = 0x2,
 	} ready;
+	GByteArray *buffer;
 } PurpleXferPrivData;
 
 static int purple_xfer_choose_file(PurpleXfer *xfer);
@@ -66,6 +67,9 @@
 {
 	PurpleXferPrivData *priv = data;
 
+	if (priv->buffer)
+		g_byte_array_free(priv->buffer, TRUE);
+	
 	g_free(priv);
 }
 
@@ -93,14 +97,21 @@
 	xfer->type    = type;
 	xfer->account = account;
 	xfer->who     = g_strdup(who);
-	xfer->ui_ops  = purple_xfers_get_ui_ops();
+	xfer->ui_ops  = ui_ops = purple_xfers_get_ui_ops();
 	xfer->message = NULL;
 	xfer->current_buffer_size = FT_INITIAL_BUFFER_SIZE;
 	xfer->fd = -1;
 
 	priv = g_new0(PurpleXferPrivData, 1);
 	priv->ready = PURPLE_XFER_READY_NONE;
-
+	
+	if (ui_ops && ui_ops->data_not_sent) {
+		/* If the ui will handle unsent data no need for buffer */
+		priv->buffer = NULL;
+	} else {
+		priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
+	}
+	
 	g_hash_table_insert(xfers_data, xfer, priv);
 
 	ui_ops = purple_xfer_get_ui_ops(xfer);
@@ -116,9 +127,12 @@
 purple_xfer_destroy(PurpleXfer *xfer)
 {
 	PurpleXferUiOps *ui_ops;
+	PurpleXferPrivData *priv;
 
 	g_return_if_fail(xfer != NULL);
 
+	priv = g_hash_table_lookup(xfers_data, xfer);
+
 	/* Close the file browser, if it's open */
 	purple_request_close_with_handle(xfer);
 
@@ -1010,6 +1024,7 @@
 	} else if (xfer->type == PURPLE_XFER_SEND) {
 		size_t result;
 		size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);
+		PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
 
 		/* this is so the prpl can keep the connection open
 		   if it needs to for some odd reason. */
@@ -1021,6 +1036,10 @@
 			return;
 		}
 
+		if (priv->buffer) {		
+			s = s - priv->buffer->len;
+		} 
+
 		if (ui_ops && ui_ops->ui_read) {
 			gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
 			if (tmp == 0) {
@@ -1033,8 +1052,13 @@
 					purple_input_remove(xfer->watcher);
 					xfer->watcher = 0;
 				}
-
-				return;
+				/* 
+				 * if we requested 0 bytes it's only normal that en up here 
+				 * we shouldn't return as we still have something to 
+				 * write in priv->buffer
+				 */
+				if (s != 0)
+					return;
 			} else if (tmp < 0) {
 				purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
 				purple_xfer_cancel_local(xfer);
@@ -1052,29 +1076,40 @@
 				return;
 			}
 		}
-
-		/* Write as much as we're allowed to. */
+		
+		if (priv->buffer) {
+			priv->buffer = g_byte_array_append(priv->buffer, buffer, result);
+			g_free(buffer);
+			buffer = priv->buffer->data;
+			result = priv->buffer->len;
+		}
+		
 		r = purple_xfer_write(xfer, buffer, result);
 
 		if (r == -1) {
 			purple_xfer_cancel_remote(xfer);
 			g_free(buffer);
 			return;
-		} else if (r < result) {
-			if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_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 {
+		} else if (r == result) {
 			/*
 			 * We managed to write the entire buffer.  This means our
 			 * network is fast and our buffer is too small, so make it
 			 * bigger.
 			 */
 			purple_xfer_increase_buffer_size(xfer);
+		} else {
+			if (ui_ops && ui_ops->data_not_sent) 
+				ui_ops->data_not_sent(xfer, buffer + r, result -r);
+		}
+
+		if (priv->buffer) {
+			/* 
+			 * Remove what we wrote
+			 * If we wrote the whole buffer the byte array will be empty
+			 * Otherwise we'll kee what wasn't sent for next time.
+			 */
+			buffer = NULL;
+			priv->buffer = g_byte_array_remove_range(priv->buffer, 0, r);
 		}
 	}