changeset 7651:580bd39219a2

[gaim-migrate @ 8295] Tim Ringenbach (marv_sf) " Because Sean asked nicely." implemented yahoo file transfer for the masses. of this second version of the patch, he adds: "Here's a new diff, that adds a right click send file menu like oscar, and should still compile without those new callbacks that got reverted." i'm using (and perhaps abusing) the permission to commit yahoo patches from him that i obtained in previous releases :-) committer: Tailor Script <tailor@pidgin.im>
author Luke Schierer <lschiere@pidgin.im>
date Sat, 29 Nov 2003 03:30:01 +0000
parents 6db061321ec4
children ac6b2b3a9a1f
files src/protocols/yahoo/Makefile.am src/protocols/yahoo/yahoo.c src/protocols/yahoo/yahoo.h src/protocols/yahoo/yahoo_filexfer.c src/protocols/yahoo/yahoo_filexfer.h
diffstat 5 files changed, 674 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/yahoo/Makefile.am	Fri Nov 28 19:30:45 2003 +0000
+++ b/src/protocols/yahoo/Makefile.am	Sat Nov 29 03:30:01 2003 +0000
@@ -9,7 +9,9 @@
 	yahoochat.h \
 	yahoo.c \
 	yahoochat.c \
-	util.c
+	util.c \
+	yahoo_filexfer.h \
+	yahoo_filexfer.c
 
 AM_CFLAGS = $(st)
 
--- a/src/protocols/yahoo/yahoo.c	Fri Nov 28 19:30:45 2003 +0000
+++ b/src/protocols/yahoo/yahoo.c	Sat Nov 29 03:30:01 2003 +0000
@@ -37,6 +37,7 @@
 #include "sha.h"
 #include "yahoo.h"
 #include "yahoochat.h"
+#include "yahoo_filexfer.h"
 #include "md5.h"
 
 extern char *yahoo_crypt(const char *, const char *);
@@ -56,13 +57,6 @@
 #define YAHOO_PAGER_PORT 5050
 #define YAHOO_PROFILE_URL "http://profiles.yahoo.com/"
 
-#ifdef YAHOO_WEBMESSENGER
-#define YAHOO_PROTO_VER 0x0065
-#else
-#define YAHOO_PROTO_VER 0x000b
-#endif
-
-#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
 
 static void yahoo_add_buddy(GaimConnection *gc, const char *who, GaimGroup *);
 
@@ -105,7 +99,7 @@
 	pkt->hash = g_slist_append(pkt->hash, pair);
 }
 
-static int yahoo_packet_length(struct yahoo_packet *pkt)
+int yahoo_packet_length(struct yahoo_packet *pkt)
 {
 	GSList *l;
 
@@ -128,24 +122,6 @@
 	return len;
 }
 
-/* sometimes i wish prpls could #include things from other prpls. then i could just
- * use the routines from libfaim and not have to admit to knowing how they work. */
-#define yahoo_put16(buf, data) ( \
-		(*(buf) = (unsigned char)((data)>>8)&0xff), \
-		(*((buf)+1) = (unsigned char)(data)&0xff),  \
-		2)
-#define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
-#define yahoo_put32(buf, data) ( \
-		(*((buf)) = (unsigned char)((data)>>24)&0xff), \
-		(*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
-		(*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
-		(*((buf)+3) = (unsigned char)(data)&0xff), \
-		4)
-#define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \
-		(((*((buf)+1))<<16)&0x00ff0000) + \
-		(((*((buf)+2))<< 8)&0x0000ff00) + \
-		(((*((buf)+3)    )&0x000000ff)))
-
 static void yahoo_packet_read(struct yahoo_packet *pkt, guchar *data, int len)
 {
 	int pos = 0;
@@ -200,7 +176,7 @@
 	}
 }
 
-static void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data)
+void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data)
 {
 	GSList *l = pkt->hash;
 	int pos = 0;
@@ -507,6 +483,39 @@
 	}
 }
 
+static char *_getcookie(char *rawcookie)
+{
+	char *cookie = NULL;
+	char *tmpcookie;
+	char *cookieend;
+
+	if (strlen(rawcookie) < 2)
+		return NULL;
+	tmpcookie = g_strdup(rawcookie+2);
+	cookieend = strchr(tmpcookie, ';');
+
+	if (cookieend)
+		*cookieend = '\0';
+
+	cookie = g_strdup(tmpcookie);
+	g_free(tmpcookie);
+
+	return cookie;
+}
+
+static void yahoo_process_cookie(struct yahoo_data *yd, char *c)
+{
+	if (c[0] == 'Y') {
+		if (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);
+		yd->cookie_t = _getcookie(c);
+	}
+}
+
 static void yahoo_process_list(GaimConnection *gc, struct yahoo_packet *pkt)
 {
 	GSList *l = pkt->hash;
@@ -524,6 +533,9 @@
 	char **buddies;
 	char **tmp, **bud;
 
+	if (pkt->id)
+		yd->session_id = pkt->id;
+
 	while (l) {
 		struct yahoo_pair *pair = l->data;
 		l = l->next;
@@ -541,6 +553,9 @@
 			else
 				g_string_append(yd->tmp_serv_ilist, pair->value);
 			break;
+		case 59: /* cookies, yum */
+			yahoo_process_cookie(yd, pair->value);
+			break;
 		}
 	}
 
@@ -1626,6 +1641,10 @@
 	case YAHOO_SERVICE_COMMENT:
 		yahoo_process_chat_message(gc, pkt);
 		break;
+	case YAHOO_SERVICE_P2PFILEXFER:
+	case YAHOO_SERVICE_FILETRANSFER:
+		yahoo_process_filetransfer(gc, pkt);
+		break;
 	default:
 		gaim_debug(GAIM_DEBUG_ERROR, "yahoo",
 				   "Unhandled service 0x%02x\n", pkt->service);
@@ -1949,6 +1968,11 @@
 	if (yd->chat_name)
 		g_free(yd->chat_name);
 
+	if (yd->cookie_y)
+		g_free(yd->cookie_y);
+	if (yd->cookie_t)
+		g_free(yd->cookie_t);
+
 	if (yd->fd >= 0)
 		close(yd->fd);
 
@@ -2184,6 +2208,13 @@
 	pbm->gc = gc;
 	m = g_list_append(m, pbm);
 
+	/* FIXME: remove this when the ui does it for us. */
+	pbm = g_new0(struct proto_buddy_menu, 1);
+	pbm->label = _("Send File");
+	pbm->callback = yahoo_ask_send_file;
+	pbm->gc = gc;
+	m = g_list_append(m, pbm);
+
 	if (f->game) {
 		char *game = f->game;
 		char *room;
@@ -2886,6 +2917,12 @@
 	yahoo_packet_free(pkt);
 }
 
+static gboolean yahoo_has_send_file(GaimConnection *gc, const char *who)
+{
+	return TRUE;
+}
+
+
 static GaimPlugin *my_protocol = NULL;
 
 static GaimPluginProtocolInfo prpl_info =
@@ -2940,7 +2977,14 @@
 	NULL, /* buddy_free */
 	NULL, /* convo_closed */
 	NULL, /* normalize */
-	NULL /* set_buddy_icon */
+	NULL, /* set_buddy_icon */
+	NULL, /* void (*remove_group)(GaimConnection *gc, const char *group);*/
+	NULL, /* char *(*get_cb_real_name)(GaimConnection *gc, int id, const char *who); */
+#if 0
+	yahoo_ask_send_file,
+	yahoo_send_file,
+	yahoo_has_send_file
+#endif
 };
 
 static GaimPluginInfo info =
@@ -2984,6 +3028,13 @@
 										 YAHOO_PAGER_PORT);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
+
+	option = gaim_account_option_string_new(_("File transfer host"), "xfer_host", YAHOO_XFER_HOST);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = gaim_account_option_int_new(_("File transfer port"), "xfer_port", YAHOO_XFER_PORT);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
 	my_protocol = plugin;
 
 	yahoo_init_colorht();
--- a/src/protocols/yahoo/yahoo.h	Fri Nov 28 19:30:45 2003 +0000
+++ b/src/protocols/yahoo/yahoo.h	Sat Nov 29 03:30:01 2003 +0000
@@ -25,6 +25,9 @@
 
 #include "prpl.h"
 
+#define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com"
+#define YAHOO_XFER_PORT 80
+
 #define WEBMESSENGER_URL "http://login.yahoo.com/config/login?.src=pg"
 
 enum yahoo_service { /* these are easier to see in hex */
@@ -122,6 +125,9 @@
 	gboolean in_chat;
 	char *chat_name;
 	char *auth;
+	char *cookie_y;
+	char *cookie_t;
+	int session_id;
 };
 
 struct yahoo_pair {
@@ -147,9 +153,38 @@
 
 #define YAHOO_MAX_STATUS_MESSAGE_LENGTH (48)
 
+#ifdef YAHOO_WEBMESSENGER
+#define YAHOO_PROTO_VER 0x0065
+#else
+#define YAHOO_PROTO_VER 0x000b
+#endif
+
+#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
+
+/* sometimes i wish prpls could #include things from other prpls. then i could just
+ * use the routines from libfaim and not have to admit to knowing how they work. */
+#define yahoo_put16(buf, data) ( \
+		(*(buf) = (unsigned char)((data)>>8)&0xff), \
+		(*((buf)+1) = (unsigned char)(data)&0xff),  \
+		2)
+#define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
+#define yahoo_put32(buf, data) ( \
+		(*((buf)) = (unsigned char)((data)>>24)&0xff), \
+		(*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
+		(*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
+		(*((buf)+3) = (unsigned char)(data)&0xff), \
+		4)
+#define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \
+		(((*((buf)+1))<<16)&0x00ff0000) + \
+		(((*((buf)+2))<< 8)&0x0000ff00) + \
+		(((*((buf)+3)    )&0x000000ff)))
+
+
 struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, enum yahoo_status status, int id);
 void yahoo_packet_hash(struct yahoo_packet *pkt, int key, const char *value);
 int yahoo_send_packet(struct yahoo_data *yd, struct yahoo_packet *pkt);
+void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data);
+int yahoo_packet_length(struct yahoo_packet *pkt);
 void yahoo_packet_free(struct yahoo_packet *pkt);
 
 /* util.c */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/yahoo/yahoo_filexfer.c	Sat Nov 29 03:30:01 2003 +0000
@@ -0,0 +1,516 @@
+/*
+ * @file yahoo_filexfer.c Yahoo Filetransfer
+ *
+ *
+ * Copyright (C) 2003 Timothy T Ringenbach <omarvo@hotmail.com>
+ * Some code borrowed from MSN and copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
+ * Some code copyright (C) 2002, Philip S Tellis <philip . tellis AT gmx . net>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "prpl.h"
+#include "internal.h"
+#include "util.h"
+#include "debug.h"
+#include "notify.h"
+#include "proxy.h"
+#include "ft.h"
+#include "yahoo.h"
+#include "yahoo_filexfer.h"
+
+
+
+struct yahoo_xfer_data {
+	gchar *host;
+	gchar *path;
+	int port;
+	GaimConnection *gc;
+	long expires;
+	gboolean started;
+	guint length;
+	gchar *rxqueue;
+	guint rxlen;
+	guint bytes_in;
+};
+
+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);
+}
+
+static void yahoo_receivefile_connected(gpointer data, gint source, GaimInputCondition condition)
+{
+	GaimXfer *xfer;
+	struct yahoo_xfer_data *xd;
+	gchar *buf;
+
+	gaim_debug(GAIM_DEBUG_INFO, "yahoo",
+			   "AAA - in yahoo_receivefile_connected\n");
+	if (!(xfer = data))
+		return;
+	if (!(xd = xfer->data))
+		return;
+	if (source < 0) {
+		gaim_xfer_error(GAIM_XFER_RECEIVE, xfer->who, _("Unable to connect."));
+		gaim_xfer_end(xfer);
+		return;
+	}
+
+	xfer->fd = source;
+	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 int yahoo_send_packet_special(int fd, struct yahoo_packet *pkt, int pad)
+{
+	int pktlen = yahoo_packet_length(pkt);
+	int len = YAHOO_PACKET_HDRLEN + pktlen;
+	int ret;
+
+	guchar *data;
+	int pos = 0;
+
+	if (fd < 0)
+		return -1;
+
+	data = g_malloc0(len + 1);
+
+	memcpy(data + pos, "YMSG", 4); pos += 4;
+	pos += yahoo_put16(data + pos, YAHOO_PROTO_VER);
+	pos += yahoo_put16(data + pos, 0x0000);
+	pos += yahoo_put16(data + pos, pktlen + pad);
+	pos += yahoo_put16(data + pos, pkt->service);
+	pos += yahoo_put32(data + pos, pkt->status);
+	pos += yahoo_put32(data + pos, pkt->id);
+
+	yahoo_packet_write(pkt, data + pos);
+
+	ret = write(fd, data, len);
+	g_free(data);
+
+	return ret;
+}
+
+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;
+	int content_length;
+	GaimConnection *gc;
+	GaimAccount *account;
+	struct yahoo_data *yd;
+
+	gaim_debug(GAIM_DEBUG_INFO, "yahoo",
+			   "AAA - in yahoo_sendfile_connected\n");
+	if (!(xfer = data))
+		return;
+	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, xfer->who, _("Unable to connect."));
+		gaim_xfer_end(xfer);
+		return;
+	}
+
+	xfer->fd = source;
+	gaim_xfer_start(xfer, source, NULL, 0);
+
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	size = g_strdup_printf("%d", gaim_xfer_get_size(xfer));
+
+	yahoo_packet_hash(pkt, 0, gaim_connection_get_display_name(gc));
+	yahoo_packet_hash(pkt, 5, xfer->who);
+	yahoo_packet_hash(pkt, 14, "");
+	yahoo_packet_hash(pkt, 27, gaim_xfer_get_local_filename(xfer));
+	yahoo_packet_hash(pkt, 28, size);
+
+	content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
+
+	buf = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+
+	post = g_strdup_printf("POST /notifyft HTTP/1.0\r\n"
+	                       "Content-length: %d\r\n"
+	                       "Host: %s:%d\r\n"
+	                       "Cookie: %s\r\n"
+	                       "\r\n",
+	                       content_length + 4 + gaim_xfer_get_size(xfer),
+			       gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST),
+			       gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT),
+			       buf);
+	write(xfer->fd, post, strlen(post));
+
+	yahoo_send_packet_special(xfer->fd, pkt, 8);
+	yahoo_packet_free(pkt);
+
+	write(xfer->fd, "29\xc0\x80", 4);
+
+	g_free(size);
+	g_free(post);
+	g_free(buf);
+}
+
+static void yahoo_xfer_init(GaimXfer *xfer)
+{
+	struct yahoo_xfer_data *xfer_data;
+	GaimConnection *gc;
+	GaimAccount *account;
+
+	xfer_data = xfer->data;
+	gc = xfer_data->gc;
+	account = gaim_connection_get_account(gc);
+
+	if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
+		if (gaim_proxy_connect(account, gaim_account_get_string(account, "xfer_host",  YAHOO_XFER_HOST),
+		                       gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT),
+		                       yahoo_sendfile_connected, xfer) == -1)
+		{
+			gaim_notify_error(gc, NULL, _("File Transfer Aborted"),
+			             _("Unable to establish file descriptor."));
+			gaim_xfer_cancel_remote(xfer);
+		}
+
+	} else {
+		xfer->fd = gaim_proxy_connect(account, xfer_data->host, xfer_data->port,
+		                              yahoo_receivefile_connected, xfer);
+		if (xfer->fd == -1) {
+			gaim_notify_error(gc, NULL, _("File Transfer Aborted"),
+			             _("Unable to establish file descriptor."));
+			gaim_xfer_cancel_remote(xfer);
+		}
+	}
+}
+
+static void yahoo_xfer_start(GaimXfer *xfer)
+{
+	/* We don't need to do anything here, do we? */
+}
+
+static void yahoo_xfer_end(GaimXfer *xfer)
+{
+	GaimAccount *account;
+	struct yahoo_xfer_data *xfer_data;
+
+	account   = gaim_xfer_get_account(xfer);
+	xfer_data = xfer->data;
+
+
+	if (xfer_data)
+		yahoo_xfer_data_free(xfer_data);
+	xfer->data = NULL;
+
+}
+
+guint calculate_length(const gchar *l, size_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (!g_ascii_isdigit(l[i]))
+			continue;
+		return strtol(l + i, NULL, 10);
+	}
+	return 0;
+}
+
+
+size_t yahoo_xfer_read(char **buffer, GaimXfer *xfer)
+{
+	gchar buf[1024];
+	size_t len;
+	gchar *start = NULL;
+	gchar *length;
+	gchar *end;
+	struct yahoo_xfer_data *xd = xfer->data;
+
+	if (gaim_xfer_get_type(xfer) != GAIM_XFER_RECEIVE) {
+		return 0;
+	}
+
+	len = read(xfer->fd, buf, sizeof(buf));
+
+	if (len == 0) {
+		if (xd->length && (xd->length == xd->bytes_in))
+			gaim_xfer_end(xfer);
+		else
+			gaim_xfer_cancel_remote(xfer);
+		return 0;
+	}
+
+
+	if (!xd->started) {
+		xd->rxqueue = g_realloc(xd->rxqueue, len + xd->rxlen);
+		memcpy(xd->rxqueue + xd->rxlen, buf, len);
+		xd->rxlen += len;
+
+		length = g_strstr_len(xd->rxqueue, len, "Content-length:");
+		if (length) {
+			end = g_strstr_len(length, length - xd->rxqueue, "\r\n");
+			if (!end)
+				return 0;
+			if ((xd->length = calculate_length(length, len - (length - xd->rxqueue))))
+				gaim_xfer_set_size(xfer, xd->length);
+		}
+		start = g_strstr_len(xd->rxqueue, len, "\r\n\r\n");
+		if (start)
+			start += 4;
+		if (!start || start > (xd->rxqueue + len))
+			return 0;
+		xd->started = TRUE;
+
+		len -= (start - xd->rxqueue);
+
+		*buffer = g_malloc(len);
+		memcpy(*buffer, start, len);
+		g_free(xd->rxqueue);
+		xd->rxqueue = NULL;
+		xd->rxlen = 0;
+	} else {
+		*buffer = g_malloc(len);
+		memcpy(*buffer, buf, len);
+	}
+
+	xd->bytes_in += len;
+	return len;
+}
+
+size_t yahoo_xfer_write(const char *buffer, size_t size, GaimXfer *xfer)
+{
+	size_t len;
+	struct yahoo_xfer_data *xd = xfer->data;
+
+	if (!xd)
+		return 0;
+
+	if (gaim_xfer_get_type(xfer) != GAIM_XFER_SEND) {
+		return 0;
+	}
+
+	len = write(xfer->fd, buffer, size);
+
+	xd->bytes_in += len;
+	if (xd->bytes_in >= gaim_xfer_get_size(xfer))
+		gaim_xfer_set_completed(xfer, TRUE);
+
+	return len;
+
+}
+
+static void yahoo_xfer_cancel_send(GaimXfer *xfer)
+{
+	GaimAccount *account;
+	struct yahoo_xfer_data *xfer_data;
+
+	xfer_data = xfer->data;
+	account   = gaim_xfer_get_account(xfer);
+
+	if (xfer_data)
+		yahoo_xfer_data_free(xfer_data);
+	xfer->data = NULL;
+}
+
+static void yahoo_xfer_cancel_recv(GaimXfer *xfer)
+{
+	GaimAccount *account;
+	struct yahoo_xfer_data *xfer_data;
+
+	account   = gaim_xfer_get_account(xfer);
+	xfer_data = xfer->data;
+
+	if (xfer_data)
+		yahoo_xfer_data_free(xfer_data);
+	xfer->data = NULL;
+}
+
+void yahoo_process_filetransfer(GaimConnection *gc, struct yahoo_packet *pkt)
+{
+	char *from = NULL;
+	char *to = NULL;
+	char *msg = NULL;
+	char *url = NULL;
+	long expires = 0;
+	GaimXfer *xfer;
+	struct yahoo_xfer_data *xfer_data;
+
+	char *service = NULL;
+
+	char *filename = NULL;
+	unsigned long filesize = 0L;
+
+	GSList *l;
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+
+		if (pair->key == 4)
+			from = pair->value;
+		if (pair->key == 5)
+			to = pair->value;
+		if (pair->key == 14)
+			msg = pair->value;
+		if (pair->key == 20)
+			url = pair->value;
+		if (pair->key == 38)
+			expires = strtol(pair->value, NULL, 10);
+
+		if (pair->key == 27)
+			filename = pair->value;
+		if (pair->key == 28)
+			filesize = atol(pair->value);
+
+		if (pair->key == 49)
+			service = pair->value;
+	}
+
+	if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) {
+		if (strcmp("FILEXFER", service) != 0) {
+			gaim_debug_misc("yahoo", "unhandled service 0x%02x", pkt->service);
+			return;
+		}
+	}
+
+	if (msg) {
+		char *tmp;
+		tmp = strchr(msg, '\006');
+		if (tmp)
+			*tmp = '\0';
+	}
+
+	if (!url || !from)
+		return;
+
+
+	/* Setup the Yahoo-specific file transfer data */
+	xfer_data = g_new0(struct yahoo_xfer_data, 1);
+	xfer_data->gc = gc;
+	if (!gaim_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path))) {
+		g_free(xfer_data);
+		return;
+	}
+
+	gaim_debug_misc("yahoo_filexfer", "Host is %s, port is %d, path is %s, and the full url was %s.\n",
+	                xfer_data->host, xfer_data->port, xfer_data->path, url);
+
+	/* Build the file transfer handle. */
+	xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, from);
+	xfer->data = xfer_data;
+
+	/* Set the info about the incoming file. */
+	if (filename)
+		gaim_xfer_set_filename(xfer, filename);
+	else {
+		gchar *start, *end;
+	 	start = g_strrstr(xfer_data->path, "/");
+		if (start)
+			start++;
+		end = g_strrstr(xfer_data->path, "?");
+		if (start && *start && end) {
+			filename = g_strndup(start, end - start);
+			gaim_xfer_set_filename(xfer, filename);
+			g_free(filename);
+			filename = NULL;
+		}
+	}
+
+	gaim_xfer_set_size(xfer, filesize);
+
+	/* Setup our I/O op functions */
+	gaim_xfer_set_init_fnc(xfer,        yahoo_xfer_init);
+	gaim_xfer_set_start_fnc(xfer,       yahoo_xfer_start);
+	gaim_xfer_set_end_fnc(xfer,         yahoo_xfer_end);
+	gaim_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
+	gaim_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
+	gaim_xfer_set_read_fnc(xfer,        yahoo_xfer_read);
+	gaim_xfer_set_write_fnc(xfer,       yahoo_xfer_write);
+
+	/* Now perform the request */
+	gaim_xfer_request(xfer);
+}
+
+void yahoo_ask_send_file(GaimConnection *gc, const char *who)
+{
+	GaimXfer *xfer;
+	struct yahoo_xfer_data *xfer_data;
+
+	xfer_data = g_new0(struct yahoo_xfer_data, 1);
+	xfer_data->gc = gc;
+
+
+	/* Build the file transfer handle. */
+	xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who);
+	xfer->data = xfer_data;
+
+	/* Setup our I/O op functions */
+	gaim_xfer_set_init_fnc(xfer,        yahoo_xfer_init);
+	gaim_xfer_set_start_fnc(xfer,       yahoo_xfer_start);
+	gaim_xfer_set_end_fnc(xfer,         yahoo_xfer_end);
+	gaim_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
+	gaim_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
+	gaim_xfer_set_read_fnc(xfer,        yahoo_xfer_read);
+	gaim_xfer_set_write_fnc(xfer,       yahoo_xfer_write);
+
+	/* Now perform the request */
+	gaim_xfer_request(xfer);
+}
+
+void yahoo_send_file(GaimConnection *gc, const char *who, const char *file)
+{
+	GaimXfer *xfer;
+	struct yahoo_xfer_data *xfer_data;
+
+	if (!who || !file)
+		return;
+
+	xfer_data = g_new0(struct yahoo_xfer_data, 1);
+	xfer_data->gc = gc;
+
+
+	/* Build the file transfer handle. */
+	xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who);
+	xfer->data = xfer_data;
+
+	/* Setup our I/O op functions */
+	gaim_xfer_set_init_fnc(xfer,        yahoo_xfer_init);
+	gaim_xfer_set_start_fnc(xfer,       yahoo_xfer_start);
+	gaim_xfer_set_end_fnc(xfer,         yahoo_xfer_end);
+	gaim_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
+	gaim_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
+	gaim_xfer_set_read_fnc(xfer,        yahoo_xfer_read);
+	gaim_xfer_set_write_fnc(xfer,       yahoo_xfer_write);
+
+	/* Now perform the request */
+	gaim_xfer_request_accepted(xfer, g_strdup(file));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/yahoo/yahoo_filexfer.h	Sat Nov 29 03:30:01 2003 +0000
@@ -0,0 +1,41 @@
+/*
+ * gaim
+ *
+ * Copyright (C) Timothy T Ringenbach <omarvo@hotmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * Process ymsg file receive invites.
+ */
+void yahoo_process_filetransfer(GaimConnection *gc, struct yahoo_packet *pkt);
+
+/**
+ * Send a file.
+ *
+ * @param gc The GaimConnection handle.
+ * @param who Who are we sending it to?
+ * @param file What file?
+ */
+void yahoo_send_file(GaimConnection *gc, const char *who, const char *file);
+
+/**
+ * Sends a file, that the user chooses after this call.
+ *
+ * @param gc The GaimConnection.
+ * @param who Who are we going to send a file to?
+ */
+void yahoo_ask_send_file(GaimConnection *gc, const char *who);