changeset 23840:3da0957e7821

propagate from branch 'im.pidgin.pidgin' (head 868098fbe53290a8abcc3307c6fec2c6054a5e00) to branch 'im.pidgin.soc.2008.vv' (head eb4a2834050e39f5387e97121b534adb7ffd1234)
author Mike Ruprecht <maiku@soc.pidgin.im>
date Thu, 26 Jun 2008 20:25:38 +0000
parents eac0561dfd55 (diff) c17d26b6f236 (current diff)
children b4696e229c6d
files COPYRIGHT configure.ac finch/gntft.c finch/gntlog.c libpurple/Makefile.am libpurple/protocols/bonjour/bonjour.c libpurple/protocols/gg/gg.c libpurple/protocols/jabber/buddy.c libpurple/protocols/jabber/buddy.h libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/libxmpp.c libpurple/protocols/jabber/presence.c libpurple/protocols/msn/msn.c libpurple/protocols/myspace/myspace.c libpurple/protocols/qq/Makefile.am libpurple/protocols/qq/qq.c libpurple/protocols/qq/qq_proxy.c libpurple/protocols/qq/qq_proxy.h libpurple/protocols/qq/recv_core.c libpurple/protocols/qq/recv_core.h libpurple/protocols/qq/send_core.c libpurple/protocols/qq/send_core.h libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sendqueue.h libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/qq/udp_proxy_s5.h libpurple/protocols/simple/simple.c libpurple/protocols/yahoo/yahoo.c libpurple/prpl.h libpurple/server.c pidgin/gtkconv.c pidgin/gtkimhtml.h pidgin/gtkimhtmltoolbar.c pidgin/pidginstock.c pidgin/pidginstock.h pidgin/pixmaps/Makefile.am
diffstat 220 files changed, 7697 insertions(+), 5792 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Thu Jun 26 19:57:37 2008 +0000
+++ b/COPYRIGHT	Thu Jun 26 20:25:38 2008 +0000
@@ -178,6 +178,7 @@
 hjheins
 Hil
 Casey Ho
+Andrew Hoffman
 Iain Holmes
 Joshua Honeycutt
 Nigel Horne
@@ -388,6 +389,7 @@
 Richard Stellingwerff
 Charlie Stockman
 David Stoddard
+Adam Strzelecki
 Andreas Stührk
 Oleg Sukhodolsky
 Sun Microsystems
--- a/ChangeLog	Thu Jun 26 19:57:37 2008 +0000
+++ b/ChangeLog	Thu Jun 26 20:25:38 2008 +0000
@@ -5,19 +5,36 @@
 	* Ability to create custom smileys (currently only the MSN protocol
 	  utilizes the feature). (Thanks to Mauro Sérgio Ferreira Brasil,
 	  Marcus Lundblad, Jorge Villaseñor and other contributors)
-	* Yahoo! Japan now uses UTF-8, matching the behavior of official clients
-	  and restoring compatibility with the web messenger (Yusuke Odate)
+	* Add a configure option, --with-system-ssl-certs to allow packagers
+	  to specify a system-wide SSL CA certificates directory.  When set,
+	  we don't install our SSL CA certs, so it's important that the
+	  libpurple package depend on the CA certificates.
 
 	Pidgin:
-	* Custom buddy icons can now be added and removed to buddy list
+	* Custom buddy icons can now be added to and removed from buddy list
 	  entries via the buddy list entry right-click menu.
 	* Resize large incoming custom smileys to a maximum of 96px on either
 	  side.
+	* Offer to add new buddies into the same contact as existing buddies
+          in the same group if the alias given is the same.
 
 	General:
 	* Group and Chat buddy list entries can now be given custom buddy
 	  icons.
 
+	Finch:
+	* Added "Invite..." menu to chats.
+	* Added "View All Logs" menu in the buddylist to display a list of all IM
+	  logs.
+	* Added '/msgcolor' command to change colors of different classes of
+	  messages in a conversation. See '/help msgcolor' for details.
+
+version 2.4.3 (??/??/2008):
+	libpurple:
+	* Yahoo! Japan now uses UTF-8, matching the behavior of official clients
+	  and restoring compatibility with the web messenger (Yusuke Odate)
+	* Setting your buddy icon once again works for Yahoo! accounts.
+
 version 2.4.2 (05/17/2008):
 	libpurple:
 	* In MySpaceIM, messages from spambots are discarded (Justin Williams)
--- a/ChangeLog.API	Thu Jun 26 19:57:37 2008 +0000
+++ b/ChangeLog.API	Thu Jun 26 20:25:38 2008 +0000
@@ -36,7 +36,7 @@
 		* GTK_IMHTML_CUSTOM_SMILEY flag for GtkIMHtml.
 		* GTK+ Custom Smiley API.
 
-version 2.4.2:
+version 2.4.2 (05/17/2008):
 	perl:
 		Added:
 		* Purple::Prefs::get_children_names.
--- a/configure.ac	Thu Jun 26 19:57:37 2008 +0000
+++ b/configure.ac	Thu Jun 26 20:25:38 2008 +0000
@@ -1261,8 +1261,8 @@
 dnl # Check for D-Bus libraries
 dnl #######################################################################
 
-AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--enable-dbus], [enable D-Bus support])], , enable_dbus=yes)
-AC_ARG_ENABLE(nm, [AC_HELP_STRING([--enable-nm], [enable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes)
+AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes)
+AC_ARG_ENABLE(nm, [AC_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes)
 
 if test "x$enable_dbus" = "xyes" ; then
 	AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no)
@@ -1615,6 +1615,18 @@
 dnl # Thanks go to Evolution for the checks.
 dnl #######################################################################
 
+AC_ARG_WITH(with-system-ssl-certs, [AC_HELP_STRING([--with-system-ssl-certs=<dir>], [directory containing system-wide SSL CA certificates])])
+
+SSL_CERTIFICATES_DIR=""
+if ! test -z "$with_system_ssl_certs" ; then
+	if ! test -d "$with_system_ssl_certs" ; then
+		AC_MSG_ERROR([$with_system_ssl_certs does not exist, if this is the correct location please make sure that it exists.])
+	fi
+	SSL_CERTIFICATES_DIR="$with_system_ssl_certs"
+fi
+AC_SUBST(SSL_CERTIFICATES_DIR)
+AM_CONDITIONAL(INSTALL_SSL_CERTIFICATES, test "x$SSL_CERTIFICATES_DIR" = "x")
+
 dnl These two are inverses of each other <-- stolen from evolution!
 
 AC_ARG_ENABLE(gnutls,
@@ -2265,9 +2277,11 @@
 AC_CHECK_HEADERS(termios.h)
 
 # sys/sysctl.h on OpenBSD 4.2 requires sys/param.h
+# sys/sysctl.h on FreeBSD requires sys/types.h
 AC_CHECK_HEADERS(sys/param.h)
 AC_CHECK_HEADERS(sys/sysctl.h, [], [],
 	[[
+		#include <sys/types.h>
 		#ifdef HAVE_PARAM_H
 		# include <sys/param.h>
 		#endif
@@ -2464,6 +2478,9 @@
 fi
 echo Build with NetworkManager..... : $enable_nm
 echo SSL Library/Libraries......... : $msg_ssl
+if test "x$SSL_CERTIFICATES_DIR" != "x" ; then
+	eval eval echo SSL CA certificates directory. : $SSL_CERTIFICATES_DIR
+fi
 echo Build with Cyrus SASL support. : $enable_cyrus_sasl
 echo Use kerberos 4 with zephyr.... : $kerberos
 echo Use external libzephyr........ : $zephyr
--- a/doc/gtkrc-2.0	Thu Jun 26 19:57:37 2008 +0000
+++ b/doc/gtkrc-2.0	Thu Jun 26 20:25:38 2008 +0000
@@ -45,7 +45,7 @@
   # Change the color of the typing notification
   GtkIMHtml::typing-notification-color = "#ff0000"
   # Disable the typing notification
-  GtkIMHtml::typing-notification-enable = 1
+  GtkIMHtml::typing-notification-enable = 0
 
   # The following settings will change the behaviour in all GTK+ applications
   # Change the cursor color
--- a/finch/gntblist.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/gntblist.c	Thu Jun 26 20:25:38 2008 +0000
@@ -2722,6 +2722,7 @@
 	PurpleConnection *gc;
 	PurpleChat *chat;
 	GHashTable *hash = NULL;
+	PurpleConversation *conv;
 
 	account = purple_request_fields_get_account(fields, "account");
 	name = purple_request_fields_get_string(fields,  "chat");
@@ -2730,7 +2731,16 @@
 		return;
 
 	gc = purple_account_get_connection(account);
-	purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name);
+	/* Create a new conversation now. This will give focus to the new window.
+	 * But it's necessary to pretend that we left the chat, because otherwise
+	 * a new conversation window will pop up when we finally join the chat. */
+	if (!(conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, account))) {
+		conv = purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name);
+		purple_conv_chat_left(PURPLE_CONV_CHAT(conv));
+	} else {
+		purple_conversation_present(conv);
+	}
+
 	chat = purple_blist_find_chat(account, name);
 	if (chat == NULL) {
 		PurplePluginProtocolInfo *info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
@@ -2841,6 +2851,12 @@
 }
 
 static void
+view_all_logs_cb(GntMenuItem *item, gpointer n)
+{
+	finch_log_show(PURPLE_LOG_IM, NULL, NULL);
+}
+
+static void
 menu_add_buddy_cb(GntMenuItem *item, gpointer null)
 {
 	purple_blist_request_add_buddy(NULL, NULL, NULL, NULL);
@@ -2905,6 +2921,11 @@
 	gnt_menu_add_item(GNT_MENU(sub), item);
 	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), view_log_cb, NULL);
 
+	item = gnt_menuitem_new(_("View All Logs"));
+	gnt_menuitem_set_id(GNT_MENU_ITEM(item), "view-all-logs");
+	gnt_menu_add_item(GNT_MENU(sub), item);
+	gnt_menuitem_set_callback(GNT_MENU_ITEM(item), view_all_logs_cb, NULL);
+
 	item = gnt_menuitem_new(_("Show"));
 	gnt_menu_add_item(GNT_MENU(sub), item);
 	subsub = gnt_menu_new(GNT_MENU_POPUP);
@@ -3012,9 +3033,6 @@
 	gnt_widget_set_position(ggblist->window, purple_prefs_get_int(PREF_ROOT "/position/x"),
 			purple_prefs_get_int(PREF_ROOT "/position/y"));
 
-	gnt_tree_set_col_width(GNT_TREE(ggblist->tree), 0,
-			purple_prefs_get_int(PREF_ROOT "/size/width") - 1);
-
 	gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree);
 
 	ggblist->status = gnt_combo_box_new();
--- a/finch/gntblist.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/gntblist.h	Thu Jun 26 20:25:38 2008 +0000
@@ -34,6 +34,10 @@
  **********************************************************************/
 /*@{*/
 
+/**
+ * Buddylist manager for finch. This decides the visility, ordering and hierarchy
+ * of the buddylist nodes. This also manages the creation of tooltips.
+ */
 typedef struct
 {
 	const char *id;                                    /**< An identifier for the manager. */
--- a/finch/gntconv.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/gntconv.c	Thu Jun 26 20:25:38 2008 +0000
@@ -38,10 +38,11 @@
 #include "gntdebug.h"
 #include "gntlog.h"
 #include "gntplugin.h"
+#include "gntpounce.h"
 #include "gntprefs.h"
+#include "gntrequest.h"
 #include "gntsound.h"
 #include "gntstatus.h"
-#include "gntpounce.h"
 
 #include "gnt.h"
 #include "gntbox.h"
@@ -140,7 +141,7 @@
 entry_key_pressed(GntWidget *w, FinchConv *ggconv)
 {
 	const char *text = gnt_entry_get_text(GNT_ENTRY(ggconv->entry));
-	if (*text == '/')
+	if (*text == '/' && *(text + 1) != '/')
 	{
 		PurpleConversation *conv = ggconv->active_conv;
 		PurpleCmdStatus status;
@@ -190,7 +191,7 @@
 	}
 	else
 	{
-		char *escape = g_markup_escape_text(text, -1);
+		char *escape = g_markup_escape_text((*text == '/' ? text + 1 : text), -1);
 		char *apos = purple_strreplace(escape, "&apos;", "'");
 		g_free(escape);
 		escape = apos;
@@ -557,6 +558,47 @@
 }
 
 static void
+invite_select_cb(FinchConv *fc, PurpleRequestFields *fields)
+{
+	PurpleConversation *conv = fc->active_conv;
+	const char *buddy = purple_request_fields_get_string(fields,  "screenname");
+	const char *message = purple_request_fields_get_string(fields,  "message");
+	serv_chat_invite(purple_conversation_get_gc(conv),
+		purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
+		message, buddy);
+
+}
+
+static void
+invite_cb(GntMenuItem *item, gpointer ggconv)
+{
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+
+	fields = purple_request_fields_new();
+
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+
+	field = purple_request_field_string_new("screenname", _("Name"), NULL, FALSE);
+	purple_request_field_set_type_hint(field, "screenname");
+	purple_request_field_set_required(field, TRUE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("message", _("Invite message"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+	purple_request_fields(finch_conv_get_handle(), _("Invite"),
+						NULL,
+						_("Please enter the name of the user "
+						  "you wish to invite,\nalong with an optional invite message."),
+						fields,
+						_("OK"), G_CALLBACK(invite_select_cb),
+						_("Cancel"), NULL,
+						NULL, NULL, NULL,
+						ggconv);
+}
+
+static void
 gg_create_menu(FinchConv *ggc)
 {
 	GntWidget *menu, *sub;
@@ -606,6 +648,10 @@
 		}
 
 		generate_send_to_menu(ggc);
+	} else if (purple_conversation_get_type(ggc->active_conv) == PURPLE_CONV_TYPE_CHAT) {
+		item = gnt_menuitem_new(_("Invite..."));
+		gnt_menu_add_item(GNT_MENU(sub), item);
+		gnt_menuitem_set_callback(item, invite_cb, ggc);
 	}
 
 	item = gnt_menuitem_new(_("View Log..."));
@@ -1195,6 +1241,47 @@
 }
 
 static PurpleCmdRet
+cmd_message_color(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
+{
+	int *msgclass  = NULL;
+	int fg, bg;
+
+	if (strcmp(args[0], "receive") == 0)
+		msgclass = &color_message_receive;
+	else if (strcmp(args[0], "send") == 0)
+		msgclass = &color_message_send;
+	else if (strcmp(args[0], "highlight") == 0)
+		msgclass = &color_message_highlight;
+	else if (strcmp(args[0], "action") == 0)
+		msgclass = &color_message_action;
+	else if (strcmp(args[0], "timestamp") == 0)
+		msgclass = &color_timestamp;
+	else {
+		if (error)
+			*error = g_strdup_printf(_("%s is not a valid message class. See '/help msgcolor' for valid message classes."), args[0]);
+		return PURPLE_CMD_STATUS_FAILED;
+	}
+
+	fg = gnt_colors_get_color(args[1]);
+	if (fg == -EINVAL) {
+		if (error)
+			*error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[1]);
+		return PURPLE_CMD_STATUS_FAILED;
+	}
+
+	bg = gnt_colors_get_color(args[2]);
+	if (bg == -EINVAL) {
+		if (error)
+			*error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[2]);
+		return PURPLE_CMD_STATUS_FAILED;
+	}
+
+	init_pair(*msgclass, fg, bg);
+
+	return PURPLE_CMD_STATUS_OK;
+}
+
+static PurpleCmdRet
 users_command_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
 {
 	FinchConv *fc = FINCH_GET_DATA(conv);
@@ -1278,6 +1365,16 @@
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
 	                  cmd_show_window, _("statuses: Show the savedstatuses window."), finch_savedstatus_show_all);
 
+	/* Allow customizing the message colors using a command during run-time */
+	purple_cmd_register("msgcolor", "www", PURPLE_CMD_P_DEFAULT,
+			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
+			cmd_message_color, _("msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: "
+				                 "Set the color for different classes of messages in the conversation window.<br>"
+				                 "    &lt;class&gt;: receive, send, highlight, action, timestamp<br>"
+				                 "    &lt;foreground/background&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>"
+								 "EXAMPLE:<br>    msgcolor send cyan default"),
+			NULL);
+
 	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing", finch_conv_get_handle(),
 					PURPLE_CALLBACK(update_buddy_typing), NULL);
 	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped", finch_conv_get_handle(),
--- a/finch/gntft.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/gntft.c	Thu Jun 26 20:25:38 2008 +0000
@@ -117,7 +117,9 @@
 			total_pct = 100 * total_bytes_xferred / total_file_size;
 		}
 
-		title = g_strdup_printf(_("File Transfers - %d%% of %d files"),
+		title = g_strdup_printf(ngettext("File Transfers - %d%% of %d file",
+						 "File Transfers - %d%% of %d files",
+						 num_active_xfers),
 				total_pct, num_active_xfers);
 		gnt_screen_rename_widget((xfer_dialog->window), title);
 		g_free(title);
--- a/finch/gntlog.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/gntlog.c	Thu Jun 26 20:25:38 2008 +0000
@@ -61,8 +61,12 @@
 	if (viewer->contact != NULL)
 		return g_direct_hash(viewer->contact);
 
-	return g_str_hash(viewer->screenname) +
-		g_str_hash(purple_account_get_username(viewer->account));
+	if (viewer->account) {
+		return g_str_hash(viewer->screenname) +
+			g_str_hash(purple_account_get_username(viewer->account));
+	}
+
+	return (guint)viewer;
 }
 
 static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
@@ -84,10 +88,14 @@
 			return FALSE;
 	}
 
-	normal = g_strdup(purple_normalize(a->account, a->screenname));
-	ret = (a->account == b->account) &&
-		!strcmp(normal, purple_normalize(b->account, b->screenname));
-	g_free(normal);
+	if (a->screenname && b->screenname) {
+		normal = g_strdup(purple_normalize(a->account, a->screenname));
+		ret = (a->account == b->account) &&
+			!strcmp(normal, purple_normalize(b->account, b->screenname));
+		g_free(normal);
+	} else {
+		ret = (a == b);
+	}
 
 	return ret;
 }
@@ -348,14 +356,28 @@
 	return lv;
 }
 
-void finch_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account) {
+static void
+our_logging_blows(PurpleLogSet *set, PurpleLogSet *setagain, GList **list)
+{
+	/* The iteration happens on the first list. So we use the shorter list in front */
+	if (set->type != PURPLE_LOG_IM)
+		return;
+	*list = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, set->name, set->account), *list);
+}
+
+void finch_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account)
+{
 	struct log_viewer_hash_t *ht;
 	FinchLogViewer *lv = NULL;
 	const char *name = screenname;
 	char *title;
+	GList *logs = NULL;
+	int size = 0;
 
-	g_return_if_fail(account != NULL);
-	g_return_if_fail(screenname != NULL);
+	if (type != PURPLE_LOG_IM) {
+		g_return_if_fail(account != NULL);
+		g_return_if_fail(screenname != NULL);
+	}
 
 	ht = g_new0(struct log_viewer_hash_t, 1);
 
@@ -383,20 +405,35 @@
 	} else {
 		PurpleBuddy *buddy;
 
-		buddy = purple_find_buddy(account, screenname);
-		if (buddy != NULL)
-			name = purple_buddy_get_contact_alias(buddy);
-
-		title = g_strdup_printf(_("Conversations with %s"), name);
+		if (screenname) {
+			buddy = purple_find_buddy(account, screenname);
+			if (buddy != NULL)
+				name = purple_buddy_get_contact_alias(buddy);
+			title = g_strdup_printf(_("Conversations with %s"), name);
+		} else {
+			title = g_strdup(_("All Conversations"));
+		}
 	}
 
-	display_log_viewer(ht, purple_log_get_logs(type, screenname, account),
-			title, purple_log_get_total_size(type, screenname, account));
+	if (screenname) {
+		logs = purple_log_get_logs(type, screenname, account);
+		size = purple_log_get_total_size(type, screenname, account);
+	} else {
+		/* This will happen only for IMs */
+		GHashTable *table = purple_log_get_log_sets();
+		g_hash_table_foreach(table, (GHFunc)our_logging_blows, &logs);
+		g_hash_table_destroy(table);
+		logs = g_list_sort(logs, purple_log_compare);
+		size = 0;
+	}
+
+	display_log_viewer(ht, logs, title, size);
 
 	g_free(title);
 }
 
-void finch_log_show_contact(PurpleContact *contact) {
+void finch_log_show_contact(PurpleContact *contact)
+{
 	struct log_viewer_hash_t *ht;
 	PurpleBlistNode *child;
 	FinchLogViewer *lv = NULL;
--- a/finch/libgnt/gntcolors.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/libgnt/gntcolors.c	Thu Jun 26 20:25:38 2008 +0000
@@ -29,6 +29,7 @@
 
 #include <glib.h>
 
+#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -168,7 +169,7 @@
 		color = -1;
 	else {
 		g_warning("Invalid color name: %s\n", key);
-		color = -1;
+		color = -EINVAL;
 	}
 	return color;
 }
--- a/finch/libgnt/gntcolors.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/libgnt/gntcolors.h	Thu Jun 26 20:25:38 2008 +0000
@@ -91,7 +91,7 @@
  *
  * @param kfile The string value
  *
- * @return A color
+ * @return A color. For an unknown color name, returns -EINVAL.
  *
  * @since 2.4.0
  */
--- a/finch/libgnt/gntentry.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/libgnt/gntentry.c	Thu Jun 26 20:25:38 2008 +0000
@@ -580,11 +580,13 @@
 	while (text && text < end && g_unichar_isspace(g_utf8_get_char(text)))
 		text = g_utf8_find_next_char(text, end);
 
-	ch = g_utf8_get_char(text);
-	while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) {
-		gunichar cur = g_utf8_get_char(text);
-		if (!SAME(ch, cur))
-			break;
+	if (text) {
+		ch = g_utf8_get_char(text);
+		while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) {
+			gunichar cur = g_utf8_get_char(text);
+			if (!SAME(ch, cur))
+				break;
+		}
 	}
 	return (text ? text : end);
 }
--- a/finch/libgnt/gntmenu.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/libgnt/gntmenu.c	Thu Jun 26 20:25:38 2008 +0000
@@ -283,6 +283,8 @@
 		do sub = sub->submenu; while (sub->submenu);
 		if (gnt_widget_key_pressed(GNT_WIDGET(sub), text))
 			return TRUE;
+		if (menu->type != GNT_MENU_TOPLEVEL)
+			return FALSE;
 	}
 
 	if ((text[0] == 27 && text[1] == 0) ||
@@ -332,10 +334,12 @@
 				return TRUE;
 			}
 		}
+		if (gnt_bindable_perform_action_key(GNT_BINDABLE(widget), text))
+			return TRUE;
 		return org_key_pressed(widget, text);
 	}
 
-	return FALSE;
+	return gnt_bindable_perform_action_key(GNT_BINDABLE(widget), text);
 }
 
 static void
@@ -434,7 +438,7 @@
 {
 	GntWidget *widget = GNT_WIDGET(instance);
 	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW | GNT_WIDGET_NO_BORDER |
-			GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_TRANSIENT);
+			GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_TRANSIENT | GNT_WIDGET_DISABLE_ACTIONS);
 	GNTDEBUG;
 }
 
--- a/finch/libgnt/gnttree.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/finch/libgnt/gnttree.c	Thu Jun 26 20:25:38 2008 +0000
@@ -110,13 +110,14 @@
 	gnt_widget_get_size(GNT_WIDGET(tree), &width, NULL);
 	if (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER))
 		width -= 2;
+	width -= 1;  /* Exclude the scrollbar from the calculation */
 	for (i = 0, total = 0; i < tree->ncol ; i++) {
 		if (tree->columns[i].flags & GNT_TREE_COLUMN_INVISIBLE)
 			continue;
 		if (tree->columns[i].flags & GNT_TREE_COLUMN_FIXED_SIZE)
-			width -= WIDTH(i) + 1;
+			width -= WIDTH(i) + (tree->priv->lastvisible != i);
 		else
-			total += WIDTH(i) + 1;
+			total += WIDTH(i) + (tree->priv->lastvisible != i);
 	}
 
 	if (total == 0)
--- a/libpurple/Makefile.am	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/Makefile.am	Thu Jun 26 20:25:38 2008 +0000
@@ -287,3 +287,9 @@
 	$(GSTREAMER_CFLAGS) \
 	$(GSTPROPS_CFLAGS) \
 	$(NETWORKMANAGER_CFLAGS)
+
+# INSTALL_SSL_CERTIFICATES is true when SSL_CERTIFICATES_DIR is empty.
+# We want to use SSL_CERTIFICATES_DIR when it's not empty.
+if ! INSTALL_SSL_CERTIFICATES
+AM_CPPFLAGS += -DSSL_CERTIFICATES_DIR=\"$(SSL_CERTIFICATES_DIR)\"
+endif
--- a/libpurple/blist.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/blist.h	Thu Jun 26 20:25:38 2008 +0000
@@ -31,13 +31,20 @@
 
 #include <glib.h>
 
+/** @copydoc _PurpleBuddyList */
 typedef struct _PurpleBuddyList PurpleBuddyList;
+/** @copydoc _PurpleBlistUiOps */
 typedef struct _PurpleBlistUiOps PurpleBlistUiOps;
+/** @copydoc _PurpleBlistNode */
 typedef struct _PurpleBlistNode PurpleBlistNode;
 
+/** @copydoc _PurpleChat */
 typedef struct _PurpleChat PurpleChat;
+/** @copydoc _PurpleGroup */
 typedef struct _PurpleGroup PurpleGroup;
+/** @copydoc _PurpleContact */
 typedef struct _PurpleContact PurpleContact;
+/** @copydoc _PurpleBuddy */
 typedef struct _PurpleBuddy PurpleBuddy;
 
 /**************************************************************************/
--- a/libpurple/certificate.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/certificate.c	Thu Jun 26 20:25:38 2008 +0000
@@ -745,8 +745,12 @@
 		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
 						   "ca-certs", NULL));
 #else
+# ifdef SSL_CERTIFICATES_DIR
+		x509_ca_paths = g_list_append(NULL, SSL_CERTIFICATES_DIR);
+# else
 		x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
 						   "purple", "ca-certs", NULL));
+# endif
 #endif
 	}
 
@@ -787,8 +791,7 @@
 
 	for (cur = lst; cur; cur = cur->next) {
 		x509_ca_element *el = cur->data;
-		/* TODO: Unsafe? */
-		if ( !strcmp(dn, el->dn) ) {
+		if (el->dn && !strcmp(dn, el->dn)) {
 			return el;
 		}
 	}
--- a/libpurple/cmds.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/cmds.h	Thu Jun 26 20:25:38 2008 +0000
@@ -30,6 +30,7 @@
 /**************************************************************************/
 /*@{*/
 
+/** The possible results of running a command with purple_cmd_do_command(). */
 typedef enum _PurpleCmdStatus {
 	PURPLE_CMD_STATUS_OK,
 	PURPLE_CMD_STATUS_FAILED,
@@ -39,16 +40,31 @@
 	PURPLE_CMD_STATUS_WRONG_TYPE,
 } PurpleCmdStatus;
 
+/** Commands registered with the core return one of these values when run.
+ *  Normally, a command will want to return one of the first two; in some
+ *  unusual cases, you might want to have several functions called for a
+ *  particular command; in this case, they should return
+ *  #PURPLE_CMD_RET_CONTINUE to cause the core to fall through to other
+ *  commands with the same name.
+ */
 typedef enum _PurpleCmdRet {
-	PURPLE_CMD_RET_OK,       /**< Everything's okay. Don't look for another command to call. */
+	PURPLE_CMD_RET_OK,       /**< Everything's okay; Don't look for another command to call. */
 	PURPLE_CMD_RET_FAILED,   /**< The command failed, but stop looking.*/
 	PURPLE_CMD_RET_CONTINUE, /**< Continue, looking for other commands with the same name to call. */
 } PurpleCmdRet;
 
 #define PURPLE_CMD_FUNC(func) ((PurpleCmdFunc)func)
 
+/** A function implementing a command, as passed to purple_cmd_register().
+ *
+ *  @todo document the arguments to these functions.
+ * */
 typedef PurpleCmdRet (*PurpleCmdFunc)(PurpleConversation *, const gchar *cmd,
                                   gchar **args, gchar **error, void *data);
+/** A unique integer representing a command registered with
+ *  purple_cmd_register(), which can subsequently be passed to
+ *  purple_cmd_unregister() to unregister that command.
+ */
 typedef guint PurpleCmdId;
 
 typedef enum _PurpleCmdPriority {
@@ -171,7 +187,7 @@
  *               include both the default formatting and any extra manual formatting.
  * @param errormsg If the command failed errormsg is filled in with the appropriate error
  *                 message. It must be freed by the caller with g_free().
- * @return A #PurpleCmdStatus indicated if the command succeeded or failed.
+ * @return A #PurpleCmdStatus indicating if the command succeeded or failed.
  */
 PurpleCmdStatus purple_cmd_do_command(PurpleConversation *conv, const gchar *cmdline,
                                   const gchar *markup, gchar **errormsg);
--- a/libpurple/connection.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/connection.h	Thu Jun 26 20:25:38 2008 +0000
@@ -27,6 +27,7 @@
 #ifndef _PURPLE_CONNECTION_H_
 #define _PURPLE_CONNECTION_H_
 
+/** @copydoc _PurpleConnection */
 typedef struct _PurpleConnection PurpleConnection;
 
 /**
@@ -121,7 +122,7 @@
 	 */
 	PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR = 15,
 
-	/** Some other error occured which fits into none of the other
+	/** Some other error occurred which fits into none of the other
 	 *  categories.
 	 */
 	/* purple_connection_error_reason() in connection.c uses the fact that
@@ -223,6 +224,8 @@
 	void (*_purple_reserved3)(void);
 } PurpleConnectionUiOps;
 
+
+/* Represents an active connection on an account. */
 struct _PurpleConnection
 {
 	PurplePlugin *prpl;            /**< The protocol plugin.               */
--- a/libpurple/conversation.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/conversation.c	Thu Jun 26 20:25:38 2008 +0000
@@ -98,7 +98,7 @@
 	char *displayed = NULL, *sent = NULL;
 	int err = 0;
 
-	if (strlen(message) == 0)
+	if (*message == '\0')
 		return;
 
 	account = purple_conversation_get_account(conv);
--- a/libpurple/conversation.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/conversation.h	Thu Jun 26 20:25:38 2008 +0000
@@ -32,11 +32,17 @@
 /**************************************************************************/
 
 
+/** @copydoc _PurpleConversationUiOps */
 typedef struct _PurpleConversationUiOps PurpleConversationUiOps;
+/** @copydoc _PurpleConversation */
 typedef struct _PurpleConversation      PurpleConversation;
+/** @copydoc _PurpleConvIm */
 typedef struct _PurpleConvIm            PurpleConvIm;
+/** @copydoc _PurpleConvChat */
 typedef struct _PurpleConvChat          PurpleConvChat;
+/** @copydoc _PurpleConvChatBuddy */
 typedef struct _PurpleConvChatBuddy     PurpleConvChatBuddy;
+/** @copydoc _PurpleConvMessage */
 typedef struct _PurpleConvMessage       PurpleConvMessage;
 
 /**
--- a/libpurple/core.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/core.h	Thu Jun 26 20:25:38 2008 +0000
@@ -1,4 +1,5 @@
 /**
+ * @file core.h Startup and shutdown of libpurple
  * @defgroup core libpurple
  * @see @ref core-signals
  */
@@ -28,12 +29,36 @@
 
 typedef struct PurpleCore PurpleCore;
 
+/** Callbacks that fire at different points of the initialization and teardown
+ *  of libpurple, along with a hook to return descriptive information about the
+ *  UI.
+ */
 typedef struct
 {
+	/** Called just after the preferences subsystem is initialized; the UI
+	 *  could use this callback to add some preferences it needs to be in
+	 *  place when other subsystems are initialized.
+	 */
 	void (*ui_prefs_init)(void);
-	void (*debug_ui_init)(void); /* Unfortunate necessity. */
+	/** Called just after the debug subsystem is initialized, but before
+	 *  just about every other component's initialization.  The UI should
+	 *  use this hook to call purple_debug_set_ui_ops() so that debugging
+	 *  information for other components can be logged during their
+	 *  initialization.
+	 */
+	void (*debug_ui_init)(void);
+	/** Called after all of libpurple has been initialized.  The UI should
+	 *  use this hook to set all other necessary UiOps structures.
+	 *
+	 *  @see @ref ui-ops
+	 */
 	void (*ui_init)(void);
+	/** Called after most of libpurple has been uninitialized. */
 	void (*quit)(void);
+
+	/** Called by purple_core_get_ui_info(); should return the information
+	 *  documented there.
+	 */
 	GHashTable* (*get_ui_info)(void);
 
 	void (*_purple_reserved1)(void);
@@ -64,17 +89,23 @@
 void purple_core_quit(void);
 
 /**
+ * <p>
  * Calls purple_core_quit().  This can be used as the function 
  * passed to purple_timeout_add() when you want to shutdown Purple 
  * in a specified amount of time.  When shutting down Purple 
  * from a plugin, you must use this instead of purple_core_quit();
  * for an immediate exit, use a timeout value of 0: 
- *   purple_timeout_add(0, purple_core_quitcb, NULL);
+ * </p>
+ *
+ * <code>purple_timeout_add(0, purple_core_quitcb, NULL);</code>
+ *
+ * <p>
  * This is ensures that code from your plugin is not being 
  * executed when purple_core_quit() is called.  If the plugin
  * called purple_core_quit() directly, you would get a core dump
  * after purple_core_quit() executes and control returns to your
  * plugin because purple_core_quit() frees all plugins.
+ * </p>
  */
 gboolean purple_core_quit_cb(gpointer unused);
 
@@ -86,7 +117,8 @@
 const char *purple_core_get_version(void);
 
 /**
- * Returns the ID of the UI that is using the core.
+ * Returns the ID of the UI that is using the core, as passed to
+ * purple_core_init().
  *
  * @return The ID of the UI that is currently using the core.
  */
@@ -95,7 +127,7 @@
 /**
  * Returns a handle to the purple core.
  *
- * This is used for such things as signals.
+ * This is used to connect to @ref core-signals "core signals".
  */
 PurpleCore *purple_get_core(void);
 
@@ -114,10 +146,10 @@
 PurpleCoreUiOps *purple_core_get_ui_ops(void);
 
 /**
- * Migrates from .gaim to .purple.
+ * Migrates from <tt>.gaim</tt> to <tt>.purple</tt>.
  *
- * UIs MUST NOT call this if they have been told to use a custom
- * user directory.
+ * UIs <strong>must not</strong> call this if they have been told to use a
+ * custom user directory.
  *
  * @return A boolean indicating success or migration failure. On failure,
  *         the application must display an error to the user and then exit.
@@ -125,20 +157,33 @@
 gboolean purple_core_migrate(void);
 
 /**
- * Ensures that only one instance is running.
+ * Ensures that only one instance is running.  If libpurple is built with D-Bus
+ * support, this checks if another process owns the libpurple bus name and if
+ * so whether that process is using the same configuration directory as this
+ * process.
  *
- * @return A boolean such that @c TRUE indicates that this is the first instance,
- *         whereas @c FALSE indicates that there is another instance running.
+ * @return @c TRUE if this is the first instance of libpurple running;
+ *         @c FALSE if there is another instance running.
  *
  * @since 2.1.0
  */
 gboolean purple_core_ensure_single_instance(void);
 
 /**
- * Returns a hashtable containing various information about the UI
+ * Returns a hash table containing various information about the UI.  The
+ * following well-known entries may be in the table (along with any others the
+ * UI might choose to include):
+ *
+ * <dl>
+ *   <dt><tt>name</tt></dt>
+ *   <dd>the user-readable name for the UI.</dd>
+ *
+ *   <dt><tt>version</tt></dt>
+ *   <dd>a user-readable description of the current version of the UI.</dd>
+ * </dl>
  *
  * @return A GHashTable with strings for keys and values.  This
- * hash table must not be freed.
+ * hash table must not be freed and should not be modified.
  *
  * @since 2.1.0
  *
--- a/libpurple/idle.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/idle.c	Thu Jun 26 20:25:38 2008 +0000
@@ -252,7 +252,7 @@
 	PurpleAccount *account;
 
 	account = purple_connection_get_account(gc);
-	idled_accts = g_list_remove(idled_accts, account);
+	set_account_unidle(account);
 }
 
 static void
--- a/libpurple/internal.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/internal.h	Thu Jun 26 20:25:38 2008 +0000
@@ -140,6 +140,14 @@
 #	define G_MAXUINT32 ((guint32) 0xffffffff)
 #endif
 
+#ifndef G_MAXSIZE
+#	if GLIB_SIZEOF_LONG == 8
+#		define G_MAXSIZE ((gsize) 0xffffffffffffffff)
+#	else
+#		define G_MAXSIZE ((gsize) 0xffffffff)
+#	endif
+#endif
+
 #if GLIB_CHECK_VERSION(2,6,0)
 #	include <glib/gstdio.h>
 #endif
--- a/libpurple/log.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/log.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1067,7 +1067,7 @@
 				set->normalized_name = g_strdup(purple_normalize(account, name));
 
 				/* Chat for .chat or .system at the end of the name to determine the type. */
-				if (len > 7) {
+				if (len >= 7) {
 					gchar *tmp = &name[len - 7];
 					if (!strcmp(tmp, ".system")) {
 						set->type = PURPLE_LOG_SYSTEM;
@@ -1083,7 +1083,7 @@
 				}
 
 				/* Determine if this (account, name) combination exists as a buddy. */
-				if (account != NULL)
+				if (account != NULL && name != NULL && *name != '\0')
 					set->buddy = (purple_find_buddy(account, name) != NULL);
 				else
 					set->buddy = FALSE;
--- a/libpurple/nat-pmp.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/nat-pmp.c	Thu Jun 26 20:25:38 2008 +0000
@@ -35,6 +35,10 @@
 #include "signals.h"
 #include "network.h"
 
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
 #ifdef HAVE_SYS_SYSCTL_H
 #include <sys/sysctl.h>
 #endif
--- a/libpurple/plugins/perl/perl-handlers.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/plugins/perl/perl-handlers.c	Thu Jun 26 20:25:38 2008 +0000
@@ -383,6 +383,9 @@
 				case PURPLE_TYPE_BOXED:
 					*((void **)copy_args[i]) = (void *)SvIV(sv_args[i]);
 					break;
+				case PURPLE_TYPE_SUBTYPE:
+					*((void **)copy_args[i]) = purple_perl_ref_object(sv_args[i]);
+					break;
 
 				default:
 					break;
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Thu Jun 26 20:25:38 2008 +0000
@@ -54,8 +54,8 @@
 	   If there are strange bugs, perhaps look here (yes, I am a
 	   hypocrite) */
 	gnutls_global_set_mem_functions(
-		(gnutls_alloc_function)   g_malloc0, /* malloc */
-		(gnutls_alloc_function)   g_malloc0, /* secure malloc */
+		(gnutls_alloc_function)   g_malloc, /* malloc */
+		(gnutls_alloc_function)   g_malloc, /* secure malloc */
 		NULL,      /* mem_is_secure */
 		(gnutls_realloc_function) g_realloc, /* realloc */
 		(gnutls_free_function)    g_free     /* free */
--- a/libpurple/privacy.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/privacy.c	Thu Jun 26 20:25:38 2008 +0000
@@ -241,6 +241,7 @@
 						gboolean restore)
 {
 	GSList *list;
+	PurplePrivacyType type = account->perm_deny;
 
 	switch (account->perm_deny) {
 		case PURPLE_PRIVACY_ALLOW_ALL:
@@ -254,10 +255,12 @@
 		case PURPLE_PRIVACY_DENY_ALL:
 			if (!restore) {
 				/* Empty the allow-list. */
+				const char *norm = purple_normalize(account, who);
 				for (list = account->permit; list != NULL;) {
-					char *who = list->data;
+					char *person = list->data;
 					list = list->next;
-					purple_privacy_permit_remove(account, who, local);
+					if (strcmp(norm, person) != 0)
+						purple_privacy_permit_remove(account, person, local);
 				}
 			}
 			purple_privacy_permit_add(account, who, local);
@@ -273,6 +276,10 @@
 		default:
 			g_return_if_reached();
 	}
+
+	/* Notify the server if the privacy setting was changed */
+	if (type != account->perm_deny && purple_account_is_connected(account))
+		serv_set_permit_deny(purple_account_get_connection(account));
 }
 
 /*
@@ -286,15 +293,18 @@
 					gboolean restore)
 {
 	GSList *list;
+	PurplePrivacyType type = account->perm_deny;
 
 	switch (account->perm_deny) {
 		case PURPLE_PRIVACY_ALLOW_ALL:
 			if (!restore) {
 				/* Empty the deny-list. */
+				const char *norm = purple_normalize(account, who);
 				for (list = account->deny; list != NULL; ) {
 					char *person = list->data;
 					list = list->next;
-					purple_privacy_deny_remove(account, person, local);
+					if (strcmp(norm, person) != 0)
+						purple_privacy_deny_remove(account, person, local);
 				}
 			}
 			purple_privacy_deny_add(account, who, local);
@@ -318,6 +328,10 @@
 		default:
 			g_return_if_reached();
 	}
+
+	/* Notify the server if the privacy setting was changed */
+	if (type != account->perm_deny && purple_account_is_connected(account))
+		serv_set_permit_deny(purple_account_get_connection(account));
 }
 
 gboolean
--- a/libpurple/protocols/gg/gg.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/gg/gg.c	Thu Jun 26 20:25:38 2008 +0000
@@ -343,7 +343,8 @@
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
 
-	purple_request_file(action, "Load buddylist from file...", NULL, FALSE,
+	purple_request_file(action, _("Load buddylist from file..."), NULL,
+			FALSE,
 			G_CALLBACK(ggp_callback_buddylist_load_ok), NULL,
 			purple_connection_get_account(gc), NULL, NULL,
 			gc);
@@ -926,8 +927,10 @@
 /* ----- INTERNAL CALLBACKS --------------------------------------------- */
 /* ---------------------------------------------------------------------- */
 
-/* just a prototype */
+/* Prototypes */
 static void ggp_set_status(PurpleAccount *account, PurpleStatus *status);
+static int ggp_to_gg_status(PurpleStatus *status, char **msg);
+
 
 /**
  * Handle change of the status of the buddy.
@@ -1488,23 +1491,12 @@
 			break;
 		case GG_EVENT_CONN_SUCCESS:
 			{
-				PurpleAccount *account;
-				PurplePresence *presence;
-				PurpleStatus *status;
-
 				purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS\n");
 				purple_input_remove(gc->inpa);
 				gc->inpa = purple_input_add(info->session->fd,
 							  PURPLE_INPUT_READ,
 							  ggp_callback_recv, gc);
 
-				/* gg_change_status(info->session, GG_STATUS_AVAIL); */
-
-				account = purple_connection_get_account(gc);
-				presence = purple_account_get_presence(account);
-				status = purple_presence_get_active_status(presence);
-
-				ggp_set_status(account, status);
 				purple_connection_set_state(gc, PURPLE_CONNECTED);
 				ggp_buddylist_send(gc);
 			}
@@ -1692,6 +1684,8 @@
 static void ggp_login(PurpleAccount *account)
 {
 	PurpleConnection *gc;
+	PurplePresence *presence;
+	PurpleStatus *status;
 	struct gg_login_params *glp;
 	GGPInfo *info;
 
@@ -1714,8 +1708,11 @@
 	glp->uin = ggp_get_uin(account);
 	glp->password = (char *)purple_account_get_password(account);
 
+	presence = purple_account_get_presence(account);
+	status = purple_presence_get_active_status(presence);
+
 	glp->async = 1;
-	glp->status = GG_STATUS_AVAIL;
+	glp->status = ggp_to_gg_status(status, &glp->status_descr);
 	glp->tls = 0;
 
 	info->session = gg_login(glp);
@@ -1826,22 +1823,15 @@
 /* }}} */
 
 /* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
-static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
+static int ggp_to_gg_status(PurpleStatus *status, char **msg)
 {
-	PurpleConnection *gc;
-	GGPInfo *info;
-	const char *status_id, *msg;
+	const char *status_id = purple_status_get_id(status);
 	int new_status, new_status_descr;
+	const char *new_msg;
 
-	if (!purple_status_is_active(status))
-		return;
+	g_return_val_if_fail(msg == NULL, 0);
 
-	gc = purple_account_get_connection(account);
-	info = gc->proto_data;
-
-	status_id = purple_status_get_id(status);
-
-	purple_debug_info("gg", "ggp_set_status: Requested status = %s\n",
+	purple_debug_info("gg", "ggp_to_gg_status: Requested status = %s\n",
 			status_id);
 
 	if (strcmp(status_id, "available") == 0) {
@@ -1860,22 +1850,45 @@
 		new_status = GG_STATUS_AVAIL;
 		new_status_descr = GG_STATUS_AVAIL_DESCR;
 		purple_debug_info("gg",
-			"ggp_set_status: uknown status requested (status_id=%s)\n",
+			"ggp_set_status: unknown status requested (status_id=%s)\n",
 			status_id);
 	}
 
-	msg = purple_status_get_attr_string(status, "message");
+	new_msg = purple_status_get_attr_string(status, "message");
+
+	if(new_msg) {
+		char *tmp = purple_markup_strip_html(new_msg);
+		*msg = charset_convert(tmp, "UTF-8", "CP1250");
+		g_free(tmp);
+
+		return new_status_descr;
+	} else {
+		*msg = NULL;
+		return new_status;
+	}
+}
+/* }}} */
 
-	if (msg == NULL) {
+/* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
+static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
+{
+	PurpleConnection *gc;
+	GGPInfo *info;
+	int new_status;
+	char *new_msg = NULL;
+
+	if (!purple_status_is_active(status))
+		return;
+
+	gc = purple_account_get_connection(account);
+	info = gc->proto_data;
+
+	new_status = ggp_to_gg_status(status, &new_msg);
+
+	if (new_msg == NULL) {
 		gg_change_status(info->session, new_status);
 	} else {
-		gchar *tmp, *new_msg;
-
-		tmp = charset_convert(msg, "UTF-8", "CP1250");
-		new_msg = purple_markup_strip_html(tmp);
-		g_free(tmp);
-
-		gg_change_status_descr(info->session, new_status_descr, new_msg);
+		gg_change_status_descr(info->session, new_status, new_msg);
 		g_free(new_msg);
 	}
 
--- a/libpurple/protocols/gg/search.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/gg/search.h	Thu Jun 26 20:25:38 2008 +0000
@@ -130,7 +130,7 @@
  * @param gc   PurpleConnection.
  * @param form Filled in GGPSearchForm.
  *
- * @return Sequence number of a search or 0 if an error occured.
+ * @return Sequence number of a search or 0 if an error occurred.
  */
 guint32
 ggp_search_start(PurpleConnection *gc, GGPSearchForm *form);
--- a/libpurple/protocols/irc/msgs.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/irc/msgs.c	Thu Jun 26 20:25:38 2008 +0000
@@ -122,7 +122,11 @@
 
 void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
-	purple_debug(PURPLE_DEBUG_INFO, "irc", "Unrecognized message: %s\n", args[0]);
+	char *clean;
+        /* This, too, should be escaped somehow (smarter) */
+        clean = purple_utf8_salvage(args[0]);
+	purple_debug(PURPLE_DEBUG_INFO, "irc", "Unrecognized message: %s\n", clean);
+        g_free(clean);
 }
 
 void irc_msg_features(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -211,7 +215,9 @@
 			/* This is an extended syntax, not in RFC 1459 */
 			int t1 = atoi(args[4]);
 			time_t t2 = time(NULL);
-			msg = g_strdup_printf(_("Ban on %s by %s, set %ld seconds ago"),
+			msg = g_strdup_printf(ngettext("Ban on %s by %s, set %ld second ago",
+						       "Ban on %s by %s, set %ld seconds ago",
+						       t2 - t1),
 			                      args[2], args[3], t2 - t1);
 		} else {
 			msg = g_strdup_printf(_("Ban on %s"), args[2]);
--- a/libpurple/protocols/irc/parse.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/irc/parse.c	Thu Jun 26 20:25:38 2008 +0000
@@ -232,7 +232,7 @@
 
 	if (encodings[0] == NULL || !g_ascii_strcasecmp("UTF-8", encodings[0])) {
 		g_strfreev(encodings);
-		return g_strdup(string);
+		return NULL;
 	}
 
 	utf8 = g_convert(string, strlen(string), encodings[0], "UTF-8", NULL, NULL, &err);
@@ -597,7 +597,7 @@
 		case 'n':
 		case 'c':
 			tmp = irc_send_convert(irc, tok);
-			g_string_append(string, tmp);
+			g_string_append(string, tmp ? tmp : tok);
 			g_free(tmp);
 			break;
 		default:
@@ -710,5 +710,10 @@
 
 static void irc_parse_error_cb(struct irc_conn *irc, char *input)
 {
-	purple_debug(PURPLE_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", input);
+	char *clean;
+        /* This really should be escaped somehow that you can tell what
+         * the junk was -- but as it is, it can crash glib. */
+        clean = purple_utf8_salvage(input);
+	purple_debug(PURPLE_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", clean);
+        g_free(clean);
 }
--- a/libpurple/protocols/jabber/auth.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/auth.c	Thu Jun 26 20:25:38 2008 +0000
@@ -589,75 +589,6 @@
 	}
 }
 
-/*!
- * @brief Given the server challenge (message) and the key (password), calculate the HMAC-MD5 digest
- *
- * This is the crammd5 response.  Inspired by cyrus-sasl's _sasl_hmac_md5()
- */
-static void
-auth_hmac_md5(const char *challenge, size_t challenge_len, const char *key, size_t key_len, guchar *digest)
-{
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
-	int i;
-	/* inner padding - key XORd with ipad */
-	unsigned char k_ipad[65];    
-	/* outer padding - key XORd with opad */
-	unsigned char k_opad[65];    
-
-	cipher = purple_ciphers_find_cipher("md5");
-
-	/* if key is longer than 64 bytes reset it to key=MD5(key) */
-	if (strlen(key) > 64) {
-		guchar keydigest[16];
-
-		context = purple_cipher_context_new(cipher, NULL);
-		purple_cipher_context_append(context, (const guchar *)key, strlen(key));
-		purple_cipher_context_digest(context, 16, keydigest, NULL);
-		purple_cipher_context_destroy(context);
-
-		key = (char *)keydigest;
-		key_len = 16;
-	} 
-
-	/*
-	 * the HMAC_MD5 transform looks like:
-	 *
-	 * MD5(K XOR opad, MD5(K XOR ipad, text))
-	 *
-	 * where K is an n byte key
-	 * ipad is the byte 0x36 repeated 64 times
-	 * opad is the byte 0x5c repeated 64 times
-	 * and text is the data being protected
-	 */
-
-	/* start out by storing key in pads */
-	memset(k_ipad, '\0', sizeof k_ipad);
-	memset(k_opad, '\0', sizeof k_opad);
-	memcpy(k_ipad, (void *)key, key_len);
-	memcpy(k_opad, (void *)key, key_len);
-
-	/* XOR key with ipad and opad values */
-	for (i=0; i<64; i++) {
-		k_ipad[i] ^= 0x36;
-		k_opad[i] ^= 0x5c;
-	}
-
-	/* perform inner MD5 */
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, k_ipad, 64); /* start with inner pad */
-	purple_cipher_context_append(context, (const guchar *)challenge, challenge_len); /* then text of datagram */
-	purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 1st pass */
-	purple_cipher_context_destroy(context);
-
-	/* perform outer MD5 */	
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, k_opad, 64); /* start with outer pad */
-	purple_cipher_context_append(context, digest, 16); /* then results of 1st hash */
-	purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 2nd pass */
-	purple_cipher_context_destroy(context);
-}
-
 static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data)
 {
 	JabberIq *iq;
@@ -703,14 +634,19 @@
 			jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
 			jabber_iq_send(iq);
 
-		} else if(js->stream_id && xmlnode_get_child(query, "crammd5")) {
+		} else if(js->stream_id && (x = xmlnode_get_child(query, "crammd5"))) {
 			const char *challenge;
-			guchar digest[16];
-			char h[17], *p;
-			int i;
+			gchar digest[33];
+			PurpleCipherContext *hmac;
 
-			challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge");
-			auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), digest);
+			/* Calculate the MHAC-MD5 digest */
+			challenge = xmlnode_get_attrib(x, "challenge");
+			hmac = purple_cipher_context_new_by_name("hmac", NULL);
+			purple_cipher_context_set_option(hmac, "hash", "md5");
+			purple_cipher_context_set_key(hmac, (guchar *)pw);
+			purple_cipher_context_append(hmac, (guchar *)challenge, strlen(challenge));
+			purple_cipher_context_digest_to_str(hmac, 33, digest, NULL);
+			purple_cipher_context_destroy(hmac);
 
 			/* Create the response query */
 			iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
@@ -723,11 +659,7 @@
 
 			x = xmlnode_new_child(query, "crammd5");
 
-			/* Translate the digest to a hexadecimal notation */
-			p = h;
-			for(i=0; i<16; i++, p+=2)
-				snprintf(p, 3, "%02x", digest[i]);
-			xmlnode_insert_data(x, h, -1);
+			xmlnode_insert_data(x, digest, 32);
 
 			jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
 			jabber_iq_send(iq);
--- a/libpurple/protocols/jabber/buddy.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Thu Jun 26 20:25:38 2008 +0000
@@ -824,10 +824,14 @@
 		}		
 		if(jbr) {
 			char *purdy = NULL;
+			const char *status_name = jabber_buddy_state_get_name(jbr->state);
 			if(jbr->status)
 				purdy = purple_strdup_withhtml(jbr->status);
-			tmp = g_strdup_printf("%s%s%s", jabber_buddy_state_get_name(jbr->state),
-							(purdy ? ": " : ""),
+			if(status_name && purdy && !strcmp(status_name, purdy))
+				status_name = NULL;
+
+			tmp = g_strdup_printf("%s%s%s", (status_name ? status_name : ""),
+							((status_name && purdy) ? ": " : ""),
 							(purdy ? purdy : ""));
 			purple_notify_user_info_prepend_pair(user_info, _("Status"), tmp);
 			g_free(tmp);
@@ -960,10 +964,12 @@
 		}
 #endif
 	} else {
-		gboolean multiple_resources = jbi->resources && (g_hash_table_size(jbi->resources) > 1);
+		gboolean multiple_resources = jbi->jb->resources && (g_list_length(jbi->jb->resources) > 1);
 
 		for(resources = jbi->jb->resources; resources; resources = resources->next) {
 			char *purdy = NULL;
+			const char *status_name = NULL;
+
 			jbr = resources->data;
 
 			if(jbr->client.name) {
@@ -987,10 +993,14 @@
 				}
 			}
 
+			status_name = jabber_buddy_state_get_name(jbr->state);
 			if(jbr->status)
 				purdy = purple_strdup_withhtml(jbr->status);
-			tmp = g_strdup_printf("%s%s%s", jabber_buddy_state_get_name(jbr->state),
-								  (purdy ? ": " : ""),
+			if(status_name && purdy && !strcmp(status_name, purdy))
+				status_name = NULL;
+			
+			tmp = g_strdup_printf("%s%s%s", (status_name ? status_name : ""),
+								  ((status_name && purdy) ? ": " : ""),
 								  (purdy ? purdy : ""));
 			purple_notify_user_info_prepend_pair(user_info, _("Status"), tmp);
 			g_free(tmp);
@@ -1783,22 +1793,6 @@
 	}
 }
 
-void jabber_buddy_get_info_chat(PurpleConnection *gc, int id,
-		const char *resource)
-{
-	JabberStream *js = gc->proto_data;
-	JabberChat *chat = jabber_chat_find_by_id(js, id);
-	char *full_jid;
-
-	if(!chat)
-		return;
-
-	full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, resource);
-	jabber_buddy_get_info_for_jid(js, full_jid);
-	g_free(full_jid);
-}
-
-
 static void jabber_buddy_set_invisibility(JabberStream *js, const char *who,
 		gboolean invisible)
 {
--- a/libpurple/protocols/jabber/buddy.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/buddy.h	Thu Jun 26 20:25:38 2008 +0000
@@ -96,8 +96,6 @@
 void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource);
 const char *jabber_buddy_get_status_msg(JabberBuddy *jb);
 void jabber_buddy_get_info(PurpleConnection *gc, const char *who);
-void jabber_buddy_get_info_chat(PurpleConnection *gc, int id,
-		const char *resource);
 
 gboolean jabber_buddy_has_capability(JabberBuddy *jb, const gchar *cap);
 
--- a/libpurple/protocols/jabber/chat.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/chat.c	Thu Jun 26 20:25:38 2008 +0000
@@ -308,7 +308,7 @@
 
 	jabber_chat_part(chat, NULL);
 
-	chat->conv = NULL;
+	chat->left = TRUE;
 }
 
 void jabber_chat_destroy(JabberChat *chat)
@@ -342,12 +342,18 @@
 {
 	JabberStream *js = gc->proto_data;
 	JabberChat *chat;
+	JabberChatMember *jcm;
 
 	chat = jabber_chat_find_by_id(js, id);
 
 	if(!chat)
 		return NULL;
 
+	jcm = g_hash_table_lookup(chat->members, who);
+	if (jcm != NULL && jcm->jid)
+		return g_strdup(jcm->jid);
+	
+
 	return g_strdup_printf("%s@%s/%s", chat->room, chat->server, who);
 }
 
--- a/libpurple/protocols/jabber/chat.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/chat.h	Thu Jun 26 20:25:38 2008 +0000
@@ -49,6 +49,7 @@
 	PurpleRequestType config_dialog_type;
 	void *config_dialog_handle;
 	GHashTable *members;
+	gboolean left;
 } JabberChat;
 
 GList *jabber_chat_info(PurpleConnection *gc);
--- a/libpurple/protocols/jabber/jabber.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Thu Jun 26 20:25:38 2008 +0000
@@ -283,70 +283,10 @@
 	purple_circ_buffer_mark_read(js->write_buffer, ret);
 }
 
-void jabber_send_raw(JabberStream *js, const char *data, int len)
+static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
 {
 	int ret;
-
-	/* because printing a tab to debug every minute gets old */
-	if(strcmp(data, "\t"))
-		purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s\n",
-				js->gsc ? " (ssl)" : "", data);
-
-	/* If we've got a security layer, we need to encode the data,
-	 * splitting it on the maximum buffer length negotiated */
-	
-	purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data);
-	if (data == NULL)
-		return;
-	
-#ifdef HAVE_CYRUS_SASL
-	if (js->sasl_maxbuf>0) {
-		int pos;
-
-		if (!js->gsc && js->fd<0)
-			return;
-		pos = 0;
-		if (len == -1)
-			len = strlen(data);
-		while (pos < len) {
-			int towrite;
-			const char *out;
-			unsigned olen;
-
-			if ((len - pos) < js->sasl_maxbuf)
-				towrite = len - pos;
-			else
-				towrite = js->sasl_maxbuf;
-
-			sasl_encode(js->sasl, &data[pos], towrite, &out, &olen);
-			pos += towrite;
-
-			if (js->writeh == 0)
-				ret = jabber_do_send(js, out, olen);
-			else {
-				ret = -1;
-				errno = EAGAIN;
-			}
-
-			if (ret < 0 && errno != EAGAIN)
-				purple_connection_error_reason (js->gc,
-					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-					_("Write error"));
-			else if (ret < olen) {
-				if (ret < 0)
-					ret = 0;
-				if (js->writeh == 0)
-					js->writeh = purple_input_add(
-						js->gsc ? js->gsc->fd : js->fd,
-						PURPLE_INPUT_WRITE,
-						jabber_send_cb, js);
-				purple_circ_buffer_append(js->write_buffer,
-					out + ret, olen - ret);
-			}
-		}
-		return;
-	}
-#endif
+	gboolean success = TRUE;
 
 	if (len == -1)
 		len = strlen(data);
@@ -358,11 +298,12 @@
 		errno = EAGAIN;
 	}
 
-	if (ret < 0 && errno != EAGAIN)
+	if (ret < 0 && errno != EAGAIN) {
 		purple_connection_error_reason (js->gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Write error"));
-	else if (ret < len) {
+		success = FALSE;
+	} else if (ret < len) {
 		if (ret < 0)
 			ret = 0;
 		if (js->writeh == 0)
@@ -372,7 +313,53 @@
 		purple_circ_buffer_append(js->write_buffer,
 			data + ret, len - ret);
 	}
-	return;
+
+	return success;
+}
+
+void jabber_send_raw(JabberStream *js, const char *data, int len)
+{
+
+	/* because printing a tab to debug every minute gets old */
+	if(strcmp(data, "\t"))
+		purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s\n",
+				js->gsc ? " (ssl)" : "", data);
+
+	/* If we've got a security layer, we need to encode the data,
+	 * splitting it on the maximum buffer length negotiated */
+
+	purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data);
+	if (data == NULL)
+		return;
+
+#ifdef HAVE_CYRUS_SASL
+	if (js->sasl_maxbuf>0) {
+		int pos = 0;
+
+		if (!js->gsc && js->fd<0)
+			return;
+
+		if (len == -1)
+			len = strlen(data);
+
+		while (pos < len) {
+			int towrite;
+			const char *out;
+			unsigned olen;
+
+			towrite = MIN((len - pos), js->sasl_maxbuf);
+
+			sasl_encode(js->sasl, &data[pos], towrite, &out, &olen);
+			pos += towrite;
+
+			if (!do_jabber_send_raw(js, out, olen))
+				break;
+		}
+		return;
+	}
+#endif
+
+	do_jabber_send_raw(js, data, len);
 }
 
 int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len)
@@ -398,9 +385,9 @@
 	g_free(txt);
 }
 
-static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout) 
+static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused)
 {
-	purple_timeout_remove(GPOINTER_TO_INT(timeout));
+	purple_timeout_remove(js->keepalive_timeout);
 	js->keepalive_timeout = -1;
 }
 
@@ -424,7 +411,7 @@
 		xmlnode_set_namespace(ping, "urn:xmpp:ping");
 		
 		js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc);
-		jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout));
+		jabber_iq_set_callback(iq, jabber_pong_cb, NULL);
 		jabber_iq_send(iq);
 	}
 }
@@ -637,7 +624,7 @@
 	js->write_buffer = purple_circ_buffer_new(512);
 	js->old_length = 0;
 	js->keepalive_timeout = -1;
-	js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain);
+	js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user ? js->user->domain : NULL);
 
 #ifdef USE_VV
 	js->sessions = NULL;
@@ -1523,8 +1510,7 @@
 
 		if (full) {
 			PurpleStatus *status;
-			PurpleValue *value;
-			
+
 			if(jb->subscription & JABBER_SUB_FROM) {
 				if(jb->subscription & JABBER_SUB_TO)
 					sub = _("Both");
@@ -1540,17 +1526,17 @@
 				else
 					sub = _("None");
 			}
-			
+
 			purple_notify_user_info_add_pair(user_info, _("Subscription"), sub);
-			
+
 			status = purple_presence_get_active_status(presence);
-			value = purple_status_get_attr_value(status, "mood");
-			if (value && purple_value_get_type(value) == PURPLE_TYPE_STRING && (mood = purple_value_get_string(value))) {
-				
-				value = purple_status_get_attr_value(status, "moodtext");
-				if(value && purple_value_get_type(value) == PURPLE_TYPE_STRING) {
-					char *moodplustext = g_strdup_printf("%s (%s)",mood,purple_value_get_string(value));
-					
+			mood = purple_status_get_attr_string(status, "mood");
+			if(mood != NULL) {
+				const char *moodtext;
+				moodtext = purple_status_get_attr_string(status, "moodtext");
+				if(moodtext != NULL) {
+					char *moodplustext = g_strdup_printf("%s (%s)", mood, moodtext);
+
 					purple_notify_user_info_add_pair(user_info, _("Mood"), moodplustext);
 					g_free(moodplustext);
 				} else
@@ -2501,5 +2487,5 @@
 void
 jabber_init_plugin(PurplePlugin *plugin)
 {
-	my_protocol = plugin;
+        my_protocol = plugin;
 }
--- a/libpurple/protocols/jabber/libxmpp.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Thu Jun 26 20:25:38 2008 +0000
@@ -89,7 +89,7 @@
 	jabber_message_send_chat,		/* chat_send */
 	jabber_keepalive,				/* keepalive */
 	jabber_register_account,		/* register_user */
-	jabber_buddy_get_info_chat,		/* get_cb_info */
+	NULL,							/* get_cb_info */
 	NULL,							/* get_cb_away */
 	jabber_roster_alias_change,		/* alias_buddy */
 	jabber_roster_group_change,		/* group_buddy */
--- a/libpurple/protocols/jabber/pep.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/pep.h	Thu Jun 26 20:25:38 2008 +0000
@@ -57,7 +57,7 @@
  * @parameter id	The item id of the requested item (may be NULL)
  * @parameter cb	The callback to be used when this item is received
  *
- * The items element passed to the callback will be NULL if any error occured (like a permission error, node doesn't exist etc.)
+ * The items element passed to the callback will be NULL if any error occurred (like a permission error, node doesn't exist etc.)
  */
 void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb);
 
--- a/libpurple/protocols/jabber/presence.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/presence.c	Thu Jun 26 20:25:38 2008 +0000
@@ -49,7 +49,7 @@
 	xmlnode *presence = user_data;
 	char *chat_full_jid;
 
-	if(!chat->conv)
+	if(!chat->conv || chat->left)
 		return;
 
 	chat_full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server,
@@ -585,13 +585,13 @@
 		if(state == JABBER_BUDDY_STATE_ERROR) {
 			char *title, *msg = jabber_parse_error(js, packet, NULL);
 
-			if(chat->conv) {
+			if (!chat->conv) {
+				title = g_strdup_printf(_("Error joining chat %s"), from);
+				purple_serv_got_join_chat_failed(js->gc, chat->components);
+			} else {
 				title = g_strdup_printf(_("Error in chat %s"), from);
 				if (g_hash_table_size(chat->members) == 0)
 					serv_got_chat_left(js->gc, chat->id);
-			} else {
-				title = g_strdup_printf(_("Error joining chat %s"), from);
-				purple_serv_got_join_chat_failed(js->gc, chat->components);
 			}
 			purple_notify_error(js->gc, title, title, msg);
 			g_free(title);
@@ -613,8 +613,9 @@
 
 			/* If we haven't joined the chat yet, we don't care that someone
 			 * left, or it was us leaving after we closed the chat */
-			if(!chat->conv) {
-				if(jid->resource && chat->handle && !strcmp(jid->resource, chat->handle))
+			if (!chat->conv || chat->left) {
+				if (chat->left &&
+						jid->resource && chat->handle && !strcmp(jid->resource, chat->handle))
 					jabber_chat_destroy(chat);
 				jabber_id_free(jid);
 				g_free(status);
--- a/libpurple/protocols/jabber/si.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/si.c	Thu Jun 26 20:25:38 2008 +0000
@@ -63,6 +63,7 @@
 	char *rxqueue;
 	size_t rxlen;
 	gsize rxmaxlen;
+	int local_streamhost_fd;
 } JabberSIXfer;
 
 static PurpleXfer*
@@ -347,7 +348,11 @@
 	g_free(jsx->rxqueue);
 	jsx->rxqueue = NULL;
 
-	purple_xfer_start(xfer, source, NULL, -1);
+	/* Before actually starting sending the file, we need to wait until the
+	 * recipient sends the IQ result with <streamhost-used/>
+	 */
+	purple_debug_info("jabber", "SOCKS5 connection negotiation completed. "
+					  "Waiting for IQ result to start file transfer.\n");
 }
 
 static void
@@ -608,6 +613,7 @@
 		PurpleInputCondition cond)
 {
 	PurpleXfer *xfer = data;
+	JabberSIXfer *jsx = xfer->data;
 	int acceptfd;
 
 	purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n");
@@ -617,12 +623,13 @@
 		return;
 	else if(acceptfd == -1) {
 		purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno));
-		/* TODO: This should cancel the ft */
+		/* Don't cancel the ft - allow it to fall to the next streamhost.*/
 		return;
 	}
 
 	purple_input_remove(xfer->watcher);
 	close(source);
+	jsx->local_streamhost_fd = -1;
 
 	xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
 					 jabber_si_xfer_bytestreams_send_read_cb, xfer);
@@ -633,19 +640,30 @@
 		gpointer data)
 {
 	PurpleXfer *xfer = data;
-	JabberSIXfer *jsx = xfer->data;
+	JabberSIXfer *jsx;
 	xmlnode *query, *streamhost_used;
 	const char *from, *type, *jid;
 	GList *matched;
 
 	/* TODO: This need to send errors if we don't see what we're looking for */
 
+	/* Make sure that the xfer is actually still valid and we're not just receiving an old iq response */
+	if (!g_list_find(js->file_transfers, xfer)) {
+		purple_debug_error("jabber", "Got bytestreams response for no longer existing xfer (%p)\n", xfer);
+		return;
+	}
+
 	/* In the case of a direct file transfer, this is expected to return */
-	if(!jsx)
+	if(!xfer->data)
 		return;
 
-	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result"))
+	jsx = xfer->data;
+
+	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) {
+		if (type && !strcmp(type, "error"))
+			purple_xfer_cancel_remote(xfer);
 		return;
+	}
 
 	if(!(from = xmlnode_get_attrib(packet, "from")))
 		return;
@@ -659,22 +677,33 @@
 	if(!(jid = xmlnode_get_attrib(streamhost_used, "jid")))
 		return;
 
-	purple_debug_info("jabber", "jabber_si_connect_proxy_cb() will be looking at jsx %p: jsx->streamhosts is %p and jid is %p",
+	purple_debug_info("jabber", "jabber_si_connect_proxy_cb() will be looking at jsx %p: jsx->streamhosts is %p and jid is %s\n",
 					  jsx, jsx->streamhosts, jid);
 
 	if(!(matched = g_list_find_custom(jsx->streamhosts, jid, jabber_si_compare_jid)))
 	{
 		gchar *my_jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
 			jsx->js->user->domain, jsx->js->user->resource);
-		if (!strcmp(jid, my_jid))
+		if (!strcmp(jid, my_jid)) {
 			purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
-		else
+			purple_xfer_start(xfer, xfer->fd, NULL, -1);
+		} else {
 			purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n");
+			purple_xfer_cancel_local(xfer);
+		}
 		g_free(my_jid);
 		return;
 	}
 
-	/* TODO: Clean up the local SOCKS5 proxy - it isn't going to be used.*/
+	/* Clean up the local streamhost - it isn't going to be used.*/
+	if (xfer->watcher > 0) {
+		purple_input_remove(xfer->watcher);
+		xfer->watcher = 0;
+	}
+	if (jsx->local_streamhost_fd >= 0) {
+		close(jsx->local_streamhost_fd);
+		jsx->local_streamhost_fd = -1;
+	}
 
 	jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched);
 	g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
@@ -692,14 +721,17 @@
 	JabberSIXfer *jsx;
 	JabberIq *iq;
 	xmlnode *query, *streamhost;
-	char *jid, port[6];
-	const char *local_ip, *public_ip, *ft_proxies;
+	const char *ft_proxies;
+	char port[6];
 	GList *tmp;
 	JabberBytestreamsStreamhost *sh, *sh2;
+	int streamhost_count = 0;
 
 	jsx = xfer->data;
 	jsx->listen_data = NULL;
 
+	/* I'm not sure under which conditions this can happen
+	 * (it seems like it shouldn't be possible */
 	if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) {
 		purple_xfer_unref(xfer);
 		return;
@@ -707,11 +739,6 @@
 
 	purple_xfer_unref(xfer);
 
-	if (sock < 0) {
-		purple_xfer_cancel_local(xfer);
-		return;
-	}
-
 	iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET,
 			"http://jabber.org/protocol/bytestreams");
 	xmlnode_set_attrib(iq->node, "to", xfer->who);
@@ -719,41 +746,45 @@
 
 	xmlnode_set_attrib(query, "sid", jsx->stream_id);
 
-	jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
+	/* If we successfully started listening locally */
+	if (sock >= 0) {
+		gchar *jid;
+		const char *local_ip, *public_ip;
+
+		jsx->local_streamhost_fd = sock;
+
+		jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
 			jsx->js->user->domain, jsx->js->user->resource);
-	xfer->local_port = purple_network_get_port_from_fd(sock);
-	g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
-
-	/* TODO: Should there be an option to not use the local host as a ft proxy?
-	 *       (to prevent revealing IP address, etc.) */
+		xfer->local_port = purple_network_get_port_from_fd(sock);
+		g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
 
-	/* Include the localhost's IP (for in-network transfers) */
-	local_ip = purple_network_get_local_system_ip(jsx->js->fd);
-	if (strcmp(local_ip, "0.0.0.0") != 0)
-	{
-		streamhost = xmlnode_new_child(query, "streamhost");
-		xmlnode_set_attrib(streamhost, "jid", jid);
-		xmlnode_set_attrib(streamhost, "host", local_ip);
-		xmlnode_set_attrib(streamhost, "port", port);
+		/* Include the localhost's IP (for in-network transfers) */
+		local_ip = purple_network_get_local_system_ip(jsx->js->fd);
+		if (strcmp(local_ip, "0.0.0.0") != 0) {
+			streamhost_count++;
+			streamhost = xmlnode_new_child(query, "streamhost");
+			xmlnode_set_attrib(streamhost, "jid", jid);
+			xmlnode_set_attrib(streamhost, "host", local_ip);
+			xmlnode_set_attrib(streamhost, "port", port);
+		}
+
+		/* Include the public IP (assuming that there is a port mapped somehow) */
+		public_ip = purple_network_get_my_ip(jsx->js->fd);
+		if (strcmp(public_ip, local_ip) != 0 && strcmp(public_ip, "0.0.0.0") != 0) {
+			streamhost_count++;
+			streamhost = xmlnode_new_child(query, "streamhost");
+			xmlnode_set_attrib(streamhost, "jid", jid);
+			xmlnode_set_attrib(streamhost, "host", public_ip);
+			xmlnode_set_attrib(streamhost, "port", port);
+		}
+
+		g_free(jid);
+
+		/* The listener for the local proxy */
+		xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
+				jabber_si_xfer_bytestreams_send_connected_cb, xfer);
 	}
 
-	/* Include the public IP (assuming that there is a port mapped somehow) */
-	/* TODO: Check that it isn't the same as above and is a valid IP */
-	public_ip = purple_network_get_my_ip(jsx->js->fd);
-	if (strcmp(public_ip, local_ip) != 0)
-	{
-		streamhost = xmlnode_new_child(query, "streamhost");
-		xmlnode_set_attrib(streamhost, "jid", jid);
-		xmlnode_set_attrib(streamhost, "host", public_ip);
-		xmlnode_set_attrib(streamhost, "port", port);
-	}
-
-	g_free(jid);
-
-	/* The listener for the local proxy */
-	xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
-			jabber_si_xfer_bytestreams_send_connected_cb, xfer);
-
 	/* insert proxies here */
 	ft_proxies = purple_account_get_string(xfer->account, "ft_proxies", NULL);
 	if (ft_proxies) {
@@ -778,11 +809,12 @@
 
 			g_snprintf(port, sizeof(port), "%hu", portnum);
 
-			purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and ft_proxy_list[%i] %p",
+			purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and ft_proxy_list[%i] %p\n",
 							  jsx, jsx->streamhosts, i, ft_proxy_list[i]);
 			if(g_list_find_custom(jsx->streamhosts, ft_proxy_list[i], jabber_si_compare_jid) != NULL)
 				continue;
 
+			streamhost_count++;
 			streamhost = xmlnode_new_child(query, "streamhost");
 			xmlnode_set_attrib(streamhost, "jid", ft_proxy_list[i]);
 			xmlnode_set_attrib(streamhost, "host", ft_proxy_list[i]);
@@ -812,6 +844,7 @@
 		if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL)
 			continue;
 
+		streamhost_count++;
 		streamhost = xmlnode_new_child(query, "streamhost");
 		xmlnode_set_attrib(streamhost, "jid", sh->jid);
 		xmlnode_set_attrib(streamhost, "host", sh->host);
@@ -827,6 +860,14 @@
 		jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2);
 	}
 
+	/* We have no way of transferring, cancel the transfer */
+	if (streamhost_count == 0) {
+		jabber_iq_free(iq);
+		/* We should probably notify the target, but this really shouldn't ever happen */
+		purple_xfer_cancel_local(xfer);
+		return;
+	}
+
 	jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer);
 
 	jabber_iq_send(iq);
@@ -841,13 +882,14 @@
 	purple_xfer_ref(xfer);
 
 	jsx = xfer->data;
+
+	/* TODO: Should there be an option to not use the local host as a ft proxy?
+	 *       (to prevent revealing IP address, etc.) */
 	jsx->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
 				jabber_si_xfer_bytestreams_listen_cb, xfer);
 	if (jsx->listen_data == NULL) {
-		purple_xfer_unref(xfer);
-		/* XXX: couldn't open a port, we're fscked */
-		purple_xfer_cancel_local(xfer);
-		return;
+		/* We couldn't open a local port.  Perhaps we can use a proxy. */
+		jabber_si_xfer_bytestreams_listen_cb(-1, xfer);
 	}
 
 }
@@ -956,7 +998,8 @@
 		purple_network_listen_cancel(jsx->listen_data);
 	if (jsx->iq_id != NULL)
 		jabber_iq_remove_callback_by_id(js, jsx->iq_id);
-
+	if (jsx->local_streamhost_fd >= 0)
+		close(jsx->local_streamhost_fd);
 	if (jsx->connect_timeout > 0)
 		purple_timeout_remove(jsx->connect_timeout);
 
@@ -1164,6 +1207,7 @@
 	{
 		xfer->data = jsx = g_new0(JabberSIXfer, 1);
 		jsx->js = js;
+		jsx->local_streamhost_fd = -1;
 
 		purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
 		purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
@@ -1183,9 +1227,6 @@
 
 	js = gc->proto_data;
 
-	if(!purple_find_buddy(gc->account, who) || !jabber_buddy_find(js, who, FALSE))
-		return;
-
 	xfer = jabber_si_new_xfer(gc, who);
 
 	if (file)
@@ -1237,6 +1278,7 @@
 		return;
 
 	jsx = g_new0(JabberSIXfer, 1);
+	jsx->local_streamhost_fd = -1;
 
 	for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
 		const char *var = xmlnode_get_attrib(field, "var");
--- a/libpurple/protocols/jabber/xdata.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/jabber/xdata.c	Thu Jun 26 20:25:38 2008 +0000
@@ -201,7 +201,7 @@
 	xmlnode *fn, *x;
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *group;
-	PurpleRequestField *field;
+	PurpleRequestField *field = NULL;
 
 	char *title = NULL;
 	char *instructions = NULL;
@@ -232,10 +232,6 @@
 		if(!label)
 			label = var;
 
-		if((valuenode = xmlnode_get_child(fn, "value")))
-			value = xmlnode_get_data(valuenode);
-
-
 		if(!strcmp(type, "text-private")) {
 			if((valuenode = xmlnode_get_child(fn, "value")))
 				value = xmlnode_get_data(valuenode);
@@ -333,14 +329,16 @@
 			g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_BOOLEAN));
 
 			g_free(value);
-		} else if(!strcmp(type, "fixed") && value) {
+		} else if(!strcmp(type, "fixed")) {
 			if((valuenode = xmlnode_get_child(fn, "value")))
 				value = xmlnode_get_data(valuenode);
 
-			field = purple_request_field_label_new("", value);
-			purple_request_field_group_add_field(group, field);
+			if(value != NULL) {
+				field = purple_request_field_label_new("", value);
+				purple_request_field_group_add_field(group, field);
 
-			g_free(value);
+				g_free(value);
+			}
 		} else if(!strcmp(type, "hidden")) {
 			if((valuenode = xmlnode_get_child(fn, "value")))
 				value = xmlnode_get_data(valuenode);
--- a/libpurple/protocols/msn/contact.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/contact.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1,5 +1,5 @@
 /**
- * @file contact.c 
+ * @file contact.c
  * 	get MSN contacts via SOAP request
  *	created by MaYuan<mayuan2006@gmail.com>
  *
@@ -80,7 +80,7 @@
 	state->session = session;
 
 	return state;
-}	
+}
 
 void
 msn_callback_state_free(MsnCallbackState *state)
@@ -217,7 +217,7 @@
 	g_return_if_fail(contact->session != NULL);
 	g_return_if_fail(contact->session->user != NULL);
 	g_return_if_fail(contact->session->user->passport != NULL);
-	
+
 	purple_debug_info("msnab","Creating an Address Book.\n");
 
 	body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport);
@@ -247,7 +247,7 @@
 		user->membership_id[list] = atoi(member_id);
 	}
 
-	msn_got_lst_user(session, user, 1 << list, NULL);         
+	msn_got_lst_user(session, user, 1 << list, NULL);
 
 	g_free(passport);
 	g_free(type);
@@ -269,7 +269,7 @@
 			char *lastchange_str = xmlnode_get_data(lastchange);
 			xmlnode *membership;
 
-			purple_debug_info("msncl","last change: %s\n", lastchange_str);	
+			purple_debug_info("msncl","last change: %s\n", lastchange_str);
 			purple_account_set_string(session->account,	"CLLastChange",
 				lastchange_str);
 
@@ -480,7 +480,7 @@
 
 			if (phone_type && !strcmp(phone_type, "ContactPhoneMobile")) {
 				xmlnode *number;
-					
+
 				if ((number = xmlnode_get_child(contact_phone, "number"))) {
 					xmlnode *messenger_enabled;
 					char *is_messenger_enabled = NULL;
@@ -489,8 +489,8 @@
 					mobile_number = xmlnode_get_data(number);
 
 					if (mobile_number &&
-							(messenger_enabled = xmlnode_get_child(contact_phone, "isMessengerEnabled")) 
-							&& (is_messenger_enabled = xmlnode_get_data(messenger_enabled)) 
+							(messenger_enabled = xmlnode_get_child(contact_phone, "isMessengerEnabled"))
+							&& (is_messenger_enabled = xmlnode_get_data(messenger_enabled))
 							&& !strcmp(is_messenger_enabled, "true"))
 						mobile = TRUE;
 
@@ -679,7 +679,7 @@
 			gchar *errorcode = xmlnode_get_data(faultnode);
 
 			purple_debug_info("MSNAB", "Error Code: %s\n", errorcode);
-						
+
 			if (g_str_equal(errorcode, "ABDoesNotExist")) {
 				g_free(errorcode);
 				return TRUE;
@@ -823,7 +823,7 @@
 	if (resp != NULL) {
 		MsnUserList *userlist = session->userlist;
 		MsnUser *user;
-	
+
 		purple_debug_info("MSNCL","Contact added successfully\n");
 
 		// the code this block is replacing didn't send ADL for yahoo contacts,
@@ -921,7 +921,7 @@
 }
 
 void
-msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, 
+msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
 			 const char *passport, const char *groupId)
 {
 	MsnUserList *userlist;
@@ -934,11 +934,11 @@
 	g_return_if_fail(contact != NULL);
 	g_return_if_fail(contact->session != NULL);
 	g_return_if_fail(contact->session->userlist != NULL);
-	
+
 	userlist = contact->session->userlist;
 
 	if (!strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) {
-		
+
 		user = msn_userlist_find_add_user(userlist, passport, passport);
 
 		if (state->action & MSN_ADD_BUDDY) {
@@ -956,13 +956,13 @@
 		return;
 	}
 
-	purple_debug_info("MSNCL", "Adding user %s to group %s\n", passport, 
+	purple_debug_info("MSNCL", "Adding user %s to group %s\n", passport,
 			  msn_userlist_find_group_name(userlist, groupId));
 
 	user = msn_userlist_find_user(userlist, passport);
 	if (user == NULL) {
 		purple_debug_warning("MSNCL", "Unable to retrieve user %s from the userlist!\n", passport);
-		msn_callback_state_free(state);                                     
+		msn_callback_state_free(state);
 		return; /* guess this never happened! */
 	}
 
@@ -1007,7 +1007,7 @@
 /*delete a Contact*/
 void
 msn_delete_contact(MsnContact *contact, const char *contactId)
-{	
+{
 	gchar *body = NULL;
 	gchar *contact_id_xml = NULL ;
 	MsnCallbackState *state;
@@ -1045,7 +1045,7 @@
 			purple_debug_info("MSNCL", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name);
 		}
 	}
-	
+
 	msn_callback_state_free(state);
 }
 
@@ -1057,15 +1057,15 @@
 	MsnCallbackState *state;
 	gchar *body, *contact_id_xml;
 	const gchar *groupId;
-	
+
 	g_return_if_fail(passport != NULL);
 	g_return_if_fail(group_name != NULL);
 	g_return_if_fail(contact != NULL);
 	g_return_if_fail(contact->session != NULL);
 	g_return_if_fail(contact->session->userlist != NULL);
-	
+
 	userlist = contact->session->userlist;
-	
+
 	groupId = msn_userlist_find_group_id(userlist, group_name);
 	if (groupId != NULL) {
 		purple_debug_info("MSNCL", "Deleting user %s from group %s\n", passport, group_name);
@@ -1073,9 +1073,9 @@
 		purple_debug_warning("MSNCL", "Unable to retrieve group id from group %s !\n", group_name);
 		return;
 	}
-	
+
 	user = msn_userlist_find_user(userlist, passport);
-	
+
 	if (user == NULL) {
 		purple_debug_warning("MSNCL", "Unable to retrieve user from passport %s!\n", passport);
 		return;
@@ -1099,7 +1099,7 @@
 			xmlnode_from_str(body, -1)),
 		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
 		msn_del_contact_from_group_read_cb, state);
-	
+
 	g_free(contact_id_xml);
 	g_free(body);
 }
@@ -1198,7 +1198,7 @@
 	} else {
 		/* list == MSN_LIST_AL || list == MSN_LIST_BL */
 		partner_scenario = MSN_PS_BLOCK_UNBLOCK;
-		
+
 		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
 	}
 
@@ -1226,13 +1226,13 @@
 	g_return_if_fail(state != NULL);
 	g_return_if_fail(state->session != NULL);
 	g_return_if_fail(state->session->contact != NULL);
-	
+
 	if (resp != NULL) {
 		purple_debug_info("MSN CL", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
 
 		if (state->list_id == MSN_LIST_RL) {
 			MsnUser *user = msn_userlist_find_user(state->session->userlist, state->who);
-		
+
 			if (user != NULL) {
 				msn_user_set_op(user, MSN_LIST_RL_OP);
 			}
@@ -1274,9 +1274,9 @@
 
 	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, state->who);
 
-	body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE, 
+	body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE,
 			       MsnSoapPartnerScenarioText[partner_scenario],
-			       MsnMemberRole[list], 
+			       MsnMemberRole[list],
 			       member);
 
 	msn_soap_message_send(contact->session,
@@ -1323,9 +1323,9 @@
 msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
 	MsnCallbackState *state = data;
-	
+
 	purple_debug_info("MSNCL", "Group request successful.\n");
-	
+
 	g_return_if_fail(state->session != NULL);
 	g_return_if_fail(state->session->userlist != NULL);
 	g_return_if_fail(state->session->contact != NULL);
@@ -1338,13 +1338,13 @@
 	if (state) {
 		MsnSession *session = state->session;
 		MsnUserList *userlist = session->userlist;
-		
+
 		if (state->action & MSN_RENAME_GROUP) {
 			msn_userlist_rename_group_id(session->userlist,
 						     state->guid,
 						     state->new_group_name);
 		}
-		
+
 		if (state->action & MSN_ADD_GROUP) {
 			/* the response is taken from
 			   http://telepathy.freedesktop.org/wiki/Pymsn/MSNP/ContactListActions
@@ -1364,7 +1364,7 @@
 						state->who,
 						state->new_group_name);
 				} else if (state->action & MSN_MOVE_BUDDY) {
-					msn_add_contact_to_group(session->contact, state, state->who, guid); 
+					msn_add_contact_to_group(session->contact, state, state->who, guid);
 					g_free(guid);
 					return;
 				}
@@ -1374,16 +1374,16 @@
 					state->new_group_name);
 			}
 		}
-		
+
 		if (state->action & MSN_DEL_GROUP) {
 			GList *l;
-			
+
 			msn_userlist_remove_group_id(session->userlist, state->guid);
 			for (l = userlist->users; l != NULL; l = l->next) {
 				msn_user_remove_group_id( (MsnUser *)l->data, state->guid);
 			}
 		}
-			
+
 		msn_callback_state_free(state);
 	}
 }
@@ -1396,7 +1396,7 @@
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(group_name != NULL);
-	
+
 	purple_debug_info("MSNCL","Adding group %s to contact list.\n", group_name);
 
 	if (state == NULL) {
@@ -1429,13 +1429,13 @@
 	const gchar *guid;
 
 	g_return_if_fail(session != NULL);
-	
+
 	g_return_if_fail(group_name != NULL);
 	purple_debug_info("MSNCL","Deleting group %s from contact list\n", group_name);
-	
+
 	guid = msn_userlist_find_group_id(session->userlist, group_name);
-	
-	/* if group uid we need to del is NULL, 
+
+	/* if group uid we need to del is NULL,
 	*  we need to delete nothing
 	*/
 	if (guid == NULL) {
@@ -1451,7 +1451,7 @@
 	state = msn_callback_state_new(session);
 	msn_callback_state_set_action(state, MSN_DEL_GROUP);
 	msn_callback_state_set_guid(state, guid);
-	
+
 	body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE, guid);
 
 	msn_soap_message_send(session,
@@ -1470,14 +1470,14 @@
 	gchar *body = NULL;
 	const gchar * guid;
 	MsnCallbackState *state;
-	
+
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->userlist != NULL);
 	g_return_if_fail(old_group_name != NULL);
 	g_return_if_fail(new_group_name != NULL);
-	
+
 	purple_debug_info("MSN CL", "Renaming group %s to %s.\n", old_group_name, new_group_name);
-	
+
 	guid = msn_userlist_find_group_id(session->userlist, old_group_name);
 	if (guid == NULL)
 		return;
@@ -1492,10 +1492,10 @@
 	}
 
 	msn_callback_state_set_action(state, MSN_RENAME_GROUP);
-	
+
 	body = g_markup_printf_escaped(MSN_GROUP_RENAME_TEMPLATE,
 		guid, new_group_name);
-	
+
 	msn_soap_message_send(session,
 		msn_soap_message_new(MSN_GROUP_RENAME_SOAP_ACTION,
 			xmlnode_from_str(body, -1)),
--- a/libpurple/protocols/msn/contact.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/contact.h	Thu Jun 26 20:25:38 2008 +0000
@@ -349,7 +349,7 @@
 #define MSN_GROUP_RENAME_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupUpdate"
 #define MSN_GROUP_RENAME_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groups><Group><groupId>%s</groupId><groupInfo><name>%s</name></groupInfo><propertiesChanged>GroupName </propertiesChanged></Group></groups></ABGroupUpdate></soap:Body></soap:Envelope>"
 
-typedef enum 
+typedef enum
 {
 	MSN_ADD_BUDDY			= 0x01,
 	MSN_MOVE_BUDDY			= 0x02,
@@ -383,7 +383,7 @@
 	MsnSession *session;
 };
 
-typedef enum 
+typedef enum
 {
 	MSN_PS_INITIAL,
 	MSN_PS_SAVE_CONTACT,
@@ -404,34 +404,34 @@
 void msn_callback_state_set_uid(MsnCallbackState *state, const gchar *uid);
 void msn_callback_state_set_old_group_name(MsnCallbackState *state,
 					   const gchar *old_group_name);
-void msn_callback_state_set_new_group_name(MsnCallbackState *state, 
+void msn_callback_state_set_new_group_name(MsnCallbackState *state,
 					   const gchar *new_group_name);
 void msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid);
 void msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id);
-void msn_callback_state_set_action(MsnCallbackState *state, 
+void msn_callback_state_set_action(MsnCallbackState *state,
 				   MsnCallbackAction action);
 
 void msn_contact_connect(MsnContact *contact);
-void msn_get_contact_list(MsnContact * contact, 
+void msn_get_contact_list(MsnContact * contact,
 			  const MsnSoapPartnerScenario partner_scenario,
 			  const char *update);
-void msn_get_address_book(MsnContact *contact, 
+void msn_get_address_book(MsnContact *contact,
 			  const MsnSoapPartnerScenario partner_scenario,
 			  const char * update, const char * gupdate);
 
 /* contact SOAP operations */
 void msn_update_contact(MsnContact *contact, const char* nickname);
 
-void msn_add_contact(MsnContact *contact, MsnCallbackState *state, 
+void msn_add_contact(MsnContact *contact, MsnCallbackState *state,
 		     const char *passport);
 void msn_delete_contact(MsnContact *contact, const char *contactId);
 
-void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, 
+void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
 			      const char *passport, const char *groupId);
-void msn_del_contact_from_group(MsnContact *contact, const char *passport, 
+void msn_del_contact_from_group(MsnContact *contact, const char *passport,
 				const char *group_name);
 /* group operations */
-void msn_add_group(MsnSession *session, MsnCallbackState *state, 
+void msn_add_group(MsnSession *session, MsnCallbackState *state,
 					const char* group_name);
 void msn_del_group(MsnSession *session, const gchar *group_name);
 void msn_contact_rename_group(MsnSession *session, const char *old_group_name,
--- a/libpurple/protocols/msn/dialog.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/dialog.c	Thu Jun 26 20:25:38 2008 +0000
@@ -135,7 +135,7 @@
 								 passport);
 	}
 
-	purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE, 
+	purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE,
 						purple_connection_get_account(gc), data->who, NULL,
 						data, 2,
 						_("Yes"), G_CALLBACK(msn_add_cb),
--- a/libpurple/protocols/msn/error.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/error.c	Thu Jun 26 20:25:38 2008 +0000
@@ -259,7 +259,7 @@
 {
 	char buf[MSN_BUF_LEN];
 	gboolean debug;
-	
+
 	g_snprintf(buf, sizeof(buf), _("MSN Error: %s\n"),
 			   msn_error_get_text(type, &debug));
 	if (debug)
--- a/libpurple/protocols/msn/msn.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1594,10 +1594,10 @@
 	MsnSession *session;
 
 	session = gc->proto_data;
-	
+
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->userlist != NULL);
-	
+
 	if (msn_userlist_find_group_with_name(session->userlist, old_name) != NULL)
 	{
 		msn_contact_rename_group(session, old_name, group->name);
@@ -1677,7 +1677,7 @@
 		purple_debug_info("MSN", "This group can't be removed, returning.\n");
 		return ;
 	}
-	
+
 	msn_del_group(session, group->name);
 }
 
--- a/libpurple/protocols/msn/msnutils.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Thu Jun 26 20:25:38 2008 +0000
@@ -169,7 +169,7 @@
 	gchar *base64, *retval;
 
 	g_return_val_if_fail(str != NULL, NULL);
-	
+
 	base64 = purple_base64_encode((guchar *)str, strlen(str));
 	retval = g_strdup_printf("=?utf-8?B?%s?=", base64);
 	g_free(base64);
@@ -509,7 +509,7 @@
  *This algorithm reference with http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
  */
 #define BUFSIZE	256
-void 
+void
 msn_handle_chl(char *input, char *output)
 {
 		PurpleCipher *cipher;
@@ -538,7 +538,7 @@
 
 		/* Split it into four integers */
 		md5Parts = (unsigned int *)md5Hash;
-		for(i=0; i<4; i++){  
+		for(i=0; i<4; i++){
 				/* adjust endianess */
 				md5Parts[i] = GUINT_TO_LE(md5Parts[i]);
 
@@ -578,7 +578,7 @@
 
 		/* adjust endianness */
 		for(i=0; i<4; i++)
-				newHashParts[i] = GUINT_TO_LE(newHashParts[i]); 
+				newHashParts[i] = GUINT_TO_LE(newHashParts[i]);
 
 		/* make a string of the parts */
 		newHash = (unsigned char *)newHashParts;
--- a/libpurple/protocols/msn/notification.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/notification.c	Thu Jun 26 20:25:38 2008 +0000
@@ -34,15 +34,6 @@
 
 static MsnTable *cbs_table;
 
-/****************************************************************************
- * 	Local Function Prototype
- ****************************************************************************/
-
-static void msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len);
-static void
-msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport,
-					 MsnListOp list_op, MsnUserType type);
-
 /**************************************************************************
  * Main
  **************************************************************************/
@@ -336,7 +327,7 @@
 	}
 
 	/*
-	 * Windows Live Messenger 8.0 
+	 * Windows Live Messenger 8.0
 	 * Notice :CVR String discriminate!
 	 * reference of http://www.microsoft.com/globaldev/reference/oslocversion.mspx
 	 * to see the Local ID
@@ -430,7 +421,7 @@
 	char *payload;
 	gsize payload_len;
 	int type;
-	
+
 	cmdproc = session->notification->cmdproc;
 	g_return_if_fail(msg     != NULL);
 	payload = msn_message_gen_payload(msg, &payload_len);
@@ -649,7 +640,7 @@
 {
 	MsnTransaction *trans;
 	purple_debug_info("MSN Notification","Sending ADL with payload: %s\n", payload);
-	trans = msn_transaction_new(cmdproc, "ADL","%" G_GSIZE_FORMAT, payload_len);
+	trans = msn_transaction_new(cmdproc, "ADL", "%i", payload_len);
 	msn_transaction_set_payload(trans, payload, payload_len);
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
@@ -709,8 +700,8 @@
 	}
 
 	display_name = purple_connection_get_display_name(session->account->gc);
-	if (display_name 
-	    && strcmp(display_name, 
+	if (display_name
+	    && strcmp(display_name,
 		      purple_account_get_username(session->account))) {
 		msn_act_id(session->account->gc, display_name);
 	}
@@ -755,15 +746,15 @@
 	purple_debug_misc("MSN Notification", "Parsing received ADL XML data\n");
 
 	g_return_if_fail(payload != NULL);
-	
+
 	root = xmlnode_from_str(payload, (gssize) len);
-	
+
 	if (root == NULL) {
 		purple_debug_info("MSN Notification", "Invalid XML!\n");
 		return;
 	}
 	for (domain_node = xmlnode_get_child(root, "d"); domain_node; domain_node = xmlnode_get_next_twin(domain_node)) {
-		const gchar * domain = NULL; 
+		const gchar * domain = NULL;
 		xmlnode *contact_node = NULL;
 
 		domain = xmlnode_get_attrib(domain_node, "n");
@@ -1024,13 +1015,13 @@
 	MsnSlpLink *slplink;
 	MsnUser *user;
 
+	/* Tell libpurple that the user has signed off */
 	user = msn_userlist_find_user(cmdproc->session->userlist, cmd->params[0]);
-
 	user->status = "offline";
 	msn_user_update(user);
 
+	/* If we have an open MsnSlpLink with the user then close it */
 	slplink = msn_session_find_slplink(cmdproc->session, cmd->params[0]);
-
 	if (slplink != NULL)
 		msn_slplink_destroy(slplink);
 
@@ -1300,7 +1291,7 @@
 			type = cmd->params[1];
 			if (!strcmp(type, "MFN")) {
 				friendlyname = purple_url_decode(cmd->params[2]);
-				
+
 				msn_update_contact(session->contact, friendlyname);
 
 				purple_connection_set_display_name(
@@ -1649,12 +1640,12 @@
 		purple_debug_error("MSN","Unable to parse GCF payload into a XML tree");
 		return;
 	}
-	
+
 	buf = xmlnode_to_formatted_str(root, &xmllen);
 
 	/* get the payload content */
 	purple_debug_info("MSNP14","GCF command payload:\n%.*s\n", xmllen, buf);
-	
+
 	g_free(buf);
 	xmlnode_free(root);
 }
@@ -1698,7 +1689,7 @@
 
 	passport = cmd->params[0];
 	user = msn_userlist_find_user(session->userlist, passport);
-	
+
 	psm_str = msn_get_psm(cmd->payload,len);
 	msn_user_set_statusline(user, psm_str);
 	g_free(psm_str);
@@ -2005,7 +1996,7 @@
 		{
 			case 1:
 				minutes = atoi(g_hash_table_lookup(table, "Arg1"));
-				g_snprintf(buf, sizeof(buf), dngettext(PACKAGE, 
+				g_snprintf(buf, sizeof(buf), dngettext(PACKAGE,
 							"The MSN server will shut down for maintenance "
 							"in %d minute. You will automatically be "
 							"signed out at that time.  Please finish any "
@@ -2032,7 +2023,7 @@
 
 void
 msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list_id,
-						   	  const char *who)
+							  const char *who)
 {
 	MsnCmdProc *cmdproc;
 	MsnListOp list_op = 1 << list_id;
@@ -2045,12 +2036,12 @@
 	adl_node = xmlnode_new("ml");
 	adl_node->child = NULL;
 
-	msn_add_contact_xml(notification->session, adl_node, who, list_op, 
+	msn_add_contact_xml(notification->session, adl_node, who, list_op,
 						MSN_USER_TYPE_PASSPORT);
 
 	payload = xmlnode_to_str(adl_node,&payload_len);
 	xmlnode_free(adl_node);
-	
+
 	msn_notification_post_adl(notification->servconn->cmdproc,
 						payload,payload_len);
 	g_free(payload);
@@ -2155,11 +2146,11 @@
 	/*initial OIM notification*/
 	msn_table_add_msg_type(cbs_table,
 							"text/x-msmsgsinitialmdatanotification",
-							initial_mdata_msg);	
+							initial_mdata_msg);
 	/*OIM notification when user online*/
 	msn_table_add_msg_type(cbs_table,
 							"text/x-msmsgsoimnotification",
-							initial_mdata_msg);	
+							initial_mdata_msg);
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsinitialemailnotification",
 						   initial_email_msg);
--- a/libpurple/protocols/msn/notification.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/notification.h	Thu Jun 26 20:25:38 2008 +0000
@@ -30,7 +30,7 @@
 #define MSNP13_WLM_PRODUCT_ID	"PROD01065C%ZFN6F"
 
 #define MSNP10_PRODUCT_KEY		"VT6PX?UQTM4WM%YR"
-#define MSNP10_PRODUCT_ID		"PROD0038W!61ZTF9" 
+#define MSNP10_PRODUCT_ID		"PROD0038W!61ZTF9"
 
 typedef struct _MsnNotification MsnNotification;
 
@@ -41,6 +41,11 @@
 struct _MsnNotification
 {
 	MsnSession *session;
+
+	/**
+	 * This is a convenience pointer that always points to
+	 * servconn->cmdproc
+	 */
 	MsnCmdProc *cmdproc;
 	MsnServConn *servconn;
 
@@ -71,7 +76,7 @@
  * Closes a notification.
  *
  * It's first closed, and then disconnected.
- * 
+ *
  * @param notification The notification object to close.
  */
 void msn_notification_close(MsnNotification *notification);
--- a/libpurple/protocols/msn/object.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/object.c	Thu Jun 26 20:25:38 2008 +0000
@@ -172,7 +172,7 @@
 	base64 = purple_base64_encode(digest, sizeof(digest));
 	msn_object_set_sha1c(msnobj, base64);
 	g_free(base64);
-	
+
 	return msnobj;
 }
 
--- a/libpurple/protocols/msn/oim.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/oim.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1,5 +1,5 @@
 /**
- * @file oim.c 
+ * @file oim.c
  * 	get and send MSN offline Instant Message via SOAP request
  *	Author
  * 		MaYuan<mayuan2006@gmail.com>
@@ -91,7 +91,7 @@
 	const char* to_member, const char *msg)
 {
 	MsnOimSendReq *request;
-	
+
 	request = g_new0(MsnOimSendReq, 1);
 	request->from_member	= g_strdup(from_member);
 	request->friendname		= g_strdup(friendname);
@@ -109,7 +109,7 @@
 	g_free(req->friendname);
 	g_free(req->to_member);
 	g_free(req->oim_msg);
-	
+
 	g_free(req);
 }
 
@@ -121,10 +121,10 @@
 msn_oim_msg_to_str(MsnOim *oim, const char *body)
 {
 	char *oim_body,*oim_base64;
-	
-	purple_debug_info("MSN OIM","encode OIM Message...\n");	
+
+	purple_debug_info("MSN OIM","encode OIM Message...\n");
 	oim_base64 = purple_base64_encode((const guchar *)body, strlen(body));
-	purple_debug_info("MSN OIM","encoded base64 body:{%s}\n",oim_base64);	
+	purple_debug_info("MSN OIM","encoded base64 body:{%s}\n",oim_base64);
 	oim_body = g_strdup_printf(MSN_OIM_MSG_TEMPLATE,
 				oim->run_id,oim->send_seq,oim_base64);
 	g_free(oim_base64);
@@ -213,7 +213,7 @@
 }
 
 /*post send single message request to oim server*/
-void 
+void
 msn_oim_send_msg(MsnOim *oim)
 {
 	MsnOimSendReq *oim_request;
@@ -333,7 +333,7 @@
 		gboolean offset_positive = TRUE;
 		int tzhrs;
 		int tzmins;
-		
+
 		for (t.tm_mon = 0;
 			 months[t.tm_mon] != NULL &&
 				 strcmp(months[t.tm_mon], month_str) != 0; t.tm_mon++);
@@ -462,7 +462,7 @@
 	}
 }
 
-/* parse the oim XML data 
+/* parse the oim XML data
  * and post it to the soap server to get the Offline Message
  * */
 void
--- a/libpurple/protocols/msn/oim.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/oim.h	Thu Jun 26 20:25:38 2008 +0000
@@ -1,7 +1,7 @@
 /**
  * @file oim.h			Header file for oim.c
  *	Author
- * 		MaYuan<mayuan2006@gmail.com>
+ *		MaYuan<mayuan2006@gmail.com>
  * purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
@@ -127,4 +127,3 @@
 void msn_oim_send_msg(MsnOim *oim);
 
 #endif/* _MSN_OIM_H_*/
-/*endof oim.h*/
--- a/libpurple/protocols/msn/servconn.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/servconn.c	Thu Jun 26 20:25:38 2008 +0000
@@ -396,15 +396,15 @@
 	if (len <= 0) {
 		switch (errno) {
 
-			case 0:	
+			case 0:
 
 			case EBADF:
 			case EAGAIN: return;
-	
+
 			default: purple_debug_error("msn", "servconn read error,"
 						"len: %d, errno: %d, error: %s\n",
 						len, errno, g_strerror(errno));
-				 msn_servconn_got_error(servconn, 
+				 msn_servconn_got_error(servconn,
 						 MSN_SERVCONN_ERROR_READ);
 				 return;
 		}
--- a/libpurple/protocols/msn/session.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/session.h	Thu Jun 26 20:25:38 2008 +0000
@@ -104,7 +104,6 @@
 
 	int servconns_count; /**< The count of server connections. */
 	GList *switches; /**< The list of all the switchboards. */
-	GList *directconns; /**< The list of all the directconnections. */
 	GList *slplinks; /**< The list of all the slplinks. */
 
 	/*psm info*/
--- a/libpurple/protocols/msn/slp.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/slp.c	Thu Jun 26 20:25:38 2008 +0000
@@ -947,15 +947,15 @@
 msn_release_buddy_icon_request_timeout(gpointer data)
 {
 	MsnUserList *userlist = (MsnUserList *)data;
-	
+
 	/* Free one window slot */
-	userlist->buddy_icon_window++;	
-	
+	userlist->buddy_icon_window++;
+
 	/* Clear the tag for our former request timer */
 	userlist->buddy_icon_request_timer = 0;
-	
+
 	msn_release_buddy_icon_request(userlist);
-	
+
 	return FALSE;
 }
 
@@ -1062,7 +1062,7 @@
 	}
 
 	/* Wait BUDDY_ICON_DELAY ms before freeing our window slot and requesting the next icon. */
-	userlist->buddy_icon_request_timer = purple_timeout_add(BUDDY_ICON_DELAY, 
+	userlist->buddy_icon_request_timer = purple_timeout_add(BUDDY_ICON_DELAY,
 														  msn_release_buddy_icon_request_timeout, userlist);
 }
 
--- a/libpurple/protocols/msn/slplink.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/slplink.c	Thu Jun 26 20:25:38 2008 +0000
@@ -58,7 +58,7 @@
  * Main
  **************************************************************************/
 
-MsnSlpLink *
+static MsnSlpLink *
 msn_slplink_new(MsnSession *session, const char *username)
 {
 	MsnSlpLink *slplink;
@@ -593,7 +593,7 @@
 	}
 	else if (slpmsg->size)
 	{
-		if ((offset + len) > slpmsg->size)
+		if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size)
 		{
 			purple_debug_error("msn",
 				"Oversized slpmsg - msgsize=%lld offset=%" G_GSIZE_FORMAT " len=%" G_GSIZE_FORMAT "\n",
--- a/libpurple/protocols/msn/slplink.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/slplink.h	Thu Jun 26 20:25:38 2008 +0000
@@ -59,11 +59,21 @@
 	GQueue *slp_msg_queue;
 };
 
-MsnSlpLink *msn_slplink_new(MsnSession *session, const char *username);
 void msn_slplink_destroy(MsnSlpLink *slplink);
+
+/**
+ * @return An MsnSlpLink for the given user, or NULL if there is no
+ *         existing MsnSlpLink.
+ */
 MsnSlpLink *msn_session_find_slplink(MsnSession *session,
 									 const char *who);
+
+/**
+ * @return An MsnSlpLink for the given user.  One will be created if
+ *         it does not already exist.
+ */
 MsnSlpLink *msn_session_get_slplink(MsnSession *session, const char *username);
+
 MsnSlpSession *msn_slplink_find_slp_session(MsnSlpLink *slplink,
 											long session_id);
 void msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall);
--- a/libpurple/protocols/msn/soap.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/soap.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1,5 +1,5 @@
 /**
- * @file soap.c 
+ * @file soap.c
  * 	SOAP connection related process
  *	Author
  * 		MaYuan<mayuan2006@gmail.com>
@@ -107,7 +107,7 @@
 /*ssl soap error callback*/
 static void
 msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
-{	
+{
 	MsnSoapConn * soapconn = data;
 
 	g_return_if_fail(data != NULL);
@@ -159,7 +159,7 @@
 	if (*handler > 0) {
 		purple_input_remove(*handler);
 		*handler = 0;
-	} 
+	}
 #ifdef MSN_SOAP_DEBUG
 	else {
 		purple_debug_misc("MSN SOAP", "Handler inactive, not removing\n");
@@ -250,7 +250,7 @@
 {
 	gssize len, requested_len;
 	char temp_buf[MSN_SOAP_READ_BUFF_SIZE];
-	
+
 	if ( soapconn->need_to_read == 0 || soapconn->need_to_read > MSN_SOAP_READ_BUFF_SIZE) {
 		requested_len = MSN_SOAP_READ_BUFF_SIZE;
 	}
@@ -264,7 +264,7 @@
 		len = read(soapconn->fd, temp_buf, requested_len);
 	}
 
-	
+
 	if ( len <= 0 ) {
 		switch (errno) {
 
@@ -298,7 +298,7 @@
 				soapconn->read_len + len + 1);
 			exit(EXIT_FAILURE);
 		}
-			
+
 	}
 
 #if defined(MSN_SOAP_DEBUG)
@@ -312,7 +312,7 @@
 }
 
 /*read the whole SOAP server response*/
-static void 
+static void
 msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	MsnSoapConn *soapconn = data;
@@ -331,10 +331,10 @@
 	session = soapconn->session;
 	g_return_if_fail(session != NULL);
 
-	
+
 	/*read the request header*/
 	len = msn_soap_read(soapconn);
-	
+
 	if ( len < 0 )
 		return;
 
@@ -342,7 +342,7 @@
 		return;
 	}
 
-	if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL) 
+	if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL)
 		|| ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) )
 	{
 		/* Redirect. */
@@ -382,14 +382,14 @@
 
 		g_free(soapconn->login_host);
 		soapconn->login_host = g_strdup(location);
-		
+
 		msn_soap_close_handler( &(soapconn->input_handler) );
 		msn_soap_close(soapconn);
 
 		if (purple_ssl_connect(session->account, soapconn->login_host,
 			PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb,
 			msn_soap_error_cb, soapconn) == NULL) {
-		
+
 			purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host);
 			// dispatch next request
 			msn_soap_post(soapconn, NULL);
@@ -429,7 +429,7 @@
 
 		g_free(soapconn->login_host);
 		soapconn->login_host = g_strdup(location);
-		
+
 		msn_soap_close_handler( &(soapconn->input_handler) );
 		msn_soap_close(soapconn);
 
@@ -489,7 +489,7 @@
 				}
 			}
 		}
-		
+
 	}
 	else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable"))
 	{
@@ -539,11 +539,11 @@
 #if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
 
 		node = xmlnode_from_str(soapconn->body, soapconn->body_len);
-	
+
 		if (node != NULL) {
 			formattedxml = xmlnode_to_formatted_str(node, NULL);
 			http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf);
-				
+
 			purple_debug_info("MSN SOAP","Data with XML payload received from the SOAP server:\n%s%s\n", http_headers, formattedxml);
 			g_free(http_headers);
 			g_free(formattedxml);
@@ -572,22 +572,22 @@
 		if ( soapconn->read_cb != NULL ) {
 			soapconn_is_valid = soapconn->read_cb(soapconn);
 		}
-		
+
 		if (!soapconn_is_valid) {
 			return;
 		}
 
 		/* dispatch next request in queue */
 		msn_soap_post(soapconn, NULL);
-	}	
+	}
 	return;
 }
 
-void 
+void
 msn_soap_free_read_buf(MsnSoapConn *soapconn)
 {
 	g_return_if_fail(soapconn != NULL);
-	
+
 	if (soapconn->read_buf) {
 		g_free(soapconn->read_buf);
 	}
@@ -626,7 +626,7 @@
 	}
 	total_len = strlen(soapconn->write_buf);
 
-	/* 
+	/*
 	 * write the content to SSL server,
 	 */
 	len = purple_ssl_write(soapconn->gsc,
@@ -690,7 +690,7 @@
 	soapconn->write_buf = write_buf;
 	soapconn->written_len = 0;
 	soapconn->written_cb = written_cb;
-	
+
 	msn_soap_free_read_buf(soapconn);
 
 	/*clear the read buffer first*/
@@ -748,7 +748,7 @@
 {
 	g_return_if_fail(soapconn != NULL);
 	g_return_if_fail(soapconn->soap_queue != NULL);
-	
+
 	if (soapconn->step == MSN_SOAP_CONNECTED ||
 	    soapconn->step == MSN_SOAP_CONNECTED_IDLE) {
 
@@ -868,7 +868,7 @@
 	else
 		purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str);
 #endif
-	
+
 	/*free read buffer*/
 	// msn_soap_free_read_buf(soapconn);
 	/*post it to server*/
--- a/libpurple/protocols/msn/soap.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/soap.h	Thu Jun 26 20:25:38 2008 +0000
@@ -66,7 +66,7 @@
 	char *soap_action;
 
 	char *body;
-	
+
 	gpointer data_cb;
 	MsnSoapReadCbFunction read_cb;
 	MsnSoapWrittenCbFunction written_cb;
--- a/libpurple/protocols/msn/soap2.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/soap2.c	Thu Jun 26 20:25:38 2008 +0000
@@ -82,6 +82,7 @@
 
 static void msn_soap_request_destroy(MsnSoapRequest *req);
 static void msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect);
+static void msn_soap_process(MsnSoapConnection *conn);
 
 static gboolean
 msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data)
@@ -264,45 +265,53 @@
 msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
 {
 	MsnSoapConnection *conn = data;
-	int count = 0, cnt;
-	char buf[8192];
-	char *linebreak;
-	char *cursor;
-	gboolean handled = FALSE;
+	int count = 0, cnt, perrno;
+	/* This buffer needs to be larger than any packets received from
+		login.live.com or Adium will fail to receive the packet
+		(something weird with the login.live.com server). With NSS it works
+		fine, so I believe it's some bug with OS X */ 
+	char buf[16 * 1024];
 
 	if (conn->message == NULL) {
 		conn->message = msn_soap_message_new(NULL, NULL);
 	}
 
+	if (conn->buf == NULL) {
+		conn->buf = g_string_new_len(buf, 0);
+	}
+	
 	while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) {
 		purple_debug_info("soap", "read %d bytes\n", cnt);
 		count += cnt;
-		if (conn->buf == NULL) {
-			conn->buf = g_string_new_len(buf, cnt);
-		} else {
-			g_string_append_len(conn->buf, buf, cnt);
-		}
+		g_string_append_len(conn->buf, buf, cnt);
 	}
 
-	if (cnt < 0) {
-		if (errno != EAGAIN) {
-			purple_debug_info("soap", "read: %s\n", g_strerror(errno));
+	/* && count is necessary for Adium, on OS X the last read always
+	   return an error, so we want to proceed anyway. See #5212 for
+	   discussion on this and the above buffer size issues */
+	if(cnt < 0 && errno == EAGAIN && count == 0)
+		return;
+
+	// msn_soap_process could alter errno
+	perrno = errno;
+	msn_soap_process(conn);
+	
+	if (cnt < 0 && perrno != EAGAIN) {
+		purple_debug_info("soap", "read: %s\n", g_strerror(perrno));
+		// It's possible msn_soap_process closed the ssl connection
+		if (conn->ssl) {
 			purple_ssl_close(conn->ssl);
 			conn->ssl = NULL;
 			msn_soap_connection_handle_next(conn);
-			return;
-		} else if (count == 0) {
-			return;
 		}
 	}
+}
 
-	if (cnt == 0 && count == 0) {
-		purple_debug_info("soap", "msn_soap_read_cb() called, but no data available?\n");
-		purple_ssl_close(conn->ssl);
-		conn->ssl = NULL;
-		msn_soap_connection_handle_next(conn);
-		return;
-	}
+static void
+msn_soap_process(MsnSoapConnection *conn) {
+	gboolean handled = FALSE;
+	char *cursor;
+	char *linebreak;
 
 	purple_debug_info("soap", "current %s\n", conn->buf->str);
 
--- a/libpurple/protocols/msn/state.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/state.c	Thu Jun 26 20:25:38 2008 +0000
@@ -150,7 +150,7 @@
 {
 	xmlnode *payloadNode, *currentmediaNode;
 	char *currentmedia;
-	
+
 	purple_debug_info("msn","msn get CurrentMedia\n");
 	payloadNode = xmlnode_from_str(xml_str, len);
 	if (!payloadNode){
@@ -176,7 +176,7 @@
 {
 	xmlnode *payloadNode, *psmNode;
 	char *psm;
-	
+
 	purple_debug_info("MSNP14","msn get PSM\n");
 	payloadNode = xmlnode_from_str(xml_str, len);
 	if (!payloadNode){
@@ -217,7 +217,7 @@
 	return ret;
 }
 
-/* set the MSN's PSM info,Currently Read from the status Line 
+/* set the MSN's PSM info,Currently Read from the status Line
  * Thanks for Cris Code
  */
 void
--- a/libpurple/protocols/msn/transaction.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/transaction.h	Thu Jun 26 20:25:38 2008 +0000
@@ -62,7 +62,7 @@
 };
 
 MsnTransaction *msn_transaction_new(MsnCmdProc *cmdproc, const char *command,
- 	const char *format, ...) G_GNUC_PRINTF(3, 4);
+	const char *format, ...) G_GNUC_PRINTF(3, 4);
 void msn_transaction_destroy(MsnTransaction *trans);
 
 char *msn_transaction_to_string(MsnTransaction *trans);
--- a/libpurple/protocols/msn/user.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/user.c	Thu Jun 26 20:25:38 2008 +0000
@@ -122,7 +122,7 @@
 					NULL);
 		} else {
 			purple_prpl_got_user_status_deactive(account, user->passport, "tune");
-		}			
+		}
 	}
 
 	if (user->idle)
@@ -239,7 +239,7 @@
 msn_user_unset_op(MsnUser *user, int list_op)
 {
 	g_return_if_fail(user != NULL);
-	
+
 	user->list_op &= ~list_op;
 }
 
--- a/libpurple/protocols/msn/user.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/user.h	Thu Jun 26 20:25:38 2008 +0000
@@ -57,9 +57,6 @@
  */
 struct _MsnUser
 {
-#if 0
-	MsnSession *session;    /**< The MSN session.               */
-#endif
 	MsnUserList *userlist;
 
 	char *passport;         /**< The passport account.          */
@@ -69,7 +66,7 @@
 	char * uid;				/*< User Id							*/
 
 	const char *status;     /**< The state of the user.         */
-	char *statusline;       /**< The state of the user.         */	
+	char *statusline;       /**< The state of the user.         */
 	CurrentMedia media;     /**< Current media of the user.     */
 
 	gboolean idle;          /**< The idle state of the user.    */
@@ -135,7 +132,7 @@
 
  /**
   *  Sets the new statusline of user.
-  * 
+  *
   *  @param user The user.
   *  @param state The statusline string.
   */
@@ -143,7 +140,7 @@
 
  /**
   *  Sets the current media of user.
-  * 
+  *
   *  @param user   The user.
   *  @param cmedia Current media.
   */
--- a/libpurple/protocols/msn/userlist.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/userlist.c	Thu Jun 26 20:25:38 2008 +0000
@@ -48,7 +48,7 @@
 	{
 		MsnSession *session = pa->gc->proto_data;
 		MsnUserList *userlist = session->userlist;
-		
+
 		msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL);
 
 		msn_del_contact_from_list(session->contact, NULL, pa->who, MSN_LIST_PL);
@@ -127,7 +127,7 @@
 
 	if (user == NULL)
 		return FALSE;
-	
+
 	list_op = 1 << list_id;
 
 	if (user->list_op & list_op)
@@ -237,7 +237,7 @@
  		if (convo) {
  			PurpleBuddy *buddy;
  			char *msg;
- 
+
  			buddy = purple_find_buddy(account, passport);
  			msg = g_strdup_printf(
  				_("%s has added you to his or her buddy list."),
@@ -246,7 +246,7 @@
  				PURPLE_MESSAGE_SYSTEM, time(NULL));
  			g_free(msg);
  		}
- 
+
 		if (!(user->list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
 		{
 			/*
@@ -341,7 +341,7 @@
 
 	passport = msn_user_get_passport(user);
 	store = msn_user_get_store_name(user);
-	
+
 	msn_user_set_op(user, list_op);
 
 	if (list_op & MSN_LIST_FL_OP)
@@ -407,7 +407,7 @@
 
 	userlist->session = session;
 	userlist->buddy_icon_requests = g_queue_new();
-	
+
 	/* buddy_icon_window is the number of allowed simultaneous buddy icon requests.
 	 * XXX With smarter rate limiting code, we could allow more at once... 5 was the limit set when
 	 * we weren't retrieiving any more than 5 per MSN session. */
@@ -644,12 +644,12 @@
 msn_userlist_rem_buddy(MsnUserList *userlist, const char *who)
 {
 	MsnUser *user = NULL;
-	
+
 	g_return_if_fail(userlist != NULL);
 	g_return_if_fail(userlist->session != NULL);
 	g_return_if_fail(userlist->session->contact != NULL);
 	g_return_if_fail(who != NULL);
-	
+
 	user = msn_userlist_find_user(userlist, who);
 
 	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_FL);
@@ -669,9 +669,9 @@
 	MsnListOp list_op = 1 << list_id;
 
 	user = msn_userlist_find_user(userlist, who);
-	
+
 	g_return_if_fail(user != NULL);
-	
+
 	if ( !msn_userlist_user_is_in_list(user, list_id)) {
 		list = lists[list_id];
 		purple_debug_info("MSN Userlist", "User %s is not in list %s, not removing.\n", who, list);
@@ -690,14 +690,14 @@
 	MsnUser *user;
 	MsnCallbackState *state = NULL;
 	const char *group_id = NULL, *new_group_name;
-	
+
 	new_group_name = group_name == NULL ? MSN_INDIVIDUALS_GROUP_NAME : group_name;
 
-	
+
 	g_return_if_fail(userlist != NULL);
 	g_return_if_fail(userlist->session != NULL);
 
-	
+
 	purple_debug_info("MSN Userlist", "Add user: %s to group: %s\n", who, new_group_name);
 
 	state = msn_callback_state_new(userlist->session);
@@ -709,9 +709,9 @@
 		/* only notify the user about problems adding to the friends list
 		 * maybe we should do something else for other lists, but it probably
 		 * won't cause too many problems if we just ignore it */
-		
+
 		char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
-		
+
 		purple_notify_error(NULL, NULL, str,
 				  _("The username specified is invalid."));
 		g_free(str);
@@ -725,7 +725,7 @@
 	{
 		/* Whoa, we must add that group first. */
 		purple_debug_info("MSN Userlist", "Adding user %s to a new group, creating group %s first\n", who, new_group_name);
-		
+
 		msn_callback_state_set_action(state, MSN_ADD_BUDDY);
 
 		msn_add_group(userlist->session, state, new_group_name);
@@ -733,9 +733,9 @@
 	} else {
 		msn_callback_state_set_guid(state, group_id);
 	}
-	
+
 	/* XXX: adding user here may not be correct (should add them in the
- 	 * ACK to the ADL command), but for now we need to make sure they exist  
+ 	 * ACK to the ADL command), but for now we need to make sure they exist
 	 * early enough that the ILN command doesn't screw us up */
 
 	user = msn_userlist_find_add_user(userlist, who, who);
@@ -751,7 +751,7 @@
 			return;
 		}
 	}
-			
+
 	purple_debug_info("MSN Userlist", "Adding user: %s to group id: %s\n", who, group_id);
 
 	msn_callback_state_set_action(state, MSN_ADD_BUDDY);
@@ -762,7 +762,7 @@
 }
 
 void
-msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, 
+msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
 							MsnListId list_id)
 {
 	MsnUser *user = NULL;
@@ -770,9 +770,9 @@
 	MsnListOp list_op = 1 << list_id;
 
 	g_return_if_fail(userlist != NULL);
-	
+
 	user = msn_userlist_find_add_user(userlist, who, who);
-	
+
 	/* First we're going to check if it's already there. */
 	if (msn_userlist_user_is_in_list(user, list_id))
 	{
@@ -780,16 +780,16 @@
 		purple_debug_info("MSN Userlist", "User '%s' is already in list: %s\n", who, list);
 		return;
 	}
-	
+
 	//store_name = (user != NULL) ? get_store_name(user) : who;
-	
+
 	//purple_debug_info("MSN Userlist", "store_name = %s\n", store_name);
-	
+
 	/* XXX: see XXX above, this should really be done when we get the response from
 		the server */
-	
+
 	msn_user_set_op(user, list_op);
-	
+
 	msn_notification_add_buddy_to_list(userlist->session->notification, list_id, who);
 }
 
@@ -799,7 +799,7 @@
 {
 	MsnUser *user;
 	gchar * group_id;
-	
+
 	g_return_val_if_fail(userlist != NULL, FALSE);
 	g_return_val_if_fail(group_name != NULL, FALSE);
 	g_return_val_if_fail(who != NULL, FALSE);
@@ -815,7 +815,7 @@
 		purple_debug_error("MSN Userlist", "User %s not found!", who);
 		return FALSE;
 	}
-	
+
 	msn_user_add_group_id(user, group_id);
 
 	return TRUE;
@@ -832,7 +832,7 @@
 	g_return_val_if_fail(userlist != NULL, FALSE);
 	g_return_val_if_fail(group_name != NULL, FALSE);
 	g_return_val_if_fail(who != NULL, FALSE);
-	
+
 	purple_debug_info("MSN Userlist","Removing buddy with passport %s from group %s\n", who, group_name);
 
 	if ( (group_id = msn_userlist_find_group_id(userlist, group_name)) == NULL) {
@@ -856,7 +856,7 @@
 {
 	const char *new_group_id;
 	MsnCallbackState *state;
-	
+
 	g_return_if_fail(userlist != NULL);
 	g_return_if_fail(userlist->session != NULL);
 	g_return_if_fail(userlist->session->contact != NULL);
@@ -870,11 +870,11 @@
 	new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
 
 	if (new_group_id == NULL)
-	{		
+	{
 		msn_add_group(userlist->session, state, new_group_name);
 		return;
 	}
-	
+
 	/* add the contact to the new group, and remove it from the old one in
 	 * the callback
 	*/
@@ -928,6 +928,6 @@
 						(char *)l->data,NULL);
 		msn_user_set_op(user, MSN_LIST_BL_OP);
 	}
-	
+
 }
 
--- a/libpurple/protocols/msn/userlist.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msn/userlist.h	Thu Jun 26 20:25:38 2008 +0000
@@ -45,11 +45,8 @@
 {
 	MsnSession *session;
 
-	/* MsnUsers *users; */
-	/* MsnGroups *groups; */
-
-	GList *users;
-	GList *groups;
+	GList *users; /* Contains MsnUsers */
+	GList *groups; /* Contains MsnGroups */
 
 	GQueue *buddy_icon_requests;
 	int buddy_icon_window;
@@ -94,7 +91,7 @@
 void msn_userlist_remove_group_id(MsnUserList *userlist, const char *group_id);
 
 void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who);
-void msn_userlist_add_buddy(MsnUserList *userlist, 
+void msn_userlist_add_buddy(MsnUserList *userlist,
 			    const char *who, const char *group_name);
 void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
 						    const char *old_group_name,
@@ -106,7 +103,7 @@
 					   const char *who,
 					   const char *group_name);
 
-void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, 
+void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
 				    MsnListId list_id);
 void msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
 				      MsnListId list_id);
--- a/libpurple/protocols/msnp9/slplink.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/msnp9/slplink.c	Thu Jun 26 20:25:38 2008 +0000
@@ -597,7 +597,7 @@
 	}
 	else if (slpmsg->size)
 	{
-		if ((offset + len) > slpmsg->size)
+		if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size)
 		{
 			purple_debug_error("msn", "Oversized slpmsg\n");
 			g_return_if_reached();
--- a/libpurple/protocols/myspace/myspace.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1328,7 +1328,10 @@
 	delta = time(NULL) - session->last_comm;
 	/* purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); */
 	if (delta >= MSIM_KEEPALIVE_INTERVAL) {
-		errmsg = g_strdup_printf(_("Connection to server lost (no data received within %d seconds)"), (int)delta);
+	        errmsg = g_strdup_printf(ngettext("Connection to server lost (no data received within %d second)",
+						  "Connection to server lost (no data received within %d seconds)",
+						  (int)delta),
+					 (int)delta);
 
 		purple_debug_info("msim", "msim_check_alive: %s > interval of %d, presumed dead\n",
 				errmsg, MSIM_KEEPALIVE_INTERVAL);
@@ -2404,7 +2407,7 @@
 		const char *username;
 
 		/* If the account does not exist, we can't look up the user. */
-		if (!account)
+		if (!account || !account->gc)
 			return str;
 
 		id = atol(str);
@@ -2946,7 +2949,10 @@
 
 	switch (GPOINTER_TO_UINT(user_data)) {
 		case MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS:
-			msg = g_strdup_printf(_("%d buddies were added or updated from the server (including buddies already on the server-side list)"), buddy_count);
+		        msg = g_strdup_printf(ngettext("%d buddy was added or updated from the server (including buddies already on the server-side list)",
+						       "%d buddies were added or updated from the server (including buddies already on the server-side list)",
+						       buddy_count),
+					      buddy_count);
 			purple_notify_info(session->account, _("Add contacts from server"), msg, NULL);
 			g_free(msg);
 			break;
--- a/libpurple/protocols/myspace/user.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/myspace/user.c	Thu Jun 26 20:25:38 2008 +0000
@@ -635,7 +635,7 @@
 	if (!body) {
 		purple_debug_info("msim_username_is_available_cb", "No body for %s?!\n", username);
 		purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, 
-				"An error occured while trying to set the username.\n"
+				"An error occurred while trying to set the username.\n"
 				"Please try again, or visit http://editprofile.myspace.com/index.cfm?"
 				"fuseaction=profile.username to set your username.");
 		return;
@@ -778,7 +778,7 @@
 	uid = msim_msg_get_integer(userinfo, "uid");
 	lid = msim_msg_get_integer(userinfo, "lid");
 	body = msim_msg_get_dictionary(userinfo, "body");
-	errmsg = g_strdup("An error occured while trying to set the username.\n"
+	errmsg = g_strdup("An error occurred while trying to set the username.\n"
 			"Please try again, or visit http://editprofile.myspace.com/index.cfm?"
 			"fuseaction=profile.username to set your username.");
 	
--- a/libpurple/protocols/oscar/family_admin.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_admin.c	Thu Jun 26 20:25:38 2008 +0000
@@ -47,8 +47,8 @@
 	byte_stream_put16(&bs, info);
 	byte_stream_put16(&bs, 0x0000);
 
-	snacid = aim_cachesnac(od, 0x0007, 0x0002, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0007, 0x0002, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0002, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -127,8 +127,8 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	snacid = aim_cachesnac(od, 0x0007, 0x0004, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0007, 0x0004, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -154,8 +154,8 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	snacid = aim_cachesnac(od, 0x0007, 0x0004, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0007, 0x0004, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -177,8 +177,8 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	snacid = aim_cachesnac(od, 0x0007, 0x0004, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0007, 0x0004, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -194,11 +194,11 @@
 void
 aim_admin_reqconfirm(OscarData *od, FlapConnection *conn)
 {
-	aim_genericreq_n(od, conn, 0x0007, 0x0006);
+	aim_genericreq_n(od, conn, SNAC_FAMILY_ADMIN, 0x0006);
 }
 
 /**
- * Subtype 0x0007 - Account confirmation request acknowledgement.
+ * Subtype SNAC_FAMILY_ADMIN - Account confirmation request acknowledgement.
  */
 static int
 accountconfirm(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
@@ -227,7 +227,7 @@
 	if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005)) {
 		infochange(od, conn, mod, frame, snac, bs);
 		return 1;
-	} else if (snac->subtype == 0x0007)
+	} else if (snac->subtype == SNAC_FAMILY_ADMIN)
 		return accountconfirm(od, conn, mod, frame, snac, bs);
 
 	return 0;
@@ -235,7 +235,7 @@
 
 int admin_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0007;
+	mod->family = SNAC_FAMILY_ADMIN;
 	mod->version = 0x0001;
 	mod->toolid = 0x0010;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_advert.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_advert.c	Thu Jun 26 20:25:38 2008 +0000
@@ -28,7 +28,7 @@
 void
 aim_ads_requestads(OscarData *od, FlapConnection *conn)
 {
-	aim_genericreq_n(od, conn, 0x0005, 0x0002);
+	aim_genericreq_n(od, conn, SNAC_FAMILY_ADVERT, 0x0002);
 }
 
 static int snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *rx, aim_modsnac_t *snac, ByteStream *bs)
@@ -39,7 +39,7 @@
 int adverts_modfirst(OscarData *od, aim_module_t *mod)
 {
 
-	mod->family = 0x0005;
+	mod->family = SNAC_FAMILY_ADVERT;
 	mod->version = 0x0001;
 	mod->toolid = 0x0001;
 	mod->toolversion = 0x0001;
--- a/libpurple/protocols/oscar/family_alert.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_alert.c	Thu Jun 26 20:25:38 2008 +0000
@@ -72,8 +72,8 @@
 	byte_stream_put16(&bs, 0xb0ee);
 	byte_stream_put16(&bs, 0x0631);
 
-	snacid = aim_cachesnac(od, 0x0018, 0x0006, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0018, 0x0006, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0006, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -188,8 +188,8 @@
 	byte_stream_put32(&bs, 0x04000000);
 	byte_stream_put32(&bs, 0x00000000);
 
-	snacid = aim_cachesnac(od, 0x0018, 0x0016, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0018, 0x0006, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0016, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -225,7 +225,7 @@
 int
 email_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0018;
+	mod->family = SNAC_FAMILY_ALERT;
 	mod->version = 0x0001;
 	mod->toolid = 0x0010;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_auth.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Thu Jun 26 20:25:38 2008 +0000
@@ -81,12 +81,9 @@
 static int
 aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
 {
-	PurpleCipher *cipher;
 	PurpleCipherContext *context;
 
-	cipher = purple_ciphers_find_cipher("md5");
-
-	context = purple_cipher_context_new(cipher, NULL);
+	context = purple_cipher_context_new_by_name("md5", NULL);
 	purple_cipher_context_append(context, (const guchar *)key, strlen(key));
 	purple_cipher_context_append(context, (const guchar *)password, password_len);
 	purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
@@ -200,9 +197,13 @@
  *        usually happens for AOL accounts.  We are told that we
  *        should truncate it if the 0x0017/0x0007 SNAC contains
  *        a TLV of type 0x0026 with data 0x0000.
+ * @param allow_multiple_logins Allow multiple logins? If TRUE, the AIM
+ *        server will prompt the user when multiple logins occur. If
+ *        FALSE, existing connections (on other clients) will be
+ *        disconnected automatically as we connect.
  */
 int
-aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key)
+aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins)
 {
 	FlapFrame *frame;
 	GSList *tlvlist = NULL;
@@ -221,8 +222,8 @@
 
 	frame = flap_frame_new(od, 0x02, 1152);
 
-	snacid = aim_cachesnac(od, 0x0017, 0x0002, 0x0000, NULL, 0);
-	aim_putsnac(&frame->data, 0x0017, 0x0002, 0x0000, snacid);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0002, 0x0000, snacid);
 
 	aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
 
@@ -256,7 +257,7 @@
 	 * If set, old-fashioned buddy lists will not work. You will need
 	 * to use SSI.
 	 */
-	aim_tlvlist_add_8(&tlvlist, 0x004a, 0x01);
+	aim_tlvlist_add_8(&tlvlist, 0x004a, (allow_multiple_logins ? 0x01 : 0x02));
 
 	aim_tlvlist_write(&frame->data, &tlvlist);
 
@@ -403,7 +404,7 @@
 
 	od->authinfo = info;
 
-	if ((userfunc = aim_callhandler(od, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003)))
+	if ((userfunc = aim_callhandler(od, snac ? snac->family : SNAC_FAMILY_AUTH, snac ? snac->subtype : 0x0003)))
 		ret = userfunc(od, conn, frame, info);
 
 	aim_tlvlist_free(tlvlist);
@@ -449,7 +450,7 @@
 	FlapFrame frame;
 	aim_rxcallback_t userfunc;
 
-	if ((userfunc = aim_callhandler(od, 0x0017, 0x0007)))
+	if ((userfunc = aim_callhandler(od, SNAC_FAMILY_AUTH, 0x0007)))
 		userfunc(od, conn, &frame, "");
 
 	return 0;
@@ -483,8 +484,8 @@
 
 	frame = flap_frame_new(od, 0x02, 10+2+2+strlen(sn)+8);
 
-	snacid = aim_cachesnac(od, 0x0017, 0x0006, 0x0000, NULL, 0);
-	aim_putsnac(&frame->data, 0x0017, 0x0006, 0x0000, snacid);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0006, 0x0000, snacid);
 
 	aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
 
@@ -628,7 +629,7 @@
 int
 auth_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0017;
+	mod->family = SNAC_FAMILY_AUTH;
 	mod->version = 0x0000;
 	mod->flags = 0;
 	strncpy(mod->name, "auth", sizeof(mod->name));
--- a/libpurple/protocols/oscar/family_bart.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_bart.c	Thu Jun 26 20:25:38 2008 +0000
@@ -43,7 +43,7 @@
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0010)) || !icon || !iconlen)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !icon || !iconlen)
 		return -EINVAL;
 
 	byte_stream_new(&bs, 2 + 2 + iconlen);
@@ -55,8 +55,8 @@
 	byte_stream_put16(&bs, iconlen);
 	byte_stream_putraw(&bs, icon, iconlen);
 
-	snacid = aim_cachesnac(od, 0x0010, 0x0002, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0010, 0x0002, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0002, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -102,7 +102,7 @@
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0010)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen)
 		return -EINVAL;
 
 	byte_stream_new(&bs, 1+strlen(sn) + 4 + 1+iconcsumlen);
@@ -120,8 +120,8 @@
 	byte_stream_put8(&bs, iconcsumlen);
 	byte_stream_putraw(&bs, iconcsum, iconcsumlen);
 
-	snacid = aim_cachesnac(od, 0x0010, 0x0004, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0010, 0x0004, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0004, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -174,7 +174,7 @@
 int
 bart_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0010;
+	mod->family = SNAC_FAMILY_BART;
 	mod->version = 0x0001;
 	mod->toolid = 0x0010;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_bos.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_bos.c	Thu Jun 26 20:25:38 2008 +0000
@@ -32,7 +32,7 @@
 void
 aim_bos_reqrights(OscarData *od, FlapConnection *conn)
 {
-	aim_genericreq_n_snacid(od, conn, 0x0009, 0x0002);
+	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_BOS, 0x0002);
 }
 
 /* Subtype 0x0003 - BOS Rights. */
@@ -81,7 +81,7 @@
 void
 aim_bos_setgroupperm(OscarData *od, FlapConnection *conn, guint32 mask)
 {
-	aim_genericreq_l(od, conn, 0x0009, 0x0004, &mask);
+	aim_genericreq_l(od, conn, SNAC_FAMILY_BOS, 0x0004, &mask);
 }
 
 /*
@@ -153,8 +153,8 @@
 	}
 	g_free(localcpy);
 
-	snacid = aim_cachesnac(od, 0x0009, subtype, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0009, subtype, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_BOS, subtype, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_BOS, subtype, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -173,7 +173,7 @@
 int
 bos_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0009;
+	mod->family = SNAC_FAMILY_BOS;
 	mod->version = 0x0001;
 	mod->toolid = 0x0110;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_buddy.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_buddy.c	Thu Jun 26 20:25:38 2008 +0000
@@ -108,8 +108,8 @@
 	byte_stream_put8(&bs, strlen(sn));
 	byte_stream_putstr(&bs, sn);
 
-	snacid = aim_cachesnac(od, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
-	flap_connection_send_snac(od, conn, 0x0003, 0x0004, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, sn, strlen(sn)+1);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -159,8 +159,8 @@
 		tmpptr = strtok(NULL, "&");
 	}
 
-	snacid = aim_cachesnac(od, 0x0003, 0x0004, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0003, 0x0004, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -190,8 +190,8 @@
 	byte_stream_put8(&bs, strlen(sn));
 	byte_stream_putstr(&bs, sn);
 
-	snacid = aim_cachesnac(od, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
-	flap_connection_send_snac(od, conn, 0x0003, 0x0005, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0005, 0x0000, sn, strlen(sn)+1);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0005, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -243,7 +243,7 @@
 int
 buddylist_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0003;
+	mod->family = SNAC_FAMILY_BUDDY;
 	mod->version = 0x0001;
 	mod->toolid = 0x0110;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_chat.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_chat.c	Thu Jun 26 20:25:38 2008 +0000
@@ -364,7 +364,7 @@
 
 	byte_stream_new(&bs, 1142);
 
-	snacid = aim_cachesnac(od, 0x000e, 0x0005, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_CHAT, 0x0005, 0x0000, NULL, 0);
 
 	/*
 	 * Cookie
@@ -432,7 +432,7 @@
 	aim_tlvlist_free(inner_tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x000e, 0x0005, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_CHAT, 0x0005, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -594,7 +594,7 @@
 int
 chat_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x000e;
+	mod->family = SNAC_FAMILY_CHAT;
 	mod->version = 0x0001;
 	mod->toolid = 0x0010;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_chatnav.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_chatnav.c	Thu Jun 26 20:25:38 2008 +0000
@@ -42,7 +42,7 @@
 		return 0;
 	}
 
-	if (snac2->family != 0x000d) {
+	if (snac2->family != SNAC_FAMILY_CHATNAV) {
 		purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2->family);
 		return 0;
 	}
@@ -80,7 +80,7 @@
  */
 void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn)
 {
-	aim_genericreq_n_snacid(od, conn, 0x000d, 0x0002);
+	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_CHATNAV, 0x0002);
 }
 
 /*
@@ -97,7 +97,7 @@
 
 	byte_stream_new(&bs, 1142);
 
-	snacid = aim_cachesnac(od, 0x000d, 0x0008, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, NULL, 0);
 
 	/* exchange */
 	byte_stream_put16(&bs, exchange);
@@ -137,7 +137,7 @@
 
 	aim_tlvlist_free(tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x000d, 0x0008, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -460,7 +460,7 @@
 		return 0;
 	}
 
-	if (snac2->family != 0x000d) {
+	if (snac2->family != SNAC_FAMILY_CHATNAV) {
 		purple_debug_misc("oscar", "faim: chatnav_parse_info: received response that maps to corrupt request! (fam=%04x)\n", snac2->family);
 		return 0;
 	}
@@ -506,7 +506,7 @@
 int
 chatnav_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x000d;
+	mod->family = SNAC_FAMILY_CHATNAV;
 	mod->version = 0x0001;
 	mod->toolid = 0x0010;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_icbm.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Thu Jun 26 20:25:38 2008 +0000
@@ -164,7 +164,7 @@
 	ByteStream bs;
 	aim_snacid_t snacid;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
 	if (!params)
@@ -182,8 +182,8 @@
 	byte_stream_put16(&bs, params->maxrecverwarn);
 	byte_stream_put32(&bs, params->minmsginterval);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0002, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0004, 0x0002, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0002, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -198,10 +198,10 @@
 {
 	FlapConnection *conn;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
-	aim_genericreq_n_snacid(od, conn, 0x0004, 0x0004);
+	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_ICBM, 0x0004);
 
 	return 0;
 }
@@ -237,7 +237,7 @@
  * Possible flags:
  *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
  *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
- *                        when the message is received (of type 0x0004/0x000c)
+ *                        when the message is received (of type SNAC_FAMILY_ICBM/0x000c)
  *   AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
  *                        online (probably ICQ only).
  *
@@ -280,7 +280,7 @@
 	int msgtlvlen;
 	static const guint8 deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
 	if (!args)
@@ -410,9 +410,9 @@
 	}
 
 	/* XXX - should be optional */
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
-
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &data);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
+
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &data);
 	byte_stream_destroy(&data);
 
 	/* clean out SNACs over 60sec old */
@@ -462,7 +462,7 @@
 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
 	if (!sn || !msg || !roomname)
@@ -472,7 +472,7 @@
 
 	byte_stream_new(&bs, 1142+strlen(sn)+strlen(roomname)+strlen(msg));
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, sn, strlen(sn)+1);
 
 	/* XXX should be uncached by an unwritten 'invite accept' handler */
 	priv = g_malloc(sizeof(struct aim_invite_priv));
@@ -519,7 +519,7 @@
 	aim_tlvlist_free(inner_tlvlist);
 	aim_tlvlist_free(outer_tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -539,7 +539,7 @@
 	aim_snacid_t snacid;
 	guchar cookie[8];
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
 	if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
@@ -549,7 +549,7 @@
 
 	byte_stream_new(&bs, 8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -589,7 +589,7 @@
 	byte_stream_put16(&bs, 0x0003);
 	byte_stream_put16(&bs, 0x0000);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -620,7 +620,7 @@
 	const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* OSCAR_CAPABILITY_ICQRTF capability in string form */
 	int servdatalen;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
 	if (!args || !args->destsn || !args->rtfmsg)
@@ -632,7 +632,7 @@
 
 	byte_stream_new(&bs, 128+servdatalen);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, cookie, 0x0002, args->destsn);
@@ -682,7 +682,7 @@
 	byte_stream_putle32(&bs, strlen(rtfcap)+1);
 	byte_stream_putraw(&bs, (const guint8 *)rtfcap, strlen(rtfcap)+1);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -704,13 +704,13 @@
 	ByteStream hdrbs;
 
 	od = peer_conn->od;
-	conn = flap_connection_findbygroup(od, 0x0004);
+	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
 	if (conn == NULL)
 		return;
 
 	byte_stream_new(&bs, 118+strlen(peer_conn->sn));
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->sn);
@@ -735,7 +735,7 @@
 	aim_tlvlist_free(inner_tlvlist);
 	aim_tlvlist_free(outer_tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -753,13 +753,13 @@
 	aim_snacid_t snacid;
 
 	od = peer_conn->od;
-	conn = flap_connection_findbygroup(od, 0x0004);
+	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
 	if (conn == NULL)
 		return;
 
 	byte_stream_new(&bs, 11+strlen(peer_conn->sn) + 4+2+8+16);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->sn);
@@ -770,7 +770,7 @@
 	byte_stream_putraw(&bs, peer_conn->cookie, 8);
 	byte_stream_putcaps(&bs, peer_conn->type);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -791,13 +791,13 @@
 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 
-	conn = flap_connection_findbygroup(od, 0x0004);
+	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
 	if (conn == NULL)
 		return;
 
 	byte_stream_new(&bs, 246+strlen(sn));
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -825,7 +825,7 @@
 	aim_tlvlist_free(inner_tlvlist);
 	aim_tlvlist_free(outer_tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -844,13 +844,13 @@
 	ByteStream hdrbs;
 	guint8 ip_comp[4];
 
-	conn = flap_connection_findbygroup(od, 0x0004);
+	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
 	if (conn == NULL)
 		return;
 
 	byte_stream_new(&bs, 246+strlen(sn));
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -888,7 +888,7 @@
 	aim_tlvlist_free(inner_tlvlist);
 	aim_tlvlist_free(outer_tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -906,13 +906,13 @@
 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 	ByteStream hdrbs;
 
-	conn = flap_connection_findbygroup(od, 0x0004);
+	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
 	if (conn == NULL)
 		return;
 
 	byte_stream_new(&bs, 1014);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -971,7 +971,7 @@
 	aim_tlvlist_free(inner_tlvlist);
 	aim_tlvlist_free(outer_tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -990,13 +990,13 @@
 	ByteStream hdrbs;
 	guint8 ip_comp[4];
 
-	conn = flap_connection_findbygroup(od, 0x0004);
+	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
 	if (conn == NULL)
 		return;
 
 	byte_stream_new(&bs, 1014);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -1064,7 +1064,7 @@
 	aim_tlvlist_free(inner_tlvlist);
 	aim_tlvlist_free(outer_tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -1085,14 +1085,14 @@
 	aim_snacid_t snacid;
 	guchar cookie[8];
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)) || !sn)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)) || !sn)
 		return -EINVAL;
 
 	aim_icbm_makecookie(cookie);
 
 	byte_stream_new(&bs, 8+2+1+strlen(sn) + 4+0x5e + 4);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	/* ICBM header */
 	aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -1160,7 +1160,7 @@
 	byte_stream_put16(&bs, 0x0003);
 	byte_stream_put16(&bs, 0x0000);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -1196,7 +1196,7 @@
 
 	byte_stream_new(&bs, 8+3+strlen(sn)+12+strlen(message)+1+4);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
 
 	aim_icbm_makecookie(cookie);
 
@@ -1229,7 +1229,7 @@
 	byte_stream_put16(&bs, 0x0006);
 	byte_stream_put16(&bs, 0x0000);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -2268,13 +2268,13 @@
 
 	byte_stream_new(&bs, strlen(sn)+3);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0008, 0x0000, sn, strlen(sn)+1);
 
 	byte_stream_put16(&bs, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000);
 	byte_stream_put8(&bs, strlen(sn));
 	byte_stream_putstr(&bs, sn);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0008, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0008, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -2321,12 +2321,12 @@
 	aim_snacid_t snacid;
 	GSList *tlvlist = NULL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 		return -EINVAL;
 
 	byte_stream_new(&bs, 8+2+1+strlen(sn)+6);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
 
 	byte_stream_putraw(&bs, cookie, 8);
 
@@ -2338,7 +2338,7 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x000b, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -2660,7 +2660,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
 		return -EINVAL;
 
-	aim_genericreq_n(od, conn, 0x0004, 0x0010);
+	aim_genericreq_n(od, conn, SNAC_FAMILY_ICBM, 0x0010);
 
 	return 0;
 }
@@ -2686,7 +2686,7 @@
 
 	byte_stream_new(&bs, 11+strlen(sn)+2);
 
-	snacid = aim_cachesnac(od, 0x0004, 0x0014, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
 
 	/*
 	 * 8 days of light
@@ -2713,7 +2713,7 @@
 	 */
 	byte_stream_put16(&bs, type2);
 
-	flap_connection_send_snac(od, conn, 0x0004, 0x0014, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -2773,7 +2773,7 @@
 int
 msg_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0004;
+	mod->family = SNAC_FAMILY_ICBM;
 	mod->version = 0x0001;
 	mod->toolid = 0x0110;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_icq.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Thu Jun 26 20:25:38 2008 +0000
@@ -33,14 +33,14 @@
 	aim_snacid_t snacid;
 	int bslen;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	bslen = 2 + 4 + 2 + 2;
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -51,7 +51,7 @@
 	byte_stream_putle16(&bs, 0x003c); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -65,14 +65,14 @@
 	aim_snacid_t snacid;
 	int bslen;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	bslen = 2 + 4 + 2 + 2;
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -83,7 +83,7 @@
 	byte_stream_putle16(&bs, 0x003e); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -99,14 +99,14 @@
 	aim_snacid_t snacid;
 	int bslen;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1;
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -126,7 +126,7 @@
 	byte_stream_putle8(&bs, 0x00);
 	byte_stream_putle8(&bs, !auth_required);
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -151,7 +151,7 @@
 	if (!passwd)
 		return -EINVAL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	passwdlen = strlen(passwd);
@@ -161,7 +161,7 @@
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -176,7 +176,7 @@
 	byte_stream_putstr(&bs, passwd);
 	byte_stream_putle8(&bs, '\0');
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -194,14 +194,14 @@
 	if (!uin || uin[0] < '0' || uin[0] > '9')
 		return -EINVAL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	bslen = 2 + 4 + 2 + 2 + 2 + 4;
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -214,7 +214,7 @@
 	byte_stream_putle16(&bs, 0x04b2); /* shrug. */
 	byte_stream_putle32(&bs, atoi(uin));
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -239,14 +239,14 @@
 	if (!uin || uin[0] < '0' || uin[0] > '9')
 		return -EINVAL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	bslen = 2 + 4 + 2 + 2 + 2 + 4;
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -259,7 +259,7 @@
 	byte_stream_putle16(&bs, 0x04ba); /* shrug. */
 	byte_stream_putle32(&bs, atoi(uin));
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -283,14 +283,14 @@
 	if (!uin || uin[0] < '0' || uin[0] > '9')
 		return -EINVAL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	bslen = 2 + 4 + 2 + 2 + 2 + 4;
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -303,7 +303,7 @@
 	byte_stream_putle16(&bs, 0x051f); /* shrug. */
 	byte_stream_putle32(&bs, atoi(uin));
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -321,14 +321,14 @@
 	if (!xml || !strlen(xml))
 		return -EINVAL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	bslen = 2 + 10 + 2 + strlen(xml) + 1;
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -342,7 +342,7 @@
 	byte_stream_putle16(&bs, strlen(xml) + 1);
 	byte_stream_putraw(&bs, (guint8 *)xml, strlen(xml) + 1);
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -380,7 +380,7 @@
 	struct tm *tm;
 	gchar *stripped;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 		return -EINVAL;
 
 	if (!name || !msg || !alias)
@@ -411,7 +411,7 @@
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -436,7 +436,7 @@
 	byte_stream_putstr(&bs, xml);
 	byte_stream_put8(&bs, 0x00);
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -460,7 +460,7 @@
 
 	purple_debug_misc("oscar", "aim_icq_getstatusnote: requesting status note for %s.\n", uin);
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
 	{
 		purple_debug_misc("oscar", "aim_icq_getstatusnote: no connection.\n");
 		return -EINVAL;
@@ -469,7 +469,7 @@
 	bslen = 2 + 4 + 2 + 2 + 2 + 2 + 58 + strlen(uin);
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
 
 	byte_stream_put16(&bs, 0x0001);
 	byte_stream_put16(&bs, bslen);
@@ -497,7 +497,7 @@
 	byte_stream_put16(&bs, strlen(uin));
 	byte_stream_putstr(&bs, uin);
 
-	flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -541,7 +541,7 @@
 }
 
 /**
- * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet.
+ * Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet.
  */
 static int
 icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
@@ -940,7 +940,7 @@
 int
 icq_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0015;
+	mod->family = SNAC_FAMILY_ICQ;
 	mod->version = 0x0001;
 	mod->toolid = 0x0110;
 	mod->toolversion = 0x047c;
--- a/libpurple/protocols/oscar/family_invite.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_invite.c	Thu Jun 26 20:25:38 2008 +0000
@@ -41,7 +41,7 @@
 int invite_modfirst(OscarData *od, aim_module_t *mod)
 {
 
-	mod->family = 0x0006;
+	mod->family = SNAC_FAMILY_INVITE;
 	mod->version = 0x0001;
 	mod->toolid = 0x0110;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_locate.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Thu Jun 26 20:25:38 2008 +0000
@@ -171,7 +171,7 @@
 	 {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
-	{OSCAR_CAPABILITY_GENERICUNKNOWN,
+	{OSCAR_CAPABILITY_ICHAT_SCREENSHARE,
 	 {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
@@ -943,7 +943,7 @@
 		return 0;
 	}
 
-	if ((snac2->family != 0x0002) && (snac2->type != 0x0015)) {
+	if ((snac2->family != SNAC_FAMILY_LOCATE) && (snac2->type != 0x0015)) {
 		purple_debug_misc("oscar", "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family);
 		return 0;
 	}
@@ -1094,12 +1094,12 @@
 
 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 
-	snacid = aim_cachesnac(od, 0x0002, 0x0004, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
 
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0002, 0x0004, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -1124,12 +1124,12 @@
 
 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 
-	snacid = aim_cachesnac(od, 0x0002, 0x0004, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
 
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0002, 0x0004, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -1157,13 +1157,13 @@
 
 	byte_stream_new(&bs, 2+1+strlen(sn));
 
-	snacid = aim_cachesnac(od, 0x0002, 0x0005, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0005, 0x0000, NULL, 0);
 
 	byte_stream_put16(&bs, infotype);
 	byte_stream_put8(&bs, strlen(sn));
 	byte_stream_putstr(&bs, sn);
 
-	flap_connection_send_snac(od, conn, 0x0002, 0x0005, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0005, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -1274,12 +1274,12 @@
 
 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 
-	snacid = aim_cachesnac(od, 0x0002, 0x0009, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0009, 0x0000, NULL, 0);
 
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0002, 0x0009, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0009, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -1302,12 +1302,12 @@
 
 	byte_stream_new(&bs, 1+strlen(sn));
 
-	snacid = aim_cachesnac(od, 0x0002, 0x000b, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, NULL, 0);
 
 	byte_stream_put8(&bs, strlen(sn));
 	byte_stream_putstr(&bs, sn);
 
-	flap_connection_send_snac(od, conn, 0x0002, 0x000b, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -1347,12 +1347,12 @@
 
 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 
-	snacid = aim_cachesnac(od, 0x0002, 0x000f, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000f, 0x0000, NULL, 0);
 
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	flap_connection_send_snac(od, conn, 0x0002, 0x000f, 0x0000, snacid, &bs);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000f, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 	return 0;
@@ -1385,8 +1385,8 @@
 	byte_stream_put8(&bs, strlen(sn));
 	byte_stream_putstr(&bs, sn);
 
-	snacid = aim_cachesnac(od, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1);
-	flap_connection_send_snac(od, conn, 0x0002, 0x0015, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, sn, strlen(sn)+1);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
--- a/libpurple/protocols/oscar/family_odir.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_odir.c	Thu Jun 26 20:25:38 2008 +0000
@@ -45,7 +45,7 @@
 	aim_snacid_t snacid;
 	GSList *tlvlist = NULL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region || !email)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region || !email)
 		return -EINVAL;
 
 	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
@@ -58,8 +58,8 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x000f, 0x0002, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -94,7 +94,7 @@
 	aim_snacid_t snacid;
 	GSList *tlvlist = NULL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region)
 		return -EINVAL;
 
 	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
@@ -126,8 +126,8 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x000f, 0x0002, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -149,7 +149,7 @@
 	aim_snacid_t snacid;
 	GSList *tlvlist = NULL;
 
-	if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region)
+	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region)
 		return -EINVAL;
 
 	/* Create a TLV chain, write it to the outgoing frame, then free the chain */
@@ -163,8 +163,8 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x000f, 0x0002, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -252,7 +252,7 @@
 int
 odir_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x000f;
+	mod->family = SNAC_FAMILY_ODIR;
 	mod->version = 0x0001;
 	mod->toolid = 0x0010;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_oservice.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Thu Jun 26 20:25:38 2008 +0000
@@ -54,8 +54,8 @@
 		}
 	}
 
-	snacid = aim_cachesnac(od, 0x0001, 0x0002, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0001, 0x0002, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -108,7 +108,7 @@
 	if(!conn)
 		return;
 
-	aim_genericreq_s(od, conn, 0x0001, 0x0004, &serviceid);
+	aim_genericreq_s(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, &serviceid);
 }
 
 /*
@@ -146,8 +146,8 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	snacid = aim_cachesnac(od, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
-	flap_connection_send_snac(od, conn, 0x0001, 0x0004, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi));
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -210,7 +210,7 @@
 void
 aim_srv_reqrates(OscarData *od, FlapConnection *conn)
 {
-	aim_genericreq_n_snacid(od, conn, 0x0001, 0x0006);
+	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x0006);
 }
 
 /*
@@ -389,8 +389,8 @@
 		byte_stream_put16(&bs, rateclass->classid);
 	}
 
-	snacid = aim_cachesnac(od, 0x0001, 0x0008, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0001, 0x0008, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -412,8 +412,8 @@
 		byte_stream_put16(&bs, rateclass->classid);
 	}
 
-	snacid = aim_cachesnac(od, 0x0001, 0x0009, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0001, 0x0009, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0009, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0009, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -503,8 +503,8 @@
 	for (cur = conn->groups; cur != NULL; cur = cur->next)
 		byte_stream_put16(&bs, GPOINTER_TO_UINT(cur->data));
 
-	snacid = aim_cachesnac(od, 0x0001, 0x000c, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0001, 0x000c, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x000c, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x000c, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -526,7 +526,7 @@
 void
 aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn)
 {
-	aim_genericreq_n_snacid(od, conn, 0x0001, 0x000e);
+	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e);
 }
 
 /* Subtype 0x000f - Self User Info */
@@ -589,7 +589,7 @@
 	if(!conn)
 		return;
 
-	aim_genericreq_l(od, conn, 0x0001, 0x0011, &idletime);
+	aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0011, &idletime);
 }
 
 /*
@@ -698,7 +698,7 @@
 void
 aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32 flags)
 {
-	aim_genericreq_l(od, conn, 0x0001, 0x0014, &flags);
+	aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0014, &flags);
 }
 
 /*
@@ -713,7 +713,7 @@
 void
 aim_srv_nop(OscarData *od, FlapConnection *conn)
 {
-	aim_genericreq_n(od, conn, 0x0001, 0x0016);
+	aim_genericreq_n(od, conn, SNAC_FAMILY_OSERVICE, 0x0016);
 }
 
 /*
@@ -753,8 +753,8 @@
 		}
 	}
 
-	snacid = aim_cachesnac(od, 0x0001, 0x0017, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0001, 0x0017, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 }
@@ -861,8 +861,8 @@
 	aim_tlvlist_write(&bs, &tlvlist);
 	aim_tlvlist_free(tlvlist);
 
-	snacid = aim_cachesnac(od, 0x0001, 0x001e, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0001, 0x001e, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -952,13 +952,10 @@
 		byte_stream_putraw(&bs, buf, 0x10);
 
 	} else if (buf && (len > 0)) { /* use input buffer */
-		PurpleCipher *cipher;
 		PurpleCipherContext *context;
 		guchar digest[16];
 
-		cipher = purple_ciphers_find_cipher("md5");
-
-		context = purple_cipher_context_new(cipher, NULL);
+		context = purple_cipher_context_new_by_name("md5", NULL);
 		purple_cipher_context_append(context, buf, len);
 		purple_cipher_context_digest(context, 16, digest, NULL);
 		purple_cipher_context_destroy(context);
@@ -966,7 +963,6 @@
 		byte_stream_putraw(&bs, digest, 0x10);
 
 	} else if (len == 0) { /* no length, just hash NULL (buf is optional) */
-		PurpleCipher *cipher;
 		PurpleCipherContext *context;
 		guchar digest[16];
 		guint8 nil = '\0';
@@ -975,9 +971,7 @@
 		 * I'm not sure if we really need the empty append with the
 		 * new MD5 functions, so I'll leave it in, just in case.
 		 */
-		cipher = purple_ciphers_find_cipher("md5");
-
-		context = purple_cipher_context_new(cipher, NULL);
+		context = purple_cipher_context_new_by_name("md5", NULL);
 		purple_cipher_context_append(context, &nil, 0);
 		purple_cipher_context_digest(context, 16, digest, NULL);
 		purple_cipher_context_destroy(context);
@@ -1012,8 +1006,8 @@
 
 	}
 
-	snacid = aim_cachesnac(od, 0x0001, 0x0020, 0x0000, NULL, 0);
-	flap_connection_send_snac(od, conn, 0x0001, 0x0020, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, NULL, 0);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -1100,7 +1094,7 @@
 
 int service_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0001;
+	mod->family = SNAC_FAMILY_OSERVICE;
 	mod->version = 0x0003;
 	mod->toolid = 0x0110;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/family_popup.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_popup.c	Thu Jun 26 20:25:38 2008 +0000
@@ -72,7 +72,7 @@
 int
 popups_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x0008;
+	mod->family = SNAC_FAMILY_POPUP;
 	mod->version = 0x0001;
 	mod->toolid = 0x0104;
 	mod->toolversion = 0x0001;
--- a/libpurple/protocols/oscar/family_stats.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_stats.c	Thu Jun 26 20:25:38 2008 +0000
@@ -52,7 +52,7 @@
 int
 stats_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x000b;
+	mod->family = SNAC_FAMILY_STATS;
 	mod->version = 0x0001;
 	mod->toolid = 0x0104;
 	mod->toolversion = 0x0001;
--- a/libpurple/protocols/oscar/family_translate.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_translate.c	Thu Jun 26 20:25:38 2008 +0000
@@ -34,7 +34,7 @@
 int translate_modfirst(OscarData *od, aim_module_t *mod)
 {
 
-	mod->family = 0x000c;
+	mod->family = SNAC_FAMILY_TRANSLATE;
 	mod->version = 0x0001;
 	mod->toolid = 0x0104;
 	mod->toolversion = 0x0001;
--- a/libpurple/protocols/oscar/family_userlookup.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/family_userlookup.c	Thu Jun 26 20:25:38 2008 +0000
@@ -74,8 +74,8 @@
 
 	byte_stream_putstr(&bs, address);
 
-	snacid = aim_cachesnac(od, 0x000a, 0x0002, 0x0000, address, strlen(address)+1);
-	flap_connection_send_snac(od, conn, 0x000a, 0x0002, 0x0000, snacid, &bs);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, address, strlen(address)+1);
+	flap_connection_send_snac(od, conn, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, snacid, &bs);
 
 	byte_stream_destroy(&bs);
 
@@ -145,7 +145,7 @@
 int
 search_modfirst(OscarData *od, aim_module_t *mod)
 {
-	mod->family = 0x000a;
+	mod->family = SNAC_FAMILY_USERLOOKUP;
 	mod->version = 0x0001;
 	mod->toolid = 0x0110;
 	mod->toolversion = 0x0629;
--- a/libpurple/protocols/oscar/oscar.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Thu Jun 26 20:25:38 2008 +0000
@@ -131,8 +131,8 @@
 	N_("Busted SNAC payload"),
 	N_("Insufficient rights"),
 	N_("In local permit/deny"),
-	N_("Too evil (sender)"),
-	N_("Too evil (receiver)"),
+	N_("Warning level too high (sender)"),
+	N_("Warning level too high (receiver)"),
 	N_("User temporarily unavailable"),
 	N_("No match"),
 	N_("List overflow"),
@@ -707,6 +707,9 @@
 			case OSCAR_CAPABILITY_CAMERA:
 				tmp = _("Camera");
 				break;
+			case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
+				tmp = _("Screen Sharing");
+				break;
 			default:
 				tmp = NULL;
 				break;
@@ -753,7 +756,7 @@
 									 const char *name, const char *value)
 {
 	gchar *utf8;
-	
+
 	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
 		purple_notify_user_info_add_pair(user_info, name, utf8);
 		g_free(utf8);
@@ -761,30 +764,152 @@
 }
 
 static void
-oscar_string_convert_and_append(PurpleAccount *account, GString *str, const char *newline,
-					const char *name, const char *value)
-{
-	gchar *utf8;
-
-	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
-		g_string_append_printf(str, "%s<b>%s:</b> %s", newline, name, utf8);
-		g_free(utf8);
-	}
-}
-
-static void
 oscar_user_info_convert_and_add(PurpleAccount *account, PurpleNotifyUserInfo *user_info,
 								const char *name, const char *value)
 {
 	gchar *utf8;
-	
+
 	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
 		purple_notify_user_info_add_pair(user_info, name, utf8);
 		g_free(utf8);
 	}
 }
 
-static void oscar_string_append_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
+/**
+ * @brief Append the status information to a user_info struct
+ *
+ * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
+ *
+ * @param gc The PurpleConnection
+ * @param user_info A PurpleNotifyUserInfo object to which status information will be added
+ * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status().
+ * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status().
+ * @param strip_html_tags If strip_html_tags is TRUE, tags embedded in the status message will be stripped, returning a non-formatted string. The string will still be HTML escaped.
+ */
+static void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags)
+{
+	PurpleAccount *account = purple_connection_get_account(gc);
+	OscarData *od;
+	PurplePresence *presence = NULL;
+	PurpleStatus *status = NULL;
+	gchar *message = NULL, *itmsurl = NULL, *tmp;
+	gboolean is_away;
+
+	od = gc->proto_data;
+
+	if (userinfo == NULL)
+		userinfo = aim_locate_finduserinfo(od, b->name);
+
+	if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
+		return;
+
+	if (b == NULL)
+		b = purple_find_buddy(purple_connection_get_account(gc), userinfo->sn);
+
+	if (b) {
+		presence = purple_buddy_get_presence(b);
+		status = purple_presence_get_active_status(presence);
+
+		message = g_strdup(purple_status_get_attr_string(status, "message"));
+		itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
+
+	} else {
+		/* This is an OSCAR contact for whom we don't have a PurpleBuddy but do have information. */
+		if ((userinfo->flags & AIM_FLAG_AWAY)) {
+			/* Away message? */
+			if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
+				tmp = oscar_encoding_extract(userinfo->away_encoding);
+				message = oscar_encoding_to_utf8(account, tmp, userinfo->away,
+												   userinfo->away_len);
+				g_free(tmp);
+				}
+		} else {
+			/* Available message? */
+			if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
+				message = oscar_encoding_to_utf8(account, userinfo->status_encoding,
+											 userinfo->status, userinfo->status_len);
+			}
+#if defined (_WIN32) || defined (__APPLE__)
+			if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0'))
+				itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
+												 userinfo->itmsurl, userinfo->itmsurl_len);
+#endif
+		}
+	}
+
+	is_away = ((status && !purple_status_is_available(status)) ||
+			   (userinfo && (userinfo->flags & AIM_FLAG_AWAY)));
+
+	if (strip_html_tags) {
+		/* Away messges are HTML, but available messages were originally plain text.
+		 * We therefore need to strip away messages but not available messages if we're asked to remove HTML tags.
+		 */
+		if (is_away && message) {
+			gchar *tmp2;
+			tmp = purple_markup_strip_html(message);
+			g_free(message);
+			tmp2 = g_markup_escape_text(tmp, -1);
+			g_free(tmp);
+			message = tmp2;
+		}
+
+	} else {
+		if (itmsurl) {
+			tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
+								  itmsurl, message);
+			g_free(itmsurl);
+			g_free(message);
+			message = tmp;
+		}
+	}
+
+	if (is_away && message) {
+		tmp = purple_str_sub_away_formatters(message, purple_account_get_username(account));
+		g_free(message);
+		message = tmp;
+	}
+
+	if (b) {
+		if (purple_presence_is_online(presence)) {
+			if (aim_snvalid_icq(b->name) || is_away || !message || !(*message)) {
+				/* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
+				 * If the status name and the message are the same, only show one. */
+				const char *status_name = purple_status_get_name(status);
+				if (status_name && message && !strcmp(status_name, message))
+					status_name = NULL;
+
+				tmp = g_strdup_printf("%s%s%s",
+									   status_name,
+									   ((status_name && message) && *message) ? ": " : "",
+									   (message && *message) ? message : "");
+				g_free(message);
+				message = tmp;
+			}
+
+		} else {
+			if (aim_ssi_waitingforauth(od->ssi.local,
+									   aim_ssi_itemlist_findparentname(od->ssi.local, b->name),
+									   b->name)) {
+				/* Note if an offline buddy is not authorized */
+				tmp = g_strdup_printf("%s%s%s",
+									  _("Not Authorized"),
+									  (message && *message) ? ": " : "",
+									  (message && *message) ? message : "");
+				g_free(message);
+				message = tmp;
+			} else {
+				g_free(message);
+				message = g_strdup(_("Offline"));
+			}
+		}
+
+	}
+
+	purple_notify_user_info_add_pair(user_info, _("Status"), message);
+	g_free(message);
+}
+
+static void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
 {
 	OscarData *od;
 	PurpleAccount *account;
@@ -815,21 +940,6 @@
 	if (userinfo != NULL)
 		bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->sn));
 
-	if (b != NULL) {
-		if (purple_presence_is_online(presence)) {
-			if (aim_snvalid_icq(b->name)) {
-				PurpleStatus *status = purple_presence_get_active_status(presence);
-				oscar_user_info_add_pair(user_info, _("Status"),	purple_status_get_name(status));
-			}
-		} else {
-			tmp = aim_ssi_itemlist_findparentname(od->ssi.local, b->name);
-			if (aim_ssi_waitingforauth(od->ssi.local, tmp, b->name))
-				oscar_user_info_add_pair(user_info, _("Status"),	_("Not Authorized"));
-			else
-				oscar_user_info_add_pair(user_info, _("Status"),	_("Offline"));
-		}
-	}
-
 	if ((bi != NULL) && (bi->ipaddr != 0)) {
 		tmp =  g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
 						(bi->ipaddr & 0xff000000) >> 24,
@@ -840,7 +950,6 @@
 		g_free(tmp);
 	}
 
-
 	if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
 		tmp = g_strdup_printf("%d", (int)(userinfo->warnlevel/10.0 + .5));
 		oscar_user_info_add_pair(user_info, _("Warning Level"), tmp);
@@ -988,8 +1097,8 @@
 
 	if (source < 0)
 	{
-		purple_debug_error("oscar", "unable to connect FLAP server "
-				"of type 0x%04hx\n", conn->type);
+		purple_debug_error("oscar", "unable to connect to FLAP "
+				"server of type 0x%04hx\n", conn->type);
 		if (conn->type == SNAC_FAMILY_AUTH)
 		{
 			gchar *msg;
@@ -1705,7 +1814,8 @@
 
 	aim_send_login(od, conn, purple_account_get_username(account),
 			purple_connection_get_password(gc), truncate_pass,
-			od->icq ? &icqinfo : &aiminfo, key);
+			od->icq ? &icqinfo : &aiminfo, key,
+			/* allow multple logins? */ purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
 
 	purple_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS);
 	ck[2] = 0x6c;
@@ -1841,6 +1951,7 @@
 	{
 		char *message = NULL;
 		char *itmsurl = NULL;
+		char *tmp;
 
 		if (info->status != NULL && info->status[0] != '\0')
 			/* Grab the available message */
@@ -1852,15 +1963,31 @@
 			itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
 					info->itmsurl, info->itmsurl_len);
 
+		tmp = (message ? g_markup_escape_text(message, -1) : NULL);
+
+		if (message == NULL && itmsurl != NULL)
+			message = "";
+
 		purple_prpl_got_user_status(account, info->sn, status_id,
-				"message", message, "itmsurl", itmsurl, NULL);
+				"message", tmp, "itmsurl", itmsurl, NULL);
+		g_free(tmp);
 
 		g_free(message);
 		g_free(itmsurl);
 	}
 	else
 	{
-		purple_prpl_got_user_status(account, info->sn, status_id, NULL);
+		PurpleBuddy *b = purple_find_buddy(account, info->sn);
+		PurplePresence *presence = purple_buddy_get_presence(b);
+		PurpleStatus *old_status = purple_presence_get_active_status(presence);
+		PurpleStatus *new_status = purple_presence_get_status(presence, status_id);
+		
+		/* If our status_id would change with this update, pass it to the core.
+		 * However, if our status_id would not change, do nothing, as we would clear out any existing
+		 * attributes on the status prematurely. purple_got_infoblock() will update the message as needed.
+		 */
+		if (old_status != new_status)
+			purple_prpl_got_user_status(account, info->sn, status_id, NULL);
 	}
 
 	/* Login time stuff */
@@ -2648,7 +2775,7 @@
 	switch(reason) {
 		case 0: /* Invalid (0) */
 			buf = g_strdup_printf(
-				   dngettext(PACKAGE, 
+				   dngettext(PACKAGE,
 				   "You missed %hu message from %s because it was invalid.",
 				   "You missed %hu messages from %s because they were invalid.",
 				   nummissed),
@@ -2657,7 +2784,7 @@
 			break;
 		case 1: /* Message too large */
 			buf = g_strdup_printf(
-				   dngettext(PACKAGE, 
+				   dngettext(PACKAGE,
 				   "You missed %hu message from %s because it was too large.",
 				   "You missed %hu messages from %s because they were too large.",
 				   nummissed),
@@ -2666,7 +2793,7 @@
 			break;
 		case 2: /* Rate exceeded */
 			buf = g_strdup_printf(
-				   dngettext(PACKAGE, 
+				   dngettext(PACKAGE,
 				   "You missed %hu message from %s because the rate limit has been exceeded.",
 				   "You missed %hu messages from %s because the rate limit has been exceeded.",
 				   nummissed),
@@ -2675,25 +2802,25 @@
 			break;
 		case 3: /* Evil Sender */
 			buf = g_strdup_printf(
-				   dngettext(PACKAGE, 
-				   "You missed %hu message from %s because he/she was too evil.",
-				   "You missed %hu messages from %s because he/she was too evil.",
+				   dngettext(PACKAGE,
+				   "You missed %hu message from %s because his/her warning level is too high.",
+				   "You missed %hu messages from %s because his/her warning level is too high.",
 				   nummissed),
 				   nummissed,
 				   userinfo->sn);
 			break;
 		case 4: /* Evil Receiver */
 			buf = g_strdup_printf(
-				   dngettext(PACKAGE, 
-				   "You missed %hu message from %s because you are too evil.",
-				   "You missed %hu messages from %s because you are too evil.",
+				   dngettext(PACKAGE,
+				   "You missed %hu message from %s because your warning level is too high.",
+				   "You missed %hu messages from %s because your warning level is too high.",
 				   nummissed),
 				   nummissed,
 				   userinfo->sn);
 			break;
 		default:
 			buf = g_strdup_printf(
-				   dngettext(PACKAGE, 
+				   dngettext(PACKAGE,
 				   "You missed %hu message from %s for an unknown reason.",
 				   "You missed %hu messages from %s for an unknown reason.",
 				   nummissed),
@@ -2749,9 +2876,9 @@
 			/* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
 			statusmsg = oscar_icqstatus(state);
 			splitmsg = g_strsplit(msg, "\r\n", 0);
-			
+
 			user_info = purple_notify_user_info_new();
-				
+
 			purple_notify_user_info_add_pair(user_info, _("UIN"), who);
 			purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
 			purple_notify_user_info_add_section_break(user_info);
@@ -2932,7 +3059,7 @@
 	PurpleConnection *gc = od->gc;
 	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleNotifyUserInfo *user_info;
-	gchar *tmp = NULL, *info_utf8 = NULL, *away_utf8 = NULL;
+	gchar *tmp = NULL, *info_utf8 = NULL;
 	va_list ap;
 	aim_userinfo_t *userinfo;
 
@@ -2941,9 +3068,19 @@
 	va_end(ap);
 
 	user_info = purple_notify_user_info_new();
-	purple_notify_user_info_add_pair(user_info, _("Username"), userinfo->sn);
-
-	if (userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) {
+
+	oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* strip_html_tags */ FALSE);
+
+	if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) {
+		tmp = purple_str_seconds_to_string(userinfo->idletime*60);
+		oscar_user_info_add_pair(user_info, _("Idle"), tmp);
+		g_free(tmp);
+	}
+
+	oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo);
+
+	if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !aim_snvalid_sms(userinfo->sn)) {
+		/* An SMS contact is always online; its Online Since valid is not useful */
 		time_t t = userinfo->onlinesince;
 		oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
 	}
@@ -2959,50 +3096,6 @@
 		g_free(tmp);
 	}
 
-	if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) {
-		tmp = purple_str_seconds_to_string(userinfo->idletime*60);
-		oscar_user_info_add_pair(user_info, _("Idle"), tmp);
-		g_free(tmp);
-	}
-
-	oscar_string_append_info(gc, user_info, NULL, userinfo);
-
-	/* Available message */
-	if ((userinfo->status != NULL) && !(userinfo->flags & AIM_FLAG_AWAY))
-	{
-		if (userinfo->status[0] != '\0')
-			tmp = oscar_encoding_to_utf8(account, userinfo->status_encoding,
-											 userinfo->status, userinfo->status_len);
-#if defined (_WIN32) || defined (__APPLE__)
-		if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
-			gchar *itmsurl, *tmp2;
-			itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
-					userinfo->itmsurl, userinfo->itmsurl_len);
-			tmp2 = g_strdup_printf("<a href=\"%s\">%s</a>",
-					itmsurl, tmp);
-			g_free(tmp);
-			tmp = tmp2;
-			g_free(itmsurl);
-		}
-#endif
-		oscar_user_info_add_pair(user_info, _("Available Message"), tmp);
-		g_free(tmp);
-	}
-
-	/* Away message */
-	if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
-		tmp = oscar_encoding_extract(userinfo->away_encoding);
-		away_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->away,
-		                                   userinfo->away_len);
-		g_free(tmp);
-		if (away_utf8 != NULL) {
-			tmp = purple_str_sub_away_formatters(away_utf8, purple_account_get_username(account));
-			oscar_user_info_add_pair(user_info, _("Away Message"), tmp);
-			g_free(tmp);
-			g_free(away_utf8);
-		}
-	}
-
 	/* Info */
 	if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
 		tmp = oscar_encoding_extract(userinfo->info_encoding);
@@ -3441,8 +3534,8 @@
 	if (code == AIM_RATE_CODE_LIMIT)
 	{
 		purple_debug_warning("oscar",  _("The last action you attempted could not be "
-					 	 "performed because you are over the rate limit. "
-						 "Please wait 10 seconds and try again."));
+				"performed because you are over the rate limit. "
+				"Please wait 10 seconds and try again."));
 	}
 
 	return 1;
@@ -3699,12 +3792,9 @@
 	PurpleConnection *gc;
 	PurpleAccount *account;
 	PurpleBuddy *buddy;
-	PurplePresence *presence;
-	PurpleStatus *status;
 	struct buddyinfo *bi;
 	gchar who[16];
 	PurpleNotifyUserInfo *user_info;
-	GString *tmp;
 	gchar *utf8;
 	gchar *buf;
 	const gchar *alias;
@@ -3722,7 +3812,7 @@
 		return 0;
 
 	user_info = purple_notify_user_info_new();
-		
+
 	g_snprintf(who, sizeof(who), "%u", info->uin);
 	buddy = purple_find_buddy(purple_connection_get_account(gc), who);
 	if (buddy != NULL)
@@ -3785,8 +3875,7 @@
 	if ((info->age > 0) && (info->age < 255)) {
 		char age[5];
 		snprintf(age, sizeof(age), "%hhd", info->age);
-		purple_notify_user_info_add_pair(user_info,
-													_("Age"), age);
+		purple_notify_user_info_add_pair(user_info, _("Age"), age);
 	}
 	if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->personalwebpage))) {
 		buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
@@ -3795,65 +3884,41 @@
 		g_free(utf8);
 	}
 
-	if (buddy != NULL) {
-		const gchar *message;
-		gchar *utf8, *tmp;
-
-		presence = purple_buddy_get_presence(buddy);
-		status = purple_presence_get_active_status(presence);
-		message = purple_status_get_attr_string(status, "message");
-
-		utf8 = message && message[0] ? oscar_utf8_try_convert(account, message) : NULL;
-		tmp = g_strdup_printf("%s%s%s",
-				purple_status_get_name(status),
-				utf8 && *utf8 ? ": " : "",
-				utf8 && *utf8 ? utf8 : "");
-		g_free(utf8);
-
-		oscar_user_info_convert_and_add(account,
-				user_info, _("Status"), tmp);
-	}
+	if (buddy != NULL)
+		oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* strip_html_tags */ FALSE);
 
 	oscar_user_info_convert_and_add(account, user_info, _("Additional Information"), info->info);
 	purple_notify_user_info_add_section_break(user_info);
 
 	if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
-		tmp = g_string_sized_new(100);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Address"), info->homeaddr);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("City"), info->homecity);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("State"), info->homestate);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Zip Code"), info->homezip);
-		
-		purple_notify_user_info_add_pair(user_info, _("Home Address"), tmp->str);
-		purple_notify_user_info_add_section_break(user_info);
-
-		g_string_free(tmp, TRUE);
+		purple_notify_user_info_add_section_header(user_info, _("Home Address"));
+
+		oscar_user_info_convert_and_add(account, user_info, _("Address"), info->homeaddr);
+		oscar_user_info_convert_and_add(account, user_info, _("City"), info->homecity);
+		oscar_user_info_convert_and_add(account, user_info, _("State"), info->homestate);
+		oscar_user_info_convert_and_add(account, user_info, _("Zip Code"), info->homezip);
 	}
 	if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
-		tmp = g_string_sized_new(100);
-
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Address"), info->workaddr);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("City"), info->workcity);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("State"), info->workstate);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Zip Code"), info->workzip);
-
-		purple_notify_user_info_add_pair(user_info, _("Work Address"), tmp->str);
-		purple_notify_user_info_add_section_break(user_info);
-
-		g_string_free(tmp, TRUE);
+		purple_notify_user_info_add_section_header(user_info, _("Work Address"));
+
+		oscar_user_info_convert_and_add(account, user_info, _("Address"), info->workaddr);
+		oscar_user_info_convert_and_add(account, user_info, _("City"), info->workcity);
+		oscar_user_info_convert_and_add(account, user_info, _("State"), info->workstate);
+		oscar_user_info_convert_and_add(account, user_info, _("Zip Code"), info->workzip);
 	}
 	if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
-		tmp = g_string_sized_new(100);
-		
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Company"), info->workcompany);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Division"), info->workdivision);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Position"), info->workposition);
+		purple_notify_user_info_add_section_header(user_info, _("Work Information"));
+
+		oscar_user_info_convert_and_add(account, user_info, _("Company"), info->workcompany);
+		oscar_user_info_convert_and_add(account, user_info, _("Division"), info->workdivision);
+		oscar_user_info_convert_and_add(account, user_info, _("Position"), info->workposition);
+
 		if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->workwebpage))) {
-			g_string_append_printf(tmp, "\n<br><b>%s:</b> <a href=\"%s\">%s</a>", _("Web Page"), utf8, utf8);
+			char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
+			purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage);
+			g_free(webpage);
 			g_free(utf8);
 		}
-		purple_notify_user_info_add_pair(user_info, _("Work Information"), tmp->str);
-		g_string_free(tmp, TRUE);
 	}
 
 	if (buddy != NULL)
@@ -4354,14 +4419,14 @@
 			/* If the length was too long, try stripping the HTML and then running it back through
 			* purple_strdup_withhtml() and the encoding process. The result may be shorter. */
 			g_free((char *)args.msg);
-			
+
 			tmp2 = purple_markup_strip_html(tmp1);
 			g_free(tmp1);
 
 			/* re-escape the entities */
 			tmp1 = g_markup_escape_text(tmp2, -1);
 			g_free(tmp2);
-			
+
 			tmp2 = purple_strdup_withhtml(tmp1);
 			g_free(tmp1);
 			tmp1 = tmp2;
@@ -5591,7 +5656,7 @@
 	return "aim";
 }
 
-const char* oscar_list_emblem(PurpleBuddy *b)
+const char *oscar_list_emblem(PurpleBuddy *b)
 {
 	PurpleConnection *gc = NULL;
 	OscarData *od = NULL;
@@ -5621,7 +5686,7 @@
 			return "not-authorized";
 		}
 	}
-	
+
 	if (userinfo != NULL ) {
 		if (userinfo->flags & AIM_FLAG_ADMINISTRATOR)
 			return "admin";
@@ -5637,54 +5702,23 @@
 	return NULL;
 }
 
-void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) {
-	PurpleConnection *gc = b->account->gc;
-	OscarData *od = gc->proto_data;
-	aim_userinfo_t *userinfo = aim_locate_finduserinfo(od, b->name);
-
-	if (PURPLE_BUDDY_IS_ONLINE(b)) {
-		PurplePresence *presence;
-		PurpleStatus *status;
-		const char *message;
-
-		if (full)
-			oscar_string_append_info(gc, user_info, b, userinfo);
-
-		presence = purple_buddy_get_presence(b);
-		status = purple_presence_get_active_status(presence);
-		message = purple_status_get_attr_string(status, "message");
-
-		if (purple_status_is_available(status))
-		{
-			if (message != NULL)
-			{
-				/* Available status messages are plain text */
-				gchar *tmp;
-				tmp = g_markup_escape_text(message, -1);
-				purple_notify_user_info_add_pair(user_info, _("Message"), tmp);
-				g_free(tmp);
-			}
-		}
-		else
-		{
-			if (message != NULL)
-			{
-				/* Away messages are HTML */
-				gchar *tmp1, *tmp2;
-				tmp2 = purple_markup_strip_html(message);
-				tmp1 = g_markup_escape_text(tmp2, -1);
-				g_free(tmp2);
-				tmp2 = purple_str_sub_away_formatters(tmp1, purple_account_get_username(purple_connection_get_account(gc)));
-				g_free(tmp1);
-				purple_notify_user_info_add_pair(user_info, _("Away Message"), tmp2);
-				g_free(tmp2);
-			}
-			else
-			{
-				purple_notify_user_info_add_pair(user_info, _("Away Message"), _("<i>(retrieving)</i>"));
-			}
-		}
-	}
+void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
+{
+	PurpleConnection *gc;
+	OscarData *od;
+	aim_userinfo_t *userinfo;
+
+	if (!PURPLE_BUDDY_IS_ONLINE(b))
+		return;
+
+	gc = b->account->gc;
+	od = gc->proto_data;
+	userinfo = aim_locate_finduserinfo(od, b->name);
+
+	oscar_user_info_append_status(gc, user_info, b, userinfo, /* strip_html_tags */ TRUE);
+
+	if (full)
+		oscar_user_info_append_extra_info(gc, user_info, b, userinfo);
 }
 
 char *oscar_status_text(PurpleBuddy *b)
@@ -5719,7 +5753,7 @@
 		message = purple_status_get_attr_string(status, "message");
 		if (message != NULL)
 		{
-			ret = g_markup_escape_text(message, -1);
+			ret = g_strdup(message);
 			purple_util_chrreplace(ret, '\n', ' ');
 		}
 	}
@@ -6390,15 +6424,12 @@
 	if (img == NULL) {
 		aim_ssi_delicon(od);
 	} else {
-		PurpleCipher *cipher;
 		PurpleCipherContext *context;
 		guchar md5[16];
 		gconstpointer data = purple_imgstore_get_data(img);
 		size_t len = purple_imgstore_get_size(img);
 
-
-		cipher = purple_ciphers_find_cipher("md5");
-		context = purple_cipher_context_new(cipher, NULL);
+		context = purple_cipher_context_new_by_name("md5", NULL);
 		purple_cipher_context_append(context, data, len);
 		purple_cipher_context_digest(context, 16, md5, NULL);
 		purple_cipher_context_destroy(context);
@@ -6752,6 +6783,10 @@
 		OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY);
 	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
 
+	option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins",
+											OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS);
+	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+	
 	if (init)
 		return;
 	init = TRUE;
--- a/libpurple/protocols/oscar/oscar.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Thu Jun 26 20:25:38 2008 +0000
@@ -353,7 +353,8 @@
 	OSCAR_CAPABILITY_ICHATAV              = 0x02000000,
 	OSCAR_CAPABILITY_LIVEVIDEO            = 0x04000000,
 	OSCAR_CAPABILITY_CAMERA               = 0x08000000,
-	OSCAR_CAPABILITY_LAST                 = 0x10000000
+	OSCAR_CAPABILITY_ICHAT_SCREENSHARE	  = 0x10000000,
+	OSCAR_CAPABILITY_LAST                 = 0x20000000
 } OscarCapability;
 
 /*
@@ -594,7 +595,7 @@
 
 void aim_clientready(OscarData *od, FlapConnection *conn);
 int aim_request_login(OscarData *od, FlapConnection *conn, const char *sn);
-int aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key);
+int aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins);
 /* 0x000b */ int aim_auth_securid_send(OscarData *od, const char *securid);
 
 void aim_cleansnacs(OscarData *, int maxage);
--- a/libpurple/protocols/oscar/oscarcommon.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/oscar/oscarcommon.h	Thu Jun 26 20:25:38 2008 +0000
@@ -41,6 +41,7 @@
 #define OSCAR_DEFAULT_HIDE_IP TRUE
 #define OSCAR_DEFAULT_WEB_AWARE FALSE
 #define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE
+#define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE
 
 #ifdef _WIN32
 const char *oscar_get_locale_charset(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Thu Jun 26 20:25:38 2008 +0000
@@ -0,0 +1,49 @@
+2008.06.07 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+	* Clean code and apply patches from QuLogic
+
+2008.05.19 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+	* Reconnect server 5 time in 5000 ms, when connect failed
+	* Rename sendqueue.c/sendqueue.h to qq_trans.c/qq_trans.h
+	* Rewrite packet_process
+	* Rewrite qq_send_cmd
+	* Create server list, try to connect every server when failed
+
+2008.05.14 - ccpaging <ecc_hy(at)hotmail.com>
+	* Move function for before login packets storing to sendqueue
+	* Use transaction data structure to store before login packets
+	* Rewrite tcp_pending and packet_process in qq_network.c
+
+2008.05.09 - ccpaging <ecc_hy(at)hotmail.com>
+	* Remove function _create_packet_head_seq in qq_network.c
+	* Create new function encap in qq_netowork.c
+	* Clean code of qq_send_packet_request_login_token and qq_send_packet_login in login_out.c
+
+2008.05.09 - ccpaging <ecc_hy(at)hotmail.com>
+	* Clean code of packet_parse.c, enable PARSER_DEBUG
+	* Rewrite send_queue
+
+2008.05.08 - ccpaging <ecc_hy(at)hotmail.com>
+	* Rewrite qq_network
+	* Add srv resolve function when qq_login
+	* Merge function _qq_common_clean in qq_proxy.c to qq_disconnect
+	* Move orignal qq_disconnect to qq_close
+	* qq_data alloc in qq_open and release in qq_close
+	* Network connect of QQ is created in qq_connect, and release in qq_disconnect
+
+2008.05.05 - ccpaging <ecc_hy(at)hotmail.com>
+	* Merge function _qq_common_clean in qq_proxy.c to qq_disconnect
+	* Move orignal qq_disconnect to qq_close
+	* qq_data alloc in qq_open and release in qq_close
+	* Network connect of QQ is created in qq_connect, and release in qq_disconnect
+
+2008.05.05 - ccpaging <ecc_hy(at)hotmail.com>
+	* Add qq_hex_dump function
+
+2008.04.25 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+	* Rewrite read_packet and create_packet functions, use qq_put and qq_get functions instead
+	* New logic in accord with protocol models to handle packets, some related functions rewritten
+
+2008.03.24 - ccpaging <ecc_hy(at)hotmail.com>
+	* Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly
+
+** since pidgin-2.4.0 ***
--- a/libpurple/protocols/qq/Makefile.am	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Thu Jun 26 20:25:38 2008 +0000
@@ -52,20 +52,14 @@
 	packet_parse.h \
 	qq.c \
 	qq.h \
-	qq_proxy.c \
-	qq_proxy.h \
-	recv_core.c \
-	recv_core.h \
-	send_core.c \
-	send_core.h \
+	qq_network.c \
+	qq_network.h \
 	send_file.c \
 	send_file.h \
-	sendqueue.c \
-	sendqueue.h \
+	qq_trans.c \
+	qq_trans.h \
 	sys_msg.c \
 	sys_msg.h \
-	udp_proxy_s5.c \
-	udp_proxy_s5.h \
 	utils.c \
 	utils.h
 
--- a/libpurple/protocols/qq/Makefile.mingw	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Thu Jun 26 20:25:38 2008 +0000
@@ -63,13 +63,10 @@
 	login_logout.c \
 	packet_parse.c \
 	qq.c \
-	qq_proxy.c \
-	recv_core.c \
-	send_core.c \
+	qq_network.c \
 	send_file.c \
-	sendqueue.c \
+	qq_trans.c \
 	sys_msg.c \
-	udp_proxy_s5.c \
 	utils.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/qq/buddy_info.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Thu Jun 26 20:25:38 2008 +0000
@@ -34,7 +34,7 @@
 #include "crypt.h"
 #include "header_info.h"
 #include "keep_alive.h"
-#include "send_core.h"
+#include "qq_network.h"
 
 #define QQ_PRIMARY_INFORMATION _("Primary Information")
 #define QQ_ADDITIONAL_INFORMATION _("Additional Information")
@@ -94,6 +94,46 @@
 	gboolean modify_info;
 } qq_info_query;
 
+typedef struct _contact_info {
+	gchar *uid;
+	gchar *nick;
+	gchar *country;
+	gchar *province;
+	gchar *zipcode;
+	gchar *address;
+	gchar *tel;
+	gchar *age;
+	gchar *gender;
+	gchar *name;
+	gchar *email;
+	gchar *pager_sn;
+	gchar *pager_num;
+	gchar *pager_sp;
+	gchar *pager_base_num;
+	gchar *pager_type;
+	gchar *occupation;
+	gchar *homepage;
+	gchar *auth_type;
+	gchar *unknown1;
+	gchar *unknown2;
+	gchar *face;
+	gchar *hp_num;
+	gchar *hp_type;
+	gchar *intro;
+	gchar *city;
+	gchar *unknown3;
+	gchar *unknown4;
+	gchar *unknown5;
+	gchar *is_open_hp;
+	gchar *is_open_contact;
+	gchar *college;
+	gchar *horoscope;
+	gchar *zodiac;
+	gchar *blood;
+	gchar *qq_show;
+	gchar *unknown6;        /* always 0x2D */
+} contact_info;
+
 /* We get an info packet on ourselves before we modify our information.
  * Even though not all of the information is modifiable, it still
  * all needs to be there when we send out the modify info packet */
@@ -137,7 +177,7 @@
 			} else {
 				return NULL;
 			}
-		/* else ASCIIized index */
+			/* else ASCIIized index */
 		} else {
 			if (strcmp(choice[index], "-") != 0)
 				return g_strdup(choice[index]);
@@ -161,14 +201,14 @@
 	if (value != NULL) {
 		purple_notify_user_info_add_pair(user_info, title, value);
 		g_free(value);
-		
+
 		return TRUE;
 	}
-	
+
 	return FALSE;
 }
 
-static PurpleNotifyUserInfo *
+	static PurpleNotifyUserInfo *
 info_to_notify_user_info(const contact_info *info)
 {
 	PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
@@ -209,25 +249,25 @@
 
 	/* for debugging */
 	/*
-	g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous");
-	append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0);
-	append_field_value(info_text, info->pager_num, "pager_num", NULL, 0);
-	append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0);
-	append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0);
-	append_field_value(info_text, info->pager_type, "pager_type", NULL, 0);
-	append_field_value(info_text, info->auth_type, "auth_type", NULL, 0);
-	append_field_value(info_text, info->unknown1, "unknown1", NULL, 0);
-	append_field_value(info_text, info->unknown2, "unknown2", NULL, 0);
-	append_field_value(info_text, info->face, "face", NULL, 0);
-	append_field_value(info_text, info->hp_type, "hp_type", NULL, 0);
-	append_field_value(info_text, info->unknown3, "unknown3", NULL, 0);
-	append_field_value(info_text, info->unknown4, "unknown4", NULL, 0);
-	append_field_value(info_text, info->unknown5, "unknown5", NULL, 0);
-	append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0);
-	append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0);
-	append_field_value(info_text, info->qq_show, "qq_show", NULL, 0);
-	append_field_value(info_text, info->unknown6, "unknown6", NULL, 0);
-	*/
+	   g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous");
+	   append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0);
+	   append_field_value(info_text, info->pager_num, "pager_num", NULL, 0);
+	   append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0);
+	   append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0);
+	   append_field_value(info_text, info->pager_type, "pager_type", NULL, 0);
+	   append_field_value(info_text, info->auth_type, "auth_type", NULL, 0);
+	   append_field_value(info_text, info->unknown1, "unknown1", NULL, 0);
+	   append_field_value(info_text, info->unknown2, "unknown2", NULL, 0);
+	   append_field_value(info_text, info->face, "face", NULL, 0);
+	   append_field_value(info_text, info->hp_type, "hp_type", NULL, 0);
+	   append_field_value(info_text, info->unknown3, "unknown3", NULL, 0);
+	   append_field_value(info_text, info->unknown4, "unknown4", NULL, 0);
+	   append_field_value(info_text, info->unknown5, "unknown5", NULL, 0);
+	   append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0);
+	   append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0);
+	   append_field_value(info_text, info->qq_show, "qq_show", NULL, 0);
+	   append_field_value(info_text, info->unknown6, "unknown6", NULL, 0);
+	   */
 
 	return user_info;
 }
@@ -243,7 +283,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(qd, QQ_CMD_GET_USER_INFO, (guint8 *) uid_str, strlen(uid_str));
 
 	query = g_new0(qq_info_query, 1);
 	query->uid = uid;
@@ -271,27 +311,141 @@
 }
 
 /* send packet to modify personal information */
-static void qq_send_packet_modify_info(PurpleConnection *gc, gchar **segments)
+static void qq_send_packet_modify_info(PurpleConnection *gc, contact_info *info)
 {
-	gint i;
-	guint8 *raw_data, *cursor, bar;
+	qq_data *qd = (qq_data *) gc->proto_data;
+	gint bytes = 0;
+	guint8 raw_data[MAX_PACKET_SIZE - 128] = {0};
+	guint8 bar;
 
-	g_return_if_fail(segments != NULL);
+	g_return_if_fail(info != NULL);
 
 	bar = 0x1f;
-	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 128);
-	cursor = raw_data;
 
-	create_packet_b(raw_data, &cursor, bar);
+	bytes += qq_put8(raw_data + bytes, bar);
 
 	/* important! skip the first uid entry */
-	for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
-		create_packet_b(raw_data, &cursor, bar);
-		create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i]));
-	}
-	create_packet_b(raw_data, &cursor, bar);
+	/*
+	   for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
+	   create_packet_b(raw_data, &cursor, bar);
+	   create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i]));
+	   }
+	   */
+	/* uid */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->uid, strlen(info->uid));
+	/* nick */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->nick, strlen(info->nick));
+	/* country */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->country, strlen(info->country));
+	/* province */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->province, strlen(info->province));
+	/* zipcode */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zipcode, strlen(info->zipcode));
+	/* address */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->address, strlen(info->address));
+	/* tel */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->tel, strlen(info->tel));
+	/* age */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->age, strlen(info->age));
+	/* gender */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->gender, strlen(info->gender));
+	/* name */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->name, strlen(info->name));
+	/* email */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->email, strlen(info->email));
+	/* pager_sn */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sn, strlen(info->pager_sn));
+	/* pager_num */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_num, strlen(info->pager_num));
+	/* pager_sp */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sp, strlen(info->pager_sp));
+	/* pager_base_num */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_base_num, strlen(info->pager_base_num));
+	/* pager_type */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_type, strlen(info->pager_type));
+	/* occupation */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->occupation, strlen(info->occupation));
+	/* homepage */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->homepage, strlen(info->homepage));
+	/* auth_type */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->auth_type, strlen(info->auth_type));
+	/* unknown1 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown1, strlen(info->unknown1));
+	/* unknown2 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown2, strlen(info->unknown2));
+	/* face */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->face, strlen(info->face));
+	/* hp_num */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_num, strlen(info->hp_num));
+	/* hp_type */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_type, strlen(info->hp_type));
+	/* intro */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->intro, strlen(info->intro));
+	/* city */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->city, strlen(info->city));
+	/* unknown3 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown3, strlen(info->unknown3));
+	/* unknown4 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown4, strlen(info->unknown4));
+	/* unknown5 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown5, strlen(info->unknown5));
+	/* is_open_hp */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_hp, strlen(info->is_open_hp));
+	/* is_open_contact */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_contact, strlen(info->is_open_contact));
+	/* college */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->college, strlen(info->college));
+	/* horoscope */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->horoscope, strlen(info->horoscope));
+	/* zodiac */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zodiac, strlen(info->zodiac));
+	/* blood */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->blood, strlen(info->blood));
+	/* qq_show */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->qq_show, strlen(info->qq_show));
+	/* unknown6 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown6, strlen(info->unknown6));
 
-	qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+	bytes += qq_put8(raw_data + bytes, bar);
+
+	qq_send_cmd(qd, QQ_CMD_UPDATE_INFO, raw_data, bytes);
 
 }
 
@@ -407,8 +561,11 @@
 		groups = groups->next;
 	}
 
-	/* This casting looks like a horrible idea to me -DAA */
-	qq_send_packet_modify_info(gc, (gchar **) info);
+	/* This casting looks like a horrible idea to me -DAA
+	 * yes, rewritten -s3e
+	 * qq_send_packet_modify_info(gc, (gchar **) info);
+	 */
+	qq_send_packet_modify_info(gc, info);
 
 	g_strfreev((gchar **) mid->info);
 	g_free(mid);
@@ -520,11 +677,11 @@
 		mid->info->unknown6 = g_strdup(info->unknown6);
 
 		purple_request_fields(gc, _("Modify my information"),
-			_("Modify my information"), NULL, fields,
-			_("Update my information"), G_CALLBACK(modify_info_ok_cb),
-			_("Cancel"), G_CALLBACK(modify_info_cancel_cb),
-			purple_connection_get_account(gc), NULL, NULL,
-			mid);
+				_("Modify my information"), NULL, fields,
+				_("Update my information"), G_CALLBACK(modify_info_ok_cb),
+				_("Cancel"), G_CALLBACK(modify_info_cancel_cb),
+				purple_connection_get_account(gc), NULL, NULL,
+				mid);
 	}
 }
 
@@ -578,10 +735,9 @@
 	gchar *data;
 	gsize len;
 
-	if (!g_file_get_contents(iconfile, &data, &len, NULL))
+	if (!g_file_get_contents(iconfile, &data, &len, NULL)) {
 		g_return_if_reached();
-	else
-	{
+	} else {
 		purple_buddy_icons_set_for_user(account, who, data, len, icon_num);
 	}
 }
@@ -608,10 +764,10 @@
 
 	/* make sure we're using an appropriate icon */
 	if (!(g_ascii_strncasecmp(icon_path, buddy_icon_dir, dir_len) == 0
-		&& icon_path[dir_len] == G_DIR_SEPARATOR
-			&& g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0
-			&& g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0
-			&& icon_len <= 3)) {
+				&& icon_path[dir_len] == G_DIR_SEPARATOR
+				&& g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0
+				&& g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0
+				&& icon_len <= 3)) {
 		if (icon_global)
 			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", errmsg);
 		else
@@ -650,13 +806,13 @@
 		old_icon_num = purple_buddy_icons_get_checksum_for_user(buddy);
 
 	if (old_icon_num == NULL ||
-	    strcmp(icon_num_str, old_icon_num))
+			strcmp(icon_num_str, old_icon_num))
 	{
 		gchar *icon_path;
 
 		icon_path = g_strconcat(qq_buddy_icon_dir(), G_DIR_SEPARATOR_S,
-		                        QQ_ICON_PREFIX, icon_num_str,
-		                        QQ_ICON_SUFFIX, NULL);
+				QQ_ICON_PREFIX, icon_num_str,
+				QQ_ICON_SUFFIX, NULL);
 
 		qq_set_buddy_icon_for_user(account, name, icon_num_str, icon_path);
 		g_free(icon_path);
@@ -665,7 +821,7 @@
 }
 
 /* after getting info or modify myself, refresh the buddy list accordingly */
-void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc)
+static void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc)
 {
 	PurpleBuddy *b;
 	qq_data *qd;
@@ -728,7 +884,7 @@
 			qd->modifying_face = FALSE;
 			g_free(info->face);
 			info->face = icon;
-			qq_send_packet_modify_info(gc, segments);
+			qq_send_packet_modify_info(gc, (contact_info *)segments);
 		}
 
 		qq_refresh_buddy_and_myself(info, gc);
@@ -777,39 +933,41 @@
 
 void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid)
 {
-	guint8 buf[5];
-	guint32 tmp = g_htonl(uid);
-	buf[0] = 0;
-	memcpy(buf+1, &tmp, 4);
-	qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, 5);
+	qq_data *qd = (qq_data *) gc->proto_data;
+	guint8 buf[16] = {0};
+	gint bytes = 0;
+
+	bytes += qq_put8(buf + bytes, 0x00);
+	bytes += qq_put32(buf + bytes, uid);
+
+	qd = (qq_data *) gc->proto_data;
+	qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, bytes);
 }
 
 void qq_send_packet_get_buddies_levels(PurpleConnection *gc)
 {
-	guint8 *buf, *tmp;
+	guint8 *buf;
 	guint16 size;
 	qq_buddy *q_bud;
 	qq_data *qd = (qq_data *) gc->proto_data;
 	GList *node = qd->buddies;
+	gint bytes = 0;
 
 	if (qd->buddies) {
 		/* server only sends back levels for online buddies, no point
- 	 	* in asking for anyone else */
-		size = 4*g_list_length(qd->buddies) + 1;
+		 * in asking for anyone else */
+		size = 4 * g_list_length(qd->buddies) + 1;
 		buf = g_new0(guint8, size);
-		tmp = buf + 1;
+		bytes += 1;
 
-		while (node != NULL) {
-			guint32 tmp4;
+		while (NULL != node) {
 			q_bud = (qq_buddy *) node->data;
-			if (q_bud != NULL) {
-				tmp4 = g_htonl(q_bud->uid);
-				memcpy(tmp, &tmp4, 4);
-				tmp += 4;
+			if (NULL != q_bud) {
+				bytes += qq_put32(buf + bytes, q_bud->uid);
 			}
 			node = node->next;
 		}
-		qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, size);
+		qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size);
 		g_free(buf);
 	}
 }
@@ -822,10 +980,11 @@
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	gint decr_len, i;
-	guint8 *decr_buf, *tmp;
+	guint8 *decr_buf;
 	PurpleAccount *account = purple_connection_get_account(gc);
 	qq_data *qd = (qq_data *) gc->proto_data;
-	
+	gint bytes = 0;
+
 	decr_len = buf_len;
 	decr_buf = g_new0(guint8, buf_len);
 	if (!qq_decrypt(buf, buf_len, qd->session_key, decr_buf, &decr_len)) {
@@ -835,28 +994,23 @@
 	decr_len--; 
 	if (decr_len % 12 != 0) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-			"Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12);
+				"Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12);
 		decr_len -= (decr_len % 12);
 	}
-		
-	tmp = decr_buf + 1;
+
+	bytes += 1;
 	/* this byte seems random */
 	/*
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]);
-	*/
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]);
+	   */
 	for (i = 0; i < decr_len; i += 12) {
-		uid = g_ntohl(*(guint32 *) tmp);
-		tmp += 4;
-		onlineTime = g_ntohl(*(guint32 *) tmp);
-		tmp += 4;
-		level = g_ntohs(*(guint16 *) tmp);
-		tmp += 2;
-		timeRemainder = g_ntohs(*(guint16 *) tmp);
-		tmp += 2;
-		/*
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n", 
+		bytes += qq_get32(&uid, decr_buf + bytes);
+		bytes += qq_get32(&onlineTime, decr_buf + bytes);
+		bytes += qq_get16(&level, decr_buf + bytes);
+		bytes += qq_get16(&timeRemainder, decr_buf + bytes);
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+				"Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n", 
 				uid, onlineTime, level, timeRemainder);
-		*/
 		purple_name = uid_to_purple_name(uid);
 		b = purple_find_buddy(account, purple_name);
 		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
@@ -872,7 +1026,7 @@
 			}
 		} else {
 			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-				"Got an online buddy %d, but not in my buddy list\n", uid);
+					"Got an online buddy %d, but not in my buddy list\n", uid);
 		}
 		g_free(purple_name);
 	}
--- a/libpurple/protocols/qq/buddy_info.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Thu Jun 26 20:25:38 2008 +0000
@@ -44,47 +44,6 @@
 #define QQ_ICON_PREFIX "qq_"
 #define QQ_ICON_SUFFIX ".png"
 
-typedef struct _contact_info {
-        gchar *uid;
-        gchar *nick;
-        gchar *country;
-        gchar *province;
-        gchar *zipcode;
-        gchar *address;
-        gchar *tel;
-        gchar *age;
-        gchar *gender;
-        gchar *name;
-        gchar *email;
-        gchar *pager_sn;
-        gchar *pager_num;
-        gchar *pager_sp;
-        gchar *pager_base_num;
-        gchar *pager_type;
-        gchar *occupation;
-        gchar *homepage;
-        gchar *auth_type;
-        gchar *unknown1;
-        gchar *unknown2;
-        gchar *face;
-        gchar *hp_num;
-        gchar *hp_type;
-        gchar *intro;
-        gchar *city;
-        gchar *unknown3;
-        gchar *unknown4;
-        gchar *unknown5;
-        gchar *is_open_hp;
-        gchar *is_open_contact;
-        gchar *college;
-        gchar *horoscope;
-        gchar *zodiac;
-        gchar *blood;
-        gchar *qq_show;
-        gchar *unknown6;        /* always 0x2D */
-} contact_info;
-
-void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc);
 void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window);
 void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
 void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile);
--- a/libpurple/protocols/qq/buddy_list.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Thu Jun 26 20:25:38 2008 +0000
@@ -38,13 +38,12 @@
 #include "crypt.h"
 #include "header_info.h"
 #include "keep_alive.h"
-#include "send_core.h"
 #include "group.h"
 #include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
 
-#include "qq_proxy.h"
+#include "qq_network.h"
 
 #define QQ_GET_ONLINE_BUDDY_02          0x02
 #define QQ_GET_ONLINE_BUDDY_03          0x03	/* unknown function */
@@ -64,25 +63,25 @@
 void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position)
 {
 	qq_data *qd;
-	guint8 *raw_data, *cursor;
+	guint8 *raw_data;
+	gint bytes = 0;
 
 	qd = (qq_data *) gc->proto_data;
 	raw_data = g_newa(guint8, 5);
-	cursor = raw_data;
 
 	/* 000-000 get online friends cmd
 	 * only 0x02 and 0x03 returns info from server, other valuse all return 0xff
 	 * I can also only send the first byte (0x02, or 0x03)
 	 * and the result is the same */
-	create_packet_b(raw_data, &cursor, QQ_GET_ONLINE_BUDDY_02);
+	bytes += qq_put8(raw_data + bytes, QQ_GET_ONLINE_BUDDY_02);
 	/* 001-001 seems it supports 255 online buddies at most */
-	create_packet_b(raw_data, &cursor, position);
+	bytes += qq_put8(raw_data + bytes, position);
 	/* 002-002 */
-	create_packet_b(raw_data, &cursor, 0x00);
+	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 003-004 */
-	create_packet_w(raw_data, &cursor, 0x0000);
+	bytes += qq_put16(raw_data + bytes, 0x0000);
 
-	qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_ONLINE, TRUE, 0, TRUE, raw_data, 5);
+	qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_ONLINE, raw_data, 5);
 	qd->last_get_online = time(NULL);
 }
 
@@ -90,42 +89,38 @@
  * server may return a position tag if list is too long for one packet */
 void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position)
 {
-	guint8 *raw_data, *cursor;
-	gint data_len;
+	qq_data *qd = (qq_data *) gc->proto_data;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
-	data_len = 3;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 	/* 000-001 starting position, can manually specify */
-	create_packet_w(raw_data, &cursor, position);
+	bytes += qq_put16(raw_data + bytes, position);
 	/* before Mar 18, 2004, any value can work, and we sent 00
 	 * I do not know what data QQ server is expecting, as QQ2003iii 0304 itself
 	 * even can sending packets 00 and get no response.
 	 * Now I tested that 00,00,00,00,00,01 work perfectly
 	 * March 22, found the 00,00,00 starts to work as well */
-	create_packet_b(raw_data, &cursor, 0x00);
+	bytes += qq_put8(raw_data + bytes, 0x00);
 
-	qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_LIST, TRUE, 0, TRUE, raw_data, data_len);
+	qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_LIST, raw_data, bytes);
 }
 
 /* get all list, buddies & Quns with groupsid support */
 void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position)
 {
-	guint8 *raw_data, *cursor;
-	gint data_len;
+	qq_data *qd = (qq_data *) gc->proto_data;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
-	data_len = 10;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 	/* 0x01 download, 0x02, upload */
-	create_packet_b(raw_data, &cursor, 0x01);
+	bytes += qq_put8(raw_data + bytes, 0x01);
 	/* unknown 0x02 */
-	create_packet_b(raw_data, &cursor, 0x02);
+	bytes += qq_put8(raw_data + bytes, 0x02);
 	/* unknown 00 00 00 00 */
-	create_packet_dw(raw_data, &cursor, 0x00000000);
-	create_packet_dw(raw_data, &cursor, position);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, position);
 
-	qq_send_cmd(gc, QQ_CMD_GET_ALL_LIST_WITH_GROUP, TRUE, 0, TRUE, raw_data, data_len);
+	qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes);
 }
 
 static void _qq_buddies_online_reply_dump_unclear(qq_friends_online_entry *fe)
@@ -151,8 +146,8 @@
 void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len, bytes;
-	guint8 *data, *cursor, position;
+	gint len, bytes, bytes_buddy;
+	guint8 *data, position;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	qq_friends_online_entry *fe;
@@ -162,96 +157,100 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n");
-	
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 
-		_qq_show_packet("Get buddies online reply packet", data, len);
-
-		read_packet_b(data, &cursor, len, &position);
-
-		fe = g_newa(qq_friends_online_entry, 1);
-		fe->s = g_newa(qq_buddy_status, 1);
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
+		return;
+	}
 
-		while (cursor < (data + len)) {
-			/* based on one online buddy entry */
-			bytes = 0;
-			/* 000-030 qq_buddy_status */
-			bytes += qq_buddy_status_read(data, &cursor, len, fe->s);
-			/* 031-032: unknown4 */
-			bytes += read_packet_w(data, &cursor, len, &fe->unknown1);
-			/* 033-033: flag1 */
-			bytes += read_packet_b(data, &cursor, len, &fe->flag1);
-			/* 034-034: comm_flag */
-			bytes += read_packet_b(data, &cursor, len, &fe->comm_flag);
-			/* 035-036: */
-			bytes += read_packet_w(data, &cursor, len, &fe->unknown2);
-			/* 037-037: */
-			bytes += read_packet_b(data, &cursor, len, &fe->ending);	/* 0x00 */
+	qq_show_packet("Get buddies online reply packet", data, len);
+
+	bytes = 0;
+	bytes += qq_get8(&position, data + bytes);
+
+	fe = g_newa(qq_friends_online_entry, 1);
+	fe->s = g_newa(qq_buddy_status, 1);
 
-			if (fe->s->uid == 0 || bytes != QQ_ONLINE_BUDDY_ENTRY_LEN) {
-				purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-						"uid=0 or entry complete len(%d) != %d", 
-						bytes, QQ_ONLINE_BUDDY_ENTRY_LEN);
-				g_free(fe->s->ip);
-				g_free(fe->s->unknown_key);
-				continue;
-			}	/* check if it is a valid entry */
-
-			if (QQ_DEBUG)
-				_qq_buddies_online_reply_dump_unclear(fe);
+	while (bytes < len) {
+		/* set flag */
+		bytes_buddy = bytes;
+		/* based on one online buddy entry */
+		/* ATTTENTION! NEWED in the sub function, but FREED here */
+		/* 000-030 qq_buddy_status */
+		bytes += qq_buddy_status_read(fe->s, data + bytes);
+		/* 031-032: unknown4 */
+		bytes += qq_get16(&fe->unknown1, data + bytes);
+		/* 033-033: flag1 */
+		bytes += qq_get8(&fe->flag1, data + bytes);
+		/* 034-034: comm_flag */
+		bytes += qq_get8(&fe->comm_flag, data + bytes);
+		/* 035-036: */
+		bytes += qq_get16(&fe->unknown2, data + bytes);
+		/* 037-037: */
+		bytes += qq_get8(&fe->ending, data + bytes);	/* 0x00 */
 
-			/* update buddy information */
-			b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid));
-			q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-
-			if (q_bud != NULL) {	/* we find one and update qq_buddy */
-				if(0 != fe->s->client_version)
-					q_bud->client_version = fe->s->client_version;
-				g_memmove(q_bud->ip, fe->s->ip, 4);
-				q_bud->port = fe->s->port;
-				q_bud->status = fe->s->status;
-				q_bud->flag1 = fe->flag1;
-				q_bud->comm_flag = fe->comm_flag;
-				qq_update_buddy_contact(gc, q_bud);
-			} else {
-				purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-						"Got an online buddy %d, but not in my buddy list\n", fe->s->uid);
-			}
-
+		if (fe->s->uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
+			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+					"uid=0 or entry complete len(%d) != %d", 
+					(bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN);
 			g_free(fe->s->ip);
 			g_free(fe->s->unknown_key);
-		}
-		
-		if(cursor > (data + len)) {
-			 purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
+			continue;
+		}	/* check if it is a valid entry */
+
+		if (QQ_DEBUG) {
+			_qq_buddies_online_reply_dump_unclear(fe);
 		}
 
-		if (position != QQ_FRIENDS_ONLINE_POSITION_END) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position);
+		/* update buddy information */
+		b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid));
+		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 
-			qq_send_packet_get_buddies_online(gc, position);
+		if (q_bud != NULL) {	/* we find one and update qq_buddy */
+			if(0 != fe->s->client_version)
+				q_bud->client_version = fe->s->client_version;
+			g_memmove(q_bud->ip, fe->s->ip, 4);
+			q_bud->port = fe->s->port;
+			q_bud->status = fe->s->status;
+			q_bud->flag1 = fe->flag1;
+			q_bud->comm_flag = fe->comm_flag;
+			qq_update_buddy_contact(gc, q_bud);
 		} else {
-			qq_send_packet_get_buddies_levels(gc);
-			qq_refresh_all_buddy_status(gc);
+			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+					"Got an online buddy %d, but not in my buddy list\n", fe->s->uid);
 		}
 
+		g_free(fe->s->ip);
+		g_free(fe->s->unknown_key);
+	}
+
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
+	}
+
+	if (position != QQ_FRIENDS_ONLINE_POSITION_END) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position);
+
+		qq_send_packet_get_buddies_online(gc, position);
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
+		qq_send_packet_get_buddies_levels(gc);
+		qq_refresh_all_buddy_status(gc);
 	}
 }
 
+
 /* process reply for get_buddies_list */
 void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	qq_buddy *q_bud;
-	gint len, bytes, bytes_expected, i;
+	gint len, bytes_expected, i;
+	gint bytes, buddy_bytes;
 	guint16 position, unknown;
-	guint8 *data, *cursor, pascal_len;
+	guint8 *data, pascal_len;
 	gchar *name;
 	PurpleBuddy *b;
 
@@ -260,81 +259,84 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
+
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
+		return;
+	}
+	bytes = 0;
+	bytes += qq_get16(&position, data + bytes);
+	/* the following data is buddy list in this packet */
+	i = 0;
+	while (bytes < len) {
+		q_bud = g_new0(qq_buddy, 1);
+		/* set flag */
+		buddy_bytes = bytes;
+		/* 000-003: uid */
+		bytes += qq_get32(&q_bud->uid, data + bytes);
+		/* 004-005: icon index (1-255) */
+		bytes += qq_get16(&q_bud->face, data + bytes);
+		/* 006-006: age */
+		bytes += qq_get8(&q_bud->age, data + bytes);
+		/* 007-007: gender */
+		bytes += qq_get8(&q_bud->gender, data + bytes);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		read_packet_w(data, &cursor, len, &position);
-		/* the following data is buddy list in this packet */
-		i = 0;
-		while (cursor < (data + len)) {
-			q_bud = g_new0(qq_buddy, 1);
-			bytes = 0;
-			/* 000-003: uid */
-			bytes += read_packet_dw(data, &cursor, len, &q_bud->uid);
-			/* 004-005: icon index (1-255) */
-			bytes += read_packet_w(data, &cursor, len, &q_bud->face);
-			/* 006-006: age */
-			bytes += read_packet_b(data, &cursor, len, &q_bud->age);
-			/* 007-007: gender */
-			bytes += read_packet_b(data, &cursor, len, &q_bud->gender);
-			pascal_len = convert_as_pascal_string(cursor, &q_bud->nickname, QQ_CHARSET_DEFAULT);
-			cursor += pascal_len;
-			bytes += pascal_len;
-			bytes += read_packet_w(data, &cursor, len, &unknown);
-			/* flag1: (0-7)
-			 *        bit1 => qq show
-			 * comm_flag: (0-7)
-			 *        bit1 => member
-			 *        bit4 => TCP mode
-			 *        bit5 => open mobile QQ
-			 *        bit6 => bind to mobile
-			 *        bit7 => whether having a video
-			 */
-			bytes += read_packet_b(data, &cursor, len, &q_bud->flag1);
-			bytes += read_packet_b(data, &cursor, len, &q_bud->comm_flag);
+		pascal_len = convert_as_pascal_string(data + bytes, &q_bud->nickname, QQ_CHARSET_DEFAULT);
+		bytes += pascal_len;
 
-			bytes_expected = 12 + pascal_len;
-
-			if (q_bud->uid == 0 || bytes != bytes_expected) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes);
-				g_free(q_bud->nickname);
-				g_free(q_bud);
-				continue;
-			} else {
-				i++;
-			}
+		bytes += qq_get16(&unknown, data + bytes);
+		/* flag1: (0-7)
+		 *        bit1 => qq show
+		 * comm_flag: (0-7)
+		 *        bit1 => member
+		 *        bit4 => TCP mode
+		 *        bit5 => open mobile QQ
+		 *        bit6 => bind to mobile
+		 *        bit7 => whether having a video
+		 */
+		bytes += qq_get8(&q_bud->flag1, data + bytes);
+		bytes += qq_get8(&q_bud->comm_flag, data + bytes);
 
-			if (QQ_DEBUG) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x\n",
-					   q_bud->uid, q_bud->flag1, q_bud->comm_flag);
-			}
+		bytes_expected = 12 + pascal_len;
 
-			name = uid_to_purple_name(q_bud->uid);
-			b = purple_find_buddy(gc->account, name);
-			g_free(name);
-
-			if (b == NULL)
-				b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE);
-
-			b->proto_data = q_bud;
-			qd->buddies = g_list_append(qd->buddies, q_bud);
-			qq_update_buddy_contact(gc, q_bud);
+		if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
+			purple_debug(PURPLE_DEBUG_INFO, "QQ",
+					"Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes);
+			g_free(q_bud->nickname);
+			g_free(q_bud);
+			continue;
+		} else {
+			i++;
 		}
 
-		if(cursor > (data + len)) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
-                }
-		if (position == QQ_FRIENDS_LIST_POSITION_END) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get friends list done, %d buddies\n", i);
-			qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
-		} else {
-			qq_send_packet_get_buddies_list(gc, position);
+		if (QQ_DEBUG) {
+			purple_debug(PURPLE_DEBUG_INFO, "QQ",
+					"buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x\n",
+					q_bud->uid, q_bud->flag1, q_bud->comm_flag);
+		}
+
+		name = uid_to_purple_name(q_bud->uid);
+		b = purple_find_buddy(gc->account, name);
+		g_free(name);
+
+		if (b == NULL) {
+			b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE);
 		}
+
+		b->proto_data = q_bud;
+		qd->buddies = g_list_append(qd->buddies, q_bud);
+		qq_update_buddy_contact(gc, q_bud);
+	}
+
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
+	}
+	if (position == QQ_FRIENDS_LIST_POSITION_END) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get friends list done, %d buddies\n", i);
+		qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
+		qq_send_packet_get_buddies_list(gc, position);
 	}
 }
 
@@ -342,7 +344,8 @@
 {
 	qq_data *qd;
 	gint len, i, j;
-	guint8 *data, *cursor;
+	gint bytes = 0;
+	guint8 *data;
 	guint8 sub_cmd, reply_code;
 	guint32 unknown, position;
 	guint32 uid;
@@ -354,62 +357,66 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
+
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
+		return;
+	}
+
+	bytes += qq_get8(&sub_cmd, data + bytes);
+	g_return_if_fail(sub_cmd == 0x01);
+
+	bytes += qq_get8(&reply_code, data + bytes);
+	if(0 != reply_code) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
+				"Get all list with group reply, reply_code(%d) is not zero", reply_code);
+	}
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		read_packet_b(data, &cursor, len, &sub_cmd);
-		g_return_if_fail(sub_cmd == 0x01);
-		read_packet_b(data, &cursor, len, &reply_code);
-		if(0 != reply_code) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-					"Get all list with group reply, reply_code(%d) is not zero", reply_code);
+	bytes += qq_get32(&unknown, data + bytes);
+	bytes += qq_get32(&position, data + bytes);
+	/* the following data is all list in this packet */
+	i = 0;
+	j = 0;
+	while (bytes < len) {
+		/* 00-03: uid */
+		bytes += qq_get32(&uid, data + bytes);
+		/* 04: type 0x1:buddy 0x4:Qun */
+		bytes += qq_get8(&type, data + bytes);
+		/* 05: groupid*4 */ /* seems to always be 0 */
+		bytes += qq_get8(&groupid, data + bytes);
+		/*
+		   purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid);
+		   groupid >>= 2;
+		   */
+		if (uid == 0 || (type != 0x1 && type != 0x4)) {
+			purple_debug(PURPLE_DEBUG_INFO, "QQ",
+					"Buddy entry, uid=%d, type=%d", uid, type);
+			continue;
+		} 
+		if(0x1 == type) { /* a buddy */
+			/* don't do anything but count - buddies are handled by 
+			 * qq_send_packet_get_buddies_list */
+			++i;
+		} else { /* a group */
+			group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID);
+			if(group == NULL) {
+				qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
+				group = g_newa(qq_group, 1);
+				group->internal_group_id = uid;
+				qq_send_cmd_group_get_group_info(gc, group);
+			} else {
+				group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+				qq_group_refresh(gc, group);
+				qq_send_cmd_group_get_group_info(gc, group);
+			}
+			++j;
 		}
-		read_packet_dw(data, &cursor, len, &unknown);
-		read_packet_dw(data, &cursor, len, &position);
-		/* the following data is all list in this packet */
-		i = 0;
-		j = 0;
-		while (cursor < (data + len)) {
-			/* 00-03: uid */
-			read_packet_dw(data, &cursor, len, &uid);
-			/* 04: type 0x1:buddy 0x4:Qun */
-			read_packet_b(data, &cursor, len, &type);
-			/* 05: groupid*4 */ /* seems to always be 0 */
-			read_packet_b(data, &cursor, len, &groupid);
-			/*
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid);
-			groupid >>= 2;
-			*/
-			if (uid == 0 || (type != 0x1 && type != 0x4)) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "Buddy entry, uid=%d, type=%d", uid, type);
-				continue;
-			} 
-			if(0x1 == type) { /* a buddy */
-				/* don't do anything but count - buddies are handled by 
-				 * qq_send_packet_get_buddies_list */
-				++i;
-			} else { /* a group */
-				group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID);
-				if(group == NULL) {
-					qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
-					group = g_newa(qq_group, 1);
-					group->internal_group_id = uid;
-					qq_send_cmd_group_get_group_info(gc, group);
-				} else {
-					group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
-					qq_group_refresh(gc, group);
-					qq_send_cmd_group_get_group_info(gc, group);
-				}
-				++j;
-			}
-		}
-		if(cursor > (data + len)) {
-			 purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
-		}
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
 	}
+
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j);
 }
--- a/libpurple/protocols/qq/buddy_opt.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Thu Jun 26 20:25:38 2008 +0000
@@ -36,7 +36,7 @@
 #include "im.h"
 #include "keep_alive.h"
 #include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 #define PURPLE_GROUP_QQ_FORMAT          "QQ (%s)"
@@ -61,33 +61,33 @@
 /* send packet to remove a buddy from my buddy list */
 static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid)
 {
+	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar uid_str[11];
 
 	g_return_if_fail(uid > 0);
 
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_DEL_FRIEND, TRUE, 0, 
-			TRUE, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(qd, QQ_CMD_DEL_FRIEND, (guint8 *) uid_str, strlen(uid_str));
 }
 
 /* try to remove myself from someone's buddy list */
 static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid)
 {
-	guint8 *raw_data, *cursor;
+	qq_data *qd = (qq_data *) gc->proto_data;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(uid > 0);
 
-	raw_data = g_newa(guint8, 4);
-	cursor = raw_data;
-	create_packet_dw(raw_data, &cursor, uid);
+	bytes += qq_put32(raw_data + bytes, uid);
 
-	qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, TRUE, 0, TRUE, raw_data, 4);
+	qq_send_cmd(qd, QQ_CMD_REMOVE_SELF, raw_data, bytes);
 }
 
 /* try to add a buddy without authentication */
 static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid)
 {
-	qq_data *qd;
+	qq_data *qd = (qq_data *) gc->proto_data;
 	qq_add_buddy_request *req;
 	gchar uid_str[11];
 
@@ -95,11 +95,9 @@
 
 	/* we need to send the ascii code of this uid to qq server */
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_ADD_FRIEND_WO_AUTH, TRUE, 0, 
-			TRUE, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(qd, QQ_CMD_ADD_FRIEND_WO_AUTH, (guint8 *) uid_str, strlen(uid_str));
 
 	/* must be set after sending packet to get the correct send_seq */
-	qd = (qq_data *) gc->proto_data;
 	req = g_new0(qq_add_buddy_request, 1);
 	req->seq = qd->send_seq;
 	req->uid = uid;
@@ -109,28 +107,29 @@
 /* this buddy needs authentication, text conversion is done at lowest level */
 static void _qq_send_packet_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text)
 {
+	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar *text_qq, uid_str[11];
-	guint8 bar, *cursor, *raw_data;
+	guint8 bar, *raw_data;
+	gint bytes = 0;
 
 	g_return_if_fail(uid != 0);
 
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
 	bar = 0x1f;
 	raw_data = g_newa(guint8, QQ_MSG_IM_MAX);
-	cursor = raw_data;
 
-	create_packet_data(raw_data, &cursor, (guint8 *) uid_str, strlen(uid_str));
-	create_packet_b(raw_data, &cursor, bar);
-	create_packet_b(raw_data, &cursor, response);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *) uid_str, strlen(uid_str));
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_put8(raw_data + bytes, response);
 
 	if (text != NULL) {
 		text_qq = utf8_to_qq(text, QQ_CHARSET_DEFAULT);
-		create_packet_b(raw_data, &cursor, bar);
-		create_packet_data(raw_data, &cursor, (guint8 *) text_qq, strlen(text_qq));
+		bytes += qq_put8(raw_data + bytes, bar);
+		bytes += qq_putdata(raw_data + bytes, (guint8 *) text_qq, strlen(text_qq));
 		g_free(text_qq);
 	}
 
-	qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+	qq_send_cmd(qd, QQ_CMD_BUDDY_AUTH, raw_data, bytes);
 }
 
 static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text)
@@ -210,10 +209,10 @@
 
 	nombre = uid_to_purple_name(uid);
 	purple_request_input(gc, _("Reject request"), msg1, msg2,
-			   _("Sorry, you are not my type..."), TRUE, FALSE,
-			   NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
-			   purple_connection_get_account(gc), nombre, NULL,
-			   g2);
+			_("Sorry, you are not my type..."), TRUE, FALSE,
+			NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
+			purple_connection_get_account(gc), nombre, NULL,
+			g2);
 	g_free(nombre);
 }
 
@@ -257,7 +256,8 @@
 {
 	qq_data *qd;
 	gint len;
-	guint8 *data, *cursor, reply;
+	gint bytes = 0;
+	guint8 *data, reply;
 	gchar **segments, *msg_utf8;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
@@ -265,22 +265,23 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
+
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n");
+	}
+
+	bytes += qq_get8(&reply, data + bytes);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		read_packet_b(data, &cursor, len, &reply);
-		if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n");
-			if (NULL == (segments = split_data(data, len, "\x1f", 2)))
-				return;
-			msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
-			purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8);
-			g_free(msg_utf8);
-		} else {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n");
+	if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n");
+		if (NULL == (segments = split_data(data, len, "\x1f", 2))) {
+			return;
 		}
+		msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
+		purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8);
+		g_free(msg_utf8);
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n");
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n");
 	}
 }
 
@@ -289,7 +290,8 @@
 {
 	qq_data *qd;
 	gint len;
-	guint8 *data, *cursor, reply;
+	gint bytes = 0;
+	guint8 *data, reply;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -297,20 +299,20 @@
 	len = buf_len;
 	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		cursor = data;
-		read_packet_b(data, &cursor, len, &reply);
-		if (reply != QQ_REMOVE_BUDDY_REPLY_OK) {
-			/* there is no reason return from server */
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n");
-		} else {		/* if reply */
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n");
-			/* TODO: We don't really need to notify the user about this, do we? */
-			purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL);
-		}
-	} else {
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove buddy reply\n");
 	}
+
+	bytes += qq_get8(&reply, data + bytes);
+
+	if (reply != QQ_REMOVE_BUDDY_REPLY_OK) {
+		/* there is no reason return from server */
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n");
+	} else {		/* if reply */
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n");
+		/* TODO: We don't really need to notify the user about this, do we? */
+		purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL);
+	}
 }
 
 /* process the server reply for my request to remove myself from a buddy */
@@ -318,7 +320,8 @@
 {
 	qq_data *qd;
 	gint len;
-	guint8 *data, *cursor, reply;
+	gint bytes = 0;
+	guint8 *data, reply;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -326,20 +329,20 @@
 	len = buf_len;
 	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		cursor = data;
-		read_packet_b(data, &cursor, len, &reply);
-		if (reply != QQ_REMOVE_SELF_REPLY_OK)
-			/* there is no reason return from server */
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n");
-		else {		/* if reply */
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n");
-			/* TODO: Does the user really need to be notified about this? */
-			purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL);
-		}
-	} else {
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove self reply\n");
 	}
+
+	bytes += qq_get8(&reply, data + bytes);
+
+	if (reply != QQ_REMOVE_SELF_REPLY_OK) {
+		/* there is no reason return from server */
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n");
+	} else {		/* if reply */
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n");
+		/* TODO: Does the user really need to be notified about this? */
+		purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL);
+	}
 }
 
 void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc)
@@ -403,14 +406,14 @@
 			g->uid = for_uid;
 			msg = g_strdup_printf(_("User %d needs authentication"), for_uid);
 			purple_request_input(gc, NULL, msg,
-					   _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
-					   _("Would you be my friend?"),
-					   TRUE, FALSE, NULL, _("Send"),
-					   G_CALLBACK
-					   (_qq_send_packet_add_buddy_auth_with_gc_and_uid),
-					   _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
-					   purple_connection_get_account(gc), nombre, NULL,
-					   g);
+					_("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
+					_("Would you be my friend?"),
+					TRUE, FALSE, NULL, _("Send"),
+					G_CALLBACK
+					(_qq_send_packet_add_buddy_auth_with_gc_and_uid),
+					_("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
+					purple_connection_get_account(gc), nombre, NULL,
+					g);
 			g_free(msg);
 			g_free(nombre);
 		} else {	/* add OK */
@@ -457,7 +460,7 @@
 	g_return_val_if_fail(a != NULL && uid != 0, NULL);
 
 	group_name = is_known ?
-	    g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
+		g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
 
 	g = qq_get_purple_group(group_name);
 
@@ -512,8 +515,8 @@
 		if (b != NULL)
 			purple_blist_remove_buddy(b);
 		purple_notify_error(gc, NULL,
-				  _("QQid Error"),
-				  _("Invalid QQid"));
+				_("QQid Error"),
+				_("Invalid QQid"));
 	}
 }
 
--- a/libpurple/protocols/qq/buddy_status.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/buddy_status.c	Thu Jun 26 20:25:38 2008 +0000
@@ -3,7 +3,7 @@
  *
  * purple
  *
- * Purple is the legal property of its developers, whose names are too numerous
+ * Purple is the legal property ofr its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
  * source distribution.
  *
@@ -33,10 +33,9 @@
 #include "header_info.h"
 #include "keep_alive.h"
 #include "packet_parse.h"
-#include "send_core.h"
 #include "utils.h"
 
-#include "qq_proxy.h"
+#include "qq_network.h"
 
 #define QQ_MISC_STATUS_HAVING_VIIDEO      0x00000001
 #define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 	0x30	/* ASCII value of "0" */
@@ -57,7 +56,7 @@
 	g_string_append_printf(dump, "013-014:     %04x   (client_version)\n", s->client_version);
 	/* g_string_append_printf(dump, "015-030:     %s   (unknown key)\n", s->unknown_key); */
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Buddy status entry, %s", dump->str);
-	_qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH);
+	qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH);
 	g_string_free(dump, TRUE);
 }
 
@@ -66,35 +65,33 @@
  * using different accounts to get info. */
 
 /* parse the data into qq_buddy_status */
-gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s)
+gint qq_buddy_status_read(qq_buddy_status *s, guint8 *data)
 {
-	gint bytes;
+	gint bytes = 0;
 
-	g_return_val_if_fail(data != NULL && *cursor != NULL && s != NULL, -1);
-
-	bytes = 0;
+	g_return_val_if_fail(data != NULL && s != NULL, -1);
 
 	/* 000-003: uid */
-	bytes += read_packet_dw(data, cursor, len, &s->uid);
+	bytes += qq_get32(&s->uid, data + bytes);
 	/* 004-004: 0x01 */
-	bytes += read_packet_b(data, cursor, len, &s->unknown1);
+	bytes += qq_get8(&s->unknown1, data + bytes);
 	/* this is no longer the IP, it seems QQ (as of 2006) no longer sends
 	 * the buddy's IP in this packet. all 0s */
 	/* 005-008: ip */
 	s->ip = g_new0(guint8, 4);
-	bytes += read_packet_data(data, cursor, len, s->ip, 4);
+	bytes += qq_getdata(s->ip, 4, data + bytes);
 	/* port info is no longer here either */
 	/* 009-010: port */
-	bytes += read_packet_w(data, cursor, len, &s->port);
+	bytes += qq_get16(&s->port, data + bytes);
 	/* 011-011: 0x00 */
-	bytes += read_packet_b(data, cursor, len, &s->unknown2);
+	bytes += qq_get8(&s->unknown2, data + bytes);
 	/* 012-012: status */
-	bytes += read_packet_b(data, cursor, len, &s->status);
+	bytes += qq_get8(&s->status, data + bytes);
 	/* 013-014: client_version */
-	bytes += read_packet_w(data, cursor, len, &s->client_version);
+	bytes += qq_get16(&s->client_version, data + bytes);
 	/* 015-030: unknown key */
 	s->unknown_key = g_new0(guint8, QQ_KEY_LENGTH);
-	bytes += read_packet_data(data, cursor, len, s->unknown_key, QQ_KEY_LENGTH);
+	bytes += qq_getdata(s->unknown_key, QQ_KEY_LENGTH, data + bytes);
 
 	if (s->uid == 0 || bytes != 31)
 		return -1;
@@ -106,17 +103,17 @@
 gboolean is_online(guint8 status)
 {
 	switch(status) {
-	case QQ_BUDDY_ONLINE_NORMAL:
-	case QQ_BUDDY_ONLINE_AWAY:
-	case QQ_BUDDY_ONLINE_INVISIBLE:
-		return TRUE;
-	case QQ_BUDDY_ONLINE_OFFLINE:
-		return FALSE;
+		case QQ_BUDDY_ONLINE_NORMAL:
+		case QQ_BUDDY_ONLINE_AWAY:
+		case QQ_BUDDY_ONLINE_INVISIBLE:
+			return TRUE;
+		case QQ_BUDDY_ONLINE_OFFLINE:
+			return FALSE;
 	}
 	return FALSE;
 }
 
- /* Help calculate the correct icon index to tell the server. */
+/* Help calculate the correct icon index to tell the server. */
 gint get_icon_offset(PurpleConnection *gc)
 { 
 	PurpleAccount *account;
@@ -131,7 +128,7 @@
 			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
 			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
 		return 1;
-        } else {
+	} else {
 		return 0;
 	}
 }
@@ -140,7 +137,9 @@
 void qq_send_packet_change_status(PurpleConnection *gc)
 {
 	qq_data *qd;
-	guint8 *raw_data, *cursor, away_cmd;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
+	guint8 away_cmd;
 	guint32 misc_status;
 	gboolean fake_video;
 	PurpleAccount *account;
@@ -163,28 +162,24 @@
 		away_cmd = QQ_BUDDY_ONLINE_NORMAL;
 	}
 
-	raw_data = g_new0(guint8, 5);
-	cursor = raw_data;
 	misc_status = 0x00000000;
-
 	fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video");
 	if (fake_video)
 		misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO;
 
-	create_packet_b(raw_data, &cursor, away_cmd);
-	create_packet_dw(raw_data, &cursor, misc_status);
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, away_cmd);
+	bytes += qq_put32(raw_data + bytes, misc_status);
 
-	qq_send_cmd(gc, QQ_CMD_CHANGE_ONLINE_STATUS, TRUE, 0, TRUE, raw_data, 5);
-
-	g_free(raw_data);
+	qq_send_cmd(qd, QQ_CMD_CHANGE_ONLINE_STATUS, raw_data, bytes);
 }
 
 /* parse the reply packet for change_status */
 void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len;
-	guint8 *data, *cursor, reply;
+	gint len, bytes;
+	guint8 *data, reply;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	gchar *name;
@@ -195,21 +190,22 @@
 	len = buf_len;
 	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		cursor = data;
-		read_packet_b(data, &cursor, len, &reply);
-		if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n");
-		} else {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n");
-			name = uid_to_purple_name(qd->uid);
-			b = purple_find_buddy(gc->account, name);
-			g_free(name);
-			q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-			qq_update_buddy_contact(gc, q_bud);
-		}
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
+		return;
+	}
+
+	bytes = 0;
+	bytes = qq_get8(&reply, data + bytes);
+	if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n");
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n");
+		name = uid_to_purple_name(qd->uid);
+		b = purple_find_buddy(gc->account, name);
+		g_free(name);
+		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+		qq_update_buddy_contact(gc, q_bud);
 	}
 }
 
@@ -219,7 +215,7 @@
 	qq_data *qd;
 	gint len, bytes;
 	guint32 my_uid;
-	guint8 *data, *cursor;
+	guint8 *data;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	qq_buddy_status *s;
@@ -230,51 +226,53 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		s = g_new0(qq_buddy_status, 1);
-		bytes = 0;
-		/* 000-030: qq_buddy_status */
-		bytes += qq_buddy_status_read(data, &cursor, len, s);
-		/* 031-034: my uid */ 
-		/* This has a value of 0 when we've changed our status to 
-		 * QQ_BUDDY_ONLINE_INVISIBLE */
-		bytes += read_packet_dw(data, &cursor, len, &my_uid);
-
-		if (bytes != 35) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes);
-			g_free(s->ip);
-			g_free(s->unknown_key);
-			g_free(s);
-			return;
-		}
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
+		return;
+	}
 
-		name = uid_to_purple_name(s->uid);
-		b = purple_find_buddy(gc->account, name);
-		g_free(name);
-		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-		if (q_bud) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid);
-			if(0 != *((guint32 *)s->ip)) { 
-				g_memmove(q_bud->ip, s->ip, 4);
-				q_bud->port = s->port;
-			}
-			q_bud->status = s->status;
-			if(0 != s->client_version) 
-				q_bud->client_version = s->client_version; 
-			if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL)
-				qq_send_packet_get_level(gc, q_bud->uid);
-			qq_update_buddy_contact(gc, q_bud);
-		} else {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"got information of unknown buddy %d\n", s->uid);
-		}
+	s = g_new0(qq_buddy_status, 1);
+	bytes = 0;
+	/* 000-030: qq_buddy_status */
+	bytes += qq_buddy_status_read(s, data + bytes);
+	/* 031-034: my uid */ 
+	/* This has a value of 0 when we've changed our status to 
+	 * QQ_BUDDY_ONLINE_INVISIBLE */
+	bytes += qq_get32(&my_uid, data + bytes);
 
+	if (bytes != 35) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes);
 		g_free(s->ip);
 		g_free(s->unknown_key);
 		g_free(s);
+		return;
+	}
+
+	name = uid_to_purple_name(s->uid);
+	b = purple_find_buddy(gc->account, name);
+	g_free(name);
+	q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+	if (q_bud) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid);
+		if(0 != *((guint32 *)s->ip)) { 
+			g_memmove(q_bud->ip, s->ip, 4);
+			q_bud->port = s->port;
+		}
+		q_bud->status = s->status;
+		if(0 != s->client_version) {
+			q_bud->client_version = s->client_version; 
+		}
+		if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) {
+			qq_send_packet_get_level(gc, q_bud->uid);
+		}
+		qq_update_buddy_contact(gc, q_bud);
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"got information of unknown buddy %d\n", s->uid);
 	}
+
+	g_free(s->ip);
+	g_free(s->unknown_key);
+	g_free(s);
 }
--- a/libpurple/protocols/qq/buddy_status.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/buddy_status.h	Thu Jun 26 20:25:38 2008 +0000
@@ -52,7 +52,7 @@
 void qq_buddy_status_dump_unclear(qq_buddy_status *s);
 gboolean is_online(guint8 status);
 
-gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s);
+gint qq_buddy_status_read(qq_buddy_status *s, guint8 *data);
 gint get_icon_offset(PurpleConnection *gc);
 
 void qq_send_packet_change_status(PurpleConnection *gc);
--- a/libpurple/protocols/qq/char_conv.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Thu Jun 26 20:25:38 2008 +0000
@@ -39,9 +39,6 @@
 #define QQ_NULL_MSG           "(NULL)"	/* return this if conversion fails */
 #define QQ_NULL_SMILEY        "(SM)"	/* return this if smiley conversion fails */
 
-/* a debug function */
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
-
 const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
 	0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
 	0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x73,
@@ -111,16 +108,19 @@
 
 	ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error);
 
-	if (error == NULL)
+	if (error == NULL) {
 		return ret;	/* conversion is OK */
-	else {			/* conversion error */
-		gchar *failed = hex_dump_to_str((guint8 *) str, (len == -1) ? strlen(str) : len);
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message);
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Dump failed text\n%s", failed);
-		g_free(failed);
-		g_error_free(error);
-		return g_strdup(QQ_NULL_MSG);
 	}
+	
+	/* conversion error */
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message);
+
+	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+		(guint8 *) str, (len == -1) ? strlen(str) : len,
+		"Dump failed text");
+
+	g_error_free(error);
+	return g_strdup(QQ_NULL_MSG);
 }
 
 /* take the input as a pascal string and return a converted c-string in UTF-8
@@ -142,22 +142,23 @@
 gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg)
 {
 	GString *encoded;
-	guint8 font_attr, font_size, color[3], bar, *cursor;
+	guint8 font_attr, font_size, color[3], bar;
 	gboolean is_bold, is_italic, is_underline;
 	guint16 charset_code;
 	gchar *font_name, *color_code, *msg_utf8, *tmp, *ret;
+	gint bytes = 0;
 
-	cursor = data;
-	_qq_show_packet("QQ_MESG recv for font style", data, len);
+	/* checked qq_show_packet OK */
+	qq_show_packet("QQ_MESG recv for font style", data, len);
 
-	read_packet_b(data, &cursor, len, &font_attr);
-	read_packet_data(data, &cursor, len, color, 3);	/* red,green,blue */
+	bytes += qq_get8(&font_attr, data + bytes);
+	bytes += qq_getdata(color, 3, data + bytes);	/* red,green,blue */
 	color_code = g_strdup_printf("#%02x%02x%02x", color[0], color[1], color[2]);
 
-	read_packet_b(data, &cursor, len, &bar);	/* skip, not sure of its use */
-	read_packet_w(data, &cursor, len, &charset_code);
+	bytes += qq_get8(&bar, data + bytes);	/* skip, not sure of its use */
+	bytes += qq_get16(&charset_code, data + bytes);
 
-	tmp = g_strndup((gchar *) cursor, data + len - cursor);
+	tmp = g_strndup((gchar *)(data + bytes), len - bytes);
 	font_name = qq_to_utf8(tmp, QQ_CHARSET_DEFAULT);
 	g_free(tmp);
 
@@ -177,11 +178,11 @@
 	/* Henry: The range QQ sends rounds from 8 to 22, where a font size
 	 * of 10 is equal to 3 in html font tag */
 	g_string_append_printf(encoded,
-			       "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
-			       color_code, font_name, font_size / 3);
+			"<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
+			color_code, font_name, font_size / 3);
 	purple_debug(PURPLE_DEBUG_INFO, "QQ_MESG",
-		   "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
-		   color_code, font_name, font_size / 3);
+			"recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
+			color_code, font_name, font_size / 3);
 	g_string_append(encoded, msg_utf8);
 
 	if (is_bold) {
--- a/libpurple/protocols/qq/crypt.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/crypt.c	Thu Jun 26 20:25:38 2008 +0000
@@ -296,3 +296,20 @@
 	}
 	return 1;
 }
+
+/* return 1 is succeed, otherwise return 0
+gint qq_crypt(gint flag,
+		const guint8 *const instr, gint instrlen, 
+		const guint8 *const key, 
+		guint8 *outstr, gint *outstrlen_ptr)
+{
+	if (flag == DECRYPT)
+		return qq_decrypt(instr, instrlen, key, outstr, outstrlen_ptr);
+	else if (flag == ENCRYPT)
+		qq_encrypt(instr, instrlen, key, outstr, outstrlen_ptr);
+	else 
+		return 0;
+
+	return 1;
+}
+*/
--- a/libpurple/protocols/qq/crypt.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/crypt.h	Thu Jun 26 20:25:38 2008 +0000
@@ -35,4 +35,13 @@
 		const guint8 *const key,
 		guint8 *outstr, gint *outstrlen_ptr);
 		
+/*
+#define DECRYPT 0x00
+#define ENCRYPT 0x01
+
+gint qq_crypt(gint flag,
+	     const guint8 *const instr, gint instrlen, 
+	     const guint8 *const key, 
+	     guint8 *outstr, gint *outstrlen_ptr);
+*/
 #endif
--- a/libpurple/protocols/qq/file_trans.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/file_trans.c	Thu Jun 26 20:25:38 2008 +0000
@@ -38,12 +38,11 @@
 #include "im.h"
 #include "packet_parse.h"
 #include "proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "send_file.h"
 #include "utils.h"
 
 struct _qq_file_header {
-	guint8 tag;
 	guint16 client_ver;
 	guint8 file_key;
 	guint32 sender_uid;
@@ -58,11 +57,11 @@
 	key = seed | (seed << 8) | (seed << 16) | (seed << 24);
 	return key;
 }
-		
+
 static guint32 _gen_file_key(void)
 {
 	guint8 seed;
-	
+
 	seed = random();
 	return _get_file_key(seed);
 }
@@ -126,16 +125,17 @@
 	purple_cipher_context_destroy(context);
 }
 
-static void _qq_get_file_header(guint8 *buf, guint8 **cursor, gint buflen, qq_file_header *fh)
+static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf)
 {
-	read_packet_b(buf, cursor, buflen, &(fh->tag));
-	read_packet_w(buf, cursor, buflen, &(fh->client_ver));
-	read_packet_b(buf, cursor, buflen, &fh->file_key);
-	read_packet_dw(buf, cursor, buflen, &(fh->sender_uid));
-	read_packet_dw(buf, cursor, buflen, &(fh->receiver_uid));
+	gint bytes = 0;
+	bytes += qq_get16(&(fh->client_ver), buf + bytes);
+	bytes += qq_get8(&fh->file_key, buf + bytes);
+	bytes += qq_get32(&(fh->sender_uid), buf + bytes);
+	bytes += qq_get32(&(fh->receiver_uid), buf + bytes);
 
 	fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key));
 	fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key));
+	return bytes;
 }
 
 static const gchar *qq_get_file_cmd_desc(gint type)
@@ -190,7 +190,7 @@
 		fd = open(purple_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644);
 		info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0);
 	}
-		
+
 	if (info->buffer == NULL) {
 		return - 1;
 	}
@@ -258,8 +258,8 @@
 
 static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16 packet_type, guint32 to_uid)
 {
-	gint bytes;
-	guint8 *cursor, *buf;
+	guint8 *raw_data;
+	gint bytes = 0;
 	guint32 file_key;
 	qq_data *qd;
 	ft_info *info;
@@ -267,21 +267,19 @@
 	qd = (qq_data *) gc->proto_data;
 	g_return_val_if_fail(qd->session_key != NULL, -1);
 	info = (ft_info *) qd->xfer->data;
-	bytes = 0;
 
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
-	cursor = buf;
+	raw_data = g_newa(guint8, MAX_PACKET_SIZE);
 	file_key = _gen_file_key();
 
-	bytes += create_packet_b(buf, &cursor, packet_type);
-	bytes += create_packet_w(buf, &cursor, QQ_CLIENT);
-	bytes += create_packet_b(buf, &cursor, file_key & 0xff);
-	bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(qd->uid, file_key));
-	bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(to_uid, file_key));
-	bytes += create_packet_data(buf, &cursor, data, len);
+	bytes += qq_put8(raw_data + bytes, packet_type);
+	bytes += qq_put16(raw_data + bytes, QQ_CLIENT);
+	bytes += qq_put8(raw_data + bytes, file_key & 0xff);
+	bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(qd->uid, file_key));
+	bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(to_uid, file_key));
+	bytes += qq_putdata(raw_data + bytes, data, len);
 
 	if (bytes == len + 12) {
-		_qq_xfer_write(buf, bytes, qd->xfer);
+		_qq_xfer_write(raw_data, bytes, qd->xfer);
 	} else
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_file: want %d but got %d\n", len + 12, bytes);
 	return bytes;
@@ -292,57 +290,56 @@
 {
 	qq_data *qd;
 	gint bytes, bytes_expected, encrypted_len;
-	guint8 *raw_data, *cursor, *encrypted_data;
+	guint8 *raw_data, *encrypted_data;
 	time_t now;
 	ft_info *info;
-	
+
 	qd = (qq_data *) gc->proto_data;
 	info = (ft_info *) qd->xfer->data;
 
-	raw_data = g_new0 (guint8, 61);
-	cursor = raw_data;
-	
+	raw_data = g_newa (guint8, 61);
 	bytes = 0;
+
 	now = time(NULL);
 
-	bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16);
-	bytes += create_packet_w(raw_data, &cursor, packet_type);
+	bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
+	bytes += qq_put16(raw_data + bytes, packet_type);
 	switch (packet_type) {
 		case QQ_FILE_CMD_SENDER_SAY_HELLO:
 		case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
 		case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
 		case QQ_FILE_CMD_NOTIFY_IP_ACK:
 		case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
-			bytes += create_packet_w(raw_data, &cursor, info->send_seq);
+			bytes += qq_put16(raw_data + bytes, info->send_seq);
 			break;
 		default:
-			bytes += create_packet_w(raw_data, &cursor, ++qd->send_seq);
+			bytes += qq_put16(raw_data + bytes, ++qd->send_seq);
 	}
-	bytes += create_packet_dw(raw_data, &cursor, (guint32) now);
-	bytes += create_packet_b(raw_data, &cursor, 0x00);
-	bytes += create_packet_b(raw_data, &cursor, qd->my_icon);
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-	bytes += create_packet_w(raw_data, &cursor, 0x0000);
-	bytes += create_packet_b(raw_data, &cursor, 0x00);
+	bytes += qq_put32(raw_data + bytes, (guint32) now);
+	bytes += qq_put8(raw_data + bytes, 0x00);
+	bytes += qq_put8(raw_data + bytes, qd->my_icon);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put16(raw_data + bytes, 0x0000);
+	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 0x65: send a file, 0x6b: send a custom face */
-	bytes += create_packet_b(raw_data, &cursor, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */
+	bytes += qq_put8(raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */
 	switch (packet_type)
 	{
 		case QQ_FILE_CMD_SENDER_SAY_HELLO:
 		case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
 		case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
 		case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
-			bytes += create_packet_b(raw_data, &cursor, 0x00);
-			bytes += create_packet_b(raw_data, &cursor, hellobyte);
+			bytes += qq_put8(raw_data + bytes, 0x00);
+			bytes += qq_put8(raw_data + bytes, hellobyte);
 			bytes_expected = 48;
 			break;
 		case QQ_FILE_CMD_PING:
 		case QQ_FILE_CMD_PONG:
 		case QQ_FILE_CMD_NOTIFY_IP_ACK:
-			bytes += qq_fill_conn_info(raw_data, &cursor, info);
+			bytes += qq_fill_conn_info(raw_data, info);
 			bytes_expected = 61;
 			break;
 		default:
@@ -350,51 +347,53 @@
 					packet_type);
 			bytes_expected = 0;
 	}
-	
-	if (bytes == bytes_expected) {
-		gchar *hex_dump = hex_dump_to_str(raw_data, bytes);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending packet[%s]: \n%s", qq_get_file_cmd_desc(packet_type), hex_dump);
-		g_free(hex_dump);
-		encrypted_len = bytes + 16;
-		encrypted_data = g_newa(guint8, encrypted_len);
-		qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
-		/*debug: try to decrypt it */
-		/*
-		if (QQ_DEBUG) {
-			guint8 *buf;
-			int buflen;
-			hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump);
-			g_free(hex_dump);
-			buf = g_newa(guint8, MAX_PACKET_SIZE);
-			buflen = encrypted_len;
-			if (qq_decrypt(encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n");
-				if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
-					purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n");
-				hex_dump = hex_dump_to_str(buf, buflen);
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump);
-				g_free(hex_dump);
-			} else {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n");
-			}
-		}
-		*/
 
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
-		_qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
-	}
-	else
+	if (bytes != bytes_expected) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d",
 				bytes_expected, bytes);
+		return;
+	}
+
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+		raw_data, bytes,
+		"sending packet[%s]:", qq_get_file_cmd_desc(packet_type));
+
+	encrypted_len = bytes + 16;
+	encrypted_data = g_newa(guint8, encrypted_len);
+	qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
+	/*debug: try to decrypt it */
+	/*
+	   if (QQ_DEBUG) {
+	   guint8 *buf;
+	   int buflen;
+	   hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump);
+	   g_free(hex_dump);
+	   buf = g_newa(guint8, MAX_PACKET_SIZE);
+	   buflen = encrypted_len;
+	   if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n");
+	   if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n");
+	   hex_dump = hex_dump_to_str(buf, buflen);
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump);
+	   g_free(hex_dump);
+	   } else {
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n");
+	   }
+	   }
+	   */
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
+	_qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
 }
 
 /* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */
 static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, 
 		guint32 fragment_index, guint16 seq, guint8 *data, gint len)
 {
+	guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
 	gint bytes;
-	guint8 *raw_data, *cursor, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
 	guint32 fragment_size = 1000;
 	gchar *filename;
 	gint filename_len, filesize;
@@ -408,17 +407,16 @@
 	filesize = purple_xfer_get_size(qd->xfer);
 
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE);
-	cursor = raw_data;
 	bytes = 0;
 
-	bytes += create_packet_b(raw_data, &cursor, 0x00);
-	bytes += create_packet_w(raw_data, &cursor, packet_type);
+	bytes += qq_put8(raw_data + bytes, 0x00);
+	bytes += qq_put16(raw_data + bytes, packet_type);
 	switch (packet_type) {
 		case QQ_FILE_BASIC_INFO:
 		case QQ_FILE_DATA_INFO:
 		case QQ_FILE_EOF:
-			bytes += create_packet_w(raw_data, &cursor, 0x0000);
-			bytes += create_packet_b(raw_data, &cursor, 0x00);
+			bytes += qq_put16(raw_data + bytes, 0x0000);
+			bytes += qq_put8(raw_data + bytes, 0x00);
 			break;
 		case QQ_FILE_CMD_FILE_OP:
 			switch(sub_type)
@@ -437,44 +435,44 @@
 							"start transfering data, %d fragments with %d length each\n",
 							info->fragment_num, info->fragment_len);
 					/* Unknown */
-					bytes += create_packet_w(raw_data, &cursor, 0x0000);
+					bytes += qq_put16(raw_data  + bytes, 0x0000);
 					/* Sub-operation type */
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
+					bytes += qq_put8(raw_data + bytes, sub_type);
 					/* Length of file */
-					bytes += create_packet_dw(raw_data, &cursor, filesize);
+					bytes += qq_put32(raw_data + bytes, filesize);
 					/* Number of fragments */
-					bytes += create_packet_dw(raw_data, &cursor, info->fragment_num);
+					bytes += qq_put32(raw_data + bytes, info->fragment_num);
 					/* Length of a single fragment */
-					bytes += create_packet_dw(raw_data, &cursor, info->fragment_len);
-					bytes += create_packet_data(raw_data, &cursor, file_md5, 16);
-					bytes += create_packet_data(raw_data, &cursor, filename_md5, 16);
+					bytes += qq_put32(raw_data + bytes, info->fragment_len);
+					bytes += qq_putdata(raw_data + bytes, file_md5, 16);
+					bytes += qq_putdata(raw_data + bytes, filename_md5, 16);
 					/* Length of filename */
-					bytes += create_packet_w(raw_data, &cursor, filename_len);
+					bytes += qq_put16(raw_data + bytes, filename_len);
 					/* 8 unknown bytes */
-					bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-					bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+					bytes += qq_put32(raw_data + bytes, 0x00000000);
+					bytes += qq_put32(raw_data + bytes, 0x00000000);
 					/* filename */
-					bytes += create_packet_data(raw_data, &cursor, (guint8 *) filename,
+					bytes += qq_putdata(raw_data + bytes, (guint8 *) filename,
 							filename_len);
 					break;
 				case QQ_FILE_DATA_INFO:
 					purple_debug(PURPLE_DEBUG_INFO, "QQ", 
 							"sending %dth fragment with length %d, offset %d\n",
 							fragment_index, len, (fragment_index-1)*fragment_size);
-					/* bytes += create_packet_w(raw_data, &cursor, ++(qd->send_seq)); */
-					bytes += create_packet_w(raw_data, &cursor, info->send_seq);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
-					/* bytes += create_packet_dw(raw_data, &cursor, fragment_index); */
-					bytes += create_packet_dw(raw_data, &cursor, fragment_index - 1);
-					bytes += create_packet_dw(raw_data, &cursor, (fragment_index - 1) * fragment_size);
-					bytes += create_packet_w(raw_data, &cursor, len);
-					bytes += create_packet_data(raw_data, &cursor, data, len);
+					/* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */
+					bytes += qq_put16(raw_data + bytes, info->send_seq);
+					bytes += qq_put8(raw_data + bytes, sub_type);
+					/* bytes += qq_put32(raw_data + bytes, fragment_index); */
+					bytes += qq_put32(raw_data + bytes, fragment_index - 1);
+					bytes += qq_put32(raw_data + bytes, (fragment_index - 1) * fragment_size);
+					bytes += qq_put16(raw_data + bytes, len);
+					bytes += qq_putdata(raw_data + bytes, data, len);
 					break;
 				case QQ_FILE_EOF:
 					purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of sending data\n");
-					/* bytes += create_packet_w(raw_data, &cursor, info->fragment_num + 1); */
-					bytes += create_packet_w(raw_data, &cursor, info->fragment_num);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
+					/* bytes += qq_put16(raw_data + bytes, info->fragment_num + 1); */
+					bytes += qq_put16(raw_data + bytes, info->fragment_num);
+					bytes += qq_put8(raw_data + bytes, sub_type);
 					/* purple_xfer_set_completed(qd->xfer, TRUE); */
 			}
 			break;
@@ -482,18 +480,18 @@
 			switch (sub_type)
 			{
 				case QQ_FILE_BASIC_INFO:
-					bytes += create_packet_w(raw_data, &cursor, 0x0000);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
-					bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+					bytes += qq_put16(raw_data + bytes, 0x0000);
+					bytes += qq_put8(raw_data + bytes, sub_type);
+					bytes += qq_put32(raw_data + bytes, 0x00000000);
 					break;
 				case QQ_FILE_DATA_INFO:
-					bytes += create_packet_w(raw_data, &cursor, seq);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
-					bytes += create_packet_dw(raw_data, &cursor, fragment_index);
+					bytes += qq_put16(raw_data + bytes, seq);
+					bytes += qq_put8(raw_data + bytes, sub_type);
+					bytes += qq_put32(raw_data + bytes, fragment_index);
 					break;
 				case QQ_FILE_EOF:
-					bytes += create_packet_w(raw_data, &cursor, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
+					bytes += qq_put16(raw_data + bytes, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2);
+					bytes += qq_put8(raw_data + bytes, sub_type);
 					break;
 			}
 	}
@@ -520,9 +518,11 @@
  */
 
 
-static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, guint8 *cursor,
-		gint len, qq_file_header *fh)
+static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint len)
 {
+	gint bytes ;
+	gint decryped_bytes;
+	qq_file_header fh;
 	guint8 *decrypted_data;
 	gint decrypted_len;
 	qq_data *qd = (qq_data *) gc->proto_data;
@@ -531,60 +531,65 @@
 	guint8 hellobyte;
 	ft_info *info = (ft_info *) qd->xfer->data;
 
+	bytes = 0;
+	bytes += _qq_get_file_header(&fh, data + bytes);
+
 	decrypted_data = g_newa(guint8, len);
 	decrypted_len = len;
 
-	if (qq_decrypt(cursor, len - (cursor - data), qd->session_md5, decrypted_data, &decrypted_len)) {
-		gchar *hex_dump;
-		cursor = decrypted_data + 16;	/* skip md5 section */
-		read_packet_w(decrypted_data, &cursor, decrypted_len, &packet_type);
-		read_packet_w(decrypted_data, &cursor, decrypted_len, &seq);
-		cursor += 4+1+1+19+1;
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
-		hex_dump = hex_dump_to_str(decrypted_data, decrypted_len);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted control packet received: \n%s", hex_dump);
-		g_free(hex_dump);
-		switch (packet_type) {
-			case QQ_FILE_CMD_NOTIFY_IP_ACK:
-				cursor = decrypted_data;
-				qq_get_conn_info(decrypted_data, &cursor, decrypted_len, info);
-/*				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);	
-				break;
-			case QQ_FILE_CMD_SENDER_SAY_HELLO:
-				/* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
-				cursor += 47;
-				read_packet_b(decrypted_data, &cursor, 
-						decrypted_len, &hellobyte);
+	if ( !qq_decrypt(data, len, qd->session_md5, decrypted_data, &decrypted_len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rcv file ctrl packet\n");
+		return;
+	}
+
+	/* only for debug info */
+	decryped_bytes = 16;	/* skip md5 section */
+	decryped_bytes += qq_get16(&packet_type, decrypted_data + decryped_bytes);
+	decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes);
+	decryped_bytes += 4+1+1+19+1;	/* skip something */
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+		decrypted_data, decrypted_len,
+		"decrypted control packet received:");
 
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh->sender_uid, 0);
-				break;
-			case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
-				/* I'm sender, do nothing */
-				break;
-			case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
-				/* I'm sender, ack the hello packet and send the first data */
-				cursor += 47;
-				read_packet_b(decrypted_data, &cursor, 
-						decrypted_len, &hellobyte);
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
-				_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
-				break;
-			case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
-				/* I'm receiver, do nothing */
-				break;
-			case QQ_FILE_CMD_PING:
-				/* I'm receiver, ack the PING */
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh->sender_uid, 0);
-				break;
-			case QQ_FILE_CMD_PONG:
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);
-				break;
-			default:
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
-		}
-	} 
+	switch (packet_type) {
+		case QQ_FILE_CMD_NOTIFY_IP_ACK:
+			decryped_bytes = 0;
+			qq_get_conn_info(info, decrypted_data + decryped_bytes);
+			/* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);	
+			break;
+		case QQ_FILE_CMD_SENDER_SAY_HELLO:
+			/* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
+			decryped_bytes += 47;
+			decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes);
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh.sender_uid, hellobyte);
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh.sender_uid, 0);
+			break;
+		case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
+			/* I'm sender, do nothing */
+			break;
+		case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
+			/* I'm sender, ack the hello packet and send the first data */
+			decryped_bytes += 47;
+			decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes);
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh.sender_uid, hellobyte);
+			_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
+			break;
+		case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
+			/* I'm receiver, do nothing */
+			break;
+		case QQ_FILE_CMD_PING:
+			/* I'm receiver, ack the PING */
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh.sender_uid, 0);
+			break;
+		case QQ_FILE_CMD_PONG:
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);
+			break;
+		default:
+			purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
+	}
 }
 
 static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16 len, guint32 index, guint32 offset)
@@ -609,15 +614,15 @@
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", index+1);
 		return;
 	}
-		
+
 	info->window |= mask;
 
 	_qq_xfer_write_file(buffer, index, len, xfer);
-	
+
 	xfer->bytes_sent += len;
 	xfer->bytes_remaining -= len;
 	purple_xfer_update_progress(xfer);
-	
+
 	mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
 	while (info->window & mask)
 	{
@@ -639,7 +644,7 @@
 	guint8 *buffer;
 	guint i;
 	gint readbytes;
-	
+
 	if (purple_xfer_get_bytes_remaining(xfer) <= 0) return;
 	if (info->window == 0 && info->max_fragment_index == 0)
 	{
@@ -655,7 +660,7 @@
 			readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + i, info->fragment_len, xfer);
 			if (readbytes > 0)
 				_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
-					info->max_fragment_index + i + 1, 0, buffer, readbytes);
+						info->max_fragment_index + i + 1, 0, buffer, readbytes);
 		}
 		if (mask & 0x8000) mask = 0x0001;
 		else mask = mask << 1;
@@ -706,8 +711,8 @@
 					info->fragment_len, xfer);
 			if (readbytes > 0)
 				_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
-					info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes);
-			
+						info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes);
+
 			info->max_fragment_index ++;
 			if (mask & 0x8000) mask = 0x0001;
 			else mask = mask << 1;
@@ -718,9 +723,10 @@
 			fragment_index, info->window, info->max_fragment_index);
 }
 
-static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint8 *cursor,
-		gint len, guint32 to_uid)
+static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint len)
 {
+	gint bytes ;
+	qq_file_header fh;
 	guint16 packet_type;
 	guint16 packet_seq;
 	guint8 sub_type;
@@ -729,24 +735,27 @@
 	guint32 fragment_offset;
 	qq_data *qd = (qq_data *) gc->proto_data;
 	ft_info *info = (ft_info *) qd->xfer->data;
-	
-	cursor += 1; /* skip an unknown byte */
-	read_packet_w(data, &cursor, len, &packet_type);
+
+	bytes = 0;
+	bytes += _qq_get_file_header(&fh, data + bytes);
+
+	bytes += 1; /* skip an unknown byte */
+	bytes += qq_get16(&packet_type, data + bytes);
 	switch(packet_type)
 	{
 		case QQ_FILE_CMD_FILE_OP:
-			read_packet_w(data, &cursor, len, &packet_seq);
-			read_packet_b(data, &cursor, len, &sub_type);
+			bytes += qq_get16(&packet_seq, data + bytes);
+			bytes += qq_get8(&sub_type, data + bytes);
 			switch (sub_type)
 			{
 				case QQ_FILE_BASIC_INFO:
-					cursor += 4;	/* file length, we have already known it from xfer */
-					read_packet_dw(data, &cursor, len, &info->fragment_num);
-					read_packet_dw(data, &cursor, len, &info->fragment_len);
+					bytes += 4;	/* file length, we have already known it from xfer */
+					bytes += qq_get32(&info->fragment_num, data + bytes);
+					bytes += qq_get32(&info->fragment_len, data + bytes);
 
-					/* FIXME: We must check the md5 here, if md5 doesn't match
-					 * we will ignore the packet or send sth as error number
-					 */
+					/* FIXME: We must check the md5 here, 
+					 * if md5 doesn't match we will ignore 
+					 * the packet or send sth as error number */
 
 					info->max_fragment_index = 0;
 					info->window = 0;
@@ -757,27 +766,27 @@
 							0, 0, NULL, 0);
 					break;
 				case QQ_FILE_DATA_INFO:
-					read_packet_dw(data, &cursor, len, &fragment_index);
-					read_packet_dw(data, &cursor, len, &fragment_offset);
-					read_packet_w(data, &cursor, len, &fragment_len);
+					bytes += qq_get32(&fragment_index, data + bytes);
+					bytes += qq_get32(&fragment_offset, data + bytes);
+					bytes += qq_get16(&fragment_len, data + bytes);
 					purple_debug(PURPLE_DEBUG_INFO, "QQ", 
 							"received %dth fragment with length %d, offset %d\n",
 							fragment_index, fragment_len, fragment_offset);
-					
+
 					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
 							fragment_index, packet_seq, NULL, 0);
-					_qq_recv_file_progess(gc, cursor, fragment_len, fragment_index, fragment_offset);
+					_qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset);
 					break;
 				case QQ_FILE_EOF:
 					purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of receiving\n");
 					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
-						0, 0, NULL, 0);
+							0, 0, NULL, 0);
 					break;
 			}
 			break;
 		case QQ_FILE_CMD_FILE_OP_ACK:
-			read_packet_w(data, &cursor, len, &packet_seq);
-			read_packet_b(data, &cursor, len, &sub_type);
+			bytes += qq_get16(&packet_seq, data + bytes);
+			bytes += qq_get8(&sub_type, data + bytes);
 			switch (sub_type)
 			{
 				case QQ_FILE_BASIC_INFO:
@@ -787,16 +796,16 @@
 					_qq_send_file_progess(gc);
 					break;
 				case QQ_FILE_DATA_INFO:
-					read_packet_dw(data, &cursor, len, &fragment_index);
+					bytes += qq_get32(&fragment_index, data + bytes);
 					_qq_update_send_progess(gc, fragment_index);
 					if (purple_xfer_is_completed(qd->xfer))
 						_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0);
-				/*	else
+					/*	else
 						_qq_send_file_progess(gc); */
 					break;
 				case QQ_FILE_EOF:
 					/* FIXME: OK, we can end the connection successfully */
-					
+
 					_qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
 					purple_xfer_set_completed(qd->xfer, TRUE);
 					break;
@@ -820,21 +829,21 @@
 
 void qq_process_recv_file(PurpleConnection *gc, guint8 *data, gint len)
 {
-	guint8 *cursor;
-	qq_file_header fh;
+	gint bytes;
+	guint8 tag;
 	qq_data *qd;
 
 	qd = (qq_data *) gc->proto_data;
 
-	cursor = data;
-	_qq_get_file_header(data, &cursor, len, &fh);
+	bytes = 0;
+	bytes += qq_get8(&tag, data + bytes);
 
-	switch (fh.tag) {
+	switch (tag) {
 		case QQ_FILE_CONTROL_PACKET_TAG:
-			_qq_process_recv_file_ctl_packet(gc, data, cursor, len, &fh);
+			_qq_process_recv_file_ctl_packet(gc, data + bytes, len - bytes);
 			break;
 		case QQ_FILE_DATA_PACKET_TAG:
-			_qq_process_recv_file_data(gc, data, cursor, len, fh.sender_uid);
+			_qq_process_recv_file_data(gc, data + bytes, len - bytes);
 			break;
 		default:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ", "unknown packet tag");
--- a/libpurple/protocols/qq/group_im.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Thu Jun 26 20:25:38 2008 +0000
@@ -58,7 +58,7 @@
 void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg)
 {
 	gint data_len, bytes;
-	guint8 *raw_data, *cursor, *send_im_tail;
+	guint8 *raw_data, *send_im_tail;
 	guint16 msg_len;
 	gchar *msg_filtered;
 
@@ -67,19 +67,19 @@
 	msg_filtered = purple_markup_strip_html(msg);
 	purple_debug_info("QQ_MESG", "filterd qq qun mesg: %s\n", msg_filtered);
 	msg_len = strlen(msg_filtered);
+
 	data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN;
 	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 
 	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEND_MSG);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-	bytes += create_packet_w(raw_data, &cursor, msg_len + QQ_SEND_IM_AFTER_MSG_LEN);
-	bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len);
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEND_MSG);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
+	bytes += qq_put16(raw_data + bytes, msg_len + QQ_SEND_IM_AFTER_MSG_LEN);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
 	send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL,
-						   FALSE, FALSE, FALSE,
-						   QQ_SEND_IM_AFTER_MSG_LEN);
-	bytes += create_packet_data(raw_data, &cursor, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN);
+			FALSE, FALSE, FALSE,
+			QQ_SEND_IM_AFTER_MSG_LEN);
+	bytes += qq_putdata(raw_data + bytes, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN);
 	g_free(send_im_tail);
 	g_free(msg_filtered);
 
@@ -87,11 +87,11 @@
 		qq_send_group_cmd(gc, group, raw_data, data_len);
 	else
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
+				"Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
 }
 
 /* this is the ACK */
-void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) 
+void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc) 
 {
 	/* return should be the internal group id
 	 * but we have nothing to do with it */
@@ -99,29 +99,26 @@
 }
 
 /* receive an application to join the group */
-void qq_process_recv_group_im_apply_join
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, user_uid;
 	guint8 group_type;
 	gchar *reason_utf8, *msg, *reason;
 	group_member_opt *g;
 	gchar *nombre;
+	gint bytes = 0;
 
 	g_return_if_fail(internal_group_id > 0 && data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg apply_join is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &user_uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&user_uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && user_uid > 0);
 
-	convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
 	msg = g_strdup_printf(_("User %d requested to join group %d"), user_uid, external_group_id);
 	reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
@@ -134,17 +131,17 @@
 	nombre = uid_to_purple_name(user_uid);
 
 	purple_request_action(gc, _("QQ Qun Operation"),
-			    msg, reason,
-			    PURPLE_DEFAULT_ACTION_NONE,
-				purple_connection_get_account(gc), nombre, NULL,
-				g, 3,
-			    _("Approve"),
-			    G_CALLBACK
-			    (qq_group_approve_application_with_struct),
-			    _("Reject"),
-			    G_CALLBACK
-			    (qq_group_reject_application_with_struct),
-			    _("Search"), G_CALLBACK(qq_group_search_application_with_struct));
+			msg, reason,
+			PURPLE_DEFAULT_ACTION_NONE,
+			purple_connection_get_account(gc), nombre, NULL,
+			g, 3,
+			_("Approve"),
+			G_CALLBACK
+			(qq_group_approve_application_with_struct),
+			_("Reject"),
+			G_CALLBACK
+			(qq_group_reject_application_with_struct),
+			_("Search"), G_CALLBACK(qq_group_search_application_with_struct));
 
 	g_free(nombre);
 	g_free(reason);
@@ -153,31 +150,28 @@
 }
 
 /* the request to join a group is rejected */
-void qq_process_recv_group_im_been_rejected
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, admin_uid;
 	guint8 group_type;
 	gchar *reason_utf8, *msg, *reason;
 	qq_group *group;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_rejected is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &admin_uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&admin_uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && admin_uid > 0);
 
-	convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
 	msg = g_strdup_printf
-	    (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid);
+		(_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid);
 	reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
@@ -194,31 +188,28 @@
 }
 
 /* the request to join a group is approved */
-void qq_process_recv_group_im_been_approved
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, admin_uid;
 	guint8 group_type;
 	gchar *reason_utf8, *msg;
 	qq_group *group;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_approved is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &admin_uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&admin_uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && admin_uid > 0);
 	/* it is also a "无" here, so do not display */
-	convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
 	msg = g_strdup_printf
-	    (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid);
+		(_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL);
 
@@ -233,24 +224,21 @@
 }
 
 /* process the packet when removed from a group */
-void qq_process_recv_group_im_been_removed
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, uid;
 	guint8 group_type;
 	gchar *msg;
 	qq_group *group;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_removed is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && uid > 0);
 
@@ -267,24 +255,21 @@
 }
 
 /* process the packet when added to a group */
-void qq_process_recv_group_im_been_added
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, uid;
 	guint8 group_type;
 	qq_group *group;
 	gchar *msg;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_added is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && uid > 0);
 
@@ -307,10 +292,9 @@
 }
 
 /* recv an IM from a group chat */
-void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 internal_group_id, PurpleConnection *gc, guint16 im_type)
+void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type)
 {
-	gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name, *hex_dump;
+	gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name;
 	guint16 unknown;
 	guint32 unknown4;
 	PurpleConversation *conv;
@@ -319,32 +303,32 @@
 	qq_group *group;
 	qq_recv_group_im *im_group;
 	gint skip_len;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && data_len > 0);
+
+	/* FIXME: check length here */
+
 	qd = (qq_data *) gc->proto_data;
 
-	hex_dump = hex_dump_to_str(*cursor, data_len - (*cursor - data));
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group im hex dump\n%s\n", hex_dump);
-
-	if (*cursor >= (data + data_len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group im_group is empty\n");
-		return;
-	}
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+		data, data_len,
+		"group im hex dump");
 
 	im_group = g_newa(qq_recv_group_im, 1);
 
-	read_packet_dw(data, cursor, data_len, &(im_group->external_group_id));
-	read_packet_b(data, cursor, data_len, &(im_group->group_type));
+	bytes += qq_get32(&(im_group->external_group_id), data + bytes);
+	bytes += qq_get8(&(im_group->group_type), data + bytes);
 
 	if(QQ_RECV_IM_TEMP_QUN_IM == im_type) {
-		read_packet_dw(data, cursor, data_len, &(internal_group_id));
+		bytes += qq_get32(&(internal_group_id), data + bytes);
 	}
 
-	read_packet_dw(data, cursor, data_len, &(im_group->member_uid));
-	read_packet_w(data, cursor, data_len, &unknown);	/* 0x0001? */
-	read_packet_w(data, cursor, data_len, &(im_group->msg_seq));
-	read_packet_time(data, cursor, data_len, &im_group->send_time);
-	read_packet_dw(data, cursor, data_len, &unknown4);	/* versionID */
+	bytes += qq_get32(&(im_group->member_uid), bytes + data);
+	bytes += qq_get16(&unknown, data + bytes);	/* 0x0001? */
+	bytes += qq_get16(&(im_group->msg_seq), data + bytes);
+	bytes += qq_getime(&im_group->send_time, data + bytes);
+	bytes += qq_get32(&unknown4, data + bytes);	/* versionID */
 	/*
 	 * length includes font_attr
 	 * this msg_len includes msg and font_attr
@@ -355,7 +339,7 @@
 	 * 3. font_attr
 	 */
 
-	read_packet_w(data, cursor, data_len, &(im_group->msg_len));
+	bytes += qq_get16(&(im_group->msg_len), data + bytes);
 	g_return_if_fail(im_group->msg_len > 0);
 
 	/*
@@ -371,14 +355,14 @@
 		skip_len = 10;
 	else
 		skip_len = 0;
-	*cursor += skip_len;
+	bytes += skip_len;
 
-	im_group->msg = g_strdup((gchar *) *cursor);
-	*cursor += strlen(im_group->msg) + 1;
+	im_group->msg = g_strdup((gchar *) data + bytes);
+	bytes += strlen(im_group->msg) + 1;
 	/* there might not be any font_attr, check it */
 	im_group->font_attr_len = im_group->msg_len - strlen(im_group->msg) - 1 - skip_len;
 	if (im_group->font_attr_len > 0)
-		im_group->font_attr = g_memdup(*cursor, im_group->font_attr_len);
+		im_group->font_attr = g_memdup(data + bytes, im_group->font_attr_len);
 	else
 		im_group->font_attr = NULL;
 
@@ -386,7 +370,7 @@
 	msg_with_purple_smiley = qq_smiley_to_purple(im_group->msg);
 	if (im_group->font_attr_len > 0)
 		msg_utf8_encoded = qq_encode_to_purple(im_group->font_attr,
-						     im_group->font_attr_len, msg_with_purple_smiley);
+				im_group->font_attr_len, msg_with_purple_smiley);
 	else
 		msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
 
@@ -406,11 +390,10 @@
 		else
 			im_src_name = g_strdup(member->nickname);
 		serv_got_chat_in(gc,
-				 purple_conv_chat_get_id(PURPLE_CONV_CHAT
-						       (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time);
+				purple_conv_chat_get_id(PURPLE_CONV_CHAT
+					(conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time);
 		g_free(im_src_name);
 	}
-	g_free(hex_dump);
 	g_free(msg_with_purple_smiley);
 	g_free(msg_utf8_encoded);
 	g_free(im_group->msg);
--- a/libpurple/protocols/qq/group_im.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_im.h	Thu Jun 26 20:25:38 2008 +0000
@@ -30,17 +30,31 @@
 #include "group.h"
 
 void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg);
-void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_recv_group_im(guint8 *data, 
-		guint8 **cursor, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type);
-void qq_process_recv_group_im_apply_join(guint8 *data,
-				    guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_rejected(guint8 *data,
-				       guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_approved(guint8 *data,
-				       guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_removed(guint8 *data,
-				      guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_added(guint8 *data,
-				    guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); */
+void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im(guint8 *data, guint8 **cursor, 
+ * gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); */
+void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type);
+
+/* void qq_process_recv_group_im_apply_join(guint8 *data, guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_rejected(guint8 *data, guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_approved(guint8 *data, guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_removed(guint8 *data, guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_added(guint8 *data,  guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
 #endif
--- a/libpurple/protocols/qq/group_info.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Thu Jun 26 20:25:38 2008 +0000
@@ -43,7 +43,7 @@
 {
 	g_return_val_if_fail(member != NULL, FALSE);
 	return (member->nickname == NULL) ||
-	    (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
+		(time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
 }
 
 /* this is done when we receive the reply to get_online_members sub_cmd
@@ -65,100 +65,83 @@
 /* send packet to get detailed information of one group */
 void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(group != NULL);
 
-	data_len = 5;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_GROUP_INFO);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
-	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_GROUP_INFO);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_GROUP_INFO));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
 /* send packet to get online group member, called by keep_alive */
 void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(group != NULL);
 
 	/* only get online members when conversation window is on */
 	if (NULL == purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,group->group_name_utf8, purple_connection_get_account(gc))) {
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8);
+				"Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8);
 		return;
 	}
 
-	data_len = 5;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_ONLINE_MEMBER);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
-	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_ONLINE_MEMBER);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_ONLINE_MEMBER));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
 /* send packet to get info for each group member */
 void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len, i;
+	guint8 *raw_data;
+	gint bytes, num, data_len;
 	GList *list;
 	qq_buddy *member;
 
 	g_return_if_fail(group != NULL);
-	for (i = 0, list = group->members; list != NULL; list = list->next) {
+	for (num = 0, list = group->members; list != NULL; list = list->next) {
 		member = (qq_buddy *) list->data;
 		if (_is_group_member_need_update_info(member))
-			i++;
+			num++;
 	}
 
-	if (i <= 0) {
+	if (num <= 0) {
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member needs to to update info now.\n");
 		return;
 	}
 
-	data_len = 5 + 4 * i;
+	data_len = 5 + 4 * num;
 	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 
 	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_MEMBER_INFO);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_MEMBER_INFO);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
 	list = group->members;
 	while (list != NULL) {
 		member = (qq_buddy *) list->data;
 		if (_is_group_member_need_update_info(member))
-			bytes += create_packet_dw(raw_data, &cursor, member->uid);
+			bytes += qq_put32(raw_data + bytes, member->uid);
 		list = list->next;
 	}
 
-	if (bytes != data_len)
+	if (bytes != data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+				"Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO));
+		return;
+	}
+
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
-void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc)
 {
 	qq_group *group;
 	qq_buddy *member;
@@ -168,16 +151,18 @@
 	guint16 unknown, max_members;
 	guint32 member_uid, internal_group_id, external_group_id;
 	GSList *pending_id;
-	gint pascal_len, i;
 	guint32 unknown4;
 	guint8 unknown1;
+	gint bytes, num;
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
 
-	read_packet_dw(data, cursor, len, &(internal_group_id));
+	bytes = 0;
+	bytes += qq_get32(&(internal_group_id), data + bytes);
 	g_return_if_fail(internal_group_id > 0);
-	read_packet_dw(data, cursor, len, &(external_group_id));
+
+	bytes += qq_get32(&(external_group_id), data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	pending_id = qq_get_pending_id(qd->adding_groups_from_server, internal_group_id);
@@ -189,32 +174,30 @@
 	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
 	g_return_if_fail(group != NULL);
 
-	read_packet_b(data, cursor, len, &(group->group_type));
-	read_packet_dw(data, cursor, len, &unknown4);	/* unknown 4 bytes */
-	read_packet_dw(data, cursor, len, &(group->creator_uid));
-	read_packet_b(data, cursor, len, &(group->auth_type));
-	read_packet_dw(data, cursor, len, &unknown4);	/* oldCategory */
-	read_packet_w(data, cursor, len, &unknown);	
-	read_packet_dw(data, cursor, len, &(group->group_category));
-	read_packet_w(data, cursor, len, &max_members);
-	read_packet_b(data, cursor, len, &unknown1);
-	read_packet_dw(data, cursor, len, &(unknown4));	/* versionID */
+	bytes += qq_get8(&(group->group_type), data + bytes);
+	bytes += qq_get32(&unknown4, data + bytes);	/* unknown 4 bytes */
+	bytes += qq_get32(&(group->creator_uid), data + bytes);
+	bytes += qq_get8(&(group->auth_type), data + bytes);
+	bytes += qq_get32(&unknown4, data + bytes);	/* oldCategory */
+	bytes += qq_get16(&unknown, data + bytes);	
+	bytes += qq_get32(&(group->group_category), data + bytes);
+	bytes += qq_get16(&max_members, data + bytes);
+	bytes += qq_get8(&unknown1, data + bytes);
+	bytes += qq_get32(&(unknown4), data + bytes);	/* versionID */
 
-	pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
-	*cursor += pascal_len;
-	read_packet_w(data, cursor, len, &(unknown));	/* 0x0000 */
-	pascal_len = convert_as_pascal_string(*cursor, &(group->notice_utf8), QQ_CHARSET_DEFAULT);
-	*cursor += pascal_len;
-	pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
-	*cursor += pascal_len;
+	/* strlen + <str content> */
+	bytes += convert_as_pascal_string(data + bytes, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
+	bytes += qq_get16(&unknown, data + bytes);	/* 0x0000 */
+	bytes += convert_as_pascal_string(data + bytes, &(group->notice_utf8), QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
 
-	i = 0;
+	num = 0;
 	/* now comes the member list separated by 0x00 */
-	while (*cursor < data + len) {
-		read_packet_dw(data, cursor, len, &member_uid);
-		i++;
-		read_packet_b(data, cursor, len, &organization);
-		read_packet_b(data, cursor, len, &role);
+	while (bytes < len) {
+		bytes += qq_get32(&member_uid, data + bytes);
+		num++;
+		bytes += qq_get8(&organization, data + bytes);
+		bytes += qq_get8(&role, data + bytes);
 
 		if(organization != 0 || role != 0) {
 			purple_debug(PURPLE_DEBUG_INFO, "QQ", "group member %d: organization=%d, role=%d\n", member_uid, organization, role);
@@ -223,11 +206,11 @@
 		if (member != NULL)
 			member->role = role;
 	}
-        if(*cursor > (data + len)) {
-                         purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
-        }
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
+	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, i);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, num);
 
 	if (group->creator_uid == qd->uid)
 		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN;
@@ -237,33 +220,32 @@
 	purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, 
 			group->group_name_utf8, purple_connection_get_account(gc));
 	if(NULL == purple_conv) {
-                purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-                           "Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8);
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+				"Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8);
 	}
 	else {
 		purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8);
 	}
 }
 
-void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc)
 {
 	guint32 internal_group_id, member_uid;
 	guint8 unknown;
-	gint bytes, i;
+	gint bytes, num;
 	qq_group *group;
 	qq_buddy *member;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (data + len - *cursor < 4) {
+	if (len <= 3) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Invalid group online member reply, discard it!\n");
 		return;
 	}
 
 	bytes = 0;
-	i = 0;
-	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
-	bytes += read_packet_b(data, cursor, len, &unknown);	/* 0x3c ?? */
+	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get8(&unknown, data + bytes);	/* 0x3c ?? */
 	g_return_if_fail(internal_group_id > 0);
 
 	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
@@ -275,61 +257,63 @@
 
 	/* set all offline first, then update those online */
 	_qq_group_set_members_all_offline(group);
-	while (*cursor < data + len) {
-		bytes += read_packet_dw(data, cursor, len, &member_uid);
-		i++;
+	num = 0;
+	while (bytes < len) {
+		bytes += qq_get32(&member_uid, data + bytes);
+		num++;
 		member = qq_group_find_or_add_member(gc, group, member_uid);
 		if (member != NULL)
 			member->status = QQ_BUDDY_ONLINE_NORMAL;
 	}
-        if(*cursor > (data + len)) {
-                         purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					 "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
-        }
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
+	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, i);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, num);
 }
 
 /* process the reply to get_members_info packet */
-void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
+	gint num;
 	guint32 internal_group_id, member_uid;
 	guint16 unknown;
-	gint pascal_len, i;
 	qq_group *group;
 	qq_buddy *member;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
 	g_return_if_fail(group != NULL);
 
-	i = 0;
+	num = 0;
 	/* now starts the member info, as get buddy list reply */
-	while (*cursor < data + len) {
-		read_packet_dw(data, cursor, len, &member_uid);
+	while (bytes < len) {
+		bytes += qq_get32(&member_uid, data + bytes);
 		g_return_if_fail(member_uid > 0);
 		member = qq_group_find_member_by_uid(group, member_uid);
 		g_return_if_fail(member != NULL);
 
-		i++;
-		read_packet_w(data, cursor, len, &(member->face));
-		read_packet_b(data, cursor, len, &(member->age));
-		read_packet_b(data, cursor, len, &(member->gender));
-		pascal_len = convert_as_pascal_string(*cursor, &(member->nickname), QQ_CHARSET_DEFAULT);
-		*cursor += pascal_len;
-		read_packet_w(data, cursor, len, &unknown);
-		read_packet_b(data, cursor, len, &(member->flag1));
-		read_packet_b(data, cursor, len, &(member->comm_flag));
+		num++;
+		bytes += qq_get16(&(member->face), data + bytes);
+		bytes += qq_get8(&(member->age), data + bytes);
+		bytes += qq_get8(&(member->gender), data + bytes);
+		bytes += convert_as_pascal_string(data + bytes, &(member->nickname), QQ_CHARSET_DEFAULT);
+		bytes += qq_get16(&unknown, data + bytes);
+		bytes += qq_get8(&(member->flag1), data + bytes);
+		bytes += qq_get8(&(member->comm_flag), data + bytes);
 
 		member->last_refresh = time(NULL);
 	}
-        if(*cursor > (data + len)) {
-                         purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					 "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
-        }
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, i);
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
+	}
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, num);
 }
--- a/libpurple/protocols/qq/group_info.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_info.h	Thu Jun 26 20:25:38 2008 +0000
@@ -32,8 +32,8 @@
 void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group);
 void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group);
 void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group);
-void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/group_join.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Thu Jun 26 20:25:38 2008 +0000
@@ -64,8 +64,8 @@
 /* send packet to join a group without auth */
 void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(group != NULL);
 
@@ -86,19 +86,11 @@
 		break;
 	}
 
-	data_len = 5;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
-	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
 static void _qq_group_join_auth_with_gc_and_id(gc_and_uid *g, const gchar *reason_utf8)
@@ -145,7 +137,7 @@
 
 void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8)
 {
-	guint8 *raw_data, *cursor;
+	guint8 *raw_data;
 	gchar *reason_qq;
 	gint bytes, data_len;
 
@@ -164,50 +156,42 @@
 
 	data_len = 10 + strlen(reason_qq) + 1;
 	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 
 	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP_AUTH);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-	bytes += create_packet_b(raw_data, &cursor, opt);
-	bytes += create_packet_dw(raw_data, &cursor, uid);
-	bytes += create_packet_b(raw_data, &cursor, strlen(reason_qq));
-	bytes += create_packet_data(raw_data, &cursor, (guint8 *) reason_qq, strlen(reason_qq));
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP_AUTH);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
+	bytes += qq_put8(raw_data + bytes, opt);
+	bytes += qq_put32(raw_data + bytes, uid);
+	bytes += qq_put8(raw_data + bytes, strlen(reason_qq));
+	bytes += qq_putdata(raw_data + bytes, (guint8 *) reason_qq, strlen(reason_qq));
 
-	if (bytes != data_len)
+	if (bytes != data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP_AUTH));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+		return;
+	}
+
+	qq_send_group_cmd(gc, group, raw_data, data_len);
 }
 
 /* send a packet to exit a group */
 void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(group != NULL);
 
-	data_len = 5;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_EXIT_GROUP);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
-	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_EXIT_GROUP);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_EXIT_GROUP));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
 /* If comes here, cmd is OK already */
-void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gint bytes, expected_bytes;
+	gint bytes;
 	guint32 internal_group_id;
 	PurpleChat *chat;
 	qq_group *group;
@@ -216,96 +200,94 @@
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
 
-	bytes = 0;
-	expected_bytes = 4;
-	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
+	if (len < 4) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+			   "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len);
+		return;
+	}
 
-	if (bytes == expected_bytes) {
-		group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
-		if (group != NULL) {
-			chat =
-			    purple_blist_find_chat
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
+
+	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	if (group != NULL) {
+		chat = purple_blist_find_chat
 			    (purple_connection_get_account(gc), g_strdup_printf("%d", group->external_group_id));
-			if (chat != NULL)
-				purple_blist_remove_chat(chat);
-			qq_group_delete_internal_record(qd, internal_group_id);
-		}
-		purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid exit group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
+		if (chat != NULL)
+			purple_blist_remove_chat(chat);
+		qq_group_delete_internal_record(qd, internal_group_id);
 	}
+	purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL);
 }
 
 /* Process the reply to group_auth subcmd */
-void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gint bytes, expected_bytes;
+	gint bytes;
 	guint32 internal_group_id;
 	qq_data *qd;
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
 
+	if (len < 4) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+			   "Invalid join group reply, expect %d bytes, read %d bytes\n", 4, len);
+		return;
+	}
 	bytes = 0;
-	expected_bytes = 4;
-	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
-	if (bytes == expected_bytes)
-		purple_notify_info
-		    (gc, _("QQ Group Auth"),
+	purple_notify_info(gc, _("QQ Group Auth"),
 		     _("Your authorization request has been accepted by the QQ server"), NULL);
-	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
 }
 
 /* process group cmd reply "join group" */
-void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gint bytes, expected_bytes;
+	gint bytes;
 	guint32 internal_group_id;
 	guint8 reply;
 	qq_group *group;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	bytes = 0;
-	expected_bytes = 5;
-	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
-	bytes += read_packet_b(data, cursor, len, &reply);
-
-	if (bytes != expected_bytes) {
+	if (len < 5) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
+			   "Invalid join group reply, expect %d bytes, read %d bytes\n", 5, len);
 		return;
-	} else {		/* join group OK */
-		group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
-		/* need to check if group is NULL or not. */
-		g_return_if_fail(group != NULL);
-		switch (reply) {
-		case QQ_GROUP_JOIN_OK:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8);
-			group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
-			qq_group_refresh(gc, group);
-			/* this must be shown before getting online members */
-			qq_group_conv_show_window(gc, group);
-			qq_send_cmd_group_get_group_info(gc, group);
-			break;
-		case QQ_GROUP_JOIN_NEED_AUTH:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "Fail joining group [%d] %s, needs authentication\n",
-				   group->external_group_id, group->group_name_utf8);
-			group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
-			qq_group_refresh(gc, group);
-			_qq_group_join_auth(gc, group);
-			break;
-		default:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "Error joining group [%d] %s, unknown reply: 0x%02x\n",
-				   group->external_group_id, group->group_name_utf8, reply);
-		}
+	}
+	
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get8(&reply, data + bytes);
+
+	/* join group OK */
+	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	/* need to check if group is NULL or not. */
+	g_return_if_fail(group != NULL);
+	switch (reply) {
+	case QQ_GROUP_JOIN_OK:
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8);
+		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+		qq_group_refresh(gc, group);
+		/* this must be shown before getting online members */
+		qq_group_conv_show_window(gc, group);
+		qq_send_cmd_group_get_group_info(gc, group);
+		break;
+	case QQ_GROUP_JOIN_NEED_AUTH:
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+			   "Fail joining group [%d] %s, needs authentication\n",
+			   group->external_group_id, group->group_name_utf8);
+		group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+		qq_group_refresh(gc, group);
+		_qq_group_join_auth(gc, group);
+		break;
+	default:
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+			   "Error joining group [%d] %s, unknown reply: 0x%02x\n",
+			   group->external_group_id, group->group_name_utf8, reply);
 	}
 }
 
--- a/libpurple/protocols/qq/group_join.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_join.h	Thu Jun 26 20:25:38 2008 +0000
@@ -46,8 +46,8 @@
 void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group);
 void qq_group_exit(PurpleConnection *gc, GHashTable *data);
 void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group);
-void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/group_network.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_network.c	Thu Jun 26 20:25:38 2008 +0000
@@ -39,7 +39,7 @@
 #include "group_opt.h"
 #include "group_search.h"
 #include "header_info.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 enum {
@@ -81,12 +81,12 @@
 }
 
 /* default process of reply error */
-static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *cursor, gint len, PurpleConnection *gc)
+static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *data, gint len, PurpleConnection *gc)
 {
 	gchar *msg, *msg_utf8;
-	g_return_if_fail(cursor != NULL && len > 0);
+	g_return_if_fail(data != NULL && len > 0);
 
-	msg = g_strndup((gchar *) cursor, len);	/* it will append 0x00 */
+	msg = g_strndup((gchar *) data, len);	/* it will append 0x00 */
 	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
 	g_free(msg);
 	msg = g_strdup_printf(_("Code [0x%02X]: %s"), reply, msg_utf8);
@@ -96,14 +96,13 @@
 }
 
 /* default process, dump only */
-static void _qq_process_group_cmd_reply_default(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+static void _qq_process_group_cmd_reply_default(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gchar *hex_dump;
 	g_return_if_fail(data != NULL && len > 0);
 
-	hex_dump = hex_dump_to_str(data, len);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Dump unprocessed group cmd reply:\n%s", hex_dump);
-	g_free(hex_dump);
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+		data, len, 
+		"Dump unprocessed group cmd reply:");
 }
 
 /* The lower layer command of send group cmd */
@@ -116,7 +115,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	qq_send_cmd(gc, QQ_CMD_GROUP_CMD, TRUE, 0, TRUE, raw_data, data_len);
+	qq_send_cmd(qd, QQ_CMD_GROUP_CMD, raw_data, data_len);
 
 	p = g_new0(group_packet, 1);
 
@@ -136,7 +135,7 @@
 	qq_data *qd;
 	gint len, bytes;
 	guint32 internal_group_id;
-	guint8 *data, *cursor, sub_cmd, reply;
+	guint8 *data, sub_cmd, reply;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -149,102 +148,101 @@
 		return;
 	}
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		if (len <= 2) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len);
-			return;
-		}
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n");
+		return;
+	}
 
-		bytes = 0;
-		cursor = data;
-		bytes += read_packet_b(data, &cursor, len, &sub_cmd);
-		bytes += read_packet_b(data, &cursor, len, &reply);
-
-		group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	if (len <= 2) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len);
+		return;
+	}
 
-		if (reply != QQ_GROUP_CMD_REPLY_OK) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				   "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd));
+	bytes = 0;
+	bytes += qq_get8(&sub_cmd, data + bytes);
+	bytes += qq_get8(&reply, data + bytes);
 
-			if (group != NULL)
-				qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
+	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
 
-			switch (reply) {	/* this should be all errors */
-			case QQ_GROUP_CMD_REPLY_NOT_MEMBER:
-				if (group != NULL) {
-					purple_debug(PURPLE_DEBUG_WARNING,
-						   "QQ",
-						   "You are not a member of group \"%s\"\n", group->group_name_utf8);
-					group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
-					qq_group_refresh(gc, group);
-				}
-				break;
-			case QQ_GROUP_CMD_REPLY_SEARCH_ERROR:
-				if (qd->roomlist != NULL) {
-					if (purple_roomlist_get_in_progress(qd->roomlist))
-						purple_roomlist_set_in_progress(qd->roomlist, FALSE);
-				}
-				_qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc);
-				break;
-			default:
-				_qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc);
-			}
-			return;
-		}
+	if (reply != QQ_GROUP_CMD_REPLY_OK) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+			   "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd));
+
+		if (group != NULL)
+			qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
 
-		/* seems ok so far, so we process the reply according to sub_cmd */
-		switch (sub_cmd) {
-		case QQ_GROUP_CMD_GET_GROUP_INFO:
-			qq_process_group_cmd_get_group_info(data, &cursor, len, gc);
+		switch (reply) {	/* this should be all errors */
+		case QQ_GROUP_CMD_REPLY_NOT_MEMBER:
 			if (group != NULL) {
-				qq_send_cmd_group_get_members_info(gc, group);
-				qq_send_cmd_group_get_online_members(gc, group);
+				purple_debug(PURPLE_DEBUG_WARNING,
+					   "QQ",
+					   "You are not a member of group \"%s\"\n", group->group_name_utf8);
+				group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+				qq_group_refresh(gc, group);
 			}
 			break;
-		case QQ_GROUP_CMD_CREATE_GROUP:
-			qq_group_process_create_group_reply(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_MODIFY_GROUP_INFO:
-			qq_group_process_modify_info_reply(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_MEMBER_OPT:
-			qq_group_process_modify_members_reply(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_ACTIVATE_GROUP:
-			qq_group_process_activate_group_reply(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_SEARCH_GROUP:
-			qq_process_group_cmd_search_group(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_JOIN_GROUP:
-			qq_process_group_cmd_join_group(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_JOIN_GROUP_AUTH:
-			qq_process_group_cmd_join_group_auth(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_EXIT_GROUP:
-			qq_process_group_cmd_exit_group(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_SEND_MSG:
-			qq_process_group_cmd_im(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_GET_ONLINE_MEMBER:
-			qq_process_group_cmd_get_online_members(data, &cursor, len, gc);
-			if (group != NULL)
-				qq_group_conv_refresh_online_member(gc, group);
-			break;
-		case QQ_GROUP_CMD_GET_MEMBER_INFO:
-			qq_process_group_cmd_get_members_info(data, &cursor, len, gc);
-			if (group != NULL)
-				qq_group_conv_refresh_online_member(gc, group);
+		case QQ_GROUP_CMD_REPLY_SEARCH_ERROR:
+			if (qd->roomlist != NULL) {
+				if (purple_roomlist_get_in_progress(qd->roomlist))
+					purple_roomlist_set_in_progress(qd->roomlist, FALSE);
+			}
+			_qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc);
 			break;
 		default:
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				   "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd));
-			_qq_process_group_cmd_reply_default(data, &cursor, len, gc);
+			_qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc);
+		}
+		return;
+	}
+
+	/* seems ok so far, so we process the reply according to sub_cmd */
+	switch (sub_cmd) {
+	case QQ_GROUP_CMD_GET_GROUP_INFO:
+		qq_process_group_cmd_get_group_info(data + bytes, len - bytes, gc);
+		if (group != NULL) {
+			qq_send_cmd_group_get_members_info(gc, group);
+			qq_send_cmd_group_get_online_members(gc, group);
 		}
-
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n");
+		break;
+	case QQ_GROUP_CMD_CREATE_GROUP:
+		qq_group_process_create_group_reply(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_MODIFY_GROUP_INFO:
+		qq_group_process_modify_info_reply(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_MEMBER_OPT:
+		qq_group_process_modify_members_reply(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_ACTIVATE_GROUP:
+		qq_group_process_activate_group_reply(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_SEARCH_GROUP:
+		qq_process_group_cmd_search_group(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_JOIN_GROUP:
+		qq_process_group_cmd_join_group(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_JOIN_GROUP_AUTH:
+		qq_process_group_cmd_join_group_auth(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_EXIT_GROUP:
+		qq_process_group_cmd_exit_group(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_SEND_MSG:
+		qq_process_group_cmd_im(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_GET_ONLINE_MEMBER:
+		qq_process_group_cmd_get_online_members(data + bytes, len - bytes, gc);
+		if (group != NULL)
+			qq_group_conv_refresh_online_member(gc, group);
+		break;
+	case QQ_GROUP_CMD_GET_MEMBER_INFO:
+		qq_process_group_cmd_get_members_info(data + bytes, len - bytes, gc);
+		if (group != NULL)
+			qq_group_conv_refresh_online_member(gc, group);
+		break;
+	default:
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+			   "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd));
+		_qq_process_group_cmd_reply_default(data + bytes, len, gc);
 	}
 }
--- a/libpurple/protocols/qq/group_opt.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Thu Jun 26 20:25:38 2008 +0000
@@ -57,22 +57,24 @@
 
 static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members)
 {
-	guint8 *data, *cursor;
+	guint8 *data;
 	gint i, count, data_len;
+	gint bytes;
 	g_return_if_fail(members != NULL);
 
-	for (i = 0; members[i] != 0xffffffff; i++) {;
+	for (count = 0; members[count] != 0xffffffff; count++) {;
 	}
-	count = i;
 	data_len = 6 + count * 4;
 	data = g_newa(guint8, data_len);
-	cursor = data;
-	create_packet_b(data, &cursor, QQ_GROUP_CMD_MEMBER_OPT);
-	create_packet_dw(data, &cursor, group->internal_group_id);
-	create_packet_b(data, &cursor, operation);
+	
+	bytes = 0;
+	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MEMBER_OPT);
+	bytes += qq_put32(data + bytes, group->internal_group_id);
+	bytes += qq_put8(data + bytes, operation);
 	for (i = 0; i < count; i++)
-		create_packet_dw(data, &cursor, members[i]);
-	qq_send_group_cmd(gc, group, data, data_len);
+		bytes += qq_put32(data + bytes, members[i]);
+
+	qq_send_group_cmd(gc, group, data, bytes);
 }
 
 static void _qq_group_do_nothing_with_struct(group_member_opt *g)
@@ -97,11 +99,11 @@
 
 	qq_send_packet_get_info(g->gc, g->member, TRUE);	/* we want to see window */
 	purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "",
-					PURPLE_DEFAULT_ACTION_NONE,
-					purple_connection_get_account(g->gc), NULL, NULL,
-					g, 2,
-					_("Reject"), G_CALLBACK(qq_group_reject_application_with_struct),
-					_("Approve"), G_CALLBACK(qq_group_approve_application_with_struct));
+				PURPLE_DEFAULT_ACTION_NONE,
+				purple_connection_get_account(g->gc), NULL, NULL,
+				g, 2,
+				_("Reject"), G_CALLBACK(qq_group_reject_application_with_struct),
+				_("Approve"), G_CALLBACK(qq_group_approve_application_with_struct));
 }
 
 void qq_group_reject_application_with_struct(group_member_opt *g)
@@ -193,13 +195,15 @@
 		_qq_group_member_opt(gc, group, QQ_GROUP_MEMBER_ADD, add_members);
 }
 
-void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint32 internal_group_id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	/* we should have its info locally */
@@ -213,8 +217,9 @@
 
 void qq_group_modify_info(PurpleConnection *gc, qq_group *group)
 {
-	gint data_len, data_written;
-	guint8 *data, *cursor;
+	guint8 *data;
+	gint data_len;
+	gint bytes;
 	gchar *group_name, *group_desc, *notice;
 
 	g_return_if_fail(group != NULL);
@@ -228,47 +233,50 @@
 	    + 1 + strlen(notice);
 
 	data = g_newa(guint8, data_len);
-	cursor = data;
-	data_written = 0;
+	bytes = 0;
 	/* 000-000 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_MODIFY_GROUP_INFO);
+	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MODIFY_GROUP_INFO);
 	/* 001-004 */
-	data_written += create_packet_dw(data, &cursor, group->internal_group_id);
+	bytes += qq_put32(data + bytes, group->internal_group_id);
 	/* 005-005 */
-	data_written += create_packet_b(data, &cursor, 0x01);
+	bytes += qq_put8(data + bytes, 0x01);
 	/* 006-006 */
-	data_written += create_packet_b(data, &cursor, group->auth_type);
+	bytes += qq_put8(data + bytes, group->auth_type);
 	/* 007-008 */
-	data_written += create_packet_w(data, &cursor, 0x0000);
+	bytes += qq_put16(data + bytes, 0x0000);
 	/* 009-010 */
-	data_written += create_packet_w(data, &cursor, group->group_category);
+	bytes += qq_put16(data + bytes, group->group_category);
 
-	data_written += create_packet_b(data, &cursor, strlen(group_name));
-	data_written += create_packet_data(data, &cursor, (guint8 *) group_name, strlen(group_name));
+	bytes += qq_put8(data + bytes, strlen(group_name));
+	bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name));
 
-	data_written += create_packet_w(data, &cursor, 0x0000);
+	bytes += qq_put16(data + bytes, 0x0000);
 
-	data_written += create_packet_b(data, &cursor, strlen(notice));
-	data_written += create_packet_data(data, &cursor, (guint8 *) notice, strlen(notice));
+	bytes += qq_put8(data + bytes, strlen(notice));
+	bytes += qq_putdata(data+ bytes, (guint8 *) notice, strlen(notice));
 
-	data_written += create_packet_b(data, &cursor, strlen(group_desc));
-	data_written += create_packet_data(data, &cursor, (guint8 *) group_desc, strlen(group_desc));
+	bytes += qq_put8(data + bytes, strlen(group_desc));
+	bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc));
 
-	if (data_written != data_len)
+	if (bytes != data_len)	{
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail to create group_modify_info packet, expect %d bytes, wrote %d bytes\n",
-			   data_len, data_written);
-	else
-		qq_send_group_cmd(gc, group, data, data_len);
+			   data_len, bytes);
+		return;
+	}
+
+	qq_send_group_cmd(gc, group, data, bytes);
 }
 
-void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint32 internal_group_id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	/* we should have its info locally */
@@ -284,42 +292,44 @@
 /* we create a very simple group first, and then let the user to modify */
 void qq_group_create_with_name(PurpleConnection *gc, const gchar *name)
 {
-	gint data_len, data_written;
-	guint8 *data, *cursor;
+	gint data_len;
+	guint8 *data;
+	gint bytes;
 	qq_data *qd;
 	g_return_if_fail(name != NULL);
 
 	qd = (qq_data *) gc->proto_data;
 	data_len = 7 + 1 + strlen(name) + 2 + 1 + 1 + 4;
 	data = g_newa(guint8, data_len);
-	cursor = data;
 
-	data_written = 0;
+	bytes = 0;
 	/* we create the simpleset group, only group name is given */
 	/* 000 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_CREATE_GROUP);
+	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_CREATE_GROUP);
 	/* 001 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_TYPE_PERMANENT);
+	bytes += qq_put8(data + bytes, QQ_GROUP_TYPE_PERMANENT);
 	/* 002 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_AUTH_TYPE_NEED_AUTH);
+	bytes += qq_put8(data + bytes, QQ_GROUP_AUTH_TYPE_NEED_AUTH);
 	/* 003-004 */
-	data_written += create_packet_w(data, &cursor, 0x0000);
+	bytes += qq_put16(data + bytes, 0x0000);
 	/* 005-006 */
-	data_written += create_packet_w(data, &cursor, 0x0003);
+	bytes += qq_put16(data + bytes, 0x0003);
 	/* 007 */
-	data_written += create_packet_b(data, &cursor, strlen(name));
-	data_written += create_packet_data(data, &cursor, (guint8 *) name, strlen(name));
-	data_written += create_packet_w(data, &cursor, 0x0000);
-	data_written += create_packet_b(data, &cursor, 0x00);	/* no group notice */
-	data_written += create_packet_b(data, &cursor, 0x00);	/* no group desc */
-	data_written += create_packet_dw(data, &cursor, qd->uid);	/* I am member of coz */
+	bytes += qq_put8(data + bytes, strlen(name));
+	bytes += qq_putdata(data + bytes, (guint8 *) name, strlen(name));
+	bytes += qq_put16(data + bytes, 0x0000);
+	bytes += qq_put8(data + bytes, 0x00);	/* no group notice */
+	bytes += qq_put8(data + bytes, 0x00);	/* no group desc */
+	bytes += qq_put32(data + bytes, qd->uid);	/* I am member of coz */
 
-	if (data_written != data_len)
+	if (bytes != data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail create create_group packet, expect %d bytes, written %d bytes\n",
-			   data_len, data_written);
-	else
-		qq_send_group_cmd(gc, NULL, data, data_len);
+			   data_len, bytes);
+		return;
+	}
+
+	qq_send_group_cmd(gc, NULL, data, bytes);
 }
 
 static void qq_group_setup_with_gc_and_uid(gc_and_uid *g)
@@ -335,8 +345,9 @@
 	g_free(g);
 }
 
-void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint32 internal_group_id, external_group_id;
 	qq_group *group;
 	gc_and_uid *g;
@@ -346,8 +357,9 @@
 	g_return_if_fail(gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
-	read_packet_dw(data, cursor, len, &external_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get32(&external_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0 && external_group_id);
 
 	group = qq_group_create_internal_record(gc, internal_group_id, external_group_id, NULL);
@@ -378,36 +390,29 @@
 /* we have to activate group after creation, otherwise the group can not be searched */
 void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id)
 {
-	gint data_len, data_written;
-	guint8 *data, *cursor;
+	guint8 data[16] = {0};
+	gint bytes = 0;
 	g_return_if_fail(internal_group_id > 0);
 
-	data_len = 5;
-	data = g_newa(guint8, data_len);
-	cursor = data;
-
-	data_written = 0;
+	bytes = 0;
 	/* we create the simplest group, only group name is given */
 	/* 000 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_ACTIVATE_GROUP);
+	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_ACTIVATE_GROUP);
 	/* 001-005 */
-	data_written += create_packet_dw(data, &cursor, internal_group_id);
+	bytes += qq_put32(data + bytes, internal_group_id);
 
-	if (data_written != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create activate_group packet, expect %d bytes, written %d bytes\n",
-			   data_len, data_written);
-	else
-		qq_send_group_cmd(gc, NULL, data, data_len);
+	qq_send_group_cmd(gc, NULL, data, bytes);
 }
 
-void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint32 internal_group_id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	/* we should have its info locally */
--- a/libpurple/protocols/qq/group_opt.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.h	Thu Jun 26 20:25:38 2008 +0000
@@ -54,12 +54,12 @@
 void qq_group_reject_application_with_struct(group_member_opt *g);
 void qq_group_search_application_with_struct(group_member_opt *g);
 
-void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc);
+void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc);
 void qq_group_manage_group(PurpleConnection *gc, GHashTable *data);
 void qq_group_create_with_name(PurpleConnection *gc, const gchar *name);
 void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id);
-void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc);
+void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/group_search.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_search.c	Thu Jun 26 20:25:38 2008 +0000
@@ -43,24 +43,18 @@
 /* send packet to search for qq_group */
 void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id)
 {
-	guint8 *raw_data, *cursor, type;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
+	guint8 type;
 
-	data_len = 6;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 	type = (external_group_id == 0x00000000) ? QQ_GROUP_SEARCH_TYPE_DEMO : QQ_GROUP_SEARCH_TYPE_BY_ID;
 
 	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEARCH_GROUP);
-	bytes += create_packet_b(raw_data, &cursor, type);
-	bytes += create_packet_dw(raw_data, &cursor, external_group_id);
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEARCH_GROUP);
+	bytes += qq_put8(raw_data + bytes, type);
+	bytes += qq_put32(raw_data + bytes, external_group_id);
 
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_SEARCH_GROUP));
-	else
-		qq_send_group_cmd(gc, NULL, raw_data, data_len);
+	qq_send_group_cmd(gc, NULL, raw_data, bytes);
 }
 
 static void _qq_setup_roomlist(qq_data *qd, qq_group *group)
@@ -89,55 +83,50 @@
 }
 
 /* process group cmd reply "search group" */
-void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint8 search_type;
 	guint16 unknown;
-	gint bytes, pascal_len;
+	qq_group group;
 	qq_data *qd;
-	qq_group *group;
 	GSList *pending_id;
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
 
-	read_packet_b(data, cursor, len, &search_type);
-	group = g_newa(qq_group, 1);
+	bytes = 0;
+	bytes += qq_get8(&search_type, data + bytes);
 
 	/* now it starts with group_info_entry */
-	bytes = 0;
-	bytes += read_packet_dw(data, cursor, len, &(group->internal_group_id));
-	bytes += read_packet_dw(data, cursor, len, &(group->external_group_id));
-	bytes += read_packet_b(data, cursor, len, &(group->group_type));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_dw(data, cursor, len, &(group->creator_uid));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_dw(data, cursor, len, &(group->group_category));
-	pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
-	bytes += pascal_len;
-	*cursor += pascal_len;
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_b(data, cursor, len, &(group->auth_type));
-	pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
-	bytes += pascal_len;
-	*cursor += pascal_len;
+	bytes += qq_get32(&(group.internal_group_id), data + bytes);
+	bytes += qq_get32(&(group.external_group_id), data + bytes);
+	bytes += qq_get8(&(group.group_type), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get32(&(group.creator_uid), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get32(&(group.group_category), data + bytes);
+	bytes += convert_as_pascal_string(data + bytes, &(group.group_name_utf8), QQ_CHARSET_DEFAULT);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get8(&(group.auth_type), data + bytes);
+	bytes += convert_as_pascal_string(data + bytes, &(group.group_desc_utf8), QQ_CHARSET_DEFAULT);
 	/* end of one qq_group */
-        if(*cursor != (data + len)) {
-                         purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					 "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
-        }
+	if(bytes != len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+			"group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
+	}
 
-	pending_id = qq_get_pending_id(qd->joining_groups, group->external_group_id);
+	pending_id = qq_get_pending_id(qd->joining_groups, group.external_group_id);
 	if (pending_id != NULL) {
-		qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
-		if (qq_group_find_by_id(gc, group->internal_group_id, QQ_INTERNAL_ID) == NULL)
+		qq_set_pending_id(&qd->joining_groups, group.external_group_id, FALSE);
+		if (qq_group_find_by_id(gc, group.internal_group_id, QQ_INTERNAL_ID) == NULL)
 			qq_group_create_internal_record(gc, 
-					group->internal_group_id, group->external_group_id, group->group_name_utf8);
-		qq_send_cmd_group_join_group(gc, group);
+					group.internal_group_id, group.external_group_id, group.group_name_utf8);
+		qq_send_cmd_group_join_group(gc, &group);
 	} else {
-		_qq_setup_roomlist(qd, group);
+		_qq_setup_roomlist(qd, &group);
 	}
 }
--- a/libpurple/protocols/qq/group_search.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/group_search.h	Thu Jun 26 20:25:38 2008 +0000
@@ -29,6 +29,6 @@
 #include "connection.h"
 
 void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id);
-void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/im.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Thu Jun 26 20:25:38 2008 +0000
@@ -40,19 +40,16 @@
 #include "header_info.h"
 #include "im.h"
 #include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "send_file.h"
 #include "utils.h"
 
 #define QQ_SEND_IM_REPLY_OK       0x00
 #define DEFAULT_FONT_NAME_LEN 	  4
 
-/* a debug function */
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
-
 enum
 {
-        QQ_NORMAL_IM_TEXT = 0x000b,
+	QQ_NORMAL_IM_TEXT = 0x000b,
 	QQ_NORMAL_IM_FILE_REQUEST_TCP = 0x0001,
 	QQ_NORMAL_IM_FILE_APPROVE_TCP = 0x0003,
 	QQ_NORMAL_IM_FILE_REJECT_TCP = 0x0005,
@@ -121,9 +118,9 @@
 #define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5"
 
 guint8 *qq_get_send_im_tail(const gchar *font_color,
-			    const gchar *font_size,
-			    const gchar *font_name,
-			    gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len)
+		const gchar *font_size,
+		const gchar *font_name,
+		gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len)
 {
 	gchar *s1;
 	unsigned char *rgb;
@@ -141,7 +138,7 @@
 	send_im_tail = g_new0(guint8, tail_len);
 
 	g_strlcpy((gchar *) (send_im_tail + QQ_SEND_IM_AFTER_MSG_HEADER_LEN),
-		  font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN);
+			font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN);
 	send_im_tail[tail_len - 1] = (guint8) tail_len;
 
 	send_im_tail[0] = 0x00;
@@ -182,39 +179,39 @@
 	send_im_tail[5] = 0x00;
 	send_im_tail[6] = 0x86;
 	send_im_tail[7] = 0x22;	/* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */
-	_qq_show_packet("QQ_MESG", send_im_tail, tail_len);
+	qq_show_packet("QQ_MESG", send_im_tail, tail_len);
 	return (guint8 *) send_im_tail;
 }
 
 static const gchar *qq_get_recv_im_type_str(gint type)
 {
 	switch (type) {
-	case QQ_RECV_IM_TO_BUDDY:
-		return "QQ_RECV_IM_TO_BUDDY";
-	case QQ_RECV_IM_TO_UNKNOWN:
-		return "QQ_RECV_IM_TO_UNKNOWN";
-	case QQ_RECV_IM_UNKNOWN_QUN_IM:
-		return "QQ_RECV_IM_UNKNOWN_QUN_IM";
-	case QQ_RECV_IM_ADD_TO_QUN:
-		return "QQ_RECV_IM_ADD_TO_QUN";
-	case QQ_RECV_IM_DEL_FROM_QUN:
-		return "QQ_RECV_IM_DEL_FROM_QUN";
-	case QQ_RECV_IM_APPLY_ADD_TO_QUN:
-		return "QQ_RECV_IM_APPLY_ADD_TO_QUN";
-	case QQ_RECV_IM_CREATE_QUN:
-		return "QQ_RECV_IM_CREATE_QUN";
-	case QQ_RECV_IM_SYS_NOTIFICATION:
-		return "QQ_RECV_IM_SYS_NOTIFICATION";
-	case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
-		return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN";
-	case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
-		return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN";
-	case QQ_RECV_IM_TEMP_QUN_IM:
-		return "QQ_RECV_IM_TEMP_QUN_IM";
-	case QQ_RECV_IM_QUN_IM:
-		return "QQ_RECV_IM_QUN_IM";
-	default:
-		return "QQ_RECV_IM_UNKNOWN";
+		case QQ_RECV_IM_TO_BUDDY:
+			return "QQ_RECV_IM_TO_BUDDY";
+		case QQ_RECV_IM_TO_UNKNOWN:
+			return "QQ_RECV_IM_TO_UNKNOWN";
+		case QQ_RECV_IM_UNKNOWN_QUN_IM:
+			return "QQ_RECV_IM_UNKNOWN_QUN_IM";
+		case QQ_RECV_IM_ADD_TO_QUN:
+			return "QQ_RECV_IM_ADD_TO_QUN";
+		case QQ_RECV_IM_DEL_FROM_QUN:
+			return "QQ_RECV_IM_DEL_FROM_QUN";
+		case QQ_RECV_IM_APPLY_ADD_TO_QUN:
+			return "QQ_RECV_IM_APPLY_ADD_TO_QUN";
+		case QQ_RECV_IM_CREATE_QUN:
+			return "QQ_RECV_IM_CREATE_QUN";
+		case QQ_RECV_IM_SYS_NOTIFICATION:
+			return "QQ_RECV_IM_SYS_NOTIFICATION";
+		case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
+			return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN";
+		case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
+			return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN";
+		case QQ_RECV_IM_TEMP_QUN_IM:
+			return "QQ_RECV_IM_TEMP_QUN_IM";
+		case QQ_RECV_IM_QUN_IM:
+			return "QQ_RECV_IM_QUN_IM";
+		default:
+			return "QQ_RECV_IM_UNKNOWN";
 	}
 }
 
@@ -222,27 +219,29 @@
  * we send an ACK which is the first 16 bytes of incoming packet */
 static void _qq_send_packet_recv_im_ack(PurpleConnection *gc, guint16 seq, guint8 *data)
 {
-	qq_send_cmd(gc, QQ_CMD_RECV_IM, FALSE, seq, FALSE, data, 16);
+	qq_data *qd;
+
+	qd = (qq_data *) gc->proto_data;
+	qq_send_cmd_detail(qd, QQ_CMD_RECV_IM, seq, FALSE, data, 16);
 }
 
 /* read the common parts of the normal_im,
  * returns the bytes read if succeed, or -1 if there is any error */
-static gint _qq_normal_im_common_read(guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common)
+static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common)
 {
 	gint bytes;
 	g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1);
 
 	bytes = 0;
 	/* now push data into common header */
-	bytes += read_packet_w(data, cursor, len, &(common->sender_ver));
-	bytes += read_packet_dw(data, cursor, len, &(common->sender_uid));
-	bytes += read_packet_dw(data, cursor, len, &(common->receiver_uid));
+	bytes += qq_get16(&(common->sender_ver), data + bytes);
+	bytes += qq_get32(&(common->sender_uid), data + bytes);
+	bytes += qq_get32(&(common->receiver_uid), data + bytes);
 
-	common->session_md5 = g_memdup(*cursor, QQ_KEY_LENGTH);
+	common->session_md5 = g_memdup(data + bytes, QQ_KEY_LENGTH);
 	bytes += QQ_KEY_LENGTH;
-	*cursor += QQ_KEY_LENGTH;
 
-	bytes += read_packet_w(data, cursor, len, &(common->normal_im_type));
+	bytes += qq_get16(&(common->normal_im_type), data + bytes);
 
 	if (bytes != 28) {	/* read common place fail */
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Expect 28 bytes, read %d bytes\n", bytes);
@@ -253,8 +252,7 @@
 }
 
 /* process received normal text IM */
-static void _qq_process_recv_normal_im_text
-    (guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
+static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
 {
 	guint16 purple_msg_type;
 	gchar *name;
@@ -262,50 +260,52 @@
 	gchar *msg_utf8_encoded;
 	qq_data *qd;
 	qq_recv_normal_im_text *im_text;
+	gint bytes = 0;
 
 	g_return_if_fail(common != NULL);
 	qd = (qq_data *) gc->proto_data;
 
 	/* now it is QQ_NORMAL_IM_TEXT */
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
-		return;
-	} else
-		im_text = g_newa(qq_recv_normal_im_text, 1);
+	/*
+	   if (*cursor >= (data + len - 1)) {
+	   purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
+	   return;
+	   } else
+	   */
+	im_text = g_newa(qq_recv_normal_im_text, 1);
 
 	im_text->common = common;
 
 	/* push data into im_text */
-	read_packet_w(data, cursor, len, &(im_text->msg_seq));
-	read_packet_dw(data, cursor, len, &(im_text->send_time));
-	read_packet_w(data, cursor, len, &(im_text->sender_icon));
-	read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown2), 3);
-	read_packet_b(data, cursor, len, &(im_text->is_there_font_attr));
+	bytes += qq_get16(&(im_text->msg_seq), data + bytes);
+	bytes += qq_get32(&(im_text->send_time), data + bytes);
+	bytes += qq_get16(&(im_text->sender_icon), data + bytes);
+	bytes += qq_getdata((guint8 *) & (im_text->unknown2), 3, data + bytes);
+	bytes += qq_get8(&(im_text->is_there_font_attr), data + bytes);
 	/**
 	 * from lumaqq	for unknown3
 	 *	totalFragments = buf.get() & 255;
-         *	fragmentSequence = buf.get() & 255;
-         *	messageId = buf.getChar();
+	 *	fragmentSequence = buf.get() & 255;
+	 *	messageId = buf.getChar();
 	 */
-	read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown3), 4);
-	read_packet_b(data, cursor, len, &(im_text->msg_type));
+	bytes += qq_getdata((guint8 *) & (im_text->unknown3), 4, data + bytes);
+	bytes += qq_get8(&(im_text->msg_type), data + bytes);
 
 	/* we need to check if this is auto-reply
 	 * QQ2003iii build 0304, returns the msg without font_attr
 	 * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */
 	if (im_text->msg_type == QQ_IM_AUTO_REPLY) {
 		im_text->is_there_font_attr = 0x00;	/* indeed there is no this flag */
-		im_text->msg = g_strndup(*(gchar **) cursor, data + len - *cursor);
+		im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes);
 	} else {		/* it is normal mesasge */
 		if (im_text->is_there_font_attr) {
-			im_text->msg = g_strdup(*(gchar **) cursor);
-			*cursor += strlen(im_text->msg) + 1;
-			im_text->font_attr_len = data + len - *cursor;
-			im_text->font_attr = g_memdup(*cursor, im_text->font_attr_len);
+			im_text->msg = g_strdup((gchar *)(data + bytes));
+			bytes += strlen(im_text->msg) + 1; /* length decided by strlen! will it cause a crash? */
+			im_text->font_attr_len = len - bytes;
+			im_text->font_attr = g_memdup(data + bytes, im_text->font_attr_len);
 		} else		/* not im_text->is_there_font_attr */
-			im_text->msg = g_strndup(*(gchar **) cursor, data + len - *cursor);
+			im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes);
 	}			/* if im_text->msg_type */
-	_qq_show_packet("QQ_MESG recv", data, *cursor - data);
 
 	name = uid_to_purple_name(common->sender_uid);
 	if (purple_find_buddy(gc->account, name) == NULL)
@@ -315,9 +315,9 @@
 
 	msg_with_purple_smiley = qq_smiley_to_purple(im_text->msg);
 	msg_utf8_encoded = im_text->is_there_font_attr ?
-	    qq_encode_to_purple(im_text->font_attr,
-			      im_text->font_attr_len,
-			      msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
+		qq_encode_to_purple(im_text->font_attr,
+				im_text->font_attr_len,
+				msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
 
 	/* send encoded to purple, note that we use im_text->send_time,
 	 * not the time we receive the message
@@ -333,81 +333,68 @@
 }
 
 /* it is a normal IM, maybe text or video request */
-static void _qq_process_recv_normal_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gint bytes;
+	gint bytes = 0;
 	qq_recv_normal_im_common *common;
 	qq_recv_normal_im_unprocessed *im_unprocessed;
-	gchar *hex_dump;
 
 	g_return_if_fail (data != NULL && len != 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Received normal IM is empty\n");
-		return;
-	}
-	else
-		common = g_newa (qq_recv_normal_im_common, 1);
+	common = g_newa (qq_recv_normal_im_common, 1);
 
-	bytes = _qq_normal_im_common_read (data, cursor, len, common);
+	bytes = _qq_normal_im_common_read(data, len, common);
 	if (bytes < 0) {
 		purple_debug (PURPLE_DEBUG_ERROR, "QQ",
-			    "Fail read the common part of normal IM\n");
+				"Fail read the common part of normal IM\n");
 		return;
 	}
 
 	switch (common->normal_im_type) {
-	case QQ_NORMAL_IM_TEXT:
-		purple_debug (PURPLE_DEBUG_INFO,
-			    "QQ",
-			    "Normal IM, text type:\n [%d] => [%d], src: %s\n",
-			    common->sender_uid, common->receiver_uid,
-			    qq_get_source_str (common->sender_ver));
-		_qq_process_recv_normal_im_text (data, cursor, len, common,
-						 gc);
-		break;
-	case QQ_NORMAL_IM_FILE_REJECT_UDP:
-		qq_process_recv_file_reject (data, cursor, len,
-					     common->sender_uid, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_APPROVE_UDP:
-		qq_process_recv_file_accept (data, cursor, len,
-					     common->sender_uid, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_REQUEST_UDP:
-		qq_process_recv_file_request (data, cursor, len,
-					      common->sender_uid, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_CANCEL:
-		qq_process_recv_file_cancel (data, cursor, len,
-					     common->sender_uid, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_NOTIFY:
-		qq_process_recv_file_notify (data, cursor, len,
-				common->sender_uid, gc);
-		break;
-	default:
-		im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1);
-		im_unprocessed->common = common;
-		im_unprocessed->unknown = *cursor;
-		im_unprocessed->length = data + len - *cursor;
-		/* a simple process here, maybe more later */
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Normal IM, unprocessed type [0x%04x]\n",
-			    common->normal_im_type);
-	       	hex_dump = hex_dump_to_str(im_unprocessed->unknown, im_unprocessed->length);
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Dump unknown part.\n%s", hex_dump);
-		g_free(hex_dump);
-		g_free (common->session_md5);
-		return;
+		case QQ_NORMAL_IM_TEXT:
+			purple_debug (PURPLE_DEBUG_INFO, "QQ",
+					"Normal IM, text type:\n [%d] => [%d], src: %s\n",
+					common->sender_uid, common->receiver_uid,
+					qq_get_source_str (common->sender_ver));
+			if (bytes >= len - 1) {
+				purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
+				return;
+			}
+			_qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_REJECT_UDP:
+			qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_APPROVE_UDP:
+			qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_REQUEST_UDP:
+			qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_CANCEL:
+			qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_NOTIFY:
+			qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		default:
+			im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1);
+			im_unprocessed->common = common;
+			im_unprocessed->unknown = data + bytes;
+			im_unprocessed->length = len - bytes;
+			/* a simple process here, maybe more later */
+			purple_debug (PURPLE_DEBUG_WARNING, "QQ",
+					"Normal IM, unprocessed type [0x%04x], unknown [0x%02x], len %d\n",
+					common->normal_im_type, im_unprocessed->unknown, im_unprocessed->length);
+			g_free (common->session_md5);
+			return;
 	}
 
 	g_free (common->session_md5);
 }
 
 /* process im from system administrator */
-static void _qq_process_recv_sys_im(guint8 *data, guint8 **cursor, gint data_len, PurpleConnection *gc)
+static void _qq_process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	gint len;
 	guint8 reply;
@@ -415,14 +402,9 @@
 
 	g_return_if_fail(data != NULL && data_len != 0);
 
-	if (*cursor >= (data + data_len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received sys IM is empty\n");
-		return;
-	}
+	len = data_len;
 
-	len = data + data_len - *cursor;
-
-	if (NULL == (segments = split_data(*cursor, len, "\x2f", 2)))
+	if (NULL == (segments = split_data(data, len, "\x2f", 2)))
 		return;
 
 	reply = strtol(segments[0], NULL, 10);
@@ -436,7 +418,7 @@
 void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data, *send_im_tail;
+	guint8 *raw_data, *send_im_tail;
 	guint16 client_tag, normal_im_type;
 	gint msg_len, raw_len, font_name_len, tail_len, bytes;
 	time_t now;
@@ -500,52 +482,51 @@
 
 	raw_len = QQ_SEND_IM_BEFORE_MSG_LEN + msg_len + tail_len;
 	raw_data = g_newa(guint8, raw_len);
-	cursor = raw_data;
 	bytes = 0;
 
 	/* 000-003: receiver uid */
-	bytes += create_packet_dw(raw_data, &cursor, qd->uid);
+	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 004-007: sender uid */
-	bytes += create_packet_dw(raw_data, &cursor, to_uid);
+	bytes += qq_put32(raw_data + bytes, to_uid);
 	/* 008-009: sender client version */
-	bytes += create_packet_w(raw_data, &cursor, client_tag);
+	bytes += qq_put16(raw_data + bytes, client_tag);
 	/* 010-013: receiver uid */
-	bytes += create_packet_dw(raw_data, &cursor, qd->uid);
+	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
-	bytes += create_packet_dw(raw_data, &cursor, to_uid);
+	bytes += qq_put32(raw_data + bytes, to_uid);
 	/* 018-033: md5 of (uid+session_key) */
-	bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16);
+	bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
 	/* 034-035: message type */
-	bytes += create_packet_w(raw_data, &cursor, normal_im_type);
+	bytes += qq_put16(raw_data + bytes, normal_im_type);
 	/* 036-037: sequence number */
-	bytes += create_packet_w(raw_data, &cursor, qd->send_seq);
+	bytes += qq_put16(raw_data + bytes, qd->send_seq);
 	/* 038-041: send time */
-	bytes += create_packet_dw(raw_data, &cursor, (guint32) now);
+	bytes += qq_put32(raw_data + bytes, (guint32) now);
 	/* 042-043: sender icon */
-	bytes += create_packet_w(raw_data, &cursor, qd->my_icon);
+	bytes += qq_put16(raw_data + bytes, qd->my_icon);
 	/* 044-046: always 0x00 */
-	bytes += create_packet_w(raw_data, &cursor, 0x0000);
-	bytes += create_packet_b(raw_data, &cursor, 0x00);
+	bytes += qq_put16(raw_data + bytes, 0x0000);
+	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 047-047: we use font attr */
-	bytes += create_packet_b(raw_data, &cursor, 0x01);
+	bytes += qq_put8(raw_data + bytes, 0x01);
 	/* 048-051: always 0x00 */
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
 	/* 052-052: text message type (normal/auto-reply) */
-	bytes += create_packet_b(raw_data, &cursor, type);
+	bytes += qq_put8(raw_data + bytes, type);
 	/* 053-   : msg ends with 0x00 */
-	bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
 	send_im_tail = qq_get_send_im_tail(font_color, font_size, font_name, is_bold,
-						   is_italic, is_underline, tail_len);
-	_qq_show_packet("QQ_MESG debug", send_im_tail, tail_len);
-	bytes += create_packet_data(raw_data, &cursor, send_im_tail, tail_len);
+			is_italic, is_underline, tail_len);
+	qq_show_packet("QQ_send_im_tail debug", send_im_tail, tail_len);
+	bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len);
 
-	_qq_show_packet("QQ_MESG raw", raw_data, cursor - raw_data);
+	qq_show_packet("QQ_raw_data debug", raw_data, bytes);
 
 	if (bytes == raw_len)	/* create packet OK */
-		qq_send_cmd(gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+		qq_send_cmd(qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes);
+				"Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes);
 
 	if (font_color)
 		g_free(font_color);
@@ -560,7 +541,8 @@
 {
 	qq_data *qd;
 	gint len;
-	guint8 *data, *cursor, reply;
+	guint8 *data, reply;
+	gint bytes = 0;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -569,8 +551,7 @@
 	data = g_newa(guint8, len);
 
 	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		cursor = data;
-		read_packet_b(data, &cursor, len, &reply);
+		bytes += qq_get8(&reply, data + bytes);
 		if (reply != QQ_SEND_IM_REPLY_OK) {
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n");
 			purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
@@ -588,7 +569,7 @@
 {
 	qq_data *qd;
 	gint len, bytes;
-	guint8 *data, *cursor;
+	guint8 *data;
 	qq_recv_im_header *im_header;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
@@ -597,98 +578,107 @@
 	len = buf_len;
 	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		if (len < 16) {	/* we need to ack with the first 16 bytes */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n");
-			return;
-		} else
-			_qq_send_packet_recv_im_ack(gc, seq, data);
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n");
+	}
+
+	if (len < 16) {	/* we need to ack with the first 16 bytes */
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n");
+		return;
+	} else {
+		_qq_send_packet_recv_im_ack(gc, seq, data);
+	}
+
+	/* check len first */
+	if (len < 20) {	/* length of im_header */
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+				"Fail read recv IM header, len should longer than 20 bytes, read %d bytes\n", len);
+		return;
+	}
 
-		cursor = data;
-		bytes = 0;
-		im_header = g_newa(qq_recv_im_header, 1);
-		bytes += read_packet_dw(data, &cursor, len, &(im_header->sender_uid));
-		bytes += read_packet_dw(data, &cursor, len, &(im_header->receiver_uid));
-		bytes += read_packet_dw(data, &cursor, len, &(im_header->server_im_seq));
-		/* if the message is delivered via server, it is server IP/port */
-		bytes += read_packet_data(data, &cursor, len, (guint8 *) & (im_header->sender_ip), 4);
-		bytes += read_packet_w(data, &cursor, len, &(im_header->sender_port));
-		bytes += read_packet_w(data, &cursor, len, &(im_header->im_type));
+	bytes = 0;
+	im_header = g_newa(qq_recv_im_header, 1);
+	bytes += qq_get32(&(im_header->sender_uid), data + bytes);
+	bytes += qq_get32(&(im_header->receiver_uid), data + bytes);
+	bytes += qq_get32(&(im_header->server_im_seq), data + bytes);
+	/* if the message is delivered via server, it is server IP/port */
+	bytes += qq_getdata((guint8 *) & (im_header->sender_ip), 4, data + bytes);
+	bytes += qq_get16(&(im_header->sender_port), data + bytes);
+	bytes += qq_get16(&(im_header->im_type), data + bytes);
+	/* im_header prepared */
 
-		if (bytes != 20) {	/* length of im_header */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				   "Fail read recv IM header, expect 20 bytes, read %d bytes\n", bytes);
-			return;
-		}
+	if (im_header->receiver_uid != qd->uid) {	/* should not happen */
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid);
+		return;
+	}
 
-		if (im_header->receiver_uid != qd->uid) {	/* should not happen */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid);
-			return;
-		}
+	/* check bytes */
+	if (bytes >= len - 1) {
+		purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received IM is empty\n");
+		return;
+	}
 
-		switch (im_header->im_type) {
+	switch (im_header->im_type) {
 		case QQ_RECV_IM_TO_BUDDY:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid);
-			_qq_process_recv_normal_im(data, &cursor, len, gc);
+					"IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid);
+			_qq_process_recv_normal_im(data + bytes, len - bytes, gc); /* position and rest length */
 			break;
 		case QQ_RECV_IM_TO_UNKNOWN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid);
-			_qq_process_recv_normal_im(data, &cursor, len, gc);
+					"IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid);
+			_qq_process_recv_normal_im(data + bytes, len - bytes, gc);
 			break;
 		case QQ_RECV_IM_UNKNOWN_QUN_IM:
 		case QQ_RECV_IM_TEMP_QUN_IM:
 		case QQ_RECV_IM_QUN_IM:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM from group, internal_id [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im(data, &cursor, len, im_header->sender_uid, gc, im_header->im_type);
+			qq_process_recv_group_im(data + bytes, len - bytes, im_header->sender_uid, gc, im_header->im_type);
 			break;
 		case QQ_RECV_IM_ADD_TO_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from group, added by group internal_id [%d]\n", im_header->sender_uid);
+					"IM from group, added by group internal_id [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id
 			 * we need this to create a dummy group and add to blist */
-			qq_process_recv_group_im_been_added(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_been_added(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_DEL_FROM_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid);
+					"IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_removed(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_been_removed(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_APPLY_ADD_TO_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid);
+					"IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_apply_join(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_apply_join(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM for group system info, approved by group internal_id [%d]\n",
-				   im_header->sender_uid);
+					"IM for group system info, approved by group internal_id [%d]\n",
+					im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_approved(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_been_approved(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM for group system info, rejected by group internal_id [%d]\n",
-				   im_header->sender_uid);
+					"IM for group system info, rejected by group internal_id [%d]\n",
+					im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_rejected(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_been_rejected(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_SYS_NOTIFICATION:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from [%d], should be a system administrator\n", im_header->sender_uid);
-			_qq_process_recv_sys_im(data, &cursor, len, gc);
+					"IM from [%d], should be a system administrator\n", im_header->sender_uid);
+			_qq_process_recv_sys_im(data + bytes, len - bytes, gc);
 			break;
 		default:
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				   "IM from [%d], [0x%02x] %s is not processed\n",
-				   im_header->sender_uid,
-				   im_header->im_type, qq_get_recv_im_type_str(im_header->im_type));
-		}
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n");
+					"IM from [%d], [0x%02x] %s is not processed\n",
+					im_header->sender_uid,
+					im_header->im_type, qq_get_recv_im_type_str(im_header->im_type));
 	}
 }
+
--- a/libpurple/protocols/qq/keep_alive.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/keep_alive.c	Thu Jun 26 20:25:38 2008 +0000
@@ -40,7 +40,7 @@
 #include "header_info.h"
 #include "keep_alive.h"
 #include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 #define QQ_UPDATE_ONLINE_INTERVAL   300	/* in sec */
@@ -49,18 +49,17 @@
 void qq_send_packet_keep_alive(PurpleConnection *gc)
 {
 	qq_data *qd;
-	guint8 *raw_data, *cursor;
+	guint8 raw_data[16] = {0};
+	gint bytes= 0;
 
 	qd = (qq_data *) gc->proto_data;
-	raw_data = g_newa(guint8, 4);
-	cursor = raw_data;
 
 	/* In fact, we can send whatever we like to server
 	 * with this command, server return the same result including
 	 * the amount of online QQ users, my ip and port */
-	create_packet_dw(raw_data, &cursor, qd->uid);
+	bytes += qq_put32(raw_data + bytes, qd->uid);
 
-	qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, TRUE, 0, TRUE, raw_data, 4);
+	qq_send_cmd(qd, QQ_CMD_KEEP_ALIVE, raw_data, 4);
 }
 
 /* parse the return of keep-alive packet, it includes some system information */
--- a/libpurple/protocols/qq/login_logout.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/login_logout.c	Thu Jun 26 20:25:38 2008 +0000
@@ -25,6 +25,7 @@
 #include "debug.h"
 #include "internal.h"
 #include "server.h"
+#include "cipher.h"
 
 #include "buddy_info.h"
 #include "buddy_list.h"
@@ -36,8 +37,7 @@
 #include "login_logout.h"
 #include "packet_parse.h"
 #include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 #define QQ_LOGIN_DATA_LENGTH		    416
@@ -70,32 +70,36 @@
 */
 
 /* for QQ 2005? copy from lumaqq */
-static const gint8 login_23_51[29] = {
-	0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 0, -122, 
-	-52, 76, 53, 44, -45, 115, 108, 20, -10, -10, 
-	-81, -61, -6, 51, -92, 1
+/* FIXME: change to guint8 */
+static const guint8 login_23_51[29] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35,
+	0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf,
+	0xc3, 0xfa, 0x33, 0xa4, 0x01
 };
 
-static const gint8 login_53_68[16] = {
-	-115, -117, -6, -20, -43, 82, 23, 74, -122, -7, 
-	-89, 117, -26, 50, -47, 109
+static const guint8 login_53_68[16] = {
+ 	0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
+ 	0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
 };
 
-static const gint8 login_100_bytes[100] = {
-	64, 
-	11, 4, 2, 0, 1, 0, 0, 0, 0, 0, 
-	3, 9, 0, 0, 0, 0, 0, 0, 0, 1, 
-	-23, 3, 1, 0, 0, 0, 0, 0, 1, -13, 
-	3, 0, 0, 0, 0, 0, 0, 1, -19, 3, 
-	0, 0, 0, 0, 0, 0, 1, -20, 3, 0, 
-	0, 0, 0, 0, 0, 3, 5, 0, 0, 0, 
-	0, 0, 0, 0, 3, 7, 0, 0, 0, 0, 
-	0, 0, 0, 1, -18, 3, 0, 0, 0, 0, 
-	0, 0, 1, -17, 3, 0, 0, 0, 0, 0, 
-	0, 1, -21, 3, 0, 0, 0, 0, 0
+static const guint8 login_100_bytes[100] = {
+	0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED,
+	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00,
+	0x00, 0x00, 0x00, 0x00
 };
 
+
 /* fixed value, not affected by version, or mac address */
 /*
 static const guint8 login_53_68[16] = {
@@ -138,74 +142,80 @@
 	guint16 new_server_port;
 };
 
-extern gint			/* defined in send_core.c */
- _create_packet_head_seq(guint8 *buf,
-			 guint8 **cursor, PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq);
-extern gint			/* defined in send_core.c */
- _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd);
+/* generate a md5 key using uid and session_key */
+static guint8 *gen_session_md5(gint uid, guint8 *session_key)
+{
+	guint8 *src, md5_str[QQ_KEY_LENGTH];
+	PurpleCipher *cipher;
+	PurpleCipherContext *context;
 
-/* It is fixed to 16 bytes 0x01 for QQ2003, 
- * Any value works (or a random 16 bytes string) */
-static guint8 *_gen_login_key(void)
-{
-	return (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
+	src = g_newa(guint8, 20);
+	/* bug found by QuLogic */
+	memcpy(src, &uid, sizeof(uid));
+	memcpy(src + sizeof(uid), session_key, QQ_KEY_LENGTH);
+
+	cipher = purple_ciphers_find_cipher("md5");
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, src, 20);
+	purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL);
+	purple_cipher_context_destroy(context);
+
+	return g_memdup(md5_str, QQ_KEY_LENGTH);
 }
 
 /* process login reply which says OK */
 static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
 {
 	gint bytes;
-	guint8 *cursor;
 	qq_data *qd;
 	qq_login_reply_ok_packet lrop;
 
 	qd = (qq_data *) gc->proto_data;
-	cursor = data;
+	/* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
 	bytes = 0;
 
 	/* 000-000: reply code */
-	bytes += read_packet_b(data, &cursor, len, &lrop.result);
+	bytes += qq_get8(&lrop.result, data + bytes);
 	/* 001-016: session key */
-	lrop.session_key = g_memdup(cursor, QQ_KEY_LENGTH);
-	cursor += QQ_KEY_LENGTH;
+	lrop.session_key = g_memdup(data + bytes, QQ_KEY_LENGTH);
 	bytes += QQ_KEY_LENGTH;
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get session_key done\n");
 	/* 017-020: login uid */
-	bytes += read_packet_dw(data, &cursor, len, &lrop.uid);
+	bytes += qq_get32(&lrop.uid, data + bytes);
 	/* 021-024: server detected user public IP */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.client_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.client_ip, 4, data + bytes);
 	/* 025-026: server detected user port */
-	bytes += read_packet_w(data, &cursor, len, &lrop.client_port);
+	bytes += qq_get16(&lrop.client_port, data + bytes);
 	/* 027-030: server detected itself ip 127.0.0.1 ? */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.server_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.server_ip, 4, data + bytes);
 	/* 031-032: server listening port */
-	bytes += read_packet_w(data, &cursor, len, &lrop.server_port);
+	bytes += qq_get16(&lrop.server_port, data + bytes);
 	/* 033-036: login time for current session */
-	bytes += read_packet_time(data, &cursor, len, &lrop.login_time);
+	bytes += qq_getime(&lrop.login_time, data + bytes);
 	/* 037-062: 26 bytes, unknown */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown1, 26);
+	bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes);
 	/* 063-066: unknown server1 ip address */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server1_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.unknown_server1_ip, 4, data + bytes);
 	/* 067-068: unknown server1 port */
-	bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server1_port);
+	bytes += qq_get16(&lrop.unknown_server1_port, data + bytes);
 	/* 069-072: unknown server2 ip address */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server2_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.unknown_server2_ip, 4, data + bytes);
 	/* 073-074: unknown server2 port */
-	bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server2_port);
+	bytes += qq_get16(&lrop.unknown_server2_port, data + bytes);
 	/* 075-076: 2 bytes unknown */
-	bytes += read_packet_w(data, &cursor, len, &lrop.unknown2);
+	bytes += qq_get16(&lrop.unknown2, data + bytes);
 	/* 077-078: 2 bytes unknown */
-	bytes += read_packet_w(data, &cursor, len, &lrop.unknown3);
+	bytes += qq_get16(&lrop.unknown3, data + bytes);
 	/* 079-110: 32 bytes unknown */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown4, 32);
+	bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes);
 	/* 111-122: 12 bytes unknown */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown5, 12);
+	bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes);
 	/* 123-126: login IP of last session */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.last_client_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.last_client_ip, 4, data + bytes);
 	/* 127-130: login time of last session */
-	bytes += read_packet_time(data, &cursor, len, &lrop.last_login_time);
+	bytes += qq_getime(&lrop.last_login_time, data + bytes);
 	/* 131-138: 8 bytes unknown */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown6, 8);
+	bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes);
 
 	if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) {	/* fail parsing login info */
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
@@ -213,9 +223,15 @@
 			   QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
 	}			/* but we still go on as login OK */
 
+	g_return_val_if_fail(qd->session_key == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
 	qd->session_key = lrop.session_key;
-	qd->session_md5 = _gen_session_md5(qd->uid, qd->session_key);
+	
+	g_return_val_if_fail(qd->session_md5 == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
+	qd->session_md5 = gen_session_md5(qd->uid, qd->session_key);
+	
+	g_return_val_if_fail(qd->my_ip == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
 	qd->my_ip = gen_ip_str(lrop.client_ip);
+	
 	qd->my_port = lrop.client_port;
 	qd->login_time = lrop.login_time;
 	qd->last_login_time = lrop.last_login_time;
@@ -247,34 +263,40 @@
 static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint len)
 {
 	gint bytes, ret;
-	guint8 *cursor;
-	gchar *new_server_str;
 	qq_data *qd;
 	qq_login_reply_redirect_packet lrrp;
 
 	qd = (qq_data *) gc->proto_data;
-	cursor = data;
 	bytes = 0;
 	/* 000-000: reply code */
-	bytes += read_packet_b(data, &cursor, len, &lrrp.result);
+	bytes += qq_get8(&lrrp.result, data + bytes);
 	/* 001-004: login uid */
-	bytes += read_packet_dw(data, &cursor, len, &lrrp.uid);
+	bytes += qq_get32(&lrrp.uid, data + bytes);
 	/* 005-008: redirected new server IP */
-	bytes += read_packet_data(data, &cursor, len, lrrp.new_server_ip, 4);
+	bytes += qq_getdata(lrrp.new_server_ip, 4, data + bytes);
 	/* 009-010: redirected new server port */
-	bytes += read_packet_w(data, &cursor, len, &lrrp.new_server_port);
+	bytes += qq_get16(&lrrp.new_server_port, data + bytes);
 
 	if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n",
 			   QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes);
 		ret = QQ_LOGIN_REPLY_MISC_ERROR;
-	} else {		/* start new connection */
-		new_server_str = gen_ip_str(lrrp.new_server_ip);
+	} else {
+		/* redirect to new server, do not disconnect or connect here
+		 * those connect should be called at packet_process */
+		if (qd->real_hostname) {
+			purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+			g_free(qd->real_hostname);
+			qd->real_hostname = NULL;
+		}
+		qd->real_hostname = gen_ip_str(lrrp.new_server_ip);
+		qd->real_port = lrrp.new_server_port;
+		qd->is_redirect = TRUE;
+
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Redirected to new server: %s:%d\n", new_server_str, lrrp.new_server_port);
-		qq_connect(gc->account, new_server_str, lrrp.new_server_port, qd->use_tcp, TRUE);
-		g_free(new_server_str);
+			   "Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port);
+
 		ret = QQ_LOGIN_REPLY_REDIRECT;
 	}
 
@@ -299,89 +321,78 @@
 void qq_send_packet_request_login_token(PurpleConnection *gc)
 {
 	qq_data *qd;
-	guint8 *buf, *cursor;
-	guint16 seq_ret;
-	gint bytes;
-
-	qd = (qq_data *) gc->proto_data;
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	guint8 buf[16] = {0};
+	gint bytes = 0;
 
-	cursor = buf;
-	bytes = 0;
-	bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_REQUEST_LOGIN_TOKEN, TRUE, &seq_ret);
-	bytes += create_packet_dw(buf, &cursor, qd->uid);
-	bytes += create_packet_b(buf, &cursor, 0);
-	bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
 
-	if (bytes == (cursor - buf))	/* packet creation OK */
-		_qq_send_packet(gc, buf, bytes, QQ_CMD_REQUEST_LOGIN_TOKEN);
-	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create request login token packet\n");
+	bytes += qq_put8(buf + bytes, 0);
+	
+	qq_send_data(qd, QQ_CMD_REQUEST_LOGIN_TOKEN, buf, bytes);
 }
 
 /* send login packet to QQ server */
 static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guint8 *token)
 {
 	qq_data *qd;
-	guint8 *buf, *cursor, *raw_data, *encrypted_data;
-	guint16 seq_ret;
-	gint encrypted_len, bytes;
-	gint pos;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted_data;
+	gint encrypted_len;
 
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
+
 	raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
+	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
+
 	encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
-	qd->inikey = _gen_login_key();
+	if (qd->inikey) {
+		g_free(qd->inikey);
+	}
+	qd->inikey = (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
 
+	bytes = 0;
 	/* now generate the encrypted data
 	 * 000-015 use pwkey as key to encrypt empty string */
-	qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data, &encrypted_len);
+	qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data + bytes, &encrypted_len);
+	bytes += 16;
 	/* 016-016 */
-	raw_data[16] = 0x00;
+	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 017-020, used to be IP, now zero */
-	*((guint32 *) (raw_data + 17)) = 0x00000000;
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
 	/* 021-022, used to be port, now zero */
-	*((guint16 *) (raw_data + 21)) = 0x0000;
+	bytes += qq_put16(raw_data + bytes, 0x0000);
 	/* 023-051, fixed value, unknown */
-	g_memmove(raw_data + 23, login_23_51, 29);
+	bytes += qq_putdata(raw_data + bytes, login_23_51, 29);
 	/* 052-052, login mode */
-	raw_data[52] = qd->login_mode;
+	bytes += qq_put8(raw_data + bytes, qd->login_mode);
 	/* 053-068, fixed value, maybe related to per machine */
-	g_memmove(raw_data + 53, login_53_68, 16);
-
+	bytes += qq_putdata(raw_data + bytes, login_53_68, 16);
 	/* 069, login token length */
-	raw_data[69] = token_length;
-	pos = 70;
+	bytes += qq_put8(raw_data + bytes, token_length);
 	/* 070-093, login token, normally 24 bytes */
-	g_memmove(raw_data + pos, token, token_length);
-	pos += token_length;
+	bytes += qq_putdata(raw_data + bytes, token, token_length);
 	/* 100 bytes unknown */
-	g_memmove(raw_data + pos, login_100_bytes, 100);
-	pos += 100;
+	bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
 	/* all zero left */
-	memset(raw_data+pos, 0, QQ_LOGIN_DATA_LENGTH - pos);
 
 	qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len);
 
-	cursor = buf;
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
 	bytes = 0;
-	bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_LOGIN, TRUE, &seq_ret);
-	bytes += create_packet_dw(buf, &cursor, qd->uid);
-	bytes += create_packet_data(buf, &cursor, qd->inikey, QQ_KEY_LENGTH);
-	bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
-	bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
+	bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
 
-	if (bytes == (cursor - buf))	/* packet creation OK */
-		_qq_send_packet(gc, buf, bytes, QQ_CMD_LOGIN);
-	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create login packet\n");
+	qq_send_data(qd, QQ_CMD_LOGIN, buf, bytes);
 }
 
 void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gchar *hex_dump;
+	gchar *error_msg;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -394,20 +405,24 @@
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
 					"Attempting to proceed with the actual packet length.\n");
 		}
-		hex_dump = hex_dump_to_str(buf+2, buf_len-2);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"<<< got a token with %d bytes -> [default] decrypt and dump\n%s", buf_len-2, hex_dump);
+		qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+				buf+2, buf_len-2,
+				"<<< got a token -> [default] decrypt and dump");
 		qq_send_packet_login(gc, buf_len-2, buf+2);
 	} else {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]);
-		hex_dump = hex_dump_to_str(buf, buf_len);
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				">>> %d bytes -> [default] decrypt and dump\n%s",
-				buf_len, hex_dump);
-		try_dump_as_gbk(buf, buf_len);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error requesting login token"));
+		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+				buf, buf_len,
+				">>> [default] decrypt and dump");
+		error_msg = try_dump_as_gbk(buf, buf_len);
+		if (error_msg) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+			g_free(error_msg);
+		} else {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Error requesting login token"));
+		}
 	}
-	g_free(hex_dump);
 }
 
 /* send logout packets to QQ server */
@@ -418,7 +433,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 	for (i = 0; i < 4; i++)
-		qq_send_cmd(gc, QQ_CMD_LOGOUT, FALSE, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
+		qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
 
 	qd->logged_in = FALSE;	/* update login status AFTER sending logout packets */
 }
@@ -429,7 +444,7 @@
 	gint len, ret, bytes;
 	guint8 *data;
 	qq_data *qd;
-	gchar *hex_dump;
+	gchar* error_msg;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -462,13 +477,14 @@
 				break;
 			default:
 				purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]);
-				hex_dump = hex_dump_to_str(data, len);
-				purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-						">>> %d bytes -> [default] decrypt and dump\n%s",
-						buf_len, hex_dump);
-				g_free(hex_dump);
-				try_dump_as_gbk(data, len);
-
+				qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+						data, len,
+						">>> [default] decrypt and dump");
+				error_msg = try_dump_as_gbk(data, len);
+				if (error_msg)	{
+					purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+					g_free(error_msg);
+				}
 				ret = QQ_LOGIN_REPLY_MISC_ERROR;
 			}
 		} else {	/* no idea how to decrypt */
--- a/libpurple/protocols/qq/packet_parse.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.c	Thu Jun 26 20:25:38 2008 +0000
@@ -25,119 +25,147 @@
 #include <string.h>
 
 #include "packet_parse.h"
+#include "debug.h"
+
+
+/*------------------------------------------------PUT------------------------------------------------*/
+
+/* note:
+ * 1, in these functions, 'b' stands for byte, 'w' stands for word, 'dw' stands for double word.
+ * 2, we use '*cursor' and 'buf' as two addresses to calculate the length.
+ * 3, change '0' to '1', if want to get more info about the packet parsing. */
+
+#if 0
+#define PARSER_DEBUG
+#endif
 
 /* read one byte from buf, 
  * return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b)
+gint qq_get8(guint8 *b, guint8 *buf)
 {
-	if (*cursor <= buf + buflen - sizeof(*b)) {
-		*b = **(guint8 **) cursor;
-		*cursor += sizeof(*b);
-		return sizeof(*b);
-	} else {
-		return -1;
-	}
+	guint8 b_dest;
+	memcpy(&b_dest, buf, sizeof(b_dest));
+	*b = b_dest;
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b);
+#endif
+	return sizeof(b_dest);
 }
 
+
 /* read two bytes as "guint16" from buf, 
  * return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w)
+gint qq_get16(guint16 *w, guint8 *buf)
 {
-	if (*cursor <= buf + buflen - sizeof(*w)) {
-		*w = g_ntohs(**(guint16 **) cursor);
-		*cursor += sizeof(*w);
-		return sizeof(*w);
-	} else {
-		return -1;
-	}
+	guint16 w_dest;
+	memcpy(&w_dest, buf, sizeof(w_dest));
+	*w = g_ntohs(w_dest);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w);
+#endif
+	return sizeof(w_dest);
 }
 
+
 /* read four bytes as "guint32" from buf, 
  * return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw)
+gint qq_get32(guint32 *dw, guint8 *buf)
 {
-	if (*cursor <= buf + buflen - sizeof(*dw)) {
-		*dw = g_ntohl(**(guint32 **) cursor);
-		*cursor += sizeof(*dw);
-		return sizeof(*dw);
-	} else {
-		return -1;
-	}
+	guint32 dw_dest;
+	memcpy(&dw_dest, buf, sizeof(dw_dest));
+	*dw = g_ntohl(dw_dest);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw);
+#endif
+	return sizeof(dw_dest);
 }
 
+
+/* read datalen bytes from buf, 
+ * return the number of bytes read if succeeds, otherwise return -1 */
+gint qq_getdata(guint8 *data, gint datalen, guint8 *buf)
+{
+    memcpy(data, buf, datalen);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getdata] buf %p\n", (void *)buf);
+#endif
+    return datalen;
+}
+
+
 /* read four bytes as "time_t" from buf,
  * return the number of bytes read if succeeds, otherwise return -1
  * This function is a wrapper around read_packet_dw() to avoid casting. */
-gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t)
+gint qq_getime(time_t *t, guint8 *buf)
 {
-	guint32 time;
-	gint ret = read_packet_dw(buf, cursor, buflen, &time);
-	if (ret != -1 ) {
-		*t = time;
-	}
-	return ret;
+	guint32 dw_dest;
+	memcpy(&dw_dest, buf, sizeof(dw_dest));
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest);
+#endif
+	dw_dest = g_ntohl(dw_dest);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest);
+#endif
+	memcpy(t, &dw_dest, sizeof(dw_dest));
+	return sizeof(dw_dest);
 }
 
-/* read datalen bytes from buf, 
- * return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen) {
-	if (*cursor <= buf + buflen - datalen) {
-		g_memmove(data, *cursor, datalen);
-		*cursor += datalen;
-		return datalen;
-	} else {
-		return -1;
-	}
+/*------------------------------------------------PUT------------------------------------------------*/
+/* pack one byte into buf
+ * return the number of bytes packed, otherwise return -1 */
+gint qq_put8(guint8 *buf, guint8 b)
+{
+    memcpy(buf, &b, sizeof(b));
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] b 0x%02x\n", b);
+#endif
+    return sizeof(b);
 }
 
-/* pack one byte into buf
- * return the number of bytes packed, otherwise return -1 */
-gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b)
-{
-	if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint8)) {
-		**(guint8 **) cursor = b;
-		*cursor += sizeof(guint8);
-		return sizeof(guint8);
-	} else {
-		return -1;
-	}
-}
 
 /* pack two bytes as "guint16" into buf
  * return the number of bytes packed, otherwise return -1 */
-gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w)
+gint qq_put16(guint8 *buf, guint16 w)
 {
-	if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint16)) {
-		**(guint16 **) cursor = g_htons(w);
-		*cursor += sizeof(guint16);
-		return sizeof(guint16);
-	} else {
-		return -1;
-	}
+    guint16 w_porter;
+    w_porter = g_htons(w);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter);
+#endif
+    memcpy(buf, &w_porter, sizeof(w_porter));
+    return sizeof(w_porter);
 }
 
+
 /* pack four bytes as "guint32" into buf
  * return the number of bytes packed, otherwise return -1 */
-gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw)
+gint qq_put32(guint8 *buf, guint32 dw)
 {
-	if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint32)) {
-		**(guint32 **) cursor = g_htonl(dw);
-		*cursor += sizeof(guint32);
-		return sizeof(guint32);
-	} else {
-		return -1;
-	}
+    guint32 dw_porter;
+    dw_porter = g_htonl(dw);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter);
+#endif
+    memcpy(buf, &dw_porter, sizeof(dw_porter));
+    return sizeof(dw_porter);
 }
 
+
 /* pack datalen bytes into buf
  * return the number of bytes packed, otherwise return -1 */
-gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen)
+gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen)
 {
-	if (*cursor <= buf + MAX_PACKET_SIZE - datalen) {
-		g_memmove(*cursor, data, datalen);
-		*cursor += datalen;
-		return datalen;
-	} else {
-		return -1;
-	}
+    memcpy(buf, data, datalen);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][putdata] buf %p\n", (void *)buf);
+#endif
+    return datalen;
 }
--- a/libpurple/protocols/qq/packet_parse.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.h	Thu Jun 26 20:25:38 2008 +0000
@@ -37,14 +37,28 @@
  */
 #define MAX_PACKET_SIZE 65535
 
+gint qq_get8(guint8 *b, guint8 *buf);
+gint qq_get16(guint16 *w, guint8 *buf);
+gint qq_get32(guint32 *dw,  guint8 *buf);
+gint qq_getime(time_t *t, guint8 *buf);
+gint qq_getdata(guint8 *data, gint datalen, guint8 *buf);
+
+gint qq_put8(guint8 *buf, guint8 b);
+gint qq_put16(guint8 *buf, guint16 w);
+gint qq_put32(guint8 *buf, guint32 dw);
+gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen);
+
+/*
 gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b);
 gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w);
 gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw);
 gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t);
 gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen);
+
 gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b);
 gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w);
 gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw);
 gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen);
+*/
 
 #endif
--- a/libpurple/protocols/qq/qq.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Thu Jun 26 20:25:38 2008 +0000
@@ -55,48 +55,94 @@
 #include "login_logout.h"
 #include "packet_parse.h"
 #include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "send_file.h"
 #include "utils.h"
 #include "version.h"
 
 #define OPENQ_AUTHOR            "Puzzlebird"
 #define OPENQ_WEBSITE            "http://openq.sourceforge.net"
-#define QQ_TCP_QUERY_PORT       "8000"
-#define QQ_UDP_PORT             "8000"
+
+#define QQ_TCP_PORT       		8000
+#define QQ_UDP_PORT             	8000
+
+static void server_list_create(PurpleAccount *account) {
+	PurpleConnection *gc;
+	qq_data *qd;
+	const gchar *user_server;
+	int port;
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create server list\n");
+	gc = purple_account_get_connection(account);
+	g_return_if_fail(gc != NULL  && gc->proto_data != NULL);
+	qd = gc->proto_data;
 
-const gchar *udp_server_list[] = {
-	"sz.tencent.com",
-	"sz2.tencent.com",
-	"sz3.tencent.com",
-	"sz4.tencent.com",
-	"sz5.tencent.com",
-	"sz6.tencent.com",
-	"sz7.tencent.com",
-	"sz8.tencent.com",
-	"sz9.tencent.com"
-};
-const gint udp_server_amount = (sizeof(udp_server_list) / sizeof(udp_server_list[0]));
+	qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE);
+	port = purple_account_get_int(account, "port", 0);
+	if (port == 0) {
+		if (qd->use_tcp) {
+			port = QQ_TCP_PORT;
+		} else {
+			port = QQ_UDP_PORT;
+		}
+	}
+	qd->user_port = port;
 
+ 	g_return_if_fail(qd->user_server == NULL);
+	user_server = purple_account_get_string(account, "server", NULL);
+	if (user_server != NULL && strlen(user_server) > 0) {
+		qd->user_server = g_strdup(user_server);
+	}
 
-const gchar *tcp_server_list[] = {
-	"tcpconn.tencent.com",
-	"tcpconn2.tencent.com",
-	"tcpconn3.tencent.com",
-	"tcpconn4.tencent.com",
-	"tcpconn5.tencent.com",
-	"tcpconn6.tencent.com"
-};
-const gint tcp_server_amount = (sizeof(tcp_server_list) / sizeof(tcp_server_list[0]));
+	if (qd->user_server != NULL) {
+		qd->servers = g_list_append(qd->servers, qd->user_server);
+		return;
+	}
+	if (qd->use_tcp) {
+		qd->servers = g_list_append(qd->servers, "tcpconn.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn2.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn3.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn4.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn5.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn6.tencent.com");
+		return;
+    }
+    
+	qd->servers = g_list_append(qd->servers, "sz.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz2.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz3.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz4.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz5.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz6.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz7.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz8.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz9.tencent.com");
+}
 
-static void _qq_login(PurpleAccount *account)
+static void server_list_remove_all(qq_data *qd) {
+ 	g_return_if_fail(qd != NULL);
+
+	if (qd->real_hostname) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+		g_free(qd->real_hostname);
+		qd->real_hostname = NULL;
+	}
+	
+	if (qd->user_server != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free user_server\n");
+		g_free(qd->user_server);
+		qd->user_server = NULL;
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "free server list\n");
+ 	g_list_free(qd->servers);
+}
+
+static void qq_login(PurpleAccount *account)
 {
-	const gchar *qq_server, *qq_port;
+	PurpleConnection *gc;
 	qq_data *qd;
-	PurpleConnection *gc;
 	PurplePresence *presence;
-	gboolean use_tcp;
 
 	g_return_if_fail(account != NULL);
 
@@ -109,13 +155,7 @@
 	qd->gc = gc;
 	gc->proto_data = qd;
 
-	qq_server = purple_account_get_string(account, "server", NULL);
-	qq_port = purple_account_get_string(account, "port", NULL);
-	use_tcp = purple_account_get_bool(account, "use_tcp", FALSE);
 	presence = purple_account_get_presence(account);
-
-	qd->use_tcp = use_tcp;
-
 	if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
 		qd->login_mode = QQ_LOGIN_MODE_HIDDEN;
 	} else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
@@ -125,26 +165,28 @@
 		qd->login_mode = QQ_LOGIN_MODE_NORMAL;
 	}
 
-	if (qq_server == NULL || strlen(qq_server) == 0)
-		qq_server = use_tcp ?
-		    tcp_server_list[random() % tcp_server_amount] :
-		    udp_server_list[random() % udp_server_amount];
+	server_list_create(account);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		"Server list has %d\n", g_list_length(qd->servers));
 
-	if (qq_port == NULL || strtol(qq_port, NULL, 10) == 0)
-		qq_port = use_tcp ? QQ_TCP_QUERY_PORT : QQ_UDP_PORT;
-
-	purple_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS);
-
-	if (qq_connect(account, qq_server, strtol(qq_port, NULL, 10), use_tcp, FALSE) < 0)
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Unable to connect."));
+	qq_connect(account);
 }
 
-/* directly goes for qq_disconnect */
-static void _qq_close(PurpleConnection *gc)
+/* clean up the given QQ connection and free all resources */
+static void qq_close(PurpleConnection *gc)
 {
-	g_return_if_fail(gc != NULL);
+	qq_data *qd;
+
+	g_return_if_fail(gc != NULL  && gc->proto_data);
+	qd = gc->proto_data;
+
 	qq_disconnect(gc);
+
+	server_list_remove_all(qd);
+	
+	g_free(qd);
+
+	gc->proto_data = NULL;
 }
 
 /* returns the icon name for a buddy or protocol */
@@ -442,8 +484,9 @@
 
 	g_string_append(info, "<hr>\n");
 
+	g_string_append_printf(info, _("<b>Server</b>: %s: %d<br>\n"), qd->server_name, qd->real_port);
 	g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP");
-	g_string_append_printf(info, _("<b>Server IP</b>: %s: %d<br>\n"), qd->server_ip, qd->server_port);
+	g_string_append_printf(info, _("<b>Real hostname</b>: %s: %d<br>\n"), qd->real_hostname, qd->real_port);
 	g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), qd->my_ip);
 
 	g_string_append(info, "<hr>\n");
@@ -594,7 +637,7 @@
 }
 
 
-static void _qq_keep_alive(PurpleConnection *gc)
+static void qq_keep_alive(PurpleConnection *gc)
 {
 	qq_group *group;
 	qq_data *qd;
@@ -651,8 +694,8 @@
 	_qq_buddy_menu,						/* blist_node_menu */
 	qq_chat_info,						/* chat_info */
 	qq_chat_info_defaults,					/* chat_info_defaults */
-	_qq_login,						/* login */
-	_qq_close,						/* close */
+	qq_login,							/* open */
+	qq_close,						/* close */
 	_qq_send_im,						/* send_im */
 	NULL,							/* set_info */
 	NULL,							/* send_typing	*/
@@ -676,7 +719,7 @@
 	NULL,							/* chat_leave */
 	NULL,							/* chat_whisper */
 	_qq_chat_send,						/* chat_send */
-	_qq_keep_alive,						/* keepalive */
+	qq_keep_alive,						/* keepalive */
 	NULL,							/* register_user */
 	_qq_get_chat_buddy_info,				/* get_cb_info	*/
 	NULL,							/* get_cb_away	*/
@@ -751,13 +794,13 @@
 {
 	PurpleAccountOption *option;
 
-	option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", FALSE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
 	option = purple_account_option_string_new(_("Server"), "server", NULL);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
-	option = purple_account_option_string_new(_("Port"), "port", NULL);
+	option = purple_account_option_int_new(_("Port"), "port", 0);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
 	my_protocol = plugin;
--- a/libpurple/protocols/qq/qq.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Thu Jun 26 20:25:38 2008 +0000
@@ -28,6 +28,9 @@
 #include <glib.h>
 #include "internal.h"
 #include "ft.h"
+#include "circbuffer.h"
+#include "dnsquery.h"
+#include "dnssrv.h"
 #include "proxy.h"
 #include "roomlist.h"
 
@@ -66,7 +69,39 @@
 };
 
 struct _qq_data {
-	gint fd;			/* socket file handler */
+	PurpleConnection *gc;
+
+	/* common network resource */
+	GList *servers;
+	gchar *user_server;
+	gint user_port;
+	gboolean use_tcp;		/* network in tcp or udp */
+	
+	gchar *server_name;
+	gboolean is_redirect;
+	gchar *real_hostname;	/* from real connction */
+	guint16 real_port;
+	guint reconnect_timeout;
+	gint reconnect_times;
+
+	PurpleProxyConnectData *connect_data;
+	gint fd;				/* socket file handler */
+	gint tx_handler; 	/* socket can_write handle, use in udp connecting and tcp send out */
+
+	GList *send_trans;	/* check ack packet and resend */
+	guint resend_timeout;
+
+	guint8 rcv_window[1 << 13];		/* windows for check duplicate packet */
+	GQueue *rcv_trans;		/* queue to store packet can not process before login */
+	
+	/* tcp related */
+	PurpleCircBuffer *tcp_txbuf;
+	guint8 *tcp_rxqueue;
+	int tcp_rxlen;
+	
+	/* udp related */
+	PurpleDnsQueryData *udp_query_data;
+
 	guint32 uid;			/* QQ number */
 	guint8 *inikey;			/* initial key to encrypt login packet */
 	guint8 *pwkey;			/* password in md5 (or md5' md5) */
@@ -76,17 +111,9 @@
 	guint16 send_seq;		/* send sequence number */
 	guint8 login_mode;		/* online of invisible */
 	gboolean logged_in;		/* used by qq-add_buddy */
-	gboolean use_tcp;		/* network in tcp or udp */
-
-	PurpleProxyType proxy_type;
-	PurpleConnection *gc;
 
 	PurpleXfer *xfer;			/* file transfer handler */
-	struct sockaddr_in dest_sin;
 
-	/* from real connction */
-	gchar *server_ip;
-	guint16 server_port;
 	/* get from login reply packet */
 	time_t login_time;
 	time_t last_login_time;
@@ -99,9 +126,6 @@
 	guint32 all_online;		/* the number of online QQ users */
 	time_t last_get_online;		/* last time send get_friends_online packet */
 
-	guint8 window[1 << 13];		/* check up for duplicated packet */
-	gint sendqueue_timeout;
-
 	PurpleRoomlist *roomlist;
 	gint channel;			/* the id for opened chat conversation */
 
@@ -112,16 +136,12 @@
 	GList *buddies;
 	GList *contact_info_window;
 	GList *group_info_window;
-	GList *sendqueue;
 	GList *info_query;
 	GList *add_buddy_request;
-	GQueue *before_login_packets;
 
 	/* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */
 	gboolean modifying_info;
 	gboolean modifying_face;
 };
 
-void qq_function_not_implemented(PurpleConnection *gc);
-
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Thu Jun 26 20:25:38 2008 +0000
@@ -0,0 +1,1258 @@
+/**
+ * @file qq_network.c
+ *
+ * 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 "cipher.h"
+#include "debug.h"
+#include "internal.h"
+
+#ifdef _WIN32
+#define random rand
+#define srandom srand
+#endif
+
+#include "buddy_info.h"
+#include "buddy_list.h"
+#include "buddy_opt.h"
+#include "buddy_status.h"
+#include "group_free.h"
+#include "char_conv.h"
+#include "crypt.h"
+#include "group_network.h"
+#include "header_info.h"
+#include "keep_alive.h"
+#include "im.h"
+#include "login_logout.h"
+#include "packet_parse.h"
+#include "qq_network.h"
+#include "qq_trans.h"
+#include "sys_msg.h"
+#include "utils.h"
+
+/* set QQ_RECONNECT_MAX to 1, when test reconnecting */
+#define QQ_RECONNECT_MAX					4
+#define QQ_RECONNECT_INTERVAL		5000
+
+static gboolean set_new_server(qq_data *qd)
+{
+	gint count;
+	gint index;
+	GList *it = NULL;
+	
+ 	g_return_val_if_fail(qd != NULL, FALSE);
+
+	if (qd->servers == NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n");
+		return FALSE;
+	}
+
+	if (qd->real_hostname) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+		g_free(qd->real_hostname);
+		qd->real_hostname = NULL;
+	}
+
+	/* remove server used before */
+	if (qd->server_name != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+			"Remove previous server [%s]\n", qd->server_name);
+   		qd->servers = g_list_remove(qd->servers, qd->server_name);
+   		qd->server_name = NULL;
+    }
+	
+	count = g_list_length(qd->servers);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count);
+	if (count <= 0) {
+		/* no server left, disconnect when result is false */
+		qd->servers = NULL;
+		return FALSE;
+	}
+	
+	/* get new server */
+	index  = random() % count;
+	it = g_list_nth(qd->servers, index);
+    qd->server_name = it->data;		/* do not free server_name */
+    if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index);
+		return FALSE;
+	}
+
+	qd->real_hostname = g_strdup(qd->server_name);
+	qd->real_port = qd->user_port;
+	
+ 	qd->reconnect_times = QQ_RECONNECT_MAX;
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		"set new server to %s:%d\n", qd->real_hostname, qd->real_port);
+	return TRUE;
+}
+
+/* QQ 2003iii uses double MD5 for the pwkey to get the session key */
+static guint8 *encrypt_account_password(const gchar *pwd)
+{
+	PurpleCipher *cipher;
+	PurpleCipherContext *context;
+
+	guchar pwkey_tmp[QQ_KEY_LENGTH];
+
+	cipher = purple_ciphers_find_cipher("md5");
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd));
+	purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
+	purple_cipher_context_destroy(context);
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH);
+	purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
+	purple_cipher_context_destroy(context);
+
+	return g_memdup(pwkey_tmp, QQ_KEY_LENGTH);
+}
+
+/* default process, decrypt and dump */
+static void process_cmd_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+{
+	qq_data *qd;
+	guint8 *data;
+	gint data_len;
+	gchar *msg_utf8 = NULL;
+
+	g_return_if_fail(buf != NULL && buf_len != 0);
+
+	qq_show_packet("Processing unknown packet", buf, buf_len);
+
+	qd = (qq_data *) gc->proto_data;
+
+	data_len = buf_len;
+	data = g_newa(guint8, data_len);
+	memset(data, 0, data_len);
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
+		return;
+	}
+	
+	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+			data, data_len,
+			">>> [%d] %s -> [default] decrypt and dump",
+			seq, qq_get_cmd_desc(cmd));
+
+	msg_utf8 = try_dump_as_gbk(data, data_len);
+	if (msg_utf8) {
+		g_free(msg_utf8);
+	}
+}
+
+static gint packet_get_header(guint8 *header_tag,  guint16 *source_tag,
+	guint16 *cmd, guint16 *seq, guint8 *buf)
+{
+	gint bytes = 0;
+	bytes += qq_get8(header_tag, buf + bytes);
+	bytes += qq_get16(source_tag, buf + bytes);
+	bytes += qq_get16(cmd, buf + bytes);
+	bytes += qq_get16(seq, buf + bytes);
+	return bytes;
+}
+
+/* check whether one sequence number is duplicated or not
+ * return TRUE if it is duplicated, otherwise FALSE */
+static gboolean packet_is_dup(qq_data *qd, guint16 seq)
+{
+	guint8 *byte, mask;
+
+	g_return_val_if_fail(qd != NULL, FALSE);
+
+	byte = &(qd->rcv_window[seq / 8]);
+	mask = (1 << (seq % 8));
+
+	if ((*byte) & mask)
+		return TRUE;	/* check mask */
+	(*byte) |= mask;
+	return FALSE;		/* set mask */
+}
+
+static gboolean packet_check_ack(qq_data *qd, guint16 seq)
+{
+	gpointer trans;
+
+	g_return_val_if_fail(qd != NULL, FALSE);
+
+	trans = qq_send_trans_find(qd, seq);
+	if (trans == NULL) {
+		return FALSE;
+	}
+	
+	qq_send_trans_remove(qd, trans);
+	return TRUE;
+}
+
+static gboolean reconnect_later_cb(gpointer data)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+
+	gc = (PurpleConnection *) data;
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
+	qd = (qq_data *) gc->proto_data;
+
+	qd->reconnect_timeout = 0;
+
+	qq_connect(gc->account);
+	return FALSE;	/* timeout callback stops */
+}
+
+static void reconnect_later(PurpleConnection *gc)
+{
+	qq_data *qd;
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	qd->reconnect_times--;
+	if (qd->reconnect_times < 0) {
+		if ( set_new_server(qd) != TRUE) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Failed to connect server"));
+			return;
+		}
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		"Reconnect to server %s:%d next retries %d in %d ms\n",
+		qd->real_hostname, qd->real_port,
+		qd->reconnect_times, QQ_RECONNECT_INTERVAL);
+
+	qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL,
+		reconnect_later_cb, gc);
+}
+
+static void process_cmd_server(
+	PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+	/* now process the packet */
+	switch (cmd) {
+		case QQ_CMD_RECV_IM:
+			qq_process_recv_im(data, data_len, seq, gc);
+			break;
+		case QQ_CMD_RECV_MSG_SYS:
+			qq_process_msg_sys(data, data_len, seq, gc);
+			break;
+		case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
+			qq_process_friend_change_status(data, data_len, gc);
+			break;
+		default:
+			process_cmd_unknow(gc, data, data_len, cmd, seq);
+			break;
+	}
+}
+
+static void process_cmd_reply(
+	PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+	/* now process the packet */
+	switch (cmd) {
+		case QQ_CMD_KEEP_ALIVE:
+			qq_process_keep_alive_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_UPDATE_INFO:
+			qq_process_modify_info_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_ADD_FRIEND_WO_AUTH:
+			qq_process_add_buddy_reply(data, data_len, seq, gc);
+			break;
+		case QQ_CMD_DEL_FRIEND:
+			qq_process_remove_buddy_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_REMOVE_SELF:
+			qq_process_remove_self_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_BUDDY_AUTH:
+			qq_process_add_buddy_auth_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GET_USER_INFO:
+			qq_process_get_info_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_CHANGE_ONLINE_STATUS:
+			qq_process_change_status_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_SEND_IM:
+			qq_process_send_im_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_LOGIN:
+			qq_process_login_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GET_FRIENDS_LIST:
+			qq_process_get_buddies_list_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GET_FRIENDS_ONLINE:
+			qq_process_get_buddies_online_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GROUP_CMD:
+			qq_process_group_cmd_reply(data, data_len, seq, gc);
+			break;
+		case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
+			qq_process_get_all_list_with_group_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GET_LEVEL:
+			qq_process_get_level_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_REQUEST_LOGIN_TOKEN:
+			qq_process_request_login_token_reply(data, data_len, gc);
+			break;
+		default:
+			process_cmd_unknow(gc, data, data_len, cmd, seq);
+			break;
+	}
+}
+
+/* process the incoming packet from qq_pending */
+static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
+{
+	qq_data *qd;
+	gint bytes, bytes_not_read;
+
+	gboolean prev_login_status;
+	guint8 *new_data;
+	gint new_data_len;
+	
+	guint8 header_tag;
+	guint16 source_tag;
+	guint16 cmd;
+	guint16 seq;		/* May be ack_seq or send_seq, depends on cmd */
+
+	gboolean is_reply;
+
+	g_return_if_fail(buf != NULL && buf_len > 0);
+
+	qd = (qq_data *) gc->proto_data;
+
+	prev_login_status = qd->logged_in;
+
+	/* Len, header and tail tag have been checked before */
+	bytes = 0;
+	bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes);
+
+	if (QQ_DEBUG) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				"==> [%05d] 0x%04X %s, from (0x%04X %s)\n",
+				seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_source_str(source_tag));
+	}
+	
+	bytes_not_read = buf_len - bytes - 1;
+
+	/* ack packet, we need to update send tranactions */
+	/* we do not check duplication for server ack */
+	is_reply = packet_check_ack(qd, seq);
+	if ( !is_reply ) {
+		if ( !qd->logged_in ) {
+			/* packets before login */
+			qq_rcv_trans_push(qd, cmd, seq, buf + bytes, bytes_not_read);
+			return;	/* do not process it now */
+		}
+		
+		/* server intiated packet, we need to send ack and check duplicaion 
+		 * this must be put after processing b4_packet
+		 * as these packets will be passed in twice */
+		if (packet_is_dup(qd, seq)) {
+			purple_debug(PURPLE_DEBUG_WARNING,
+					"QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
+			return;
+		}
+		process_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
+		return;
+	}
+
+	/* this is the length of all the encrypted data (also remove tail tag */
+	process_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
+
+	/* check is redirect or not, and do it now */
+	if (qd->is_redirect) {
+	 	/* free resource except real_hostname and port */
+		qq_disconnect(gc);
+	 	qd->reconnect_times = QQ_RECONNECT_MAX;
+		reconnect_later(gc);
+		return;
+	}
+
+	if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) {
+		/* logged_in, but we have packets before login */
+		new_data = g_newa(guint8, MAX_PACKET_SIZE);
+		while (1) {
+			memset(new_data, 0, MAX_PACKET_SIZE);
+			new_data_len = qq_rcv_trans_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE);
+			if (new_data_len < 0) {
+				break;
+			}
+			if (new_data_len == 0) {
+				continue;
+			}
+			process_cmd_reply(gc, seq, cmd, new_data, new_data_len);
+		}
+	}
+}
+
+static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	guint8 buf[1024];		/* set to 16 when test  tcp_rxqueue */
+	gint buf_len;
+	gint bytes;
+	
+	guint8 *pkt;
+	guint16 pkt_len;
+	
+	gchar *error_msg;
+	guint8 *jump;
+	gint jump_len;
+
+	gc = (PurpleConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	if(cond != PURPLE_INPUT_READ) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Socket error"));
+		return;
+	}
+
+	qd = (qq_data *) gc->proto_data;
+	
+	/* test code, not using tcp_rxqueue
+	memset(pkt,0, sizeof(pkt));
+	buf_len = read(qd->fd, pkt, sizeof(pkt));
+	if (buf_len > 2) {
+		packet_process(gc, pkt + 2, buf_len - 2);
+	}
+	return;
+	*/
+	
+	buf_len = read(qd->fd, buf, sizeof(buf));
+	if (buf_len < 0) {
+		if (errno == EAGAIN)
+			/* No worries */
+			return;
+
+		error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+		g_free(error_msg);
+		return;
+	} else if (buf_len == 0) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Server closed the connection."));
+		return;
+	}
+
+	gc->last_received = time(NULL);
+	purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+			   "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen);
+	qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen);
+	memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len);
+	qd->tcp_rxlen += buf_len;
+	
+	pkt = g_newa(guint8, MAX_PACKET_SIZE);
+	while (1) {
+		if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) {
+			break;
+		}
+		
+		bytes = 0;
+		bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes);
+		if (qd->tcp_rxlen < pkt_len) {
+			break;
+		}
+
+		purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+				   "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen);
+
+		if ( pkt_len < QQ_TCP_HEADER_LENGTH
+		    || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG
+			|| *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) {
+			/* HEY! This isn't even a QQ. What are you trying to pull? */
+
+			purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING",
+				 "Packet error, failed to check header and tail tag\n");
+
+			jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1);
+			if ( !jump ) {
+				purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+				 	"Failed to find next QQ_PACKET_TAIL, clear receive buffer\n");
+				g_free(qd->tcp_rxqueue);
+				qd->tcp_rxqueue = NULL;
+				qd->tcp_rxlen = 0;
+				return;
+			}
+
+			/* jump and over QQ_PACKET_TAIL */
+			jump_len = (jump - qd->tcp_rxqueue) + 1;
+			purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+				"Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1);
+			g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len);
+			qd->tcp_rxlen -= jump_len;
+			continue;
+		}
+
+		memset(pkt, 0, MAX_PACKET_SIZE);
+		g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes);
+		
+		/* jump to next packet */
+		qd->tcp_rxlen -= pkt_len;
+		if (qd->tcp_rxlen) {
+			purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING",
+			 	"shrink tcp_rxqueue to %d\n", qd->tcp_rxlen);		
+			jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen);
+			g_free(qd->tcp_rxqueue);
+			qd->tcp_rxqueue = jump;
+		} else {
+			purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING",
+			 	"free tcp_rxqueue\n");		
+			g_free(qd->tcp_rxqueue);
+			qd->tcp_rxqueue = NULL;
+		}
+
+		if (pkt == NULL) {
+			continue;
+		}
+		/* do not call packet_process before jump 
+		 * packet_process may call disconnect and destory tcp_rxqueue */
+		packet_process(gc, pkt, pkt_len - bytes);
+	}
+}
+
+static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	guint8 *buf;
+	gint buf_len;
+
+	gc = (PurpleConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	if(cond != PURPLE_INPUT_READ) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Socket error"));
+		return;
+	}
+
+	qd = (qq_data *) gc->proto_data;
+	g_return_if_fail(qd->fd >= 0);
+	
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+
+	/* here we have UDP proxy suppport */
+	buf_len = read(qd->fd, buf, MAX_PACKET_SIZE);
+	if (buf_len <= 0) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Unable to read from socket"));
+		return;
+	}
+
+	gc->last_received = time(NULL);
+
+	if (buf_len < QQ_UDP_HEADER_LENGTH) {
+		if (buf[0] != QQ_PACKET_TAG || buf[buf_len - 1] != QQ_PACKET_TAIL) {
+			qq_hex_dump(PURPLE_DEBUG_ERROR, "UDP_PENDING",
+					buf, buf_len,
+					"Received packet is too short, or no header and tail tag");
+			return;
+		}
+	}
+	
+	packet_process(gc, buf, buf_len);
+}
+
+static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len)
+{
+	gint ret;
+
+	g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Send %d bytes to socket %d\n", data_len, qd->fd);
+
+	errno = 0;
+	ret = send(qd->fd, data, data_len, 0);
+	if (ret < 0 && errno == EAGAIN) {
+		return ret;
+	}
+	
+	if (ret < 0) {
+		/* TODO: what to do here - do we really have to disconnect? */
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Send failed: %d, %s\n", errno, g_strerror(errno));
+		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+	}
+	return ret;
+}
+
+static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond)
+{
+	qq_data *qd = data;
+	int ret, writelen;
+
+	writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf);
+	if (writelen == 0) {
+		purple_input_remove(qd->tx_handler);
+		qd->tx_handler = 0;
+		return;
+	}
+
+	ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen);
+	purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE",
+		"total %d bytes is sent %d\n", writelen, ret);
+
+	if (ret < 0 && errno == EAGAIN)
+		return;
+	else if (ret < 0) {
+		/* TODO: what to do here - do we really have to disconnect? */
+		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               _("Write Error"));
+		return;
+	}
+
+	purple_circ_buffer_mark_read(qd->tcp_txbuf, ret);
+}
+
+static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len)
+{
+	gint ret;
+
+	g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
+
+	/*
+	 * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
+	 */
+
+	if (qd->tx_handler == 0) {
+		ret = write(qd->fd, data, data_len);
+	} else {
+		ret = -1;
+		errno = EAGAIN;
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
+		"Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret);
+	if (ret < 0 && errno == EAGAIN) {
+		/* socket is busy, send later */
+		/*
+		 * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n");
+		 */
+		ret = 0;
+	} else if (ret <= 0) {
+		/* TODO: what to do here - do we really have to disconnect? */
+		purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT",
+			"Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno));
+		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+		return ret;
+	}
+
+	if (ret < data_len) {
+		purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
+			"Add %d bytes to buffer\n", data_len - ret);
+		if (qd->tx_handler == 0) {
+			qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd);
+		}
+		purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret);
+	}
+	return ret;
+}
+
+static gboolean trans_timeout(gpointer data)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	guint8 *buf;
+	gint buf_len = 0;
+	guint16 cmd;
+	gint retries = 0;
+	int index;
+	
+	gc = (PurpleConnection *) data;
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE);
+
+	qd = (qq_data *) gc->proto_data;
+	
+	index = 0;
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+
+	while (1) {
+		if (index < 0) {
+			/* next record is NULL */
+			break;
+		}
+		/* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); */
+		memset(buf, 0, MAX_PACKET_SIZE);
+		buf_len = qq_send_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries);
+		if (buf_len <= 0) {
+			/* curr record is empty, whole trans  is NULL */
+			break;
+		}
+		/* index = -1, when get last record of transactions */
+		
+		/* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); */
+		if (retries > 0) {
+			if (qd->use_tcp) {
+				tcp_send_out(qd, buf, buf_len);
+			} else {
+				udp_send_out(qd, buf, buf_len);
+			}
+			continue;
+		}
+
+		/* retries <= 0 */
+		switch (cmd) {
+		case QQ_CMD_KEEP_ALIVE:
+			if (qd->logged_in) {
+				purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n");
+				purple_connection_error_reason(gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
+				qd->logged_in = FALSE;
+			}
+			break;
+		case QQ_CMD_LOGIN:
+		case QQ_CMD_REQUEST_LOGIN_TOKEN:
+			if (!qd->logged_in)	{
+				/* cancel login progress */
+				purple_connection_error_reason(gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply"));
+			}
+			break;
+		default:
+			purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
+				"%s packet lost.\n", qq_get_cmd_desc(cmd));
+		}
+	}
+
+	return TRUE;		/* if return FALSE, timeout callback stops */
+}
+
+/* the callback function after socket is built
+ * we setup the qq protocol related configuration here */
+static void qq_connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+	qq_data *qd;
+	PurpleConnection *gc;
+	gchar *conn_msg;
+	const gchar *passwd;
+
+	gc = (PurpleConnection *) data;
+
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n");
+		close(source);
+		return;
+	}
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+	/* Connect is now complete; clear the PurpleProxyConnectData */
+	qd->connect_data = NULL;
+
+	if (source < 0) {	/* socket returns -1 */
+		purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n");
+		qq_disconnect(gc);
+		reconnect_later(gc);
+		return;
+	}
+
+	/* _qq_show_socket("Got login socket", source); */
+
+	/* QQ use random seq, to minimize duplicated packets */
+	srandom(time(NULL));
+	qd->send_seq = random() & 0x0000ffff;
+	qd->fd = source;
+	qd->logged_in = FALSE;
+	qd->channel = 1;
+	qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
+
+	/* now generate md5 processed passwd */
+	passwd = purple_account_get_password(purple_connection_get_account(gc));
+	g_return_if_fail(qd->pwkey == NULL);
+	qd->pwkey = encrypt_account_password(passwd);
+
+	g_return_if_fail(qd->resend_timeout == 0);
+	/* call trans_timeout every 5 seconds */
+	qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc);
+	
+	if (qd->use_tcp)
+		gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc);
+	else
+		gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc);
+
+	/* Update the login progress status display */
+	conn_msg = g_strdup_printf("Login as %d", qd->uid);
+	purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
+	g_free(conn_msg);
+
+	qq_send_packet_request_login_token(gc);
+}
+
+static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	socklen_t len;
+	int error=0, ret;
+
+	gc = (PurpleConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+
+	purple_debug_info("proxy", "Connected.\n");
+
+	/*
+	 * getsockopt after a non-blocking connect returns -1 if something is
+	 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
+	 * error holds what connect would have returned if it blocked until now.
+	 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
+	 * and anything else is a real error.
+	 *
+	 * (error == EINPROGRESS can happen after a select because the kernel can
+	 * be overly optimistic sometimes. select is just a hint that you might be
+	 * able to do something.)
+	 */
+	len = sizeof(error);
+	ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
+	if (ret == 0 && error == EINPROGRESS)
+		return; /* we'll be called again later */
+		
+	purple_input_remove(qd->tx_handler);
+	qd->tx_handler = 0;
+	if (ret < 0 || error != 0) {
+		if(ret != 0) 
+			error = errno;
+
+		close(source);
+
+		purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error));
+
+		qq_connect_cb(gc, -1, _("Unable to connect"));
+		return;
+	}
+
+	qq_connect_cb(gc, source, NULL);
+}
+
+static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) {
+	PurpleConnection *gc;
+	qq_data *qd;
+	struct sockaddr server_addr;
+	int addr_size;
+	gint fd = -1;
+	int flags;
+
+	gc = (PurpleConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+	/* udp_query_data must be set as NULL.
+	 * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */
+	qd->udp_query_data = NULL;
+
+	if (!hosts || !hosts->data) {
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Couldn't resolve host"));
+		return;
+	}
+
+	addr_size = GPOINTER_TO_INT(hosts->data);
+	hosts = g_slist_remove(hosts, hosts->data);
+	memcpy(&server_addr, hosts->data, addr_size);
+	g_free(hosts->data);
+	
+	hosts = g_slist_remove(hosts, hosts->data);
+	while(hosts) {
+		hosts = g_slist_remove(hosts, hosts->data);
+		g_free(hosts->data);
+		hosts = g_slist_remove(hosts, hosts->data);
+	}
+
+	fd = socket(PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"Unable to create socket: %s\n", g_strerror(errno));
+		return;
+	}
+
+	/* we use non-blocking mode to speed up connection */
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+	/* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
+	 *
+	 * If a UDP socket is unconnected, which is the normal state after a
+	 * bind() call, then send() or write() are not allowed, since no
+	 * destination is available; only sendto() can be used to send data.
+	 *   
+	 * Calling connect() on the socket simply records the specified address
+	 * and port number as being the desired communications partner. That
+	 * means that send() or write() are now allowed; they use the destination
+	 * address and port given on the connect call as the destination of packets.
+	 */
+	if (connect(fd, &server_addr, addr_size) >= 0) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n");
+		flags = fcntl(fd, F_GETFL);
+		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+		qq_connect_cb(gc, fd, NULL);
+		return;
+	}
+	
+	/* [EINPROGRESS]
+	 *    The socket is marked as non-blocking and the connection cannot be 
+	 *    completed immediately. It is possible to select for completion by 
+	 *    selecting the socket for writing.
+	 * [EINTR]
+	 *    A signal interrupted the call. 
+	 *    The connection is established asynchronously.
+	 */
+	if ((errno == EINPROGRESS) || (errno == EINTR)) {
+			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
+			qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc);
+			return;
+		}
+
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %d\n", g_strerror(errno));
+	close(fd);
+}
+
+/* establish a generic QQ connection 
+ * TCP/UDP, and direct/redirected */
+void qq_connect(PurpleAccount *account)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	gchar *conn_msg;
+
+	gc = purple_account_get_connection(account);
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+
+	/* test set_new_server
+	while (set_new_server(qd)) {
+   		purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST",
+   			"New server %s:%d  Real server %s:%d\n",
+   			qd->server_name, qd->user_port, qd->real_hostname, qd->real_port);
+	}
+	purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n",
+ 			qd->servers);
+ 	exit(1);
+	*/
+	if (qd->server_name == NULL) {
+		/* must be first call this function */
+		if ( set_new_server(qd) != TRUE) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Failed to connect server"));
+			return;
+		}
+	}
+
+	if (qd->real_hostname == NULL || qd->real_port == 0) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("hostname is NULL or port is 0"));
+		return;
+	}
+
+	conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"),
+		qd->real_hostname, qd->reconnect_times);
+	purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS);
+	g_free(conn_msg);
+
+	if (qd->is_redirect) {
+   		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n",
+   			qd->real_hostname, qd->real_port);
+   	}
+	qd->is_redirect = FALSE;
+
+	qd->fd = -1;
+	qd->tx_handler = 0;
+	
+	/* QQ connection via UDP/TCP. 
+	* Now use Purple proxy function to provide TCP proxy support,
+	* and qq_udp_proxy.c to add UDP proxy support (thanks henry) */
+	if(qd->use_tcp) {
+   		purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n",
+   			qd->real_hostname, qd->real_port);
+
+		/* TODO: is there a good default grow size? */
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n");
+		qd->tcp_txbuf = purple_circ_buffer_new(0);
+
+		qd->connect_data = purple_proxy_connect(NULL, account,
+				qd->real_hostname, qd->real_port, qq_connect_cb, gc);
+		if (qd->connect_data == NULL) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Unable to connect."));
+		}
+		return;
+	}
+	
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n",
+		qd->real_hostname, qd->real_port);
+
+	g_return_if_fail(qd->udp_query_data == NULL);
+	qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port,
+		udp_host_resolved, gc);
+	if (qd->udp_query_data == NULL) {
+		purple_connection_error_reason(qd->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not resolve hostname"));
+	}
+}
+
+/* clean up qq_data structure and all its components
+ * always used before a redirectly connection */
+void qq_disconnect(PurpleConnection *gc)
+{
+	qq_data *qd;
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n");
+	/* finish  all I/O */
+	if (qd->fd >= 0 && qd->logged_in) {
+		qq_send_packet_logout(gc);
+	}
+
+	if (qd->resend_timeout > 0) {
+		purple_timeout_remove(qd->resend_timeout);
+		qd->resend_timeout = 0;
+	}
+
+	if (gc->inpa > 0) {
+		purple_input_remove(gc->inpa);
+		gc->inpa = 0;
+	}
+
+	if (qd->fd >= 0) {
+		close(qd->fd);
+		qd->fd = -1;
+	}
+
+	if (qd->reconnect_timeout > 0) {
+		purple_timeout_remove(qd->reconnect_timeout);
+		qd->reconnect_timeout = 0;
+	}
+
+	if (qd->connect_data != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n");
+		purple_proxy_connect_cancel(qd->connect_data);
+	}
+	
+	if(qd->tcp_txbuf != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n");
+		purple_circ_buffer_destroy(qd->tcp_txbuf);
+		qd->tcp_txbuf = NULL;
+	}
+	
+	if (qd->tx_handler) {
+		purple_input_remove(qd->tx_handler);
+		qd->tx_handler = 0;
+	}
+	if (qd->tcp_rxqueue != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n");
+		g_free(qd->tcp_rxqueue);
+		qd->tcp_rxqueue = NULL;
+		qd->tcp_rxlen = 0;
+	}
+	
+	if (qd->udp_query_data != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n");
+		purple_dnsquery_destroy(qd->udp_query_data);
+		qd->udp_query_data = NULL;
+	}
+
+	memset(qd->rcv_window, 0, sizeof(qd->rcv_window));
+	qq_rcv_trans_remove_all(qd);
+	qq_send_trans_remove_all(qd);
+	
+	if (qd->inikey) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n");
+		g_free(qd->inikey);
+		qd->inikey = NULL;
+	}
+	if (qd->pwkey) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n");
+		g_free(qd->pwkey);
+		qd->pwkey = NULL;
+	}
+	if (qd->session_key) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n");
+		g_free(qd->session_key);
+		qd->session_key = NULL;
+	}
+	if (qd->session_md5) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n");
+		g_free(qd->session_md5);
+		qd->session_md5 = NULL;
+	}
+	if (qd->my_ip) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n");
+		g_free(qd->my_ip);
+		qd->my_ip = NULL;
+	}
+
+	qq_group_packets_free(qd);
+	qq_group_free_all(qd);
+	qq_add_buddy_request_free(qd);
+	qq_info_query_free(qd);
+	qq_buddies_list_free(gc->account, qd);
+}
+
+static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, 
+	guint8 *data, gint data_len)
+{
+	gint bytes = 0;
+	g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1);
+	
+	if (data == NULL) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data is NULL\n");
+		return -1;
+	}
+	if (data_len <= 0) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data len <= 0\n");
+		return -1;
+	}
+
+	/* QQ TCP packet has two bytes in the begining defines packet length
+	 * so leave room here to store packet size */
+	if (qd->use_tcp) {
+		bytes += qq_put16(buf + bytes, 0x0000);
+	}
+	/* now comes the normal QQ packet as UDP */
+	bytes += qq_put8(buf + bytes, QQ_PACKET_TAG);
+	bytes += qq_put16(buf + bytes, QQ_CLIENT);
+	bytes += qq_put16(buf + bytes, cmd);
+	
+	bytes += qq_put16(buf + bytes, seq);
+
+	bytes += qq_put32(buf + bytes, qd->uid);
+	bytes += qq_putdata(buf + bytes, data, data_len);
+	bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL);
+
+	/* set TCP packet length at begin of the packet */
+	if (qd->use_tcp) {
+		qq_put16(buf, bytes);
+	}
+
+	return bytes;
+}
+
+gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
+{
+	guint8 *buf;
+	gint buf_len;
+	gint bytes_sent;
+	gint seq;
+
+	g_return_val_if_fail(qd != NULL, -1);
+	g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
+	seq = ++(qd->send_seq);
+	buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len);
+	if (buf_len <= 0) {
+		return -1;
+	}
+
+	if (qd->use_tcp) {
+		bytes_sent = tcp_send_out(qd, buf, buf_len);
+	} else {
+		bytes_sent = udp_send_out(qd, buf, buf_len);
+	}
+
+	/* always need ack */
+	qq_send_trans_append(qd, buf, buf_len, cmd, seq);
+
+	if (QQ_DEBUG) {
+		qq_show_packet("QQ_SEND_DATA", buf, buf_len);
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				"<== [%05d], %s, total %d bytes is sent %d\n", 
+				seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
+	}
+	return bytes_sent;
+}
+
+/* send the packet generated with the given cmd and data
+ * return the number of bytes sent to socket if succeeds
+ * return -1 if there is any error */
+gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+	guint8 *data, gint data_len)
+{
+	guint8 *buf;
+	gint buf_len;
+	guint8 *encrypted_data;
+	gint encrypted_len;
+	gint bytes_sent;
+
+	g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1);
+	g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+	encrypted_len = data_len + 16;	/* at most 16 bytes more */
+	encrypted_data = g_newa(guint8, encrypted_len);
+
+	qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len);
+
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
+	buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, encrypted_data, encrypted_len);
+	if (buf_len <= 0) {
+		return -1;
+	}
+
+	if (QQ_DEBUG) {
+		qq_show_packet("QQ_SEND_CMD", buf, buf_len);
+	}
+	if (qd->use_tcp) {
+		bytes_sent = tcp_send_out(qd, buf, buf_len);
+	} else {
+		bytes_sent = udp_send_out(qd, buf, buf_len);
+	}
+	
+	/* if it does not need ACK, we send ACK manually several times */
+	if (need_ack)  {
+		qq_send_trans_append(qd, buf, buf_len, cmd, seq);
+	}
+
+	if (QQ_DEBUG) {
+		qq_show_packet("QQ_SEND_CMD", buf, buf_len);
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				"<== [%05d], %s, total %d bytes is sent %d\n", 
+				seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
+	}
+	return bytes_sent;
+}
+
+gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
+{
+	g_return_val_if_fail(qd != NULL, -1);
+	g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+	qd->send_seq++;
+	return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_network.h	Thu Jun 26 20:25:38 2008 +0000
@@ -0,0 +1,44 @@
+/**
+ * @file qq_network.h
+ *
+ * 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 _QQ_PROXY_H
+#define _QQ_PROXY_H
+
+#include <glib.h>
+#include "connection.h"
+
+#include "qq.h"
+
+#define QQ_CONNECT_STEPS    3	/* steps in connection */
+
+void qq_connect(PurpleAccount *account);
+void qq_disconnect(PurpleConnection *gc);
+void qq_connect_later(PurpleConnection *gc);
+
+gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+	guint8 *data, gint data_len);
+
+#endif
--- a/libpurple/protocols/qq/qq_proxy.c	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,529 +0,0 @@
-/**
- * @file qq_proxy.c
- *
- * 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 "cipher.h"
-#include "debug.h"
-#include "internal.h"
-
-#ifdef _WIN32
-#define random rand
-#define srandom srand
-#endif
-
-#include "packet_parse.h"
-#include "buddy_info.h"
-#include "buddy_opt.h"
-#include "char_conv.h"
-#include "group_free.h"
-#include "login_logout.h"
-#include "qq_proxy.h"
-#include "recv_core.h"
-#include "send_core.h"
-#include "sendqueue.h"
-#include "udp_proxy_s5.h"
-#include "utils.h"
-
-/* These functions are used only in development phase */
-/*
-static void _qq_show_socket(gchar *desc, gint fd) {
-	struct sockaddr_in sin;
-	socklen_t len = sizeof(sin);
-	getsockname(fd, (struct sockaddr *)&sin, &len);
-	purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n",
-            inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
-}
-*/
-
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
-{
-	char buf1[8*len+2], buf2[10];
-	int i;
-	buf1[0] = 0;
-	for (i = 0; i < len; i++) {
-		sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff);
-		strcat(buf1, buf2);
-	}
-	strcat(buf1, "\n");
-	purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1);
-}
-
-/* QQ 2003iii uses double MD5 for the pwkey to get the session key */
-static guint8 *_gen_pwkey(const gchar *pwd)
-{
-        PurpleCipher *cipher;
-        PurpleCipherContext *context;
-
-	guchar pwkey_tmp[QQ_KEY_LENGTH];
-
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd));
-	purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
-	purple_cipher_context_destroy(context);
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH);
-	purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
-	purple_cipher_context_destroy(context);
-
-	return g_memdup(pwkey_tmp, QQ_KEY_LENGTH);
-}
-
-static gboolean _qq_fill_host(GSList *hosts, struct sockaddr_in *addr, gint *addr_size)
-{
-	if (!hosts || !hosts->data)
-		return FALSE;
-
-	*addr_size = GPOINTER_TO_INT(hosts->data);
-
-	hosts = g_slist_remove(hosts, hosts->data);
-	memcpy(addr, hosts->data, *addr_size);
-	g_free(hosts->data);
-	hosts = g_slist_remove(hosts, hosts->data);
-	while(hosts) {
-		hosts = g_slist_remove(hosts, hosts->data);
-		g_free(hosts->data);
-		hosts = g_slist_remove(hosts, hosts->data);
-	}
-
-	return TRUE;
-}
-
-/* set up any finalizing start-up stuff */
-static void _qq_start_services(PurpleConnection *gc)
-{
-	/* start watching for IMs about to be sent */
-	/*
-	purple_signal_connect(purple_conversations_get_handle(),
-			"sending-im-msg", gc,
-			PURPLE_CALLBACK(qq_sending_im_msg_cb), NULL);
-			*/
-}
-
-/* the callback function after socket is built
- * we setup the qq protocol related configuration here */
-static void _qq_got_login(gpointer data, gint source, const gchar *error_message)
-{
-	qq_data *qd;
-	PurpleConnection *gc;
-	gchar *buf;
-	const gchar *passwd;
-
-	gc = (PurpleConnection *) data;
-
-	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
-		close(source);
-		return;
-	}
-
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-
-	if (source < 0) {	/* socket returns -1 */
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
-		return;
-	}
-
-	qd = (qq_data *) gc->proto_data;
-
-	/*
-	_qq_show_socket("Got login socket", source);
-	*/
-
-	/* QQ use random seq, to minimize duplicated packets */
-	srandom(time(NULL));
-	qd->send_seq = random() & 0x0000ffff;
-	qd->fd = source;
-	qd->logged_in = FALSE;
-	qd->channel = 1;
-	qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
-
-	/* now generate md5 processed passwd */
-	passwd = purple_account_get_password(purple_connection_get_account(gc));
-	qd->pwkey = _gen_pwkey(passwd);
-
-	qd->sendqueue_timeout = purple_timeout_add(QQ_SENDQUEUE_TIMEOUT, qq_sendqueue_timeout_callback, gc);
-	gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, qq_input_pending, gc);
-
-	/* Update the login progress status display */
-	buf = g_strdup_printf("Login as %d", qd->uid);
-	purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS);
-	g_free(buf);
-
-	_qq_start_services(gc);
-
-	qq_send_packet_request_login_token(gc);
-}
-
-/* clean up qq_data structure and all its components
- * always used before a redirectly connection */
-static void _qq_common_clean(PurpleConnection *gc)
-{
-	qq_data *qd;
-
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	/* finish  all I/O */
-	if (qd->fd >= 0 && qd->logged_in)
-		qq_send_packet_logout(gc);
-	close(qd->fd);
-
-	if (qd->sendqueue_timeout > 0) {
-		purple_timeout_remove(qd->sendqueue_timeout);
-		qd->sendqueue_timeout = 0;
-	}
-
-	if (gc->inpa > 0) {
-		purple_input_remove(gc->inpa);
-		gc->inpa = 0;
-	}
-
-	qq_b4_packets_free(qd);
-	qq_sendqueue_free(qd);
-	qq_group_packets_free(qd);
-	qq_group_free_all(qd);
-	qq_add_buddy_request_free(qd);
-	qq_info_query_free(qd);
-	qq_buddies_list_free(gc->account, qd);
-}
-
-static void no_one_calls(gpointer data, gint source, PurpleInputCondition cond)
-{
-        struct PHB *phb = data;
-	socklen_t len;
-	int error=0, ret;
-
-	purple_debug_info("proxy", "Connected.\n");
-
-	len = sizeof(error);
-
-	/*
-	* getsockopt after a non-blocking connect returns -1 if something is
-	* really messed up (bad descriptor, usually). Otherwise, it returns 0 and
-	* error holds what connect would have returned if it blocked until now.
-	* Thus, error == 0 is success, error == EINPROGRESS means "try again",
-	* and anything else is a real error.
-	*
-	* (error == EINPROGRESS can happen after a select because the kernel can
-	* be overly optimistic sometimes. select is just a hint that you might be
-	* able to do something.)
-	*/
-	ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
-	if (ret == 0 && error == EINPROGRESS)
-		return; /* we'll be called again later */
-	if (ret < 0 || error != 0) {
-		if(ret!=0) 
-			error = errno;
-		close(source);
-		purple_input_remove(phb->inpa);
-
-		purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error));
-
-		phb->func(phb->data, -1, _("Unable to connect"));
-		return;
-	}
-
-	purple_input_remove(phb->inpa);
-
-	if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-		phb->func(phb->data, source, NULL);
-	}
-
-	g_free(phb->host);
-	g_free(phb);
-}
-
-/* returns -1 if fails, otherwise returns the file handle */
-static gint _qq_proxy_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
-{
-	gint fd = -1;
-	int flags;
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Using UDP without proxy\n");
-	fd = socket(PF_INET, SOCK_DGRAM, 0);
-
-	if (fd < 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ Redirect", 
-			"Unable to create socket: %s\n", g_strerror(errno));
-		return -1;
-	}
-
-	/* we use non-blocking mode to speed up connection */
-	flags = fcntl(fd, F_GETFL);
-	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-
-	/* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
-	 *
-	 * If a UDP socket is unconnected, which is the normal state after a
-	 * bind() call, then send() or write() are not allowed, since no
-	 * destination is available; only sendto() can be used to send data.
-	 *   
-	 * Calling connect() on the socket simply records the specified address
-	 * and port number as being the desired communications partner. That
-	 * means that send() or write() are now allowed; they use the destination
-	 * address and port given on the connect call as the destination of packets.
-	 */
-	if (connect(fd, addr, addrlen) < 0) {
-		/* [EINPROGRESS]
-		 *    The socket is marked as non-blocking and the connection cannot be 
-		 *    completed immediately. It is possible to select for completion by 
-		 *    selecting the socket for writing.
-		 * [EINTR]
-		 *    A signal interrupted the call. 
-		 *    The connection is established asynchronously.
-		 */
-		if ((errno == EINPROGRESS) || (errno == EINTR)) {
-			purple_debug_warning("QQ", "Connect in asynchronous mode.\n");
-			phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, no_one_calls, phb);
-		} else {
-			purple_debug_error("QQ", "Connection failed: %s\n", g_strerror(errno));
-			close(fd);
-			return -1;
-		}		/* if errno */
-	} else {		/* connect returns 0 */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n");
-		flags = fcntl(fd, F_GETFL);
-		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
-		phb->func(phb->data, fd, NULL);
-	}
-
-	return fd;
-}
-
-static void _qq_proxy_resolved(GSList *hosts, gpointer data, const char *error_message)
-{
-	struct PHB *phb = (struct PHB *) data;
-	struct sockaddr_in addr;
-	gint addr_size, ret = -1;
-
-	if(_qq_fill_host(hosts, &addr, &addr_size))
-		ret = qq_proxy_socks5(phb, (struct sockaddr *) &addr, addr_size);
-
-	if (ret < 0) {
-		phb->func(phb->data, -1, _("Unable to connect"));
-		g_free(phb->host);
-		g_free(phb);
-	}
-}
-
-static void _qq_server_resolved(GSList *hosts, gpointer data, const char *error_message)
-{
-	struct PHB *phb = (struct PHB *) data;
-	PurpleConnection *gc = (PurpleConnection *) phb->data;
-	qq_data *qd = (qq_data *) gc->proto_data;
-	struct sockaddr_in addr;
-	gint addr_size, ret = -1;
-
-	if(_qq_fill_host(hosts, &addr, &addr_size)) {
-		switch (purple_proxy_info_get_type(phb->gpi)) {
-			case PURPLE_PROXY_NONE:
-				ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size);
-				break;
-			case PURPLE_PROXY_SOCKS5:
-				ret = 0;
-				if (purple_proxy_info_get_host(phb->gpi) == NULL || 
-						purple_proxy_info_get_port(phb->gpi) == 0) {
-					purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-							"Use of socks5 proxy selected but host or port info doesn't exist.\n");
-					ret = -1;
-				} else {
-					/* as the destination is always QQ server during the session, 
-				 	* we can set dest_sin here, instead of _qq_s5_canread_again */
-					memcpy(&qd->dest_sin, &addr, addr_size);
-					if (purple_dnsquery_a(purple_proxy_info_get_host(phb->gpi),
-							purple_proxy_info_get_port(phb->gpi),
-							_qq_proxy_resolved, phb) == NULL)
-						ret = -1;
-				}
-				break;
-			default:
-				purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-						"Proxy type %i is unsupported, not using a proxy.\n",
-						purple_proxy_info_get_type(phb->gpi));
-				ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size);
-		}
-	}
-
-	if (ret < 0) {
-		phb->func(gc, -1, _("Unable to connect"));
-		g_free(phb->host);
-		g_free(phb);
-	}
-}
-
-/* returns -1 if dns lookup fails, otherwise returns 0 */
-static gint _qq_udp_proxy_connect(PurpleAccount *account,
-			   const gchar *server, guint16 port, 
-			   void callback(gpointer, gint, const gchar *error_message), 
-			   PurpleConnection *gc)
-{
-	PurpleProxyInfo *info;
-	struct PHB *phb;
-	qq_data *qd = (qq_data *) gc->proto_data;
-
-	g_return_val_if_fail(gc != NULL && qd != NULL, -1);
-
-	info = purple_proxy_get_setup(account);
-
-	phb = g_new0(struct PHB, 1);
-	phb->host = g_strdup(server);
-	phb->port = port;
-	phb->account = account;
-	phb->gpi = info;
-	phb->func = callback;
-	phb->data = gc;
-	qd->proxy_type = purple_proxy_info_get_type(phb->gpi);
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Choosing proxy type %d\n", 
-			purple_proxy_info_get_type(phb->gpi));
-
-	if (purple_dnsquery_a(server, port, _qq_server_resolved, phb) == NULL) {
-		phb->func(gc, -1, _("Unable to connect"));
-		g_free(phb->host);
-		g_free(phb);
-		return -1;
-	} else {
-		return 0;
-	}
-}
-
-/* QQ connection via UDP/TCP. 
- * I use Purple proxy function to provide TCP proxy support,
- * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */
-static gint _proxy_connect_full (PurpleAccount *account, const gchar *host, guint16 port, 
-		PurpleProxyConnectFunction func, gpointer data, gboolean use_tcp)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-
-	gc = purple_account_get_connection(account);
-	qd = (qq_data *) gc->proto_data;
-	qd->server_ip = g_strdup(host);
-	qd->server_port = port;
-
-	if(use_tcp)
-		return (purple_proxy_connect(NULL, account, host, port, func, data) == NULL);
-	else
-		return _qq_udp_proxy_connect(account, host, port, func, data);
-}
-
-/* establish a generic QQ connection 
- * TCP/UDP, and direct/redirected */
-gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, 
-		gboolean use_tcp, gboolean is_redirect)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-
-	g_return_val_if_fail(host != NULL, -1);
-	g_return_val_if_fail(port > 0, -1);
-
-	gc = purple_account_get_connection(account);
-	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
-
-	if (is_redirect)
-		_qq_common_clean(gc);
-
-	qd = (qq_data *) gc->proto_data;
-	qd->before_login_packets = g_queue_new();
-
-	return _proxy_connect_full(account, host, port, _qq_got_login, gc, use_tcp);
-}
-
-/* clean up the given QQ connection and free all resources */
-void qq_disconnect(PurpleConnection *gc)
-{
-	qq_data *qd;
-
-	g_return_if_fail(gc != NULL);
-
-	_qq_common_clean(gc);
-
-	qd = gc->proto_data;
-	g_free(qd->inikey);
-	g_free(qd->pwkey);
-	g_free(qd->session_key);
-	g_free(qd->session_md5);
-	g_free(qd->my_ip);
-	g_free(qd);
-
-	gc->proto_data = NULL;
-}
-
-/* send packet with proxy support */
-gint qq_proxy_write(qq_data *qd, guint8 *data, gint len)
-{
-	guint8 *buf;
-	gint ret;
-
-	g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && len > 0, -1);
-
-	/* TCP sock5 may be processed twice
-	 * so we need to check qd->use_tcp as well */
-	if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) {	/* UDP sock5 */
-		buf = g_newa(guint8, len + 10);
-		buf[0] = 0x00;
-		buf[1] = 0x00;	/* reserved */
-		buf[2] = 0x00;	/* frag */
-		buf[3] = 0x01;	/* type */
-		g_memmove(buf + 4, &(qd->dest_sin.sin_addr.s_addr), 4);
-		g_memmove(buf + 8, &(qd->dest_sin.sin_port), 2);
-		g_memmove(buf + 10, data, len);
-		errno = 0;
-		ret = send(qd->fd, buf, len + 10, 0);
-	} else {
-		errno = 0;
-		ret = send(qd->fd, data, len, 0);
-	}
-	if (ret == -1)
-		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
-
-	return ret;
-}
-
-/* read packet input with proxy support */
-gint qq_proxy_read(qq_data *qd, guint8 *data, gint len)
-{
-	guint8 *buf;
-	gint bytes;
-	buf = g_newa(guint8, MAX_PACKET_SIZE + 10);
-
-	g_return_val_if_fail(qd != NULL && data != NULL && len > 0, -1);
-	g_return_val_if_fail(qd->fd > 0, -1);
-
-	bytes = read(qd->fd, buf, len + 10);
-	if (bytes < 0)
-		return -1;
-
-	if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) {	/* UDP sock5 */
-		if (bytes < 10)
-			return -1;
-		bytes -= 10;
-		g_memmove(data, buf + 10, bytes);	/* cut off the header */
-	} else {
-		g_memmove(data, buf, bytes);
-	}
-
-	return bytes;
-}
--- a/libpurple/protocols/qq/qq_proxy.h	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * @file qq_proxy.h
- *
- * 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 _QQ_PROXY_H
-#define _QQ_PROXY_H
-
-#include <glib.h>
-#include "dnsquery.h"
-#include "proxy.h"
-
-#include "qq.h"
-
-#define QQ_CONNECT_STEPS    2	/* steps in connection */
-
-struct PHB {
-	PurpleProxyConnectFunction func;
-	gpointer data;
-	gchar *host;
-	gint port;
-	gint inpa;
-	PurpleProxyInfo *gpi;
-	PurpleAccount *account;
-	gint udpsock;
-	gpointer sockbuf;
-};
-
-gint qq_proxy_read(qq_data *qd, guint8 *data, gint len);
-gint qq_proxy_write(qq_data *qd, guint8 *data, gint len);
-
-gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, gboolean use_tcp, gboolean is_redirect);
-void qq_disconnect(PurpleConnection *gc);
-
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_trans.c	Thu Jun 26 20:25:38 2008 +0000
@@ -0,0 +1,246 @@
+/**
+ * @file qq_trans.c
+ *
+ * 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 "connection.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "request.h"
+
+#include "header_info.h"
+#include "qq_network.h"
+#include "qq_trans.h"
+
+#define QQ_RESEND_MAX               8	/* max resend per packet */
+
+typedef struct _transaction {
+	guint16 seq;
+	guint16 cmd;
+	guint8 *buf;
+	gint buf_len;
+
+	gint fd;
+	gint retries;
+	time_t create_time;
+} transaction;
+
+void qq_send_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+{
+	transaction *trans = g_new0(transaction, 1);
+
+	g_return_if_fail(trans != NULL);
+
+	trans->fd = qd->fd;
+	trans->cmd = cmd;
+	trans->seq = seq;
+	trans->retries = QQ_RESEND_MAX;
+	trans->create_time = time(NULL);
+	trans->buf = g_memdup(buf, buf_len);	/* don't use g_strdup, may have 0x00 */
+	trans->buf_len = buf_len;
+
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+			"Add to transaction, seq = %d, buf = %p, len = %d\n",
+			trans->seq, trans->buf, trans->buf_len);
+	qd->send_trans = g_list_append(qd->send_trans, trans);
+}
+
+/* Remove a packet with seq from send trans */
+void qq_send_trans_remove(qq_data *qd, gpointer data) 
+{
+	transaction *trans = (transaction *)data;
+
+	g_return_if_fail(qd != NULL && data != NULL);
+	
+	purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				"ack [%05d] %s, remove from send tranactions\n",
+				trans->seq, qq_get_cmd_desc(trans->cmd));
+
+	if (trans->buf)	g_free(trans->buf);
+	qd->send_trans = g_list_remove(qd->send_trans, trans);
+	g_free(trans);
+}
+
+gpointer qq_send_trans_find(qq_data *qd, guint16 seq)
+{
+	GList *curr;
+	GList *next;
+	transaction *trans;
+
+	curr = qd->send_trans;
+	while(curr) {
+		next = curr->next;
+		trans = (transaction *) (curr->data);
+		if(trans->seq == seq) {
+			return trans;
+		}
+		curr = next;
+	}
+
+	return NULL;
+}
+
+/* clean up send trans and free all contents */
+void qq_send_trans_remove_all(qq_data *qd)
+{
+	GList *curr;
+	GList *next;
+	transaction *trans;
+	gint count = 0;
+
+	curr = qd->send_trans;
+	while(curr) {
+		next = curr->next;
+		
+		trans = (transaction *) (curr->data);
+		/*
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+			"Remove to transaction, seq = %d, buf = %p, len = %d\n",
+			trans->seq, trans->buf, trans->len);
+		*/
+		qq_send_trans_remove(qd, trans);
+
+		count++;
+		curr = next;
+	}
+	g_list_free(qd->send_trans);
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in send tranactions are freed!\n", count);
+}
+
+gint qq_send_trans_scan(qq_data *qd, gint *start,
+	guint8 *buf, gint maxlen, guint16 *cmd, gint *retries)
+{
+	GList *curr;
+	GList *next = NULL;
+	transaction *trans;
+	gint copylen;
+
+	g_return_val_if_fail(qd != NULL && *start >= 0 && maxlen > 0, -1);
+	
+	/* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Scan from %d\n", *start); */
+	curr = g_list_nth(qd->send_trans, *start);
+	while(curr) {
+		next = curr->next;
+		*start = g_list_position(qd->send_trans, next);
+		
+		trans = (transaction *) (curr->data);
+		if (trans->buf == NULL || trans->buf_len <= 0) {
+			qq_send_trans_remove(qd, trans);
+			curr = next;
+			continue;
+		}
+
+		if (trans->retries < 0) {
+			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+				"Remove transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
+				trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
+			qq_send_trans_remove(qd, trans);
+			curr = next;
+			continue;
+		}
+
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+				"Resend transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
+				trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
+		copylen = MIN(trans->buf_len, maxlen);
+		g_memmove(buf, trans->buf, copylen);
+
+		*cmd = trans->cmd;
+		*retries = trans->retries;
+		trans->retries--;
+		return copylen;
+	}
+
+	/* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); */
+	return -1;
+}
+
+void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+	transaction *trans = g_new0(transaction, 1);
+
+	g_return_if_fail(data != NULL && data_len > 0);
+	g_return_if_fail(trans != NULL);
+
+	trans->cmd = cmd;
+	trans->seq = seq;
+	trans->buf = g_memdup(data, data_len);
+	trans->buf_len = data_len;
+	trans->create_time = time(NULL);
+
+	if (qd->rcv_trans == NULL)
+		qd->rcv_trans = g_queue_new();
+
+	g_queue_push_head(qd->rcv_trans, trans);
+}
+
+gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len)
+{
+	transaction *trans = NULL;
+	gint copy_len;
+
+	g_return_val_if_fail(data != NULL && max_len > 0, -1);
+
+	if (g_queue_is_empty(qd->rcv_trans)) {
+		return -1;
+	}
+	trans = (transaction *) g_queue_pop_head(qd->rcv_trans);
+	if (trans == NULL) {
+		return 0;
+	}
+	if (trans->buf == NULL || trans->buf_len <= 0) {
+		return 0;
+	}
+
+	copy_len = MIN(max_len, trans->buf_len);
+	g_memmove(data, trans->buf, copy_len);
+	*cmd = trans->cmd;
+	*seq = trans->seq;
+
+	g_free(trans->buf);
+	g_free(trans);
+	return copy_len;
+}
+
+/* clean up the packets before login */
+void qq_rcv_trans_remove_all(qq_data *qd)
+{
+	transaction *trans = NULL;
+	gint count = 0;
+
+	g_return_if_fail(qd != NULL);
+
+	/* now clean up my own data structures */
+	if (qd->rcv_trans != NULL) {
+		while (NULL != (trans = g_queue_pop_tail(qd->rcv_trans))) {
+			g_free(trans->buf);
+			g_free(trans);
+			count++;
+		}
+		g_queue_free(qd->rcv_trans);
+	}
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in receive tranactions are freed!\n", count);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_trans.h	Thu Jun 26 20:25:38 2008 +0000
@@ -0,0 +1,42 @@
+/**
+ * @file qq_trans.h
+ *
+ * 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 _QQ_SEND_QUEUE_H_
+#define _QQ_SEND_QUEUE_H_
+
+#include <glib.h>
+#include "qq.h"
+
+void qq_send_trans_append(qq_data *qd, guint8 *buf, gint bus_len, guint16 cmd, guint16 seq);
+void qq_send_trans_remove(qq_data *qd, gpointer data);
+gpointer qq_send_trans_find(qq_data *qd, guint16 seq);
+void qq_send_trans_remove_all(qq_data *qd);
+
+gint qq_send_trans_scan(qq_data *qd, gint *start, guint8 *buf, gint maxlen, guint16 *cmd, gint *retries);
+
+void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
+gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16* seq, guint8 *data, gint max_len);
+void qq_rcv_trans_remove_all(qq_data *qd);
+
+#endif
--- a/libpurple/protocols/qq/recv_core.c	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,326 +0,0 @@
-/**
- * @file recv_core.c
- *
- * 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 "debug.h"
-#include "internal.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_opt.h"
-#include "buddy_status.h"
-#include "char_conv.h"
-#include "crypt.h"
-#include "group_network.h"
-#include "header_info.h"
-#include "keep_alive.h"
-#include "im.h"
-#include "login_logout.h"
-#include "packet_parse.h"
-#include "qq_proxy.h"
-#include "recv_core.h"
-#include "sendqueue.h"
-#include "sys_msg.h"
-#include "utils.h"
-
-typedef struct _packet_before_login packet_before_login;
-typedef struct _qq_recv_msg_header qq_recv_msg_header;
-
-struct _packet_before_login {
-	guint8 *buf;
-	gint len;
-};
-
-struct _qq_recv_msg_header {
-	guint8 header_tag;
-	guint16 source_tag;
-	guint16 cmd;
-	guint16 seq;		/* can be ack_seq or send_seq, depends on cmd */
-};
-
-/* check whether one sequence number is duplicated or not
- * return TRUE if it is duplicated, otherwise FALSE */
-static gboolean _qq_check_packet_set_window(guint16 seq, PurpleConnection *gc)
-{
-	qq_data *qd;
-	guint8 *byte, mask;
-
-	qd = (qq_data *) gc->proto_data;
-	byte = &(qd->window[seq / 8]);
-	mask = (1 << (seq % 8));
-
-	if ((*byte) & mask)
-		return TRUE;	/* check mask */
-	(*byte) |= mask;
-	return FALSE;		/* set mask */
-}
-
-/* default process, decrypt and dump */
-static void _qq_process_packet_default(guint8 *buf, gint buf_len, guint16 cmd, guint16 seq, PurpleConnection *gc)
-{
-	qq_data *qd;
-	guint8 *data;
-	gchar *msg_utf8;
-	gint len;
-
-	g_return_if_fail(buf != NULL && buf_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-	msg_utf8 = NULL;
-
-	_qq_show_packet("Processing unknown packet", buf, len);
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		gchar *hex_dump = hex_dump_to_str(data, len);
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   ">>> [%d] %s, %d bytes -> [default] decrypt and dump\n%s",
-			   seq, qq_get_cmd_desc(cmd), buf_len, hex_dump);
-		g_free(hex_dump);
-		try_dump_as_gbk(data, len);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
-	}
-}
-
-/* process the incoming packet from qq_pending */
-static void _qq_packet_process(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
-	qq_data *qd;
-	gint len, bytes_expected, bytes_read;
-	guint16 buf_len_read;	/* two bytes in the begining of TCP packet */
-	guint8 *cursor;
-	qq_recv_msg_header header;
-	packet_before_login *b4_packet;
-
-	g_return_if_fail(buf != NULL && buf_len > 0);
-
-	qd = (qq_data *) gc->proto_data;
-	bytes_expected = qd->use_tcp ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH;
-
-	if (buf_len < bytes_expected) {
-		gchar *hex_dump = hex_dump_to_str(buf, buf_len);
-		purple_debug(PURPLE_DEBUG_ERROR,
-			   "QQ", "Received packet is too short, dump and drop\n%s", hex_dump);
-		g_free(hex_dump);
-		return;
-	}
-	/* initialize */
-	cursor = buf;
-	bytes_read = 0;
-
-	/* QQ TCP packet returns first 2 bytes the length of this packet */
-	if (qd->use_tcp) {
-		bytes_read += read_packet_w(buf, &cursor, buf_len, &buf_len_read);
-		if (buf_len_read != buf_len) {	/* wrong */
-			purple_debug
-			    (PURPLE_DEBUG_ERROR,
-			     "QQ",
-			     "TCP read %d bytes, header says %d bytes, use header anyway\n", buf_len, buf_len_read);
-			buf_len = buf_len_read;	/* we believe header is more accurate */
-		}
-	}
-
-	/* now goes the normal QQ packet as UDP packet */
-	bytes_read += read_packet_b(buf, &cursor, buf_len, &header.header_tag);
-	bytes_read += read_packet_w(buf, &cursor, buf_len, &header.source_tag);
-	bytes_read += read_packet_w(buf, &cursor, buf_len, &header.cmd);
-	bytes_read += read_packet_w(buf, &cursor, buf_len, &header.seq);
-
-	if (bytes_read != bytes_expected) {	/* read error */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail reading packet header, expect %d bytes, read %d bytes\n", 
-			   bytes_expected, bytes_read);
-		return;
-	}
-
-	if ((buf[buf_len - 1] != QQ_PACKET_TAIL) || (header.header_tag != QQ_PACKET_TAG)) {
-		gchar *hex_dump = hex_dump_to_str(buf, buf_len);
-		purple_debug(PURPLE_DEBUG_ERROR,
-			   "QQ", "Unknown QQ proctocol, dump and drop\n%s", hex_dump);
-		g_free(hex_dump);
-		return;
-	}
-
-	if (QQ_DEBUG)
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-			   "==> [%05d] %s, from (%s)\n",
-			   header.seq, qq_get_cmd_desc(header.cmd), qq_get_source_str(header.source_tag));
-
-	if (header.cmd != QQ_CMD_LOGIN && header.cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) {
-		if (!qd->logged_in) {	/* packets before login */
-			b4_packet = g_new0(packet_before_login, 1);
-			/* must duplicate, buffer will be freed after exiting this function */
-			b4_packet->buf = g_memdup(buf, buf_len);
-			b4_packet->len = buf_len;
-			if (qd->before_login_packets == NULL)
-				qd->before_login_packets = g_queue_new();
-			g_queue_push_head(qd->before_login_packets, b4_packet);
-			return;	/* do not process it now */
-		} else if (!g_queue_is_empty(qd->before_login_packets)) {
-			/* logged_in, but we have packets before login */
-			b4_packet = (packet_before_login *)
-			    g_queue_pop_head(qd->before_login_packets);
-			_qq_packet_process(b4_packet->buf, b4_packet->len, gc);
-			/* in fact this is a recursive call,  
-			 * all packets before login will be processed before goes on */
-			g_free(b4_packet->buf);	/* the buf is duplicated, need to be freed */
-			g_free(b4_packet);
-		}
-	}
-
-	/* this is the length of all the encrypted data (also remove tail tag */
-	len = buf_len - (bytes_read) - 1;
-
-	/* whether it is an ack */
-	switch (header.cmd) {
-	case QQ_CMD_RECV_IM:
-	case QQ_CMD_RECV_MSG_SYS:
-	case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
-		/* server intiated packet, we need to send ack and check duplicaion 
-		 * this must be put after processing b4_packet
-		 * as these packets will be passed in twice */
-		if (_qq_check_packet_set_window(header.seq, gc)) {
-			purple_debug(PURPLE_DEBUG_WARNING,
-				   "QQ", "dup [%05d] %s, discard...\n", header.seq, qq_get_cmd_desc(header.cmd));
-			return;
-		}
-		break;
-	default:{	/* ack packet, we need to update sendqueue */
-			/* we do not check duplication for server ack */
-			qq_sendqueue_remove(qd, header.seq);
-			if (QQ_DEBUG)
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "ack [%05d] %s, remove from sendqueue\n",
-					   header.seq, qq_get_cmd_desc(header.cmd));
-		}
-	}
-
-	/* now process the packet */
-	switch (header.cmd) {
-	case QQ_CMD_KEEP_ALIVE:
-		qq_process_keep_alive_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_UPDATE_INFO:
-		qq_process_modify_info_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_ADD_FRIEND_WO_AUTH:
-		qq_process_add_buddy_reply(cursor, len, header.seq, gc);
-		break;
-	case QQ_CMD_DEL_FRIEND:
-		qq_process_remove_buddy_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_REMOVE_SELF:
-		qq_process_remove_self_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_BUDDY_AUTH:
-		qq_process_add_buddy_auth_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GET_USER_INFO:
-		qq_process_get_info_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_CHANGE_ONLINE_STATUS:
-		qq_process_change_status_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_SEND_IM:
-		qq_process_send_im_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_RECV_IM:
-		qq_process_recv_im(cursor, len, header.seq, gc);
-		break;
-	case QQ_CMD_LOGIN:
-		qq_process_login_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GET_FRIENDS_LIST:
-		qq_process_get_buddies_list_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GET_FRIENDS_ONLINE:
-		qq_process_get_buddies_online_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GROUP_CMD:
-		qq_process_group_cmd_reply(cursor, len, header.seq, gc);
-		break;
-	case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
-		qq_process_get_all_list_with_group_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GET_LEVEL:
-		qq_process_get_level_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_REQUEST_LOGIN_TOKEN:
-		qq_process_request_login_token_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_RECV_MSG_SYS:
-		qq_process_msg_sys(cursor, len, header.seq, gc);
-		break;
-	case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
-		qq_process_friend_change_status(cursor, len, gc);
-		break;
-	default:
-		_qq_process_packet_default(cursor, len, header.cmd, header.seq, gc);
-		break;
-	}
-}
-
-/* clean up the packets before login */
-void qq_b4_packets_free(qq_data *qd)
-{
-	packet_before_login *b4_packet;
-	g_return_if_fail(qd != NULL);
-	/* now clean up my own data structures */
-	if (qd->before_login_packets != NULL) {
-		while (NULL != (b4_packet = g_queue_pop_tail(qd->before_login_packets))) {
-			g_free(b4_packet->buf);
-			g_free(b4_packet);
-		}
-		g_queue_free(qd->before_login_packets);
-	}
-}
-
-void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-	guint8 *buf;
-	gint len;
-
-	gc = (PurpleConnection *) data;
-
-	if(cond != PURPLE_INPUT_READ) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Socket error"));
-		return;
-	}
-
-	qd = (qq_data *) gc->proto_data;
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
-
-	/* here we have UDP proxy suppport */
-	len = qq_proxy_read(qd, buf, MAX_PACKET_SIZE);
-	if (len <= 0) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Unable to read from socket"));
-		return;
-	} else {
-		_qq_packet_process(buf, len, gc);
-	}
-}
--- a/libpurple/protocols/qq/recv_core.h	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/**
- * @file recv_core.h
- *
- * 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 _QQ_RECV_CORE_H_
-#define _QQ_RECV_CORE_H_
-
-#include <glib.h>
-#include "connection.h"
-#include "qq.h"
-
-void qq_b4_packets_free(qq_data *qd);
-
-void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond);
-
-#endif
--- a/libpurple/protocols/qq/send_core.c	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-/**
- * @file send_core.c
- *
- * 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 "debug.h"
-#include "internal.h"
-
-#include "crypt.h"
-#include "header_info.h"
-#include "packet_parse.h"
-#include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
-#include "sendqueue.h"
-
-/* create qq packet header with given sequence
- * return the number of bytes in header if succeeds
- * return -1 if there is any error */
-gint _create_packet_head_seq(guint8 *buf, guint8 **cursor,
-			     PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq)
-{
-	qq_data *qd;
-	gint bytes_expected, bytes_written;
-
-	g_return_val_if_fail(buf != NULL && cursor != NULL && *cursor != NULL, -1);
-
-	qd = (qq_data *) gc->proto_data;
-	if (is_auto_seq)
-		*seq = ++(qd->send_seq);
-
-	*cursor = buf;
-	bytes_written = 0;
-	bytes_expected = (qd->use_tcp) ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH;
-
-	/* QQ TCP packet has two bytes in the begining defines packet length
-	 * so I leave room here for size */
-	if (qd->use_tcp)
-		bytes_written += create_packet_w(buf, cursor, 0x0000);
-
-	/* now comes the normal QQ packet as UDP */
-	bytes_written += create_packet_b(buf, cursor, QQ_PACKET_TAG);
-	bytes_written += create_packet_w(buf, cursor, QQ_CLIENT);
-	bytes_written += create_packet_w(buf, cursor, cmd);
-	bytes_written += create_packet_w(buf, cursor, *seq);
-
-	if (bytes_written != bytes_expected) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create qq header, expect %d bytes, written %d bytes\n", bytes_expected, bytes_written);
-		bytes_written = -1;
-	}
-	return bytes_written;
-}
-
-/* for those need ack and resend no ack feed back from server
- * return number of bytes written to the socket,
- * return -1 if there is any error */
-gint _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd)
-{
-	qq_data *qd;
-	qq_sendpacket *p;
-	gint bytes_sent;
-	guint8 *cursor;
-
-	qd = (qq_data *) gc->proto_data;
-
-	if (qd->use_tcp) {
-		if (len > MAX_PACKET_SIZE) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				   "xxx [%05d] %s, %d bytes is too large, do not send\n",
-				   qd->send_seq, qq_get_cmd_desc(cmd), len);
-			return -1;
-		} else {	/* I update the len for TCP packet */
-			cursor = buf;
-			create_packet_w(buf, &cursor, len);
-		}
-	}
-
-	bytes_sent = qq_proxy_write(qd, buf, len);
-
-	if (bytes_sent >= 0) {		/* put to queue, for matching server ACK usage */
-		p = g_new0(qq_sendpacket, 1);
-		p->fd = qd->fd;
-		p->cmd = cmd;
-		p->send_seq = qd->send_seq;
-		p->resend_times = 0;
-		p->sendtime = time(NULL);
-		p->buf = g_memdup(buf, len);	/* don't use g_strdup, may have 0x00 */
-		p->len = len;
-		qd->sendqueue = g_list_append(qd->sendqueue, p);
-	}
-
-	return bytes_sent;
-}
-
-/* send the packet generated with the given cmd and data
- * return the number of bytes sent to socket if succeeds
- * return -1 if there is any error */
-gint qq_send_cmd(PurpleConnection *gc, guint16 cmd,
-		 gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint len)
-{
-	qq_data *qd;
-	guint8 *buf, *cursor, *encrypted_data;
-	guint16 seq_ret;
-	gint encrypted_len, bytes_written, bytes_expected, bytes_sent;
-
-	qd = (qq_data *) gc->proto_data;
-	g_return_val_if_fail(qd->session_key != NULL, -1);
-
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
-	encrypted_len = len + 16;	/* at most 16 bytes more */
-	encrypted_data = g_newa(guint8, encrypted_len);
-	cursor = buf;
-	bytes_written = 0;
-
-	qq_encrypt(data, len, qd->session_key, encrypted_data, &encrypted_len);
-
-	seq_ret = seq;
-	if (_create_packet_head_seq(buf, &cursor, gc, cmd, is_auto_seq, &seq_ret) >= 0) {
-		bytes_expected = 4 + encrypted_len + 1;
-		bytes_written += create_packet_dw(buf, &cursor, (guint32) qd->uid);
-		bytes_written += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
-		bytes_written += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
-		if (bytes_written == bytes_expected) {	/* packet OK */
-			/* if it does not need ACK, we send ACK manually several times */
-			if (need_ack)   /* my request, send it */
-				bytes_sent = _qq_send_packet(gc, buf, cursor - buf, cmd);
-			else		/* server's request, send ACK */
-				bytes_sent = qq_proxy_write(qd, buf, cursor - buf);
-
-			if (QQ_DEBUG)
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "<== [%05d] %s, %d bytes\n", seq_ret, qq_get_cmd_desc(cmd), bytes_sent);
-			return bytes_sent;
-		} else {	/* bad packet */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				   "Fail creating packet, expect %d bytes, written %d bytes\n",
-				   bytes_expected, bytes_written);
-			return -1;
-		}
-	}
-
-	return -1;
-}
--- a/libpurple/protocols/qq/send_core.h	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/**
- * @file send_core.h
- *
- * 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 _QQ_SEND_CORE_H_
-#define _QQ_SEND_CORE_H_
-
-#include <glib.h>
-#include "connection.h"
-
-gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 seq, 
-		gboolean need_ack, guint8 *data, gint len);
-gint _qq_send_packet(PurpleConnection * gc, guint8 *buf, gint len, guint16 cmd);
-gint _create_packet_head_seq(guint8 *buf, guint8 **cursor,
-		PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq);
-
-#endif
--- a/libpurple/protocols/qq/send_file.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/send_file.c	Thu Jun 26 20:25:38 2008 +0000
@@ -36,7 +36,7 @@
 #include "im.h"
 #include "keep_alive.h"
 #include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 enum
@@ -103,6 +103,7 @@
 	return send(info->sender_fd, buf, len, 0);
 }
 */
+
 static ssize_t _qq_xfer_udp_send(const guint8 *buf, size_t len, PurpleXfer *xfer)
 {
 	struct sockaddr_in sin;
@@ -155,7 +156,7 @@
 	gint size;
 	/* FIXME: It seems that the transfer never use a packet
 	 * larger than 1500 bytes, so if it happened to be a
-	 * larger packet, either error occured or protocol should
+	 * larger packet, either error occurred or protocol should
 	 * be modified
 	 */
 	ft_info *info;
@@ -243,42 +244,45 @@
 	g_free(internet_ip_str);
 }
 
-void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info)
+#define QQ_CONN_INFO_LEN	61
+gint qq_get_conn_info(ft_info *info, guint8 *data)
 {
-	read_packet_data(data, cursor, data_len, info->file_session_key, 16);
-	*cursor += 30;
-	read_packet_b(data, cursor, data_len, &info->conn_method);
-	read_packet_dw(data, cursor, data_len, &info->remote_internet_ip);
-	read_packet_w(data, cursor, data_len, &info->remote_internet_port);
-	read_packet_w(data, cursor, data_len, &info->remote_major_port);
-	read_packet_dw(data, cursor, data_len, &info->remote_real_ip);
-	read_packet_w(data, cursor, data_len, &info->remote_minor_port);
+	gint bytes = 0;
+	/* 16 + 30 + 1 + 4 + 2 + 2 + 4 + 2 = 61 */
+	bytes += qq_getdata(info->file_session_key, 16, data + bytes);
+	bytes += 30;	/* skip 30 bytes */
+	bytes += qq_get8(&info->conn_method, data + bytes);
+	bytes += qq_get32(&info->remote_internet_ip, data + bytes);
+	bytes += qq_get16(&info->remote_internet_port, data + bytes);
+	bytes += qq_get16(&info->remote_major_port, data + bytes);
+	bytes += qq_get32(&info->remote_real_ip, data + bytes);
+	bytes += qq_get16(&info->remote_minor_port, data + bytes);
 	qq_show_conn_info(info);
+	return bytes;
 }
 
-gint qq_fill_conn_info(guint8 *raw_data, guint8 **cursor, ft_info *info)
+gint qq_fill_conn_info(guint8 *raw_data, ft_info *info)
 {
-	gint bytes;
-	bytes = 0;
+	gint bytes = 0;
 	/* 064: connection method, UDP 0x00, TCP 0x03 */
-	bytes += create_packet_b (raw_data, cursor, info->conn_method);
+	bytes += qq_put8 (raw_data + bytes, info->conn_method);
 	/* 065-068: outer ip address of sender (proxy address) */
-	bytes += create_packet_dw (raw_data, cursor, info->local_internet_ip);
+	bytes += qq_put32 (raw_data + bytes, info->local_internet_ip);
 	/* 069-070: sender port */
-	bytes += create_packet_w (raw_data, cursor, info->local_internet_port);
+	bytes += qq_put16 (raw_data + bytes, info->local_internet_port);
 	/* 071-072: the first listening port(TCP doesn't have this part) */
-	bytes += create_packet_w (raw_data, cursor, info->local_major_port);
+	bytes += qq_put16 (raw_data + bytes, info->local_major_port);
 	/* 073-076: real ip */
-	bytes += create_packet_dw (raw_data, cursor, info->local_real_ip);
+	bytes += qq_put32 (raw_data + bytes, info->local_real_ip);
 	/* 077-078: the second listening port */
-	bytes += create_packet_w (raw_data, cursor, info->local_minor_port);
+	bytes += qq_put16 (raw_data + bytes, info->local_minor_port);
 	return bytes;
 }
 
 
 /* fill in the common information of file transfer */
 static gint _qq_create_packet_file_header
-(guint8 *raw_data, guint8 **cursor, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack)
+(guint8 *raw_data, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack)
 {
 	gint bytes;
 	time_t now;
@@ -294,42 +298,42 @@
 	}
 
 	/* 000-003: receiver uid */
-	bytes += create_packet_dw (raw_data, cursor, qd->uid);
+	bytes += qq_put32 (raw_data + bytes, qd->uid);
 	/* 004-007: sender uid */
-	bytes += create_packet_dw (raw_data, cursor, to_uid);
+	bytes += qq_put32 (raw_data + bytes, to_uid);
 	/* 008-009: sender client version */
-	bytes += create_packet_w (raw_data, cursor, QQ_CLIENT);
+	bytes += qq_put16 (raw_data + bytes, QQ_CLIENT);
 	/* 010-013: receiver uid */
-	bytes += create_packet_dw (raw_data, cursor, qd->uid);
+	bytes += qq_put32 (raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
-	bytes += create_packet_dw (raw_data, cursor, to_uid);
+	bytes += qq_put32 (raw_data + bytes, to_uid);
 	/* 018-033: md5 of (uid+session_key) */
-	bytes += create_packet_data (raw_data, cursor, qd->session_md5, 16);
+	bytes += qq_putdata (raw_data + bytes, qd->session_md5, 16);
 	/* 034-035: message type */
-	bytes += create_packet_w (raw_data, cursor, message_type);
+	bytes += qq_put16 (raw_data + bytes, message_type);
 	/* 036-037: sequence number */
-	bytes += create_packet_w (raw_data, cursor, seq);
+	bytes += qq_put16 (raw_data + bytes, seq);
 	/* 038-041: send time */
-	bytes += create_packet_dw (raw_data, cursor, (guint32) now);
+	bytes += qq_put32 (raw_data + bytes, (guint32) now);
 	/* 042-042: always 0x00 */
-	bytes += create_packet_b (raw_data, cursor, 0x00);
+	bytes += qq_put8 (raw_data + bytes, 0x00);
 	/* 043-043: sender icon */
-	bytes += create_packet_b (raw_data, cursor, qd->my_icon);
+	bytes += qq_put8 (raw_data + bytes, qd->my_icon);
 	/* 044-046: always 0x00 */
-	bytes += create_packet_w (raw_data, cursor, 0x0000);
-	bytes += create_packet_b (raw_data, cursor, 0x00);
+	bytes += qq_put16 (raw_data + bytes, 0x0000);
+	bytes += qq_put8 (raw_data + bytes, 0x00);
 	/* 047-047: we use font attr */
-	bytes += create_packet_b (raw_data, cursor, 0x01);
+	bytes += qq_put8 (raw_data + bytes, 0x01);
 	/* 048-051: always 0x00 */
-	bytes += create_packet_dw (raw_data, cursor, 0x00000000);
+	bytes += qq_put32 (raw_data + bytes, 0x00000000);
 
 	/* 052-062: always 0x00 */
-	bytes += create_packet_dw (raw_data, cursor, 0x00000000);
-	bytes += create_packet_dw (raw_data, cursor, 0x00000000);
-	bytes += create_packet_w (raw_data, cursor, 0x0000);
-	bytes += create_packet_b (raw_data, cursor, 0x00);
+	bytes += qq_put32 (raw_data + bytes, 0x00000000);
+	bytes += qq_put32 (raw_data + bytes, 0x00000000);
+	bytes += qq_put16 (raw_data + bytes, 0x0000);
+	bytes += qq_put8 (raw_data + bytes, 0x00);
 	/* 063: transfer_type,  0x65: FILE 0x6b: FACE */
-	bytes += create_packet_b (raw_data, cursor, QQ_FILE_TRANSFER_FILE); /* FIXME */
+	bytes += qq_put8 (raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME */
 
 	return bytes;
 }
@@ -433,7 +437,7 @@
 static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid, gchar *filename, gint filesize)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	gchar *filelen_str;
 	gint filename_len, filelen_strlen, packet_len, bytes;
 	ft_info *info;
@@ -455,27 +459,24 @@
 
 	packet_len = 82 + filename_len + filelen_strlen;
 	raw_data = g_newa(guint8, packet_len);
-	cursor = raw_data;
+	bytes = 0;
 
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, 
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, 
 			QQ_FILE_TRANS_REQ, qd, FALSE);
-	bytes += qq_fill_conn_info(raw_data, &cursor, info);
+	bytes += qq_fill_conn_info(raw_data + bytes, info);
 	/* 079: 0x20 */
-	bytes += create_packet_b (raw_data, &cursor, 0x20);
+	bytes += qq_put8 (raw_data + bytes, 0x20);
 	/* 080: 0x1f */
-	bytes += create_packet_b (raw_data, &cursor, 0x1f);
+	bytes += qq_put8 (raw_data + bytes, 0x1f);
 	/* undetermined len: filename */
-	bytes += create_packet_data (raw_data, &cursor, (guint8 *) filename,
-				     filename_len);
+	bytes += qq_putdata (raw_data + bytes, (guint8 *) filename, filename_len);
 	/* 0x1f */
-	bytes += create_packet_b (raw_data, &cursor, 0x1f);
+	bytes += qq_put8 (raw_data + bytes, 0x1f);
 	/* file length */
-	bytes += create_packet_data (raw_data, &cursor, (guint8 *) filelen_str,
-				     filelen_strlen);
+	bytes += qq_putdata (raw_data + bytes, (guint8 *) filelen_str, filelen_strlen);
 
 	if (packet_len == bytes)
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_request",
 			    "%d bytes expected but got %d bytes\n",
@@ -488,7 +489,7 @@
 static void _qq_send_packet_file_accept(PurpleConnection *gc, guint32 to_uid)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	guint16 minor_port;
 	guint32 real_ip;
 	gint packet_len, bytes;
@@ -502,22 +503,21 @@
 
 	packet_len = 79;
 	raw_data = g_newa (guint8, packet_len);
-	cursor = raw_data;
+	bytes = 0;
 
 	minor_port = info->local_minor_port;
 	real_ip = info->local_real_ip;
 	info->local_minor_port = 0;
 	info->local_real_ip = 0;
 
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE);
-	bytes += qq_fill_conn_info(raw_data, &cursor, info);
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE);
+	bytes += qq_fill_conn_info(raw_data + bytes, info);
 
 	info->local_minor_port = minor_port;
 	info->local_real_ip = real_ip;
 
 	if (packet_len == bytes)
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_accept",
 			    "%d bytes expected but got %d bytes\n",
@@ -529,7 +529,7 @@
 	PurpleXfer *xfer;
 	ft_info *info;
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	gint packet_len, bytes;
 
 	qd = (qq_data *) gc->proto_data;
@@ -538,14 +538,13 @@
 
 	packet_len = 79;
 	raw_data = g_newa (guint8, packet_len);
-	cursor = raw_data;
+	bytes = 0;
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== sending qq file notify ip packet\n");
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE);
-	bytes += qq_fill_conn_info(raw_data, &cursor, info);
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE);
+	bytes += qq_fill_conn_info(raw_data + bytes, info);
 	if (packet_len == bytes)
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_notify",
 			    "%d bytes expected but got %d bytes\n",
@@ -560,7 +559,7 @@
 static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	gint packet_len, bytes;
 
 	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_reject", "start");
@@ -568,14 +567,12 @@
 
 	packet_len = 64;
 	raw_data = g_newa (guint8, packet_len);
-	cursor = raw_data;
 	bytes = 0;
 
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE);
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE);
 
 	if (packet_len == bytes)
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
 			    "%d bytes expected but got %d bytes\n",
@@ -586,7 +583,7 @@
 static void _qq_send_packet_file_cancel (PurpleConnection *gc, guint32 to_uid)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	gint packet_len, bytes;
 
 	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n");
@@ -594,17 +591,15 @@
 
 	packet_len = 64;
 	raw_data = g_newa (guint8, packet_len);
-	cursor = raw_data;
 	bytes = 0;
 
 	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before create header\n");
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE);
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE);
 	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n");
 
 	if (packet_len == bytes) {
 		purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before send cmd\n");
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	}
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
@@ -688,7 +683,7 @@
 }
 
 /* process reject im for file transfer request */
-void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len, 
+void qq_process_recv_file_reject (guint8 *data, gint data_len, 
 		guint32 sender_uid, PurpleConnection *gc)
 {
 	gchar *msg, *filename;
@@ -698,11 +693,13 @@
 	qd = (qq_data *) gc->proto_data;
 	g_return_if_fail (qd->xfer != NULL);
 
+	/*	border has been checked before
 	if (*cursor >= (data + data_len - 1)) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
 			    "Received file reject message is empty\n");
 		return;
 	}
+	*/
 	filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1;
 	msg = g_strdup_printf(_("%d has declined the file %s"),
 		 sender_uid, filename);
@@ -715,7 +712,7 @@
 }
 
 /* process cancel im for file transfer request */
-void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len, 
+void qq_process_recv_file_cancel (guint8 *data, gint data_len, 
 		guint32 sender_uid, PurpleConnection *gc)
 {
 	gchar *msg, *filename;
@@ -726,11 +723,13 @@
 	g_return_if_fail (qd->xfer != NULL
 			&& purple_xfer_get_filename(qd->xfer) != NULL);
 
+	/*	border has been checked before
 	if (*cursor >= (data + data_len - 1)) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
 			    "Received file reject message is empty\n");
 		return;
 	}
+	*/
 	filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1;
 	msg = g_strdup_printf
 		(_("%d canceled the transfer of %s"),
@@ -744,27 +743,26 @@
 }
 
 /* process accept im for file transfer request */
-void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc)
+void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc)
 {
 	qq_data *qd;
+	gint bytes;
 	ft_info *info;
 	PurpleXfer *xfer;
 
 	g_return_if_fail (data != NULL && data_len != 0);
 	qd = (qq_data *) gc->proto_data;
 	xfer = qd->xfer;
+	info = (ft_info *) qd->xfer->data;
 
-	if (*cursor >= (data + data_len - 1)) {
+	if (data_len <= 30 + QQ_CONN_INFO_LEN) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
 			    "Received file reject message is empty\n");
 		return;
 	}
 
-	info = (ft_info *) qd->xfer->data;
-
-	*cursor = data + 18 + 12;
-	qq_get_conn_info(data, cursor, data_len, info);
+	bytes = 18 + 12;	/* skip 30 bytes */
+	qq_get_conn_info(info, data + bytes);
 	_qq_xfer_init_socket(qd->xfer);
 
 	_qq_xfer_init_udp_channel(info);
@@ -772,8 +770,7 @@
 }
 
 /* process request from buddy's im for file transfer request */
-void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection * gc)
+void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection * gc)
 {
 	qq_data *qd;
 	PurpleXfer *xfer;
@@ -781,25 +778,27 @@
 	ft_info *info;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
+	gint bytes;
 
 	g_return_if_fail (data != NULL && data_len != 0);
 	qd = (qq_data *) gc->proto_data;
 
-	if (*cursor >= (data + data_len - 1)) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Received file reject message is empty\n");
-		return;
-	}
-
-	info = g_new0(ft_info, 1);
+	info = g_newa(ft_info, 1);
 	info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip));
 	info->local_internet_port = qd->my_port;
 	info->local_real_ip = 0x00000000;
 	info->to_uid = sender_uid;
-	read_packet_w(data, cursor, data_len, &(info->send_seq));
+	
+	if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
+		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
+			    "Received file request message is empty\n");
+		return;
+	}
+	bytes = 0;
+	bytes += qq_get16(&(info->send_seq), data + bytes);
 
-	*cursor = data + 18 + 12;
-	qq_get_conn_info(data, cursor, data_len, info);
+	bytes += 18 + 12;	/* skip 30 bytes */
+	bytes += qq_get_conn_info(info, data + bytes);
 
 	fileinfo = g_strsplit((gchar *) (data + 81 + 12), "\x1f", 2);
 	g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL);
@@ -880,9 +879,10 @@
 	*/
 }
 
-void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, 
+void qq_process_recv_file_notify(guint8 *data, gint data_len, 
 		guint32 sender_uid, PurpleConnection *gc)
 {
+	gint bytes;
 	qq_data *qd;
 	ft_info *info;
 	PurpleXfer *xfer;
@@ -890,19 +890,19 @@
 	g_return_if_fail (data != NULL && data_len != 0);
 	qd = (qq_data *) gc->proto_data;
 
-	if (*cursor >= (data + data_len - 1)) {
+	xfer = qd->xfer;
+	info = (ft_info *) qd->xfer->data;
+	if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
 			    "Received file notify message is empty\n");
 		return;
 	}
+	
+	bytes = 0;
+	bytes += qq_get16(&(info->send_seq), data + bytes);
 
-	xfer = qd->xfer;
-	info = (ft_info *) qd->xfer->data;
-	/* FIXME */
-	read_packet_w(data, cursor, data_len, &(info->send_seq));
-
-	*cursor = data + 18 + 12;
-	qq_get_conn_info(data, cursor, data_len, info);
+	bytes += 18 + 12;
+	bytes += qq_get_conn_info(info, data + bytes);
 
 	_qq_xfer_init_udp_channel(info);
 
@@ -938,7 +938,7 @@
 /*
 static void qq_send_packet_request_key(PurpleConnection *gc, guint8 key)
 {
-	qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1);
+	qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, &key, 1);
 }
 
 static void qq_process_recv_request_key(PurpleConnection *gc)
--- a/libpurple/protocols/qq/send_file.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/send_file.h	Thu Jun 26 20:25:38 2008 +0000
@@ -66,20 +66,15 @@
 	gboolean use_major;
 } ft_info;
 
-void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_reject(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_cancel(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_reject(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_cancel(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_notify(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
 gboolean qq_can_receive_file(PurpleConnection *gc, const char *who);
 void qq_send_file(PurpleConnection *gc, const char *who, const char *file);
-void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info);
-gint qq_fill_conn_info(guint8 *data, guint8 **cursor, ft_info *info);
+gint qq_get_conn_info(ft_info *info, guint8 *data);
+gint qq_fill_conn_info(guint8 *data, ft_info *info);
 gssize _qq_xfer_write(const guint8 *buf, size_t len, PurpleXfer *xfer);
 
 #endif
--- a/libpurple/protocols/qq/sendqueue.c	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-/**
- * @file sendqueue.c
- *
- * 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 "connection.h"
-#include "debug.h"
-#include "notify.h"
-#include "prefs.h"
-#include "request.h"
-
-#include "header_info.h"
-#include "qq_proxy.h"
-#include "sendqueue.h"
-
-#define QQ_RESEND_MAX               8	/* max resend per packet */
-
-typedef struct _gc_and_packet gc_and_packet;
-
-struct _gc_and_packet {
-	PurpleConnection *gc;
-	qq_sendpacket *packet;
-};
-
-/* Remove a packet with send_seq from sendqueue */
-void qq_sendqueue_remove(qq_data *qd, guint16 send_seq)
-{
-	GList *list;
-	qq_sendpacket *p;
-
-	list = qd->sendqueue;
-	while (list != NULL) {
-		p = (qq_sendpacket *) (list->data);
-		if (p->send_seq == send_seq) {
-			qd->sendqueue = g_list_remove(qd->sendqueue, p);
-			g_free(p->buf);
-			g_free(p);
-			break;
-		}
-		list = list->next;
-	}
-}
-		
-/* clean up sendqueue and free all contents */
-void qq_sendqueue_free(qq_data *qd)
-{
-	qq_sendpacket *p;
-	gint i;
-
-	i = 0;
-	while (qd->sendqueue != NULL) {
-		p = (qq_sendpacket *) (qd->sendqueue->data);
-		qd->sendqueue = g_list_remove(qd->sendqueue, p);
-		g_free(p->buf);
-		g_free(p);
-		i++;
-	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", i);
-}
-
-/* FIXME We shouldn't be dropping packets, but for now we have to because
- * somewhere we're generating invalid packets that the server won't ack.
- * Given enough time, a buildup of those packets would crash the client. */
-gboolean qq_sendqueue_timeout_callback(gpointer data)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-	GList *list;
-	qq_sendpacket *p;
-	time_t now;
-	gint wait_time;
-
-	gc = (PurpleConnection *) data;
-	qd = (qq_data *) gc->proto_data;
-	now = time(NULL);
-	list = qd->sendqueue;
-
-	/* empty queue, return TRUE so that timeout continues functioning */
-	if (qd->sendqueue == NULL)
-		return TRUE;
-
-	while (list != NULL) {	/* remove all packet whose resend_times == -1 */
-		p = (qq_sendpacket *) list->data;
-		if (p->resend_times == -1) {	/* to remove */
-			qd->sendqueue = g_list_remove(qd->sendqueue, p);
-			g_free(p->buf);
-			g_free(p);
-			list = qd->sendqueue;
-		} else {
-			list = list->next;
-		}
-	}
-
-	list = qd->sendqueue;
-	while (list != NULL) {
-		p = (qq_sendpacket *) list->data;
-		if (p->resend_times == QQ_RESEND_MAX) {	/* reach max */
-			switch (p->cmd) {
-			case QQ_CMD_KEEP_ALIVE:
-				if (qd->logged_in) {
-					purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n");
-					purple_connection_error_reason(gc,
-						PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
-					qd->logged_in = FALSE;
-				}
-				p->resend_times = -1;
-				break;
-			case QQ_CMD_LOGIN:
-			case QQ_CMD_REQUEST_LOGIN_TOKEN:
-				if (!qd->logged_in)	/* cancel login progress */
-					purple_connection_error_reason(gc,
-						PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply"));
-				p->resend_times = -1;
-				break;
-			default:{
-				purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-					"%s packet sent %d times but not acked. Not resending it.\n", 
-					qq_get_cmd_desc(p->cmd), QQ_RESEND_MAX);
-				}
-				p->resend_times = -1;
-			}
-		} else {	/* resend_times < QQ_RESEND_MAX, so sent it again */
-			wait_time = (gint) (QQ_SENDQUEUE_TIMEOUT / 1000);
-			if (difftime(now, p->sendtime) > (wait_time * (p->resend_times + 1))) {
-				qq_proxy_write(qd, p->buf, p->len);
-				p->resend_times++;
-				purple_debug(PURPLE_DEBUG_INFO,
-					   "QQ", "<<< [%05d] send again for %d times!\n", 
-					   p->send_seq, p->resend_times);
-			}
-		}
-		list = list->next;
-	}
-	return TRUE;		/* if we return FALSE, the timeout callback stops functioning */
-}
--- a/libpurple/protocols/qq/sendqueue.h	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/**
- * @file sendqueue.h
- *
- * 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 _QQ_SEND_QUEUE_H_
-#define _QQ_SEND_QUEUE_H_
-
-#include <glib.h>
-#include "qq.h"
-
-#define QQ_SENDQUEUE_TIMEOUT 			5000	/* in 1/1000 sec */
-
-typedef struct _qq_sendpacket qq_sendpacket;
-
-struct _qq_sendpacket {
-	gint fd;
-	gint len;
-	guint8 *buf;
-	guint16 cmd;
-	guint16 send_seq;
-	gint resend_times;
-	time_t sendtime;
-};
-
-void qq_sendqueue_free(qq_data *qd);
-
-void qq_sendqueue_remove(qq_data *qd, guint16 send_seq);
-gboolean qq_sendqueue_timeout_callback(gpointer data);
-
-#endif
--- a/libpurple/protocols/qq/sys_msg.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/sys_msg.c	Thu Jun 26 20:25:38 2008 +0000
@@ -35,7 +35,7 @@
 #include "header_info.h"
 #include "packet_parse.h"
 #include "qq.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "sys_msg.h"
 #include "utils.h"
 
@@ -120,27 +120,29 @@
 /* Send ACK if the sys message needs an ACK */
 static void _qq_send_packet_ack_msg_sys(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
 {
-	guint8 bar, *ack, *cursor;
+	qq_data *qd;
+	guint8 bar, *ack;
 	gchar *str;
 	gint ack_len, bytes;
 
+	qd = (qq_data *) gc->proto_data;
+	
 	str = g_strdup_printf("%d", from);
 	bar = 0x1e;
 	ack_len = 1 + 1 + strlen(str) + 1 + 2;
 	ack = g_newa(guint8, ack_len);
-	cursor = ack;
+
 	bytes = 0;
-
-	bytes += create_packet_b(ack, &cursor, code);
-	bytes += create_packet_b(ack, &cursor, bar);
-	bytes += create_packet_data(ack, &cursor, (guint8 *) str, strlen(str));
-	bytes += create_packet_b(ack, &cursor, bar);
-	bytes += create_packet_w(ack, &cursor, seq);
+	bytes += qq_put8(ack + bytes, code);
+	bytes += qq_put8(ack + bytes, bar);
+	bytes += qq_putdata(ack + bytes, (guint8 *) str, strlen(str));
+	bytes += qq_put8(ack + bytes, bar);
+	bytes += qq_put16(ack + bytes, seq);
 
 	g_free(str);
 
 	if (bytes == ack_len)	/* creation OK */
-		qq_send_cmd(gc, QQ_CMD_ACK_SYS_MSG, TRUE, 0, FALSE, ack, ack_len);
+		qq_send_cmd_detail(qd, QQ_CMD_ACK_SYS_MSG, 0, FALSE, ack, ack_len);
 	else
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
--- a/libpurple/protocols/qq/udp_proxy_s5.c	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,381 +0,0 @@
-/**
- * @file udp_proxy_s5.c
- *
- * 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 "debug.h"
-
-#include "udp_proxy_s5.h"
-
-static void _qq_s5_canread_again(gpointer data, gint source, PurpleInputCondition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-	struct sockaddr_in sin;
-	int len, error;
-	socklen_t errlen;
-	int flags;
-
-	purple_input_remove(phb->inpa);
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n");
-
-	len = read(source, buf, 10);
-	if (len < 10) {
-		purple_debug(PURPLE_DEBUG_WARNING, "socks5 proxy", "or not...\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, source, NULL);
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-	if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
-		if ((buf[0] == 0x05) && (buf[1] < 0x09))
-			purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]);
-		else
-			purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Bad data.\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	sin.sin_family = AF_INET;
-	memcpy(&sin.sin_addr.s_addr, buf + 4, 4);
-	memcpy(&sin.sin_port, buf + 8, 2);
-
-	if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", g_strerror(errno));
-		close(phb->udpsock);
-		close(source);
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	error = ETIMEDOUT;
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connect didn't block\n");
-	errlen = sizeof(error);
-	if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "getsockopt failed.\n");
-		close(phb->udpsock);
-		return;
-	}
-	flags = fcntl(phb->udpsock, F_GETFL);
-	fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK);
-
-	if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-		phb->func(phb->data, phb->udpsock, NULL);
-	}
-
-	g_free(phb->host);
-	g_free(phb);
-}
-
-static void _qq_s5_sendconnect(gpointer data, gint source)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-	struct sockaddr_in sin, ctlsin;
-	int port; 
-	socklen_t ctllen;
-	int flags;
-
-	purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port);
-
-	buf[0] = 0x05;
-	buf[1] = 0x03;		/* udp relay */
-	buf[2] = 0x00;		/* reserved */
-	buf[3] = 0x01;		/* address type -- ipv4 */
-	memset(buf + 4, 0, 0x04);
-
-	ctllen = sizeof(ctlsin);
-	if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "getsockname: %s\n", g_strerror(errno));
-		close(source);
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0);
-	purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock);
-	if (phb->udpsock < 0) {
-		close(source);
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	flags = fcntl(phb->udpsock, F_GETFL);
-	fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK);
-
-	port = g_ntohs(ctlsin.sin_port) + 1;
-	while (1) {
-		inet_aton("0.0.0.0", &(sin.sin_addr));
-		sin.sin_family = AF_INET;
-		sin.sin_port = g_htons(port);
-		if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
-			port++;
-			if (port > 65500) {
-				close(source);
-				g_free(phb->host);
-				g_free(phb);
-				return;
-			}
-		} else
-			break;
-	}
-
-	memset(buf + 4, 0, 0x04);
-	memcpy(buf + 8, &(sin.sin_port), 0x02);
-
-	if (write(source, buf, 10) < 10) {
-		close(source);
-		purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "packet too small\n");
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread_again, phb);
-}
-
-static void _qq_s5_readauth(gpointer data, gint source, PurpleInputCondition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-
-	purple_input_remove(phb->inpa);
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got auth response.\n");
-
-	if (read(source, buf, 2) < 2) {
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	_qq_s5_sendconnect(phb, source);
-}
-
-static void _qq_s5_canread(gpointer data, gint source, PurpleInputCondition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb;
-	int ret;
-
-	phb = data;
-
-	purple_input_remove(phb->inpa);
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read.\n");
-
-	ret = read(source, buf, 2);
-	if (ret < 2) {
-		purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
-		purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "unsupport\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	if (buf[1] == 0x02) {
-		unsigned int i, j;
-
-		i = strlen(purple_proxy_info_get_username(phb->gpi));
-		j = strlen(purple_proxy_info_get_password(phb->gpi));
-
-		buf[0] = 0x01;	/* version 1 */
-		buf[1] = i;
-		memcpy(buf + 2, purple_proxy_info_get_username(phb->gpi), i);
-		buf[2 + i] = j;
-		memcpy(buf + 2 + i + 1, purple_proxy_info_get_password(phb->gpi), j);
-
-		if (write(source, buf, 3 + i + j) < 3 + i + j) {
-			close(source);
-
-			if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-				phb->func(phb->data, -1, _("Unable to connect"));
-			}
-
-			g_free(phb->host);
-			g_free(phb);
-			return;
-		}
-
-		phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_readauth, phb);
-	} else {
-		purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n");
-		_qq_s5_sendconnect(phb, source);
-	}
-}
-
-static void _qq_s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
-{
-	unsigned char buf[512];
-	int i;
-	struct PHB *phb = data;
-	socklen_t len;
-	int error = ETIMEDOUT;
-	int flags;
-
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n");
-
-	if (phb->inpa > 0)
-		purple_input_remove(phb->inpa);
-
-	len = sizeof(error);
-	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "getsockopt", "%s\n", g_strerror(errno));
-		close(source);
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-	flags = fcntl(source, F_GETFL);
-	fcntl(source, F_SETFL, flags & ~O_NONBLOCK);
-
-	i = 0;
-	buf[0] = 0x05;		/* SOCKS version 5 */
-
-	if (purple_proxy_info_get_username(phb->gpi) != NULL) {
-		buf[1] = 0x02;	/* two methods */
-		buf[2] = 0x00;	/* no authentication */
-		buf[3] = 0x02;	/* username/password authentication */
-		i = 4;
-	} else {
-		buf[1] = 0x01;
-		buf[2] = 0x00;
-		i = 3;
-	}
-
-	if (write(source, buf, i) < i) {
-		purple_debug(PURPLE_DEBUG_INFO, "write", "%s\n", g_strerror(errno));
-		purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Unable to write\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread, phb);
-}
-
-gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
-{
-	gint fd;
-	int flags;
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ",
-		   "Connecting to %s:%d via %s:%d using SOCKS5\n",
-		   phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi));
-
-	if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0)
-		return -1;
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd);
-
-	flags = fcntl(fd, F_GETFL);
-	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-	if (connect(fd, addr, addrlen) < 0) {
-		if ((errno == EINPROGRESS) || (errno == EINTR)) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
-			phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, _qq_s5_canwrite, phb);
-		} else {
-			close(fd);
-			return -1;
-		}
-	} else {
-		purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n");
-		flags = fcntl(fd, F_GETFL);
-		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
-		_qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE);
-	}
-
-	return fd;
-}
--- a/libpurple/protocols/qq/udp_proxy_s5.h	Thu Jun 26 19:57:37 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/**
- * @file udp_proxy_s5.h
- *
- * 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 _QQ_UDP_PROXY_S5_H_
-#define _QQ_UDP_PROXY_S5_H_
-
-#include "internal.h"		/* for socket stuff */
-
-#include "qq_proxy.h"
-
-gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen);
-
-#endif
--- a/libpurple/protocols/qq/utils.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/utils.c	Thu Jun 26 20:25:38 2008 +0000
@@ -22,7 +22,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#include "cipher.h"
 #include "limits.h"
 #include "stdlib.h"
 #include "string.h"
@@ -40,6 +39,17 @@
 
 #define QQ_NAME_FORMAT    "%d"
 
+/* These functions are used only in development phase */
+/*
+   static void _qq_show_socket(gchar *desc, gint fd) {
+   struct sockaddr_in sin;
+   socklen_t len = sizeof(sin);
+   getsockname(fd, (struct sockaddr *)&sin, &len);
+   purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n",
+   inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
+   }
+   */
+
 gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount)
 {
 	gint index;
@@ -113,26 +123,6 @@
 	return segments;
 }
 
-/* generate a md5 key using uid and session_key */
-guint8 *_gen_session_md5(gint uid, guint8 *session_key)
-{
-	guint8 *src, md5_str[QQ_KEY_LENGTH];
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
-
-	src = g_newa(guint8, 20);
-	memcpy(src, &uid, 4);
-	memcpy(src, session_key, QQ_KEY_LENGTH);
-
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, src, 20);
-	purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL);
-	purple_cipher_context_destroy(context);
-
-	return g_memdup(md5_str, QQ_KEY_LENGTH);
-}
-
 /* given a four-byte ip data, convert it into a human readable ip string
  * the return needs to be freed */
 gchar *gen_ip_str(guint8 *ip)
@@ -194,7 +184,7 @@
 }
 
 /* try to dump the data as GBK */
-void try_dump_as_gbk(const guint8 *const data, gint len)
+gchar* try_dump_as_gbk(const guint8 *const data, gint len)
 {
 	gint i;
 	guint8 *incoming;
@@ -215,8 +205,8 @@
 
 	if (msg_utf8 != NULL) {
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Try extract GB msg: %s\n", msg_utf8);
-		g_free(msg_utf8);
 	}
+	return msg_utf8;
 }
 
 /* strips whitespace */
@@ -294,7 +284,7 @@
 
 /* Dumps a chunk of raw data into an ASCII hex string.
  * The return should be freed later. */
-gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
+static gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
 {
 	GString *str;
 	gchar *ret;
@@ -331,6 +321,51 @@
 	return ret;
 }
 
+void qq_hex_dump(PurpleDebugLevel level, const char *category,
+		const guint8 *pdata, gint bytes,	
+		const char *format, ...)
+{
+	va_list args;
+	char *arg_s = NULL;
+	gchar *phex = NULL;
+
+	g_return_if_fail(level != PURPLE_DEBUG_ALL);
+	g_return_if_fail(format != NULL);
+
+	va_start(args, format);
+	arg_s = g_strdup_vprintf(format, args);
+	va_end(args);
+
+	if (bytes <= 0) {
+		purple_debug(level, category, arg_s);
+		return;
+	}
+
+	phex = hex_dump_to_str(pdata, bytes);
+	purple_debug(level, category, "%s - (len %d)\n%s", arg_s, bytes, phex);
+	g_free(phex);
+}
+
+void qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
+{
+	/*
+	   char buf1[8*len+2], buf2[10];
+	   int i;
+	   buf1[0] = 0;
+	   for (i = 0; i < len; i++) {
+	   sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff);
+	   strcat(buf1, buf2);
+	   }
+	   strcat(buf1, "\n");
+	   purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1);
+	   */
+
+	/* modified by s3e, 20080424 */
+	qq_hex_dump(PURPLE_DEBUG_INFO, desc,
+		buf, len,
+		"");
+}
+
 /* convert face num from packet (0-299) to local face (1-100) */
 gchar *face_to_icon_str(gint face)
 {
--- a/libpurple/protocols/qq/utils.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/qq/utils.h	Thu Jun 26 20:25:38 2008 +0000
@@ -28,12 +28,13 @@
 #include <stdio.h>
 #include <glib.h>
 
+#include "debug.h"
+
 gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount);
 gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount);
 gint qq_string_to_dec_value(const gchar *str);
 
 gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields);
-guint8 *_gen_session_md5(gint uid, guint8 *session_key);
 
 gchar *gen_ip_str(guint8 *ip);
 guint8 *str_ip_gen(gchar *str);
@@ -44,10 +45,13 @@
 
 gchar *face_to_icon_str(gint face);
 
-void try_dump_as_gbk(const guint8 *const data, gint len);
+gchar *try_dump_as_gbk(const guint8 *const data, gint len);
 
+void qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
+void qq_hex_dump(PurpleDebugLevel level, const char *category,
+		const guint8 *pdata, gint bytes,	
+		const char *format, ...);
 guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len);
-gchar *hex_dump_to_str(const guint8 *buf, gint buf_len);
 
 const gchar *qq_buddy_icon_dir(void);
 const gchar *qq_win32_buddy_icon_dir(void);
--- a/libpurple/protocols/silc/buddy.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/silc/buddy.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1434,13 +1434,25 @@
 void silcpurple_idle_set(PurpleConnection *gc, int idle)
 
 {
-	SilcPurple sg = gc->proto_data;
-	SilcClient client = sg->client;
-	SilcClientConnection conn = sg->conn;
+	SilcPurple sg;
+	SilcClient client;
+	SilcClientConnection conn;
 	SilcAttributeObjService service;
 	const char *server;
 	int port;
 
+	sg = gc->proto_data;
+	if (sg == NULL)
+		return;
+
+	client = sg->client;
+	if (client == NULL)
+		return;
+
+	conn = sg->conn;
+	if (conn == NULL)
+		return;
+
 	server = purple_account_get_string(sg->account, "server",
 					 "silc.silcnet.org");
 	port = purple_account_get_int(sg->account, "port", 706),
--- a/libpurple/protocols/silc10/buddy.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/silc10/buddy.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1434,13 +1434,25 @@
 void silcpurple_idle_set(PurpleConnection *gc, int idle)
 
 {
-	SilcPurple sg = gc->proto_data;
-	SilcClient client = sg->client;
-	SilcClientConnection conn = sg->conn;
+	SilcPurple sg;
+	SilcClient client;
+	SilcClientConnection conn;
 	SilcAttributeObjService service;
 	const char *server;
 	int port;
 
+	sg = gc->proto_data;
+	if (sg == NULL)
+		return;
+
+	client = sg->client;
+	if (client == NULL)
+		return;
+
+	conn = sg->conn;
+	if (conn == NULL)
+		return;
+
 	server = purple_account_get_string(sg->account, "server",
 					 "silc.silcnet.org");
 	port = purple_account_get_int(sg->account, "port", 706),
--- a/libpurple/protocols/simple/simple.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/simple/simple.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1898,7 +1898,7 @@
 	PurpleConnection *gc;
 	struct simple_account_data *sip;
 	gchar **userserver;
-	gchar *hosttoconnect;
+	const gchar *hosttoconnect;
 
 	const char *username = purple_account_get_username(account);
 	gc = purple_account_get_connection(account);
@@ -1934,14 +1934,13 @@
 	sip->status = g_strdup("available");
 
 	if(!purple_account_get_bool(account, "useproxy", FALSE)) {
-		hosttoconnect = g_strdup(sip->servername);
+		hosttoconnect = sip->servername;
 	} else {
-		hosttoconnect = g_strdup(purple_account_get_string(account, "proxy", sip->servername));
+		hosttoconnect = purple_account_get_string(account, "proxy", sip->servername);
 	}
 
 	sip->srv_query_data = purple_srv_resolve("sip",
 			sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
-	g_free(hosttoconnect);
 }
 
 static void simple_close(PurpleConnection *gc)
--- a/libpurple/protocols/yahoo/yahoo.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Thu Jun 26 20:25:38 2008 +0000
@@ -777,6 +777,7 @@
 				list = g_slist_append(list, im);
 				im->from = pair->value;
 				im->time = time(NULL);
+				im->utf8 = TRUE;
 			}
 			if (pair->key == 97)
 				if (im)
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Thu Jun 26 20:25:38 2008 +0000
@@ -137,6 +137,9 @@
 		if (url_data != NULL) {
 			yd = gc->proto_data;
 			yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+		} else {
+			g_free(data->who);
+			g_free(data);
 		}
 	} else if (who && send_icon_info) {
 		yahoo_send_picture_info(gc, who);
@@ -244,13 +247,12 @@
 	}
 
 	if (url) {
-		if (yd->picture_url)
-			g_free(yd->picture_url);
+		g_free(yd->picture_url);
 		yd->picture_url = g_strdup(url);
 		purple_account_set_string(account, YAHOO_PICURL_SETTING, url);
 		purple_account_set_int(account, YAHOO_PICCKSUM_SETTING, yd->picture_checksum);
+		yahoo_send_picture_checksum(gc);
 		yahoo_send_picture_update(gc, 2);
-		yahoo_send_picture_checksum(gc);
 	}
 }
 
@@ -402,8 +404,15 @@
 
 	if (ret < 0 && errno == EAGAIN)
 		return;
-	else if (ret <= 0)
+	else if (ret <= 0) {
+		purple_debug_info("yahoo", "Buddy icon upload response (%d) bytes (> ~400 indicates failure):\n%.*s\n",
+			d->str->len, d->str->len, d->str->str);
+
 		yahoo_buddy_icon_upload_data_free(d);
+		return;
+	}
+
+	g_string_append_len(d->str, buf, ret);
 }
 
 static void yahoo_buddy_icon_upload_pending(gpointer data, gint source, PurpleInputCondition condition)
@@ -421,6 +430,7 @@
 	if (wrote < 0 && errno == EAGAIN)
 		return;
 	if (wrote <= 0) {
+		purple_debug_info("yahoo", "Error uploading buddy icon.\n");
 		yahoo_buddy_icon_upload_data_free(d);
 		return;
 	}
@@ -428,6 +438,9 @@
 	if (d->pos >= d->str->len) {
 		purple_debug_misc("yahoo", "Finished uploading buddy icon.\n");
 		purple_input_remove(d->watcher);
+		/* Clean out the sent buffer and reuse it to read the result */
+		g_string_free(d->str, TRUE);
+		d->str = g_string_new("");
 		d->watcher = purple_input_add(d->fd, PURPLE_INPUT_READ, yahoo_buddy_icon_upload_reading, d);
 	}
 }
@@ -436,16 +449,16 @@
 {
 	struct yahoo_buddy_icon_upload_data *d = data;
 	struct yahoo_packet *pkt;
-	gchar *size, *header;
+	gchar *tmp, *header;
 	guchar *pkt_buf;
 	const char *host;
 	int port;
-	size_t content_length, pkt_buf_len;
-	PurpleConnection *gc;
+	gsize pkt_buf_len;
+	PurpleConnection *gc = d->gc;
 	PurpleAccount *account;
 	struct yahoo_data *yd;
+	gboolean use_whole_url = FALSE;
 
-	gc = d->gc;
 	account = purple_connection_get_account(gc);
 	yd = gc->proto_data;
 
@@ -457,44 +470,55 @@
 		yahoo_buddy_icon_upload_data_free(d);
 		return;
 	}
+	/* use whole URL if using HTTP Proxy */
+	if ((gc->account->proxy_info)
+	    	&& (gc->account->proxy_info->type == PURPLE_PROXY_HTTP))
+		use_whole_url = TRUE;
 
-	pkt = yahoo_packet_new(0xc2, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPLOAD, YAHOO_STATUS_AVAILABLE, yd->session_id);
 
-	size = g_strdup_printf("%" G_GSIZE_FORMAT, d->str->len);
+	tmp = g_strdup_printf("%" G_GSIZE_FORMAT, d->str->len);
 	/* 1 = me, 38 = expire time(?), 0 = me, 28 = size, 27 = filename, 14 = NULL, 29 = data */
 	yahoo_packet_hash_str(pkt, 1, purple_connection_get_display_name(gc));
 	yahoo_packet_hash_str(pkt, 38, "604800"); /* time til expire */
 	purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, time(NULL) + 604800);
 	yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc));
-	yahoo_packet_hash_str(pkt, 28, size);
-	g_free(size);
+	yahoo_packet_hash_str(pkt, 28, tmp);
+	g_free(tmp);
 	yahoo_packet_hash_str(pkt, 27, d->filename);
 	yahoo_packet_hash_str(pkt, 14, "");
+	/* 4 padding for the 29 key name */
+	pkt_buf_len = yahoo_packet_build(pkt, 4, FALSE, yd->jp, &pkt_buf);
+	yahoo_packet_free(pkt);
 
-	content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
+	/* header + packet + "29" + 0xc0 + 0x80) + pictureblob */
 
 	host = purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST);
 	port = purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT);
-	header = g_strdup_printf(
-		"POST http://%s:%d/notifyft HTTP/1.0\r\n"
-		"Content-length: %" G_GSIZE_FORMAT "\r\n"
-		"Host: %s:%d\r\n"
-		"Cookie: Y=%s; T=%s\r\n"
-		"\r\n",
-		host, port, content_length + 4 + d->str->len,
-		host, port, yd->cookie_y, yd->cookie_t);
+	tmp = g_strdup_printf("%s:%d", host, port);
+	header = g_strdup_printf("POST %s%s/notifyft HTTP/1.1\r\n"
+		"User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n"
+		"Cookie: T=%s; Y=%s\r\n"
+		"Host: %s\r\n"
+		"Content-Length: %" G_GSIZE_FORMAT "\r\n"
+		"Cache-Control: no-cache\r\n\r\n",
+		use_whole_url ? "http://" : "", use_whole_url ? tmp : "",
+		yd->cookie_t, yd->cookie_y, 
+		tmp,
+		pkt_buf_len + 4 + d->str->len);
+	g_free(tmp);
 
 	/* There's no magic here, we just need to prepend in reverse order */
 	g_string_prepend(d->str, "29\xc0\x80");
 
-	pkt_buf_len = yahoo_packet_build(pkt, 8, FALSE, yd->jp, &pkt_buf);
-	yahoo_packet_free(pkt);
 	g_string_prepend_len(d->str, (char *)pkt_buf, pkt_buf_len);
 	g_free(pkt_buf);
 
 	g_string_prepend(d->str, header);
 	g_free(header);
 
+	purple_debug_info("yahoo", "Buddy icon upload data:\n%.*s\n", d->str->len, d->str->str);
+
 	d->fd = source;
 	d->watcher = purple_input_add(d->fd, PURPLE_INPUT_WRITE, yahoo_buddy_icon_upload_pending, d);
 
@@ -525,6 +549,28 @@
 	}
 }
 
+static int yahoo_buddy_icon_calculate_checksum(const guchar *data, gsize len)
+{
+	/* This code is borrowed from Kopete, which seems to be managing to calculate
+	   checksums in such a manner that Yahoo!'s servers are happy */
+
+	const guchar *p = data;
+	int checksum = 0, g, i = len;
+
+	while(i--) {
+		checksum = (checksum << 4) + *p++;
+
+		if((g = (checksum & 0xf0000000)) != 0)
+			checksum ^= g >> 23;
+
+		checksum &= ~g;
+	}
+
+	purple_debug_misc("yahoo", "Calculated buddy icon checksum: %d", checksum);
+
+	return checksum;
+} 
+
 void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
 	struct yahoo_data *yd = gc->proto_data;
@@ -534,6 +580,8 @@
 		g_free(yd->picture_url);
 		yd->picture_url = NULL;
 
+		/* TODO: don't we have to clear it on the server too?! */
+
 		purple_account_set_string(account, YAHOO_PICURL_SETTING, NULL);
 		purple_account_set_int(account, YAHOO_PICCKSUM_SETTING, 0);
 		purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, 0);
@@ -549,14 +597,8 @@
 		int oldcksum = purple_account_get_int(account, YAHOO_PICCKSUM_SETTING, 0);
 		int expire = purple_account_get_int(account, YAHOO_PICEXPIRE_SETTING, 0);
 		const char *oldurl = purple_account_get_string(account, YAHOO_PICURL_SETTING, NULL);
-		char *iconfile;
 
-		/* TODO: At some point, it'd be nice to fix this for real, or
-		 * TODO: at least change it to be something like:
-		 * TODO: purple_imgstore_get_filename(img);
-		 * TODO: But it would be great if we knew how to calculate the
-		 * TODO: Checksum correctly. */
-		yd->picture_checksum = g_string_hash(s);
+		yd->picture_checksum = yahoo_buddy_icon_calculate_checksum(data, len);
 
 		if ((yd->picture_checksum == oldcksum) &&
 			(expire > (time(NULL) + 60*60*24)) && oldurl)
@@ -569,12 +611,11 @@
 		}
 
 		/* We use this solely for sending a filename to the server */
-		iconfile = g_strdup(purple_imgstore_get_filename(img));
 		d = g_new0(struct yahoo_buddy_icon_upload_data, 1);
 		d->gc = gc;
 		d->str = s;
 		d->fd = -1;
-		d->filename = iconfile;
+		d->filename = g_strdup(purple_imgstore_get_filename(img));
 
 		if (!yd->logged_in) {
 			yd->picture_upload_todo = d;
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Thu Jun 26 20:25:38 2008 +0000
@@ -745,6 +745,7 @@
 				p += 1; /* skip only the ' ' */
 				q = strchr(p, ' ');
 				if (q) {
+					g_free(it);
 					it = g_strndup(p, q - p);
 				}
 			}
--- a/libpurple/proxy.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/proxy.c	Thu Jun 26 20:25:38 2008 +0000
@@ -265,6 +265,7 @@
 				"'manual' but no proxy server is specified.  Using "
 				"Pidgin's proxy settings instead.\n");
 		g_free(info.host);
+		info.host = NULL;
 		return purple_global_proxy_get_info();
 	}
 
@@ -272,6 +273,7 @@
 			&info.username, NULL, NULL, NULL))
 	{
 		g_free(info.host);
+		info.host = NULL;
 		return purple_global_proxy_get_info();
 	}
 	g_strchomp(info.username);
@@ -280,7 +282,9 @@
 			&info.password, NULL, NULL, NULL))
 	{
 		g_free(info.host);
+		info.host = NULL;
 		g_free(info.username);
+		info.username = NULL;
 		return purple_global_proxy_get_info();
 	}
 	g_strchomp(info.password);
@@ -289,8 +293,11 @@
 			&tmp, NULL, NULL, NULL))
 	{
 		g_free(info.host);
+		info.host = NULL;
 		g_free(info.username);
+		info.username = NULL;
 		g_free(info.password);
+		info.password = NULL;
 		return purple_global_proxy_get_info();
 	}
 	info.port = atoi(tmp);
--- a/libpurple/proxy.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/proxy.h	Thu Jun 26 20:25:38 2008 +0000
@@ -239,9 +239,9 @@
  *                   to something descriptive (hopefully).
  * @param data       User-defined data.
  *
- * @return NULL if there was an error, or a reference to a data
- *         structure that can be used to cancel the pending
- *         connection, if needed.
+ * @return NULL if there was an error, or a reference to an
+ *         opaque data structure that can be used to cancel
+ *         the pending connection, if needed.
  */
 PurpleProxyConnectData *purple_proxy_connect(void *handle,
 			PurpleAccount *account,
@@ -265,9 +265,9 @@
  *                   to something descriptive (hopefully).
  * @param data       User-defined data.
  *
- * @return NULL if there was an error, or a reference to a data
- *         structure that can be used to cancel the pending
- *         connection, if needed.
+ * @return NULL if there was an error, or a reference to an
+ *         opaque data structure that can be used to cancel
+ *         the pending connection, if needed.
  */
 PurpleProxyConnectData *purple_proxy_connect_socks5(void *handle,
 			PurpleProxyInfo *gpi,
--- a/libpurple/prpl.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/prpl.h	Thu Jun 26 20:25:38 2008 +0000
@@ -31,6 +31,7 @@
 #define _PURPLE_PRPL_H_
 
 typedef struct _PurplePluginProtocolInfo PurplePluginProtocolInfo;
+/** @copydoc _PurpleAttentionType */
 typedef struct _PurpleAttentionType PurpleAttentionType;
 
 /**************************************************************************/
@@ -100,6 +101,9 @@
 	gboolean secret;
 };
 
+/** Represents "nudges" and "buzzes" that you may send to a buddy to attract
+ *  their attention (or vice-versa).
+ */
 struct _PurpleAttentionType
 {
 	const char *name;                  /**< Shown in GUI elements */
--- a/libpurple/roomlist.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/roomlist.h	Thu Jun 26 20:25:38 2008 +0000
@@ -30,6 +30,7 @@
 typedef struct _PurpleRoomlist PurpleRoomlist;
 typedef struct _PurpleRoomlistRoom PurpleRoomlistRoom;
 typedef struct _PurpleRoomlistField PurpleRoomlistField;
+/** @copydoc _PurpleRoomlistUiOps */
 typedef struct _PurpleRoomlistUiOps PurpleRoomlistUiOps;
 
 /**
--- a/libpurple/server.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/server.c	Thu Jun 26 20:25:38 2008 +0000
@@ -154,7 +154,6 @@
 	 */
 	auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
 	if((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
-			flags & PURPLE_MESSAGE_AUTO_RESP &&
 			!purple_presence_is_available(presence) &&
 			strcmp(auto_reply_pref, "never")) {
 
@@ -731,6 +730,7 @@
 		PurpleStatusPrimitive primitive;
 		const gchar *auto_reply_pref;
 		const char *away_msg = NULL;
+		gboolean mobile = FALSE;
 
 		auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
 
@@ -738,9 +738,10 @@
 		status = purple_presence_get_active_status(presence);
 		status_type = purple_status_get_type(status);
 		primitive = purple_status_type_get_primitive(status_type);
+		mobile = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE);
 		if ((primitive == PURPLE_STATUS_AVAILABLE) ||
 			(primitive == PURPLE_STATUS_INVISIBLE) ||
-			(primitive == PURPLE_STATUS_MOBILE) ||
+			mobile ||
 		    !strcmp(auto_reply_pref, "never") ||
 		    (!purple_presence_is_idle(presence) && !strcmp(auto_reply_pref, "awayidle")))
 		{
--- a/libpurple/smiley.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/smiley.c	Thu Jun 26 20:25:38 2008 +0000
@@ -641,10 +641,9 @@
 	old_filename = purple_imgstore_get_filename(old_img);
 	new_filename = purple_imgstore_get_filename(smiley->img);
 
-	if (g_ascii_strcasecmp(old_filename, new_filename)) {
+	if (g_ascii_strcasecmp(old_filename, new_filename))
 		purple_smiley_data_unstore(old_filename);
-		purple_imgstore_unref(old_img);
-	}
+	purple_imgstore_unref(old_img);
 }
 
 
--- a/libpurple/sound.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/sound.h	Thu Jun 26 20:25:38 2008 +0000
@@ -55,6 +55,9 @@
 
 } PurpleSoundEventID;
 
+/** Operations used by the core to request that particular sound files, or the
+ *  sound associated with a particular event, should be played.
+ */
 typedef struct _PurpleSoundUiOps
 {
 	void (*init)(void);
--- a/libpurple/status.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/status.c	Thu Jun 26 20:25:38 2008 +0000
@@ -107,8 +107,6 @@
 	PurpleStatusType *type;
 	PurplePresence *presence;
 
-	const char *title;
-
 	gboolean active;
 
 	/*
--- a/libpurple/util.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/util.c	Thu Jun 26 20:25:38 2008 +0000
@@ -832,6 +832,11 @@
 				if (offset_positive)
 					tzoff *= -1;
 			}
+			else if ((*c == 'Z') && (c = c + 1))
+			{
+				/* 'Z' = Zulu = UTC */
+				tzoff = 0;
+			}
 			else if (utc)
 			{
 				static struct tm tmptm;
@@ -1357,6 +1362,14 @@
 	GString *cdata = NULL;
 	GList *tags = NULL, *tag;
 	const char *c = html;
+	char quote = '\0';
+
+#define CHECK_QUOTE(ptr) if (*(ptr) == '\'' || *(ptr) == '\"') \
+			quote = *(ptr++); \
+		else \
+			quote = '\0';
+
+#define VALID_CHAR(ptr) (*(ptr) && *(ptr) != quote && (quote || (*(ptr) != ' ' && *(ptr) != '>')))
 
 	g_return_if_fail(xhtml_out != NULL || plain_out != NULL);
 
@@ -1514,38 +1527,37 @@
 						xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>");
 					continue;
 				}
-				if(!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) {
-					const char *p = c;
+				if (!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) {
+					const char *p = c + 4;
 					GString *src = NULL, *alt = NULL;
-					while(*p && *p != '>') {
-						if(!g_ascii_strncasecmp(p, "src=", strlen("src="))) {
-							const char *q = p + strlen("src=");
+					while (*p && *p != '>') {
+						if (!g_ascii_strncasecmp(p, "src=", 4)) {
+							const char *q = p + 4;
 							if (src)
 								g_string_free(src, TRUE);
 							src = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								src = g_string_append_c(src, *q);
 								q++;
 							}
 							p = q;
-						} else if(!g_ascii_strncasecmp(p, "alt=", strlen("alt="))) {
-							const char *q = p + strlen("alt=");
+						} else if (!g_ascii_strncasecmp(p, "alt=", 4)) {
+							const char *q = p + 4;
 							if (alt)
 								g_string_free(alt, TRUE);
 							alt = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								alt = g_string_append_c(alt, *q);
 								q++;
 							}
 							p = q;
+						} else {
+							p++;
 						}
-						p++;
 					}
-					if ((c = strchr(c, '>')) != NULL)
+					if ((c = strchr(p, '>')) != NULL)
 						c++;
 					else
 						c = p;
@@ -1562,21 +1574,20 @@
 					g_string_free(src, TRUE);
 					continue;
 				}
-				if(!g_ascii_strncasecmp(c, "<a", 2) && (*(c+2) == '>' || *(c+2) == ' ')) {
-					const char *p = c;
+				if (!g_ascii_strncasecmp(c, "<a", 2) && (*(c+2) == '>' || *(c+2) == ' ')) {
+					const char *p = c + 2;
 					struct purple_parse_tag *pt;
-					while(*p && *p != '>') {
-						if(!g_ascii_strncasecmp(p, "href=", strlen("href="))) {
-							const char *q = p + strlen("href=");
+					while (*p && *p != '>') {
+						if (!g_ascii_strncasecmp(p, "href=", 5)) {
+							const char *q = p + 5;
 							if (url)
 								g_string_free(url, TRUE);
 							url = g_string_new("");
 							if (cdata)
 								g_string_free(cdata, TRUE);
 							cdata = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								int len;
 								if ((*q == '&') && (purple_markup_unescape_entity(q, &len) == NULL))
 									url = g_string_append(url, "&amp;");
@@ -1585,10 +1596,11 @@
 								q++;
 							}
 							p = q;
+						} else {
+							p++;
 						}
-						p++;
 					}
-					if ((c = strchr(c, '>')) != NULL)
+					if ((c = strchr(p, '>')) != NULL)
 						c++;
 					else
 						c = p;
@@ -1601,55 +1613,48 @@
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) {
-					const char *p = c;
+					const char *p = c + 5;
 					GString *style = g_string_new("");
 					struct purple_parse_tag *pt;
-					while(*p && *p != '>') {
-						if(!g_ascii_strncasecmp(p, "back=", strlen("back="))) {
-							const char *q = p + strlen("back=");
+					while (*p && *p != '>') {
+						if (!g_ascii_strncasecmp(p, "back=", 5)) {
+							const char *q = p + 5;
 							GString *color = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								color = g_string_append_c(color, *q);
 								q++;
 							}
 							g_string_append_printf(style, "background: %s; ", color->str);
 							g_string_free(color, TRUE);
 							p = q;
-						} else if(!g_ascii_strncasecmp(p, "color=", strlen("color="))) {
-							const char *q = p + strlen("color=");
+						} else if (!g_ascii_strncasecmp(p, "color=", 6)) {
+							const char *q = p + 6;
 							GString *color = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								color = g_string_append_c(color, *q);
 								q++;
 							}
 							g_string_append_printf(style, "color: %s; ", color->str);
 							g_string_free(color, TRUE);
 							p = q;
-						} else if(!g_ascii_strncasecmp(p, "face=", strlen("face="))) {
-							const char *q = p + strlen("face=");
-							gboolean space_allowed = FALSE;
+						} else if (!g_ascii_strncasecmp(p, "face=", 5)) {
+							const char *q = p + 5;
 							GString *face = g_string_new("");
-							if(*q == '\'' || *q == '\"') {
-								space_allowed = TRUE;
-								q++;
-							}
-							while(*q && *q != '\"' && *q != '\'' && (space_allowed || *q != ' ')) {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								face = g_string_append_c(face, *q);
 								q++;
 							}
 							g_string_append_printf(style, "font-family: %s; ", g_strstrip(face->str));
 							g_string_free(face, TRUE);
 							p = q;
-						} else if(!g_ascii_strncasecmp(p, "size=", strlen("size="))) {
-							const char *q = p + strlen("size=");
+						} else if (!g_ascii_strncasecmp(p, "size=", 5)) {
+							const char *q = p + 5;
 							int sz;
 							const char *size = "medium";
-							if(*q == '\'' || *q == '\"')
-								q++;
+							CHECK_QUOTE(q);
 							sz = atoi(q);
 							switch (sz)
 							{
@@ -1679,10 +1684,11 @@
 							}
 							g_string_append_printf(style, "font-size: %s; ", size);
 							p = q;
+						} else {
+							p++;
 						}
-						p++;
 					}
-					if ((c = strchr(c, '>')) != NULL)
+					if ((c = strchr(p, '>')) != NULL)
 						c++;
 					else
 						c = p;
@@ -1697,24 +1703,23 @@
 					g_string_free(style, TRUE);
 					continue;
 				}
-				if(!g_ascii_strncasecmp(c, "<body ", 6)) {
-					const char *p = c;
+				if (!g_ascii_strncasecmp(c, "<body ", 6)) {
+					const char *p = c + 6;
 					gboolean did_something = FALSE;
-					while(*p && *p != '>') {
-						if(!g_ascii_strncasecmp(p, "bgcolor=", strlen("bgcolor="))) {
-							const char *q = p + strlen("bgcolor=");
+					while (*p && *p != '>') {
+						if (!g_ascii_strncasecmp(p, "bgcolor=", 8)) {
+							const char *q = p + 8;
 							struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1);
 							GString *color = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								color = g_string_append_c(color, *q);
 								q++;
 							}
-							if(xhtml)
+							if (xhtml)
 								g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str));
 							g_string_free(color, TRUE);
-							if ((c = strchr(c, '>')) != NULL)
+							if ((c = strchr(p, '>')) != NULL)
 								c++;
 							else
 								c = p;
@@ -1726,7 +1731,7 @@
 						}
 						p++;
 					}
-					if(did_something) continue;
+					if (did_something) continue;
 				}
 				/* this has to come after the special case for bgcolor */
 				ALLOW_TAG("body");
@@ -1789,6 +1794,8 @@
 		g_string_free(url, TRUE);
 	if (cdata)
 		g_string_free(cdata, TRUE);
+#undef CHECK_QUOTE
+#undef VALID_CHAR
 }
 
 /* The following are probably reasonable changes:
@@ -3592,7 +3599,7 @@
 static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message);
 
 static gboolean
-parse_redirect(const char *data, size_t data_len, gint sock,
+parse_redirect(const char *data, size_t data_len,
 			   PurpleUtilFetchUrlData *gfud)
 {
 	gchar *s;
@@ -3766,7 +3773,7 @@
 					header_len, gfud->webdata);
 
 				/* See if we can find a redirect. */
-				if(parse_redirect(gfud->webdata, header_len, source, gfud))
+				if(parse_redirect(gfud->webdata, header_len, gfud))
 					return;
 
 				gfud->got_headers = TRUE;
--- a/libpurple/win32/global.mak	Thu Jun 26 19:57:37 2008 +0000
+++ b/libpurple/win32/global.mak	Thu Jun 26 20:25:38 2008 +0000
@@ -112,4 +112,4 @@
 MINGW_MAKEFILE := Makefile.mingw
 
 INSTALL_PIXMAPS ?= 1
-
+INSTALL_SSL_CERTIFICATES ?= 1
--- a/pidgin/gtkaccount.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkaccount.c	Thu Jun 26 20:25:38 2008 +0000
@@ -2191,8 +2191,7 @@
 					G_TYPE_STRING,     /* COLUMN_SCREENNAME */
 					G_TYPE_BOOLEAN,    /* COLUMN_ENABLED */
 					G_TYPE_STRING,     /* COLUMN_PROTOCOL */
-					G_TYPE_POINTER,    /* COLUMN_DATA */
-					G_TYPE_POINTER     /* COLUMN_PULSE_DATA */
+					G_TYPE_POINTER     /* COLUMN_DATA */
 					);
 
 	/* And now the actual treeview */
--- a/pidgin/gtkblist.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkblist.c	Thu Jun 26 20:25:38 2008 +0000
@@ -3259,18 +3259,18 @@
 
 	/* Accounts menu */
 	{ N_("/_Accounts"), NULL, NULL, 0, "<Branch>", NULL },
-	{ N_("/Accounts/Manage"), "<CTL>A", pidgin_accounts_window_show, 0, "<Item>", NULL },
+	{ N_("/Accounts/Manage Accounts"), "<CTL>A", pidgin_accounts_window_show, 0, "<Item>", NULL },
 
 	/* Tools */
 	{ N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL },
 	{ N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 1, "<Item>", NULL },
 	{ N_("/Tools/_Certificates"), NULL, pidgin_certmgr_show, 0, "<Item>", NULL },
-	{ N_("/Tools/Smile_y"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
 	{ N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 2, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS },
 	{ N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
 	{ N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL },
+	{ N_("/Tools/Smile_y"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
 	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL },
-	{ N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<Item>", NULL },
+	{ N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_TRANSFER },
 	{ N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL },
 	{ N_("/Tools/System _Log"), NULL, gtk_blist_show_systemlog_cb, 3, "<Item>", NULL },
 	{ "/Tools/sep3", NULL, NULL, 0, "<Separator>", NULL },
@@ -5305,7 +5305,7 @@
 	tmp = g_strdup_printf(_("<span weight='bold' size='larger'>Welcome to %s!</span>\n\n"
 
 					       "You have no accounts enabled. Enable your IM accounts from the "
-					       "<b>Accounts</b> window at <b>Accounts->Manage</b>. Once you "
+					       "<b>Accounts</b> window at <b>Accounts->Manage Accounts</b>. Once you "
 					       "enable accounts, you'll be able to sign on, set your status, "
 					       "and talk to your friends."), PIDGIN_NAME);
 	pretty = pidgin_make_pretty_arrows(tmp);
@@ -6474,6 +6474,10 @@
 		purple_blist_add_buddy(b, NULL, g, NULL);
 		purple_account_add_buddy(data->account, b);
 
+		/* Offer to merge people with the same alias. */
+		if (whoalias != NULL)
+			gtk_blist_auto_personize((PurpleBlistNode *)g, whoalias);
+
 		/*
 		 * XXX
 		 * It really seems like it would be better if the call to
@@ -7609,12 +7613,58 @@
 	for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = g_list_delete_link(l, l)) {
 		menuitem = l->data;
 
-		if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Manage")))
+		if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Manage Accounts")))
 			gtk_widget_destroy(menuitem);
 	}
 
 	for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
 		char *buf = NULL;
+		GtkWidget *image = NULL;
+		PurpleAccount *account = NULL;
+		GdkPixbuf *pixbuf = NULL;
+
+		account = accounts->data;
+
+		if(!purple_account_get_enabled(account, PIDGIN_UI)) {
+			if (!disabled_accounts) {
+				menuitem = gtk_menu_item_new_with_label(_("Enable Account"));
+				gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
+				gtk_widget_show(menuitem);
+
+				submenu = gtk_menu_new();
+				gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
+				gtk_menu_set_accel_path(GTK_MENU(submenu), N_("<PurpleMain>/Accounts/Enable Account"));
+				gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+				gtk_widget_show(submenu);
+
+				disabled_accounts = TRUE;
+			}
+
+			buf = g_strconcat(purple_account_get_username(account), " (",
+				purple_account_get_protocol_name(account), ")", NULL);
+			menuitem = gtk_image_menu_item_new_with_label(buf);
+			g_free(buf);
+			pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+			if (pixbuf != NULL)
+			{
+				if (!purple_account_is_connected(account))
+					gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
+				image = gtk_image_new_from_pixbuf(pixbuf);
+				g_object_unref(G_OBJECT(pixbuf));
+				gtk_widget_show(image);
+				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
+			}
+			g_signal_connect(G_OBJECT(menuitem), "activate",
+				G_CALLBACK(enable_account_cb), account);
+			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+			gtk_widget_show(menuitem);
+		}
+	}
+
+	pidgin_separator(accountmenu);
+
+	for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
+		char *buf = NULL;
 		char *accel_path_buf = NULL;
 		GtkWidget *image = NULL;
 		PurpleConnection *gc = NULL;
@@ -7684,51 +7734,6 @@
 		}
 	}
 
-	if(disabled_accounts) {
-		pidgin_separator(accountmenu);
-		menuitem = gtk_menu_item_new_with_label(_("Enable Account"));
-		gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
-		gtk_widget_show(menuitem);
-
-		submenu = gtk_menu_new();
-		gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
-		gtk_menu_set_accel_path(GTK_MENU(submenu), N_("<PurpleMain>/Accounts/Enable Account"));
-		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-		gtk_widget_show(submenu);
-
-		for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
-			char *buf = NULL;
-			GtkWidget *image = NULL;
-			PurpleAccount *account = NULL;
-			GdkPixbuf *pixbuf = NULL;
-
-			account = accounts->data;
-
-			if(!purple_account_get_enabled(account, PIDGIN_UI)) {
-
-				disabled_accounts = TRUE;
-
-				buf = g_strconcat(purple_account_get_username(account), " (",
-						purple_account_get_protocol_name(account), ")", NULL);
-				menuitem = gtk_image_menu_item_new_with_label(buf);
-				g_free(buf);
-				pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
-				if (pixbuf != NULL)
-				{
-					if (!purple_account_is_connected(account))
-						gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
-					image = gtk_image_new_from_pixbuf(pixbuf);
-					g_object_unref(G_OBJECT(pixbuf));
-					gtk_widget_show(image);
-					gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
-				}
-				g_signal_connect(G_OBJECT(menuitem), "activate",
-						G_CALLBACK(enable_account_cb), account);
-				gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-				gtk_widget_show(menuitem);
-			}
-		}
-	}
 }
 
 static GList *plugin_submenus = NULL;
--- a/pidgin/gtkconv.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkconv.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1495,7 +1495,7 @@
 	PurpleAccount *account;
 	PurpleConnection *gc;
 	PurplePluginProtocolInfo *prpl_info = NULL;
-	char *real_who;
+	gchar *real_who = NULL;
 
 	account = purple_conversation_get_account(conv);
 	g_return_if_fail(account != NULL);
@@ -1508,13 +1508,11 @@
 	if (prpl_info && prpl_info->get_cb_real_name)
 		real_who = prpl_info->get_cb_real_name(gc,
 				purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
-	else
-		real_who = g_strdup(who);
-
-	if(!real_who)
+
+	if(!who && !real_who)
 		return;
 
-	pidgin_dialogs_im_with_user(account, real_who);
+	pidgin_dialogs_im_with_user(account, real_who ? real_who : who);
 
 	g_free(real_who);
 }
@@ -1553,11 +1551,22 @@
 static void
 menu_chat_send_file_cb(GtkWidget *w, PidginConversation *gtkconv)
 {
+	PurplePluginProtocolInfo *prpl_info;
 	PurpleConversation *conv = gtkconv->active_conv;
 	const char *who = g_object_get_data(G_OBJECT(w), "user_data");
 	PurpleConnection *gc  = purple_conversation_get_gc(conv);
-
-	serv_send_file(gc, who, NULL);
+	gchar *real_who = NULL;
+
+	g_return_if_fail(gc != NULL);
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if (prpl_info && prpl_info->get_cb_real_name)
+		real_who = prpl_info->get_cb_real_name(gc,
+				purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+
+	serv_send_file(gc, real_who ? real_who : who, NULL);
+	g_free(real_who);
 }
 
 static void
@@ -1673,23 +1682,34 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 
 
 		if (prpl_info && prpl_info->send_file)
 		{
+			gboolean can_receive_file = TRUE;
+
 			button = pidgin_new_item_from_stock(menu, _("Send File"),
 				PIDGIN_STOCK_TOOLBAR_SEND_FILE, G_CALLBACK(menu_chat_send_file_cb),
 				PIDGIN_CONVERSATION(conv), 0, 0, NULL);
 
-			if (gc == NULL || prpl_info == NULL ||
-			    !(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who)))
-			{
+			if (gc == NULL || prpl_info == NULL)
+				can_receive_file = FALSE;
+			else {
+				gchar *real_who = NULL;
+				if (prpl_info->get_cb_real_name)
+					real_who = prpl_info->get_cb_real_name(gc,
+						purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+				if (!(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, real_who ? real_who : who)))
+					can_receive_file = FALSE;
+				g_free(real_who);
+			}
+
+			if (!can_receive_file)
 				gtk_widget_set_sensitive(button, FALSE);
-			}
-
-			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+			else
+				g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 		}
 
 
@@ -1702,8 +1722,8 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
 	if (prpl_info && (prpl_info->get_info || prpl_info->get_cb_info)) {
@@ -1712,8 +1732,8 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
 	if (prpl_info && prpl_info->get_cb_away) {
@@ -1722,8 +1742,8 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
 	if (!is_me && prpl_info && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
@@ -1736,8 +1756,8 @@
 
 		if (gc == NULL)
 			gtk_widget_set_sensitive(button, FALSE);
-
-		g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+		else
+			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
 	button = pidgin_new_item_from_stock(menu, _("Last said"), GTK_STOCK_INDEX,
@@ -3181,7 +3201,8 @@
 			PurpleAccount *account = purple_conversation_get_account(conv);
 			PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
 			PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-			if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_info_defaults)) {
+			if (purple_account_get_connection(account) != NULL &&
+					PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_info_defaults)) {
 				components = prpl_info->chat_info_defaults(purple_account_get_connection(account),
 						purple_conversation_get_name(conv));
 			} else {
--- a/pidgin/gtkdialogs.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkdialogs.c	Thu Jun 26 20:25:38 2008 +0000
@@ -75,7 +75,7 @@
 	{"Thomas Butter",				N_("developer"), NULL},
 	{"Ka-Hing Cheung",				N_("developer"), NULL},
 	{"Sadrul Habib Chowdhury",		N_("developer"), NULL},
-	{"Mark 'KingAnt' Doliner",		N_("developer"), NULL},
+	{"Mark 'KingAnt' Doliner",		N_("developer"), "mark@kingant.net"},
 	{"Sean Egan",					N_("developer"), "sean.egan@gmail.com"},
 	{"Casey Harkins",               N_("developer"),   NULL},
 	{"Gary 'grim' Kramlich",		N_("developer"), NULL},
--- a/pidgin/gtkft.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkft.c	Thu Jun 26 20:25:38 2008 +0000
@@ -226,8 +226,10 @@
 			total_pct = 100 * total_bytes_xferred / total_file_size;
 		}
 
-		title = g_strdup_printf(_("File Transfers - %d%% of %d files"),
-				total_pct, num_active_xfers);
+		title = g_strdup_printf(ngettext("File Transfers - %d%% of %d file",
+						 "File Transfers - %d%% of %d files",
+						 num_active_xfers),
+					total_pct, num_active_xfers);
 		gtk_window_set_title(GTK_WINDOW(dialog->window), title);
 		g_free(title);
 	} else {
--- a/pidgin/gtkimhtml.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkimhtml.c	Thu Jun 26 20:25:38 2008 +0000
@@ -514,6 +514,7 @@
 			tmp);
 		g_free(tmp);
 
+		g_object_unref(layout);
 		return FALSE;
 	}
 
@@ -551,6 +552,7 @@
 	gtk_widget_show (imhtml->tip_window);
 
 	pango_font_metrics_unref(font_metrics);
+	g_object_unref(font);
 	g_object_unref(layout);
 
 	return FALSE;
@@ -766,7 +768,7 @@
 	gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &end,
 	                                   buf_x + event->area.width, buf_y + event->area.height);
 
-
+	gtk_text_iter_order(&start, &end);
 
 	cur = start;
 
@@ -4864,7 +4866,7 @@
 		gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox ? ebox : icon, anchor);
 	} else if (imhtml_smiley != NULL && (imhtml->format_functions & GTK_IMHTML_SMILEY)) {
 		anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
-		imhtml_smiley->anchors = g_slist_append(imhtml_smiley->anchors, anchor);
+		imhtml_smiley->anchors = g_slist_append(imhtml_smiley->anchors, g_object_ref(anchor));
 		if (ebox) {
 			GtkWidget *img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_MENU);
 			char *text = g_strdup(unescaped);
@@ -5023,9 +5025,9 @@
 			str += g_snprintf(str, sizeof(buf) - (str - buf),
 					"color: #%02x%02x%02x;",
 					color->red >> 8, color->green >> 8, color->blue >> 8);
-			gdk_color_free(color);
 			empty = FALSE;
 		}
+		gdk_color_free(color);
 
 		/* Background color */
 		g_object_get(obj, "background-set", &isset, "background-gdk", &color, NULL);
@@ -5033,9 +5035,9 @@
 			str += g_snprintf(str, sizeof(buf) - (str - buf),
 					"background: #%02x%02x%02x;",
 					color->red >> 8, color->green >> 8, color->blue >> 8);
-			gdk_color_free(color);
 			empty = FALSE;
 		}
+		gdk_color_free(color);
 
 		/* Underline */
 		g_object_get(obj, "underline-set", &isset, "underline", &ivalue, NULL);
@@ -5046,6 +5048,7 @@
 					break;
 				default:
 					str += g_snprintf(str, sizeof(buf) - (str - buf), "text-decoration: underline;");
+					empty = FALSE;
 			}
 		}
 
@@ -5097,6 +5100,38 @@
 	}
 }
 
+typedef struct {
+	GtkTextTag *tag;
+	char *end;
+	char *start;
+} PidginTextTagData;
+
+static PidginTextTagData *text_tag_data_new(GtkTextTag *tag)
+{
+	const char *start, *end;
+	PidginTextTagData *ret = NULL;
+
+	start = tag_to_html_start(tag);
+	if (!start || !*start)
+		return NULL;
+	end = tag_to_html_end(tag);
+	if (!end || !*end)
+		return NULL;
+
+	ret = g_new0(PidginTextTagData, 1);
+	ret->start = g_strdup(start);
+	ret->end = g_strdup(end);
+	ret->tag = tag;
+	return ret;
+}
+
+static void text_tag_data_destroy(PidginTextTagData *data)
+{
+	g_free(data->start);
+	g_free(data->end);
+	g_free(data);
+}
+
 static gboolean tag_ends_here(GtkTextTag *tag, GtkTextIter *iter, GtkTextIter *niter)
 {
 	return ((gtk_text_iter_has_tag(iter, GTK_TEXT_TAG(tag)) &&
@@ -5117,12 +5152,11 @@
 	gboolean is_rtl_message = FALSE;
 	GString *str = g_string_new("");
 	GSList *tags, *sl;
-	GQueue *q, *r;
+	GQueue *q;
 	GtkTextTag *tag;
+	PidginTextTagData *tagdata;
 
 	q = g_queue_new();
-	r = g_queue_new();
-
 
 	gtk_text_iter_order(start, end);
 	non_neutral_iter = next_iter = iter = *start;
@@ -5145,9 +5179,11 @@
 	for (sl = tags; sl; sl = sl->next) {
 		tag = sl->data;
 		if (!gtk_text_iter_toggles_tag(start, GTK_TEXT_TAG(tag))) {
-			if (strlen(tag_to_html_end(tag)) > 0)
-				g_string_append(str, tag_to_html_start(tag));
-			g_queue_push_tail(q, tag);
+			PidginTextTagData *data = text_tag_data_new(tag);
+			if (data) {
+				g_string_append(str, data->start);
+				g_queue_push_tail(q, data);
+			}
 		}
 	}
 	g_slist_free(tags);
@@ -5159,13 +5195,14 @@
 		for (sl = tags; sl; sl = sl->next) {
 			tag = sl->data;
 			if (gtk_text_iter_begins_tag(&iter, GTK_TEXT_TAG(tag))) {
-				if (strlen(tag_to_html_end(tag)) > 0)
-					g_string_append(str, tag_to_html_start(tag));
-				g_queue_push_tail(q, tag);
+				PidginTextTagData *data = text_tag_data_new(tag);
+				if (data) {
+					g_string_append(str, data->start);
+					g_queue_push_tail(q, data);
+				}
 			}
 		}
 
-
 		if (c == 0xFFFC) {
 			GtkTextChildAnchor* anchor = gtk_text_iter_get_child_anchor(&iter);
 			if (anchor) {
@@ -5191,28 +5228,33 @@
 		for (sl = tags; sl; sl = sl->next) {
 			tag = sl->data;
 			/** don't worry about non-printing tags ending */
-			if (tag_ends_here(tag, &iter, &next_iter) && strlen(tag_to_html_end(tag)) > 0) {
-
-				GtkTextTag *tmp;
-
-				while ((tmp = g_queue_pop_tail(q)) != tag) {
-					if (tmp == NULL)
-						break;
-
-					if (!tag_ends_here(tmp, &iter, &next_iter) && strlen(tag_to_html_end(tmp)) > 0)
+			if (tag_ends_here(tag, &iter, &next_iter) &&
+					strlen(tag_to_html_end(tag)) > 0 &&
+					strlen(tag_to_html_start(tag)) > 0) {
+
+				PidginTextTagData *tmp;
+				GQueue *r = g_queue_new();
+
+				while ((tmp = g_queue_pop_tail(q)) && tmp->tag != tag) {
+					g_string_append(str, tmp->end);
+					if (!tag_ends_here(tmp->tag, &iter, &next_iter))
 						g_queue_push_tail(r, tmp);
-					g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tmp)));
+					else
+						text_tag_data_destroy(tmp);
 				}
 
 				if (tmp == NULL)
 					purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n");
-				else
-					g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tag)));
+				else {
+					g_string_append(str, tmp->end);
+					text_tag_data_destroy(tmp);
+				}
 
 				while ((tmp = g_queue_pop_head(r))) {
-					g_string_append(str, tag_to_html_start(GTK_TEXT_TAG(tmp)));
+					g_string_append(str, tmp->start);
 					g_queue_push_tail(q, tmp);
 				}
+				g_queue_free(r);
 			}
 		}
 
@@ -5221,15 +5263,16 @@
 		gtk_text_iter_forward_char(&next_iter);
 	}
 
-	while ((tag = g_queue_pop_tail(q)))
-		g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tag)));
+	while ((tagdata = g_queue_pop_tail(q))) {
+		g_string_append(str, tagdata->end);
+		text_tag_data_destroy(tagdata);
+	}
 
 	/* Bi-directional text support - close tags */
 	if (is_rtl_message)
 		g_string_append(str, "</SPAN>");
 
 	g_queue_free(q);
-	g_queue_free(r);
 	return g_string_free(str, FALSE);
 }
 
@@ -5465,8 +5508,11 @@
 	}
 
 	for (current = smiley->anchors; current; current = g_slist_next(current)) {
-
-		icon = gtk_image_new_from_animation(smiley->icon);
+		anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
+		if (gtk_text_child_anchor_get_deleted(anchor))
+			icon = NULL;
+		else
+			icon = gtk_image_new_from_animation(smiley->icon);
 
 #ifdef DEBUG_CUSTOM_SMILEY
 		purple_debug_info("custom-smiley", "gtk_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n",
@@ -5476,7 +5522,6 @@
 			GList *wids;
 			gtk_widget_show(icon);
 
-			anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
 			wids = gtk_text_child_anchor_get_widgets(anchor);
 
 			g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", purple_unescape_html(smiley->smile), g_free);
@@ -5493,7 +5538,7 @@
 			}
 			g_list_free(wids);
 		}
-
+		g_object_unref(anchor);
 	}
 
 	g_slist_free(smiley->anchors);
--- a/pidgin/gtkimhtml.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkimhtml.h	Thu Jun 26 20:25:38 2008 +0000
@@ -854,11 +854,36 @@
  */
 void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
 
+/**
+ * Create a new GtkIMHtmlSmiley.
+ *
+ * @param file       The image file for the smiley
+ * @param shortcut   The key shortcut for the smiley
+ * @param hide       @c TRUE if the smiley should be hidden in the smiley dialog, @c FALSE otherwise
+ * @param flags      The smiley flags
+ *
+ * @return The newly created smiley
+ * @since 2.5.0
+ */
 GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide,
 		GtkIMHtmlSmileyFlags flags);
 
+/**
+ * Reload the image data for the smiley.
+ *
+ * @param smiley   The smiley to reload
+ *
+ * @since 2.5.0
+ */
 void gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley);
 
+/**
+ * Destroy a GtkIMHtmlSmiley.
+ *
+ * @param smiley   The smiley to destroy
+ *
+ * @since 2.5.0
+ */
 void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley);
 /*@}*/
 
--- a/pidgin/gtkimhtmltoolbar.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Thu Jun 26 20:25:38 2008 +0000
@@ -616,8 +616,7 @@
 
 static struct smiley_button_list *
 sort_smileys(struct smiley_button_list *ls, GtkIMHtmlToolbar *toolbar,
-			 int *width, const GtkIMHtmlSmiley *smiley,
-			 const GSList *custom_smileys)
+			 int *width, const GtkIMHtmlSmiley *smiley)
 {
 	GtkWidget *image;
 	GtkWidget *button;
@@ -627,6 +626,7 @@
 	const gchar *filename = smiley->file;
 	gchar *face = smiley->smile;
 	PurpleSmiley *psmiley = NULL;
+	gboolean supports_custom = (gtk_imhtml_get_format_functions(GTK_IMHTML(toolbar->imhtml)) & GTK_IMHTML_CUSTOM_SMILEY);
 
 	cur = g_new0(struct smiley_button_list, 1);
 	it = ls;
@@ -680,10 +680,12 @@
 	/* If this is a "non-custom" smiley, check to see if its shortcut is
 	  "shadowed" by any custom smiley. This can only happen if the connection
 	  is custom smiley-enabled */
-	if (psmiley && !(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
-		gtk_tooltips_set_tip(toolbar->tooltips, button,
-				_("This smiley is disabled because a custom smiley exists for this shortcut."),
-				NULL);
+	if (supports_custom && psmiley && !(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
+		gchar tip[128];
+		g_snprintf(tip, sizeof(tip), 
+			_("This smiley is disabled because a custom smiley exists for this shortcut:\n %s"),
+			face);
+		gtk_tooltips_set_tip(toolbar->tooltips, button, tip, NULL);
 		gtk_widget_set_sensitive(button, FALSE);
 	} else if (psmiley) {
 		/* Remove the button if the smiley is destroyed */
@@ -785,11 +787,14 @@
 	else
 		smileys = pidgin_themes_get_proto_smileys(NULL);
 
+	/* Note: prepend smileys to list to avoid O(n^2) overhead when there is
+	  a large number of smileys... need to revers the list after for the dialog
+	  work... */
 	while(smileys) {
 		GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) smileys->data;
 		if(!smiley->hidden) {
 			if(smiley_is_unique(unique_smileys, smiley)) {
-				unique_smileys = g_slist_append(unique_smileys, smiley);
+				unique_smileys = g_slist_prepend(unique_smileys, smiley);
 			}
 		}
 		smileys = smileys->next;
@@ -802,9 +807,12 @@
 		for (iterator = custom_smileys ; iterator ;
 			 iterator = g_slist_next(iterator)) {
 			GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) iterator->data;
-			unique_smileys = g_slist_append(unique_smileys, smiley);
+			unique_smileys = g_slist_prepend(unique_smileys, smiley);
 		}
 	}
+	
+	/* we need to reverse the list to get the smileys in the correct order */
+	unique_smileys = g_slist_reverse(unique_smileys);
 
 	dialog = pidgin_create_dialog(_("Smile!"), 0, "smiley_dialog", FALSE);
 	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
@@ -836,7 +844,7 @@
 		while (unique_smileys) {
 			GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) unique_smileys->data;
 			if (!smiley->hidden) {
-				ls = sort_smileys(ls, toolbar, &max_line_width, smiley, custom_smileys);
+				ls = sort_smileys(ls, toolbar, &max_line_width, smiley);
 			}
 			unique_smileys = g_slist_delete_link(unique_smileys, unique_smileys);
 		}
@@ -1124,7 +1132,10 @@
 	}
 
 	destroy_toolbar_font(NULL, NULL, toolbar);
-	destroy_smiley_dialog(toolbar);
+	if (toolbar->smiley_dialog != NULL) {
+		g_signal_handlers_disconnect_by_func(G_OBJECT(toolbar->smiley_dialog), close_smiley_dialog, toolbar);
+		destroy_smiley_dialog(toolbar);
+	}
 	destroy_toolbar_bgcolor(NULL, NULL, toolbar);
 	destroy_toolbar_fgcolor(NULL, NULL, toolbar);
 	close_link_dialog(toolbar);
--- a/pidgin/gtkmenutray.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkmenutray.c	Thu Jun 26 20:25:38 2008 +0000
@@ -84,19 +84,24 @@
 }
 
 static void
-pidgin_menu_tray_finalize(GObject *obj) {
+pidgin_menu_tray_finalize(GObject *obj)
+{
+	PidginMenuTray *tray = PIDGIN_MENU_TRAY(obj);
 #if 0
 	/* This _might_ be leaking, but I have a sneaking suspicion that the widget is
 	 * getting destroyed in GtkContainer's finalize function.  But if were are
 	 * leaking here, be sure to figure out why this causes a crash.
 	 *	-- Gary
 	 */
-	PidginMenuTray *tray = PIDGIN_MENU_TRAY(obj);
 
 	if(GTK_IS_WIDGET(tray->tray))
 		gtk_widget_destroy(GTK_WIDGET(tray->tray));
 #endif
 
+	if (tray->tooltips) {
+		gtk_object_sink(GTK_OBJECT(tray->tooltips));
+	}
+
 	G_OBJECT_CLASS(parent_class)->finalize(obj);
 }
 
--- a/pidgin/gtksavedstatuses.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtksavedstatuses.c	Thu Jun 26 20:25:38 2008 +0000
@@ -59,6 +59,7 @@
 	STATUS_WINDOW_COLUMN_MESSAGE,
 	/** A hidden column containing a pointer to the editor for this saved status. */
 	STATUS_WINDOW_COLUMN_WINDOW,
+	STATUS_WINDOW_COLUMN_ICON,
 	STATUS_WINDOW_NUM_COLUMNS
 };
 
@@ -80,6 +81,7 @@
 	STATUS_EDITOR_COLUMN_STATUS_ID,
 	STATUS_EDITOR_COLUMN_STATUS_NAME,
 	STATUS_EDITOR_COLUMN_STATUS_MESSAGE,
+	STATUS_EDITOR_COLUMN_STATUS_ICON,
 	STATUS_EDITOR_NUM_COLUMNS
 };
 
@@ -392,12 +394,35 @@
     g_list_free(sel_paths);
 }
 
+static const gchar *
+get_stock_icon_from_primitive(PurpleStatusPrimitive type)
+{
+	switch (type) {
+		case PURPLE_STATUS_AVAILABLE:
+			return PIDGIN_STOCK_STATUS_AVAILABLE;
+		case PURPLE_STATUS_AWAY:
+			return PIDGIN_STOCK_STATUS_AWAY;
+		case PURPLE_STATUS_EXTENDED_AWAY:
+			return PIDGIN_STOCK_STATUS_XA;
+		case PURPLE_STATUS_INVISIBLE:
+			return PIDGIN_STOCK_STATUS_INVISIBLE;
+		case PURPLE_STATUS_OFFLINE:
+			return PIDGIN_STOCK_STATUS_OFFLINE;
+		case PURPLE_STATUS_UNAVAILABLE:
+			return PIDGIN_STOCK_STATUS_BUSY;
+		default:
+			/* this shouldn't happen */
+			return NULL;
+	}
+}
+
 static void
 add_status_to_saved_status_list(GtkListStore *model, PurpleSavedStatus *saved_status)
 {
 	GtkTreeIter iter;
 	const char *title;
 	const char *type;
+	const gchar *icon;
 	char *message;
 
 	if (purple_savedstatus_is_transient(saved_status))
@@ -406,14 +431,16 @@
 	title = purple_savedstatus_get_title(saved_status);
 	type = purple_primitive_get_name_from_type(purple_savedstatus_get_type(saved_status));
 	message = purple_markup_strip_html(purple_savedstatus_get_message(saved_status));
+	icon = get_stock_icon_from_primitive(purple_savedstatus_get_type(saved_status));
 
 	gtk_list_store_append(model, &iter);
 	gtk_list_store_set(model, &iter,
+					   STATUS_WINDOW_COLUMN_ICON, icon,
 					   STATUS_WINDOW_COLUMN_TITLE, title,
 					   STATUS_WINDOW_COLUMN_TYPE, type,
 					   STATUS_WINDOW_COLUMN_MESSAGE, message,
 					   -1);
-	free(message);
+	g_free(message);
 }
 
 static void
@@ -479,7 +506,8 @@
 									   G_TYPE_STRING,
 									   G_TYPE_STRING,
 									   G_TYPE_STRING,
-									   G_TYPE_POINTER);
+									   G_TYPE_POINTER,
+									   G_TYPE_STRING);
 
 	/* Create the treeview */
 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
@@ -517,6 +545,10 @@
 	gtk_tree_view_column_set_sort_column_id(column,
 											STATUS_WINDOW_COLUMN_TYPE);
 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+	renderer = gtk_cell_renderer_pixbuf_new();
+	gtk_tree_view_column_pack_start(column, renderer, TRUE);
+	gtk_tree_view_column_add_attribute(column, renderer, "stock-id",
+									   STATUS_WINDOW_COLUMN_ICON);
 	renderer = gtk_cell_renderer_text_new();
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer, "text",
@@ -717,8 +749,8 @@
 }
 
 
-static gboolean
-status_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+static void
+status_editor_destroy_cb(GtkWidget *widget, gpointer user_data)
 {
 	StatusEditor *dialog = user_data;
 
@@ -726,19 +758,13 @@
 	g_free(dialog->original_title);
 	g_object_unref(G_OBJECT(dialog->model));
 	g_free(dialog);
-
-	return FALSE;
 }
 
 static void
 status_editor_cancel_cb(GtkButton *button, gpointer user_data)
 {
 	StatusEditor *dialog = user_data;
-
-	status_editor_remove_dialog(dialog);
 	gtk_widget_destroy(dialog->window);
-	g_free(dialog->original_title);
-	g_free(dialog);
 }
 
 static void
@@ -842,20 +868,11 @@
 	g_free(message);
 	g_free(unformatted);
 
-	status_editor_remove_dialog(dialog);
-	gtk_widget_destroy(dialog->window);
-	g_free(dialog->original_title);
-
-/*
-	if (status_window != NULL)
-	  add_status_to_saved_status_list(status_window->model, saved_status);
-*/
-
 	/* If they clicked on "Save & Use" or "Use," then activate the status */
 	if (button != dialog->save_button)
 		purple_savedstatus_activate(saved_status);
 
-	g_free(dialog);
+	gtk_widget_destroy(dialog->window);
 }
 
 static void
@@ -871,6 +888,26 @@
 }
 
 static GtkWidget *
+create_stock_item(const gchar *str, const gchar *icon)
+{
+	GtkWidget *menuitem = gtk_menu_item_new();
+	GtkWidget *label = gtk_label_new_with_mnemonic(str);
+	GtkWidget *hbox = gtk_hbox_new(FALSE, 4);
+	GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
+	GtkWidget *image = gtk_image_new_from_stock(icon, icon_size);;
+
+	gtk_widget_show(label);
+	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+
+	gtk_container_add(GTK_CONTAINER(menuitem), hbox);
+
+	return menuitem;
+}
+
+static GtkWidget *
 create_status_type_menu(PurpleStatusPrimitive type)
 {
 	int i;
@@ -889,7 +926,9 @@
 			 * status types, so don't show them in the list.
 			 */
 			continue;
-		item = gtk_menu_item_new_with_label(purple_primitive_get_name_from_type(i));
+
+		item = create_stock_item(purple_primitive_get_name_from_type(i),
+					get_stock_icon_from_primitive(i));
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 	}
 
@@ -945,6 +984,7 @@
 						   STATUS_EDITOR_COLUMN_STATUS_ID, NULL,
 						   STATUS_EDITOR_COLUMN_STATUS_NAME, NULL,
 						   STATUS_EDITOR_COLUMN_STATUS_MESSAGE, NULL,
+						   STATUS_EDITOR_COLUMN_STATUS_ICON, NULL,
 						   -1);
 	}
 }
@@ -990,6 +1030,10 @@
 	gtk_tree_view_column_set_title(column, _("Status"));
 	gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1);
 	gtk_tree_view_column_set_resizable(column, TRUE);
+	renderer = gtk_cell_renderer_pixbuf_new();
+	gtk_tree_view_column_pack_start(column, renderer, FALSE);
+	gtk_tree_view_column_add_attribute(column, renderer, "stock-id",
+			STATUS_EDITOR_COLUMN_STATUS_ICON);
 	renderer = gtk_cell_renderer_text_new();
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer, "text",
@@ -1016,6 +1060,7 @@
 {
 	GdkPixbuf *pixbuf;
 	const char *id = NULL, *name = NULL, *message = NULL;
+	PurpleStatusPrimitive prim = PURPLE_STATUS_UNSET;
 
 	pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 	if ((pixbuf != NULL) && !purple_account_is_connected(account))
@@ -1030,6 +1075,7 @@
 		type = purple_savedstatus_substatus_get_type(substatus);
 		id = purple_status_type_get_id(type);
 		name = purple_status_type_get_name(type);
+		prim = purple_status_type_get_primitive(type);
 		if (purple_status_type_get_attr(type, "message"))
 			message = purple_savedstatus_substatus_get_message(substatus);
 	}
@@ -1042,6 +1088,7 @@
 			STATUS_EDITOR_COLUMN_STATUS_ID, id,
 			STATUS_EDITOR_COLUMN_STATUS_NAME, name,
 			STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message,
+			STATUS_EDITOR_COLUMN_STATUS_ICON, get_stock_icon_from_primitive(prim),
 			-1);
 
 	if (pixbuf != NULL)
@@ -1133,7 +1180,7 @@
 
 	dialog->window = win = pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
 
-	g_signal_connect(G_OBJECT(win), "delete_event",
+	g_signal_connect(G_OBJECT(win), "destroy",
 					 G_CALLBACK(status_editor_destroy_cb), dialog);
 
 	/* Setup the vbox */
@@ -1198,6 +1245,7 @@
 									   G_TYPE_STRING,
 									   G_TYPE_STRING,
 									   G_TYPE_STRING,
+									   G_TYPE_STRING,
 									   G_TYPE_STRING);
 
 	/* Create the treeview */
@@ -1325,25 +1373,20 @@
 	}
 }
 
-static gboolean
-substatus_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+static void
+substatus_editor_destroy_cb(GtkWidget *widget, gpointer user_data)
 {
 	SubStatusEditor *dialog = user_data;
 
 	substatus_editor_remove_dialog(dialog);
 	g_free(dialog);
-
-	return FALSE;
 }
 
 static void
 substatus_editor_cancel_cb(GtkButton *button, gpointer user_data)
 {
 	SubStatusEditor *dialog = user_data;
-
-	substatus_editor_remove_dialog(dialog);
 	gtk_widget_destroy(dialog->window);
-	g_free(dialog);
 }
 
 
@@ -1356,12 +1399,11 @@
 	PurpleStatusType *type;
 	char *id = NULL;
 	char *message = NULL;
-	const char *name = NULL;
+	const char *name = NULL, *stock = NULL;
 
 	if (!gtk_combo_box_get_active_iter(dialog->box, &iter))
 	{
 		gtk_widget_destroy(dialog->window);
-		g_free(dialog);
 		return;
 	}
 
@@ -1372,6 +1414,7 @@
 	if (purple_status_type_get_attr(type, "message") != NULL)
 		message = gtk_imhtml_get_markup(GTK_IMHTML(dialog->message));
 	name = purple_status_type_get_name(type);
+	stock = get_stock_icon_from_primitive(purple_status_type_get_primitive(type));
 
 	status_editor = dialog->status_editor;
 
@@ -1383,13 +1426,13 @@
 						   STATUS_EDITOR_COLUMN_STATUS_NAME, name,
 						   STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message,
 						   STATUS_EDITOR_COLUMN_WINDOW, NULL,
+						   STATUS_EDITOR_COLUMN_STATUS_ICON, stock,
 						   -1);
 	}
 
 	gtk_widget_destroy(dialog->window);
 	g_free(id);
 	g_free(message);
-	g_free(dialog);
 }
 
 static void
@@ -1438,7 +1481,7 @@
 	dialog->window = win = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
 	g_free(tmp);
 
-	g_signal_connect(G_OBJECT(win), "delete_event",
+	g_signal_connect(G_OBJECT(win), "destroy",
 					 G_CALLBACK(substatus_editor_destroy_cb), dialog);
 
 	/* Setup the vbox */
@@ -1679,6 +1722,96 @@
 	return currently_selected;
 }
 
+static void
+pidgin_status_menu_update_iter(GtkWidget *combobox, GtkListStore *store, GtkTreeIter *iter,
+		PurpleSavedStatus *status)
+{
+	GdkPixbuf *pixbuf;
+
+	if (store == NULL)
+		store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
+
+	pixbuf = pidgin_create_status_icon(purple_savedstatus_get_type(status),
+			combobox, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
+	gtk_list_store_set(store, iter,
+			SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS,
+			SS_MENU_ICON_COLUMN, pixbuf,
+			SS_MENU_TEXT_COLUMN, purple_savedstatus_get_title(status),
+			SS_MENU_DATA_COLUMN, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status)),
+			SS_MENU_EMBLEM_COLUMN, GTK_STOCK_SAVE,
+			SS_MENU_EMBLEM_VISIBLE_COLUMN, TRUE,
+			-1);
+	if (pixbuf)
+		g_object_unref(G_OBJECT(pixbuf));
+}
+
+static gboolean
+pidgin_status_menu_find_iter(GtkListStore *store, GtkTreeIter *iter, PurpleSavedStatus *find)
+{
+	int type;
+	gpointer data;
+	time_t creation_time = purple_savedstatus_get_creation_time(find);
+	GtkTreeModel *model = GTK_TREE_MODEL(store);
+
+	if (!gtk_tree_model_get_iter_first(model, iter))
+		return FALSE;
+
+	do {
+		gtk_tree_model_get(model, iter,
+				SS_MENU_TYPE_COLUMN, &type,
+				SS_MENU_DATA_COLUMN, &data,
+				-1);
+		if (type == SS_MENU_ENTRY_TYPE_PRIMITIVE)
+			continue;
+		if (GPOINTER_TO_INT(data) == creation_time)
+			return TRUE;
+	} while (gtk_tree_model_iter_next(model, iter));
+
+	return FALSE;
+}
+
+static void
+savedstatus_added_cb(PurpleSavedStatus *status, GtkWidget *combobox)
+{
+	GtkListStore *store;
+	GtkTreeIter iter;
+
+	if (purple_savedstatus_is_transient(status))
+		return;
+
+	store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
+	gtk_list_store_append(store, &iter);
+	pidgin_status_menu_update_iter(combobox, store, &iter, status);
+}
+
+static void
+savedstatus_deleted_cb(PurpleSavedStatus *status, GtkWidget *combobox)
+{
+	GtkListStore *store;
+	GtkTreeIter iter;
+
+	if (purple_savedstatus_is_transient(status))
+		return;
+
+	store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
+	if (pidgin_status_menu_find_iter(store, &iter, status))
+		gtk_list_store_remove(store, &iter);
+}
+
+static void
+savedstatus_modified_cb(PurpleSavedStatus *status, GtkWidget *combobox)
+{
+	GtkListStore *store;
+	GtkTreeIter iter;
+
+	if (purple_savedstatus_is_transient(status))
+		return;
+
+	store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
+	if (pidgin_status_menu_find_iter(store, &iter, status))
+		pidgin_status_menu_update_iter(combobox, store, &iter, status);
+}
+
 GtkWidget *pidgin_status_menu(PurpleSavedStatus *current_status, GCallback callback)
 {
 	GtkWidget *combobox;
@@ -1686,7 +1819,6 @@
 	GList *sorted, *cur;
 	int i = 0;
 	int index = -1;
-	GdkPixbuf *pixbuf;
 	GtkTreeIter iter;
 	GtkCellRenderer *text_rend;
 	GtkCellRenderer *icon_rend;
@@ -1720,18 +1852,9 @@
 		PurpleSavedStatus *status = (PurpleSavedStatus *) cur->data;
 		if (!purple_savedstatus_is_transient(status))
 		{
-			pixbuf = pidgin_create_status_icon(purple_savedstatus_get_type(status),
-							combobox, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
 			gtk_list_store_append(model, &iter);
-			gtk_list_store_set(model, &iter,
-				SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS,
-				SS_MENU_ICON_COLUMN, pixbuf,
-				SS_MENU_TEXT_COLUMN, purple_savedstatus_get_title(status),
-				SS_MENU_DATA_COLUMN, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status)),
-				SS_MENU_EMBLEM_COLUMN, GTK_STOCK_SAVE,
-				SS_MENU_EMBLEM_VISIBLE_COLUMN, TRUE,
-				-1);
-			g_object_unref(G_OBJECT(pixbuf));
+
+			pidgin_status_menu_update_iter(combobox, model, &iter, status);
 
 			if (status == current_status)
 				index = i;
@@ -1756,6 +1879,17 @@
 	gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), index);
 	g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(status_menu_cb), callback);
 
+	/* Make sure the list is updated dynamically when a substatus is changed/deleted
+	 * or a new one is added. */
+	purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-added",
+			combobox, G_CALLBACK(savedstatus_added_cb), combobox);
+	purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-deleted",
+			combobox, G_CALLBACK(savedstatus_deleted_cb), combobox);
+	purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-modified",
+			combobox, G_CALLBACK(savedstatus_modified_cb), combobox);
+	g_signal_connect(G_OBJECT(combobox), "destroy",
+			G_CALLBACK(purple_signals_disconnect_by_handle), NULL);
+
 	return combobox;
 }
 
--- a/pidgin/gtksmiley.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtksmiley.c	Thu Jun 26 20:25:38 2008 +0000
@@ -96,6 +96,18 @@
 	gtksmiley->smile = g_strdup(purple_smiley_get_shortcut(smiley));
 }
 
+static void
+image_changed_cb(PurpleSmiley *smiley, gpointer dontcare, GtkIMHtmlSmiley *gtksmiley)
+{
+	const char *file;
+
+	g_free(gtksmiley->file);
+
+	file = purple_imgstore_get_filename(purple_smiley_get_stored_image(smiley));
+	gtksmiley->file = g_build_filename(purple_smileys_get_storing_dir(), file, NULL);
+	gtk_imhtml_smiley_reload(gtksmiley);
+}
+
 static GtkIMHtmlSmiley *smiley_purple_to_gtkimhtml(PurpleSmiley *smiley)
 {
 	GtkIMHtmlSmiley *gtksmiley;
@@ -114,6 +126,10 @@
 	g_signal_connect(G_OBJECT(smiley), "notify::shortcut",
 			G_CALLBACK(shortcut_changed_cb), gtksmiley);
 
+	/* And update the pixbuf too when the image is changed */
+	g_signal_connect(G_OBJECT(smiley), "notify::image",
+			G_CALLBACK(image_changed_cb), gtksmiley);
+
 	return gtksmiley;
 }
 
--- a/pidgin/gtkstatusbox.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkstatusbox.c	Thu Jun 26 20:25:38 2008 +0000
@@ -209,7 +209,8 @@
 	for (l = purple_account_get_status_types(account); l != NULL; l = l->next) {
 		PurpleStatusType *status_type = (PurpleStatusType *)l->data;
 
-		if (!purple_status_type_is_user_settable(status_type))
+		if (!purple_status_type_is_user_settable(status_type) ||
+				purple_status_type_is_independent(status_type))
 			continue;
 		status_no++;
 		if (statustype == status_type)
@@ -769,7 +770,8 @@
 
 	for (i = 0; l; l = l->next) {
 		PurpleStatusType *status_type = l->data;
-		if (!purple_status_type_is_user_settable(status_type))
+		if (!purple_status_type_is_user_settable(status_type) ||
+				purple_status_type_is_independent(status_type))
 			continue;
 
 		if (active == i)
@@ -1030,12 +1032,13 @@
 		PurpleStatusType *status_type = (PurpleStatusType *)l->data;
 		PurpleStatusPrimitive prim;
 
-		if (!purple_status_type_is_user_settable(status_type))
+		if (!purple_status_type_is_user_settable(status_type) ||
+				purple_status_type_is_independent(status_type))
 			continue;
 
-            	prim = purple_status_type_get_primitive(status_type);
-
-                pixbuf = pidgin_status_box_get_pixbuf(status_box, prim);
+		prim = purple_status_type_get_primitive(status_type);
+
+		pixbuf = pidgin_status_box_get_pixbuf(status_box, prim);
 
 		pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box),
 					PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, pixbuf,
@@ -1706,6 +1709,48 @@
 }
 
 static void
+treeview_cursor_changed_cb(GtkTreeView *treeview, gpointer data)
+{
+	GtkTreeSelection *sel = gtk_tree_view_get_selection (treeview);
+	GtkTreeModel *model = GTK_TREE_MODEL (data);
+	GtkTreeIter iter;
+	GtkTreePath *cursor;
+	GtkTreePath *selection;
+	gint cmp;
+
+	if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
+		if ((selection = gtk_tree_model_get_path (model, &iter)) == NULL) {
+			/* Shouldn't happen, but ignore anyway */
+			return;
+		}
+	} else {
+		/* I don't think this can happen, but we'll just ignore it */
+		return;
+	}
+
+	gtk_tree_view_get_cursor (treeview, &cursor, NULL);
+	if (cursor == NULL) {
+		/* Probably won't happen in a 'cursor-changed' event? */
+		gtk_tree_path_free (selection);
+		return;
+	}
+
+	cmp = gtk_tree_path_compare (cursor, selection);
+	if (cmp < 0) {
+		/* The cursor moved up without moving the selection, so move it up again */
+		gtk_tree_path_prev (cursor);
+		gtk_tree_view_set_cursor (treeview, cursor, NULL, FALSE);
+	} else if (cmp > 0) {
+		/* The cursor moved down without moving the selection, so move it down again */
+		gtk_tree_path_next (cursor);
+		gtk_tree_view_set_cursor (treeview, cursor, NULL, FALSE);
+	}
+
+	gtk_tree_path_free (selection);
+	gtk_tree_path_free (cursor);
+}
+
+static void
 pidgin_status_box_init (PidginStatusBox *status_box)
 {
 	GtkCellRenderer *text_rend;
@@ -1869,6 +1914,8 @@
 					G_CALLBACK(imhtml_scroll_event_cb), status_box->imhtml);
 	g_signal_connect(G_OBJECT(status_box->popup_window), "button_release_event", G_CALLBACK(treeview_button_release_cb), status_box);
 	g_signal_connect(G_OBJECT(status_box->popup_window), "key_press_event", G_CALLBACK(treeview_key_press_event), status_box);
+	g_signal_connect(G_OBJECT(status_box->tree_view), "cursor-changed",
+					 G_CALLBACK(treeview_cursor_changed_cb), status_box->dropdown_store);
 
 #if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_view_set_row_separator_func(GTK_TREE_VIEW(status_box->tree_view), dropdown_store_row_separator_func, NULL, NULL);
--- a/pidgin/gtkutils.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/gtkutils.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1001,13 +1001,14 @@
 	}
 
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl);
+	if (prpl_info != NULL && prpl_info->get_cb_real_name)
+		who = prpl_info->get_cb_real_name(conn, chat, name);
 	if (prpl_info == NULL || prpl_info->get_cb_info == NULL) {
-		pidgin_retrieve_user_info(conn, name);
+		pidgin_retrieve_user_info(conn, who ? who : name);
+		g_free(who);
 		return;
 	}
 
-	if (prpl_info->get_cb_real_name)
-		who = prpl_info->get_cb_real_name(conn, chat, name);
 	show_retrieveing_info(conn, who ? who : name);
 	prpl_info->get_cb_info(conn, chat, name);
 	g_free(who);
--- a/pidgin/pidginstock.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pidginstock.c	Thu Jun 26 20:25:38 2008 +0000
@@ -168,6 +168,7 @@
 	{ PIDGIN_STOCK_TOOLBAR_UNBLOCK, "toolbar", "unblock.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, "toolbar", "select-avatar.png", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_SEND_FILE, "toolbar", "send-file.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+	{ PIDGIN_STOCK_TOOLBAR_TRANSFER, "toolbar", "transfer.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 
 #ifdef USE_VV
 	{ PIDGIN_STOCK_TOOLBAR_AUDIO_CALL, "toolbar", "audio-call.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
@@ -214,39 +215,39 @@
 
 	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL);
 	source = gtk_icon_source_new();
-	gtk_icon_source_set_filename(source, filename);
+        gtk_icon_source_set_filename(source, filename);
 	gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR);
-	gtk_icon_source_set_direction_wildcarded(source, !rtl);
+        gtk_icon_source_set_direction_wildcarded(source, !rtl);
 	gtk_icon_source_set_size(source, sizeid);
-	gtk_icon_source_set_size_wildcarded(source, FALSE);
-	gtk_icon_source_set_state_wildcarded(source, TRUE);
-	gtk_icon_set_add_source(iconset, source);
+        gtk_icon_source_set_size_wildcarded(source, FALSE);
+        gtk_icon_source_set_state_wildcarded(source, TRUE);
+        gtk_icon_set_add_source(iconset, source);
 	gtk_icon_source_free(source);
 
 	if (sizeid == gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)) {
 		source = gtk_icon_source_new();
-		gtk_icon_source_set_filename(source, filename);
-		gtk_icon_source_set_direction_wildcarded(source, TRUE);
-		gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU);
-		gtk_icon_source_set_size_wildcarded(source, FALSE);
-		gtk_icon_source_set_state_wildcarded(source, TRUE);
-		gtk_icon_set_add_source(iconset, source);
-		gtk_icon_source_free(source);
+	        gtk_icon_source_set_filename(source, filename);
+        	gtk_icon_source_set_direction_wildcarded(source, TRUE);
+	        gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU);
+	        gtk_icon_source_set_size_wildcarded(source, FALSE);
+        	gtk_icon_source_set_state_wildcarded(source, TRUE);
+	        gtk_icon_set_add_source(iconset, source);
+	        gtk_icon_source_free(source);
 	}
-	g_free(filename);
+        g_free(filename);
 
-	if (rtl) {
+       if (rtl) {
 		filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL);
-		source = gtk_icon_source_new();
-		gtk_icon_source_set_filename(source, filename);
-		gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL);
-		gtk_icon_source_set_size(source, sizeid);
-		gtk_icon_source_set_size_wildcarded(source, FALSE);
-		gtk_icon_source_set_state_wildcarded(source, TRUE);
-		gtk_icon_set_add_source(iconset, source);
+                source = gtk_icon_source_new();
+                gtk_icon_source_set_filename(source, filename);
+                gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL);
+                gtk_icon_source_set_size(source, sizeid);
+                gtk_icon_source_set_size_wildcarded(source, FALSE);
+                gtk_icon_source_set_state_wildcarded(source, TRUE);
+                gtk_icon_set_add_source(iconset, source);
 		g_free(filename);
 		gtk_icon_source_free(source);
-	}
+        }
 
 
 }
--- a/pidgin/pidginstock.h	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pidginstock.h	Thu Jun 26 20:25:38 2008 +0000
@@ -129,6 +129,7 @@
 #define PIDGIN_STOCK_TOOLBAR_UNBLOCK      "pidgin-unblock"
 #define PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR "pidgin-select-avatar"
 #define PIDGIN_STOCK_TOOLBAR_SEND_FILE    "pidgin-send-file"
+#define PIDGIN_STOCK_TOOLBAR_TRANSFER     "pidgin-transfer"
 #ifdef USE_VV
 #define PIDGIN_STOCK_TOOLBAR_AUDIO_CALL   "pidgin-audio-call"
 #define PIDGIN_STOCK_TOOLBAR_VIDEO_CALL   "pidgin-video-call"
--- a/pidgin/pixmaps/Makefile.am	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pixmaps/Makefile.am	Thu Jun 26 20:25:38 2008 +0000
@@ -566,6 +566,7 @@
 		toolbar/16/message-new.png \
 		toolbar/16/plugins.png \
 		toolbar/16/send-file.png \
+		toolbar/16/transfer.png \
 		toolbar/16/unblock.png \
 		toolbar/16/video-call.png
 
Binary file pidgin/pixmaps/status/16/available.png has changed
Binary file pidgin/pixmaps/status/16/away.png has changed
Binary file pidgin/pixmaps/status/16/busy.png has changed
Binary file pidgin/pixmaps/status/16/chat.png has changed
Binary file pidgin/pixmaps/status/16/offline.png has changed
Binary file pidgin/pixmaps/status/16/person.png has changed
--- a/pidgin/pixmaps/status/16/scalable/available.svg	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pixmaps/status/16/scalable/available.svg	Thu Jun 26 20:25:38 2008 +0000
@@ -2,7 +2,7 @@
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:cc="http://creativecommons.org/ns#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
@@ -13,9 +13,9 @@
    height="16"
    id="svg2"
    sodipodi:version="0.32"
-   inkscape:version="0.45"
+   inkscape:version="0.46+devel"
    version="1.0"
-   inkscape:export-filename="/home/hbons/GUI/Tango/Gaim Refresh/status/16/available.png"
+   inkscape:export-filename="/home/hbons/Desktop/available.png"
    inkscape:export-xdpi="90"
    inkscape:export-ydpi="90"
    sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable"
@@ -26,6 +26,25 @@
      id="defs4">
     <linearGradient
        inkscape:collect="always"
+       id="linearGradient3286">
+      <stop
+         style="stop-color:#459000;stop-opacity:1"
+         offset="0"
+         id="stop3288" />
+      <stop
+         style="stop-color:#204300;stop-opacity:1"
+         offset="1"
+         id="stop3290" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 8 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="16 : 8 : 1"
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
+       id="perspective43" />
+    <linearGradient
+       inkscape:collect="always"
        id="linearGradient2898">
       <stop
          style="stop-color:white;stop-opacity:1;"
@@ -50,10 +69,10 @@
        xlink:href="#linearGradient3149"
        id="linearGradient4740"
        gradientUnits="userSpaceOnUse"
-       x1="15.498499"
-       y1="9.4211226"
-       x2="24.240097"
-       y2="36.603138" />
+       x1="11.127699"
+       y1="10.823074"
+       x2="30.341434"
+       y2="31.325201" />
     <linearGradient
        inkscape:collect="always"
        xlink:href="#linearGradient3149"
@@ -230,6 +249,39 @@
        fx="31.112698"
        fy="19.008621"
        r="8.6620579" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3286"
+       id="linearGradient3292"
+       x1="15.893391"
+       y1="15.213944"
+       x2="26.533659"
+       y2="28.579245"
+       gradientUnits="userSpaceOnUse" />
+    <filter
+       inkscape:collect="always"
+       id="filter3360"
+       x="-0.13093077"
+       width="1.2618615"
+       y="-0.11042095"
+       height="1.2208419">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.19314814"
+         id="feGaussianBlur3362" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3374"
+       x="-0.13117394"
+       width="1.2623479"
+       y="-0.11015608"
+       height="1.2203122">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.19309804"
+         id="feGaussianBlur3376" />
+    </filter>
   </defs>
   <sodipodi:namedview
      id="base"
@@ -239,16 +291,27 @@
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
      inkscape:zoom="25.992076"
-     inkscape:cx="4.1907826"
-     inkscape:cy="8.6773979"
+     inkscape:cx="12.058565"
+     inkscape:cy="10.562588"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
      fill="#eeeeec"
-     inkscape:window-width="1274"
-     inkscape:window-height="844"
-     inkscape:window-x="3"
-     inkscape:window-y="25" />
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="0"
+     inkscape:window-y="22"
+     inkscape:snap-bbox="true"
+     inkscape:snap-nodes="false"
+     objecttolerance="13"
+     gridtolerance="10">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3284"
+       empspacing="5"
+       visible="true"
+       enabled="true" />
+  </sodipodi:namedview>
   <metadata
      id="metadata7">
     <rdf:RDF>
@@ -265,14 +328,24 @@
      inkscape:groupmode="layer"
      id="layer1">
     <path
-       transform="matrix(0.538297,0,0,0.538297,-1.630177,-1.459246)"
-       style="fill:url(#linearGradient4738);fill-opacity:1;fill-rule:evenodd;stroke:#306300;stroke-width:1.85770929;stroke-miterlimit:4;stroke-opacity:1"
+       style="fill:url(#linearGradient4738);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3292);stroke-width:1.85770929000000007;stroke-miterlimit:4;stroke-opacity:1"
        d="M 31.822886,17.572527 C 31.822886,25.263442 25.580983,31.505344 17.890068,31.505344 C 10.199153,31.505344 3.9572506,25.263442 3.9572506,17.572527 C 3.9572506,9.8816117 10.199153,3.6397095 17.890068,3.6397095 C 25.580983,3.6397095 31.822886,9.8816117 31.822886,17.572527 z "
-       id="path4331" />
+       id="path4331"
+       transform="matrix(0.538297,0,0,0.538297,-1.630177,-1.459246)" />
+    <path
+       style="opacity:0.59999999999999998;fill:url(#linearGradient4740);fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:2.14350747999999980;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 31.822886,17.572527 C 31.822886,25.263442 25.580983,31.505344 17.890068,31.505344 C 10.199153,31.505344 3.9572506,25.263442 3.9572506,17.572527 C 3.9572506,9.8816117 10.199153,3.6397095 17.890068,3.6397095 C 25.580983,3.6397095 31.822886,9.8816117 31.822886,17.572527 z "
+       id="path4333"
+       transform="matrix(0.466524,0,0,0.466525,-0.346154,-0.198015)" />
     <path
-       transform="matrix(0.466524,0,0,0.466525,-0.346154,-0.198015)"
-       style="opacity:0.6;fill:url(#linearGradient4740);fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:2.14350748;stroke-miterlimit:4;stroke-opacity:1"
-       d="M 31.822886,17.572527 C 31.822886,25.263442 25.580983,31.505344 17.890068,31.505344 C 10.199153,31.505344 3.9572506,25.263442 3.9572506,17.572527 C 3.9572506,9.8816117 10.199153,3.6397095 17.890068,3.6397095 C 25.580983,3.6397095 31.822886,9.8816117 31.822886,17.572527 z "
-       id="path4333" />
+       sodipodi:type="arc"
+       style="opacity:0.19499996000000000;fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3374)"
+       id="path3302"
+       sodipodi:cx="11.484269"
+       sodipodi:cy="4.7465701"
+       sodipodi:rx="1.8659533"
+       sodipodi:ry="1.9428998"
+       d="m 13.350222,4.7465701 a 1.8659533,1.9428998 0 1 1 -3.7319067,0 A 1.8659533,1.9428998 0 1 1 13.350222,4.7465701 z"
+       transform="matrix(2.2014717,-1.281888,0.9447394,1.6503281,-23.266565,12.888149)" />
   </g>
 </svg>
--- a/pidgin/pixmaps/status/16/scalable/away.svg	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pixmaps/status/16/scalable/away.svg	Thu Jun 26 20:25:38 2008 +0000
@@ -2,7 +2,7 @@
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:cc="http://creativecommons.org/ns#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
@@ -13,9 +13,9 @@
    height="16"
    id="svg2"
    sodipodi:version="0.32"
-   inkscape:version="0.45"
+   inkscape:version="0.46+devel"
    version="1.0"
-   inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/status/16/available16.png"
+   inkscape:export-filename="/home/hbons/Desktop/away.png"
    inkscape:export-xdpi="90"
    inkscape:export-ydpi="90"
    sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable"
@@ -26,6 +26,37 @@
      id="defs4">
     <linearGradient
        inkscape:collect="always"
+       id="linearGradient3284">
+      <stop
+         style="stop-color:#173867;stop-opacity:1;"
+         offset="0"
+         id="stop3286" />
+      <stop
+         style="stop-color:#173867;stop-opacity:0;"
+         offset="1"
+         id="stop3288" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3276">
+      <stop
+         style="stop-color:#2863b7;stop-opacity:1"
+         offset="0"
+         id="stop3278" />
+      <stop
+         style="stop-color:#14325c;stop-opacity:1"
+         offset="1"
+         id="stop3280" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 8 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="16 : 8 : 1"
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
+       id="perspective35" />
+    <linearGradient
+       inkscape:collect="always"
        id="linearGradient2812">
       <stop
          style="stop-color:#2e3436;stop-opacity:1;"
@@ -94,9 +125,9 @@
        xlink:href="#linearGradient2804"
        id="linearGradient2810"
        x1="4.5264969"
-       y1="2.7991772"
-       x2="10.623409"
-       y2="11.024895"
+       y1="2.6807978"
+       x2="9.7444448"
+       y2="9.9594812"
        gradientUnits="userSpaceOnUse" />
     <radialGradient
        inkscape:collect="always"
@@ -108,6 +139,24 @@
        fy="19.008621"
        r="8.6620579"
        gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3276"
+       id="linearGradient3282"
+       x1="15.377563"
+       y1="12.744186"
+       x2="22.868998"
+       y2="29.821121"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3284"
+       id="linearGradient3290"
+       x1="23.221344"
+       y1="24.700239"
+       x2="8.2601509"
+       y2="0.92288947"
+       gradientUnits="userSpaceOnUse" />
   </defs>
   <sodipodi:namedview
      id="base"
@@ -117,8 +166,8 @@
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
      inkscape:zoom="25.992076"
-     inkscape:cx="14.729231"
-     inkscape:cy="2.7799575"
+     inkscape:cx="13.286484"
+     inkscape:cy="9.6281985"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
@@ -126,11 +175,22 @@
      inkscape:window-width="1434"
      inkscape:window-height="840"
      inkscape:window-x="3"
-     inkscape:window-y="25"
+     inkscape:window-y="27"
      inkscape:object-paths="false"
      inkscape:grid-bbox="true"
      inkscape:guide-bbox="false"
-     inkscape:grid-points="true" />
+     inkscape:grid-points="true"
+     objecttolerance="14"
+     gridtolerance="18"
+     inkscape:snap-bbox="true"
+     inkscape:snap-nodes="false">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3274"
+       empspacing="5"
+       visible="true"
+       enabled="true" />
+  </sodipodi:namedview>
   <metadata
      id="metadata7">
     <rdf:RDF>
@@ -148,7 +208,7 @@
      id="layer1">
     <path
        sodipodi:type="arc"
-       style="opacity:1;color:#000000;fill:#729fcf;fill-opacity:1;fill-rule:evenodd;stroke:#173867;stroke-width:1.91314828px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       style="opacity:1;color:#000000;fill:#729fcf;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3282);stroke-width:1.91314827999999992px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
        id="path1339"
        sodipodi:cx="15.590227"
        sodipodi:cy="16.57217"
@@ -168,7 +228,7 @@
        transform="matrix(2.192102,0,0,2.091316,16.34939,1.090661)" />
     <path
        sodipodi:type="arc"
-       style="opacity:0.40340911;color:black;fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:#173867;stroke-width:2.60821199px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       style="opacity:0.40340911000000002;color:black;fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3290);stroke-width:2.60821199000000004px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
        id="path1341"
        sodipodi:cx="15.590227"
        sodipodi:cy="16.57217"
@@ -188,7 +248,7 @@
        transform="matrix(3.094296,0,0,3.766968,-10.69048,-20.45989)" />
     <path
        sodipodi:type="arc"
-       style="opacity:0.76704544;fill:none;fill-opacity:1;stroke:url(#linearGradient2810);stroke-width:0.80677563;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       style="opacity:0.76704543999999986;fill:none;fill-opacity:1;stroke:url(#linearGradient2810);stroke-width:0.80677562999999985;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
        id="path2802"
        sodipodi:cx="7.5"
        sodipodi:cy="7"
@@ -197,7 +257,7 @@
        d="M 13 7 A 5.5 5 0 1 1  2,7 A 5.5 5 0 1 1  13 7 z"
        transform="matrix(1.18182,0,0,1.3,-0.86365,-1.1)" />
     <rect
-       style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       style="opacity:1;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
        id="rect2820"
        width="1"
        height="1"
@@ -231,7 +291,7 @@
        y="-10"
        transform="matrix(0,1,-1,0,0,0)" />
     <rect
-       style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       style="opacity:1;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
        id="rect2846"
        width="1"
        height="1"
--- a/pidgin/pixmaps/status/16/scalable/busy.svg	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pixmaps/status/16/scalable/busy.svg	Thu Jun 26 20:25:38 2008 +0000
@@ -2,7 +2,7 @@
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:cc="http://creativecommons.org/ns#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
@@ -13,9 +13,9 @@
    height="16"
    id="svg2"
    sodipodi:version="0.32"
-   inkscape:version="0.45"
+   inkscape:version="0.46+devel"
    version="1.0"
-   inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/status/16/available16.png"
+   inkscape:export-filename="/home/hbons/Desktop/busy.png"
    inkscape:export-xdpi="90"
    inkscape:export-ydpi="90"
    sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable"
@@ -26,6 +26,37 @@
      id="defs4">
     <linearGradient
        inkscape:collect="always"
+       id="linearGradient3290">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0"
+         id="stop3292" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3294" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3280">
+      <stop
+         style="stop-color:#a60000;stop-opacity:1"
+         offset="0"
+         id="stop3282" />
+      <stop
+         style="stop-color:#460000;stop-opacity:1"
+         offset="1"
+         id="stop3284" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 8 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="16 : 8 : 1"
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
+       id="perspective39" />
+    <linearGradient
+       inkscape:collect="always"
        id="linearGradient2898">
       <stop
          style="stop-color:white;stop-opacity:1;"
@@ -71,28 +102,6 @@
        r="8.6620579" />
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient2186">
-      <stop
-         style="stop-color:#ffffff;stop-opacity:1;"
-         offset="0"
-         id="stop2188" />
-      <stop
-         style="stop-color:#ffffff;stop-opacity:0;"
-         offset="1"
-         id="stop2190" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient2186"
-       id="linearGradient2194"
-       x1="9.2594385"
-       y1="-1.5641226"
-       x2="11.226587"
-       y2="17.697369"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(0.684526,0,0,0.687171,-0.20455,-0.253325)" />
-    <linearGradient
-       inkscape:collect="always"
        id="linearGradient2239">
       <stop
          style="stop-color:#ffffff;stop-opacity:1;"
@@ -107,12 +116,12 @@
        inkscape:collect="always"
        xlink:href="#linearGradient2239"
        id="linearGradient2245"
-       x1="8.7505674"
-       y1="4.5934086"
-       x2="31.18539"
-       y2="39.834526"
+       x1="-1.5418521"
+       y1="-6.2826729"
+       x2="63.127094"
+       y2="59.183727"
        gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(0.482882,0,0,0.482874,0.269812,0.26982)" />
+       gradientTransform="matrix(0.482882,0,0,0.482874,0.269812,0.2698205)" />
     <linearGradient
        inkscape:collect="always"
        xlink:href="#linearGradient3149"
@@ -177,6 +186,240 @@
        x2="12.233074"
        y2="27.77807"
        gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3280"
+       id="linearGradient3286"
+       x1="11.549973"
+       y1="7.078577"
+       x2="33.836056"
+       y2="45.494511"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290"
+       id="linearGradient3282"
+       x1="5.8424845"
+       y1="-1.2794704"
+       x2="10.945268"
+       y2="11.145247"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="28.579245"
+       x2="26.533659"
+       y1="15.213944"
+       x1="15.893391"
+       id="linearGradient3292"
+       xlink:href="#linearGradient3280"
+       inkscape:collect="always" />
+    <radialGradient
+       r="8.6620579"
+       fy="19.008621"
+       fx="31.112698"
+       cy="19.008621"
+       cx="31.112698"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2621"
+       xlink:href="#linearGradient2812"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="11.024895"
+       x2="10.623409"
+       y1="2.7991772"
+       x1="4.5264969"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3007"
+       xlink:href="#linearGradient2804"
+       inkscape:collect="always" />
+    <radialGradient
+       r="8.6620579"
+       fy="19.008621"
+       fx="31.112698"
+       cy="19.008621"
+       cx="31.112698"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3005"
+       xlink:href="#linearGradient2812"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient2804">
+      <stop
+         style="stop-color:white;stop-opacity:1;"
+         offset="0"
+         id="stop2806" />
+      <stop
+         style="stop-color:white;stop-opacity:0;"
+         offset="1"
+         id="stop2808" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3149"
+       id="linearGradient2949"
+       gradientUnits="userSpaceOnUse"
+       x1="17.890068"
+       y1="8.0617304"
+       x2="17.890068"
+       y2="40.032413" />
+    <linearGradient
+       id="linearGradient2951">
+      <stop
+         style="stop-color:#73d216;stop-opacity:1;"
+         offset="0"
+         id="stop2953" />
+      <stop
+         style="stop-color:#5ca911;stop-opacity:1;"
+         offset="1"
+         id="stop2955" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2851"
+       id="linearGradient2963"
+       x1="6.878005"
+       y1="11.789385"
+       x2="12.233074"
+       y2="27.77807"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2804"
+       id="linearGradient2810"
+       x1="4.5264969"
+       y1="2.7991772"
+       x2="10.623409"
+       y2="11.024895"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2812"
+       id="radialGradient2818"
+       cx="31.112698"
+       cy="19.008621"
+       fx="31.112698"
+       fy="19.008621"
+       r="8.6620579"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(3.018423,0.664359,-1.388844,4.257661,-0.134567,-26.02469)"
+       r="0.8078171"
+       fy="6.9473476"
+       fx="4.8470273"
+       cy="6.9473476"
+       cx="4.8470273"
+       id="radialGradient2601"
+       xlink:href="#linearGradient2898"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientUnits="userSpaceOnUse"
+       y2="27.77807"
+       x2="12.233074"
+       y1="11.789385"
+       x1="6.878005"
+       id="linearGradient2599"
+       xlink:href="#linearGradient2851"
+       inkscape:collect="always" />
+    <radialGradient
+       gradientTransform="matrix(0.914124,-3.896132e-15,-2.475021e-18,1.631747,2.671799,-12.00863)"
+       gradientUnits="userSpaceOnUse"
+       r="8.6620579"
+       fy="19.008621"
+       fx="31.112698"
+       cy="19.008621"
+       cx="31.112698"
+       id="radialGradient2597"
+       xlink:href="#linearGradient3816"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient2579">
+      <stop
+         id="stop2581"
+         offset="0"
+         style="stop-color:#73d216;stop-opacity:1;" />
+      <stop
+         id="stop2583"
+         offset="1"
+         style="stop-color:#5ca911;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       y2="40.032413"
+       x2="17.890068"
+       y1="8.0617304"
+       x1="17.890068"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2577"
+       xlink:href="#linearGradient3149"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="31.325201"
+       x2="30.341434"
+       y1="10.823074"
+       x1="11.127699"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4740"
+       xlink:href="#linearGradient3149"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="27.77807"
+       x2="12.233074"
+       y1="11.789385"
+       x1="6.878005"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4738"
+       xlink:href="#linearGradient2851"
+       inkscape:collect="always" />
+    <inkscape:perspective
+       id="perspective43"
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
+       inkscape:vp_z="16 : 8 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 8 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3149"
+       id="linearGradient2635"
+       gradientUnits="userSpaceOnUse"
+       x1="11.127699"
+       y1="10.823074"
+       x2="30.341434"
+       y2="31.325201"
+       gradientTransform="matrix(0.466524,0,0,0.466525,-23.253129,0.8019768)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2851"
+       id="linearGradient2638"
+       gradientUnits="userSpaceOnUse"
+       x1="6.878005"
+       y1="11.789385"
+       x2="12.233074"
+       y2="27.77807"
+       gradientTransform="matrix(0.538297,0,0,0.538297,-24.537152,-0.4592542)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3280"
+       id="linearGradient2640"
+       gradientUnits="userSpaceOnUse"
+       x1="15.893391"
+       y1="15.213944"
+       x2="26.533659"
+       y2="28.579245"
+       gradientTransform="matrix(0.538297,0,0,0.538297,-24.537152,-0.4592542)" />
+    <filter
+       inkscape:collect="always"
+       id="filter3416"
+       x="-0.13117394"
+       width="1.2623479"
+       y="-0.11015608"
+       height="1.2203122">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.19309804"
+         id="feGaussianBlur3418" />
+    </filter>
   </defs>
   <sodipodi:namedview
      id="base"
@@ -185,17 +428,21 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="25.992076"
-     inkscape:cx="8.3811422"
-     inkscape:cy="5.1075896"
+     inkscape:zoom="18.379173"
+     inkscape:cx="0.35485832"
+     inkscape:cy="7.0130435"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
      fill="#eeeeec"
-     inkscape:window-width="1268"
-     inkscape:window-height="844"
-     inkscape:window-x="3"
-     inkscape:window-y="25" />
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="0"
+     inkscape:window-y="22">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2508" />
+  </sodipodi:namedview>
   <metadata
      id="metadata7">
     <rdf:RDF>
@@ -223,7 +470,7 @@
        transform="matrix(0.468971,0,0,0.468971,0.730372,0.26987)" />
     <path
        sodipodi:type="arc"
-       style="opacity:1;color:#000000;fill:#f24747;fill-opacity:1;fill-rule:evenodd;stroke:#820000;stroke-width:1.91298747px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       style="opacity:1;color:#000000;fill:#f13d3d;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3286);stroke-width:1.91298747000000002px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
        id="path1339"
        sodipodi:cx="15.590227"
        sodipodi:cy="16.57217"
@@ -232,17 +479,27 @@
        d="M 29.935402 16.57217 A 14.345175 14.345175 0 1 1  1.2450523,16.57217 A 14.345175 14.345175 0 1 1  29.935402 16.57217 z"
        transform="matrix(0.522706,0,0,0.522779,-0.14857,-0.663013)" />
     <path
-       style="opacity:0.6;color:#000000;fill:url(#linearGradient2194);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2245);stroke-width:1.00000012px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
-       d="M 14.525974,7.9894993 C 14.525974,11.598577 11.608166,14.527685 8.0130095,14.527685 C 4.4178543,14.527685 1.500047,11.598577 1.500047,7.9894993 C 1.500047,4.3804219 4.4178543,1.4513155 8.0130095,1.4513155 C 11.608166,1.4513155 14.525974,4.3804219 14.525974,7.9894993 z "
+       style="opacity:0.75000000000000000;color:#000000;fill:url(#linearGradient3282);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2245);stroke-width:1.00000011999999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       d="m 14.525974,7.9894993 c 0,3.6090777 -2.917808,6.5381857 -6.5129645,6.5381857 -3.5951552,0 -6.5129625,-2.929108 -6.5129625,-6.5381857 0,-3.6090774 2.9178073,-6.5381838 6.5129625,-6.5381838 3.5951565,0 6.5129645,2.9291064 6.5129645,6.5381838 z"
        id="path2220" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:0.19499996000000000;fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3416)"
+       id="path3302"
+       sodipodi:cx="11.484269"
+       sodipodi:cy="4.7465701"
+       sodipodi:rx="1.8659533"
+       sodipodi:ry="1.9428998"
+       d="m 13.350222,4.7465701 a 1.8659533,1.9428998 0 1 1 -3.7319067,0 A 1.8659533,1.9428998 0 1 1 13.350222,4.7465701 z"
+       transform="matrix(2.2014717,-1.281888,0.9447394,1.6503281,-23.212768,12.908772)" />
     <rect
-       style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#a40000;stroke-width:1.00000024;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#a40000;stroke-width:1.00000024000000010;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
        id="rect3207"
        width="11.000004"
        height="2.9999959"
        x="2.500001"
        y="6.5000038"
-       rx="0.96183157"
-       ry="0.96183157" />
+       rx="1.0387781"
+       ry="1.0387781" />
   </g>
 </svg>
--- a/pidgin/pixmaps/status/16/scalable/chat.svg	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pixmaps/status/16/scalable/chat.svg	Thu Jun 26 20:25:38 2008 +0000
@@ -2,92 +2,313 @@
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:cc="http://creativecommons.org/ns#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
-   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    width="16"
    height="16"
-   id="svg13306"
+   id="svg7380"
    sodipodi:version="0.32"
-   inkscape:version="0.43"
-   version="1.0"
-   inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/status/16/irc16.png"
+   inkscape:version="0.46+devel"
+   sodipodi:docbase="/home/hbons/Desktop"
+   sodipodi:docname="person.svg"
+   inkscape:export-filename="/home/hbons/Desktop/person.png"
    inkscape:export-xdpi="90"
    inkscape:export-ydpi="90"
-   sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/status/16/scalable"
-   sodipodi:docname="irc16.svg">
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
   <defs
-     id="defs13308">
+     id="defs7382">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3301">
+      <stop
+         style="stop-color:#46284e;stop-opacity:1;"
+         offset="0"
+         id="stop3303" />
+      <stop
+         style="stop-color:#7a4588;stop-opacity:1"
+         offset="1"
+         id="stop3305" />
+    </linearGradient>
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient2280">
+       id="linearGradient3483">
+      <stop
+         style="stop-color:#c17802;stop-opacity:1"
+         offset="0"
+         id="stop3485" />
       <stop
-         style="stop-color:#d3d7cf;stop-opacity:1;"
+         style="stop-color:#935a00;stop-opacity:1"
+         offset="1"
+         id="stop3487" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3475">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1;"
          offset="0"
-         id="stop2282" />
+         id="stop3477" />
       <stop
-         style="stop-color:#d3d7cf;stop-opacity:0;"
+         style="stop-color:#eeeeec;stop-opacity:0;"
          offset="1"
-         id="stop2284" />
+         id="stop3479" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3451">
+      <stop
+         style="stop-color:#3465a4;stop-opacity:1"
+         offset="0"
+         id="stop3453" />
+      <stop
+         style="stop-color:#15325b;stop-opacity:1"
+         offset="1"
+         id="stop3455" />
     </linearGradient>
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient3150">
+       id="linearGradient3335">
+      <stop
+         style="stop-color:#b2730d;stop-opacity:1;"
+         offset="0"
+         id="stop3337" />
       <stop
-         style="stop-color:#2e3436;stop-opacity:1;"
+         style="stop-color:#935f0a;stop-opacity:1"
+         offset="1"
+         id="stop3339" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3327">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
          offset="0"
-         id="stop3152" />
+         id="stop3329" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3331" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 5.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="11 : 5.5 : 1"
+       inkscape:persp3d-origin="5.5 : 3.6666667 : 1"
+       id="perspective28" />
+    <linearGradient
+       id="linearGradient3800">
+      <stop
+         style="stop-color:#f4d9b1;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop3802" />
       <stop
-         style="stop-color:#2e3436;stop-opacity:0;"
+         style="stop-color:#df9725;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop3804" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3800"
+       id="radialGradient4171"
+       gradientUnits="userSpaceOnUse"
+       cx="27.702486"
+       cy="14.540437"
+       fx="27.702486"
+       fy="14.540437"
+       r="9.1620579"
+       gradientTransform="matrix(1.191087,0,0,1.124022,-5.086983,-1.361697)" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient7300">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1;"
+         offset="0"
+         id="stop7302" />
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:0;"
          offset="1"
-         id="stop3154" />
+         id="stop7304" />
     </linearGradient>
     <radialGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient3150"
-       id="radialGradient3156"
-       cx="10.748654"
-       cy="10.457643"
-       fx="10.748654"
-       fy="10.457643"
-       r="6.6449099"
-       gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
+       xlink:href="#linearGradient7300"
+       id="radialGradient7306"
+       cx="24.248138"
+       cy="27.184834"
+       fx="24.248138"
+       fy="27.184834"
+       r="12.499089"
+       gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
        gradientUnits="userSpaceOnUse" />
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient3131">
+       id="linearGradient3816">
       <stop
-         style="stop-color:#eeeeec;stop-opacity:1;"
+         style="stop-color:#000000;stop-opacity:1;"
          offset="0"
-         id="stop3133" />
+         id="stop3818" />
       <stop
-         style="stop-color:#d3d7cf"
+         style="stop-color:#000000;stop-opacity:0;"
          offset="1"
-         id="stop3135" />
+         id="stop3820" />
     </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3816"
+       id="radialGradient4179"
+       gradientUnits="userSpaceOnUse"
+       cx="31.112698"
+       cy="19.008621"
+       fx="31.112698"
+       fy="19.008621"
+       r="8.6620579" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7300"
+       id="radialGradient4244"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+       cx="24.248138"
+       cy="27.184834"
+       fx="24.248138"
+       fy="27.184834"
+       r="12.499089" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3800"
+       id="radialGradient4246"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.191087,0,0,1.124022,-5.086983,-1.361697)"
+       cx="27.702486"
+       cy="14.540437"
+       fx="27.702486"
+       fy="14.540437"
+       r="9.1620579" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7300"
+       id="radialGradient2631"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+       cx="24.248138"
+       cy="27.184834"
+       fx="24.248138"
+       fy="27.184834"
+       r="12.499089" />
+    <inkscape:perspective
+       id="perspective2506"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
     <linearGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient3131"
-       id="linearGradient3137"
-       x1="18.206755"
-       y1="4.8468447"
-       x2="18.150391"
-       y2="13.775416"
+       xlink:href="#linearGradient3327"
+       id="linearGradient3333"
+       x1="32.26284"
+       y1="18.39094"
+       x2="40.463146"
+       y2="28.908117"
+       gradientUnits="userSpaceOnUse" />
+    <inkscape:perspective
+       id="perspective3358"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <radialGradient
+       r="9.1620579"
+       fy="14.809424"
+       fx="26.819485"
+       cy="14.809424"
+       cx="26.819485"
+       gradientTransform="matrix(0.9647715,0.3755394,-0.3764009,0.966985,7.9289748,-9.623708)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3376"
+       xlink:href="#linearGradient3800"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="25.307449"
+       x2="33.637684"
+       y1="20.449879"
+       x1="30.189112"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3378"
+       xlink:href="#linearGradient3335"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3327"
+       id="linearGradient3409"
+       gradientUnits="userSpaceOnUse"
+       x1="32.26284"
+       y1="18.39094"
+       x2="40.463146"
+       y2="28.908117" />
+    <inkscape:perspective
+       id="perspective3418"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7300"
+       id="radialGradient3449"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+       cx="24.248138"
+       cy="27.184834"
+       fx="24.248138"
+       fy="27.184834"
+       r="12.499089" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3451"
+       id="linearGradient3457"
+       x1="5.0000005"
+       y1="11.446214"
+       x2="8.2252016"
+       y2="16.493296"
        gradientUnits="userSpaceOnUse" />
     <radialGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient2280"
-       id="radialGradient2286"
-       cx="10.332581"
-       cy="6.9103003"
-       fx="10.332581"
-       fy="6.9103003"
-       r="9.5"
-       gradientTransform="matrix(1.895669,2.346468e-16,-3.526656e-16,2.238626,-11.89066,-12.56638)"
+       xlink:href="#linearGradient3475"
+       id="radialGradient3481"
+       cx="28.779234"
+       cy="14.68485"
+       fx="28.779234"
+       fy="14.68485"
+       r="9.8994964"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.1189106,0,0,1.1189106,-3.422157,-1.7461848)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3483"
+       id="linearGradient3489"
+       x1="30.669531"
+       y1="17.247086"
+       x2="34.812038"
+       y2="24.987169"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3301"
+       id="linearGradient3307"
+       x1="16.510134"
+       y1="13.467242"
+       x2="13.781501"
+       y2="9.1689024"
        gradientUnits="userSpaceOnUse" />
   </defs>
   <sodipodi:namedview
@@ -97,20 +318,34 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="39.59798"
-     inkscape:cx="14.115129"
-     inkscape:cy="9.9583634"
+     inkscape:zoom="36.060436"
+     inkscape:cx="12.472661"
+     inkscape:cy="7.0601917"
      inkscape:current-layer="layer1"
      showgrid="true"
      inkscape:grid-bbox="true"
      inkscape:document-units="px"
-     fill="#eeeeec"
-     inkscape:window-width="1268"
-     inkscape:window-height="971"
-     inkscape:window-x="6"
-     inkscape:window-y="21" />
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="0"
+     inkscape:window-y="22"
+     width="11px"
+     height="11px"
+     inkscape:snap-nodes="false"
+     inkscape:snap-bbox="true"
+     objecttolerance="7"
+     gridtolerance="7"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2497"
+       empspacing="5"
+       visible="true"
+       enabled="true" />
+  </sodipodi:namedview>
   <metadata
-     id="metadata13311">
+     id="metadata7385">
     <rdf:RDF>
       <cc:Work
          rdf:about="">
@@ -125,91 +360,80 @@
      inkscape:label="Layer 1"
      inkscape:groupmode="layer">
     <path
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.00000095;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="M 0.49919572,3.436028 L 0.49919572,8.024108 C 0.49919572,10.030365 0.43094041,12.50277 3.4779698,12.50277 L 3.5190997,14.938304 L 6.2391563,12.547388 L 10.144168,12.531827 C 13.477975,12.531827 15.5,11.518177 15.5,9.379431 L 15.484468,3.556577 C 15.484468,1.4311219 14.423292,0.51556128 12.453894,0.51556128 L 3.4753207,0.50000048 C 1.4741104,0.50000048 0.49919572,1.561826 0.49919572,3.436028 z "
-       id="path2199"
-       sodipodi:nodetypes="ccccccccccc" />
-    <path
-       sodipodi:type="inkscape:offset"
-       inkscape:radius="-1.0020103"
-       inkscape:original="M 10.03125 5.5 C 5.2809556 5.6861502 1.5 8.7231084 1.5 12.4375 C 1.5 14.570281 2.4139213 16.850503 4.375 18.125 C 4.790921 19.906271 3.5825788 21.282326 3.375 21.5 C 3.7506605 21.398222 6.7302843 20.58004 7.84375 19.375 C 8.9660824 19.328744 9.5914383 19.40625 10.5 19.40625 C 15.465015 19.40625 19.500001 16.271711 19.5 12.4375 C 19.499999 8.6032883 15.465015 5.5 10.5 5.5 C 10.344844 5.4999998 10.184486 5.4939951 10.03125 5.5 z "
-       xlink:href="#path13316"
-       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
-       id="path2288"
-       inkscape:href="#path13316"
-       d="M 10.0625,6.5 C 7.8900031,6.5851338 5.9762716,7.3427975 4.625,8.4375 C 3.2737284,9.5322025 2.5,10.920293 2.5,12.4375 C 2.5,14.284032 3.2837785,16.226812 4.90625,17.28125 C 5.1287835,17.423752 5.2860248,17.648382 5.34375,17.90625 C 5.4955807,18.556498 5.4707531,19.125743 5.375,19.65625 C 6.0781419,19.333618 6.8270886,18.976092 7.09375,18.6875 C 7.2809756,18.490079 7.540428,18.377274 7.8125,18.375 C 8.9961476,18.326217 9.6484235,18.40625 10.5,18.40625 C 12.771643,18.40625 14.815021,17.674738 16.25,16.5625 C 17.684979,15.450262 18.5,14.003112 18.5,12.4375 C 18.5,10.871887 17.684227,9.4172787 16.25,8.3125 C 14.815773,7.2077213 12.773745,6.5 10.5,6.5 C 10.318992,6.4999998 10.162289,6.4960896 10.0625,6.5 z "
-       transform="matrix(-1,0,0,1,23.99247,-3.990738)" />
-    <path
-       style="opacity:1;fill:url(#radialGradient2286);fill-opacity:1;stroke:#555753;stroke-width:1.00000095;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="M 0.49919572,3.436028 L 0.49919572,8.024108 C 0.49919572,10.030365 0.43094041,12.50277 3.4779698,12.50277 L 3.5190997,14.938304 L 6.2391563,12.547388 L 10.144168,12.531827 C 13.477975,12.531827 15.5,11.518177 15.5,9.379431 L 15.484468,3.556577 C 15.484468,1.4311219 14.423292,0.51556128 12.453894,0.51556128 L 3.4753207,0.50000048 C 1.4741104,0.50000048 0.49919572,1.561826 0.49919572,3.436028 z "
-       id="rect1326"
-       sodipodi:nodetypes="ccccccccccc" />
-    <path
-       sodipodi:type="inkscape:offset"
-       inkscape:radius="-0.99553573"
-       inkscape:original="M 3.46875 0.5 C 1.4675396 0.5 0.5 1.563298 0.5 3.4375 L 0.5 8.03125 C 0.5 10.037507 0.42172061 12.5 3.46875 12.5 L 3.53125 14.9375 L 6.25 12.5625 L 10.15625 12.53125 C 13.490057 12.53125 15.5 11.513746 15.5 9.375 L 15.5 3.5625 C 15.5 1.4370449 14.438148 0.5 12.46875 0.5 L 3.46875 0.5 z "
-       xlink:href="#rect1326"
-       style="opacity:1;fill:none;fill-opacity:1;stroke:#eeeeec;stroke-width:1.03363752;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="path2248"
-       inkscape:href="#rect1326"
-       d="M 3.96875,9.0625 C 3.1444632,9.0625002 2.7022172,9.2621947 2.4375,9.53125 C 2.1727828,9.8003053 2,10.234211 2,11 L 2,15.59375 C 2,16.609826 2.0106197,17.613475 2.25,18.1875 C 2.4893803,18.761525 2.71342,19.0625 3.96875,19.0625 C 4.5099878,19.05993 4.9541408,19.490203 4.96875,20.03125 L 4.96875,20.34375 L 6.09375,19.34375 C 6.2748748,19.183476 6.5081463,19.094611 6.75,19.09375 L 10.65625,19.09375 C 12.221872,19.09375 13.413842,18.823397 14.09375,18.4375 C 14.773658,18.051603 15,17.695013 15,16.9375 L 15,11.125 C 15,10.228058 14.802621,9.7651678 14.53125,9.5 C 14.259879,9.2348322 13.795556,9.0625 12.96875,9.0625 L 3.96875,9.0625 z "
-       transform="matrix(0.997403,0,0,0.938411,3.636372e-2,0.123235)" />
-    <path
-       sodipodi:type="arc"
-       style="opacity:0.2;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="path3140"
-       sodipodi:cx="10.748654"
-       sodipodi:cy="10.457643"
-       sodipodi:rx="6.6449099"
-       sodipodi:ry="2.3675451"
-       d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1  4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1  17.393564 10.457643 z"
-       transform="matrix(1.655402,0,0,1.267134,-5.793347,6.748769)" />
+       style="opacity:1;color:#000000;fill:#855b8c;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3307);stroke-width:0.99999987999999995px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       d="m 9.2852706,13.499999 3.6531493,0 c 1.0350584,0 2.059811,-0.3777864 2.4354322,-1.4545452 C 15.730548,11.022944 15.434737,9.0757587 13.121077,7.5000022 l -4.3228932,0 C 6.4845226,8.9545462 6.1953886,10.943768 6.7280666,12.106061 7.2707356,13.290156 8.1893256,13.5 9.2852706,13.5 z"
+       id="path3443"
+       sodipodi:nodetypes="cczcczc" />
     <path
        sodipodi:type="inkscape:offset"
-       inkscape:radius="-1.0020103"
-       inkscape:original="M 10.03125 5.5 C 5.2809556 5.6861502 1.5 8.7231084 1.5 12.4375 C 1.5 14.570281 2.4139213 16.850503 4.375 18.125 C 4.790921 19.906271 3.5825788 21.282326 3.375 21.5 C 3.7506605 21.398222 6.7302843 20.58004 7.84375 19.375 C 8.9660824 19.328744 9.5914383 19.40625 10.5 19.40625 C 15.465015 19.40625 19.500001 16.271711 19.5 12.4375 C 19.499999 8.6032883 15.465015 5.5 10.5 5.5 C 10.344844 5.4999998 10.184486 5.4939951 10.03125 5.5 z "
-       xlink:href="#path13316"
-       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
-       id="path13434"
-       inkscape:href="#path13316"
-       d="M 10.0625,6.5 C 7.8900031,6.5851338 5.9762716,7.3427975 4.625,8.4375 C 3.2737284,9.5322025 2.5,10.920293 2.5,12.4375 C 2.5,14.284032 3.2837785,16.226812 4.90625,17.28125 C 5.1287835,17.423752 5.2860248,17.648382 5.34375,17.90625 C 5.4955807,18.556498 5.4707531,19.125743 5.375,19.65625 C 6.0781419,19.333618 6.8270886,18.976092 7.09375,18.6875 C 7.2809756,18.490079 7.540428,18.377274 7.8125,18.375 C 8.9961476,18.326217 9.6484235,18.40625 10.5,18.40625 C 12.771643,18.40625 14.815021,17.674738 16.25,16.5625 C 17.684979,15.450262 18.5,14.003112 18.5,12.4375 C 18.5,10.871887 17.684227,9.4172787 16.25,8.3125 C 14.815773,7.2077213 12.773745,6.5 10.5,6.5 C 10.318992,6.4999998 10.162289,6.4960896 10.0625,6.5 z "
-       transform="matrix(-1,0,0,1,32,-8.84375)" />
+       inkscape:radius="-1.1784238"
+       inkscape:original="M 24.5625 24.125 C 17.844986 28.367641 17.015916 34.172303 18.5625 37.5625 C 20.138096 41.016289 22.818019 41.625 26 41.625 L 36.59375 41.625 C 39.598953 41.624999 42.565667 40.546959 43.65625 37.40625 C 44.691891 34.423774 43.842514 28.721194 37.125 24.125 L 24.5625 24.125 z "
+       style="opacity:0.5;color:#000000;fill:url(#radialGradient3449);fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:3.58186674000000016px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       id="path3445"
+       d="m 24.96875,25.3125 c -6.080867,3.980556 -6.594767,9.007702 -5.34375,11.75 0.693936,1.521145 1.541625,2.278662 2.5625,2.75 1.020875,0.471338 2.296653,0.625 3.8125,0.625 l 10.59375,0 c 1.361692,0 2.658712,-0.23791 3.6875,-0.78125 1.028788,-0.54334 1.79962,-1.327976 2.25,-2.625 0.804003,-2.315397 0.274744,-7.39869 -5.8125,-11.71875 z"
+       transform="matrix(0.2947246,0,0,0.2644629,1.8788978,1.8057826)" />
+    <path
+       style="opacity:0.78977272;fill:#eeeeec;fill-opacity:1;stroke:none"
+       d="m 12.999999,10.150001 c 0,1.0212 -1.053115,1.320578 -2,1.85 -1.339673,-0.49828 -2,-0.8288 -2,-1.85 0,-1.0212014 0.8959998,-1.8500014 2,-1.8500014 1.104,0 2,0.8288 2,1.8500014 z"
+       id="path3447"
+       sodipodi:nodetypes="ccssc" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;color:#000000;fill:#d28812;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3489);stroke-width:1.92490505999999995px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       id="path2549"
+       sodipodi:cx="31.112698"
+       sodipodi:cy="19.008621"
+       sodipodi:rx="8.6620579"
+       sodipodi:ry="8.6620579"
+       d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+       transform="matrix(0.5195068,0,0,0.5195069,-5.1632599,-4.8751084)" />
+    <path
+       d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+       sodipodi:ry="8.6620579"
+       sodipodi:rx="8.6620579"
+       sodipodi:cy="19.008621"
+       sodipodi:cx="31.112698"
+       id="path2551"
+       style="opacity:0.625;color:#000000;fill:url(#radialGradient3481);fill-opacity:1;stroke:url(#linearGradient3333);stroke-width:2.47487712px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       sodipodi:type="arc"
+       transform="matrix(0.4040609,0,0,0.4040609,-1.5714252,-2.6806412)" />
+    <path
+       style="opacity:1;color:#000000;fill:#3465a4;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3457);stroke-width:0.99999987999999995px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       d="m 3.2852716,15.499999 3.6531493,0 c 1.0350584,0 2.059811,-0.3777864 2.4354322,-1.4545452 C 9.7305489,13.022944 9.4347379,11.075758 7.1210779,9.5000014 l -4.3228932,0 c -2.3136607,1.454544 -2.6027951,3.443766 -2.0701172,4.606059 0.54266971,1.184095 1.4612592,1.393939 2.5572041,1.393939 z"
+       id="path4308"
+       sodipodi:nodetypes="cczcczc" />
     <path
        sodipodi:type="inkscape:offset"
-       inkscape:radius="-1.0020103"
-       inkscape:original="M 10.03125 5.5 C 5.2809556 5.6861502 1.5 8.7231084 1.5 12.4375 C 1.5 14.570281 2.4139213 16.850503 4.375 18.125 C 4.790921 19.906271 3.5825788 21.282326 3.375 21.5 C 3.7506605 21.398222 6.7302843 20.58004 7.84375 19.375 C 8.9660824 19.328744 9.5914383 19.40625 10.5 19.40625 C 15.465015 19.40625 19.500001 16.271711 19.5 12.4375 C 19.499999 8.6032883 15.465015 5.5 10.5 5.5 C 10.344844 5.4999998 10.184486 5.4939951 10.03125 5.5 z "
-       xlink:href="#path13316"
-       style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
-       id="path13323"
-       inkscape:href="#path13316"
-       d="M 10.0625,6.5 C 7.8900031,6.5851338 5.9762716,7.3427975 4.625,8.4375 C 3.2737284,9.5322025 2.5,10.920293 2.5,12.4375 C 2.5,14.284032 3.2837785,16.226812 4.90625,17.28125 C 5.1287835,17.423752 5.2860248,17.648382 5.34375,17.90625 C 5.4955807,18.556498 5.4707531,19.125743 5.375,19.65625 C 6.0781419,19.333618 6.8270886,18.976092 7.09375,18.6875 C 7.2809756,18.490079 7.540428,18.377274 7.8125,18.375 C 8.9961476,18.326217 9.6484235,18.40625 10.5,18.40625 C 12.771643,18.40625 14.815021,17.674738 16.25,16.5625 C 17.684979,15.450262 18.5,14.003112 18.5,12.4375 C 18.5,10.871887 17.684227,9.4172787 16.25,8.3125 C 14.815773,7.2077213 12.773745,6.5 10.5,6.5 C 10.318992,6.4999998 10.162289,6.4960896 10.0625,6.5 z "
-       transform="matrix(0.750603,0,0,0.750603,0.123492,-2.625632)" />
-    <rect
-       style="opacity:1;fill:#9a9b96;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect2207"
-       width="9"
-       height="1"
-       x="3"
-       y="4"
-       rx="0.5"
-       ry="0.5" />
-    <rect
-       style="opacity:1;fill:#9a9b96;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect1343"
-       width="8"
-       height="1"
-       x="3"
-       y="8"
-       rx="0.5"
-       ry="0.5" />
-    <rect
-       style="opacity:1;fill:#9a9b96;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect1345"
-       width="7"
-       height="1"
-       x="3"
-       y="6"
-       rx="0.5"
-       ry="0.5" />
+       inkscape:radius="-1.1784238"
+       inkscape:original="M 24.5625 24.125 C 17.844986 28.367641 17.015916 34.172303 18.5625 37.5625 C 20.138096 41.016289 22.818019 41.625 26 41.625 L 36.59375 41.625 C 39.598953 41.624999 42.565667 40.546959 43.65625 37.40625 C 44.691891 34.423774 43.842514 28.721194 37.125 24.125 L 24.5625 24.125 z "
+       style="opacity:0.5;color:#000000;fill:url(#radialGradient2631);fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:3.58186674000000016px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       id="path7281"
+       d="m 24.96875,25.3125 c -6.080867,3.980556 -6.594767,9.007702 -5.34375,11.75 0.693936,1.521145 1.541625,2.278662 2.5625,2.75 1.020875,0.471338 2.296653,0.625 3.8125,0.625 l 10.59375,0 c 1.361692,0 2.658712,-0.23791 3.6875,-0.78125 1.028788,-0.54334 1.79962,-1.327976 2.25,-2.625 0.804003,-2.315397 0.274744,-7.39869 -5.8125,-11.71875 z"
+       transform="matrix(0.2947246,0,0,0.2644629,-4.1211012,3.8057819)" />
+    <path
+       style="opacity:0.78977272;fill:#eeeeec;fill-opacity:1;stroke:none"
+       d="M 7,12.15 C 7,13.1712 5.9468852,13.470578 5.0000003,14 3.6603268,13.50172 2.9999998,13.1712 2.9999998,12.15 c 0,-1.021201 0.896,-1.850001 2.000001,-1.850001 1.1039998,0 1.9999997,0.8288 1.9999997,1.850001 z"
+       id="path7285"
+       sodipodi:nodetypes="ccssc" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;color:#000000;fill:url(#radialGradient3376);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3378);stroke-width:1.92490506px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       id="path3405"
+       sodipodi:cx="31.112698"
+       sodipodi:cy="19.008621"
+       sodipodi:rx="8.6620579"
+       sodipodi:ry="8.6620579"
+       d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+       transform="matrix(0.5195068,0,0,0.5195069,-11.163257,-2.8751094)" />
+    <path
+       d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+       sodipodi:ry="8.6620579"
+       sodipodi:rx="8.6620579"
+       sodipodi:cy="19.008621"
+       sodipodi:cx="31.112698"
+       id="path3407"
+       style="opacity:0.625;color:#000000;fill:none;stroke:url(#linearGradient3409);stroke-width:2.47487712px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       sodipodi:type="arc"
+       transform="matrix(0.4040609,0,0,0.4040609,-7.5714222,-0.6806422)" />
   </g>
 </svg>
--- a/pidgin/pixmaps/status/16/scalable/offline.svg	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pixmaps/status/16/scalable/offline.svg	Thu Jun 26 20:25:38 2008 +0000
@@ -2,7 +2,7 @@
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:cc="http://creativecommons.org/ns#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
@@ -13,7 +13,7 @@
    height="16"
    id="svg2"
    sodipodi:version="0.32"
-   inkscape:version="0.45"
+   inkscape:version="0.46+devel"
    version="1.0"
    inkscape:export-filename="/home/hbons/Desktop/offline.png"
    inkscape:export-xdpi="90"
@@ -26,25 +26,47 @@
      id="defs4">
     <linearGradient
        inkscape:collect="always"
-       id="linearGradient2225">
+       id="linearGradient3303">
       <stop
-         style="stop-color:#eeeeec;stop-opacity:1;"
+         style="stop-color:#ffffff;stop-opacity:1;"
          offset="0"
-         id="stop2227" />
+         id="stop3305" />
       <stop
-         style="stop-color:#eeeeec;stop-opacity:0;"
+         style="stop-color:#ffffff;stop-opacity:0;"
          offset="1"
-         id="stop2229" />
+         id="stop3307" />
     </linearGradient>
     <linearGradient
        inkscape:collect="always"
-       xlink:href="#linearGradient2225"
-       id="linearGradient2231"
-       x1="11.802028"
-       y1="1.9986149"
-       x2="11.802028"
-       y2="14.895812"
-       gradientUnits="userSpaceOnUse" />
+       id="linearGradient3295">
+      <stop
+         style="stop-color:#babdb6;stop-opacity:1"
+         offset="0"
+         id="stop3297" />
+      <stop
+         style="stop-color:#888a85;stop-opacity:1"
+         offset="1"
+         id="stop3299" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3275">
+      <stop
+         style="stop-color:#2e3436;stop-opacity:1;"
+         offset="0"
+         id="stop3277" />
+      <stop
+         style="stop-color:#61635f;stop-opacity:1"
+         offset="1"
+         id="stop3279" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 8 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="16 : 8 : 1"
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
+       id="perspective34" />
     <linearGradient
        inkscape:collect="always"
        id="linearGradient2186">
@@ -153,6 +175,33 @@
        x2="12.233074"
        y2="27.77807"
        gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3275"
+       id="linearGradient3281"
+       x1="14.345539"
+       y1="14.69435"
+       x2="6.8097677"
+       y2="4.3450422"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3295"
+       id="linearGradient3301"
+       x1="4.8374491"
+       y1="3.565635"
+       x2="12.060304"
+       y2="13.821809"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3303"
+       id="linearGradient3309"
+       x1="6.8490844"
+       y1="8.6799088"
+       x2="-3.3852992"
+       y2="-4.1349444"
+       gradientUnits="userSpaceOnUse" />
   </defs>
   <sodipodi:namedview
      id="base"
@@ -161,17 +210,28 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="25.992076"
-     inkscape:cx="26.27121"
-     inkscape:cy="5.5692688"
+     inkscape:zoom="8"
+     inkscape:cx="1.3444167"
+     inkscape:cy="3.0859953"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
      fill="#eeeeec"
-     inkscape:window-width="1434"
-     inkscape:window-height="844"
-     inkscape:window-x="3"
-     inkscape:window-y="25" />
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="0"
+     inkscape:window-y="22"
+     inkscape:snap-nodes="false"
+     inkscape:snap-bbox="true"
+     objecttolerance="11"
+     gridtolerance="10">
+    <inkscape:grid
+       type="xygrid"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       id="grid2503" />
+  </sodipodi:namedview>
   <metadata
      id="metadata7">
     <rdf:RDF>
@@ -188,7 +248,7 @@
      inkscape:groupmode="layer"
      id="layer1">
     <path
-       style="fill:#888a85;fill-opacity:1;stroke:#2e3436;stroke-width:0.99999827;stroke-miterlimit:4;stroke-opacity:1"
+       style="fill:url(#linearGradient3301);fill-opacity:1;stroke:url(#linearGradient3281);stroke-width:0.99999826999999997;stroke-miterlimit:4;stroke-opacity:1"
        d="M 13.307074,13.307079 C 10.376958,16.237198 5.6213214,16.237693 2.6918157,13.308187 C -0.23769028,10.378679 -0.23719421,5.623042 2.692923,2.6929237 C 5.62304,-0.23719442 10.378675,-0.23769056 13.308181,2.6918165 C 16.237687,5.6213234 16.237192,10.376962 13.307074,13.307079 z "
        id="path2187" />
     <path
@@ -196,15 +256,22 @@
        inkscape:radius="-1.0137641"
        inkscape:original="M 8 0.5 C 6.0786384 0.50020041 4.1525585 1.2224409 2.6875 2.6875 C -0.24261721 5.6176183 -0.24200589 10.382992 2.6875 13.3125 C 5.6170057 16.242006 10.382384 16.242619 13.3125 13.3125 C 16.242618 10.382383 16.242006 5.6170068 13.3125 2.6875 C 11.847747 1.2227465 9.9213616 0.49979959 8 0.5 z "
        xlink:href="#path2187"
-       style="opacity:0.4;fill:url(#linearGradient2231);fill-opacity:1;stroke:#ffffff;stroke-width:0.99995583;stroke-miterlimit:4;stroke-opacity:1"
+       style="opacity:0.40000000000000002;fill:none;fill-opacity:1;stroke:url(#linearGradient3309);stroke-width:0.99995583000000021;stroke-miterlimit:4;stroke-opacity:1"
        id="path2215"
        inkscape:href="#path2187"
-       d="M 8,1.5 C 6.3326173,1.5001739 4.674966,2.1375335 3.40625,3.40625 C 0.86479124,5.9477097 0.86538373,10.052882 3.40625,12.59375 C 5.9471154,15.134616 10.052293,15.135209 12.59375,12.59375 C 15.135209,10.052292 15.134616,5.9471167 12.59375,3.40625 C 11.325315,2.1378146 9.6669929,1.4998261 8,1.5 z "
-       transform="matrix(1.000056,0,0,1.000028,-4.353349e-4,-2.926381e-5)" />
+       d="M 8,1.5 C 6.3326173,1.5001739 4.674966,2.1375335 3.40625,3.40625 c -2.5414588,2.5414597 -2.5408663,6.646632 0,9.1875 2.5408654,2.540866 6.646043,2.541459 9.1875,0 2.541459,-2.541458 2.540866,-6.6466333 0,-9.1875 C 11.325315,2.1378146 9.6669929,1.4998261 8,1.5 z"
+       transform="matrix(-1.000056,0,0,-1.000028,16.000461,16.000041)" />
     <path
-       style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:1.00000036;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="M 11.54182,4.2658182 C 10.931116,3.6551131 10.447965,3.347327 9.8372602,3.9580321 L 8.0000001,5.7952921 L 6.1627401,3.9580321 C 5.5520351,3.3473271 4.9919392,3.7705329 4.381234,4.381238 C 3.770529,4.991943 3.347323,5.5520393 3.9580281,6.1627441 L 5.7952881,8.0000041 L 3.9580281,9.8372641 C 3.3473229,10.447969 3.6935827,10.969592 4.3042875,11.580298 C 4.9149927,12.191002 5.5520349,12.652681 6.1627401,12.041977 L 8.0000001,10.204716 L 9.8372602,12.041977 C 10.447965,12.652681 11.046535,12.306422 11.657239,11.695718 C 12.267944,11.085012 12.652677,10.447969 12.041972,9.8372641 L 10.204713,8.0000041 L 12.041972,6.1627441 C 12.652677,5.552039 12.152526,4.8765236 11.54182,4.2658182 z "
+       style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:1.00000036;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="M 11.85704,4.0822721 C 11.226174,3.4531102 10.534706,3.2129687 9.903839,3.8421305 L 8.0059223,5.7349162 6.1080057,3.8421305 C 5.4771385,3.2129688 4.7446581,3.5335449 4.1137906,4.1627068 3.4829234,4.7918685 3.1996385,5.4843118 3.8305058,6.1134733 L 5.7284224,8.006259 3.8305058,9.8990447 C 3.1996384,10.528207 3.4803833,11.334907 4.1112504,11.96407 4.7421178,12.59323 5.4771383,12.799549 6.1080057,12.170389 L 8.0059223,10.277602 9.903839,12.170389 C 10.534706,12.799549 11.306931,12.596719 11.937797,11.967558 12.568664,11.338395 12.812206,10.528207 12.181339,9.8990449 L 10.283423,8.0062592 12.181339,6.1134736 C 12.812206,5.4843115 12.487908,4.7114342 11.85704,4.0822721 z"
        id="rect2920"
        sodipodi:nodetypes="ccccscccscccscccc" />
+    <rect
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none"
+       id="rect3285"
+       width="4"
+       height="4"
+       x="6"
+       y="6" />
   </g>
 </svg>
--- a/pidgin/pixmaps/status/16/scalable/person.svg	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/pixmaps/status/16/scalable/person.svg	Thu Jun 26 20:25:38 2008 +0000
@@ -2,26 +2,107 @@
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:cc="http://creativecommons.org/ns#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="16px"
-   height="16px"
+   width="16"
+   height="16"
    id="svg7380"
    sodipodi:version="0.32"
-   inkscape:version="0.44.1"
-   sodipodi:docbase="/home/hbons/GUI/Tango/Gaim Refresh/status/16/scalable"
+   inkscape:version="0.46+devel"
+   sodipodi:docbase="/home/hbons/Desktop"
    sodipodi:docname="person.svg"
-   inkscape:export-filename="/home/hbons/GUI/Tango/Gaim Refresh/status/16/person.png"
+   inkscape:export-filename="/home/hbons/Desktop/person.png"
    inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
+   inkscape:export-ydpi="90"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0">
   <defs
      id="defs7382">
     <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3301">
+      <stop
+         style="stop-color:#46284e;stop-opacity:1;"
+         offset="0"
+         id="stop3303" />
+      <stop
+         style="stop-color:#7a4588;stop-opacity:1"
+         offset="1"
+         id="stop3305" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3483">
+      <stop
+         style="stop-color:#c17802;stop-opacity:1"
+         offset="0"
+         id="stop3485" />
+      <stop
+         style="stop-color:#935a00;stop-opacity:1"
+         offset="1"
+         id="stop3487" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3475">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1;"
+         offset="0"
+         id="stop3477" />
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:0;"
+         offset="1"
+         id="stop3479" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3451">
+      <stop
+         style="stop-color:#3465a4;stop-opacity:1"
+         offset="0"
+         id="stop3453" />
+      <stop
+         style="stop-color:#15325b;stop-opacity:1"
+         offset="1"
+         id="stop3455" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3335">
+      <stop
+         style="stop-color:#b2730d;stop-opacity:1;"
+         offset="0"
+         id="stop3337" />
+      <stop
+         style="stop-color:#935f0a;stop-opacity:1"
+         offset="1"
+         id="stop3339" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3327">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3329" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3331" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 5.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="11 : 5.5 : 1"
+       inkscape:persp3d-origin="5.5 : 3.6666667 : 1"
+       id="perspective28" />
+    <linearGradient
        id="linearGradient3800">
       <stop
          style="stop-color:#f4d9b1;stop-opacity:1.0000000;"
@@ -110,6 +191,160 @@
        fx="27.702486"
        fy="14.540437"
        r="9.1620579" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7300"
+       id="radialGradient2631"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+       cx="24.248138"
+       cy="27.184834"
+       fx="24.248138"
+       fy="27.184834"
+       r="12.499089" />
+    <inkscape:perspective
+       id="perspective2506"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3327"
+       id="linearGradient3333"
+       x1="32.26284"
+       y1="18.39094"
+       x2="40.463146"
+       y2="28.908117"
+       gradientUnits="userSpaceOnUse" />
+    <inkscape:perspective
+       id="perspective3358"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <radialGradient
+       r="9.1620579"
+       fy="14.809424"
+       fx="26.819485"
+       cy="14.809424"
+       cx="26.819485"
+       gradientTransform="matrix(0.9647715,0.3755394,-0.3764009,0.966985,7.9289748,-9.623708)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient3376"
+       xlink:href="#linearGradient3800"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="25.307449"
+       x2="33.637684"
+       y1="20.449879"
+       x1="30.189112"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3378"
+       xlink:href="#linearGradient3335"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3327"
+       id="linearGradient3409"
+       gradientUnits="userSpaceOnUse"
+       x1="32.26284"
+       y1="18.39094"
+       x2="40.463146"
+       y2="28.908117" />
+    <inkscape:perspective
+       id="perspective3418"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3451"
+       id="linearGradient3457"
+       x1="5.0000005"
+       y1="11.446214"
+       x2="8.2252016"
+       y2="16.493296"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3475"
+       id="radialGradient3481"
+       cx="28.779234"
+       cy="14.68485"
+       fx="28.779234"
+       fy="14.68485"
+       r="9.8994964"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.1189106,0,0,1.1189106,-3.422157,-1.7461848)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3483"
+       id="linearGradient3489"
+       x1="30.669531"
+       y1="17.247086"
+       x2="34.812038"
+       y2="24.987169"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3301"
+       id="linearGradient3307"
+       x1="13.753093"
+       y1="16.35816"
+       x2="11.875512"
+       y2="10.748822"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.1111113,0,0,1.1666667,-4.7222232,-0.2500063)" />
+    <inkscape:perspective
+       id="perspective2538"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <radialGradient
+       r="9.1620579"
+       fy="14.809424"
+       fx="26.819485"
+       cy="14.809424"
+       cx="26.819485"
+       gradientTransform="matrix(0.9647715,0.3755394,-0.3764009,0.966985,7.9289748,-9.623708)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient2556"
+       xlink:href="#linearGradient3800"
+       inkscape:collect="always" />
+    <linearGradient
+       y2="25.307449"
+       x2="33.637684"
+       y1="20.449879"
+       x1="30.189112"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient2558"
+       xlink:href="#linearGradient3335"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3327"
+       id="linearGradient2587"
+       gradientUnits="userSpaceOnUse"
+       x1="32.26284"
+       y1="18.39094"
+       x2="40.463146"
+       y2="28.908117" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7300"
+       id="linearGradient3300"
+       x1="17.57398"
+       y1="32.875"
+       x2="44.321774"
+       y2="32.875"
+       gradientUnits="userSpaceOnUse" />
   </defs>
   <sodipodi:namedview
      id="base"
@@ -118,17 +353,32 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="36.060436"
-     inkscape:cx="13.280605"
-     inkscape:cy="9.4853742"
+     inkscape:zoom="25.498579"
+     inkscape:cx="8.3447229"
+     inkscape:cy="9.4891345"
      inkscape:current-layer="layer1"
      showgrid="true"
      inkscape:grid-bbox="true"
      inkscape:document-units="px"
-     inkscape:window-width="1274"
-     inkscape:window-height="972"
-     inkscape:window-x="6"
-     inkscape:window-y="25" />
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="0"
+     inkscape:window-y="22"
+     width="11px"
+     height="11px"
+     inkscape:snap-nodes="false"
+     inkscape:snap-bbox="true"
+     objecttolerance="7"
+     gridtolerance="7"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2497"
+       empspacing="5"
+       visible="true"
+       enabled="true" />
+  </sodipodi:namedview>
   <metadata
      id="metadata7385">
     <rdf:RDF>
@@ -145,44 +395,42 @@
      inkscape:label="Layer 1"
      inkscape:groupmode="layer">
     <path
-       style="opacity:1;color:black;fill:#ad7fa8;fill-opacity:1;fill-rule:evenodd;stroke:#5c3566;stroke-width:2.39089775px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
-       d="M 25.986174,41.636039 L 36.592776,41.636039 C 39.59798,41.636039 42.57326,40.534107 43.663843,37.393398 C 44.699482,34.410922 43.84062,28.73134 37.123106,24.135146 L 24.57196,24.135146 C 17.854446,28.377786 17.014969,34.179977 18.561553,37.570174 C 20.137148,41.023964 22.804193,41.636039 25.986174,41.636039 z "
-       id="path4308"
-       sodipodi:nodetypes="cczcczc"
-       transform="matrix(0.382691,0,0,0.457119,-3.349933,-3.532635)" />
+       style="opacity:1;color:#000000;fill:#855b8c;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3307);stroke-width:0.99999963999999997px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       d="m 5.5947456,15.499992 4.0590553,0 c 1.1500651,0 2.2886792,-0.44075079 2.7060362,-1.6969693 C 12.756166,12.610095 12.427487,10.338379 9.856753,8.4999962 l -4.8032154,0 C 2.4828029,10.196964 2.1615429,12.517723 2.7534074,13.873731 c 0.6029656,1.381444 1.6236213,1.626262 2.8413382,1.626262 z"
+       id="path3443"
+       sodipodi:nodetypes="cczcczc" />
     <path
        sodipodi:type="inkscape:offset"
        inkscape:radius="-1.1784238"
        inkscape:original="M 24.5625 24.125 C 17.844986 28.367641 17.015916 34.172303 18.5625 37.5625 C 20.138096 41.016289 22.818019 41.625 26 41.625 L 36.59375 41.625 C 39.598953 41.624999 42.565667 40.546959 43.65625 37.40625 C 44.691891 34.423774 43.842514 28.721194 37.125 24.125 L 24.5625 24.125 z "
-       style="opacity:0.45454544;color:black;fill:url(#radialGradient7306);fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:2.73569775px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
-       id="path7281"
-       d="M 24.96875,25.3125 C 18.887883,29.293056 18.373983,34.320202 19.625,37.0625 C 20.318936,38.583645 21.166625,39.341162 22.1875,39.8125 C 23.208375,40.283838 24.484153,40.4375 26,40.4375 L 36.59375,40.4375 C 37.955442,40.4375 39.252462,40.19959 40.28125,39.65625 C 41.310038,39.11291 42.08087,38.328274 42.53125,37.03125 C 43.335253,34.715853 42.805994,29.63256 36.71875,25.3125 L 24.96875,25.3125 z "
-       transform="matrix(0.336828,0,0,0.396695,-1.924113,-1.54134)" />
+       style="opacity:0.5;color:#000000;fill:url(#linearGradient3300);fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:2.99680495px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       id="path3445"
+       d="m 24.96875,25.3125 c -6.080867,3.980556 -6.594767,9.007702 -5.34375,11.75 0.693936,1.521145 1.541625,2.278662 2.5625,2.75 1.020875,0.471338 2.296653,0.625 3.8125,0.625 l 10.59375,0 c 1.361692,0 2.658712,-0.23791 3.6875,-0.78125 1.028788,-0.54334 1.79962,-1.327976 2.25,-2.625 0.804003,-2.315397 0.274744,-7.39869 -5.8125,-11.71875 z"
+       transform="matrix(-0.3368281,0,0,0.3305786,17.924115,1.1322288)" />
     <path
-       transform="matrix(0.761596,0,0,0.870395,-3.301291,-7.391088)"
-       style="opacity:0.78977272;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
-       d="M 20.091094,19.980694 C 20.091094,21.249084 17.671238,21.620929 15.495478,22.278502 C 12.417166,21.65961 10.899861,21.249084 10.899861,19.980694 C 10.899861,18.712304 12.958697,17.682885 15.495478,17.682885 C 18.032258,17.682885 20.091094,18.712304 20.091094,19.980694 z "
-       id="path7285"
+       style="opacity:0.78977272;fill:#eeeeec;fill-opacity:1;stroke:none"
+       d="M 10,10.500001 C 10,11.88 8.6836063,12.284565 7.5,13 5.8254087,12.326649 5,11.88 5,10.500001 5,9.1200001 6.1199997,8 7.5,8 8.88,8 10,9.1199996 10,10.500001 z"
+       id="path3447"
        sodipodi:nodetypes="ccssc" />
     <path
        sodipodi:type="arc"
-       style="opacity:1;color:black;fill:url(#radialGradient4171);fill-opacity:1;fill-rule:evenodd;stroke:#b2730d;stroke-width:1.73241472px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
-       id="path4320"
+       style="opacity:1;color:#000000;fill:url(#radialGradient2556);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2558);stroke-width:1.73241425px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+       id="path3405"
        sodipodi:cx="31.112698"
        sodipodi:cy="19.008621"
        sodipodi:rx="8.6620579"
        sodipodi:ry="8.6620579"
-       d="M 39.774755 19.008621 A 8.6620579 8.6620579 0 1 1  22.45064,19.008621 A 8.6620579 8.6620579 0 1 1  39.774755 19.008621 z"
-       transform="matrix(0.57723,0,0,0.57723,-9.459179,-5.472346)" />
+       d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+       transform="matrix(0.57723,0,0,0.5772299,-10.459182,-5.4723453)" />
     <path
-       d="M 39.774755 19.008621 A 8.6620579 8.6620579 0 1 1  22.45064,19.008621 A 8.6620579 8.6620579 0 1 1  39.774755 19.008621 z"
+       d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
        sodipodi:ry="8.6620579"
        sodipodi:rx="8.6620579"
        sodipodi:cy="19.008621"
        sodipodi:cx="31.112698"
-       id="path4322"
-       style="opacity:0.25;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:2.165519px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       id="path3407"
+       style="opacity:0.625;color:#000000;fill:none;stroke:url(#linearGradient2587);stroke-width:2.16551685px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
        sodipodi:type="arc"
-       transform="matrix(0.461783,0,0,0.461784,-5.867326,-3.277881)" />
+       transform="matrix(0.461784,0,0,0.461784,-6.8673454,-3.2778773)" />
   </g>
 </svg>
Binary file pidgin/pixmaps/toolbar/16/send-file.png has changed
Binary file pidgin/pixmaps/toolbar/16/transfer.png has changed
--- a/pidgin/plugins/Makefile.mingw	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/plugins/Makefile.mingw	Thu Jun 26 20:25:38 2008 +0000
@@ -84,6 +84,7 @@
 		notify.dll \
 		pidginrc.dll \
 		relnot.dll \
+		sendbutton.dll \
 		spellchk.dll \
 		timestamp_format.dll \
 		timestamp.dll
--- a/pidgin/plugins/cap/cap.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/plugins/cap/cap.c	Thu Jun 26 20:25:38 2008 +0000
@@ -333,7 +333,7 @@
 
 static gboolean max_message_difference_cb(gpointer data) {
 	CapStatistics *stats = data;
-	purple_debug_info("cap", "Max Message Difference timeout occured\n");
+	purple_debug_info("cap", "Max Message Difference timeout occurred\n");
 	insert_cap_failure(stats);
 	stats->timeout_source_id = 0;
 	return FALSE;
--- a/pidgin/plugins/spellchk.c	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/plugins/spellchk.c	Thu Jun 26 20:25:38 2008 +0000
@@ -1740,6 +1740,7 @@
 			"BAD wroking\nGOOD working\n"
 			"BAD wtih\nGOOD with\n"
 			"BAD wuould\nGOOD would\n"
+			"BAD wud\nGOOD would\n"
 			"BAD wut\nGOOD what\n"
 			"BAD wya\nGOOD way\n"
 			"BAD y\nGOOD why\n"
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Thu Jun 26 19:57:37 2008 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Thu Jun 26 20:25:38 2008 +0000
@@ -748,6 +748,7 @@
     Delete "$INSTDIR\plugins\pidginrc.dll"
     Delete "$INSTDIR\plugins\psychic.dll"
     Delete "$INSTDIR\plugins\relnot.dll"
+    Delete "$INSTDIR\plugins\sendbutton.dll"
     Delete "$INSTDIR\plugins\spellchk.dll"
     Delete "$INSTDIR\plugins\ssl-nss.dll"
     Delete "$INSTDIR\plugins\ssl.dll"
@@ -1387,67 +1388,6 @@
 
 FunctionEnd
 
-; This is a modified StartRadioButtons (from Sections.nsh)
-; The only difference is that it allows for nothing in the group to be selected
-; In that case, the default variable should be set to ""
-!macro StartRadioButtonsUnselectable var
-
-  !define StartRadioButtons_Var "${var}"
-
-  Push $R0
-  Push $R1
-
-   ;If we have no selection, don't try to unselect it
-   StrCmp "${StartRadioButtons_Var}" "" +4
-   SectionGetFlags "${StartRadioButtons_Var}" $R0
-   IntOp $R1 $R0 & ${SF_SELECTED}
-   IntOp $R0 $R0 & ${SECTION_OFF}
-   SectionSetFlags "${StartRadioButtons_Var}" $R0
-
-   ; If the previous value isn't currently selected,
-   ; we don't want to select it at the end
-   IntCmp $R1 ${SF_SELECTED} +2
-   StrCpy "${StartRadioButtons_Var}" ""
-
-   StrCpy $R1 "${StartRadioButtons_Var}"
-
-!macroend
-
-Function .onSelChange
-  Push $0
-  Push $1
-  Push $2
-
-  ; Check that at most one of the non-readonly spelling dictionaries are selected
-  ; We can't use $R0 or $R1 in this block since they're used in the macros
-  !insertmacro StartRadioButtonsUnselectable $SPELLCHECK_SEL
-    ; Start with the first language dictionary
-    IntOp $2 ${SecSpellCheck} + 1
-
-    start_spellcheck_radio:
-    SectionGetFlags $2 $0
-
-    IntOp $1 $0 & ${SF_SECGRPEND}
-    ; If it is the end of the section group, stop
-    IntCmp $1 ${SF_SECGRPEND} end_spellcheck_radio
-
-    IntOp $0 $0 & ${SF_RO}
-    IntCmp $0 ${SF_RO} after_button_insert
-    ; If !readonly, then it is part of the radiobutton group
-    !insertmacro RadioButton $2
-    after_button_insert:
-
-    IntOp $2 $2 + 1 ;Advance to the next section
-    Goto start_spellcheck_radio
-
-    end_spellcheck_radio:
-  !insertmacro EndRadioButtons
-
-  Pop $2
-  Pop $1
-  Pop $0
-FunctionEnd
-
 ; Page enter and exit functions..
 
 Function preWelcomePage
--- a/po/ar.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/ar.po	Thu Jun 26 20:25:38 2008 +0000
@@ -12918,12 +12918,18 @@
 #, c-format
 msgid "%s requests %s to accept %d file: %s (%.2f %s)%s%s"
 msgid_plural "%s requests %s to accept %d files: %s (%.2f %s)%s%s"
-msgstr[0] "‏%1$s لا يطلب من %2$s أن يقبل أيّة ملفات : %4$s (%5$.2f %6$s)%7$s%8$s"
-msgstr[1] "‏%1$s يطلب من %2$s أن يقبل ملفا واحدا: %4$s (%5$.2f %6$s)%7$s%8$s"
-msgstr[2] "‏%1$s يطلب من %2$s أن يقبل ملفين: %4$s (%5$.2f %6$s)%7$s%8$s"
-msgstr[3] "‏%s يطلب من %s أن يقبل %Id ملفات: %s (%.2f %s)%s%s"
-msgstr[4] "‏%s يطلب من %s أن يقبل %Id ملفا: %s (%.2f %s)%s%s"
-msgstr[5] "‏%s يطلب من %s أن يقبل %Id ملف: %s (%.2f %s)%s%s"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+#msgstr[0] "‏%1$s لا يطلب من %2$s أن يقبل أيّة ملفات : %4$s (%5$.2f %6$s)%7$s%8$s"
+#msgstr[1] "‏%1$s يطلب من %2$s أن يقبل ملفا واحدا: %4$s (%5$.2f %6$s)%7$s%8$s"
+#msgstr[2] "‏%1$s يطلب من %2$s أن يقبل ملفين: %4$s (%5$.2f %6$s)%7$s%8$s"
+#msgstr[3] "‏%s يطلب من %s أن يقبل %Id ملفات: %s (%.2f %s)%s%s"
+#msgstr[4] "‏%s يطلب من %s أن يقبل %Id ملفا: %s (%.2f %s)%s%s"
+#msgstr[5] "‏%s يطلب من %s أن يقبل %Id ملف: %s (%.2f %s)%s%s"
 
 #: ../libpurple/protocols/toc/toc.c:2216
 #, c-format
@@ -14108,12 +14114,18 @@
 #, c-format
 msgid "You have %d contact named %s. Would you like to merge them?"
 msgid_plural "You currently have %d contacts named %s. Would you like to merge them?"
-msgstr[0] "لا مراسلين لديك باسم %2$s. أتريد دمجهم؟"
-msgstr[1] "لديك مراسل واحد باسم %2$s. أتريد دمجه؟"
-msgstr[2] "لديك مراسليْن باسم %2$s. أتريد دمجهما؟"
-msgstr[3] "لديك %Id مراسلين باسم %s. أتريد دمجهم؟"
-msgstr[4] "لديك %Id مراسلا باسم %s. أتريد دمجهم؟"
-msgstr[5] "لديك %Id مراسل باسم %s. أتريد دمجهم؟"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+#msgstr[0] "لا مراسلين لديك باسم %2$s. أتريد دمجهم؟"
+#msgstr[1] "لديك مراسل واحد باسم %2$s. أتريد دمجه؟"
+#msgstr[2] "لديك مراسليْن باسم %2$s. أتريد دمجهما؟"
+#msgstr[3] "لديك %Id مراسلين باسم %s. أتريد دمجهم؟"
+#msgstr[4] "لديك %Id مراسلا باسم %s. أتريد دمجهم؟"
+#msgstr[5] "لديك %Id مراسل باسم %s. أتريد دمجهم؟"
 
 #: ../pidgin/gtkblist.c:550
 msgid ""
@@ -14476,12 +14488,18 @@
 #, c-format
 msgid "%d unread message from %s\n"
 msgid_plural "%d unread messages from %s\n"
-msgstr[0] "لا رسائل غير مقروءة من %2$s\n"
-msgstr[1] "رسالة واحدة غير مقروءة من %2$s\n"
-msgstr[2] "رسالتان غير مقروءتان من %2$s\n"
-msgstr[3] "%Id رسائل غير مقروءة من %s\n"
-msgstr[4] "%Id رسالة غير مقروءة من %s\n"
-msgstr[5] "%Id رسالة غير مقروءة من %s\n"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+#msgstr[0] "لا رسائل غير مقروءة من %2$s\n"
+#msgstr[1] "رسالة واحدة غير مقروءة من %2$s\n"
+#msgstr[2] "رسالتان غير مقروءتان من %2$s\n"
+#msgstr[3] "%Id رسائل غير مقروءة من %s\n"
+#msgstr[4] "%Id رسالة غير مقروءة من %s\n"
+#msgstr[5] "%Id رسالة غير مقروءة من %s\n"
 
 #: ../pidgin/gtkblist.c:4238
 msgid "Manually"
--- a/po/bn.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/bn.po	Thu Jun 26 20:25:38 2008 +0000
@@ -6296,8 +6296,9 @@
 "%s on the local list is inside the group \"%s\" but not on the server list. "
 "Do you want this buddy to be added?"
 msgstr ""
-"স্থানীয় তালিকার %s গ্রুপ এ আছেন কিন্তু সার্ভার তালিকায় নেই।আপনি কি এই বন্ধুটিকে যোগ "
-"করতে চান?"
+#msgstr ""
+#"স্থানীয় তালিকার %s গ্রুপ এ আছেন কিন্তু সার্ভার তালিকায় নেই।আপনি কি এই বন্ধুটিকে যোগ "
+#"করতে চান?"
 
 #: ../libpurple/protocols/msn/dialog.c:124
 #, c-format
@@ -7833,7 +7834,8 @@
 #: ../libpurple/protocols/novell/novell.c:705
 #, c-format
 msgid "Could not get details for user %s (%s)."
-msgstr "ব্যবহারকারী %s এর বিবরন পাওয়া যাচ্ছে না"
+msgstr ""
+#msgstr "ব্যবহারকারী %s এর বিবরন পাওয়া যাচ্ছে না"
 
 #: ../libpurple/protocols/novell/novell.c:751
 #: ../libpurple/protocols/novell/novell.c:897
@@ -13730,8 +13732,8 @@
 #, c-format
 msgid "%d unread message from %s\n"
 msgid_plural "%d unread messages from %s\n"
-msgstr[0] "%s এর %d টি না পড়া বার্তা রয়েছে\n"
-msgstr[1] "%s এর %d টি না পড়া বার্তা রয়েছে\n"
+msgstr[0] "%2$s এর %1$d টি না পড়া বার্তা রয়েছে\n"
+msgstr[1] "%2$s এর %1$d টি না পড়া বার্তা রয়েছে\n"
 
 #: ../pidgin/gtkblist.c:3822
 msgid "Manually"
--- a/po/bs.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/bs.po	Thu Jun 26 20:25:38 2008 +0000
@@ -8858,7 +8858,7 @@
 msgid "You missed %hu message from %s because it was too large."
 msgid_plural "You missed %hu messages from %s because they were too large."
 msgstr[0] "Propustili ste %hu poruku od %s, jer je bila prevelika."
-msgstr[1] "Propustili ste %hu poruke, jer su bile prevelike."
+msgstr[1] "Propustili ste %hu poruke od %s, jer su bile prevelike."
 
 #: ../libpurple/protocols/oscar/oscar.c:2614
 #, c-format
--- a/po/ca@valencia.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/ca@valencia.po	Thu Jun 26 20:25:38 2008 +0000
@@ -13740,15 +13740,16 @@
 "You can come back to this window to add, edit, or remove accounts from "
 "<b>Accounts->Add/Edit</b> in the Buddy List window"
 msgstr ""
-"<span size='larger' weight='bold'>Benvinguts al %s!</span>\n"
-"\n"
-"No teniu cap compte de MI configurat. Per a connectar-vos amb el %s premeu "
-"el botó <b>Afig</b> de davall, i configureu el vostre primer compte. Si "
-"voleu connectar-vos amb més d'un compte de MI, torneu a prémer <b>Afig</b> "
-"fins a configurar-los tots.\n"
-"\n"
-"Podeu tornar a esta finestra per afegir, editar o suprimir comptes, a partir "
-"del menú <b>Comptes->Afig/Edita</b> de la finestra de la llista d'amics."
+#msgstr ""
+#"<span size='larger' weight='bold'>Benvinguts al %s!</span>\n"
+#"\n"
+#"No teniu cap compte de MI configurat. Per a connectar-vos amb el %s premeu "
+#"el botó <b>Afig</b> de davall, i configureu el vostre primer compte. Si "
+#"voleu connectar-vos amb més d'un compte de MI, torneu a prémer <b>Afig</b> "
+#"fins a configurar-los tots.\n"
+#"\n"
+#"Podeu tornar a esta finestra per afegir, editar o suprimir comptes, a partir "
+#"del menú <b>Comptes->Afig/Edita</b> de la finestra de la llista d'amics."
 
 #: ../pidgin/gtkblist.c:767
 msgid "Join a Chat"
--- a/po/de.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/de.po	Thu Jun 26 20:25:38 2008 +0000
@@ -11,9 +11,9 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-26 21:14+0200\n"
-"PO-Revision-Date: 2008-05-26 21:14+0200\n"
-"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
+"POT-Creation-Date: 2008-06-25 23:30+0200\n"
+"PO-Revision-Date: 2008-06-25 23:27+0200\n"
+"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -413,6 +413,9 @@
 msgid "View Log..."
 msgstr "Mitschnitt anzeigen..."
 
+msgid "View All Logs"
+msgstr "Alle Mitschnitte anzeigen"
+
 msgid "Show"
 msgstr "Anzeigen"
 
@@ -612,6 +615,19 @@
 msgid "Send To"
 msgstr "Senden an"
 
+msgid "Invite message"
+msgstr "Einladungsnachricht"
+
+msgid "Invite"
+msgstr "Einladen"
+
+msgid ""
+"Please enter the name of the user you wish to invite,\n"
+"along with an optional invite message."
+msgstr ""
+"Bitte geben Sie den Benutzernamen der Person ein, die Sie einladen möchten "
+"zusammen mit einer optionalen Einladungsnachricht."
+
 msgid "Conversation"
 msgstr "Unterhaltung"
 
@@ -624,6 +640,9 @@
 msgid "Add Buddy Pounce..."
 msgstr "Buddy-Alarm hinzufügen..."
 
+msgid "Invite..."
+msgstr "Einladen..."
+
 msgid "Enable Logging"
 msgstr "Mitschnitt einschalten"
 
@@ -651,6 +670,20 @@
 "zu erhalten.\n"
 "Die folgenden Kommandos sind in diesem Kontext verfügbar:\n"
 
+#, c-format
+msgid ""
+"%s is not a valid message class. See '/help msgcolor' for valid message "
+"classes."
+msgstr ""
+"%s ist keine gültige Nachrichtenklasse. Die gültigen Nachrichtenklassen "
+"finden Sie unter '/help msgcolor'."
+
+#, c-format
+msgid "%s is not a valid color. See '/help msgcolor' for valid colors."
+msgstr ""
+"%s ist keine gültige Farbe. Die gültigen Farben finden Sie unter '/help "
+"msgcolor'."
+
 msgid ""
 "say &lt;message&gt;:  Send a message normally as if you weren't using a "
 "command."
@@ -697,6 +730,20 @@
 msgid "statuses: Show the savedstatuses window."
 msgstr "statuses: Das Fenster mit gespeicherten Status-Infos anzeigen."
 
+msgid ""
+"msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: Set the color "
+"for different classes of messages in the conversation window.<br>    &lt;"
+"class&gt;: receive, send, highlight, action, timestamp<br>    &lt;foreground/"
+"background&gt;: black, red, green, blue, white, gray, darkgray, magenta, "
+"cyan, default<br><br>EXAMPLE:<br>    msgcolor send cyan default"
+msgstr ""
+"msgcolor &lt;Klasse&gt; &lt;Vordergrund&gt; &lt;Hintergrund&gt;: Die Farbe "
+"für verschiedene Klassen von Nachrichten im Gesprächsfenster festlegen."
+"<br>    &lt;Klasse&gt;: receive, send, highlight, action, timestamp<br>    "
+"&lt;Vordergrund/Hintergrund&gt;: black, red, green, blue, white, gray, "
+"darkgray, magenta, cyan, default<br><br>BEISPIEL:<br>    msgcolor send cyan "
+"default"
+
 msgid "Unable to open file."
 msgstr "Konnte die Datei nicht öffnen."
 
@@ -717,8 +764,10 @@
 msgstr "Pause"
 
 #, c-format
-msgid "File Transfers - %d%% of %d files"
-msgstr "Dateiübertragungen - %d%% von %d Dateien"
+msgid "File Transfers - %d%% of %d file"
+msgid_plural "File Transfers - %d%% of %d files"
+msgstr[0] "Dateiübertragungen - %d%% von %d Datei"
+msgstr[1] "Dateiübertragungen - %d%% von %d Dateien"
 
 #. Create the window.
 msgid "File Transfers"
@@ -833,11 +882,14 @@
 msgid "Conversations with %s"
 msgstr "Unterhaltung mit %s"
 
+msgid "All Conversations"
+msgstr "Alle Unterhaltungen"
+
 msgid "System Log"
 msgstr "System-Mitschnitt"
 
 msgid "Emails"
-msgstr "Emails"
+msgstr "E-Mails"
 
 msgid "You have mail!"
 msgstr "Sie haben Post!"
@@ -870,9 +922,6 @@
 msgid "IM"
 msgstr "Nachricht"
 
-msgid "Invite"
-msgstr "Einladen"
-
 msgid "(none)"
 msgstr "(kein)"
 
@@ -2785,7 +2834,7 @@
 msgstr "Nachname"
 
 msgid "Email"
-msgstr "Email"
+msgstr "E-Mail"
 
 msgid "AIM Account"
 msgstr "AIM-Konto"
@@ -2875,6 +2924,9 @@
 msgid "Save buddylist..."
 msgstr "Buddy-Liste speichern..."
 
+msgid "Load buddylist from file..."
+msgstr "Buddy-Liste aus Datei laden..."
+
 msgid "Fill in the registration fields."
 msgstr "Füllen Sie die Registrierungsfelder aus."
 
@@ -3051,9 +3103,6 @@
 msgid "Save buddylist to file..."
 msgstr "Buddy-Liste in Datei speichern..."
 
-msgid "Load buddylist from file..."
-msgstr "Buddy-Liste aus Datei laden..."
-
 #. magic
 #. major_version
 #. minor_version
@@ -3183,8 +3232,10 @@
 msgstr "Falscher Modus"
 
 #, c-format
-msgid "Ban on %s by %s, set %ld seconds ago"
-msgstr "Verbot zu %s von %s, gesetzt vor %ld Sekunden"
+msgid "Ban on %s by %s, set %ld second ago"
+msgid_plural "Ban on %s by %s, set %ld seconds ago"
+msgstr[0] "Verbot zu %s von %s, gesetzt vor %ld Sekunde"
+msgstr[1] "Verbot zu %s von %s, gesetzt vor %ld Sekunden"
 
 #, c-format
 msgid "Ban on %s"
@@ -3620,6 +3671,8 @@
 msgid "Country"
 msgstr "Land"
 
+#. lots of clients (including purple) do this, but it's
+#. * out of spec
 msgid "Telephone"
 msgstr "Telefon"
 
@@ -3807,12 +3860,12 @@
 msgid "Capabilities"
 msgstr "Fähigkeiten"
 
+msgid "Priority"
+msgstr "Priorität"
+
 msgid "Resource"
 msgstr "Ressource"
 
-msgid "Priority"
-msgstr "Priorität"
-
 msgid "Middle Name"
 msgstr "Zweiter Name"
 
@@ -3897,7 +3950,7 @@
 "Benutzern zu suchen."
 
 msgid "Email Address"
-msgstr "Email-Adresse"
+msgstr "E-Mail-Adresse"
 
 msgid "Search for XMPP users"
 msgstr "Suche nach XMPP-Benutzern"
@@ -4534,14 +4587,14 @@
 msgstr "Standards _akzeptieren"
 
 #, c-format
+msgid "Error joining chat %s"
+msgstr "Fehler beim Betreten des Chats %s"
+
+#, c-format
 msgid "Error in chat %s"
 msgstr "Fehler im Chat %s"
 
 #, c-format
-msgid "Error joining chat %s"
-msgstr "Fehler beim Betreten des Chats %s"
-
-#, c-format
 msgid "Unable to send file to %s, user does not support file transfers"
 msgstr ""
 "Kann die Datei nicht an %s senden, da der Client des Benutzers keine "
@@ -4635,7 +4688,7 @@
 msgstr "Syntaxfehler (wahrscheinlich ein Client-Bug)"
 
 msgid "Invalid email address"
-msgstr "Ungültige Email-Adresse"
+msgstr "Ungültige E-Mail-Adresse"
 
 msgid "User does not exist"
 msgstr "Benutzer existiert nicht"
@@ -4808,7 +4861,7 @@
 msgstr "%s anstoßen..."
 
 msgid "Email Address..."
-msgstr "Email-Adresse..."
+msgstr "E-Mail-Adresse..."
 
 msgid "Your new MSN friendly name is too long."
 msgstr "Ihr neuer MSN-Benutzername zu lang."
@@ -4997,7 +5050,7 @@
 msgstr "Fax (privat)"
 
 msgid "Personal Email"
-msgstr "Email (privat)"
+msgstr "E-Mail (privat)"
 
 msgid "Personal IM"
 msgstr "IM (privat)"
@@ -5040,7 +5093,7 @@
 msgstr "Fax (geschäftlich)"
 
 msgid "Work Email"
-msgstr "Email (geschäftlich)"
+msgstr "E-Mail (geschäftlich)"
 
 msgid "Work IM"
 msgstr "IM (geschäftlich)"
@@ -5367,8 +5420,11 @@
 msgstr "Logge ein"
 
 #, c-format
-msgid "Connection to server lost (no data received within %d seconds)"
-msgstr ""
+msgid "Connection to server lost (no data received within %d second)"
+msgid_plural "Connection to server lost (no data received within %d seconds)"
+msgstr[0] ""
+"Verbindung zum Server verloren (seit %d Sekunde keine Daten empfangen)"
+msgstr[1] ""
 "Verbindung zum Server verloren (seit %d Sekunden keine Daten empfangen)"
 
 #. Can't write _()'d strings in array initializers. Workaround.
@@ -5468,9 +5524,15 @@
 
 #, c-format
 msgid ""
+"%d buddy was added or updated from the server (including buddies already on "
+"the server-side list)"
+msgid_plural ""
 "%d buddies were added or updated from the server (including buddies already "
 "on the server-side list)"
-msgstr ""
+msgstr[0] ""
+"%d Buddy wurde vom Server hinzugefügt oder aktualisiert (inklusive der "
+"Buddys, die schon auf der Serverliste sind)"
+msgstr[1] ""
 "%d Buddys wurden vom Server hinzugefügt oder aktualisiert (inklusive der "
 "Buddys, die schon auf der Serverliste sind)"
 
@@ -6110,11 +6172,11 @@
 msgid "In local permit/deny"
 msgstr "In lokaler erlaubt/verboten-Liste"
 
-msgid "Too evil (sender)"
-msgstr "Zu boshaft (Sender)"
-
-msgid "Too evil (receiver)"
-msgstr "Zu boshaft (Empfänger)"
+msgid "Warning level too high (sender)"
+msgstr "Warnstufe zu hoch (Absender)"
+
+msgid "Warning level too high (receiver)"
+msgstr "Warnstufe zu hoch (Empfänger)"
 
 msgid "User temporarily unavailable"
 msgstr "Benutzer ist temporär nicht verfügbar"
@@ -6218,6 +6280,9 @@
 msgid "Camera"
 msgstr "Kamera"
 
+msgid "Screen Sharing"
+msgstr "Gemeinsamer Bildschirm"
+
 msgid "Free For Chat"
 msgstr "Bereit zum Chatten"
 
@@ -6275,7 +6340,7 @@
 "only letters, numbers and spaces, or contain only numbers."
 msgstr ""
 "Anmeldung fehlgeschlagen: Sie konnten nicht als %s angemeldet werden, da der "
-"Benutzername fehlerhaft ist.  Benutzernamen müssen gültige Email-Adressen "
+"Benutzername fehlerhaft ist.  Benutzernamen müssen gültige E-Mail-Adressen "
 "sein oder mit einem Buchstaben beginnen und nur Buchstaben, Ziffern und "
 "Leerzeichen enthalten oder nur aus Ziffern bestehen."
 
@@ -6350,6 +6415,7 @@
 msgid "Unable to get a valid login hash."
 msgstr "Konnte keinen gültigen Login-Hash bekommen."
 
+#. allow multple logins?
 msgid "Password sent"
 msgstr "Passwort gesendet"
 
@@ -6424,7 +6490,7 @@
 "Message is:\n"
 "%s"
 msgstr ""
-"Sie haben eine ICQ-Email empfangen von %s [%s]\n"
+"Sie haben eine ICQ-E-Mail empfangen von %s [%s]\n"
 "\n"
 "Nachricht:\n"
 "%s"
@@ -6469,20 +6535,26 @@
 "überschritten wurde."
 
 #, c-format
-msgid "You missed %hu message from %s because he/she was too evil."
-msgid_plural "You missed %hu messages from %s because he/she was too evil."
+msgid ""
+"You missed %hu message from %s because his/her warning level is too high."
+msgid_plural ""
+"You missed %hu messages from %s because his/her warning level is too high."
 msgstr[0] ""
-"Sie haben %hu Nachricht von %s nicht erhalten, da er/sie zu boshaft war."
+"Sie haben %hu Nachricht von %s nicht erhalten, da seine/ihre Warnstufe zu "
+"hoch ist."
 msgstr[1] ""
-"Sie haben %hu Nachrichten von %s nicht erhalten,/sie zu boshaft war."
-
-#, c-format
-msgid "You missed %hu message from %s because you are too evil."
-msgid_plural "You missed %hu messages from %s because you are too evil."
+"Sie haben %hu Nachrichten von %s nicht erhalten, da seine/ihre Warnstufe zu "
+"hoch ist."
+
+#, c-format
+msgid "You missed %hu message from %s because your warning level is too high."
+msgid_plural ""
+"You missed %hu messages from %s because your warning level is too high."
 msgstr[0] ""
-"Sie haben %hu Nachricht von %s nicht erhalten, da Sie zu boshaft sind."
+"Sie haben %hu Nachricht von %s nicht erhalten, da Ihre Warnstufe zu hoch ist."
 msgstr[1] ""
-"Sie haben %hu Nachrichten von %s nicht erhalten, da Sie zu boshaft sind."
+"Sie haben %hu Nachrichten von %s nicht erhalten, da Ihre Warnstufe zu hoch "
+"ist."
 
 #, c-format
 msgid "You missed %hu message from %s for an unknown reason."
@@ -6514,9 +6586,6 @@
 msgid "Member Since"
 msgstr "Mitglied seit"
 
-msgid "Available Message"
-msgstr "Verfügbarkeitsnachricht"
-
 msgid "Your AIM connection may be lost."
 msgstr "Ihre AIM-Verbindung könnte unterbrochen sein."
 
@@ -6546,12 +6615,17 @@
 msgid "Personal Web Page"
 msgstr "Persönliche Webseite"
 
+#. aim_userinfo_t
+#. strip_html_tags
 msgid "Additional Information"
 msgstr "Zusätzliche Informationen"
 
 msgid "Zip Code"
 msgstr "PLZ"
 
+msgid "Work Information"
+msgstr "Information (Arbeit)"
+
 msgid "Division"
 msgstr "Abteilung"
 
@@ -6561,9 +6635,6 @@
 msgid "Web Page"
 msgstr "Webseite"
 
-msgid "Work Information"
-msgstr "Information (Arbeit)"
-
 msgid "Pop-Up Message"
 msgstr "Pop-Up Nachricht"
 
@@ -6575,12 +6646,12 @@
 
 #, c-format
 msgid "No results found for email address %s"
-msgstr "Keine Ergebnisse für die Email-Adresse %s gefunden"
+msgstr "Keine Ergebnisse für die E-Mail-Adresse %s gefunden"
 
 #, c-format
 msgid "You should receive an email asking to confirm %s."
 msgstr ""
-"Sie sollten eine Email erhalten, in der Sie aufgefordert werden, %s zu "
+"Sie sollten eine E-Mail erhalten, in der Sie aufgefordert werden, %s zu "
 "bestätigen."
 
 msgid "Account Confirmation Requested"
@@ -6613,7 +6684,7 @@
 "Error 0x%04x: Unable to change email address because there is already a "
 "request pending for this username."
 msgstr ""
-"Error 0x%04x: Kann die Email-Adresse nicht ändern, weil es schon eine "
+"Error 0x%04x: Kann die E-Mail-Adresse nicht ändern, weil es schon eine "
 "laufende Anfrage für diesen Benutzernamen gibt."
 
 #, c-format
@@ -6621,7 +6692,7 @@
 "Error 0x%04x: Unable to change email address because the given address has "
 "too many usernames associated with it."
 msgstr ""
-"Fehler 0x%04x: Kann die Email-Adresse nicht ändern, weil zu dieser Adresse "
+"Fehler 0x%04x: Kann die E-Mail-Adresse nicht ändern, weil zu dieser Adresse "
 "schon zu viele Benutzernamen gehören."
 
 #, c-format
@@ -6629,7 +6700,7 @@
 "Error 0x%04x: Unable to change email address because the given address is "
 "invalid."
 msgstr ""
-"Fehler 0x%04x: Kann die Email-Adresse nicht ändern, weil die angegebene "
+"Fehler 0x%04x: Kann die E-Mail-Adresse nicht ändern, weil die angegebene "
 "Adresse falsch ist."
 
 #, c-format
@@ -6641,7 +6712,7 @@
 
 #, c-format
 msgid "The email address for %s is %s"
-msgstr "Die Email-Adresse für %s ist %s"
+msgstr "Die E-Mail-Adresse für %s ist %s"
 
 msgid "Account Info"
 msgstr "Konto-Info"
@@ -6705,7 +6776,7 @@
 "numbers and spaces, or contain only numbers."
 msgstr ""
 "Konnte den Buddy %s nicht hinzufügen, da der Benutzername falsch ist.  "
-"Benutzernamen müssen gültige Email-Adressen sein oder mit einem Buchstaben "
+"Benutzernamen müssen gültige E-Mail-Adressen sein oder mit einem Buchstaben "
 "beginnen und nur Buchstaben, Ziffern und Leerzeichen enthalten oder nur aus "
 "Ziffern bestehen."
 
@@ -6784,12 +6855,6 @@
 "Ihr IM-Bild wurde nicht gesendet. Sie können keine IM-Bilder in AIM-Chats "
 "senden."
 
-msgid "Away Message"
-msgstr "Abwesenheitsnachricht"
-
-msgid "<i>(retrieving)</i>"
-msgstr "<i>(empfange)</i>"
-
 msgid "iTunes Music Store Link"
 msgstr "iTunes Music Store Link"
 
@@ -6864,13 +6929,13 @@
 "fragen“ auswählen."
 
 msgid "Find Buddy by Email"
-msgstr "Suche Buddys nach Email-Adresse"
+msgstr "Suche Buddys nach E-Mail-Adresse"
 
 msgid "Search for a buddy by email address"
-msgstr "Suche nach einem Buddy mit einer bestimmten Email-Adresse"
+msgstr "Suche nach einem Buddy mit einer bestimmten E-Mail-Adresse"
 
 msgid "Type the email address of the buddy you are searching for."
-msgstr "Geben Sie die Email-Adresse des Buddys ein, nach dem Sie suchen."
+msgstr "Geben Sie die E-Mail-Adresse des Buddys ein, nach dem Sie suchen."
 
 msgid "_Search"
 msgstr "_Suchen"
@@ -6893,16 +6958,16 @@
 msgstr "Konto bestätigen"
 
 msgid "Display Currently Registered Email Address"
-msgstr "Zeige die aktuell registrierte Email-Adresse"
+msgstr "Zeige die aktuell registrierte E-Mail-Adresse"
 
 msgid "Change Currently Registered Email Address..."
-msgstr "Ändere die aktuell registrierte Email-Adresse..."
+msgstr "Ändere die aktuell registrierte E-Mail-Adresse..."
 
 msgid "Show Buddies Awaiting Authorization"
 msgstr "Zeige Buddys, von denen Sie Autorisierung erwarten"
 
 msgid "Search for Buddy by Email Address..."
-msgstr "Suche Buddys nach Email-Adresse..."
+msgstr "Suche Buddys nach E-Mail-Adresse..."
 
 msgid "Search for Buddy by Information"
 msgstr "Suche Buddy nach Information"
@@ -6916,6 +6981,9 @@
 "Dateiübertragungen und Direkt-IM (langsamer,\n"
 "aber zeigt Ihre IP-Adresse nicht)"
 
+msgid "Allow multiple simultaneous logins"
+msgstr "Mehrere gleichzeitige Logins erlauben"
+
 #, c-format
 msgid "Asking %s to connect to us at %s:%hu for Direct IM."
 msgstr "Frage %s, ob er sich zu uns auf %s:%hu für Direkt-IM verbinden möchte."
@@ -8740,7 +8808,7 @@
 
 #, c-format
 msgid "Email: \t\t%s\n"
-msgstr "Email: \t\t%s\n"
+msgstr "E-Mail: \t\t%s\n"
 
 #, c-format
 msgid "Host Name: \t%s\n"
@@ -8988,7 +9056,7 @@
 msgstr "Verzeichnis-Dienst ist zur Zeit nicht verfügbar."
 
 msgid "Email lookup restricted."
-msgstr "Email-Suche eingeschränkt."
+msgstr "E-Mail-Suche eingeschränkt."
 
 msgid "Keyword ignored."
 msgstr "Stichwort ignoriert."
@@ -9695,7 +9763,6 @@
 msgid "Stored Image"
 msgstr "Gespeichertes Bild"
 
-#, fuzzy
 msgid "Stored Image. (that'll have to do for now)"
 msgstr "Gespeichertes Bild. (Das muss erstmal reichen)"
 
@@ -10185,8 +10252,8 @@
 msgid "/_Accounts"
 msgstr "/_Konten"
 
-msgid "/Accounts/Manage"
-msgstr "/Konten/Verwalten"
+msgid "/Accounts/Manage Accounts"
+msgstr "/Konten/Konten verwalten"
 
 #. Tools
 msgid "/_Tools"
@@ -10198,9 +10265,6 @@
 msgid "/Tools/_Certificates"
 msgstr "/Werkzeuge/_Zertifikate"
 
-msgid "/Tools/Smile_y"
-msgstr "/Werkzeuge/Smile_y"
-
 msgid "/Tools/Plu_gins"
 msgstr "/Werkzeuge/Plu_gins"
 
@@ -10210,6 +10274,9 @@
 msgid "/Tools/Pr_ivacy"
 msgstr "/Werkzeuge/Pri_vatsphäre"
 
+msgid "/Tools/Smile_y"
+msgstr "/Werkzeuge/Smile_y"
+
 msgid "/Tools/_File Transfers"
 msgstr "/Werkzeuge/_Dateiübertragungen"
 
@@ -10376,13 +10443,13 @@
 "<span weight='bold' size='larger'>Welcome to %s!</span>\n"
 "\n"
 "You have no accounts enabled. Enable your IM accounts from the <b>Accounts</"
-"b> window at <b>Accounts->Manage</b>. Once you enable accounts, you'll be "
-"able to sign on, set your status, and talk to your friends."
+"b> window at <b>Accounts->Manage Accounts</b>. Once you enable accounts, "
+"you'll be able to sign on, set your status, and talk to your friends."
 msgstr ""
 "<span weight='bold' size='larger'>Willkommen bei %s!</span>\n"
 "\n"
 "Sie haben keine Konten aktiviert. Aktivieren Sie Ihre IM-Konten vom "
-"<b>Konten</b>-Fenster über <b>Konten->Verwalten</b>. Wenn Sie Konten "
+"<b>Konten</b>-Fenster über <b>Konten->Konten verwalten</b>. Wenn Sie Konten "
 "aktiviert haben, können Sie sich anmelden, Ihren Status setzen und mit Ihren "
 "Freunden reden."
 
@@ -10443,6 +10510,12 @@
 msgid "Please enter the name of the group to be added."
 msgstr "Bitte geben Sie den Namen der Gruppe ein, die hinzugefügt werden soll."
 
+msgid "Enable Account"
+msgstr "Konten aktivieren"
+
+msgid "<PurpleMain>/Accounts/Enable Account"
+msgstr "<PurpleMain>/Konten/Konto aktivieren"
+
 msgid "<PurpleMain>/Accounts/"
 msgstr "<PurpleMain>/Konten/"
 
@@ -10455,12 +10528,6 @@
 msgid "_Disable"
 msgstr "_Deaktivieren"
 
-msgid "Enable Account"
-msgstr "Konten aktivieren"
-
-msgid "<PurpleMain>/Accounts/Enable Account"
-msgstr "<PurpleMain>/Konten/Konto aktivieren"
-
 msgid "/Tools"
 msgstr "/Werkzeuge"
 
@@ -11375,9 +11442,8 @@
 msgid "Color to draw the name of an action message."
 msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird."
 
-#, fuzzy
 msgid "Action Message Name Color for Whispered Message"
-msgstr "Farbe des Absendernamens für Aktions-Nachrichten"
+msgstr "Farbe des Absendernamens für geflüsterte Aktions-Nachrichten"
 
 msgid "Whisper Message Name Color"
 msgstr "Farbe des Absendernamens für Flüster-Nachrichten"
@@ -11400,7 +11466,7 @@
 msgstr "Tipp-Benachrichtigung aktivieren"
 
 msgid "_Copy Email Address"
-msgstr "Kopiere _Email-Adresse"
+msgstr "Kopiere _E-Mail-Adresse"
 
 msgid "_Open Link in Browser"
 msgstr "Ö_ffne Link im Browser"
@@ -11452,7 +11518,6 @@
 msgid "_Save Image..."
 msgstr "Bild _speichern..."
 
-#, fuzzy
 msgid "_Add Custom Smiley..."
 msgstr "Benutzerdefinierten Smiley _hinzufügen..."
 
@@ -11494,16 +11559,18 @@
 msgid "Insert Image"
 msgstr "Bild einfügen"
 
-msgid ""
-"This smiley is disabled because a custom smiley exists for this shortcut."
+#, c-format
+msgid ""
+"This smiley is disabled because a custom smiley exists for this shortcut:\n"
+" %s"
 msgstr ""
 "Dieser Smiley ist deaktiviert, da ein benutzerdefinierter Smiley für diese "
-"Tastenkombination existiert."
+"Tastenkombination existiert:\n"
+" %s"
 
 msgid "Smile!"
 msgstr "Lächeln!"
 
-#, fuzzy
 msgid "_Manage custom smileys"
 msgstr "Benutzerdefinierte Smileys _verwalten"
 
@@ -11771,8 +11838,8 @@
 #, c-format
 msgid "<b>%d new email.</b>"
 msgid_plural "<b>%d new emails.</b>"
-msgstr[0] "<b>%d neue Email.</b>"
-msgstr[1] "<b>%d neue Emails.</b>"
+msgstr[0] "<b>%d neue E-Mail.</b>"
+msgstr[1] "<b>%d neue E-Mails.</b>"
 
 #, c-format
 msgid "The browser command \"%s\" is invalid."
@@ -12857,19 +12924,19 @@
 msgstr "_Assoziiere den Buddy"
 
 msgid "Unable to send email"
-msgstr "Email konnte nicht gesendet werden"
+msgstr "E-Mail konnte nicht gesendet werden"
 
 msgid "The evolution executable was not found in the PATH."
 msgstr "Die ausführbare Evolution-Datei wurde nicht im Pfad (PATH) gefunden."
 
 msgid "An email address was not found for this buddy."
-msgstr "Für diesen Buddy wurde keine Email-Adresse gefunden."
+msgstr "Für diesen Buddy wurde keine E-Mail-Adresse gefunden."
 
 msgid "Add to Address Book"
 msgstr "Zum Adressbuch hinzufügen"
 
 msgid "Send Email"
-msgstr "Email senden"
+msgstr "E-Mail senden"
 
 #. Configuration frame
 msgid "Evolution Integration Configuration"
@@ -12919,7 +12986,7 @@
 msgstr "Nachname:"
 
 msgid "Email:"
-msgstr "Email:"
+msgstr "E-Mail:"
 
 #. *< type
 #. *< ui_requirement
@@ -12972,7 +13039,7 @@
 
 msgid "Adds a small box to the buddy list that shows if you have new mail."
 msgstr ""
-"Fügt eine kleine Box zur Buddy-Liste hinzu, die zeigt, ob Sie neue Emails "
+"Fügt eine kleine Box zur Buddy-Liste hinzu, die zeigt, ob Sie neue E-Mails "
 "haben."
 
 msgid "Markerline"
--- a/po/en_AU.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/en_AU.po	Thu Jun 26 20:25:38 2008 +0000
@@ -14896,7 +14896,7 @@
 msgid "%d person in room"
 msgid_plural "%d people in room"
 msgstr[0] "%d person in room"
-msgstr[1] "%d person in room"
+msgstr[1] "%d people in room"
 
 #: ../pidgin/gtkconv.c:6486 ../pidgin/gtkstatusbox.c:660
 #, fuzzy
--- a/po/en_GB.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/en_GB.po	Thu Jun 26 20:25:38 2008 +0000
@@ -2911,7 +2911,7 @@
 msgstr "Your current password is different from the one that you specified."
 
 msgid "Unable to change password. Error occurred.\n"
-msgstr "Unable to change password. An error occured.\n"
+msgstr "Unable to change password. An error occurred.\n"
 
 msgid "Change password for the Gadu-Gadu account"
 msgstr "Change password for the Gadu-Gadu account"
@@ -5270,7 +5270,7 @@
 msgstr "Message could not be sent because the user is offline:"
 
 msgid "Message could not be sent because a connection error occurred:"
-msgstr "Message could not be sent because a connection error occured:"
+msgstr "Message could not be sent because a connection error occurred:"
 
 msgid "Message could not be sent because we are sending too quickly:"
 msgstr "Message could not be sent because we are sending too quickly:"
--- a/po/fi.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/fi.po	Thu Jun 26 20:25:38 2008 +0000
@@ -16488,7 +16488,6 @@
 "<span foreground=\"red\" weight=\"bold\">Error: %s\n"
 "Check the plugin website for an update.</span>"
 msgstr ""
-"%s\n"
 "<span foreground=\"red\" weight=\"bold\">Virhe: %s\n"
 "Tarkista onko liitännäisen WWW-sivustolla päivitystä.</span>"
 
--- a/po/gl.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/gl.po	Thu Jun 26 20:25:38 2008 +0000
@@ -14,7 +14,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
 "X-Generator: KBabel 1.11.4\n"
 
 #: ../finch/finch.c:64 ../finch/finch.c:301 ../finch/finch.c:330
--- a/po/it.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/it.po	Thu Jun 26 20:25:38 2008 +0000
@@ -3833,8 +3833,8 @@
 msgstr "Impossibile trovare un'installazione di ActiveTCL. Se vuoi usare i plugin TCL, installa ActiveTCL da http://www.activestate.com\n"
 
 #: ../libpurple/protocols/bonjour/bonjour.c:107
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
-msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging per maggiori informazioni."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
+msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://d.pidgin.im/BonjourWindows per maggiori informazioni."
 
 #: ../libpurple/protocols/bonjour/bonjour.c:126
 msgid "Unable to listen for incoming IM connections\n"
--- a/po/ko.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/ko.po	Thu Jun 26 20:25:38 2008 +0000
@@ -913,7 +913,7 @@
 msgid "%s (%s) has %d new message."
 msgid_plural "%s (%s) has %d new messages."
 msgstr[0] "%s (%s) 에는 %d 개의 새 메일이 있습니다."
-msgstr[1] "%s (%s) 에는 %d 개의 새 메일이 있습니다."
+#msgstr[1] "%s (%s) 에는 %d 개의 새 메일이 있습니다."
 
 #: ../console/gntnotify.c:206
 #: ../gtk/gtknotify.c:322
@@ -1875,7 +1875,7 @@
 msgid "%d unread message from %s\n"
 msgid_plural "%d unread messages from %s\n"
 msgstr[0] "%2$s 님으로부터 %1$d개의 읽지 않은 메일이 있습니다.\n"
-msgstr[1] "%2$s さんから %1$d個の未?のメッセ?ジがあります\n"
+#msgstr[1] "%2$s さんから %1$d個の未?のメッセ?ジがあります\n"
 
 #: ../gtk/gtkblist.c:3654
 msgid "Manually"
@@ -2357,7 +2357,7 @@
 msgid "%d person in room"
 msgid_plural "%d people in room"
 msgstr[0] "대화실에 %d 명이 있습니다."
-msgstr[1] "대화실에 %d 명이 있습니다."
+#msgstr[1] "대화실에 %d 명이 있습니다."
 
 #: ../gtk/gtkconv.c:5855
 #: ../gtk/gtkstatusbox.c:567
@@ -2902,7 +2902,7 @@
 msgid "You are about to remove the contact containing %s and %d other buddy from your buddy list.  Do you want to continue?"
 msgid_plural "You are about to remove the contact containing %s and %d other buddies from your buddy list.  Do you want to continue?"
 msgstr[0] "친구 목록에서 %s 님을 포함한 %d 명의 친구의 삭제하려고 합니다. 계속 하시겠습니까?"
-msgstr[1] "친구 목록에서 %s 님을 포함한 %d 명의 친구의 삭제하려고 합니다. 계속 하시겠습니까?"
+#msgstr[1] "친구 목록에서 %s 님을 포함한 %d 명의 친구의 삭제하려고 합니다. 계속 하시겠습니까?"
 
 #: ../gtk/gtkdialogs.c:1016
 msgid "Remove Contact"
@@ -3598,14 +3598,14 @@
 msgid "%s has %d new message."
 msgid_plural "%s has %d new messages."
 msgstr[0] "%s 에는 %d 개의 새로운 메시지가 있습니다."
-msgstr[1] "%s 에는 %d 개의 새로운 메시지가 있습니다."
+#msgstr[1] "%s 에는 %d 개의 새로운 메시지가 있습니다."
 
 #: ../gtk/gtknotify.c:511
 #, c-format
 msgid "<b>You have %d new email.</b>"
 msgid_plural "<b>You have %d new emails.</b>"
 msgstr[0] "<b>%d 개의 새로운 메시지가 있습니다.</b>"
-msgstr[1] "<b>%d 개의 새로운 메시지가 있습니다.</b>"
+#msgstr[1] "<b>%d 개의 새로운 메시지가 있습니다.</b>"
 
 #: ../gtk/gtknotify.c:699
 #: ../libgaim/protocols/sametime/sametime.c:5548
@@ -5919,7 +5919,7 @@
 msgid "%d buddy from group %s was not removed because it belongs to an account which is disabled or offline.  This buddy and the group were not removed.\n"
 msgid_plural "%d buddies from group %s were not removed because they belong to accounts which are currently disabled or offline.  These buddies and the group were not removed.\n"
 msgstr[0] "%d 개의 계정이 사용 안 함 또는 오프라인 상태이기 때문에 그 계정을 그룹 %s (으)로부터 삭제하지 못했습니다. 그 친구와 그룹은 삭제할 수 없습니다.\n"
-msgstr[1] "%d 개의 계정이 사용 안 함 또는 오프라인 상태이기 때문에 그 계정을 그룹 %s (으)로부터 삭제하지 못했습니다. 그 친구와 그룹은 삭제할 수 없습니다.\n"
+#msgstr[1] "%d 개의 계정이 사용 안 함 또는 오프라인 상태이기 때문에 그 계정을 그룹 %s (으)로부터 삭제하지 못했습니다. 그 친구와 그룹은 삭제할 수 없습니다.\n"
 
 #: ../libgaim/blist.c:1929
 msgid "Group not removed"
@@ -9869,10 +9869,10 @@
 "MSN 서비스가 유지보수를 위해 %d 분간 정지하고 있습니다. 자동으로 접속이 끊길 예정이므로, 지금 수행되고 있는 대화를 종료해 주십시오.\n"
 "\n"
 "유지보수가 완료되면 다시 접속이 가능합니다."
-msgstr[1] ""
-"MSN 서비스가 유지보수를 위해 %d 분간 정지하고 있습니다. 자동으로 접속이 끊길 예정이므로, 지금 수행되고 있는 대화를 종료해 주십시오.\n"
-"\n"
-"유지보수가 완료되면 다시 접속이 가능합니다."
+#msgstr[1] ""
+#"MSN 서비스가 유지보수를 위해 %d 분간 정지하고 있습니다. 자동으로 접속이 끊길 예정이므로, 지금 수행되고 있는 대화를 종료해 주십시오.\n"
+#"\n"
+#"유지보수가 완료되면 다시 접속이 가능합니다."
 
 #: ../libgaim/protocols/msn/servconn.c:135
 msgid "Writing error"
@@ -10921,42 +10921,42 @@
 msgid "You missed %hu message from %s because it was invalid."
 msgid_plural "You missed %hu messages from %s because they were invalid."
 msgstr[0] "%2$s 님으로부터의 %1$hu 개의 메시지는 타당하지 않아 받지 못했습니다."
-msgstr[1] "%2$s 님으로부터의 %1$hu 개의 메시지는 타당하지 않아 받지 못했습니다."
+#msgstr[1] "%2$s 님으로부터의 %1$hu 개의 메시지는 타당하지 않아 받지 못했습니다."
 
 #: ../libgaim/protocols/oscar/oscar.c:2560
 #, c-format
 msgid "You missed %hu message from %s because it was too large."
 msgid_plural "You missed %hu messages from %s because they were too large."
 msgstr[0] "%2$s 님으로부터의 %1$hu개의 메시지는 너무 커서 받지 못했습니다."
-msgstr[1] "%2$s 님으로부터의 %1$hu개의 메시지는 너무 커서 받지 못했습니다."
+#msgstr[1] "%2$s 님으로부터의 %1$hu개의 메시지는 너무 커서 받지 못했습니다."
 
 #: ../libgaim/protocols/oscar/oscar.c:2569
 #, c-format
 msgid "You missed %hu message from %s because the rate limit has been exceeded."
 msgid_plural "You missed %hu messages from %s because the rate limit has been exceeded."
 msgstr[0] "속도 제한을 상회하여, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
-msgstr[1] "속도 제한을 상회하여, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
+#msgstr[1] "속도 제한을 상회하여, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
 
 #: ../libgaim/protocols/oscar/oscar.c:2578
 #, c-format
 msgid "You missed %hu message from %s because he/she was too evil."
 msgid_plural "You missed %hu messages from %s because he/she was too evil."
 msgstr[0] "유해한 상대방이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
-msgstr[1] "유해한 상대방이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
+#msgstr[1] "유해한 상대방이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
 
 #: ../libgaim/protocols/oscar/oscar.c:2587
 #, c-format
 msgid "You missed %hu message from %s because you are too evil."
 msgid_plural "You missed %hu messages from %s because you are too evil."
 msgstr[0] "유해한 자신이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
-msgstr[1] "유해한 자신이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
+#msgstr[1] "유해한 자신이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
 
 #: ../libgaim/protocols/oscar/oscar.c:2596
 #, c-format
 msgid "You missed %hu message from %s for an unknown reason."
 msgid_plural "You missed %hu messages from %s for an unknown reason."
 msgstr[0] "원인은 알 수 없지만, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
-msgstr[1] "원인은 알 수 없지만, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
+#msgstr[1] "원인은 알 수 없지만, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
 
 #: ../libgaim/protocols/oscar/oscar.c:2718
 #, c-format
@@ -11178,7 +11178,7 @@
 msgid "The maximum profile length of %d byte has been exceeded.  It has been truncated for you."
 msgid_plural "The maximum profile length of %d bytes has been exceeded.  It has been truncated for you."
 msgstr[0] "프로파일 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
-msgstr[1] "프로파일 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
+#msgstr[1] "프로파일 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
 
 #: ../libgaim/protocols/oscar/oscar.c:4461
 msgid "Profile too long."
@@ -11189,7 +11189,7 @@
 msgid "The maximum away message length of %d byte has been exceeded.  It has been truncated for you."
 msgid_plural "The maximum away message length of %d bytes has been exceeded.  It has been truncated for you."
 msgstr[0] "자리 비움 메시지 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
-msgstr[1] "자리 비움 메시지 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
+#msgstr[1] "자리 비움 메시지 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
 
 #: ../libgaim/protocols/oscar/oscar.c:4510
 msgid "Away message too long."
@@ -14183,7 +14183,7 @@
 msgid "%s requests %s to accept %d file: %s (%.2f %s)%s%s"
 msgid_plural "%s requests %s to accept %d files: %s (%.2f %s)%s%s"
 msgstr[0] "%s 님으로부터 %s 님에게 %d 개의 파일 요청: %s (%.2f %s)%s%s"
-msgstr[1] "%s 님으로부터 %s 님에게 %d 개의 파일 요청: %s (%.2f %s)%s%s"
+#msgstr[1] "%s 님으로부터 %s 님에게 %d 개의 파일 요청: %s (%.2f %s)%s%s"
 
 #: ../libgaim/protocols/toc/toc.c:2236
 #, c-format
@@ -14945,42 +14945,42 @@
 msgid "%d second"
 msgid_plural "%d seconds"
 msgstr[0] "%d 초"
-msgstr[1] "%d 초"
+#msgstr[1] "%d 초"
 
 #: ../libgaim/util.c:2939
 #, c-format
 msgid "%d day"
 msgid_plural "%d days"
 msgstr[0] "%d 일"
-msgstr[1] "%d 일"
+#msgstr[1] "%d 일"
 
 #: ../libgaim/util.c:2947
 #, c-format
 msgid "%s, %d hour"
 msgid_plural "%s, %d hours"
 msgstr[0] "%s %d 시간"
-msgstr[1] "%s %d 시간"
+#msgstr[1] "%s %d 시간"
 
 #: ../libgaim/util.c:2953
 #, c-format
 msgid "%d hour"
 msgid_plural "%d hours"
 msgstr[0] "%d 시간"
-msgstr[1] "%d 시간"
+#msgstr[1] "%d 시간"
 
 #: ../libgaim/util.c:2961
 #, c-format
 msgid "%s, %d minute"
 msgid_plural "%s, %d minutes"
 msgstr[0] "%s %d 분"
-msgstr[1] "%s %d 분"
+#msgstr[1] "%s %d 분"
 
 #: ../libgaim/util.c:2967
 #, c-format
 msgid "%d minute"
 msgid_plural "%d minutes"
 msgstr[0] "%d 분"
-msgstr[1] "%d 분"
+#msgstr[1] "%d 분"
 
 #: ../libgaim/util.c:3166
 #: ../libgaim/util.c:3464
--- a/po/lo.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/lo.po	Thu Jun 26 20:25:38 2008 +0000
@@ -14,7 +14,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+#"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
 #: ../finch/finch.c:64 ../finch/finch.c:301 ../finch/finch.c:330
 #: ../finch/finch.c:418
--- a/po/lt.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/lt.po	Thu Jun 26 20:25:38 2008 +0000
@@ -721,8 +721,11 @@
 msgstr "Pristabdyti"
 
 #, c-format
-msgid "File Transfers - %d%% of %d files"
-msgstr "Failų perdavimai – %d%% iš %d"
+msgid "File Transfers - %d%% of %d file"
+msgid_plural "File Transfers - %d%% of %d files"
+msgstr[0] "Failų perdavimai – %d%% iš %d failo"
+msgstr[1] "Failų perdavimai – %d%% iš %d failų"
+msgstr[2] "Failų perdavimai – %d%% iš %d failų"
 
 #. Create the window.
 msgid "File Transfers"
@@ -3258,8 +3261,11 @@
 msgstr "Bloga būsena"
 
 #, c-format
-msgid "Ban on %s by %s, set %ld seconds ago"
-msgstr "Vartotojui %s uždraudė prisijungti %s prieš %ld sekundžių"
+msgid "Ban on %s by %s, set %ld second ago"
+msgid_plural "Ban on %s by %s, set %ld seconds ago"
+msgstr[0] "Vartotojui %s uždraudė prisijungti %s prieš %ld sekundę"
+msgstr[1] "Vartotojui %s uždraudė prisijungti %s prieš %ld sekundes"
+msgstr[2] "Vartotojui %s uždraudė prisijungti %s prieš %ld sekundžių"
 
 #, c-format
 msgid "Ban on %s"
@@ -5439,8 +5445,11 @@
 msgstr "Prisijungiama"
 
 #, c-format
-msgid "Connection to server lost (no data received within %d seconds)"
-msgstr "Nutrūko ryšys su serveriu (negauta jokių duomenų per %d sekundžių)"
+msgid "Connection to server lost (no data received within %d second)"
+msgid_plural "Connection to server lost (no data received within %d seconds)"
+msgstr[0] "Nutrūko ryšys su serveriu (negauta jokių duomenų per %d sekundę)"
+msgstr[1] "Nutrūko ryšys su serveriu (negauta jokių duomenų per %d sekundes)"
+msgstr[2] "Nutrūko ryšys su serveriu (negauta jokių duomenų per %d sekundžių)"
 
 #. Can't write _()'d strings in array initializers. Workaround.
 msgid "New mail messages"
@@ -5538,9 +5547,18 @@
 
 #, c-format
 msgid ""
+"%d buddy was added or updated from the server (including buddies already on "
+"the server-side list)"
+msgid_plural ""
 "%d buddies were added or updated from the server (including buddies already "
 "on the server-side list)"
-msgstr ""
+msgstr[0] ""
+"%d bičiulis buvo pridėtas ar atnaujintas iš serverio (įskaitant ir "
+"bičiulius, jau esančius serverio sąraše)"
+msgstr[1] ""
+"%d bičiuliai buvo pridėti ar atnaujinti iš serverio (įskaitant ir bičiulius, "
+"jau esančius serverio sąraše)"
+msgstr[2] ""
 "%d bičiulių buvo pridėta ar atnaujinta iš serverio (įskaitant ir bičiulius, "
 "jau esančius serverio sąraše)"
 
--- a/po/nb.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/nb.po	Thu Jun 26 20:25:38 2008 +0000
@@ -3877,7 +3877,7 @@
 msgstr "Kunne ikke finne en ActiveTCL installasjon. Om du ønsker å bruke TCL tillegg, installer ActiveTCL fra http://www.activestate.com\n"
 
 #: ../libpurple/protocols/bonjour/bonjour.c:101
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
 msgstr ""
 
 #: ../libpurple/protocols/bonjour/bonjour.c:120
@@ -10311,7 +10311,7 @@
 #: ../libpurple/protocols/qq/sys_msg.c:166
 #, c-format
 msgid "You have been added by %s"
-msgstr "Du har blitt lagt til av %s (%s)"
+msgstr "Du har blitt lagt til av %s"
 
 #: ../libpurple/protocols/qq/sys_msg.c:169
 #: ../libpurple/protocols/qq/sys_msg.c:263
--- a/po/nl.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/nl.po	Thu Jun 26 20:25:38 2008 +0000
@@ -2489,10 +2489,11 @@
 "Activation date: %s\n"
 "Expiration date: %s\n"
 msgstr ""
-"Gemeenschappelijke naam: %s\n"
-"\n"
-"Vingerafdruk (SHA1): %s\n"
-"\n"
+#msgstr ""
+#"Gemeenschappelijke naam: %s\n"
+#"\n"
+#"Vingerafdruk (SHA1): %s\n"
+#"\n"
 
 #. TODO: Find what the handle ought to be
 #: ../libpurple/certificate.c:1905
@@ -6982,7 +6983,7 @@
 #: ../libpurple/protocols/msn/msn.c:129 ../libpurple/protocols/msnp9/msn.c:130
 #, c-format
 msgid "%s has nudged you!"
-msgstr "%s heeft je aangestoten [%s]"
+msgstr "%s heeft je aangestoten"
 
 #: ../libpurple/protocols/msn/msn.c:129 ../libpurple/protocols/msnp9/msn.c:130
 #, c-format
@@ -8177,7 +8178,8 @@
 #: ../libpurple/protocols/myspace/zap.c:59
 #, c-format
 msgid "%s has torched you!"
-msgstr "De gebruiker heeft u befakkeld"
+msgstr ""
+#msgstr "De gebruiker heeft u befakkeld"
 
 #: ../libpurple/protocols/myspace/zap.c:59
 #, c-format
@@ -8876,9 +8878,10 @@
 "(There was an error receiving this message.  Either you and %s have "
 "different encodings selected, or %s has a buggy client.)"
 msgstr ""
-"(Er is een fout opgetreden bij het ontvangen van het bericht. Of jij en %s "
-"hebben verschillende coderingen geselecteerd of de andere persoon gebruikt "
-"een chatprogramma met fouten.)"
+#msgstr ""
+#"(Er is een fout opgetreden bij het ontvangen van het bericht. Of jij en %s "
+#"hebben verschillende coderingen geselecteerd of de andere persoon gebruikt "
+#"een chatprogramma met fouten.)"
 
 #. Label
 #: ../libpurple/protocols/oscar/oscar.c:640 ../pidgin/gtkutils.c:2449
@@ -10452,7 +10455,7 @@
 #: ../libpurple/protocols/qq/send_file.c:707
 #, c-format
 msgid "%d has declined the file %s"
-msgstr "%s heeft het onderwerp veranderd naar: %sx"
+msgstr "%d heeft het onderwerp veranderd naar: %s"
 
 #: ../libpurple/protocols/qq/send_file.c:710
 #: ../libpurple/protocols/qq/send_file.c:739
@@ -14558,7 +14561,7 @@
 "<b>Occupants:</b> %d"
 msgstr ""
 "\n"
-"<b>Deelnemers:</b> %s"
+"<b>Deelnemers:</b> %d"
 
 #: ../pidgin/gtkblist.c:3253
 #, c-format
--- a/po/ps.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/ps.po	Thu Jun 26 20:25:38 2008 +0000
@@ -15,7 +15,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+#"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
 #: ../finch/finch.c:64 ../finch/finch.c:301 ../finch/finch.c:330
 #: ../finch/finch.c:418
--- a/po/sq.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/sq.po	Thu Jun 26 20:25:38 2008 +0000
@@ -14,6 +14,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: KBabel 1.0.2\n"
 "X-Poedit-Language: Albanian\n"
 "X-Poedit-Country: ALBANIA\n"
--- a/po/ta.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/ta.po	Thu Jun 26 20:25:38 2008 +0000
@@ -17,7 +17,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+"Plural-Forms: Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Poedit-Country: INDIA\n"
 "X-Generator: KBabel 1.11.4\n"
 "X-Poedit-Bookmarks: -1,-1,-1,-1,-1,-1,-1,-1,-1,1715\n"
--- a/po/te.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/te.po	Thu Jun 26 20:25:38 2008 +0000
@@ -9,6 +9,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Translate Toolkit 0.10\n"
 "X-Poedit-Language: Telugu\n"
 "X-Poedit-SourceCharset: utf-8\n"
@@ -349,7 +350,7 @@
 #: ../pidgin/gtkaccount.c:2447
 #, c-format
 msgid "%s%s%s%s has made %s his or her buddy%s%s"
-msgstr " %s%s%s తన మిత్రుడు లేదా స్నేహితురాలు %s%s  గా  %s ను  చేర్చుకున్నారు "
+msgstr "%s%s%s%s తన మిత్రుడు లేదా స్నేహితురాలు %s%s  గా  %s ను  చేర్చుకున్నారు "
 
 #: ../finch/gntaccount.c:884
 #: ../pidgin/gtkaccount.c:2499
@@ -1044,7 +1045,7 @@
 msgstr ""
 "%s\n"
 "\n"
-"%s లోపాన్ని సరిచేసి అకౌంటును తిరిగి క్రియాశీలం చేసేదాకా అకౌంటుకు మళ్ళీ కనెక్ట్ చేయడానికి ప్రయత్నించదు."
+"లోపాన్ని సరిచేసి అకౌంటును తిరిగి క్రియాశీలం చేసేదాకా అకౌంటుకు మళ్ళీ కనెక్ట్ చేయడానికి ప్రయత్నించదు."
 
 #: ../finch/gntconn.c:138
 msgid "Re-enable Account"
@@ -1427,8 +1428,8 @@
 #, c-format
 msgid "%s (%s) has %d new message."
 msgid_plural "%s (%s) has %d new messages."
-msgstr[0] "%s has %d new message."
-msgstr[1] "%s के %d लिए नये संदेश हैं।"
+msgstr[0] "%s (%s) has %d new message."
+msgstr[1] "%s (%s) के %d लिए नये संदेश हैं।"
 
 #: ../finch/gntnotify.c:226
 #: ../pidgin/gtknotify.c:342
@@ -2496,7 +2497,8 @@
 #: ../libpurple/certificate.c:1185
 #, c-format
 msgid "Accept certificate for %s?"
-msgstr "సంభాషణ ఆహ్వానాన్ని అంగీకరించారా?"
+msgstr ""
+#msgstr "సంభాషణ ఆహ్వానాన్ని అంగీకరించారా?"
 
 #. TODO: Find what the handle ought to be
 #: ../libpurple/certificate.c:1191
@@ -3122,7 +3124,8 @@
 #: ../libpurple/plugin.c:663
 #, c-format
 msgid "%s requires %s, but it failed to unload."
-msgstr "%s డిపెండర్ ప్లగ్ ఇన్ అన్ లోడ్ కావడంలో వైఫల్యం."
+msgstr ""
+#msgstr "%s డిపెండర్ ప్లగ్ ఇన్ అన్ లోడ్ కావడంలో వైఫల్యం."
 
 #: ../libpurple/plugins/autoaccept.c:23
 msgid "Autoaccept"
@@ -3508,7 +3511,7 @@
 #: ../libpurple/plugins/statenotify.c:80
 #, c-format
 msgid "%s has signed off."
-msgstr "%s సైన్ ఆఫ్ చేశారు. (%s)"
+msgstr "%s సైన్ ఆఫ్ చేశారు."
 
 #: ../libpurple/plugins/log_reader.c:1587
 msgid "One or more messages may have been undeliverable."
@@ -4056,7 +4059,7 @@
 msgstr "ActiveTCL ఇన్స్టాలేషన్ ను కనుగొనడంలో అశక్తత. మీరు TCL ప్లగ్ ఇన్లను ఉపయోగించాలనుకుంటే http://www.activestate.com\n"
 
 #: ../libpurple/protocols/bonjour/bonjour.c:108
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
 msgstr ""
 
 #: ../libpurple/protocols/bonjour/bonjour.c:127
@@ -4197,7 +4200,8 @@
 #: ../libpurple/protocols/gg/gg.c:278
 #, c-format
 msgid "Couldn't write buddy list for %s to %s"
-msgstr "మిత్రుల జాబితాను లోడ్ చేయలేకపోతోంది"
+msgstr ""
+#msgstr "మిత్రుల జాబితాను లోడ్ చేయలేకపోతోంది"
 
 #: ../libpurple/protocols/gg/gg.c:303
 #: ../libpurple/protocols/gg/gg.c:304
@@ -5024,7 +5028,8 @@
 #: ../libpurple/protocols/irc/msgs.c:1113
 #, c-format
 msgid "Cannot join %s: Registration is required."
-msgstr "నమోదు అవసరం"
+msgstr ""
+#msgstr "నమోదు అవసరం"
 
 #: ../libpurple/protocols/irc/msgs.c:1114
 #: ../libpurple/protocols/silc/ops.c:1093
@@ -5234,7 +5239,8 @@
 #: ../libpurple/protocols/jabber/auth.c:518
 #, c-format
 msgid "%s requires plaintext authentication over an unencrypted connection.  Allow this and continue authentication?"
-msgstr "ఈ సర్వర్‌కు ఎన్ క్రిప్ట్ కాని కనెక్షన్ కన్నా సాధారణ టెక్స్ట్ ప్రమాణీకరణ అవసరం. దీనికి అనుమతించి ప్రమాణీకరణను కొనసాగించమంటారా?"
+msgstr ""
+#msgstr "ఈ సర్వర్‌కు ఎన్ క్రిప్ట్ కాని కనెక్షన్ కన్నా సాధారణ టెక్స్ట్ ప్రమాణీకరణ అవసరం. దీనికి అనుమతించి ప్రమాణీకరణను కొనసాగించమంటారా?"
 
 #: ../libpurple/protocols/jabber/auth.c:325
 #: ../libpurple/protocols/jabber/auth.c:326
@@ -6007,7 +6013,8 @@
 #: ../libpurple/protocols/jabber/jabber.c:689
 #, c-format
 msgid "Registration to %s successful"
-msgstr "%s@%s నమోదు విజయవంతమైనది"
+msgstr ""
+#msgstr "%s@%s నమోదు విజయవంతమైనది"
 
 #: ../libpurple/protocols/jabber/jabber.c:691
 #: ../libpurple/protocols/jabber/jabber.c:692
@@ -6022,7 +6029,8 @@
 #: ../libpurple/protocols/jabber/jabber.c:719
 #, c-format
 msgid "Registration from %s successfully removed"
-msgstr "%s@%s నమోదు విజయవంతమైనది"
+msgstr ""
+#msgstr "%s@%s నమోదు విజయవంతమైనది"
 
 #: ../libpurple/protocols/jabber/jabber.c:721
 #: ../libpurple/protocols/jabber/jabber.c:722
@@ -6101,7 +6109,8 @@
 #: ../libpurple/protocols/jabber/jabber.c:1040
 #, c-format
 msgid "Register New Account at %s"
-msgstr "కొత్త జాబర్ అకౌంట్‌ను నమోదు చేయండి"
+msgstr ""
+#msgstr "కొత్త జాబర్ అకౌంట్‌ను నమోదు చేయండి"
 
 #: ../libpurple/protocols/jabber/jabber.c:1043
 msgid "Change Registration"
@@ -6582,7 +6591,8 @@
 #: ../libpurple/protocols/yahoo/yahoo.c:4131
 #, c-format
 msgid "%s has buzzed you!"
-msgstr "%s మీకు [%s] ను చేర్చారు"
+msgstr ""
+#msgstr "%s మీకు [%s] ను చేర్చారు"
 
 #: ../libpurple/protocols/jabber/jabber.c:2281
 #: ../libpurple/protocols/yahoo/yahoo.c:4132
@@ -7185,7 +7195,8 @@
 #: ../libpurple/protocols/msnp9/msn.c:131
 #, c-format
 msgid "%s has nudged you!"
-msgstr "%s మీకు [%s] ను చేర్చారు"
+msgstr ""
+#msgstr "%s మీకు [%s] ను చేర్చారు"
 
 #: ../libpurple/protocols/msn/msn.c:132
 #: ../libpurple/protocols/msnp9/msn.c:132
@@ -7767,7 +7778,8 @@
 #: ../libpurple/protocols/msn/notification.c:836
 #, c-format
 msgid "Unknown error (%d)"
-msgstr "అజ్ఞాత పొరపాటు"
+msgstr ""
+#msgstr "అజ్ఞాత పొరపాటు"
 
 #: ../libpurple/protocols/msn/notification.c:837
 #: ../libpurple/protocols/sametime/sametime.c:4471
@@ -8151,7 +8163,8 @@
 #: ../libpurple/protocols/myspace/myspace.c:1794
 #, c-format
 msgid "Protocol error, code %d: %s"
-msgstr "లోపం కోడ్ %d ను ప్రక్రియ తిప్పి పంపింది "
+msgstr ""
+#msgstr "లోపం కోడ్ %d ను ప్రక్రియ తిప్పి పంపింది "
 
 #: ../libpurple/protocols/myspace/myspace.c:1990
 #: ../libpurple/protocols/myspace/myspace.c:2024
@@ -8211,7 +8224,8 @@
 #: ../libpurple/protocols/myspace/myspace.c:2499
 #, c-format
 msgid "Couldn't connect to host: %s (%d)"
-msgstr "హోస్ట్‌కు కనెక్ట్ చేయలేదు"
+msgstr ""
+#msgstr "హోస్ట్‌కు కనెక్ట్ చేయలేదు"
 
 #: ../libpurple/protocols/myspace/myspace.c:2670
 #, fuzzy
@@ -9503,8 +9517,10 @@
 #, c-format
 msgid "You missed %hu message from %s for an unknown reason."
 msgid_plural "You missed %hu messages from %s for an unknown reason."
-msgstr[0] "అజ్ఞాత కారణములచేత మీరు %hu సందేశాలను %s నుండి  కోల్పోయినారు. "
-msgstr[1] "आपको अज्ञात कारनों से साइन-ऑफ करा गया है।"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "అజ్ఞాత కారణములచేత మీరు %hu సందేశాలను %s నుండి  కోల్పోయినారు. "
+#msgstr[1] "आपको अज्ञात कारनों से साइन-ऑफ करा गया है।"
 
 # Data is assumed to be the destination sn
 #. Data is assumed to be the destination sn
@@ -9757,7 +9773,8 @@
 #: ../libpurple/protocols/oscar/oscar.c:5109
 #, c-format
 msgid "Could not add the buddy %s for an unknown reason."
-msgstr "అజ్ఞాత కారణంవల్ల మీ కమాండ్ విఫలమైనది."
+msgstr ""
+#msgstr "అజ్ఞాత కారణంవల్ల మీ కమాండ్ విఫలమైనది."
 
 #: ../libpurple/protocols/oscar/oscar.c:5226
 #, c-format
@@ -10169,7 +10186,8 @@
 #: ../libpurple/protocols/qq/buddy_info.c:601
 #, c-format
 msgid "Setting custom faces is not currently supported. Please choose an image from %s."
-msgstr "మీకిష్టమైన ముఖారవిందాలను సెట్ చేయడానికి ఇప్పుడు అవకాశంలేదు. ఇక్కడున్న బొమ్మల్లో  ఒకదాన్ని దయచేసి ఎంపికచేసుకోండి"
+msgstr ""
+#msgstr "మీకిష్టమైన ముఖారవిందాలను సెట్ చేయడానికి ఇప్పుడు అవకాశంలేదు. ఇక్కడున్న బొమ్మల్లో  ఒకదాన్ని దయచేసి ఎంపికచేసుకోండి"
 
 #: ../libpurple/protocols/qq/buddy_info.c:618
 #: ../libpurple/protocols/qq/buddy_info.c:631
@@ -10598,7 +10616,7 @@
 #: ../libpurple/protocols/qq/send_file.c:707
 #, c-format
 msgid "%d has declined the file %s"
-msgstr "ఫైలు %s ను %d నిరాకరించింది "
+msgstr "ఫైలు %2$s ను %1$d నిరాకరించింది "
 
 #: ../libpurple/protocols/qq/send_file.c:710
 #: ../libpurple/protocols/qq/send_file.c:739
@@ -10608,7 +10626,8 @@
 #: ../libpurple/protocols/qq/send_file.c:736
 #, c-format
 msgid "%d canceled the transfer of %s"
-msgstr "%sను ట్రాన్స్‌ఫర్ చేయడాన్ని %s రద్దు చేశారు."
+msgstr ""
+#msgstr "%sను ట్రాన్స్‌ఫర్ చేయడాన్ని %s రద్దు చేశారు."
 
 #: ../libpurple/protocols/qq/sendqueue.c:124
 msgid "Connection lost"
@@ -10637,7 +10656,8 @@
 #: ../libpurple/protocols/qq/sys_msg.c:176
 #, c-format
 msgid "%s has added you [%s] to his or her buddy list"
-msgstr "%s మిమ్మల్ని తన మిత్రుల జాబితాలో చేర్చారు."
+msgstr ""
+#msgstr "%s మిమ్మల్ని తన మిత్రుల జాబితాలో చేర్చారు."
 
 #: ../libpurple/protocols/qq/sys_msg.c:192
 #, c-format
@@ -13318,8 +13338,9 @@
 "Lost connection with %s:\n"
 "%s"
 msgstr ""
-"సర్వర్ తో కనెక్షన్ పోయింది:\n"
-"%s"
+#msgstr ""
+#"సర్వర్ తో కనెక్షన్ పోయింది:\n"
+#"%s"
 
 #: ../libpurple/protocols/yahoo/yahoo.c:2733
 #, c-format
@@ -13327,8 +13348,9 @@
 "Could not establish a connection with %s:\n"
 "%s"
 msgstr ""
-"సర్వర్ తో కనెక్షన్ సాధ్యపడలేదు: \n"
-"%s"
+#msgstr ""
+#"సర్వర్ తో కనెక్షన్ సాధ్యపడలేదు: \n"
+#"%s"
 
 #: ../libpurple/protocols/yahoo/yahoo.c:3092
 #: ../libpurple/protocols/yahoo/yahoo.c:3778
@@ -13836,7 +13858,8 @@
 msgid ""
 "Unable to create socket:\n"
 "%s"
-msgstr "సాకెట్‌ను సృష్టించలేదు. "
+msgstr ""
+#msgstr "సాకెట్‌ను సృష్టించలేదు. "
 
 #: ../libpurple/proxy.c:662
 #, c-format
@@ -14012,7 +14035,8 @@
 #: ../libpurple/status.c:613
 #, c-format
 msgid "%s (%s) changed status from %s to %s"
-msgstr "%s స్థితిని %s నుంచి %s కు మార్చబడింది"
+msgstr ""
+#msgstr "%s స్థితిని %s నుంచి %s కు మార్చబడింది"
 
 #: ../libpurple/status.c:624
 #, c-format
@@ -14022,17 +14046,20 @@
 #: ../libpurple/status.c:626
 #, c-format
 msgid "%s (%s) is now %s"
-msgstr "%s ఇప్పుడు %s"
+msgstr ""
+#msgstr "%s ఇప్పుడు %s"
 
 #: ../libpurple/status.c:632
 #, c-format
 msgid "%s is no longer %s"
-msgstr "ఇప్పుడు %s  దూరంలో లేరు/దు."
+msgstr ""
+#msgstr "ఇప్పుడు %s  దూరంలో లేరు/దు."
 
 #: ../libpurple/status.c:634
 #, c-format
 msgid "%s (%s) is no longer %s"
-msgstr "ఇప్పుడు %s  దూరంలో లేరు/దు."
+msgstr ""
+#msgstr "ఇప్పుడు %s  దూరంలో లేరు/దు."
 
 #: ../libpurple/status.c:1247
 #, c-format
@@ -14131,7 +14158,8 @@
 #: ../libpurple/util.c:3943
 #, c-format
 msgid "Unable to connect to %s"
-msgstr ",ర్వర్‌కు కనెక్ట్‌చేయలేని స్థితి. "
+msgstr ""
+#msgstr ",ర్వర్‌కు కనెక్ట్‌చేయలేని స్థితి. "
 
 #: ../libpurple/util.c:3770
 #, c-format
@@ -14151,7 +14179,8 @@
 #: ../libpurple/util.c:3861
 #, c-format
 msgid "Unable to connect to %s: %s"
-msgstr "సర్వర్‌కు కనెక్ట్‌చేయలేని స్థితి. "
+msgstr ""
+#msgstr "సర్వర్‌కు కనెక్ట్‌చేయలేని స్థితి. "
 
 #: ../pidgin.desktop.in.h:1
 msgid "Internet Messenger"
@@ -14345,9 +14374,10 @@
 "\n"
 "You can come back to this window to add, edit, or remove accounts from <b>Accounts->Add/Edit</b> in the Buddy List window"
 msgstr ""
-"<స్పాన్ పరిమాణం='పెద్ద' బరువు='బోల్డ్'>స్వాగతం %s!</స్పాన్>\n"
-"\n"
-"మీ IM అకౌంట్లు కన్ఫిగర్ కాలేదు. %s తో కనెక్ట్ కావడం ప్రారంభించడానికి  <b>Add</b> ప్రెస్ చేయండి"
+#msgstr ""
+#"<స్పాన్ పరిమాణం='పెద్ద' బరువు='బోల్డ్'>స్వాగతం %s!</స్పాన్>\n"
+#"\n"
+#"మీ IM అకౌంట్లు కన్ఫిగర్ కాలేదు. %s తో కనెక్ట్ కావడం ప్రారంభించడానికి  <b>Add</b> ప్రెస్ చేయండి"
 
 #: ../pidgin/gtkblist.c:543
 #, c-format
@@ -14679,7 +14709,8 @@
 #: ../pidgin/gtkblist.c:3693
 #, c-format
 msgid "Idle %dd %dh %02dm"
-msgstr "ఖాళీ %dh %02dm"
+msgstr ""
+#msgstr "ఖాళీ %dh %02dm"
 
 #: ../pidgin/gtkblist.c:3695
 #, c-format
@@ -14754,7 +14785,8 @@
 #: ../pidgin/gtkblist.c:4494
 #, c-format
 msgid "%s disabled"
-msgstr "కమాండ్ పని చేయడం లేదు"
+msgstr ""
+#msgstr "కమాండ్ పని చేయడం లేదు"
 
 #: ../pidgin/gtkblist.c:4498
 msgid "Reconnect"
@@ -14778,8 +14810,10 @@
 #, c-format
 msgid "%d account was disabled because you signed on from another location."
 msgid_plural "%d accounts were disabled because you signed on from another location."
-msgstr[0] "మీరు మరో ప్రదేశంనుండి సైన్ ఆన్ చేశారు."
-msgstr[1] "మీరు మరో ప్రదేశంనుండి సైన్ ఆన్ చేశారు."
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "మీరు మరో ప్రదేశంనుండి సైన్ ఆన్ చేశారు."
+#msgstr[1] "మీరు మరో ప్రదేశంనుండి సైన్ ఆన్ చేశారు."
 
 #: ../pidgin/gtkblist.c:4860
 msgid "<b>Username:</b>"
@@ -15277,8 +15311,10 @@
 #, c-format
 msgid "%d person in room"
 msgid_plural "%d people in room"
-msgstr[0] "గదిలో %d మనుషులున్నారు "
-msgstr[1] "कक्ष में 0 लोग हैं"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "గదిలో %d మనుషులున్నారు "
+#msgstr[1] "कक्ष में 0 लोग हैं"
 
 #: ../pidgin/gtkconv.c:6574
 #: ../pidgin/gtkstatusbox.c:660
@@ -16545,7 +16581,7 @@
 #: ../pidgin/gtkmain.c:398
 #, c-format
 msgid "%s %s. Try `%s -h' for more information.\n"
-msgstr "Gaim %s. ఇంకా వివరములకు `%s -h' ప్రయత్నించండి.\n"
+msgstr "%s %s. ఇంకా వివరములకు `%s -h' ప్రయత్నించండి.\n"
 
 #: ../pidgin/gtkmain.c:400
 #, c-format
@@ -16595,23 +16631,6 @@
 "on other protocols is at\n"
 "%swiki/DeveloperPages\n"
 msgstr ""
-"%s has segfaulted and attempted to dump a core file.\n"
-"This is a bug in the software and has happened through\n"
-"no fault of your own.\n"
-"\n"
-"If you can reproduce the crash, please notify the Pidgin\n"
-"developers by reporting a bug at\n"
-"%sbug.php\n"
-"\n"
-"Please make sure to specify what you were doing at the time\n"
-"and post the backtrace from the core file.  If you do not know\n"
-"how to get the backtrace, please read the instructions at\n"
-"%sgdb.php\n"
-"\n"
-"If you need further assistance, please IM either SeanEgn or \n"
-"LSchiere (via AIM).  Contact information for Sean and Luke \n"
-"on other protocols is at\n"
-"%scontactinfo.php\n"
 
 #. Translators may want to transliterate the name.
 #. It is not to be translated.
@@ -16644,8 +16663,10 @@
 #, c-format
 msgid "<b>%d new email.</b>"
 msgid_plural "<b>%d new emails.</b>"
-msgstr[0] "<b>ప్లగ్ ఇన్ వివరాలు</b>"
-msgstr[1] "<b>ప్లగ్ ఇన్ వివరాలు</b>"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "<b>ప్లగ్ ఇన్ వివరాలు</b>"
+#msgstr[1] "<b>ప్లగ్ ఇన్ వివరాలు</b>"
 
 #: ../pidgin/gtknotify.c:998
 #, c-format
@@ -18136,8 +18157,8 @@
 "\n"
 "<b>Buddy Note</b>: %s"
 msgstr ""
-"\n"
-"<b>సోమరి:</b>"
+#"\n"
+#"<b>సోమరి:</b>"
 
 #: ../pidgin/plugins/history.c:195
 msgid "History"
@@ -18560,7 +18581,8 @@
 #: ../pidgin/plugins/relnot.c:71
 #, c-format
 msgid "You are using %s version %s.  The current version is %s.  You can get it from <a href=\"%s\">%s</a><hr>"
-msgstr "మీరు %s వెర్షన్‌ అనువాదాన్ని ఉపయోగిస్తున్నారు. ప్రస్తుత వెర్షన్ %s.<hr>"
+msgstr ""
+#msgstr "మీరు %s వెర్షన్‌ అనువాదాన్ని ఉపయోగిస్తున్నారు. ప్రస్తుత వెర్షన్ %s.<hr>"
 
 #: ../pidgin/plugins/relnot.c:79
 #, c-format
--- a/po/th.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/th.po	Thu Jun 26 20:25:38 2008 +0000
@@ -16,7 +16,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
 
 #: ../finch/finch.c:64 ../finch/finch.c:301 ../finch/finch.c:330
 #: ../finch/finch.c:418
@@ -15016,8 +15016,9 @@
 "\n"
 "%s"
 msgstr ""
-"เกิดข้อผิดพลาดขณะบันทึกรูป: %s\n"
-"%s"
+#msgstr ""
+#"เกิดข้อผิดพลาดขณะบันทึกรูป: %s\n"
+#"%s"
 
 #: ../pidgin/gtkimhtml.c:3482 ../pidgin/gtkimhtml.c:3494
 msgid "Save Image"
--- a/po/ur.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/ur.po	Thu Jun 26 20:25:38 2008 +0000
@@ -13,6 +13,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Translate Toolkit 0.10\n"
 "X-Poedit-Language: Urdu\n"
 "X-Poedit-SourceCharset: utf.8\n"
@@ -1398,8 +1399,10 @@
 #, c-format
 msgid "%s (%s) has %d new message."
 msgid_plural "%s (%s) has %d new messages."
-msgstr[0] "%s کے پاس %dنیا پیام ہے۔"
-msgstr[1] "%sک ےپاس    %dنئے پیامات ہیں۔"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "%s کے پاس %dنیا پیام ہے۔"
+#msgstr[1] "%sک ےپاس    %dنئے پیامات ہیں۔"
 
 #: ../finch/gntnotify.c:226
 #: ../pidgin/gtknotify.c:342
@@ -2466,7 +2469,8 @@
 #: ../libpurple/certificate.c:1185
 #, c-format
 msgid "Accept certificate for %s?"
-msgstr "دعوت گفتگو قبول ؟"
+msgstr ""
+#msgstr "دعوت گفتگو قبول ؟"
 
 #. TODO: Find what the handle ought to be
 #: ../libpurple/certificate.c:1191
@@ -3083,7 +3087,8 @@
 #: ../libpurple/plugin.c:663
 #, c-format
 msgid "%s requires %s, but it failed to unload."
-msgstr "انحصاری پلگ ان%sان لوڈہونے میں ناكام ہوا۔"
+msgstr ""
+#msgstr "انحصاری پلگ ان%sان لوڈہونے میں ناكام ہوا۔"
 
 #: ../libpurple/plugins/autoaccept.c:23
 #, fuzzy
@@ -3099,7 +3104,8 @@
 #: ../libpurple/plugins/autoaccept.c:80
 #, c-format
 msgid "Autoaccepted file transfer of \"%s\" from \"%s\" completed."
-msgstr "%s سے ٹرانسفردرخواست فائل قبول؟"
+msgstr ""
+#msgstr "%s سے ٹرانسفردرخواست فائل قبول؟"
 
 #: ../libpurple/plugins/autoaccept.c:82
 msgid "Autoaccept complete"
@@ -3885,7 +3891,7 @@
 msgstr "ایكٹیو TCL ا نسٹالیشن كی  جانچ  نا ممكن۔ اگر آپ TCL پلگ انس  استعمال كرنا چاہتے ہیں ، ایكٹیوTCL انسٹال كروfrom http://www.activestate.com\n"
 
 #: ../libpurple/protocols/bonjour/bonjour.c:108
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
 msgstr ""
 
 #: ../libpurple/protocols/bonjour/bonjour.c:127
@@ -4025,7 +4031,8 @@
 #: ../libpurple/protocols/gg/gg.c:278
 #, c-format
 msgid "Couldn't write buddy list for %s to %s"
-msgstr "بڈّی لسٹ لوڈ نہیں كرسكا"
+msgstr ""
+#msgstr "بڈّی لسٹ لوڈ نہیں كرسكا"
 
 #: ../libpurple/protocols/gg/gg.c:303
 #: ../libpurple/protocols/gg/gg.c:304
@@ -4821,7 +4828,8 @@
 #: ../libpurple/protocols/irc/msgs.c:1113
 #, c-format
 msgid "Cannot join %s: Registration is required."
-msgstr "رجسٹریشن کی ضروت تھی"
+msgstr ""
+#msgstr "رجسٹریشن کی ضروت تھی"
 
 #: ../libpurple/protocols/irc/msgs.c:1114
 #: ../libpurple/protocols/silc/ops.c:1093
@@ -5028,7 +5036,8 @@
 #: ../libpurple/protocols/jabber/auth.c:518
 #, c-format
 msgid "%s requires plaintext authentication over an unencrypted connection.  Allow this and continue authentication?"
-msgstr "اس سرور کو ان اینکریپٹیڈ کنیکشن پر سادہ متن تصدیق کی ضرورت ہے ۔ اس کو اجازت  ہے اور تصدیق جاری رکھنا ہے؟"
+msgstr ""
+#msgstr "اس سرور کو ان اینکریپٹیڈ کنیکشن پر سادہ متن تصدیق کی ضرورت ہے ۔ اس کو اجازت  ہے اور تصدیق جاری رکھنا ہے؟"
 
 #: ../libpurple/protocols/jabber/auth.c:325
 #: ../libpurple/protocols/jabber/auth.c:326
@@ -5789,7 +5798,7 @@
 #: ../libpurple/protocols/jabber/jabber.c:689
 #, c-format
 msgid "Registration to %s successful"
-msgstr "کا رجسٹریشن %s@%s کامیاب"
+msgstr "کا رجسٹریشن %s کامیاب"
 
 #: ../libpurple/protocols/jabber/jabber.c:691
 #: ../libpurple/protocols/jabber/jabber.c:692
@@ -5804,7 +5813,7 @@
 #: ../libpurple/protocols/jabber/jabber.c:719
 #, c-format
 msgid "Registration from %s successfully removed"
-msgstr "کا رجسٹریشن %s@%s کامیاب"
+msgstr "کا رجسٹریشن %s کامیاب"
 
 #: ../libpurple/protocols/jabber/jabber.c:721
 #: ../libpurple/protocols/jabber/jabber.c:722
@@ -5880,7 +5889,8 @@
 #: ../libpurple/protocols/jabber/jabber.c:1040
 #, c-format
 msgid "Register New Account at %s"
-msgstr "نیا مہمل اکاؤنٹ رجسٹر کریں"
+msgstr ""
+#msgstr "نیا مہمل اکاؤنٹ رجسٹر کریں"
 
 #: ../libpurple/protocols/jabber/jabber.c:1043
 msgid "Change Registration"
@@ -6342,7 +6352,8 @@
 #: ../libpurple/protocols/yahoo/yahoo.c:4131
 #, c-format
 msgid "%s has buzzed you!"
-msgstr "%s has added you [%s]"
+msgstr ""
+#msgstr "%s has added you [%s]"
 
 #: ../libpurple/protocols/jabber/jabber.c:2281
 #: ../libpurple/protocols/yahoo/yahoo.c:4132
@@ -6935,7 +6946,8 @@
 #: ../libpurple/protocols/msnp9/msn.c:131
 #, c-format
 msgid "%s has nudged you!"
-msgstr "%s has added you [%s]"
+msgstr ""
+#msgstr "%s has added you [%s]"
 
 #: ../libpurple/protocols/msn/msn.c:132
 #: ../libpurple/protocols/msnp9/msn.c:132
@@ -7495,7 +7507,8 @@
 #: ../libpurple/protocols/msn/notification.c:836
 #, c-format
 msgid "Unknown error (%d)"
-msgstr "نامعلوم خامی "
+msgstr ""
+#msgstr "نامعلوم خامی "
 
 #: ../libpurple/protocols/msn/notification.c:837
 #: ../libpurple/protocols/sametime/sametime.c:4471
@@ -7864,7 +7877,8 @@
 #: ../libpurple/protocols/myspace/myspace.c:1794
 #, c-format
 msgid "Protocol error, code %d: %s"
-msgstr "پروسیس لوٹایا گيا خامی کوڈ %d"
+msgstr ""
+#msgstr "پروسیس لوٹایا گيا خامی کوڈ %d"
 
 #: ../libpurple/protocols/myspace/myspace.c:1990
 #: ../libpurple/protocols/myspace/myspace.c:2024
@@ -7923,7 +7937,8 @@
 #: ../libpurple/protocols/myspace/myspace.c:2499
 #, c-format
 msgid "Couldn't connect to host: %s (%d)"
-msgstr "ہاسٹ سے کنیکٹ نہیں ہو سکا"
+msgstr ""
+#msgstr "ہاسٹ سے کنیکٹ نہیں ہو سکا"
 
 #: ../libpurple/protocols/myspace/myspace.c:2670
 #, fuzzy
@@ -8728,7 +8743,8 @@
 #: ../libpurple/protocols/oscar/oscar.c:457
 #, c-format
 msgid "(There was an error receiving this message.  Either you and %s have different encodings selected, or %s has a buggy client.)"
-msgstr "(پیام موصول ہونے میں خامی تھی۔  جس بڈی سے آپ بات کررہے تھے بہت حد تک قیاس ہے کہ کلائنٹ بگی تھا۔)"
+msgstr ""
+#msgstr "(پیام موصول ہونے میں خامی تھی۔  جس بڈی سے آپ بات کررہے تھے بہت حد تک قیاس ہے کہ کلائنٹ بگی تھا۔)"
 
 #. Label
 #: ../libpurple/protocols/oscar/oscar.c:639
@@ -9123,22 +9139,24 @@
 #, c-format
 msgid "You missed %hu message from %s because it was invalid."
 msgid_plural "You missed %hu messages from %s because they were invalid."
-msgstr[0] "آپ نے %huپیام"
-msgstr[1] "آپ نے "
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "آپ نے %huپیام"
+#msgstr[1] "آپ نے "
 
 #: ../libpurple/protocols/oscar/oscar.c:2615
 #, c-format
 msgid "You missed %hu message from %s because it was too large."
 msgid_plural "You missed %hu messages from %s because they were too large."
-msgstr[0] "آپ نے %sسے %hu پیام کو مس کردیا کیونکہ یہ بہت بڑا تھا۔"
-msgstr[1] "آپ نے %sسے %hu پیامات کو مس کردیا کیونکہ یہ بہت بڑےتھے۔"
+msgstr[0] "آپ نے %2$sسے %1$hu پیام کو مس کردیا کیونکہ یہ بہت بڑا تھا۔"
+msgstr[1] "آپ نے %2$sسے %1$hu پیامات کو مس کردیا کیونکہ یہ بہت بڑےتھے۔"
 
 #: ../libpurple/protocols/oscar/oscar.c:2624
 #, c-format
 msgid "You missed %hu message from %s because the rate limit has been exceeded."
 msgid_plural "You missed %hu messages from %s because the rate limit has been exceeded."
-msgstr[0] "آپ نے %sسے  %huپیام مس کردیا کیونکہ قیمت حد گذر گئی۔"
-msgstr[1] "آپ نے%sسے  %hu پیامات مس کردیئے کیونکہ قیمت حد گذر گئی۔"
+msgstr[0] "آپ نے %2$sسے  %1$huپیام مس کردیا کیونکہ قیمت حد گذر گئی۔"
+msgstr[1] "آپ نے%2$sسے  %1$hu پیامات مس کردیئے کیونکہ قیمت حد گذر گئی۔"
 
 #: ../libpurple/protocols/oscar/oscar.c:2633
 #, c-format
@@ -9151,15 +9169,15 @@
 #, c-format
 msgid "You missed %hu message from %s because you are too evil."
 msgid_plural "You missed %hu messages from %s because you are too evil."
-msgstr[0] "آپ نے  %sسے%huپیام مس کردیا کیونکہ آپ  بہت  ناشائستہ ہیں۔"
-msgstr[1] "آپ نے  %sسے%huپیامات مس کردیئے کیونکہ آپ  بہت   ناشائستہ  ہیں ۔"
+msgstr[0] "آپ نے  %2$sسے%1$huپیام مس کردیا کیونکہ آپ  بہت  ناشائستہ ہیں۔"
+msgstr[1] "آپ نے  %2$sسے%1$huپیامات مس کردیئے کیونکہ آپ  بہت   ناشائستہ  ہیں ۔"
 
 #: ../libpurple/protocols/oscar/oscar.c:2651
 #, c-format
 msgid "You missed %hu message from %s for an unknown reason."
 msgid_plural "You missed %hu messages from %s for an unknown reason."
-msgstr[0] " آپ نے نا معلوم وجہ کےلئے %sسے%hu پیام مس کردیا ۔"
-msgstr[1] " آپ نے نا معلوم وجہ کےلئے %sسے%hu پیامات مس کردیئے ۔"
+msgstr[0] " آپ نے نا معلوم وجہ کےلئے %2$sسے%1$hu پیام مس کردیا ۔"
+msgstr[1] " آپ نے نا معلوم وجہ کےلئے %2$sسے%1$hu پیامات مس کردیئے ۔"
 
 # Data is assumed to be the destination sn
 #. Data is assumed to be the destination sn
@@ -9405,7 +9423,8 @@
 #: ../libpurple/protocols/oscar/oscar.c:5109
 #, c-format
 msgid "Could not add the buddy %s for an unknown reason."
-msgstr "آپ کا کمانڈ نا معلوم سبب کے لئے ناکام ہوا۔"
+msgstr ""
+#msgstr "آپ کا کمانڈ نا معلوم سبب کے لئے ناکام ہوا۔"
 
 #: ../libpurple/protocols/oscar/oscar.c:5226
 #, c-format
@@ -9810,7 +9829,8 @@
 #: ../libpurple/protocols/qq/buddy_info.c:601
 #, c-format
 msgid "Setting custom faces is not currently supported. Please choose an image from %s."
-msgstr "آپ كسٹم فیس سیٹ كرنے كی كوشش كررہے ہیں ۔ گائم  جاری طورپر صرف معیاری فیسیس كو اجازت دیتا ہے۔ براہ كرم ایك امیج منتخب كیجیے"
+msgstr ""
+#msgstr "آپ كسٹم فیس سیٹ كرنے كی كوشش كررہے ہیں ۔ گائم  جاری طورپر صرف معیاری فیسیس كو اجازت دیتا ہے۔ براہ كرم ایك امیج منتخب كیجیے"
 
 #: ../libpurple/protocols/qq/buddy_info.c:618
 #: ../libpurple/protocols/qq/buddy_info.c:631
@@ -10271,7 +10291,8 @@
 #: ../libpurple/protocols/qq/sys_msg.c:176
 #, c-format
 msgid "%s has added you [%s] to his or her buddy list"
-msgstr "%sنے آپ كواپنی بڈی لسٹ میں شامل كردیا۔"
+msgstr ""
+#msgstr "%sنے آپ كواپنی بڈی لسٹ میں شامل كردیا۔"
 
 #: ../libpurple/protocols/qq/sys_msg.c:192
 #, c-format
@@ -12897,8 +12918,9 @@
 "Lost connection with %s:\n"
 "%s"
 msgstr ""
-"سرور كے ساتھ كنیكشن گم ہوا:\n"
-"%s"
+#msgstr ""
+#"سرور كے ساتھ كنیكشن گم ہوا:\n"
+#"%s"
 
 #: ../libpurple/protocols/yahoo/yahoo.c:2733
 #, c-format
@@ -12906,8 +12928,9 @@
 "Could not establish a connection with %s:\n"
 "%s"
 msgstr ""
-"سرور كے ساتھ كوئی كنیكشن اسٹابلیش نہیں كیا جاسكتا:\n"
-"%s"
+#msgstr ""
+#"سرور كے ساتھ كوئی كنیكشن اسٹابلیش نہیں كیا جاسكتا:\n"
+#"%s"
 
 #: ../libpurple/protocols/yahoo/yahoo.c:3092
 #: ../libpurple/protocols/yahoo/yahoo.c:3778
@@ -13551,12 +13574,12 @@
 #: ../libpurple/status.c:610
 #, c-format
 msgid "%s changed status from %s to %s"
-msgstr "%s changed status from %s to %s"
+msgstr ""
 
 #: ../libpurple/status.c:613
 #, c-format
 msgid "%s (%s) changed status from %s to %s"
-msgstr "%s changed status from %s to %s"
+msgstr ""
 
 #: ../libpurple/status.c:624
 #, c-format
@@ -13566,7 +13589,8 @@
 #: ../libpurple/status.c:626
 #, c-format
 msgid "%s (%s) is now %s"
-msgstr "%sابھی ہے %s"
+msgstr ""
+#msgstr "%sابھی ہے %s"
 
 #: ../libpurple/status.c:632
 #, c-format
@@ -13576,7 +13600,8 @@
 #: ../libpurple/status.c:634
 #, c-format
 msgid "%s (%s) is no longer %s"
-msgstr "%s اوریادہ نہیں ہےr %s"
+msgstr ""
+#msgstr "%s اوریادہ نہیں ہےr %s"
 
 #: ../libpurple/status.c:1247
 #, c-format
@@ -13673,7 +13698,8 @@
 #: ../libpurple/util.c:3943
 #, c-format
 msgid "Unable to connect to %s"
-msgstr "ہوسٹ سےكنیكٹ ہونے میں نااہل"
+msgstr ""
+#msgstr "ہوسٹ سےكنیكٹ ہونے میں نااہل"
 
 #: ../libpurple/util.c:3770
 #, c-format
@@ -13689,7 +13715,8 @@
 #: ../libpurple/util.c:3861
 #, c-format
 msgid "Unable to connect to %s: %s"
-msgstr "ہوسٹ سےكنیكٹ ہونے میں نااہل"
+msgstr ""
+#msgstr "ہوسٹ سےكنیكٹ ہونے میں نااہل"
 
 #: ../pidgin.desktop.in.h:1
 msgid "Internet Messenger"
@@ -13873,11 +13900,12 @@
 "\n"
 "You can come back to this window to add, edit, or remove accounts from <b>Accounts->Add/Edit</b> in the Buddy List window"
 msgstr ""
-"<span size='larger' weight='bold'> گائم میں خوش آمدید!</span>\n"
-"\n"
-"آپ كوئي ہیئت والاIM اكاؤنٹس نہیں ركھتے ہیں ۔ گائم كے ساتھ   جوڑنا شروع كرنے كے لیے <b>ملاؤ</b> بٹن پر نیچے كلك  كیجیےا ور آپ كا پہلا اكاؤنٹ كو  ہیئت دیجیے۔  اگر آپ گائم كو متعدد IM اكاؤنٹس میں  چاہتے ہیں ، مال كو ہیئت دینے كے لیے دوبارہ <b>ملاؤ</b>  پر كلك كیجیے۔\n"
-"\n"
-"آپ ملانے ، مرتب، یا  اكاؤنٹس نكالنے كے لیے اس ونڈو كو  واپس جاسكتے ہیں ،بڈی فہرست  ونڈو میں <b>اكاؤنٹس->ملاؤ/مرتب</b>"
+#msgstr ""
+#"<span size='larger' weight='bold'> گائم میں خوش آمدید!</span>\n"
+#"\n"
+#"آپ كوئي ہیئت والاIM اكاؤنٹس نہیں ركھتے ہیں ۔ گائم كے ساتھ   جوڑنا شروع كرنے كے لیے <b>ملاؤ</b> بٹن پر نیچے كلك  كیجیےا ور آپ كا پہلا اكاؤنٹ كو  ہیئت دیجیے۔  اگر آپ گائم كو متعدد IM اكاؤنٹس میں  چاہتے ہیں ، مال كو ہیئت دینے كے لیے دوبارہ <b>ملاؤ</b>  پر كلك كیجیے۔\n"
+#"\n"
+#"آپ ملانے ، مرتب، یا  اكاؤنٹس نكالنے كے لیے اس ونڈو كو  واپس جاسكتے ہیں ،بڈی فہرست  ونڈو میں <b>اكاؤنٹس->ملاؤ/مرتب</b>"
 
 #: ../pidgin/gtkblist.c:543
 #, c-format
@@ -14203,7 +14231,7 @@
 #: ../pidgin/gtkblist.c:3693
 #, c-format
 msgid "Idle %dd %dh %02dm"
-msgstr "Idle %dh %02dm"
+msgstr ""
 
 #: ../pidgin/gtkblist.c:3695
 #, c-format
@@ -14278,7 +14306,8 @@
 #: ../pidgin/gtkblist.c:4494
 #, c-format
 msgid "%s disabled"
-msgstr "نااہل كیاگیا"
+msgstr ""
+#msgstr "نااہل كیاگیا"
 
 #: ../pidgin/gtkblist.c:4498
 msgid "Reconnect"
@@ -14300,8 +14329,10 @@
 #, c-format
 msgid "%d account was disabled because you signed on from another location."
 msgid_plural "%d accounts were disabled because you signed on from another location."
-msgstr[0] "آپ نے دوسرے لوکیشن سے سائنڈ آن کیا ہے۔"
-msgstr[1] "آپ نے دوسرے لوکیشن سے سائنڈ آن کیا ہے۔"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "آپ نے دوسرے لوکیشن سے سائنڈ آن کیا ہے۔"
+#msgstr[1] "آپ نے دوسرے لوکیشن سے سائنڈ آن کیا ہے۔"
 
 #: ../pidgin/gtkblist.c:4860
 msgid "<b>Username:</b>"
@@ -14327,9 +14358,10 @@
 "\n"
 "You have no accounts enabled. Enable your IM accounts from the <b>Accounts</b> window at <b>Accounts->Manage</b>. Once you enable accounts, you'll be able to sign on, set your status, and talk to your friends."
 msgstr ""
-"<span weight='bold' size='larger'>گائم كو خوش آمدید!</span>\n"
-"\n"
-" آپ كوئی اكاؤنٹ ممكن نہيں ركھتے ہیں ۔<b>Accounts</b> اكاؤنٹس<b>Accounts->Add/Edit</b>ونڈو سے آپ كا  IM اكاؤنٹس ممكن كرو۔ایك  بار آپ  اكاؤنٹس  ممكن كرتے ہیں ،آپ سائن آن كو قابل كرتے ہیں ،آپ كا اسٹیٹس سیٹ كیجیے ، اورآپ كے فرینڈس سے بات  كیجیے۔"
+#msgstr ""
+#"<span weight='bold' size='larger'>گائم كو خوش آمدید!</span>\n"
+#"\n"
+#" آپ كوئی اكاؤنٹ ممكن نہيں ركھتے ہیں ۔<b>Accounts</b> اكاؤنٹس<b>Accounts->Add/Edit</b>ونڈو سے آپ كا  IM اكاؤنٹس ممكن كرو۔ایك  بار آپ  اكاؤنٹس  ممكن كرتے ہیں ،آپ سائن آن كو قابل كرتے ہیں ،آپ كا اسٹیٹس سیٹ كیجیے ، اورآپ كے فرینڈس سے بات  كیجیے۔"
 
 #. set the Show Offline Buddies option. must be done
 #. * after the treeview or faceprint gets mad. -Robot101
@@ -14756,8 +14788,9 @@
 #, c-format
 msgid "%d person in room"
 msgid_plural "%d people in room"
-msgstr[0] "%d فرد کمرے میں"
+msgstr[0] ""
 msgstr[1] ""
+#msgstr[0] "%d فرد کمرے میں"
 
 #: ../pidgin/gtkconv.c:6574
 #: ../pidgin/gtkstatusbox.c:660
@@ -15310,12 +15343,14 @@
 #: ../pidgin/gtkdialogs.c:368
 #, c-format
 msgid "About %s"
-msgstr "گائم کےبارےمیں"
+msgstr ""
+#msgstr "گائم کےبارےمیں"
 
 #: ../pidgin/gtkdialogs.c:411
 #, c-format
 msgid "%s is a graphical modular messaging client based on libpurple which is capable of connecting to AIM, MSN, Yahoo!, XMPP, ICQ, IRC, SILC, SIP/SIMPLE, Novell GroupWise, Lotus Sametime, Bonjour, Zephyr, MySpaceIM, Gadu-Gadu, and QQ all at once.  It is written using GTK+.<BR><BR>You may modify and redistribute the program under the terms of the GPL (version 2 or later).  A copy of the GPL is contained in the 'COPYING' file distributed with %s.  %s is copyrighted by its contributors.  See the 'COPYRIGHT' file for the complete list of contributors.  We provide no warranty for this program.<BR><BR>"
-msgstr "گائم AIM, MSN, Yahoo!,  كو استعمال كرتے ہوئے ایك ماڈیولر میسیجینگ كلائںٹ  كیبل ہے ۔Jabber, ICQ, IRC, SILC, SIP/SIMPLE, ناول  گروپ وائس، لوٹس مشابہ ٹائم،بونزور، زیفیر،گڈو گڈو، اورQQایك بار میں ۔یہ GTK+.<BR><BR> استعمال كرتے ہوئے لكھا جاتا ہے  آپ ترمیم كرینگے  اور  پروگرام كوGPL شرطوں كے ماتحٹ دوبارہ تقسیم كرینگے (version 2 or later).GPLكی ایك كاپی  'COPYING' فائل میں ركھتی ہے گائم سے تقسیم كی جاتی ہے۔ گائم  اسے شراكت داروں سے   كاپی رائٹیڈ ہے ۔ شراكت داروں كی مكمل فہرست كے لیے   گائم كی 'COPYRIGHT'   فائل دیكھیے۔ ہم اس پروگرام كے لیے كوئي وارنٹی مہیا نہیں كرتے ہیں ۔<BR><BR>"
+msgstr ""
+#msgstr "گائم AIM, MSN, Yahoo!,  كو استعمال كرتے ہوئے ایك ماڈیولر میسیجینگ كلائںٹ  كیبل ہے ۔Jabber, ICQ, IRC, SILC, SIP/SIMPLE, ناول  گروپ وائس، لوٹس مشابہ ٹائم،بونزور، زیفیر،گڈو گڈو، اورQQایك بار میں ۔یہ GTK+.<BR><BR> استعمال كرتے ہوئے لكھا جاتا ہے  آپ ترمیم كرینگے  اور  پروگرام كوGPL شرطوں كے ماتحٹ دوبارہ تقسیم كرینگے (version 2 or later).GPLكی ایك كاپی  'COPYING' فائل میں ركھتی ہے گائم سے تقسیم كی جاتی ہے۔ گائم  اسے شراكت داروں سے   كاپی رائٹیڈ ہے ۔ شراكت داروں كی مكمل فہرست كے لیے   گائم كی 'COPYRIGHT'   فائل دیكھیے۔ ہم اس پروگرام كے لیے كوئي وارنٹی مہیا نہیں كرتے ہیں ۔<BR><BR>"
 
 #: ../pidgin/gtkdialogs.c:429
 #, fuzzy
@@ -15898,17 +15933,18 @@
 #: ../pidgin/gtklog.c:309
 #, c-format
 msgid "Are you sure you want to permanently delete the system log which started at %s?"
-msgstr "كیا آپ واقعی %s for %sپرجھپٹنا خارج كرناچاہتےہیں؟"
+msgstr ""
+#msgstr "كیا آپ واقعی %s for %sپرجھپٹنا خارج كرناچاہتےہیں؟"
 
 #: ../pidgin/gtklog.c:453
 #, c-format
 msgid "<span size='larger' weight='bold'>Conversation in %s on %s</span>"
-msgstr "<span size='larger' weight='bold'>Conversation in %s on %s</span>"
+msgstr ""
 
 #: ../pidgin/gtklog.c:456
 #, c-format
 msgid "<span size='larger' weight='bold'>Conversation with %s on %s</span>"
-msgstr "<span size='larger' weight='bold'>Conversation with %s on %s</span>"
+msgstr ""
 
 #: ../pidgin/gtklog.c:503
 msgid "%B %Y"
@@ -15957,7 +15993,7 @@
 #: ../pidgin/gtkmain.c:398
 #, c-format
 msgid "%s %s. Try `%s -h' for more information.\n"
-msgstr "%s. كوشش `%s -h' مزید معلومات كے لیے.\n"
+msgstr "%s %s. كوشش `%s -h' مزید معلومات كے لیے.\n"
 
 #: ../pidgin/gtkmain.c:400
 #, c-format
@@ -15975,16 +16011,6 @@
 "  --display=DISPLAY   X display to use\n"
 "  -v, --version       display the current version and exit\n"
 msgstr ""
-"Gaim %s\n"
-"Usage: %s [OPTION]...\n"
-"\n"
-"  -c, --config=DIR    use DIR for config files\n"
-"  -d, --debug         print debugging messages to stdout\n"
-"  -h, --help          display this help and exit\n"
-"  -n, --nologin       don't automatically login\n"
-"  -l, --login[=NAME]  automatically login (optional argument NAME specifies\n"
-"                      account(s) to use, separated by commas)\n"
-"  -v, --version       display the current version and exit\n"
 
 #: ../pidgin/gtkmain.c:528
 #, c-format
@@ -16007,23 +16033,24 @@
 "on other protocols is at\n"
 "%swiki/DeveloperPages\n"
 msgstr ""
-"گائم سیگافالٹ ہوگیاہےاوراہم فائل كوپھینكنےكی كوشش كی۔ \n"
-"یہ سافٹ وئرمیں بگ ہے اوراس كے ذریعے ہواہے \n"
-"آپ كی كوئی غلطی نہیں۔\n"
-"\n"
-"اگرآپ دوبارہ كریش پروڈیوس كرسكتے ہیں تو، براہ كرم گائم\n"
-"ڈیولپرز كو بگ كی رپورٹ كریں\n"
-"%sbug.php\n"
-"\n"
-"اس بات كا خیال ركھیں یہ بتانانہ بھولیں كہ آپ اس وقت كیاكررہےتھے\n"
-"اور اہم فائل سےبیك ٹریس پوسٹ كریں۔ اگرآپ كو معلوم نہیں ہے \n"
-"بیك ٹریس كس طرح حاصل كریں تو،براہ كرم ہدایات پڑھیں\n"
-"%sgdb.php\n"
-"\n"
-"اگرآپ كومزیدمددكی ضرورت ہےتو، براہ كرم IM either SeanEgn or \n"
-"LSchiere (via AIM)٫  سین اورلیوك معلومات كےلیےرابطہ كریں  \n"
-"دیگرپروٹوكالزہے\n"
-"%scontactinfo.php\n"
+#msgstr ""
+#"گائم سیگافالٹ ہوگیاہےاوراہم فائل كوپھینكنےكی كوشش كی۔ \n"
+#"یہ سافٹ وئرمیں بگ ہے اوراس كے ذریعے ہواہے \n"
+#"آپ كی كوئی غلطی نہیں۔\n"
+#"\n"
+#"اگرآپ دوبارہ كریش پروڈیوس كرسكتے ہیں تو، براہ كرم گائم\n"
+#"ڈیولپرز كو بگ كی رپورٹ كریں\n"
+#"%sbug.php\n"
+#"\n"
+#"اس بات كا خیال ركھیں یہ بتانانہ بھولیں كہ آپ اس وقت كیاكررہےتھے\n"
+#"اور اہم فائل سےبیك ٹریس پوسٹ كریں۔ اگرآپ كو معلوم نہیں ہے \n"
+#"بیك ٹریس كس طرح حاصل كریں تو،براہ كرم ہدایات پڑھیں\n"
+#"%sgdb.php\n"
+#"\n"
+#"اگرآپ كومزیدمددكی ضرورت ہےتو، براہ كرم IM either SeanEgn or \n"
+#"LSchiere (via AIM)٫  سین اورلیوك معلومات كےلیےرابطہ كریں  \n"
+#"دیگرپروٹوكالزہے\n"
+#"%scontactinfo.php\n"
 
 #. Translators may want to transliterate the name.
 #. It is not to be translated.
@@ -16052,8 +16079,8 @@
 #, c-format
 msgid "<b>%d new email.</b>"
 msgid_plural "<b>%d new emails.</b>"
-msgstr[0] "<b>Plugin Details</b>"
-msgstr[1] "<b>Plugin Details</b>"
+msgstr[0] ""
+msgstr[1] ""
 
 #: ../pidgin/gtknotify.c:998
 #, c-format
@@ -16891,7 +16918,8 @@
 #: ../pidgin/gtkutils.c:1501
 #, c-format
 msgid "%s cannot transfer a folder. You will need to send the files within individually."
-msgstr "گائم فولڈ رٹرانسفر نہیں کر سکا ۔آپ کو انفرادی طور پر فائلیں بھیجنے کی ضرورت ہے"
+msgstr ""
+#msgstr "گائم فولڈ رٹرانسفر نہیں کر سکا ۔آپ کو انفرادی طور پر فائلیں بھیجنے کی ضرورت ہے"
 
 #: ../pidgin/gtkutils.c:1535
 #: ../pidgin/gtkutils.c:1547
@@ -17806,7 +17834,8 @@
 #: ../pidgin/plugins/relnot.c:71
 #, c-format
 msgid "You are using %s version %s.  The current version is %s.  You can get it from <a href=\"%s\">%s</a><hr>"
-msgstr "آپ گائم ورجن %s استعمال کررہے ہیں۔حالیہ ورجن ہے%s.<hr>"
+msgstr ""
+#msgstr "آپ گائم ورجن %s استعمال کررہے ہیں۔حالیہ ورجن ہے%s.<hr>"
 
 #: ../pidgin/plugins/relnot.c:79
 #, c-format
@@ -18091,7 +18120,8 @@
 #: ../pidgin/plugins/win32/winprefs/winprefs.c:312
 #, c-format
 msgid "_Start %s on Windows startup"
-msgstr "_ ونڈوز اسٹارٹ اپ پر گائم شروع کریں"
+msgstr ""
+#msgstr "_ ونڈوز اسٹارٹ اپ پر گائم شروع کریں"
 
 #: ../pidgin/plugins/win32/winprefs/winprefs.c:327
 msgid "_Dockable Buddy List"
--- a/po/vi.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/vi.po	Thu Jun 26 20:25:38 2008 +0000
@@ -3916,7 +3916,7 @@
 "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://"
 "developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-"
 "LocalMessaging for more information."
-msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging » để tìm chi tiết."
+msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://d.pidgin.im/BonjourWindows » để tìm chi tiết."
 
 #: ../libpurple/protocols/bonjour/bonjour.c:120
 msgid "Unable to listen for incoming IM connections\n"
@@ -5860,7 +5860,8 @@
 #: ../libpurple/protocols/jabber/jabber.c:710
 #, c-format
 msgid "Registration to %s successful"
-msgstr "Đăng ký thành công với %s@%"
+msgstr ""
+#msgstr "Đăng ký thành công với %s@%"
 
 #: ../libpurple/protocols/jabber/jabber.c:712
 #: ../libpurple/protocols/jabber/jabber.c:713
@@ -11668,7 +11669,7 @@
 #: ../libpurple/protocols/silc10/ops.c:712
 #, c-format
 msgid "You have been kicked off <I>%s</I> by <I>%s</I> (%s)"
-msgstr "Bạn đã bị <I>%2$s</I> đã khỏi <I>%1$s</I> (%s)"
+msgstr "Bạn đã bị <I>%2$s</I> đã khỏi <I>%1$s</I> (%3$s)"
 
 #: ../libpurple/protocols/silc/ops.c:718
 #: ../libpurple/protocols/silc/ops.c:723
--- a/po/zh_CN.po	Thu Jun 26 19:57:37 2008 +0000
+++ b/po/zh_CN.po	Thu Jun 26 20:25:38 2008 +0000
@@ -13383,7 +13383,7 @@
 msgid "You have %d contact named %s. Would you like to merge them?"
 msgid_plural ""
 "You currently have %d contacts named %s. Would you like to merge them?"
-msgstr[0] "您已经有名为 %s 的 %d 位联系人。您是否想要合并?"
+msgstr[0] "您已经有名为 %2$s 的 %1$d 位联系人。您是否想要合并?"
 
 #: ../pidgin/gtkblist.c:525
 msgid ""
--- a/share/ca-certs/Makefile.am	Thu Jun 26 19:57:37 2008 +0000
+++ b/share/ca-certs/Makefile.am	Thu Jun 26 20:25:38 2008 +0000
@@ -1,5 +1,4 @@
-cacertsdir =	$(datadir)/purple/ca-certs
-cacerts_DATA =	\
+CERTIFICATES = \
 		Equifax_Secure_CA.pem \
 		GTE_CyberTrust_Global_Root.pem \
 		Microsoft_Secure_Server_Authority.pem \
@@ -7,7 +6,12 @@
 		Verisign_RSA_Secure_Server_CA.pem \
 		Verisign_Class3_Primary_CA.pem
 
+if INSTALL_SSL_CERTIFICATES
+cacertsdir =	$(datadir)/purple/ca-certs
+cacerts_DATA =	$(CERTIFICATES)
+endif
+
 EXTRA_DIST =	\
 		Makefile.mingw \
-		$(cacerts_DATA)
+		$(CERTIFICATES)
 
--- a/share/ca-certs/Makefile.mingw	Thu Jun 26 19:57:37 2008 +0000
+++ b/share/ca-certs/Makefile.mingw	Thu Jun 26 20:25:38 2008 +0000
@@ -8,14 +8,17 @@
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
 datadir := $(PIDGIN_INSTALL_DIR)
-include ./Makefile.am
+-include ./Makefile.am.mingw
 cacertsdir := $(PIDGIN_INSTALL_DIR)/ca-certs
 
 .PHONY: install
 
-install:
+install: ./Makefile.am.mingw
 	if test '$(cacerts_DATA)'; then \
 	  mkdir -p $(cacertsdir); \
 	  cp $(cacerts_DATA) $(cacertsdir); \
 	fi;
 
+./Makefile.am.mingw: ./Makefile.am
+	sed -e 's/^if\ INSTALL_SSL_CERTIFICATES/ifeq (\$$(INSTALL_SSL_CERTIFICATES), 1)/' ./Makefile.am > $@
+