changeset 27382:09772222714a

merge of '599603c54f97d6978ff312a4ff140857302b0033' and '85d6ddccd30dc452a4afcd6adc18d9369901b646'
author Mark Doliner <mark@kingant.net>
date Mon, 06 Jul 2009 09:08:17 +0000
parents c374d57b3215 (diff) f541583e31bd (current diff)
children ecf2dec4ef5e
files libpurple/protocols/yahoo/libymsg.c libpurple/protocols/yahoo/yahoo.c libpurple/protocols/yahoo/yahoo.h libpurple/protocols/yahoo/ycht.c
diffstat 34 files changed, 6159 insertions(+), 5596 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jul 06 09:07:42 2009 +0000
+++ b/ChangeLog	Mon Jul 06 09:08:17 2009 +0000
@@ -10,10 +10,6 @@
 	  in a group on the buddy list. (Paul Aurich)
 	* Removed the unmaintained and unneeded toc protocol plugin.
 	* Fixed NTLM authentication on big-endian systems.
-	* The Pidgin and Purple perl modules are no longer installed into @INC,
-	  this should hopefully prevent some minor confusion.
-	* Use GLib's implementations of SHA1, SHA256, and MD5 when available.
-	  (GLib 2.14 or higher)
 
 	libpurple:
 	* Various memory cleanups when unloading libpurple. (Nick Hebner)
@@ -32,6 +28,7 @@
 	  "off" variable.  Set it to any value to turn it on and unset it to turn
 	  it off.  This will optionally be used to only show less useful debug
 	  information on an as-needed basis.
+	* Add support for receiving handwritten (ink) messages on MSN.
 
 	Gadu-Gadu:
 	* Accounts can specify a server to which to connect.
@@ -84,13 +81,16 @@
 	* Display kicks (and the reasons given) in chat rooms when an occupant is
 	  kicked.
 
-	Yahoo:
+	Yahoo!/Yahoo! JAPAN:
 	* P2P file transfers.  (Sulabh Mahajan)
 	* Sending text messages (address to +<countrycode><phone number>).
 	  (Sulabh Mahajan)
 	* Addition of MSN buddies to Yahoo accounts by adding them as
 	  'msn/buddy@somedomain.com' is now supported.  (Sulabh Mahajan)
 	* Further fixes for buddy pictures, aliases etc.
+	* Yahoo! and Yahoo! JAPAN are now two separate protocol plugins that share
+	  common protocol code.  You can now have the same account on both
+	  networks.
 
 	Pidgin:
 	* Added -f command line option to tell Pidgin to ignore NetworkManager
--- a/ChangeLog.API	Mon Jul 06 09:07:42 2009 +0000
+++ b/ChangeLog.API	Mon Jul 06 09:08:17 2009 +0000
@@ -16,6 +16,7 @@
 		* blist-node-added and blist-node-removed signals (see
 		  blist-signals.dox)
 		* Jabber plugin signals (see jabber-signals.dox)
+		* purple_account_remove_setting
 		* purple_buddy_destroy
 		* purple_buddy_get_protocol_data
 		* purple_buddy_set_protocol_data
@@ -74,6 +75,7 @@
 		  emitted), but this signal was not emitted.
 		* Added a client_type field in the get_ui_info core UI op. See
 		  core.h for details.
+		* Added introspection of signals exposed via the D-Bus API.
 
 		Deprecated:
 		* buddy-added and buddy-removed blist signals
--- a/libpurple/Makefile.am	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/Makefile.am	Mon Jul 06 09:08:17 2009 +0000
@@ -1,5 +1,6 @@
 EXTRA_DIST = \
 		dbus-analyze-functions.py \
+		dbus-analyze-signals.py \
 		dbus-analyze-types.py \
 		marshallers.list \
 		purple-notifications-example \
@@ -169,6 +170,7 @@
 	dbus-bindings.c \
 	dbus-client-binding.c \
 	dbus-client-binding.h \
+	dbus-signals.c \
 	dbus-types.c \
 	dbus-types.h \
 	marshallers.c \
@@ -189,6 +191,10 @@
 purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \
 		$(purple_builtheaders)
 dbus_build_exported = $(addprefix $(srcdir)/, $(dbus_exported))
+# We should probably make this better
+dbus_signals = $(purple_coresources) \
+	protocols/irc/irc.c \
+	protocols/jabber/libxmpp.c
 
 dbus-types.c: dbus-analyze-types.py $(purple_build_coreheaders)
 	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@
@@ -199,8 +205,11 @@
 dbus-bindings.c: dbus-analyze-functions.py $(dbus_exported)
 	cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@
 
-dbus-server.$(OBJEXT): dbus-bindings.c dbus-types.c dbus-types.h
-dbus-server.lo: dbus-bindings.c dbus-types.c dbus-types.h
+dbus-signals.c: dbus-analyze-signals.py $(dbus_signals)
+	cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@
+
+dbus-server.$(OBJEXT): dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
+dbus-server.lo: dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
 $(libpurple_la_OBJECTS): dbus-types.h
 
 # libpurple-client
@@ -240,9 +249,10 @@
 bin_SCRIPTS = purple-remote purple-send purple-send-async purple-url-handler
 
 BUILT_SOURCES = $(purple_builtheaders) \
+	dbus-bindings.c \
+	dbus-signals.c \
 	dbus-types.c \
 	dbus-types.h \
-	dbus-bindings.c \
 	marshallers.c \
 	marshallers.h \
 	purple-client-bindings.c \
--- a/libpurple/account.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/account.c	Mon Jul 06 09:08:17 2009 +0000
@@ -481,6 +481,36 @@
 /*********************************************************************
  * Reading from disk                                                 *
  *********************************************************************/
+static void
+migrate_yahoo_japan(PurpleAccount *account)
+{
+	/* detect a Yahoo! JAPAN account that existed prior to 2.6.0 and convert it
+	 * to use the new prpl-yahoojp.  Also remove the account-specific settings
+	 * we no longer need */
+
+	if(purple_strequal(purple_account_get_protocol_id(account), "prpl-yahoo")) {
+		if(purple_account_get_bool(account, "yahoojp", FALSE)) {
+			const char *serverjp = purple_account_get_string(account, "serverjp", NULL);
+			const char *xferjp_host = purple_account_get_string(account, "xferjp_host", NULL);
+
+			g_return_if_fail(serverjp != NULL);
+			g_return_if_fail(xferjp_host != NULL);
+
+			purple_account_set_string(account, "server", serverjp);
+			purple_account_set_string(account, "xfer_host", xferjp_host);
+
+			purple_account_set_protocol_id(account, "prpl-yahoojp");
+		}
+
+		/* these should always be nuked */
+		purple_account_remove_setting(account, "yahoojp");
+		purple_account_remove_setting(account, "serverjp");
+		purple_account_remove_setting(account, "xferjp_host");
+
+	}
+
+	return;
+}
 
 static void
 parse_settings(xmlnode *node, PurpleAccount *account)
@@ -545,6 +575,10 @@
 
 		g_free(data);
 	}
+
+	/* we do this here because we need access to account settings to determine
+	 * if we can/should migrate an old Yahoo! JAPAN account */
+	migrate_yahoo_japan(account);
 }
 
 static GList *
@@ -1728,6 +1762,15 @@
 }
 
 void
+purple_account_remove_setting(PurpleAccount *account, const char *setting)
+{
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(setting != NULL);
+
+	g_hash_table_remove(account->settings, setting);
+}
+
+void
 purple_account_set_int(PurpleAccount *account, const char *name, int value)
 {
 	PurpleAccountSetting *setting;
--- a/libpurple/account.h	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/account.h	Mon Jul 06 09:08:17 2009 +0000
@@ -459,6 +459,16 @@
 void purple_account_clear_settings(PurpleAccount *account);
 
 /**
+ * Removes an account-specific setting by name.
+ *
+ * @param account The account.
+ * @param setting The setting to remove.
+ *
+ * @since 2.6.0
+ */
+void purple_account_remove_setting(PurpleAccount *account, const char *setting);
+
+/**
  * Sets a protocol-specific integer setting for an account.
  *
  * @param account The account.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/dbus-analyze-signals.py	Mon Jul 06 09:08:17 2009 +0000
@@ -0,0 +1,60 @@
+# This program takes a C source as the input and produces the list of
+# all signals registered.
+#
+# Output is:
+#   <signal name="Changed">
+#       <arg name="new_value" type="b"/>
+#   </signal>
+
+import re
+import sys
+
+# List "excluded" contains signals that shouldn't be exported via
+# DBus.  If you remove a signal from this list, please make sure
+# that it does not break "make" with the configure option
+# "--enable-dbus" turned on.
+
+excluded = [\
+    # purple_dbus_signal_emit_purple prevents our "dbus-method-called"
+    # signal from being propagated to dbus.
+	"dbus-method-called",
+    ]
+
+registerregex = re.compile("purple_signal_register[^;]+\"([\w\-]+)\"[^;]+(purple_marshal_\w+)[^;]+;")
+nameregex = re.compile('[-_][a-z]')
+
+print "/* Generated by %s.  Do not edit! */" % sys.argv[0]
+print "const char *dbus_signals = "
+for match in registerregex.finditer(sys.stdin.read()):
+    signal = match.group(1)
+    marshal = match.group(2)
+    if signal in excluded:
+        continue
+
+    signal = nameregex.sub(lambda x:x.group()[1].upper(), '-'+signal)
+    print "\"<signal name='%s'>\\n\""%signal
+
+    args = marshal.split('_')
+    # ['purple', 'marshal', <return type>, '', args...]
+    if len(args) > 4:
+        for arg in args[4:]:
+            if arg == "POINTER":
+                type = 'p'
+            elif arg == "ENUM":
+                type = 'i'
+            elif arg == "INT":
+                type = 'i'
+            elif arg == "UINT":
+                type = 'u'
+            elif arg == "INT64":
+                type = 'x'
+            elif arg == "UINT64":
+                type = 't'
+            elif arg == "BOOLEAN":
+                type = 'b'
+            print "\"<arg type='%s'/>\\n\""%type
+
+    print "\"</signal>\\n\""
+
+print ";"
+
--- a/libpurple/dbus-server.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/dbus-server.c	Mon Jul 06 09:08:17 2009 +0000
@@ -421,6 +421,7 @@
 }
 
 #include "dbus-bindings.c"
+#include "dbus-signals.c"
 
 static gboolean
 purple_dbus_dispatch_cb(DBusConnection *connection,
@@ -489,6 +490,9 @@
 	DBusMessage *reply;
 	GString *str;
 	GList *bindings_list, *node;
+	const char *signals;
+	const char *type;
+	const char *pointer_type;
 
 	str = g_string_sized_new(0x1000); /* TODO: why this size? */
 
@@ -529,6 +533,19 @@
 		}
 	}
 
+	if (sizeof(int) == sizeof(dbus_int32_t))
+		pointer_type = "type='i'";
+	else
+		pointer_type = "type='x'";
+
+	signals = dbus_signals;
+	while ((type = strstr(signals, "type='p'")) != NULL) {
+		g_string_append_len(str, signals, type - signals);
+		g_string_append(str, pointer_type);
+		signals = type + sizeof("type='p'") - 1;
+	}
+	g_string_append(str, signals);
+
 	g_string_append(str, "</interface>\n</node>\n");
 
 	reply = dbus_message_new_method_return(message);
--- a/libpurple/dbus-server.h	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/dbus-server.h	Mon Jul 06 09:08:17 2009 +0000
@@ -192,9 +192,9 @@
 /**
 
  Macro #DBUS_EXPORT expands to nothing.  It is used to indicate to the
- dbus-analize-functions.py script that the given function should be
+ dbus-analyze-functions.py script that the given function should be
  available to other applications through DBUS.  If
- dbus-analize-functions.py is run without the "--export-only" option,
+ dbus-analyze-functions.py is run without the "--export-only" option,
  this prefix is ignored.
 
  */
@@ -204,7 +204,7 @@
 /*
    Here we include the list of #PURPLE_DBUS_DECLARE_TYPE statements for
    all structs defined in purple.  This file has been generated by the
-   #dbus-analize-types.py script.
+   #dbus-analyze-types.py script.
 */
 
 #include "dbus-types.h"
--- a/libpurple/protocols/msn/msg.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/msn/msg.c	Mon Jul 06 09:08:17 2009 +0000
@@ -1048,3 +1048,14 @@
 	g_hash_table_destroy(body);
 }
 
+/* Only called from chats. Handwritten messages for IMs come as a SLP message */
+void
+msn_handwritten_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+	const char *body;
+	size_t body_len;
+
+	body = msn_message_get_bin_data(msg, &body_len);
+	msn_switchboard_show_ink(cmdproc->data, msg->remote_user, body);
+}
+
--- a/libpurple/protocols/msn/msg.h	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/msn/msg.h	Mon Jul 06 09:08:17 2009 +0000
@@ -345,4 +345,6 @@
 
 void msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
 
+void msn_handwritten_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
 #endif /* _MSN_MSG_H_ */
--- a/libpurple/protocols/msn/msn.h	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/msn/msn.h	Mon Jul 06 09:08:17 2009 +0000
@@ -138,7 +138,7 @@
 } MsnClientVerId;
 
 #define MSN_CLIENT_ID_VERSION      MSN_CLIENT_VER_7_0
-#define MSN_CLIENT_ID_CAPABILITIES MSN_CLIENT_CAP_PACKET
+#define MSN_CLIENT_ID_CAPABILITIES (MSN_CLIENT_CAP_PACKET|MSN_CLIENT_CAP_INK_GIF)
 
 #define MSN_CLIENT_ID \
 	((MSN_CLIENT_ID_VERSION    << 24) | \
--- a/libpurple/protocols/msn/slpcall.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Mon Jul 06 09:08:17 2009 +0000
@@ -210,8 +210,51 @@
 	{
 		char *body_str;
 
-		body_str = g_strndup((const char *)body, body_len);
-		slpcall = msn_slp_sip_recv(slplink, body_str);
+		if (slpmsg->session_id == 64)
+		{
+			/* This is for handwritten messages (Ink) */
+			GError *error;
+			glong items_read, items_written;
+
+			body_str = g_utf16_to_utf8((gunichar2 *)body, body_len / 2,
+			                           &items_read, &items_written, &error);
+			body_len -= items_read * 2 + 2;
+			body += items_read * 2 + 2;
+			if (body_str == NULL
+			 || body_len <= 0
+			 || strstr(body_str, "image/gif") == NULL)
+			{
+				if (error != NULL)
+					purple_debug_error("msn",
+					                   "Unable to convert Ink header from UTF-16 to UTF-8: %s\n",
+					                   error->message);
+				else
+					purple_debug_error("msn",
+					                   "Received Ink in unknown format\n");
+				g_free(body_str);
+				return NULL;
+			}
+			g_free(body_str);
+
+			body_str = g_utf16_to_utf8((gunichar2 *)body, body_len / 2,
+			                           &items_read, &items_written, &error);
+			if (!body_str)
+			{
+				purple_debug_error("msn",
+				                   "Unable to convert Ink body from UTF-16 to UTF-8: %s\n",
+				                   error->message);
+				return NULL;
+			}
+
+			msn_switchboard_show_ink(slpmsg->slplink->swboard,
+			                         slplink->remote_user,
+			                         body_str);
+		}
+		else
+		{
+			body_str = g_strndup((const char *)body, body_len);
+			slpcall = msn_slp_sip_recv(slplink, body_str);
+		}
 		g_free(body_str);
 	}
 	else if (slpmsg->flags == 0x20 ||
--- a/libpurple/protocols/msn/switchboard.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Mon Jul 06 09:08:17 2009 +0000
@@ -905,6 +905,47 @@
 #endif
 }
 
+void
+msn_switchboard_show_ink(MsnSwitchBoard *swboard, const char *passport,
+                         const char *data)
+{
+	PurpleConnection *gc;
+	guchar *image_data;
+	size_t image_len;
+	int imgid;
+	char *image_msg;
+
+	if (!purple_str_has_prefix(data, "base64:"))
+	{
+		purple_debug_error("msn", "Ignoring Ink not in Base64 format.\n");
+		return;
+	}
+
+	gc = purple_account_get_connection(swboard->session->account);
+
+	data += sizeof("base64:") - 1;
+	image_data = purple_base64_decode(data, &image_len);
+	if (!image_data || !image_len) 
+	{
+		purple_debug_error("msn", "Unable to decode Ink from Base64 format.\n");
+		return;
+	}
+
+	imgid = purple_imgstore_add_with_id(image_data, image_len, NULL);
+	image_msg = g_strdup_printf("<IMG ID=%d/>", imgid);
+
+	if (swboard->current_users > 1 ||
+		((swboard->conv != NULL) &&
+		 purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
+		serv_got_chat_in(gc, swboard->chat_id, passport, 0, image_msg,
+						 time(NULL));
+	else
+		serv_got_im(gc, passport, image_msg, 0, time(NULL));
+
+	purple_imgstore_unref_by_id(imgid);
+	g_free(image_msg);
+}
+
 /**************************************************************************
  * Connect stuff
  **************************************************************************/
@@ -1239,6 +1280,8 @@
 						   msn_datacast_msg);
 	msn_table_add_msg_type(cbs_table, "text/x-msmsgsinvite",
 						   msn_invite_msg);
+	msn_table_add_msg_type(cbs_table, "image/gif",
+						   msn_handwritten_msg);
 }
 
 void
--- a/libpurple/protocols/msn/switchboard.h	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.h	Mon Jul 06 09:08:17 2009 +0000
@@ -280,4 +280,14 @@
  */
 void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
 
+/**
+ * Shows an ink message from this switchboard.
+ *
+ * @param swboard  The switchboard.
+ * @param passport The user that sent the ink.
+ * @param data     The ink data.
+ */
+void msn_switchboard_show_ink(MsnSwitchBoard *swboard, const char *passport,
+                              const char *data);
+
 #endif /* _MSN_SWITCHBOARD_H_ */
--- a/libpurple/protocols/yahoo/Makefile.am	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/Makefile.am	Mon Jul 06 09:08:17 2009 +0000
@@ -4,9 +4,9 @@
 pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
 
 YAHOOSOURCES = \
+	libymsg.c \
+	libymsg.h \
 	util.c \
-	yahoo.c \
-	yahoo.h \
 	yahoochat.h \
 	yahoochat.c \
 	yahoo_aliases.c \
@@ -28,20 +28,28 @@
 AM_CFLAGS = $(st)
 
 libyahoo_la_LDFLAGS = -module -avoid-version
+libyahoojp_la_LDFLAGS = -module -avoid-version
 
 if STATIC_YAHOO
 
 st = -DPURPLE_STATIC_PRPL
 noinst_LTLIBRARIES  = libyahoo.la
-libyahoo_la_SOURCES = $(YAHOOSOURCES)
-libyahoo_la_CFLAGS  = $(AM_CFLAGS)
+libymsg_la_SOURCES = $(YAHOOSOURCES) libyahoo.c libyahoojp.c
+libymsg_la_CFLAGS  = $(AM_CFLAGS)
 
 else
 
 st =
-pkg_LTLIBRARIES     = libyahoo.la
-libyahoo_la_SOURCES = $(YAHOOSOURCES)
-libyahoo_la_LIBADD  = $(GLIB_LIBS)
+pkg_LTLIBRARIES     = libymsg.la libyahoo.la libyahoojp.la
+
+libymsg_la_SOURCES = $(YAHOOSOURCES)
+libymsg_la_LIBADD  = $(GLIB_LIBS)
+
+libyahoo_la_SOURCES = libyahoo.c
+libyahoo_la_LIBADD = libymsg.la
+
+libyahoojp_la_SOURCES = libyahoojp.c
+libyahoojp_la_LIBADD = libymsg.la
 
 endif
 
@@ -49,4 +57,4 @@
 	-I$(top_srcdir)/libpurple \
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
-	$(DEBUG_CFLAGS)
\ No newline at end of file
+	$(DEBUG_CFLAGS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/yahoo/libyahoo.c	Mon Jul 06 09:08:17 2009 +0000
@@ -0,0 +1,350 @@
+/*
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 <account.h>
+#include <core.h>
+
+#include "libymsg.h"
+#include "yahoochat.h"
+#include "yahoo_aliases.h"
+#include "yahoo_doodle.h"
+#include "yahoo_filexfer.h"
+#include "yahoo_picture.h"
+
+static PurplePlugin *my_protocol = NULL;
+
+static void yahoo_register_commands(void)
+{
+	purple_cmd_register("join", "s", PURPLE_CMD_P_PRPL,
+	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
+	                  PURPLE_CMD_FLAG_PRPL_ONLY,
+	                  "prpl-yahoo", yahoopurple_cmd_chat_join,
+	                  _("join &lt;room&gt;:  Join a chat room on the Yahoo network"), NULL);
+	purple_cmd_register("list", "", PURPLE_CMD_P_PRPL,
+	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
+	                  PURPLE_CMD_FLAG_PRPL_ONLY,
+	                  "prpl-yahoo", yahoopurple_cmd_chat_list,
+	                  _("list: List rooms on the Yahoo network"), NULL);
+	purple_cmd_register("buzz", "", PURPLE_CMD_P_PRPL,
+	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
+	                  "prpl-yahoo", yahoopurple_cmd_buzz,
+	                  _("buzz: Buzz a user to get their attention"), NULL);
+	purple_cmd_register("doodle", "", PURPLE_CMD_P_PRPL,
+	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
+	                  "prpl-yahoo", yahoo_doodle_purple_cmd_start,
+	                 _("doodle: Request user to start a Doodle session"), NULL);
+}
+
+static PurpleAccount *find_acct(const char *prpl, const char *acct_id)
+{
+	PurpleAccount *acct = NULL;
+
+	/* If we have a specific acct, use it */
+	if (acct_id) {
+		acct = purple_accounts_find(acct_id, prpl);
+		if (acct && !purple_account_is_connected(acct))
+			acct = NULL;
+	} else { /* Otherwise find an active account for the protocol */
+		GList *l = purple_accounts_get_all();
+		while (l) {
+			if (!strcmp(prpl, purple_account_get_protocol_id(l->data))
+					&& purple_account_is_connected(l->data)) {
+				acct = l->data;
+				break;
+			}
+			l = l->next;
+		}
+	}
+
+	return acct;
+}
+
+/* This may not be the best way to do this, but we find the first key w/o a value
+ * and assume it is the buddy name */
+static void yahoo_find_uri_novalue_param(gpointer key, gpointer value, gpointer user_data)
+{
+	char **retval = user_data;
+
+	if (value == NULL && *retval == NULL) {
+		*retval = key;
+	}
+}
+
+static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable *params)
+{
+	char *acct_id = g_hash_table_lookup(params, "account");
+	PurpleAccount *acct;
+
+	if (g_ascii_strcasecmp(proto, "ymsgr"))
+		return FALSE;
+
+	acct = find_acct(purple_plugin_get_id(my_protocol), acct_id);
+
+	if (!acct)
+		return FALSE;
+
+	/* ymsgr:SendIM?screename&m=The+Message */
+	if (!g_ascii_strcasecmp(cmd, "SendIM")) {
+		char *sname = NULL;
+		g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &sname);
+		if (sname) {
+			char *message = g_hash_table_lookup(params, "m");
+
+			PurpleConversation *conv = purple_find_conversation_with_account(
+				PURPLE_CONV_TYPE_IM, sname, acct);
+			if (conv == NULL)
+				conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname);
+			purple_conversation_present(conv);
+
+			if (message) {
+				/* Spaces are encoded as '+' */
+				g_strdelimit(message, "+", ' ');
+				purple_conv_send_confirm(conv, message);
+			}
+		}
+		/* 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(); */
+
+		return TRUE;
+	}
+	/* ymsgr:Chat?roomname */
+	else if (!g_ascii_strcasecmp(cmd, "Chat")) {
+		char *rname = NULL;
+		g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &rname);
+		if (rname) {
+			/* This is somewhat hacky, but the params aren't useful after this command */
+			g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
+			g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat"));
+			serv_join_chat(purple_account_get_connection(acct), params);
+		}
+		/* else
+			** Same as above (except that this would have to be re-written using purple_request_*)
+			pidgin_blist_joinchat_show(); */
+
+		return TRUE;
+	}
+	/* ymsgr:AddFriend?name */
+	else if (!g_ascii_strcasecmp(cmd, "AddFriend")) {
+		char *name = NULL;
+		g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &name);
+		purple_blist_request_add_buddy(acct, name, NULL, NULL);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static GHashTable *
+yahoo_get_account_text_table(PurpleAccount *account)
+{
+	GHashTable *table;
+	table = g_hash_table_new(g_str_hash, g_str_equal);
+	g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo ID..."));
+	return table;
+}
+
+static gboolean yahoo_unload_plugin(PurplePlugin *plugin)
+{
+	yahoo_dest_colorht();
+
+	return TRUE;
+}
+
+static PurpleWhiteboardPrplOps yahoo_whiteboard_prpl_ops =
+{
+	yahoo_doodle_start,
+	yahoo_doodle_end,
+	yahoo_doodle_get_dimensions,
+	NULL,
+	yahoo_doodle_get_brush,
+	yahoo_doodle_set_brush,
+	yahoo_doodle_send_draw_list,
+	yahoo_doodle_clear,
+
+	/* padding */
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static PurplePluginProtocolInfo prpl_info =
+{
+	OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC,
+	NULL, /* user_splits */
+	NULL, /* protocol_options */
+	{"png,gif,jpeg", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND},
+	yahoo_list_icon,
+	yahoo_list_emblem,
+	yahoo_status_text,
+	yahoo_tooltip_text,
+	yahoo_status_types,
+	yahoo_blist_node_menu,
+	yahoo_c_info,
+	yahoo_c_info_defaults,
+	yahoo_login,
+	yahoo_close,
+	yahoo_send_im,
+	NULL, /* set info */
+	yahoo_send_typing,
+	yahoo_get_info,
+	yahoo_set_status,
+	yahoo_set_idle,
+	NULL, /* change_passwd*/
+	yahoo_add_buddy,
+	NULL, /* add_buddies */
+	yahoo_remove_buddy,
+	NULL, /* remove_buddies */
+	NULL, /* add_permit */
+	yahoo_add_deny,
+	NULL, /* rem_permit */
+	yahoo_rem_deny,
+	yahoo_set_permit_deny,
+	yahoo_c_join,
+	NULL, /* reject chat invite */
+	yahoo_get_chat_name,
+	yahoo_c_invite,
+	yahoo_c_leave,
+	NULL, /* chat whisper */
+	yahoo_c_send,
+	yahoo_keepalive,
+	NULL, /* register_user */
+	NULL, /* get_cb_info */
+	NULL, /* get_cb_away */
+	yahoo_update_alias, /* alias_buddy */
+	yahoo_change_buddys_group,
+	yahoo_rename_group,
+	NULL, /* buddy_free */
+	NULL, /* convo_closed */
+	purple_normalize_nocase, /* normalize */
+	yahoo_set_buddy_icon,
+	NULL, /* void (*remove_group)(PurpleConnection *gc, const char *group);*/
+	NULL, /* char *(*get_cb_real_name)(PurpleConnection *gc, int id, const char *who); */
+	NULL, /* set_chat_topic */
+	NULL, /* find_blist_chat */
+	yahoo_roomlist_get_list,
+	yahoo_roomlist_cancel,
+	yahoo_roomlist_expand_category,
+	NULL, /* can_receive_file */
+	yahoo_send_file,
+	yahoo_new_xfer,
+	yahoo_offline_message, /* offline_message */
+	&yahoo_whiteboard_prpl_ops,
+	NULL, /* send_raw */
+	NULL, /* roomlist_room_serialize */
+	NULL, /* unregister_user */
+
+	yahoo_send_attention,
+	yahoo_attention_types,
+
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
+	yahoo_get_account_text_table,    /* get_account_text_table */
+	NULL, /* initiate_media */
+	NULL  /* can_do_media */
+};
+
+static PurplePluginInfo info =
+{
+	PURPLE_PLUGIN_MAGIC,
+	PURPLE_MAJOR_VERSION,
+	PURPLE_MINOR_VERSION,
+	PURPLE_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */
+	"prpl-yahoo",                                     /**< id             */
+	"Yahoo",	                                      /**< name           */
+	DISPLAY_VERSION,                                  /**< version        */
+	                                                  /**  summary        */
+	N_("Yahoo Protocol Plugin"),
+	                                                  /**  description    */
+	N_("Yahoo Protocol Plugin"),
+	NULL,                                             /**< author         */
+	PURPLE_WEBSITE,                                     /**< homepage       */
+	NULL,                                             /**< load           */
+	yahoo_unload_plugin,                              /**< unload         */
+	NULL,                                             /**< destroy        */
+	NULL,                                             /**< ui_info        */
+	&prpl_info,                                       /**< extra_info     */
+	NULL,
+	yahoo_actions,
+
+	/* padding */
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+	PurpleAccountOption *option;
+
+	option = purple_account_option_string_new(_("Pager server"), "server", YAHOO_PAGER_HOST);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOO_XFER_HOST);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_int_new(_("File transfer port"), "xfer_port", YAHOO_XFER_PORT);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOO_ROOMLIST_LOCALE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8");
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+
+#if 0
+	option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_string_new(_("Yahoo Chat server"), "ycht-server", YAHOO_YCHT_HOST);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_int_new(_("Yahoo Chat port"), "ycht-port", YAHOO_YCHT_PORT);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+#endif
+
+	my_protocol = plugin;
+	yahoo_register_commands();
+	yahoo_init_colorht();
+
+	purple_signal_connect(purple_get_core(), "uri-handler", plugin,
+		PURPLE_CALLBACK(yahoo_uri_handler), NULL);
+}
+
+PURPLE_INIT_PLUGIN(yahoo, init_plugin, info);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/yahoo/libyahoojp.c	Mon Jul 06 09:08:17 2009 +0000
@@ -0,0 +1,243 @@
+/*
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 <account.h>
+
+#include "libymsg.h"
+#include "yahoochat.h"
+#include "yahoo_aliases.h"
+#include "yahoo_doodle.h"
+#include "yahoo_filexfer.h"
+#include "yahoo_picture.h"
+
+static void yahoojp_register_commands(void)
+{
+	purple_cmd_register("join", "s", PURPLE_CMD_P_PRPL,
+	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
+	                  PURPLE_CMD_FLAG_PRPL_ONLY,
+	                  "prpl-yahoojp", yahoopurple_cmd_chat_join,
+	                  _("join &lt;room&gt;:  Join a chat room on the Yahoo network"), NULL);
+	purple_cmd_register("list", "", PURPLE_CMD_P_PRPL,
+	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
+	                  PURPLE_CMD_FLAG_PRPL_ONLY,
+	                  "prpl-yahoojp", yahoopurple_cmd_chat_list,
+	                  _("list: List rooms on the Yahoo network"), NULL);
+	purple_cmd_register("buzz", "", PURPLE_CMD_P_PRPL,
+	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
+	                  "prpl-yahoojp", yahoopurple_cmd_buzz,
+	                  _("buzz: Buzz a user to get their attention"), NULL);
+	purple_cmd_register("doodle", "", PURPLE_CMD_P_PRPL,
+	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
+	                  "prpl-yahoojp", yahoo_doodle_purple_cmd_start,
+	                 _("doodle: Request user to start a Doodle session"), NULL);
+}
+
+static GHashTable *
+yahoojp_get_account_text_table(PurpleAccount *account)
+{
+	GHashTable *table;
+	table = g_hash_table_new(g_str_hash, g_str_equal);
+	g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo JAPAN ID..."));
+	return table;
+}
+
+static gboolean yahoojp_unload_plugin(PurplePlugin *plugin)
+{
+	yahoo_dest_colorht();
+
+	return TRUE;
+}
+
+static PurpleWhiteboardPrplOps yahoo_whiteboard_prpl_ops =
+{
+	yahoo_doodle_start,
+	yahoo_doodle_end,
+	yahoo_doodle_get_dimensions,
+	NULL,
+	yahoo_doodle_get_brush,
+	yahoo_doodle_set_brush,
+	yahoo_doodle_send_draw_list,
+	yahoo_doodle_clear,
+
+	/* padding */
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static PurplePluginProtocolInfo prpl_info =
+{
+	OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC,
+	NULL, /* user_splits */
+	NULL, /* protocol_options */
+	{"png,gif,jpeg", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND},
+	yahoo_list_icon,
+	yahoo_list_emblem,
+	yahoo_status_text,
+	yahoo_tooltip_text,
+	yahoo_status_types,
+	yahoo_blist_node_menu,
+	yahoo_c_info,
+	yahoo_c_info_defaults,
+	yahoo_login,
+	yahoo_close,
+	yahoo_send_im,
+	NULL, /* set info */
+	yahoo_send_typing,
+	yahoo_get_info,
+	yahoo_set_status,
+	yahoo_set_idle,
+	NULL, /* change_passwd*/
+	yahoo_add_buddy,
+	NULL, /* add_buddies */
+	yahoo_remove_buddy,
+	NULL, /* remove_buddies */
+	NULL, /* add_permit */
+	yahoo_add_deny,
+	NULL, /* rem_permit */
+	yahoo_rem_deny,
+	yahoo_set_permit_deny,
+	yahoo_c_join,
+	NULL, /* reject chat invite */
+	yahoo_get_chat_name,
+	yahoo_c_invite,
+	yahoo_c_leave,
+	NULL, /* chat whisper */
+	yahoo_c_send,
+	yahoo_keepalive,
+	NULL, /* register_user */
+	NULL, /* get_cb_info */
+	NULL, /* get_cb_away */
+	yahoo_update_alias, /* alias_buddy */
+	yahoo_change_buddys_group,
+	yahoo_rename_group,
+	NULL, /* buddy_free */
+	NULL, /* convo_closed */
+	purple_normalize_nocase, /* normalize */
+	yahoo_set_buddy_icon,
+	NULL, /* void (*remove_group)(PurpleConnection *gc, const char *group);*/
+	NULL, /* char *(*get_cb_real_name)(PurpleConnection *gc, int id, const char *who); */
+	NULL, /* set_chat_topic */
+	NULL, /* find_blist_chat */
+	yahoo_roomlist_get_list,
+	yahoo_roomlist_cancel,
+	yahoo_roomlist_expand_category,
+	NULL, /* can_receive_file */
+	yahoo_send_file,
+	yahoo_new_xfer,
+	yahoo_offline_message, /* offline_message */
+	&yahoo_whiteboard_prpl_ops,
+	NULL, /* send_raw */
+	NULL, /* roomlist_room_serialize */
+	NULL, /* unregister_user */
+
+	yahoo_send_attention,
+	yahoo_attention_types,
+
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
+	yahoojp_get_account_text_table,    /* get_account_text_table */
+	NULL, /* initiate_media */
+	NULL  /* can_do_media */
+};
+
+static PurplePluginInfo info =
+{
+	PURPLE_PLUGIN_MAGIC,
+	PURPLE_MAJOR_VERSION,
+	PURPLE_MINOR_VERSION,
+	PURPLE_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */
+	"prpl-yahoojp",                                     /**< id             */
+	"Yahoo JAPAN",	                                      /**< name           */
+	DISPLAY_VERSION,                                  /**< version        */
+	                                                  /**  summary        */
+	N_("Yahoo JAPAN Protocol Plugin"),
+	                                                  /**  description    */
+	N_("Yahoo JAPAN Protocol Plugin"),
+	NULL,                                             /**< author         */
+	PURPLE_WEBSITE,                                     /**< homepage       */
+	NULL,                                             /**< load           */
+	yahoojp_unload_plugin,                              /**< unload         */
+	NULL,                                             /**< destroy        */
+	NULL,                                             /**< ui_info        */
+	&prpl_info,                                       /**< extra_info     */
+	NULL,
+	yahoo_actions,
+
+	/* padding */
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+	PurpleAccountOption *option;
+
+	option = purple_account_option_string_new(_("Pager server"), "server", YAHOOJP_PAGER_HOST);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOOJP_XFER_HOST);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_int_new(_("File transfer port"), "xfer_port", YAHOO_XFER_PORT);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOOJP_ROOMLIST_LOCALE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_string_new(_("Encoding"), "local_charset", "UTF-8");
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+
+#if 0
+	option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_string_new(_("Yahoo Chat server"), "ycht-server", YAHOO_YCHT_HOST);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_int_new(_("Yahoo Chat port"), "ycht-port", YAHOO_YCHT_PORT);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+#endif
+
+	yahoojp_register_commands();
+	yahoo_init_colorht();
+}
+
+PURPLE_INIT_PLUGIN(yahoojp, init_plugin, info);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/yahoo/libymsg.c	Mon Jul 06 09:08:17 2009 +0000
@@ -0,0 +1,4925 @@
+/*
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 "account.h"
+#include "accountopt.h"
+#include "blist.h"
+#include "cipher.h"
+#include "cmds.h"
+#include "core.h"
+#include "debug.h"
+#include "network.h"
+#include "notify.h"
+#include "privacy.h"
+#include "prpl.h"
+#include "proxy.h"
+#include "request.h"
+#include "server.h"
+#include "util.h"
+#include "version.h"
+#include "xmlnode.h"
+
+#include "libymsg.h"
+#include "yahoochat.h"
+#include "yahoo_aliases.h"
+#include "yahoo_doodle.h"
+#include "yahoo_filexfer.h"
+#include "yahoo_friend.h"
+#include "yahoo_packet.h"
+#include "yahoo_picture.h"
+#include "ycht.h"
+
+/* #define YAHOO_DEBUG */
+
+/* #define TRY_WEBMESSENGER_LOGIN 0 */
+
+/* One hour */
+#define PING_TIMEOUT 3600
+
+/* One minute */
+#define KEEPALIVE_TIMEOUT 60
+
+#ifdef TRY_WEBMESSENGER_LOGIN
+static void yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message);
+#endif /* TRY_WEBMESSENGER_LOGIN */
+
+static gboolean yahoo_is_japan(PurpleAccount *account)
+{
+	return purple_strequal(purple_account_get_protocol_id(account), "prpl-yahoojp");
+}
+
+static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFriend *f)
+{
+	char *status = NULL;
+
+	if (!gc || !name || !f || !purple_find_buddy(purple_connection_get_account(gc), name))
+		return;
+
+	switch (f->status) {
+	case YAHOO_STATUS_OFFLINE:
+		status = YAHOO_STATUS_TYPE_OFFLINE;
+		break;
+	case YAHOO_STATUS_AVAILABLE:
+		status = YAHOO_STATUS_TYPE_AVAILABLE;
+		break;
+	case YAHOO_STATUS_BRB:
+		status = YAHOO_STATUS_TYPE_BRB;
+		break;
+	case YAHOO_STATUS_BUSY:
+		status = YAHOO_STATUS_TYPE_BUSY;
+		break;
+	case YAHOO_STATUS_NOTATHOME:
+		status = YAHOO_STATUS_TYPE_NOTATHOME;
+		break;
+	case YAHOO_STATUS_NOTATDESK:
+		status = YAHOO_STATUS_TYPE_NOTATDESK;
+		break;
+	case YAHOO_STATUS_NOTINOFFICE:
+		status = YAHOO_STATUS_TYPE_NOTINOFFICE;
+		break;
+	case YAHOO_STATUS_ONPHONE:
+		status = YAHOO_STATUS_TYPE_ONPHONE;
+		break;
+	case YAHOO_STATUS_ONVACATION:
+		status = YAHOO_STATUS_TYPE_ONVACATION;
+		break;
+	case YAHOO_STATUS_OUTTOLUNCH:
+		status = YAHOO_STATUS_TYPE_OUTTOLUNCH;
+		break;
+	case YAHOO_STATUS_STEPPEDOUT:
+		status = YAHOO_STATUS_TYPE_STEPPEDOUT;
+		break;
+	case YAHOO_STATUS_INVISIBLE: /* this should never happen? */
+		status = YAHOO_STATUS_TYPE_INVISIBLE;
+		break;
+	case YAHOO_STATUS_CUSTOM:
+	case YAHOO_STATUS_IDLE:
+		if (!f->away)
+			status = YAHOO_STATUS_TYPE_AVAILABLE;
+		else
+			status = YAHOO_STATUS_TYPE_AWAY;
+		break;
+	default:
+		purple_debug_warning("yahoo", "Warning, unknown status %d\n", f->status);
+		break;
+	}
+
+	if (status) {
+		if (f->status == YAHOO_STATUS_CUSTOM)
+			purple_prpl_got_user_status(purple_connection_get_account(gc), name, status, "message",
+			                          yahoo_friend_get_status_message(f), NULL);
+		else
+			purple_prpl_got_user_status(purple_connection_get_account(gc), name, status, NULL);
+	}
+
+	if (f->idle != 0)
+		purple_prpl_got_user_idle(purple_connection_get_account(gc), name, TRUE, f->idle);
+	else
+		purple_prpl_got_user_idle(purple_connection_get_account(gc), name, FALSE, 0);
+
+	if (f->sms)
+		purple_prpl_got_user_status(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE, NULL);
+	else
+		purple_prpl_got_user_status_deactive(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE);
+}
+
+static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	PurpleAccount *account = purple_connection_get_account(gc);
+	GSList *l = pkt->hash;
+	YahooFriend *f = NULL;
+	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))
+			purple_account_set_password(account, NULL);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE,
+			_("You have signed on from another location"));
+		return;
+	}
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 0: /* we won't actually do anything with this */
+		case 1: /* we won't actually do anything with this */
+			break;
+		case 8: /* how many online buddies we have */
+			break;
+		case 7: /* the current buddy */
+			/* update the previous buddy before changing the variables */
+			if (f) {
+				if (message)
+					yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
+				if (name)
+					yahoo_update_status(gc, name, f);
+			}
+			name = message = NULL;
+			f = NULL;
+			if (pair->value && g_utf8_validate(pair->value, -1, NULL)) {
+				GSList *tmplist;
+				int protocol = 0;
+
+				name = pair->value;
+
+				/* Look ahead to see if we have the protocol info about the buddy */
+				for (tmplist = l->next; tmplist; tmplist = tmplist->next) {
+					struct yahoo_pair *p = tmplist->data;
+					if (p->key == 7)
+						break;
+					if (p->key == 241) {
+						if(strtol(p->value, NULL, 10) == 2) {
+							g_free(msn_name);
+							msn_name = g_strconcat("msn/", name, NULL);
+							name = msn_name;
+							protocol = 2;
+						}
+						break;
+					}
+				}
+				f = yahoo_friend_find_or_new(gc, name);
+				f->protocol = protocol;
+			}
+			break;
+		case 10: /* state */
+			if (!f)
+				break;
+
+			f->status = strtol(pair->value, NULL, 10);
+			if ((f->status >= YAHOO_STATUS_BRB) && (f->status <= YAHOO_STATUS_STEPPEDOUT))
+				f->away = 1;
+			else
+				f->away = 0;
+
+			if (f->status == YAHOO_STATUS_IDLE) {
+				/* Idle may have already been set in a more precise way in case 137 */
+				if (f->idle == 0)
+					f->idle = time(NULL);
+			} else
+				f->idle = 0;
+
+			if (f->status != YAHOO_STATUS_CUSTOM)
+				yahoo_friend_set_status_message(f, NULL);
+
+			f->sms = 0;
+			break;
+		case 19: /* custom message */
+			if (f)
+				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;
+		case 47: /* is custom status away or not? 2=idle*/
+			if (!f)
+				break;
+
+			/* I have no idea what it means when this is
+			 * set when someone's available, but it doesn't
+			 * mean idle. */
+			if (f->status == YAHOO_STATUS_AVAILABLE)
+				break;
+
+			f->away = strtol(pair->value, NULL, 10);
+			if (f->away == 2) {
+				/* Idle may have already been set in a more precise way in case 137 */
+				if (f->idle == 0)
+					f->idle = time(NULL);
+			}
+
+			break;
+		case 138: /* either we're not idle, or we are but won't say how long */
+			if (!f)
+				break;
+
+			if (f->idle)
+				f->idle = -1;
+			break;
+		case 137: /* usually idle time in seconds, sometimes login time */
+			if (!f)
+				break;
+
+			if (f->status != YAHOO_STATUS_AVAILABLE)
+				f->idle = time(NULL) - strtol(pair->value, NULL, 10);
+			break;
+		case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
+			if (strtol(pair->value, NULL, 10) == 0) {
+				if (f)
+					f->status = YAHOO_STATUS_OFFLINE;
+				if (name) {
+					purple_prpl_got_user_status(account, name, "offline", NULL);
+					purple_prpl_got_user_status_deactive(account, name, YAHOO_STATUS_TYPE_MOBILE);
+				}
+				break;
+			}
+			break;
+		case 60: /* SMS */
+			if (f) {
+				f->sms = strtol(pair->value, NULL, 10);
+				yahoo_update_status(gc, name, f);
+			}
+			break;
+		case 197: /* Avatars */
+		{
+			guchar *decoded;
+			char *tmp;
+			gsize len;
+
+			if (pair->value) {
+				decoded = purple_base64_decode(pair->value, &len);
+				if (len) {
+					tmp = purple_str_binary_to_ascii(decoded, len);
+					purple_debug_info("yahoo", "Got key 197, value = %s\n", tmp);
+					g_free(tmp);
+				}
+				g_free(decoded);
+			}
+			break;
+		}
+		case 192: /* Pictures, aka Buddy Icons, checksum */
+		{
+			/* FIXME: Please, if you know this protocol,
+			 * FIXME: fix up the strtol() stuff if possible. */
+			int cksum = strtol(pair->value, NULL, 10);
+			const char *locksum = NULL;
+			PurpleBuddy *b;
+
+			if (!name)
+				break;
+
+			b = purple_find_buddy(gc->account, name);
+
+			if (!cksum || (cksum == -1)) {
+				if (f)
+					yahoo_friend_set_buddy_icon_need_request(f, TRUE);
+				purple_buddy_icons_set_for_user(gc->account, name, NULL, 0, NULL);
+				break;
+			}
+
+			if (!f)
+				break;
+
+			yahoo_friend_set_buddy_icon_need_request(f, FALSE);
+			if (b) {
+				locksum = purple_buddy_icons_get_checksum_for_user(b);
+				if (!locksum || (cksum != strtol(locksum, NULL, 10)))
+					yahoo_send_picture_request(gc, name);
+			}
+
+			break;
+		}
+		case 16: /* Custom error message */
+			{
+				char *tmp = yahoo_string_decode(gc, pair->value, TRUE);
+				purple_notify_error(gc, NULL, tmp, NULL);
+				g_free(tmp);
+			}
+			break;
+		case 97: /* Unicode status message */
+			unicode = !strcmp(pair->value, "1");
+			break;
+		case 244: /* client version number. Yahoo Client Detection */
+			if(f && strtol(pair->value, NULL, 10))
+				f->version_id = strtol(pair->value, NULL, 10);
+			break;
+		case 241: /* protocol buddy belongs to */
+			break;  /* We process this when get '7' */
+		default:
+			purple_debug_warning("yahoo",
+					   "Unknown status key %d\n", pair->key);
+			break;
+		}
+
+		l = l->next;
+	}
+
+	if (f) {
+		if (pkt->service == YAHOO_SERVICE_LOGOFF)
+			f->status = YAHOO_STATUS_OFFLINE;
+		if (message)
+			yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
+
+		if (name) /* update the last buddy */
+			yahoo_update_status(gc, name, f);
+	}
+	g_free(msn_name);
+}
+
+static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group)
+{
+	PurpleBuddy *b;
+	PurpleGroup *g;
+	GSList *list, *i;
+	gboolean onlist = 0;
+	char *oname = NULL;
+	char **oname_p = &oname;
+	GSList **list_p = &list;
+
+	if (!g_hash_table_lookup_extended(ht, purple_normalize(account, name), (gpointer *) oname_p, (gpointer *) list_p))
+		list = purple_find_buddies(account, name);
+	else
+		g_hash_table_steal(ht, name);
+
+	for (i = list; i; i = i->next) {
+		b = i->data;
+		g = purple_buddy_get_group(b);
+		if (!purple_utf8_strcasecmp(group, purple_group_get_name(g))) {
+			purple_debug_misc("yahoo",
+				"Oh good, %s is in the right group (%s).\n", name, group);
+			list = g_slist_delete_link(list, i);
+			onlist = 1;
+			break;
+		}
+	}
+
+	if (!onlist) {
+		purple_debug_misc("yahoo",
+			"Uhoh, %s isn't on the list (or not in this group), adding him to group %s.\n", name, group);
+		if (!(g = purple_find_group(group))) {
+			g = purple_group_new(group);
+			purple_blist_add_group(g, NULL);
+		}
+		b = purple_buddy_new(account, name, NULL);
+		purple_blist_add_buddy(b, NULL, g, NULL);
+	}
+
+	if (list) {
+		if (!oname)
+			oname = g_strdup(purple_normalize(account, name));
+		g_hash_table_insert(ht, oname, list);
+	} else if (oname)
+		g_free(oname);
+}
+
+static void yahoo_do_group_cleanup(gpointer key, gpointer value, gpointer user_data)
+{
+	char *name = key;
+	GSList *list = value, *i;
+	PurpleBuddy *b;
+	PurpleGroup *g;
+
+	for (i = list; i; i = i->next) {
+		b = i->data;
+		g = purple_buddy_get_group(b);
+		purple_debug_misc("yahoo", "Deleting Buddy %s from group %s.\n", name,
+				purple_group_get_name(g));
+		purple_blist_remove_buddy(b);
+	}
+}
+
+static char *_getcookie(char *rawcookie)
+{
+	char *cookie = NULL;
+	char *tmpcookie;
+	char *cookieend;
+
+	if (strlen(rawcookie) < 2)
+		return NULL;
+	tmpcookie = g_strdup(rawcookie+2);
+	cookieend = strchr(tmpcookie, ';');
+
+	if (cookieend)
+		*cookieend = '\0';
+
+	cookie = g_strdup(tmpcookie);
+	g_free(tmpcookie);
+
+	return cookie;
+}
+
+static void yahoo_process_cookie(struct yahoo_data *yd, char *c)
+{
+	if (c[0] == 'Y') {
+		if (yd->cookie_y)
+			g_free(yd->cookie_y);
+		yd->cookie_y = _getcookie(c);
+	} else if (c[0] == 'T') {
+		if (yd->cookie_t)
+			g_free(yd->cookie_t);
+		yd->cookie_t = _getcookie(c);
+	} else
+		purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]);
+	yd->cookies = g_slist_prepend(yd->cookies, g_strdup(c));
+}
+
+static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	GSList *l = pkt->hash;
+
+	PurpleAccount *account = purple_connection_get_account(gc);
+	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);
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+		l = l->next;
+
+		switch (pair->key) {
+		case 302:
+			/* This is always 318 before a group, 319 before the first s/n in a group, 320 before any ignored s/n.
+			 * It is not sent for s/n's in a group after the first.
+			 * All ignored s/n's are listed last, so when we see a 320 we clear the group and begin marking the
+			 * s/n's as ignored.  It is always followed by an identical 300 key.
+			 */
+			if (pair->value && !strcmp(pair->value, "320")) {
+				/* No longer in any group; this indicates the start of the ignore list. */
+				g_free(yd->current_list15_grp);
+				yd->current_list15_grp = NULL;
+			}
+
+			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;
+		case 65: /* This is the group */
+			g_free(yd->current_list15_grp);
+			yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE);
+			break;
+		case 7: /* buddy's s/n */
+			temp = g_strdup(purple_normalize(account, pair->value));
+			break;
+		case 241: /* another protocol user */
+			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 */
+			stealth = strtol(pair->value, NULL, 10);
+			break;
+		/* case 242: */ /* this seems related to 241 */
+			/* break; */
+		}
+	}
+
+	g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
+
+	/* Now that we have processed the buddy list, we can say yahoo has connected */
+	purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account)));
+	purple_connection_set_state(gc, PURPLE_CONNECTED);
+	yd->logged_in = TRUE;
+	if (yd->picture_upload_todo) {
+		yahoo_buddy_icon_upload(gc, yd->picture_upload_todo);
+		yd->picture_upload_todo = NULL;
+	}
+	yahoo_set_status(account, purple_account_get_active_status(account));
+	purple_debug_info("yahoo","Authentication: Connection established\n");
+
+	g_hash_table_destroy(ht);
+	g_free(norm_bud);
+	g_free(temp);
+}
+
+static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	GSList *l = pkt->hash;
+	gboolean export = FALSE;
+	gboolean got_serv_list = FALSE;
+	PurpleBuddy *b;
+	PurpleGroup *g;
+	YahooFriend *f = NULL;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	struct yahoo_data *yd = gc->proto_data;
+	GHashTable *ht;
+
+	char **lines;
+	char **split;
+	char **buddies;
+	char **tmp, **bud, *norm_bud;
+	char *grp = NULL;
+
+	if (pkt->id)
+		yd->session_id = pkt->id;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+		l = l->next;
+
+		switch (pair->key) {
+		case 87:
+			if (!yd->tmp_serv_blist)
+				yd->tmp_serv_blist = g_string_new(pair->value);
+			else
+				g_string_append(yd->tmp_serv_blist, pair->value);
+			break;
+		case 88:
+			if (!yd->tmp_serv_ilist)
+				yd->tmp_serv_ilist = g_string_new(pair->value);
+			else
+				g_string_append(yd->tmp_serv_ilist, pair->value);
+			break;
+		case 89:
+			yd->profiles = g_strsplit(pair->value, ",", -1);
+			break;
+		case 59: /* cookies, yum */
+			yahoo_process_cookie(yd, pair->value);
+			break;
+		case YAHOO_SERVICE_PRESENCE_PERM:
+			if (!yd->tmp_serv_plist)
+				yd->tmp_serv_plist = g_string_new(pair->value);
+			else
+				g_string_append(yd->tmp_serv_plist, pair->value);
+			break;
+		}
+	}
+
+	if (pkt->status != 0)
+		return;
+
+	if (yd->tmp_serv_blist) {
+		ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
+
+		lines = g_strsplit(yd->tmp_serv_blist->str, "\n", -1);
+		for (tmp = lines; *tmp; tmp++) {
+			split = g_strsplit(*tmp, ":", 2);
+			if (!split)
+				continue;
+			if (!split[0] || !split[1]) {
+				g_strfreev(split);
+				continue;
+			}
+			grp = yahoo_string_decode(gc, split[0], FALSE);
+			buddies = g_strsplit(split[1], ",", -1);
+			for (bud = buddies; bud && *bud; bud++) {
+				norm_bud = g_strdup(purple_normalize(account, *bud));
+				f = yahoo_friend_find_or_new(gc, norm_bud);
+
+				if (!(b = purple_find_buddy(account, norm_bud))) {
+					if (!(g = purple_find_group(grp))) {
+						g = purple_group_new(grp);
+						purple_blist_add_group(g, NULL);
+					}
+					b = purple_buddy_new(account, norm_bud, NULL);
+					purple_blist_add_buddy(b, NULL, g, NULL);
+					export = TRUE;
+				}
+
+				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);
+			g_strfreev(split);
+			g_free(grp);
+		}
+		g_strfreev(lines);
+
+		g_string_free(yd->tmp_serv_blist, TRUE);
+		yd->tmp_serv_blist = NULL;
+		g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
+		g_hash_table_destroy(ht);
+	}
+
+	if (yd->tmp_serv_ilist) {
+		buddies = g_strsplit(yd->tmp_serv_ilist->str, ",", -1);
+		for (bud = buddies; bud && *bud; bud++) {
+			/* The server is already ignoring the user */
+			got_serv_list = TRUE;
+			purple_privacy_deny_add(account, *bud, 1);
+		}
+		g_strfreev(buddies);
+
+		g_string_free(yd->tmp_serv_ilist, TRUE);
+		yd->tmp_serv_ilist = NULL;
+	}
+
+	if (got_serv_list &&
+		((account->perm_deny != PURPLE_PRIVACY_ALLOW_BUDDYLIST) &&
+		(account->perm_deny != PURPLE_PRIVACY_DENY_ALL) &&
+		(account->perm_deny != PURPLE_PRIVACY_ALLOW_USERS)))
+	{
+		account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
+		purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_PRIVACY_DENY_USERS.\n",
+				account->username);
+	}
+
+	if (yd->tmp_serv_plist) {
+		buddies = g_strsplit(yd->tmp_serv_plist->str, ",", -1);
+		for (bud = buddies; bud && *bud; bud++) {
+			f = yahoo_friend_find(gc, *bud);
+			if (f) {
+				purple_debug_info("yahoo", "%s setting presence for %s to PERM_OFFLINE\n",
+						account->username, *bud);
+				f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
+			}
+		}
+		g_strfreev(buddies);
+		g_string_free(yd->tmp_serv_plist, TRUE);
+		yd->tmp_serv_plist = NULL;
+
+	}
+	/* Now that we've got the list, request aliases */
+	yahoo_fetch_aliases(gc);
+}
+
+/* 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;
+	char *from = NULL;
+	char *stat = NULL;
+	char *game = NULL;
+	YahooFriend *f = NULL;
+	GSList *l = pkt->hash;
+	gint val_11 = 0;
+	struct yahoo_data *yd = gc->proto_data;
+	gboolean msn = FALSE;
+
+	account = purple_connection_get_account(gc);
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 4 || pair->key == 1)
+			from = pair->value;
+		if (pair->key == 49)
+			msg = pair->value;
+		if (pair->key == 13)
+			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 (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
+		&& (purple_privacy_check(account, from)))
+	{
+		if(msn) {
+			char *msn_from = g_strconcat("msn/", from, NULL);
+			if (*stat == '1')
+				serv_got_typing(gc, msn_from, 0, PURPLE_TYPING);
+			else
+				serv_got_typing_stopped(gc, msn_from);
+			g_free(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);
+
+		if (!bud) {
+			purple_debug_warning("yahoo",
+					   "%s is playing a game, and doesn't want you to know.\n", from);
+		}
+
+		f = yahoo_friend_find(gc, from);
+		if (!f)
+			return; /* if they're not on the list, don't bother */
+
+		yahoo_friend_set_game(f, NULL);
+
+		if (*stat == '1') {
+			yahoo_friend_set_game(f, game);
+			if (bud)
+				yahoo_update_status(gc, from, f);
+		}
+	} else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
+		PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account);
+		char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from);
+		purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
+		g_free(buf);
+	}
+}
+
+
+struct _yahoo_im {
+	char *from;
+	char *active_id;
+	int time;
+	int utf8;
+	int buddy_icon;
+	char *id;
+	char *msg;
+	gboolean msn;
+};
+
+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;
+	GSList *l = pkt->hash;
+	GSList *list = NULL;
+	struct _yahoo_im *im = NULL;
+	const char *imv = NULL;
+	gint val_11 = 0;
+
+	account = purple_connection_get_account(gc);
+
+	if (pkt->status <= 1 || pkt->status == 5 || 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 || pair->key == 1) {
+				im = g_new0(struct _yahoo_im, 1);
+				list = g_slist_append(list, im);
+				im->from = pair->value;
+				im->time = time(NULL);
+				im->utf8 = TRUE;
+			}
+			if (im && pair->key == 5)
+				im->active_id = pair->value;
+			if (pair->key == 97)
+				if (im)
+					im->utf8 = strtol(pair->value, NULL, 10);
+			if (pair->key == 15)
+				if (im)
+					im->time = strtol(pair->value, NULL, 10);
+			if (pair->key == 206)
+				if (im)
+					im->buddy_icon = strtol(pair->value, NULL, 10);
+			if (pair->key == 14) {
+				if (im)
+					im->msg = pair->value;
+			}
+			if (im && pair->key == 241) {
+				if(strtol(pair->value, NULL, 10) == 2)
+					im->msn = TRUE;
+			}
+			/* peer session id */
+			if (pair->key == 11) {
+				if (im)
+					val_11 = strtol(pair->value, NULL, 10);
+			}
+			/* IMV key */
+			if (pair->key == 63)
+			{
+				imv = pair->value;
+			}
+			if (pair->key == 429)
+				if (im)
+					im->id = pair->value;
+			l = l->next;
+		}
+	} else if (pkt->status == 2) {
+		purple_notify_error(gc, NULL,
+		                  _("Your Yahoo! message did not get sent."), NULL);
+	}
+
+	/* disconnect the peer if connected through p2p and sends wrong value for session id */
+	if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) {
+		purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from);
+		/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
+		g_hash_table_remove(yd->peers, im->from);
+		return;
+	}
+
+	/** TODO: It seems that this check should be per IM, not global */
+	/* Check for the Doodle IMV */
+	if (im != NULL && imv!= NULL && im->from != NULL)
+	{
+		g_hash_table_replace(yd->imvironments, g_strdup(im->from), g_strdup(imv));
+
+		if (strstr(imv, "doodle;") != NULL)
+		{
+			PurpleWhiteboard *wb;
+
+			if (!purple_privacy_check(account, im->from)) {
+				purple_debug_info("yahoo", "Doodle request from %s dropped.\n", im->from);
+				return;
+			}
+
+			/* I'm not sure the following ever happens -DAA */
+
+			wb = purple_whiteboard_get_session(account, im->from);
+
+			/* If a Doodle session doesn't exist between this user */
+			if(wb == NULL)
+			{
+				doodle_session *ds;
+				wb = purple_whiteboard_create(account, im->from, DOODLE_STATE_REQUESTED);
+				ds = wb->proto_data;
+				ds->imv_key = g_strdup(imv);
+
+				yahoo_doodle_command_send_request(gc, im->from, imv);
+				yahoo_doodle_command_send_ready(gc, im->from, imv);
+			}
+		}
+	}
+
+	for (l = list; l; l = l->next) {
+		YahooFriend *f;
+		char *m, *m2;
+		char *msn_from = NULL;
+		const char *from;
+		PurpleConversation *c;
+		im = l->data;
+
+		if (!im->from || !im->msg) {
+			g_free(im);
+			continue;
+		}
+
+		if (!purple_privacy_check(account, im->from)) {
+			purple_debug_info("yahoo", "Message from %s dropped.\n", im->from);
+			return;
+		}
+
+		/*
+		 * TODO: Is there anything else we should check when determining whether
+		 *       we should send an acknowledgement?
+		 */
+		if (im->id != NULL) {
+			/* Send acknowledgement.  If we don't do this then the official
+			 * Yahoo Messenger client for Windows will send us the same
+			 * message 7 seconds later as an offline message.  This is true
+			 * for at least version 9.0.0.2162 on Windows XP. */
+			struct yahoo_packet *pkt2;
+			pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK,
+					YAHOO_STATUS_AVAILABLE, pkt->id);
+			yahoo_packet_hash(pkt2, "ssisii",
+					1, im->active_id,  /* May not always be the connection's display name */
+					5, im->from,
+					302, 430,
+					430, im->id,
+					303, 430,
+					450, 0);
+			yahoo_packet_send_and_free(pkt2, yd);
+		}
+
+		m = yahoo_string_decode(gc, im->msg, im->utf8);
+		/* This may actually not be necessary, but it appears
+		 * that at least at one point some clients were sending
+		 * "\r\n" as line delimiters, so we want to avoid double
+		 * lines. */
+		m2 = purple_strreplace(m, "\r\n", "\n");
+		g_free(m);
+		m = m2;
+		purple_util_chrreplace(m, '\r', '\n');
+
+		if (im->msn) {
+			msn_from = g_strconcat("msn/", im->from, NULL);
+			from = msn_from;
+		} else {
+			from = im->from;
+		}
+
+		c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account);
+
+		if (!strcmp(m, "<ding>")) {
+			char *username;
+
+			if (c == NULL) {
+				c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from);
+			}
+			username = g_markup_escape_text(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, from, m2, 0, im->time);
+		g_free(m2);
+
+		/* laters : implement buddy icon for msn friends */
+		if (!im->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);
+}
+
+static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	GSList *l = pkt->hash;
+	char *prim, *me = NULL, *msg = NULL;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		if (pair->key == 5)
+			me = pair->value;
+		if (pair->key == 14)
+			msg = pair->value;
+
+		l = l->next;
+	}
+
+	if (!msg || !g_utf8_validate(msg, -1, NULL))
+		return;
+
+	prim = g_strdup_printf(_("Yahoo! system message for %s:"),
+	                       me?me:purple_connection_get_display_name(gc));
+	purple_notify_info(NULL, NULL, prim, msg);
+	g_free(prim);
+}
+
+struct yahoo_add_request {
+	PurpleConnection *gc;
+	char *id;
+	char *who;
+	int protocol;
+};
+
+static void
+yahoo_buddy_add_authorize_cb(gpointer data)
+{
+	struct yahoo_add_request *add_req = data;
+	struct yahoo_packet *pkt;
+	struct yahoo_data *yd = add_req->gc->proto_data;
+	const char *who = add_req->who;
+
+	if (add_req->protocol == 2)
+		who += 4;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pkt, "ssiii",
+					  1, add_req->id,
+					  5, who,
+					  241, add_req->protocol,
+					  13, 1,
+					  334, 0);
+	yahoo_packet_send_and_free(pkt, yd);
+
+	g_free(add_req->id);
+	g_free(add_req->who);
+	g_free(add_req);
+}
+
+static void
+yahoo_buddy_add_deny_cb(struct yahoo_add_request *add_req, const char *msg)
+{
+	struct yahoo_data *yd = add_req->gc->proto_data;
+	struct yahoo_packet *pkt;
+	char *encoded_msg = NULL;
+	PurpleAccount *account = purple_connection_get_account(add_req->gc);
+
+	if (msg && *msg)
+		encoded_msg = yahoo_string_encode(add_req->gc, msg, NULL);
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
+			YAHOO_STATUS_AVAILABLE, 0);
+
+	yahoo_packet_hash(pkt, "ssiiis",
+			1, purple_normalize(account, purple_account_get_username(account)),
+			5, add_req->who,
+			13, 2,
+			334, 0,
+			97, 1,
+			14, encoded_msg ? encoded_msg : "");
+
+	yahoo_packet_send_and_free(pkt, yd);
+
+	g_free(encoded_msg);
+
+	g_free(add_req->id);
+	g_free(add_req->who);
+	g_free(add_req);
+}
+
+static void
+yahoo_buddy_add_deny_noreason_cb(struct yahoo_add_request *add_req, const char*msg)
+{
+	yahoo_buddy_add_deny_cb(add_req, NULL);
+}
+
+static void
+yahoo_buddy_add_deny_reason_cb(gpointer data) {
+	struct yahoo_add_request *add_req = data;
+	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
+			NULL, _("No reason given."), TRUE, FALSE, NULL,
+			_("OK"), G_CALLBACK(yahoo_buddy_add_deny_cb),
+			_("Cancel"), G_CALLBACK(yahoo_buddy_add_deny_noreason_cb),
+			purple_connection_get_account(add_req->gc), add_req->who, NULL,
+			add_req);
+}
+
+static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
+{
+	char *notify_msg;
+	struct yahoo_data *yd = gc->proto_data;
+
+	if (who == NULL)
+		return;
+
+	if (reason != NULL) {
+		char *msg2 = yahoo_string_decode(gc, reason, FALSE);
+		notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
+		g_free(msg2);
+	} else
+		notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who);
+
+	purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg);
+	g_free(notify_msg);
+
+	g_hash_table_remove(yd->friends, who);
+	purple_prpl_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
+	/* TODO: Shouldn't we remove the buddy from our local list? */
+}
+
+static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) {
+	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) {
+		char *temp = NULL;
+		char *who = NULL;
+		int response = 0;
+
+		while (l) {
+			struct yahoo_pair *pair = l->data;
+
+			switch (pair->key) {
+			case 4:
+				temp = pair->value;
+				break;
+			case 13:
+				response = strtol(pair->value, NULL, 10);
+				break;
+			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 */
+			purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
+			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;
+
+		while (l) {
+			struct yahoo_pair *pair = l->data;
+
+			switch (pair->key) {
+			case 4:
+				temp = pair->value;
+				add_req->who = g_strdup(pair->value);
+				break;
+			case 5:
+				add_req->id = g_strdup(pair->value);
+				break;
+			case 14:
+				msg = pair->value;
+				break;
+			case 216:
+				firstname = pair->value;
+				break;
+			case 241:
+				add_req->protocol = strtol(pair->value, NULL, 10);
+				break;
+			case 254:
+				lastname = pair->value;
+				break;
+
+			}
+			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;
+
+			if (!purple_privacy_check(account, add_req->who))
+			{
+				purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
+						  add_req->who);
+				yahoo_buddy_add_deny_cb(add_req, NULL);
+				return;
+			}
+
+			if (msg)
+				dec_msg = yahoo_string_decode(gc, msg, FALSE);
+
+			if (firstname && lastname)
+				alias = g_strdup_printf("%s %s", firstname, lastname);
+			else if (firstname)
+				alias = g_strdup(firstname);
+			else if (lastname)
+				alias = g_strdup(lastname);
+
+			/* DONE! this is almost exactly the same as what MSN does,
+			 * this should probably be moved to the core.
+			 */
+			 purple_account_request_authorization(account, add_req->who, add_req->id,
+					alias, dec_msg,
+					purple_find_buddy(account, add_req->who) != NULL,
+					yahoo_buddy_add_authorize_cb,
+					yahoo_buddy_add_deny_reason_cb,
+					add_req);
+			g_free(alias);
+			g_free(dec_msg);
+		} else {
+			g_free(add_req->id);
+			g_free(add_req->who);
+			g_free(add_req);
+		}
+	} else {
+		purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status);
+	}
+}
+
+/* I don't think this happens anymore in Version 15 */
+static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) {
+	PurpleAccount *account;
+	struct yahoo_add_request *add_req;
+	char *msg = NULL;
+	GSList *l = pkt->hash;
+
+	account = purple_connection_get_account(gc);
+
+	add_req = g_new0(struct yahoo_add_request, 1);
+	add_req->gc = gc;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 1:
+			add_req->id = g_strdup(pair->value);
+			break;
+		case 3:
+			add_req->who = g_strdup(pair->value);
+			break;
+		case 15: /* time, for when they add us and we're offline */
+			break;
+		case 14:
+			msg = pair->value;
+			break;
+		}
+		l = l->next;
+	}
+
+	if (add_req->id && add_req->who) {
+		char *dec_msg = NULL;
+
+		if (!purple_privacy_check(account, add_req->who)) {
+			purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
+					  add_req->who);
+			yahoo_buddy_add_deny_cb(add_req, NULL);
+			return;
+		}
+
+		if (msg)
+			dec_msg = yahoo_string_decode(gc, msg, FALSE);
+
+		/* DONE! this is almost exactly the same as what MSN does,
+		 * this should probably be moved to the core.
+		 */
+		 purple_account_request_authorization(account, add_req->who, add_req->id,
+				NULL, dec_msg,
+				purple_find_buddy(account,add_req->who) != NULL,
+						yahoo_buddy_add_authorize_cb,
+						yahoo_buddy_add_deny_reason_cb, add_req);
+		g_free(dec_msg);
+	} else {
+		g_free(add_req->id);
+		g_free(add_req->who);
+		g_free(add_req);
+	}
+}
+
+/* I have no idea if this every gets called in version 15 */
+static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	char *who = NULL;
+	char *msg = NULL;
+	GSList *l = pkt->hash;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 3:
+			who = pair->value;
+			break;
+		case 14:
+			msg = pair->value;
+			break;
+		}
+		l = l->next;
+	}
+
+	yahoo_buddy_denied_our_add(gc, who, msg);
+}
+
+static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	switch (pkt->status) {
+	case 1:
+		yahoo_process_status(gc, pkt);
+		return;
+	case 3:
+		yahoo_buddy_added_us(gc, pkt);
+		break;
+	case 7:
+		yahoo_buddy_denied_our_add_old(gc, pkt);
+		break;
+	default:
+		break;
+	}
+}
+
+#define OUT_CHARSET "utf-8"
+
+static char *yahoo_decode(const char *text)
+{
+	char *converted = NULL;
+	char *n, *new;
+	const char *end, *p;
+	int i, k;
+
+	n = new = g_malloc(strlen (text) + 1);
+	end = text + strlen(text);
+
+	for (p = text; p < end; p++, n++) {
+		if (*p == '\\') {
+			if (p[1] >= '0' && p[1] <= '7') {
+				p += 1;
+				for (i = 0, k = 0; k < 3; k += 1) {
+					char c = p[k];
+					if (c < '0' || c > '7') break;
+					i *= 8;
+					i += c - '0';
+				}
+				*n = i;
+				p += k - 1;
+			} else { /* bug 959248 */
+				/* If we see a \ not followed by an octal number,
+				 * it means that it is actually a \\ with one \
+				 * already eaten by some unknown function.
+				 * This is arguably broken.
+				 *
+				 * I think wing is wrong here, there is no function
+				 * called that I see that could have done it. I guess
+				 * it is just really sending single \'s. That's yahoo
+				 * for you.
+				 */
+				*n = *p;
+			}
+		}
+		else
+			*n = *p;
+	}
+
+	*n = '\0';
+
+	if (strstr(text, "\033$B"))
+		converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL);
+	if (!converted)
+		converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL);
+	g_free(new);
+
+	return converted;
+}
+
+static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	PurpleAccount *account = purple_connection_get_account(gc);
+	struct yahoo_data *yd = gc->proto_data;
+	const char *who = NULL;
+	const char *email = NULL;
+	const char *subj = NULL;
+	const char *yahoo_mail_url = (yd->jp? YAHOOJP_MAIL_URL: YAHOO_MAIL_URL);
+	int count = 0;
+	GSList *l = pkt->hash;
+
+	if (!purple_account_get_check_mail(account))
+		return;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 9)
+			count = strtol(pair->value, NULL, 10);
+		else if (pair->key == 43)
+			who = pair->value;
+		else if (pair->key == 42)
+			email = pair->value;
+		else if (pair->key == 18)
+			subj = pair->value;
+		l = l->next;
+	}
+
+	if (who && subj && email && *email) {
+		char *dec_who = yahoo_decode(who);
+		char *dec_subj = yahoo_decode(subj);
+		char *from = g_strdup_printf("%s (%s)", dec_who, email);
+
+		purple_notify_email(gc, dec_subj, from, purple_account_get_username(account),
+						  yahoo_mail_url, NULL, NULL);
+
+		g_free(dec_who);
+		g_free(dec_subj);
+		g_free(from);
+	} else if (count > 0) {
+		const char *tos[2] = { purple_account_get_username(account) };
+		const char *urls[2] = { yahoo_mail_url };
+
+		purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls,
+						   NULL, NULL);
+	}
+}
+
+/* We use this structure once while we authenticate */
+struct yahoo_auth_data
+{
+	PurpleConnection *gc;
+	char *seed;
+};
+
+/* This is the y64 alphabet... it's like base64, but has a . and a _ */
+static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
+
+/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function
+ * in util.c, but it is different from the one yahoo uses */
+static void to_y64(char *out, const unsigned char *in, gsize inlen)
+     /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
+{
+	for (; inlen >= 3; inlen -= 3)
+		{
+			*out++ = base64digits[in[0] >> 2];
+			*out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
+			*out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
+			*out++ = base64digits[in[2] & 0x3f];
+			in += 3;
+		}
+	if (inlen > 0)
+		{
+			unsigned char fragment;
+
+			*out++ = base64digits[in[0] >> 2];
+			fragment = (in[0] << 4) & 0x30;
+			if (inlen > 1)
+				fragment |= in[1] >> 4;
+			*out++ = base64digits[fragment];
+			*out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c];
+			*out++ = '-';
+		}
+	*out = '\0';
+}
+
+static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	const char *name = purple_normalize(account, purple_account_get_username(account));
+	PurpleCipher *md5_cipher;
+	PurpleCipherContext *md5_ctx;
+	guchar md5_digest[16];
+	gchar base64_string[25];
+	struct yahoo_packet *pkt;
+
+	purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n");
+
+	md5_cipher = purple_ciphers_find_cipher("md5");
+	md5_ctx = purple_cipher_context_new(md5_cipher, NULL);
+	purple_cipher_context_append(md5_ctx, (guchar *)crypt, strlen(crypt));
+	purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), md5_digest, NULL);
+
+	to_y64(base64_string, md5_digest, 16);
+
+	purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status);
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, yd->session_id);
+	if(yd->jp) {
+		yahoo_packet_hash(pkt, "ssssssss",
+					1, name,
+					0, name,
+					277, yd->cookie_y,
+					278, yd->cookie_t,
+					307, base64_string,
+					2, name,
+					2, "1",
+					135, YAHOOJP_CLIENT_VERSION);
+	} else	{
+		yahoo_packet_hash(pkt, "sssssssss",
+					1, name,
+					0, name,
+					277, yd->cookie_y,
+					278, yd->cookie_t,
+					307, base64_string,
+					244, YAHOO_CLIENT_VERSION_ID,
+					2, name,
+					2, "1",
+					135, YAHOO_CLIENT_VERSION);
+	}
+	if (yd->picture_checksum)
+		yahoo_packet_hash_int(pkt, 192, yd->picture_checksum);
+	yahoo_packet_send_and_free(pkt, yd);
+
+	purple_cipher_context_destroy(md5_ctx);
+}
+
+static void yahoo_auth16_stage2(PurpleUtilFetchUrlData *unused, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
+{
+	struct yahoo_auth_data *auth_data = user_data;
+	PurpleConnection *gc = auth_data->gc;
+	struct yahoo_data *yd;
+	gboolean try_login_on_error = FALSE;
+
+	purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n");
+
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		g_free(auth_data->seed);
+		g_free(auth_data);
+		g_return_if_reached();
+	}
+
+	yd = (struct yahoo_data *)gc->proto_data;
+
+	if (error_message != NULL) {
+		purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
+		g_free(auth_data->seed);
+		g_free(auth_data);
+		return;
+	}
+	else if (len > 0 && ret_data && *ret_data) {
+		gchar **split_data = g_strsplit(ret_data, "\r\n", -1);
+		int totalelements = 0;
+		int response_no = -1;
+		char *crumb = NULL;
+		char *crypt = NULL;
+
+#if GLIB_CHECK_VERSION(2,6,0)
+		totalelements = g_strv_length(split_data);
+#else
+		while (split_data[++totalelements] != NULL);
+#endif
+		if (totalelements >= 4) {
+			response_no = strtol(split_data[0], NULL, 10);
+			crumb = g_strdup(split_data[1] + strlen("crumb="));
+			yd->cookie_y = g_strdup(split_data[2] + strlen("Y="));
+			yd->cookie_t = g_strdup(split_data[3] + strlen("T="));
+		}
+
+		g_strfreev(split_data);
+
+		if(response_no != 0) {
+			/* Some error in the login process */
+			PurpleConnectionError error;
+			char *error_reason = NULL;
+
+			switch(response_no) {
+				case -1:
+					/* Some error in the received stream */
+					error_reason = g_strdup(_("Received invalid data"));
+					error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+					break;
+				case 100:
+					/* Unknown error */
+					error_reason = g_strdup(_("Unknown error"));
+					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+					break;
+				default:
+					/* if we have everything we need, why not try to login irrespective of response */
+					if((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) {
+						try_login_on_error = TRUE;
+						break;
+					}
+					error_reason = g_strdup(_("Unknown error"));
+					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+					break;
+			}
+			if(error_reason) {
+				purple_debug_error("yahoo", "Authentication error: %s\n",
+				                   error_reason);
+				purple_connection_error_reason(gc, error, error_reason);
+				g_free(error_reason);
+				g_free(auth_data->seed);
+				g_free(auth_data);
+				return;
+			}
+		}
+
+		crypt = g_strconcat(crumb, auth_data->seed, NULL);
+		yahoo_auth16_stage3(gc, crypt);
+		g_free(crypt);
+		g_free(crumb);
+	}
+	g_free(auth_data->seed);
+	g_free(auth_data);
+}
+
+static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *unused, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
+{
+	struct yahoo_auth_data *auth_data = user_data;
+	PurpleConnection *gc = auth_data->gc;
+
+	purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n");
+
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		g_free(auth_data->seed);
+		g_free(auth_data);
+		g_return_if_reached();
+	}
+
+	if (error_message != NULL) {
+		purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
+		g_free(auth_data->seed);
+		g_free(auth_data);
+		return;
+	}
+	else if (len > 0 && ret_data && *ret_data) {
+		gchar **split_data = g_strsplit(ret_data, "\r\n", -1);
+		int totalelements = 0;
+		int response_no = -1;
+		char *token = NULL;
+
+#if GLIB_CHECK_VERSION(2,6,0)
+		totalelements = g_strv_length(split_data);
+#else
+		while (split_data[++totalelements] != NULL);
+#endif
+		if(totalelements == 1)
+			response_no = strtol(split_data[0], NULL, 10);
+		else if(totalelements >= 2) {
+			response_no = strtol(split_data[0], NULL, 10);
+			token = g_strdup(split_data[1] + strlen("ymsgr="));
+		}
+
+		g_strfreev(split_data);
+
+		if(response_no != 0) {
+			/* Some error in the login process */
+			PurpleConnectionError error;
+			char *error_reason;
+
+			switch(response_no) {
+				case -1:
+					/* Some error in the received stream */
+					error_reason = g_strdup(_("Received invalid data"));
+					error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+					break;
+				case 1212:
+					/* Password incorrect */
+					/* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */
+					if (!purple_account_get_remember_password(purple_connection_get_account(gc)))
+						purple_account_set_password(purple_connection_get_account(gc), NULL);
+					error_reason = g_strdup(_("Incorrect password"));
+					error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+					break;
+				case 1213:
+					/* security lock from too many failed login attempts */
+					error_reason = g_strdup(_("Account locked: Too many failed login attempts.  Logging into the Yahoo! website may fix this."));
+					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+					break;
+				case 1235:
+					/* the username does not exist */
+					error_reason = g_strdup(_("Username does not exist"));
+					error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
+					break;
+				case 1214:
+				case 1236:
+					/* indicates a lock of some description */
+					error_reason = g_strdup(_("Account locked: Unknown reason.  Logging into the Yahoo! website may fix this."));
+					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+					break;
+				case 100:
+					/* username or password missing */
+					error_reason = g_strdup(_("Username or password missing"));
+					error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+					break;
+				default:
+					/* Unknown error! */
+					error_reason = g_strdup(_("Unknown error"));
+					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+					break;
+			}
+			purple_debug_error("yahoo", "Authentication error: %s\n",
+			                   error_reason);
+			purple_connection_error_reason(gc, error, error_reason);
+			g_free(error_reason);
+			g_free(auth_data->seed);
+			g_free(auth_data);
+		}
+		else {
+			/* OK to login, correct information provided */
+			PurpleUtilFetchUrlData *url_data = NULL;
+			PurpleAccount *account = purple_connection_get_account(gc);
+			char *url = NULL;
+			gboolean yahoojp = yahoo_is_japan(account);
+
+			url = g_strdup_printf(yahoojp ? YAHOOJP_LOGIN_URL : YAHOO_LOGIN_URL, token);
+			url_data = purple_util_fetch_url_request_len_with_account(account, url,
+					TRUE, YAHOO_CLIENT_USERAGENT, TRUE, NULL, FALSE, -1,
+					yahoo_auth16_stage2, auth_data);
+			g_free(url);
+			g_free(token);
+		}
+	}
+}
+
+static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed)
+{
+	PurpleUtilFetchUrlData *url_data = NULL;
+	struct yahoo_auth_data *auth_data = NULL;
+	char *url = NULL;
+	char *encoded_username;
+	char *encoded_password;
+	gboolean yahoojp;
+
+	purple_debug_info("yahoo", "Authentication: In yahoo_auth16_stage1\n");
+
+	if(!purple_ssl_is_supported()) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable"));
+		return;
+	}
+
+	yahoojp = yahoo_is_japan(purple_connection_get_account(gc));
+	auth_data = g_new0(struct yahoo_auth_data, 1);
+	auth_data->gc = gc;
+	auth_data->seed = g_strdup(seed);
+
+	encoded_username = g_strdup(purple_url_encode(purple_account_get_username(purple_connection_get_account(gc))));
+	encoded_password = g_strdup(purple_url_encode(purple_connection_get_password(gc)));
+	url = g_strdup_printf(yahoojp ? YAHOOJP_TOKEN_URL : YAHOO_TOKEN_URL,
+			encoded_username, encoded_password, purple_url_encode(seed));
+	g_free(encoded_password);
+	g_free(encoded_username);
+
+	url_data = purple_util_fetch_url_request_len_with_account(
+			purple_connection_get_account(gc), url, TRUE,
+			YAHOO_CLIENT_USERAGENT, TRUE, NULL, FALSE, -1,
+			yahoo_auth16_stage1_cb, auth_data);
+
+	g_free(url);
+}
+
+static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	char *seed = NULL;
+	char *sn   = NULL;
+	GSList *l = pkt->hash;
+	int m = 0;
+	gchar *buf;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 94)
+			seed = pair->value;
+		if (pair->key == 1)
+			sn = pair->value;
+		if (pair->key == 13)
+			m = atoi(pair->value);
+		l = l->next;
+	}
+
+	if (seed) {
+		switch (m) {
+		case 0:
+			/* used to be for really old auth routine, dont support now */
+		case 1:
+		case 2: /* Yahoo ver 16 authentication */
+			yahoo_auth16_stage1(gc, seed);
+			break;
+		default:
+			{
+				GHashTable *ui_info = purple_core_get_ui_info();
+
+				buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized "
+							"authentication method.  You will probably not be able "
+							"to successfully sign on to Yahoo.  Check %s for updates."),
+							((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE));
+				purple_notify_error(gc, "", _("Failed Yahoo! Authentication"),
+							buf);
+				g_free(buf);
+				yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */
+				break;
+			}
+		}
+	}
+}
+
+static void ignore_buddy(PurpleBuddy *buddy) {
+	PurpleGroup *group;
+	PurpleAccount *account;
+	gchar *name;
+
+	if (!buddy)
+		return;
+
+	group = purple_buddy_get_group(buddy);
+	name = g_strdup(purple_buddy_get_name(buddy));
+	account = purple_buddy_get_account(buddy);
+
+	purple_debug_info("yahoo", "blist: Removing '%s' from buddy list.\n", name);
+	purple_account_remove_buddy(account, buddy, group);
+	purple_blist_remove_buddy(buddy);
+
+	serv_add_deny(purple_account_get_connection(account), name);
+
+	g_free(name);
+}
+
+static void keep_buddy(PurpleBuddy *b)
+{
+	purple_privacy_deny_remove(purple_buddy_get_account(b),
+			purple_buddy_get_name(b), 1);
+}
+
+static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt) {
+	PurpleBuddy *b;
+	GSList *l;
+	gchar *who = NULL;
+	gchar *me = NULL;
+	gchar buf[BUF_LONG];
+	gboolean ignore = TRUE;
+	gint status = 0;
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		switch (pair->key) {
+		case 0:
+			who = pair->value;
+			break;
+		case 1:
+			me = pair->value;
+			break;
+		case 13:
+			/* 1 == ignore, 2 == unignore */
+			ignore = (strtol(pair->value, NULL, 10) == 1);
+			break;
+		case 66:
+			status = strtol(pair->value, NULL, 10);
+			break;
+		default:
+			break;
+		}
+	}
+
+	/*
+	 * status
+	 * 0  - ok
+	 * 2  - already in ignore list, could not add
+	 * 3  - not in ignore list, could not delete
+	 * 12 - is a buddy, could not add (and possibly also a not-in-ignore list condition?)
+	 */
+	switch (status) {
+		case 12:
+			purple_debug_info("yahoo", "Server reported \"is a buddy\" for %s while %s",
+							  who, (ignore ? "ignoring" : "unignoring"));
+
+			if (ignore) {
+				b = purple_find_buddy(gc->account, who);
+				g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the "
+											   "user is on your buddy list.  Clicking \"Yes\" "
+											   "will remove and ignore the buddy."), who);
+				purple_request_yes_no(gc, NULL, _("Ignore buddy?"), buf, 0,
+									  gc->account, who, NULL,
+									  b,
+									  G_CALLBACK(ignore_buddy),
+									  G_CALLBACK(keep_buddy));
+				break;
+			}
+		case 2:
+			purple_debug_info("yahoo", "Server reported that %s is already in the ignore list.",
+							  who);
+			break;
+		case 3:
+			purple_debug_info("yahoo", "Server reported that %s is not in the ignore list; could not delete",
+							  who);
+		case 0:
+		default:
+			break;
+	}
+}
+
+static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+#ifdef TRY_WEBMESSENGER_LOGIN
+	struct yahoo_data *yd = gc->proto_data;
+#endif /* TRY_WEBMESSENGER_LOGIN */
+	GSList *l = pkt->hash;
+	int err = 0;
+	char *msg;
+	char *url = NULL;
+	char *fullmsg;
+	PurpleAccount *account = gc->account;
+	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		if (pair->key == 66)
+			err = strtol(pair->value, NULL, 10);
+		else if (pair->key == 20)
+			url = pair->value;
+
+		l = l->next;
+	}
+
+	switch (err) {
+	case 0:
+		msg = g_strdup(_("Unknown error"));
+		reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
+		break;
+	case 3:
+		msg = g_strdup(_("Username does not exist"));
+		reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
+		break;
+	case 13:
+#ifdef TRY_WEBMESSENGER_LOGIN
+		if (!yd->wm) {
+			PurpleUtilFetchUrlData *url_data;
+			yd->wm = TRUE;
+			if (yd->fd >= 0)
+				close(yd->fd);
+			if (gc->inpa)
+				purple_input_remove(gc->inpa);
+			url_data = purple_util_fetch_url(WEBMESSENGER_URL, TRUE,
+					"Purple/" VERSION, FALSE, yahoo_login_page_cb, gc);
+			if (url_data != NULL)
+				yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+			return;
+		}
+#endif /* TRY_WEBMESSENGER_LOGIN */
+		if (!purple_account_get_remember_password(account))
+			purple_account_set_password(account, NULL);
+
+		msg = g_strdup(_("Incorrect password"));
+		reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+		break;
+	case 14:
+		msg = g_strdup(_("Your account is locked, please log in to the Yahoo! website."));
+		reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+		break;
+	default:
+		msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err);
+	}
+
+	if (url)
+		fullmsg = g_strdup_printf("%s\n%s", msg, url);
+	else
+		fullmsg = g_strdup(msg);
+
+	purple_connection_error_reason(gc, reason, fullmsg);
+	g_free(msg);
+	g_free(fullmsg);
+}
+
+static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	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;
+
+		switch (pair->key) {
+		case 66:
+			err = strtol(pair->value, NULL, 10);
+			break;
+		case 7:
+			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 (!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);
+		g_free(who);
+		return;
+	}
+
+	decoded_group = yahoo_string_decode(gc, group, FALSE);
+	buf = g_strdup_printf(_("Unable to add buddy %s to group %s to the server list on account %s."),
+				who, decoded_group, purple_connection_get_display_name(gc));
+	if (!purple_conv_present_error(who, purple_connection_get_account(gc), buf))
+		purple_notify_error(gc, NULL, _("Unable to 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 == NULL)
+			return;
+
+		g_memmove(buf, start, len - (start - buf));
+		len -= start - buf;
+	}
+
+	pos += 4;	/* YMSG */
+	pos += 2;
+	pos += 2;
+
+	pktlen = yahoo_get16(buf + pos); pos += 2;
+	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_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 */
+	if (yd->yahoo_p2p_server_timeout_handle) {
+		purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
+		yd->yahoo_p2p_server_timeout_handle = 0;
+	}
+
+	/* remove watcher and close p2p server */
+	if (yd->yahoo_p2p_server_watcher) {
+		purple_input_remove(yd->yahoo_p2p_server_watcher);
+		yd->yahoo_p2p_server_watcher = 0;
+	}
+	if (yd->yahoo_local_p2p_server_fd >= 0) {
+		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;
+	p2p_data->source = -1;
+
+	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)
+{
+	GSList *l = pkt->hash;
+	char *who = NULL;
+	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;
+
+		switch (pair->key) {
+		case 5:
+			/* our identity */
+			break;
+		case 4:
+			who = pair->value;
+			break;
+		case 1:
+			/* who again, the master identity this time? */
+			break;
+		case 12:
+			base64 = pair->value;
+			/* 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	packet count ??
+			yahoo: Key: 49          Value: PEERTOPEER
+			yahoo: Key: 140         Value: 1
+		*/
+
+		}
+
+		l = l->next;
+	}
+
+	if (base64) {
+		guint32 ip;
+		YahooFriend *f;
+		char *host_ip;
+		struct yahoo_p2p_data *p2p_data;
+
+		decoded = purple_base64_decode(base64, &len);
+		if (len) {
+			char *tmp = purple_str_binary_to_ascii(decoded, len);
+			purple_debug_info("yahoo", "Got P2P service packet (from server): who = %s, ip = %s\n", who, tmp);
+			g_free(tmp);
+		}
+
+		ip = strtol((gchar *)decoded, NULL, 10);
+		g_free(decoded);
+		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, 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 = g_new0(struct yahoo_p2p_data, 1);
+		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;
+		p2p_data->source = -1;
+
+		/* connect to host */
+		if((purple_proxy_connect(gc, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) {
+			purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip);
+			g_free(p2p_data->host_ip);
+			g_free(p2p_data->host_username);
+			g_free(p2p_data);
+		}
+	}
+}
+
+static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	PurpleAccount *account;
+	char *who = NULL, *msg = NULL, *id = NULL;
+	GSList *l = pkt->hash;
+
+	account = purple_connection_get_account(gc);
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 4:
+			who = pair->value;
+			break;
+		case 5:
+			/* us */
+			break;
+		case 230:
+			/* the audible, in foo.locale.bar.baz format
+			   eg: base.tw.smiley.smiley43 */
+			id = pair->value;
+			break;
+		case 231:
+			/* the text of the audible */
+			msg = pair->value;
+			break;
+		case 232:
+			/* weird number (md5 hash?), like 8ebab9094156135f5dcbaccbeee662a5c5fd1420 */
+			break;
+		}
+
+		l = l->next;
+	}
+
+	if (!msg)
+		msg = id;
+	if (!who || !msg)
+		return;
+	if (!g_utf8_validate(msg, -1, NULL)) {
+		purple_debug_misc("yahoo", "Warning, nonutf8 audible, ignoring!\n");
+		return;
+	}
+	if (!purple_privacy_check(account, who)) {
+		purple_debug_misc("yahoo", "Audible message from %s for %s dropped!\n",
+				purple_account_get_username(account), who);
+		return;
+	}
+	if (id) {
+		/* "http://us.dl1.yimg.com/download.yahoo.com/dl/aud/"+locale+"/"+id+".swf" */
+		char **audible_locale = g_strsplit(id, ".", 0);
+		char *buf = g_strdup_printf(_("[ Audible %s/%s/%s.swf ] %s"), YAHOO_AUDIBLE_URL, audible_locale[1], id, msg);
+		g_strfreev(audible_locale);
+
+		serv_got_im(gc, who, buf, 0, time(NULL));
+		g_free(buf);
+	} else
+		serv_got_im(gc, who, msg, 0, time(NULL));
+}
+
+static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt)
+{
+	switch (pkt->service) {
+	case YAHOO_SERVICE_LOGON:
+	case YAHOO_SERVICE_LOGOFF:
+	case YAHOO_SERVICE_ISAWAY:
+	case YAHOO_SERVICE_ISBACK:
+	case YAHOO_SERVICE_GAMELOGON:
+	case YAHOO_SERVICE_GAMELOGOFF:
+	case YAHOO_SERVICE_CHATLOGON:
+	case YAHOO_SERVICE_CHATLOGOFF:
+	case YAHOO_SERVICE_Y6_STATUS_UPDATE:
+	case YAHOO_SERVICE_STATUS_15:
+		yahoo_process_status(gc, pkt);
+		break;
+	case YAHOO_SERVICE_NOTIFY:
+		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_PKT_TYPE_SERVER);
+		break;
+	case YAHOO_SERVICE_SYSMESSAGE:
+		yahoo_process_sysmessage(gc, pkt);
+			break;
+	case YAHOO_SERVICE_NEWMAIL:
+		yahoo_process_mail(gc, pkt);
+		break;
+	case YAHOO_SERVICE_NEWCONTACT:
+		yahoo_process_contact(gc, pkt);
+		break;
+	case YAHOO_SERVICE_AUTHRESP:
+		yahoo_process_authresp(gc, pkt);
+		break;
+	case YAHOO_SERVICE_LIST:
+		yahoo_process_list(gc, pkt);
+		break;
+	case YAHOO_SERVICE_LIST_15:
+		yahoo_process_list_15(gc, pkt);
+		break;
+	case YAHOO_SERVICE_AUTH:
+		yahoo_process_auth(gc, pkt);
+		break;
+	case YAHOO_SERVICE_AUTH_REQ_15:
+		yahoo_buddy_auth_req_15(gc, pkt);
+		break;
+	case YAHOO_SERVICE_ADDBUDDY:
+		yahoo_process_addbuddy(gc, pkt);
+		break;
+	case YAHOO_SERVICE_IGNORECONTACT:
+		yahoo_process_ignore(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CONFINVITE:
+	case YAHOO_SERVICE_CONFADDINVITE:
+		yahoo_process_conference_invite(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CONFDECLINE:
+		yahoo_process_conference_decline(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CONFLOGON:
+		yahoo_process_conference_logon(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CONFLOGOFF:
+		yahoo_process_conference_logoff(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CONFMSG:
+		yahoo_process_conference_message(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CHATONLINE:
+		yahoo_process_chat_online(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CHATLOGOUT:
+		yahoo_process_chat_logout(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CHATGOTO:
+		yahoo_process_chat_goto(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CHATJOIN:
+		yahoo_process_chat_join(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CHATLEAVE: /* XXX is this right? */
+	case YAHOO_SERVICE_CHATEXIT:
+		yahoo_process_chat_exit(gc, pkt);
+		break;
+	case YAHOO_SERVICE_CHATINVITE: /* XXX never seen this one, might not do it right */
+	case YAHOO_SERVICE_CHATADDINVITE:
+		yahoo_process_chat_addinvite(gc, pkt);
+		break;
+	case YAHOO_SERVICE_COMMENT:
+		yahoo_process_chat_message(gc, pkt);
+		break;
+	case YAHOO_SERVICE_PRESENCE_PERM:
+	case YAHOO_SERVICE_PRESENCE_SESSION:
+		yahoo_process_presence(gc, pkt);
+		break;
+	case YAHOO_SERVICE_P2PFILEXFER:
+		/* This case had no break and continued; thus keeping it this way.*/
+		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;
+	case YAHOO_SERVICE_PEERTOPEER:
+		yahoo_process_p2p(gc, pkt);
+		break;
+	case YAHOO_SERVICE_PICTURE:
+		yahoo_process_picture(gc, pkt);
+		break;
+	case YAHOO_SERVICE_PICTURE_CHECKSUM:
+		yahoo_process_picture_checksum(gc, pkt);
+		break;
+	case YAHOO_SERVICE_PICTURE_UPLOAD:
+		yahoo_process_picture_upload(gc, pkt);
+		break;
+	case YAHOO_SERVICE_PICTURE_UPDATE:
+	case YAHOO_SERVICE_AVATAR_UPDATE:
+		yahoo_process_avatar_update(gc, pkt);
+		break;
+	case YAHOO_SERVICE_AUDIBLE:
+		yahoo_process_audible(gc, pkt);
+		break;
+	case YAHOO_SERVICE_FILETRANS_15:
+		yahoo_process_filetrans_15(gc, pkt);
+		break;
+	case YAHOO_SERVICE_FILETRANS_INFO_15:
+		yahoo_process_filetrans_info_15(gc, pkt);
+		break;
+	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_error("yahoo", "Unhandled service 0x%02x\n", pkt->service);
+		break;
+	}
+}
+
+static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc = data;
+	struct yahoo_data *yd = gc->proto_data;
+	char buf[1024];
+	int len;
+
+	len = read(yd->fd, buf, sizeof(buf));
+
+	if (len < 0) {
+		gchar *tmp;
+
+		if (errno == EAGAIN)
+			/* No worries */
+			return;
+
+		tmp = g_strdup_printf(_("Lost connection with server: %s"),
+				g_strerror(errno));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+		g_free(tmp);
+		return;
+	} else if (len == 0) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Server closed the connection"));
+		return;
+	}
+	gc->last_received = time(NULL);
+	yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen);
+	memcpy(yd->rxqueue + yd->rxlen, buf, len);
+	yd->rxlen += len;
+
+	while (1) {
+		struct yahoo_packet *pkt;
+		int pos = 0;
+		int pktlen;
+
+		if (yd->rxlen < YAHOO_PACKET_HDRLEN)
+			return;
+
+		if (strncmp((char *)yd->rxqueue, "YMSG", MIN(4, yd->rxlen)) != 0) {
+			/* HEY! This isn't even a YMSG packet. What
+			 * are you trying to pull? */
+			guchar *start;
+
+			purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n");
+
+			start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1);
+			if (start) {
+				g_memmove(yd->rxqueue, start, yd->rxlen - (start - yd->rxqueue));
+				yd->rxlen -= start - yd->rxqueue;
+				continue;
+			} else {
+				g_free(yd->rxqueue);
+				yd->rxqueue = NULL;
+				yd->rxlen = 0;
+				return;
+			}
+		}
+
+		pos += 4; /* YMSG */
+		pos += 2;
+		pos += 2;
+
+		pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2;
+		purple_debug_misc("yahoo", "%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen);
+
+		if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen))
+			return;
+
+		yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
+
+		pkt = yahoo_packet_new(0, 0, 0);
+
+		pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2;
+		pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4;
+		purple_debug_misc("yahoo", "Yahoo Service: 0x%02x Status: %d\n",
+				   pkt->service, pkt->status);
+		pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4;
+
+		yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen);
+
+		yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
+		if (yd->rxlen) {
+			guchar *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen);
+			g_free(yd->rxqueue);
+			yd->rxqueue = tmp;
+		} else {
+			g_free(yd->rxqueue);
+			yd->rxqueue = NULL;
+		}
+
+		yahoo_packet_process(gc, pkt);
+
+		yahoo_packet_free(pkt);
+	}
+}
+
+static void yahoo_got_connected(gpointer data, gint source, const gchar *error_message)
+{
+	PurpleConnection *gc = data;
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+
+	if (source < 0) {
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+		g_free(tmp);
+		return;
+	}
+
+	yd = gc->proto_data;
+	yd->fd = source;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, 0);
+
+	yahoo_packet_hash_str(pkt, 1, purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))));
+	yahoo_packet_send_and_free(pkt, yd);
+
+	gc->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
+}
+
+#ifdef TRY_WEBMESSENGER_LOGIN
+static void yahoo_got_web_connected(gpointer data, gint source, const gchar *error_message)
+{
+	PurpleConnection *gc = data;
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+
+	if (source < 0) {
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+		g_free(tmp);
+		return;
+	}
+
+	yd = gc->proto_data;
+	yd->fd = source;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, 0);
+
+	yahoo_packet_hash(pkt, "sss", 0,
+	                  purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))),
+	                  1, purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))),
+	                  6, yd->auth);
+	yahoo_packet_send_and_free(pkt, yd);
+
+	g_free(yd->auth);
+	gc->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
+}
+
+static void yahoo_web_pending(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc = data;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	struct yahoo_data *yd = gc->proto_data;
+	char bufread[2048], *i = bufread, *buf = bufread;
+	int len;
+	GString *s;
+
+	len = read(source, bufread, sizeof(bufread) - 1);
+
+	if (len < 0) {
+		gchar *tmp;
+
+		if (errno == EAGAIN)
+			/* No worries */
+			return;
+
+		tmp = g_strdup_printf(_("Lost connection with server: %s"),
+				g_strerror(errno));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+		g_free(tmp);
+		return;
+	} else if (len == 0) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Server closed the connection"));
+		return;
+	}
+
+	if (yd->rxlen > 0 || !g_strstr_len(buf, len, "\r\n\r\n")) {
+		yd->rxqueue = g_realloc(yd->rxqueue, yd->rxlen + len + 1);
+		memcpy(yd->rxqueue + yd->rxlen, buf, len);
+		yd->rxlen += len;
+		i = buf = (char *)yd->rxqueue;
+		len = yd->rxlen;
+	}
+	buf[len] = '\0';
+
+	if ((strncmp(buf, "HTTP/1.0 302", strlen("HTTP/1.0 302")) &&
+			  strncmp(buf, "HTTP/1.1 302", strlen("HTTP/1.1 302")))) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Received unexpected HTTP response from server"));
+		purple_debug_misc("yahoo", "Unexpected HTTP response: %s\n", buf);
+		return;
+	}
+
+	s = g_string_sized_new(len);
+
+	while ((i = strstr(i, "Set-Cookie: "))) {
+
+		i += strlen("Set-Cookie: ");
+		for (;*i != ';' && *i != '\0'; i++)
+			g_string_append_c(s, *i);
+
+		g_string_append(s, "; ");
+		/* Should these cookies be included too when trying for xfer?
+		 * It seems to work without these
+		 */
+	}
+
+	yd->auth = g_string_free(s, FALSE);
+	purple_input_remove(gc->inpa);
+	close(source);
+	g_free(yd->rxqueue);
+	yd->rxqueue = NULL;
+	yd->rxlen = 0;
+	/* Now we have our cookies to login with.  I'll go get the milk. */
+	if (purple_proxy_connect(gc, account, "wcs2.msg.dcn.yahoo.com",
+	                         purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
+	                         yahoo_got_web_connected, gc) == NULL) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               _("Unable to connect"));
+		return;
+	}
+}
+
+static void yahoo_got_cookies_send_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc;
+	struct yahoo_data *yd;
+	int written, remaining;
+
+	gc = data;
+	yd = gc->proto_data;
+
+	remaining = strlen(yd->auth) - yd->auth_written;
+	written = write(source, yd->auth + yd->auth_written, remaining);
+
+	if (written < 0 && errno == EAGAIN)
+		written = 0;
+	else if (written <= 0) {
+		gchar *tmp;
+		g_free(yd->auth);
+		yd->auth = NULL;
+		if (gc->inpa)
+			purple_input_remove(gc->inpa);
+		gc->inpa = 0;
+		tmp = g_strdup_printf(_("Lost connection with %s: %s"),
+				"login.yahoo.com:80", g_strerror(errno));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+		g_free(tmp);
+		return;
+	}
+
+	if (written < remaining) {
+		yd->auth_written += written;
+		return;
+	}
+
+	g_free(yd->auth);
+	yd->auth = NULL;
+	yd->auth_written = 0;
+	purple_input_remove(gc->inpa);
+	gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, yahoo_web_pending, gc);
+}
+
+static void yahoo_got_cookies(gpointer data, gint source, const gchar *error_message)
+{
+	PurpleConnection *gc = data;
+
+	if (source < 0) {
+		gchar *tmp;
+		tmp = g_strdup_printf(_("Unable to establish a connection with %s: %s"),
+				"login.yahoo.com:80", error_message);
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+		g_free(tmp);
+		return;
+	}
+
+	if (gc->inpa == 0)
+	{
+		gc->inpa = purple_input_add(source, PURPLE_INPUT_WRITE,
+			yahoo_got_cookies_send_cb, gc);
+		yahoo_got_cookies_send_cb(gc, source, PURPLE_INPUT_WRITE);
+	}
+}
+
+static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url)
+{
+	if (!strcmp(key, "passwd") || !strcmp(key, "login"))
+		return;
+	g_string_append_c(url, '&');
+	g_string_append(url, key);
+	g_string_append_c(url, '=');
+	if (!strcmp(key, ".save") || !strcmp(key, ".js"))
+		g_string_append_c(url, '1');
+	else if (!strcmp(key, ".challenge"))
+		g_string_append(url, val);
+	else
+		g_string_append(url, purple_url_encode(val));
+}
+
+static GHashTable *yahoo_login_page_hash(const char *buf, size_t len)
+{
+	GHashTable *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	const char *c = buf;
+	char *d;
+	char name[64], value[64];
+	int count;
+	int input_len = strlen("<input ");
+	int name_len = strlen("name=\"");
+	int value_len = strlen("value=\"");
+	while ((len > ((c - buf) + input_len))
+			&& (c = strstr(c, "<input "))) {
+		if (!(c = g_strstr_len(c, len - (c - buf), "name=\"")))
+			continue;
+		c += name_len;
+		count = sizeof(name)-1;
+		for (d = name; (len > ((c - buf) + 1)) && *c!='"'
+				&& count; c++, d++, count--)
+			*d = *c;
+		*d = '\0';
+		count = sizeof(value)-1;
+		if (!(d = g_strstr_len(c, len - (c - buf), "value=\"")))
+			continue;
+		d += value_len;
+		if (strchr(c, '>') < d)
+			break;
+		for (c = d, d = value; (len > ((c - buf) + 1))
+				&& *c!='"' && count; c++, d++, count--)
+			*d = *c;
+		*d = '\0';
+		g_hash_table_insert(hash, g_strdup(name), g_strdup(value));
+	}
+	return hash;
+}
+
+static void
+yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
+		const gchar *url_text, size_t len, const gchar *error_message)
+{
+	PurpleConnection *gc = (PurpleConnection *)user_data;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	struct yahoo_data *yd = gc->proto_data;
+	const char *sn = purple_account_get_username(account);
+	const char *pass = purple_connection_get_password(gc);
+	GHashTable *hash = yahoo_login_page_hash(url_text, len);
+	GString *url = g_string_new("GET http://login.yahoo.com/config/login?login=");
+	char md5[33], *hashp = md5, *chal;
+	int i;
+	PurpleCipher *cipher;
+	PurpleCipherContext *context;
+	guchar digest[16];
+
+	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+
+	if (error_message != NULL)
+	{
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               error_message);
+		return;
+	}
+
+	url = g_string_append(url, sn);
+	url = g_string_append(url, "&passwd=");
+
+	cipher = purple_ciphers_find_cipher("md5");
+	context = purple_cipher_context_new(cipher, NULL);
+
+	purple_cipher_context_append(context, (const guchar *)pass, strlen(pass));
+	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
+	for (i = 0; i < 16; ++i) {
+		g_snprintf(hashp, 3, "%02x", digest[i]);
+		hashp += 2;
+	}
+
+	chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL);
+	purple_cipher_context_reset(context, NULL);
+	purple_cipher_context_append(context, (const guchar *)chal, strlen(chal));
+	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
+	hashp = md5;
+	for (i = 0; i < 16; ++i) {
+		g_snprintf(hashp, 3, "%02x", digest[i]);
+		hashp += 2;
+	}
+	/*
+	 * I dunno why this is here and commented out.. but in case it's needed
+	 * I updated it..
+
+	purple_cipher_context_reset(context, NULL);
+	purple_cipher_context_append(context, md5, strlen(md5));
+	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
+	hashp = md5;
+	for (i = 0; i < 16; ++i) {
+		g_snprintf(hashp, 3, "%02x", digest[i]);
+		hashp += 2;
+	}
+	*/
+	g_free(chal);
+
+	url = g_string_append(url, md5);
+	g_hash_table_foreach(hash, (GHFunc)yahoo_login_page_hash_iter, url);
+
+	url = g_string_append(url, "&.hash=1&.md5=1 HTTP/1.1\r\n"
+			      "Host: login.yahoo.com\r\n\r\n");
+	g_hash_table_destroy(hash);
+	yd->auth = g_string_free(url, FALSE);
+	if (purple_proxy_connect(gc, account, "login.yahoo.com", 80, yahoo_got_cookies, gc) == NULL) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               _("Unable to connect"));
+		return;
+	}
+
+	purple_cipher_context_destroy(context);
+}
+#endif /* TRY_WEBMESSENGER_LOGIN */
+
+static void yahoo_server_check(PurpleAccount *account)
+{
+	const char *server;
+
+	server = purple_account_get_string(account, "server", YAHOO_PAGER_HOST);
+
+	if (*server == '\0' || g_str_equal(server, "scs.yahoo.com") ||
+			g_str_equal(server, "scs.msg.yahoo.com"))
+		purple_account_set_string(account, "server", YAHOO_PAGER_HOST);
+}
+
+static void yahoo_picture_check(PurpleAccount *account)
+{
+	PurpleConnection *gc = purple_account_get_connection(account);
+	PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
+
+	yahoo_set_buddy_icon(gc, img);
+	purple_imgstore_unref(img);
+}
+
+static int get_yahoo_status_from_purple_status(PurpleStatus *status)
+{
+	PurplePresence *presence;
+	const char *status_id;
+	const char *msg;
+
+	presence = purple_status_get_presence(status);
+	status_id = purple_status_get_id(status);
+	msg = purple_status_get_attr_string(status, "message");
+
+	if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) {
+		if ((msg != NULL) && (*msg != '\0'))
+			return YAHOO_STATUS_CUSTOM;
+		else
+			return YAHOO_STATUS_AVAILABLE;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BRB)) {
+		return YAHOO_STATUS_BRB;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BUSY)) {
+		return YAHOO_STATUS_BUSY;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATHOME)) {
+		return YAHOO_STATUS_NOTATHOME;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATDESK)) {
+		return YAHOO_STATUS_NOTATDESK;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTINOFFICE)) {
+		return YAHOO_STATUS_NOTINOFFICE;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONPHONE)) {
+		return YAHOO_STATUS_ONPHONE;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONVACATION)) {
+		return YAHOO_STATUS_ONVACATION;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_OUTTOLUNCH)) {
+		return YAHOO_STATUS_OUTTOLUNCH;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_STEPPEDOUT)) {
+		return YAHOO_STATUS_STEPPEDOUT;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_INVISIBLE)) {
+		return YAHOO_STATUS_INVISIBLE;
+	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AWAY)) {
+		return YAHOO_STATUS_CUSTOM;
+	} else if (purple_presence_is_idle(presence)) {
+		return YAHOO_STATUS_IDLE;
+	} else {
+		purple_debug_error("yahoo", "Unexpected PurpleStatus!\n");
+		return YAHOO_STATUS_AVAILABLE;
+	}
+}
+
+void yahoo_login(PurpleAccount *account) {
+	PurpleConnection *gc = purple_account_get_connection(account);
+	struct yahoo_data *yd = gc->proto_data = g_new0(struct yahoo_data, 1);
+	PurpleStatus *status = purple_account_get_active_status(account);
+	gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC;
+
+	purple_connection_update_progress(gc, _("Connecting"), 1, 2);
+
+	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? */
+	yd->txbuf = purple_circ_buffer_new(0);
+	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);
+
+	yd->current_status = get_yahoo_status_from_purple_status(status);
+
+	yahoo_server_check(account);
+	yahoo_picture_check(account);
+
+	if (yahoo_is_japan(account)) {
+		yd->jp = TRUE;
+		if (purple_proxy_connect(gc, account,
+		                       purple_account_get_string(account, "server",  YAHOOJP_PAGER_HOST),
+		                       purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
+		                       yahoo_got_connected, gc) == NULL)
+		{
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			                               _("Unable to connect"));
+			return;
+		}
+	} else {
+		yd->jp = FALSE;
+		if (purple_proxy_connect(gc, account,
+		                       purple_account_get_string(account, "server",  YAHOO_PAGER_HOST),
+		                       purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
+		                       yahoo_got_connected, gc) == NULL)
+		{
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			                               _("Unable to connect"));
+			return;
+		}
+	}
+}
+
+void yahoo_close(PurpleConnection *gc) {
+	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
+	GSList *l;
+
+	if (gc->inpa)
+		purple_input_remove(gc->inpa);
+
+	while (yd->url_datas) {
+		purple_util_fetch_url_cancel(yd->url_datas->data);
+		yd->url_datas = g_slist_delete_link(yd->url_datas, yd->url_datas);
+	}
+
+	for (l = yd->confs; l; l = l->next) {
+		PurpleConversation *conv = l->data;
+
+		yahoo_conf_leave(yd, purple_conversation_get_name(conv),
+		                 purple_connection_get_display_name(gc),
+				 purple_conv_chat_get_users(PURPLE_CONV_CHAT(conv)));
+	}
+	g_slist_free(yd->confs);
+
+	for (l = yd->cookies; l; l = l->next) {
+		g_free(l->data);
+		l->data=NULL;
+	}
+	g_slist_free(yd->cookies);
+
+	yd->chat_online = FALSE;
+	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);
+		yd->yahoo_p2p_server_timeout_handle = 0;
+	}
+
+	/* close p2p server if it is waiting for a peer to connect */
+	if (yd->yahoo_p2p_server_watcher) {
+		purple_input_remove(yd->yahoo_p2p_server_watcher);
+		yd->yahoo_p2p_server_watcher = 0;
+	}
+	if (yd->yahoo_local_p2p_server_fd >= 0) {
+		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);
+	g_free(yd->chat_name);
+
+	g_free(yd->cookie_y);
+	g_free(yd->cookie_t);
+
+	if (yd->txhandler)
+		purple_input_remove(yd->txhandler);
+
+	purple_circ_buffer_destroy(yd->txbuf);
+
+	if (yd->fd >= 0)
+		close(yd->fd);
+
+	g_free(yd->rxqueue);
+	yd->rxlen = 0;
+	g_free(yd->picture_url);
+
+	if (yd->buddy_icon_connect_data)
+		purple_proxy_connect_cancel(yd->buddy_icon_connect_data);
+	if (yd->picture_upload_todo)
+		yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo);
+	if (yd->ycht)
+		ycht_connection_close(yd->ycht);
+
+	g_free(yd->pending_chat_room);
+	g_free(yd->pending_chat_id);
+	g_free(yd->pending_chat_topic);
+	g_free(yd->pending_chat_goto);
+	g_strfreev(yd->profiles);
+
+	g_free(yd->current_list15_grp);
+
+	g_free(yd);
+	gc->proto_data = NULL;
+}
+
+const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b)
+{
+	return "yahoo";
+}
+
+const char *yahoo_list_emblem(PurpleBuddy *b)
+{
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	struct yahoo_data *yd;
+	YahooFriend *f;
+	PurplePresence *presence;
+
+	if (!b || !(account = purple_buddy_get_account(b)) ||
+			!(gc = purple_account_get_connection(account)) ||
+			!(yd = gc->proto_data))
+		return NULL;
+
+	f = yahoo_friend_find(gc, purple_buddy_get_name(b));
+	if (!f) {
+		return "not-authorized";
+	}
+
+	presence = purple_buddy_get_presence(b);
+
+	if (purple_presence_is_online(presence)) {
+		if (yahoo_friend_get_game(f))
+			return "game";
+		if (f->protocol == 2)
+			return "msn";
+	}
+	return NULL;
+}
+
+static const char *yahoo_get_status_string(enum yahoo_status a)
+{
+	switch (a) {
+	case YAHOO_STATUS_BRB:
+		return _("Be Right Back");
+	case YAHOO_STATUS_BUSY:
+		return _("Busy");
+	case YAHOO_STATUS_NOTATHOME:
+		return _("Not at Home");
+	case YAHOO_STATUS_NOTATDESK:
+		return _("Not at Desk");
+	case YAHOO_STATUS_NOTINOFFICE:
+		return _("Not in Office");
+	case YAHOO_STATUS_ONPHONE:
+		return _("On the Phone");
+	case YAHOO_STATUS_ONVACATION:
+		return _("On Vacation");
+	case YAHOO_STATUS_OUTTOLUNCH:
+		return _("Out to Lunch");
+	case YAHOO_STATUS_STEPPEDOUT:
+		return _("Stepped Out");
+	case YAHOO_STATUS_INVISIBLE:
+		return _("Invisible");
+	case YAHOO_STATUS_IDLE:
+		return _("Idle");
+	case YAHOO_STATUS_OFFLINE:
+		return _("Offline");
+	default:
+		return _("Available");
+	}
+}
+
+static void yahoo_initiate_conference(PurpleBlistNode *node, gpointer data) {
+
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+
+	GHashTable *components;
+	struct yahoo_data *yd;
+	int id;
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *) node;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	yd = gc->proto_data;
+	id = yd->conf_id;
+
+	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	g_hash_table_replace(components, g_strdup("room"),
+		g_strdup_printf("%s-%d", purple_connection_get_display_name(gc), id));
+	g_hash_table_replace(components, g_strdup("topic"), g_strdup("Join my conference..."));
+	g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
+	yahoo_c_join(gc, components);
+	g_hash_table_destroy(components);
+
+	yahoo_c_invite(gc, id, "Join my conference...", purple_buddy_get_name(buddy));
+}
+
+static void yahoo_presence_settings(PurpleBlistNode *node, gpointer data) {
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+	int presence_val = GPOINTER_TO_INT(data);
+
+	buddy = (PurpleBuddy *) node;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+
+	yahoo_friend_update_presence(gc, purple_buddy_get_name(buddy), presence_val);
+}
+
+static void yahoo_game(PurpleBlistNode *node, gpointer data) {
+
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+
+	struct yahoo_data *yd;
+	const char *game;
+	char *game2;
+	char *t;
+	char url[256];
+	YahooFriend *f;
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *) node;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	yd = (struct yahoo_data *) gc->proto_data;
+
+	f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
+	if (!f)
+		return;
+
+	game = yahoo_friend_get_game(f);
+	if (!game)
+		return;
+
+	t = game2 = g_strdup(strstr(game, "ante?room="));
+	while (*t && *t != '\t')
+		t++;
+	*t = 0;
+	g_snprintf(url, sizeof url, "http://games.yahoo.com/games/%s", game2);
+	purple_notify_uri(gc, url);
+	g_free(game2);
+}
+
+char *yahoo_status_text(PurpleBuddy *b)
+{
+	YahooFriend *f = NULL;
+	const char *msg;
+	char *msg2;
+	PurpleAccount *account;
+
+	account = purple_buddy_get_account(b);
+	f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
+	if (!f)
+		return g_strdup(_("Not on server list"));
+
+	switch (f->status) {
+	case YAHOO_STATUS_AVAILABLE:
+		return NULL;
+	case YAHOO_STATUS_IDLE:
+		if (f->idle == -1)
+			return g_strdup(yahoo_get_status_string(f->status));
+		return NULL;
+	case YAHOO_STATUS_CUSTOM:
+		if (!(msg = yahoo_friend_get_status_message(f)))
+			return NULL;
+		msg2 = g_markup_escape_text(msg, strlen(msg));
+		purple_util_chrreplace(msg2, '\n', ' ');
+		return msg2;
+
+	default:
+		return g_strdup(yahoo_get_status_string(f->status));
+	}
+}
+
+void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
+{
+	YahooFriend *f;
+	char *escaped;
+	char *status = NULL;
+	const char *presence = NULL;
+	PurpleAccount *account;
+
+	account = purple_buddy_get_account(b);
+	f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
+	if (!f)
+		status = g_strdup_printf("\n%s", _("Not on server list"));
+	else {
+		switch (f->status) {
+		case YAHOO_STATUS_CUSTOM:
+			if (!yahoo_friend_get_status_message(f))
+				return;
+			status = g_strdup(yahoo_friend_get_status_message(f));
+			break;
+		case YAHOO_STATUS_OFFLINE:
+			break;
+		default:
+			status = g_strdup(yahoo_get_status_string(f->status));
+			break;
+		}
+
+		switch (f->presence) {
+			case YAHOO_PRESENCE_ONLINE:
+				presence = _("Appear Online");
+				break;
+			case YAHOO_PRESENCE_PERM_OFFLINE:
+				presence = _("Appear Permanently Offline");
+				break;
+			case YAHOO_PRESENCE_DEFAULT:
+				break;
+			default:
+				purple_debug_error("yahoo", "Unknown presence in yahoo_tooltip_text\n");
+				break;
+		}
+	}
+
+	if (status != NULL) {
+		escaped = g_markup_escape_text(status, strlen(status));
+		purple_notify_user_info_add_pair(user_info, _("Status"), escaped);
+		g_free(status);
+		g_free(escaped);
+	}
+
+	if (presence != NULL)
+		purple_notify_user_info_add_pair(user_info, _("Presence"), presence);
+}
+
+static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data)
+{
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *) node;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+
+	yahoo_add_buddy(gc, buddy, NULL);
+}
+
+
+static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data)
+{
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *) node;
+	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+
+	yahoo_chat_goto(gc, purple_buddy_get_name(buddy));
+}
+
+static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) {
+	GList *m = NULL;
+	PurpleMenuAction *act;
+	struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data;
+
+	if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
+		if (f->presence != YAHOO_PRESENCE_ONLINE) {
+			act = purple_menu_action_new(_("Appear Online"),
+			                           PURPLE_CALLBACK(yahoo_presence_settings),
+			                           GINT_TO_POINTER(YAHOO_PRESENCE_ONLINE),
+			                           NULL);
+			m = g_list_append(m, act);
+		} else if (f->presence != YAHOO_PRESENCE_DEFAULT) {
+			act = purple_menu_action_new(_("Appear Offline"),
+			                           PURPLE_CALLBACK(yahoo_presence_settings),
+			                           GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
+			                           NULL);
+			m = g_list_append(m, act);
+		}
+	}
+
+	if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) {
+		act = purple_menu_action_new(_("Don't Appear Permanently Offline"),
+		                           PURPLE_CALLBACK(yahoo_presence_settings),
+		                           GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
+		                           NULL);
+		m = g_list_append(m, act);
+	} else {
+		act = purple_menu_action_new(_("Appear Permanently Offline"),
+		                           PURPLE_CALLBACK(yahoo_presence_settings),
+		                           GINT_TO_POINTER(YAHOO_PRESENCE_PERM_OFFLINE),
+		                           NULL);
+		m = g_list_append(m, act);
+	}
+
+	return m;
+}
+
+static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data)
+{
+	PurpleBuddy *b = (PurpleBuddy *)node;
+	PurpleAccount *account = purple_buddy_get_account(b);
+	PurpleConnection *gc = purple_account_get_connection(account);
+
+	yahoo_doodle_initiate(gc, purple_buddy_get_name(b));
+}
+
+static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
+{
+	GList *m = NULL;
+	PurpleMenuAction *act;
+
+	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
+	struct yahoo_data *yd = gc->proto_data;
+	static char buf2[1024];
+	YahooFriend *f;
+
+	f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
+
+	if (!f && !yd->wm) {
+		act = purple_menu_action_new(_("Add Buddy"),
+		                           PURPLE_CALLBACK(yahoo_addbuddyfrommenu_cb),
+		                           NULL, NULL);
+		m = g_list_append(m, act);
+
+		return m;
+
+	}
+
+	if (f && f->status != YAHOO_STATUS_OFFLINE) {
+		if (!yd->wm) {
+			act = purple_menu_action_new(_("Join in Chat"),
+			                           PURPLE_CALLBACK(yahoo_chat_goto_menu),
+			                           NULL, NULL);
+			m = g_list_append(m, act);
+		}
+
+		act = purple_menu_action_new(_("Initiate Conference"),
+		                           PURPLE_CALLBACK(yahoo_initiate_conference),
+		                           NULL, NULL);
+		m = g_list_append(m, act);
+
+		if (yahoo_friend_get_game(f)) {
+			const char *game = yahoo_friend_get_game(f);
+			char *room;
+			char *t;
+
+			if ((room = strstr(game, "&follow="))) {/* skip ahead to the url */
+				while (*room && *room != '\t')          /* skip to the tab */
+					room++;
+				t = room++;                             /* room as now at the name */
+				while (*t != '\n')
+					t++;                            /* replace the \n with a space */
+				*t = ' ';
+				g_snprintf(buf2, sizeof buf2, "%s", room);
+
+				act = purple_menu_action_new(buf2,
+				                           PURPLE_CALLBACK(yahoo_game),
+				                           NULL, NULL);
+				m = g_list_append(m, act);
+			}
+		}
+	}
+
+	if (f) {
+		act = purple_menu_action_new(_("Presence Settings"), NULL, NULL,
+		                           build_presence_submenu(f, gc));
+		m = g_list_append(m, act);
+	}
+
+	if (f) {
+		act = purple_menu_action_new(_("Start Doodling"),
+		                           PURPLE_CALLBACK(yahoo_doodle_blist_node),
+		                           NULL, NULL);
+		m = g_list_append(m, act);
+	}
+
+	return m;
+}
+
+GList *yahoo_blist_node_menu(PurpleBlistNode *node)
+{
+	if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
+		return yahoo_buddy_menu((PurpleBuddy *) node);
+	} else {
+		return NULL;
+	}
+}
+
+static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	const char *name = yd->profiles[purple_request_fields_get_choice(fields, "id")];
+
+	struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash_str(pkt, 3, name);
+	yahoo_packet_send_and_free(pkt, yd);
+
+	purple_connection_set_display_name(gc, name);
+}
+
+static void
+yahoo_get_inbox_token_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
+		const gchar *token, size_t len, const gchar *error_message)
+{
+	PurpleConnection *gc = user_data;
+	gboolean set_cookie = FALSE;
+	gchar *url;
+	struct yahoo_data *yd = gc->proto_data;
+
+	g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
+
+	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
+
+	if (error_message != NULL)
+		purple_debug_error("yahoo", "Requesting mail login token failed: %s\n", error_message);
+	else if (len > 0 && token && *token) {
+	 	/* Should we not be hardcoding the rd url? */
+		url = g_strdup_printf(
+			"http://login.yahoo.com/config/reset_cookies_token?"
+			".token=%s"
+			"&.done=http://us.rd.yahoo.com/messenger/client/%%3fhttp://mail.yahoo.com/",
+			token);
+		set_cookie = TRUE;
+	}
+
+	if (!set_cookie) {
+		purple_debug_error("yahoo", "No mail login token; forwarding to login screen.\n");
+		url = g_strdup(yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
+	}
+
+	/* Open the mailbox with the parsed url data */
+	purple_notify_uri(gc, url);
+
+	g_free(url);
+}
+
+
+static void yahoo_show_inbox(PurplePluginAction *action)
+{
+	/* Setup a cookie that can be used by the browser */
+	/* XXX I have no idea how this will work with Yahoo! Japan. */
+
+	PurpleConnection *gc = action->context;
+	struct yahoo_data *yd = gc->proto_data;
+
+	PurpleUtilFetchUrlData *url_data;
+	const char* base_url = "http://login.yahoo.com";
+	/* use whole URL if using HTTP Proxy */
+	gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
+	gchar *request = g_strdup_printf(
+		"POST %s/config/cookie_token HTTP/1.0\r\n"
+		"Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s;\r\n"
+		"User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
+		"Host: login.yahoo.com\r\n"
+		"Content-Length: 0\r\n\r\n",
+		use_whole_url ? base_url : "",
+		yd->cookie_t, yd->cookie_y);
+
+	url_data = purple_util_fetch_url_request_len_with_account(
+			purple_connection_get_account(gc), base_url, use_whole_url,
+			YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
+			yahoo_get_inbox_token_cb, gc);
+
+	g_free(request);
+
+	if (url_data != NULL)
+		yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+	else {
+		const char *yahoo_mail_url = (yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
+		purple_debug_error("yahoo",
+				   "Unable to request mail login token; forwarding to login screen.");
+		purple_notify_uri(gc, yahoo_mail_url);
+	}
+
+}
+
+
+static void yahoo_show_act_id(PurplePluginAction *action)
+{
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	struct yahoo_data *yd = purple_connection_get_protocol_data(gc);
+	const char *name = purple_connection_get_display_name(gc);
+	int iter;
+
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+	field = purple_request_field_choice_new("id", "Activate which ID?", 0);
+	purple_request_field_group_add_field(group, field);
+
+	for (iter = 0; yd->profiles[iter]; iter++) {
+		purple_request_field_choice_add(field, yd->profiles[iter]);
+		if (purple_strequal(yd->profiles[iter], name))
+			purple_request_field_choice_set_default_value(field, iter);
+	}
+
+	purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL,
+					   fields,
+					   _("OK"), G_CALLBACK(yahoo_act_id),
+					   _("Cancel"), NULL,
+					   purple_connection_get_account(gc), NULL, NULL,
+					   gc);
+}
+
+static void yahoo_show_chat_goto(PurplePluginAction *action)
+{
+	PurpleConnection *gc = (PurpleConnection *) action->context;
+	purple_request_input(gc, NULL, _("Join whom in chat?"), NULL,
+					   "", FALSE, FALSE, NULL,
+					   _("OK"), G_CALLBACK(yahoo_chat_goto),
+					   _("Cancel"), NULL,
+					   purple_connection_get_account(gc), NULL, NULL,
+					   gc);
+}
+
+GList *yahoo_actions(PurplePlugin *plugin, gpointer context) {
+	GList *m = NULL;
+	PurplePluginAction *act;
+
+	act = purple_plugin_action_new(_("Activate ID..."),
+			yahoo_show_act_id);
+	m = g_list_append(m, act);
+
+	act = purple_plugin_action_new(_("Join User in Chat..."),
+			yahoo_show_chat_goto);
+	m = g_list_append(m, act);
+
+	m = g_list_append(m, NULL);
+	act = purple_plugin_action_new(_("Open Inbox"),
+			yahoo_show_inbox);
+	m = g_list_append(m, act);
+
+	return m;
+}
+
+struct yahoo_sms_carrier_cb_data	{
+	PurpleConnection *gc;
+	char *who;
+	char *what;
+};
+
+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: " YAHOO_CLIENT_USERAGENT "\r\n"
+		"Host: validate.msg.yahoo.com\r\n"
+		"Content-Length: %" G_GSIZE_FORMAT "\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_len_with_account(
+			purple_connection_get_account(gc), YAHOO_SMS_CARRIER_URL, use_whole_url,
+			YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
+			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);
+	}
+}
+
+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 = NULL;
+	char *msg = yahoo_html_to_codes(what);
+	char *msg2;
+	gboolean utf8 = TRUE;
+	PurpleWhiteboard *wb;
+	int ret = 1;
+	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) {
+		lenb = strlen(msg2);
+		lenc = g_utf8_strlen(msg2, -1);
+
+		if(lenb > YAHOO_MAX_MESSAGE_LENGTH_BYTES || lenc > YAHOO_MAX_MESSAGE_LENGTH_CHARS) {
+			purple_debug_info("yahoo", "Message too big.  Length is %" G_GSIZE_FORMAT
+					" bytes, %ld characters.  Max is %d bytes, %d chars."
+					"  Message is '%s'.\n", lenb, lenc, YAHOO_MAX_MESSAGE_LENGTH_BYTES,
+					YAHOO_MAX_MESSAGE_LENGTH_CHARS, msg2);
+			g_free(msg);
+			g_free(msg2);
+			return -E2BIG;
+		}
+	}
+
+	msn = !g_strncasecmp(who, "msn/", 4);
+
+	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_strdup(who);
+			sms_cb_data->what = g_strdup(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");
+	yahoo_packet_hash_str(pkt, 14, msg2);
+
+	/*
+	 * IMVironment.
+	 *
+	 * If this message is to a user who is also Doodling with the local user,
+	 * format the chat packet with the correct IMV information (thanks Yahoo!)
+	 *
+	 * Otherwise attempt to use the same IMVironment as the remote user,
+	 * just so that we don't inadvertantly reset their IMVironment back
+	 * to nothing.
+	 *
+	 * If they have no set an IMVironment, then use the default.
+	 */
+	wb = purple_whiteboard_get_session(gc->account, who);
+	if (wb)
+		yahoo_packet_hash_str(pkt, 63, DOODLE_IMV_KEY);
+	else
+	{
+		const char *imv;
+		imv = g_hash_table_lookup(yd->imvironments, who);
+		if (imv != NULL)
+			yahoo_packet_hash_str(pkt, 63, imv);
+		else
+			yahoo_packet_hash_str(pkt, 63, ";0");
+	}
+
+	yahoo_packet_hash_str(pkt,   64, "0"); /* no idea */
+	yahoo_packet_hash_str(pkt, 1002, "1"); /* no idea, Yahoo 6 or later only it seems */
+	if (!yd->picture_url)
+		yahoo_packet_hash_str(pkt, 206, "0"); /* 0 = no picture, 2 = picture, maybe 1 = avatar? */
+	else
+		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) {
+		/* 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;
+
+	yahoo_packet_free(pkt);
+
+	g_free(msg);
+	g_free(msg2);
+
+	return ret;
+}
+
+unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	struct yahoo_p2p_data *p2p_data;
+	gboolean msn = !g_strncasecmp(who, "msn/", 4);
+	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, 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;
+}
+
+static void yahoo_session_presence_remove(gpointer key, gpointer value, gpointer data)
+{
+	YahooFriend *f = value;
+	if (f && f->presence == YAHOO_PRESENCE_ONLINE)
+		f->presence = YAHOO_PRESENCE_DEFAULT;
+}
+
+void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
+{
+	PurpleConnection *gc;
+	PurplePresence *presence;
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+	int old_status;
+	const char *msg = NULL;
+	char *tmp = NULL;
+	char *conv_msg = NULL;
+	gboolean utf8 = TRUE;
+
+	if (!purple_status_is_active(status))
+		return;
+
+	gc = purple_account_get_connection(account);
+	presence = purple_status_get_presence(status);
+	yd = (struct yahoo_data *)gc->proto_data;
+	old_status = yd->current_status;
+
+	yd->current_status = get_yahoo_status_from_purple_status(status);
+
+	if (yd->current_status == YAHOO_STATUS_CUSTOM)
+	{
+		msg = purple_status_get_attr_string(status, "message");
+
+		if (purple_status_is_available(status)) {
+			tmp = yahoo_string_encode(gc, msg, &utf8);
+			conv_msg = purple_markup_strip_html(tmp);
+			g_free(tmp);
+		} else {
+			if ((msg == NULL) || (*msg == '\0'))
+				msg = _("Away");
+			tmp = yahoo_string_encode(gc, msg, &utf8);
+			conv_msg = purple_markup_strip_html(tmp);
+			g_free(tmp);
+		}
+	}
+
+	if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
+		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash_str(pkt, 13, "2");
+		yahoo_packet_send_and_free(pkt, yd);
+
+		return;
+	}
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash_int(pkt, 10, yd->current_status);
+
+	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
+		yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
+		yahoo_packet_hash_str(pkt, 19, conv_msg);
+	} else {
+		yahoo_packet_hash_str(pkt, 19, "");
+	}
+
+	g_free(conv_msg);
+
+	if (purple_presence_is_idle(presence))
+		yahoo_packet_hash_str(pkt, 47, "2");
+	else if (!purple_status_is_available(status))
+		yahoo_packet_hash_str(pkt, 47, "1");
+
+	yahoo_packet_send_and_free(pkt, yd);
+
+	if (old_status == YAHOO_STATUS_INVISIBLE) {
+		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash_str(pkt, 13, "1");
+		yahoo_packet_send_and_free(pkt, yd);
+
+		/* Any per-session presence settings are removed */
+		g_hash_table_foreach(yd->friends, yahoo_session_presence_remove, NULL);
+
+	}
+}
+
+void yahoo_set_idle(PurpleConnection *gc, int idle)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	struct yahoo_packet *pkt = NULL;
+	char *msg = NULL, *msg2 = NULL;
+	PurpleStatus *status = NULL;
+
+	if (idle && yd->current_status != YAHOO_STATUS_CUSTOM)
+		yd->current_status = YAHOO_STATUS_IDLE;
+	else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) {
+		status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
+		yd->current_status = get_yahoo_status_from_purple_status(status);
+	}
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
+
+	yahoo_packet_hash_int(pkt, 10, yd->current_status);
+	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
+		const char *tmp;
+		if (status == NULL)
+			status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
+		tmp = purple_status_get_attr_string(status, "message");
+		if (tmp != NULL) {
+			gboolean utf8 = TRUE;
+			msg = yahoo_string_encode(gc, tmp, &utf8);
+			msg2 = purple_markup_strip_html(msg);
+			yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
+			yahoo_packet_hash_str(pkt, 19, msg2);
+		} else {
+			/* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
+			 * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */
+			yahoo_packet_hash_str(pkt, 19, _("Away"));
+		}
+	} else {
+		yahoo_packet_hash_str(pkt, 19, "");
+	}
+
+	if (idle)
+		yahoo_packet_hash_str(pkt, 47, "2");
+	else if (!purple_presence_is_available(purple_account_get_presence(purple_connection_get_account(gc))))
+		yahoo_packet_hash_str(pkt, 47, "1");
+
+	yahoo_packet_send_and_free(pkt, yd);
+
+	g_free(msg);
+	g_free(msg2);
+}
+
+GList *yahoo_status_types(PurpleAccount *account)
+{
+	PurpleStatusType *type;
+	GList *types = NULL;
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, YAHOO_STATUS_TYPE_AVAILABLE,
+	                                       NULL, TRUE, TRUE, FALSE,
+	                                       "message", _("Message"),
+	                                       purple_value_new(PURPLE_TYPE_STRING), NULL);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_AWAY,
+	                                       NULL, TRUE, TRUE, FALSE,
+	                                       "message", _("Message"),
+	                                       purple_value_new(PURPLE_TYPE_STRING), NULL);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY, _("Busy"), TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATDESK, _("Not at Desk"), TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTINOFFICE, _("Not in Office"), TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_ONPHONE, _("On the Phone"), TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_EXTENDED_AWAY, YAHOO_STATUS_TYPE_ONVACATION, _("On Vacation"), TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_OUTTOLUNCH, _("Out to Lunch"), TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_STEPPEDOUT, _("Stepped Out"), TRUE);
+	types = g_list_append(types, type);
+
+
+	type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, YAHOO_STATUS_TYPE_INVISIBLE, NULL, TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new(PURPLE_STATUS_OFFLINE, YAHOO_STATUS_TYPE_OFFLINE, NULL, TRUE);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, YAHOO_STATUS_TYPE_MOBILE, NULL, FALSE, FALSE, TRUE);
+	types = g_list_append(types, type);
+
+	return types;
+}
+
+void yahoo_keepalive(PurpleConnection *gc)
+{
+	struct yahoo_packet *pkt;
+	struct yahoo_data *yd = gc->proto_data;
+	time_t now = time(NULL);
+
+	/* We're only allowed to send a ping once an hour or the servers will boot us */
+	if ((now - yd->last_ping) >= PING_TIMEOUT) {
+		yd->last_ping = now;
+
+		/* The native client will only send PING or CHATPING */
+		if (yd->chat_online) {
+			if (yd->wm) {
+				ycht_chat_send_keepalive(yd->ycht);
+			} else {
+				pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, 0);
+				yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc));
+				yahoo_packet_send_and_free(pkt, yd);
+			}
+		} else {
+			pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, 0);
+			yahoo_packet_send_and_free(pkt, yd);
+		}
+	}
+
+	if ((now - yd->last_keepalive) >= KEEPALIVE_TIMEOUT) {
+		yd->last_keepalive = now;
+		pkt = yahoo_packet_new(YAHOO_SERVICE_KEEPALIVE, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc));
+		yahoo_packet_send_and_free(pkt, yd);
+	}
+
+}
+
+void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
+{
+	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
+	struct yahoo_packet *pkt;
+	const char *group = NULL;
+	char *group2;
+	YahooFriend *f;
+	const char *bname;
+	gboolean msn = FALSE;
+
+	if (!yd->logged_in)
+		return;
+
+	bname = purple_buddy_get_name(buddy);
+	if (!purple_privacy_check(purple_connection_get_account(gc), bname))
+		return;
+
+	f = yahoo_friend_find(gc, bname);
+	msn = !g_strncasecmp(bname, "msn/", 4);
+
+	g = purple_buddy_get_group(buddy);
+	if (g)
+		group = purple_group_get_name(g);
+	else
+		group = "Buddies";
+
+	group2 = yahoo_string_encode(gc, group, NULL);
+	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, 0);
+	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);
+}
+
+void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+{
+	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
+	struct yahoo_packet *pkt;
+	GSList *buddies, *l;
+	PurpleGroup *g;
+	gboolean remove = TRUE;
+	char *cg;
+	const char *bname, *gname;
+	YahooFriend *f = NULL;
+	gboolean msn = FALSE;
+
+	bname = purple_buddy_get_name(buddy);
+	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))) {
+			remove = FALSE;
+			break;
+		}
+	}
+
+	g_slist_free(buddies);
+
+	if (remove)
+		g_hash_table_remove(yd->friends, bname);
+
+	cg = yahoo_string_encode(gc, gname, NULL);
+	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, 0);
+
+	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);
+}
+
+void yahoo_add_deny(PurpleConnection *gc, const char *who) {
+	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
+	struct yahoo_packet *pkt;
+
+	if (!yd->logged_in)
+		return;
+
+	if (!who || who[0] == '\0')
+		return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
+	                  7, who, 13, "1");
+	yahoo_packet_send_and_free(pkt, yd);
+}
+
+void yahoo_rem_deny(PurpleConnection *gc, const char *who) {
+	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
+	struct yahoo_packet *pkt;
+
+	if (!yd->logged_in)
+		return;
+
+	if (!who || who[0] == '\0')
+		return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "2");
+	yahoo_packet_send_and_free(pkt, yd);
+}
+
+void yahoo_set_permit_deny(PurpleConnection *gc)
+{
+	PurpleAccount *account;
+	GSList *deny;
+
+	account = purple_connection_get_account(gc);
+
+	switch (account->perm_deny)
+	{
+		case PURPLE_PRIVACY_ALLOW_ALL:
+			for (deny = account->deny; deny; deny = deny->next)
+				yahoo_rem_deny(gc, deny->data);
+			break;
+
+		case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
+		case PURPLE_PRIVACY_ALLOW_USERS:
+		case PURPLE_PRIVACY_DENY_USERS:
+		case PURPLE_PRIVACY_DENY_ALL:
+			for (deny = account->deny; deny; deny = deny->next)
+				yahoo_add_deny(gc, deny->data);
+			break;
+	}
+}
+
+void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
+				   const char *old_group, const char *new_group)
+{
+	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 (!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.
+	 */
+	gpn = yahoo_string_encode(gc, new_group, NULL);
+	gpo = yahoo_string_encode(gc, old_group, NULL);
+	if (!strcmp(gpn, gpo)) {
+		g_free(gpn);
+		g_free(gpo);
+		return;
+	}
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, 0);
+	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);
+
+	g_free(gpn);
+	g_free(gpo);
+}
+
+void yahoo_rename_group(PurpleConnection *gc, const char *old_name,
+							   PurpleGroup *group, GList *moved_buddies)
+{
+	struct yahoo_data *yd = gc->proto_data;
+	struct yahoo_packet *pkt;
+	char *gpn, *gpo;
+
+	gpn = yahoo_string_encode(gc, purple_group_get_name(group), NULL);
+	gpo = yahoo_string_encode(gc, old_name, NULL);
+	if (!strcmp(gpn, gpo)) {
+		g_free(gpn);
+		g_free(gpo);
+		return;
+	}
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
+	                  65, gpo, 67, gpn);
+	yahoo_packet_send_and_free(pkt, yd);
+	g_free(gpn);
+	g_free(gpo);
+}
+
+/********************************* Commands **********************************/
+
+PurpleCmdRet
+yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data) {
+	PurpleAccount *account = purple_conversation_get_account(c);
+
+	if (*args && args[0])
+		return PURPLE_CMD_RET_FAILED;
+
+	purple_prpl_send_attention(account->gc, c->name, YAHOO_BUZZ);
+
+	return PURPLE_CMD_RET_OK;
+}
+
+PurpleCmdRet
+yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd,
+                        char **args, char **error, void *data)
+{
+	GHashTable *comp;
+	PurpleConnection *gc;
+	struct yahoo_data *yd;
+	int id;
+
+	if (!args || !args[0])
+		return PURPLE_CMD_RET_FAILED;
+
+	gc = purple_conversation_get_gc(conv);
+	yd = gc->proto_data;
+	id = yd->conf_id;
+	purple_debug_info("yahoo", "Trying to join %s \n", args[0]);
+
+	comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+	g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1));
+	g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat"));
+
+	yahoo_c_join(gc, comp);
+
+	g_hash_table_destroy(comp);
+	return PURPLE_CMD_RET_OK;
+}
+
+PurpleCmdRet
+yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd,
+                        char **args, char **error, void *data)
+{
+	PurpleAccount *account = purple_conversation_get_account(conv);
+	if (*args && args[0])
+		return PURPLE_CMD_RET_FAILED;
+	purple_roomlist_show_with_account(account);
+	return PURPLE_CMD_RET_OK;
+}
+
+gboolean yahoo_offline_message(const PurpleBuddy *buddy)
+{
+	return TRUE;
+}
+
+gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type)
+{
+	PurpleConversation *c;
+
+	c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+			username, gc->account);
+
+	g_return_val_if_fail(c != NULL, FALSE);
+
+	purple_debug_info("yahoo", "Sending <ding> on account %s to buddy %s.\n",
+			username, c->name);
+	purple_conv_im_send_with_flags(PURPLE_CONV_IM(c), "<ding>", PURPLE_MESSAGE_INVISIBLE);
+
+	return TRUE;
+}
+
+GList *yahoo_attention_types(PurpleAccount *account)
+{
+	static GList *list = NULL;
+
+	if (!list) {
+		/* Yahoo only supports one attention command: the 'buzz'. */
+		/* This is index number YAHOO_BUZZ. */
+		list = g_list_append(list, purple_attention_type_new("Buzz", _("Buzz"),
+				_("%s has buzzed you!"), _("Buzzing %s...")));
+	}
+
+	return list;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/yahoo/libymsg.h	Mon Jul 06 09:08:17 2009 +0000
@@ -0,0 +1,338 @@
+/**
+ * @file libymsg.h The Yahoo! and Yahoo! JAPAN Protocol Plugins
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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
+ */
+
+#ifndef _LIBYMSG_H_
+#define _LIBYMSG_H_
+
+#include "circbuffer.h"
+#include "cmds.h"
+#include "prpl.h"
+
+#define YAHOO_PAGER_HOST "scsa.msg.yahoo.com"
+#define YAHOO_PAGER_PORT 5050
+#define YAHOO_PAGER_PORT_P2P 5101
+#define YAHOO_LOGIN_URL "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s"
+#define YAHOO_TOKEN_URL "https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s"
+#define YAHOO_P2P_KEEPALIVE_SECS 300
+#define YAHOO_P2P_SERVER_TIMEOUT 10
+#define YAHOO_PROFILE_URL "http://profiles.yahoo.com/"
+#define YAHOO_MAIL_URL "http://rd.yahoo.com/messenger/client/?http://mail.yahoo.com/"
+#define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com"
+#define YAHOO_XFER_PORT 80
+#define YAHOO_XFER_RELAY_HOST "relay.msg.yahoo.com"
+#define YAHOO_XFER_RELAY_PORT 80
+#define YAHOO_ROOMLIST_URL "http://insider.msg.yahoo.com/ycontent/"
+#define YAHOO_ROOMLIST_LOCALE "us"
+/* really we should get the list of servers from
+ http://update.messenger.yahoo.co.jp/servers.html */
+#define YAHOOJP_PAGER_HOST "cs.yahoo.co.jp"
+#define YAHOOJP_TOKEN_URL "https://login.yahoo.co.jp/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s"
+#define YAHOOJP_LOGIN_URL "https://login.yahoo.co.jp/config/pwtoken_login?src=ymsgr&ts=&token=%s"
+#define YAHOOJP_PROFILE_URL "http://profiles.yahoo.co.jp/"
+#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: */
+#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/"
+#define YAHOOJP_ROOMLIST_LOCALE "ja"
+
+#define YAHOO_AUDIBLE_URL "http://us.dl1.yimg.com/download.yahoo.com/dl/aud"
+
+#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"
+
+#define YAHOO_STATUS_TYPE_OFFLINE "offline"
+#define YAHOO_STATUS_TYPE_AVAILABLE "available"
+#define YAHOO_STATUS_TYPE_BRB "brb"
+#define YAHOO_STATUS_TYPE_BUSY "busy"
+#define YAHOO_STATUS_TYPE_NOTATHOME "notathome"
+#define YAHOO_STATUS_TYPE_NOTATDESK "notatdesk"
+#define YAHOO_STATUS_TYPE_NOTINOFFICE "notinoffice"
+#define YAHOO_STATUS_TYPE_ONPHONE "onphone"
+#define YAHOO_STATUS_TYPE_ONVACATION "onvacation"
+#define YAHOO_STATUS_TYPE_OUTTOLUNCH "outtolunch"
+#define YAHOO_STATUS_TYPE_STEPPEDOUT "steppedout"
+#define YAHOO_STATUS_TYPE_AWAY "away"
+#define YAHOO_STATUS_TYPE_INVISIBLE "invisible"
+#define YAHOO_STATUS_TYPE_MOBILE "mobile"
+
+#define YAHOO_CLIENT_VERSION_ID "4194239"
+#define YAHOO_CLIENT_VERSION "9.0.0.2152"
+
+#define YAHOOJP_CLIENT_VERSION_ID "4194239"
+#define YAHOOJP_CLIENT_VERSION "9.0.0.2152"
+
+#define YAHOO_CLIENT_USERAGENT "Mozilla/5.0"
+
+/* 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,
+	YAHOO_STATUS_BUSY,
+	YAHOO_STATUS_NOTATHOME,
+	YAHOO_STATUS_NOTATDESK,
+	YAHOO_STATUS_NOTINOFFICE,
+	YAHOO_STATUS_ONPHONE,
+	YAHOO_STATUS_ONVACATION,
+	YAHOO_STATUS_OUTTOLUNCH,
+	YAHOO_STATUS_STEPPEDOUT,
+	YAHOO_STATUS_INVISIBLE = 12,
+	YAHOO_STATUS_CUSTOM = 99,
+	YAHOO_STATUS_IDLE = 999,
+	YAHOO_STATUS_WEBLOGIN = 0x5a55aa55,
+	YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */
+	YAHOO_STATUS_TYPING = 0x16,
+	YAHOO_STATUS_DISCONNECTED = 0xffffffff /* in ymsg 15. doesnt mean the normal sense of 'disconnected' */
+};
+
+struct yahoo_buddy_icon_upload_data {
+	PurpleConnection *gc;
+	GString *str;
+	char *filename;
+	int pos;
+	int fd;
+	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 {
+	PurpleConnection *gc;
+	int fd;
+	guchar *rxqueue;
+	int rxlen;
+	PurpleCircBuffer *txbuf;
+	guint txhandler;
+	GHashTable *friends;
+
+	char **profiles;  /* Multiple profiles can be associated with an account */
+
+	/**
+	 * This is used to keep track of the IMVironment chosen
+	 * by people you talk to.  We don't do very much with
+	 * this right now... but at least now if the remote user
+	 * selects an IMVironment we won't reset it back to the
+	 * default of nothing.
+	 */
+	GHashTable *imvironments;
+
+	int current_status;
+	gboolean logged_in;
+	GString *tmp_serv_blist, *tmp_serv_ilist, *tmp_serv_plist;
+	GSList *confs;
+	unsigned int conf_id; /* just a counter */
+	gboolean chat_online;
+	gboolean in_chat;
+	char *chat_name;
+	char *pending_chat_room;
+	char *pending_chat_id;
+	char *pending_chat_topic;
+	char *pending_chat_goto;
+	char *auth;
+	gsize auth_written;
+	char *cookie_y;
+	char *cookie_t;
+	int session_id;
+	gboolean jp;
+	gboolean wm; /* connected w/ web messenger method */
+	/* picture aka buddy icon stuff */
+	char *picture_url;
+	int picture_checksum;
+
+	/* ew. we have to check the icon before we connect,
+	 * but can't upload it til we're connected. */
+	struct yahoo_buddy_icon_upload_data *picture_upload_todo;
+	PurpleProxyConnectData *buddy_icon_connect_data;
+
+	struct _YchtConn *ycht;
+
+	/**
+	 * This linked list contains PurpleUtilFetchUrlData structs
+	 * 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 */
+
+	/**
+	 * We may receive a list15 in multiple packets with no prior warning as to how many we'll be getting;
+	 * the server expects us to keep track of the group for which it is sending us contact names.
+	 */
+	char *current_list15_grp;
+	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)
+
+/*
+ * Current Maximum Length for Instant Messages
+ *
+ * This was found by experiment.
+ *
+ * The YMSG protocol allows a message of up to 948 bytes, but the official client
+ * limits to 800 characters.  According to experiments I conducted, it seems that
+ * the discrepancy is to allow some leeway for messages with mixed single- and
+ * multi-byte characters, as I was able to send messages of 840 and 932 bytes
+ * by using some multibyte characters (some random Chinese or Japanese characters,
+ * to be precise). - rekkanoryo
+ */
+#define YAHOO_MAX_MESSAGE_LENGTH_BYTES 948
+#define YAHOO_MAX_MESSAGE_LENGTH_CHARS 800
+
+/* sometimes i wish prpls could #include things from other prpls. then i could just
+ * use the routines from libfaim and not have to admit to knowing how they work. */
+#define yahoo_put16(buf, data) ( \
+		(*(buf) = (unsigned char)((data)>>8)&0xff), \
+		(*((buf)+1) = (unsigned char)(data)&0xff),  \
+		2)
+#define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
+#define yahoo_put32(buf, data) ( \
+		(*((buf)) = (unsigned char)((data)>>24)&0xff), \
+		(*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
+		(*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
+		(*((buf)+3) = (unsigned char)(data)&0xff), \
+		4)
+#define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \
+		(((*((buf)+1))<<16)&0x00ff0000) + \
+		(((*((buf)+2))<< 8)&0x0000ff00) + \
+		(((*((buf)+3)    )&0x000000ff)))
+
+/* util.c */
+void yahoo_init_colorht(void);
+void yahoo_dest_colorht(void);
+char *yahoo_codes_to_html(const char *x);
+char *yahoo_html_to_codes(const char *src);
+
+gboolean
+yahoo_account_use_http_proxy(PurpleConnection *conn);
+
+/**
+ * Encode some text to send to the yahoo server.
+ *
+ * @param gc The connection handle.
+ * @param str The null terminated utf8 string to encode.
+ * @param utf8 If not @c NULL, whether utf8 is okay or not.
+ *             Even if it is okay, we may not use it. If we
+ *             used it, we set this to @c TRUE, else to
+ *             @c FALSE. If @c NULL, false is assumed, and
+ *             it is not dereferenced.
+ * @return The g_malloced string in the appropriate encoding.
+ */
+char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8);
+
+/**
+ * Decode some text received from the server.
+ *
+ * @param gc The gc handle.
+ * @param str The null terminated string to decode.
+ * @param utf8 Did the server tell us it was supposed to be utf8?
+ * @return The decoded, utf-8 string, which must be g_free()'d.
+ */
+char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8);
+
+char *yahoo_convert_to_numeric(const char *str);
+
+
+/* yahoo_profile.c */
+void yahoo_get_info(PurpleConnection *gc, const char *name);
+
+/* libymsg.h  - these functions were formerly static but need not to be for the
+ * new two-prpl model. */
+const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b);
+const char *yahoo_list_emblem(PurpleBuddy *b);
+char *yahoo_status_text(PurpleBuddy *b);
+void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
+GList *yahoo_status_types(PurpleAccount *account);
+GList *yahoo_blist_node_menu(PurpleBlistNode *node);
+void yahoo_login(PurpleAccount *account);
+void yahoo_close(PurpleConnection *gc);
+int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags);
+unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state);
+void yahoo_set_status(PurpleAccount *account, PurpleStatus *status);
+void yahoo_set_idle(PurpleConnection *gc, int idle);
+void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g);
+void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
+void yahoo_add_deny(PurpleConnection *gc, const char *who);
+void yahoo_rem_deny(PurpleConnection *gc, const char *who);
+void yahoo_set_permit_deny(PurpleConnection *gc);
+void yahoo_keepalive(PurpleConnection *gc);
+void yahoo_change_buddys_group(PurpleConnection *gc, const char *who, const char *old_group, const char *new_group);
+void yahoo_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies);
+gboolean yahoo_offline_message(const PurpleBuddy *buddy);
+gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type);
+GList *yahoo_attention_types(PurpleAccount *account);
+
+GList *yahoo_actions(PurplePlugin *plugin, gpointer context);
+void yahoopurple_register_commands(void);
+
+PurpleCmdRet yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data);
+PurpleCmdRet yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data);
+PurpleCmdRet yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data);
+/* needed for xfer, thought theyd be useful for other enhancements later on
+   Returns list of cookies stored in yahoo_data formatted as a single null terminated string
+   returned value must be g_freed
+*/
+gchar* yahoo_get_cookies(PurpleConnection *gc);
+
+/* 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 /* _LIBYMSG_H_ */
--- a/libpurple/protocols/yahoo/util.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/util.c	Mon Jul 06 09:08:17 2009 +0000
@@ -28,7 +28,7 @@
 #include "internal.h"
 #include "prpl.h"
 
-#include "yahoo.h"
+#include "libymsg.h"
 
 #include <string.h>
 
--- a/libpurple/protocols/yahoo/yahoo.c	Mon Jul 06 09:07:42 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5252 +0,0 @@
-/*
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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 "account.h"
-#include "accountopt.h"
-#include "blist.h"
-#include "cipher.h"
-#include "cmds.h"
-#include "core.h"
-#include "debug.h"
-#include "network.h"
-#include "notify.h"
-#include "privacy.h"
-#include "prpl.h"
-#include "proxy.h"
-#include "request.h"
-#include "server.h"
-#include "util.h"
-#include "version.h"
-#include "xmlnode.h"
-
-#include "yahoo.h"
-#include "yahoochat.h"
-#include "yahoo_aliases.h"
-#include "yahoo_doodle.h"
-#include "yahoo_filexfer.h"
-#include "yahoo_friend.h"
-#include "yahoo_packet.h"
-#include "yahoo_picture.h"
-#include "ycht.h"
-
-/* #define YAHOO_DEBUG */
-
-/* #define TRY_WEBMESSENGER_LOGIN 0 */
-
-/* One hour */
-#define PING_TIMEOUT 3600
-
-/* One minute */
-#define KEEPALIVE_TIMEOUT 60
-
-static void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *, PurpleGroup *);
-#ifdef TRY_WEBMESSENGER_LOGIN
-static void yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message);
-#endif /* TRY_WEBMESSENGER_LOGIN */
-static void yahoo_set_status(PurpleAccount *account, PurpleStatus *status);
-
-static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFriend *f)
-{
-	char *status = NULL;
-
-	if (!gc || !name || !f || !purple_find_buddy(purple_connection_get_account(gc), name))
-		return;
-
-	switch (f->status) {
-	case YAHOO_STATUS_OFFLINE:
-		status = YAHOO_STATUS_TYPE_OFFLINE;
-		break;
-	case YAHOO_STATUS_AVAILABLE:
-		status = YAHOO_STATUS_TYPE_AVAILABLE;
-		break;
-	case YAHOO_STATUS_BRB:
-		status = YAHOO_STATUS_TYPE_BRB;
-		break;
-	case YAHOO_STATUS_BUSY:
-		status = YAHOO_STATUS_TYPE_BUSY;
-		break;
-	case YAHOO_STATUS_NOTATHOME:
-		status = YAHOO_STATUS_TYPE_NOTATHOME;
-		break;
-	case YAHOO_STATUS_NOTATDESK:
-		status = YAHOO_STATUS_TYPE_NOTATDESK;
-		break;
-	case YAHOO_STATUS_NOTINOFFICE:
-		status = YAHOO_STATUS_TYPE_NOTINOFFICE;
-		break;
-	case YAHOO_STATUS_ONPHONE:
-		status = YAHOO_STATUS_TYPE_ONPHONE;
-		break;
-	case YAHOO_STATUS_ONVACATION:
-		status = YAHOO_STATUS_TYPE_ONVACATION;
-		break;
-	case YAHOO_STATUS_OUTTOLUNCH:
-		status = YAHOO_STATUS_TYPE_OUTTOLUNCH;
-		break;
-	case YAHOO_STATUS_STEPPEDOUT:
-		status = YAHOO_STATUS_TYPE_STEPPEDOUT;
-		break;
-	case YAHOO_STATUS_INVISIBLE: /* this should never happen? */
-		status = YAHOO_STATUS_TYPE_INVISIBLE;
-		break;
-	case YAHOO_STATUS_CUSTOM:
-	case YAHOO_STATUS_IDLE:
-		if (!f->away)
-			status = YAHOO_STATUS_TYPE_AVAILABLE;
-		else
-			status = YAHOO_STATUS_TYPE_AWAY;
-		break;
-	default:
-		purple_debug_warning("yahoo", "Warning, unknown status %d\n", f->status);
-		break;
-	}
-
-	if (status) {
-		if (f->status == YAHOO_STATUS_CUSTOM)
-			purple_prpl_got_user_status(purple_connection_get_account(gc), name, status, "message",
-			                          yahoo_friend_get_status_message(f), NULL);
-		else
-			purple_prpl_got_user_status(purple_connection_get_account(gc), name, status, NULL);
-	}
-
-	if (f->idle != 0)
-		purple_prpl_got_user_idle(purple_connection_get_account(gc), name, TRUE, f->idle);
-	else
-		purple_prpl_got_user_idle(purple_connection_get_account(gc), name, FALSE, 0);
-
-	if (f->sms)
-		purple_prpl_got_user_status(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE, NULL);
-	else
-		purple_prpl_got_user_status_deactive(purple_connection_get_account(gc), name, YAHOO_STATUS_TYPE_MOBILE);
-}
-
-static void yahoo_process_status(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	PurpleAccount *account = purple_connection_get_account(gc);
-	GSList *l = pkt->hash;
-	YahooFriend *f = NULL;
-	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))
-			purple_account_set_password(account, NULL);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE,
-			_("You have signed on from another location"));
-		return;
-	}
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-
-		switch (pair->key) {
-		case 0: /* we won't actually do anything with this */
-		case 1: /* we won't actually do anything with this */
-			break;
-		case 8: /* how many online buddies we have */
-			break;
-		case 7: /* the current buddy */
-			/* update the previous buddy before changing the variables */
-			if (f) {
-				if (message)
-					yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
-				if (name)
-					yahoo_update_status(gc, name, f);
-			}
-			name = message = NULL;
-			f = NULL;
-			if (pair->value && g_utf8_validate(pair->value, -1, NULL)) {
-				GSList *tmplist;
-				int protocol = 0;
-
-				name = pair->value;
-
-				/* Look ahead to see if we have the protocol info about the buddy */
-				for (tmplist = l->next; tmplist; tmplist = tmplist->next) {
-					struct yahoo_pair *p = tmplist->data;
-					if (p->key == 7)
-						break;
-					if (p->key == 241) {
-						if(strtol(p->value, NULL, 10) == 2) {
-							g_free(msn_name);
-							msn_name = g_strconcat("msn/", name, NULL);
-							name = msn_name;
-							protocol = 2;
-						}
-						break;
-					}
-				}
-				f = yahoo_friend_find_or_new(gc, name);
-				f->protocol = protocol;
-			}
-			break;
-		case 10: /* state */
-			if (!f)
-				break;
-
-			f->status = strtol(pair->value, NULL, 10);
-			if ((f->status >= YAHOO_STATUS_BRB) && (f->status <= YAHOO_STATUS_STEPPEDOUT))
-				f->away = 1;
-			else
-				f->away = 0;
-
-			if (f->status == YAHOO_STATUS_IDLE) {
-				/* Idle may have already been set in a more precise way in case 137 */
-				if (f->idle == 0)
-					f->idle = time(NULL);
-			} else
-				f->idle = 0;
-
-			if (f->status != YAHOO_STATUS_CUSTOM)
-				yahoo_friend_set_status_message(f, NULL);
-
-			f->sms = 0;
-			break;
-		case 19: /* custom message */
-			if (f)
-				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;
-		case 47: /* is custom status away or not? 2=idle*/
-			if (!f)
-				break;
-
-			/* I have no idea what it means when this is
-			 * set when someone's available, but it doesn't
-			 * mean idle. */
-			if (f->status == YAHOO_STATUS_AVAILABLE)
-				break;
-
-			f->away = strtol(pair->value, NULL, 10);
-			if (f->away == 2) {
-				/* Idle may have already been set in a more precise way in case 137 */
-				if (f->idle == 0)
-					f->idle = time(NULL);
-			}
-
-			break;
-		case 138: /* either we're not idle, or we are but won't say how long */
-			if (!f)
-				break;
-
-			if (f->idle)
-				f->idle = -1;
-			break;
-		case 137: /* usually idle time in seconds, sometimes login time */
-			if (!f)
-				break;
-
-			if (f->status != YAHOO_STATUS_AVAILABLE)
-				f->idle = time(NULL) - strtol(pair->value, NULL, 10);
-			break;
-		case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
-			if (strtol(pair->value, NULL, 10) == 0) {
-				if (f)
-					f->status = YAHOO_STATUS_OFFLINE;
-				if (name) {
-					purple_prpl_got_user_status(account, name, "offline", NULL);
-					purple_prpl_got_user_status_deactive(account, name, YAHOO_STATUS_TYPE_MOBILE);
-				}
-				break;
-			}
-			break;
-		case 60: /* SMS */
-			if (f) {
-				f->sms = strtol(pair->value, NULL, 10);
-				yahoo_update_status(gc, name, f);
-			}
-			break;
-		case 197: /* Avatars */
-		{
-			guchar *decoded;
-			char *tmp;
-			gsize len;
-
-			if (pair->value) {
-				decoded = purple_base64_decode(pair->value, &len);
-				if (len) {
-					tmp = purple_str_binary_to_ascii(decoded, len);
-					purple_debug_info("yahoo", "Got key 197, value = %s\n", tmp);
-					g_free(tmp);
-				}
-				g_free(decoded);
-			}
-			break;
-		}
-		case 192: /* Pictures, aka Buddy Icons, checksum */
-		{
-			/* FIXME: Please, if you know this protocol,
-			 * FIXME: fix up the strtol() stuff if possible. */
-			int cksum = strtol(pair->value, NULL, 10);
-			const char *locksum = NULL;
-			PurpleBuddy *b;
-
-			if (!name)
-				break;
-
-			b = purple_find_buddy(gc->account, name);
-
-			if (!cksum || (cksum == -1)) {
-				if (f)
-					yahoo_friend_set_buddy_icon_need_request(f, TRUE);
-				purple_buddy_icons_set_for_user(gc->account, name, NULL, 0, NULL);
-				break;
-			}
-
-			if (!f)
-				break;
-
-			yahoo_friend_set_buddy_icon_need_request(f, FALSE);
-			if (b) {
-				locksum = purple_buddy_icons_get_checksum_for_user(b);
-				if (!locksum || (cksum != strtol(locksum, NULL, 10)))
-					yahoo_send_picture_request(gc, name);
-			}
-
-			break;
-		}
-		case 16: /* Custom error message */
-			{
-				char *tmp = yahoo_string_decode(gc, pair->value, TRUE);
-				purple_notify_error(gc, NULL, tmp, NULL);
-				g_free(tmp);
-			}
-			break;
-		case 97: /* Unicode status message */
-			unicode = !strcmp(pair->value, "1");
-			break;
-		case 244: /* client version number. Yahoo Client Detection */
-			if(f && strtol(pair->value, NULL, 10))
-				f->version_id = strtol(pair->value, NULL, 10);
-			break;
-		case 241: /* protocol buddy belongs to */
-			break;  /* We process this when get '7' */
-		default:
-			purple_debug_warning("yahoo",
-					   "Unknown status key %d\n", pair->key);
-			break;
-		}
-
-		l = l->next;
-	}
-
-	if (f) {
-		if (pkt->service == YAHOO_SERVICE_LOGOFF)
-			f->status = YAHOO_STATUS_OFFLINE;
-		if (message)
-			yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode));
-
-		if (name) /* update the last buddy */
-			yahoo_update_status(gc, name, f);
-	}
-	g_free(msn_name);
-}
-
-static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group)
-{
-	PurpleBuddy *b;
-	PurpleGroup *g;
-	GSList *list, *i;
-	gboolean onlist = 0;
-	char *oname = NULL;
-	char **oname_p = &oname;
-	GSList **list_p = &list;
-
-	if (!g_hash_table_lookup_extended(ht, purple_normalize(account, name), (gpointer *) oname_p, (gpointer *) list_p))
-		list = purple_find_buddies(account, name);
-	else
-		g_hash_table_steal(ht, name);
-
-	for (i = list; i; i = i->next) {
-		b = i->data;
-		g = purple_buddy_get_group(b);
-		if (!purple_utf8_strcasecmp(group, purple_group_get_name(g))) {
-			purple_debug_misc("yahoo",
-				"Oh good, %s is in the right group (%s).\n", name, group);
-			list = g_slist_delete_link(list, i);
-			onlist = 1;
-			break;
-		}
-	}
-
-	if (!onlist) {
-		purple_debug_misc("yahoo",
-			"Uhoh, %s isn't on the list (or not in this group), adding him to group %s.\n", name, group);
-		if (!(g = purple_find_group(group))) {
-			g = purple_group_new(group);
-			purple_blist_add_group(g, NULL);
-		}
-		b = purple_buddy_new(account, name, NULL);
-		purple_blist_add_buddy(b, NULL, g, NULL);
-	}
-
-	if (list) {
-		if (!oname)
-			oname = g_strdup(purple_normalize(account, name));
-		g_hash_table_insert(ht, oname, list);
-	} else if (oname)
-		g_free(oname);
-}
-
-static void yahoo_do_group_cleanup(gpointer key, gpointer value, gpointer user_data)
-{
-	char *name = key;
-	GSList *list = value, *i;
-	PurpleBuddy *b;
-	PurpleGroup *g;
-
-	for (i = list; i; i = i->next) {
-		b = i->data;
-		g = purple_buddy_get_group(b);
-		purple_debug_misc("yahoo", "Deleting Buddy %s from group %s.\n", name,
-				purple_group_get_name(g));
-		purple_blist_remove_buddy(b);
-	}
-}
-
-static char *_getcookie(char *rawcookie)
-{
-	char *cookie = NULL;
-	char *tmpcookie;
-	char *cookieend;
-
-	if (strlen(rawcookie) < 2)
-		return NULL;
-	tmpcookie = g_strdup(rawcookie+2);
-	cookieend = strchr(tmpcookie, ';');
-
-	if (cookieend)
-		*cookieend = '\0';
-
-	cookie = g_strdup(tmpcookie);
-	g_free(tmpcookie);
-
-	return cookie;
-}
-
-static void yahoo_process_cookie(struct yahoo_data *yd, char *c)
-{
-	if (c[0] == 'Y') {
-		if (yd->cookie_y)
-			g_free(yd->cookie_y);
-		yd->cookie_y = _getcookie(c);
-	} else if (c[0] == 'T') {
-		if (yd->cookie_t)
-			g_free(yd->cookie_t);
-		yd->cookie_t = _getcookie(c);
-	} else
-		purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]);
-	yd->cookies = g_slist_prepend(yd->cookies, g_strdup(c));
-}
-
-static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	GSList *l = pkt->hash;
-
-	PurpleAccount *account = purple_connection_get_account(gc);
-	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);
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-		l = l->next;
-
-		switch (pair->key) {
-		case 302:
-			/* This is always 318 before a group, 319 before the first s/n in a group, 320 before any ignored s/n.
-			 * It is not sent for s/n's in a group after the first.
-			 * All ignored s/n's are listed last, so when we see a 320 we clear the group and begin marking the
-			 * s/n's as ignored.  It is always followed by an identical 300 key.
-			 */
-			if (pair->value && !strcmp(pair->value, "320")) {
-				/* No longer in any group; this indicates the start of the ignore list. */
-				g_free(yd->current_list15_grp);
-				yd->current_list15_grp = NULL;
-			}
-
-			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;
-		case 65: /* This is the group */
-			g_free(yd->current_list15_grp);
-			yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE);
-			break;
-		case 7: /* buddy's s/n */
-			temp = g_strdup(purple_normalize(account, pair->value));
-			break;
-		case 241: /* another protocol user */
-			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 */
-			stealth = strtol(pair->value, NULL, 10);
-			break;
-		/* case 242: */ /* this seems related to 241 */
-			/* break; */
-		}
-	}
-
-	g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
-
-	/* Now that we have processed the buddy list, we can say yahoo has connected */
-	purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account)));
-	purple_connection_set_state(gc, PURPLE_CONNECTED);
-	yd->logged_in = TRUE;
-	if (yd->picture_upload_todo) {
-		yahoo_buddy_icon_upload(gc, yd->picture_upload_todo);
-		yd->picture_upload_todo = NULL;
-	}
-	yahoo_set_status(account, purple_account_get_active_status(account));
-	purple_debug_info("yahoo","Authentication: Connection established\n");
-
-	g_hash_table_destroy(ht);
-	g_free(norm_bud);
-	g_free(temp);
-}
-
-static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	GSList *l = pkt->hash;
-	gboolean export = FALSE;
-	gboolean got_serv_list = FALSE;
-	PurpleBuddy *b;
-	PurpleGroup *g;
-	YahooFriend *f = NULL;
-	PurpleAccount *account = purple_connection_get_account(gc);
-	struct yahoo_data *yd = gc->proto_data;
-	GHashTable *ht;
-
-	char **lines;
-	char **split;
-	char **buddies;
-	char **tmp, **bud, *norm_bud;
-	char *grp = NULL;
-
-	if (pkt->id)
-		yd->session_id = pkt->id;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-		l = l->next;
-
-		switch (pair->key) {
-		case 87:
-			if (!yd->tmp_serv_blist)
-				yd->tmp_serv_blist = g_string_new(pair->value);
-			else
-				g_string_append(yd->tmp_serv_blist, pair->value);
-			break;
-		case 88:
-			if (!yd->tmp_serv_ilist)
-				yd->tmp_serv_ilist = g_string_new(pair->value);
-			else
-				g_string_append(yd->tmp_serv_ilist, pair->value);
-			break;
-		case 89:
-			yd->profiles = g_strsplit(pair->value, ",", -1);
-			break;
-		case 59: /* cookies, yum */
-			yahoo_process_cookie(yd, pair->value);
-			break;
-		case YAHOO_SERVICE_PRESENCE_PERM:
-			if (!yd->tmp_serv_plist)
-				yd->tmp_serv_plist = g_string_new(pair->value);
-			else
-				g_string_append(yd->tmp_serv_plist, pair->value);
-			break;
-		}
-	}
-
-	if (pkt->status != 0)
-		return;
-
-	if (yd->tmp_serv_blist) {
-		ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free);
-
-		lines = g_strsplit(yd->tmp_serv_blist->str, "\n", -1);
-		for (tmp = lines; *tmp; tmp++) {
-			split = g_strsplit(*tmp, ":", 2);
-			if (!split)
-				continue;
-			if (!split[0] || !split[1]) {
-				g_strfreev(split);
-				continue;
-			}
-			grp = yahoo_string_decode(gc, split[0], FALSE);
-			buddies = g_strsplit(split[1], ",", -1);
-			for (bud = buddies; bud && *bud; bud++) {
-				norm_bud = g_strdup(purple_normalize(account, *bud));
-				f = yahoo_friend_find_or_new(gc, norm_bud);
-
-				if (!(b = purple_find_buddy(account, norm_bud))) {
-					if (!(g = purple_find_group(grp))) {
-						g = purple_group_new(grp);
-						purple_blist_add_group(g, NULL);
-					}
-					b = purple_buddy_new(account, norm_bud, NULL);
-					purple_blist_add_buddy(b, NULL, g, NULL);
-					export = TRUE;
-				}
-
-				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);
-			g_strfreev(split);
-			g_free(grp);
-		}
-		g_strfreev(lines);
-
-		g_string_free(yd->tmp_serv_blist, TRUE);
-		yd->tmp_serv_blist = NULL;
-		g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL);
-		g_hash_table_destroy(ht);
-	}
-
-	if (yd->tmp_serv_ilist) {
-		buddies = g_strsplit(yd->tmp_serv_ilist->str, ",", -1);
-		for (bud = buddies; bud && *bud; bud++) {
-			/* The server is already ignoring the user */
-			got_serv_list = TRUE;
-			purple_privacy_deny_add(account, *bud, 1);
-		}
-		g_strfreev(buddies);
-
-		g_string_free(yd->tmp_serv_ilist, TRUE);
-		yd->tmp_serv_ilist = NULL;
-	}
-
-	if (got_serv_list &&
-		((account->perm_deny != PURPLE_PRIVACY_ALLOW_BUDDYLIST) &&
-		(account->perm_deny != PURPLE_PRIVACY_DENY_ALL) &&
-		(account->perm_deny != PURPLE_PRIVACY_ALLOW_USERS)))
-	{
-		account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
-		purple_debug_info("yahoo", "%s privacy defaulting to PURPLE_PRIVACY_DENY_USERS.\n",
-				account->username);
-	}
-
-	if (yd->tmp_serv_plist) {
-		buddies = g_strsplit(yd->tmp_serv_plist->str, ",", -1);
-		for (bud = buddies; bud && *bud; bud++) {
-			f = yahoo_friend_find(gc, *bud);
-			if (f) {
-				purple_debug_info("yahoo", "%s setting presence for %s to PERM_OFFLINE\n",
-						account->username, *bud);
-				f->presence = YAHOO_PRESENCE_PERM_OFFLINE;
-			}
-		}
-		g_strfreev(buddies);
-		g_string_free(yd->tmp_serv_plist, TRUE);
-		yd->tmp_serv_plist = NULL;
-
-	}
-	/* Now that we've got the list, request aliases */
-	yahoo_fetch_aliases(gc);
-}
-
-/* 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;
-	char *from = NULL;
-	char *stat = NULL;
-	char *game = NULL;
-	YahooFriend *f = NULL;
-	GSList *l = pkt->hash;
-	gint val_11 = 0;
-	struct yahoo_data *yd = gc->proto_data;
-	gboolean msn = FALSE;
-
-	account = purple_connection_get_account(gc);
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-		if (pair->key == 4 || pair->key == 1)
-			from = pair->value;
-		if (pair->key == 49)
-			msg = pair->value;
-		if (pair->key == 13)
-			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 (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING"))
-		&& (purple_privacy_check(account, from)))
-	{
-		if(msn) {
-			char *msn_from = g_strconcat("msn/", from, NULL);
-			if (*stat == '1')
-				serv_got_typing(gc, msn_from, 0, PURPLE_TYPING);
-			else
-				serv_got_typing_stopped(gc, msn_from);
-			g_free(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);
-
-		if (!bud) {
-			purple_debug_warning("yahoo",
-					   "%s is playing a game, and doesn't want you to know.\n", from);
-		}
-
-		f = yahoo_friend_find(gc, from);
-		if (!f)
-			return; /* if they're not on the list, don't bother */
-
-		yahoo_friend_set_game(f, NULL);
-
-		if (*stat == '1') {
-			yahoo_friend_set_game(f, game);
-			if (bud)
-				yahoo_update_status(gc, from, f);
-		}
-	} else if (!g_ascii_strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) {
-		PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account);
-		char *buf = g_strdup_printf(_("%s has sent you a webcam invite, which is not yet supported."), from);
-		purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
-		g_free(buf);
-	}
-}
-
-
-struct _yahoo_im {
-	char *from;
-	char *active_id;
-	int time;
-	int utf8;
-	int buddy_icon;
-	char *id;
-	char *msg;
-	gboolean msn;
-};
-
-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;
-	GSList *l = pkt->hash;
-	GSList *list = NULL;
-	struct _yahoo_im *im = NULL;
-	const char *imv = NULL;
-	gint val_11 = 0;
-
-	account = purple_connection_get_account(gc);
-
-	if (pkt->status <= 1 || pkt->status == 5 || 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 || pair->key == 1) {
-				im = g_new0(struct _yahoo_im, 1);
-				list = g_slist_append(list, im);
-				im->from = pair->value;
-				im->time = time(NULL);
-				im->utf8 = TRUE;
-			}
-			if (im && pair->key == 5)
-				im->active_id = pair->value;
-			if (pair->key == 97)
-				if (im)
-					im->utf8 = strtol(pair->value, NULL, 10);
-			if (pair->key == 15)
-				if (im)
-					im->time = strtol(pair->value, NULL, 10);
-			if (pair->key == 206)
-				if (im)
-					im->buddy_icon = strtol(pair->value, NULL, 10);
-			if (pair->key == 14) {
-				if (im)
-					im->msg = pair->value;
-			}
-			if (im && pair->key == 241) {
-				if(strtol(pair->value, NULL, 10) == 2)
-					im->msn = TRUE;
-			}
-			/* peer session id */
-			if (pair->key == 11) {
-				if (im)
-					val_11 = strtol(pair->value, NULL, 10);
-			}
-			/* IMV key */
-			if (pair->key == 63)
-			{
-				imv = pair->value;
-			}
-			if (pair->key == 429)
-				if (im)
-					im->id = pair->value;
-			l = l->next;
-		}
-	} else if (pkt->status == 2) {
-		purple_notify_error(gc, NULL,
-		                  _("Your Yahoo! message did not get sent."), NULL);
-	}
-
-	/* disconnect the peer if connected through p2p and sends wrong value for session id */
-	if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) {
-		purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from);
-		/* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */
-		g_hash_table_remove(yd->peers, im->from);
-		return;
-	}
-
-	/** TODO: It seems that this check should be per IM, not global */
-	/* Check for the Doodle IMV */
-	if (im != NULL && imv!= NULL && im->from != NULL)
-	{
-		g_hash_table_replace(yd->imvironments, g_strdup(im->from), g_strdup(imv));
-
-		if (strstr(imv, "doodle;") != NULL)
-		{
-			PurpleWhiteboard *wb;
-
-			if (!purple_privacy_check(account, im->from)) {
-				purple_debug_info("yahoo", "Doodle request from %s dropped.\n", im->from);
-				return;
-			}
-
-			/* I'm not sure the following ever happens -DAA */
-
-			wb = purple_whiteboard_get_session(account, im->from);
-
-			/* If a Doodle session doesn't exist between this user */
-			if(wb == NULL)
-			{
-				doodle_session *ds;
-				wb = purple_whiteboard_create(account, im->from, DOODLE_STATE_REQUESTED);
-				ds = wb->proto_data;
-				ds->imv_key = g_strdup(imv);
-
-				yahoo_doodle_command_send_request(gc, im->from, imv);
-				yahoo_doodle_command_send_ready(gc, im->from, imv);
-			}
-		}
-	}
-
-	for (l = list; l; l = l->next) {
-		YahooFriend *f;
-		char *m, *m2;
-		char *msn_from = NULL;
-		const char *from;
-		PurpleConversation *c;
-		im = l->data;
-
-		if (!im->from || !im->msg) {
-			g_free(im);
-			continue;
-		}
-
-		if (!purple_privacy_check(account, im->from)) {
-			purple_debug_info("yahoo", "Message from %s dropped.\n", im->from);
-			return;
-		}
-
-		/*
-		 * TODO: Is there anything else we should check when determining whether
-		 *       we should send an acknowledgement?
-		 */
-		if (im->id != NULL) {
-			/* Send acknowledgement.  If we don't do this then the official
-			 * Yahoo Messenger client for Windows will send us the same
-			 * message 7 seconds later as an offline message.  This is true
-			 * for at least version 9.0.0.2162 on Windows XP. */
-			struct yahoo_packet *pkt2;
-			pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK,
-					YAHOO_STATUS_AVAILABLE, pkt->id);
-			yahoo_packet_hash(pkt2, "ssisii",
-					1, im->active_id,  /* May not always be the connection's display name */
-					5, im->from,
-					302, 430,
-					430, im->id,
-					303, 430,
-					450, 0);
-			yahoo_packet_send_and_free(pkt2, yd);
-		}
-
-		m = yahoo_string_decode(gc, im->msg, im->utf8);
-		/* This may actually not be necessary, but it appears
-		 * that at least at one point some clients were sending
-		 * "\r\n" as line delimiters, so we want to avoid double
-		 * lines. */
-		m2 = purple_strreplace(m, "\r\n", "\n");
-		g_free(m);
-		m = m2;
-		purple_util_chrreplace(m, '\r', '\n');
-
-		if (im->msn) {
-			msn_from = g_strconcat("msn/", im->from, NULL);
-			from = msn_from;
-		} else {
-			from = im->from;
-		}
-
-		c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, from, account);
-
-		if (!strcmp(m, "<ding>")) {
-			char *username;
-
-			if (c == NULL) {
-				c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, from);
-			}
-			username = g_markup_escape_text(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, from, m2, 0, im->time);
-		g_free(m2);
-
-		/* laters : implement buddy icon for msn friends */
-		if (!im->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);
-}
-
-static void yahoo_process_sysmessage(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	GSList *l = pkt->hash;
-	char *prim, *me = NULL, *msg = NULL;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-
-		if (pair->key == 5)
-			me = pair->value;
-		if (pair->key == 14)
-			msg = pair->value;
-
-		l = l->next;
-	}
-
-	if (!msg || !g_utf8_validate(msg, -1, NULL))
-		return;
-
-	prim = g_strdup_printf(_("Yahoo! system message for %s:"),
-	                       me?me:purple_connection_get_display_name(gc));
-	purple_notify_info(NULL, NULL, prim, msg);
-	g_free(prim);
-}
-
-struct yahoo_add_request {
-	PurpleConnection *gc;
-	char *id;
-	char *who;
-	int protocol;
-};
-
-static void
-yahoo_buddy_add_authorize_cb(gpointer data)
-{
-	struct yahoo_add_request *add_req = data;
-	struct yahoo_packet *pkt;
-	struct yahoo_data *yd = add_req->gc->proto_data;
-	const char *who = add_req->who;
-
-	if (add_req->protocol == 2)
-		who += 4;
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "ssiii",
-					  1, add_req->id,
-					  5, who,
-					  241, add_req->protocol,
-					  13, 1,
-					  334, 0);
-	yahoo_packet_send_and_free(pkt, yd);
-
-	g_free(add_req->id);
-	g_free(add_req->who);
-	g_free(add_req);
-}
-
-static void
-yahoo_buddy_add_deny_cb(struct yahoo_add_request *add_req, const char *msg)
-{
-	struct yahoo_data *yd = add_req->gc->proto_data;
-	struct yahoo_packet *pkt;
-	char *encoded_msg = NULL;
-	PurpleAccount *account = purple_connection_get_account(add_req->gc);
-
-	if (msg && *msg)
-		encoded_msg = yahoo_string_encode(add_req->gc, msg, NULL);
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15,
-			YAHOO_STATUS_AVAILABLE, 0);
-
-	yahoo_packet_hash(pkt, "ssiiis",
-			1, purple_normalize(account, purple_account_get_username(account)),
-			5, add_req->who,
-			13, 2,
-			334, 0,
-			97, 1,
-			14, encoded_msg ? encoded_msg : "");
-
-	yahoo_packet_send_and_free(pkt, yd);
-
-	g_free(encoded_msg);
-
-	g_free(add_req->id);
-	g_free(add_req->who);
-	g_free(add_req);
-}
-
-static void
-yahoo_buddy_add_deny_noreason_cb(struct yahoo_add_request *add_req, const char*msg)
-{
-	yahoo_buddy_add_deny_cb(add_req, NULL);
-}
-
-static void
-yahoo_buddy_add_deny_reason_cb(gpointer data) {
-	struct yahoo_add_request *add_req = data;
-	purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
-			NULL, _("No reason given."), TRUE, FALSE, NULL,
-			_("OK"), G_CALLBACK(yahoo_buddy_add_deny_cb),
-			_("Cancel"), G_CALLBACK(yahoo_buddy_add_deny_noreason_cb),
-			purple_connection_get_account(add_req->gc), add_req->who, NULL,
-			add_req);
-}
-
-static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
-{
-	char *notify_msg;
-	struct yahoo_data *yd = gc->proto_data;
-
-	if (who == NULL)
-		return;
-
-	if (reason != NULL) {
-		char *msg2 = yahoo_string_decode(gc, reason, FALSE);
-		notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list for the following reason: %s."), who, msg2);
-		g_free(msg2);
-	} else
-		notify_msg = g_strdup_printf(_("%s has (retroactively) denied your request to add them to your list."), who);
-
-	purple_notify_info(gc, NULL, _("Add buddy rejected"), notify_msg);
-	g_free(notify_msg);
-
-	g_hash_table_remove(yd->friends, who);
-	purple_prpl_got_user_status(purple_connection_get_account(gc), who, "offline", NULL); /* FIXME: make this set not on list status instead */
-	/* TODO: Shouldn't we remove the buddy from our local list? */
-}
-
-static void yahoo_buddy_auth_req_15(PurpleConnection *gc, struct yahoo_packet *pkt) {
-	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) {
-		char *temp = NULL;
-		char *who = NULL;
-		int response = 0;
-
-		while (l) {
-			struct yahoo_pair *pair = l->data;
-
-			switch (pair->key) {
-			case 4:
-				temp = pair->value;
-				break;
-			case 13:
-				response = strtol(pair->value, NULL, 10);
-				break;
-			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 */
-			purple_debug_info("yahoo", "Received authorization decline from buddy '%s'.\n", who ? who : "(Unknown Buddy)");
-			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;
-
-		while (l) {
-			struct yahoo_pair *pair = l->data;
-
-			switch (pair->key) {
-			case 4:
-				temp = pair->value;
-				add_req->who = g_strdup(pair->value);
-				break;
-			case 5:
-				add_req->id = g_strdup(pair->value);
-				break;
-			case 14:
-				msg = pair->value;
-				break;
-			case 216:
-				firstname = pair->value;
-				break;
-			case 241:
-				add_req->protocol = strtol(pair->value, NULL, 10);
-				break;
-			case 254:
-				lastname = pair->value;
-				break;
-
-			}
-			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;
-
-			if (!purple_privacy_check(account, add_req->who))
-			{
-				purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
-						  add_req->who);
-				yahoo_buddy_add_deny_cb(add_req, NULL);
-				return;
-			}
-
-			if (msg)
-				dec_msg = yahoo_string_decode(gc, msg, FALSE);
-
-			if (firstname && lastname)
-				alias = g_strdup_printf("%s %s", firstname, lastname);
-			else if (firstname)
-				alias = g_strdup(firstname);
-			else if (lastname)
-				alias = g_strdup(lastname);
-
-			/* DONE! this is almost exactly the same as what MSN does,
-			 * this should probably be moved to the core.
-			 */
-			 purple_account_request_authorization(account, add_req->who, add_req->id,
-					alias, dec_msg,
-					purple_find_buddy(account, add_req->who) != NULL,
-					yahoo_buddy_add_authorize_cb,
-					yahoo_buddy_add_deny_reason_cb,
-					add_req);
-			g_free(alias);
-			g_free(dec_msg);
-		} else {
-			g_free(add_req->id);
-			g_free(add_req->who);
-			g_free(add_req);
-		}
-	} else {
-		purple_debug_error("yahoo", "Received authorization of unknown status (%d).\n", pkt->status);
-	}
-}
-
-/* I don't think this happens anymore in Version 15 */
-static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) {
-	PurpleAccount *account;
-	struct yahoo_add_request *add_req;
-	char *msg = NULL;
-	GSList *l = pkt->hash;
-
-	account = purple_connection_get_account(gc);
-
-	add_req = g_new0(struct yahoo_add_request, 1);
-	add_req->gc = gc;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-
-		switch (pair->key) {
-		case 1:
-			add_req->id = g_strdup(pair->value);
-			break;
-		case 3:
-			add_req->who = g_strdup(pair->value);
-			break;
-		case 15: /* time, for when they add us and we're offline */
-			break;
-		case 14:
-			msg = pair->value;
-			break;
-		}
-		l = l->next;
-	}
-
-	if (add_req->id && add_req->who) {
-		char *dec_msg = NULL;
-
-		if (!purple_privacy_check(account, add_req->who)) {
-			purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n",
-					  add_req->who);
-			yahoo_buddy_add_deny_cb(add_req, NULL);
-			return;
-		}
-
-		if (msg)
-			dec_msg = yahoo_string_decode(gc, msg, FALSE);
-
-		/* DONE! this is almost exactly the same as what MSN does,
-		 * this should probably be moved to the core.
-		 */
-		 purple_account_request_authorization(account, add_req->who, add_req->id,
-				NULL, dec_msg,
-				purple_find_buddy(account,add_req->who) != NULL,
-						yahoo_buddy_add_authorize_cb,
-						yahoo_buddy_add_deny_reason_cb, add_req);
-		g_free(dec_msg);
-	} else {
-		g_free(add_req->id);
-		g_free(add_req->who);
-		g_free(add_req);
-	}
-}
-
-/* I have no idea if this every gets called in version 15 */
-static void yahoo_buddy_denied_our_add_old(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	char *who = NULL;
-	char *msg = NULL;
-	GSList *l = pkt->hash;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-
-		switch (pair->key) {
-		case 3:
-			who = pair->value;
-			break;
-		case 14:
-			msg = pair->value;
-			break;
-		}
-		l = l->next;
-	}
-
-	yahoo_buddy_denied_our_add(gc, who, msg);
-}
-
-static void yahoo_process_contact(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	switch (pkt->status) {
-	case 1:
-		yahoo_process_status(gc, pkt);
-		return;
-	case 3:
-		yahoo_buddy_added_us(gc, pkt);
-		break;
-	case 7:
-		yahoo_buddy_denied_our_add_old(gc, pkt);
-		break;
-	default:
-		break;
-	}
-}
-
-#define OUT_CHARSET "utf-8"
-
-static char *yahoo_decode(const char *text)
-{
-	char *converted = NULL;
-	char *n, *new;
-	const char *end, *p;
-	int i, k;
-
-	n = new = g_malloc(strlen (text) + 1);
-	end = text + strlen(text);
-
-	for (p = text; p < end; p++, n++) {
-		if (*p == '\\') {
-			if (p[1] >= '0' && p[1] <= '7') {
-				p += 1;
-				for (i = 0, k = 0; k < 3; k += 1) {
-					char c = p[k];
-					if (c < '0' || c > '7') break;
-					i *= 8;
-					i += c - '0';
-				}
-				*n = i;
-				p += k - 1;
-			} else { /* bug 959248 */
-				/* If we see a \ not followed by an octal number,
-				 * it means that it is actually a \\ with one \
-				 * already eaten by some unknown function.
-				 * This is arguably broken.
-				 *
-				 * I think wing is wrong here, there is no function
-				 * called that I see that could have done it. I guess
-				 * it is just really sending single \'s. That's yahoo
-				 * for you.
-				 */
-				*n = *p;
-			}
-		}
-		else
-			*n = *p;
-	}
-
-	*n = '\0';
-
-	if (strstr(text, "\033$B"))
-		converted = g_convert(new, n - new, OUT_CHARSET, "iso-2022-jp", NULL, NULL, NULL);
-	if (!converted)
-		converted = g_convert(new, n - new, OUT_CHARSET, "iso-8859-1", NULL, NULL, NULL);
-	g_free(new);
-
-	return converted;
-}
-
-static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	PurpleAccount *account = purple_connection_get_account(gc);
-	struct yahoo_data *yd = gc->proto_data;
-	const char *who = NULL;
-	const char *email = NULL;
-	const char *subj = NULL;
-	const char *yahoo_mail_url = (yd->jp? YAHOOJP_MAIL_URL: YAHOO_MAIL_URL);
-	int count = 0;
-	GSList *l = pkt->hash;
-
-	if (!purple_account_get_check_mail(account))
-		return;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-		if (pair->key == 9)
-			count = strtol(pair->value, NULL, 10);
-		else if (pair->key == 43)
-			who = pair->value;
-		else if (pair->key == 42)
-			email = pair->value;
-		else if (pair->key == 18)
-			subj = pair->value;
-		l = l->next;
-	}
-
-	if (who && subj && email && *email) {
-		char *dec_who = yahoo_decode(who);
-		char *dec_subj = yahoo_decode(subj);
-		char *from = g_strdup_printf("%s (%s)", dec_who, email);
-
-		purple_notify_email(gc, dec_subj, from, purple_account_get_username(account),
-						  yahoo_mail_url, NULL, NULL);
-
-		g_free(dec_who);
-		g_free(dec_subj);
-		g_free(from);
-	} else if (count > 0) {
-		const char *tos[2] = { purple_account_get_username(account) };
-		const char *urls[2] = { yahoo_mail_url };
-
-		purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls,
-						   NULL, NULL);
-	}
-}
-
-/* We use this structure once while we authenticate */
-struct yahoo_auth_data
-{
-	PurpleConnection *gc;
-	char *seed;
-};
-
-/* This is the y64 alphabet... it's like base64, but has a . and a _ */
-static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
-
-/* This is taken from Sylpheed by Hiroyuki Yamamoto. We have our own tobase64 function
- * in util.c, but it is different from the one yahoo uses */
-static void to_y64(char *out, const unsigned char *in, gsize inlen)
-     /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
-{
-	for (; inlen >= 3; inlen -= 3)
-		{
-			*out++ = base64digits[in[0] >> 2];
-			*out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)];
-			*out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
-			*out++ = base64digits[in[2] & 0x3f];
-			in += 3;
-		}
-	if (inlen > 0)
-		{
-			unsigned char fragment;
-
-			*out++ = base64digits[in[0] >> 2];
-			fragment = (in[0] << 4) & 0x30;
-			if (inlen > 1)
-				fragment |= in[1] >> 4;
-			*out++ = base64digits[fragment];
-			*out++ = (inlen < 2) ? '-' : base64digits[(in[1] << 2) & 0x3c];
-			*out++ = '-';
-		}
-	*out = '\0';
-}
-
-static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
-{
-	struct yahoo_data *yd = gc->proto_data;
-	PurpleAccount *account = purple_connection_get_account(gc);
-	const char *name = purple_normalize(account, purple_account_get_username(account));
-	PurpleCipher *md5_cipher;
-	PurpleCipherContext *md5_ctx;
-	guchar md5_digest[16];
-	gchar base64_string[25];
-	struct yahoo_packet *pkt;
-
-	purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage3\n");
-
-	md5_cipher = purple_ciphers_find_cipher("md5");
-	md5_ctx = purple_cipher_context_new(md5_cipher, NULL);
-	purple_cipher_context_append(md5_ctx, (guchar *)crypt, strlen(crypt));
-	purple_cipher_context_digest(md5_ctx, sizeof(md5_digest), md5_digest, NULL);
-
-	to_y64(base64_string, md5_digest, 16);
-
-	purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status);
-	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, yd->session_id);
-	if(yd->jp) {
-		yahoo_packet_hash(pkt, "ssssssss",
-					1, name,
-					0, name,
-					277, yd->cookie_y,
-					278, yd->cookie_t,
-					307, base64_string,
-					2, name,
-					2, "1",
-					135, YAHOOJP_CLIENT_VERSION);
-	} else	{
-		yahoo_packet_hash(pkt, "sssssssss",
-					1, name,
-					0, name,
-					277, yd->cookie_y,
-					278, yd->cookie_t,
-					307, base64_string,
-					244, YAHOO_CLIENT_VERSION_ID,
-					2, name,
-					2, "1",
-					135, YAHOO_CLIENT_VERSION);
-	}
-	if (yd->picture_checksum)
-		yahoo_packet_hash_int(pkt, 192, yd->picture_checksum);
-	yahoo_packet_send_and_free(pkt, yd);
-
-	purple_cipher_context_destroy(md5_ctx);
-}
-
-static void yahoo_auth16_stage2(PurpleUtilFetchUrlData *unused, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
-{
-	struct yahoo_auth_data *auth_data = user_data;
-	PurpleConnection *gc = auth_data->gc;
-	struct yahoo_data *yd;
-	gboolean try_login_on_error = FALSE;
-
-	purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage2\n");
-
-	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
-		g_free(auth_data->seed);
-		g_free(auth_data);
-		g_return_if_reached();
-	}
-
-	yd = (struct yahoo_data *)gc->proto_data;
-
-	if (error_message != NULL) {
-		purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
-		g_free(auth_data->seed);
-		g_free(auth_data);
-		return;
-	}
-	else if (len > 0 && ret_data && *ret_data) {
-		gchar **split_data = g_strsplit(ret_data, "\r\n", -1);
-		int totalelements = 0;
-		int response_no = -1;
-		char *crumb = NULL;
-		char *crypt = NULL;
-
-#if GLIB_CHECK_VERSION(2,6,0)
-		totalelements = g_strv_length(split_data);
-#else
-		while (split_data[++totalelements] != NULL);
-#endif
-		if (totalelements >= 4) {
-			response_no = strtol(split_data[0], NULL, 10);
-			crumb = g_strdup(split_data[1] + strlen("crumb="));
-			yd->cookie_y = g_strdup(split_data[2] + strlen("Y="));
-			yd->cookie_t = g_strdup(split_data[3] + strlen("T="));
-		}
-
-		g_strfreev(split_data);
-
-		if(response_no != 0) {
-			/* Some error in the login process */
-			PurpleConnectionError error;
-			char *error_reason = NULL;
-
-			switch(response_no) {
-				case -1:
-					/* Some error in the received stream */
-					error_reason = g_strdup(_("Received invalid data"));
-					error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-					break;
-				case 100:
-					/* Unknown error */
-					error_reason = g_strdup(_("Unknown error"));
-					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
-					break;
-				default:
-					/* if we have everything we need, why not try to login irrespective of response */
-					if((crumb != NULL) && (yd->cookie_y != NULL) && (yd->cookie_t != NULL)) {
-						try_login_on_error = TRUE;
-						break;
-					}
-					error_reason = g_strdup(_("Unknown error"));
-					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
-					break;
-			}
-			if(error_reason) {
-				purple_debug_error("yahoo", "Authentication error: %s\n",
-				                   error_reason);
-				purple_connection_error_reason(gc, error, error_reason);
-				g_free(error_reason);
-				g_free(auth_data->seed);
-				g_free(auth_data);
-				return;
-			}
-		}
-
-		crypt = g_strconcat(crumb, auth_data->seed, NULL);
-		yahoo_auth16_stage3(gc, crypt);
-		g_free(crypt);
-		g_free(crumb);
-	}
-	g_free(auth_data->seed);
-	g_free(auth_data);
-}
-
-static void yahoo_auth16_stage1_cb(PurpleUtilFetchUrlData *unused, gpointer user_data, const gchar *ret_data, size_t len, const gchar *error_message)
-{
-	struct yahoo_auth_data *auth_data = user_data;
-	PurpleConnection *gc = auth_data->gc;
-
-	purple_debug_info("yahoo","Authentication: In yahoo_auth16_stage1_cb\n");
-
-	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
-		g_free(auth_data->seed);
-		g_free(auth_data);
-		g_return_if_reached();
-	}
-
-	if (error_message != NULL) {
-		purple_debug_error("yahoo", "Login Failed, unable to retrieve login url: %s\n", error_message);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
-		g_free(auth_data->seed);
-		g_free(auth_data);
-		return;
-	}
-	else if (len > 0 && ret_data && *ret_data) {
-		gchar **split_data = g_strsplit(ret_data, "\r\n", -1);
-		int totalelements = 0;
-		int response_no = -1;
-		char *token = NULL;
-
-#if GLIB_CHECK_VERSION(2,6,0)
-		totalelements = g_strv_length(split_data);
-#else
-		while (split_data[++totalelements] != NULL);
-#endif
-		if(totalelements == 1)
-			response_no = strtol(split_data[0], NULL, 10);
-		else if(totalelements >= 2) {
-			response_no = strtol(split_data[0], NULL, 10);
-			token = g_strdup(split_data[1] + strlen("ymsgr="));
-		}
-
-		g_strfreev(split_data);
-
-		if(response_no != 0) {
-			/* Some error in the login process */
-			PurpleConnectionError error;
-			char *error_reason;
-
-			switch(response_no) {
-				case -1:
-					/* Some error in the received stream */
-					error_reason = g_strdup(_("Received invalid data"));
-					error = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-					break;
-				case 1212:
-					/* Password incorrect */
-					/* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */
-					if (!purple_account_get_remember_password(purple_connection_get_account(gc)))
-						purple_account_set_password(purple_connection_get_account(gc), NULL);
-					error_reason = g_strdup(_("Incorrect password"));
-					error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-					break;
-				case 1213:
-					/* security lock from too many failed login attempts */
-					error_reason = g_strdup(_("Account locked: Too many failed login attempts.  Logging into the Yahoo! website may fix this."));
-					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
-					break;
-				case 1235:
-					/* the username does not exist */
-					error_reason = g_strdup(_("Username does not exist"));
-					error = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
-					break;
-				case 1214:
-				case 1236:
-					/* indicates a lock of some description */
-					error_reason = g_strdup(_("Account locked: Unknown reason.  Logging into the Yahoo! website may fix this."));
-					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
-					break;
-				case 100:
-					/* username or password missing */
-					error_reason = g_strdup(_("Username or password missing"));
-					error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-					break;
-				default:
-					/* Unknown error! */
-					error_reason = g_strdup(_("Unknown error"));
-					error = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
-					break;
-			}
-			purple_debug_error("yahoo", "Authentication error: %s\n",
-			                   error_reason);
-			purple_connection_error_reason(gc, error, error_reason);
-			g_free(error_reason);
-			g_free(auth_data->seed);
-			g_free(auth_data);
-		}
-		else {
-			/* OK to login, correct information provided */
-			PurpleUtilFetchUrlData *url_data = NULL;
-			char *url = NULL;
-			gboolean yahoojp = purple_account_get_bool(purple_connection_get_account(gc),
-				"yahoojp", 0);
-
-			url = g_strdup_printf(yahoojp ? YAHOOJP_LOGIN_URL : YAHOO_LOGIN_URL, token);
-			url_data = purple_util_fetch_url_request_len_with_account(
-					purple_connection_get_account(gc), url, TRUE,
-					YAHOO_CLIENT_USERAGENT, TRUE, NULL, FALSE, -1,
-					yahoo_auth16_stage2, auth_data);
-			g_free(url);
-			g_free(token);
-		}
-	}
-}
-
-static void yahoo_auth16_stage1(PurpleConnection *gc, const char *seed)
-{
-	PurpleUtilFetchUrlData *url_data = NULL;
-	struct yahoo_auth_data *auth_data = NULL;
-	char *url = NULL;
-	char *encoded_username;
-	char *encoded_password;
-	gboolean yahoojp;
-
-	purple_debug_info("yahoo", "Authentication: In yahoo_auth16_stage1\n");
-
-	if(!purple_ssl_is_supported()) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable"));
-		return;
-	}
-
-	yahoojp =  purple_account_get_bool(purple_connection_get_account(gc),
-			"yahoojp", 0);
-	auth_data = g_new0(struct yahoo_auth_data, 1);
-	auth_data->gc = gc;
-	auth_data->seed = g_strdup(seed);
-
-	encoded_username = g_strdup(purple_url_encode(purple_account_get_username(purple_connection_get_account(gc))));
-	encoded_password = g_strdup(purple_url_encode(purple_connection_get_password(gc)));
-	url = g_strdup_printf(yahoojp ? YAHOOJP_TOKEN_URL : YAHOO_TOKEN_URL,
-			encoded_username, encoded_password, purple_url_encode(seed));
-	g_free(encoded_password);
-	g_free(encoded_username);
-
-	url_data = purple_util_fetch_url_request_len_with_account(
-			purple_connection_get_account(gc), url, TRUE,
-			YAHOO_CLIENT_USERAGENT, TRUE, NULL, FALSE, -1,
-			yahoo_auth16_stage1_cb, auth_data);
-
-	g_free(url);
-}
-
-static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	char *seed = NULL;
-	char *sn   = NULL;
-	GSList *l = pkt->hash;
-	int m = 0;
-	gchar *buf;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-		if (pair->key == 94)
-			seed = pair->value;
-		if (pair->key == 1)
-			sn = pair->value;
-		if (pair->key == 13)
-			m = atoi(pair->value);
-		l = l->next;
-	}
-
-	if (seed) {
-		switch (m) {
-		case 0:
-			/* used to be for really old auth routine, dont support now */
-		case 1:
-		case 2: /* Yahoo ver 16 authentication */
-			yahoo_auth16_stage1(gc, seed);
-			break;
-		default:
-			{
-				GHashTable *ui_info = purple_core_get_ui_info();
-
-				buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized "
-							"authentication method.  You will probably not be able "
-							"to successfully sign on to Yahoo.  Check %s for updates."),
-							((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE));
-				purple_notify_error(gc, "", _("Failed Yahoo! Authentication"),
-							buf);
-				g_free(buf);
-				yahoo_auth16_stage1(gc, seed); /* Can't hurt to try it anyway. */
-				break;
-			}
-		}
-	}
-}
-
-static void ignore_buddy(PurpleBuddy *buddy) {
-	PurpleGroup *group;
-	PurpleAccount *account;
-	gchar *name;
-
-	if (!buddy)
-		return;
-
-	group = purple_buddy_get_group(buddy);
-	name = g_strdup(purple_buddy_get_name(buddy));
-	account = purple_buddy_get_account(buddy);
-
-	purple_debug_info("yahoo", "blist: Removing '%s' from buddy list.\n", name);
-	purple_account_remove_buddy(account, buddy, group);
-	purple_blist_remove_buddy(buddy);
-
-	serv_add_deny(purple_account_get_connection(account), name);
-
-	g_free(name);
-}
-
-static void keep_buddy(PurpleBuddy *b)
-{
-	purple_privacy_deny_remove(purple_buddy_get_account(b),
-			purple_buddy_get_name(b), 1);
-}
-
-static void yahoo_process_ignore(PurpleConnection *gc, struct yahoo_packet *pkt) {
-	PurpleBuddy *b;
-	GSList *l;
-	gchar *who = NULL;
-	gchar *me = NULL;
-	gchar buf[BUF_LONG];
-	gboolean ignore = TRUE;
-	gint status = 0;
-
-	for (l = pkt->hash; l; l = l->next) {
-		struct yahoo_pair *pair = l->data;
-		switch (pair->key) {
-		case 0:
-			who = pair->value;
-			break;
-		case 1:
-			me = pair->value;
-			break;
-		case 13:
-			/* 1 == ignore, 2 == unignore */
-			ignore = (strtol(pair->value, NULL, 10) == 1);
-			break;
-		case 66:
-			status = strtol(pair->value, NULL, 10);
-			break;
-		default:
-			break;
-		}
-	}
-
-	/*
-	 * status
-	 * 0  - ok
-	 * 2  - already in ignore list, could not add
-	 * 3  - not in ignore list, could not delete
-	 * 12 - is a buddy, could not add (and possibly also a not-in-ignore list condition?)
-	 */
-	switch (status) {
-		case 12:
-			purple_debug_info("yahoo", "Server reported \"is a buddy\" for %s while %s",
-							  who, (ignore ? "ignoring" : "unignoring"));
-
-			if (ignore) {
-				b = purple_find_buddy(gc->account, who);
-				g_snprintf(buf, sizeof(buf), _("You have tried to ignore %s, but the "
-											   "user is on your buddy list.  Clicking \"Yes\" "
-											   "will remove and ignore the buddy."), who);
-				purple_request_yes_no(gc, NULL, _("Ignore buddy?"), buf, 0,
-									  gc->account, who, NULL,
-									  b,
-									  G_CALLBACK(ignore_buddy),
-									  G_CALLBACK(keep_buddy));
-				break;
-			}
-		case 2:
-			purple_debug_info("yahoo", "Server reported that %s is already in the ignore list.",
-							  who);
-			break;
-		case 3:
-			purple_debug_info("yahoo", "Server reported that %s is not in the ignore list; could not delete",
-							  who);
-		case 0:
-		default:
-			break;
-	}
-}
-
-static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-#ifdef TRY_WEBMESSENGER_LOGIN
-	struct yahoo_data *yd = gc->proto_data;
-#endif /* TRY_WEBMESSENGER_LOGIN */
-	GSList *l = pkt->hash;
-	int err = 0;
-	char *msg;
-	char *url = NULL;
-	char *fullmsg;
-	PurpleAccount *account = gc->account;
-	PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-
-		if (pair->key == 66)
-			err = strtol(pair->value, NULL, 10);
-		else if (pair->key == 20)
-			url = pair->value;
-
-		l = l->next;
-	}
-
-	switch (err) {
-	case 0:
-		msg = g_strdup(_("Unknown error"));
-		reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-		break;
-	case 3:
-		msg = g_strdup(_("Username does not exist"));
-		reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
-		break;
-	case 13:
-#ifdef TRY_WEBMESSENGER_LOGIN
-		if (!yd->wm) {
-			PurpleUtilFetchUrlData *url_data;
-			yd->wm = TRUE;
-			if (yd->fd >= 0)
-				close(yd->fd);
-			if (gc->inpa)
-				purple_input_remove(gc->inpa);
-			url_data = purple_util_fetch_url(WEBMESSENGER_URL, TRUE,
-					"Purple/" VERSION, FALSE, yahoo_login_page_cb, gc);
-			if (url_data != NULL)
-				yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
-			return;
-		}
-#endif /* TRY_WEBMESSENGER_LOGIN */
-		if (!purple_account_get_remember_password(account))
-			purple_account_set_password(account, NULL);
-
-		msg = g_strdup(_("Incorrect password"));
-		reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-		break;
-	case 14:
-		msg = g_strdup(_("Your account is locked, please log in to the Yahoo! website."));
-		reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-		break;
-	default:
-		msg = g_strdup_printf(_("Unknown error number %d. Logging into the Yahoo! website may fix this."), err);
-	}
-
-	if (url)
-		fullmsg = g_strdup_printf("%s\n%s", msg, url);
-	else
-		fullmsg = g_strdup(msg);
-
-	purple_connection_error_reason(gc, reason, fullmsg);
-	g_free(msg);
-	g_free(fullmsg);
-}
-
-static void yahoo_process_addbuddy(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	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;
-
-		switch (pair->key) {
-		case 66:
-			err = strtol(pair->value, NULL, 10);
-			break;
-		case 7:
-			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 (!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);
-		g_free(who);
-		return;
-	}
-
-	decoded_group = yahoo_string_decode(gc, group, FALSE);
-	buf = g_strdup_printf(_("Unable to add buddy %s to group %s to the server list on account %s."),
-				who, decoded_group, purple_connection_get_display_name(gc));
-	if (!purple_conv_present_error(who, purple_connection_get_account(gc), buf))
-		purple_notify_error(gc, NULL, _("Unable to 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 == NULL)
-			return;
-
-		g_memmove(buf, start, len - (start - buf));
-		len -= start - buf;
-	}
-
-	pos += 4;	/* YMSG */
-	pos += 2;
-	pos += 2;
-
-	pktlen = yahoo_get16(buf + pos); pos += 2;
-	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_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 */
-	if (yd->yahoo_p2p_server_timeout_handle) {
-		purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle);
-		yd->yahoo_p2p_server_timeout_handle = 0;
-	}
-
-	/* remove watcher and close p2p server */
-	if (yd->yahoo_p2p_server_watcher) {
-		purple_input_remove(yd->yahoo_p2p_server_watcher);
-		yd->yahoo_p2p_server_watcher = 0;
-	}
-	if (yd->yahoo_local_p2p_server_fd >= 0) {
-		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;
-	p2p_data->source = -1;
-
-	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)
-{
-	GSList *l = pkt->hash;
-	char *who = NULL;
-	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;
-
-		switch (pair->key) {
-		case 5:
-			/* our identity */
-			break;
-		case 4:
-			who = pair->value;
-			break;
-		case 1:
-			/* who again, the master identity this time? */
-			break;
-		case 12:
-			base64 = pair->value;
-			/* 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	packet count ??
-			yahoo: Key: 49          Value: PEERTOPEER
-			yahoo: Key: 140         Value: 1
-		*/
-
-		}
-
-		l = l->next;
-	}
-
-	if (base64) {
-		guint32 ip;
-		YahooFriend *f;
-		char *host_ip;
-		struct yahoo_p2p_data *p2p_data;
-
-		decoded = purple_base64_decode(base64, &len);
-		if (len) {
-			char *tmp = purple_str_binary_to_ascii(decoded, len);
-			purple_debug_info("yahoo", "Got P2P service packet (from server): who = %s, ip = %s\n", who, tmp);
-			g_free(tmp);
-		}
-
-		ip = strtol((gchar *)decoded, NULL, 10);
-		g_free(decoded);
-		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, 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 = g_new0(struct yahoo_p2p_data, 1);
-		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;
-		p2p_data->source = -1;
-
-		/* connect to host */
-		if((purple_proxy_connect(gc, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) {
-			purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip);
-			g_free(p2p_data->host_ip);
-			g_free(p2p_data->host_username);
-			g_free(p2p_data);
-		}
-	}
-}
-
-static void yahoo_process_audible(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	PurpleAccount *account;
-	char *who = NULL, *msg = NULL, *id = NULL;
-	GSList *l = pkt->hash;
-
-	account = purple_connection_get_account(gc);
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-
-		switch (pair->key) {
-		case 4:
-			who = pair->value;
-			break;
-		case 5:
-			/* us */
-			break;
-		case 230:
-			/* the audible, in foo.locale.bar.baz format
-			   eg: base.tw.smiley.smiley43 */
-			id = pair->value;
-			break;
-		case 231:
-			/* the text of the audible */
-			msg = pair->value;
-			break;
-		case 232:
-			/* weird number (md5 hash?), like 8ebab9094156135f5dcbaccbeee662a5c5fd1420 */
-			break;
-		}
-
-		l = l->next;
-	}
-
-	if (!msg)
-		msg = id;
-	if (!who || !msg)
-		return;
-	if (!g_utf8_validate(msg, -1, NULL)) {
-		purple_debug_misc("yahoo", "Warning, nonutf8 audible, ignoring!\n");
-		return;
-	}
-	if (!purple_privacy_check(account, who)) {
-		purple_debug_misc("yahoo", "Audible message from %s for %s dropped!\n",
-				purple_account_get_username(account), who);
-		return;
-	}
-	if (id) {
-		/* "http://us.dl1.yimg.com/download.yahoo.com/dl/aud/"+locale+"/"+id+".swf" */
-		char **audible_locale = g_strsplit(id, ".", 0);
-		char *buf = g_strdup_printf(_("[ Audible %s/%s/%s.swf ] %s"), YAHOO_AUDIBLE_URL, audible_locale[1], id, msg);
-		g_strfreev(audible_locale);
-
-		serv_got_im(gc, who, buf, 0, time(NULL));
-		g_free(buf);
-	} else
-		serv_got_im(gc, who, msg, 0, time(NULL));
-}
-
-static void yahoo_packet_process(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	switch (pkt->service) {
-	case YAHOO_SERVICE_LOGON:
-	case YAHOO_SERVICE_LOGOFF:
-	case YAHOO_SERVICE_ISAWAY:
-	case YAHOO_SERVICE_ISBACK:
-	case YAHOO_SERVICE_GAMELOGON:
-	case YAHOO_SERVICE_GAMELOGOFF:
-	case YAHOO_SERVICE_CHATLOGON:
-	case YAHOO_SERVICE_CHATLOGOFF:
-	case YAHOO_SERVICE_Y6_STATUS_UPDATE:
-	case YAHOO_SERVICE_STATUS_15:
-		yahoo_process_status(gc, pkt);
-		break;
-	case YAHOO_SERVICE_NOTIFY:
-		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_PKT_TYPE_SERVER);
-		break;
-	case YAHOO_SERVICE_SYSMESSAGE:
-		yahoo_process_sysmessage(gc, pkt);
-			break;
-	case YAHOO_SERVICE_NEWMAIL:
-		yahoo_process_mail(gc, pkt);
-		break;
-	case YAHOO_SERVICE_NEWCONTACT:
-		yahoo_process_contact(gc, pkt);
-		break;
-	case YAHOO_SERVICE_AUTHRESP:
-		yahoo_process_authresp(gc, pkt);
-		break;
-	case YAHOO_SERVICE_LIST:
-		yahoo_process_list(gc, pkt);
-		break;
-	case YAHOO_SERVICE_LIST_15:
-		yahoo_process_list_15(gc, pkt);
-		break;
-	case YAHOO_SERVICE_AUTH:
-		yahoo_process_auth(gc, pkt);
-		break;
-	case YAHOO_SERVICE_AUTH_REQ_15:
-		yahoo_buddy_auth_req_15(gc, pkt);
-		break;
-	case YAHOO_SERVICE_ADDBUDDY:
-		yahoo_process_addbuddy(gc, pkt);
-		break;
-	case YAHOO_SERVICE_IGNORECONTACT:
-		yahoo_process_ignore(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CONFINVITE:
-	case YAHOO_SERVICE_CONFADDINVITE:
-		yahoo_process_conference_invite(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CONFDECLINE:
-		yahoo_process_conference_decline(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CONFLOGON:
-		yahoo_process_conference_logon(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CONFLOGOFF:
-		yahoo_process_conference_logoff(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CONFMSG:
-		yahoo_process_conference_message(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CHATONLINE:
-		yahoo_process_chat_online(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CHATLOGOUT:
-		yahoo_process_chat_logout(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CHATGOTO:
-		yahoo_process_chat_goto(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CHATJOIN:
-		yahoo_process_chat_join(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CHATLEAVE: /* XXX is this right? */
-	case YAHOO_SERVICE_CHATEXIT:
-		yahoo_process_chat_exit(gc, pkt);
-		break;
-	case YAHOO_SERVICE_CHATINVITE: /* XXX never seen this one, might not do it right */
-	case YAHOO_SERVICE_CHATADDINVITE:
-		yahoo_process_chat_addinvite(gc, pkt);
-		break;
-	case YAHOO_SERVICE_COMMENT:
-		yahoo_process_chat_message(gc, pkt);
-		break;
-	case YAHOO_SERVICE_PRESENCE_PERM:
-	case YAHOO_SERVICE_PRESENCE_SESSION:
-		yahoo_process_presence(gc, pkt);
-		break;
-	case YAHOO_SERVICE_P2PFILEXFER:
-		/* This case had no break and continued; thus keeping it this way.*/
-		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;
-	case YAHOO_SERVICE_PEERTOPEER:
-		yahoo_process_p2p(gc, pkt);
-		break;
-	case YAHOO_SERVICE_PICTURE:
-		yahoo_process_picture(gc, pkt);
-		break;
-	case YAHOO_SERVICE_PICTURE_CHECKSUM:
-		yahoo_process_picture_checksum(gc, pkt);
-		break;
-	case YAHOO_SERVICE_PICTURE_UPLOAD:
-		yahoo_process_picture_upload(gc, pkt);
-		break;
-	case YAHOO_SERVICE_PICTURE_UPDATE:
-	case YAHOO_SERVICE_AVATAR_UPDATE:
-		yahoo_process_avatar_update(gc, pkt);
-		break;
-	case YAHOO_SERVICE_AUDIBLE:
-		yahoo_process_audible(gc, pkt);
-		break;
-	case YAHOO_SERVICE_FILETRANS_15:
-		yahoo_process_filetrans_15(gc, pkt);
-		break;
-	case YAHOO_SERVICE_FILETRANS_INFO_15:
-		yahoo_process_filetrans_info_15(gc, pkt);
-		break;
-	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_error("yahoo", "Unhandled service 0x%02x\n", pkt->service);
-		break;
-	}
-}
-
-static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond)
-{
-	PurpleConnection *gc = data;
-	struct yahoo_data *yd = gc->proto_data;
-	char buf[1024];
-	int len;
-
-	len = read(yd->fd, buf, sizeof(buf));
-
-	if (len < 0) {
-		gchar *tmp;
-
-		if (errno == EAGAIN)
-			/* No worries */
-			return;
-
-		tmp = g_strdup_printf(_("Lost connection with server: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
-		g_free(tmp);
-		return;
-	} else if (len == 0) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Server closed the connection"));
-		return;
-	}
-	gc->last_received = time(NULL);
-	yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen);
-	memcpy(yd->rxqueue + yd->rxlen, buf, len);
-	yd->rxlen += len;
-
-	while (1) {
-		struct yahoo_packet *pkt;
-		int pos = 0;
-		int pktlen;
-
-		if (yd->rxlen < YAHOO_PACKET_HDRLEN)
-			return;
-
-		if (strncmp((char *)yd->rxqueue, "YMSG", MIN(4, yd->rxlen)) != 0) {
-			/* HEY! This isn't even a YMSG packet. What
-			 * are you trying to pull? */
-			guchar *start;
-
-			purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n");
-
-			start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1);
-			if (start) {
-				g_memmove(yd->rxqueue, start, yd->rxlen - (start - yd->rxqueue));
-				yd->rxlen -= start - yd->rxqueue;
-				continue;
-			} else {
-				g_free(yd->rxqueue);
-				yd->rxqueue = NULL;
-				yd->rxlen = 0;
-				return;
-			}
-		}
-
-		pos += 4; /* YMSG */
-		pos += 2;
-		pos += 2;
-
-		pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2;
-		purple_debug_misc("yahoo", "%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen);
-
-		if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen))
-			return;
-
-		yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
-
-		pkt = yahoo_packet_new(0, 0, 0);
-
-		pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2;
-		pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4;
-		purple_debug_misc("yahoo", "Yahoo Service: 0x%02x Status: %d\n",
-				   pkt->service, pkt->status);
-		pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4;
-
-		yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen);
-
-		yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
-		if (yd->rxlen) {
-			guchar *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen);
-			g_free(yd->rxqueue);
-			yd->rxqueue = tmp;
-		} else {
-			g_free(yd->rxqueue);
-			yd->rxqueue = NULL;
-		}
-
-		yahoo_packet_process(gc, pkt);
-
-		yahoo_packet_free(pkt);
-	}
-}
-
-static void yahoo_got_connected(gpointer data, gint source, const gchar *error_message)
-{
-	PurpleConnection *gc = data;
-	struct yahoo_data *yd;
-	struct yahoo_packet *pkt;
-
-	if (source < 0) {
-		gchar *tmp;
-		tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
-		g_free(tmp);
-		return;
-	}
-
-	yd = gc->proto_data;
-	yd->fd = source;
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, 0);
-
-	yahoo_packet_hash_str(pkt, 1, purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))));
-	yahoo_packet_send_and_free(pkt, yd);
-
-	gc->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
-}
-
-#ifdef TRY_WEBMESSENGER_LOGIN
-static void yahoo_got_web_connected(gpointer data, gint source, const gchar *error_message)
-{
-	PurpleConnection *gc = data;
-	struct yahoo_data *yd;
-	struct yahoo_packet *pkt;
-
-	if (source < 0) {
-		gchar *tmp;
-		tmp = g_strdup_printf(_("Unable to connect: %s"), error_message);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
-		g_free(tmp);
-		return;
-	}
-
-	yd = gc->proto_data;
-	yd->fd = source;
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, 0);
-
-	yahoo_packet_hash(pkt, "sss", 0,
-	                  purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))),
-	                  1, purple_normalize(gc->account, purple_account_get_username(purple_connection_get_account(gc))),
-	                  6, yd->auth);
-	yahoo_packet_send_and_free(pkt, yd);
-
-	g_free(yd->auth);
-	gc->inpa = purple_input_add(yd->fd, PURPLE_INPUT_READ, yahoo_pending, gc);
-}
-
-static void yahoo_web_pending(gpointer data, gint source, PurpleInputCondition cond)
-{
-	PurpleConnection *gc = data;
-	PurpleAccount *account = purple_connection_get_account(gc);
-	struct yahoo_data *yd = gc->proto_data;
-	char bufread[2048], *i = bufread, *buf = bufread;
-	int len;
-	GString *s;
-
-	len = read(source, bufread, sizeof(bufread) - 1);
-
-	if (len < 0) {
-		gchar *tmp;
-
-		if (errno == EAGAIN)
-			/* No worries */
-			return;
-
-		tmp = g_strdup_printf(_("Lost connection with server: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
-		g_free(tmp);
-		return;
-	} else if (len == 0) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Server closed the connection"));
-		return;
-	}
-
-	if (yd->rxlen > 0 || !g_strstr_len(buf, len, "\r\n\r\n")) {
-		yd->rxqueue = g_realloc(yd->rxqueue, yd->rxlen + len + 1);
-		memcpy(yd->rxqueue + yd->rxlen, buf, len);
-		yd->rxlen += len;
-		i = buf = (char *)yd->rxqueue;
-		len = yd->rxlen;
-	}
-	buf[len] = '\0';
-
-	if ((strncmp(buf, "HTTP/1.0 302", strlen("HTTP/1.0 302")) &&
-			  strncmp(buf, "HTTP/1.1 302", strlen("HTTP/1.1 302")))) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Received unexpected HTTP response from server"));
-		purple_debug_misc("yahoo", "Unexpected HTTP response: %s\n", buf);
-		return;
-	}
-
-	s = g_string_sized_new(len);
-
-	while ((i = strstr(i, "Set-Cookie: "))) {
-
-		i += strlen("Set-Cookie: ");
-		for (;*i != ';' && *i != '\0'; i++)
-			g_string_append_c(s, *i);
-
-		g_string_append(s, "; ");
-		/* Should these cookies be included too when trying for xfer?
-		 * It seems to work without these
-		 */
-	}
-
-	yd->auth = g_string_free(s, FALSE);
-	purple_input_remove(gc->inpa);
-	close(source);
-	g_free(yd->rxqueue);
-	yd->rxqueue = NULL;
-	yd->rxlen = 0;
-	/* Now we have our cookies to login with.  I'll go get the milk. */
-	if (purple_proxy_connect(gc, account, "wcs2.msg.dcn.yahoo.com",
-	                         purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
-	                         yahoo_got_web_connected, gc) == NULL) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-		                               _("Unable to connect"));
-		return;
-	}
-}
-
-static void yahoo_got_cookies_send_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	PurpleConnection *gc;
-	struct yahoo_data *yd;
-	int written, remaining;
-
-	gc = data;
-	yd = gc->proto_data;
-
-	remaining = strlen(yd->auth) - yd->auth_written;
-	written = write(source, yd->auth + yd->auth_written, remaining);
-
-	if (written < 0 && errno == EAGAIN)
-		written = 0;
-	else if (written <= 0) {
-		gchar *tmp;
-		g_free(yd->auth);
-		yd->auth = NULL;
-		if (gc->inpa)
-			purple_input_remove(gc->inpa);
-		gc->inpa = 0;
-		tmp = g_strdup_printf(_("Lost connection with %s: %s"),
-				"login.yahoo.com:80", g_strerror(errno));
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
-		g_free(tmp);
-		return;
-	}
-
-	if (written < remaining) {
-		yd->auth_written += written;
-		return;
-	}
-
-	g_free(yd->auth);
-	yd->auth = NULL;
-	yd->auth_written = 0;
-	purple_input_remove(gc->inpa);
-	gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, yahoo_web_pending, gc);
-}
-
-static void yahoo_got_cookies(gpointer data, gint source, const gchar *error_message)
-{
-	PurpleConnection *gc = data;
-
-	if (source < 0) {
-		gchar *tmp;
-		tmp = g_strdup_printf(_("Unable to establish a connection with %s: %s"),
-				"login.yahoo.com:80", error_message);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
-		g_free(tmp);
-		return;
-	}
-
-	if (gc->inpa == 0)
-	{
-		gc->inpa = purple_input_add(source, PURPLE_INPUT_WRITE,
-			yahoo_got_cookies_send_cb, gc);
-		yahoo_got_cookies_send_cb(gc, source, PURPLE_INPUT_WRITE);
-	}
-}
-
-static void yahoo_login_page_hash_iter(const char *key, const char *val, GString *url)
-{
-	if (!strcmp(key, "passwd") || !strcmp(key, "login"))
-		return;
-	g_string_append_c(url, '&');
-	g_string_append(url, key);
-	g_string_append_c(url, '=');
-	if (!strcmp(key, ".save") || !strcmp(key, ".js"))
-		g_string_append_c(url, '1');
-	else if (!strcmp(key, ".challenge"))
-		g_string_append(url, val);
-	else
-		g_string_append(url, purple_url_encode(val));
-}
-
-static GHashTable *yahoo_login_page_hash(const char *buf, size_t len)
-{
-	GHashTable *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	const char *c = buf;
-	char *d;
-	char name[64], value[64];
-	int count;
-	int input_len = strlen("<input ");
-	int name_len = strlen("name=\"");
-	int value_len = strlen("value=\"");
-	while ((len > ((c - buf) + input_len))
-			&& (c = strstr(c, "<input "))) {
-		if (!(c = g_strstr_len(c, len - (c - buf), "name=\"")))
-			continue;
-		c += name_len;
-		count = sizeof(name)-1;
-		for (d = name; (len > ((c - buf) + 1)) && *c!='"'
-				&& count; c++, d++, count--)
-			*d = *c;
-		*d = '\0';
-		count = sizeof(value)-1;
-		if (!(d = g_strstr_len(c, len - (c - buf), "value=\"")))
-			continue;
-		d += value_len;
-		if (strchr(c, '>') < d)
-			break;
-		for (c = d, d = value; (len > ((c - buf) + 1))
-				&& *c!='"' && count; c++, d++, count--)
-			*d = *c;
-		*d = '\0';
-		g_hash_table_insert(hash, g_strdup(name), g_strdup(value));
-	}
-	return hash;
-}
-
-static void
-yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
-		const gchar *url_text, size_t len, const gchar *error_message)
-{
-	PurpleConnection *gc = (PurpleConnection *)user_data;
-	PurpleAccount *account = purple_connection_get_account(gc);
-	struct yahoo_data *yd = gc->proto_data;
-	const char *sn = purple_account_get_username(account);
-	const char *pass = purple_connection_get_password(gc);
-	GHashTable *hash = yahoo_login_page_hash(url_text, len);
-	GString *url = g_string_new("GET http://login.yahoo.com/config/login?login=");
-	char md5[33], *hashp = md5, *chal;
-	int i;
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
-	guchar digest[16];
-
-	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
-
-	if (error_message != NULL)
-	{
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-		                               error_message);
-		return;
-	}
-
-	url = g_string_append(url, sn);
-	url = g_string_append(url, "&passwd=");
-
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-
-	purple_cipher_context_append(context, (const guchar *)pass, strlen(pass));
-	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
-	for (i = 0; i < 16; ++i) {
-		g_snprintf(hashp, 3, "%02x", digest[i]);
-		hashp += 2;
-	}
-
-	chal = g_strconcat(md5, g_hash_table_lookup(hash, ".challenge"), NULL);
-	purple_cipher_context_reset(context, NULL);
-	purple_cipher_context_append(context, (const guchar *)chal, strlen(chal));
-	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
-	hashp = md5;
-	for (i = 0; i < 16; ++i) {
-		g_snprintf(hashp, 3, "%02x", digest[i]);
-		hashp += 2;
-	}
-	/*
-	 * I dunno why this is here and commented out.. but in case it's needed
-	 * I updated it..
-
-	purple_cipher_context_reset(context, NULL);
-	purple_cipher_context_append(context, md5, strlen(md5));
-	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
-	hashp = md5;
-	for (i = 0; i < 16; ++i) {
-		g_snprintf(hashp, 3, "%02x", digest[i]);
-		hashp += 2;
-	}
-	*/
-	g_free(chal);
-
-	url = g_string_append(url, md5);
-	g_hash_table_foreach(hash, (GHFunc)yahoo_login_page_hash_iter, url);
-
-	url = g_string_append(url, "&.hash=1&.md5=1 HTTP/1.1\r\n"
-			      "Host: login.yahoo.com\r\n\r\n");
-	g_hash_table_destroy(hash);
-	yd->auth = g_string_free(url, FALSE);
-	if (purple_proxy_connect(gc, account, "login.yahoo.com", 80, yahoo_got_cookies, gc) == NULL) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-		                               _("Unable to connect"));
-		return;
-	}
-
-	purple_cipher_context_destroy(context);
-}
-#endif /* TRY_WEBMESSENGER_LOGIN */
-
-static void yahoo_server_check(PurpleAccount *account)
-{
-	const char *server;
-
-	server = purple_account_get_string(account, "server", YAHOO_PAGER_HOST);
-
-	if (*server == '\0' || g_str_equal(server, "scs.yahoo.com") ||
-			g_str_equal(server, "scs.msg.yahoo.com"))
-		purple_account_set_string(account, "server", YAHOO_PAGER_HOST);
-}
-
-static void yahoo_picture_check(PurpleAccount *account)
-{
-	PurpleConnection *gc = purple_account_get_connection(account);
-	PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
-
-	yahoo_set_buddy_icon(gc, img);
-	purple_imgstore_unref(img);
-}
-
-static int get_yahoo_status_from_purple_status(PurpleStatus *status)
-{
-	PurplePresence *presence;
-	const char *status_id;
-	const char *msg;
-
-	presence = purple_status_get_presence(status);
-	status_id = purple_status_get_id(status);
-	msg = purple_status_get_attr_string(status, "message");
-
-	if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) {
-		if ((msg != NULL) && (*msg != '\0'))
-			return YAHOO_STATUS_CUSTOM;
-		else
-			return YAHOO_STATUS_AVAILABLE;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BRB)) {
-		return YAHOO_STATUS_BRB;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BUSY)) {
-		return YAHOO_STATUS_BUSY;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATHOME)) {
-		return YAHOO_STATUS_NOTATHOME;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTATDESK)) {
-		return YAHOO_STATUS_NOTATDESK;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_NOTINOFFICE)) {
-		return YAHOO_STATUS_NOTINOFFICE;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONPHONE)) {
-		return YAHOO_STATUS_ONPHONE;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_ONVACATION)) {
-		return YAHOO_STATUS_ONVACATION;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_OUTTOLUNCH)) {
-		return YAHOO_STATUS_OUTTOLUNCH;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_STEPPEDOUT)) {
-		return YAHOO_STATUS_STEPPEDOUT;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_INVISIBLE)) {
-		return YAHOO_STATUS_INVISIBLE;
-	} else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AWAY)) {
-		return YAHOO_STATUS_CUSTOM;
-	} else if (purple_presence_is_idle(presence)) {
-		return YAHOO_STATUS_IDLE;
-	} else {
-		purple_debug_error("yahoo", "Unexpected PurpleStatus!\n");
-		return YAHOO_STATUS_AVAILABLE;
-	}
-}
-
-static void yahoo_login(PurpleAccount *account) {
-	PurpleConnection *gc = purple_account_get_connection(account);
-	struct yahoo_data *yd = gc->proto_data = g_new0(struct yahoo_data, 1);
-	PurpleStatus *status = purple_account_get_active_status(account);
-	gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC;
-
-	purple_connection_update_progress(gc, _("Connecting"), 1, 2);
-
-	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? */
-	yd->txbuf = purple_circ_buffer_new(0);
-	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);
-
-	yd->current_status = get_yahoo_status_from_purple_status(status);
-
-	yahoo_server_check(account);
-	yahoo_picture_check(account);
-
-	if (purple_account_get_bool(account, "yahoojp", FALSE)) {
-		yd->jp = TRUE;
-		if (purple_proxy_connect(gc, account,
-		                       purple_account_get_string(account, "serverjp",  YAHOOJP_PAGER_HOST),
-		                       purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
-		                       yahoo_got_connected, gc) == NULL)
-		{
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			                               _("Unable to connect"));
-			return;
-		}
-	} else {
-		yd->jp = FALSE;
-		if (purple_proxy_connect(gc, account,
-		                       purple_account_get_string(account, "server",  YAHOO_PAGER_HOST),
-		                       purple_account_get_int(account, "port", YAHOO_PAGER_PORT),
-		                       yahoo_got_connected, gc) == NULL)
-		{
-			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			                               _("Unable to connect"));
-			return;
-		}
-	}
-}
-
-static void yahoo_close(PurpleConnection *gc) {
-	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
-	GSList *l;
-
-	if (gc->inpa)
-		purple_input_remove(gc->inpa);
-
-	while (yd->url_datas) {
-		purple_util_fetch_url_cancel(yd->url_datas->data);
-		yd->url_datas = g_slist_delete_link(yd->url_datas, yd->url_datas);
-	}
-
-	for (l = yd->confs; l; l = l->next) {
-		PurpleConversation *conv = l->data;
-
-		yahoo_conf_leave(yd, purple_conversation_get_name(conv),
-		                 purple_connection_get_display_name(gc),
-				 purple_conv_chat_get_users(PURPLE_CONV_CHAT(conv)));
-	}
-	g_slist_free(yd->confs);
-
-	for (l = yd->cookies; l; l = l->next) {
-		g_free(l->data);
-		l->data=NULL;
-	}
-	g_slist_free(yd->cookies);
-
-	yd->chat_online = FALSE;
-	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);
-		yd->yahoo_p2p_server_timeout_handle = 0;
-	}
-
-	/* close p2p server if it is waiting for a peer to connect */
-	if (yd->yahoo_p2p_server_watcher) {
-		purple_input_remove(yd->yahoo_p2p_server_watcher);
-		yd->yahoo_p2p_server_watcher = 0;
-	}
-	if (yd->yahoo_local_p2p_server_fd >= 0) {
-		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);
-	g_free(yd->chat_name);
-
-	g_free(yd->cookie_y);
-	g_free(yd->cookie_t);
-
-	if (yd->txhandler)
-		purple_input_remove(yd->txhandler);
-
-	purple_circ_buffer_destroy(yd->txbuf);
-
-	if (yd->fd >= 0)
-		close(yd->fd);
-
-	g_free(yd->rxqueue);
-	yd->rxlen = 0;
-	g_free(yd->picture_url);
-
-	if (yd->buddy_icon_connect_data)
-		purple_proxy_connect_cancel(yd->buddy_icon_connect_data);
-	if (yd->picture_upload_todo)
-		yahoo_buddy_icon_upload_data_free(yd->picture_upload_todo);
-	if (yd->ycht)
-		ycht_connection_close(yd->ycht);
-
-	g_free(yd->pending_chat_room);
-	g_free(yd->pending_chat_id);
-	g_free(yd->pending_chat_topic);
-	g_free(yd->pending_chat_goto);
-	g_strfreev(yd->profiles);
-
-	g_free(yd->current_list15_grp);
-
-	g_free(yd);
-	gc->proto_data = NULL;
-}
-
-static const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b)
-{
-	return "yahoo";
-}
-
-static const char *yahoo_list_emblem(PurpleBuddy *b)
-{
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	struct yahoo_data *yd;
-	YahooFriend *f;
-	PurplePresence *presence;
-
-	if (!b || !(account = purple_buddy_get_account(b)) ||
-			!(gc = purple_account_get_connection(account)) ||
-			!(yd = gc->proto_data))
-		return NULL;
-
-	f = yahoo_friend_find(gc, purple_buddy_get_name(b));
-	if (!f) {
-		return "not-authorized";
-	}
-
-	presence = purple_buddy_get_presence(b);
-
-	if (purple_presence_is_online(presence)) {
-		if (yahoo_friend_get_game(f))
-			return "game";
-		if (f->protocol == 2)
-			return "msn";
-	}
-	return NULL;
-}
-
-static const char *yahoo_get_status_string(enum yahoo_status a)
-{
-	switch (a) {
-	case YAHOO_STATUS_BRB:
-		return _("Be Right Back");
-	case YAHOO_STATUS_BUSY:
-		return _("Busy");
-	case YAHOO_STATUS_NOTATHOME:
-		return _("Not at Home");
-	case YAHOO_STATUS_NOTATDESK:
-		return _("Not at Desk");
-	case YAHOO_STATUS_NOTINOFFICE:
-		return _("Not in Office");
-	case YAHOO_STATUS_ONPHONE:
-		return _("On the Phone");
-	case YAHOO_STATUS_ONVACATION:
-		return _("On Vacation");
-	case YAHOO_STATUS_OUTTOLUNCH:
-		return _("Out to Lunch");
-	case YAHOO_STATUS_STEPPEDOUT:
-		return _("Stepped Out");
-	case YAHOO_STATUS_INVISIBLE:
-		return _("Invisible");
-	case YAHOO_STATUS_IDLE:
-		return _("Idle");
-	case YAHOO_STATUS_OFFLINE:
-		return _("Offline");
-	default:
-		return _("Available");
-	}
-}
-
-static void yahoo_initiate_conference(PurpleBlistNode *node, gpointer data) {
-
-	PurpleBuddy *buddy;
-	PurpleConnection *gc;
-
-	GHashTable *components;
-	struct yahoo_data *yd;
-	int id;
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	yd = gc->proto_data;
-	id = yd->conf_id;
-
-	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	g_hash_table_replace(components, g_strdup("room"),
-		g_strdup_printf("%s-%d", purple_connection_get_display_name(gc), id));
-	g_hash_table_replace(components, g_strdup("topic"), g_strdup("Join my conference..."));
-	g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
-	yahoo_c_join(gc, components);
-	g_hash_table_destroy(components);
-
-	yahoo_c_invite(gc, id, "Join my conference...", purple_buddy_get_name(buddy));
-}
-
-static void yahoo_presence_settings(PurpleBlistNode *node, gpointer data) {
-	PurpleBuddy *buddy;
-	PurpleConnection *gc;
-	int presence_val = GPOINTER_TO_INT(data);
-
-	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-
-	yahoo_friend_update_presence(gc, purple_buddy_get_name(buddy), presence_val);
-}
-
-static void yahoo_game(PurpleBlistNode *node, gpointer data) {
-
-	PurpleBuddy *buddy;
-	PurpleConnection *gc;
-
-	struct yahoo_data *yd;
-	const char *game;
-	char *game2;
-	char *t;
-	char url[256];
-	YahooFriend *f;
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	yd = (struct yahoo_data *) gc->proto_data;
-
-	f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
-	if (!f)
-		return;
-
-	game = yahoo_friend_get_game(f);
-	if (!game)
-		return;
-
-	t = game2 = g_strdup(strstr(game, "ante?room="));
-	while (*t && *t != '\t')
-		t++;
-	*t = 0;
-	g_snprintf(url, sizeof url, "http://games.yahoo.com/games/%s", game2);
-	purple_notify_uri(gc, url);
-	g_free(game2);
-}
-
-static char *yahoo_status_text(PurpleBuddy *b)
-{
-	YahooFriend *f = NULL;
-	const char *msg;
-	char *msg2;
-	PurpleAccount *account;
-
-	account = purple_buddy_get_account(b);
-	f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
-	if (!f)
-		return g_strdup(_("Not on server list"));
-
-	switch (f->status) {
-	case YAHOO_STATUS_AVAILABLE:
-		return NULL;
-	case YAHOO_STATUS_IDLE:
-		if (f->idle == -1)
-			return g_strdup(yahoo_get_status_string(f->status));
-		return NULL;
-	case YAHOO_STATUS_CUSTOM:
-		if (!(msg = yahoo_friend_get_status_message(f)))
-			return NULL;
-		msg2 = g_markup_escape_text(msg, strlen(msg));
-		purple_util_chrreplace(msg2, '\n', ' ');
-		return msg2;
-
-	default:
-		return g_strdup(yahoo_get_status_string(f->status));
-	}
-}
-
-void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
-{
-	YahooFriend *f;
-	char *escaped;
-	char *status = NULL;
-	const char *presence = NULL;
-	PurpleAccount *account;
-
-	account = purple_buddy_get_account(b);
-	f = yahoo_friend_find(purple_account_get_connection(account), purple_buddy_get_name(b));
-	if (!f)
-		status = g_strdup_printf("\n%s", _("Not on server list"));
-	else {
-		switch (f->status) {
-		case YAHOO_STATUS_CUSTOM:
-			if (!yahoo_friend_get_status_message(f))
-				return;
-			status = g_strdup(yahoo_friend_get_status_message(f));
-			break;
-		case YAHOO_STATUS_OFFLINE:
-			break;
-		default:
-			status = g_strdup(yahoo_get_status_string(f->status));
-			break;
-		}
-
-		switch (f->presence) {
-			case YAHOO_PRESENCE_ONLINE:
-				presence = _("Appear Online");
-				break;
-			case YAHOO_PRESENCE_PERM_OFFLINE:
-				presence = _("Appear Permanently Offline");
-				break;
-			case YAHOO_PRESENCE_DEFAULT:
-				break;
-			default:
-				purple_debug_error("yahoo", "Unknown presence in yahoo_tooltip_text\n");
-				break;
-		}
-	}
-
-	if (status != NULL) {
-		escaped = g_markup_escape_text(status, strlen(status));
-		purple_notify_user_info_add_pair(user_info, _("Status"), escaped);
-		g_free(status);
-		g_free(escaped);
-	}
-
-	if (presence != NULL)
-		purple_notify_user_info_add_pair(user_info, _("Presence"), presence);
-}
-
-static void yahoo_addbuddyfrommenu_cb(PurpleBlistNode *node, gpointer data)
-{
-	PurpleBuddy *buddy;
-	PurpleConnection *gc;
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-
-	yahoo_add_buddy(gc, buddy, NULL);
-}
-
-
-static void yahoo_chat_goto_menu(PurpleBlistNode *node, gpointer data)
-{
-	PurpleBuddy *buddy;
-	PurpleConnection *gc;
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-
-	yahoo_chat_goto(gc, purple_buddy_get_name(buddy));
-}
-
-static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) {
-	GList *m = NULL;
-	PurpleMenuAction *act;
-	struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data;
-
-	if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
-		if (f->presence != YAHOO_PRESENCE_ONLINE) {
-			act = purple_menu_action_new(_("Appear Online"),
-			                           PURPLE_CALLBACK(yahoo_presence_settings),
-			                           GINT_TO_POINTER(YAHOO_PRESENCE_ONLINE),
-			                           NULL);
-			m = g_list_append(m, act);
-		} else if (f->presence != YAHOO_PRESENCE_DEFAULT) {
-			act = purple_menu_action_new(_("Appear Offline"),
-			                           PURPLE_CALLBACK(yahoo_presence_settings),
-			                           GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
-			                           NULL);
-			m = g_list_append(m, act);
-		}
-	}
-
-	if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) {
-		act = purple_menu_action_new(_("Don't Appear Permanently Offline"),
-		                           PURPLE_CALLBACK(yahoo_presence_settings),
-		                           GINT_TO_POINTER(YAHOO_PRESENCE_DEFAULT),
-		                           NULL);
-		m = g_list_append(m, act);
-	} else {
-		act = purple_menu_action_new(_("Appear Permanently Offline"),
-		                           PURPLE_CALLBACK(yahoo_presence_settings),
-		                           GINT_TO_POINTER(YAHOO_PRESENCE_PERM_OFFLINE),
-		                           NULL);
-		m = g_list_append(m, act);
-	}
-
-	return m;
-}
-
-static void yahoo_doodle_blist_node(PurpleBlistNode *node, gpointer data)
-{
-	PurpleBuddy *b = (PurpleBuddy *)node;
-	PurpleAccount *account = purple_buddy_get_account(b);
-	PurpleConnection *gc = purple_account_get_connection(account);
-
-	yahoo_doodle_initiate(gc, purple_buddy_get_name(b));
-}
-
-static GList *yahoo_buddy_menu(PurpleBuddy *buddy)
-{
-	GList *m = NULL;
-	PurpleMenuAction *act;
-
-	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	struct yahoo_data *yd = gc->proto_data;
-	static char buf2[1024];
-	YahooFriend *f;
-
-	f = yahoo_friend_find(gc, purple_buddy_get_name(buddy));
-
-	if (!f && !yd->wm) {
-		act = purple_menu_action_new(_("Add Buddy"),
-		                           PURPLE_CALLBACK(yahoo_addbuddyfrommenu_cb),
-		                           NULL, NULL);
-		m = g_list_append(m, act);
-
-		return m;
-
-	}
-
-	if (f && f->status != YAHOO_STATUS_OFFLINE) {
-		if (!yd->wm) {
-			act = purple_menu_action_new(_("Join in Chat"),
-			                           PURPLE_CALLBACK(yahoo_chat_goto_menu),
-			                           NULL, NULL);
-			m = g_list_append(m, act);
-		}
-
-		act = purple_menu_action_new(_("Initiate Conference"),
-		                           PURPLE_CALLBACK(yahoo_initiate_conference),
-		                           NULL, NULL);
-		m = g_list_append(m, act);
-
-		if (yahoo_friend_get_game(f)) {
-			const char *game = yahoo_friend_get_game(f);
-			char *room;
-			char *t;
-
-			if ((room = strstr(game, "&follow="))) {/* skip ahead to the url */
-				while (*room && *room != '\t')          /* skip to the tab */
-					room++;
-				t = room++;                             /* room as now at the name */
-				while (*t != '\n')
-					t++;                            /* replace the \n with a space */
-				*t = ' ';
-				g_snprintf(buf2, sizeof buf2, "%s", room);
-
-				act = purple_menu_action_new(buf2,
-				                           PURPLE_CALLBACK(yahoo_game),
-				                           NULL, NULL);
-				m = g_list_append(m, act);
-			}
-		}
-	}
-
-	if (f) {
-		act = purple_menu_action_new(_("Presence Settings"), NULL, NULL,
-		                           build_presence_submenu(f, gc));
-		m = g_list_append(m, act);
-	}
-
-	if (f) {
-		act = purple_menu_action_new(_("Start Doodling"),
-		                           PURPLE_CALLBACK(yahoo_doodle_blist_node),
-		                           NULL, NULL);
-		m = g_list_append(m, act);
-	}
-
-	return m;
-}
-
-static GList *yahoo_blist_node_menu(PurpleBlistNode *node)
-{
-	if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
-		return yahoo_buddy_menu((PurpleBuddy *) node);
-	} else {
-		return NULL;
-	}
-}
-
-static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
-{
-	struct yahoo_data *yd = gc->proto_data;
-	const char *name = yd->profiles[purple_request_fields_get_choice(fields, "id")];
-
-	struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash_str(pkt, 3, name);
-	yahoo_packet_send_and_free(pkt, yd);
-
-	purple_connection_set_display_name(gc, name);
-}
-
-static void
-yahoo_get_inbox_token_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
-		const gchar *token, size_t len, const gchar *error_message)
-{
-	PurpleConnection *gc = user_data;
-	gboolean set_cookie = FALSE;
-	gchar *url;
-	struct yahoo_data *yd = gc->proto_data;
-
-	g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
-
-	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
-
-	if (error_message != NULL)
-		purple_debug_error("yahoo", "Requesting mail login token failed: %s\n", error_message);
-	else if (len > 0 && token && *token) {
-	 	/* Should we not be hardcoding the rd url? */
-		url = g_strdup_printf(
-			"http://login.yahoo.com/config/reset_cookies_token?"
-			".token=%s"
-			"&.done=http://us.rd.yahoo.com/messenger/client/%%3fhttp://mail.yahoo.com/",
-			token);
-		set_cookie = TRUE;
-	}
-
-	if (!set_cookie) {
-		purple_debug_error("yahoo", "No mail login token; forwarding to login screen.\n");
-		url = g_strdup(yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
-	}
-
-	/* Open the mailbox with the parsed url data */
-	purple_notify_uri(gc, url);
-
-	g_free(url);
-}
-
-
-static void yahoo_show_inbox(PurplePluginAction *action)
-{
-	/* Setup a cookie that can be used by the browser */
-	/* XXX I have no idea how this will work with Yahoo! Japan. */
-
-	PurpleConnection *gc = action->context;
-	struct yahoo_data *yd = gc->proto_data;
-
-	PurpleUtilFetchUrlData *url_data;
-	const char* base_url = "http://login.yahoo.com";
-	/* use whole URL if using HTTP Proxy */
-	gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
-	gchar *request = g_strdup_printf(
-		"POST %s/config/cookie_token HTTP/1.0\r\n"
-		"Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s;\r\n"
-		"User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
-		"Host: login.yahoo.com\r\n"
-		"Content-Length: 0\r\n\r\n",
-		use_whole_url ? base_url : "",
-		yd->cookie_t, yd->cookie_y);
-
-	url_data = purple_util_fetch_url_request_len_with_account(
-			purple_connection_get_account(gc), base_url, use_whole_url,
-			YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
-			yahoo_get_inbox_token_cb, gc);
-
-	g_free(request);
-
-	if (url_data != NULL)
-		yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
-	else {
-		const char *yahoo_mail_url = (yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL);
-		purple_debug_error("yahoo",
-				   "Unable to request mail login token; forwarding to login screen.");
-		purple_notify_uri(gc, yahoo_mail_url);
-	}
-
-}
-
-
-static void yahoo_show_act_id(PurplePluginAction *action)
-{
-	PurpleRequestFields *fields;
-	PurpleRequestFieldGroup *group;
-	PurpleRequestField *field;
-	PurpleConnection *gc = (PurpleConnection *) action->context;
-	struct yahoo_data *yd = purple_connection_get_protocol_data(gc);
-	const char *name = purple_connection_get_display_name(gc);
-	int iter;
-
-	fields = purple_request_fields_new();
-	group = purple_request_field_group_new(NULL);
-	purple_request_fields_add_group(fields, group);
-	field = purple_request_field_choice_new("id", "Activate which ID?", 0);
-	purple_request_field_group_add_field(group, field);
-
-	for (iter = 0; yd->profiles[iter]; iter++) {
-		purple_request_field_choice_add(field, yd->profiles[iter]);
-		if (purple_strequal(yd->profiles[iter], name))
-			purple_request_field_choice_set_default_value(field, iter);
-	}
-
-	purple_request_fields(gc, NULL, _("Select the ID you want to activate"), NULL,
-					   fields,
-					   _("OK"), G_CALLBACK(yahoo_act_id),
-					   _("Cancel"), NULL,
-					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
-}
-
-static void yahoo_show_chat_goto(PurplePluginAction *action)
-{
-	PurpleConnection *gc = (PurpleConnection *) action->context;
-	purple_request_input(gc, NULL, _("Join whom in chat?"), NULL,
-					   "", FALSE, FALSE, NULL,
-					   _("OK"), G_CALLBACK(yahoo_chat_goto),
-					   _("Cancel"), NULL,
-					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
-}
-
-static GList *yahoo_actions(PurplePlugin *plugin, gpointer context) {
-	GList *m = NULL;
-	PurplePluginAction *act;
-
-	act = purple_plugin_action_new(_("Activate ID..."),
-			yahoo_show_act_id);
-	m = g_list_append(m, act);
-
-	act = purple_plugin_action_new(_("Join User in Chat..."),
-			yahoo_show_chat_goto);
-	m = g_list_append(m, act);
-
-	m = g_list_append(m, NULL);
-	act = purple_plugin_action_new(_("Open Inbox"),
-			yahoo_show_inbox);
-	m = g_list_append(m, act);
-
-	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: " YAHOO_CLIENT_USERAGENT "\r\n"
-		"Host: validate.msg.yahoo.com\r\n"
-		"Content-Length: %" G_GSIZE_FORMAT "\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_len_with_account(
-			purple_connection_get_account(gc), YAHOO_SMS_CARRIER_URL, use_whole_url,
-			YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
-			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 = NULL;
-	char *msg = yahoo_html_to_codes(what);
-	char *msg2;
-	gboolean utf8 = TRUE;
-	PurpleWhiteboard *wb;
-	int ret = 1;
-	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) {
-		lenb = strlen(msg2);
-		lenc = g_utf8_strlen(msg2, -1);
-
-		if(lenb > YAHOO_MAX_MESSAGE_LENGTH_BYTES || lenc > YAHOO_MAX_MESSAGE_LENGTH_CHARS) {
-			purple_debug_info("yahoo", "Message too big.  Length is %" G_GSIZE_FORMAT
-					" bytes, %ld characters.  Max is %d bytes, %d chars."
-					"  Message is '%s'.\n", lenb, lenc, YAHOO_MAX_MESSAGE_LENGTH_BYTES,
-					YAHOO_MAX_MESSAGE_LENGTH_CHARS, msg2);
-			g_free(msg);
-			g_free(msg2);
-			return -E2BIG;
-		}
-	}
-
-	msn = !g_strncasecmp(who, "msn/", 4);
-
-	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_strdup(who);
-			sms_cb_data->what = g_strdup(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");
-	yahoo_packet_hash_str(pkt, 14, msg2);
-
-	/*
-	 * IMVironment.
-	 *
-	 * If this message is to a user who is also Doodling with the local user,
-	 * format the chat packet with the correct IMV information (thanks Yahoo!)
-	 *
-	 * Otherwise attempt to use the same IMVironment as the remote user,
-	 * just so that we don't inadvertantly reset their IMVironment back
-	 * to nothing.
-	 *
-	 * If they have no set an IMVironment, then use the default.
-	 */
-	wb = purple_whiteboard_get_session(gc->account, who);
-	if (wb)
-		yahoo_packet_hash_str(pkt, 63, DOODLE_IMV_KEY);
-	else
-	{
-		const char *imv;
-		imv = g_hash_table_lookup(yd->imvironments, who);
-		if (imv != NULL)
-			yahoo_packet_hash_str(pkt, 63, imv);
-		else
-			yahoo_packet_hash_str(pkt, 63, ";0");
-	}
-
-	yahoo_packet_hash_str(pkt,   64, "0"); /* no idea */
-	yahoo_packet_hash_str(pkt, 1002, "1"); /* no idea, Yahoo 6 or later only it seems */
-	if (!yd->picture_url)
-		yahoo_packet_hash_str(pkt, 206, "0"); /* 0 = no picture, 2 = picture, maybe 1 = avatar? */
-	else
-		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) {
-		/* 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;
-
-	yahoo_packet_free(pkt);
-
-	g_free(msg);
-	g_free(msg2);
-
-	return ret;
-}
-
-static unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
-{
-	struct yahoo_data *yd = gc->proto_data;
-	struct yahoo_p2p_data *p2p_data;
-	gboolean msn = !g_strncasecmp(who, "msn/", 4);
-	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, 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;
-}
-
-static void yahoo_session_presence_remove(gpointer key, gpointer value, gpointer data)
-{
-	YahooFriend *f = value;
-	if (f && f->presence == YAHOO_PRESENCE_ONLINE)
-		f->presence = YAHOO_PRESENCE_DEFAULT;
-}
-
-static void yahoo_set_status(PurpleAccount *account, PurpleStatus *status)
-{
-	PurpleConnection *gc;
-	PurplePresence *presence;
-	struct yahoo_data *yd;
-	struct yahoo_packet *pkt;
-	int old_status;
-	const char *msg = NULL;
-	char *tmp = NULL;
-	char *conv_msg = NULL;
-	gboolean utf8 = TRUE;
-
-	if (!purple_status_is_active(status))
-		return;
-
-	gc = purple_account_get_connection(account);
-	presence = purple_status_get_presence(status);
-	yd = (struct yahoo_data *)gc->proto_data;
-	old_status = yd->current_status;
-
-	yd->current_status = get_yahoo_status_from_purple_status(status);
-
-	if (yd->current_status == YAHOO_STATUS_CUSTOM)
-	{
-		msg = purple_status_get_attr_string(status, "message");
-
-		if (purple_status_is_available(status)) {
-			tmp = yahoo_string_encode(gc, msg, &utf8);
-			conv_msg = purple_markup_strip_html(tmp);
-			g_free(tmp);
-		} else {
-			if ((msg == NULL) || (*msg == '\0'))
-				msg = _("Away");
-			tmp = yahoo_string_encode(gc, msg, &utf8);
-			conv_msg = purple_markup_strip_html(tmp);
-			g_free(tmp);
-		}
-	}
-
-	if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
-		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, 0);
-		yahoo_packet_hash_str(pkt, 13, "2");
-		yahoo_packet_send_and_free(pkt, yd);
-
-		return;
-	}
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash_int(pkt, 10, yd->current_status);
-
-	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
-		yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
-		yahoo_packet_hash_str(pkt, 19, conv_msg);
-	} else {
-		yahoo_packet_hash_str(pkt, 19, "");
-	}
-
-	g_free(conv_msg);
-
-	if (purple_presence_is_idle(presence))
-		yahoo_packet_hash_str(pkt, 47, "2");
-	else if (!purple_status_is_available(status))
-		yahoo_packet_hash_str(pkt, 47, "1");
-
-	yahoo_packet_send_and_free(pkt, yd);
-
-	if (old_status == YAHOO_STATUS_INVISIBLE) {
-		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBLE_TOGGLE, YAHOO_STATUS_AVAILABLE, 0);
-		yahoo_packet_hash_str(pkt, 13, "1");
-		yahoo_packet_send_and_free(pkt, yd);
-
-		/* Any per-session presence settings are removed */
-		g_hash_table_foreach(yd->friends, yahoo_session_presence_remove, NULL);
-
-	}
-}
-
-static void yahoo_set_idle(PurpleConnection *gc, int idle)
-{
-	struct yahoo_data *yd = gc->proto_data;
-	struct yahoo_packet *pkt = NULL;
-	char *msg = NULL, *msg2 = NULL;
-	PurpleStatus *status = NULL;
-
-	if (idle && yd->current_status != YAHOO_STATUS_CUSTOM)
-		yd->current_status = YAHOO_STATUS_IDLE;
-	else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) {
-		status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
-		yd->current_status = get_yahoo_status_from_purple_status(status);
-	}
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
-
-	yahoo_packet_hash_int(pkt, 10, yd->current_status);
-	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
-		const char *tmp;
-		if (status == NULL)
-			status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
-		tmp = purple_status_get_attr_string(status, "message");
-		if (tmp != NULL) {
-			gboolean utf8 = TRUE;
-			msg = yahoo_string_encode(gc, tmp, &utf8);
-			msg2 = purple_markup_strip_html(msg);
-			yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
-			yahoo_packet_hash_str(pkt, 19, msg2);
-		} else {
-			/* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
-			 * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message */
-			yahoo_packet_hash_str(pkt, 19, _("Away"));
-		}
-	} else {
-		yahoo_packet_hash_str(pkt, 19, "");
-	}
-
-	if (idle)
-		yahoo_packet_hash_str(pkt, 47, "2");
-	else if (!purple_presence_is_available(purple_account_get_presence(purple_connection_get_account(gc))))
-		yahoo_packet_hash_str(pkt, 47, "1");
-
-	yahoo_packet_send_and_free(pkt, yd);
-
-	g_free(msg);
-	g_free(msg2);
-}
-
-static GList *yahoo_status_types(PurpleAccount *account)
-{
-	PurpleStatusType *type;
-	GList *types = NULL;
-
-	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, YAHOO_STATUS_TYPE_AVAILABLE,
-	                                       NULL, TRUE, TRUE, FALSE,
-	                                       "message", _("Message"),
-	                                       purple_value_new(PURPLE_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_AWAY,
-	                                       NULL, TRUE, TRUE, FALSE,
-	                                       "message", _("Message"),
-	                                       purple_value_new(PURPLE_TYPE_STRING), NULL);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY, _("Busy"), TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATDESK, _("Not at Desk"), TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTINOFFICE, _("Not in Office"), TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_ONPHONE, _("On the Phone"), TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_EXTENDED_AWAY, YAHOO_STATUS_TYPE_ONVACATION, _("On Vacation"), TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_OUTTOLUNCH, _("Out to Lunch"), TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_STEPPEDOUT, _("Stepped Out"), TRUE);
-	types = g_list_append(types, type);
-
-
-	type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, YAHOO_STATUS_TYPE_INVISIBLE, NULL, TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new(PURPLE_STATUS_OFFLINE, YAHOO_STATUS_TYPE_OFFLINE, NULL, TRUE);
-	types = g_list_append(types, type);
-
-	type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, YAHOO_STATUS_TYPE_MOBILE, NULL, FALSE, FALSE, TRUE);
-	types = g_list_append(types, type);
-
-	return types;
-}
-
-static void yahoo_keepalive(PurpleConnection *gc)
-{
-	struct yahoo_packet *pkt;
-	struct yahoo_data *yd = gc->proto_data;
-	time_t now = time(NULL);
-
-	/* We're only allowed to send a ping once an hour or the servers will boot us */
-	if ((now - yd->last_ping) >= PING_TIMEOUT) {
-		yd->last_ping = now;
-
-		/* The native client will only send PING or CHATPING */
-		if (yd->chat_online) {
-			if (yd->wm) {
-				ycht_chat_send_keepalive(yd->ycht);
-			} else {
-				pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, 0);
-				yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc));
-				yahoo_packet_send_and_free(pkt, yd);
-			}
-		} else {
-			pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, 0);
-			yahoo_packet_send_and_free(pkt, yd);
-		}
-	}
-
-	if ((now - yd->last_keepalive) >= KEEPALIVE_TIMEOUT) {
-		yd->last_keepalive = now;
-		pkt = yahoo_packet_new(YAHOO_SERVICE_KEEPALIVE, YAHOO_STATUS_AVAILABLE, 0);
-		yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc));
-		yahoo_packet_send_and_free(pkt, yd);
-	}
-
-}
-
-static void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
-{
-	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
-	struct yahoo_packet *pkt;
-	const char *group = NULL;
-	char *group2;
-	YahooFriend *f;
-	const char *bname;
-	gboolean msn = FALSE;
-
-	if (!yd->logged_in)
-		return;
-
-	bname = purple_buddy_get_name(buddy);
-	if (!purple_privacy_check(purple_connection_get_account(gc), bname))
-		return;
-
-	f = yahoo_friend_find(gc, bname);
-	msn = !g_strncasecmp(bname, "msn/", 4);
-
-	g = purple_buddy_get_group(buddy);
-	if (g)
-		group = purple_group_get_name(g);
-	else
-		group = "Buddies";
-
-	group2 = yahoo_string_encode(gc, group, NULL);
-	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, 0);
-	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);
-}
-
-static void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
-{
-	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
-	struct yahoo_packet *pkt;
-	GSList *buddies, *l;
-	PurpleGroup *g;
-	gboolean remove = TRUE;
-	char *cg;
-	const char *bname, *gname;
-	YahooFriend *f = NULL;
-	gboolean msn = FALSE;
-
-	bname = purple_buddy_get_name(buddy);
-	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))) {
-			remove = FALSE;
-			break;
-		}
-	}
-
-	g_slist_free(buddies);
-
-	if (remove)
-		g_hash_table_remove(yd->friends, bname);
-
-	cg = yahoo_string_encode(gc, gname, NULL);
-	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, 0);
-
-	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);
-}
-
-static void yahoo_add_deny(PurpleConnection *gc, const char *who) {
-	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
-	struct yahoo_packet *pkt;
-
-	if (!yd->logged_in)
-		return;
-
-	if (!who || who[0] == '\0')
-		return;
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
-	                  7, who, 13, "1");
-	yahoo_packet_send_and_free(pkt, yd);
-}
-
-static void yahoo_rem_deny(PurpleConnection *gc, const char *who) {
-	struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data;
-	struct yahoo_packet *pkt;
-
-	if (!yd->logged_in)
-		return;
-
-	if (!who || who[0] == '\0')
-		return;
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, who, 13, "2");
-	yahoo_packet_send_and_free(pkt, yd);
-}
-
-static void yahoo_set_permit_deny(PurpleConnection *gc)
-{
-	PurpleAccount *account;
-	GSList *deny;
-
-	account = purple_connection_get_account(gc);
-
-	switch (account->perm_deny)
-	{
-		case PURPLE_PRIVACY_ALLOW_ALL:
-			for (deny = account->deny; deny; deny = deny->next)
-				yahoo_rem_deny(gc, deny->data);
-			break;
-
-		case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
-		case PURPLE_PRIVACY_ALLOW_USERS:
-		case PURPLE_PRIVACY_DENY_USERS:
-		case PURPLE_PRIVACY_DENY_ALL:
-			for (deny = account->deny; deny; deny = deny->next)
-				yahoo_add_deny(gc, deny->data);
-			break;
-	}
-}
-
-static gboolean yahoo_unload_plugin(PurplePlugin *plugin)
-{
-	yahoo_dest_colorht();
-
-	return TRUE;
-}
-
-static void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
-				   const char *old_group, const char *new_group)
-{
-	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 (!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.
-	 */
-	gpn = yahoo_string_encode(gc, new_group, NULL);
-	gpo = yahoo_string_encode(gc, old_group, NULL);
-	if (!strcmp(gpn, gpo)) {
-		g_free(gpn);
-		g_free(gpo);
-		return;
-	}
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, 0);
-	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);
-
-	g_free(gpn);
-	g_free(gpo);
-}
-
-static void yahoo_rename_group(PurpleConnection *gc, const char *old_name,
-							   PurpleGroup *group, GList *moved_buddies)
-{
-	struct yahoo_data *yd = gc->proto_data;
-	struct yahoo_packet *pkt;
-	char *gpn, *gpo;
-
-	gpn = yahoo_string_encode(gc, purple_group_get_name(group), NULL);
-	gpo = yahoo_string_encode(gc, old_name, NULL);
-	if (!strcmp(gpn, gpo)) {
-		g_free(gpn);
-		g_free(gpo);
-		return;
-	}
-
-	pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc),
-	                  65, gpo, 67, gpn);
-	yahoo_packet_send_and_free(pkt, yd);
-	g_free(gpn);
-	g_free(gpo);
-}
-
-/********************************* Commands **********************************/
-
-static PurpleCmdRet
-yahoopurple_cmd_buzz(PurpleConversation *c, const gchar *cmd, gchar **args, gchar **error, void *data) {
-	PurpleAccount *account = purple_conversation_get_account(c);
-
-	if (*args && args[0])
-		return PURPLE_CMD_RET_FAILED;
-
-	purple_prpl_send_attention(account->gc, c->name, YAHOO_BUZZ);
-
-	return PURPLE_CMD_RET_OK;
-}
-
-static PurplePlugin *my_protocol = NULL;
-
-static PurpleCmdRet
-yahoopurple_cmd_chat_join(PurpleConversation *conv, const char *cmd,
-                        char **args, char **error, void *data)
-{
-	GHashTable *comp;
-	PurpleConnection *gc;
-	struct yahoo_data *yd;
-	int id;
-
-	if (!args || !args[0])
-		return PURPLE_CMD_RET_FAILED;
-
-	gc = purple_conversation_get_gc(conv);
-	yd = gc->proto_data;
-	id = yd->conf_id;
-	purple_debug_info("yahoo", "Trying to join %s \n", args[0]);
-
-	comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1));
-	g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat"));
-
-	yahoo_c_join(gc, comp);
-
-	g_hash_table_destroy(comp);
-	return PURPLE_CMD_RET_OK;
-}
-
-static PurpleCmdRet
-yahoopurple_cmd_chat_list(PurpleConversation *conv, const char *cmd,
-                        char **args, char **error, void *data)
-{
-	PurpleAccount *account = purple_conversation_get_account(conv);
-	if (*args && args[0])
-		return PURPLE_CMD_RET_FAILED;
-	purple_roomlist_show_with_account(account);
-	return PURPLE_CMD_RET_OK;
-}
-
-static gboolean yahoo_offline_message(const PurpleBuddy *buddy)
-{
-	return TRUE;
-}
-
-gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type)
-{
-	PurpleConversation *c;
-
-	c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-			username, gc->account);
-
-	g_return_val_if_fail(c != NULL, FALSE);
-
-	purple_debug_info("yahoo", "Sending <ding> on account %s to buddy %s.\n",
-			username, c->name);
-	purple_conv_im_send_with_flags(PURPLE_CONV_IM(c), "<ding>", PURPLE_MESSAGE_INVISIBLE);
-
-	return TRUE;
-}
-
-GList *yahoo_attention_types(PurpleAccount *account)
-{
-	static GList *list = NULL;
-
-	if (!list) {
-		/* Yahoo only supports one attention command: the 'buzz'. */
-		/* This is index number YAHOO_BUZZ. */
-		list = g_list_append(list, purple_attention_type_new("Buzz", _("Buzz"),
-				_("%s has buzzed you!"), _("Buzzing %s...")));
-	}
-
-	return list;
-}
-
-/************************** Plugin Initialization ****************************/
-static void
-yahoopurple_register_commands(void)
-{
-	purple_cmd_register("join", "s", PURPLE_CMD_P_PRPL,
-	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
-	                  PURPLE_CMD_FLAG_PRPL_ONLY,
-	                  "prpl-yahoo", yahoopurple_cmd_chat_join,
-	                  _("join &lt;room&gt;:  Join a chat room on the Yahoo network"), NULL);
-	purple_cmd_register("list", "", PURPLE_CMD_P_PRPL,
-	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
-	                  PURPLE_CMD_FLAG_PRPL_ONLY,
-	                  "prpl-yahoo", yahoopurple_cmd_chat_list,
-	                  _("list: List rooms on the Yahoo network"), NULL);
-	purple_cmd_register("buzz", "", PURPLE_CMD_P_PRPL,
-	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
-	                  "prpl-yahoo", yahoopurple_cmd_buzz,
-	                  _("buzz: Buzz a user to get their attention"), NULL);
-	purple_cmd_register("doodle", "", PURPLE_CMD_P_PRPL,
-	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
-	                  "prpl-yahoo", yahoo_doodle_purple_cmd_start,
-	                 _("doodle: Request user to start a Doodle session"), NULL);
-}
-
-static PurpleAccount *find_acct(const char *prpl, const char *acct_id)
-{
-	PurpleAccount *acct = NULL;
-
-	/* If we have a specific acct, use it */
-	if (acct_id) {
-		acct = purple_accounts_find(acct_id, prpl);
-		if (acct && !purple_account_is_connected(acct))
-			acct = NULL;
-	} else { /* Otherwise find an active account for the protocol */
-		GList *l = purple_accounts_get_all();
-		while (l) {
-			if (!strcmp(prpl, purple_account_get_protocol_id(l->data))
-					&& purple_account_is_connected(l->data)) {
-				acct = l->data;
-				break;
-			}
-			l = l->next;
-		}
-	}
-
-	return acct;
-}
-
-/* This may not be the best way to do this, but we find the first key w/o a value
- * and assume it is the buddy name */
-static void yahoo_find_uri_novalue_param(gpointer key, gpointer value, gpointer user_data)
-{
-	char **retval = user_data;
-
-	if (value == NULL && *retval == NULL) {
-		*retval = key;
-	}
-}
-
-static gboolean yahoo_uri_handler(const char *proto, const char *cmd, GHashTable *params)
-{
-	char *acct_id = g_hash_table_lookup(params, "account");
-	PurpleAccount *acct;
-
-	if (g_ascii_strcasecmp(proto, "ymsgr"))
-		return FALSE;
-
-	acct = find_acct(purple_plugin_get_id(my_protocol), acct_id);
-
-	if (!acct)
-		return FALSE;
-
-	/* ymsgr:SendIM?screename&m=The+Message */
-	if (!g_ascii_strcasecmp(cmd, "SendIM")) {
-		char *sname = NULL;
-		g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &sname);
-		if (sname) {
-			char *message = g_hash_table_lookup(params, "m");
-
-			PurpleConversation *conv = purple_find_conversation_with_account(
-				PURPLE_CONV_TYPE_IM, sname, acct);
-			if (conv == NULL)
-				conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname);
-			purple_conversation_present(conv);
-
-			if (message) {
-				/* Spaces are encoded as '+' */
-				g_strdelimit(message, "+", ' ');
-				purple_conv_send_confirm(conv, message);
-			}
-		}
-		/* 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(); */
-
-		return TRUE;
-	}
-	/* ymsgr:Chat?roomname */
-	else if (!g_ascii_strcasecmp(cmd, "Chat")) {
-		char *rname = NULL;
-		g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &rname);
-		if (rname) {
-			/* This is somewhat hacky, but the params aren't useful after this command */
-			g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
-			g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat"));
-			serv_join_chat(purple_account_get_connection(acct), params);
-		}
-		/* else
-			** Same as above (except that this would have to be re-written using purple_request_*)
-			pidgin_blist_joinchat_show(); */
-
-		return TRUE;
-	}
-	/* ymsgr:AddFriend?name */
-	else if (!g_ascii_strcasecmp(cmd, "AddFriend")) {
-		char *name = NULL;
-		g_hash_table_foreach(params, yahoo_find_uri_novalue_param, &name);
-		purple_blist_request_add_buddy(acct, name, NULL, NULL);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static GHashTable *
-yahoo_get_account_text_table(PurpleAccount *account)
-{
-	GHashTable *table;
-	table = g_hash_table_new(g_str_hash, g_str_equal);
-	g_hash_table_insert(table, "login_label", (gpointer)_("Yahoo ID..."));
-	return table;
-}
-
-static PurpleWhiteboardPrplOps yahoo_whiteboard_prpl_ops =
-{
-	yahoo_doodle_start,
-	yahoo_doodle_end,
-	yahoo_doodle_get_dimensions,
-	NULL,
-	yahoo_doodle_get_brush,
-	yahoo_doodle_set_brush,
-	yahoo_doodle_send_draw_list,
-	yahoo_doodle_clear,
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
-};
-
-static PurplePluginProtocolInfo prpl_info =
-{
-	OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC,
-	NULL, /* user_splits */
-	NULL, /* protocol_options */
-	{"png,gif,jpeg", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND},
-	yahoo_list_icon,
-	yahoo_list_emblem,
-	yahoo_status_text,
-	yahoo_tooltip_text,
-	yahoo_status_types,
-	yahoo_blist_node_menu,
-	yahoo_c_info,
-	yahoo_c_info_defaults,
-	yahoo_login,
-	yahoo_close,
-	yahoo_send_im,
-	NULL, /* set info */
-	yahoo_send_typing,
-	yahoo_get_info,
-	yahoo_set_status,
-	yahoo_set_idle,
-	NULL, /* change_passwd*/
-	yahoo_add_buddy,
-	NULL, /* add_buddies */
-	yahoo_remove_buddy,
-	NULL, /* remove_buddies */
-	NULL, /* add_permit */
-	yahoo_add_deny,
-	NULL, /* rem_permit */
-	yahoo_rem_deny,
-	yahoo_set_permit_deny,
-	yahoo_c_join,
-	NULL, /* reject chat invite */
-	yahoo_get_chat_name,
-	yahoo_c_invite,
-	yahoo_c_leave,
-	NULL, /* chat whisper */
-	yahoo_c_send,
-	yahoo_keepalive,
-	NULL, /* register_user */
-	NULL, /* get_cb_info */
-	NULL, /* get_cb_away */
-	yahoo_update_alias, /* alias_buddy */
-	yahoo_change_buddys_group,
-	yahoo_rename_group,
-	NULL, /* buddy_free */
-	NULL, /* convo_closed */
-	purple_normalize_nocase, /* normalize */
-	yahoo_set_buddy_icon,
-	NULL, /* void (*remove_group)(PurpleConnection *gc, const char *group);*/
-	NULL, /* char *(*get_cb_real_name)(PurpleConnection *gc, int id, const char *who); */
-	NULL, /* set_chat_topic */
-	NULL, /* find_blist_chat */
-	yahoo_roomlist_get_list,
-	yahoo_roomlist_cancel,
-	yahoo_roomlist_expand_category,
-	NULL, /* can_receive_file */
-	yahoo_send_file,
-	yahoo_new_xfer,
-	yahoo_offline_message, /* offline_message */
-	&yahoo_whiteboard_prpl_ops,
-	NULL, /* send_raw */
-	NULL, /* roomlist_room_serialize */
-	NULL, /* unregister_user */
-
-	yahoo_send_attention,
-	yahoo_attention_types,
-
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
-	yahoo_get_account_text_table,    /* get_account_text_table */
-	NULL, /* initiate_media */
-	NULL  /* can_do_media */
-};
-
-static PurplePluginInfo info =
-{
-	PURPLE_PLUGIN_MAGIC,
-	PURPLE_MAJOR_VERSION,
-	PURPLE_MINOR_VERSION,
-	PURPLE_PLUGIN_PROTOCOL,                             /**< type           */
-	NULL,                                             /**< ui_requirement */
-	0,                                                /**< flags          */
-	NULL,                                             /**< dependencies   */
-	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */
-	"prpl-yahoo",                                     /**< id             */
-	"Yahoo",	                                      /**< name           */
-	DISPLAY_VERSION,                                  /**< version        */
-	                                                  /**  summary        */
-	N_("Yahoo Protocol Plugin"),
-	                                                  /**  description    */
-	N_("Yahoo Protocol Plugin"),
-	NULL,                                             /**< author         */
-	PURPLE_WEBSITE,                                     /**< homepage       */
-	NULL,                                             /**< load           */
-	yahoo_unload_plugin,                              /**< unload         */
-	NULL,                                             /**< destroy        */
-	NULL,                                             /**< ui_info        */
-	&prpl_info,                                       /**< extra_info     */
-	NULL,
-	yahoo_actions,
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
-};
-
-static void
-init_plugin(PurplePlugin *plugin)
-{
-	PurpleAccountOption *option;
-
-	option = purple_account_option_bool_new(_("Yahoo Japan"), "yahoojp", FALSE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_string_new(_("Pager server"), "server", YAHOO_PAGER_HOST);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_string_new(_("Japan Pager server"), "serverjp", YAHOOJP_PAGER_HOST);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_int_new(_("Pager port"), "port", YAHOO_PAGER_PORT);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_string_new(_("File transfer server"), "xfer_host", YAHOO_XFER_HOST);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_string_new(_("Japan file transfer server"), "xferjp_host", YAHOOJP_XFER_HOST);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_int_new(_("File transfer port"), "xfer_port", YAHOO_XFER_PORT);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_string_new(_("Chat room locale"), "room_list_locale", YAHOO_ROOMLIST_LOCALE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_bool_new(_("Ignore conference and chatroom invitations"), "ignore_invites", FALSE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_string_new(_("Encoding"), "local_charset", "ISO-8859-1");
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-
-#if 0
-	option = purple_account_option_string_new(_("Chat room list URL"), "room_list", YAHOO_ROOMLIST_URL);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_string_new(_("Yahoo Chat server"), "ycht-server", YAHOO_YCHT_HOST);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
-	option = purple_account_option_int_new(_("Yahoo Chat port"), "ycht-port", YAHOO_YCHT_PORT);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-#endif
-
-	my_protocol = plugin;
-	yahoopurple_register_commands();
-	yahoo_init_colorht();
-
-	purple_signal_connect(purple_get_core(), "uri-handler", plugin,
-		PURPLE_CALLBACK(yahoo_uri_handler), NULL);
-}
-
-PURPLE_INIT_PLUGIN(yahoo, init_plugin, info);
--- a/libpurple/protocols/yahoo/yahoo.h	Mon Jul 06 09:07:42 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,310 +0,0 @@
-/**
- * @file yahoo.h The Yahoo! protocol plugin
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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
- */
-
-#ifndef _YAHOO_H_
-#define _YAHOO_H_
-
-#include "circbuffer.h"
-#include "prpl.h"
-
-#define YAHOO_PAGER_HOST "scsa.msg.yahoo.com"
-#define YAHOO_PAGER_PORT 5050
-#define YAHOO_PAGER_PORT_P2P 5101
-#define YAHOO_LOGIN_URL "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%s"
-#define YAHOO_TOKEN_URL "https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s"
-#define YAHOO_P2P_KEEPALIVE_SECS 300
-#define YAHOO_P2P_SERVER_TIMEOUT 10
-#define YAHOO_PROFILE_URL "http://profiles.yahoo.com/"
-#define YAHOO_MAIL_URL "http://rd.yahoo.com/messenger/client/?http://mail.yahoo.com/"
-#define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com"
-#define YAHOO_XFER_PORT 80
-#define YAHOO_XFER_RELAY_HOST "relay.msg.yahoo.com"
-#define YAHOO_XFER_RELAY_PORT 80
-#define YAHOO_ROOMLIST_URL "http://insider.msg.yahoo.com/ycontent/"
-#define YAHOO_ROOMLIST_LOCALE "us"
-/* really we should get the list of servers from
- http://update.messenger.yahoo.co.jp/servers.html */
-#define YAHOOJP_PAGER_HOST "cs.yahoo.co.jp"
-#define YAHOOJP_TOKEN_URL "https://login.yahoo.co.jp/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s"
-#define YAHOOJP_LOGIN_URL "https://login.yahoo.co.jp/config/pwtoken_login?src=ymsgr&ts=&token=%s"
-#define YAHOOJP_PROFILE_URL "http://profiles.yahoo.co.jp/"
-#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: */
-#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/"
-#define YAHOOJP_ROOMLIST_LOCALE "ja"
-
-#define YAHOO_AUDIBLE_URL "http://us.dl1.yimg.com/download.yahoo.com/dl/aud"
-
-#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"
-
-#define YAHOO_STATUS_TYPE_OFFLINE "offline"
-#define YAHOO_STATUS_TYPE_AVAILABLE "available"
-#define YAHOO_STATUS_TYPE_BRB "brb"
-#define YAHOO_STATUS_TYPE_BUSY "busy"
-#define YAHOO_STATUS_TYPE_NOTATHOME "notathome"
-#define YAHOO_STATUS_TYPE_NOTATDESK "notatdesk"
-#define YAHOO_STATUS_TYPE_NOTINOFFICE "notinoffice"
-#define YAHOO_STATUS_TYPE_ONPHONE "onphone"
-#define YAHOO_STATUS_TYPE_ONVACATION "onvacation"
-#define YAHOO_STATUS_TYPE_OUTTOLUNCH "outtolunch"
-#define YAHOO_STATUS_TYPE_STEPPEDOUT "steppedout"
-#define YAHOO_STATUS_TYPE_AWAY "away"
-#define YAHOO_STATUS_TYPE_INVISIBLE "invisible"
-#define YAHOO_STATUS_TYPE_MOBILE "mobile"
-
-#define YAHOO_CLIENT_VERSION_ID "4194239"
-#define YAHOO_CLIENT_VERSION "9.0.0.2152"
-
-#define YAHOOJP_CLIENT_VERSION_ID "4194239"
-#define YAHOOJP_CLIENT_VERSION "9.0.0.2152"
-
-#define YAHOO_CLIENT_USERAGENT "Mozilla/5.0"
-
-/* 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,
-	YAHOO_STATUS_BUSY,
-	YAHOO_STATUS_NOTATHOME,
-	YAHOO_STATUS_NOTATDESK,
-	YAHOO_STATUS_NOTINOFFICE,
-	YAHOO_STATUS_ONPHONE,
-	YAHOO_STATUS_ONVACATION,
-	YAHOO_STATUS_OUTTOLUNCH,
-	YAHOO_STATUS_STEPPEDOUT,
-	YAHOO_STATUS_INVISIBLE = 12,
-	YAHOO_STATUS_CUSTOM = 99,
-	YAHOO_STATUS_IDLE = 999,
-	YAHOO_STATUS_WEBLOGIN = 0x5a55aa55,
-	YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */
-	YAHOO_STATUS_TYPING = 0x16,
-	YAHOO_STATUS_DISCONNECTED = 0xffffffff /* in ymsg 15. doesnt mean the normal sense of 'disconnected' */
-};
-
-struct yahoo_buddy_icon_upload_data {
-	PurpleConnection *gc;
-	GString *str;
-	char *filename;
-	int pos;
-	int fd;
-	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 {
-	PurpleConnection *gc;
-	int fd;
-	guchar *rxqueue;
-	int rxlen;
-	PurpleCircBuffer *txbuf;
-	guint txhandler;
-	GHashTable *friends;
-
-	char **profiles;  /* Multiple profiles can be associated with an account */
-
-	/**
-	 * This is used to keep track of the IMVironment chosen
-	 * by people you talk to.  We don't do very much with
-	 * this right now... but at least now if the remote user
-	 * selects an IMVironment we won't reset it back to the
-	 * default of nothing.
-	 */
-	GHashTable *imvironments;
-
-	int current_status;
-	gboolean logged_in;
-	GString *tmp_serv_blist, *tmp_serv_ilist, *tmp_serv_plist;
-	GSList *confs;
-	unsigned int conf_id; /* just a counter */
-	gboolean chat_online;
-	gboolean in_chat;
-	char *chat_name;
-	char *pending_chat_room;
-	char *pending_chat_id;
-	char *pending_chat_topic;
-	char *pending_chat_goto;
-	char *auth;
-	gsize auth_written;
-	char *cookie_y;
-	char *cookie_t;
-	int session_id;
-	gboolean jp;
-	gboolean wm; /* connected w/ web messenger method */
-	/* picture aka buddy icon stuff */
-	char *picture_url;
-	int picture_checksum;
-
-	/* ew. we have to check the icon before we connect,
-	 * but can't upload it til we're connected. */
-	struct yahoo_buddy_icon_upload_data *picture_upload_todo;
-	PurpleProxyConnectData *buddy_icon_connect_data;
-
-	struct _YchtConn *ycht;
-
-	/**
-	 * This linked list contains PurpleUtilFetchUrlData structs
-	 * 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 */
-
-	/**
-	 * We may receive a list15 in multiple packets with no prior warning as to how many we'll be getting;
-	 * the server expects us to keep track of the group for which it is sending us contact names.
-	 */
-	char *current_list15_grp;
-	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)
-
-/*
- * Current Maximum Length for Instant Messages
- *
- * This was found by experiment.
- *
- * The YMSG protocol allows a message of up to 948 bytes, but the official client
- * limits to 800 characters.  According to experiments I conducted, it seems that
- * the discrepancy is to allow some leeway for messages with mixed single- and
- * multi-byte characters, as I was able to send messages of 840 and 932 bytes
- * by using some multibyte characters (some random Chinese or Japanese characters,
- * to be precise). - rekkanoryo
- */
-#define YAHOO_MAX_MESSAGE_LENGTH_BYTES 948
-#define YAHOO_MAX_MESSAGE_LENGTH_CHARS 800
-
-/* sometimes i wish prpls could #include things from other prpls. then i could just
- * use the routines from libfaim and not have to admit to knowing how they work. */
-#define yahoo_put16(buf, data) ( \
-		(*(buf) = (unsigned char)((data)>>8)&0xff), \
-		(*((buf)+1) = (unsigned char)(data)&0xff),  \
-		2)
-#define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
-#define yahoo_put32(buf, data) ( \
-		(*((buf)) = (unsigned char)((data)>>24)&0xff), \
-		(*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
-		(*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
-		(*((buf)+3) = (unsigned char)(data)&0xff), \
-		4)
-#define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \
-		(((*((buf)+1))<<16)&0x00ff0000) + \
-		(((*((buf)+2))<< 8)&0x0000ff00) + \
-		(((*((buf)+3)    )&0x000000ff)))
-
-/* util.c */
-void yahoo_init_colorht(void);
-void yahoo_dest_colorht(void);
-char *yahoo_codes_to_html(const char *x);
-char *yahoo_html_to_codes(const char *src);
-
-gboolean
-yahoo_account_use_http_proxy(PurpleConnection *conn);
-
-/**
- * Encode some text to send to the yahoo server.
- *
- * @param gc The connection handle.
- * @param str The null terminated utf8 string to encode.
- * @param utf8 If not @c NULL, whether utf8 is okay or not.
- *             Even if it is okay, we may not use it. If we
- *             used it, we set this to @c TRUE, else to
- *             @c FALSE. If @c NULL, false is assumed, and
- *             it is not dereferenced.
- * @return The g_malloced string in the appropriate encoding.
- */
-char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8);
-
-/**
- * Decode some text received from the server.
- *
- * @param gc The gc handle.
- * @param str The null terminated string to decode.
- * @param utf8 Did the server tell us it was supposed to be utf8?
- * @return The decoded, utf-8 string, which must be g_free()'d.
- */
-char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8);
-
-char *yahoo_convert_to_numeric(const char *str);
-
-/* previously-static functions, now needed for yahoo_profile.c */
-void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
-
-/* yahoo_profile.c */
-void yahoo_get_info(PurpleConnection *gc, const char *name);
-
-/* needed for xfer, thought theyd be useful for other enhancements later on
-   Returns list of cookies stored in yahoo_data formatted as a single null terminated string
-   returned value must be g_freed
-*/
-gchar* yahoo_get_cookies(PurpleConnection *gc);
-
-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_aliases.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Mon Jul 06 09:08:17 2009 +0000
@@ -30,7 +30,7 @@
 #include "debug.h"
 #include "util.h"
 #include "version.h"
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_aliases.h"
 #include "yahoo_friend.h"
 #include "yahoo_packet.h"
--- a/libpurple/protocols/yahoo/yahoo_aliases.h	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_aliases.h	Mon Jul 06 09:08:17 2009 +0000
@@ -30,7 +30,7 @@
 #include "debug.h"
 #include "util.h"
 #include "version.h"
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_packet.h"
 
 void yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias);
--- a/libpurple/protocols/yahoo/yahoo_doodle.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_doodle.c	Mon Jul 06 09:08:17 2009 +0000
@@ -41,7 +41,7 @@
 #include "util.h"
 #include "version.h"
 
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_packet.h"
 #include "yahoo_friend.h"
 #include "yahoochat.h"
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Mon Jul 06 09:08:17 2009 +0000
@@ -30,7 +30,7 @@
 #include "notify.h"
 #include "proxy.h"
 #include "ft.h"
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_packet.h"
 #include "yahoo_filexfer.h"
 #include "yahoo_doodle.h"
--- a/libpurple/protocols/yahoo/yahoo_friend.h	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_friend.h	Mon Jul 06 09:08:17 2009 +0000
@@ -25,7 +25,7 @@
 #ifndef _YAHOO_FRIEND_H_
 #define _YAHOO_FRIEND_H_
 
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_packet.h"
 
 typedef enum {
--- a/libpurple/protocols/yahoo/yahoo_packet.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c	Mon Jul 06 09:08:17 2009 +0000
@@ -24,7 +24,7 @@
 #include "internal.h"
 #include "debug.h"
 
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_packet.h"
 
 struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, enum yahoo_status status, int id)
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Mon Jul 06 09:08:17 2009 +0000
@@ -32,7 +32,7 @@
 #include "proxy.h"
 #include "util.h"
 
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_packet.h"
 #include "yahoo_friend.h"
 #include "yahoo_picture.h"
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Mon Jul 06 09:08:17 2009 +0000
@@ -31,7 +31,7 @@
 #include "imgstore.h"
 #endif /* PHOTO_SUPPORT */
 
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_friend.h"
 
 typedef struct {
--- a/libpurple/protocols/yahoo/yahoochat.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoochat.c	Mon Jul 06 09:08:17 2009 +0000
@@ -40,7 +40,7 @@
 #include "notify.h"
 #include "util.h"
 
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_packet.h"
 #include "yahoochat.h"
 #include "ycht.h"
--- a/libpurple/protocols/yahoo/ycht.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/libpurple/protocols/yahoo/ycht.c	Mon Jul 06 09:08:17 2009 +0000
@@ -36,7 +36,7 @@
 #include "conversation.h"
 #include "util.h"
 
-#include "yahoo.h"
+#include "libymsg.h"
 #include "yahoo_packet.h"
 #include "ycht.h"
 #include "yahoochat.h"
--- a/pidgin/gtksmiley.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/pidgin/gtksmiley.c	Mon Jul 06 09:08:17 2009 +0000
@@ -74,6 +74,7 @@
 static void
 pidgin_smiley_destroy(PidginSmiley *smiley)
 {
+	g_object_set_data(G_OBJECT(smiley->smiley), "edit-dialog", NULL);
 	gtk_widget_destroy(smiley->parent);
 	g_free(smiley->filename);
 	if (smiley->custom_pixbuf)
@@ -402,6 +403,7 @@
 			smiley ? GTK_STOCK_SAVE : GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
 			NULL);
 	s->parent = window;
+	g_object_set_data(G_OBJECT(smiley), "edit-dialog", window);
 
 	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
 
@@ -650,8 +652,12 @@
 smiley_edit_iter(SmileyManager *dialog, GtkTreeIter *iter)
 {
 	PurpleSmiley *smiley = NULL;
+	GtkWidget *window = NULL;
 	gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), iter, SMILEY, &smiley, -1);
-	pidgin_smiley_edit(gtk_widget_get_toplevel(GTK_WIDGET(dialog->treeview)), smiley);
+	if ((window = g_object_get_data(G_OBJECT(smiley), "edit-dialog")) != NULL)
+		gtk_window_present(GTK_WINDOW(window));
+	else
+		pidgin_smiley_edit(gtk_widget_get_toplevel(GTK_WIDGET(dialog->treeview)), smiley);
 	g_object_unref(G_OBJECT(smiley));
 }
 
--- a/pidgin/plugins/disco/xmppdisco.c	Mon Jul 06 09:07:42 2009 +0000
+++ b/pidgin/plugins/disco/xmppdisco.c	Mon Jul 06 09:08:17 2009 +0000
@@ -500,6 +500,10 @@
 			++list->fetch_count;
 			pidgin_disco_list_ref(list);
 		}
+		else {
+			pidgin_disco_list_set_in_progress(list, FALSE);
+			g_free(cb_data);
+		}
 	}
 	else {
 		error = xmlnode_get_child(iq, "error");