changeset 27933:f44832c6a65b

propagate from branch 'im.pidgin.pidgin' (head a840b7a77c7e395db5e7877fab90763d91f70b3a) to branch 'im.pidgin.pidgin.yaz' (head 42d24988b115ba197f27a95547b1445c10f06d2c)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Sun, 08 Mar 2009 20:38:09 +0000
parents 4033fea1709c (current diff) 7114d475a9e7 (diff)
children c1f1ed4d7a12
files libpurple/protocols/jabber/jabber.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h libpurple/protocols/yahoo/yahoo.c libpurple/protocols/yahoo/yahoo.h libpurple/protocols/yahoo/yahoo_filexfer.c libpurple/protocols/yahoo/yahoo_packet.h pidgin/gtkblist.c pidgin/gtkconn.c
diffstat 21 files changed, 2123 insertions(+), 453 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue Mar 03 08:03:11 2009 +0000
+++ b/COPYRIGHT	Sun Mar 08 20:38:09 2009 +0000
@@ -57,6 +57,7 @@
 Philip Brown
 Dan Bruce
 Norbert Buchmuller
+Johannes Buchner
 Sean Burke
 Thomas Butter
 Trevor Caira
@@ -261,6 +262,7 @@
 Lucio Maciel
 Brian Macke
 Paolo Maggi
+Sulabh Mahajan
 Willian T. Mahan
 Kris Marsh
 Fidel Martinez
--- a/ChangeLog	Tue Mar 03 08:03:11 2009 +0000
+++ b/ChangeLog	Sun Mar 08 20:38:09 2009 +0000
@@ -18,9 +18,6 @@
 	* Pressing the Enter key in the message entry box of the New Status
 	  dialog and various other dialogs now causes the cursor to move to
 	  the next line.
-	* Authentication failures now clear the account password, causing Pidgin
-	  to prompt for the password when clicking the "Re-enable" button on the
-	  minidialog.
 
 version 2.5.5 (03/01/2009):
 	libpurple:
--- a/doc/funniest_home_convos.txt	Tue Mar 03 08:03:11 2009 +0000
+++ b/doc/funniest_home_convos.txt	Sun Mar 08 20:38:09 2009 +0000
@@ -565,3 +565,10 @@
              for using pidgen
 22:36 <user> why do they think this is a bad client? does it have history?
 
+--
+
+15:45 <deryni> We've had a Grand Plugin Database Plan for approximately forever.
+15:45 <SimGuy> ah, the GPDP
+15:46 <khc> well, there was a Grand Smiley Theme Database
+15:47 <SimGuy> the GSTD sounds like a bad acronym
+15:47 <khc> I realized after typing that
--- a/libpurple/ft.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/ft.c	Sun Mar 08 20:38:09 2009 +0000
@@ -857,8 +857,12 @@
 	else
 		s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);
 
-	if (xfer->ops.read != NULL)
+	if (xfer->ops.read != NULL)	{
 		r = (xfer->ops.read)(buffer, xfer);
+		if ((purple_xfer_get_size(xfer) > 0) &&
+			((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
+			purple_xfer_set_completed(xfer, TRUE);
+	}
 	else {
 		*buffer = g_malloc0(s);
 
--- a/libpurple/plugins/Makefile.am	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/plugins/Makefile.am	Sun Mar 08 20:38:09 2009 +0000
@@ -36,6 +36,7 @@
 newline_la_LDFLAGS          = -module -avoid-version
 notify_example_la_LDFLAGS   = -module -avoid-version
 offlinemsg_la_LDFLAGS       = -module -avoid-version
+one_time_password_la_LDFLAGS	= -module -avoid-version
 pluginpref_example_la_LDFLAGS = -module -avoid-version
 psychic_la_LDFLAGS          = -module -avoid-version
 signals_test_la_LDFLAGS		= -module -avoid-version
@@ -65,6 +66,7 @@
 	debug_example.la \
 	helloworld.la \
 	notify_example.la \
+	one_time_password.la \
 	pluginpref_example.la \
 	signals_test.la \
 	simple.la
@@ -81,6 +83,7 @@
 newline_la_SOURCES          = newline.c
 notify_example_la_SOURCES   = notify_example.c
 offlinemsg_la_SOURCES       = offlinemsg.c
+one_time_password_la_SOURCES	= one_time_password.c
 pluginpref_example_la_SOURCES = pluginpref_example.c
 psychic_la_SOURCES          = psychic.c
 signals_test_la_SOURCES		= signals-test.c
@@ -97,6 +100,7 @@
 newline_la_LIBADD           = $(GLIB_LIBS)
 notify_example_la_LIBADD    = $(GLIB_LIBS)
 offlinemsg_la_LIBADD        = $(GLIB_LIBS)
+one_time_password_la_LIBADD = $(GLIB_LIBS)
 pluginpref_example_la_LIBADD = $(GLIB_LIBS)
 psychic_la_LIBADD           = $(GLIB_LIBS)
 signals_test_la_LIBADD		= $(GLIB_LIBS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/one_time_password.c	Sun Mar 08 20:38:09 2009 +0000
@@ -0,0 +1,151 @@
+/*
+ * One Time Password support plugin for libpurple
+ *
+ * Copyright (C) 2009, Daniel Atallah <datallah@pidgin.im>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02111-1301, USA.
+ */
+#include "internal.h"
+#include "debug.h"
+#include "plugin.h"
+#include "version.h"
+#include "account.h"
+#include "accountopt.h"
+
+#define PLUGIN_ID "core-one_time_password"
+#define PREF_NAME PLUGIN_ID "_enabled"
+
+static void
+signed_on_cb(PurpleConnection *conn, void *data)
+{
+	PurpleAccount *account = purple_connection_get_account(conn);
+
+	if (purple_account_get_bool(account, PREF_NAME, FALSE)) {
+		if(purple_account_get_remember_password(account))
+			purple_debug_error("One Time Password",
+					   "Unable to enforce one time password for account %s (%s).\n"
+					   "Account is set to remember the password.\n",
+					   purple_account_get_username(account),
+					   purple_account_get_protocol_name(account));
+		else {
+
+			purple_debug_info("One Time Password", "Clearing password for account %s (%s).\n",
+					  purple_account_get_username(account),
+					  purple_account_get_protocol_name(account));
+
+			purple_account_set_password(account, NULL);
+			/* TODO: Do we need to somehow clear conn->password ? */
+		}
+	}
+}
+
+static gboolean
+plugin_load(PurplePlugin *plugin)
+{
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleAccountOption *option;
+	GList *l;
+
+	/* Register protocol preference. */
+	for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) {
+		prpl = (PurplePlugin *)l->data;
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+		if (prpl_info != NULL && !(prpl_info->options & OPT_PROTO_NO_PASSWORD)) {
+			option = purple_account_option_bool_new(_("One Time Password"),
+								PREF_NAME, FALSE);
+			prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+		}
+	}
+
+	/* Register callback. */
+	purple_signal_connect(purple_connections_get_handle(), "signed-on",
+			      plugin, PURPLE_CALLBACK(signed_on_cb), NULL);
+
+	return TRUE;
+}
+
+static gboolean
+plugin_unload(PurplePlugin *plugin)
+{
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleAccountOption *option;
+	GList *l, *options;
+
+	/* Remove protocol preference. */
+	for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) {
+		prpl = (PurplePlugin *)l->data;
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+		if (prpl_info != NULL && !(prpl_info->options & OPT_PROTO_NO_PASSWORD)) {
+			options = prpl_info->protocol_options;
+			while (options != NULL) {
+				option = (PurpleAccountOption *) options->data;
+				if (strcmp(PREF_NAME, purple_account_option_get_setting(option)) == 0) {
+					prpl_info->protocol_options = g_list_delete_link(prpl_info->protocol_options, options);
+					purple_account_option_destroy(option);
+					break;
+				}
+				options = options->next;
+			}
+		}
+	}
+
+	/* Callback will be automagically unregistered */
+
+	return TRUE;
+}
+
+static PurplePluginInfo info =
+{
+	PURPLE_PLUGIN_MAGIC,
+	PURPLE_MAJOR_VERSION,
+	PURPLE_MINOR_VERSION,
+	PURPLE_PLUGIN_STANDARD,				/**< type           */
+	NULL,						/**< ui_requirement */
+	0,						/**< flags          */
+	NULL,						/**< dependencies   */
+	PURPLE_PRIORITY_DEFAULT,			/**< priority       */
+	PLUGIN_ID,					/**< id             */
+	N_("One Time Password Support"),		/**< name           */
+	DISPLAY_VERSION,				/**< version        */
+							/**  summary        */
+	N_("Enforce that passwords are used only once."),
+							/**  description    */
+	N_("Allows you to enforce on a per-account basis that passwords not "
+	   "being saved are only used in a single successful connection.\n"
+	   "Note: The account password must not be saved for this to work."),
+	"Daniel Atallah <datallah@pidgin.im>",		/**< author         */
+	PURPLE_WEBSITE,					/**< homepage       */
+	plugin_load,					/**< load           */
+	plugin_unload,					/**< unload         */
+	NULL,						/**< destroy        */
+	NULL,						/**< ui_info        */
+	NULL,						/**< extra_info     */
+	NULL,						/**< prefs_info     */
+	NULL,						/**< actions        */
+	NULL,						/**< reserved 1     */
+	NULL,						/**< reserved 2     */
+	NULL,						/**< reserved 3     */
+	NULL						/**< reserved 4     */
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(one_time_password, init_plugin, info)
--- a/libpurple/protocols/jabber/jabber.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sun Mar 08 20:38:09 2009 +0000
@@ -2524,7 +2524,7 @@
 		return TRUE;
 	} else {
 		*error = g_strdup_printf(_("Unable to buzz, because %s does "
-			"not support it or do not wish to receive buzzes now."), alias);
+			"not support it or does not wish to receive buzzes now."), alias);
 		return FALSE;
 	}
 }
--- a/libpurple/protocols/oscar/bstream.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/oscar/bstream.c	Sun Mar 08 20:38:09 2009 +0000
@@ -311,3 +311,37 @@
 
 	return byte_stream_putle32(bs, atoi(purple_account_get_username(account)));
 }
+
+void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data)
+{
+	byte_stream_put16(bs, type);
+
+	if (data != NULL && data->len > 0) {
+		/* Flags. 0x04 means "this asset has data attached to it" */
+		byte_stream_put8(bs, 0x04); /* Flags */
+		byte_stream_put8(bs, data->len); /* Length */
+		byte_stream_rewind(data);
+		byte_stream_putbs(bs, data, data->len); /* Data */
+	} else {
+		byte_stream_put8(bs, 0x00); /* No flags */
+		byte_stream_put8(bs, 0x00); /* Length */
+		/* No data */
+	}
+}
+
+void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr)
+{
+	ByteStream data;
+	size_t len = datastr != NULL ? strlen(datastr) : 0;
+
+	if (len > 0) {
+		byte_stream_new(&data, 2 + len + 2);
+		byte_stream_put16(&data, len); /* Length */
+		byte_stream_putstr(&data, datastr); /* String */
+		byte_stream_put16(&data, 0x0000); /* Unknown */
+		byte_stream_put_bart_asset(bs, type, &data);
+		byte_stream_destroy(&data);
+	} else {
+		byte_stream_put_bart_asset(bs, type, NULL);
+	}
+}
--- a/libpurple/protocols/oscar/family_oservice.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Sun Mar 08 20:38:09 2009 +0000
@@ -825,7 +825,7 @@
 int
 aim_srv_setextrainfo(OscarData *od,
 		gboolean seticqstatus, guint32 icqstatus,
-		gboolean setavailmsg, const char *availmsg, const char *itmsurl)
+		gboolean setstatusmsg, const char *statusmsg, const char *itmsurl)
 {
 	FlapConnection *conn;
 	ByteStream bs;
@@ -851,30 +851,17 @@
 	}
 #endif
 
-	if (setavailmsg)
+	if (setstatusmsg)
 	{
-		int availmsglen, itmsurllen;
+		size_t statusmsglen, itmsurllen;
 		ByteStream tmpbs;
 
-		availmsglen = (availmsg != NULL) ? strlen(availmsg) : 0;
+		statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0;
 		itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0;
 
-		byte_stream_new(&tmpbs, availmsglen + 8 + itmsurllen + 8);
-		byte_stream_put16(&tmpbs, 0x0002);
-		byte_stream_put8(&tmpbs, 0x04); /* Flags */
-		byte_stream_put8(&tmpbs, availmsglen + 4);
-		byte_stream_put16(&tmpbs, availmsglen);
-		if (availmsglen > 0)
-			byte_stream_putstr(&tmpbs, availmsg);
-		byte_stream_put16(&tmpbs, 0x0000);
-
-		byte_stream_put16(&tmpbs, 0x0009);
-		byte_stream_put8(&tmpbs, 0x04); /* Flags */
-		byte_stream_put8(&tmpbs, itmsurllen + 4);
-		byte_stream_put16(&tmpbs, itmsurllen);
-		if (itmsurllen > 0)
-			byte_stream_putstr(&tmpbs, itmsurl);
-		byte_stream_put16(&tmpbs, 0x0000);
+		byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8);
+		byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg);
+		byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl);
 
 		aim_tlvlist_add_raw(&tlvlist, 0x001d,
 				byte_stream_curpos(&tmpbs), tmpbs.data);
--- a/libpurple/protocols/oscar/oscar.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sun Mar 08 20:38:09 2009 +0000
@@ -4767,16 +4767,17 @@
 	PurpleStatusType *status_type;
 	PurpleStatusPrimitive primitive;
 
-	char *htmlinfo;
 	char *info_encoding = NULL;
 	char *info = NULL;
 	gsize infolen = 0;
 
-	const char *htmlaway;
 	char *away_encoding = NULL;
 	char *away = NULL;
 	gsize awaylen = 0;
 
+	char *status_text = NULL;
+	const char *itmsurl = NULL;
+
 	status_type = purple_status_get_type(status);
 	primitive = purple_status_type_get_primitive(status_type);
 
@@ -4794,7 +4795,7 @@
 	}
 	else if (rawinfo != NULL)
 	{
-		htmlinfo = purple_strdup_withhtml(rawinfo);
+		char *htmlinfo = purple_strdup_withhtml(rawinfo);
 		info = purple_prpl_oscar_convert_to_infotext(htmlinfo, &infolen, &info_encoding);
 		g_free(htmlinfo);
 
@@ -4811,16 +4812,54 @@
 		}
 	}
 
-	if (!setstatus)
+	if (setstatus)
 	{
-		/* Do nothing! */
-	}
-	else if (primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
-	{
-		const char *status_html, *itmsurl;
-		char *status_text = NULL;
+		const char *status_html;
 
 		status_html = purple_status_get_attr_string(status, "message");
+
+		if (primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
+		{
+			/* This is needed for us to un-set any previous away message. */
+			away = g_strdup("");
+		}
+		else
+		{
+			gchar *linkified;
+
+			/* We do this for icq too so that they work for old third party clients */
+			linkified = purple_markup_linkify(status_html);
+			away = purple_prpl_oscar_convert_to_infotext(linkified, &awaylen, &away_encoding);
+			g_free(linkified);
+
+			if (awaylen > od->rights.maxawaymsglen)
+			{
+				gchar *errstr;
+
+				errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte "
+										 "has been exceeded.  It has been truncated for you.",
+										 "The maximum away message length of %d bytes "
+										 "has been exceeded.  It has been truncated for you.",
+										 od->rights.maxawaymsglen), od->rights.maxawaymsglen);
+				purple_notify_warning(gc, NULL, _("Away message too long."), errstr);
+				g_free(errstr);
+			}
+		}
+	}
+
+	aim_locate_setprofile(od,
+			info_encoding, info, MIN(infolen, od->rights.maxsiglen),
+			away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
+	g_free(info);
+	g_free(away);
+
+	if (setstatus)
+	{
+		const char *status_html;
+
+		status_html = purple_status_get_attr_string(status, "message");
+		if (od->icq && (status_html == NULL || status_html[0] == '\0'))
+			status_html = purple_status_type_get_name(status_type);
 		if (status_html != NULL)
 		{
 			status_text = purple_markup_strip_html(status_html);
@@ -4831,82 +4870,28 @@
 				strcpy(tmp, "...");
 			}
 		}
+
 		itmsurl = purple_status_get_attr_string(status, "itmsurl");
 
+		/* TODO: Combine these two calls! */
 		aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, itmsurl);
-		g_free(status_text);
-
-		/* This is needed for us to un-set any previous away message. */
-		away = g_strdup("");
-	}
-	else
-	{
-		char *status_text = NULL;
-		
-		htmlaway = purple_status_get_attr_string(status, "message");
-		if ((htmlaway == NULL) || (*htmlaway == '\0'))
-			htmlaway = purple_status_type_get_name(status_type);
-		
-		/* ICQ 6.x seems to use an available message for all statuses so set one */
-		if (od->icq) 
-		{
-			status_text = purple_markup_strip_html(htmlaway);
-			/* If the status_text is longer than 251 characters then truncate it */
-			if (strlen(status_text) > MAXAVAILMSGLEN)
-			{
-				char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]);
-				strcpy(tmp, "...");
-			}
-			aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, NULL);
-		}
-		g_free(status_text);
-
-		/* Set a proper away message for icq too so that they work for old third party clients */
-		
-		away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding);
-
-		if (awaylen > od->rights.maxawaymsglen)
-		{
-			gchar *errstr;
-
-			errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte "
-									 "has been exceeded.  It has been truncated for you.",
-									 "The maximum away message length of %d bytes "
-									 "has been exceeded.  It has been truncated for you.",
-									 od->rights.maxawaymsglen), od->rights.maxawaymsglen);
-			purple_notify_warning(gc, NULL, _("Away message too long."), errstr);
-			g_free(errstr);
-		}
-	}
-
-	if (setstatus)
 		oscar_set_extendedstatus(gc);
-
-	aim_locate_setprofile(od, info_encoding, info, MIN(infolen, od->rights.maxsiglen),
-									away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
-	g_free(info);
-	g_free(away);
+	}
 }
 
 static void
-oscar_set_status_icq(PurpleAccount *account, PurpleStatus *status)
+oscar_set_status_icq(PurpleAccount *account)
 {
 	PurpleConnection *gc = purple_account_get_connection(account);
-	OscarData *od = NULL;
-
-	if (gc)
-		od = purple_connection_get_protocol_data(gc);
-	if (!od)
-		return;
-
-	if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_INVISIBLE)
-		account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
-	else
-		account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
-
-	if ((od->ssi.received_data) && (aim_ssi_getpermdeny(od->ssi.local) != account->perm_deny))
-		aim_ssi_setpermdeny(od, account->perm_deny, 0xffffffff);
-
+
+	/* Our permit/deny setting affects our invisibility */
+	oscar_set_permit_deny(gc);
+
+	/*
+	 * TODO: I guess we should probably wait and do this after we get
+	 * confirmation from the above SSI call?  Right now other people
+	 * see our status blip to "invisible" before we appear offline.
+	 */
 	oscar_set_extendedstatus(gc);
 }
 
@@ -4926,7 +4911,7 @@
 
 	/* Set the ICQ status for ICQ accounts only */
 	if (oscar_util_valid_name_icq(purple_account_get_username(account)))
-		oscar_set_status_icq(account, status);
+		oscar_set_status_icq(account);
 }
 
 #ifdef CRAZY_WARN
@@ -5082,7 +5067,7 @@
 		return 1;
 	}
 
-	oscar_set_extendedstatus(gc);
+	oscar_set_status_icq(purple_connection_get_account(gc));
 
 	return 1;
 }
@@ -5374,15 +5359,19 @@
 			} break;
 
 			case 0x0004: { /* Permit/deny setting */
-				if (curitem->data) {
-					guint8 permdeny;
-					if ((permdeny = aim_ssi_getpermdeny(od->ssi.local)) && (permdeny != account->perm_deny)) {
+				/*
+				 * We don't inherit the permit/deny setting from the server
+				 * for ICQ because, for ICQ, this setting controls who can
+				 * see your online status when you are invisible.  Thus it is
+				 * a part of your status and not really related to blocking.
+				 */
+				if (!od->icq && curitem->data) {
+					guint8 perm_deny = aim_ssi_getpermdeny(od->ssi.local);
+					if (perm_deny != 0 && perm_deny != account->perm_deny)
+					{
 						purple_debug_info("oscar",
-								   "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny);
-						account->perm_deny = permdeny;
-						if (od->icq && account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) {
-							purple_presence_set_status_active(purple_account_get_presence(account), OSCAR_STATUS_ID_INVISIBLE, TRUE);
-						}
+								   "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, perm_deny);
+						account->perm_deny = perm_deny;
 					}
 				}
 			} break;
@@ -5393,7 +5382,7 @@
 		} /* End of switch on curitem->type */
 	} /* End of for loop */
 
-	oscar_set_extendedstatus(gc);
+	oscar_set_status_icq(account);
 
 	/* Activate SSI */
 	/* Sending the enable causes other people to be able to see you, and you to see them */
@@ -6074,29 +6063,36 @@
 void oscar_set_permit_deny(PurpleConnection *gc) {
 	PurpleAccount *account = purple_connection_get_account(gc);
 	OscarData *od = purple_connection_get_protocol_data(gc);
-
-	if (od->ssi.received_data) {
-		switch (account->perm_deny) {
-			case PURPLE_PRIVACY_ALLOW_ALL:
-				aim_ssi_setpermdeny(od, 0x01, 0xffffffff);
-				break;
-			case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
-				aim_ssi_setpermdeny(od, 0x05, 0xffffffff);
-				break;
-			case PURPLE_PRIVACY_ALLOW_USERS:
-				aim_ssi_setpermdeny(od, 0x03, 0xffffffff);
-				break;
-			case PURPLE_PRIVACY_DENY_ALL:
-				aim_ssi_setpermdeny(od, 0x02, 0xffffffff);
-				break;
-			case PURPLE_PRIVACY_DENY_USERS:
-				aim_ssi_setpermdeny(od, 0x04, 0xffffffff);
-				break;
-			default:
-				aim_ssi_setpermdeny(od, 0x01, 0xffffffff);
-				break;
-		}
-	}
+	PurplePrivacyType perm_deny;
+
+	/*
+	 * For ICQ the permit/deny setting controls who you can see you
+	 * online when you set your status to "invisible."  If we're ICQ
+	 * and we're invisible then we need to use one of
+	 * PURPLE_PRIVACY_ALLOW_USERS or PURPLE_PRIVACY_ALLOW_BUDDYLIST or
+	 * PURPLE_PRIVACY_DENY_USERS if we actually want to be invisible
+	 * to anyone.
+	 *
+	 * These three permit/deny settings correspond to:
+	 * 1. Invisible to everyone except the people on my "permit" list
+	 * 2. Invisible to everyone except the people on my buddy list
+	 * 3. Invisible only to the people on my "deny" list
+	 *
+	 * It would be nice to allow cases 2 and 3, but our UI doesn't have
+	 * a nice way to do it.  For now we just force case 1.
+	 */
+	if (od->icq && purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE))
+		perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
+	else
+		perm_deny = account->perm_deny;
+
+	if (od->ssi.received_data)
+		/*
+		 * Conveniently there is a one-to-one mapping between the
+		 * values of libpurple's PurplePrivacyType and the values used
+		 * by the oscar protocol.
+		 */
+		aim_ssi_setpermdeny(od, perm_deny, 0xffffffff);
 }
 
 void oscar_add_permit(PurpleConnection *gc, const char *who) {
--- a/libpurple/protocols/oscar/oscar.h	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Sun Mar 08 20:38:09 2009 +0000
@@ -665,7 +665,7 @@
 /* 0x0014 */ void aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32);
 /* 0x0016 */ void aim_srv_nop(OscarData *od, FlapConnection *conn);
 /* 0x0017 */ void aim_srv_setversions(OscarData *od, FlapConnection *conn);
-/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setavailmsg, const char *availmsg, const char *itmsurl);
+/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setstatusmsg, const char *statusmsg, const char *itmsurl);
 
 
 void aim_bos_reqrights(OscarData *od, FlapConnection *conn);
@@ -1595,6 +1595,18 @@
 int byte_stream_putuid(ByteStream *bs, OscarData *od);
 int byte_stream_putcaps(ByteStream *bs, guint32 caps);
 
+/**
+ * Inserts a BART asset block into the given byte stream.  The flags
+ * and length are set appropriately based on the value of data.
+ */
+void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data);
+
+/**
+ * A helper function that calls byte_stream_put_bart_asset with the
+ * appropriate data ByteStream given the datastr.
+ */
+void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr);
+
 /*
  * Generic SNAC structure.  Rarely if ever used.
  */
--- a/libpurple/protocols/yahoo/yahoo.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sun Mar 08 20:38:09 2009 +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"
@@ -38,6 +39,7 @@
 #include "server.h"
 #include "util.h"
 #include "version.h"
+#include "xmlnode.h"
 
 #include "yahoo.h"
 #include "yahoochat.h"
@@ -153,6 +155,7 @@
 	char *name = NULL;
 	gboolean unicode = FALSE;
 	char *message = NULL;
+	char *msn_name = NULL;
 
 	if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
 		if (!purple_account_get_remember_password(account))
@@ -235,6 +238,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;
@@ -350,7 +355,12 @@
 			if(f && strtol(pair->value, NULL, 10))
 				f->version_id = strtol(pair->value, NULL, 10);
 			break;
-
+		case 241: /* protocol buddy belongs to */
+			if(strtol(pair->value, NULL, 10) == 2)	{
+				msn_name = g_strconcat("msn/", name, NULL);
+				name = msn_name;
+			}
+			break;
 		default:
 			purple_debug_warning("yahoo",
 					   "Unknown status key %d\n", pair->key);
@@ -472,10 +482,13 @@
 	struct yahoo_data *yd = gc->proto_data;
 	GHashTable *ht;
 	char *norm_bud = NULL;
+	char *temp = NULL;
 	YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */
 	                       /* But what if you had no friends? */
 	PurpleBuddy *b;
 	PurpleGroup *g;
+	int protocol = 0;
+	int stealth = 0;
 
 
 	ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
@@ -499,6 +512,48 @@
 
 			break;
 		case 301: /* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */
+			if(temp != NULL)	{
+				if(protocol == 2)
+					norm_bud = g_strconcat("msn/", temp, NULL);
+				else
+					norm_bud = g_strdup(temp);
+
+				if (yd->current_list15_grp) {
+					/* This buddy is in a group */
+					f = yahoo_friend_find_or_new(gc, norm_bud);
+					if (!(b = purple_find_buddy(account, norm_bud))) {
+						if (!(g = purple_find_group(yd->current_list15_grp))) {
+							g = purple_group_new(yd->current_list15_grp);
+							purple_blist_add_group(g, NULL);
+					}
+					b = purple_buddy_new(account, norm_bud, NULL);
+					purple_blist_add_buddy(b, NULL, g, NULL);
+				}
+				yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp);
+				if(protocol != 0)	{
+					f->protocol = protocol;
+					purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol);
+				}
+				if(stealth == 2)
+					f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
+
+				/* set p2p status not connected and no p2p packet sent */
+				if(protocol == 0)	{
+					yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED);
+					f->p2p_packet_sent = 0;
+				} else
+					yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT);
+				} else {
+					/* This buddy is on the ignore list (and therefore in no group) */
+					purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n",account->username, norm_bud);
+					purple_privacy_deny_add(account, norm_bud, 1);
+				}
+			
+				protocol = 0;
+				stealth = 0;
+				norm_bud = NULL;
+				temp = NULL;
+			}
 			break;
 		case 300: /* This is 318 before a group, 319 before any s/n in a group, and 320 before any ignored s/n. */
 			break;
@@ -507,42 +562,16 @@
 			yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE);
 			break;
 		case 7: /* buddy's s/n */
-			g_free(norm_bud);
-			norm_bud = g_strdup(purple_normalize(account, pair->value));
-
-			if (yd->current_list15_grp) {
-				/* This buddy is in a group */
-				f = yahoo_friend_find_or_new(gc, norm_bud);
-				if (!(b = purple_find_buddy(account, norm_bud))) {
-					if (!(g = purple_find_group(yd->current_list15_grp))) {
-						g = purple_group_new(yd->current_list15_grp);
-						purple_blist_add_group(g, NULL);
-					}
-					b = purple_buddy_new(account, norm_bud, NULL);
-					purple_blist_add_buddy(b, NULL, g, NULL);
-				}
-				yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp);
-
-			} else {
-				/* This buddy is on the ignore list (and therefore in no group) */
-				purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n",
-								  account->username, norm_bud);
-				purple_privacy_deny_add(account, norm_bud, 1);
-			}
+			temp = g_strdup(purple_normalize(account, pair->value));
 			break;
 		case 241: /* another protocol user */
-			if (f) {
-				f->protocol = strtol(pair->value, NULL, 10);
-				purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol);
-			}
+			protocol = strtol(pair->value, NULL, 10);
 			break;
 		case 59: /* somebody told cookies come here too, but im not sure */
 			yahoo_process_cookie(yd, pair->value);
 			break;
 		case 317: /* Stealth Setting */
-			if (f && (strtol(pair->value, NULL, 10) == 2)) {
-				f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
-			}
+			stealth = strtol(pair->value, NULL, 10);
 			break;
 		/* case 242: */ /* this seems related to 241 */
 			/* break; */
@@ -552,6 +581,7 @@
 	g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
 	g_hash_table_destroy(ht);
 	g_free(norm_bud);
+	g_free(temp);
 }
 
 static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
@@ -636,6 +666,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);
@@ -692,7 +726,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;
@@ -701,12 +736,16 @@
 	char *game = NULL;
 	YahooFriend *f = NULL;
 	GSList *l = pkt->hash;
+	gint val_11 = 0;
+	struct yahoo_data *yd = gc->proto_data;
+	gboolean msn = FALSE;
+	char *msn_from = NULL;
 
 	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;
@@ -714,19 +753,43 @@
 			stat = pair->value;
 		if (pair->key == 14)
 			game = pair->value;
+		if (pair->key == 11)
+			val_11 = strtol(pair->value, NULL, 10);
+		if (pair->key == 241)
+			if(strtol(pair->value, NULL, 10) == 2)
+				msn = TRUE;
 		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(msn)
+		msn_from = g_strconcat("msn/", from, NULL);
+
 	if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
 		&& (purple_privacy_check(account, from)))
 	{
-		if (*stat == '1')
-			serv_got_typing(gc, from, 0, PURPLE_TYPING);
-		else
-			serv_got_typing_stopped(gc, from);
+		if(msn)	{
+			if (*stat == '1')
+				serv_got_typing(gc, msn_from, 0, PURPLE_TYPING);
+			else
+				serv_got_typing_stopped(gc, msn_from);
+		}
+		else	{
+			if (*stat == '1')
+				serv_got_typing(gc, from, 0, PURPLE_TYPING);
+			else
+				serv_got_typing_stopped(gc, from);
+		}
 	} else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) {
 		PurpleBuddy *bud = purple_find_buddy(account, from);
 
@@ -754,6 +817,7 @@
 		g_free(buf);
 	}
 
+	g_free(msn_from);
 }
 
 
@@ -765,7 +829,69 @@
 	char *msg;
 };
 
-static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt)
+static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	PurpleAccount *account;
+	GSList *l = pkt->hash;
+	struct _yahoo_im *sms = NULL;
+	struct yahoo_data *yd;
+	char *server_msg = NULL;
+	char *m;
+	
+	yd = gc->proto_data;	
+	account = purple_connection_get_account(gc);
+
+	while (l != NULL) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 4)	{
+			sms = g_new0(struct _yahoo_im, 1);
+			sms->from = g_strdup_printf("+%s", pair->value);
+			sms->time = time(NULL);
+			sms->utf8 = TRUE;
+		}
+		if (pair->key == 14) {
+			if (sms)
+				sms->msg = pair->value;
+		}
+		if (pair->key == 68)
+			if(sms)
+				g_hash_table_insert(yd->sms_carrier, g_strdup(sms->from), g_strdup(pair->value));
+		if (pair->key == 16)
+			server_msg = pair->value;
+		l = l->next;
+	}
+
+	if( (pkt->status == -1) || (pkt->status == YAHOO_STATUS_DISCONNECTED) )	{
+		if (server_msg)	{
+			PurpleConversation *c;
+			c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms->from, account);
+			if (c == NULL)
+				c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sms->from);
+			purple_conversation_write(c, NULL, server_msg, PURPLE_MESSAGE_SYSTEM, time(NULL));
+		}
+		else
+			purple_notify_error(gc, NULL, _("Your SMS was not delivered"), NULL);
+		
+		g_free(sms->from);
+		g_free(sms);
+		return ;
+	}
+
+	if (!sms->from || !sms->msg) {
+		g_free(sms);
+		return;
+	}
+
+	m = yahoo_string_decode(gc, sms->msg, sms->utf8);
+	serv_got_im(gc, sms->from, m, 0, sms->time);
+		
+	g_free(m);
+	g_free(sms->from);
+	g_free(sms);
+}
+
+/* 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;
@@ -773,13 +899,17 @@
 	GSList *list = NULL;
 	struct _yahoo_im *im = NULL;
 	const char *imv = NULL;
+	gint val_11 = 0;
+	gboolean msn = FALSE;
+	char *msn_from = NULL;
 
 	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 received 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;
@@ -799,6 +929,15 @@
 				if (im)
 					im->msg = pair->value;
 			}
+			if (pair->key == 241)	{
+				if(strtol(pair->value, NULL, 10) == 2)
+					msn = TRUE;
+			}
+			/* peer session id */
+			if (pair->key == 11) {
+				if (im)
+					val_11 = strtol(pair->value, NULL, 10);
+			}
 			/* IMV key */
 			if (pair->key == 63)
 			{
@@ -811,6 +950,17 @@
 		                  _("Your Yahoo! message did not get sent."), NULL);
 	}
 
+	if(msn)
+		msn_from = g_strconcat("msn/", im->from, 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)
@@ -847,6 +997,7 @@
 	for (l = list; l; l = l->next) {
 		YahooFriend *f;
 		char *m, *m2;
+		PurpleConversation *c;
 		im = l->data;
 
 		if (!im->from || !im->msg) {
@@ -869,35 +1020,54 @@
 		m = m2;
 		purple_util_chrreplace(m, '\r', '\n');
 
+		c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->from, account);
+		if ((c == NULL) && msn)
+			c=purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, msn_from, account);
+
 		if (!strcmp(m, "<ding>")) {
-			PurpleConversation *c;
 			char *username;
 
-			c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->from, account);
-			if (c == NULL)
-				c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, im->from);
-
-			username = g_markup_escape_text(im->from, -1);
+			if(c == NULL)	{
+				if(msn)
+					c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, msn_from);
+				else
+					c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, im->from);
+			}
+			if(msn)
+				username = g_markup_escape_text(msn_from, -1);
+			else
+				username = g_markup_escape_text(im->from, -1);
+			
 			purple_prpl_got_attention(gc, username, YAHOO_BUZZ);
 			g_free(username);
 			g_free(m);
 			g_free(im);
+			g_free(msn_from);
 			continue;
 		}
 
 		m2 = yahoo_codes_to_html(m);
 		g_free(m);
-		serv_got_im(gc, im->from, m2, 0, im->time);
+
+		if(msn)
+			serv_got_im(gc, msn_from, m2, 0, im->time);
+		else
+			serv_got_im(gc, im->from, m2, 0, im->time);
+
 		g_free(m2);
 
-		if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) {
-			if (yahoo_friend_get_buddy_icon_need_request(f)) {
-				yahoo_send_picture_request(gc, im->from);
-				yahoo_friend_set_buddy_icon_need_request(f, FALSE);
+		/* laters : implement buddy icon for msn friends */ 
+		if(!msn)	{
+			if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) {
+				if (yahoo_friend_get_buddy_icon_need_request(f)) {
+					yahoo_send_picture_request(gc, im->from);
+					yahoo_friend_set_buddy_icon_need_request(f, FALSE);
+				}
 			}
 		}
 
 		g_free(im);
+		g_free(msn_from);
 	}
 	g_slist_free(list);
 }
@@ -1030,12 +1200,14 @@
 	PurpleAccount *account;
 	GSList *l = pkt->hash;
 	const char *msg = NULL;
+	int protocol = 0;
 
 	account = purple_connection_get_account(gc);
 
 	/* Buddy authorized/declined our addition */
 	if (pkt->status == 1) {
-		const char *who = NULL;
+		char *temp = NULL;
+		char *who = NULL;
 		int response = 0;
 
 		while (l) {
@@ -1043,7 +1215,7 @@
 
 			switch (pair->key) {
 			case 4:
-				who = pair->value;
+				temp = pair->value;
 				break;
 			case 13:
 				response = strtol(pair->value, NULL, 10);
@@ -1051,10 +1223,18 @@
 			case 14:
 				msg = pair->value;
 				break;
+			case 241:
+				protocol = strtol(pair->value, NULL, 10);
+				break;
 			}
 			l = l->next;
 		}
 
+		if(protocol == 0)
+			who = g_strdup(temp);
+		else if(protocol == 2)
+			who = g_strconcat("msn/", temp, NULL);
+
 		if (response == 1) /* Authorized */
 			purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
 		else if (response == 2) { /* Declined */
@@ -1062,12 +1242,13 @@
 			yahoo_buddy_denied_our_add(gc, who, msg);
 		} else
 			purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)");
-
+	g_free(who);
 	}
 	/* Buddy requested authorization to add us. */
 	else if (pkt->status == 3) {
 		struct yahoo_add_request *add_req;
 		const char *firstname = NULL, *lastname = NULL;
+		char *temp = NULL;
 
 		add_req = g_new0(struct yahoo_add_request, 1);
 		add_req->gc = gc;
@@ -1077,6 +1258,7 @@
 
 			switch (pair->key) {
 			case 4:
+				temp = pair->value;
 				add_req->who = g_strdup(pair->value);
 				break;
 			case 5:
@@ -1098,6 +1280,10 @@
 			}
 			l = l->next;
 		}
+		if(add_req->protocol == 2)
+			add_req->who = g_strconcat("msn/", temp, NULL);
+		else
+			add_req->who = g_strdup(temp);
 
 		if (add_req->id && add_req->who) {
 			char *alias = NULL, *dec_msg = NULL;
@@ -2199,11 +2385,15 @@
 {
 	int err = 0;
 	char *who = NULL;
+	char *temp = NULL;
 	char *group = NULL;
 	char *decoded_group;
 	char *buf;
 	YahooFriend *f;
 	GSList *l = pkt->hash;
+	struct yahoo_data *yd = gc->proto_data;
+	int protocol = 0;
+	gboolean msn = FALSE;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
@@ -2213,24 +2403,48 @@
 			err = strtol(pair->value, NULL, 10);
 			break;
 		case 7:
-			who = pair->value;
+			temp = pair->value;
 			break;
 		case 65:
 			group = pair->value;
 			break;
+		case 241:
+			protocol = strtol(pair->value, NULL, 10);
+			if(protocol == 2)
+				msn = TRUE;
+			break;
 		}
 
 		l = l->next;
 	}
 
-	if (!who)
+	if (!temp)
 		return;
 	if (!group)
 		group = "";
+	
+	if(msn)
+		who = g_strconcat("msn/", temp, NULL);
+	else
+		who = g_strdup(temp);
 
 	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(protocol)
+			f->protocol = protocol;
+
+		if( !g_hash_table_lookup(yd->peers, who) )	{
+			/* we are not connected as client, so set friend to not connected */
+			if(msn)
+				yahoo_friend_set_p2p_status(f,YAHOO_P2PSTATUS_DO_NOT_CONNECT);
+			else	{
+				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;
 	}
 
@@ -2241,6 +2455,431 @@
 		purple_notify_error(gc, NULL, _("Could not add buddy to server list"), buf);
 	g_free(buf);
 	g_free(decoded_group);
+	g_free(who);
+}
+
+/* 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);
+}
+
+static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_data)
+{
+	struct yahoo_p2p_data *p2p_data = value;
+	PurpleConnection *gc = user_data;
+	struct yahoo_packet *pkt_to_send;
+	PurpleAccount *account;
+	struct yahoo_data *yd = gc->proto_data;
+
+	account = purple_connection_get_account(gc);
+
+	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, 7);
+	yahoo_p2p_write_pkt(p2p_data->source, pkt_to_send);
+
+	yahoo_packet_free(pkt_to_send);
+}
+
+static gboolean yahoo_p2p_keepalive(gpointer data)
+{
+	PurpleConnection *gc = data;
+	struct yahoo_data *yd = gc->proto_data;
+
+	g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc);
+
+	return TRUE;
+}
+
+/* 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);
+}
+
+/* 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;
+	YahooFriend *f;
+
+	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 : if( g_hash_table_lookup(yd->peers, p2p_data->host_username) )
+				return;
+			 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);
+
+	if( val_13_to_send == 7 )
+		if( !g_hash_table_lookup(yd->peers, p2p_data->host_username) )	{
+			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)	{
+				if(p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER)	{
+					p2p_data->session_id = f->session_id;
+					yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER);
+				}
+				else
+					yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
+			}
+		}
+}
+
+/* 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 */
+		if( g_hash_table_lookup(yd->peers, p2p_data->host_username) )
+			g_hash_table_remove(yd->peers,p2p_data->host_username);
+		else
+			yahoo_p2p_disconnect_destroy_data(data);
+		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;
+	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 timeout */
+	purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
+	yd->yahoo_p2p_server_timeout_handle = 0;
+
+	/* 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;
+
+	/* 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;
+}
+
+static gboolean yahoo_cancel_p2p_server_listen_cb(gpointer data)
+{
+	struct yahoo_p2p_data *p2p_data;
+	struct yahoo_data *yd;
+
+	if(!(p2p_data = data))
+		return FALSE;
+
+	yd = p2p_data->gc->proto_data;
+
+	purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect");
+	yahoo_p2p_disconnect_destroy_data(data);
+	purple_input_remove(yd->yahoo_p2p_server_watcher);
+	yd->yahoo_p2p_server_watcher = 0;
+	close(yd->yahoo_local_p2p_server_fd);
+	yd->yahoo_local_p2p_server_fd = -1;
+	yd->yahoo_p2p_server_timeout_handle = 0;
+
+	return FALSE;
+}
+
+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);
+
+	/* add timeout */
+	yd->yahoo_p2p_server_timeout_handle = purple_timeout_add_seconds(YAHOO_P2P_SERVER_TIMEOUT, yahoo_cancel_p2p_server_listen_cb, data);
+}
+
+/* send p2p pkt containing our encoded ip, asking peer to connect to us */
+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;
+
+	f = yahoo_friend_find(gc, who);
+	account = purple_connection_get_account(gc);
+
+	/* Do not send invitation if already listening for other connection */
+	if(yd->yahoo_local_p2p_server_fd >= 0)
+		return;
+
+	/* One shouldn't try to connect to self */
+	if( strcmp(purple_normalize(account, purple_account_get_username(account)), who) == 0)
+		return;
+
+	/* 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;
+
+	/* Dont send p2p packet to buddies of other protocols */
+	if(f->protocol)
+		return;
+
+	/* Finally, don't try to connect to buddies not online or on sms */
+	if( (f->status == YAHOO_STATUS_OFFLINE) || f->sms )
+		return;
+
+	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) );
+
+	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 = g_new0(struct yahoo_p2p_data, 1);
+
+	p2p_data->gc = gc;
+	p2p_data->host_ip = NULL;
+	p2p_data->host_username = g_strdup(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;
+	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;
+
+	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)
@@ -2250,6 +2889,14 @@
 	char *base64 = NULL;
 	guchar *decoded;
 	gsize len;
+	gint val_13 = 0;
+	gint val_11 = 0;
+	PurpleAccount *account;
+	YahooFriend *f;
+
+	/* if status is not 1 ie YAHOO_STATUS_BRB, the packet bounced back, so contains our own ip */
+	if(!(pkt->status == YAHOO_STATUS_BRB))
+		return ;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
@@ -2269,14 +2916,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
 		*/
 
 		}
@@ -2288,6 +2942,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) {
@@ -2300,12 +2956,34 @@
 		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 = g_strdup(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);
+		}
 	}
 }
 
@@ -2385,12 +3063,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);
@@ -2467,7 +3145,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;
@@ -2501,6 +3180,9 @@
 	case YAHOO_SERVICE_FILETRANS_ACC_15:
 		yahoo_process_filetrans_acc_15(gc, pkt);
 		break;
+	case YAHOO_SERVICE_SMS_MSG:
+		yahoo_process_sms_message(gc, pkt);
+		break;
 
 	default:
 		purple_debug(PURPLE_DEBUG_ERROR, "yahoo",
@@ -3016,6 +3698,7 @@
 
 	purple_connection_set_display_name(gc, purple_account_get_username(account));
 
+	yd->yahoo_local_p2p_server_fd = -1;
 	yd->fd = -1;
 	yd->txhandler = 0;
 	/* TODO: Is there a good grow size for the buffer? */
@@ -3023,6 +3706,9 @@
 	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->sms_carrier = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	yd->yahoo_p2p_timer = purple_timeout_add_seconds(YAHOO_P2P_KEEPALIVE_SECS, yahoo_p2p_keepalive, gc);
 	yd->confs = NULL;
 	yd->conf_id = 2;
 	yd->last_keepalive = yd->last_ping = time(NULL);
@@ -3088,6 +3774,17 @@
 	if (yd->in_chat)
 		yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */
 
+	purple_timeout_remove(yd->yahoo_p2p_timer);
+	if(yd->yahoo_p2p_server_timeout_handle != 0)
+		purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
+
+	/* close p2p server if it is waiting for a peer to connect */
+	purple_input_remove(yd->yahoo_p2p_server_watcher);
+	close(yd->yahoo_local_p2p_server_fd);
+	yd->yahoo_local_p2p_server_fd = -1;
+
+	g_hash_table_destroy(yd->sms_carrier);
+	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);
@@ -3636,10 +4333,129 @@
 	return m;
 }
 
+struct yahoo_sms_carrier_cb_data	{
+	PurpleConnection *gc;
+	char *who;
+	char *what;
+};
+
+static int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags);
+
+static void yahoo_get_sms_carrier_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
+		const gchar *webdata, size_t len, const gchar *error_message)
+{
+	struct yahoo_sms_carrier_cb_data *sms_cb_data = user_data;
+	PurpleConnection *gc = sms_cb_data->gc;
+	struct yahoo_data *yd = gc->proto_data;
+	char *mobile_no = NULL;
+	char *status = NULL;
+	char *carrier = NULL;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account);
+
+	if (error_message != NULL)	{
+		purple_conversation_write(conv, NULL, "Cant send SMS, Unable to obtain mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+		g_free(sms_cb_data->who);
+		g_free(sms_cb_data->what);
+		g_free(sms_cb_data);
+		return ;
+	}
+	else if (len > 0 && webdata && *webdata) {
+		xmlnode *validate_data_root = xmlnode_from_str(webdata, -1);
+		xmlnode *validate_data_child = xmlnode_get_child(validate_data_root, "mobile_no");
+		mobile_no = (char *)xmlnode_get_attrib(validate_data_child, "msisdn");
+		
+		validate_data_root = xmlnode_copy(validate_data_child);
+		validate_data_child = xmlnode_get_child(validate_data_root, "status");
+		status = xmlnode_get_data(validate_data_child);
+
+		validate_data_child = xmlnode_get_child(validate_data_root, "carrier");
+		carrier = xmlnode_get_data(validate_data_child);
+
+		purple_debug_info("yahoo","SMS validate data: Mobile:%s, Status:%s, Carrier:%s\n", mobile_no, status, carrier);
+
+		if( strcmp(status, "Valid") == 0)	{
+			g_hash_table_insert(yd->sms_carrier, g_strdup_printf("+%s", mobile_no), g_strdup(carrier));
+			yahoo_send_im(sms_cb_data->gc, sms_cb_data->who, sms_cb_data->what, PURPLE_MESSAGE_SEND);
+		}
+		else	{
+			g_hash_table_insert(yd->sms_carrier, g_strdup_printf("+%s", mobile_no), g_strdup("Unknown"));
+			purple_conversation_write(conv, NULL, "Cant send SMS, Unknown mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
+		}
+
+		xmlnode_free(validate_data_child);
+		xmlnode_free(validate_data_root);
+		g_free(sms_cb_data->who);
+		g_free(sms_cb_data->what);
+		g_free(sms_cb_data);
+		g_free(mobile_no);
+		g_free(status);
+		g_free(carrier);
+	}
+}
+
+static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	PurpleUtilFetchUrlData *url_data;
+	struct yahoo_sms_carrier_cb_data *sms_cb_data;
+	char *validate_request_str = NULL;
+	char *request = NULL;
+	gboolean use_whole_url = FALSE;
+	xmlnode *validate_request_root = NULL;
+	xmlnode *validate_request_child = NULL;
+
+	if(!(sms_cb_data = data))
+		return;
+
+	validate_request_root = xmlnode_new("validate");
+	xmlnode_set_attrib(validate_request_root, "intl", "us");
+	xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION);
+	xmlnode_set_attrib(validate_request_root, "qos", "0");
+
+	validate_request_child = xmlnode_new_child(validate_request_root, "mobile_no");
+	xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1);
+
+	validate_request_str = xmlnode_to_str(validate_request_root, NULL);
+
+	xmlnode_free(validate_request_child);
+	xmlnode_free(validate_request_root);
+
+	request = g_strdup_printf(
+		"POST /mobileno?intl=us&version=%s HTTP/1.1\r\n"
+		"Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s; path=/; domain=.yahoo.com;\r\n"
+		"User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n"
+		"Host: validate.msg.yahoo.com\r\n"
+		"Content-Length: %d\r\n"
+		"Cache-Control: no-cache\r\n\r\n%s",
+		YAHOO_CLIENT_VERSION, yd->cookie_t, yd->cookie_y, strlen(validate_request_str), validate_request_str);
+
+	/* use whole URL if using HTTP Proxy */
+	if ((gc->account->proxy_info) && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP))
+	    use_whole_url = TRUE;
+
+	url_data = purple_util_fetch_url_request(YAHOO_SMS_CARRIER_URL, use_whole_url,
+			"Mozilla/4.0 (compatible; MSIE 5.5)", TRUE, request, FALSE,
+			yahoo_get_sms_carrier_cb, data);
+
+	g_free(request);
+	g_free(validate_request_str);
+
+	if (!url_data)	{
+		PurpleAccount *account = purple_connection_get_account(gc);
+		PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account);
+		purple_conversation_write(conv, NULL, "Cant send SMS, Unable to obtain mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
+		g_free(sms_cb_data->who);
+		g_free(sms_cb_data->what);
+		g_free(sms_cb_data);
+	}
+}
+
 static int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
 {
 	struct yahoo_data *yd = gc->proto_data;
-	struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, 0);
+	struct yahoo_packet *pkt = NULL;
 	char *msg = yahoo_html_to_codes(what);
 	char *msg2;
 	gboolean utf8 = TRUE;
@@ -3648,7 +4464,8 @@
 	YahooFriend *f = NULL;
 	gsize lenb = 0;
 	glong lenc = 0;
-
+	struct yahoo_p2p_data *p2p_data;
+	gboolean msn = FALSE;
 	msg2 = yahoo_string_encode(gc, msg, &utf8);
 	
 	if(msg2) {
@@ -3667,9 +4484,67 @@
 		}
 	}
 
-	yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who);
-	if ((f = yahoo_friend_find(gc, who)) && f->protocol)
-		yahoo_packet_hash_int(pkt, 241, f->protocol);
+	msn = g_str_has_prefix(who, "msn/") || g_str_has_prefix(who, "MSN/");
+
+	if( strncmp(who, "+", 1) == 0 )	{
+		/* we have an sms to be sent */
+		gchar *carrier = NULL;
+		const char *alias = NULL;
+		PurpleAccount *account = purple_connection_get_account(gc);
+		PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account);
+
+		carrier = g_hash_table_lookup(yd->sms_carrier, who);
+		if (!carrier)	{
+			struct yahoo_sms_carrier_cb_data *sms_cb_data;
+			sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data));
+			sms_cb_data->gc = gc;
+			sms_cb_data->who = g_malloc(strlen(who));
+			sms_cb_data->what = g_malloc(strlen(what));
+			strcpy(sms_cb_data->who, who);
+			strcpy(sms_cb_data->what, what);
+
+			purple_conversation_write(conv, NULL, "Getting mobile carrier to send the sms", PURPLE_MESSAGE_SYSTEM, time(NULL));
+			
+			yahoo_get_sms_carrier(gc, sms_cb_data);
+
+			g_free(msg);
+			g_free(msg2);
+			return ret;
+		}
+		else if( strcmp(carrier,"Unknown") == 0 ) {
+			purple_conversation_write(conv, NULL, "Cant send SMS, Unknown mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+			g_free(msg);
+			g_free(msg2);
+			return -1;
+		}
+
+		alias = purple_account_get_alias(account);
+		pkt = yahoo_packet_new(YAHOO_SERVICE_SMS_MSG, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash(pkt, "sssss",
+			1, purple_connection_get_display_name(gc),
+			69, alias,
+			5, who + 1,
+			68, carrier,
+			14, msg2);
+		yahoo_packet_send_and_free(pkt, yd);
+
+		g_free(msg);
+		g_free(msg2);
+
+		return ret;
+	}
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, 0);
+	if(msn)	{
+		yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who+4);
+		yahoo_packet_hash_int(pkt, 241, 2);
+	}
+	else	{
+		yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who);
+		if ((f = yahoo_friend_find(gc, who)) && f->protocol)
+			yahoo_packet_hash_int(pkt, 241, f->protocol);
+	}
 
 	if (utf8)
 		yahoo_packet_hash_str(pkt, 97, "1");
@@ -3708,8 +4583,18 @@
 		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)) && !msn )	{
+			yahoo_packet_hash_int(pkt, 11, p2p_data->session_id);
+			yahoo_p2p_write_pkt(p2p_data->source, pkt);
+		}
+		else	{
+			yahoo_packet_send(pkt, yd);
+			if(!msn)
+				yahoo_send_p2p_pkt(gc, who, 0);		/* send p2p packet, with val_13=0 */
+		}
+	}
 	else
 		ret = -E2BIG;
 
@@ -3724,12 +4609,35 @@
 static unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
 {
 	struct yahoo_data *yd = gc->proto_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),
+	struct yahoo_p2p_data *p2p_data;
+	gboolean msn = (g_str_has_prefix(who, "msn/") || g_str_has_prefix(who, "MSN/"));
+	struct yahoo_packet *pkt = NULL;
+
+	/* Don't do anything if sms is being typed */
+	if( strncmp(who, "+", 1) == 0 )
+		return 0;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, 0);
+
+	/* check to see if p2p link exists, send through it */
+	if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !msn )	{
+		yahoo_packet_hash(pkt, "sssssis", 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);
+	                  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 */
+		if(msn)
+			yahoo_packet_hash(pkt, "sssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
+	                  14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
+	                  5, who+4, 1002, "1", 241, "2");
+		else
+			yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc),
+	                  14, " ", 13, state == PURPLE_TYPING ? "1" : "0",
+	                  5, who+4, 1002, "1");
+		yahoo_packet_send_and_free(pkt, yd);
+	}
 
 	return 0;
 }
@@ -3975,6 +4883,7 @@
 	char *group2;
 	YahooFriend *f;
 	const char *bname;
+	gboolean msn = FALSE;
 
 	if (!yd->logged_in)
 		return;
@@ -3984,6 +4893,7 @@
 		return;
 
 	f = yahoo_friend_find(gc, bname);
+	msn = g_str_has_prefix(bname, "msn/") || g_str_has_prefix(bname, "MSN/");
 
 	g = purple_buddy_get_group(buddy);
 	if (g)
@@ -3993,20 +4903,38 @@
 
 	group2 = yahoo_string_encode(gc, group, NULL);
 	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "ssssssssss",
-	                  14, "",
-	                  65, group2,
-	                  97, "1",
-	                  1, purple_connection_get_display_name(gc),
-	                  302, "319",
-	                  300, "319",
-	                  7, bname,
-	                  334, "0",
-	                  301, "319",
-	                  303, "319"
-	);
-	if (f && f->protocol)
+	if(msn)	{
+		yahoo_packet_hash(pkt, "sssssssssss",
+			14, "",
+			65, group2,
+			97, "1",
+			1, purple_connection_get_display_name(gc),
+			302, "319",
+			300, "319",
+			7, bname + 4,
+			241, "2",
+			334, "0",
+			301, "319",
+			303, "319"
+		);
+	}
+	else	{
+		yahoo_packet_hash(pkt, "ssssssssss",
+			14, "",
+			65, group2,
+			97, "1",
+			1, purple_connection_get_display_name(gc),
+			302, "319",
+			300, "319",
+	                7, bname,
+			334, "0",
+			301, "319",
+			303, "319"
+		);
+	}
+	if (f && f->protocol && !msn)
 		yahoo_packet_hash_int(pkt, 241, f->protocol);
+
 	yahoo_packet_send_and_free(pkt, yd);
 	g_free(group2);
 }
@@ -4020,13 +4948,18 @@
 	gboolean remove = TRUE;
 	char *cg;
 	const char *bname, *gname;
+	YahooFriend *f = NULL;
+	gboolean msn = FALSE;
 
 	bname = purple_buddy_get_name(buddy);
-	if (!(yahoo_friend_find(gc, bname)))
+	f = yahoo_friend_find(gc, bname);
+	if (!f)
 		return;
 
 	gname = purple_group_get_name(group);
 	buddies = purple_find_buddies(purple_connection_get_account(gc), bname);
+	if(f->protocol == 2)
+		msn = TRUE;
 	for (l = buddies; l; l = l->next) {
 		g = purple_buddy_get_group(l->data);
 		if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) {
@@ -4042,8 +4975,15 @@
 
 	cg = yahoo_string_encode(gc, gname, NULL);
 	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
+
+	if(msn)
+		yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
+	                  7, bname+4, 65, cg);
+	else
+		yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
 	                  7, bname, 65, cg);
+	if(f->protocol)
+		yahoo_packet_hash_int(pkt, 241, f->protocol);
 	yahoo_packet_send_and_free(pkt, yd);
 	g_free(cg);
 }
@@ -4116,13 +5056,22 @@
 	struct yahoo_data *yd = gc->proto_data;
 	struct yahoo_packet *pkt;
 	char *gpn, *gpo;
+	YahooFriend *f = yahoo_friend_find(gc, who);
+	gboolean msn = FALSE;
+	const char *temp = NULL;
 
 	/* Step 0:  If they aren't on the server list anyway,
 	 *          don't bother letting the server know.
 	 */
-	if (!yahoo_friend_find(gc, who))
+	if (!f)
 		return;
 
+	if(f->protocol == 2)	{
+		msn = TRUE;
+		temp = who+4;
+	} else
+		temp = who;
+
 	/* If old and new are the same, we would probably
 	 * end up deleting the buddy, which would be bad.
 	 * This might happen because of the charset conversation.
@@ -4136,8 +5085,13 @@
 	}
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc),
-	                  302, "240", 300, "240", 7, who, 224, gpo, 264, gpn, 301,
+	if(f->protocol)
+		yahoo_packet_hash(pkt, "ssssissss", 1, purple_connection_get_display_name(gc),
+	                  302, "240", 300, "240", 7, temp, 241, f->protocol, 224, gpo, 264, gpn, 301,
+	                  "240", 303, "240");
+	else
+		yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc),
+	                  302, "240", 300, "240", 7, temp, 224, gpo, 264, gpn, 301,
 	                  "240", 303, "240");
 	yahoo_packet_send_and_free(pkt, yd);
 
@@ -4349,10 +5303,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;
 	}
@@ -4366,7 +5320,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(); */
 
@@ -4436,7 +5390,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	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Sun Mar 08 20:38:09 2009 +0000
@@ -30,6 +30,9 @@
 
 #define YAHOO_PAGER_HOST "scs.msg.yahoo.com"
 #define YAHOO_PAGER_PORT 5050
+#define YAHOO_PAGER_PORT_P2P 5101
+#define YAHOO_P2P_KEEPALIVE_SECS 300
+#define YAHOO_P2P_SERVER_TIMEOUT 10
 #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 +48,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/"
@@ -55,6 +58,8 @@
 
 #define WEBMESSENGER_URL "http://login.yahoo.com/config/login?.src=pg"
 
+#define YAHOO_SMS_CARRIER_URL "http://lookup.msg.vip.mud.yahoo.com"
+
 #define YAHOO_PICURL_SETTING "picture_url"
 #define YAHOO_PICCKSUM_SETTING "picture_checksum"
 #define YAHOO_PICEXPIRE_SETTING "picture_expire"
@@ -80,10 +85,19 @@
 #define YAHOOJP_CLIENT_VERSION_ID "524223"
 #define YAHOOJP_CLIENT_VERSION "7,0,1,3"
 
-
 /* 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 +127,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,8 +193,8 @@
 	 * 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;
@@ -178,6 +203,12 @@
 	char *current_list15_grp;
 	time_t last_ping;
 	time_t last_keepalive;
+	GHashTable *peers;	/* information about p2p data */
+	int yahoo_p2p_timer;
+	int yahoo_local_p2p_server_fd;
+	int yahoo_p2p_server_watcher;
+	GHashTable *sms_carrier;	/* sms carrier data */
+	guint yahoo_p2p_server_timeout_handle;
 };
 
 #define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255)
@@ -265,4 +296,7 @@
 gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type);
 GList *yahoo_attention_types(PurpleAccount *account);
 
+/* send p2p pkt containing our encoded ip, asking peer to connect to us */
+void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13);
+
 #endif /* _YAHOO_H_ */
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Sun Mar 08 20:38:09 2009 +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;
@@ -605,6 +614,26 @@
 	xfer->data = NULL;
 }
 
+/* Send HTTP OK after receiving file */
+static void yahoo_p2p_ft_server_send_OK(PurpleXfer *xfer)
+{
+	char *tx = NULL;
+	int written;
+
+	tx = g_strdup_printf("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nContent-Type: application/octet-stream\r\nConnection: close\r\n\r\n");
+	written = write(xfer->fd, tx, strlen(tx));
+
+	if (written < 0 && errno == EAGAIN)
+		written = 0;
+	else if (written <= 0)
+		purple_debug_info("yahoo", "p2p filetransfer: Unable to write HTTP OK");
+
+	/* close connection */	
+	close(xfer->fd);
+	xfer->fd = -1;
+	g_free(tx);
+}
+
 static void yahoo_xfer_end(PurpleXfer *xfer_old)
 {
 	struct yahoo_xfer_data *xfer_data;
@@ -616,6 +645,10 @@
 	if(xfer_data && xfer_data->version == 15
 	   && purple_xfer_get_type(xfer_old) == PURPLE_XFER_RECEIVE
 	   && xfer_data->filename_list) {
+
+		/* Send HTTP OK in case of p2p transfer, when we act as server */
+		if((xfer_data->xfer_url != NULL) && (xfer_old->fd >=0) && (purple_xfer_get_status(xfer_old) == PURPLE_XFER_STATUS_DONE))
+			yahoo_p2p_ft_server_send_OK(xfer_old);
 		
 		/* removing top of filename & size list completely */
 		g_free( xfer_data->filename_list->data );
@@ -689,7 +722,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);
 
@@ -985,7 +1018,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;
@@ -1035,34 +1068,24 @@
 	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;
 	struct yahoo_data *yd = gc->proto_data;
-	int ver = 0;
 	PurpleXfer *xfer = yahoo_new_xfer(gc, who);
-	YahooFriend *yf = yahoo_friend_find(gc, who);
-
-	/* 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; 
-
-	/* recent yahoo japan --yaz */
-	if(yd->jp && yf && yf->version_id > 500000)
-		ver = 15;
 
 	g_return_if_fail(xfer != NULL);
 
-	if(ver == 15) {
-		xfer_data = xfer->data;
-		xfer_data->status_15 = STARTED;
-		purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15);
-		xfer_data->version = 15;
-		xfer_data->xfer_peer_idstring = yahoo_xfer_new_xfer_id();
-		g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer);
-	}
+	/* if we don't have a p2p connection, try establishing it now */
+	if( !g_hash_table_lookup(yd->peers, who) )
+		yahoo_send_p2p_pkt(gc, who, 0);
+
+	xfer_data = xfer->data;
+	xfer_data->status_15 = STARTED;
+	purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15);
+	xfer_data->version = 15;
+	xfer_data->xfer_peer_idstring = yahoo_xfer_new_xfer_id();
+	g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer);
 
 	/* Now perform the request */
 	if (file)
@@ -1071,7 +1094,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;
@@ -1111,7 +1136,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)
@@ -1160,7 +1185,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);
@@ -1171,21 +1196,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;
@@ -1212,31 +1249,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
 		{
@@ -1257,6 +1324,225 @@
 	}
 }
 
+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 *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));
+		filename_without_spaces = g_strdup(filename);
+		purple_util_chrreplace(filename_without_spaces, ' ', '+');
+		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_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;
@@ -1302,14 +1588,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 */
 
 		}
 	}
@@ -1337,6 +1623,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,
@@ -1350,7 +1644,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.
@@ -1366,7 +1660,7 @@
 			return;
 		}
 	}
-	/*end processing*/
+	/* end processing */
 
 	if(!filename_list)
 		return;
@@ -1441,6 +1735,7 @@
 	GSList *l;
 	struct yahoo_packet *pkt_to_send;
 	PurpleAccount *account;
+	struct yahoo_p2p_data *p2p_data;
 
 	yd = gc->proto_data;
 
@@ -1464,13 +1759,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;
@@ -1498,35 +1788,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;
@@ -1537,6 +1840,8 @@
 	GSList *l;
 	PurpleAccount *account;
 	long val_66 = 0;
+	gchar *url = NULL;
+	int val_249 = 0;
 
 	yd = gc->proto_data;
 	for (l = pkt->hash; l; l = l->next) {
@@ -1551,19 +1856,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	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_friend.c	Sun Mar 08 20:38:09 2009 +0000
@@ -147,24 +147,36 @@
 {
 	GSList *l = pkt->hash;
 	YahooFriend *f;
+	char *temp = NULL;
 	char *who = NULL;
 	int value = 0;
+	int protocol = 0;
+	gboolean msn = FALSE;
 
 	while (l) {
 		struct yahoo_pair *pair = l->data;
 
 		switch (pair->key) {
 			case 7:
-				who = pair->value;
+				temp = pair->value;
 				break;
 			case 31:
 				value = strtol(pair->value, NULL, 10);
 				break;
+			case 241:
+				protocol = strtol(pair->value, NULL, 10);
+				msn = TRUE;
+				break;
 		}
 
 		l = l->next;
 	}
 
+	if(msn)
+		who = g_strconcat("msn/", temp, NULL);
+	else
+		who = g_strdup(temp);
+
 	if (value != 1 && value != 2) {
 		purple_debug_error("yahoo", "Received unknown value for presence key: %d\n", value);
 		return;
@@ -173,8 +185,10 @@
 	g_return_if_fail(who != NULL);
 
 	f = yahoo_friend_find(gc, who);
-	if (!f)
+	if (!f)	{
+		g_free(who);
 		return;
+	}
 
 	if (pkt->service == YAHOO_SERVICE_PRESENCE_PERM) {
 		purple_debug_info("yahoo", "Setting permanent presence for %s to %d.\n", who, (value == 1));
@@ -194,6 +208,7 @@
 		else
 			f->presence = YAHOO_PRESENCE_DEFAULT;
 	}
+	g_free(who);
 }
 
 void yahoo_friend_update_presence(PurpleConnection *gc, const char *name,
@@ -204,6 +219,7 @@
 	YahooFriend *f;
 	const char *thirtyone, *thirteen;
 	int service = -1;
+	const char *temp = NULL;
 
 	if (!yd->logged_in)
 		return;
@@ -212,6 +228,11 @@
 	if (!f)
 		return;
 
+	if(f->protocol == 2)
+		temp = name+4;
+	else
+		temp = name;
+
 	/* No need to change the value if it is already correct */
 	if (f->presence == presence) {
 		purple_debug_info("yahoo", "Not setting presence because there are no changes.\n");
@@ -236,12 +257,21 @@
 		if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) {
 			pkt = yahoo_packet_new(YAHOO_SERVICE_PRESENCE_PERM,
 					YAHOO_STATUS_AVAILABLE, yd->session_id);
-			yahoo_packet_hash(pkt, "ssssssss",
+			if(f->protocol)
+				yahoo_packet_hash(pkt, "ssssssiss",
 					1, purple_connection_get_display_name(gc),
 					31, "2", 13, "2",
 					302, "319", 300, "319",
-					7, name,
+					7, temp, 241, f->protocol,
 					301, "319", 303, "319");
+			else
+				yahoo_packet_hash(pkt, "ssssssss",
+					1, purple_connection_get_display_name(gc),
+					31, "2", 13, "2",
+					302, "319", 300, "319",
+					7, temp,
+					301, "319", 303, "319");
+
 			yahoo_packet_send_and_free(pkt, yd);
 		}
 
@@ -254,13 +284,31 @@
 		pkt = yahoo_packet_new(service,
 				YAHOO_STATUS_AVAILABLE, yd->session_id);
 
-		yahoo_packet_hash(pkt, "ssssssss",
+		if(f->protocol)
+			yahoo_packet_hash(pkt, "ssssssiss",
 				1, purple_connection_get_display_name(gc),
 				31, thirtyone, 13, thirteen,
 				302, "319", 300, "319",
-				7, name,
+				7, temp, 241, f->protocol,
+				301, "319", 303, "319");
+		else
+			yahoo_packet_hash(pkt, "ssssssss",
+				1, purple_connection_get_display_name(gc),
+				31, thirtyone, 13, thirteen,
+				302, "319", 300, "319",
+				7, temp,
 				301, "319", 303, "319");
 
 		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	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_friend.h	Sun Mar 08 20:38:09 2009 +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	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.h	Sun Mar 08 20:38:09 2009 +0000
@@ -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 {
--- a/libpurple/purple-url-handler	Tue Mar 03 08:03:11 2009 +0000
+++ b/libpurple/purple-url-handler	Sun Mar 08 20:38:09 2009 +0000
@@ -207,11 +207,7 @@
 
     def correct_server(account):
         username = cpurple.PurpleAccountGetUsername(account)
-        user_split = (username.split("@"))
-        # Not all accounts have a split, so append an empty string so the
-        # [1] doesn't throw an IndexError.
-        user_split.append("")
-        return (server == user_split[1])
+        return ("@" in username) and (server == (username.split("@"))[1])
 
     account = findaccount(protocol, matcher=correct_server)
 
--- a/pidgin/gtkblist.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/pidgin/gtkblist.c	Sun Mar 08 20:38:09 2009 +0000
@@ -4002,7 +4002,7 @@
 	} else if (!purple_presence_is_online(presence)) {
 		if (theme)
 			pair = pidgin_blist_theme_get_offline_text_info(theme);
-		name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black";
+		name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL;
 		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
 
 		if (theme)
@@ -4013,7 +4013,7 @@
 	} else if (purple_presence_is_available(presence)) {
 		if (theme)
 			pair = pidgin_blist_theme_get_online_text_info(theme);
-		name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black";
+		name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL;
 		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
 
 		if (theme)
@@ -4024,7 +4024,7 @@
 	} else {
 		if (theme)
 			pair = pidgin_blist_theme_get_away_text_info(theme);
-		name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black";
+		name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL;
 		name_font = (pair != NULL && pair->font != NULL) ? pair->font : "";
 
 		if (theme)
@@ -4034,23 +4034,49 @@
 	}
 
 	if (aliased && selected) {
-		name_color = "black";
-		status_color = "black";
+		if (theme) {
+			name_color = "black";
+			status_color = "black";
+		} else {
+			name_color = NULL;
+			status_color = NULL;
+		}
 	}
 
 	/* Put it all together */
 	if (aliased && biglist && (statustext || idletime)) {
 		/* using <span size='smaller'> breaks the status, so it must be seperated into <small><span>*/
-		text = g_strdup_printf("<span font_desc='%s' foreground='%s'>%s</span>\n"
-				 	"<small><span font_desc='%s' foreground='%s'>%s%s%s</span></small>",
-					name_font, name_color, nametext, status_font, status_color,
-					idletime != NULL ? idletime : "",
-				        (idletime != NULL && statustext != NULL) ? " - " : "",
-				        statustext != NULL ? statustext : "");
-
-	} else
-		text = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>", name_font, name_color, nametext);
-
+		if (name_color) {
+			text = g_strdup_printf("<span font_desc='%s' foreground='%s'>%s</span>\n"
+				 		"<small><span font_desc='%s' foreground='%s'>%s%s%s</span></small>",
+						name_font, name_color, nametext, status_font, status_color,
+						idletime != NULL ? idletime : "",
+				    		(idletime != NULL && statustext != NULL) ? " - " : "",
+				    		statustext != NULL ? statustext : "");
+		} else if (status_color) {
+			text = g_strdup_printf("<span font_desc='%s'>%s</span>\n"
+				 		"<small><span font_desc='%s' foreground='%s'>%s%s%s</span></small>",
+						name_font, nametext, status_font, status_color,
+						idletime != NULL ? idletime : "",
+				    		(idletime != NULL && statustext != NULL) ? " - " : "",
+				    		statustext != NULL ? statustext : "");
+		} else {
+			text = g_strdup_printf("<span font_desc='%s'>%s</span>\n"
+				 		"<small><span font_desc='%s'>%s%s%s</span></small>",
+						name_font, nametext, status_font,
+						idletime != NULL ? idletime : "",
+				    		(idletime != NULL && statustext != NULL) ? " - " : "",
+				    		statustext != NULL ? statustext : "");
+		}
+	} else {
+		if (name_color) {
+			text = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>", 
+				name_font, name_color, nametext);
+		} else {
+			text = g_strdup_printf("<span font_desc='%s'>%s</span>", name_font,
+				nametext);
+		}
+	}
 	g_free(nametext);
 	g_free(statustext);
 	g_free(idletime);
@@ -4818,8 +4844,9 @@
 #define SSL_FAQ_URI "http://d.pidgin.im/wiki/FAQssl"
 
 static void
-ssl_faq_clicked_cb(GtkButton *button,
-                   PurpleAccount *account)
+ssl_faq_clicked_cb(PidginMiniDialog *mini_dialog,
+                   GtkButton *button,
+                   gpointer ignored)
 {
 	purple_notify_uri(NULL, SSL_FAQ_URI);
 }
@@ -4852,25 +4879,9 @@
 	g_object_set_data(G_OBJECT(mini_dialog), OBJECT_DATA_KEY_ACCOUNT,
 		account);
 
-	if(err->type == PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT) {
-		GtkWidget *faq_button = gtk_button_new();
-		GtkWidget *faq_label = gtk_label_new(NULL);
-		gtk_label_set_markup(GTK_LABEL(faq_label),
-			"<span underline=\"single\" foreground=\"blue\""
-			" size=\"smaller\">" SSL_FAQ_URI "</span>");
-#if GTK_CHECK_VERSION(2,6,0)
-		g_object_set(G_OBJECT(faq_label), "ellipsize",
-			PANGO_ELLIPSIZE_MIDDLE, NULL);
-#endif
-		gtk_container_add(GTK_CONTAINER(faq_button), faq_label);
-		gtk_button_set_relief(GTK_BUTTON(faq_button), GTK_RELIEF_NONE);
-
-		g_signal_connect(faq_button, "clicked",
-			(GCallback)ssl_faq_clicked_cb, account);
-
-		gtk_box_pack_start(PIDGIN_MINI_DIALOG(mini_dialog)->contents,
-			faq_button, FALSE, FALSE, 0);
-	}
+	 if(err->type == PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT)
+		pidgin_mini_dialog_add_button(PIDGIN_MINI_DIALOG(mini_dialog),
+				_("SSL FAQs"), ssl_faq_clicked_cb, NULL);
 
 	g_signal_connect_after(mini_dialog, "destroy",
 		(GCallback)generic_error_destroy_cb,
@@ -6165,12 +6176,17 @@
 		pair = pidgin_blist_theme_get_collapsed_text_info(theme);
 
 
-	text_color = (selected || pair == NULL || pair->color == NULL) ? "black" : pair->color;
+	text_color = (selected || pair == NULL || pair->color == NULL) ? NULL : pair->color;
 	text_font = (pair == NULL || pair->font == NULL) ? "" : pair->font;
 
 	esc = g_markup_escape_text(group->name, -1);
-	mark = g_strdup_printf("<span foreground='%s' font_desc='%s'><b>%s</b>%s</span>",
+	if (text_color) {
+		mark = g_strdup_printf("<span foreground='%s' font_desc='%s'><b>%s</b>%s</span>",
 							text_color, text_font, esc ? esc : "", group_count);
+	} else {
+		mark = g_strdup_printf("<span font_desc='%s'><b>%s</b>%s</span>",
+							text_font, esc ? esc : "", group_count);
+	}
 
 	g_free(esc);
 	return mark;
@@ -6231,10 +6247,17 @@
 			if (!selected && theme != NULL && (pair = pidgin_blist_theme_get_idle_text_info(theme)) != NULL && pair->color != NULL)
 				textcolor = pair->color;
 			else
-				textcolor = "black";
-
-			idle = g_strdup_printf("<span color='%s' font_desc='%s'>%d:%02d</span>", textcolor,
-					      (pair == NULL || pair->font == NULL) ? "" : pair->font, ihrs, imin);
+				textcolor = NULL;
+
+			if (textcolor) {
+				idle = g_strdup_printf("<span color='%s' font_desc='%s'>%d:%02d</span>",
+					textcolor, (pair == NULL || pair->font == NULL) ? "" : pair->font, 
+					ihrs, imin);
+			} else {
+				idle = g_strdup_printf("<span font_desc='%s'>%d:%02d</span>",
+					(pair == NULL || pair->font == NULL) ? "" : pair->font, 
+					ihrs, imin);
+			}
 		}
 	}
 
@@ -6331,10 +6354,15 @@
 			}
 
 			font = (pair == NULL || pair->font == NULL) ? "" : pair->font;
-			fg_color = (selected || pair == NULL || pair->color == NULL) ? "black" : pair->color;
-
-			tmp = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>",
+			fg_color = (selected || pair == NULL || pair->color == NULL) ? NULL : pair->color;
+
+			if (fg_color) {
+				tmp = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>",
 						font, fg_color, mark);
+			} else {
+				tmp = g_strdup_printf("<span font_desc='%s'>%s</span>", font, 
+					mark);
+			}
 			g_free(mark);
 			mark = tmp;
 
@@ -6464,13 +6492,17 @@
 		font = (pair == NULL || pair->font == NULL) ? "" : pair->font;
 		if (selected || pair == NULL || pair->color == NULL)
 			/* nick_said color is the same as gtkconv:tab-label-attention */
-			color = (nick_said ? "#006aff" : "black");
+			color = (nick_said ? "#006aff" : NULL);
 		else
 			color = pair->color;
 
-		tmp = g_strdup_printf("<span font_desc='%s' color='%s' weight='%s'>%s</span>",
-				      font, color, hidden ? "bold" : "normal", mark);
-
+		if (color) {
+			tmp = g_strdup_printf("<span font_desc='%s' color='%s' weight='%s'>%s</span>",
+				  	  font, color, hidden ? "bold" : "normal", mark);
+		} else {
+			tmp = g_strdup_printf("<span font_desc='%s' weight='%s'>%s</span>",
+				  	  font, hidden ? "bold" : "normal", mark);
+		}
 		g_free(mark);
 		mark = tmp;
 
--- a/pidgin/gtkconn.c	Tue Mar 03 08:03:11 2009 +0000
+++ b/pidgin/gtkconn.c	Sun Mar 08 20:38:09 2009 +0000
@@ -163,11 +163,6 @@
 			g_hash_table_remove(auto_reconns, account);
 
 		purple_account_set_enabled(account, PIDGIN_UI, FALSE);
-
-		/* clear the saved password if this is an authentication failure */
-		if(reason == PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED)
-			purple_account_set_password(account, NULL);
-			
 	}
 
 	/* If we have any open chats, we probably want to rejoin when we get back online. */
--- a/po/ca.po	Tue Mar 03 08:03:11 2009 +0000
+++ b/po/ca.po	Sun Mar 08 20:38:09 2009 +0000
@@ -33,8 +33,8 @@
 msgstr ""
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-02-22 10:28+0100\n"
-"PO-Revision-Date: 2009-02-22 15:18+0100\n"
+"POT-Creation-Date: 2009-03-05 22:22+0100\n"
+"PO-Revision-Date: 2009-03-05 22:37+0100\n"
 "Last-Translator: Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>\n"
 "Language-Team: Catalan <tradgnome@softcatala.net>\n"
 "MIME-Version: 1.0\n"
@@ -4468,17 +4468,24 @@
 msgstr "No s'ha pogut fer ping a l'usuari %s"
 
 #, c-format
-msgid "Unable to buzz, because there is nothing known about user %s."
+msgid "Unable to buzz, because there is nothing known about %s."
 msgstr "No s'ha pogut botzinar perquè no es coneix res de l'usuari %s."
 
 #, c-format
-msgid "Unable to buzz, because user %s might be offline."
+msgid "Unable to buzz, because %s might be offline."
 msgstr ""
 "No s'ha pogut botzinar possiblement perquè l'usuari %s està desconnectat."
 
 #, c-format
-msgid "Unable to buzz, because the user %s does not support it."
-msgstr "No s'ha pogut botzinar perquè l'usuari %s no ho permet."
+msgid ""
+"Unable to buzz, because %s does not support it or do not wish to receive "
+"buzzes now."
+msgstr ""
+"No s'ha pogut botzinar perquè no és pot, o bé l'usuari %s no ho permet."
+
+#, c-format
+msgid "Buzzing %s..."
+msgstr "S'està botzinant a %s..."
 
 #. Yahoo only supports one attention command: the 'buzz'.
 #. This is index number YAHOO_BUZZ.
@@ -4489,10 +4496,6 @@
 msgid "%s has buzzed you!"
 msgstr "%s us ha botzinat!"
 
-#, c-format
-msgid "Buzzing %s..."
-msgstr "S'està botzinant a %s..."
-
 msgid "config:  Configure a chat room."
 msgstr "config:  configura la sala de xat."
 
@@ -4646,6 +4649,19 @@
 msgid "Error in chat %s"
 msgstr "S'ha produït un error en el xat %s"
 
+#, fuzzy
+msgid "An error occured on the in-band bytestream transfer\n"
+msgstr "S'ha produït un error en obrir el fitxer."
+
+msgid "Transfer was closed."
+msgstr "La transferència s'ha tancat."
+
+msgid "Failed to open the file"
+msgstr "No s'ha pogut obrir el fitxer"
+
+msgid "Failed to open in-band bytestream"
+msgstr ""
+
 #, c-format
 msgid "Unable to send file to %s, user does not support file transfers"
 msgstr ""
@@ -5794,26 +5810,25 @@
 msgid "Zap"
 msgstr ""
 
-#, fuzzy, c-format
+#, c-format
 msgid "%s has zapped you!"
-msgstr "%s us ha afegit [%s]"
-
-#, fuzzy, c-format
+msgstr ""
+
+#, c-format
 msgid "Zapping %s..."
-msgstr "S'està trucant a %s"
+msgstr ""
 
 #. Whack means "to hit or strike someone with a sharp blow"
-#, fuzzy
 msgid "Whack"
 msgstr "bufetejar"
 
-#, fuzzy, c-format
+#, c-format
 msgid "%s has whacked you!"
-msgstr "%s us ha afegit [%s]"
-
-#, fuzzy, c-format
+msgstr "%s us ha bufetejat [%s]"
+
+#, c-format
 msgid "Whacking %s..."
-msgstr "Bufetejant"
+msgstr "S'està bufetejant %s..."
 
 #. Torch means "to set on fire."  Don't worry, this doesn't
 #. * make a whole lot of sense in English, either.  Feel free
@@ -5897,15 +5912,15 @@
 #. * someone to perform a mischievous trick or practical joke.
 #, fuzzy
 msgid "Punk"
-msgstr "Ping"
+msgstr "Enredar"
 
 #, fuzzy, c-format
 msgid "%s has punk'd you!"
-msgstr "%s s'ha connectat."
+msgstr "%s us ha enredat!"
 
 #, fuzzy, c-format
 msgid "Punking %s..."
-msgstr "Ping"
+msgstr "S'està enredant %s..."
 
 #. Raspberry is a slang term for the vibrating sound made
 #. * when you stick your tongue out of your mouth with your
@@ -6513,7 +6528,7 @@
 "començar amb una lletra i contenir només lletres, nombres o espais, o només "
 "nombres."
 
-#. Unregistered screen name
+#. Unregistered username
 #. uid is not exist
 msgid "Invalid username."
 msgstr "El nom d'usuari no és vàlid"
@@ -6530,7 +6545,7 @@
 msgstr ""
 "El servei de missatges instantanis d'AOL no està disponible temporalment."
 
-#. screen name connecting too frequently
+#. username connecting too frequently
 #. IP address connecting too frequently
 msgid ""
 "You have been connecting and disconnecting too frequently. Wait ten minutes "
@@ -6717,7 +6732,7 @@
 msgstr[0] "Heu perdut %hu missatge de %s per motius desconeguts."
 msgstr[1] "Heu perdut %hu missatges de %s per motius desconeguts."
 
-#. Data is assumed to be the destination sn
+#. Data is assumed to be the destination bn
 #, c-format
 msgid "Unable to send message: %s"
 msgstr "No s'ha pogut enviar el missatge: %s"
@@ -7308,6 +7323,35 @@
 msgid "Could not change buddy information."
 msgstr "No s'ha pogut canviar la informació l'amic."
 
+msgid "Mobile"
+msgstr "Mòbil"
+
+msgid "Note"
+msgstr "Nota"
+
+# FIXME: "memo", el qq té una terminologia molt peculiar
+#. callback
+msgid "Buddy Memo"
+msgstr "Memo de l'amic"
+
+msgid "Change his/her memo as you like"
+msgstr "Canvieu-ne el memo"
+
+msgid "_Modify"
+msgstr "_Modifica"
+
+msgid "Memo Modify"
+msgstr "Modifica el memo"
+
+msgid "Server says:"
+msgstr "El servidor diu:"
+
+msgid "Your request was accepted."
+msgstr "S'ha acceptat la vostra sol·licitud."
+
+msgid "Your request was rejected."
+msgstr "S'ha rebutjat la vostra sol·licitud."
+
 #, c-format
 msgid "%u requires verification"
 msgstr "Cal verificació per a %u"
@@ -7620,6 +7664,9 @@
 msgid "<p><b>Acknowledgement</b>:<br>\n"
 msgstr "<p><b>Reconeixement</b>:<br>\n"
 
+msgid "<p><b>Scrupulous Testers</b>:<br>\n"
+msgstr "<p><b>Comprovadors del codi</b>:<br>\n"
+
 # FIXME: ush... traducció lliure... 
 msgid "<p><i>And, all the boys in the backroom...</i><br>\n"
 msgstr "<p><i>I tothom que ho ha fet possible...<i><br>\n"
@@ -7646,6 +7693,9 @@
 msgid "About OpenQ"
 msgstr "Quant a l'OpenQ"
 
+msgid "Modify Buddy Memo"
+msgstr "Modifica el memo de l'amic"
+
 #. *< type
 #. *< ui_requirement
 #. *< flags
@@ -8662,9 +8712,6 @@
 msgid "Unit"
 msgstr "Unitat"
 
-msgid "Note"
-msgstr "Nota"
-
 msgid "Join Chat"
 msgstr "Entra a un xat"
 
@@ -10166,9 +10213,6 @@
 msgid "Extended away"
 msgstr "Absent durant una bona estona"
 
-msgid "Mobile"
-msgstr "Mòbil"
-
 # És un estat, com "fora de línia", etc. (josep)
 msgid "Listening to music"
 msgstr "Escoltant música"
@@ -10211,18 +10255,6 @@
 msgid "%x %X"
 msgstr "%x %X"
 
-#, c-format
-msgid "Error Reading %s"
-msgstr "S'ha produït un error en llegir %s"
-
-#, c-format
-msgid ""
-"An error was encountered reading your %s.  They have not been loaded, and "
-"the old file has been renamed to %s~."
-msgstr ""
-"S'ha produït un error en llegir el vostre %s. No s'han carregat, i s'ha "
-"canviat el nom del fitxer per %s~."
-
 msgid "Calculating..."
 msgstr "S'està calculant..."
 
@@ -10332,6 +10364,18 @@
 msgid "Address already in use."
 msgstr "Aquesta adreça ja s'està fent servir"
 
+#, c-format
+msgid "Error Reading %s"
+msgstr "S'ha produït un error en llegir %s"
+
+#, c-format
+msgid ""
+"An error was encountered reading your %s.  The file has not been loaded, and "
+"the old file has been renamed to %s~."
+msgstr ""
+"S'ha produït un error en llegir el vostre %s. No s'ha carregat el fitxer, i "
+"s'ha canviat el nom per %s~."
+
 msgid "Internet Messenger"
 msgstr "Missatger d'Internet"
 
@@ -10657,6 +10701,9 @@
 msgid "/Tools/_Certificates"
 msgstr "/Eines/C_ertificats"
 
+msgid "/Tools/Custom Smile_ys"
+msgstr "/Eines/Em_oticones personalitzades"
+
 msgid "/Tools/Plu_gins"
 msgstr "/Eines/_Connectors"
 
@@ -10666,9 +10713,6 @@
 msgid "/Tools/Pr_ivacy"
 msgstr "/Eines/_Privadesa"
 
-msgid "/Tools/Smile_y"
-msgstr "/Eines/Em_oticona"
-
 msgid "/Tools/_File Transfers"
 msgstr "/Eines/_Transferència de fitxers"
 
@@ -10786,8 +10830,8 @@
 msgid "By status"
 msgstr "Per estat"
 
-msgid "By log size"
-msgstr "Per la mida del registre"
+msgid "By recent log activity"
+msgstr "Per activitat recent en el registre"
 
 #, c-format
 msgid "%s disconnected"
@@ -11902,15 +11946,6 @@
 msgid "Enable typing notification"
 msgstr "Habilita les notificacions de que s'està escrivint"
 
-msgid "_Copy Email Address"
-msgstr "_Copia l'adreça de correu"
-
-msgid "_Open Link in Browser"
-msgstr "_Obre l'enllaç en el navegador"
-
-msgid "_Copy Link Location"
-msgstr "_Copia la ubicació de l'enllaç"
-
 msgid ""
 "<span size='larger' weight='bold'>Unrecognized file type</span>\n"
 "\n"
@@ -12169,6 +12204,7 @@
 "\n"
 "  -c, --config=DIR    use DIR for config files\n"
 "  -d, --debug         print debugging messages to stdout\n"
+"  -f, --force-online  force online, regardless of network status\n"
 "  -h, --help          display this help and exit\n"
 "  -m, --multiple      do not ensure single instance\n"
 "  -n, --nologin       don't automatically login\n"
@@ -12183,8 +12219,11 @@
 "\n"
 "  -c, --config=DIR    utilitza DIR per als fitxers de configuració\n"
 "  -d, --debug         mostra missatges de depuració a la sortida estàndard\n"
+"  -f, --force-online  força que s'estigui en línia, independent de l'estat "
+"de\n"
+"                      la xarxa\n"
 "  -h, --help          mostra aquesta ajuda i surt\n"
-"  -m, --multiple      permet que hi hagi més d'una instància\n"
+"  -m, --multiple      no controla que només hi hagi una instància\n"
 "  -n, --nologin       no entra automàticament\n"
 "  -l, --login[=NOM]   habilita el compte especificat (l'argument opcional "
 "NOM\n"
@@ -12201,6 +12240,7 @@
 "\n"
 "  -c, --config=DIR    use DIR for config files\n"
 "  -d, --debug         print debugging messages to stdout\n"
+"  -f, --force-online  force online, regardless of network status\n"
 "  -h, --help          display this help and exit\n"
 "  -m, --multiple      do not ensure single instance\n"
 "  -n, --nologin       don't automatically login\n"
@@ -12214,8 +12254,11 @@
 "\n"
 "  -c, --config=DIR    utilitza DIR per als fitxers de configuració\n"
 "  -d, --debug         mostra missatges de depuració a la sortida estàndard\n"
+"  -f, --force-online  força que s'estigui en línia, independent de l'estat "
+"de\n"
+"                      la xarxa\n"
 "  -h, --help          mostra aquesta ajuda i surt\n"
-"  -m, --multiple      permet que hi hagi més d'una instància\n"
+"  -m, --multiple      no controla que només hi hagi una instància\n"
 "  -n, --nologin       no entra automàticament\n"
 "  -l, --login[=NOM]   habilita el compte especificat (l'argument opcional "
 "NOM\n"
@@ -12346,6 +12389,9 @@
 msgid "Select a file"
 msgstr "Seleccioneu un fitxer"
 
+msgid "Modify Buddy Pounce"
+msgstr "Modifica l'avís per a l'amic"
+
 # FIXME
 #. Create the "Pounce on Whom" frame.
 msgid "Pounce on Whom"
@@ -12443,6 +12489,11 @@
 msgid "Cl_ose conversations with the Escape key"
 msgstr "Tanca les converses amb la tecla d'_escapament"
 
+#. Buddy List Themes
+msgid "Buddy List Theme"
+msgstr "Tema de la llista d'amics"
+
+#. System Tray
 msgid "System Tray Icon"
 msgstr "Icona d'estat"
 
@@ -12928,6 +12979,12 @@
 msgid "Status for %s"
 msgstr "Estat per a %s"
 
+#.
+#. * TODO: We should enable/disable the add button based on
+#. *       whether the user has entered all required data.  That
+#. *       would eliminate the need for this check and provide a
+#. *       better user experience.
+#.
 msgid "Custom Smiley"
 msgstr "Emoticona personalitzada"
 
@@ -12937,16 +12994,15 @@
 msgid "Please provide a shortcut to associate with the smiley."
 msgstr "Especifiqueu una drecera associada a l'emoticona."
 
+#, c-format
+msgid ""
+"A custom smiley for '%s' already exists.  Please use a different shortcut."
+msgstr ""
+"Ja hi ha una emoticona personalitzada per a «%s». Indiqueu-ne una de diferent."
+
 msgid "Duplicate Shortcut"
 msgstr "Drecera duplicada"
 
-msgid ""
-"A custom smiley for the selected shortcut already exists. Please specify a "
-"different shortcut."
-msgstr ""
-"Hi ha una emoticona personalitzada per la drecera que heu seleccionat. "
-"Indiqueu-ne una de diferent."
-
 msgid "Please select an image for the smiley."
 msgstr "Seleccioneu una imatge per a l'emoticona."
 
@@ -12956,16 +13012,19 @@
 msgid "Add Smiley"
 msgstr "Afegeix una emoticona"
 
-msgid "Smiley _Image"
-msgstr "_Imatge de l'emoticona"
-
-#. Smiley shortcut
-msgid "Smiley S_hortcut"
-msgstr "_Dreceres de l'emoticona"
+msgid "_Image:"
+msgstr "_Imatge:"
+
+#. Shortcut text
+msgid "S_hortcut text:"
+msgstr "_Drecera:"
 
 msgid "Smiley"
 msgstr "Emoticona"
 
+msgid "Shortcut Text"
+msgstr "Drecera"
+
 msgid "Custom Smiley Manager"
 msgstr "Gestor d'emoticones personalitzades"
 
@@ -13093,6 +13152,15 @@
 "No s'ha pogut carregar la imatge «%s»: no se'n coneix el motiu, possiblement "
 "la imatge estigui corrompuda"
 
+msgid "_Open Link"
+msgstr "_Obre l'enllaç"
+
+msgid "_Copy Link Location"
+msgstr "_Copia l'enllaç"
+
+msgid "_Copy Email Address"
+msgstr "_Copia l'adreça de correu"
+
 msgid "Save File"
 msgstr "Desa un fitxer"
 
@@ -14126,6 +14194,18 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr "Aquest connector és útil per a depurar servidors i clients XMPP."
 
+#~ msgid "By log size"
+#~ msgstr "Per la mida del registre"
+
+#~ msgid "_Open Link in Browser"
+#~ msgstr "_Obre l'enllaç en el navegador"
+
+#~ msgid "Smiley _Image"
+#~ msgstr "_Imatge de l'emoticona"
+
+#~ msgid "Smiley S_hortcut"
+#~ msgstr "_Dreceres de l'emoticona"
+
 #~ msgid "Unable to retrieve MSN Address Book"
 #~ msgstr "No s'ha pogut obtenir la llibreta d'adreces MSN"