changeset 26217:91a7d0ad2021

propagate from branch 'im.pidgin.pidgin' (head d2e4560619da68f5b4346dcf1247908d88f39eb3) to branch 'im.pidgin.soc.2008.yahoo' (head 9a753e6b9d2389b3b7a87d60eebbe98228c2fb36)
author Sulabh Mahajan <sulabh@soc.pidgin.im>
date Sun, 13 Jul 2008 14:19:59 +0000
parents 4dd866b83e8e (diff) 0e54d1fea7e2 (current diff)
children 2666b864dc89
files COPYRIGHT libpurple/protocols/msn/soap2.c libpurple/protocols/msn/soap2.h libpurple/protocols/qq/qq_proxy.c libpurple/protocols/qq/qq_proxy.h libpurple/protocols/qq/recv_core.c libpurple/protocols/qq/recv_core.h libpurple/protocols/qq/send_core.c libpurple/protocols/qq/send_core.h libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sendqueue.h libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/qq/udp_proxy_s5.h libpurple/protocols/yahoo/yahoo.c
diffstat 7 files changed, 919 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sun Jul 13 10:09:19 2008 +0000
+++ b/COPYRIGHT	Sun Jul 13 14:19:59 2008 +0000
@@ -241,6 +241,7 @@
 Lucio Maciel
 Brian Macke
 Paolo Maggi
+Sulabh Mahajan
 Willian T. Mahan
 Kris Marsh
 Fidel Martinez
--- a/libpurple/protocols/yahoo/yahoo.c	Sun Jul 13 10:09:19 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sun Jul 13 14:19:59 2008 +0000
@@ -30,6 +30,7 @@
 #include "cmds.h"
 #include "core.h"
 #include "debug.h"
+#include "network.h"
 #include "notify.h"
 #include "privacy.h"
 #include "prpl.h"
@@ -215,7 +216,7 @@
 			if (f->status == YAHOO_STATUS_IDLE) {
 				/* Idle may have already been set in a more precise way in case 137 */
 				if (f->idle == 0)
-					f->idle = time(NULL);
+					f->idle = time(NULL) - 60;	/* Start idle at 1 min */
 			} else
 				f->idle = 0;
 
@@ -229,6 +230,8 @@
 				message = pair->value;
 			break;
 		case 11: /* this is the buddy's session id */
+			if (f)
+				f->session_id = strtol(pair->value, NULL, 10);
 			break;
 		case 17: /* in chat? */
 			break;
@@ -246,7 +249,7 @@
 			if (f->away == 2) {
 				/* Idle may have already been set in a more precise way in case 137 */
 				if (f->idle == 0)
-					f->idle = time(NULL);
+					f->idle = time(NULL) - 60;	/* start idle at 1 min */
 			}
 
 			break;
@@ -515,6 +518,10 @@
 					purple_blist_add_buddy(b, NULL, g, NULL);
 				}
 				yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp);
+				
+				/* set p2p status not connected and no p2p packet sent */
+				yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
+				f->p2p_packet_sent = 0;
 
 			} else {
 				/* This buddy is on the ignore list (and therefore in no group) */
@@ -629,6 +636,10 @@
 				}
 
 				yahoo_do_group_check(account, ht, norm_bud, grp);
+				/* set p2p status not connected and no p2p packet sent */
+				yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
+				f->p2p_packet_sent = 0;
+
 				g_free(norm_bud);
 			}
 			g_strfreev(buddies);
@@ -685,7 +696,8 @@
 	yahoo_fetch_aliases(gc);
 }
 
-static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt)
+/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
+static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
 {
 	PurpleAccount *account;
 	char *msg = NULL;
@@ -694,12 +706,14 @@
 	char *game = NULL;
 	YahooFriend *f = NULL;
 	GSList *l = pkt->hash;
+	gint val_11 = 0;
+	struct yahoo_data *yd = gc->proto_data;
 
 	account = purple_connection_get_account(gc);
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
-		if (pair->key == 4)
+		if (pair->key == 4 || pair->key == 1)
 			from = pair->value;
 		if (pair->key == 49)
 			msg = pair->value;
@@ -707,12 +721,22 @@
 			stat = pair->value;
 		if (pair->key == 14)
 			game = pair->value;
+		if (pair->key == 11)
+			val_11 = strtol(pair->value, NULL, 10);
 		l = l->next;
 	}
 
 	if (!from || !msg)
 		return;
 
+	/* disconnect the peer if connected through p2p and sends wrong value for session id */
+	if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) {
+		purple_debug_warning("yahoo","p2p: %s sent us notify with wrong session id. Disconnecting p2p connection to peer\n", from);
+		/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
+		g_hash_table_remove(yd->peers, from);
+		return;
+	}
+
 	if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
 		&& (purple_privacy_check(account, from)))
 	{
@@ -749,7 +773,6 @@
 
 }
 
-
 struct _yahoo_im {
 	char *from;
 	int time;
@@ -758,7 +781,8 @@
 	char *msg;
 };
 
-static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt)
+/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */
+static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
 {
 	PurpleAccount *account;
 	struct yahoo_data *yd = gc->proto_data;
@@ -766,13 +790,15 @@
 	GSList *list = NULL;
 	struct _yahoo_im *im = NULL;
 	const char *imv = NULL;
+	gint val_11 = 0;
 
 	account = purple_connection_get_account(gc);
 
-	if (pkt->status <= 1 || pkt->status == 5) {
+	if (pkt->status <= 1 || pkt->status == 5 || pkt->status == YAHOO_STATUS_OFFLINE) {
+	/* messages are reveived with status YAHOO_STATUS_OFFLINE in case of p2p */
 		while (l != NULL) {
 			struct yahoo_pair *pair = l->data;
-			if (pair->key == 4) {
+			if (pair->key == 4 || pair->key == 1) {
 				im = g_new0(struct _yahoo_im, 1);
 				list = g_slist_append(list, im);
 				im->from = pair->value;
@@ -792,6 +818,11 @@
 				if (im)
 					im->msg = pair->value;
 			}
+			/* peer session id */
+			if (pair->key == 11) {
+				if (im)
+					val_11 = strtol(pair->value, NULL, 10);
+			}
 			/* IMV key */
 			if (pair->key == 63)
 			{
@@ -804,6 +835,14 @@
 		                  _("Your Yahoo! message did not get sent."), NULL);
 	}
 
+	/* disconnect the peer if connected through p2p and sends wrong value for session id */
+	if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) {
+		purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from);
+		/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
+		g_hash_table_remove(yd->peers, im->from);
+		return;
+	}
+
 	/** TODO: It seems that this check should be per IM, not global */
 	/* Check for the Doodle IMV */
 	if (im != NULL && imv!= NULL && im->from != NULL)
@@ -2181,6 +2220,7 @@
 	char *buf;
 	YahooFriend *f;
 	GSList *l = pkt->hash;
+	struct yahoo_data *yd = gc->proto_data;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
@@ -2208,6 +2248,14 @@
 	if (!err || (err == 2)) { /* 0 = ok, 2 = already on serv list */
 		f = yahoo_friend_find_or_new(gc, who);
 		yahoo_update_status(gc, who, f);
+		
+		if( !g_hash_table_lookup(yd->peers, who) )	{
+			/* we are not connected as client, so set friend to not connected */
+			yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
+			f->p2p_packet_sent = 0;
+		}
+		else	/* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */
+			yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
 		return;
 	}
 
@@ -2220,6 +2268,348 @@
 	g_free(decoded_group);
 }
 
+/* destroy p2p_data associated with a peer and close p2p connection.
+ * g_hash_table_remove() calls this function to destroy p2p_data associated with the peer,
+ * call g_hash_table_remove() instead of this fucntion if peer has an entry in the table */
+static void yahoo_p2p_disconnect_destroy_data(gpointer data)
+{
+	struct yahoo_p2p_data *p2p_data;
+	YahooFriend *f;
+
+	if(!(p2p_data = data))
+		return ;
+
+	/* If friend, set him not connected */
+	f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username);
+	if (f)
+		yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
+
+	if(p2p_data->source >= 0)
+		close(p2p_data->source);
+	purple_input_remove(p2p_data->input_event);
+	g_free(p2p_data->host_ip);
+	g_free(p2p_data->host_username);
+	g_free(p2p_data);
+}
+
+/* write pkt to the source */
+static void yahoo_p2p_write_pkt(gint source, struct yahoo_packet *pkt)
+{
+	size_t pkt_len;
+	guchar *raw_packet;
+	
+	/*build the raw packet and send it to the host*/
+	pkt_len = yahoo_packet_build(pkt, 0, 0, 0, &raw_packet);
+	if(write(source, raw_packet, pkt_len) != pkt_len)
+		purple_debug_warning("yahoo","p2p: couldn't write to the source\n");
+	g_free(raw_packet);
+}
+
+/* exchange of initial p2pfilexfer packets, service type YAHOO_SERVICE_P2PFILEXFER */
+static void yahoo_p2p_process_p2pfilexfer(gpointer data, gint source, struct yahoo_packet *pkt)
+{
+	struct yahoo_p2p_data *p2p_data;
+	char *who = NULL;
+	GSList *l = pkt->hash;
+	struct yahoo_packet *pkt_to_send;
+	PurpleAccount *account;
+	int val_13_to_send = 0;
+	struct yahoo_data *yd;
+
+	if(!(p2p_data = data))
+		return ;
+
+	yd = p2p_data->gc->proto_data;
+
+	/* lets see whats in the packet */
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 4:
+			who = pair->value;
+			if(strncmp(who, p2p_data->host_username, strlen(p2p_data->host_username)) != 0) {
+				/* from whom are we receiving the packets ?? */
+				purple_debug_warning("yahoo","p2p: received data from wrong user\n");
+				return;
+			}
+			break;
+		case 13:
+			p2p_data->val_13 = strtol(pair->value, NULL, 10);	/* Value should be 5-7 */
+			break;
+		/* case 5, 49 look laters, no use right now */
+		}
+		l = l->next;
+	}
+
+	account = purple_connection_get_account(p2p_data->gc);
+
+	/* key_13: sort of a counter.
+	 * WHEN WE ARE CLIENT: yahoo server sends val_13 = 0, we send to peer val_13 = 1, receive back val_13 = 5,
+	 * we send val_13=6, receive val_13=7, we send val_13=7, HALT. Keep sending val_13 = 7 as keep alive.
+	 * WHEN WE ARE SERVER: we send val_13 = 0 to yahoo server, peer sends us val_13 = 1, we send val_13 = 5,
+	 * receive val_13 = 6, send val_13 = 7, receive val_13 = 7. HALT. Keep sending val_13 = 7 as keep alive. */
+
+	switch(p2p_data->val_13)	{
+		case 1 : val_13_to_send = 5; break;
+		case 5 : val_13_to_send = 6; break;
+		case 6 : val_13_to_send = 7; break;
+		case 7 : val_13_to_send = 7; break;
+		default: purple_debug_warning("yahoo","p2p:Unknown value for key 13\n");
+			 return;
+		}
+
+	/* Build the yahoo packet */
+	pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt_to_send, "ssisi",
+		4, purple_normalize(account, purple_account_get_username(account)),
+		5, p2p_data->host_username,
+		241, 0,		/* Protocol identifier */
+		49, "PEERTOPEER",
+		13, val_13_to_send);
+
+	/* build the raw packet and send it to the host */
+	yahoo_p2p_write_pkt(source, pkt_to_send);
+	yahoo_packet_free(pkt_to_send);
+}
+
+/* callback function associated with receiving of data, not considering receipt of multiple YMSG packets in a single TCP packet */
+static void yahoo_p2p_read_pkt_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	guchar buf[1024];	/* is it safe to assume a fixed array length of 1024 ?? */
+	int len;
+	int pos = 0;
+	int pktlen;
+	struct yahoo_packet *pkt;
+	guchar *start = NULL;
+	struct yahoo_p2p_data *p2p_data;
+	struct yahoo_data *yd;
+
+	if(!(p2p_data = data))
+		return ;
+	yd = p2p_data->gc->proto_data;
+
+	len = read(source, buf, sizeof(buf));
+	if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
+		return ; /* No Worries*/
+	else if (len <= 0)
+	{
+		purple_debug_warning("yahoo","p2p: Error in connection, or host disconnected\n");
+		/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
+		g_hash_table_remove(yd->peers,p2p_data->host_username);
+		return;
+	}
+	
+	if(len < YAHOO_PACKET_HDRLEN)
+		return;
+
+	if(strncmp((char *)buf, "YMSG", MIN(4, len)) != 0) {
+		/* Not a YMSG packet */
+		purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n");
+
+		start = memchr(buf + 1, 'Y', len - 1);
+		if(start) {
+			g_memmove(buf, start, len - (start - buf));
+			len -= start - buf;
+		} else {
+			g_free(buf);
+			return;
+		}
+	}
+
+	pos += 4;	/* YMSG */
+	pos += 2;
+	pos += 2;
+
+	pktlen = yahoo_get16(buf + pos); pos += 2;
+	purple_debug(PURPLE_DEBUG_MISC, "yahoo", "p2p: %d bytes to read\n", len);
+
+	pkt = yahoo_packet_new(0, 0, 0);
+	pkt->service = yahoo_get16(buf + pos); pos += 2;
+	pkt->status = yahoo_get32(buf + pos); pos += 4;
+	pkt->id = yahoo_get32(buf + pos); pos += 4;
+
+	purple_debug(PURPLE_DEBUG_MISC, "yahoo", "p2p: Yahoo Service: 0x%02x Status: %d\n",pkt->service, pkt->status);
+	yahoo_packet_read(pkt, buf + pos, pktlen);
+
+	/* packet processing */
+	switch(pkt->service)	{
+		case YAHOO_SERVICE_P2PFILEXFER:
+			yahoo_p2p_process_p2pfilexfer(data, source, pkt);
+			break;
+		case YAHOO_SERVICE_MESSAGE:
+			yahoo_process_message(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P);
+			break;
+		case YAHOO_SERVICE_NOTIFY:
+			yahoo_process_notify(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P);
+			break;
+		default:
+			purple_debug_warning("yahoo","p2p: p2p service %d Unhandled\n",pkt->service);
+	}
+
+	yahoo_packet_free(pkt);
+}
+
+static void yahoo_p2p_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	int acceptfd;
+	struct yahoo_p2p_data *p2p_data;
+	YahooFriend *f;
+	struct yahoo_data *yd;
+
+	if(!(p2p_data = data))
+		return ;
+	yd = p2p_data->gc->proto_data;
+
+	acceptfd = accept(source, NULL, 0);
+	if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+		return;
+	else if(acceptfd == -1) {
+		purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno));
+		yahoo_p2p_disconnect_destroy_data(data);
+		return;
+	}
+
+	/* remove watcher and close p2p server */
+	purple_input_remove(yd->yahoo_p2p_server_watcher);
+	close(yd->yahoo_local_p2p_server_fd);
+	yd->yahoo_local_p2p_server_fd = -1;
+
+	if( (f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username)) )	{
+		p2p_data->session_id = f->session_id;
+		yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER);
+	}
+
+	/* Add an Input Read event to the file descriptor */
+	p2p_data->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data);
+	p2p_data->source = acceptfd;
+
+	g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data);
+}
+
+static void yahoo_p2p_server_listen_cb(int listenfd, gpointer data)
+{
+	struct yahoo_p2p_data *p2p_data;
+	struct yahoo_data *yd;
+
+	if(!(p2p_data = data))
+		return ;
+
+	if(listenfd == -1)	{
+		purple_debug_warning("yahoo","p2p: error starting p2p server\n");
+		yahoo_p2p_disconnect_destroy_data(data);
+		return;
+	}
+
+	yd = p2p_data->gc->proto_data;
+
+	/* Add an Input Read event to the file descriptor */
+	yd->yahoo_local_p2p_server_fd = listenfd;
+	yd->yahoo_p2p_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_server_send_connected_cb,data);
+}
+
+/* send p2p pkt containing our encoded ip, asking peer to connect to us */
+static void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13)
+{
+	const char *public_ip;
+	guint32 temp[4];	
+	guint32 ip;
+	char temp_str[100];
+	gchar *base64_ip = NULL;
+	YahooFriend *f;
+	struct yahoo_packet *pkt;
+	PurpleAccount *account;
+	struct yahoo_data *yd = gc->proto_data;
+	struct yahoo_p2p_data *p2p_data = g_new0(struct yahoo_p2p_data, 1);
+
+	public_ip = purple_network_get_public_ip();
+	if( (sscanf(public_ip, "%u.%u.%u.%u", &temp[0], &temp[1], &temp[2], &temp[3])) !=4 )
+		return ;
+
+	ip = (temp[3] << 24) | (temp[2] <<16) | (temp[1] << 8) | temp[0];
+	sprintf(temp_str, "%d", ip);
+	base64_ip = purple_base64_encode( (guchar *)temp_str, strlen(temp_str) );
+
+	f = yahoo_friend_find(gc, who);
+	account = purple_connection_get_account(gc);
+
+	/* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */ 
+	if( !( f && (yahoo_friend_get_p2p_status(f) == YAHOO_P2PSTATUS_NOT_CONNECTED) && (f->p2p_packet_sent == 0)) )
+		return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_PEERTOPEER, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pkt, "sssissis",
+		1, purple_normalize(account, purple_account_get_username(account)),
+		4, purple_normalize(account, purple_account_get_username(account)),
+		12, base64_ip,	/* base64 encode ip */
+		61, 0,		/* To-do : figure out what is 61 for?? */
+		2, "",
+		5, who,
+		13, val_13,
+		49, "PEERTOPEER");
+	yahoo_packet_send_and_free(pkt, yd);
+
+	f->p2p_packet_sent = 1;	/* set p2p_packet_sent to sent */
+
+	p2p_data->gc = gc;
+	p2p_data->host_ip = NULL;
+	p2p_data->host_username = (char *)g_malloc(strlen(who));
+	strcpy(p2p_data->host_username, who);
+	p2p_data->val_13 = val_13;
+	p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER;
+
+	purple_network_listen(YAHOO_PAGER_PORT_P2P, SOCK_STREAM, yahoo_p2p_server_listen_cb, p2p_data);
+
+	g_free(base64_ip);
+}
+
+/* function called when connection to p2p host is setup */
+static void yahoo_p2p_init_cb(gpointer data, gint source, const gchar *error_message)
+{
+	struct yahoo_p2p_data *p2p_data;
+	struct yahoo_packet *pkt_to_send;
+	PurpleAccount *account;
+	YahooFriend *f;
+	struct yahoo_data *yd;
+
+	if(!(p2p_data = data))
+		return ;
+	yd = p2p_data->gc->proto_data;
+
+	if(error_message != NULL)	{
+		purple_debug_warning("yahoo","p2p: %s\n",error_message);
+		yahoo_send_p2p_pkt(p2p_data->gc, p2p_data->host_username, 2);/* send p2p init packet with val_13=2 */
+		
+		yahoo_p2p_disconnect_destroy_data(p2p_data);
+		return;
+	}
+
+	/* Add an Input Read event to the file descriptor */
+	p2p_data->input_event = purple_input_add(source, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data);
+	p2p_data->source = source;
+
+	g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data);
+	
+	/* If the peer is a friend, set him connected */
+	f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username);
+	if (f)
+		yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
+
+	account = purple_connection_get_account(p2p_data->gc);
+
+	/* Build the yahoo packet */
+	pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt_to_send, "ssisi",
+		4, purple_normalize(account, purple_account_get_username(account)),
+		5, p2p_data->host_username,
+		241, 0,		/* Protocol identifier */
+		49, "PEERTOPEER",
+		13, 1);		/* we receive key13= 0 or 2, we send key13=1 */
+
+	yahoo_p2p_write_pkt(source, pkt_to_send);	/* build raw packet and send */
+	yahoo_packet_free(pkt_to_send);
+}
+
 static void yahoo_process_p2p(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	GSList *l = pkt->hash;
@@ -2227,6 +2617,10 @@
 	char *base64 = NULL;
 	guchar *decoded;
 	gsize len;
+	gint val_13 = 0;
+	gint val_11 = 0;
+	PurpleAccount *account;
+	YahooFriend *f;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
@@ -2246,14 +2640,21 @@
 			/* so, this is an ip address. in base64. decoded it's in ascii.
 			   after strtol, it's in reversed byte order. Who thought this up?*/
 			break;
+		case 13:
+			val_13 = strtol(pair->value, NULL, 10);
+			break;
+		case 11:
+			val_11 = strtol(pair->value, NULL, 10);		/* session id of peer */
+			if( (f = yahoo_friend_find(gc, who)) )
+				f->session_id = val_11;
+			break;
 		/*
 			TODO: figure these out
 			yahoo: Key: 61          Value: 0
 			yahoo: Key: 2   Value:
-			yahoo: Key: 13          Value: 0
+			yahoo: Key: 13          Value: 0	packet count ??
 			yahoo: Key: 49          Value: PEERTOPEER
 			yahoo: Key: 140         Value: 1
-			yahoo: Key: 11          Value: -1786225828
 		*/
 
 		}
@@ -2265,6 +2666,8 @@
 		guint32 ip;
 		char *tmp2;
 		YahooFriend *f;
+		char *host_ip;
+		struct yahoo_p2p_data *p2p_data = g_new0(struct yahoo_p2p_data, 1);
 
 		decoded = purple_base64_decode(base64, &len);
 		if (len) {
@@ -2277,12 +2680,35 @@
 		ip = strtol(tmp2, NULL, 10);
 		g_free(tmp2);
 		g_free(decoded);
-		tmp2 = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff,
+		host_ip = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff,
 		                       (ip >> 24) & 0xff);
 		f = yahoo_friend_find(gc, who);
 		if (f)
-			yahoo_friend_set_ip(f, tmp2);
-		g_free(tmp2);
+			yahoo_friend_set_ip(f, host_ip);
+		purple_debug_info("yahoo", "IP : %s\n", host_ip);
+
+		account = purple_connection_get_account(gc);
+
+		if(val_11==0)	{
+			if(!f)
+				return;
+			else
+				val_11 = f->session_id;
+		}
+
+		p2p_data->host_username = (char *)g_malloc(strlen(who));
+		strcpy(p2p_data->host_username, who);		
+		p2p_data->val_13 = val_13;
+		p2p_data->session_id = val_11;
+		p2p_data->host_ip = host_ip;
+		p2p_data->gc = gc;
+		p2p_data->connection_type = YAHOO_P2P_WE_ARE_CLIENT;
+
+		/* connect to host */
+		if((purple_proxy_connect(NULL, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL)	{
+			yahoo_p2p_disconnect_destroy_data(p2p_data);
+			purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip);
+		}
 	}
 }
 
@@ -2362,12 +2788,12 @@
 		yahoo_process_status(gc, pkt);
 		break;
 	case YAHOO_SERVICE_NOTIFY:
-		yahoo_process_notify(gc, pkt);
+		yahoo_process_notify(gc, pkt, YAHOO_PKT_TYPE_SERVER);
 		break;
 	case YAHOO_SERVICE_MESSAGE:
 	case YAHOO_SERVICE_GAMEMSG:
 	case YAHOO_SERVICE_CHATMSG:
-		yahoo_process_message(gc, pkt);
+		yahoo_process_message(gc, pkt, YAHOO_PKT_TYPE_SERVER);
 		break;
 	case YAHOO_SERVICE_SYSMESSAGE:
 		yahoo_process_sysmessage(gc, pkt);
@@ -2444,7 +2870,8 @@
 		break;
 	case YAHOO_SERVICE_P2PFILEXFER:
 		/* This case had no break and continued; thus keeping it this way.*/
-		yahoo_process_p2pfilexfer(gc, pkt);
+		yahoo_process_p2p(gc, pkt);	/* P2PFILEXFER handled the same way as process_p2p */
+		yahoo_process_p2pfilexfer(gc, pkt);	/* redundant ??, need to have a break now */
 	case YAHOO_SERVICE_FILETRANSFER:
 		yahoo_process_filetransfer(gc, pkt);
 		break;
@@ -2999,6 +3426,7 @@
 	yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free);
 	yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 	yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+	yd->peers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_p2p_disconnect_destroy_data);
 	yd->confs = NULL;
 	yd->conf_id = 2;
 
@@ -3063,6 +3491,7 @@
 	if (yd->in_chat)
 		yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */
 
+	g_hash_table_destroy(yd->peers);
 	g_hash_table_destroy(yd->friends);
 	g_hash_table_destroy(yd->imvironments);
 	g_hash_table_destroy(yd->xfer_peer_idstring_map);
@@ -3486,11 +3915,13 @@
 
 static void
 yahoo_get_inbox_token_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
-		const gchar *token, size_t len, const gchar *error_message)
+		const gchar *webdata, size_t len, const gchar *error_message)
 {
 	PurpleConnection *gc = user_data;
 	gboolean set_cookie = FALSE;
 	gchar *url;
+	gchar *token = NULL;
+	int token_size;
 	struct yahoo_data *yd = gc->proto_data;
 
 	g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
@@ -3499,8 +3930,14 @@
 
 	if (error_message != NULL)
 		purple_debug_error("yahoo", "Requesting mail login token failed: %s\n", error_message);
-	else if (len > 0 && token && *token) {
-	 	/* Should we not be hardcoding the rd url? */
+	else if (len > 0 && webdata && *webdata) {
+		/* Extract token from the chunked webdata */
+		sscanf(webdata,"%x",&token_size);
+		token = g_malloc(token_size);
+		strncpy(token, strstr(webdata,"\r\n")+2, token_size);
+		token[token_size-1]='\0';
+
+		/* Should we not be hardcoding the rd url? */
 		url = g_strdup_printf(
 			"http://login.yahoo.com/config/reset_cookies_token?"
 			".token=%s"
@@ -3518,6 +3955,7 @@
 	purple_notify_uri(gc, url);
 
 	g_free(url);
+	g_free(token);
 }
 
 
@@ -3532,12 +3970,13 @@
 	PurpleUtilFetchUrlData *url_data;
 	const char* base_url = "http://login.yahoo.com";
 	char *request = g_strdup_printf(
-		"POST /config/cookie_token HTTP/1.0\r\n"
-		"Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s;\r\n"
+		"POST /config/cookie_token HTTP/1.1\r\n"
+		"Cookie: Y=%s; path=/; domain=.yahoo.com; T=%s; path=/; domain=.yahoo.com;\r\n"
 		"User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n"
 		"Host: login.yahoo.com\r\n"
-		"Content-Length: 0\r\n\r\n",
-		yd->cookie_t, yd->cookie_y);
+		"Content-Length: 0\r\n"
+		"Cache-Control: no-cache\r\n\r\n",
+		yd->cookie_y, yd->cookie_t);
 	gboolean use_whole_url = FALSE;
 
 	/* use whole URL if using HTTP Proxy */
@@ -3555,7 +3994,7 @@
 	else {
 		const char *yahoo_mail_url = (yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
 		purple_debug_error("yahoo",
-				   "Unable to request mail login token; forwarding to login screen.");
+				   "Unable to request mail login token; forwarding to login screen.\n");
 		purple_notify_uri(gc, yahoo_mail_url);
 	}
 
@@ -3614,6 +4053,7 @@
 	PurpleWhiteboard *wb;
 	int ret = 1;
 	YahooFriend *f = NULL;
+	struct yahoo_p2p_data *p2p_data;
 
 	msg2 = yahoo_string_encode(gc, msg, &utf8);
 
@@ -3658,8 +4098,17 @@
 		yahoo_packet_hash_str(pkt, 206, "2");
 
 	/* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */
-	if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000)
-		yahoo_packet_send(pkt, yd);
+	if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000)	{
+		/* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */
+		if( (p2p_data = g_hash_table_lookup(yd->peers, who)) )	{
+			yahoo_packet_hash_int(pkt, 11, p2p_data->session_id);
+			yahoo_p2p_write_pkt(p2p_data->source, pkt);
+		}
+		else	{
+			yahoo_packet_send(pkt, yd);
+			yahoo_send_p2p_pkt(gc, who, 0);		/* send p2p packet, with val_13=0 */
+		}
+	}
 	else
 		ret = -E2BIG;
 
@@ -3674,12 +4123,24 @@
 static unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
 {
 	struct yahoo_data *yd = gc->proto_data;
+	struct yahoo_p2p_data *p2p_data;
+
 	struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, 0);
-	yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
+
+	/* check to see if p2p link exists, send through it */
+	if( (p2p_data = g_hash_table_lookup(yd->peers, who)) )	{
+		yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc),
+	                  14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
+	                  5, who, 11, p2p_data->session_id, 1002, "1");	/* To-do: key 15 to be sent in case of p2p */	
+		yahoo_p2p_write_pkt(p2p_data->source, pkt);
+		yahoo_packet_free(pkt);
+	}
+	else	{	/* send through yahoo server */
+		yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
 	                  14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
 	                  5, who, 1002, "1");
-
-	yahoo_packet_send_and_free(pkt, yd);
+		yahoo_packet_send_and_free(pkt, yd);
+	}
 
 	return 0;
 }
@@ -4269,10 +4730,10 @@
 				purple_conv_send_confirm(conv, message);
 			}
 		}
-		/*else
+		/* else
 			**If pidgindialogs_im() was in the core, we could use it here.
 			 * It is all purple_request_* based, but I'm not sure it really belongs in the core
-			pidgindialogs_im();*/
+			pidgindialogs_im(); */
 
 		return TRUE;
 	}
@@ -4286,7 +4747,7 @@
 			g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat"));
 			serv_join_chat(purple_account_get_connection(acct), params);
 		}
-		/*else
+		/* else
 			** Same as above (except that this would have to be re-written using purple_request_*)
 			pidgin_blist_joinchat_show(); */
 
@@ -4347,7 +4808,7 @@
 	yahoo_add_buddy,
 	NULL, /* add_buddies */
 	yahoo_remove_buddy,
-	NULL, /*remove_buddies */
+	NULL, /* remove_buddies */
 	NULL, /* add_permit */
 	yahoo_add_deny,
 	NULL, /* rem_permit */
--- a/libpurple/protocols/yahoo/yahoo.h	Sun Jul 13 10:09:19 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Sun Jul 13 14:19:59 2008 +0000
@@ -30,6 +30,7 @@
 
 #define YAHOO_PAGER_HOST "scs.msg.yahoo.com"
 #define YAHOO_PAGER_PORT 5050
+#define YAHOO_PAGER_PORT_P2P 5101
 #define YAHOO_PROFILE_URL "http://profiles.yahoo.com/"
 #define YAHOO_MAIL_URL "https://login.yahoo.com/config/login?.src=ym"
 #define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com"
@@ -45,7 +46,7 @@
 #define YAHOOJP_MAIL_URL "http://mail.yahoo.co.jp/"
 #define YAHOOJP_XFER_HOST "filetransfer.msg.yahoo.co.jp"
 #define YAHOOJP_WEBCAM_HOST "wc.yahoo.co.jp"
-/*not sure, must test:*/
+/* not sure, must test: */
 #define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.co.jp" 
 #define YAHOOJP_XFER_RELAY_PORT 80
 #define YAHOOJP_ROOMLIST_URL "http://insider.msg.yahoo.co.jp/ycontent/"
@@ -80,10 +81,19 @@
 #define YAHOOJP_CLIENT_VERSION_ID "524223"
 #define YAHOOJP_CLIENT_VERSION "7,0,1,1"
 
-
 /* Index into attention types list. */
 #define YAHOO_BUZZ 0
 
+typedef enum {
+	YAHOO_PKT_TYPE_SERVER = 0,
+	YAHOO_PKT_TYPE_P2P
+} yahoo_pkt_type;
+
+typedef enum {
+	YAHOO_P2P_WE_ARE_CLIENT =0,
+	YAHOO_P2P_WE_ARE_SERVER
+} yahoo_p2p_connection_type;
+
 enum yahoo_status {
 	YAHOO_STATUS_AVAILABLE = 0,
 	YAHOO_STATUS_BRB,
@@ -113,6 +123,17 @@
 	guint watcher;
 };
 
+struct yahoo_p2p_data	{
+	PurpleConnection *gc;
+	char *host_ip;
+	char *host_username;
+	int val_13;
+	guint input_event;
+	gint source;
+	int session_id;
+	yahoo_p2p_connection_type connection_type;
+};
+
 struct _YchtConn;
 
 struct yahoo_data {
@@ -168,14 +189,17 @@
 	 * for when we lookup people profile or photo information.
 	 */
 	GSList *url_datas;
-	GHashTable *xfer_peer_idstring_map;/*Hey, i dont know, but putting this HashTable next to friends gives a run time fault...*/
-	GSList *cookies;/*contains all cookies, including _y and _t*/
+	GHashTable *xfer_peer_idstring_map;/* Hey, i dont know, but putting this HashTable next to friends gives a run time fault... */
+	GSList *cookies;/* contains all cookies, including _y and _t */
 	
 	/**
 	 * We may receive a list15 in multiple packets with no prior warning as to how many we'll be getting;
 	 * the server expects us to keep track of the group for which it is sending us contact names.
 	 */
 	char *current_list15_grp;
+	GHashTable *peers;	/* information about p2p data */
+	int yahoo_local_p2p_server_fd;
+	int yahoo_p2p_server_watcher;
 };
 
 #define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255)
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Sun Jul 13 10:09:19 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Sun Jul 13 14:19:59 2008 +0000
@@ -26,6 +26,7 @@
 #include "prpl.h"
 #include "util.h"
 #include "debug.h"
+#include "network.h"
 #include "notify.h"
 #include "proxy.h"
 #include "ft.h"
@@ -50,7 +51,7 @@
 	guint rxlen;
 	gchar *xfer_peer_idstring;
 	gchar *xfer_idstring_for_relay;
-	int version; /*0 for old, 15 for Y7(YMSG 15)*/
+	int version; /* 0 for old, 15 for Y7(YMSG 15) */
 	int info_val_249;
 
 	enum {
@@ -58,14 +59,22 @@
 		HEAD_REQUESTED,
 		HEAD_REPLY_RECEIVED,
 		TRANSFER_PHASE,
-		ACCEPTED
+		ACCEPTED,
+		P2P_HEAD_REQUESTED,
+		P2P_HEAD_REPLIED,
+		P2P_GET_REQUESTED
 	} status_15;
 
 	/* contains all filenames, in case of multiple transfers, with the first
 	 * one in the list being the current file's name (ymsg15) */
 	GSList *filename_list;
-	GSList *size_list; /*corresponds to filename_list, with size as **STRING** */
+	GSList *size_list; /* corresponds to filename_list, with size as **STRING** */
 	gboolean firstoflist;
+	gchar *xfer_url;	/* url of the file, used when we are p2p server */
+	int yahoo_local_p2p_ft_server_fd;
+	int yahoo_local_p2p_ft_server_port;
+	int yahoo_p2p_ft_server_watcher;
+	int input_event;
 };
 
 static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd)
@@ -78,14 +87,14 @@
 	gc = xd->gc;
 	yd = gc->proto_data;
 
-	/*remove entry from map*/
+	/* remove entry from map */
 	if(xd->xfer_peer_idstring) {
 		xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xd->xfer_peer_idstring);
 		if(xfer)
 			g_hash_table_remove(yd->xfer_peer_idstring_map, xd->xfer_peer_idstring);
 	}
 
-	/*empty file & filesize list*/
+	/* empty file & filesize list */
 	for (l = xd->filename_list; l; l = l->next) {
 		g_free(l->data);
 		l->data=NULL;
@@ -684,7 +693,7 @@
 				purple_xfer_set_write_fnc(xfer,       yahoo_xfer_write);
 				purple_xfer_set_request_denied_fnc(xfer,yahoo_xfer_cancel_recv);
 
-				/*update map to current xfer*/
+				/* update map to current xfer */
 				g_hash_table_remove(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring);
 				g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer);
 
@@ -980,7 +989,7 @@
 		return;
 	}
 	
-	/*TODO:actually, u must try with addr no.1 , if its not working addr no.2 .....*/
+	/* TODO:actually, u must try with addr no.1 , if its not working addr no.2 ..... */
 	addr = hosts->data;
 	actaddr = addr->sin_addr.s_addr;
 	d = actaddr % 256;
@@ -1030,7 +1039,6 @@
 	yahoo_packet_send_and_free(pkt, yd);
 }
 
-
 void yahoo_send_file(PurpleConnection *gc, const char *who, const char *file)
 {
 	struct yahoo_xfer_data *xfer_data;
@@ -1042,7 +1050,10 @@
 	/* To determine if we should use yahoo p15 for transfer.  Check other user's
 	 * reported version, but if we're on Yahoo Japan, ignore it. */
 	if(yf && yf->version_id > 500000 && !yd->jp)
-		ver = 15; 
+		ver = 15;
+	/* Default to ver 15 if user isn't a friend */
+	if(!yf)
+		ver = 15;
 
 	g_return_if_fail(xfer != NULL);
 
@@ -1062,7 +1073,9 @@
 		purple_xfer_request(xfer);
 }
 
-static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message);/*using this in recv_cb*/
+static void yahoo_p2p_ft_server_listen_cb(int listenfd, gpointer data);	/* using this in yahoo_xfer_send_cb_15 */
+static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message);/* using this in recv_cb */
+
 static void yahoo_xfer_recv_cb_15(gpointer data, gint source, PurpleInputCondition condition)
 {
 	PurpleXfer *xfer;
@@ -1102,7 +1115,7 @@
 
 	if(xd->status_15 == HEAD_REQUESTED) {
 		xd->status_15 = HEAD_REPLY_RECEIVED;
-		close(source);/*Is this required?*/
+		close(source);/* Is this required? */
 		g_free(xd->txbuf);
 		xd->txbuf = NULL;
 		if (purple_proxy_connect(NULL, account, xd->host, xd->port, yahoo_xfer_connected_15, xfer) == NULL)
@@ -1151,7 +1164,7 @@
 	xd->txbuf_written = 0;
 
 	if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED)
-	{	
+	{
 		xd->status_15 = HEAD_REQUESTED;
 		xd->tx_handler = purple_input_add(source, PURPLE_INPUT_READ, yahoo_xfer_recv_cb_15, xfer);
 		yahoo_xfer_recv_cb_15(xfer, source, PURPLE_INPUT_READ);
@@ -1162,21 +1175,33 @@
 		xfer->fd = source;
 		purple_xfer_start(xfer, source, NULL, 0);
 	}
-	else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED)
+	else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && (xd->status_15 == ACCEPTED || xd->status_15 == P2P_GET_REQUESTED) )
 	{
 		xd->status_15 = TRANSFER_PHASE;
 		xfer->fd = source;
+		/* Remove Read event */
+		purple_input_remove(xd->input_event);
+		xd->input_event = 0;
 		purple_xfer_start(xfer, source, NULL, 0);
 	}
+	else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == P2P_HEAD_REQUESTED)
+	{
+		xd->status_15 = P2P_HEAD_REPLIED;
+		/* Remove Read event and close descriptor */
+		purple_input_remove(xd->input_event);
+		xd->input_event = 0;
+		close(source);
+		xfer->fd = -1;
+		/* start local server, listen for connections */
+		purple_network_listen(xd->yahoo_local_p2p_ft_server_port, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer);
+	}
 	else
 	{
 		purple_debug_error("yahoo", "Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", purple_xfer_get_type(xfer), xd->status_15);
 		return;
 	}
-
 }
 
-
 static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message)
 {
 	PurpleXfer *xfer;
@@ -1203,31 +1228,61 @@
 		cookies = yahoo_get_cookies(xd->gc);
 		if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED)
 		{
-			xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n",
+			if(xd->info_val_249 == 2)
+				{
+				/* sending file via p2p, we are connected as client */
+				xd->txbuf = g_strdup_printf("POST /%s HTTP/1.1\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n",
+										xd->path,
+										xd->host,
+										(long int)xfer->size);	/* to do, add Referer */
+				}
+			else
+				{
+				/* sending file via relaying */
+				xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n",
 										purple_url_encode(xd->xfer_idstring_for_relay),
 										purple_normalize(account, purple_account_get_username(account)),
 										xfer->who,
 										cookies,
 										xd->host,
 										(long int)xfer->size);
+				}
 		}
 		else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED)
 		{	
-			xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nAccept:*/*\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n",
+			if(xd->info_val_249 == 1)
+				{
+				/* receiving file via p2p, connected as client */
+				xd->txbuf = g_strdup_printf("HEAD /%s HTTP/1.1\r\nAccept:*/*\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n",xd->path,xd->host);
+			}
+			else
+				{
+				/* receiving file via relaying */
+				xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nAccept:*/*\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n",
 										purple_url_encode(xd->xfer_idstring_for_relay),
 										purple_normalize(account, purple_account_get_username(account)),
 										xfer->who,
 										cookies,
 										xd->host);
+			}
 		}
 		else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == HEAD_REPLY_RECEIVED)
 		{
-			xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nConnection: Keep-Alive\r\n\r\n",
+			if(xd->info_val_249 == 1)
+				{
+				/* receiving file via p2p, connected as client */
+				xd->txbuf = g_strdup_printf("GET /%s HTTP/1.1\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n",xd->path,xd->host);
+			}
+			else
+				{
+				/* receiving file via relaying */
+				xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nConnection: Keep-Alive\r\n\r\n",
 										purple_url_encode(xd->xfer_idstring_for_relay),
 										purple_normalize(account, purple_account_get_username(account)),
 										xfer->who,
 										cookies,
 										xd->host);
+			}
 		}
 		else
 		{
@@ -1248,6 +1303,227 @@
 	}
 }
 
+static void yahoo_p2p_ft_POST_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleXfer *xfer;
+	struct yahoo_xfer_data *xd;
+
+	xfer = data;
+	if (!(xd = xfer->data))	{
+		purple_input_remove(xd->input_event);
+		purple_xfer_cancel_remote(xfer);
+		return;
+	}
+
+	purple_input_remove(xd->input_event);
+	xd->status_15 = TRANSFER_PHASE;
+	xfer->fd = source;
+	purple_xfer_start(xfer, source, NULL, 0);
+}
+
+static void yahoo_p2p_ft_HEAD_GET_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleXfer *xfer;
+	struct yahoo_xfer_data *xd;
+	guchar buf[1024];
+	int len;
+	char *url_head;
+	char *url_get;
+	time_t unix_time;
+	char *time_str;
+
+	xfer = data;
+	if (!(xd = xfer->data))	{
+		purple_input_remove(xd->input_event);
+		purple_xfer_cancel_remote(xfer);
+		return;
+	}
+
+	len = read(source, buf, sizeof(buf));
+	if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
+		return ; /* No Worries*/
+	else if (len <= 0)	{
+		purple_debug_warning("yahoo","p2p-ft: Error in connection, or host disconnected\n");
+		purple_input_remove(xd->input_event);
+		purple_xfer_cancel_remote(xfer);
+		return;
+	}
+
+	url_head = g_strdup_printf("HEAD %s", xd->xfer_url);
+	url_get = g_strdup_printf("GET %s", xd->xfer_url);
+
+	if( strncmp(url_head, (char *)buf, strlen(url_head)) == 0 )
+		xd->status_15 = P2P_HEAD_REQUESTED;
+	else if( strncmp(url_get, (char *)buf, strlen(url_get)) == 0 )
+		xd->status_15 = P2P_GET_REQUESTED;
+	else	{
+		purple_debug_warning("yahoo","p2p-ft: Wrong HEAD/GET request from peer, disconnecting host\n");
+		purple_input_remove(xd->input_event);
+		purple_xfer_cancel_remote(xfer);
+		g_free(url_head);
+		return;
+	}
+
+	unix_time = time(NULL);
+	time_str = ctime(&unix_time);
+	strcpy(time_str + strlen(time_str) - 1, "\0");
+
+	if (xd->txbuflen == 0)	{
+		xd->txbuf = g_strdup_printf("HTTP/1.0 200 OK\r\nDate: %s GMT\r\nServer: Y!/1.0\r\nMIME-version: 1.0\r\nLast-modified: %s GMT\r\nContent-length: %d\r\n\r\n", time_str, time_str, xfer->size);
+		xd->txbuflen = strlen(xd->txbuf);
+		xd->txbuf_written = 0;
+	}
+
+	if (!xd->tx_handler)	{
+		xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE, yahoo_xfer_send_cb_15, xfer);
+		yahoo_xfer_send_cb_15(xfer, source, PURPLE_INPUT_WRITE);
+	}
+
+	g_free(url_head);
+	g_free(url_get);
+}
+
+static void yahoo_p2p_ft_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	int acceptfd;
+	PurpleXfer *xfer;
+	struct yahoo_xfer_data *xd;
+
+	xfer = data;
+	if (!(xd = xfer->data))	{
+		purple_xfer_cancel_remote(xfer);
+		return;
+	}
+
+	acceptfd = accept(source, NULL, 0);
+	if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+		return;
+	else if(acceptfd == -1) {
+		purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno));
+		purple_xfer_cancel_remote(xfer);
+		/* remove watcher and close p2p ft server */
+		purple_input_remove(xd->yahoo_p2p_ft_server_watcher);
+		close(xd->yahoo_local_p2p_ft_server_fd);
+		return;
+	}
+
+	/* remove watcher and close p2p ft server */
+	purple_input_remove(xd->yahoo_p2p_ft_server_watcher);
+	close(xd->yahoo_local_p2p_ft_server_fd);
+
+	/* Add an Input Read event to the file descriptor */
+	xfer->fd = acceptfd;
+	if(xfer->type == PURPLE_XFER_RECEIVE)
+		xd->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_ft_POST_cb, data);
+	else
+		xd->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_ft_HEAD_GET_cb, data);
+}
+
+static void yahoo_p2p_ft_server_listen_cb(int listenfd, gpointer data)
+{
+	PurpleXfer *xfer;
+	struct yahoo_xfer_data *xd;
+	struct yahoo_packet *pkt;
+	PurpleAccount *account;
+	struct yahoo_data *yd;
+	gchar *filename;
+	const char *local_ip;
+	gchar *url_to_send = NULL;
+	char **split;
+	char *filename_without_spaces = NULL;
+
+	xfer = data;
+	if ( !( (xd = xfer->data) || (listenfd != -1) ) )	{
+		purple_debug_warning("yahoo","p2p: error starting server for p2p file transfer\n");
+		purple_xfer_cancel_remote(xfer);
+		return;
+	}
+
+	if( (xfer->type == PURPLE_XFER_RECEIVE) || (xd->status_15 != P2P_HEAD_REPLIED) )	{
+		yd = xd->gc->proto_data;
+		account = purple_connection_get_account(xd->gc);
+		local_ip = purple_network_get_my_ip(listenfd);
+		xd->yahoo_local_p2p_ft_server_port = purple_network_get_port_from_fd(listenfd);
+
+		filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
+		split = g_strsplit(filename, " ", 0);
+		filename_without_spaces = g_strjoinv("+", split);
+		xd->xfer_url = g_strdup_printf("/Messenger.%s.%d000%s?AppID=Messenger&UserID=%s&K=lc9lu2u89gz1llmplwksajkjx", xfer->who, (int)time(NULL), filename_without_spaces, xfer->who);
+		url_to_send = g_strdup_printf("http://%s:%d%s", local_ip, xd->yahoo_local_p2p_ft_server_port, xd->xfer_url);
+
+		if(xfer->type == PURPLE_XFER_RECEIVE)	{
+			xd->info_val_249 = 2;	/* 249=2: we are p2p server, and receiving file */
+			pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15,
+				YAHOO_STATUS_AVAILABLE, yd->session_id);
+			yahoo_packet_hash(pkt, "ssssis",
+				1, purple_normalize(account, purple_account_get_username(account)),
+				5, xfer->who,
+				265, xd->xfer_peer_idstring,
+				27, xfer->filename,
+				249, 2,
+				250, url_to_send);
+		}
+		else	{
+			xd->info_val_249 = 1;	/* 249=1: we are p2p server, and sending file */
+			pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
+			yahoo_packet_hash(pkt, "ssssis",
+				1, purple_normalize(account, purple_account_get_username(account)),
+				5, xfer->who,
+				265, xd->xfer_peer_idstring,
+				27,  filename,
+				249, 1,
+				250, url_to_send);
+		}
+
+		yahoo_packet_send_and_free(pkt, yd);
+
+		g_free(filename);
+		g_free(url_to_send);
+		g_strfreev(split);
+		g_free(filename_without_spaces);
+	}
+
+	/* Add an Input Read event to the file descriptor */
+	xd->yahoo_local_p2p_ft_server_fd = listenfd;
+	xd->yahoo_p2p_ft_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_ft_server_send_connected_cb, data);
+}
+
+/* send (p2p) file transfer information */
+static void yahoo_p2p_client_send_ft_info(PurpleConnection *gc, PurpleXfer *xfer)
+{
+	struct yahoo_xfer_data *xd;
+	struct yahoo_packet *pkt;
+	PurpleAccount *account;
+	struct yahoo_data *yd;
+	gchar *filename;
+	struct yahoo_p2p_data *p2p_data;
+
+	if (!(xd = xfer->data))
+		return;
+
+	account = purple_connection_get_account(gc);
+	yd = gc->proto_data;
+
+	p2p_data = g_hash_table_lookup(yd->peers, xfer->who);
+	if( p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER )
+		if(purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer))
+			return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
+
+	yahoo_packet_hash(pkt, "ssssi",
+		1, purple_normalize(account, purple_account_get_username(account)),
+		5, xfer->who,
+		265, xd->xfer_peer_idstring,
+		27,  filename,
+		249, 2);	/* 249=2: we are p2p client */
+	xd->info_val_249 = 2;
+	yahoo_packet_send_and_free(pkt, yd);
+
+	g_free(filename);
+}
+
 void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	char *from = NULL;
@@ -1293,14 +1569,14 @@
 			/* 1=send, 2=cancel, 3=accept, 4=reject */ 
 			break;
 
-		/*check for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it.*/
+		/* check for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it. */
 		case 49:
 			service = pair->value;
 			break;
 		case 63:
 			imv = pair->value;
 			break;
-		/*end check*/
+		/* end check */
 
 		}
 	}
@@ -1328,6 +1604,14 @@
 		*	so, purple dnsquery is used... but retries, trying with next ip
 		*	address etc. is not implemented..TODO
 		*/
+		
+		/* To send through p2p */
+		if( g_hash_table_lookup(yd->peers, from) )	{
+			/* send p2p file transfer information */
+			yahoo_p2p_client_send_ft_info(gc, xfer);
+			return;
+		}
+
 		if (yd->jp)
 		{
 			purple_dnsquery_a(YAHOOJP_XFER_RELAY_HOST, YAHOOJP_XFER_RELAY_PORT,
@@ -1341,7 +1625,7 @@
 		return;
 	}
 
-	/*processing for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it.*/
+	/* processing for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it. */
 	/*
 	* The remote user has changed their IMVironment.  We
 	* record it for later use.
@@ -1357,7 +1641,7 @@
 			return;
 		}
 	}
-	/*end processing*/
+	/* end processing */
 
 	if(!filename_list)
 		return;
@@ -1432,6 +1716,7 @@
 	GSList *l;
 	struct yahoo_packet *pkt_to_send;
 	PurpleAccount *account;
+	struct yahoo_p2p_data *p2p_data;
 
 	yd = gc->proto_data;
 
@@ -1455,13 +1740,8 @@
 			val_66 = strtol(pair->value, NULL, 10);
 			break;
 		case 249:
-			val_249 = strtol(pair->value, NULL, 10); /*
-					* really pissed off with this- i hv seen 2 occurences of this
-					* being 1(its normally 3) - and in those cases, the url
-					* format and corresponding processing seems to be different
-					* (i havent tested - couldnt reproduce a 1), although i 
-					* guess its easier.
-					*/
+			val_249 = strtol(pair->value, NULL, 10);
+			/* 249 has value 1 or 2 when doing p2p transfer and value 3 when relaying through yahoo server */
 			break;
 		case 250:
 			url = pair->value;
@@ -1489,35 +1769,48 @@
 
 	xfer_data->info_val_249 = val_249;
 	xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay);
-	if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) {
-		purple_xfer_cancel_remote(xfer);
-		return;
-	}
+	if(val_249 == 1 || val_249 == 3)	{
+		if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) {
+			purple_xfer_cancel_remote(xfer);
+			return;
+		}
+		
+		account = purple_connection_get_account(xfer_data->gc);
 
-	account = purple_connection_get_account(xfer_data->gc);
+		pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15,
+			YAHOO_STATUS_AVAILABLE, yd->session_id);
+		yahoo_packet_hash(pkt_to_send, "ssssisi",
+			1, purple_normalize(account, purple_account_get_username(account)),
+			5, xfer->who,
+			265, xfer_data->xfer_peer_idstring,
+			27, xfer->filename,
+			249, xfer_data->info_val_249,
+			251, xfer_data->xfer_idstring_for_relay,
+			222, 3);
 
-	pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15,
-		YAHOO_STATUS_AVAILABLE, yd->session_id);
+		yahoo_packet_send_and_free(pkt_to_send, yd);
 
-	yahoo_packet_hash(pkt_to_send, "ssssisi",
-		1, purple_normalize(account, purple_account_get_username(account)),
-		5, xfer->who,
-		265, xfer_data->xfer_peer_idstring,
-		27, xfer->filename,
-		249, xfer_data->info_val_249,
-		251, xfer_data->xfer_idstring_for_relay,
-		222, 3);
+		if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port,
+			yahoo_xfer_connected_15, xfer) == NULL) {
+			purple_notify_error(gc, NULL, _("File Transfer Failed"),
+				_("Unable to establish file descriptor."));
+			purple_xfer_cancel_remote(xfer);
+		}
+	}
+	else if(val_249 == 2)	{
+		p2p_data = g_hash_table_lookup(yd->peers, xfer->who);
+		if( !( p2p_data && (p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) ) )	{
+			purple_xfer_cancel_remote(xfer);
+			return;
+		}
+		if(!purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer)) {
+			purple_xfer_cancel_remote(xfer);
+			return;
+		}
+	}
+}
 
-	yahoo_packet_send_and_free(pkt_to_send, yd);
-	if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port,
-		yahoo_xfer_connected_15, xfer) == NULL) {
-		purple_notify_error(gc, NULL, _("File Transfer Failed"),
-			_("Unable to establish file descriptor."));
-		purple_xfer_cancel_remote(xfer);
-		}
-
-}
-/*TODO: Check filename etc. No probs till some hacker comes in the way*/
+/* TODO: Check filename etc. No probs till some hacker comes in the way */
 void yahoo_process_filetrans_acc_15(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	gchar *xfer_peer_idstring = NULL;
@@ -1528,6 +1821,8 @@
 	GSList *l;
 	PurpleAccount *account;
 	long val_66 = 0;
+	gchar *url = NULL;
+	int val_249;
 
 	yd = gc->proto_data;
 	for (l = pkt->hash; l; l = l->next) {
@@ -1542,19 +1837,35 @@
 			break;
 		case 66:
 			val_66 = atol(pair->value);
+			break;
+		case 249:
+			val_249 = atol(pair->value);
+			break;
+		case 250:
+			url = pair->value;	/* we get a p2p url here when sending file, connected as client */
+			break;
 		}
 	}
 
 	xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_peer_idstring);
 	if(!xfer) return;
 
-	if(val_66 == -1 || !(xfer_idstring_for_relay))
+	if(val_66 == -1 || ( (!(xfer_idstring_for_relay)) && (val_249 != 2) ))
+	{
+		purple_xfer_cancel_remote(xfer);
+		return;
+	}
+
+	if( (val_249 == 2) && (!(url)) )
 	{
 		purple_xfer_cancel_remote(xfer);
 		return;
 	}
 
 	xfer_data = xfer->data;
+	if(url)
+		purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL);
+		
 	xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay);
 	xfer_data->status_15 = ACCEPTED;
 	account = purple_connection_get_account(gc);
--- a/libpurple/protocols/yahoo/yahoo_friend.c	Sun Jul 13 10:09:19 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_friend.c	Sun Jul 13 14:19:59 2008 +0000
@@ -264,3 +264,13 @@
 		yahoo_packet_send_and_free(pkt, yd);
 	}
 }
+
+void yahoo_friend_set_p2p_status(YahooFriend *f, YahooP2PStatus p2p_status)
+{
+	f->p2p_status = p2p_status;
+}
+
+YahooP2PStatus yahoo_friend_get_p2p_status(YahooFriend *f)
+{
+	return f->p2p_status;
+}
--- a/libpurple/protocols/yahoo/yahoo_friend.h	Sun Jul 13 10:09:19 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_friend.h	Sun Jul 13 14:19:59 2008 +0000
@@ -34,6 +34,13 @@
 	YAHOO_PRESENCE_PERM_OFFLINE
 } YahooPresenceVisibility;
 
+typedef enum {
+	YAHOO_P2PSTATUS_NOT_CONNECTED = 0,
+	YAHOO_P2PSTATUS_DO_NOT_CONNECT,
+	YAHOO_P2PSTATUS_WE_ARE_SERVER,
+	YAHOO_P2PSTATUS_WE_ARE_CLIENT
+} YahooP2PStatus;
+
 /* these are called friends instead of buddies mainly so I can use variables
  * named f and not confuse them with variables named b
  */
@@ -50,6 +57,9 @@
 	int protocol; /* 1=LCS, 2=MSN*/
 	long int version_id;
 	gchar *alias_id;
+	YahooP2PStatus p2p_status;
+	gboolean p2p_packet_sent;	/* 0:not sent, 1=sent */
+	gint session_id;	/* session id of friend */
 } YahooFriend;
 
 YahooFriend *yahoo_friend_find(PurpleConnection *gc, const char *name);
@@ -76,4 +86,7 @@
 void yahoo_friend_update_presence(PurpleConnection *gc, const char *name,
 		YahooPresenceVisibility presence);
 
+void yahoo_friend_set_p2p_status(YahooFriend *f, YahooP2PStatus p2p_status);
+YahooP2PStatus yahoo_friend_get_p2p_status(YahooFriend *f);
+
 #endif /* _YAHOO_FRIEND_H_ */
--- a/libpurple/protocols/yahoo/yahoo_packet.h	Sun Jul 13 10:09:19 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.h	Sun Jul 13 14:19:59 2008 +0000
@@ -76,7 +76,7 @@
 	YAHOO_SERVICE_IGNORECONTACT,    /* > 1, 7, 13 < 1, 66, 13, 0*/
 	YAHOO_SERVICE_REJECTCONTACT,
 	YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */
-	/* YAHOO_SERVICE_??? = 0x8A, */
+	/* YAHOO_SERVICE_??? = 0x8A,some sort of keep alive sent to the server every 60 secs */
 	YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/
 	YAHOO_SERVICE_CHATGOTO,
 	YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */
@@ -98,15 +98,18 @@
 	YAHOO_SERVICE_AVATAR_UPDATE = 0xc7,
 	YAHOO_SERVICE_VERIFY_ID_EXISTS = 0xc8,
 	YAHOO_SERVICE_AUDIBLE = 0xd0,
+	/* YAHOO_SERVICE_CHAT_SESSION = 0xd4,?? Reports start of chat session, gets an id from server */
 	YAHOO_SERVICE_AUTH_REQ_15 = 0xd6,
+	YAHOO_SERVICE_FILETRANS_15 = 0xdc,
+	YAHOO_SERVICE_FILETRANS_INFO_15 = 0xdd,
+	YAHOO_SERVICE_FILETRANS_ACC_15 = 0xde,
+	/* photo sharing services ?? - 0xd2, 0xd7, 0xd8, 0xda */
 	YAHOO_SERVICE_CHGRP_15 = 0xe7,
 	YAHOO_SERVICE_STATUS_15 = 0xf0,
 	YAHOO_SERVICE_LIST_15 = 0xf1,
-	YAHOO_SERVICE_FILETRANS_15 = 0xdc,
-	YAHOO_SERVICE_FILETRANS_INFO_15 = 0xdd,
-	YAHOO_SERVICE_FILETRANS_ACC_15 = 0xde,
 	YAHOO_SERVICE_WEBLOGIN = 0x0226,
 	YAHOO_SERVICE_SMS_MSG = 0x02ea
+	/* YAHOO_SERVICE_DISCONNECT = 0x07d1 Server forces us to disconnect. Is sent with TCP FIN flag set */
 };
 
 struct yahoo_pair {