changeset 21803:ffbd2e3e10e4

Patch from Alex Badea to support receiving files from Yahoo users using the newer yahoo protocol. Committing this is very long overdue, it somehow slipped through the cracks for a long time. References #708.
author Daniel Atallah <daniel.atallah@gmail.com>
date Mon, 10 Dec 2007 02:07:01 +0000
parents d01d9107f263
children fb73a6ed8197
files libpurple/protocols/yahoo/yahoo.c libpurple/protocols/yahoo/yahoo.h libpurple/protocols/yahoo/yahoo_filexfer.c libpurple/protocols/yahoo/yahoo_filexfer.h libpurple/protocols/yahoo/yahoo_packet.h
diffstat 5 files changed, 282 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/yahoo/yahoo.c	Sun Dec 09 11:25:15 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Mon Dec 10 02:07:01 2007 +0000
@@ -493,13 +493,14 @@
 static void yahoo_process_cookie(struct yahoo_data *yd, char *c)
 {
 	if (c[0] == 'Y') {
-		if (yd->cookie_y)
-			g_free(yd->cookie_y);
+		g_free(yd->cookie_y);
 		yd->cookie_y = _getcookie(c);
 	} else if (c[0] == 'T') {
-		if (yd->cookie_t)
-			g_free(yd->cookie_t);
+		g_free(yd->cookie_t);
 		yd->cookie_t = _getcookie(c);
+	} else if (c[0] == 'C') {
+		g_free(yd->cookie_c);
+		yd->cookie_c = _getcookie(c);
 	} else
 		purple_debug_info("yahoo", "Ignoring unrecognized cookie '%c'\n", c[0]);
 }
@@ -2433,6 +2434,12 @@
 	case YAHOO_SERVICE_AUDIBLE:
 		yahoo_process_audible(gc, pkt);
 		break;
+	case YAHOO_SERVICE_Y7_FILETRANSFER:
+		yahoo_process_y7_filetransfer(gc, pkt);
+		break;
+	case YAHOO_SERVICE_Y7_FILETRANSFER_INFO:
+		yahoo_process_y7_filetransfer_info(gc, pkt);
+		break;
 	default:
 		purple_debug(PURPLE_DEBUG_ERROR, "yahoo",
 				   "Unhandled service 0x%02x\n", pkt->service);
@@ -3012,6 +3019,7 @@
 
 	g_free(yd->cookie_y);
 	g_free(yd->cookie_t);
+	g_free(yd->cookie_c);
 
 	if (yd->txhandler)
 		purple_input_remove(yd->txhandler);
--- a/libpurple/protocols/yahoo/yahoo.h	Sun Dec 09 11:25:15 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Mon Dec 10 02:07:01 2007 +0000
@@ -134,6 +134,7 @@
 	gsize auth_written;
 	char *cookie_y;
 	char *cookie_t;
+	char *cookie_c;
 	int session_id;
 	gboolean jp;
 	gboolean wm; /* connected w/ web messenger method */
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Sun Dec 09 11:25:15 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Mon Dec 10 02:07:01 2007 +0000
@@ -46,6 +46,10 @@
 	guint tx_handler;
 	gchar *rxqueue;
 	guint rxlen;
+
+	gboolean y7;	/* true for Y7 transfers (receive only for now) */
+	gchar *token;
+	gchar *tid;
 };
 
 static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd)
@@ -53,11 +57,70 @@
 	g_free(xd->host);
 	g_free(xd->path);
 	g_free(xd->txbuf);
+	g_free(xd->token);
+	g_free(xd->tid);
 	if (xd->tx_handler)
 		purple_input_remove(xd->tx_handler);
 	g_free(xd);
 }
 
+
+static void yahoo_xfer_y7_request_next_file(PurpleXfer *xfer)
+{
+	struct yahoo_packet *pack;
+	struct yahoo_xfer_data *xd = xfer->data;
+	PurpleConnection *gc = xd->gc;
+	struct yahoo_data *yd = gc->proto_data;
+
+	g_return_if_fail(xd->y7);
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pack, "sssi",
+		1, purple_connection_get_display_name(xd->gc),
+		5, xfer->who,
+		265, xd->tid,
+		271, 1);
+	yahoo_packet_send_and_free(pack, yd);
+}
+
+static void yahoo_xfer_y7_cancel_receive(PurpleXfer *xfer)
+{
+	struct yahoo_packet *pack;
+	struct yahoo_xfer_data *xd = xfer->data;
+	PurpleConnection *gc = xd->gc;
+	struct yahoo_data *yd = gc->proto_data;
+
+	g_return_if_fail(xd->y7);
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, -1, 0);
+	yahoo_packet_hash(pack, "sssi",
+		1, purple_connection_get_display_name(gc),
+		5, xfer->who,
+		265, xd->tid,
+		66, -1);
+	yahoo_packet_send_and_free(pack, yd);
+}
+
+static void yahoo_xfer_y7_accept_file(PurpleXfer *xfer)
+{
+	struct yahoo_packet *pack;
+	struct yahoo_xfer_data *xd = xfer->data;
+	PurpleConnection *gc = xd->gc;
+	struct yahoo_data *yd = gc->proto_data;
+
+	g_return_if_fail(xd->y7);
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pack, "ssssis",
+		1, purple_connection_get_display_name(gc),
+		5, xfer->who,				/* XXX this needs an accessor */
+		265, xd->tid,
+		27, purple_xfer_get_filename(xfer),	/* XXX this might be of incorrect encoding */
+		249, 3,
+		251, xd->token);
+	yahoo_packet_send_and_free(pack, yd);
+}
+
 static void yahoo_receivefile_send_cb(gpointer data, gint source, PurpleInputCondition condition)
 {
 	PurpleXfer *xfer;
@@ -97,6 +160,7 @@
 {
 	PurpleXfer *xfer;
 	struct yahoo_xfer_data *xd;
+	struct yahoo_data *yd;
 
 	purple_debug(PURPLE_DEBUG_INFO, "yahoo",
 			   "AAA - in yahoo_receivefile_connected\n");
@@ -112,11 +176,22 @@
 	}
 
 	xfer->fd = source;
+	yd = xd->gc->proto_data;
 
 	/* 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);
+		if (!xd->y7)
+			xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
+				      xd->path, xd->host);
+		else
+			xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\n"
+				"Connection: close\r\n"
+				"Accept: */*\r\n"
+				"Host: %s\r\n"
+				"Cookie: Y=%s; T=%s\r\n"
+				"\r\n",
+				xd->path, xd->host, yd->cookie_y, yd->cookie_t);
+		purple_debug(PURPLE_DEBUG_INFO, "yahoo_filexfer", "HTTP request: [%s]\n", xd->txbuf);
 		xd->txbuflen = strlen(xd->txbuf);
 		xd->txbuf_written = 0;
 	}
@@ -281,6 +356,9 @@
 			}
 		}
 	} else {
+		if (xfer_data->y7)
+			yahoo_xfer_y7_accept_file(xfer);
+
 		xfer->fd = -1;
 		if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port,
 		                              yahoo_receivefile_connected, xfer) == NULL) {
@@ -340,6 +418,8 @@
 		if ((purple_xfer_get_size(xfer) > 0) &&
 		    (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer))) {
 			purple_xfer_set_completed(xfer, TRUE);
+			if (xd->y7)
+				yahoo_xfer_y7_request_next_file(xfer);
 			return 0;
 		} else
 			return -1;
@@ -430,11 +510,24 @@
 
 	xfer_data = xfer->data;
 
-	if (xfer_data)
+	if (xfer_data) {
+		if (xfer_data->y7)
+			yahoo_xfer_y7_cancel_receive(xfer);
 		yahoo_xfer_data_free(xfer_data);
+	}
 	xfer->data = NULL;
 }
 
+static void yahoo_xfer_request_denied(PurpleXfer *xfer)
+{
+	struct yahoo_xfer_data *xfer_data;
+
+	xfer_data = xfer->data;
+
+	if (xfer_data->y7)
+		yahoo_xfer_y7_cancel_receive(xfer);
+}
+
 void yahoo_process_p2pfilexfer(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	GSList *l = pkt->hash;
@@ -628,6 +721,165 @@
 	}
 }
 
+void yahoo_process_y7_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	char *who = NULL, *name = NULL;
+	int ttype = 0;
+	char *tid = NULL;
+	GSList *l = pkt->hash;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 4:
+			/* them */
+			who = pair->value;
+			break;
+		case 5:
+			/* us */
+			name = pair->value;
+			break;
+		case 222:
+			/* 1=send, 2=cancel, 3=accept, 4=reject */
+			if(pair->value)
+				ttype = atoi(pair->value);
+			break;
+		case 265:
+			/* transfer ID */
+			tid = pair->value;
+			break;
+		case 266:
+			/* number of files */
+			break;
+		case 27:
+			/* filename */
+			break;
+		case 28:
+			/* filesize */
+			break;
+		}
+
+		l = l->next;
+	}
+	if (ttype == 1 && tid) {
+		/* We auto-accept all offers here, and ask the user about each individual
+		 * file in yahoo_process_y7_filetransfer_info. This works fine for receiving
+		 * a single file; when receiving multiple canceling one in the middle
+		 * will also cancel the rest of them.
+		 * Maybe TODO: UI and API allowing transfer of multiple files as a package. */
+		struct yahoo_packet *pack;
+		pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash(pack, "sssi", 1, name, 5, who, 265, tid, 222, 3);
+		yahoo_packet_send_and_free(pack, yd);
+	}
+}
+
+void yahoo_process_y7_filetransfer_info(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	char *who = NULL, *name = NULL;
+	int medium = 0;
+	char *tid = NULL, *server_host = NULL, *server_token = NULL, *filename = NULL;
+	GSList *l = pkt->hash;
+	struct yahoo_packet *pack;
+	PurpleXfer *xfer;
+	struct yahoo_xfer_data *xfer_data;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 4:
+			/* them */
+			who = pair->value;
+			break;
+		case 5:
+			/* us */
+			name = pair->value;
+			break;
+		case 249:
+			/* 1=p2p, 3=reflection server */
+			if(pair->value)
+				medium = atoi(pair->value);
+			break;
+		case 265:
+			/* transfer ID */
+			tid = pair->value;
+			break;
+		case 27:
+			filename = pair->value;
+			break;
+		case 250:
+			server_host = pair->value;
+			break;
+		case 251:
+			server_token = pair->value;
+			break;
+		}
+
+		l = l->next;
+	}
+	if (medium == 1) {
+		/* reject P2P transfers */
+		pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash(pack, "sssi", 1, name, 5, who, 265, tid, 66, -3);
+		yahoo_packet_send_and_free(pack, yd);
+		return;
+	}
+
+	if (medium != 3) {
+		purple_debug_error("yahoo", "Unexpected medium %d.\n", medium);
+		/* weird */
+		return;
+	}
+
+	/* Setup the Yahoo-specific file transfer data */
+	xfer_data = g_new0(struct yahoo_xfer_data, 1);
+	xfer_data->gc = gc;
+	xfer_data->host = g_strdup(server_host);
+	xfer_data->token = g_strdup(server_token);
+	xfer_data->tid = g_strdup(tid);
+	xfer_data->port = 80;
+	xfer_data->y7 = TRUE;
+	
+	/* TODO: full urlencode here */
+	server_token = purple_strreplace(server_token, "\002", "%02");
+	xfer_data->path = g_strdup_printf("relay?token=%s&sender=%s&recver=%s",
+		server_token, who, name);
+	g_free(server_token);
+
+	purple_debug_misc("yahoo_filexfer", "Host is %s, port is %d, path is %s.\n",
+	                xfer_data->host, xfer_data->port, xfer_data->path);
+
+	/* Build the file transfer handle. */
+	xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, who);
+	xfer->data = xfer_data;
+
+	/* Set the info about the incoming file. */
+	{
+		char *utf8_filename = yahoo_string_decode(gc, filename, TRUE);
+		purple_xfer_set_filename(xfer, utf8_filename);
+		g_free(utf8_filename);
+	}
+
+	/* purple_xfer_set_size(xfer, filesize); */
+
+	/* Setup our I/O op functions */
+	purple_xfer_set_init_fnc(xfer,        yahoo_xfer_init);
+	purple_xfer_set_start_fnc(xfer,       yahoo_xfer_start);
+	purple_xfer_set_end_fnc(xfer,         yahoo_xfer_end);
+	purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
+	purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
+	purple_xfer_set_read_fnc(xfer,        yahoo_xfer_read);
+	purple_xfer_set_write_fnc(xfer,       yahoo_xfer_write);
+	purple_xfer_set_request_denied_fnc(xfer, yahoo_xfer_request_denied);
+
+	/* Now perform the request */
+	purple_xfer_request(xfer);
+}
+
 PurpleXfer *yahoo_new_xfer(PurpleConnection *gc, const char *who)
 {
 	PurpleXfer *xfer;
--- a/libpurple/protocols/yahoo/yahoo_filexfer.h	Sun Dec 09 11:25:15 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.h	Mon Dec 10 02:07:01 2007 +0000
@@ -30,6 +30,16 @@
 void yahoo_process_p2pfilexfer( PurpleConnection *gc, struct yahoo_packet *pkt );
 
 /**
+ * Process ymsg version 7 file receive invites.
+ */
+void yahoo_process_y7_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt);
+
+/**
+ * Process ymsg version 7 file receive connection setups.
+ */
+void yahoo_process_y7_filetransfer_info(PurpleConnection *gc, struct yahoo_packet *pkt);
+
+/**
  * Process ymsg file receive invites.
  */
 void yahoo_process_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt);
--- a/libpurple/protocols/yahoo/yahoo_packet.h	Sun Dec 09 11:25:15 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.h	Mon Dec 10 02:07:01 2007 +0000
@@ -99,9 +99,12 @@
 	YAHOO_SERVICE_VERIFY_ID_EXISTS = 0xc8,
 	YAHOO_SERVICE_AUDIBLE = 0xd0,
 	YAHOO_SERVICE_AUTH_REQ_15 = 0xd6,
+	YAHOO_SERVICE_Y7_FILETRANSFER = 0xdc,
+	YAHOO_SERVICE_Y7_FILETRANSFER_INFO = 0xdd,
+	YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT = 0xde,
 	YAHOO_SERVICE_CHGRP_15 = 0xe7,
 	YAHOO_SERVICE_STATUS_15 = 0xf0,
-	YAHOO_SERVICE_LIST_15 = 0Xf1,
+	YAHOO_SERVICE_LIST_15 = 0xf1,
 	YAHOO_SERVICE_WEBLOGIN = 0x0226,
 	YAHOO_SERVICE_SMS_MSG = 0x02ea
 };