diff src/protocols/yahoo/yahoo_filexfer.c @ 13200:33bef17125c2

[gaim-migrate @ 15563] This is the soon-to-be-infamous nonblocking network activity patch that I've been working on. Feel free to yell at me if this makes you unhappy. committer: Tailor Script <tailor@pidgin.im>
author Daniel Atallah <daniel.atallah@gmail.com>
date Thu, 09 Feb 2006 04:17:56 +0000
parents a1d5f154585b
children aaeb6ea63c56
line wrap: on
line diff
--- a/src/protocols/yahoo/yahoo_filexfer.c	Thu Feb 09 04:14:54 2006 +0000
+++ b/src/protocols/yahoo/yahoo_filexfer.c	Thu Feb 09 04:17:56 2006 +0000
@@ -39,16 +39,21 @@
 	GaimConnection *gc;
 	long expires;
 	gboolean started;
+	guchar *txbuf;
+	gsize txbuflen;
+	gsize txbuf_written;
+	guint tx_handler;
 	gchar *rxqueue;
 	guint rxlen;
 };
 
 static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd)
 {
-	if (xd->host)
-		g_free(xd->host);
-	if (xd->path)
-		g_free(xd->path);
+	g_free(xd->host);
+	g_free(xd->path);
+	g_free(xd->txbuf);
+	if (xd->tx_handler)
+		gaim_input_remove(xd->tx_handler);
 	g_free(xd);
 }
 
@@ -56,7 +61,7 @@
 {
 	GaimXfer *xfer;
 	struct yahoo_xfer_data *xd;
-	gchar *buf;
+	int total_len, written;
 
 	gaim_debug(GAIM_DEBUG_INFO, "yahoo",
 			   "AAA - in yahoo_receivefile_connected\n");
@@ -71,29 +76,53 @@
 		return;
 	}
 
+	/* The first time we get here, assemble the tx buffer */
+	if (xd->txbuflen == 0) {
+		xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
+			      xd->path, xd->host);
+		xd->txbuflen = strlen(xd->txbuf);
+		xd->txbuf_written = 0;
+	}
+
+	total_len = xd->txbuflen - xd->txbuf_written;
+
 	xfer->fd = source;
+
+	written = write(xfer->fd, xd->txbuf + xd->txbuf_written, total_len);
+
+	if (written < 0 && errno == EAGAIN)
+		written = 0;
+	else if (written <= 0) {
+		gaim_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno);
+		gaim_xfer_cancel_remote(xfer);
+		return;
+	}
+
+	if (written < total_len) {
+		if (!xd->tx_handler)
+			xd->tx_handler = gaim_input_add(source, GAIM_INPUT_WRITE,
+				yahoo_receivefile_connected, xfer);
+		xd->txbuf_written += written;
+		return;
+	}
+
+	if (xd->tx_handler)
+		gaim_input_remove(xd->tx_handler);
+	xd->tx_handler = 0;
+	g_free(xd->txbuf);
+	xd->txbuf = NULL;
+	xd->txbuflen = 0;
+
 	gaim_xfer_start(xfer, source, NULL, 0);
 
-	buf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
-			      xd->path, xd->host);
-	write(xfer->fd, buf, strlen(buf));
-	g_free(buf);
+}
 
-	return;
-}
 
 static void yahoo_sendfile_connected(gpointer data, gint source, GaimInputCondition condition)
 {
 	GaimXfer *xfer;
 	struct yahoo_xfer_data *xd;
-	struct yahoo_packet *pkt;
-	gchar *size, *post, *buf;
-	const char *host;
-	int content_length, port;
-	GaimConnection *gc;
-	GaimAccount *account;
-	struct yahoo_data *yd;
-	char *filename, *encoded_filename;
+	int written, total_len;
 
 	gaim_debug(GAIM_DEBUG_INFO, "yahoo",
 			   "AAA - in yahoo_sendfile_connected\n");
@@ -102,9 +131,6 @@
 	if (!(xd = xfer->data))
 		return;
 
-	gc = xd->gc;
-	account = gaim_connection_get_account(gc);
-	yd = gc->proto_data;
 
 	if (source < 0) {
 		gaim_xfer_error(GAIM_XFER_RECEIVE, gaim_xfer_get_account(xfer),
@@ -113,43 +139,96 @@
 		return;
 	}
 
-	xfer->fd = source;
-	gaim_xfer_start(xfer, source, NULL, 0);
+	/* The first time we get here, assemble the tx buffer */
+	if (xd->txbuflen == 0) {
+		struct yahoo_packet *pkt;
+		gchar *size, *filename, *encoded_filename, *header;
+		guchar *pkt_buf;
+		const char *host;
+		int port;
+		gsize content_length, header_len, pkt_buf_len;
+		GaimConnection *gc;
+		GaimAccount *account;
+		struct yahoo_data *yd;
 
-	pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
+		gc = xd->gc;
+		account = gaim_connection_get_account(gc);
+		yd = gc->proto_data;
+
+		pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER,
+			YAHOO_STATUS_AVAILABLE, yd->session_id);
 
-	size = g_strdup_printf("%" G_GSIZE_FORMAT, gaim_xfer_get_size(xfer));
-	filename = g_path_get_basename(gaim_xfer_get_local_filename(xfer));
-	encoded_filename = yahoo_string_encode(gc, filename, NULL);
+		size = g_strdup_printf("%" G_GSIZE_FORMAT, gaim_xfer_get_size(xfer));
+		filename = g_path_get_basename(gaim_xfer_get_local_filename(xfer));
+		encoded_filename = yahoo_string_encode(gc, filename, NULL);
+
+		yahoo_packet_hash(pkt, "sssss", 0, gaim_connection_get_display_name(gc),
+			  5, xfer->who, 14, "", 27, encoded_filename, 28, size);
+		g_free(size);
+		g_free(encoded_filename);
+		g_free(filename);
+
+		content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
 
-	yahoo_packet_hash(pkt, "sssss", 0, gaim_connection_get_display_name(gc),
-	                  5, xfer->who, 14, "", 27, encoded_filename, 28, size);
+		pkt_buf_len = yahoo_packet_build(pkt, 8, FALSE, &pkt_buf);
+		yahoo_packet_free(pkt);
 
-	content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
-
-	buf = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+		host = gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST);
+		port = gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT);
+		header = g_strdup_printf(
+			"POST http://%s:%d/notifyft HTTP/1.0\r\n"
+			"Content-length: %" G_GSIZE_FORMAT "\r\n"
+			"Host: %s:%d\r\n"
+			"Cookie: Y=%s; T=%s\r\n"
+			"\r\n",
+			host, port, content_length + 4 + gaim_xfer_get_size(xfer),
+			host, port, yd->cookie_y, yd->cookie_t);
 
-	host = gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST);
-	port = gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT);
-	post = g_strdup_printf("POST http://%s:%d/notifyft HTTP/1.0\r\n"
-	                       "Content-length: %" G_GSIZE_FORMAT "\r\n"
-	                       "Host: %s:%d\r\n"
-	                       "Cookie: %s\r\n"
-	                       "\r\n",
-			       host, port, content_length + 4 + gaim_xfer_get_size(xfer),
-			       host, port, buf);
-	write(xfer->fd, post, strlen(post));
+
+		header_len = strlen(header);
+
+		xd->txbuflen = header_len + pkt_buf_len + 4;
+		xd->txbuf = g_malloc(xd->txbuflen);
+
+		memcpy(xd->txbuf, header, header_len);
+		g_free(header);
+		memcpy(xd->txbuf + header_len, pkt_buf, pkt_buf_len);
+		g_free(pkt_buf);
+		memcpy(xd->txbuf + header_len + pkt_buf_len, "29\xc0\x80", 4);
+
+		xd->txbuf_written = 0;
+	}
+
+	total_len = xd->txbuflen - xd->txbuf_written;
+
+	xfer->fd = source;
+
+	written = write(xfer->fd, xd->txbuf + xd->txbuf_written, total_len);
 
-	yahoo_packet_send_special(pkt, xfer->fd, 8);
-	yahoo_packet_free(pkt);
-
-	write(xfer->fd, "29\xc0\x80", 4);
+	if (written < 0 && errno == EAGAIN)
+		written = 0;
+	else if (written <= 0) {
+		gaim_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno);
+		gaim_xfer_cancel_remote(xfer);
+		return;
+	}
 
-	g_free(size);
-	g_free(post);
-	g_free(buf);
-	g_free(encoded_filename);
-	g_free(filename);
+	if (written < total_len) {
+		if (!xd->tx_handler)
+			xd->tx_handler = gaim_input_add(source, GAIM_INPUT_WRITE,
+				yahoo_sendfile_connected, xfer);
+		xd->txbuf_written += written;
+		return;
+	}
+
+	if (xd->tx_handler)
+		gaim_input_remove(xd->tx_handler);
+	xd->tx_handler = 0;
+	g_free(xd->txbuf);
+	xd->txbuf = NULL;
+	xd->txbuflen = 0;
+
+	gaim_xfer_start(xfer, source, NULL, 0);
 }
 
 static void yahoo_xfer_init(GaimXfer *xfer)