changeset 32692:0f94ec89f0bc

merged from im.pidgin.pidgin
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 26 Sep 2011 14:57:21 +0900
parents 55e678325bda (current diff) 7fcf55606339 (diff)
children 4a34689eeb33
files configure.ac libpurple/buddyicon.c libpurple/certificate.c libpurple/conversation.c libpurple/conversation.h libpurple/plugins/perl/common/Util.xs libpurple/protocols/irc/irc.c libpurple/protocols/irc/parse.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/message.c libpurple/protocols/jabber/si.c libpurple/protocols/msn/msg.c libpurple/protocols/msn/msn.c libpurple/protocols/oscar/oft.c libpurple/protocols/oscar/oscar.c libpurple/protocols/yahoo/libyahoo.c libpurple/protocols/yahoo/libyahoojp.c libpurple/protocols/yahoo/libymsg.c libpurple/protocols/yahoo/libymsg.h libpurple/protocols/yahoo/util.c libpurple/protocols/yahoo/yahoo_filexfer.c libpurple/protocols/yahoo/yahoo_profile.c libpurple/protocols/yahoo/yahoochat.c libpurple/server.c libpurple/status.c libpurple/util.c libpurple/util.h libpurple/xmlnode.c pidgin/gtkblist.c pidgin/gtkconv.c pidgin/gtkdocklet-gtk.c pidgin/gtkimhtml.c pidgin/gtkimhtmltoolbar.h pidgin/gtknotify.c pidgin/gtkprefs.c pidgin/gtkutils.c pidgin/gtkutils.h
diffstat 238 files changed, 7486 insertions(+), 4320 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Aug 29 12:59:57 2011 +0900
+++ b/ChangeLog	Mon Sep 26 14:57:21 2011 +0900
@@ -14,6 +14,11 @@
 	* The buddy's name was not centered vertically in the buddy-list if they
 	  did not have a status-message or mood set.
 
+	XMPP:
+	* Strip element prefixes from XHTML-IM messages as they're presented
+	  to the core (and UIs) as incoming messages (Thijs Alkemade).
+	  (#14529)
+
 version 2.10.0 (08/18/2011):
 	Pidgin:
 	* Make the max size of incoming smileys a pref instead of hardcoding it.
--- a/ChangeLog.API	Mon Aug 29 12:59:57 2011 +0900
+++ b/ChangeLog.API	Mon Sep 26 14:57:21 2011 +0900
@@ -3,11 +3,24 @@
 version 3.0.0 (??/??/????):
 	libpurple:
 		Added:
+		* pidgin_create_webview
+		* purple_conversation_get_ui_data
+		* purple_conversation_set_ui_data
+		* purple_notify_searchresult_column_is_visible
 		* purple_notify_searchresult_column_set_visible
-		* purple_notify_searchresult_column_is_visible
 		* purple_notify_user_info_prepend_pair_plaintext
+		* purple_menu_action_get_callback
+		* purple_menu_action_get_children
+		* purple_menu_action_get_data
+		* purple_menu_action_set_label
+		* purple_menu_action_set_data
+		* purple_menu_action_set_callback
+		* purple_menu_action_set_children
+		* purple_request_field_get_tooltip
+		* purple_request_field_group_get_fields_list
 		* purple_request_field_set_tooltip
-		* purple_request_field_get_tooltip
+		* purple_request_fields_get_ui_data
+		* purple_request_fields_set_ui_data
 		* purple_roomlist_get_account
 		* purple_roomlist_get_proto_data
 		* purple_roomlist_get_ui_data
@@ -15,24 +28,56 @@
 		* purple_roomlist_room_set_expanded_once
 		* purple_roomlist_set_proto_data
 		* purple_roomlist_set_ui_data
+		* purple_whiteboard_get_account
+		* purple_whiteboard_get_draw_list
+		* purple_whiteboard_set_draw_list
+		* purple_whiteboard_get_protocol_data
+		* purple_whiteboard_set_protocol_data
+		* purple_whiteboard_get_state
+		* purple_whiteboard_set_state
+		* purple_whiteboard_get_ui_data
+		* purple_whiteboard_set_ui_data
+		* purple_whiteboard_get_who
+		* purple_xfer_get_fd
 		* purple_xfer_get_protocol_data
-		* purple_xfer_set_protocol_data
 		* purple_xfer_get_ui_data
+		* purple_xfer_get_watcher
+		* purple_xfer_set_fd
+		* purple_Xfer_set_local_port
+		* purple_xfer_set_protocol_data
+		* purple_xfer_set_status
 		* purple_xfer_set_ui_data
-		* purple_conversation_get_ui_data
-		* purple_conversation_set_ui_data
+		* purple_xfer_set_watcher
+		* Various WebKit-related functions in gtkwebview.h
+		* xmlnode_get_default_namespace
+		* xmlnode_strip_prefixes
 
 		Changed:
+		* purple_account_add_buddy now takes an invite message as the last
+		  parameter
+		* purple_account_add_buddies now takes an invite message as the last
+		  parameter
+		* purple_certificate_check_signature_chain_with_failing renamed
+		  to purple_certificate_check_signature_chain
 		* purple_connection_error now takes a PurpleConnectionError
 		  as the second parameter
+		* purple_dnsquery_a now takes a PurpleAccount as the first parameter
+		* purple_network_listen_family renamed to purple_network_listen
+		* purple_network_listen_range_family renamed to
+		  purple_network_listen_range
 		* purple_notify_user_info_add_pair renamed to
 		  purple_notify_user_info_add_pair_html
 		* purple_notify_user_info_get_entries returns a GQueue instead of
 		  a GList
 		* purple_notify_user_info_prepend_pair renamed to
 		  purple_notify_user_info_prepend_pair_html
+		* purple_srv_resolve now takes a PurpleAccount as the first parameter
+		* purple_txt_resolve now takes a PurpleAccount as the first parameter
 		* purple_util_fetch_url_request_len now takes a PurpleAccount as
 		  the first parameter
+		* purple_util_fetch_url_request_len renamed to purple_util_fetch_url_request
+		* purple_util_fetch_url_len renamed to purple_util_fetch_url
+		* purple_xfer_is_canceled renamed to purple_xfer_is_cancelled
 		* PurpleConnectionUiOps.report_disconnect now passes a
 		  PurpleConnectionError as the second parameter
 
@@ -56,32 +101,88 @@
 		* _XMLNodeType
 		* GtkIMHtml.clipboard_html_string
 		* GtkIMHtml.clipboard_text_string
+		* GtkIMHtmlFontDetail
+		* gtk_imhtml_animation_free
+		* gtk_imhtml_animation_new
+		* gtk_imhtml_image_add_to
+		* gtk_imhtml_image_free
+		* gtk_imhtml_image_new
+		* gtk_imhtml_image_scale
 		* pidgin_blist_update_account_error_state
 		* pidgin_check_if_dir
 		* PIDGIN_DIALOG
 		* pidgin_dialogs_alias_contact
 		* pidgin_set_custom_buddy_icon
 		* pidgin_setup_screenname_autocomplete
+		* PidginBuddyList.connection_errors
 		* PidginConversation.sg
+		* purple_account_add_buddies_with_invite
+		* purple_account_add_buddy_with_invite
+		* purple_blist_update_buddy_icon
+		* purple_buddy_get_local_alias
+		* purple_buddy_icons_has_custom_icon
+		* purple_buddy_icons_find_custom_icon
+		* purple_buddy_icons_set_custom_icon
 		* purple_connection_error_reason
+		* purple_connection_new
+		* purple_connection_new_unregister
+		* purple_connection_destroy
+		* purple_contact_set_alias
+		* purple_conv_chat_set_users
 		* purple_core_migrate
+		* purple_dnsquery_a_account
+		* purple_network_listen_map_external
 		* purple_notify_searchresults_column_get_title
 		* purple_notify_searchresults_get_columns_count
 		* purple_notify_searchresults_get_rows_count
 		* purple_notify_searchresults_row_get
+		* purple_plugins_register_load_notify_cb
+		* purple_plugins_register_probe_notify_cb
+		* purple_plugins_register_unload_notify_cb
+		* purple_plugins_unregister_load_notify_cb
+		* purple_plugins_unregister_probe_notify_cb
+		* purple_plugins_unregister_unload_notify_cb
+		* purple_presence_add_status
+		* purple_presence_add_list
+		* purple_proxy_connect_socks5
+		* purple_request_field_list_add
+		* purple_srv_cancel
+		* purple_srv_resolve_account
+		* purple_ssl_connect_fd
+		* purple_status_set_attr_boolean
+		* purple_status_set_attr_int
+		* purple_status_set_attr_string
+		* purple_status_type_add_attr
+		* purple_status_type_add_attrs
+		* purple_status_type_add_attrs_vargs
 		* purple_status_type_get_primary_attr
 		* purple_status_type_set_primary_attr
 		* purple_strlcat
 		* purple_strlcpy
+		* purple_txt_cancel
+		* purple_txt_resolve_account
 		* purple_util_fetch_url_request_len_with_account.  Use
-		  purple_util_fetch_url_request_len, insetad.
+		  purple_util_fetch_url_request_len, instead.
 		* PurpleConnectionUiOps.report_disconnect_reason
+		* PurplePluginProtocolInfo.add_buddy_with_invite
+		* PurplePluginProtocolInfo.add_buddies_with_invite
+		* PurplePluginProtocolInfo.get_cb_away
+		* serv_got_attention
+		* serv_send_attention
 		* struct _GtkIMHtmlFontDetail
+		* struct _PidginChatPane
+		* struct _PidginImPane
+		* struct _PurpleAttentionType
+		* struct _PurpleMenuAction
 		* struct _PurplePounce
+		* struct _PurpleProxyInfo
 		* struct _PurpleRequestField
 		* struct _PurpleRoomlist
 		* struct _PurpleRoomlistField
 		* struct _PurpleRoomlistRoom
+		* wpurple_g_access
+		* xmlnode_set_attrib_with_namespace
+		* xmlnode_set_attrib_with_prefix
 
 version 2.10.0:
 	libpurple:
--- a/configure.ac	Mon Aug 29 12:59:57 2011 +0900
+++ b/configure.ac	Mon Sep 26 14:57:21 2011 +0900
@@ -434,6 +434,16 @@
 	PKG_CHECK_MODULES(PANGO, [pango >= 1.4.0],
 			AC_DEFINE(HAVE_PANGO14, 1, [Define if we have Pango 1.4 or newer.]),:)
 
+	PKG_CHECK_MODULES(WEBKIT, [webkit-1.0 >= 1.1.1], , [
+		AC_MSG_RESULT(no)
+		AC_MSG_ERROR([
+You must have WebKit 1.1.1 or newer development headers installed to compile
+Pidgin.  If you want to build only Finch then specify --disable-gtkui when
+running configure.
+])])
+	AC_SUBST(WEBKIT_CFLAGS)
+	AC_SUBST(WEBKIT_LIBS)
+
 	dnl #######################################################################
 	dnl # Check if we should compile with X support
 	dnl #######################################################################
@@ -1070,7 +1080,7 @@
 fi
 
 if test "x$STATIC_PRPLS" = "xall" ; then
-	STATIC_PRPLS="bonjour gg irc jabber msn myspace mxit novell oscar sametime silc simple yahoo zephyr"
+	STATIC_PRPLS="bonjour gg irc jabber msn mxit myspace novell oscar sametime silc simple yahoo zephyr"
 fi
 if test "x$have_meanwhile" != "xyes" ; then
 	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'`
@@ -1115,8 +1125,8 @@
 		irc)		static_irc=yes ;;
 		jabber)		static_jabber=yes ;;
 		msn)		static_msn=yes ;;
+		mxit)		static_mxit=yes ;;
 		myspace)	static_myspace=yes ;;
-		mxit)		static_mxit=yes ;;
 		novell)		static_novell=yes ;;
 		oscar)		static_oscar=yes ;;
 		aim)		static_oscar=yes ;;
@@ -1134,8 +1144,8 @@
 AM_CONDITIONAL(STATIC_IRC, test "x$static_irc" = "xyes")
 AM_CONDITIONAL(STATIC_JABBER, test "x$static_jabber" = "xyes")
 AM_CONDITIONAL(STATIC_MSN, test "x$static_msn" = "xyes")
+AM_CONDITIONAL(STATIC_MXIT, test "x$static_mxit" = "xyes")
 AM_CONDITIONAL(STATIC_MYSPACE, test "x$static_myspace" = "xyes")
-AM_CONDITIONAL(STATIC_MXIT, test "x$static_mxit" = "xyes")
 AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes")
 AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes")
 AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes")
@@ -1149,7 +1159,7 @@
 
 AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`])
 if test "x$DYNAMIC_PRPLS" = "xall" ; then
-	DYNAMIC_PRPLS="bonjour gg irc jabber msn myspace mxit novell oscar sametime silc simple yahoo zephyr"
+	DYNAMIC_PRPLS="bonjour gg irc jabber msn mxit myspace novell oscar sametime silc simple yahoo zephyr"
 fi
 if test "x$have_meanwhile" != "xyes"; then
 	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'`
@@ -1168,8 +1178,8 @@
 		irc)		dynamic_irc=yes ;;
 		jabber)		dynamic_jabber=yes ;;
 		msn)		dynamic_msn=yes ;;
+		mxit)		dynamic_mxit=yes ;;
 		myspace)	dynamic_myspace=yes ;;
-		mxit)		dynamic_mxit=yes ;;
 		novell)		dynamic_novell=yes ;;
 		null)		dynamic_null=yes ;;
 		oscar)		dynamic_oscar=yes ;;
@@ -2486,6 +2496,7 @@
 		   pidgin/pixmaps/emotes/none/Makefile
 		   pidgin/pixmaps/emotes/small/16/Makefile
 		   pidgin/plugins/Makefile
+		   pidgin/plugins/adiumthemes/Makefile
 		   pidgin/plugins/cap/Makefile
 		   pidgin/plugins/disco/Makefile
 		   pidgin/plugins/gestures/Makefile
--- a/doc/account-signals.dox	Mon Aug 29 12:59:57 2011 +0900
+++ b/doc/account-signals.dox	Mon Sep 26 14:57:21 2011 +0900
@@ -34,7 +34,6 @@
   @signaldesc
    Emitted when an account is created by calling purple_account_new.
   @param account The account.
-  @since 2.6.0
  @endsignaldef
 
  @signaldef account-destroying
@@ -44,7 +43,6 @@
   @signaldesc
    Emitted when an account is about to be destroyed.
   @param account The account.
-  @since 2.6.0
  @endsignaldef
 
  @signaldef account-added
@@ -156,7 +154,6 @@
   @return Less than zero to deny the request without prompting, greater
           than zero if the request should be granted. If zero is returned,
           then the user will be prompted with the request.
-  @since 2.3.0
  @endsignaldef
 
  @signaldef account-authorization-requested-with-message
@@ -173,7 +170,6 @@
           get informed, PURPLE_ACCOUNT_RESPONSE_ACCEPT if the request should be
           granted. If PURPLE_ACCOUNT_RESPONSE_PASS is returned, then the user
           will be prompted with the request.
-  @since 2.8.0
  @endsignaldef
 
  @signaldef account-authorization-denied
@@ -184,7 +180,6 @@
    Emitted when the authorization request for a buddy is denied.
   @param account The account.
   @param user    The name of the user requesting authorization.
-  @since 2.3.0
  @endsignaldef
 
  @signaldef account-authorization-granted
@@ -195,7 +190,6 @@
    Emitted when the authorization request for a buddy is granted.
   @param account The account.
   @param user    The name of the user requesting authorization.
-  @since 2.3.0
  @endsignaldef
 
  @signaldef account-error-changed
@@ -214,7 +208,6 @@
                    pointer just after the next time this signal is emitted
                    for this @a account.
   @see purple_account_get_current_error()
-  @since 2.3.0
  @endsignaldef
 
  @signaldef account-signed-on
@@ -224,7 +217,6 @@
   @signaldesc
    Emitted when an account has signed on.
   @param account The account that has signed on.
-  @since 2.7.0
  @endsignaldef
 
  @signaldef account-signed-off
@@ -234,7 +226,6 @@
   @signaldesc
    Emitted when an account has signed off.
   @param account The account that has signed off.
-  @since 2.7.0
  @endsignaldef
 
  @signaldef account-connection-error
@@ -246,7 +237,6 @@
    @param account The account on which the error has occurred
    @param err     The error that occurred
    @param desc    A description of the error, giving more information.
-  @since 2.7.0
  @endsignaldef
  */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/blist-signals.dox	Mon Aug 29 12:59:57 2011 +0900
+++ b/doc/blist-signals.dox	Mon Sep 26 14:57:21 2011 +0900
@@ -136,7 +136,6 @@
   @param buddy	  The buddy
   @param newcaps
   @param oldcaps
-  @since 2.7.0
  @endsignaldef
 
  @signaldef ui-caps-changed
@@ -147,7 +146,6 @@
     Emitted when updating the media capabilities of the UI.
   @param newcaps
   @param oldcaps
-  @since 2.7.0
  @endsignaldef
  
  */
--- a/doc/connection-signals.dox	Mon Aug 29 12:59:57 2011 +0900
+++ b/doc/connection-signals.dox	Mon Sep 26 14:57:21 2011 +0900
@@ -44,7 +44,6 @@
   @return @c TRUE if the signal was handled or @c FALSE otherwise.  In
           practice, the return value is irrelevant, as it really only
           exists so plugins can block the UI's autojoin.
-  @since 2.7.0
  @endsignaldef
 
  @signaldef signing-off
--- a/doc/conversation-signals.dox	Mon Aug 29 12:59:57 2011 +0900
+++ b/doc/conversation-signals.dox	Mon Sep 26 14:57:21 2011 +0900
@@ -149,7 +149,6 @@
   @param message The message that was blocked.
   @param flags   The IM message flags.
   @param when    The time the message was sent.
-  @since 2.5.0
  @endsignaldef
 
  @signaldef writing-chat-msg
@@ -436,7 +435,6 @@
   @param name     The name of the chat invited to.
   @param message  The invitation message sent.
   @param data     Hashtable containing data about the invited chat.
-  @since 2.5.0
  @endsignaldef
 
  @signaldef chat-joined
@@ -477,7 +475,6 @@
    conversation.
   @param conv   The conversation.
   @param list   A pointer to the list of actions.
-  @since 2.1.0
  @endsignaldef
 
  @signaldef cleared-message-history
@@ -487,7 +484,6 @@
   @signaldesc
     Emitted when the conversation history is cleared.
   @param conv   The conversation.
-  @since 2.8.0
  @endsignaldef
 
  @signaldef sent-attention
@@ -501,7 +497,6 @@
   @param who      The name of the person receiving the attention
   @param conv     The conversation
   @param type     The attention type (an index starting at 0)
-  @since 2.7.0
  @endsignaldef
 
  @signaldef got-attention
@@ -515,7 +510,6 @@
   @param who      The name of the person sending the attention
   @param conv     The conversation
   @param type     The attention type (an index starting at 0)
-  @since 2.7.0
  @endsignaldef
 */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/gtkconv-signals.dox	Mon Aug 29 12:59:57 2011 +0900
+++ b/doc/gtkconv-signals.dox	Mon Sep 26 14:57:21 2011 +0900
@@ -129,7 +129,6 @@
   @signaldesc
    Emitted immediately before an existing conversation is hidden.
   @param gtkconv  The PidginConversation
-  @since 2.2.0
  @endsignaldef
 
  @signaldef conversation-displayed
@@ -139,7 +138,6 @@
   @signaldesc
    Emitted right after the Pidgin UI is attached to a new or a hidden conversation.
   @param gtkconv  The PidginConversation
-  @since 2.2.0
  @endsignaldef
 
 */
--- a/doc/notify-signals.dox	Mon Aug 29 12:59:57 2011 +0900
+++ b/doc/notify-signals.dox	Mon Sep 26 14:57:21 2011 +0900
@@ -35,7 +35,6 @@
   @param from      Who the email is from.
   @param to        Who the email is to.
   @param url       A url to view the email.
-  @since 2.1.0
  @endsignaldef
 
  @signaldef displaying-emails-notification
@@ -53,7 +52,6 @@
   @param tos        Who the emails are to.
   @param urls       The urls to view the emails.
   @param count      Number of emails being notified of.
-  @since 2.1.0
  @endsignaldef
 
 */
--- a/finch/gntaccount.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/finch/gntaccount.h	Mon Sep 26 14:57:21 2011 +0900
@@ -59,8 +59,6 @@
  * Show the edit dialog for an account.
  *
  * @param account  The account to edit, or @c NULL to create a new account.
- *
- * @since 2.2.0
  */
 void finch_account_dialog_show(PurpleAccount *account);
 
--- a/finch/gntblist.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/finch/gntblist.c	Mon Sep 26 14:57:21 2011 +0900
@@ -663,7 +663,7 @@
 		purple_blist_add_buddy(buddy, NULL, grp, NULL);
 	}
 
-	purple_account_add_buddy_with_invite(account, buddy, invite);
+	purple_account_add_buddy(account, buddy, invite);
 }
 
 static void
@@ -1078,9 +1078,10 @@
 	PurpleBlistNode *node = ggblist->cnode;
 	if (action) {
 		void (*callback)(PurpleBlistNode *, gpointer);
-		callback = (void (*)(PurpleBlistNode *, gpointer))action->callback;
+		callback = (void (*)(PurpleBlistNode *, gpointer))
+			purple_menu_action_get_callback(action);
 		if (callback)
-			callback(node, action->data);
+			callback(node, purple_menu_action_get_data(action));
 		else
 			return;
 	}
@@ -1095,15 +1096,17 @@
 	if (action == NULL)
 		return;
 
-	item = gnt_menuitem_new(action->label);
-	if (action->callback)
+	item = gnt_menuitem_new(purple_menu_action_get_label(action));
+	if (purple_menu_action_get_callback(action))
 		gnt_menuitem_set_callback(GNT_MENU_ITEM(item), context_menu_callback, action);
 	gnt_menu_add_item(menu, GNT_MENU_ITEM(item));
 
-	if (action->children) {
+	list = purple_menu_action_get_children(action);
+
+	if (list) {
 		GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
 		gnt_menuitem_set_submenu(item, GNT_MENU(sub));
-		for (list = action->children; list; list = list->next)
+		for (; list; list = list->next)
 			gnt_append_menu_action(GNT_MENU(sub), list->data, action);
 	}
 }
@@ -1123,7 +1126,7 @@
 		PurpleMenuAction *act = (PurpleMenuAction *) list->data;
 		if (!act)
 			continue;
-		act->data = node;
+		purple_menu_action_set_data(act, node);
 		gnt_append_menu_action(menu, act, NULL);
 		g_signal_connect_swapped(G_OBJECT(menu), "destroy",
 			G_CALLBACK(purple_menu_action_free), act);
@@ -1216,7 +1219,7 @@
 autojoin_toggled(GntMenuItem *item, gpointer data)
 {
 	PurpleMenuAction *action = data;
-	purple_blist_node_set_bool(action->data, "gnt-autojoin",
+	purple_blist_node_set_bool(purple_menu_action_get_data(action), "gnt-autojoin",
 				gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item)));
 }
 
@@ -1224,7 +1227,8 @@
 create_chat_menu(GntMenu *menu, PurpleChat *chat)
 {
 	PurpleMenuAction *action = purple_menu_action_new(_("Auto-join"), NULL, chat, NULL);
-	GntMenuItem *check = gnt_menuitem_check_new(action->label);
+	GntMenuItem *check = gnt_menuitem_check_new(
+			purple_menu_action_get_label(action));
 	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check),
 				purple_blist_node_get_bool((PurpleBlistNode*)chat, "gnt-autojoin"));
 	gnt_menu_add_item(menu, check);
--- a/finch/gntblist.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/finch/gntblist.h	Mon Sep 26 14:57:21 2011 +0900
@@ -115,15 +115,12 @@
  * @param name   The user to get information about.
  *
  * @return  Returns the ui-handle for the userinfo notification.
- *
- * @since 2.1.0
  */
 gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name);
 
 /**
  * Get the tree list of the buddy list.
  * @return  The GntTree widget.
- * @since 2.4.0
  */
 GntTree * finch_blist_get_tree(void);
 
@@ -131,7 +128,6 @@
  * Add an alternate buddy list manager.
  *
  * @param manager   The alternate buddylist manager.
- * @since 2.4.0
  */
 void finch_blist_install_manager(const FinchBlistManager *manager);
 
@@ -139,7 +135,6 @@
  * Remove an alternate buddy list manager.
  *
  * @param manager   The buddy list manager to remove.
- * @since 2.4.0
  */
 void finch_blist_uninstall_manager(const FinchBlistManager *manager);
 
@@ -149,7 +144,6 @@
  * @param id   The identifier for the desired buddy list manager.
  *
  * @return  The manager with the requested identifier, if available. @c NULL otherwise.
- * @since 2.4.0
  */
 FinchBlistManager * finch_blist_manager_find(const char *id);
 
@@ -157,7 +151,6 @@
  * Request the active buddy list manager to add a node.
  *
  * @param node  The node to add
- * @since 2.4.0
  */
 void finch_blist_manager_add_node(PurpleBlistNode *node);
 
--- a/finch/gntft.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/finch/gntft.c	Mon Sep 26 14:57:21 2011 +0900
@@ -149,7 +149,7 @@
 		while (iter) {
 			PurpleXfer *xfer = iter->data;
 			iter = iter->next;
-			if (purple_xfer_is_completed(xfer) || purple_xfer_is_canceled(xfer))
+			if (purple_xfer_is_completed(xfer) || purple_xfer_is_cancelled(xfer))
 			finch_xfer_dialog_remove_xfer(xfer);
 		}
 	}
@@ -160,7 +160,7 @@
 {
 	PurpleXfer *selected_xfer = gnt_tree_get_selection_data(GNT_TREE(xfer_dialog->tree));
 	if (selected_xfer && (purple_xfer_is_completed(selected_xfer) ||
-				purple_xfer_is_canceled(selected_xfer))) {
+				purple_xfer_is_cancelled(selected_xfer))) {
 		finch_xfer_dialog_remove_xfer(selected_xfer);
 	}
 }
@@ -382,7 +382,7 @@
 
 	update_title_progress();
 
-	if (purple_xfer_is_canceled(xfer))
+	if (purple_xfer_is_cancelled(xfer))
 		status = _("Cancelled");
 	else
 		status = _("Failed");
--- a/finch/gntrequest.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/finch/gntrequest.h	Mon Sep 26 14:57:21 2011 +0900
@@ -63,7 +63,6 @@
  * @param field   The request field.
  *
  * @return A GntWidget for the request field.
- * @since 2.4.0
  */
 GntWidget *finch_request_field_get_widget(PurpleRequestField *field);
 /*@}*/
--- a/finch/gntsound.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/finch/gntsound.h	Mon Sep 26 14:57:21 2011 +0900
@@ -37,8 +37,6 @@
  * Get the name of the active sound profile.
  *
  * @return The name of the profile
- *
- * @since 2.1.0
  */
 const char *finch_sound_get_active_profile(void);
 
@@ -46,8 +44,6 @@
  * Set the active profile.  If the profile doesn't exist, nothing is changed.
  *
  * @param name  The name of the profile
- *
- * @since 2.1.0
  */
 void finch_sound_set_active_profile(const char *name);
 
@@ -56,8 +52,6 @@
  *
  * @return A list of strings denoting sound profile names.
  *         Caller must free the list (but not the data).
- *
- * @since 2.1.0
  */
 GList *finch_sound_get_profiles(void);
 
@@ -66,8 +60,6 @@
  *
  * @return Returns FALSE if preference is set to 'No sound', or if volume is
  *         set to zero.
- *
- * @since 2.2.0
  */
 gboolean finch_sound_is_enabled(void);
 
@@ -75,15 +67,11 @@
  * Gets GNT sound UI ops.
  *
  * @return The UI operations structure.
- *
- * @since 2.1.0
  */
 PurpleSoundUiOps *finch_sound_get_ui_ops(void);
 
 /**
  * Show the sound settings dialog.
- *
- * @since 2.1.0
  */
 void finch_sounds_show_all(void);
 
--- a/finch/plugins/gnttinyurl.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/finch/plugins/gnttinyurl.c	Mon Sep 26 14:57:21 2011 +0900
@@ -319,7 +319,7 @@
 			url = g_strdup_printf("%s%s", purple_prefs_get_string(PREF_URL), purple_url_encode(tmp));
 		}
 		g_free(tmp);
-		purple_util_fetch_url(url, TRUE, "finch", FALSE, url_fetched, cbdata);
+		purple_util_fetch_url(url, TRUE, "finch", FALSE, -1, url_fetched, cbdata);
 		i = gnt_text_view_get_lines_below(tv);
 		str = g_strdup_printf(_("\nFetching TinyURL..."));
 		gnt_text_view_append_text_with_tag((tv), str, GNT_TEXT_FLAG_DIM, cbdata->tag);
@@ -383,7 +383,7 @@
 	/* Store the return value of _fetch_url and destroy that when win is
 	   destroyed, so that the callback for _fetch_url does not try to molest a
 	   non-existent window */
-	urlcb = purple_util_fetch_url(fullurl, TRUE, "finch", FALSE, tinyurl_notify_fetch_cb, win);
+	urlcb = purple_util_fetch_url(fullurl, TRUE, "finch", FALSE, -1, tinyurl_notify_fetch_cb, win);
 	g_free(fullurl);
 	g_signal_connect_swapped(G_OBJECT(win), "destroy",
 			G_CALLBACK(purple_util_fetch_url_cancel), urlcb);
--- a/libpurple/account.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/account.c	Mon Sep 26 14:57:21 2011 +0900
@@ -1086,7 +1086,8 @@
 
 	purple_account_set_status_types(account, NULL);
 
-	purple_presence_destroy(account->presence);
+	if (account->presence)
+		purple_presence_destroy(account->presence);
 
 	if(account->system_log)
 		purple_log_free(account->system_log);
@@ -2518,7 +2519,7 @@
 }
 
 void
-purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy)
+purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy, const char *message)
 {
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc;
@@ -2535,40 +2536,13 @@
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
 	if (prpl_info != NULL) {
-		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy_with_invite))
-			prpl_info->add_buddy_with_invite(gc, buddy, purple_buddy_get_group(buddy), NULL);
-		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy))
-			prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy));
+		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy))
+			prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy), message);
 	}
 }
 
 void
-purple_account_add_buddy_with_invite(PurpleAccount *account, PurpleBuddy *buddy, const char *message)
-{
-	PurplePluginProtocolInfo *prpl_info = NULL;
-	PurpleConnection *gc;
-	PurplePlugin *prpl = NULL;
-
-	g_return_if_fail(account != NULL);
-	g_return_if_fail(buddy != NULL);
-
-	gc = purple_account_get_connection(account);
-	if (gc != NULL)
-		prpl = purple_connection_get_prpl(gc);
-
-	if (prpl != NULL)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info != NULL) {
-		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy_with_invite))
-			prpl_info->add_buddy_with_invite(gc, buddy, purple_buddy_get_group(buddy), message);
-		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy))
-			prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy));
-	}
-}
-
-void
-purple_account_add_buddies(PurpleAccount *account, GList *buddies)
+purple_account_add_buddies(PurpleAccount *account, GList *buddies, const char *message)
 {
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = purple_account_get_connection(account);
@@ -2589,73 +2563,13 @@
 			groups = g_list_append(groups, purple_buddy_get_group(buddy));
 		}
 
-		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies_with_invite))
-			prpl_info->add_buddies_with_invite(gc, buddies, groups, NULL);
-		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies))
-			prpl_info->add_buddies(gc, buddies, groups);
-		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy_with_invite)) {
-			GList *curb = buddies, *curg = groups;
-
-			while ((curb != NULL) && (curg != NULL)) {
-				prpl_info->add_buddy_with_invite(gc, curb->data, curg->data, NULL);
-				curb = curb->next;
-				curg = curg->next;
-			}
-		}
+		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies))
+			prpl_info->add_buddies(gc, buddies, groups, message);
 		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy)) {
 			GList *curb = buddies, *curg = groups;
 
 			while ((curb != NULL) && (curg != NULL)) {
-				prpl_info->add_buddy(gc, curb->data, curg->data);
-				curb = curb->next;
-				curg = curg->next;
-			}
-		}
-
-		g_list_free(groups);
-	}
-}
-
-void
-purple_account_add_buddies_with_invite(PurpleAccount *account, GList *buddies, const char *message)
-{
-	PurplePluginProtocolInfo *prpl_info = NULL;
-	PurpleConnection *gc = purple_account_get_connection(account);
-	PurplePlugin *prpl = NULL;
-
-	if (gc != NULL)
-		prpl = purple_connection_get_prpl(gc);
-
-	if (prpl != NULL)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info) {
-		GList *cur, *groups = NULL;
-
-		/* Make a list of what group each buddy is in */
-		for (cur = buddies; cur != NULL; cur = cur->next) {
-			PurpleBuddy *buddy = cur->data;
-			groups = g_list_append(groups, purple_buddy_get_group(buddy));
-		}
-
-		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies_with_invite))
-			prpl_info->add_buddies_with_invite(gc, buddies, groups, message);
-		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy_with_invite)) {
-			GList *curb = buddies, *curg = groups;
-
-			while ((curb != NULL) && (curg != NULL)) {
-				prpl_info->add_buddy_with_invite(gc, curb->data, curg->data, message);
-				curb = curb->next;
-				curg = curg->next;
-			}
-		}
-		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies))
-			prpl_info->add_buddies(gc, buddies, groups);
-		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy)) {
-			GList *curb = buddies, *curg = groups;
-
-			while ((curb != NULL) && (curg != NULL)) {
-				prpl_info->add_buddy(gc, curb->data, curg->data);
+				prpl_info->add_buddy(gc, curb->data, curg->data, message);
 				curb = curb->next;
 				curg = curg->next;
 			}
--- a/libpurple/account.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/account.h	Mon Sep 26 14:57:21 2011 +0900
@@ -433,8 +433,6 @@
  *
  * @param account      The account.
  * @param privacy_type The privacy type.
- *
- * @since 2.7.0
  */
 void purple_account_set_privacy_type(PurpleAccount *account, PurplePrivacyType privacy_type);
 
@@ -491,8 +489,6 @@
  *                   is successfully set on the server (or NULL).
  * @param failure_cb A callback which will be called if the alias
  *                   is not successfully set on the server (or NULL).
- *
- * @since 2.7.0
  */
 void purple_account_set_public_alias(PurpleAccount *account,
 	const char *alias, PurpleSetPublicAliasSuccessCallback success_cb,
@@ -506,7 +502,6 @@
  * @param success_cb A callback which will be called with the alias
  * @param failure_cb A callback which will be called if the prpl is
  *                   unable to retrieve the server-side alias.
- * @since 2.7.0
  */
 void purple_account_get_public_alias(PurpleAccount *account,
 	PurpleGetPublicAliasSuccessCallback success_cb,
@@ -542,8 +537,6 @@
  *
  * @param account The account.
  * @param setting The setting to remove.
- *
- * @since 2.6.0
  */
 void purple_account_remove_setting(PurpleAccount *account, const char *setting);
 
@@ -717,8 +710,6 @@
  * @param account The account.
  *
  * @return The name to display.
- *
- * @since 2.7.0
  */
 const gchar *purple_account_get_name_for_display(const PurpleAccount *account);
 
@@ -767,8 +758,6 @@
  * @param account   The account.
  *
  * @return The privacy type.
- *
- * @since 2.7.0
  */
 PurplePrivacyType purple_account_get_privacy_type(const PurpleAccount *account);
 
@@ -958,40 +947,18 @@
  *
  * @param account The account.
  * @param buddy The buddy to add.
- *
- * @deprecated Use purple_account_add_buddy_with_invite and \c NULL message.
+ * @param message The invite message.  This may be ignored by a prpl.
  */
-void purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy);
-/**
- * Adds a buddy to the server-side buddy list for the specified account.
- *
- * @param account The account.
- * @param buddy The buddy to add.
- * @param message The invite message.  This may be ignored by a prpl.
- *
- * @since 2.8.0
- */
-void purple_account_add_buddy_with_invite(PurpleAccount *account, PurpleBuddy *buddy, const char *message);
+void purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy, const char *message);
 
 /**
  * Adds a list of buddies to the server-side buddy list.
  *
  * @param account The account.
  * @param buddies The list of PurpleBlistNodes representing the buddies to add.
- *
- * @deprecated Use purple_account_add_buddies_with_invite and \c NULL message.
+ * @param message The invite message.  This may be ignored by a prpl.
  */
-void purple_account_add_buddies(PurpleAccount *account, GList *buddies);
-/**
- * Adds a list of buddies to the server-side buddy list.
- *
- * @param account The account.
- * @param buddies The list of PurpleBlistNodes representing the buddies to add.
- * @param message The invite message.  This may be ignored by a prpl.
- *
- * @since 2.8.0
- */
-void purple_account_add_buddies_with_invite(PurpleAccount *account, GList *buddies, const char *message);
+void purple_account_add_buddies(PurpleAccount *account, GList *buddies, const char *message);
 
 /**
  * Removes a buddy from the server-side buddy list.
--- a/libpurple/blist.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/blist.c	Mon Sep 26 14:57:21 2011 +0900
@@ -956,12 +956,6 @@
 		ops->update(purplebuddylist, node);
 }
 
-void
-purple_blist_update_buddy_icon(PurpleBuddy *buddy)
-{
-	purple_blist_update_node_icon((PurpleBlistNode *)buddy);
-}
-
 /*
  * TODO: Maybe remove the call to this from server.c and call it
  * from oscar.c and toc.c instead?
@@ -1313,7 +1307,7 @@
 
 				purple_account_remove_buddies(account, buddies, groups);
 				g_list_free(groups);
-				purple_account_add_buddies(account, buddies);
+				purple_account_add_buddies(account, buddies, NULL);
 			}
 
 			g_list_free(buddies);
@@ -1743,11 +1737,6 @@
 	return (PurpleGroup *)(((PurpleBlistNode *)contact)->parent);
 }
 
-void purple_contact_set_alias(PurpleContact *contact, const char *alias)
-{
-	purple_blist_alias_contact(contact,alias);
-}
-
 const char *purple_contact_get_alias(PurpleContact* contact)
 {
 	g_return_val_if_fail(contact != NULL, NULL);
@@ -2375,26 +2364,6 @@
 	return NULL;
 }
 
-const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
-{
-	PurpleContact *c;
-
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	/* Search for an alias for the buddy. In order of precedence: */
-	/* The buddy alias */
-	if (buddy->alias != NULL)
-		return buddy->alias;
-
-	/* The contact alias */
-	c = purple_buddy_get_contact(buddy);
-	if ((c != NULL) && (c->alias != NULL))
-		return c->alias;
-
-	/* The buddy's user name (i.e. no alias) */
-	return buddy->name;
-}
-
 const char *purple_chat_get_name(PurpleChat *chat)
 {
 	char *ret = NULL;
--- a/libpurple/blist.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/blist.h	Mon Sep 26 14:57:21 2011 +0900
@@ -75,9 +75,6 @@
 
 } PurpleBlistNodeFlags;
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_BLIST_NODE(obj) ((PurpleBlistNode *)(obj))
 
 #define PURPLE_BLIST_NODE_HAS_FLAG(b, f) (purple_blist_node_get_flags((PurpleBlistNode*)(b)) & (f))
@@ -86,24 +83,12 @@
 #define PURPLE_BLIST_NODE_NAME(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_CHAT_NODE  ? purple_chat_get_name((PurpleChat*)n) :        \
 				     purple_blist_node_get_type(n) == PURPLE_BLIST_BUDDY_NODE ? purple_buddy_get_name((PurpleBuddy*)n) : NULL)
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_GROUP(obj) ((PurpleGroup *)(obj))
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_CONTACT(obj) ((PurpleContact *)(obj))
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_BUDDY(obj) ((PurpleBuddy *)(obj))
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_CHAT(obj) ((PurpleChat *)(obj))
 
 #include "account.h"
@@ -226,8 +211,6 @@
 	 * previous libpurple versions.
 	 *
 	 * @param node    The node which has been modified.
-	 *
-	 * @since 2.6.0.
 	 */
 	void (*save_node)(PurpleBlistNode *node);
 
@@ -241,7 +224,6 @@
 	 * previous libpurple versions.
 	 *
 	 * @param node  The node which has been modified.
-	 * @since 2.6.0.
 	 */
 	void (*remove_node)(PurpleBlistNode *node);
 
@@ -256,7 +238,6 @@
 	 *
 	 * @param account  The account whose data to save. If NULL, save all data
 	 *                  for all accounts.
-	 * @since 2.6.0.
 	 */
 	void (*save_account)(PurpleAccount *account);
 
@@ -311,7 +292,6 @@
  *         freeing the list.
  *
  * @see purple_find_buddies
- * @since 2.6.0
  */
 GSList *purple_blist_get_buddies(void);
 
@@ -319,8 +299,6 @@
  * Returns the UI data for the list.
  *
  * @return The UI data for the list.
- *
- * @since 2.6.0
  */
 gpointer purple_blist_get_ui_data(void);
 
@@ -328,8 +306,6 @@
  * Sets the UI data for the list.
  *
  * @param ui_data The UI data for the list.
- *
- * @since 2.6.0
  */
 void purple_blist_set_ui_data(gpointer ui_data);
 
@@ -352,7 +328,7 @@
  *
  * @param node A node.
  * @return  The parent node.
- * @since 2.4.0
+ *
  * @see purple_blist_node_get_first_child
  * @see purple_blist_node_get_sibling_next
  * @see purple_blist_node_get_sibling_prev
@@ -365,7 +341,7 @@
  *
  * @param node A node.
  * @return  The child node.
- * @since 2.4.0
+ *
  * @see purple_blist_node_get_parent
  * @see purple_blist_node_get_sibling_next
  * @see purple_blist_node_get_sibling_prev
@@ -378,7 +354,7 @@
  *
  * @param node A node.
  * @return  The sibling node.
- * @since 2.4.0
+ *
  * @see purple_blist_node_get_parent
  * @see purple_blist_node_get_first_child
  * @see purple_blist_node_get_sibling_prev
@@ -391,7 +367,7 @@
  *
  * @param node A node.
  * @return  The sibling node.
- * @since 2.4.0
+ *
  * @see purple_blist_node_get_parent
  * @see purple_blist_node_get_first_child
  * @see purple_blist_node_get_sibling_next
@@ -404,7 +380,6 @@
  *
  * @param node The node.
  * @return The UI data.
- * @since 2.6.0
  */
 gpointer purple_blist_node_get_ui_data(const PurpleBlistNode *node);
 
@@ -413,8 +388,6 @@
  *
  * @param node The node.
  * @param ui_data The UI data.
- *
- * @since 2.6.0
  */
 void purple_blist_node_set_ui_data(PurpleBlistNode *node, gpointer ui_data);
 
@@ -454,21 +427,9 @@
  * Updates a node's custom icon.
  *
  * @param node  The PurpleBlistNode whose custom icon has changed.
- *
- * @since 2.5.0
  */
 void purple_blist_update_node_icon(PurpleBlistNode *node);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Updates a buddy's icon.
- *
- * @param buddy  The buddy whose buddy icon has changed
- * @deprecated Use purple_blist_update_node_icon() instead.
- */
-void purple_blist_update_buddy_icon(PurpleBuddy *buddy);
-#endif
-
 /**
  * Renames a buddy in the buddy list.
  *
@@ -623,7 +584,6 @@
  * @return      The protocol data.
  *
  * @see purple_buddy_set_protocol_data()
- * @since 2.6.0
  */
 gpointer purple_buddy_get_protocol_data(const PurpleBuddy *buddy);
 
@@ -636,7 +596,6 @@
  * @param data  The data.
  *
  * @see purple_buddy_get_protocol_data()
- * @since 2.6.0
  */
 void purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data);
 
@@ -663,8 +622,6 @@
  *
  * @param buddy The buddy.
  * @return      The media caps.
- *
- * @since 2.7.0
  */
 PurpleMediaCaps purple_buddy_get_media_caps(const PurpleBuddy *buddy);
 
@@ -739,8 +696,6 @@
  *
  * @param contact  The contact
  * @return         The group
- *
- * @since 2.7.0
  */
 PurpleGroup *purple_contact_get_group(const PurpleContact *contact);
 
@@ -774,18 +729,6 @@
  */
 PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Sets the alias for a contact.
- *
- * @param contact  The contact
- * @param alias    The alias to set, or NULL to unset
- *
- * @deprecated Use purple_blist_alias_contact() instead.
- */
-void purple_contact_set_alias(PurpleContact *contact, const char *alias);
-#endif
-
 /**
  * Gets the alias for a contact.
  *
@@ -876,19 +819,6 @@
  */
 const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Returns the correct alias for this user, ignoring server aliases.  Used
- * when a user-recognizable name is required.  In order: buddy's alias; buddy's
- * contact alias; buddy's user name.
- *
- * @param buddy  The buddy whose alias will be returned.
- * @return       The appropriate name or alias.
- * @deprecated   Try purple_buddy_get_alias(), if server aliases are okay.
- */
-const char *purple_buddy_get_local_alias(PurpleBuddy *buddy);
-#endif
-
 /**
  * Returns the correct name to display for a buddy. In order of precedence:
  * the buddy's alias; the buddy's server alias; the buddy's contact alias;
@@ -904,8 +834,6 @@
  *
  * @param buddy  The buddy
  * @return       The local alias for the buddy
- *
- * @since 2.6.0
  */
 const char *purple_buddy_get_local_buddy_alias(PurpleBuddy *buddy);
 
@@ -981,8 +909,6 @@
  * @param chat  The chat.
  *
  * @return  The account the chat belongs to.
- *
- * @since 2.4.0
  */
 PurpleAccount *purple_chat_get_account(PurpleChat *chat);
 
@@ -992,8 +918,6 @@
  * @param chat  The chat.
  *
  * @constreturn  The hashtable.
- *
- * @since 2.4.0
  */
 GHashTable *purple_chat_get_components(PurpleChat *chat);
 
@@ -1211,8 +1135,6 @@
  * @param node The node.
  *
  * @return The type of the node.
- *
- * @since 2.1.0
  */
 PurpleBlistNodeType purple_blist_node_get_type(PurpleBlistNode *node);
 
--- a/libpurple/buddyicon.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/buddyicon.c	Mon Sep 26 14:57:21 2011 +0900
@@ -955,25 +955,6 @@
 	return purple_buddy_icons_node_set_custom_icon(node, data, len);
 }
 
-gboolean
-purple_buddy_icons_has_custom_icon(PurpleContact *contact)
-{
-	return purple_buddy_icons_node_has_custom_icon((PurpleBlistNode*)contact);
-}
-
-PurpleStoredImage *
-purple_buddy_icons_find_custom_icon(PurpleContact *contact)
-{
-	return purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact);
-}
-
-PurpleStoredImage *
-purple_buddy_icons_set_custom_icon(PurpleContact *contact, guchar *icon_data,
-                                   size_t icon_len)
-{
-	return purple_buddy_icons_node_set_custom_icon((PurpleBlistNode*)contact, icon_data, icon_len);
-}
-
 static void
 delete_buddy_icon_settings(PurpleBlistNode *node, const char *setting_name)
 {
--- a/libpurple/buddyicon.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/buddyicon.h	Mon Sep 26 14:57:21 2011 +0900
@@ -275,7 +275,6 @@
  * @param node The blist node.
  *
  * @return A boolean indicating if @a node has a custom buddy icon.
- * @since 2.5.0
  */
 gboolean
 purple_buddy_icons_node_has_custom_icon(PurpleBlistNode *node);
@@ -293,7 +292,6 @@
  * @param node The node.
  *
  * @return The custom buddy icon.
- * @since 2.5.0
  */
 PurpleStoredImage *
 purple_buddy_icons_node_find_custom_icon(PurpleBlistNode *node);
@@ -311,7 +309,6 @@
  *
  * @return The icon that was set. The caller does NOT own a reference to this,
  *         and must call purple_imgstore_ref() if it wants one.
- * @since 2.5.0
  */
 PurpleStoredImage *
 purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node,
@@ -329,45 +326,11 @@
  *
  * @return The icon that was set. The caller does NOT own a reference to this,
  *         and must call purple_imgstore_ref() if it wants one.
- * @since 2.5.0
  */
 PurpleStoredImage *
 purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node,
                                                   const gchar *filename);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BUDDYICON_C_)
-/**
- * PurpleContact version of purple_buddy_icons_node_has_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_has_custom_icon()
- *
- * @deprecated Use purple_buddy_icons_node_has_custom_icon instead.
- */
-gboolean
-purple_buddy_icons_has_custom_icon(PurpleContact *contact);
-
-/**
- * PurpleContact version of purple_buddy_icons_node_find_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_find_custom_icon()
- *
- * @deprecated Use purple_buddy_icons_node_find_custom_icon instead.
- */
-PurpleStoredImage *
-purple_buddy_icons_find_custom_icon(PurpleContact *contact);
-
-/**
- * PurpleContact version of purple_buddy_icons_node_set_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_set_custom_icon()
- *
- * @deprecated Use purple_buddy_icons_node_set_custom_icon instead.
- */
-PurpleStoredImage *
-purple_buddy_icons_set_custom_icon(PurpleContact *contact,
-                                   guchar *icon_data, size_t icon_len);
-#endif
-
 /**
  * Sets whether or not buddy icon caching is enabled.
  *
--- a/libpurple/certificate.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/certificate.c	Mon Sep 26 14:57:21 2011 +0900
@@ -275,7 +275,7 @@
 }
 
 gboolean
-purple_certificate_check_signature_chain_with_failing(GList *chain,
+purple_certificate_check_signature_chain(GList *chain,
                                                       PurpleCertificate **failing)
 {
 	GList *cur;
@@ -363,12 +363,6 @@
 	return TRUE;
 }
 
-gboolean
-purple_certificate_check_signature_chain(GList *chain)
-{
-	return purple_certificate_check_signature_chain_with_failing(chain, NULL);
-}
-
 PurpleCertificate *
 purple_certificate_import(PurpleCertificateScheme *scheme, const gchar *filename)
 {
@@ -1622,7 +1616,7 @@
 	ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca");
 
 	/* Next, check that the certificate chain is valid */
-	if (!purple_certificate_check_signature_chain_with_failing(chain,
+	if (!purple_certificate_check_signature_chain(chain,
 				&failing_crt))
 	{
 		gboolean chain_validated = FALSE;
--- a/libpurple/certificate.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/certificate.h	Mon Sep 26 14:57:21 2011 +0900
@@ -2,7 +2,6 @@
  * @file certificate.h Public-Key Certificate API
  * @ingroup core
  * @see @ref certificate-signals
- * @since 2.2.0
  */
 
 /*
@@ -462,33 +461,12 @@
  *                   chain fails to validate, this will be set to the
  *                   certificate whose signature could not be validated.
  * @return TRUE if the chain is valid. See description.
- *
- * @since 2.6.0
- * @deprecated  This function will become
- *              purple_certificate_check_signature_chain in 3.0.0
  */
 gboolean
-purple_certificate_check_signature_chain_with_failing(GList *chain,
+purple_certificate_check_signature_chain(GList *chain,
 		PurpleCertificate **failing);
 
 /**
- * Check that a certificate chain is valid
- *
- * Uses purple_certificate_signed_by() to verify that each PurpleCertificate
- * in the chain carries a valid signature from the next. A single-certificate
- * chain is considered to be valid.
- *
- * @param chain      List of PurpleCertificate instances comprising the chain,
- *                   in the order certificate, issuer, issuer's issuer, etc.
- * @return TRUE if the chain is valid. See description.
- * @todo Specify which certificate in the chain caused a failure
- * @deprecated  This function will be removed in 3.0.0 and replaced with
- *              purple_certificate_check_signature_chain_with_failing
- */
-gboolean
-purple_certificate_check_signature_chain(GList *chain);
-
-/**
  * Imports a PurpleCertificate from a file
  *
  * @param scheme      Scheme to import under
--- a/libpurple/cmds.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/cmds.h	Mon Sep 26 14:57:21 2011 +0900
@@ -225,19 +225,16 @@
 /**
  * Get the handle for the commands API
  * @return The handle
- * @since 2.5.0
  */
 gpointer purple_cmds_get_handle(void);
 
 /**
  * Initialize the commands subsystem.
- * @since 2.5.0
  */
 void purple_cmds_init(void);
 
 /**
  * Uninitialize the commands subsystem.
- * @since 2.5.0
  */
 void purple_cmds_uninit(void);
 
--- a/libpurple/connection.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/connection.c	Mon Sep 26 14:57:21 2011 +0900
@@ -92,12 +92,6 @@
 }
 
 void
-purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
-{
-	_purple_connection_new(account, regist, password);
-}
-
-void
 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
 {
 	PurpleConnection *gc;
@@ -171,11 +165,6 @@
 		prpl_info->login(account);
 	}
 }
-void
-purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data)
-{
-	_purple_connection_new_unregister(account, password, cb, user_data);
-}
 
 void
 _purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data)
@@ -234,12 +223,6 @@
 }
 
 void
-purple_connection_destroy(PurpleConnection *gc)
-{
-	_purple_connection_destroy(gc);
-}
-
-void
 _purple_connection_destroy(PurpleConnection *gc)
 {
 	PurpleAccount *account;
--- a/libpurple/connection.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/connection.h	Mon Sep 26 14:57:21 2011 +0900
@@ -59,8 +59,6 @@
 
 /**
  * Possible errors that can cause a connection to be closed.
- *
- *  @since 2.3.0
  */
 typedef enum
 {
@@ -216,8 +214,6 @@
 	 * @param text  a localized message describing the disconnection
 	 *              in more detail to the user.
 	 * @see #purple_connection_error
-	 *
-	 * @since 2.3.0
 	 */
 	void (*report_disconnect)(PurpleConnection *gc,
 	                          PurpleConnectionError reason,
@@ -272,63 +268,6 @@
 /**************************************************************************/
 /*@{*/
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
-/**
- * This function should only be called by purple_account_connect()
- * in account.c.  If you're trying to sign on an account, use that
- * function instead.
- *
- * Creates a connection to the specified account and either connects
- * or attempts to register a new account.  If you are logging in,
- * the connection uses the current active status for this account.
- * So if you want to sign on as "away," for example, you need to
- * have called purple_account_set_status(account, "away").
- * (And this will call purple_account_connect() automatically).
- *
- * @param account  The account the connection should be connecting to.
- * @param regist   Whether we are registering a new account or just
- *                 trying to do a normal signon.
- * @param password The password to use.
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
- */
-void purple_connection_new(PurpleAccount *account, gboolean regist,
-									const char *password);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
-/**
- * This function should only be called by purple_account_unregister()
- * in account.c.
- *
- * Tries to unregister the account on the server. If the account is not
- * connected, also creates a new connection.
- *
- * @param account  The account to unregister
- * @param password The password to use.
- * @param cb Optional callback to be called when unregistration is complete
- * @param user_data user data to pass to the callback
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
- */
-void purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
-/**
- * Disconnects and destroys a PurpleConnection.
- *
- * This function should only be called by purple_account_disconnect()
- * in account.c.  If you're trying to sign off an account, use that
- * function instead.
- *
- * @param gc The purple connection to destroy.
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
- */
-void purple_connection_destroy(PurpleConnection *gc);
-#endif
-
 /**
  * Sets the connection state.  PRPLs should call this and pass in
  * the state #PURPLE_CONNECTED when the account is completely
@@ -362,8 +301,6 @@
  *
  * @param connection The PurpleConnection.
  * @param proto_data The protocol data to set for the connection.
- *
- * @since 2.6.0
  */
 void purple_connection_set_protocol_data(PurpleConnection *connection, void *proto_data);
 
@@ -399,8 +336,6 @@
  * @param gc The connection.
  *
  * @return The protocol plugin.
- *
- * @since 2.4.0
  */
 PurplePlugin * purple_connection_get_prpl(const PurpleConnection *gc);
 
@@ -428,8 +363,6 @@
  * @param connection The PurpleConnection.
  *
  * @return The protocol data for the connection.
- *
- * @since 2.6.0
  */
 void *purple_connection_get_protocol_data(const PurpleConnection *connection);
 
@@ -461,8 +394,6 @@
  * @param gc          the connection which is closing.
  * @param reason      why the connection is closing.
  * @param description a non-@c NULL localized description of the error.
- *
- * @since 2.3.0
  */
 void
 purple_connection_error(PurpleConnection *gc,
@@ -473,8 +404,6 @@
  * Closes a connection due to an SSL error; this is basically a shortcut to
  * turning the #PurpleSslErrorType into a #PurpleConnectionError and a
  * human-readable string and then calling purple_connection_error().
- *
- * @since 2.3.0
  */
 void
 purple_connection_ssl_error (PurpleConnection *gc,
@@ -497,8 +426,6 @@
  *
  * @return @c TRUE if the account should not be automatically reconnected, and
  *         @c FALSE otherwise.
- *
- * @since 2.3.0
  */
 gboolean
 purple_connection_error_is_fatal (PurpleConnectionError reason);
--- a/libpurple/conversation.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/conversation.c	Mon Sep 26 14:57:21 2011 +0900
@@ -1393,16 +1393,6 @@
 }
 
 GList *
-purple_conv_chat_set_users(PurpleConvChat *chat, GList *users)
-{
-	g_return_val_if_fail(chat != NULL, NULL);
-
-	chat->in_room = users;
-
-	return users;
-}
-
-GList *
 purple_conv_chat_get_users(const PurpleConvChat *chat)
 {
 	g_return_val_if_fail(chat != NULL, NULL);
--- a/libpurple/conversation.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/conversation.h	Mon Sep 26 14:57:21 2011 +0900
@@ -125,7 +125,7 @@
 	PURPLE_MESSAGE_IMAGES      = 0x1000, /**< Message contains images  */
 	PURPLE_MESSAGE_NOTIFY      = 0x2000, /**< Message is a notification */
 	PURPLE_MESSAGE_NO_LINKIFY  = 0x4000, /**< Message should not be auto-
-										   linkified @since 2.1.0 */
+										   linkified */
 	PURPLE_MESSAGE_INVISIBLE   = 0x8000  /**< Message should not be displayed */
 } PurpleMessageFlags;
 
@@ -140,7 +140,7 @@
 	PURPLE_CBFLAGS_OP            = 0x0004, /**< Channel Op or Moderator      */
 	PURPLE_CBFLAGS_FOUNDER       = 0x0008, /**< Channel Founder              */
 	PURPLE_CBFLAGS_TYPING        = 0x0010, /**< Currently typing             */
-	PURPLE_CBFLAGS_AWAY          = 0x0020  /**< Currently away. @since 2.8.0 */
+	PURPLE_CBFLAGS_AWAY          = 0x0020  /**< Currently away.              */
 
 } PurpleConvChatBuddyFlags;
 
@@ -281,9 +281,7 @@
 	char *nick;                      /**< Your nick in this chat.       */
 
 	gboolean left;                   /**< We left the chat and kept the window open */
-	GHashTable *users;               /**< Hash table of the users in the room.
-	                                  *   @since 2.9.0
-	                                  */
+	GHashTable *users;               /**< Hash table of the users in the room. */
 };
 
 /**
@@ -291,31 +289,43 @@
  */
 struct _PurpleConvChatBuddy
 {
-	char *name;                      /**< The chat participant's name in the chat. */
-	char *alias;                     /**< The chat participant's alias, if known;
-	                                  *   @a NULL otherwise.
-	                                  */
-	char *alias_key;                 /**< A string by which this buddy will be sorted,
-	                                  *   or @c NULL if the buddy should be sorted by
-	                                  *   its @c name.  (This is currently always @c
-	                                  *   NULL.)
-	                                  */
-	gboolean buddy;                  /**< @a TRUE if this chat participant is on the
-	                                  *   buddy list; @a FALSE otherwise.
-	                                  */
-	PurpleConvChatBuddyFlags flags;  /**< A bitwise OR of flags for this participant,
-	                                  *   such as whether they are a channel operator.
-	                                  */
-	GHashTable *attributes;          /**< A hash table of attributes about the user, such as
-                                    *   real name, user@host, etc.
-                                    */
-	gpointer ui_data;                /** < The UI can put whatever it wants here. */
+	/** The chat participant's name in the chat. */
+	char *name;
+
+	/** The chat participant's alias, if known; @a NULL otherwise. */
+	char *alias;
+
+	/**
+	 * A string by which this buddy will be sorted, or @c NULL if the
+	 * buddy should be sorted by its @c name.  (This is currently always
+	 * @c NULL.
+	 */
+	char *alias_key;
+
+	/**
+	 * @a TRUE if this chat participant is on the buddy list;
+	 * @a FALSE otherwise.
+	 */
+	gboolean buddy;
+
+	/**
+	 * A bitwise OR of flags for this participant, such as whether they
+	 * are a channel operator.
+	 */
+	PurpleConvChatBuddyFlags flags;
+
+	/**
+	 * A hash table of attributes about the user, such as real name,
+	 * user\@host, etc.
+	 */
+	GHashTable *attributes;
+
+	/** The UI can put whatever it wants here. */
+	gpointer ui_data;
 };
 
 /**
  * Description of a conversation message
- *
- * @since 2.2.0
  */
 struct _PurpleConvMessage
 {
@@ -323,8 +333,8 @@
 	char *what;
 	PurpleMessageFlags flags;
 	time_t when;
-	PurpleConversation *conv;  /**< @since 2.3.0 */
-	char *alias;               /**< @since 2.3.0 */
+	PurpleConversation *conv;
+	char *alias;
 };
 
 /**
@@ -744,8 +754,6 @@
  * @return  A GList of PurpleConvMessage's. The must not modify the list or the data within.
  *          The list contains the newest message at the beginning, and the oldest message at
  *          the end.
- *
- * @since 2.2.0
  */
 GList *purple_conversation_get_message_history(PurpleConversation *conv);
 
@@ -753,8 +761,6 @@
  * Clear the message history of a conversation.
  *
  * @param conv  The conversation
- *
- * @since 2.2.0
  */
 void purple_conversation_clear_message_history(PurpleConversation *conv);
 
@@ -764,8 +770,6 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The name of the sender of the message
- *
- * @since 2.2.0
  */
 const char *purple_conversation_message_get_sender(PurpleConvMessage *msg);
 
@@ -775,8 +779,6 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The name of the sender of the message
- *
- * @since 2.2.0
  */
 const char *purple_conversation_message_get_message(PurpleConvMessage *msg);
 
@@ -786,8 +788,6 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The message flags
- *
- * @since 2.2.0
  */
 PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg);
 
@@ -797,8 +797,6 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The timestamp of the message
- *
- * @since 2.2.0
  */
 time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg);
 
@@ -1080,22 +1078,6 @@
 PurpleConversation *purple_conv_chat_get_conversation(const PurpleConvChat *chat);
 
 /**
- * Sets the list of users in the chat room.
- *
- * @note Calling this function will not update the display of the users.
- *       Please use purple_conv_chat_add_user(), purple_conv_chat_add_users(),
- *       purple_conv_chat_remove_user(), and purple_conv_chat_remove_users() instead.
- *
- * @param chat  The chat.
- * @param users The list of users.
- *
- * @return The list passed.
- *
- * @deprecated This function will be removed in 3.0.0.  You shouldn't be using it anyway.
- */
-GList *purple_conv_chat_set_users(PurpleConvChat *chat, GList *users);
-
-/**
  * Returns a list of users in the chat room.  The members of the list
  * are PurpleConvChatBuddy objects.
  *
@@ -1381,8 +1363,6 @@
  * @param message  The message to send with the invitation.
  * @param confirm  Prompt before sending the invitation. The user is always
  *                 prompted if either \a user or \a message is @c NULL.
- *
- * @since 2.6.0
  */
 void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user,
 		const char *message, gboolean confirm);
@@ -1442,8 +1422,6 @@
  * @return  A list of PurpleMenuAction items, harvested by the
  *          chat-extended-menu signal. The list and the menuaction
  *          items should be freed by the caller.
- *
- * @since 2.1.0
  */
 GList * purple_conversation_get_extended_menu(PurpleConversation *conv);
 
@@ -1457,8 +1435,6 @@
  *                message, if not @c NULL. It must be freed by the caller with g_free().
  *
  * @return  @c TRUE if the command was executed successfully, @c FALSE otherwise.
- *
- * @since 2.1.0
  */
 gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error);
 
--- a/libpurple/core.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/core.h	Mon Sep 26 14:57:21 2011 +0900
@@ -163,8 +163,6 @@
  *
  * @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);
 
@@ -196,8 +194,6 @@
  * @return A GHashTable with strings for keys and values.  This
  * hash table must not be freed and should not be modified.
  *
- * @since 2.1.0
- *
  */
 GHashTable* purple_core_get_ui_info(void);
 
--- a/libpurple/dbus-analyze-functions.py	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/dbus-analyze-functions.py	Mon Sep 26 14:57:21 2011 +0900
@@ -29,7 +29,11 @@
     # Similar to the above:
     "purple_account_set_register_callback",
     "purple_account_unregister",
-    "purple_connection_new_unregister",
+
+    # Similar to the above, again
+    "purple_menu_action_new",
+    "purple_menu_action_set_callback",
+    "purple_menu_action_get_callback",
 
     # These functions are excluded because they involve setting arbitrary
     # data via pointers for protocols and UIs.  This just won't work.
--- a/libpurple/dbus-server.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/dbus-server.h	Mon Sep 26 14:57:21 2011 +0900
@@ -173,8 +173,6 @@
 
 /**
  * Determines whether this instance owns the DBus service name
- *
- * @since 2.1.0
  */
 gboolean purple_dbus_is_owner(void);
 
--- a/libpurple/debug.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/debug.c	Mon Sep 26 14:57:21 2011 +0900
@@ -224,12 +224,5 @@
 		purple_debug_set_verbose(TRUE);
 
 	purple_prefs_add_none("/purple/debug");
-
-	/*
-	 * This pref is obsolete and no longer referenced anywhere. It only
-	 * survives here because it would be an API break if we removed it.
-	 * Remove this when we get to 3.0.0 :)
-	 */
-	purple_prefs_add_bool("/purple/debug/timestamps", TRUE);
 }
 
--- a/libpurple/debug.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/debug.h	Mon Sep 26 14:57:21 2011 +0900
@@ -161,8 +161,6 @@
  * plugins.
  *
  * @param verbose TRUE to enable verbose debugging or FALSE to disable it.
- *
- * @since 2.6.0
  */
 void purple_debug_set_verbose(gboolean verbose);
 
@@ -170,8 +168,6 @@
  * Check if verbose logging is enabled.
  *
  * @return TRUE if verbose debugging is enabled, FALSE if it is not.
- *
- * @since 2.6.0
  */
 gboolean purple_debug_is_verbose(void);
 
@@ -183,8 +179,6 @@
  * @param unsafe TRUE to enable debug logging of messages that could
  *        potentially contain passwords and other sensitive information.
  *        FALSE to disable it.
- *
- * @since 2.6.0
  */
 void purple_debug_set_unsafe(gboolean unsafe);
 
@@ -194,8 +188,6 @@
  * @return TRUE if the debug logging of all messages is enabled, FALSE
  *         if messages that could potentially contain passwords and other
  *         sensitive information are not logged.
- *
- * @since 2.6.0
  */
 gboolean purple_debug_is_unsafe(void);
 
--- a/libpurple/dnsquery.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/dnsquery.c	Mon Sep 26 14:57:21 2011 +0900
@@ -914,7 +914,7 @@
 }
 
 PurpleDnsQueryData *
-purple_dnsquery_a_account(PurpleAccount *account, const char *hostname, int port,
+purple_dnsquery_a(PurpleAccount *account, const char *hostname, int port,
 				PurpleDnsQueryConnectFunction callback, gpointer data)
 {
 	PurpleDnsQueryData *query_data;
@@ -944,13 +944,6 @@
 	return query_data;
 }
 
-PurpleDnsQueryData *
-purple_dnsquery_a(const char *hostname, int port,
-				PurpleDnsQueryConnectFunction callback, gpointer data)
-{
-	return purple_dnsquery_a_account(NULL, hostname, port, callback, data);
-}
-
 void
 purple_dnsquery_destroy(PurpleDnsQueryData *query_data)
 {
--- a/libpurple/dnsquery.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/dnsquery.h	Mon Sep 26 14:57:21 2011 +0900
@@ -88,7 +88,7 @@
 /**
  * Perform an asynchronous DNS query.
  *
- * @param account the account that the query is being done for (or NULL)
+ * @param account  The account that the query is being done for (or NULL)
  * @param hostname The hostname to resolve.
  * @param port     A port number which is stored in the struct sockaddr.
  * @param callback The callback function to call after resolving.
@@ -98,27 +98,8 @@
  *         a data structure that can be used to cancel the pending
  *         DNS query, if needed.
  *
- * @since 2.8.0
  */
-PurpleDnsQueryData *purple_dnsquery_a_account(PurpleAccount *account, const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_DNSQUERY_C_)
-/**
- * Perform an asynchronous DNS query.
- *
- * @param hostname The hostname to resolve.
- * @param port     A port number which is stored in the struct sockaddr.
- * @param callback The callback function to call after resolving.
- * @param data     Extra data to pass to the callback function.
- *
- * @return NULL if there was an error, otherwise return a reference to
- *         a data structure that can be used to cancel the pending
- *         DNS query, if needed.
- *
- * @deprecated Use purple_dnsquery_a_account instead
- */
-PurpleDnsQueryData *purple_dnsquery_a(const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data);
-#endif
+PurpleDnsQueryData *purple_dnsquery_a(PurpleAccount *account, const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data);
 
 /**
  * Cancel a DNS query and destroy the associated data structure.
--- a/libpurple/dnssrv.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/dnssrv.c	Mon Sep 26 14:57:21 2011 +0900
@@ -725,15 +725,7 @@
 #endif
 
 PurpleSrvTxtQueryData *
-purple_srv_resolve(const char *protocol, const char *transport,
-	const char *domain, PurpleSrvCallback cb, gpointer extradata)
-{
-	return purple_srv_resolve_account(NULL, protocol, transport, domain,
-			cb, extradata);
-}
-
-PurpleSrvTxtQueryData *
-purple_srv_resolve_account(PurpleAccount *account, const char *protocol,
+purple_srv_resolve(PurpleAccount *account, const char *protocol,
 	const char *transport, const char *domain, PurpleSrvCallback cb,
 	gpointer extradata)
 {
@@ -869,13 +861,7 @@
 #endif
 }
 
-PurpleSrvTxtQueryData *purple_txt_resolve(const char *owner,
-	const char *domain, PurpleTxtCallback cb, gpointer extradata)
-{
-	return purple_txt_resolve_account(NULL, owner, domain, cb, extradata);
-}
-
-PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account,
+PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account,
 	const char *owner, const char *domain, PurpleTxtCallback cb,
 	gpointer extradata)
 {
@@ -1006,18 +992,6 @@
 #endif
 }
 
-void
-purple_txt_cancel(PurpleSrvTxtQueryData *query_data)
-{
-	purple_srv_txt_query_destroy(query_data);
-}
-
-void
-purple_srv_cancel(PurpleSrvTxtQueryData *query_data)
-{
-	purple_srv_txt_query_destroy(query_data);
-}
-
 const gchar *
 purple_txt_response_get_content(PurpleTxtResponse *resp)
 {
--- a/libpurple/dnssrv.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/dnssrv.h	Mon Sep 26 14:57:21 2011 +0900
@@ -70,7 +70,7 @@
 
 	/** Called just before @a query_data is freed; this should cancel any
 	 *  further use of @a query_data the UI would make. Unneeded if
-	 *  #resolve_host is not implemented.
+	 *  #resolve is not implemented.
 	 */
 	void (*destroy)(PurpleSrvTxtQueryData *query_data);
 
@@ -99,86 +99,40 @@
 /**
  * Queries an SRV record.
  *
- * @param account the account that the query is being done for (or NULL)
- * @param protocol Name of the protocol (e.g. "sip")
+ * @param account   The account that the query is being done for (or NULL)
+ * @param protocol  Name of the protocol (e.g. "sip")
  * @param transport Name of the transport ("tcp" or "udp")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
+ * @param domain    Domain name to query (e.g. "blubb.com")
+ * @param cb        A callback which will be called with the results
  * @param extradata Extra data to be passed to the callback
  *
- * @since 2.8.0
+ * @return NULL if there was an error, otherwise return a reference to
+ *         a data structure that can be used to cancel the pending
+ *         DNS query, if needed.
  */
-PurpleSrvTxtQueryData *purple_srv_resolve_account(PurpleAccount *account, const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_DNSSRV_C_)
-/**
- * Queries an SRV record.
- *
- * @param protocol Name of the protocol (e.g. "sip")
- * @param transport Name of the transport ("tcp" or "udp")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
- * @param extradata Extra data to be passed to the callback
- *
- * @deprecated Use purple_srv_resolve_account instead
- */
-PurpleSrvTxtQueryData *purple_srv_resolve(const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata);
-#endif
-
-/**
- * Cancel an SRV or DNS query.
- *
- * @param query_data The request to cancel.
- *
- * @deprecated Use purple_srv_txt_query_destroy instead
- */
-void purple_srv_cancel(PurpleSrvTxtQueryData *query_data);
+PurpleSrvTxtQueryData *purple_srv_resolve(PurpleAccount *account, const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata);
 
 /**
  * Queries an TXT record.
  *
- * @param account the account that the query is being done for (or NULL)
- * @param owner Name of the protocol (e.g. "_xmppconnect")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
+ * @param account   The account that the query is being done for (or NULL)
+ * @param owner     Name of the protocol (e.g. "_xmppconnect")
+ * @param domain    Domain name to query (e.g. "blubb.com")
+ * @param cb        A callback which will be called with the results
  * @param extradata Extra data to be passed to the callback
  *
- * @since 2.8.0
+ * @return NULL if there was an error, otherwise return a reference to
+ *         a data structure that can be used to cancel the pending
+ *         DNS query, if needed.
  */
-PurpleSrvTxtQueryData *purple_txt_resolve_account(PurpleAccount *account, const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata);
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_DNSSRV_C_)
-/**
- * Queries an TXT record.
- *
- * @param owner Name of the protocol (e.g. "_xmppconnect")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
- * @param extradata Extra data to be passed to the callback
- *
- * @deprecated Use purple_txt_resolve_account instead
- *
- * @since 2.6.0
- */
-PurpleSrvTxtQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata);
-#endif
-
-/**
- * Cancel an TXT DNS query.
- *
- * @param query_data The request to cancel.
- * @since 2.6.0
- *
- * @deprecated Use purple_srv_txt_query_destroy instead
- */
-void purple_txt_cancel(PurpleSrvTxtQueryData *query_data);
+PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account, const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata);
 
 /**
  * Get the value of the current TXT record.
  *
  * @param response  The TXT response record
- * @returns The value of the current TXT record.
- * @since 2.6.0
+ *
+ * @return The value of the current TXT record.
  */
 const gchar *purple_txt_response_get_content(PurpleTxtResponse *response);
 
@@ -186,7 +140,6 @@
  * Destroy a TXT DNS response object.
  *
  * @param response The PurpleTxtResponse to destroy.
- * @since 2.6.0
  */
 void purple_txt_response_destroy(PurpleTxtResponse *response);
 
@@ -216,7 +169,7 @@
 PurpleSrvTxtQueryUiOps *purple_srv_txt_query_get_ui_ops(void);
 
 /**
- * Get the query from a PurpleDnsQueryData
+ * Get the query from a PurpleSrvTxtQueryData
  *
  * @param query_data The SRV/TXT query
  * @return The query.
@@ -224,7 +177,7 @@
 char *purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data);
 
 /**
- * Get the type from a PurpleDnsQueryData (TXT or SRV)
+ * Get the type from a PurpleSrvTxtQueryData (TXT or SRV)
  *
  * @param query_data The query
  * @return The query.
@@ -236,3 +189,4 @@
 #endif
 
 #endif /* _PURPLE_DNSSRV_H */
+
--- a/libpurple/eventloop.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/eventloop.h	Mon Sep 26 14:57:21 2011 +0900
@@ -145,7 +145,6 @@
 	 * #timeout_add.
 	 *
 	 * @see purple_timeout_add_seconds()
-	 * @since 2.1.0
 	 **/
 	guint (*timeout_add_seconds)(guint interval, GSourceFunc function,
 	                             gpointer data);
@@ -192,8 +191,6 @@
  * @param data		data to pass to @a function.
  * @return A handle to the timer which can be passed to
  *         purple_timeout_remove() to remove the timer.
- *
- * @since 2.1.0
  */
 guint purple_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data);
 
--- a/libpurple/ft.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/ft.c	Mon Sep 26 14:57:21 2011 +0900
@@ -220,7 +220,7 @@
 		purple_xfer_destroy(xfer);
 }
 
-static void
+void
 purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatusType status)
 {
 	g_return_if_fail(xfer != NULL);
@@ -720,6 +720,20 @@
 	purple_xfer_unref(xfer);
 }
 
+int purple_xfer_get_fd(PurpleXfer *xfer)
+{
+	g_return_val_if_fail(xfer != NULL, 0);
+
+	return xfer->fd;
+}
+
+int purple_xfer_get_watcher(PurpleXfer *xfer)
+{
+	g_return_val_if_fail(xfer != NULL, 0);
+
+	return xfer->watcher;
+}
+
 PurpleXferType
 purple_xfer_get_type(const PurpleXfer *xfer)
 {
@@ -751,9 +765,8 @@
 	return xfer->status;
 }
 
-/* FIXME: Rename with cancelled for 3.0.0. */
 gboolean
-purple_xfer_is_canceled(const PurpleXfer *xfer)
+purple_xfer_is_cancelled(const PurpleXfer *xfer)
 {
 	g_return_val_if_fail(xfer != NULL, TRUE);
 
@@ -864,6 +877,20 @@
 	return xfer->end_time;
 }
 
+void purple_xfer_set_fd(PurpleXfer *xfer, int fd)
+{
+	g_return_if_fail(xfer != NULL);
+
+	xfer->fd = fd;
+}
+
+void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher)
+{
+	g_return_if_fail(xfer != NULL);
+
+	xfer->watcher = watcher;
+}
+
 void
 purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed)
 {
@@ -947,6 +974,14 @@
 }
 
 void
+purple_xfer_set_local_port(PurpleXfer *xfer, unsigned int local_port)
+{
+	g_return_if_fail(xfer != NULL);
+
+	xfer->local_port = local_port;
+}
+
+void
 purple_xfer_set_bytes_sent(PurpleXfer *xfer, size_t bytes_sent)
 {
 	g_return_if_fail(xfer != NULL);
@@ -1404,13 +1439,6 @@
 
 	purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_STARTED);
 
-	/*
-	 * FIXME 3.0.0 -- there's too much broken code depending on fd == 0
-	 * meaning "don't use a real fd"
-	 */
-	if (fd == 0)
-		fd = -1;
-
 	if (type == PURPLE_XFER_RECEIVE) {
 		cond = PURPLE_INPUT_READ;
 
--- a/libpurple/ft.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/ft.h	Mon Sep 26 14:57:21 2011 +0900
@@ -87,7 +87,6 @@
 	 *
 	 * @return size if the write was successful, or a value between 0 and
 	 *         size on error.
-	 * @since 2.6.0
 	 */
 	gssize (*ui_write)(PurpleXfer *xfer, const guchar *buffer, gssize size);
 
@@ -102,7 +101,6 @@
 	 * @returns The amount of data in the buffer, 0 if nothing is available,
 	 *          and a negative value if an error occurred and the transfer
 	 *          should be cancelled (libpurple will cancel).
-	 * @since 2.6.0
 	 */
 	gssize (*ui_read)(PurpleXfer *xfer, guchar **buffer, gssize size);
 
@@ -115,8 +113,6 @@
 	 * @param xfer    The file transfer structure
 	 * @param buffer  A pointer to the beginning of the unwritten data.
 	 * @param size    The amount of unwritten data.
-	 *
-	 * @since 2.6.0
 	 */
 	void (*data_not_sent)(PurpleXfer *xfer, const guchar *buffer, gsize size);
 
@@ -266,6 +262,24 @@
 void purple_xfer_request_denied(PurpleXfer *xfer);
 
 /**
+ * Returns the socket file descriptor.
+ *
+ * @param xfer The file transfer.
+ *
+ * @return The socket file descriptor.
+ */
+int purple_xfer_get_fd(PurpleXfer *xfer);
+
+/**
+ * Returns the Watcher for the transfer.
+ *
+ * @param xfer The file transfer.
+ *
+ * @return The watcher.
+ */
+int purple_xfer_get_watcher(PurpleXfer *xfer);
+
+/**
  * Returns the type of file transfer.
  *
  * @param xfer The file transfer.
@@ -289,8 +303,6 @@
  * @param xfer The file transfer.
  *
  * @return The name of the remote user.
- *
- * @since 2.1.0
  */
 const char *purple_xfer_get_remote_user(const PurpleXfer *xfer);
 
@@ -309,9 +321,8 @@
  * @param xfer The file transfer.
  *
  * @return Whether or not the transfer was cancelled.
- * FIXME: This should be renamed using cancelled for 3.0.0.
  */
-gboolean purple_xfer_is_canceled(const PurpleXfer *xfer);
+gboolean purple_xfer_is_cancelled(const PurpleXfer *xfer);
 
 /**
  * Returns the completed state for a file transfer.
@@ -411,7 +422,6 @@
  * @param xfer  The file transfer.
  *
  * @return The time when the transfer started.
- * @since 2.4.0
  */
 time_t purple_xfer_get_start_time(const PurpleXfer *xfer);
 
@@ -421,11 +431,26 @@
  * @param xfer  The file transfer.
  *
  * @return The time when the transfer ended.
- * @since 2.4.0
  */
 time_t purple_xfer_get_end_time(const PurpleXfer *xfer);
 
 /**
+ * Sets the socket file descriptor.
+ *
+ * @param xfer      The file transfer.
+ * @param fd        The file descriptor.
+ */
+void purple_xfer_set_fd(PurpleXfer *xfer, int fd);
+
+/**
+ * Sets the watcher for the file transfer.
+ *
+ * @param xfer      The file transfer.
+ * @param watcher   The watcher.
+ */
+void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher);
+
+/**
  * Sets the completed state for the file transfer.
  *
  * @param xfer      The file transfer.
@@ -434,6 +459,14 @@
 void purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed);
 
 /**
+ * Sets the current status for the file transfer.
+ *
+ * @param xfer      The file transfer.
+ * @param status    The current status.
+ */
+void purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatusType status);
+
+/**
  * Sets the filename for the file transfer.
  *
  * @param xfer     The file transfer.
@@ -466,6 +499,14 @@
 void purple_xfer_set_size(PurpleXfer *xfer, size_t size);
 
 /**
+ * Sets the local port of the file transfer.
+ *
+ * @param xfer          The file transfer.
+ * @param local_port    The local port.
+ */
+void purple_xfer_set_local_port(PurpleXfer *xfer, unsigned int local_port);
+
+/**
  * Sets the current working position in the active file transfer.  This
  * can be used to jump backward in the file if the protocol detects
  * that some bit of data needs to be resent or has been sent twice.
@@ -596,11 +637,8 @@
  * file receive transfer. On send, @a fd must be specified, and
  * @a ip and @a port are ignored.
  *
- * Prior to libpurple 2.6.0, passing '0' to @a fd was special-cased to
- * allow the protocol plugin to facilitate the file transfer itself. As of
- * 2.6.0, this is supported (for backward compatibility), but will be
- * removed in libpurple 3.0.0. If a prpl detects that the running libpurple
- * is running 2.6.0 or higher, it should use the invalid fd '-1'.
+ * Passing @a fd as '-1' is a special-case and indicates to the
+ * protocol plugin to facilitate the file transfer itself.
  *
  * @param xfer The file transfer.
  * @param fd   The file descriptor for the socket.
@@ -677,8 +715,6 @@
  * read/write/data_not_sent UI ops.
  *
  * @param xfer The file transfer which is ready.
- *
- * @since 2.6.0
  */
 void purple_xfer_ui_ready(PurpleXfer *xfer);
 
@@ -688,8 +724,6 @@
  * ops and cannot/does not provide a raw fd to the core.
  *
  * @param xfer The file transfer which is ready.
- *
- * @since 2.6.0
  */
 void purple_xfer_prpl_ready(PurpleXfer *xfer);
 
@@ -700,7 +734,6 @@
  * @param len  If not @c NULL, the length of the thumbnail data returned
  *             will be set in the location pointed to by this.
  * @return The thumbnail data, or NULL if there is no thumbnail
- * @since 2.7.0
  */
 gconstpointer purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len);
 
@@ -709,7 +742,6 @@
  *
  * @param xfer The file transfer to get the mimetype for
  * @return The mimetype of the thumbnail, or @c NULL if not thumbnail is set
- * @since 2.7.0
  */
 const gchar *purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer);
 
@@ -721,7 +753,6 @@
  * @param thumbnail A pointer to the thumbnail data, this will be copied
  * @param size The size in bytes of the passed in thumbnail data
  * @param mimetype The mimetype of the generated thumbnail
- * @since 2.7.0
  */
 void purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
 	gsize size, const gchar *mimetype);
@@ -733,7 +764,6 @@
  * @param xfer The file transfer to create a thumbnail for
  * @param formats A comma-separated list of mimetypes for image formats
  *	 	  the protocols can use for thumbnails.
- * @since 2.7.0
  */
 void purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats);
 
@@ -741,9 +771,7 @@
  * Sets the protocol data for a file transfer.
  *
  * @param xfer			The file transfer.
- * @param protol_data	The protocol data to set for the file transfer.
- *
- * @since 3.0.0
+ * @param proto_data	The protocol data to set for the file transfer.
  */
 void purple_xfer_set_protocol_data(PurpleXfer *xfer, gpointer proto_data);
  
@@ -753,8 +781,6 @@
  * @param xfer			The file transfer.
  *
  * @return The protocol data for the file transfer.
- *
- * @since 3.0.0
  */
 gpointer purple_xfer_get_protocol_data(const PurpleXfer *xfer);
 
--- a/libpurple/imgstore.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/imgstore.h	Mon Sep 26 14:57:21 2011 +0900
@@ -68,7 +68,6 @@
  * @param path  The path to the image.
  *
  * @return  The stored image.
- * @since 2.X.X
  */
 PurpleStoredImage *
 purple_imgstore_new_from_file(const char *path);
--- a/libpurple/log.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/log.h	Mon Sep 26 14:57:21 2011 +0900
@@ -301,8 +301,6 @@
  * @param name                The name of the log
  * @param account             The account
  * @return                    The activity score
- *
- * @since 2.6.0
  */
 int purple_log_get_activity_score(PurpleLogType type, const char *name, PurpleAccount *account);
 
--- a/libpurple/media-gst.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/media-gst.h	Mon Sep 26 14:57:21 2011 +0900
@@ -81,8 +81,6 @@
  * Gets the element type's GType.
  *
  * @return The element type's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_element_type_get_type(void);
 
@@ -90,8 +88,6 @@
  * Gets the element info's GType.
  *
  * @return The element info's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_element_info_get_type(void);
 
@@ -102,8 +98,6 @@
  * @param sess_id The session id of the session to get the source from.
  *
  * @return The source retrieved.
- *
- * @since 2.6.0
  */
 GstElement *purple_media_get_src(PurpleMedia *media, const gchar *sess_id);
 
@@ -115,8 +109,6 @@
  * @param participant Optionally, the participant of the stream to get the tee from.
  *
  * @return The GstTee element from the chosen session/stream.
- *
- * @since 2.6.0
  */
 GstElement *purple_media_get_tee(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant);
@@ -128,8 +120,6 @@
  * @param manager The media manager to get the pipeline from.
  *
  * @return The pipeline.
- *
- * @since 2.6.0
  */
 GstElement *purple_media_manager_get_pipeline(PurpleMediaManager *manager);
 
@@ -141,8 +131,6 @@
  * @param media The media call this element is requested for.
  * @param session_id The id of the session this element is requested for or NULL.
  * @param participant The remote user this element is requested for or NULL.
- *
- * @since 2.6.0
  */
 GstElement *purple_media_manager_get_element(PurpleMediaManager *manager,
 		PurpleMediaSessionType type, PurpleMedia *media,
@@ -167,8 +155,6 @@
  *
  * @param manager The media manager to set the media formats.
  * @param caps Set of allowed media formats.
- *
- * @since 2.8.0
  */
 void purple_media_manager_set_video_caps(PurpleMediaManager *manager,
 		GstCaps *caps);
@@ -179,8 +165,6 @@
  * @param manager The media manager to get the media formats from.
  *
  * @return @c GstCaps limiting the video source's formats.
- *
- * @since 2.8.0
  */
 GstCaps *purple_media_manager_get_video_caps(PurpleMediaManager *manager);
 
--- a/libpurple/media.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/media.h	Mon Sep 26 14:57:21 2011 +0900
@@ -57,8 +57,6 @@
  * Gets the media class's GType
  *
  * @return The media class's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_get_type(void);
 
@@ -68,8 +66,6 @@
  * @param media The media session from which to retrieve session IDs.
  *
  * @return GList of session IDs. The caller must free the list.
- *
- * @since 2.6.0
  */
 GList *purple_media_get_session_ids(PurpleMedia *media);
 
@@ -79,8 +75,6 @@
  * @param media The media session to retrieve the account from.
  *
  * @return The account retrieved.
- *
- * @since 2.6.0
  */
 PurpleAccount *purple_media_get_account(PurpleMedia *media);
 
@@ -90,8 +84,6 @@
  * @param media The media session to retrieve the prpl data from.
  *
  * @return The prpl data retrieved.
- *
- * @since 2.6.0
  */
 gpointer purple_media_get_prpl_data(PurpleMedia *media);
 
@@ -100,8 +92,6 @@
  *
  * @param media The media session to set the prpl data on.
  * @param prpl_data The data to set on the media session.
- *
- * @since 2.6.0
  */
 void purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data);
 
@@ -111,8 +101,6 @@
  * @param media The media object to set the state on.
  * @param error The format of the error message to send in the signal.
  * @param ... The arguments to plug into the format.
- *
- * @since 2.6.0
  */
 void purple_media_error(PurpleMedia *media, const gchar *error, ...);
 
@@ -122,8 +110,6 @@
  * @param media The media object with which to end streams.
  * @param session_id The session to end streams on.
  * @param participant The participant to end streams with.
- *
- * @since 2.6.0
  */
 void purple_media_end(PurpleMedia *media, const gchar *session_id,
 		const gchar *participant);
@@ -136,8 +122,6 @@
  * @param session_id The id of the session of the stream being signaled.
  * @param participant The participant of the stream being signaled.
  * @param local TRUE if the info originated locally, FALSE if on the remote end.
- *
- * @since 2.6.0
  */
 void purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
 		const gchar *session_id, const gchar *participant,
@@ -158,8 +142,6 @@
  * @param media The media object to set the parameters on.
  * @param num_params The number of parameters to pass
  * @param params Array of @c GParameter to pass
- *
- * @since 2.8.0
  */
 void purple_media_set_params(PurpleMedia *media,
 		guint num_params, GParameter *params);
@@ -172,8 +154,6 @@
  * @param media The media object
  *
  * @return NULL-terminated array of names of supported parameters.
- *
- * @since 2.8.0
  */
 const gchar **purple_media_get_available_params(PurpleMedia *media);
 
@@ -184,8 +164,6 @@
  * @param param name of parameter
  *
  * @return @c TRUE if backend recognizes the parameter, @c FALSE otherwise.
- *
- * @since 2.8.0
  */
 gboolean purple_media_param_is_supported(PurpleMedia *media, const gchar *param);
 
@@ -205,8 +183,6 @@
  * @param params The parameters to pass to Farsight.
  *
  * @return @c TRUE The stream was added successfully, @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
 		const gchar *who, PurpleMediaSessionType type,
@@ -220,8 +196,6 @@
  * @param sess_id The session id of the session to get the type from.
  *
  * @return The retreived session type.
- *
- * @since 2.6.0
  */
 PurpleMediaSessionType purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id);
 
@@ -231,8 +205,6 @@
  * @param media The media object to get the manager instance from.
  *
  * @return The PurpleMediaManager instance retrieved.
- *
- * @since 2.6.0
  */
 struct _PurpleMediaManager *purple_media_get_manager(PurpleMedia *media);
 
@@ -243,8 +215,6 @@
  * @param sess_id The session id of the session to get the codecs from.
  *
  * @return The retreieved codecs.
- *
- * @since 2.6.0
  */
 GList *purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id);
 
@@ -255,8 +225,6 @@
  * @param sess_id The session id of the session find the stream in.
  * @param participant The name of the remote user to add the candidates for.
  * @param remote_candidates The remote candidates to add.
- *
- * @since 2.6.0
  */
 void purple_media_add_remote_candidates(PurpleMedia *media,
 					const gchar *sess_id,
@@ -269,8 +237,6 @@
  * @param media The media object to find the session in.
  * @param sess_id The session id of the session to find the stream in.
  * @param participant The name of the remote user to get the candidates from.
- *
- * @since 2.6.0
  */
 GList *purple_media_get_local_candidates(PurpleMedia *media,
 					 const gchar *sess_id,
@@ -285,8 +251,6 @@
  *                    from.
  *
  * @return The active candidates retrieved.
- *
- * @since 2.8.0
  */
 GList *purple_media_get_active_local_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant);
@@ -300,8 +264,6 @@
  *                    from.
  *
  * @return The remote candidates retrieved.
- *
- * @since 2.8.0
  */
 GList *purple_media_get_active_remote_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant);
@@ -315,8 +277,6 @@
  * @param codecs The list of remote codecs to set.
  *
  * @return @c TRUE The codecs were set successfully, or @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
 					const gchar *participant, GList *codecs);
@@ -329,8 +289,6 @@
  * @param participant The remote user to check for.
  *
  * @return @c TRUE All streams for the given session_id/participant combination have candidates prepared, @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_candidates_prepared(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant);
@@ -343,8 +301,6 @@
  * @param codec The codec to set the session to stream.
  *
  * @return @c TRUE The codec was successfully changed, or @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec);
 
@@ -355,8 +311,6 @@
  * @param sess_id The session id of the session to check.
  *
  * @return @c TRUE The codecs are ready, or @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id);
 
@@ -368,8 +322,6 @@
  * @param participant The participant of the stream to check.
  *
  * @return TRUE if the local user is the stream's initator, else FALSE.
- *
- * @since 2.6.0
  */
 gboolean purple_media_is_initiator(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant);
@@ -382,8 +334,6 @@
  * @param participant The participant to check.
  *
  * @return @c TRUE The selected streams have been accepted, or @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
 		const gchar *participant);
@@ -394,8 +344,6 @@
  * @param media The media object the sessions are in.
  * @param session_id The session to select (if any).
  * @param level The level to set the volume to.
- *
- * @since 2.6.0
  */
 void purple_media_set_input_volume(PurpleMedia *media, const gchar *session_id, double level);
 
@@ -406,8 +354,6 @@
  * @param session_id The session to limit the streams to (if any).
  * @param participant The participant to limit the streams to (if any).
  * @param level The level to set the volume to.
- *
- * @since 2.6.0
  */
 void purple_media_set_output_volume(PurpleMedia *media, const gchar *session_id,
 		const gchar *participant, double level);
@@ -421,8 +367,6 @@
  * @param window_id The window id use for embedding the video in.
  *
  * @return An id to reference the output window.
- *
- * @since 2.6.0
  */
 gulong purple_media_set_output_window(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant,
@@ -432,8 +376,6 @@
  * Removes all output windows from a given media session.
  *
  * @param media The instance to remove all output windows from.
- *
- * @since 2.6.0
  */
 void purple_media_remove_output_windows(PurpleMedia *media);
 
--- a/libpurple/media/backend-fs2.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/media/backend-fs2.h	Mon Sep 26 14:57:21 2011 +0900
@@ -50,8 +50,6 @@
  * Gets the type of the Farsight 2 media backend object.
  *
  * @return The Farsight 2 media backend's GType
- *
- * @since 2.7.0
  */
 GType purple_media_backend_fs2_get_type(void);
 
--- a/libpurple/media/backend-iface.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/media/backend-iface.h	Mon Sep 26 14:57:21 2011 +0900
@@ -77,8 +77,6 @@
  * Gets the media backend's GType.
  *
  * @return The media backend's GType.
- *
- * @since 2.7.0
  */
 GType purple_media_backend_get_type(void);
 
@@ -95,8 +93,6 @@
  * @param params The additional parameters to pass when creating the stream.
  *
  * @return True if the stream was successfully created, othewise False.
- *
- * @since 2.7.0
  */
 gboolean purple_media_backend_add_stream(PurpleMediaBackend *self,
 		const gchar *sess_id, const gchar *who,
@@ -111,8 +107,6 @@
  * @param sess_id The session id associated with the stream.
  * @param participant The participant associated with the stream.
  * @param remote_candidates The list of remote candidates to add.
- *
- * @since 2.7.0
  */
 void purple_media_backend_add_remote_candidates(PurpleMediaBackend *self,
 		const gchar *sess_id, const gchar *participant,
@@ -128,8 +122,6 @@
  * @param sess_id The session id of the session to check.
  *
  * @return True if the codecs are ready, otherwise False.
- *
- * @since 2.7.0
  */
 gboolean purple_media_backend_codecs_ready(PurpleMediaBackend *self,
 		const gchar *sess_id);
@@ -144,8 +136,6 @@
  * @param sess_id The session id of the session to use.
  *
  * @return The codec intersection list.
- *
- * @since 2.7.0
  */
 GList *purple_media_backend_get_codecs(PurpleMediaBackend *self,
 		const gchar *sess_id);
@@ -158,8 +148,6 @@
  * @param particilant The participant associated with the stream.
  *
  * @return The list of local candidates.
- *
- * @since 2.7.0
  */
 GList *purple_media_backend_get_local_candidates(PurpleMediaBackend *self,
 		const gchar *sess_id, const gchar *participant);
@@ -173,8 +161,6 @@
  * @param codecs The list of remote codecs to set.
  *
  * @return True if the remote codecs were set successfully, otherwise False.
- *
- * @since 2.7.0
  */
 gboolean purple_media_backend_set_remote_codecs(PurpleMediaBackend *self,
 		const gchar *sess_id, const gchar *participant,
@@ -188,8 +174,6 @@
  * @param codec The codec to set.
  *
  * @return True if set successfully, otherwise False.
- *
- * @since 2.7.0
  */
 gboolean purple_media_backend_set_send_codec(PurpleMediaBackend *self,
 		const gchar *sess_id, PurpleMediaCodec *codec);
@@ -200,8 +184,6 @@
  * @param self The media backend to set the parameters on.
  * @param num_params The number of parameters to pass to backend
  * @param params Array of @c GParameter to pass to backend
- *
- * @since 2.8.0
  */
 void purple_media_backend_set_params(PurpleMediaBackend *self,
 		guint num_params, GParameter *params);
@@ -214,8 +196,6 @@
  * @param self The media backend
  *
  * @return NULL-terminated array of names of supported parameters.
- *
- * @since 2.8.0
  */
 const gchar **purple_media_backend_get_available_params(PurpleMediaBackend *self);
 
--- a/libpurple/media/candidate.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/media/candidate.h	Mon Sep 26 14:57:21 2011 +0900
@@ -47,8 +47,6 @@
  * Gets the type of the media candidate structure.
  *
  * @return The media canditate's GType
- *
- * @since 2.6.0
  */
 GType purple_media_candidate_get_type(void);
 
@@ -63,8 +61,6 @@
  * @param port The network port.
  *
  * @return The newly created PurpleMediaCandidate instance.
- *
- * @since 2.6.0
  */
 PurpleMediaCandidate *purple_media_candidate_new(
 		const gchar *foundation, guint component_id,
@@ -78,8 +74,6 @@
  * @param candidate The candidate to copy.
  *
  * @return The copy of the PurpleMediaCandidate.
- *
- * @since 2.7.0
  */
 PurpleMediaCandidate *purple_media_candidate_copy(
 		PurpleMediaCandidate *candidate);
@@ -90,8 +84,6 @@
  * @param candidates The list of candidates to be copied.
  *
  * @return The copy of the GList.
- *
- * @since 2.6.0
  */
 GList *purple_media_candidate_list_copy(GList *candidates);
 
@@ -99,8 +91,6 @@
  * Frees a GList of PurpleMediaCandidate and its contents.
  *
  * @param candidates The list of candidates to be freed.
- *
- * @since 2.6.0
  */
 void purple_media_candidate_list_free(GList *candidates);
 
@@ -110,8 +100,6 @@
  * @param candidate The candidate to get the foundation from.
  *
  * @return The foundation.
- *
- * @since 2.6.0
  */
 gchar *purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate);
 
@@ -121,8 +109,6 @@
  * @param candidate The candidate to get the compnent id from.
  *
  * @return The component id.
- *
- * @since 2.6.0
  */
 guint purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate);
 
@@ -132,8 +118,6 @@
  * @param candidate The candidate to get the IP address from.
  *
  * @return The IP address.
- *
- * @since 2.6.0
  */
 gchar *purple_media_candidate_get_ip(PurpleMediaCandidate *candidate);
 
@@ -143,8 +127,6 @@
  * @param candidate The candidate to get the port from.
  *
  * @return The port.
- *
- * @since 2.6.0
  */
 guint16 purple_media_candidate_get_port(PurpleMediaCandidate *candidate);
 
@@ -156,8 +138,6 @@
  * @param candidate The candidate to get the base IP address from.
  *
  * @return The base IP address.
- *
- * @since 2.6.0
  */
 gchar *purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate);
 
@@ -169,8 +149,6 @@
  * @param candidate The candidate to get the base port.
  *
  * @return The base port.
- *
- * @since 2.6.0
  */
 guint16 purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate);
 
@@ -180,8 +158,6 @@
  * @param candidate The candidate to get the protocol from.
  *
  * @return The protocol.
- *
- * @since 2.6.0
  */
 PurpleMediaNetworkProtocol purple_media_candidate_get_protocol(
 		PurpleMediaCandidate *candidate);
@@ -192,8 +168,6 @@
  * @param candidate The candidate to get the priority from.
  *
  * @return The priority.
- *
- * @since 2.6.0
  */
 guint32 purple_media_candidate_get_priority(PurpleMediaCandidate *candidate);
 
@@ -203,8 +177,6 @@
  * @param candidate The candidate to get the candidate type from.
  *
  * @return The candidate type.
- *
- * @since 2.6.0
  */
 PurpleMediaCandidateType purple_media_candidate_get_candidate_type(
 		PurpleMediaCandidate *candidate);
@@ -217,8 +189,6 @@
  * @param The candidate to get the username from.
  *
  * @return The username.
- *
- * @since 2.6.0
  */
 gchar *purple_media_candidate_get_username(PurpleMediaCandidate *candidate);
 
@@ -230,8 +200,6 @@
  * @param The candidate to get the password from.
  *
  * @return The password.
- *
- * @since 2.6.0
  */
 gchar *purple_media_candidate_get_password(PurpleMediaCandidate *candidate);
 
@@ -241,8 +209,6 @@
  * @param The candidate to get the TTL from.
  *
  * @return The TTL.
- *
- * @since 2.6.0
  */
 guint purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate);
 
--- a/libpurple/media/codec.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/media/codec.h	Mon Sep 26 14:57:21 2011 +0900
@@ -50,8 +50,6 @@
  * Gets the type of the media codec structure.
  *
  * @return The media codec's GType
- *
- * @since 2.6.0
  */
 GType purple_media_codec_get_type(void);
 
@@ -64,8 +62,6 @@
  * @param clock_rate The clock rate this codec encodes at, if applicable.
  *
  * @return The newly created PurpleMediaCodec.
- *
- * @since 2.6.0
  */
 PurpleMediaCodec *purple_media_codec_new(int id, const char *encoding_name,
 		PurpleMediaSessionType media_type, guint clock_rate);
@@ -76,8 +72,6 @@
  * @param The codec to get the id from.
  *
  * @return The codec id.
- *
- * @since 2.6.0
  */
 guint purple_media_codec_get_id(PurpleMediaCodec *codec);
 
@@ -87,8 +81,6 @@
  * @param The codec to get the encoding name from.
  *
  * @return The encoding name.
- *
- * @since 2.6.0
  */
 gchar *purple_media_codec_get_encoding_name(PurpleMediaCodec *codec);
 
@@ -98,8 +90,6 @@
  * @param The codec to get the clock rate from.
  *
  * @return The clock rate.
- *
- * @since 2.6.0
  */
 guint purple_media_codec_get_clock_rate(PurpleMediaCodec *codec);
 
@@ -109,8 +99,6 @@
  * @param The codec to get the number of channels from.
  *
  * @return The number of channels.
- *
- * @since 2.6.0
  */
 guint purple_media_codec_get_channels(PurpleMediaCodec *codec);
 
@@ -123,8 +111,6 @@
  *
  * @return The list of optional parameters. The list is owned by the codec and
  *         should not be freed.
- *
- * @since 2.6.0
  */
 GList *purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec);
 
@@ -134,8 +120,6 @@
  * @param codec The codec to add the parameter to.
  * @param name The name of the parameter to add.
  * @param value The value of the parameter to add.
- *
- * @since 2.6.0
  */
 void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
 		const gchar *name, const gchar *value);
@@ -145,8 +129,6 @@
  *
  * @param codec The codec to remove the parameter from.
  * @param param A pointer to the parameter to remove.
- *
- * @since 2.6.0
  */
 void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
 		PurpleKeyValuePair *param);
@@ -159,8 +141,6 @@
  * @param value The value to search for or NULL.
  *
  * @return The value found or NULL.
- *
- * @since 2.6.0
  */
 PurpleKeyValuePair *purple_media_codec_get_optional_parameter(
 		PurpleMediaCodec *codec, const gchar *name,
@@ -172,8 +152,6 @@
  * @param codec The codec to copy.
  *
  * @return The copy of the codec.
- *
- * @since 2.7.0
  */
 PurpleMediaCodec *purple_media_codec_copy(PurpleMediaCodec *codec);
 
@@ -183,8 +161,6 @@
  * @param codecs The list of codecs to be copied.
  *
  * @return The copy of the GList.
- *
- * @since 2.6.0
  */
 GList *purple_media_codec_list_copy(GList *codecs);
 
@@ -192,8 +168,6 @@
  * Frees a GList of PurpleMediaCodec and its contents.
  *
  * @param codecs The list of codecs to be freed.
- *
- * @since 2.6.0
  */
 void purple_media_codec_list_free(GList *codecs);
 
@@ -203,8 +177,6 @@
  * @param codec The codec to create the string of.
  *
  * @return The new string representation.
- *
- * @since 2.6.0
  */
 gchar *purple_media_codec_to_string(const PurpleMediaCodec *codec);
 
--- a/libpurple/media/enum-types.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/media/enum-types.h	Mon Sep 26 14:57:21 2011 +0900
@@ -107,8 +107,6 @@
  * Gets the media candidate type's GType
  *
  * @return The media candidate type's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_candidate_type_get_type(void);
 
@@ -116,8 +114,6 @@
  * Gets the type of the media caps flags
  *
  * @return The media caps flags' GType
- *
- * @since 2.7.0
  */
 GType purple_media_caps_get_type(void);
 
@@ -125,8 +121,6 @@
  * Gets the type of the info type enum
  *
  * @return The info type enum's GType
- *
- * @since 2.6.0
  */
 GType purple_media_info_type_get_type(void);
 
@@ -134,8 +128,6 @@
  * Gets the media network protocol's GType
  *
  * @return The media network protocol's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_network_protocol_get_type(void);
 
@@ -143,8 +135,6 @@
  * Gets the media session type's GType
  *
  * @return The media session type's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_session_type_get_type(void);
 
@@ -152,8 +142,6 @@
  * Gets the type of the state-changed enum
  *
  * @return The state-changed enum's GType
- *
- * @since 2.6.0
  */
 GType purple_media_state_changed_get_type(void);
 
--- a/libpurple/mediamanager.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/mediamanager.h	Mon Sep 26 14:57:21 2011 +0900
@@ -60,8 +60,6 @@
  * Gets the media manager's GType.
  *
  * @return The media manager's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_manager_get_type(void);
 
@@ -69,8 +67,6 @@
  * Gets the "global" media manager object. It's created if it doesn't already exist.
  *
  * @return The "global" instance of the media manager object.
- *
- * @since 2.6.0
  */
 PurpleMediaManager *purple_media_manager_get(void);
 
@@ -84,8 +80,6 @@
  * @param initiator TRUE if the local user is the initiator of this media call, FALSE otherwise.
  *
  * @return A newly created media session.
- *
- * @since 2.6.0
  */
 PurpleMedia *purple_media_manager_create_media(PurpleMediaManager *manager,
 						PurpleAccount *account,
@@ -99,8 +93,6 @@
  * @param manager The media manager to get all of the sessions from.
  *
  * @return A list of all the media sessions.
- *
- * @since 2.6.0
  */
 GList *purple_media_manager_get_media(PurpleMediaManager *manager);
 
@@ -111,8 +103,6 @@
  * @param account The account the sessions are on.
  *
  * @return A list of the media sessions on the given account.
- *
- * @since 2.6.0
  */
 GList *purple_media_manager_get_media_by_account(
 		PurpleMediaManager *manager, PurpleAccount *account);
@@ -122,8 +112,6 @@
  *
  * @param manager The media manager to remove the media session from.
  * @param media The media session to remove.
- *
- * @since 2.6.0
  */
 void
 purple_media_manager_remove_media(PurpleMediaManager *manager,
@@ -140,8 +128,6 @@
  * @param participant The participant the output windows are registered with.
  *
  * @return TRUE if it succeeded, FALSE if it failed.
- *
- * @since 2.6.0
  */
 gboolean purple_media_manager_create_output_window(
 		PurpleMediaManager *manager, PurpleMedia *media,
@@ -157,8 +143,6 @@
  * @param window_id The window ID to embed the video in.
  *
  * @return A unique ID to the registered output window, 0 if it failed.
- *
- * @since 2.6.0
  */
 gulong purple_media_manager_set_output_window(PurpleMediaManager *manager,
 		PurpleMedia *media, const gchar *session_id,
@@ -171,8 +155,6 @@
  * @param output_window_id The ID of the output window.
  *
  * @return TRUE if it found the output window and was successful, else FALSE.
- *
- * @since 2.6.0
  */
 gboolean purple_media_manager_remove_output_window(
 		PurpleMediaManager *manager, gulong output_window_id);
@@ -184,8 +166,6 @@
  * @param media The media instance the output windows were registered for.
  * @param session_id The session the output windows were registered for.
  * @param participant The participant the output windows were registered for.
- *
- * @since 2.6.0
  */
 void purple_media_manager_remove_output_windows(
 		PurpleMediaManager *manager, PurpleMedia *media,
@@ -196,8 +176,6 @@
  *
  * @param manager The manager to set the caps on.
  * @param caps The caps to set.
- *
- * @since 2.6.0
  */
 void purple_media_manager_set_ui_caps(PurpleMediaManager *manager,
 		PurpleMediaCaps caps);
@@ -208,8 +186,6 @@
  * @param manager The manager to get caps from.
  *
  * @return caps The caps retrieved.
- *
- * @since 2.6.0
  */
 PurpleMediaCaps purple_media_manager_get_ui_caps(PurpleMediaManager *manager);
 
@@ -218,8 +194,6 @@
  *
  * @param manager The manager to set the caps on.
  * @param backend_type The media backend type to use.
- *
- * @since 2.7.0
  */
 void purple_media_manager_set_backend_type(PurpleMediaManager *manager,
 		GType backend_type);
@@ -230,8 +204,6 @@
  * @param manager The manager to get the media backend type from.
  *
  * @return The type of media backend type media objects will use.
- *
- * @since 2.7.0
  */
 GType purple_media_manager_get_backend_type(PurpleMediaManager *manager);
 
--- a/libpurple/network.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/network.c	Mon Sep 26 14:57:21 2011 +0900
@@ -391,14 +391,9 @@
 	return FALSE;
 }
 
-static gboolean listen_map_external = TRUE;
-void purple_network_listen_map_external(gboolean map_external)
-{
-	listen_map_external = map_external;
-}
-
 static PurpleNetworkListenData *
-purple_network_do_listen(unsigned short port, int socket_family, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
+purple_network_do_listen(unsigned short port, int socket_family, int socket_type, gboolean map_external,
+                             PurpleNetworkListenCallback cb, gpointer cb_data)
 {
 	int listenfd = -1;
 	int flags;
@@ -503,7 +498,7 @@
 	listen_data->cb_data = cb_data;
 	listen_data->socket_type = socket_type;
 
-	if (!purple_socket_speaks_ipv4(listenfd) || !listen_map_external ||
+	if (!purple_socket_speaks_ipv4(listenfd) || !map_external ||
 			!purple_prefs_get_bool("/purple/network/map_ports"))
 	{
 		purple_debug_info("network", "Skipping external port mapping.\n");
@@ -531,27 +526,19 @@
 }
 
 PurpleNetworkListenData *
-purple_network_listen_family(unsigned short port, int socket_family,
-                             int socket_type, PurpleNetworkListenCallback cb,
+purple_network_listen(unsigned short port, int socket_family, int socket_type,
+                             gboolean map_external, PurpleNetworkListenCallback cb,
                              gpointer cb_data)
 {
 	g_return_val_if_fail(port != 0, NULL);
 
-	return purple_network_do_listen(port, socket_family, socket_type,
+	return purple_network_do_listen(port, socket_family, socket_type, map_external,
 	                                cb, cb_data);
 }
 
 PurpleNetworkListenData *
-purple_network_listen(unsigned short port, int socket_type,
-		PurpleNetworkListenCallback cb, gpointer cb_data)
-{
-	return purple_network_listen_family(port, AF_UNSPEC, socket_type,
-	                                    cb, cb_data);
-}
-
-PurpleNetworkListenData *
-purple_network_listen_range_family(unsigned short start, unsigned short end,
-                                   int socket_family, int socket_type,
+purple_network_listen_range(unsigned short start, unsigned short end,
+                                   int socket_family, int socket_type, gboolean map_external,
                                    PurpleNetworkListenCallback cb,
                                    gpointer cb_data)
 {
@@ -566,7 +553,7 @@
 	}
 
 	for (; start <= end; start++) {
-		ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, cb, cb_data);
+		ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, map_external, cb, cb_data);
 		if (ret != NULL)
 			break;
 	}
@@ -574,15 +561,6 @@
 	return ret;
 }
 
-PurpleNetworkListenData *
-purple_network_listen_range(unsigned short start, unsigned short end,
-                            int socket_type, PurpleNetworkListenCallback cb,
-                            gpointer cb_data)
-{
-	return purple_network_listen_range_family(start, end, AF_UNSPEC,
-	                                          socket_type, cb, cb_data);
-}
-
 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
 {
 	if (listen_data->mapping_data != NULL)
@@ -991,7 +969,7 @@
 	if (stun_server && stun_server[0] != '\0') {
 		if (purple_network_is_available()) {
 			purple_debug_info("network", "running DNS query for STUN server\n");
-			purple_dnsquery_a_account(NULL, stun_server, 3478, purple_network_ip_lookup_cb,
+			purple_dnsquery_a(NULL, stun_server, 3478, purple_network_ip_lookup_cb,
 				&stun_ip);
 		} else {
 			purple_debug_info("network",
@@ -1009,7 +987,7 @@
 	if (turn_server && turn_server[0] != '\0') {
 		if (purple_network_is_available()) {
 			purple_debug_info("network", "running DNS query for TURN server\n");
-			purple_dnsquery_a_account(NULL, turn_server,
+			purple_dnsquery_a(NULL, turn_server,
 				purple_prefs_get_int("/purple/network/turn_port"),
 				purple_network_ip_lookup_cb, &turn_ip);
 		} else {
--- a/libpurple/network.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/network.h	Mon Sep 26 14:57:21 2011 +0900
@@ -92,7 +92,6 @@
  *
  * @note The caller must free this list.  If libpurple was built with
  *       support for it, this function also enumerates IPv6 addresses.
- * @since 2.7.0
  *
  * @return A list of local IP addresses.
  */
@@ -118,18 +117,6 @@
 const char *purple_network_get_my_ip(int fd);
 
 /**
- * Should calls to purple_network_listen() and purple_network_listen_range()
- * map the port externally using NAT-PMP or UPnP?
- * The default value is TRUE
- *
- * @param map_external Should the open port be mapped externally?
- * @deprecated In 3.0.0 a boolean will be added to the functions mentioned
- *             above to perform the same function.
- * @since 2.3.0
- */
-void purple_network_listen_map_external(gboolean map_external);
-
-/**
  * Attempts to open a listening port ONLY on the specified port number.
  * You probably want to use purple_network_listen_range() instead of this.
  * This function is useful, for example, if you wanted to write a telnet
@@ -142,9 +129,22 @@
  * close the listening socket, and add a new watcher on the new socket accept
  * returned.
  *
+ * Libpurple does not currently do any port mapping (stateful firewall hole
+ * poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
+ * addresses, a mapping is done).
+ *
  * @param port The port number to bind to.  Must be greater than 0.
+ * @param socket_family The protocol family of the socket.  This should be
+ *                      AF_INET for IPv4 or AF_INET6 for IPv6.  IPv6 sockets
+ *                      may or may not be able to accept IPv4 connections
+ *                      based on the system configuration (use
+ *                      purple_socket_speaks_ipv4 to check).  If an IPv6
+ *                      socket doesn't accept V4-mapped addresses, you will
+ *                      need a second listener to support both v4 and v6.
  * @param socket_type The type of socket to open for listening.
  *   This will be either SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
+ * @param map_external Should the open port be mapped externally using
+ *           NAT-PNP or UPnP?  (default should be TRUE)
  * @param cb The callback to be invoked when the port to listen on is available.
  *           The file descriptor of the listening socket will be specified in
  *           this callback, or -1 if no socket could be established.
@@ -155,28 +155,8 @@
  *         socket to listen on.
  */
 PurpleNetworkListenData *purple_network_listen(unsigned short port,
-		int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data);
-
-/**
- * \copydoc purple_network_listen
- *
- * Libpurple does not currently do any port mapping (stateful firewall hole
- * poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
- * addresses, a mapping is done).
- *
- * @param socket_family The protocol family of the socket.  This should be
- *                      AF_INET for IPv4 or AF_INET6 for IPv6.  IPv6 sockets
- *                      may or may not be able to accept IPv4 connections
- *                      based on the system configuration (use
- *                      purple_socket_speaks_ipv4 to check).  If an IPv6
- *                      socket doesn't accept V4-mapped addresses, you will
- *                      need a second listener to support both v4 and v6.
- * @since 2.7.0
- * @deprecated This function will be renamed to purple_network_listen in 3.0.0.
- */
-PurpleNetworkListenData *purple_network_listen_family(unsigned short port,
-	int socket_family, int socket_type, PurpleNetworkListenCallback cb,
-	gpointer cb_data);
+	int socket_family, int socket_type, gboolean map_external,
+	PurpleNetworkListenCallback cb, gpointer cb_data);
 
 /**
  * Opens a listening port selected from a range of ports.  The range of
@@ -192,13 +172,26 @@
  * the listening socket, and add a new watcher on the new socket accept
  * returned.
  *
+ * Libpurple does not currently do any port mapping (stateful firewall hole
+ * poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
+ * addresses, a mapping is done).
+ *
  * @param start The port number to bind to, or 0 to pick a random port.
  *              Users are allowed to override this arg in prefs.
  * @param end The highest possible port in the range of ports to listen on,
  *            or 0 to pick a random port.  Users are allowed to override this
  *            arg in prefs.
+ * @param socket_family The protocol family of the socket.  This should be
+ *                      AF_INET for IPv4 or AF_INET6 for IPv6.  IPv6 sockets
+ *                      may or may not be able to accept IPv4 connections
+ *                      based on the system configuration (use
+ *                      purple_socket_speaks_ipv4 to check).  If an IPv6
+ *                      socket doesn't accept V4-mapped addresses, you will
+ *                      need a second listener to support both v4 and v6.
  * @param socket_type The type of socket to open for listening.
  *   This will be either SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
+ * @param map_external Should the open port be mapped externally using
+ *           NAT-PNP or UPnP?  (default should be TRUE)
  * @param cb The callback to be invoked when the port to listen on is available.
  *           The file descriptor of the listening socket will be specified in
  *           this callback, or -1 if no socket could be established.
@@ -208,31 +201,10 @@
  *         the pending listener, or NULL if unable to obtain a local
  *         socket to listen on.
  */
-PurpleNetworkListenData *purple_network_listen_range(unsigned short start,
-		unsigned short end, int socket_type,
-		PurpleNetworkListenCallback cb, gpointer cb_data);
-
-/**
- * \copydoc purple_network_listen_range
- *
- * Libpurple does not currently do any port mapping (stateful firewall hole
- * poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
- * addresses, a mapping is done).
- *
- * @param socket_family The protocol family of the socket.  This should be
- *                      AF_INET for IPv4 or AF_INET6 for IPv6.  IPv6 sockets
- *                      may or may not be able to accept IPv4 connections
- *                      based on the system configuration (use
- *                      purple_socket_speaks_ipv4 to check).  If an IPv6
- *                      socket doesn't accept V4-mapped addresses, you will
- *                      need a second listener to support both v4 and v6.
- * @since 2.7.0
- * @deprecated This function will be renamed to purple_network_listen_range
- *             in 3.0.0.
- */
-PurpleNetworkListenData *purple_network_listen_range_family(
+PurpleNetworkListenData *purple_network_listen_range(
 	unsigned short start, unsigned short end, int socket_family,
-	int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data);
+	int socket_type, gboolean map_external,
+	PurpleNetworkListenCallback cb, gpointer cb_data);
 
 /**
  * This can be used to cancel any in-progress listener connection
@@ -267,8 +239,6 @@
  * This is what backs the --force-online command line argument in Pidgin,
  * for example.  This is useful for offline testing, especially when
  * combined with nullprpl.
- *
- * @since 2.6.0
  */
 void purple_network_force_online(void);
 
@@ -284,7 +254,6 @@
  * Will result in a DNS query being executed asynchronous
  *
  * @param stun_server The host name of the STUN server to set
- * @since 2.6.0
  */
 void purple_network_set_stun_server(const gchar *stun_server);
 
@@ -292,7 +261,6 @@
  * Get the IP address of the STUN server as a string representation
  *
  * @return the IP address
- * @since 2.6.0
  */
 const gchar *purple_network_get_stun_ip(void);
 
@@ -301,7 +269,6 @@
  * Will result in a DNS query being executed asynchronous
  *
  * @param turn_server The host name of the TURN server to set
- * @since 2.6.0
  */
 void purple_network_set_turn_server(const gchar *turn_server);
 
@@ -309,7 +276,6 @@
  * Get the IP address of the TURN server as a string representation
  *
  * @return the IP address
- * @since 2.6.0
  */
 const gchar *purple_network_get_turn_ip(void);
 
@@ -317,7 +283,6 @@
  * Remove a port mapping (UPnP or NAT-PMP) associated with listening socket
  *
  * @param fd Socket to remove the port mapping for
- * @since 2.6.0
  */
 void purple_network_remove_port_mapping(gint fd);
 
@@ -336,7 +301,6 @@
  *                The caller is responsible for freeing this.
  * @returns       0 on success, -1 if the out is NULL, or an error code
  *                that currently corresponds to the Idna_rc enum in libidn.
- * @since 2.6.0
  */
 int purple_network_convert_idn_to_ascii(const gchar *in, gchar **out);
 
--- a/libpurple/notify.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/notify.h	Mon Sep 26 14:57:21 2011 +0900
@@ -279,7 +279,7 @@
 /**
  * Sets whether or not a search result column is visible.
  *
- * @param field   The search column object.
+ * @param column  The search column object.
  * @param visible TRUE if visible, or FALSE if not.
  */
 void purple_notify_searchresult_column_set_visible(PurpleNotifySearchColumn *column, gboolean visible);
@@ -287,7 +287,7 @@
 /**
  * Returns whether or not a search result column is visible.
  *
- * @param field The search column object.
+ * @param column The search column object.
  *
  * @return TRUE if the search result column is visible. FALSE otherwise.
  */
@@ -550,7 +550,6 @@
  * Prepend a section break.  A UI might display this as a horizontal line.
  *
  * @param user_info  The PurpleNotifyUserInfo
- * @since 2.5.0
  */
 void purple_notify_user_info_prepend_section_break(PurpleNotifyUserInfo *user_info);
 
@@ -569,7 +568,6 @@
  *
  * @param user_info  The PurpleNotifyUserInfo
  * @param label      The name of the section
- * @since 2.5.0
  */
 void purple_notify_user_info_prepend_section_header(PurpleNotifyUserInfo *user_info, const char *label);
 
--- a/libpurple/plugin.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugin.c	Mon Sep 26 14:57:21 2011 +0900
@@ -64,13 +64,6 @@
 static GList *plugins_to_disable = NULL;
 #endif
 
-static void (*probe_cb)(void *) = NULL;
-static void *probe_cb_data = NULL;
-static void (*load_cb)(PurplePlugin *, void *) = NULL;
-static void *load_cb_data = NULL;
-static void (*unload_cb)(PurplePlugin *, void *) = NULL;
-static void *unload_cb_data = NULL;
-
 #ifdef PURPLE_PLUGINS
 
 static gboolean
@@ -615,9 +608,6 @@
 
 	plugin->loaded = TRUE;
 
-	if (load_cb != NULL)
-		load_cb(plugin, load_cb_data);
-
 	purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
 
 	return TRUE;
@@ -745,9 +735,6 @@
 	g_free(plugin->error);
 	plugin->error = NULL;
 
-	if (unload_cb != NULL)
-		unload_cb(plugin, unload_cb_data);
-
 	purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
 
 	purple_prefs_disconnect_by_handle(plugin);
@@ -1439,10 +1426,6 @@
 													(GCompareFunc)compare_prpl);
 		}
 	}
-
-	if (probe_cb != NULL)
-		probe_cb(probe_cb_data);
-
 #endif /* PURPLE_PLUGINS */
 }
 
@@ -1513,50 +1496,6 @@
 #endif
 }
 
-void
-purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
-{
-	probe_cb = func;
-	probe_cb_data = data;
-}
-
-void
-purple_plugins_unregister_probe_notify_cb(void (*func)(void *))
-{
-	probe_cb = NULL;
-	probe_cb_data = NULL;
-}
-
-void
-purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
-									 void *data)
-{
-	load_cb = func;
-	load_cb_data = data;
-}
-
-void
-purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *))
-{
-	load_cb = NULL;
-	load_cb_data = NULL;
-}
-
-void
-purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
-									   void *data)
-{
-	unload_cb = func;
-	unload_cb_data = data;
-}
-
-void
-purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *))
-{
-	unload_cb = NULL;
-	unload_cb_data = NULL;
-}
-
 PurplePlugin *
 purple_plugins_find_with_name(const char *name)
 {
--- a/libpurple/plugin.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugin.h	Mon Sep 26 14:57:21 2011 +0900
@@ -317,8 +317,6 @@
  * startup" by excluding said plugins from the list of plugins to save.  The
  * UI needs to call purple_plugins_save_loaded() after calling this for it
  * to have any effect.
- *
- * @since 2.3.0
  */
 void purple_plugin_disable(PurplePlugin *plugin);
 
@@ -515,8 +513,6 @@
  * Returns a list of plugin search paths.
  *
  * @constreturn A list of searched paths.
- *
- * @since 2.6.0
  */
 GList *purple_plugins_get_search_paths(void);
 
@@ -566,72 +562,6 @@
  */
 gboolean purple_plugins_enabled(void);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when probing is finished.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated If you need this, ask for a plugin-probe signal to be added.
- */
-void purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Unregisters a function that would be called when probing is finished.
- *
- * @param func The callback function.
- * @deprecated If you need this, ask for a plugin-probe signal to be added.
- */
-void purple_plugins_unregister_probe_notify_cb(void (*func)(void *));
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when a plugin is loaded.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated Use the plugin-load signal instead.
- */
-void purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
-										  void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Unregisters a function that would be called when a plugin is loaded.
- *
- * @param func The callback function.
- * @deprecated Use the plugin-load signal instead.
- */
-void purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *));
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when a plugin is unloaded.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated Use the plugin-unload signal instead.
- */
-void purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
-											void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Unregisters a function that would be called when a plugin is unloaded.
- *
- * @param func The callback function.
- * @deprecated Use the plugin-unload signal instead.
- */
-void purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *,
-														   void *));
-#endif
-
 /**
  * Finds a plugin with the specified name.
  *
--- a/libpurple/plugins/perl/common/Account.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/Account.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -199,9 +199,10 @@
     Purple::Account account
 
 void
-purple_account_add_buddies(account, list)
+purple_account_add_buddies(account, list, message)
     Purple::Account account
     SV * list
+    const char *message
 PREINIT:
     GList *t_GL;
     int i, t_len;
@@ -212,13 +213,14 @@
     for (i = 0; i <= t_len; i++)
         t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(list), i, 0)));
 
-    purple_account_add_buddies(account, t_GL);
+    purple_account_add_buddies(account, t_GL, message);
     g_list_free(t_GL);
 
 void
-purple_account_add_buddy(account, buddy)
-    Purple::Account account
-    Purple::BuddyList::Buddy  buddy
+purple_account_add_buddy(account, buddy, message)
+    Purple::Account          account
+    Purple::BuddyList::Buddy buddy
+    const char *             message
 
 void
 purple_account_change_password(account, a, b)
--- a/libpurple/plugins/perl/common/BuddyList.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/BuddyList.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -82,11 +82,6 @@
 purple_contact_get_priority_buddy(contact)
 	Purple::BuddyList::Contact contact
 
-void
-purple_contact_set_alias(contact, alias)
-	Purple::BuddyList::Contact contact
-	const char * alias
-
 const char *
 purple_contact_get_alias(contact)
 	Purple::BuddyList::Contact contact
@@ -200,10 +195,6 @@
 	Purple::Status old_status
 
 void
-purple_blist_update_buddy_icon(buddy)
-	Purple::BuddyList::Buddy buddy
-
-void
 purple_blist_rename_buddy(buddy, name)
 	Purple::BuddyList::Buddy buddy
 	const char * name
@@ -430,9 +421,5 @@
 	Purple::BuddyList::Buddy buddy
 
 const char *
-purple_buddy_get_local_alias(buddy)
-	Purple::BuddyList::Buddy buddy
-
-const char *
 purple_buddy_get_alias(buddy)
 	Purple::BuddyList::Buddy buddy
--- a/libpurple/plugins/perl/common/Certificate.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/Certificate.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -202,7 +202,7 @@
 			l = g_list_prepend(l, purple_perl_ref_object(ST(i)));
 		}
 		l = g_list_reverse(l);
-		ret = purple_certificate_check_signature_chain(l);
+		ret = purple_certificate_check_signature_chain(l, NULL);
 		g_list_free(l);
 		if(ret) XSRETURN_YES;
 		XSRETURN_NO;
--- a/libpurple/plugins/perl/common/Connection.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/Connection.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -36,10 +36,6 @@
 	const char *text
 
 void
-purple_connection_destroy(gc)
-	Purple::Connection gc
-
-void
 purple_connection_set_state(gc, state)
 	Purple::Connection gc
 	Purple::ConnectionState state
--- a/libpurple/plugins/perl/common/Conversation.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/Conversation.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -338,24 +338,6 @@
 	Purple::Conversation::Chat chat
 
 void
-purple_conv_chat_set_users(chat, users)
-	Purple::Conversation::Chat chat
-	SV * users
-PREINIT:
-	GList *l, *t_GL;
-	int i, t_len;
-PPCODE:
-	t_GL = NULL;
-	t_len = av_len((AV *)SvRV(users));
-
-	for (i = 0; i <= t_len; i++)
-		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(users), i, 0)));
-
-	for (l = purple_conv_chat_set_users(chat, t_GL); l != NULL; l = l->next) {
-		XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry")));
-	}
-
-void
 purple_conv_chat_get_users(chat)
 	Purple::Conversation::Chat chat
 PREINIT:
--- a/libpurple/plugins/perl/common/FT.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/FT.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -113,7 +113,7 @@
 	Purple::Xfer xfer
 
 gboolean 
-purple_xfer_is_canceled(xfer)
+purple_xfer_is_cancelled(xfer)
 	Purple::Xfer xfer
 
 gboolean 
--- a/libpurple/plugins/perl/common/Network.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/Network.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -23,17 +23,21 @@
 	const char *ip
 
 Purple::NetworkListenData
-purple_network_listen(port, socket_type, cb, cb_data)
+purple_network_listen(port, socket_family, socket_type, map_external, cb, cb_data)
 	unsigned short port
+	int socket_family
 	int socket_type
+	gboolean map_external
 	Purple::NetworkListenCallback cb
 	gpointer cb_data
 
 Purple::NetworkListenData
-purple_network_listen_range(start, end, socket_type, cb, cb_data)
+purple_network_listen_range(start, end, socket_family, socket_type, map_external, cb, cb_data)
 	unsigned short start
 	unsigned short end
+	int socket_family
 	int socket_type
+	gboolean map_external
 	Purple::NetworkListenCallback cb
 	gpointer cb_data
 
--- a/libpurple/plugins/perl/common/Request.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/Request.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -374,12 +374,6 @@
 	C_ARGS: id, text
 
 void
-purple_request_field_list_add(field, item, data)
-	Purple::Request::Field field
-	const char *item
-	void * data
-
-void
 purple_request_field_list_add_icon(field, item, icon_path, data)
 	Purple::Request::Field field
 	const char *item
--- a/libpurple/plugins/perl/common/SSLConn.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/SSLConn.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -16,13 +16,6 @@
 	Purple::Ssl::Connection gsc
 	Purple::SslInputFunction func
 
-Purple::Ssl::Connection
-purple_ssl_connect_fd(account, fd, func, error_func, data)
-	Purple::Account account
-	int fd
-	PurpleSslInputFunction func
-	PurpleSslErrorFunction error_func
-
 */
 
 MODULE = Purple::SSL  PACKAGE = Purple::SSL   PREFIX = purple_ssl_
--- a/libpurple/plugins/perl/common/Status.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/Status.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -74,28 +74,6 @@
 		newCONSTSUB(primitive_stash, (char *)civ->name, newSViv(civ->iv));
 }
 
-void
-purple_presence_add_list(presence, source_list)
-	Purple::Presence presence
-	SV *source_list
-PREINIT:
-	GList *t_GL;
-	int i, t_len;
-PPCODE:
-	t_GL = NULL;
-	t_len = av_len((AV *)SvRV(source_list));
-
-	for (i = 0; i <= t_len; i++) {
-		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(source_list), i, 0)));
-	}
-	purple_presence_add_list(presence, t_GL);
-	g_list_free(t_GL);
-
-void
-purple_presence_add_status(presence, status)
-	Purple::Presence presence
-	Purple::Status status
-
 gint
 purple_presence_compare(presence1, presence2)
 	Purple::Presence presence1
@@ -329,29 +307,10 @@
 	Purple::Status status
 	gboolean active
 
-void
-purple_status_set_attr_boolean(status, id, value)
-	Purple::Status status
-	const char *id
-	gboolean value
-
-void
-purple_status_set_attr_string(status, id, value)
-	Purple::Status status
-	const char *id
-	const char *value
-
 MODULE = Purple::Status  PACKAGE = Purple::StatusType  PREFIX = purple_status_type_
 PROTOTYPES: ENABLE
 
 void
-purple_status_type_add_attr(status_type, id, name, value)
-	Purple::StatusType status_type
-	const char *id
-	const char *name
-	Purple::Value value
-
-void
 purple_status_type_destroy(status_type)
 	Purple::StatusType status_type
 
--- a/libpurple/plugins/perl/common/Util.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/plugins/perl/common/Util.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -202,6 +202,14 @@
 purple_ip_address_is_valid(ip)
 	const char* ip
 
+gboolean
+purple_ipv4_address_is_valid(ip)
+	const char* ip
+
+gboolean
+purple_ipv6_address_is_valid(ip)
+	const char* ip
+
 const char*
 purple_normalize_nocase(account, str)
 	Purple::Account account
@@ -454,12 +462,13 @@
 
  #XXX: expand...
 void
-purple_util_fetch_url(plugin, url, full, user_agent, http11, cb)
+purple_util_fetch_url(plugin, url, full, user_agent, http11, max_len, cb)
 	Purple::Plugin plugin
 	const char *url
 	gboolean full
 	const char *user_agent
 	gboolean http11
+	int max_len
 	SV * cb
 PREINIT:
 	PurpleUtilFetchUrlData *data;
@@ -468,7 +477,7 @@
 	SV *sv = purple_perl_sv_from_fun(plugin, cb);
 
 	if (sv != NULL) {
-		data = purple_util_fetch_url(url, full, user_agent, http11,
+		data = purple_util_fetch_url(url, full, user_agent, http11, max_len,
 		                      purple_perl_util_url_cb, sv);
 		XPUSHs(sv_2mortal(purple_perl_bless_object(data, "Purple::Util::FetchUrlData")));
 	} else {
--- a/libpurple/pounce.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/pounce.h	Mon Sep 26 14:57:21 2011 +0900
@@ -101,8 +101,6 @@
  * Destroys all buddy pounces for a buddy
  *
  * @param buddy The buddy whose pounces are to be removed
- *
- * @since 2.8.0
  */
 void purple_pounce_destroy_all_by_buddy(PurpleBuddy *buddy);
 
@@ -334,7 +332,6 @@
  *
  * @return The list of buddy pounces. The list should be freed by
  *         the caller when it's no longer used.
- * @since  2.1.0
  */
 GList *purple_pounces_get_all_for_ui(const char *ui);
 
--- a/libpurple/prefs.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/prefs.h	Mon Sep 26 14:57:21 2011 +0900
@@ -317,8 +317,6 @@
  * @return A list of newly allocated strings denoting the names of the children.
  *         Returns @c NULL if there are no children or if pref doesn't exist.
  *         The caller must free all the strings and the list.
- *
- * @since 2.1.0
  */
 GList *purple_prefs_get_children_names(const char *name);
 
--- a/libpurple/protocols/Makefile.am	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/Makefile.am	Mon Sep 26 14:57:21 2011 +0900
@@ -1,5 +1,5 @@
 EXTRA_DIST = Makefile.mingw
 
-DIST_SUBDIRS = bonjour gg irc jabber msn myspace mxit novell null oscar sametime silc simple yahoo zephyr
+DIST_SUBDIRS = bonjour gg irc jabber msn mxit myspace novell null oscar sametime silc simple yahoo zephyr
 
 SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
--- a/libpurple/protocols/bonjour/bonjour.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/bonjour/bonjour.c	Mon Sep 26 14:57:21 2011 +0900
@@ -51,7 +51,7 @@
 bonjour_get_jid(PurpleAccount *account)
 {
 	PurpleConnection *conn = purple_account_get_connection(account);
-	BonjourData *bd = conn->proto_data;
+	BonjourData *bd = purple_connection_get_protocol_data(conn);
 	return bd->jid;
 }
 
@@ -103,7 +103,8 @@
 #endif /* _WIN32 */
 
 	gc->flags |= PURPLE_CONNECTION_HTML;
-	gc->proto_data = bd = g_new0(BonjourData, 1);
+	bd = g_new0(BonjourData, 1);
+	purple_connection_set_protocol_data(gc, bd);
 
 	/* Start waiting for jabber connections (iChat style) */
 	bd->jabber_data = g_new0(BonjourJabber, 1);
@@ -157,7 +158,7 @@
 bonjour_close(PurpleConnection *connection)
 {
 	PurpleGroup *bonjour_group;
-	BonjourData *bd = connection->proto_data;
+	BonjourData *bd = purple_connection_get_protocol_data(connection);
 
 	bonjour_group = purple_find_group(BONJOUR_GROUP_NAME);
 
@@ -192,7 +193,7 @@
 	if (bd != NULL)
 		g_free(bd->jid);
 	g_free(bd);
-	connection->proto_data = NULL;
+	purple_connection_set_protocol_data(connection, NULL);
 }
 
 static const char *
@@ -204,10 +205,12 @@
 static int
 bonjour_send_im(PurpleConnection *connection, const char *to, const char *msg, PurpleMessageFlags flags)
 {
+	BonjourData *bd = purple_connection_get_protocol_data(connection);
+
 	if(!to || !msg)
 		return 0;
 
-	return bonjour_jabber_send_message(((BonjourData*)(connection->proto_data))->jabber_data, to, msg);
+	return bonjour_jabber_send_message(bd->jabber_data, to, msg);
 }
 
 static void
@@ -220,7 +223,7 @@
 	gchar *stripped;
 
 	gc = purple_account_get_connection(account);
-	bd = gc->proto_data;
+	bd = purple_connection_get_protocol_data(gc);
 	presence = purple_account_get_presence(account);
 
 	message = purple_status_get_attr_string(status, "message");
@@ -253,7 +256,7 @@
  * if there is no add_buddy callback.
  */
 static void
-bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) {
+bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) {
 	purple_debug_error("bonjour", "Buddy '%s' manually added; removing.  "
 				      "Bonjour buddies must be discovered and not manually added.\n",
 			   purple_buddy_get_name(buddy));
@@ -325,7 +328,7 @@
 static
 void bonjour_set_buddy_icon(PurpleConnection *conn, PurpleStoredImage *img)
 {
-	BonjourData *bd = conn->proto_data;
+	BonjourData *bd = purple_connection_get_protocol_data(conn);
 	bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
 }
 
@@ -481,6 +484,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),                        /* struct_size */
 	OPT_PROTO_NO_PASSWORD,
 	NULL,                                                    /* user_splits */
 	NULL,                                                    /* protocol_options */
@@ -521,7 +525,6 @@
 	NULL,                                                    /* keepalive */
 	NULL,                                                    /* register_user */
 	NULL,                                                    /* get_cb_info */
-	NULL,                                                    /* get_cb_away */
 	NULL,                                                    /* alias_buddy */
 	bonjour_group_buddy,                                     /* group_buddy */
 	bonjour_rename_group,                                    /* rename_group */
@@ -546,15 +549,12 @@
 	NULL,                                                    /* unregister_user */
 	NULL,                                                    /* send_attention */
 	NULL,                                                    /* get_attention_types */
-	sizeof(PurplePluginProtocolInfo),                        /* struct_size */
 	NULL,                                                    /* get_account_text_table */
 	NULL,                                                    /* initiate_media */
 	NULL,                                                    /* get_media_caps */
 	NULL,                                                    /* get_moods */
 	NULL,                                                    /* set_public_alias */
-	NULL,                                                    /* get_public_alias */
-	NULL,                                                    /* add_buddy_with_invite */
-	NULL                                                     /* add_buddies_with_invite */
+	NULL                                                     /* get_public_alias */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Mon Sep 26 14:57:21 2011 +0900
@@ -95,7 +95,7 @@
 	purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
 
 	if(xf)
-		xep_ft_si_reject(xf->data, xf->sid, xfer->who, "403", "cancel");
+		xep_ft_si_reject(xf->data, xf->sid, purple_xfer_get_remote_user(xfer), "403", "cancel");
 
 	bonjour_free_xfer(xfer);
 }
@@ -165,8 +165,8 @@
 		xf = purple_xfer_get_protocol_data(xfer);
 		if(xf == NULL)
 			break;
-		if(xf->sid && xfer->who && !strcmp(xf->sid, sid) &&
-				!strcmp(xfer->who, from))
+		if(xf->sid && purple_xfer_get_remote_user(xfer) && !strcmp(xf->sid, sid) &&
+				!strcmp(purple_xfer_get_remote_user(xfer), from))
 			return xfer;
 	}
 
@@ -210,8 +210,8 @@
 
 	file = xmlnode_new_child(si_node, "file");
 	xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
-	xmlnode_set_attrib(file, "name", xfer->filename);
-	g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
+	xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer));
+	g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, purple_xfer_get_size(xfer));
 	xmlnode_set_attrib(file, "size", buf);
 
 	feature = xmlnode_new_child(si_node, "feature");
@@ -240,7 +240,7 @@
 }
 
 static void
-xep_ft_si_result(PurpleXfer *xfer, char *to)
+xep_ft_si_result(PurpleXfer *xfer, const char *to)
 {
 	xmlnode *si_node, *feature, *field, *value, *x;
 	XepIq *iq;
@@ -290,14 +290,14 @@
 		return;
 	}
 
-	purple_debug_info("bonjour", "bonjour-free-xfer-%p.\n", xfer);
+	purple_debug_misc("bonjour", "bonjour-free-xfer-%p.\n", xfer);
 
 	xf = purple_xfer_get_protocol_data(xfer);
 	if(xf != NULL) {
 		BonjourData *bd = (BonjourData*)xf->data;
 		if(bd != NULL) {
 			bd->xfer_lists = g_slist_remove(bd->xfer_lists, xfer);
-			purple_debug_info("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
+			purple_debug_misc("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
 		}
 		if (xf->proxy_connection != NULL)
 			purple_proxy_connect_cancel(xf->proxy_connection);
@@ -314,7 +314,7 @@
 		purple_xfer_set_protocol_data(xfer, NULL);
 	}
 
-	purple_debug_info("bonjour", "Need close socket=%d.\n", xfer->fd);
+	purple_debug_misc("bonjour", "Need close socket.\n");
 }
 
 PurpleXfer *
@@ -328,7 +328,7 @@
 		return NULL;
 
 	purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who);
-	bd = (BonjourData*) gc->proto_data;
+	bd = purple_connection_get_protocol_data(gc);
 	if(bd == NULL)
 		return NULL;
 
@@ -386,7 +386,7 @@
 
 	purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
 
-	buddy = purple_find_buddy(xfer->account, xfer->who);
+	buddy = purple_find_buddy(purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer));
 	/* this buddy is offline. */
 	if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
 		return;
@@ -397,10 +397,10 @@
 	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
 		/* initiate file transfer, send SI offer. */
 		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
-		xep_ft_si_offer(xfer, xfer->who);
+		xep_ft_si_offer(xfer, purple_xfer_get_remote_user(xfer));
 	} else {
 		/* accept file transfer request, send SI result. */
-		xep_ft_si_result(xfer, xfer->who);
+		xep_ft_si_result(xfer, purple_xfer_get_remote_user(xfer));
 		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_RECEIVE.\n");
 	}
 }
@@ -417,7 +417,7 @@
 	g_return_if_fail(packet != NULL);
 	g_return_if_fail(pb != NULL);
 
-	bd = (BonjourData*) pc->proto_data;
+	bd = purple_connection_get_protocol_data(pc);
 	if(bd == NULL)
 		return;
 
@@ -503,7 +503,7 @@
 	g_return_if_fail(packet != NULL);
 	g_return_if_fail(pb != NULL);
 
-	bd = (BonjourData*) pc->proto_data;
+	bd = purple_connection_get_protocol_data(pc);
 	if(bd == NULL)
 		return;
 
@@ -565,7 +565,7 @@
 				purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
 
 				if (iq_id && xfer != NULL)
-					xep_ft_si_reject(bd, iq_id, xfer->who, "404", "cancel");
+					xep_ft_si_reject(bd, iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel");
 			}
 
 		} else {
@@ -585,7 +585,7 @@
 	if(pc == NULL || id == NULL || from == NULL)
 		return;
 
-	bd = (BonjourData*) pc->proto_data;
+	bd = purple_connection_get_protocol_data(pc);
 	if(bd == NULL)
 		return;
 
@@ -634,8 +634,6 @@
 			/* This should cancel the ft */
 			purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno);
 
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
@@ -664,9 +662,6 @@
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0){
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
-			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
 		} else {
@@ -685,8 +680,6 @@
 		if (len < 0 && errno == EAGAIN)
 			return;
 		else if (len < 0) {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
@@ -723,8 +716,6 @@
 		if (len < 0 && errno == EAGAIN) {
 			return;
 		} else if (len < 0) {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
@@ -766,7 +757,7 @@
 
 	bd = xf->data;
 
-	iq = xep_iq_new(bd, XEP_IQ_SET, xfer->who, bonjour_get_jid(bd->jabber_data->account), xf->sid);
+	iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->sid);
 
 	query = xmlnode_new_child(iq->node, "query");
 	xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
@@ -777,7 +768,7 @@
 
 	local_ips = bonjour_jabber_get_local_ips(sock);
 
-	port = g_strdup_printf("%hu", xfer->local_port);
+	port = g_strdup_printf("%hu", purple_xfer_get_local_port(xfer));
 	while(local_ips) {
 		streamhost = xmlnode_new_child(query, "streamhost");
 		xmlnode_set_attrib(streamhost, "jid", xf->sid);
@@ -801,10 +792,8 @@
 	purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
 	xf = purple_xfer_get_protocol_data(xfer);
 
-	purple_network_listen_map_external(FALSE);
-	xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+	xf->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, FALSE,
 						      bonjour_bytestreams_listen, xfer);
-	purple_network_listen_map_external(TRUE);
 	if (xf->listen_data == NULL)
 		purple_xfer_cancel_local(xfer);
 
@@ -825,7 +814,7 @@
 	if(source < 0) {
 		purple_debug_error("bonjour", "Error connecting via SOCKS5 - %s\n",
 			error_message ? error_message : "(null)");
-		xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel");
+		xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel");
 		/* Cancel the connection */
 		purple_xfer_cancel_local(xfer);
 		return;
@@ -838,7 +827,7 @@
 	/* Here, start the file transfer.*/
 
 	/* Notify Initiator of Connection */
-	iq = xep_iq_new(bd, XEP_IQ_RESULT, xfer->who, bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
+	iq = xep_iq_new(bd, XEP_IQ_RESULT, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
 	q_node = xmlnode_new_child(iq->node, "query");
 	xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams");
 	tmp_node = xmlnode_new_child(q_node, "streamhost-used");
@@ -893,7 +882,7 @@
 							   bonjour_bytestreams_connect_cb, xfer);
 
 	if(xf->proxy_connection == NULL) {
-		xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel");
+		xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel");
 		/* Cancel the connection */
 		purple_xfer_cancel_local(xfer);
 	}
--- a/libpurple/protocols/bonjour/jabber.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/bonjour/jabber.c	Mon Sep 26 14:57:21 2011 +0900
@@ -744,7 +744,7 @@
 
 #if 0
 	/* TODO: Why isn't this being used? */
-	data->socket = purple_network_listen(jdata->port, SOCK_STREAM);
+	data->socket = purple_network_listen(jdata->port, AF_UNSPEC, SOCK_STREAM, TRUE);
 
 	if (jdata->socket == -1)
 	{
@@ -930,7 +930,8 @@
 			ip = tmp->data;
 			if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
 				PurpleConnection *pc = purple_account_get_connection(bconv->account);
-				BonjourJabber *jdata = ((BonjourData *)pc->proto_data)->jabber_data;
+				BonjourData *bd = purple_connection_get_protocol_data(pc);
+				BonjourJabber *jdata = bd->jabber_data;
 
 				purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
 					purple_buddy_get_name(pb), bconv->ip);
@@ -964,7 +965,8 @@
 void
 bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
 	PurpleConnection *pc = purple_account_get_connection(bconv->account);
-	BonjourJabber *jdata = ((BonjourData *)pc->proto_data)->jabber_data;
+	BonjourData *bd = purple_connection_get_protocol_data(pc);
+	BonjourJabber *jdata = bd->jabber_data;
 	struct _match_buddies_by_address_t *mbba;
 	GSList *buddies;
 
@@ -1125,7 +1127,8 @@
 void
 async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
 	PurpleConnection *pc = purple_account_get_connection(bconv->account);
-	BonjourJabber *jdata = ((BonjourData *)pc->proto_data)->jabber_data;
+	BonjourData *bd = purple_connection_get_protocol_data(pc);
+	BonjourJabber *jdata = bd->jabber_data;
 
 	jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
 
@@ -1147,7 +1150,7 @@
 
 		PurpleConnection *pc = purple_account_get_connection(bconv->account);
 		if (PURPLE_CONNECTION_IS_VALID(pc)) {
-			bd = pc->proto_data;
+			bd = purple_connection_get_protocol_data(pc);
 			bd->jabber_data->pending_conversations = g_slist_remove(bd->jabber_data->pending_conversations, bconv);
 		}
 
--- a/libpurple/protocols/bonjour/mdns_avahi.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Mon Sep 26 14:57:21 2011 +0900
@@ -615,7 +615,7 @@
 
 void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy) {
 	PurpleConnection *conn = purple_account_get_connection(buddy->account);
-	BonjourData *bd = conn->proto_data;
+	BonjourData *bd = purple_connection_get_protocol_data(conn);
 	AvahiSessionImplData *session_idata = bd->dns_sd_data->mdns_impl_data;
 	AvahiBuddyImplData *idata = buddy->mdns_impl_data;
 	gchar *name;
--- a/libpurple/protocols/bonjour/mdns_common.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/bonjour/mdns_common.c	Mon Sep 26 14:57:21 2011 +0900
@@ -257,7 +257,7 @@
 bonjour_dns_sd_set_jid(PurpleAccount *account, const char *hostname)
 {
 	PurpleConnection *conn = purple_account_get_connection(account);
-	BonjourData *bd = conn->proto_data;
+	BonjourData *bd = purple_connection_get_protocol_data(conn);
 	const char *tmp, *account_name = purple_account_get_username(account);
 
 	/* Previously we allowed the hostname part of the jid to be set
--- a/libpurple/protocols/bonjour/mdns_win32.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Mon Sep 26 14:57:21 2011 +0900
@@ -177,7 +177,7 @@
 	args->resolver_query = NULL;
 
 	if ((pb = purple_find_buddy(args->account, args->res_data->name))) {
-		if (pb->proto_data != args->bb) {
+		if (purple_buddy_get_protocol_data(pb) != args->bb) {
 			purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record.",
 				args->res_data->name);
 			goto cleanup;
@@ -348,7 +348,7 @@
 
 				/* Is there an existing buddy? */
 				if ((pb = purple_find_buddy(account, serviceName)))
-					bb = pb->proto_data;
+					bb = purple_buddy_get_protocol_data(pb);
 				/* Is there a pending buddy? */
 				else {
 					while (tmp) {
@@ -368,7 +368,7 @@
 					if (pb == NULL)
 						pending_buddies = g_slist_prepend(pending_buddies, bb);
 					else
-						pb->proto_data = bb;
+						purple_buddy_set_protocol_data(pb, bb);
 				}
 
 				rd = g_new0(Win32SvcResolverData, 1);
@@ -408,7 +408,7 @@
 			GSList *l;
 			/* There may be multiple presences, we should only get rid of this one */
 			Win32SvcResolverData *rd_search;
-			BonjourBuddy *bb = pb->proto_data;
+			BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
 			Win32BuddyImplData *idata;
 
 			g_return_if_fail(bb != NULL);
--- a/libpurple/protocols/gg/buddylist.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/gg/buddylist.c	Mon Sep 26 14:57:21 2011 +0900
@@ -38,7 +38,7 @@
 /* void ggp_buddylist_send(PurpleConnection *gc) {{{ */
 void ggp_buddylist_send(PurpleConnection *gc)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 	GSList *buddies;
 	uin_t *userlist;
--- a/libpurple/protocols/gg/confer.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/gg/confer.c	Mon Sep 26 14:57:21 2011 +0900
@@ -42,7 +42,7 @@
 							 const uin_t uin)
 {
 	PurpleConversation *conv;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPChat *chat;
 	GList *l;
 	gchar *str_uin;
@@ -73,7 +73,7 @@
 void ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name,
 				 const uin_t *recipients, int count)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GList *l;
 	gchar *str_uin;
 
@@ -111,7 +111,7 @@
 const char *ggp_confer_find_by_participants(PurpleConnection *gc,
 					    const uin_t *recipients, int count)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPChat *chat = NULL;
 	GList *l;
 	int matches;
@@ -149,7 +149,7 @@
 /* const char *ggp_confer_add_new(PurpleConnection *gc, const char *name) {{{ */
 const char *ggp_confer_add_new(PurpleConnection *gc, const char *name)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPChat *chat;
 
 	chat = g_new0(GGPChat, 1);
--- a/libpurple/protocols/gg/gg.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/gg/gg.c	Mon Sep 26 14:57:21 2011 +0900
@@ -97,7 +97,7 @@
 static void ggp_async_token_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPToken *token = info->token;
 	GGPTokenCallback cb;
 
@@ -170,7 +170,7 @@
 	if (ggp_setup_proxy(account) == -1)
 		return;
 
-	info = gc->proto_data;
+	info = purple_connection_get_protocol_data(gc);
 
 	if ((req = gg_token(1)) == NULL) {
 		purple_notify_error(account,
@@ -199,7 +199,7 @@
 static void ggp_action_buddylist_get(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 
 	purple_debug_info("gg", "Downloading...\n");
 
@@ -214,7 +214,7 @@
 static void ggp_action_buddylist_put(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 
 	char *buddylist = ggp_buddylist_dump(purple_connection_get_account(gc));
 
@@ -235,7 +235,7 @@
 static void ggp_action_buddylist_delete(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 
 	purple_debug_info("gg", "Deleting...\n");
 
@@ -334,7 +334,7 @@
 					     PurpleRequestFields *fields)
 {
 	PurpleAccount *account;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	struct gg_http *h = NULL;
 	struct gg_pubdir *s;
 	uin_t uin;
@@ -415,7 +415,7 @@
 static void ggp_callback_register_account_cancel(PurpleConnection *gc,
 						 PurpleRequestFields *fields)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPToken *token = info->token;
 
 	purple_account_disconnect(gc->account);
@@ -433,7 +433,7 @@
 	PurpleRequestFieldGroup *group;
 	PurpleRequestField *field;
 
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPToken *token = info->token;
 
 
@@ -483,7 +483,7 @@
 
 static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPSearchForm *form = user_data;
 	guint32 seq;
 
@@ -520,7 +520,7 @@
 
 static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPSearchForm *form;
 	guint32 seq;
 
@@ -621,7 +621,7 @@
 static void ggp_callback_change_passwd_ok(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	PurpleAccount *account;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	struct gg_http *h;
 	gchar *cur, *p1, *p2, *t;
 
@@ -696,7 +696,7 @@
 	PurpleRequestFieldGroup *group;
 	PurpleRequestField *field;
 
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPToken *token = info->token;
 
 	char *msg;
@@ -758,7 +758,7 @@
 
 static void ggp_action_change_status_broadcasting_ok(PurpleConnection *gc, PurpleRequestFields *fields)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	int selected_field;
 	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleStatus *status;
@@ -778,7 +778,7 @@
 static void ggp_action_change_status_broadcasting(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *group;
@@ -848,7 +848,7 @@
 
 	buddy = (PurpleBuddy *)node;
 	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	info = gc->proto_data;
+	info = purple_connection_get_protocol_data(gc);
 
 	fields = purple_request_fields_new();
 	group = purple_request_field_group_new(NULL);
@@ -857,7 +857,7 @@
 	field = purple_request_field_list_new("name", "Chat name");
 	for (l = info->chats; l != NULL; l = l->next) {
 		GGPChat *chat = l->data;
-		purple_request_field_list_add(field, chat->name, chat->name);
+		purple_request_field_list_add_icon(field, chat->name, NULL, chat->name);
 	}
 	purple_request_field_group_add_field(group, field);
 
@@ -879,7 +879,7 @@
 
 static void ggp_add_deny(PurpleConnection *gc, const char *who)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	uin_t uin = ggp_str_to_uin(who);
 	
 	purple_debug_info("gg", "ggp_add_deny: %u\n", uin);
@@ -890,7 +890,7 @@
 
 static void ggp_rem_deny(PurpleConnection *gc, const char *who)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	uin_t uin = ggp_str_to_uin(who);
 	
 	purple_debug_info("gg", "ggp_rem_deny: %u\n", uin);
@@ -1024,7 +1024,7 @@
 
 				purple_debug_info("gg", "gg_get_avatar_url_cb: "
 					"requesting avatar for %s\n", uin);
-				url_data = purple_util_fetch_url_request_len(account,
+				url_data = purple_util_fetch_url_request(account,
 						bigavatar, TRUE, "Mozilla/4.0 (compatible; MSIE 5.0)",
 						FALSE, NULL, FALSE, -1, gg_fetch_avatar_cb, data);
 			}
@@ -1052,7 +1052,7 @@
 
 	avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%u/0.xml", uin);
 
-	url_data = purple_util_fetch_url_request_len(
+	url_data = purple_util_fetch_url_request(
 			purple_connection_get_account(gc), avatarurl, TRUE,
 			"Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1,
 			gg_get_avatar_url_cb, gc);
@@ -1349,7 +1349,7 @@
 
 static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPSearchForm *form;
 	int res_count;
 	guint32 seq;
@@ -1391,7 +1391,7 @@
 static void ggp_recv_image_handler(PurpleConnection *gc, const struct gg_event *ev)
 {
 	gint imgid = 0;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GList *entry = g_list_first(info->pending_richtext_messages);
 	gchar *handlerid = g_strdup_printf("IMGID_HANDLER-%i", ev->event.image_reply.crc32);
 
@@ -1439,7 +1439,7 @@
  */
 static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	PurpleConversation *conv;
 	gchar *from;
 	gchar *msg;
@@ -1608,7 +1608,7 @@
 
 static void ggp_send_image_handler(PurpleConnection *gc, const struct gg_event *ev)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	PurpleStoredImage *image;
 	gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32)));
 
@@ -1714,7 +1714,7 @@
 static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	struct gg_event *ev;
 	int i;
 
@@ -1849,7 +1849,7 @@
 
 	g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
 
-	info = gc->proto_data;
+	info = purple_connection_get_protocol_data(gc);
 
 	purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n",
 			info->session->check, info->session->state);
@@ -2067,13 +2067,15 @@
 	PurpleMenuAction *act;
 	GList *m = NULL;
 	PurpleAccount *account;
+	PurpleConnection *gc;
 	GGPInfo *info;
 
 	if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
 		return NULL;
 
 	account = purple_buddy_get_account((PurpleBuddy *) node);
-	info = purple_account_get_connection(account)->proto_data;
+	gc = purple_account_get_connection(account);
+	info = purple_connection_get_protocol_data(gc);
 	if (info->chats) {
 		act = purple_menu_action_new(_("Add to chat"),
 			PURPLE_CALLBACK(ggp_bmenu_add_to_chat),
@@ -2125,7 +2127,7 @@
 	info->pending_images = g_hash_table_new(g_direct_hash, g_direct_equal);
 	info->status_broadcasting = purple_account_get_bool(account, "status_broadcasting", TRUE);
 	
-	gc->proto_data = info;
+	purple_connection_set_protocol_data(gc, info);
 
 	glp->uin = ggp_get_uin(account);
 	glp->password = (char *)purple_account_get_password(account);
@@ -2189,18 +2191,19 @@
 
 static void ggp_close(PurpleConnection *gc)
 {
+	PurpleAccount *account;
+	GGPInfo *info;;
 
 	if (gc == NULL) {
 		purple_debug_info("gg", "gc == NULL\n");
 		return;
 	}
 
-	if (gc->proto_data) {
-		PurpleAccount *account = purple_connection_get_account(gc);
-		PurpleStatus *status;
-		GGPInfo *info = gc->proto_data;
-
-		status = purple_account_get_active_status(account);
+	account = purple_connection_get_account(gc);
+	info = purple_connection_get_protocol_data(gc);
+
+	if (info) {
+		PurpleStatus *status = purple_account_get_active_status(account);
 
 		if (info->session != NULL) {
 			ggp_set_status(account, status);
@@ -2219,7 +2222,7 @@
 		g_list_free(info->pending_richtext_messages);
 		g_hash_table_destroy(info->pending_images);
 		g_free(info);
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 	}
 
 	if (gc->inpa > 0)
@@ -2231,7 +2234,7 @@
 static int ggp_send_im(PurpleConnection *gc, const char *who, const char *msg,
 		       PurpleMessageFlags flags)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	char *tmp, *plain;
 	int ret = 1;
 	unsigned char format[1024];
@@ -2349,6 +2352,7 @@
 
 static unsigned int ggp_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
 {
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	int dummy_length; // we don't send real length of typed message
 	
 	if (state == PURPLE_TYPED) // not supported
@@ -2360,7 +2364,7 @@
 		dummy_length = 0;
 	
 	gg_typing_notification(
-		((GGPInfo*)gc->proto_data)->session,
+		info->session,
 		ggp_str_to_uin(name),
 		dummy_length); 
 	
@@ -2369,7 +2373,7 @@
 
 static void ggp_get_info(PurpleConnection *gc, const char *name)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPSearchForm *form;
 	guint32 seq;
 
@@ -2445,7 +2449,7 @@
 		return;
 
 	gc = purple_account_get_connection(account);
-	info = gc->proto_data;
+	info = purple_connection_get_protocol_data(gc);
 
 	new_status = ggp_to_gg_status(status, &new_msg);
 
@@ -2463,10 +2467,10 @@
 
 }
 
-static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
 {
 	PurpleAccount *account;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	const gchar *name = purple_buddy_get_name(buddy);
 
 	gg_add_notify(info->session, ggp_str_to_uin(name));
@@ -2480,14 +2484,14 @@
 static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
 						 PurpleGroup *group)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 
 	gg_remove_notify(info->session, ggp_str_to_uin(purple_buddy_get_name(buddy)));
 }
 
 static void ggp_join_chat(PurpleConnection *gc, GHashTable *data)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPChat *chat;
 	char *chat_name;
 	GList *l;
@@ -2525,7 +2529,7 @@
 static int ggp_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
 {
 	PurpleConversation *conv;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPChat *chat = NULL;
 	GList *l;
 	/* char *msg, *plain; */
@@ -2580,7 +2584,7 @@
 
 static void ggp_keepalive(PurpleConnection *gc)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 
 	/* purple_debug_info("gg", "Keeping connection alive....\n"); */
 
@@ -2651,6 +2655,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_IM_IMAGE,
 	NULL,				/* user_splits */
 	NULL,				/* protocol_options */
@@ -2691,7 +2696,6 @@
 	ggp_keepalive,			/* keepalive */
 	ggp_register_user,		/* register_user */
 	NULL,				/* get_cb_info */
-	NULL,				/* get_cb_away */
 	NULL,				/* alias_buddy */
 	NULL,				/* group_buddy */
 	NULL,				/* rename_group */
@@ -2716,15 +2720,12 @@
 	NULL,				/* unregister_user */
 	NULL,				/* send_attention */
 	NULL,				/* get_attention_types */
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,                           /* get_account_text_table */
 	NULL,                           /* initiate_media */
 	NULL,                            /* can_do_media */
 	NULL,				/* get_moods */
 	NULL,				/* set_public_alias */
-	NULL,				/* get_public_alias */
-	NULL,				/* add_buddy_with_invite */
-	NULL				/* add_buddies_with_invite */
+	NULL				/* get_public_alias */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/protocols/gg/search.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/gg/search.c	Mon Sep 26 14:57:21 2011 +0900
@@ -135,7 +135,7 @@
 /* guint32 ggp_search_start(PurpleConnection *gc, GGPSearchForm *form) {{{ */
 guint32 ggp_search_start(PurpleConnection *gc, GGPSearchForm *form)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	gg_pubdir50_t req;
 	guint seq, offset;
 
--- a/libpurple/protocols/irc/dcc_send.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/irc/dcc_send.c	Mon Sep 26 14:57:21 2011 +0900
@@ -34,6 +34,7 @@
 
 struct irc_xfer_rx_data {
 	gchar *ip;
+	unsigned int remote_port;
 };
 
 static void irc_dccsend_recv_destroy(PurpleXfer *xfer)
@@ -51,10 +52,10 @@
  */
 static void irc_dccsend_recv_ack(PurpleXfer *xfer, const guchar *data, size_t size) {
 	guint32 l;
-	size_t result;
+	gssize result;
 
-	l = htonl(xfer->bytes_sent);
-	result = write(xfer->fd, &l, sizeof(l));
+	l = htonl(purple_xfer_get_bytes_sent(xfer));
+	result = purple_xfer_write(xfer, (guchar *)&l, sizeof(l));
 	if (result != sizeof(l)) {
 		purple_debug_error("irc", "unable to send acknowledgement: %s\n", g_strerror(errno));
 		/* TODO: We should probably close the connection here or something. */
@@ -64,7 +65,7 @@
 static void irc_dccsend_recv_init(PurpleXfer *xfer) {
 	struct irc_xfer_rx_data *xd = purple_xfer_get_protocol_data(xfer);
 
-	purple_xfer_start(xfer, -1, xd->ip, xfer->remote_port);
+	purple_xfer_start(xfer, -1, xd->ip, xd->remote_port);
 	g_free(xd->ip);
 	xd->ip = NULL;
 }
@@ -117,7 +118,7 @@
 		purple_xfer_set_protocol_data(xfer, xd);
 
 		purple_xfer_set_filename(xfer, filename->str);
-		xfer->remote_port = atoi(token[i+1]);
+		xd->remote_port = atoi(token[i+1]);
 
 		nip = strtoul(token[i], NULL, 10);
 		if (nip) {
@@ -228,13 +229,13 @@
 static gssize irc_dccsend_send_write(const guchar *buffer, size_t size, PurpleXfer *xfer)
 {
 	gssize s;
-	int ret;
+	gssize ret;
 
 	s = MIN(purple_xfer_get_bytes_remaining(xfer), size);
 	if (!s)
 		return 0;
 
-	ret = write(xfer->fd, buffer, s);
+	ret = purple_xfer_write(xfer, buffer, s);
 
 	if (ret < 0 && errno == EAGAIN)
 		ret = 0;
@@ -296,7 +297,7 @@
 
 	xd = purple_xfer_get_protocol_data(xfer);
 	gc = purple_account_get_connection(purple_xfer_get_account(xfer));
-	irc = gc->proto_data;
+	irc = purple_connection_get_protocol_data(gc);
 
 	purple_xfer_unref(xfer);
 
@@ -316,13 +317,13 @@
 	                                 irc_dccsend_send_connected, xfer);
 
 	/* Send the intended recipient the DCC request */
-	arg[0] = xfer->who;
+	arg[0] = purple_xfer_get_remote_user(xfer);
 	inet_aton(purple_network_get_my_ip(irc->fd), &addr);
 	arg[1] = tmp = g_strdup_printf("\001DCC SEND \"%s\" %u %hu %" G_GSIZE_FORMAT "\001",
-	                               xfer->filename, ntohl(addr.s_addr),
-	                               port, xfer->size);
+	                               purple_xfer_get_filename(xfer), ntohl(addr.s_addr),
+	                               port, purple_xfer_get_size(xfer));
 
-	irc_cmd_privmsg(gc->proto_data, "msg", NULL, arg);
+	irc_cmd_privmsg(purple_connection_get_protocol_data(gc), "msg", NULL, arg);
 	g_free(tmp);
 }
 
@@ -333,12 +334,12 @@
 	PurpleConnection *gc = purple_account_get_connection(purple_xfer_get_account(xfer));
 	struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
 
-	xfer->filename = g_path_get_basename(xfer->local_filename);
+	purple_xfer_set_filename(xfer, g_path_get_basename(purple_xfer_get_local_filename(xfer)));
 
 	purple_xfer_ref(xfer);
 
 	/* Create a listening socket */
-	xd->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+	xd->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, TRUE,
 			irc_dccsend_network_listen_cb, xfer);
 	if (xd->listen_data == NULL) {
 		purple_xfer_unref(xfer);
--- a/libpurple/protocols/irc/irc.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/irc/irc.c	Mon Sep 26 14:57:21 2011 +0900
@@ -70,11 +70,11 @@
 	struct irc_conn *irc;
 	char *title;
 
-	if (gc == NULL || gc->proto_data == NULL) {
+	if (gc == NULL || purple_connection_get_protocol_data(gc) == NULL) {
 		purple_debug(PURPLE_DEBUG_ERROR, "irc", "got MOTD request for NULL gc\n");
 		return;
 	}
-	irc = gc->proto_data;
+	irc = purple_connection_get_protocol_data(gc);
 	if (irc->motd == NULL) {
 		purple_notify_error(gc, _("Error displaying MOTD"), _("No MOTD available"),
 				  _("There is no MOTD associated with this connection."));
@@ -100,7 +100,7 @@
 
 static int irc_send_raw(PurpleConnection *gc, const char *buf, int len)
 {
-	struct irc_conn *irc = (struct irc_conn*)gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	if (len == -1) {
 		len = strlen(buf);
 	}
@@ -365,7 +365,8 @@
 		return;
 	}
 
-	gc->proto_data = irc = g_new0(struct irc_conn, 1);
+	irc = g_new0(struct irc_conn, 1);
+	purple_connection_set_protocol_data(gc, irc);
 	irc->fd = -1;
 	irc->account = account;
 	irc->outbuf = purple_circ_buffer_new(512);
@@ -415,7 +416,7 @@
 	char *buf, *tmp = NULL;
 	char *server;
 	const char *username, *realname;
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	const char *pass = purple_connection_get_password(gc);
 
 	if (pass && *pass) {
@@ -485,7 +486,7 @@
 static void irc_login_cb(gpointer data, gint source, const gchar *error_message)
 {
 	PurpleConnection *gc = data;
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 
 	if (source < 0) {
 		gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
@@ -508,7 +509,7 @@
 		gpointer data)
 {
 	PurpleConnection *gc = data;
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 
 	irc->gsc = NULL;
 
@@ -517,7 +518,7 @@
 
 static void irc_close(PurpleConnection *gc)
 {
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 
 	if (irc == NULL)
 		return;
@@ -558,7 +559,7 @@
 
 static int irc_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
 {
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	char *plain;
 	const char *args[2];
 
@@ -574,7 +575,7 @@
 
 static void irc_get_info(PurpleConnection *gc, const char *who)
 {
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	const char *args[2];
 	args[0] = who;
 	args[1] = NULL;
@@ -589,7 +590,7 @@
 	const char *status_id = purple_status_get_id(status);
 
 	g_return_if_fail(gc != NULL);
-	irc = gc->proto_data;
+	irc = purple_connection_get_protocol_data(gc);
 
 	if (!purple_status_is_active(status))
 		return;
@@ -606,9 +607,9 @@
 	}
 }
 
-static void irc_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+static void irc_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
 {
-	struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	struct irc_buddy *ib;
 	const char *bname = purple_buddy_get_name(buddy);
 
@@ -633,7 +634,7 @@
 
 static void irc_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
-	struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	struct irc_buddy *ib;
 
 	ib = g_hash_table_lookup(irc->buddies, purple_buddy_get_name(buddy));
@@ -678,7 +679,7 @@
 {
 
 	PurpleConnection *gc = data;
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	int len;
 
 	if(!g_list_find(purple_connections_get_all(), gc)) {
@@ -716,7 +717,7 @@
 static void irc_input_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = data;
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	int len;
 
 	if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) {
@@ -746,7 +747,7 @@
 
 static void irc_chat_join (PurpleConnection *gc, GHashTable *data)
 {
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	const char *args[2];
 
 	args[0] = g_hash_table_lookup(data, "channel");
@@ -760,7 +761,7 @@
 
 static void irc_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
 {
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo = purple_find_chat(gc, id);
 	const char *args[2];
 
@@ -776,7 +777,7 @@
 
 static void irc_chat_leave (PurpleConnection *gc, int id)
 {
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo = purple_find_chat(gc, id);
 	const char *args[2];
 
@@ -791,7 +792,7 @@
 
 static int irc_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags)
 {
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo = purple_find_chat(gc, id);
 	const char *args[2];
 	char *tmp;
@@ -845,7 +846,7 @@
 	const char *name = NULL;
 	struct irc_conn *irc;
 
-	irc = gc->proto_data;
+	irc = purple_connection_get_protocol_data(gc);
 	name = purple_conversation_get_name(purple_find_chat(gc, id));
 
 	if (name == NULL)
@@ -863,7 +864,7 @@
 	PurpleRoomlistField *f;
 	char *buf;
 
-	irc = gc->proto_data;
+	irc = purple_connection_get_protocol_data(gc);
 
 	if (irc->roomlist)
 		purple_roomlist_unref(irc->roomlist);
@@ -897,7 +898,7 @@
 	if (gc == NULL)
 		return;
 
-	irc = gc->proto_data;
+	irc = purple_connection_get_protocol_data(gc);
 
 	purple_roomlist_set_in_progress(list, FALSE);
 
@@ -909,13 +910,14 @@
 
 static void irc_keepalive(PurpleConnection *gc)
 {
-	struct irc_conn *irc = gc->proto_data;
+	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	if ((time(NULL) - irc->recv_time) > PING_TIMEOUT)
 		irc_cmd_ping(irc, NULL, NULL, NULL);
 }
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),    /* struct_size */
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL |
 	OPT_PROTO_SLASH_COMMANDS_NATIVE,
 	NULL,					/* user_splits */
@@ -957,7 +959,6 @@
 	irc_keepalive,		/* keepalive */
 	NULL,					/* register_user */
 	NULL,					/* get_cb_info */
-	NULL,					/* get_cb_away */
 	NULL,					/* alias_buddy */
 	NULL,					/* group_buddy */
 	NULL,					/* rename_group */
@@ -982,15 +983,12 @@
 	NULL,                   /* unregister_user */
 	NULL,                   /* send_attention */
 	NULL,                   /* get_attention_types */
-	sizeof(PurplePluginProtocolInfo),    /* struct_size */
 	NULL,                    /* get_account_text_table */
 	NULL,                    /* initiate_media */
 	NULL,					 /* get_media_caps */
 	NULL,					 /* get_moods */
 	NULL,					 /* set_public_alias */
-	NULL,					 /* get_public_alias */
-	NULL,					 /* add_buddy_with_invite */
-	NULL					 /* add_buddies_with_invite */
+	NULL					 /* get_public_alias */
 };
 
 static gboolean load_plugin (PurplePlugin *plugin) {
--- a/libpurple/protocols/irc/parse.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/irc/parse.c	Mon Sep 26 14:57:21 2011 +0900
@@ -189,7 +189,7 @@
 	if (!gc)
 		return PURPLE_CMD_RET_FAILED;
 
-	irc = gc->proto_data;
+	irc = purple_connection_get_protocol_data(gc);
 
 	if ((cmdent = g_hash_table_lookup(irc->cmds, cmd)) == NULL)
 		return PURPLE_CMD_RET_FAILED;
--- a/libpurple/protocols/jabber/adhoccommands.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/adhoccommands.c	Mon Sep 26 14:57:21 2011 +0900
@@ -228,7 +228,8 @@
 		JabberAdHocCommands *cmd = data;
 		PurpleBuddy *buddy = (PurpleBuddy *) node;
 		PurpleAccount *account = purple_buddy_get_account(buddy);
-		JabberStream *js = purple_account_get_connection(account)->proto_data;
+		PurpleConnection *gc = purple_account_get_connection(account);
+		JabberStream *js = purple_connection_get_protocol_data(gc);
 
 		jabber_adhoc_execute(js, cmd);
 	}
@@ -321,7 +322,7 @@
 	JabberAdHocCommands *cmd = action->user_data;
 	if(cmd) {
 		PurpleConnection *gc = (PurpleConnection *) action->context;
-		JabberStream *js = gc->proto_data;
+		JabberStream *js = purple_connection_get_protocol_data(gc);
 
 		jabber_adhoc_execute(js, cmd);
 	}
--- a/libpurple/protocols/jabber/buddy.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/buddy.c	Mon Sep 26 14:57:21 2011 +0900
@@ -557,7 +557,7 @@
 	PurpleAccount *account = purple_connection_get_account(gc);
 
 	/* Publish the avatar as specified in XEP-0084 */
-	jabber_avatar_set(gc->proto_data, img);
+	jabber_avatar_set(purple_connection_get_protocol_data(gc), img);
 	/* Set the image in our vCard */
 	jabber_set_info(gc, purple_account_get_user_info(account));
 
--- a/libpurple/protocols/jabber/caps.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/caps.c	Mon Sep 26 14:57:21 2011 +0900
@@ -958,7 +958,7 @@
 		const char *prpl_id = purple_account_get_protocol_id(account);
 		if (g_str_equal("prpl-jabber", prpl_id) && purple_account_is_connected(account)) {
 			PurpleConnection *gc = purple_account_get_connection(account);
-			jabber_presence_send(gc->proto_data, TRUE);
+			jabber_presence_send(purple_connection_get_protocol_data(gc), TRUE);
 		}
 	}
 
--- a/libpurple/protocols/jabber/chat.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/chat.c	Mon Sep 26 14:57:21 2011 +0900
@@ -70,7 +70,7 @@
 GHashTable *jabber_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
 {
 	GHashTable *defaults;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
 
@@ -146,7 +146,7 @@
 	int id;
 	if (!gc)
 		return NULL;
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 	id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
 	return jabber_chat_find_by_id(js, id);
 }
@@ -154,7 +154,7 @@
 void jabber_chat_invite(PurpleConnection *gc, int id, const char *msg,
 		const char *name)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberChat *chat;
 	xmlnode *message, *body, *x, *invite;
 	char *room_jid;
@@ -360,7 +360,7 @@
 {
 	char *room, *server, *handle, *passwd;
 	JabberID *jid;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	char *tmp;
 
 	room = g_hash_table_lookup(data, "room");
@@ -418,10 +418,9 @@
 
 void jabber_chat_leave(PurpleConnection *gc, int id)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberChat *chat = jabber_chat_find_by_id(js, id);
 
-
 	if(!chat)
 		return;
 
@@ -459,7 +458,7 @@
 
 char *jabber_chat_buddy_real_name(PurpleConnection *gc, int id, const char *who)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberChat *chat;
 	JabberChatMember *jcm;
 
@@ -893,7 +892,7 @@
 
 PurpleRoomlist *jabber_roomlist_get_list(PurpleConnection *gc)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	GList *fields = NULL;
 	PurpleRoomlistField *f;
 
@@ -934,7 +933,7 @@
 
 	account = purple_roomlist_get_account(list);
 	gc = purple_account_get_connection(account);
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 
 	purple_roomlist_set_in_progress(list, FALSE);
 
--- a/libpurple/protocols/jabber/disco.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/disco.c	Mon Sep 26 14:57:21 2011 +0900
@@ -485,7 +485,7 @@
 			resp[0].hostname, resp[0].port);
 		account = purple_connection_get_account(js->gc);
 		js->stun_query =
-			purple_dnsquery_a_account(account, resp[0].hostname, resp[0].port,
+			purple_dnsquery_a(account, resp[0].hostname, resp[0].port,
 				jabber_disco_stun_lookup_cb, js);
 	}
 }
@@ -550,7 +550,7 @@
 		} else if (purple_network_get_stun_ip() == NULL ||
 		    purple_strequal(purple_network_get_stun_ip(), "")) {
 			js->srv_query_data =
-				purple_srv_resolve_account(
+				purple_srv_resolve(
 					purple_connection_get_account(js->gc), "stun", "udp",
 					js->user->domain,
 					jabber_disco_stun_srv_resolve_cb, js);
--- a/libpurple/protocols/jabber/google/jingleinfo.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/google/jingleinfo.c	Mon Sep 26 14:57:21 2011 +0900
@@ -117,7 +117,7 @@
 					purple_dnsquery_destroy(js->stun_query);
 
 				account = purple_connection_get_account(js->gc);
-				js->stun_query = purple_dnsquery_a_account(account, host, port,
+				js->stun_query = purple_dnsquery_a(account, host, port,
 					jabber_google_stun_lookup_cb, js);
 			}
 		}
--- a/libpurple/protocols/jabber/google/relay.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/google/relay.c	Mon Sep 26 14:57:21 2011 +0900
@@ -137,7 +137,7 @@
 	purple_debug_info("jabber",
 		"sending Google relay request %s to %s\n", request, url);
 	url_data =
-		purple_util_fetch_url_request(url, FALSE, NULL, FALSE, request, FALSE,
+		purple_util_fetch_url_request(NULL, url, FALSE, NULL, FALSE, request, FALSE, -1,
 			jabber_google_relay_fetch_cb, data);
 	if (url_data) {
 		js->google_relay_requests =
--- a/libpurple/protocols/jabber/jabber.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/jabber.c	Mon Sep 26 14:57:21 2011 +0900
@@ -618,7 +618,7 @@
 
 static gboolean jabber_keepalive_timeout(PurpleConnection *gc)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 					_("Ping timed out"));
 	js->keepalive_timeout = 0;
@@ -644,7 +644,7 @@
 		PurpleInputCondition cond)
 {
 	PurpleConnection *gc = data;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	int len;
 	static char buf[4096];
 
@@ -749,7 +749,7 @@
 		g_return_if_reached();
 	}
 
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 
 	if(js->state == JABBER_STREAM_CONNECTING)
 		jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
@@ -815,7 +815,7 @@
 			try_srv_connect(js);
 		} else {
 			purple_debug_info("jabber","Couldn't connect directly to %s.  Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
-			js->srv_query_data = purple_txt_resolve_account(
+			js->srv_query_data = purple_txt_resolve(
 					purple_connection_get_account(gc), "_xmppconnect",
 					js->user->domain, txt_resolved_cb, js);
 		}
@@ -844,7 +844,7 @@
 	/* If the connection is already disconnected, we don't need to do anything else */
 	g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
 
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 	js->gsc = NULL;
 
 	purple_connection_ssl_error (gc, error);
@@ -930,7 +930,8 @@
 	gchar *user;
 	gchar *slash;
 
-	js = gc->proto_data = g_new0(JabberStream, 1);
+	js = g_new0(JabberStream, 1);
+	purple_connection_set_protocol_data(gc, js);
 	js->gc = gc;
 	js->fd = -1;
 
@@ -1075,7 +1076,7 @@
 		jabber_login_connect(js, js->user->domain, connect_server,
 				purple_account_get_int(account, "port", 5222), TRUE);
 	} else {
-		js->srv_query_data = purple_srv_resolve_account(account, "xmpp-client",
+		js->srv_query_data = purple_srv_resolve(account, "xmpp-client",
 				"tcp", js->user->domain, srv_resolved_cb, js);
 	}
 }
@@ -1561,14 +1562,14 @@
 	if(gc->state != PURPLE_CONNECTED) {
 		if(gc->state != PURPLE_CONNECTING)
 			jabber_login(account);
-		js = gc->proto_data;
+		js = purple_connection_get_protocol_data(gc);
 		js->unregistration = TRUE;
 		js->unregistration_cb = cb;
 		js->unregistration_user_data = user_data;
 		return;
 	}
 
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 
 	if (js->unregistration) {
 		purple_debug_error("jabber", "Unregistration in process; ignoring duplicate request.\n");
@@ -1600,7 +1601,7 @@
 		jabber_send_raw(js, "</stream:stream>", -1);
 
 	if (js->srv_query_data)
-		purple_srv_cancel(js->srv_query_data);
+		purple_srv_txt_query_destroy(js->srv_query_data);
 
 	if(js->gsc) {
 		purple_ssl_close(js->gsc);
@@ -1728,7 +1729,7 @@
 
 	g_free(js);
 
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 }
 
 void jabber_stream_set_state(JabberStream *js, JabberStreamState state)
@@ -1782,7 +1783,7 @@
 
 void jabber_idle_set(PurpleConnection *gc, int idle)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	js->idle = idle ? time(NULL) - idle : idle;
 
@@ -2135,7 +2136,7 @@
 	if(!gc)
 		return NULL;
 
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 	if(js)
 		jb = jabber_buddy_find(js, purple_buddy_get_name(b), FALSE);
 
@@ -2176,8 +2177,8 @@
 	PurpleAccount *account = purple_buddy_get_account(b);
 	PurpleConnection *gc = purple_account_get_connection(account);
 
-	if (gc && gc->proto_data)
-		jb = jabber_buddy_find(gc->proto_data, purple_buddy_get_name(b), FALSE);
+	if (gc && purple_connection_get_protocol_data(gc))
+		jb = jabber_buddy_find(purple_connection_get_protocol_data(gc), purple_buddy_get_name(b), FALSE);
 
 	if(jb && !PURPLE_BUDDY_IS_ONLINE(b) && (jb->subscription & JABBER_SUB_PENDING || !(jb->subscription & JABBER_SUB_TO))) {
 		ret = g_strdup(_("Not Authorized"));
@@ -2252,6 +2253,7 @@
 	JabberBuddy *jb;
 	PurpleAccount *account;
 	PurpleConnection *gc;
+	JabberStream *js;
 
 	g_return_if_fail(b != NULL);
 
@@ -2260,9 +2262,11 @@
 
 	gc = purple_account_get_connection(account);
 	g_return_if_fail(gc != NULL);
-	g_return_if_fail(gc->proto_data != NULL);
-
-	jb = jabber_buddy_find(gc->proto_data, purple_buddy_get_name(b), FALSE);
+
+	js = purple_connection_get_protocol_data(gc);
+	g_return_if_fail(js != NULL);
+
+	jb = jabber_buddy_find(js, purple_buddy_get_name(b), FALSE);
 
 	if(jb) {
 		JabberBuddyResource *jbr = NULL;
@@ -2534,7 +2538,7 @@
 {
 
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *group;
 	PurpleRequestField *field;
@@ -2566,7 +2570,7 @@
 GList *jabber_actions(PurplePlugin *plugin, gpointer context)
 {
 	PurpleConnection *gc = (PurpleConnection *) context;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	GList *m = NULL;
 	PurplePluginAction *act;
 
@@ -2637,7 +2641,7 @@
 
 void jabber_convo_closed(PurpleConnection *gc, const char *who)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberID *jid;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
@@ -3162,7 +3166,7 @@
 static PurpleCmdRet jabber_cmd_buzz(PurpleConversation *conv,
 		const char *cmd, char **args, char **error, void *data)
 {
-	JabberStream *js = conv->account->gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(conv->account->gc);
 	const gchar *who;
 	gchar *description;
 	PurpleBuddy *buddy;
@@ -3209,7 +3213,7 @@
 
 gboolean jabber_send_attention(PurpleConnection *gc, const char *username, guint code)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	gchar *error = NULL;
 
 	if (!_jabber_send_buzz(js, username, &error)) {
@@ -3292,8 +3296,8 @@
 		      PurpleMediaSessionType type)
 {
 #ifdef USE_VV
-	JabberStream *js = (JabberStream *)
-			purple_account_get_connection(account)->proto_data;
+	PurpleConnection *gc = purple_account_get_connection(account);
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr = NULL;
 	char *resource;
@@ -3304,7 +3308,6 @@
 		return FALSE;
 	}
 
-
 	if((resource = jabber_get_resource(who)) != NULL) {
 		/* they've specified a resource, no need to ask or
 		 * default or anything, just do it */
@@ -3435,8 +3438,8 @@
 PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who)
 {
 #ifdef USE_VV
-	JabberStream *js = (JabberStream *)
-			purple_account_get_connection(account)->proto_data;
+	PurpleConnection *gc = purple_account_get_connection(account);
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
 	PurpleMediaCaps total = PURPLE_MEDIA_CAPS_NONE;
@@ -3524,7 +3527,7 @@
 
 gboolean jabber_can_receive_file(PurpleConnection *gc, const char *who)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	if (js) {
 		JabberBuddy *jb = jabber_buddy_find(js, who, FALSE);
@@ -3574,7 +3577,7 @@
 jabber_cmd_mood(PurpleConversation *conv,
 		const char *cmd, char **args, char **error, void *data)
 {
-	JabberStream *js = conv->account->gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(conv->account->gc);
 
 	if (js->pep) {
 		/* if no argument was given, unset mood */
@@ -3758,7 +3761,7 @@
 
 	if (!purple_account_is_connected(account))
 		return FALSE;
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 
 	if (!(resource = jabber_get_resource(jid)) ||
 	    !(jb = jabber_buddy_find(js, jid, FALSE)) ||
--- a/libpurple/protocols/jabber/jutil.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/jutil.c	Mon Sep 26 14:57:21 2011 +0900
@@ -583,11 +583,16 @@
 
 const char *jabber_normalize(const PurpleAccount *account, const char *in)
 {
-	PurpleConnection *gc = account ? account->gc : NULL;
-	JabberStream *js = gc ? gc->proto_data : NULL;
+	PurpleConnection *gc = NULL;
+	JabberStream *js = NULL;
 	static char buf[3072]; /* maximum legal length of a jabber jid */
 	JabberID *jid;
 
+	if (account)
+		gc = purple_account_get_connection(account);
+	if (gc)
+		js = purple_connection_get_protocol_data(gc);
+
 	jid = jabber_id_new_internal(in, TRUE);
 	if(!jid)
 		return NULL;
--- a/libpurple/protocols/jabber/libxmpp.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/libxmpp.c	Mon Sep 26 14:57:21 2011 +0900
@@ -53,6 +53,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK |
 #ifdef HAVE_CYRUS_SASL
 	OPT_PROTO_PASSWORD_OPTIONAL |
@@ -97,7 +98,6 @@
 	jabber_keepalive,				/* keepalive */
 	jabber_register_account,		/* register_user */
 	NULL,							/* get_cb_info */
-	NULL,							/* get_cb_away */
 	jabber_roster_alias_change,		/* alias_buddy */
 	jabber_roster_group_change,		/* group_buddy */
 	jabber_roster_group_rename,		/* rename_group */
@@ -122,16 +122,12 @@
 	jabber_unregister_account,		/* unregister_user */
 	jabber_send_attention,			/* send_attention */
 	jabber_attention_types,			/* attention_types */
-
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL, /* get_account_text_table */
 	jabber_initiate_media,          /* initiate_media */
 	jabber_get_media_caps,                  /* get_media_caps */
 	jabber_get_moods,  							/* get_moods */
 	NULL, /* set_public_alias */
-	NULL, /* get_public_alias */
-	NULL, /* add_buddy_with_invite */
-	NULL  /* add_buddies_with_invite */
+	NULL  /* get_public_alias */
 };
 
 static gboolean load_plugin(PurplePlugin *plugin)
--- a/libpurple/protocols/jabber/message.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/message.c	Mon Sep 26 14:57:21 2011 +0900
@@ -643,6 +643,8 @@
 					jabber_message_add_remote_smileys(js, to, packet);
 				}
 
+				xmlnode_strip_prefixes(child);
+
 				/* reformat xhtml so that img tags with a "cid:" src gets
 				  translated to the bare text of the emoticon (the "alt" attrib) */
 				/* this is done also when custom smiley retrieval is turned off,
@@ -1155,13 +1157,13 @@
 
 	resource = jabber_get_resource(who);
 
-	jb = jabber_buddy_find(gc->proto_data, who, TRUE);
+	jb = jabber_buddy_find(purple_connection_get_protocol_data(gc), who, TRUE);
 	jbr = jabber_buddy_find_resource(jb, resource);
 
 	g_free(resource);
 
 	jm = g_new0(JabberMessage, 1);
-	jm->js = gc->proto_data;
+	jm->js = purple_connection_get_protocol_data(gc);
 	jm->type = JABBER_MESSAGE_CHAT;
 	jm->chat_state = JM_STATE_ACTIVE;
 	jm->to = g_strdup(who);
@@ -1218,14 +1220,14 @@
 	if(!msg || !gc)
 		return 0;
 
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 	chat = jabber_chat_find_by_id(js, id);
 
 	if(!chat)
 		return 0;
 
 	jm = g_new0(JabberMessage, 1);
-	jm->js = gc->proto_data;
+	jm->js = purple_connection_get_protocol_data(gc);
 	jm->type = JABBER_MESSAGE_GROUPCHAT;
 	jm->to = g_strdup_printf("%s@%s", chat->room, chat->server);
 	jm->id = jabber_get_next_id(jm->js);
--- a/libpurple/protocols/jabber/oob.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/oob.c	Mon Sep 26 14:57:21 2011 +0900
@@ -76,7 +76,7 @@
 	JabberIq *iq;
 
 	iq = jabber_iq_new(jox->js, JABBER_IQ_RESULT);
-	xmlnode_set_attrib(iq->node, "to", xfer->who);
+	xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
 	jabber_iq_set_id(iq, jox->iq_id);
 
 	jabber_iq_send(iq);
@@ -89,7 +89,7 @@
 	JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
 	int len, total_len = strlen(jox->write_buffer);
 
-	len = write(xfer->fd, jox->write_buffer + jox->written_len,
+	len = purple_xfer_write(xfer, (guchar*) jox->write_buffer + jox->written_len,
 		total_len - jox->written_len);
 
 	if(len < 0 && errno == EAGAIN)
@@ -163,7 +163,7 @@
 	xmlnode *y, *z;
 
 	iq = jabber_iq_new(jox->js, JABBER_IQ_ERROR);
-	xmlnode_set_attrib(iq->node, "to", xfer->who);
+	xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
 	jabber_iq_set_id(iq, jox->iq_id);
 	y = xmlnode_new_child(iq->node, "error");
 	xmlnode_set_attrib(y, "code", code);
--- a/libpurple/protocols/jabber/presence.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/presence.c	Mon Sep 26 14:57:21 2011 +0900
@@ -299,11 +299,6 @@
 	jabber_presence_fake_to_self(js, status);
 }
 
-xmlnode *jabber_presence_create(JabberBuddyState state, const char *msg, int priority)
-{
-    return jabber_presence_create_js(NULL, state, msg, priority);
-}
-
 xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority)
 {
 	xmlnode *show, *status, *presence, *pri, *c;
@@ -393,7 +388,7 @@
 {
 	struct _jabber_add_permit *jap = data;
 	if(PURPLE_CONNECTION_IS_VALID(jap->gc))
-		jabber_presence_subscription_set(jap->gc->proto_data,
+		jabber_presence_subscription_set(purple_connection_get_protocol_data(jap->gc),
 			jap->who, "subscribed");
 	g_free(jap->who);
 	g_free(jap);
@@ -403,7 +398,7 @@
 {
 	struct _jabber_add_permit *jap = data;
 	if(PURPLE_CONNECTION_IS_VALID(jap->gc))
-		jabber_presence_subscription_set(jap->gc->proto_data,
+		jabber_presence_subscription_set(purple_connection_get_protocol_data(jap->gc),
 			jap->who, "unsubscribed");
 	g_free(jap->who);
 	g_free(jap);
--- a/libpurple/protocols/jabber/presence.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/presence.h	Mon Sep 26 14:57:21 2011 +0900
@@ -92,7 +92,6 @@
  */
 void jabber_presence_send(JabberStream *js, gboolean force);
 
-xmlnode *jabber_presence_create(JabberBuddyState state, const char *msg, int priority); /* DEPRECATED */
 xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority);
 void jabber_presence_parse(JabberStream *js, xmlnode *packet);
 void jabber_presence_subscription_set(JabberStream *js, const char *who,
--- a/libpurple/protocols/jabber/roster.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/roster.c	Mon Sep 26 14:57:21 2011 +0900
@@ -358,9 +358,9 @@
 }
 
 void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
-		PurpleGroup *group)
+		PurpleGroup *group, const char *message)
 {
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 	char *who;
 	JabberID *jid;
 	JabberBuddy *jb;
@@ -431,7 +431,7 @@
 		purple_debug_info("jabber", "jabber_roster_alias_change(): Aliased %s to %s\n",
 				name, alias ? alias : "(null)");
 
-		jabber_roster_update(gc->proto_data, name, NULL);
+		jabber_roster_update(purple_connection_get_protocol_data(gc), name, NULL);
 	}
 }
 
@@ -461,7 +461,7 @@
 	purple_debug_info("jabber", "jabber_roster_group_change(): Moving %s from %s to %s\n",
 	                  name, old_group, new_group);
 
-	jabber_roster_update(gc->proto_data, name, groups);
+	jabber_roster_update(purple_connection_get_protocol_data(gc), name, groups);
 }
 
 void jabber_roster_group_rename(PurpleConnection *gc, const char *old_name,
@@ -496,9 +496,9 @@
 		purple_debug_info("jabber", "jabber_roster_remove_buddy(): Removing %s from %s\n",
 		                  purple_buddy_get_name(buddy), purple_group_get_name(group));
 
-		jabber_roster_update(gc->proto_data, name, groups);
+		jabber_roster_update(purple_connection_get_protocol_data(gc), name, groups);
 	} else {
-		JabberIq *iq = jabber_iq_new_query(gc->proto_data, JABBER_IQ_SET,
+		JabberIq *iq = jabber_iq_new_query(purple_connection_get_protocol_data(gc), JABBER_IQ_SET,
 				"jabber:iq:roster");
 		xmlnode *query = xmlnode_get_child(iq->node, "query");
 		xmlnode *item = xmlnode_new_child(query, "item");
--- a/libpurple/protocols/jabber/roster.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/roster.h	Mon Sep 26 14:57:21 2011 +0900
@@ -32,7 +32,7 @@
                          JabberIqType type, const char *id, xmlnode *query);
 
 void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
-		PurpleGroup *group);
+		PurpleGroup *group, const char *message);
 void jabber_roster_alias_change(PurpleConnection *gc, const char *name,
 		const char *alias);
 void jabber_roster_group_change(PurpleConnection *gc, const char *name,
--- a/libpurple/protocols/jabber/si.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/si.c	Mon Sep 26 14:57:21 2011 +0900
@@ -90,8 +90,8 @@
 	for(xfers = js->file_transfers; xfers; xfers = xfers->next) {
 		PurpleXfer *xfer = xfers->data;
 		JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
-		if(jsx->stream_id && xfer->who &&
-				!strcmp(jsx->stream_id, sid) && !strcmp(xfer->who, from))
+		if(jsx->stream_id && purple_xfer_get_remote_user(xfer) &&
+				!strcmp(jsx->stream_id, sid) && !strcmp(purple_xfer_get_remote_user(xfer), from))
 			return xfer;
 	}
 
@@ -145,7 +145,7 @@
 	}
 
 	/* unknown file transfer type is assumed to be RECEIVE */
-	if(xfer->type == PURPLE_XFER_SEND)
+	if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
 	{
 		xmlnode *activate;
 		iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS);
@@ -153,14 +153,14 @@
 		query = xmlnode_get_child(iq->node, "query");
 		xmlnode_set_attrib(query, "sid", jsx->stream_id);
 		activate = xmlnode_new_child(query, "activate");
-		xmlnode_insert_data(activate, xfer->who, -1);
+		xmlnode_insert_data(activate, purple_xfer_get_remote_user(xfer), -1);
 
 		/* TODO: We need to wait for an activation result before starting */
 	}
 	else
 	{
 		iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, NS_BYTESTREAMS);
-		xmlnode_set_attrib(iq->node, "to", xfer->who);
+		xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
 		jabber_iq_set_id(iq, jsx->iq_id);
 		query = xmlnode_get_child(iq->node, "query");
 		su = xmlnode_new_child(query, "streamhost-used");
@@ -231,7 +231,7 @@
 		if(jsx->iq_id)
 			jabber_iq_set_id(iq, jsx->iq_id);
 
-		xmlnode_set_attrib(iq->node, "to", xfer->who);
+		xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
 		error = xmlnode_new_child(iq->node, "error");
 		xmlnode_set_attrib(error, "code", "404");
 		xmlnode_set_attrib(error, "type", "cancel");
@@ -273,7 +273,7 @@
 		purple_proxy_info_destroy(jsx->gpi);
 	jsx->gpi = NULL;
 
-	dstjid = jabber_id_new(xfer->who);
+	dstjid = jabber_id_new(purple_xfer_get_remote_user(xfer));
 
 	/* TODO: Deal with zeroconf */
 
@@ -286,7 +286,7 @@
 		purple_proxy_info_set_port(jsx->gpi, streamhost->port);
 
 		/* unknown file transfer type is assumed to be RECEIVE */
-		if(xfer->type == PURPLE_XFER_SEND)
+		if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
 			dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, jsx->js->user->node, jsx->js->user->domain,
 				jsx->js->user->resource, dstjid->node, dstjid->domain, dstjid->resource);
 		else
@@ -304,7 +304,7 @@
 		g_free(dstaddr);
 
 		/* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */
-		if (xfer->type != PURPLE_XFER_SEND && jsx->connect_data != NULL)
+		if (purple_xfer_get_type(xfer) != PURPLE_XFER_SEND && jsx->connect_data != NULL)
 			jsx->connect_timeout = purple_timeout_add_seconds(
 				STREAMHOST_CONNECT_TIMEOUT, connect_timeout_cb, xfer);
 
@@ -384,8 +384,6 @@
 	if (len < 0 && errno == EAGAIN)
 		return;
 	else if (len < 0) {
-		purple_input_remove(xfer->watcher);
-		xfer->watcher = 0;
 		g_free(jsx->rxqueue);
 		jsx->rxqueue = NULL;
 		close(source);
@@ -428,8 +426,6 @@
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0) {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
@@ -443,8 +439,6 @@
 		purple_debug_info("jabber", "Invalid socks5 conn req. header[0x%x,0x%x,0x%x,0x%x,0x%x]\n",
 				  jsx->rxqueue[0], jsx->rxqueue[1], jsx->rxqueue[2],
 				  jsx->rxqueue[3], jsx->rxqueue[4]);
-		purple_input_remove(xfer->watcher);
-		xfer->watcher = 0;
 		close(source);
 		purple_xfer_cancel_remote(xfer);
 		return;
@@ -457,8 +451,6 @@
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0) {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
@@ -477,7 +469,7 @@
 
 	dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id,
 			jsx->js->user->node, jsx->js->user->domain,
-			jsx->js->user->resource, xfer->who);
+			jsx->js->user->resource, purple_xfer_get_remote_user(xfer));
 
 	/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
 	hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
@@ -536,8 +528,6 @@
 	if (len < 0 && errno == EAGAIN)
 		return;
 	else if (len < 0) {
-		purple_input_remove(xfer->watcher);
-		xfer->watcher = 0;
 		g_free(jsx->rxqueue);
 		jsx->rxqueue = NULL;
 		close(source);
@@ -549,11 +539,9 @@
 	if (jsx->rxlen < jsx->rxmaxlen)
 		return;
 
-	purple_input_remove(xfer->watcher);
-	xfer->watcher = 0;
-
 	/* If we sent a "Success", wait for a response, otherwise give up and cancel */
 	if (jsx->rxqueue[1] == 0x00) {
+		purple_input_remove(xfer->watcher);
 		xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
 			jabber_si_xfer_bytestreams_send_read_again_cb, xfer);
 		g_free(jsx->rxqueue);
@@ -586,9 +574,6 @@
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0) {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
-			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
 		}
@@ -605,9 +590,6 @@
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0) {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
-			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
 		}
@@ -620,13 +602,9 @@
 	if(jsx->rxlen -2 < jsx->rxqueue[1])
 		return;
 
-	purple_input_remove(xfer->watcher);
-	xfer->watcher = 0;
-
 	purple_debug_info("jabber", "checking to make sure we're socks FIVE\n");
 
 	if(jsx->rxqueue[0] != 0x05) {
-		close(source);
 		purple_xfer_cancel_remote(xfer);
 		return;
 	}
@@ -643,6 +621,7 @@
 			jsx->rxqueue = g_malloc(jsx->rxmaxlen);
 			jsx->rxqueue[0] = 0x05;
 			jsx->rxqueue[1] = 0x00;
+			purple_input_remove(xfer->watcher);
 			xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
 				jabber_si_xfer_bytestreams_send_read_response_cb,
 				xfer);
@@ -660,6 +639,7 @@
 	jsx->rxqueue = g_malloc(jsx->rxmaxlen);
 	jsx->rxqueue[0] = 0x05;
 	jsx->rxqueue[1] = 0xFF;
+	purple_input_remove(xfer->watcher);
 	xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
 		jabber_si_xfer_bytestreams_send_read_response_cb, xfer);
 	jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
@@ -850,7 +830,7 @@
 	purple_xfer_unref(xfer);
 
 	iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS);
-	xmlnode_set_attrib(iq->node, "to", xfer->who);
+	xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
 	query = xmlnode_get_child(iq->node, "query");
 
 	xmlnode_set_attrib(query, "sid", jsx->stream_id);
@@ -868,7 +848,7 @@
 		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);
+		g_snprintf(port, sizeof(port), "%hu", purple_xfer_get_local_port(xfer));
 
 		public_ip = purple_network_get_my_ip(jsx->js->fd);
 
@@ -981,7 +961,7 @@
 		purple_debug_info("jabber", "Skipping attempting local streamhost.\n");
 		jsx->listen_data = NULL;
 	} else
-		jsx->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+		jsx->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, TRUE,
 				jabber_si_xfer_bytestreams_listen_cb, xfer);
 
 	if (jsx->listen_data == NULL) {
@@ -1251,19 +1231,20 @@
 
 	purple_xfer_prepare_thumbnail(xfer, "jpeg,png");
 #endif
-	/* yaz */
-	f1 = g_filename_display_basename(xfer->local_filename);
-	f2 = botch_utf(f1, strlen(f1), &dummy);
-	if(f2) {
-		xfer->filename = f2;
-		g_free(f1); f1 = NULL;
-	}
+    /* yaz */
+    f1 = g_filename_display_basename(purple_xfer_get_local_filename(xfer));
+    f2 = botch_utf(f1, strlen(f1), &dummy);
+    if(f2){
+        purple_xfer_set_filename(xfer, (char *)f2);
+    }
     else {
-		xfer->filename = f1;
+        purple_xfer_set_filename(xfer, (char *)f1);
     }
+    g_free(f1); f1 = NULL;
+    g_free(f2); f2 = NULL;
 
 	iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
-	xmlnode_set_attrib(iq->node, "to", xfer->who);
+	xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
 	si = xmlnode_new_child(iq->node, "si");
 	xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
 	jsx->stream_id = jabber_get_next_id(jsx->js);
@@ -1272,8 +1253,8 @@
 
 	file = xmlnode_new_child(si, "file");
 	xmlnode_set_namespace(file, NS_SI_FILE_TRANSFER);
-	xmlnode_set_attrib(file, "name", xfer->filename);
-	g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
+	xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer));
+	g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, purple_xfer_get_size(xfer));
 	xmlnode_set_attrib(file, "size", buf);
 	/* maybe later we'll do hash and date attribs */
 
@@ -1337,8 +1318,7 @@
 			jabber_iq_remove_callback_by_id(js, jsx->iq_id);
 		if (jsx->local_streamhost_fd >= 0)
 			close(jsx->local_streamhost_fd);
-		if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND &&
-			xfer->fd >= 0) {
+		if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xfer->fd >= 0) {
 			purple_debug_info("jabber", "remove port mapping\n");
 			purple_network_remove_port_mapping(xfer->fd);
 		}
@@ -1404,7 +1384,7 @@
 		JabberIq *iq;
 		xmlnode *error, *child;
 		iq = jabber_iq_new(js, JABBER_IQ_ERROR);
-		xmlnode_set_attrib(iq->node, "to", xfer->who);
+		xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
 		jabber_iq_set_id(iq, jsx->iq_id);
 
 		error = xmlnode_new_child(iq->node, "error");
@@ -1472,7 +1452,7 @@
 static void do_transfer_send(PurpleXfer *xfer, const char *resource)
 {
 	JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
-	char **who_v = g_strsplit(xfer->who, "/", 2);
+	char **who_v = g_strsplit(purple_xfer_get_remote_user(xfer), "/", 2);
 	char *who;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr = NULL;
@@ -1529,7 +1509,7 @@
 		char *resource;
 		GList *resources = NULL;
 
-		if(NULL != (resource = jabber_get_resource(xfer->who))) {
+		if(NULL != (resource = jabber_get_resource(purple_xfer_get_remote_user(xfer)))) {
 			/* they've specified a resource, no need to ask or
 			 * default or anything, just do it */
 
@@ -1538,7 +1518,7 @@
 			return;
 		}
 
-		jb = jabber_buddy_find(jsx->js, xfer->who, TRUE);
+		jb = jabber_buddy_find(jsx->js, purple_xfer_get_remote_user(xfer), TRUE);
 
 		if (jb) {
 			GList *l;
@@ -1562,11 +1542,11 @@
 			char *msg;
 
 			if(!jb) {
-				msg = g_strdup_printf(_("Unable to send file to %s, invalid JID"), xfer->who);
+				msg = g_strdup_printf(_("Unable to send file to %s, invalid JID"), purple_xfer_get_remote_user(xfer));
 			} else if(jb->subscription & JABBER_SUB_TO) {
-				msg = g_strdup_printf(_("Unable to send file to %s, user is not online"), xfer->who);
+				msg = g_strdup_printf(_("Unable to send file to %s, user is not online"), purple_xfer_get_remote_user(xfer));
 			} else {
-				msg = g_strdup_printf(_("Unable to send file to %s, not subscribed to user presence"), xfer->who);
+				msg = g_strdup_printf(_("Unable to send file to %s, not subscribed to user presence"), purple_xfer_get_remote_user(xfer));
 			}
 
 			purple_notify_error(jsx->js->gc, _("File Send Failed"), _("File Send Failed"), msg);
@@ -1579,7 +1559,7 @@
 		} else {
 			/* we've got multiple resources, we need to pick one to send to */
 			GList *l;
-			char *msg = g_strdup_printf(_("Please select the resource of %s to which you would like to send a file"), xfer->who);
+			char *msg = g_strdup_printf(_("Please select the resource of %s to which you would like to send a file"), purple_xfer_get_remote_user(xfer));
 			PurpleRequestFields *fields = purple_request_fields_new();
 			PurpleRequestField *field = purple_request_field_choice_new("resource", _("Resource"), 0);
 			PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
@@ -1595,7 +1575,7 @@
 
 			purple_request_fields(jsx->js->gc, _("Select a Resource"), msg, NULL, fields,
 					_("Send File"), G_CALLBACK(resource_select_ok_cb), _("Cancel"), G_CALLBACK(resource_select_cancel_cb),
-					jsx->js->gc->account, xfer->who, NULL, xfer);
+					jsx->js->gc->account, purple_xfer_get_remote_user(xfer), NULL, xfer);
 
 			g_free(msg);
 		}
@@ -1605,7 +1585,7 @@
 		xmlnode *si, *feature, *x, *field, *value;
 
 		iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT);
-		xmlnode_set_attrib(iq->node, "to", xfer->who);
+		xmlnode_set_attrib(iq->node, "to", purple_xfer_get_remote_user(xfer));
 		if(jsx->iq_id)
 			jabber_iq_set_id(iq, jsx->iq_id);
 		else
@@ -1649,7 +1629,7 @@
 	PurpleXfer *xfer;
 	JabberSIXfer *jsx;
 
-	js = gc->proto_data;
+	js = purple_connection_get_protocol_data(gc);
 
 	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
 	if (xfer)
--- a/libpurple/protocols/jabber/useravatar.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/useravatar.c	Mon Sep 26 14:57:21 2011 +0900
@@ -377,7 +377,7 @@
 				JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1);
 				info->js = js;
 
-				url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE,
+				url_data = purple_util_fetch_url(url, TRUE, NULL, TRUE,
 										  MAX_HTTP_BUDDYICON_BYTES,
 										  do_buddy_avatar_update_fromurl, info);
 				if (url_data) {
--- a/libpurple/protocols/jabber/usertune.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/jabber/usertune.c	Mon Sep 26 14:57:21 2011 +0900
@@ -117,7 +117,7 @@
 
 void jabber_tune_set(PurpleConnection *gc, const PurpleJabberTuneInfo *tuneinfo) {
 	xmlnode *publish, *tunenode;
-	JabberStream *js = gc->proto_data;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	publish = xmlnode_new("publish");
 	xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/tune");
--- a/libpurple/protocols/msn/msg.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/msn/msg.c	Mon Sep 26 14:57:21 2011 +0900
@@ -984,7 +984,7 @@
 			msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
 		}
 
-		msn_object_destroy(obj);
+		msn_object_destroy(obj, FALSE);
 		obj =   NULL;
 		who =   NULL;
 		sha1 = NULL;
@@ -1039,7 +1039,7 @@
 		slplink = msn_session_get_slplink(session, who);
 		msn_slplink_request_object(slplink, data, got_wink_cb, NULL, obj);
 
-		msn_object_destroy(obj);
+		msn_object_destroy(obj, FALSE);
 
 
 	} else if (!strcmp(id, "3")) {
@@ -1059,7 +1059,7 @@
 		slplink = msn_session_get_slplink(session, who);
 		msn_slplink_request_object(slplink, data, got_voiceclip_cb, NULL, obj);
 
-		msn_object_destroy(obj);
+		msn_object_destroy(obj, FALSE);
 
 	} else if (!strcmp(id, "4")) {
 		/* Action */
--- a/libpurple/protocols/msn/msn.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/msn/msn.c	Mon Sep 26 14:57:21 2011 +0900
@@ -119,7 +119,7 @@
 	MsnSwitchBoard *swboard;
 
 	msg = msn_message_new_nudge();
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	swboard = msn_session_get_swboard(session, username, MSN_SB_FLAG_IM);
 
 	msn_switchboard_send_msg(swboard, msg, TRUE);
@@ -336,7 +336,7 @@
 	MsnSession *session;
 	MsnTransaction *trans;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	cmdproc = session->notification->cmdproc;
 
 	if (entry == NULL || *entry == '\0')
@@ -394,7 +394,7 @@
 	const char *mobile_number = NULL;
 	gsize payload_len;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	cmdproc = session->notification->cmdproc;
 
 	page = msn_page_new();
@@ -674,7 +674,7 @@
 	MsnSession *session;
 
 	gc = (PurpleConnection *) action->context;
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	purple_request_input(gc, NULL, _("Set your home phone number."), NULL,
 					   msn_user_get_home_phone(session->user), FALSE, FALSE, NULL,
@@ -691,7 +691,7 @@
 	MsnSession *session;
 
 	gc = (PurpleConnection *) action->context;
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	purple_request_input(gc, NULL, _("Set your work phone number."), NULL,
 					   msn_user_get_work_phone(session->user), FALSE, FALSE, NULL,
@@ -708,7 +708,7 @@
 	MsnSession *session;
 
 	gc = (PurpleConnection *) action->context;
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	purple_request_input(gc, NULL, _("Set your mobile phone number."), NULL,
 					   msn_user_get_mobile_phone(session->user), FALSE, FALSE, NULL,
@@ -746,7 +746,7 @@
 	MsnSession *session;
 	char *title;
 
-	session = pc->proto_data;
+	session = purple_connection_get_protocol_data(pc);
 
 	title = g_strdup_printf(_("Blocked Text for %s"), session->account->username);
 	if (session->blocked_text == NULL) {
@@ -770,7 +770,7 @@
 	MsnSession *session;
 
 	gc = (PurpleConnection *) action->context;
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	if (!session->passport_info.email_enabled) {
 		purple_notify_error(gc, NULL,
@@ -837,7 +837,7 @@
 	MsnTransaction *trans;
 
 	account = purple_connection_get_account(gc);
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	cmdproc = session->notification->cmdproc;
 
 	if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
@@ -867,7 +867,7 @@
 	account = purple_buddy_get_account(buddy);
 	gc = purple_account_get_connection(account);
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	swboard = msn_switchboard_new(session);
 	msn_switchboard_request(swboard);
@@ -906,7 +906,7 @@
 	MsnSession *session;
 	PurpleXfer *xfer;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
 
@@ -945,7 +945,7 @@
 	g_free(normal);
 
 	if (ret) {
-		MsnSession *session = gc->proto_data;
+		MsnSession *session = purple_connection_get_protocol_data(gc);
 		if (session) {
 			MsnUser *user = msn_userlist_find_user(session->userlist, who);
 			if (user) {
@@ -1243,7 +1243,7 @@
 	PurplePluginAction *act;
 
 	gc = (PurpleConnection *) context;
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	act = purple_plugin_action_new(_("Set Friendly Name..."),
 								 msn_show_set_friendly_name);
@@ -1380,7 +1380,7 @@
 
 	session = msn_session_new(account);
 
-	gc->proto_data = session;
+	purple_connection_set_protocol_data(gc, session);
 	gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR |
 		PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY;
 
@@ -1414,13 +1414,13 @@
 {
 	MsnSession *session;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	g_return_if_fail(session != NULL);
 
 	msn_session_destroy(session);
 
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 }
 
 static gboolean
@@ -1485,7 +1485,7 @@
 static void msn_emoticon_destroy(MsnEmoticon *emoticon)
 {
 	if (emoticon->obj)
-		msn_object_destroy(emoticon->obj);
+		msn_object_destroy(emoticon->obj, FALSE);
 	g_free(emoticon->smile);
 	g_free(emoticon);
 }
@@ -1571,7 +1571,7 @@
 	account = purple_connection_get_account(gc);
 	username = purple_account_get_username(account);
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	swboard = msn_session_find_swboard(session, who);
 
 	if (!strncmp("tel:+", who, 5)) {
@@ -1698,7 +1698,7 @@
 	MsnMessage *msg;
 
 	account = purple_connection_get_account(gc);
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	/*
 	 * TODO: I feel like this should be "if (state != PURPLE_TYPING)"
@@ -1747,7 +1747,7 @@
 
 	if (gc != NULL)
 	{
-		session = gc->proto_data;
+		session = purple_connection_get_protocol_data(gc);
 		msn_change_status(session);
 	}
 }
@@ -1757,7 +1757,7 @@
 {
 	MsnSession *session;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	msn_change_status(session);
 }
@@ -1887,7 +1887,7 @@
 	MsnSession *session;
 	MsnUserList *userlist;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	userlist = session->userlist;
 
 	if (!session->logged_in)
@@ -1904,7 +1904,7 @@
 	MsnUserList *userlist;
 	MsnUser *user;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	userlist = session->userlist;
 	user = msn_userlist_find_user(userlist, who);
 
@@ -1932,7 +1932,7 @@
 	MsnUserList *userlist;
 	MsnUser *user;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	userlist = session->userlist;
 	user = msn_userlist_find_user(userlist, who);
 
@@ -1959,7 +1959,7 @@
 	MsnUserList *userlist;
 	MsnUser *user;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	userlist = session->userlist;
 
 	if (!session->logged_in)
@@ -1982,7 +1982,7 @@
 	MsnUserList *userlist;
 	MsnUser *user;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	userlist = session->userlist;
 
 	if (!session->logged_in)
@@ -2011,7 +2011,7 @@
 	MsnSession *session;
 	MsnSwitchBoard *swboard;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	swboard = msn_session_find_swboard_with_id(session, id);
 
@@ -2036,7 +2036,7 @@
 	MsnSwitchBoard *swboard;
 	PurpleConversation *conv;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	swboard = msn_session_find_swboard_with_id(session, id);
 
@@ -2073,7 +2073,7 @@
 	GString *emoticons = NULL;
 
 	account = purple_connection_get_account(gc);
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	username = purple_account_get_username(account);
 	swboard = msn_session_find_swboard_with_id(session, id);
 
@@ -2139,7 +2139,7 @@
 	MsnSession *session;
 	MsnTransaction *trans;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	if (!session->http_method)
 	{
@@ -2157,7 +2157,7 @@
 {
 	MsnSession *session;
 
-	session = pc->proto_data;
+	session = purple_connection_get_protocol_data(pc);
 
 	msn_update_contact(session, name, MSN_UPDATE_ALIAS, alias);
 }
@@ -2169,7 +2169,7 @@
 	MsnSession *session;
 	MsnUserList *userlist;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	userlist = session->userlist;
 
 	msn_userlist_move_buddy(userlist, who, old_group_name, new_group_name);
@@ -2182,7 +2182,7 @@
 	MsnSession *session;
 	const char *gname;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->userlist != NULL);
@@ -2206,7 +2206,7 @@
 	MsnSwitchBoard *swboard;
 	PurpleConversation *conv;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	swboard = msn_session_find_swboard(session, who);
 
@@ -2242,7 +2242,7 @@
 	MsnSession *session;
 	MsnUser *user;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	user = session->user;
 
 	msn_user_set_buddy_icon(user, img);
@@ -2256,7 +2256,7 @@
 	MsnSession *session;
 	const char *gname;
 
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	gname = purple_group_get_name(group);
 
 	purple_debug_info("msn", "Remove group %s\n", gname);
@@ -2774,7 +2774,7 @@
 	/* Try to put the photo in there too, if there's one */
 	if (photo_url_text)
 	{
-		url_data = purple_util_fetch_url_len(photo_url_text, FALSE, NULL, FALSE,
+		url_data = purple_util_fetch_url(photo_url_text, FALSE, NULL, FALSE,
 		                                     MAX_HTTP_BUDDYICON_BYTES,
 		                                     msn_got_photo, info2_data);
 		session->url_datas = g_slist_prepend(session->url_datas, url_data);
@@ -2873,7 +2873,7 @@
 
 	url_data = purple_util_fetch_url(url, FALSE,
 	                                 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
-	                                 TRUE, msn_got_info, data);
+	                                 TRUE, -1, msn_got_info, data);
 	session->url_datas = g_slist_prepend(session->url_datas, url_data);
 
 	g_free(url);
@@ -2968,6 +2968,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),	/* struct_size */
 	OPT_PROTO_MAIL_CHECK|OPT_PROTO_INVITE_MESSAGE,
 	NULL,                               /* user_splits */
 	NULL,                               /* protocol_options */
@@ -2989,7 +2990,7 @@
 	msn_set_status,                     /* set_away */
 	msn_set_idle,                       /* set_idle */
 	NULL,                               /* change_passwd */
-	NULL,                               /* add_buddy */
+	msn_add_buddy,                      /* add_buddy */
 	NULL,                               /* add_buddies */
 	msn_rem_buddy,                      /* remove_buddy */
 	NULL,                               /* remove_buddies */
@@ -3008,7 +3009,6 @@
 	msn_keepalive,                      /* keepalive */
 	NULL,                               /* register_user */
 	NULL,                               /* get_cb_info */
-	NULL,                               /* get_cb_away */
 	msn_alias_buddy,                    /* alias_buddy */
 	msn_group_buddy,                    /* group_buddy */
 	msn_rename_group,                   /* rename_group */
@@ -3033,15 +3033,12 @@
 	NULL,                               /* unregister_user */
 	msn_send_attention,                 /* send_attention */
 	msn_attention_types,                /* attention_types */
-	sizeof(PurplePluginProtocolInfo),	/* struct_size */
 	msn_get_account_text_table,         /* get_account_text_table */
 	NULL,                               /* initiate_media */
 	NULL,                               /* get_media_caps */
 	NULL,                               /* get_moods */
 	msn_set_public_alias,               /* set_public_alias */
-	msn_get_public_alias,               /* get_public_alias */
-	msn_add_buddy,                      /* add_buddy_with_invite */
-	NULL                                /* add_buddies_with_invite */
+	msn_get_public_alias                /* get_public_alias */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/msn/object.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/msn/object.c	Mon Sep 26 14:57:21 2011 +0900
@@ -103,7 +103,7 @@
 	if (obj->creator == NULL || obj->size == 0 || obj->type == 0
 	 || obj->sha1d == NULL) {
 		purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str);
-		msn_object_destroy(obj);
+		msn_object_destroy(obj, FALSE);
 		return NULL;
 	}
 
@@ -111,12 +111,12 @@
 		/* Location/friendly are required for non-buddyicon objects */
 		if (obj->type != MSN_OBJECT_USERTILE) {
 			purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str);
-			msn_object_destroy(obj);
+			msn_object_destroy(obj, FALSE);
 			return NULL;
 		/* Buddy icon object can contain Url/Url1 instead */
 		} else if (obj->url == NULL || obj->url1 == NULL) {
 			purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str);
-			msn_object_destroy(obj);
+			msn_object_destroy(obj, FALSE);
 			return NULL;
 		}
 	}
@@ -193,10 +193,13 @@
 }
 
 void
-msn_object_destroy(MsnObject *obj)
+msn_object_destroy(MsnObject *obj, gboolean only_remote)
 {
 	g_return_if_fail(obj != NULL);
 
+	if (only_remote && obj->local)
+		return;
+
 	g_free(obj->creator);
 	g_free(obj->location);
 	g_free(obj->friendly);
--- a/libpurple/protocols/msn/object.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/msn/object.h	Mon Sep 26 14:57:21 2011 +0900
@@ -86,9 +86,10 @@
 /**
  * Destroys an MsnObject structure.
  *
- * @param obj The object structure.
+ * @param obj         The object structure.
+ * @param only_remote Only destroy non-local objects.
  */
-void msn_object_destroy(MsnObject *obj);
+void msn_object_destroy(MsnObject *obj, gboolean only_remote);
 
 /**
  * Outputs a string representation of an MsnObject.
--- a/libpurple/protocols/msn/slp.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/msn/slp.c	Mon Sep 26 14:57:21 2011 +0900
@@ -253,7 +253,7 @@
 			data->session = session;
 			data->remote_user = user->passport;
 			data->sha1 = info;
-			url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE, 200*1024,
+			url_data = purple_util_fetch_url(url, TRUE, NULL, TRUE, 200*1024,
 			                                     fetched_user_display, data);
 			session->url_datas = g_slist_prepend(session->url_datas, url_data);
 		} else {
--- a/libpurple/protocols/msn/slpcall.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/msn/slpcall.c	Mon Sep 26 14:57:21 2011 +0900
@@ -485,7 +485,7 @@
 			if (img)
 				purple_imgstore_ref(img);
 		}
-		msn_object_destroy(obj);
+		msn_object_destroy(obj, FALSE);
 
 		if (img != NULL) {
 			/* DATA PREP */
@@ -731,7 +731,9 @@
 
 			dc->listen_data = purple_network_listen_range(
 				0, 0,
+				AF_UNSPEC,
 				SOCK_STREAM,
+				TRUE,
 				msn_dc_listen_socket_created_cb,
 				dc
 			);
@@ -832,7 +834,9 @@
 
 		dc->listen_data = purple_network_listen_range(
 			0, 0,
+			AF_UNSPEC,
 			SOCK_STREAM,
+			TRUE,
 			msn_dc_listen_socket_created_cb,
 			dc
 		);
--- a/libpurple/protocols/msn/user.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/msn/user.c	Mon Sep 26 14:57:21 2011 +0900
@@ -76,7 +76,7 @@
 	}
 
 	if (user->msnobj != NULL)
-		msn_object_destroy(user->msnobj);
+		msn_object_destroy(user->msnobj, FALSE);
 
 	g_free(user->passport);
 	g_free(user->friendly_name);
@@ -406,7 +406,7 @@
 
 	gc = purple_account_get_connection(account);
 	if (gc != NULL)
-		session = gc->proto_data;
+		session = purple_connection_get_protocol_data(gc);
 
 	if ((session != NULL) && (user = msn_userlist_find_user(session->userlist, name)) != NULL)
 	{
@@ -588,8 +588,8 @@
 {
 	g_return_if_fail(user != NULL);
 
-	if (user->msnobj != NULL && !msn_object_find_local(msn_object_get_sha1(obj)))
-		msn_object_destroy(user->msnobj);
+	if (user->msnobj != NULL)
+		msn_object_destroy(user->msnobj, TRUE);
 
 	user->msnobj = obj;
 
--- a/libpurple/protocols/msn/userlist.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/msn/userlist.c	Mon Sep 26 14:57:21 2011 +0900
@@ -54,7 +54,7 @@
 
 	if (PURPLE_CONNECTION_IS_VALID(pa->gc))
 	{
-		MsnSession *session = pa->gc->proto_data;
+		MsnSession *session = purple_connection_get_protocol_data(pa->gc);
 		MsnUserList *userlist = session->userlist;
 		PurpleAccount *account = purple_connection_get_account(pa->gc);
 
@@ -79,7 +79,7 @@
 
 	if (PURPLE_CONNECTION_IS_VALID(pa->gc))
 	{
-		MsnSession *session = pa->gc->proto_data;
+		MsnSession *session = purple_connection_get_protocol_data(pa->gc);
 		MsnUserList *userlist = session->userlist;
 		MsnCallbackState *state = msn_callback_state_new(session);
 
--- a/libpurple/protocols/mxit/formcmds.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/mxit/formcmds.c	Mon Sep 26 14:57:21 2011 +0900
@@ -86,7 +86,6 @@
 static void mxit_cb_ii_returned(PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message)
 {
 	struct ii_url_request*	iireq		= (struct ii_url_request*) user_data;
-	char*					ii_data;
 	int*					intptr		= NULL;
 	int						id;
 
@@ -106,12 +105,8 @@
 		goto done;
 	}
 
-	/* make a copy of the data */
-	ii_data = g_malloc(len);
-	memcpy(ii_data, (const char*) url_text, len);
-
-	/* we now have the inline image, store it in the imagestore */
-	id = purple_imgstore_add_with_id(ii_data, len, NULL);
+	/* we now have the inline image, store a copy in the imagestore */
+	id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
 
 	/* map the inline image id to purple image id */
 	intptr = g_malloc(sizeof(int));
@@ -367,7 +362,7 @@
 				purple_debug_info(MXIT_PLUGIN_ID, "sending request for inline image '%s'\n", iireq->url);
 
 				/* request the image (reference: "libpurple/util.h") */
-				purple_util_fetch_url_request(iireq->url, TRUE, NULL, TRUE, NULL, FALSE, mxit_cb_ii_returned, iireq);
+				purple_util_fetch_url(iireq->url, TRUE, NULL, TRUE, -1, mxit_cb_ii_returned, iireq);
 				mx->img_count++;
 			}
 		}
--- a/libpurple/protocols/mxit/login.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/mxit/login.c	Mon Sep 26 14:57:21 2011 +0900
@@ -549,7 +549,7 @@
 			session->logindata->wapserver, session->logindata->sessionid, purple_url_encode( session->acc->username ), PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CLIENT_ID, MXIT_CP_ARCH,
 			captcha_resp, session->logindata->cc, session->logindata->locale, ( state == MXIT_STATE_REGISTER1 ) ? 0 : 1, MXIT_CP_PLATFORM, MXIT_CP_OS,
 			MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH, time( NULL ) );
-	url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_clientinfo2, session );
+	url_data = purple_util_fetch_url_request( session->acc, url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, -1, mxit_cb_clientinfo2, session );
 
 #ifdef	DEBUG_PROTOCOL
 	purple_debug_info( MXIT_PLUGIN_ID, "HTTP REQUEST: '%s'\n", url );
@@ -656,7 +656,7 @@
 			/* oops, this is not good, time to bail */
 			break;
 		}
-		purple_request_field_list_add( field, country[1], g_strdup( country[0] ) );
+		purple_request_field_list_add_icon( field, country[1], NULL, g_strdup( country[0] ) );
 		if ( strcmp( country[1], parts[6] ) == 0 ) {
 			/* based on the user's IP, this is his current country code, so we default to it */
 			purple_request_field_list_add_selected( field, country[1] );
@@ -677,7 +677,7 @@
 			/* oops, this is not good, time to bail */
 			break;
 		}
-		purple_request_field_list_add( field, locale[1], g_strdup( locale[0] ) );
+		purple_request_field_list_add_icon( field, locale[1], NULL, g_strdup( locale[0] ) );
 		g_strfreev( locale );
 	}
 	purple_request_field_list_add_selected( field, "English" );
@@ -713,7 +713,7 @@
 
 	/* reference: "libpurple/util.h" */
 	url = g_strdup_printf( "%s/res/?type=challenge&getcountries=true&getlanguage=true&getimage=true&h=%i&w=%i&ts=%li", wapserver, MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH, time( NULL ) );
-	url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_clientinfo1, session );
+	url_data = purple_util_fetch_url_request( session->acc, url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, -1, mxit_cb_clientinfo1, session );
 
 #ifdef	DEBUG_PROTOCOL
 	purple_debug_info( MXIT_PLUGIN_ID, "HTTP REQUEST: '%s'\n", url );
--- a/libpurple/protocols/mxit/markup.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/mxit/markup.c	Mon Sep 26 14:57:21 2011 +0900
@@ -628,7 +628,7 @@
 
 	/* reference: "libpurple/util.h" */
 	url = g_strdup_printf( "%s/res/?type=emo&mlh=%i&sc=%s&ts=%li", wapserver, MXIT_EMOTICON_SIZE, id, time( NULL ) );
-	url_data = purple_util_fetch_url_request( url, TRUE, NULL, TRUE, NULL, FALSE, emoticon_returned, mx );
+	url_data = purple_util_fetch_url( url, TRUE, NULL, TRUE, -1, emoticon_returned, mx );
 	g_free( url );
 }
 
--- a/libpurple/protocols/mxit/mxit.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/mxit/mxit.c	Mon Sep 26 14:57:21 2011 +0900
@@ -668,6 +668,7 @@
 /*========================================================================================================================*/
 
 static PurplePluginProtocolInfo proto_info = {
+	sizeof( PurplePluginProtocolInfo ),		/* struct_size */
 	OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE,			/* options */
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
@@ -696,7 +697,7 @@
 	mxit_set_status,		/* set_status */
 	NULL,					/* set_idle */
 	NULL,					/* change_passwd */
-	NULL,					/* add_buddy				[roster.c] */
+	mxit_add_buddy,			/* add_buddy				[roster.c] */
 	NULL,					/* add_buddies */
 	mxit_remove_buddy,		/* remove_buddy				[roster.c] */
 	NULL,					/* remove_buddies */
@@ -715,7 +716,6 @@
 	mxit_keepalive,			/* keepalive */
 	mxit_register,			/* register_user */
 	NULL,					/* get_cb_info */
-	NULL,					/* get_cb_away */
 	mxit_buddy_alias,		/* alias_buddy				[roster.c] */
 	mxit_buddy_group,		/* group_buddy				[roster.c] */
 	mxit_rename_group,		/* rename_group				[roster.c] */
@@ -740,15 +740,12 @@
 	NULL,					/* unregister_user */
 	NULL,					/* send_attention */
 	NULL,					/* attention_types */
-	sizeof( PurplePluginProtocolInfo ),		/* struct_size */
 	mxit_get_text_table,	/* get_account_text_table */
 	mxit_media_initiate,	/* initiate_media */
 	mxit_media_caps,		/* get_media_caps */
 	mxit_get_moods,			/* get_moods */
 	NULL,					/* set_public_alias */
-	NULL,					/* get_public_alias */
-	mxit_add_buddy,			/* add_buddy_with_invite */
-	NULL					/* add_buddies_with_invite */
+	NULL					/* get_public_alias */
 };
 
 
--- a/libpurple/protocols/mxit/protocol.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/mxit/protocol.c	Mon Sep 26 14:57:21 2011 +0900
@@ -311,7 +311,7 @@
 #endif
 
 	/* send the HTTP request */
-	session->http_out_req = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_http_rx, session );
+	session->http_out_req = purple_util_fetch_url_request( session->acc, url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, -1, mxit_cb_http_rx, session );
 
 	g_free( url );
 	if ( part )
@@ -2136,13 +2136,13 @@
 					contact = get_mxit_invite_contact( session, chunk.mxitid );
 					if ( contact ) {
 						/* this is an invite (add image to the internal image store) */
-						contact->imgid = purple_imgstore_add_with_id( chunk.data, chunk.length, NULL );
+						contact->imgid = purple_imgstore_add_with_id( g_memdup( chunk.data, chunk.length ), chunk.length, NULL );
 						/* show the profile */
 						mxit_show_profile( session, chunk.mxitid, contact->profile );
 					}
 					else {
 						/* this is a contact's avatar, so update it */
-						purple_buddy_icons_set_for_user( session->acc, chunk.mxitid, g_memdup( chunk.data, chunk.length), chunk.length, chunk.avatarid );
+						purple_buddy_icons_set_for_user( session->acc, chunk.mxitid, g_memdup( chunk.data, chunk.length ), chunk.length, chunk.avatarid );
 					}
 				}
 			}
--- a/libpurple/protocols/myspace/myspace.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/myspace/myspace.c	Mon Sep 26 14:57:21 2011 +0900
@@ -429,11 +429,9 @@
 	user = msim_get_user_from_buddy(buddy, TRUE);
 
 	if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
-		MsimSession *session;
 		PurpleAccount *account = purple_buddy_get_account(buddy);
 		PurpleConnection *gc = purple_account_get_connection(account);
-
-		session = (MsimSession *)gc->proto_data;
+		MsimSession *session = purple_connection_get_protocol_data(gc);
 
 		/* TODO: if (full), do something different? */
 
@@ -2022,7 +2020,7 @@
 	g_return_if_fail(source >= 0);  /* Note: 0 is a valid fd */
 
 	gc = (PurpleConnection *)(gc_uncasted);
-	session = gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	/* libpurple/eventloop.h only defines these two */
 	if (cond != PURPLE_INPUT_READ && cond != PURPLE_INPUT_WRITE) {
@@ -2157,7 +2155,7 @@
 	g_return_if_fail(data != NULL);
 
 	gc = (PurpleConnection *)data;
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	if (source < 0) {
 		gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
@@ -2191,7 +2189,7 @@
 	purple_debug_info("msim", "logging in %s\n", acct->username);
 
 	gc = purple_account_get_connection(acct);
-	gc->proto_data = msim_session_new(acct);
+	purple_connection_set_protocol_data(gc, msim_session_new(acct));
 	gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_URLDESC;
 
 	/*
@@ -2259,11 +2257,11 @@
 		buddies = g_slist_delete_link(buddies, buddies);
 	}
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	if (session == NULL)
 		return;
 
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 
 	if (session->gc->inpa) {
 		purple_input_remove(session->gc->inpa);
@@ -2304,7 +2302,7 @@
 
 	/* 'flags' has many options, not used here. */
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	message_msim = html_to_msim_markup(session, message);
 
@@ -2344,7 +2342,7 @@
 	g_return_val_if_fail(gc != NULL, 0);
 	g_return_val_if_fail(name != NULL, 0);
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	switch (state) {
 		case PURPLE_TYPING:
@@ -2430,7 +2428,7 @@
 	g_return_if_fail(gc != NULL);
 	g_return_if_fail(username != NULL);
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	/* Obtain uid of buddy. */
 	user = msim_find_user(session, username);
@@ -2487,6 +2485,7 @@
 static void
 msim_set_status(PurpleAccount *account, PurpleStatus *status)
 {
+	PurpleConnection *gc = purple_account_get_connection(account);
 	PurpleStatusType *type;
 	PurplePresence *pres;
 	MsimSession *session;
@@ -2495,7 +2494,7 @@
 	gchar *stripped;
 	gchar *unrecognized_msg;
 
-	session = (MsimSession *)account->gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	type = purple_status_get_type(status);
 	pres = purple_status_get_presence(status);
@@ -2558,7 +2557,7 @@
 
 	g_return_if_fail(gc != NULL);
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	status = purple_account_get_active_status(session->account);
 
@@ -2628,7 +2627,7 @@
  * Add a buddy to user's buddy list.
  */
 static void
-msim_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+msim_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
 {
 	MsimSession *session;
 	MsimMessage *msg;
@@ -2636,7 +2635,7 @@
 	MsimMessage *body;
 	const char *name, *gname;
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	name = purple_buddy_get_name(buddy);
 	gname = group ? purple_group_get_name(group) : NULL;
 
@@ -2709,7 +2708,7 @@
 	MsimMessage *persist_msg;
 	const char *name;
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 	name = purple_buddy_get_name(buddy);
 
 	delbuddy_msg = msim_msg_new(
@@ -2766,7 +2765,7 @@
 	MsimSession *session;
 	MsimMessage *msg, *body;
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	/* Remove from buddy list */
 	msg = msim_msg_new(
@@ -2818,7 +2817,7 @@
 	MsimSession *session;
 	MsimMessage *msg, *body;
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	/*
 	 * Remove from our list of blocked contacts, so we know they
@@ -2948,7 +2947,7 @@
 	g_return_val_if_fail(buf != NULL, -1);
 	g_return_val_if_fail(total_bytes >= 0, -1);
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	/* Loop until all data is sent, or a failure occurs. */
 	total_bytes_sent = 0;
@@ -3008,6 +3007,7 @@
  * Callbacks called by Purple, to access this plugin.
  */
 static PurplePluginProtocolInfo prpl_info = {
+	sizeof(PurplePluginProtocolInfo), /* struct_size */
 	/* options */
 	  OPT_PROTO_USE_POINTSIZE        /* specify font size in sane point size */
 	| OPT_PROTO_MAIL_CHECK,
@@ -3052,7 +3052,6 @@
 	NULL,              /* keepalive */
 	NULL,              /* register_user */
 	NULL,              /* get_cb_info */
-	NULL,              /* get_cb_away */
 	NULL,              /* alias_buddy */
 	NULL,              /* group_buddy */
 	NULL,              /* rename_group */
@@ -3077,15 +3076,12 @@
 	NULL,                  /* unregister_user */
 	msim_send_attention,   /* send_attention */
 	msim_attention_types,  /* attention_types */
-	sizeof(PurplePluginProtocolInfo), /* struct_size */
 	msim_get_account_text_table,              /* get_account_text_table */
 	NULL,                   /* initiate_media */
 	NULL,                   /* get_media_caps */
 	NULL,                   /* get_moods */
 	NULL,                   /* set_public_alias */
-	NULL,                   /* get_public_alias */
-	NULL,                   /* add_buddy_with_invite */
-	NULL                    /* add_buddies_with_invite */
+	NULL                    /* get_public_alias */
 };
 
 /**
@@ -3151,7 +3147,7 @@
 	gchar *group_name;
 
 	gc = (PurpleConnection *)action->context;
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	group_name = "MySpace Friends";
 
@@ -3530,6 +3526,7 @@
 msim_uri_handler(const gchar *proto, const gchar *cmd, GHashTable *params)
 {
 	PurpleAccount *account;
+	PurpleConnection *gc;
 	MsimSession *session;
 	GList *l;
 	gchar *uid_str, *cid_str;
@@ -3578,7 +3575,8 @@
 		return FALSE;
 	}
 
-	session = (MsimSession *)account->gc->proto_data;
+	gc = purple_account_get_connection(account);
+	session = purple_connection_get_protocol_data(gc);
 	g_return_val_if_fail(session != NULL, FALSE);
 
 	/* Lookup userid to username. TODO: push this down, to IM sending/contact
--- a/libpurple/protocols/myspace/user.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/myspace/user.c	Mon Sep 26 14:57:21 2011 +0900
@@ -384,7 +384,7 @@
 		if (!previous_url || !g_str_equal(previous_url, user->image_url)) {
 			if (user->url_data != NULL)
 				purple_util_fetch_url_cancel(user->url_data);
-			user->url_data = purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, msim_downloaded_buddy_icon, (gpointer)user);
+			user->url_data = purple_util_fetch_url(user->image_url, TRUE, NULL, TRUE, -1, msim_downloaded_buddy_icon, (gpointer)user);
 		}
 	} else if (g_str_equal(key_str, "LastImageUpdated")) {
 		/* TODO: use somewhere */
@@ -412,7 +412,7 @@
  *
  * @param session
  * @param msg The user information reply, with any amount of information.
- * @param user The structure to save to, or NULL to save in PurpleBuddy->proto_data.
+ * @param user The structure to save to, or NULL to save in PurpleBuddy's protocol_data.
  *
  * Variable information is saved to the passed MsimUser structure. Permanent
  * information (UserID) is stored in the blist node of the buddy list (and
@@ -758,7 +758,7 @@
 
 	g_return_if_fail(gc != NULL);
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	user_msg = msim_msg_new(
 			"user", MSIM_TYPE_STRING, g_strdup(msim_username_to_set),
@@ -851,7 +851,7 @@
 
 	g_return_if_fail(gc != NULL);
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	purple_debug_info("msim_check_username_availability_cb", "Checking username: %s\n", username_to_check);
 
--- a/libpurple/protocols/myspace/zap.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/myspace/zap.c	Mon Sep 26 14:57:21 2011 +0900
@@ -132,7 +132,7 @@
 	PurpleAttentionType *attn;
 	PurpleBuddy *buddy;
 
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	/* Look for this attention type, by the code index given. */
 	types = msim_attention_types(gc->account);
@@ -175,7 +175,7 @@
 	/* Find the session */
 	account = purple_buddy_get_account(buddy);
 	gc = purple_account_get_connection(account);
-	session = (MsimSession *)gc->proto_data;
+	session = purple_connection_get_protocol_data(gc);
 
 	zap = GPOINTER_TO_INT(zap_num_ptr);
 
--- a/libpurple/protocols/novell/novell.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/novell/novell.c	Mon Sep 26 14:57:21 2011 +0900
@@ -1629,7 +1629,7 @@
 	buddy = (PurpleBuddy *) node;
 	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -1685,7 +1685,7 @@
 	NMUser *user;
 
 	gc = data;
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	user->conn->ssl_conn->data = NULL;
 
 	purple_connection_ssl_error (gc, error);
@@ -1702,7 +1702,7 @@
 	if (gc == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -1736,7 +1736,7 @@
 	if (gc == NULL || gsc == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if ((user == NULL) || (conn = user->conn) == NULL)
 		return;
 
@@ -2206,7 +2206,7 @@
 	user = nm_initialize_user(name, server, port, account, _event_callback);
 	if (user && user->conn) {
 		/* save user */
-		gc->proto_data = user;
+		purple_connection_set_protocol_data(gc, user);
 
 		/* connect to the server */
 		purple_connection_update_progress(gc, _("Connecting"),
@@ -2238,7 +2238,7 @@
 	if (gc == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user) {
 		conn = user->conn;
 		if (conn && conn->ssl_conn) {
@@ -2246,7 +2246,7 @@
 		}
 		nm_deinitialize_user(user);
 	}
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 }
 
 static int
@@ -2266,7 +2266,7 @@
 		message_body == NULL || *message_body == '\0')
 		return 0;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return 0;
 
@@ -2352,7 +2352,7 @@
 	if (gc == NULL || name == NULL)
 		return 0;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return 0;
 
@@ -2386,7 +2386,7 @@
 	if (gc == NULL || who == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user && (dn = nm_lookup_dn(user, who))) {
 		conf = nm_find_conversation(user, dn);
 		if (conf) {
@@ -2408,7 +2408,7 @@
 	if (gc == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -2440,7 +2440,7 @@
 	if (gc == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -2479,7 +2479,7 @@
 	if (gc == NULL || text == NULL)
 		return -1;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return -1;
 
@@ -2545,7 +2545,7 @@
 }
 
 static void
-novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group)
+novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group, const char *message)
 {
 	NMFolder *folder = NULL;
 	NMContact *contact;
@@ -2619,7 +2619,7 @@
 	if (gc == NULL || buddy == NULL || group == NULL)
 		return;
 
-	user = (NMUser *) gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user && (dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)))) {
 		gname = purple_group_get_name(group);
 		if (strcmp(gname, NM_ROOT_FOLDER_NAME) == 0) {
@@ -2651,7 +2651,7 @@
 	if (gc == NULL || group == NULL)
 		return;
 
-	user = (NMUser *) gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user) {
 		NMFolder *folder = nm_find_folder(user, purple_group_get_name(group));
 
@@ -2676,7 +2676,7 @@
 	if (gc == NULL || name == NULL || alias == NULL)
 		return;
 
-	user = (NMUser *) gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user && (dn = nm_lookup_dn(user, name))) {
 
 		/* Alias all of instances of the contact */
@@ -2735,7 +2735,7 @@
 		old_group_name == NULL || new_group_name == NULL)
 		return;
 
-	user = (NMUser *) gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user && (dn = nm_lookup_dn(user, name))) {
 
 		/* Find the old folder */
@@ -2793,7 +2793,7 @@
 		return;
 	}
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user) {
 		const char *gname = purple_group_get_name(group);
 		/* Does new folder exist already? */
@@ -2839,7 +2839,7 @@
 		return;
 
 	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	if (gc == NULL || (user = gc->proto_data) == NULL)
+	if (gc == NULL || (user = purple_connection_get_protocol_data(gc)) == NULL)
 		return;
 
 	if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
@@ -2891,7 +2891,7 @@
 	if (gc == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -2920,7 +2920,7 @@
 	if (gc == NULL || name == NULL)
 		return;
 
-	user = (NMUser *) gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user) {
 
 		user_record = nm_find_user_record(user, name);
@@ -2949,17 +2949,19 @@
 	if (buddy && account) {
 		PurpleConnection *gc = purple_account_get_connection(account);
 
-		if (gc && gc->proto_data) {
-			NMUser *user = gc->proto_data;
-
-			dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
-			if (dn) {
-				NMUserRecord *user_record = nm_find_user_record(user, dn);
-
-				if (user_record) {
-					text = nm_user_record_get_status_text(user_record);
-					if (text)
-						return g_strdup(text);
+		if (gc) {
+			NMUser *user = purple_connection_get_protocol_data(gc);
+
+			if (user) {
+				dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
+				if (dn) {
+					NMUserRecord *user_record = nm_find_user_record(user, dn);
+
+					if (user_record) {
+						text = nm_user_record_get_status_text(user_record);
+						if (text)
+							return g_strdup(text);
+					}
 				}
 			}
 		}
@@ -3035,7 +3037,7 @@
 		return;
 
 	gc = purple_account_get_connection(account);
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -3082,7 +3084,7 @@
 	if (gc == NULL || who == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -3126,7 +3128,7 @@
 	if (gc == NULL || who == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -3170,7 +3172,7 @@
 	if (gc == NULL || who == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -3200,7 +3202,7 @@
 	if (gc == NULL || who == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -3235,7 +3237,7 @@
 	if (gc == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -3463,7 +3465,7 @@
 	if (gc == NULL)
 		return;
 
-	user = gc->proto_data;
+	user = purple_connection_get_protocol_data(gc);
 	if (user == NULL)
 		return;
 
@@ -3472,6 +3474,7 @@
 }
 
 static PurplePluginProtocolInfo prpl_info = {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	0,
 	NULL,						/* user_splits */
 	NULL,						/* protocol_options */
@@ -3512,7 +3515,6 @@
 	novell_keepalive,			/* keepalive */
 	NULL,						/* register_user */
 	NULL,						/* get_cb_info */
-	NULL,						/* get_cb_away */
 	novell_alias_buddy,			/* alias_buddy */
 	novell_group_buddy,			/* group_buddy */
 	novell_rename_group,		/* rename_group */
@@ -3537,15 +3539,12 @@
 	NULL,						/* unregister_user */
 	NULL,						/* send_attention */
 	NULL,						/* get_attention_types */
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,						/* get_account_text_table */
 	NULL,						/* initiate_media */
 	NULL,						/* get_media_caps */
 	NULL,						/* get_moods */
 	NULL,						/* set_public_alias */
-	NULL,						/* get_public_alias */
-	NULL,						/* add_buddy_with_invite */
-	NULL						/* add_buddies_with_invite */
+	NULL						/* get_public_alias */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/protocols/null/nullprpl.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/null/nullprpl.c	Mon Sep 26 14:57:21 2011 +0900
@@ -260,7 +260,7 @@
     char *msg = nullprpl_status_text(buddy);
 	/* TODO: Check whether it's correct to call add_pair_html,
 	         or if we should be using add_pair_plaintext */
-    purple_notify_user_info_add_pair(info, purple_status_get_name(status),
+    purple_notify_user_info_add_pair_html(info, purple_status_get_name(status),
                                      msg);
     g_free(msg);
 
@@ -269,7 +269,7 @@
       if (user_info)
 		/* TODO: Check whether it's correct to call add_pair_html,
 		         or if we should be using add_pair_plaintext */
-        purple_notify_user_info_add_pair(info, _("User info"), user_info);
+        purple_notify_user_info_add_pair_html(info, _("User info"), user_info);
     }
 
   } else {
@@ -516,7 +516,7 @@
     body = _("No user info.");
   /* TODO: Check whether it's correct to call add_pair_html,
            or if we should be using add_pair_plaintext */
-  purple_notify_user_info_add_pair(info, "Info", body);
+  purple_notify_user_info_add_pair_html(info, "Info", body);
 
   /* show a buddy's user info in a nice dialog box */
   purple_notify_userinfo(gc,        /* connection the buddy info came through */
@@ -548,7 +548,7 @@
 }
 
 static void nullprpl_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
-                               PurpleGroup *group)
+                               PurpleGroup *group, const char *message)
 {
   const char *username = gc->account->username;
   PurpleConnection *buddy_gc = get_nullprpl_gc(buddy->name);
@@ -571,20 +571,20 @@
                                  username,
                                  NULL,   /* local account id (rarely used) */
                                  NULL,   /* alias */
-                                 NULL);  /* message */
+                                 message);  /* message */
     }
   }
 }
 
 static void nullprpl_add_buddies(PurpleConnection *gc, GList *buddies,
-                                 GList *groups) {
+                                 GList *groups, const char *message) {
   GList *buddy = buddies;
   GList *group = groups;
 
   purple_debug_info("nullprpl", "adding multiple buddies\n");
 
   while (buddy && group) {
-    nullprpl_add_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data);
+    nullprpl_add_buddy(gc, (PurpleBuddy *)buddy->data, (PurpleGroup *)group->data, message);
     buddy = g_list_next(buddy);
     group = g_list_next(group);
   }
@@ -1019,14 +1019,17 @@
 }
 
 static void nullprpl_roomlist_cancel(PurpleRoomlist *list) {
+ PurpleAccount *account = purple_roomlist_get_account(list);
  purple_debug_info("nullprpl", "%s asked to cancel room list request\n",
-                   list->account->username);
+                   purple_account_get_username(account));
 }
 
 static void nullprpl_roomlist_expand_category(PurpleRoomlist *list,
                                               PurpleRoomlistRoom *category) {
+ PurpleAccount *account = purple_roomlist_get_account(list);
  purple_debug_info("nullprpl", "%s asked to expand room list category %s\n",
-                   list->account->username, category->name);
+                   purple_account_get_username(account),
+                   purple_roomlist_room_get_name(category));
 }
 
 /* nullprpl doesn't support file transfer...yet... */
@@ -1049,6 +1052,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+  sizeof(PurplePluginProtocolInfo),    /* struct_size */
   OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC,  /* options */
   NULL,               /* user_splits, initialized in nullprpl_init() */
   NULL,               /* protocol_options, initialized in nullprpl_init() */
@@ -1097,7 +1101,6 @@
   NULL,                                /* keepalive */
   nullprpl_register_user,              /* register_user */
   nullprpl_get_cb_info,                /* get_cb_info */
-  NULL,                                /* get_cb_away */
   nullprpl_alias_buddy,                /* alias_buddy */
   nullprpl_group_buddy,                /* group_buddy */
   nullprpl_rename_group,               /* rename_group */
@@ -1122,15 +1125,12 @@
   NULL,                                /* unregister_user */
   NULL,                                /* send_attention */
   NULL,                                /* get_attention_types */
-  sizeof(PurplePluginProtocolInfo),    /* struct_size */
   NULL,                                /* get_account_text_table */
   NULL,                                /* initiate_media */
   NULL,                                /* get_media_caps */
   NULL,                                /* get_moods */
   NULL,                                /* set_public_alias */
-  NULL,                                /* get_public_alias */
-  NULL,                                /* add_buddy_with_invite */
-  NULL                                 /* add_buddies_with_invite */
+  NULL                                 /* get_public_alias */
 };
 
 static void nullprpl_init(PurplePlugin *plugin)
--- a/libpurple/protocols/oscar/clientlogin.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/oscar/clientlogin.c	Mon Sep 26 14:57:21 2011 +0900
@@ -384,7 +384,7 @@
 	g_free(signature);
 
 	/* Make the request */
-	od->url_data = purple_util_fetch_url_request_len(account,
+	od->url_data = purple_util_fetch_url_request(account,
 			url, TRUE, NULL, FALSE, NULL, FALSE, -1,
 			start_oscar_session_cb, od);
 	g_free(url);
@@ -646,7 +646,7 @@
 	g_string_free(body, TRUE);
 
 	/* Send the POST request  */
-	od->url_data = purple_util_fetch_url_request_len(
+	od->url_data = purple_util_fetch_url_request(
 			purple_connection_get_account(gc), get_client_login_url(od),
 			TRUE, NULL, FALSE, request->str, FALSE, -1,
 			client_login_cb, od);
--- a/libpurple/protocols/oscar/libaim.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/oscar/libaim.c	Mon Sep 26 14:57:21 2011 +0900
@@ -29,6 +29,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE,
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
@@ -50,7 +51,7 @@
 	oscar_set_status,		/* set_status */
 	oscar_set_idle,			/* set_idle */
 	oscar_change_passwd,	/* change_passwd */
-	NULL,					/* add_buddy */
+	oscar_add_buddy,		/* add_buddy */
 	NULL,					/* add_buddies */
 	oscar_remove_buddy,		/* remove_buddy */
 	NULL,					/* remove_buddies */
@@ -69,7 +70,6 @@
 	oscar_keepalive,		/* keepalive */
 	NULL,					/* register_user */
 	NULL,					/* get_cb_info */
-	NULL,					/* get_cb_away */
 	oscar_alias_buddy,		/* alias_buddy */
 	oscar_move_buddy,		/* group_buddy */
 	oscar_rename_group,		/* rename_group */
@@ -94,15 +94,12 @@
 	NULL,					/* unregister_user */
 	NULL,					/* send_attention */
 	NULL,					/* get_attention_types */
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,					/* get_account_text_table */
 	NULL,					/* initiate_media */
 	NULL,					/* get_media_caps */
 	NULL,					/* get_moods */
 	NULL,					/* set_public_alias */
-	NULL,					/* get_public_alias */
-	oscar_add_buddy,		/* add_buddy_with_invite */
-	NULL					/* add_buddies_with_invite */
+	NULL					/* get_public_alias */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/oscar/libicq.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/oscar/libicq.c	Mon Sep 26 14:57:21 2011 +0900
@@ -38,6 +38,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE,
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
@@ -59,7 +60,7 @@
 	oscar_set_status,		/* set_status */
 	oscar_set_idle,			/* set_idle */
 	oscar_change_passwd,	/* change_passwd */
-	NULL,					/* add_buddy */
+	oscar_add_buddy,		/* add_buddy */
 	NULL,					/* add_buddies */
 	oscar_remove_buddy,		/* remove_buddy */
 	NULL,					/* remove_buddies */
@@ -78,7 +79,6 @@
 	oscar_keepalive,		/* keepalive */
 	NULL,					/* register_user */
 	NULL,					/* get_cb_info */
-	NULL,					/* get_cb_away */
 	oscar_alias_buddy,		/* alias_buddy */
 	oscar_move_buddy,		/* group_buddy */
 	oscar_rename_group,		/* rename_group */
@@ -103,16 +103,12 @@
 	NULL,					/* unregister_user */
 	NULL,					/* send_attention */
 	NULL,					/* get_attention_types */
-
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	icq_get_account_text_table, /* get_account_text_table */
 	NULL,					/* initiate_media */
 	NULL,					/* can_do_media */
 	oscar_get_purple_moods, /* get_moods */
 	NULL,					/* set_public_alias */
-	NULL,					/* get_public_alias */
-	oscar_add_buddy,		/* add_buddy_with_invite */
-	NULL					/* add_buddies_with_invite */
+	NULL					/* get_public_alias */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/oscar/oft.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/oscar/oft.c	Mon Sep 26 14:57:21 2011 +0900
@@ -362,10 +362,10 @@
 
 	if (purple_circ_buffer_get_max_read(conn->buffer_outgoing) == 0)
 	{
+		int fd = conn->fd;
 		conn->sending_data_timer = 0;
-		conn->xfer->fd = conn->fd;
 		conn->fd = -1;
-		purple_xfer_start(conn->xfer, conn->xfer->fd, NULL, 0);
+		purple_xfer_start(conn->xfer, fd, NULL, 0);
 		return FALSE;
 	}
 
@@ -667,9 +667,9 @@
 		size2 = purple_str_size_to_units(G_MAXUINT32);
 		tmp = g_strdup_printf(_("File %s is %s, which is larger than "
 				"the maximum size of %s."),
-				xfer->local_filename, size1, size2);
+				purple_xfer_get_local_filename(xfer), size1, size2);
 		purple_xfer_error(purple_xfer_get_type(xfer),
-				purple_xfer_get_account(xfer), xfer->who, tmp);
+				purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer), tmp);
 		g_free(size1);
 		g_free(size2);
 		g_free(tmp);
@@ -693,16 +693,19 @@
 	conn->xferdata.cretime = 0;
 
 	/* yaz */
-	f1 = g_filename_display_basename(xfer->local_filename);
+	f1 = g_filename_display_basename(purple_xfer_get_local_filename(xfer));
 	f2 = botch_utf(f1, strlen(f1), &dummy);
 	if(f2){
 		purple_xfer_set_filename(xfer, (char *)f2);
 	}
+    else {
+		purple_xfer_set_filename(xfer, (char *)f1);
+	}
 	g_free(f1); f1 = NULL;
 	g_free(f2); f2 = NULL;
 
-	conn->xferdata.name_length = MAX(64, strlen(xfer->filename) + 1);
-	conn->xferdata.name = (guchar *)g_strndup(xfer->filename, conn->xferdata.name_length - 1);
+	conn->xferdata.name_length = MAX(64, strlen(purple_xfer_get_filename(xfer)) + 1);
+	conn->xferdata.name = (guchar *)g_strndup(purple_xfer_get_filename(xfer), conn->xferdata.name_length - 1);
 
 	peer_oft_checksum_file(conn, xfer,
 			peer_oft_checksum_calculated_cb, G_MAXUINT32);
--- a/libpurple/protocols/oscar/oscar.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/oscar/oscar.c	Mon Sep 26 14:57:21 2011 +0900
@@ -3806,7 +3806,7 @@
 			}
 
 			purple_account_remove_buddies(account, moved_buddies, groups);
-			purple_account_add_buddies(account, moved_buddies);
+			purple_account_add_buddies(account, moved_buddies, NULL);
 			g_list_free(groups);
 			purple_debug_info("oscar",
 					   "ssi: moved all buddies from group %s to %s\n", old_name, gname);
@@ -5040,7 +5040,7 @@
 	name = purple_buddy_get_name(buddy);
 	account = purple_buddy_get_account(buddy);
 	gc = purple_account_get_connection(account);
-	od = gc->proto_data;
+	od = purple_connection_get_protocol_data(gc);
 	conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
 
 	if (conn != NULL)
--- a/libpurple/protocols/oscar/peer.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/oscar/peer.c	Mon Sep 26 14:57:21 2011 +0900
@@ -842,7 +842,7 @@
 		 */
 		conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
 
-		conn->listen_data = purple_network_listen_range(5190, 5290, SOCK_STREAM,
+		conn->listen_data = purple_network_listen_range(5190, 5290, AF_UNSPEC, SOCK_STREAM, TRUE,
 				peer_connection_establish_listener_cb, conn);
 		if (conn->listen_data != NULL)
 		{
--- a/libpurple/protocols/sametime/sametime.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/sametime/sametime.c	Mon Sep 26 14:57:21 2011 +0900
@@ -197,7 +197,7 @@
 
 
 /** the purple plugin data.
-    available as gc->proto_data and mwSession_getClientData */
+    available as purple_connection_get_protocol_data(gc) and mwSession_getClientData */
 struct mwPurplePluginData {
   struct mwSession *session;
 
@@ -320,7 +320,7 @@
 
   g_return_val_if_fail(gc != NULL, NULL);
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   g_return_val_if_fail(pd != NULL, NULL);
 
   return pd->session;
@@ -499,7 +499,7 @@
   gc = mwAwareList_getClientData(list);
   acct = purple_connection_get_account(gc);
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   idle = aware->status.time;
   stat = aware->status.status;
   id = aware->id.user;
@@ -851,7 +851,7 @@
 static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group,
 			       struct mwSametimeUser *stuser) {
 
-  struct mwPurplePluginData *pd = gc->proto_data;
+  struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
   PurpleBuddy *buddy;
   PurpleAccount *acct = purple_connection_get_account(gc);
 
@@ -968,7 +968,7 @@
 
   if(type == mwSametimeGroup_DYNAMIC) {
     purple_blist_node_set_string(gn, GROUP_KEY_OWNER, owner);
-    group_add(gc->proto_data, group);
+    group_add(purple_connection_get_protocol_data(gc), group);
   }
 
   return group;
@@ -1398,7 +1398,7 @@
   }
 
   if(add_buds) {
-    purple_account_add_buddies(acct, add_buds);
+    purple_account_add_buddies(acct, add_buds, NULL);
     g_list_free(add_buds);
   }
 }
@@ -2157,7 +2157,7 @@
 
   ft = purple_xfer_get_protocol_data(xfer);
 
-  fp = g_fopen(xfer->local_filename, "wb");
+  fp = g_fopen(purple_xfer_get_local_filename(xfer), "wb");
   if(! fp) {
     mwFileTransfer_cancel(ft);
     return;
@@ -2231,8 +2231,7 @@
   if(fread(buf, (size_t) o.len, 1, fp)) {
 
     /* calculate progress and display it */
-    xfer->bytes_sent += o.len;
-    xfer->bytes_remaining -= o.len;
+    purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + o.len);
     purple_xfer_update_progress(xfer);
 
     mwFileTransfer_send(ft, &o);
@@ -2264,7 +2263,7 @@
   }
 
   if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
-    xfer->dest_fp = g_fopen(xfer->local_filename, "rb");
+    xfer->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer), "rb");
     ft_send(ft, xfer->dest_fp);
   }
 }
@@ -2334,8 +2333,7 @@
   }
 
   /* update the progress */
-  xfer->bytes_sent += data->len;
-  xfer->bytes_remaining -= data->len;
+  purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + data->len);
   purple_xfer_update_progress(xfer);
 
   /* let the other side know we got it, and to send some more */
@@ -3194,7 +3192,7 @@
   mwSession_addCipher(pd->session, mwCipher_new_RC2_128(pd->session));
 
   mwSession_setClientData(pd->session, pd, NULL);
-  gc->proto_data = pd;
+  purple_connection_set_protocol_data(gc, pd);
 
   return pd;
 }
@@ -3203,7 +3201,7 @@
 static void mwPurplePluginData_free(struct mwPurplePluginData *pd) {
   g_return_if_fail(pd != NULL);
 
-  pd->gc->proto_data = NULL;
+  purple_connection_set_protocol_data(pd->gc, NULL);
 
   mwSession_removeService(pd->session, mwService_AWARE);
   mwSession_removeService(pd->session, mwService_CONFERENCE);
@@ -3265,7 +3263,7 @@
   const char *ret = NULL;
 
   if ((gc = purple_account_get_connection(purple_buddy_get_account(b)))
-      && (pd = gc->proto_data))
+      && (pd = purple_connection_get_protocol_data(gc)))
     ret = mwServiceAware_getText(pd->srvc_aware, &t);
 
   return (ret && g_utf8_validate(ret, -1, NULL)) ? g_markup_escape_text(ret, -1): NULL;
@@ -3328,7 +3326,7 @@
   char *tmp;
 
   if ((gc = purple_account_get_connection(purple_buddy_get_account(b)))
-      && (pd = gc->proto_data))
+      && (pd = purple_connection_get_protocol_data(gc)))
      message = mwServiceAware_getText(pd->srvc_aware, &idb);
 
   status = status_text(b);
@@ -3405,7 +3403,7 @@
 
   acct = purple_buddy_get_account(buddy);
   gc = purple_account_get_connection(acct);
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   srvc = pd->srvc_conf;
 
   f = purple_request_fields_get_field(fields, CHAT_KEY_TOPIC);
@@ -3571,7 +3569,7 @@
   gc = purple_account_get_connection(acct);
   g_return_if_fail(gc != NULL);
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   g_return_if_fail(pd != NULL);
 
   /*
@@ -3610,7 +3608,7 @@
   gc = purple_account_get_connection(acct);
   g_return_if_fail(gc != NULL);
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   g_return_if_fail(pd != NULL);
 
   rcpt_name = g_strdup_printf("@U %s", buddy->name);
@@ -3815,7 +3813,7 @@
 
   g_return_if_fail(gc != NULL);
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   g_return_if_fail(pd != NULL);
 
   /* get rid of the blist save timeout */
@@ -3829,7 +3827,7 @@
   mwSession_stop(pd->session, 0x00);
 
   /* no longer necessary */
-  gc->proto_data = NULL;
+  purple_connection_set_protocol_data(gc, NULL);
 
   /* stop watching the socket */
   if(gc->inpa) {
@@ -4020,7 +4018,7 @@
   struct mwConversation *conv;
 
   g_return_val_if_fail(gc != NULL, 0);
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
 
   g_return_val_if_fail(pd != NULL, 0);
 
@@ -4095,7 +4093,7 @@
   gpointer t = GINT_TO_POINTER(!! state);
 
   g_return_val_if_fail(gc != NULL, 0);
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
 
   g_return_val_if_fail(pd != NULL, 0);
 
@@ -4191,7 +4189,7 @@
   g_return_if_fail(who != NULL);
   g_return_if_fail(*who != '\0');
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
 
   acct = purple_connection_get_account(gc);
   b = purple_find_buddy(acct, who);
@@ -4436,7 +4434,7 @@
   buddy = data->buddy;
 
   gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
 
   if(results)
     res = results->data;
@@ -4515,9 +4513,10 @@
 
 static void mw_prpl_add_buddy(PurpleConnection *gc,
 			      PurpleBuddy *buddy,
-			      PurpleGroup *group) {
-
-  struct mwPurplePluginData *pd = gc->proto_data;
+			      PurpleGroup *group,
+			      const char *message) {
+
+  struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
   struct mwServiceResolve *srvc;
   GList *query;
   enum mwResolveFlag flags;
@@ -4562,13 +4561,14 @@
 
 static void mw_prpl_add_buddies(PurpleConnection *gc,
 				GList *buddies,
-				GList *groups) {
+				GList *groups,
+				const char *message) {
 
   struct mwPurplePluginData *pd;
   GHashTable *group_sets;
   struct mwAwareIdBlock *idbs, *idb;
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
 
   /* map PurpleGroup:GList of mwAwareIdBlock */
   group_sets = g_hash_table_new(g_direct_hash, g_direct_equal);
@@ -4619,7 +4619,7 @@
 
   GList *rem = g_list_prepend(NULL, &idb);
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   group = purple_buddy_get_group(buddy);
   list = list_ensure(pd, group);
 
@@ -4666,7 +4666,7 @@
   acct = purple_connection_get_account(gc);
   g_return_if_fail(acct != NULL);
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   g_return_if_fail(pd != NULL);
 
   session = pd->session;
@@ -4750,7 +4750,7 @@
   struct mwPurplePluginData *pd;
   char *c, *t;
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
 
   c = g_hash_table_lookup(components, CHAT_KEY_NAME);
   t = g_hash_table_lookup(components, CHAT_KEY_TOPIC);
@@ -4792,7 +4792,7 @@
   struct mwServiceConference *srvc;
   char *c;
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   srvc = pd->srvc_conf;
 
   if(g_hash_table_lookup(components, CHAT_KEY_IS_PLACE)) {
@@ -4824,7 +4824,7 @@
   struct mwPlace *place;
   struct mwIdBlock idb = { (char *) who, NULL };
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   g_return_if_fail(pd != NULL);
 
   conf = ID_TO_CONF(pd, id);
@@ -4848,7 +4848,7 @@
   struct mwPurplePluginData *pd;
   struct mwConference *conf;
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
 
   g_return_if_fail(pd != NULL);
   conf = ID_TO_CONF(pd, id);
@@ -4884,7 +4884,7 @@
   char *msg;
   int ret;
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
 
   g_return_val_if_fail(pd != NULL, 0);
   conf = ID_TO_CONF(pd, id);
@@ -4922,7 +4922,7 @@
 				const char *who,
 				const char *alias) {
 
-  struct mwPurplePluginData *pd = gc->proto_data;
+  struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
   g_return_if_fail(pd != NULL);
 
   /* it's a change to the buddy list, so we've gotta reflect that in
@@ -4940,7 +4940,7 @@
   struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL };
   GList *gl = g_list_prepend(NULL, &idb);
 
-  struct mwPurplePluginData *pd = gc->proto_data;
+  struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
   PurpleGroup *group;
   struct mwAwareList *list;
 
@@ -4966,7 +4966,7 @@
 				 PurpleGroup *group,
 				 GList *buddies) {
 
-  struct mwPurplePluginData *pd = gc->proto_data;
+  struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
   g_return_if_fail(pd != NULL);
 
   /* it's a change in the buddy list, so we've gotta reflect that in
@@ -4986,7 +4986,7 @@
 
 
 static void mw_prpl_convo_closed(PurpleConnection *gc, const char *who) {
-  struct mwPurplePluginData *pd = gc->proto_data;
+  struct mwPurplePluginData *pd = purple_connection_get_protocol_data(gc);
   struct mwServiceIm *srvc;
   struct mwConversation *conv;
   struct mwIdBlock idb = { (char *) who, NULL };
@@ -5021,7 +5021,7 @@
   struct mwPurplePluginData *pd;
   struct mwAwareList *list;
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   g_return_if_fail(pd != NULL);
   g_return_if_fail(pd->group_list_map != NULL);
 
@@ -5045,7 +5045,7 @@
 
   g_return_val_if_fail(gc != NULL, FALSE);
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   g_return_val_if_fail(pd != NULL, FALSE);
 
   srvc = pd->srvc_aware;
@@ -5077,12 +5077,12 @@
 
   acct = purple_xfer_get_account(xfer);
   gc = purple_account_get_connection(acct);
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   srvc = pd->srvc_ft;
 
   filename = purple_xfer_get_local_filename(xfer);
   filesize = purple_xfer_get_size(xfer);
-  idb.user = xfer->who;
+  idb.user = purple_xfer_get_remote_user(xfer);
 
   purple_xfer_update_progress(xfer);
 
@@ -5091,7 +5091,7 @@
   if(! fp) {
     char *msg = g_strdup_printf(_("Error reading file %s: \n%s\n"),
 				filename, g_strerror(errno));
-    purple_xfer_error(purple_xfer_get_type(xfer), acct, xfer->who, msg);
+    purple_xfer_error(purple_xfer_get_type(xfer), acct, purple_xfer_get_remote_user(xfer), msg);
     g_free(msg);
     return;
   }
@@ -5154,6 +5154,7 @@
 
 
 static PurplePluginProtocolInfo mw_prpl_info = {
+  .struct_size               = sizeof(PurplePluginProtocolInfo),
   .options                   = OPT_PROTO_IM_IMAGE,
   .user_splits               = NULL, /*< set in mw_plugin_init */
   .protocol_options          = NULL, /*< set in mw_plugin_init */
@@ -5194,7 +5195,6 @@
   .keepalive                 = mw_prpl_keepalive,
   .register_user             = NULL,
   .get_cb_info               = NULL,
-  .get_cb_away               = NULL,
   .alias_buddy               = mw_prpl_alias_buddy,
   .group_buddy               = mw_prpl_group_buddy,
   .rename_group              = mw_prpl_rename_group,
@@ -5213,8 +5213,7 @@
   .new_xfer                  = mw_prpl_new_xfer,
   .offline_message           = NULL,
   .whiteboard_prpl_ops       = NULL,
-  .send_raw                  = NULL,
-  .struct_size               = sizeof(PurplePluginProtocolInfo)
+  .send_raw                  = NULL
 };
 
 
@@ -5536,7 +5535,7 @@
   enum mwResolveFlag flags;
   guint32 req;
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   srvc = pd->srvc_resolve;
 
   query = g_list_prepend(NULL, (char *) name);
@@ -5661,7 +5660,7 @@
   enum mwResolveFlag flags;
   guint32 req;
 
-  pd = gc->proto_data;
+  pd = purple_connection_get_protocol_data(gc);
   srvc = pd->srvc_resolve;
 
   query = g_list_prepend(NULL, (char *) name);
--- a/libpurple/protocols/silc/buddy.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/silc/buddy.c	Mon Sep 26 14:57:21 2011 +0900
@@ -72,7 +72,7 @@
 			   void *context)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 
 	if (!sg->conn)
 		return;
@@ -146,7 +146,7 @@
 silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
 			   gboolean force_local)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcDList clients;
 	SilcClientEntry client_entry;
 	SilcClientConnectionParams params;
@@ -346,7 +346,7 @@
 
 	b = (PurpleBuddy *) node;
 	gc = purple_account_get_connection(purple_buddy_get_account(b));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	/* Find client entry */
 	clients = silc_client_get_clients_local(sg->client, sg->conn,
@@ -426,7 +426,7 @@
 static void
 silcpurple_buddy_privkey(PurpleConnection *gc, const char *name)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcPurplePrivkey p;
 	SilcDList clients;
 	SilcClientEntry client_entry;
@@ -554,7 +554,7 @@
 static void
 silcpurple_buddy_getkey(PurpleConnection *gc, const char *name)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcClientEntry client_entry;
@@ -618,7 +618,7 @@
 
 	b = (PurpleBuddy *) node;
 	gc = purple_account_get_connection(purple_buddy_get_account(b));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	pkfile = purple_blist_node_get_string(node, "public-key");
 	if (!silc_pkcs_load_public_key(pkfile, &public_key)) {
@@ -669,7 +669,7 @@
 
 void silcpurple_get_info(PurpleConnection *gc, const char *who)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcClientEntry client_entry;
@@ -1338,7 +1338,7 @@
 static void
 silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcPurpleBuddyRes r;
@@ -1396,7 +1396,7 @@
 	silc_buffer_free(attrs);
 }
 
-void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
 {
 	/* Don't add if the buddy is already on the list.
 	 *
@@ -1437,7 +1437,7 @@
 	const char *server;
 	int port;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 	if (sg == NULL)
 		return;
 
@@ -1467,7 +1467,7 @@
 {
 	PurpleAccount *account = purple_buddy_get_account(b);
 	PurpleConnection *gc = purple_account_get_connection(account);
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcClientID *client_id = purple_buddy_get_protocol_data(b);
@@ -1533,7 +1533,7 @@
 {
 	PurpleAccount *account = purple_buddy_get_account(b);
 	PurpleConnection *gc = purple_account_get_connection(account);
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcClientID *client_id = purple_buddy_get_protocol_data(b);
@@ -1629,7 +1629,7 @@
 
 	b = (PurpleBuddy *) node;
 	gc = purple_account_get_connection(purple_buddy_get_account(b));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	/* Call KILL */
 	silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
@@ -1653,7 +1653,7 @@
 {
 	PurpleAccount *account = purple_buddy_get_account(buddy);
 	PurpleConnection *gc = purple_account_get_connection(account);
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClientConnection conn = sg->conn;
 	const char *pkfile = NULL;
 	SilcClientEntry client_entry = NULL;
@@ -1719,7 +1719,7 @@
 
 void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcMime mime;
--- a/libpurple/protocols/silc/chat.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/silc/chat.c	Mon Sep 26 14:57:21 2011 +0900
@@ -91,7 +91,7 @@
 static void
 silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	const char *chname;
 	char tmp[256], *tmp2;
 	GString *s;
@@ -500,7 +500,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -587,7 +587,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	p = silc_calloc(1, sizeof(*p));
 	if (!p)
@@ -637,7 +637,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -656,7 +656,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (!sg->conn)
 		return;
@@ -733,7 +733,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (!sg->conn)
 		return;
@@ -768,7 +768,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -786,7 +786,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -804,7 +804,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -822,7 +822,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -840,7 +840,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -858,7 +858,7 @@
 
 	chat = (PurpleChat *) node;
 	gc = purple_account_get_connection(purple_chat_get_account(chat));
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
@@ -882,7 +882,7 @@
 {
 	GHashTable *components = purple_chat_get_components(chat);
 	PurpleConnection *gc = purple_account_get_connection(purple_chat_get_account(chat));
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClientConnection conn = sg->conn;
 	const char *chname = NULL;
 	SilcChannelEntry channel = NULL;
@@ -1020,7 +1020,7 @@
 
 void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	const char *channel, *passphrase, *parentch;
@@ -1103,7 +1103,7 @@
 void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
 			    const char *name)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcHashTableList htl;
@@ -1148,7 +1148,7 @@
 
 void silcpurple_chat_leave(PurpleConnection *gc, int id)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcHashTableList htl;
@@ -1218,7 +1218,7 @@
 int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
 			 PurpleMessageFlags msgflags)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcHashTableList htl;
@@ -1340,7 +1340,7 @@
 
 void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcHashTableList htl;
@@ -1384,7 +1384,7 @@
 
 PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	GList *fields = NULL;
@@ -1425,7 +1425,7 @@
 
 	if (!gc)
 		return;
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	purple_roomlist_set_in_progress(list, FALSE);
 	if (sg->roomlist == list) {
--- a/libpurple/protocols/silc/ft.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/silc/ft.c	Mon Sep 26 14:57:21 2011 +0900
@@ -132,8 +132,7 @@
 	if (!offset && filesize)
 		purple_xfer_set_size(xfer->xfer, filesize);
 	if (offset && filesize) {
-		xfer->xfer->bytes_sent = offset;
-		xfer->xfer->bytes_remaining = filesize - offset;
+		purple_xfer_set_bytes_sent(xfer->xfer, offset);
 	}
 	purple_xfer_update_progress(xfer->xfer);
 
@@ -310,7 +309,7 @@
 			    const char *hostname, SilcUInt16 port)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcPurpleXfer xfer;
 
 	xfer = silc_calloc(1, sizeof(*xfer));
@@ -435,7 +434,7 @@
 
 PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcDList clients;
--- a/libpurple/protocols/silc/ops.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/silc/ops.c	Mon Sep 26 14:57:21 2011 +0900
@@ -88,7 +88,7 @@
 			gboolean recursive)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	const char *type;
 	const unsigned char *data;
 	SilcUInt32 data_len;
@@ -264,7 +264,7 @@
 		     SilcUInt32 message_len)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo = NULL;
 	char *msg, *tmp;
 
@@ -354,7 +354,7 @@
 		     SilcUInt32 message_len)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo = NULL;
 	char *msg, *tmp;
 
@@ -431,7 +431,7 @@
 {
 	va_list va;
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleConversation *convo;
 	SilcClientEntry client_entry, client_entry2;
@@ -941,7 +941,7 @@
 	     SilcUInt32 argc, unsigned char **argv)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 
 	switch (command) {
 
@@ -1078,7 +1078,7 @@
 		   SilcStatus error, va_list ap)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	PurpleConversation *convo;
 
 	switch (command) {
@@ -1724,7 +1724,7 @@
 		     SilcGetAuthMeth completion, void *context)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcPurpleAskPassphrase internal;
 	const char *password;
 
@@ -1775,7 +1775,7 @@
 		       SilcVerifyPublicKey completion, void *context)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 
 	if (!sg->conn && (conn_type == SILC_CONN_SERVER ||
 			  conn_type == SILC_CONN_ROUTER)) {
--- a/libpurple/protocols/silc/pk.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/silc/pk.c	Mon Sep 26 14:57:21 2011 +0900
@@ -79,7 +79,7 @@
 static void silcpurple_verify_details(PublicKeyVerify verify, gint id)
 {
 	PurpleConnection *gc = verify->client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 
 	silcpurple_show_public_key(sg, verify->entity_name, verify->public_key,
 				   G_CALLBACK(silcpurple_verify_details_cb),
--- a/libpurple/protocols/silc/silc.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/silc/silc.c	Mon Sep 26 14:57:21 2011 +0900
@@ -78,7 +78,7 @@
 	const char *state;
 
 	if (gc != NULL)
-		sg = gc->proto_data;
+		sg = purple_connection_get_protocol_data(gc);
 
 	if (status == NULL)
 		return;
@@ -125,7 +125,7 @@
 static void
 silcpurple_keepalive(PurpleConnection *gc)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	silc_packet_send(sg->conn->stream, SILC_PACKET_HEARTBEAT, 0,
 			 NULL, 0);
 }
@@ -180,7 +180,7 @@
 {
 	SilcClient client = (SilcClient)context;
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcPurpleTask ptask = NULL;
 
 	if (added) {
@@ -252,7 +252,7 @@
 	struct utsname u;
 #endif
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	switch (status) {
 	case SILC_CLIENT_CONN_SUCCESS:
@@ -367,7 +367,7 @@
 	SilcClientConnectionParams params;
 	const char *dfile;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (status != SILC_SOCKET_OK) {
 		purple_connection_error(gc,
@@ -376,7 +376,7 @@
 		silc_pkcs_public_key_free(sg->public_key);
 		silc_pkcs_private_key_free(sg->private_key);
 		silc_free(sg);
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 		return;
 	}
 
@@ -415,7 +415,7 @@
 
 	g_return_if_fail(gc != NULL);
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (source < 0) {
 		purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -423,7 +423,7 @@
 		silc_pkcs_public_key_free(sg->public_key);
 		silc_pkcs_private_key_free(sg->private_key);
 		silc_free(sg);
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 		return;
 	}
 
@@ -449,7 +449,7 @@
 	{
 		purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 		                             _("Unable to connect"));
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 		silc_free(sg);
 		return;
 	}
@@ -457,7 +457,7 @@
 
 static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields *fields)
 {
-	SilcPurple sg = (SilcPurple)gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 	char pkd[256], prd[256];
 	const char *password;
@@ -473,7 +473,7 @@
 	if (!password || !*password)
 	{
 		purple_notify_error(gc, NULL, _("Password is required to sign on."), NULL);
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 		silc_free(sg);
 		return;
 	}
@@ -492,7 +492,7 @@
 				&sg->public_key, &sg->private_key)) {
 		purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 		                             _("Unable to load SILC key pair"));
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 		silc_free(sg);
 		return;
 	}
@@ -505,10 +505,10 @@
 	/* The password prompt dialog doesn't get disposed if the account disconnects */
 	if (!PURPLE_CONNECTION_IS_VALID(gc))
 		return;
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 	purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 			_("Unable to load SILC key pair"));
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 	silc_free(sg);
 }
 
@@ -537,7 +537,7 @@
 		}
 		purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 		                             _("Unable to load SILC key pair"));
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 		silc_free(sg);
 		return;
 	}
@@ -558,7 +558,7 @@
 	gc = account->gc;
 	if (!gc)
 		return;
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 
 	memset(&params, 0, sizeof(params));
 	strcat(params.nickname_format, "%n#a");
@@ -612,14 +612,14 @@
 	sg->client = client;
 	sg->gc = gc;
 	sg->account = account;
-	gc->proto_data = sg;
+	purple_connection_set_protocol_data(gc, sg);
 
 	/* Init SILC client */
 	if (!silc_client_init(client, username, hostname, realname,
 			      silcpurple_running, sg)) {
 		purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 		                             _("Unable to initialize SILC protocol"));
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 		silc_free(sg);
 		silc_free(hostname);
 		g_free(username);
@@ -632,7 +632,7 @@
 	if (!silcpurple_check_silc_dir(gc)) {
 		purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 		                             _("Error loading SILC key pair"));
-		gc->proto_data = NULL;
+		purple_connection_set_protocol_data(gc, NULL);
 		silc_free(sg);
 		return;
 	}
@@ -669,7 +669,7 @@
 static void
 silcpurple_close(PurpleConnection *gc)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 #if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1)
 	SilcPurpleTask task;
 #endif /* __SILC_TOOLKIT_VERSION */
@@ -733,7 +733,7 @@
 static void
 silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	PurpleRequestField *f;
@@ -744,7 +744,7 @@
 	SilcVCardStruct vcard;
 	const char *val;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 	if (!sg)
 		return;
 
@@ -905,7 +905,7 @@
 silcpurple_attrs(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	PurpleRequestFields *fields;
@@ -922,7 +922,7 @@
 	gboolean device = TRUE;
 	char status[1024], tz[16];
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 	if (!sg)
 		return;
 
@@ -1072,7 +1072,7 @@
 
 	if (!gc)
 		return;
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 	if (!sg)
 		return;
 
@@ -1090,7 +1090,7 @@
 
 	if (!gc)
 		return;
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 	if (!sg)
 		return;
 
@@ -1125,7 +1125,7 @@
 	int keylen = SILCPURPLE_DEF_PKCS_LEN;
 	SilcPublicKey public_key;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 	if (!sg)
 		return;
 
@@ -1208,7 +1208,7 @@
 silcpurple_create_keypair(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *g;
 	PurpleRequestField *f;
@@ -1356,7 +1356,7 @@
 			    void *context)
 {
 	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcPurpleIM im = context;
 	PurpleConversation *convo;
 	char tmp[256];
@@ -1433,7 +1433,7 @@
 silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
 		   PurpleMessageFlags flags)
 {
-	SilcPurple sg = gc->proto_data;
+	SilcPurple sg = purple_connection_get_protocol_data(gc);
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	SilcDList clients;
@@ -1718,7 +1718,7 @@
 	if (gc == NULL)
 		return PURPLE_CMD_RET_FAILED;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (sg == NULL)
 		return PURPLE_CMD_RET_FAILED;
@@ -1747,7 +1747,7 @@
 	if (gc == NULL)
 		return PURPLE_CMD_RET_FAILED;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (sg == NULL)
 		return PURPLE_CMD_RET_FAILED;
@@ -1769,10 +1769,10 @@
 
 	gc = purple_conversation_get_gc(conv);
 
-	if (gc == NULL || !args || gc->proto_data == NULL)
+	if (gc == NULL || !args || purple_connection_get_protocol_data(gc) == NULL)
 		return PURPLE_CMD_RET_FAILED;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (args[0])
 		chname = args[0];
@@ -1823,7 +1823,7 @@
 	if (gc == NULL)
 		return PURPLE_CMD_RET_FAILED;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (sg == NULL)
 		return PURPLE_CMD_RET_FAILED;
@@ -1855,7 +1855,7 @@
 	if (gc == NULL)
 		return PURPLE_CMD_RET_FAILED;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (sg == NULL)
 		return PURPLE_CMD_RET_FAILED;
@@ -1892,7 +1892,7 @@
 	if (gc == NULL)
 		return PURPLE_CMD_RET_FAILED;
 
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	if (sg == NULL)
 		return PURPLE_CMD_RET_FAILED;
@@ -2050,6 +2050,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
 	OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE |
 	OPT_PROTO_SLASH_COMMANDS_NATIVE,
@@ -2092,7 +2093,6 @@
 	silcpurple_keepalive,			/* keepalive */
 	NULL,					/* register_user */
 	NULL,					/* get_cb_info */
-	NULL,					/* get_cb_away */
 	NULL,					/* alias_buddy */
 	NULL,					/* group_buddy */
 	NULL,					/* rename_group */
@@ -2117,15 +2117,12 @@
 	NULL,				        /* unregister_user */
 	NULL,				        /* send_attention */
 	NULL,				        /* get_attention_types */
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,				        /* get_account_text_table */
 	NULL,				        /* initiate_media */
 	NULL,				        /* get_media_caps */
 	NULL,				        /* get_moods */
 	NULL,				        /* set_public_alias */
-	NULL,				        /* get_public_alias */
-	NULL,				        /* add_buddy_with_invite */
-	NULL				        /* add_buddies_with_invite */
+	NULL				        /* get_public_alias */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/silc/silcpurple.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/silc/silcpurple.h	Mon Sep 26 14:57:21 2011 +0900
@@ -105,7 +105,7 @@
 				  SilcVerifyPublicKey completion,
 				  void *context);
 GList *silcpurple_buddy_menu(PurpleBuddy *buddy);
-void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
+void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message);
 void silcpurple_send_buddylist(PurpleConnection *gc);
 void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 void silcpurple_buddy_keyagr_request(SilcClient client,
--- a/libpurple/protocols/silc/wb.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/silc/wb.c	Mon Sep 26 14:57:21 2011 +0900
@@ -105,7 +105,7 @@
 
 PurpleWhiteboard *silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry)
 {
-        SilcClientConnection conn;
+	SilcClientConnection conn;
 	PurpleWhiteboard *wb;
 	SilcPurpleWb wbs;
 
@@ -116,7 +116,7 @@
 	if (!wb)
 		return NULL;
 
-	if (!wb->proto_data) {
+	if (!purple_whiteboard_get_protocol_data(wb)) {
 		wbs = silc_calloc(1, sizeof(*wbs));
 		if (!wbs)
 			return NULL;
@@ -126,7 +126,7 @@
 		wbs->height = SILCPURPLE_WB_HEIGHT;
 		wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL;
 		wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK;
-		wb->proto_data = wbs;
+		purple_whiteboard_set_protocol_data(wb, wbs);
 
 		/* Start the whiteboard */
 		purple_whiteboard_start(wb);
@@ -147,7 +147,7 @@
 	if (!wb)
 		return NULL;
 
-	if (!wb->proto_data) {
+	if (!purple_whiteboard_get_protocol_data(wb)) {
 		wbs = silc_calloc(1, sizeof(*wbs));
 		if (!wbs)
 			return NULL;
@@ -157,7 +157,7 @@
 		wbs->height = SILCPURPLE_WB_HEIGHT;
 		wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL;
 		wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK;
-		wb->proto_data = wbs;
+		purple_whiteboard_set_protocol_data(wb, wbs);
 
 		/* Start the whiteboard */
 		purple_whiteboard_start(wb);
@@ -168,9 +168,10 @@
 }
 
 static void
-silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb,
+silcpurple_wb_parse(PurpleWhiteboard *wb,
 		  unsigned char *message, SilcUInt32 message_len)
 {
+	SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
 	SilcUInt8 command;
 	SilcUInt16 width, height, brush_size;
 	SilcUInt32 brush_color, x, y, dx, dy;
@@ -238,15 +239,15 @@
 {
 	PurpleWhiteboard *wb;
 
-        if (id != 1)
-                goto out;
+	if (id != 1)
+		goto out;
 
 	if (!req->channel)
 		wb = silcpurple_wb_init(req->sg, req->sender);
 	else
 		wb = silcpurple_wb_init_ch(req->sg, req->channel);
 
-	silcpurple_wb_parse(wb->proto_data, wb, req->message, req->message_len);
+	silcpurple_wb_parse(wb, req->message, req->message_len);
 
   out:
 	silc_free(req->message);
@@ -264,7 +265,7 @@
 	SilcPurple sg;
 
 	gc = client->application;
-	sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	/* Open whiteboard automatically if requested */
 	if (purple_account_get_bool(sg->account, "open-wb", FALSE)) {
@@ -275,7 +276,7 @@
 		else
 			wb = silcpurple_wb_init_ch(sg, channel);
 
-		silcpurple_wb_parse(wb->proto_data, wb,
+		silcpurple_wb_parse(wb,
 				    (unsigned char *)message,
 				    message_len);
 		return;
@@ -318,12 +319,11 @@
 			 SilcUInt32 message_len)
 {
 	SilcPurple sg;
-        PurpleConnection *gc;
+	PurpleConnection *gc;
 	PurpleWhiteboard *wb;
-	SilcPurpleWb wbs;
 
 	gc = client->application;
-        sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	wb = purple_whiteboard_get_session(sg->account, sender->nickname);
 	if (!wb) {
@@ -333,8 +333,7 @@
 		return;
 	}
 
-	wbs = wb->proto_data;
-	silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len);
+	silcpurple_wb_parse(wb, (unsigned char *)message, message_len);
 }
 
 /* Process incoming whiteboard message on channel */
@@ -347,12 +346,11 @@
 			    SilcUInt32 message_len)
 {
 	SilcPurple sg;
-        PurpleConnection *gc;
+	PurpleConnection *gc;
 	PurpleWhiteboard *wb;
-	SilcPurpleWb wbs;
 
 	gc = client->application;
-        sg = gc->proto_data;
+	sg = purple_connection_get_protocol_data(gc);
 
 	wb = purple_whiteboard_get_session(sg->account, channel->channel_name);
 	if (!wb) {
@@ -362,25 +360,24 @@
 		return;
 	}
 
-	wbs = wb->proto_data;
-	silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len);
+	silcpurple_wb_parse(wb, (unsigned char *)message, message_len);
 }
 
 /* Send whiteboard message */
 
 void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list)
 {
-	SilcPurpleWb wbs = wb->proto_data;
+	SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
 	SilcBuffer packet;
 	GList *list;
 	int len;
-        PurpleConnection *gc;
-        SilcPurple sg;
+	PurpleConnection *gc;
+	SilcPurple sg;
 
 	g_return_if_fail(draw_list);
-	gc = purple_account_get_connection(wb->account);
+	gc = purple_account_get_connection(purple_whiteboard_get_account(wb));
 	g_return_if_fail(gc);
- 	sg = gc->proto_data;
+ 	sg = purple_connection_get_protocol_data(gc);
 	g_return_if_fail(sg);
 
 	len = SILCPURPLE_WB_HEADER;
@@ -435,20 +432,22 @@
 
 void silcpurple_wb_end(PurpleWhiteboard *wb)
 {
-	silc_free(wb->proto_data);
-	wb->proto_data = NULL;
+	SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
+
+	silc_free(wbs);
+	purple_whiteboard_set_protocol_data(wb, NULL);
 }
 
 void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height)
 {
-	SilcPurpleWb wbs = wb->proto_data;
+	SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
 	*width = wbs->width;
 	*height = wbs->height;
 }
 
 void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height)
 {
-	SilcPurpleWb wbs = wb->proto_data;
+	SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
 	wbs->width = width > SILCPURPLE_WB_WIDTH_MAX ? SILCPURPLE_WB_WIDTH_MAX :
 			width;
 	wbs->height = height > SILCPURPLE_WB_HEIGHT_MAX ? SILCPURPLE_WB_HEIGHT_MAX :
@@ -460,14 +459,14 @@
 
 void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color)
 {
-	SilcPurpleWb wbs = wb->proto_data;
+	SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
 	*size = wbs->brush_size;
 	*color = wbs->brush_color;
 }
 
 void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color)
 {
-	SilcPurpleWb wbs = wb->proto_data;
+	SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
 	wbs->brush_size = size;
 	wbs->brush_color = color;
 
@@ -477,15 +476,15 @@
 
 void silcpurple_wb_clear(PurpleWhiteboard *wb)
 {
-	SilcPurpleWb wbs = wb->proto_data;
+	SilcPurpleWb wbs = purple_whiteboard_get_protocol_data(wb);
 	SilcBuffer packet;
 	int len;
-        PurpleConnection *gc;
-        SilcPurple sg;
+	PurpleConnection *gc;
+	SilcPurple sg;
 
-	gc = purple_account_get_connection(wb->account);
+	gc = purple_account_get_connection(purple_whiteboard_get_account(wb));
 	g_return_if_fail(gc);
- 	sg = gc->proto_data;
+ 	sg = purple_connection_get_protocol_data(gc);
 	g_return_if_fail(sg);
 
 	len = SILCPURPLE_WB_HEADER;
--- a/libpurple/protocols/simple/simple.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/simple/simple.c	Mon Sep 26 14:57:21 2011 +0900
@@ -67,7 +67,7 @@
 }
 
 static void simple_keep_alive(PurpleConnection *gc) {
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	if(sip->udp) { /* in case of UDP send a packet only with a 0 byte to
 			 remain in the NAT table */
 		gchar buf[2] = {0, 0};
@@ -100,14 +100,15 @@
 }
 
 static void simple_set_status(PurpleAccount *account, PurpleStatus *status) {
+	PurpleConnection *gc = purple_account_get_connection(account);
 	PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
 	struct simple_account_data *sip = NULL;
 
 	if (!purple_status_is_active(status))
 		return;
 
-	if (account->gc)
-		sip = account->gc->proto_data;
+	if (gc)
+		sip = purple_connection_get_protocol_data(gc);
 
 	if (sip)
 	{
@@ -192,9 +193,9 @@
 	}
 }
 
-static void simple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+static void simple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *message)
 {
-	struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	struct simple_buddy *b;
 	const char *name = purple_buddy_get_name(buddy);
 	if(strncmp(name, "sip:", 4)) {
@@ -222,7 +223,7 @@
 	buddies = purple_find_buddies(account, NULL);
 	while (buddies) {
 		PurpleBuddy *buddy = buddies->data;
-		simple_add_buddy(gc, buddy, purple_buddy_get_group(buddy));
+		simple_add_buddy(gc, buddy, purple_buddy_get_group(buddy), NULL);
 
 		buddies = g_slist_delete_link(buddies, buddies);
 	}
@@ -231,7 +232,7 @@
 static void simple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	const char *name = purple_buddy_get_name(buddy);
-	struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	struct simple_buddy *b = g_hash_table_lookup(sip->buddies, name);
 	g_hash_table_remove(sip->buddies, name);
 	g_free(b->name);
@@ -412,7 +413,7 @@
 
 static void simple_canwrite_cb(gpointer data, gint source, PurpleInputCondition cond) {
 	PurpleConnection *gc = data;
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	gsize max_write;
 	gssize written;
 
@@ -457,7 +458,7 @@
 		return;
 	}
 
-	sip = gc->proto_data;
+	sip = purple_connection_get_protocol_data(gc);
 	sip->fd = source;
 	sip->connecting = FALSE;
 
@@ -474,7 +475,7 @@
 
 
 static void sendlater(PurpleConnection *gc, const char *buf) {
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 
 	if(!sip->connecting) {
 		purple_debug_info("simple", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
@@ -491,7 +492,7 @@
 }
 
 static void sendout_pkt(PurpleConnection *gc, const char *buf) {
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	time_t currtime = time(NULL);
 	int writelen = strlen(buf);
 
@@ -629,7 +630,7 @@
 static void send_sip_request(PurpleConnection *gc, const gchar *method,
 		const gchar *url, const gchar *to, const gchar *addheaders,
 		const gchar *body, struct sip_dialog *dialog, TransCallback tc) {
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	char *callid = dialog ? g_strdup(dialog->callid) : gencallid();
 	char *auth = NULL;
 	const char *addh = "";
@@ -1026,7 +1027,7 @@
 }
 
 static int simple_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags) {
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	char *to = g_strdup(who);
 	char *text = purple_unescape_html(what);
 	simple_send_message(sip, to, text, NULL);
@@ -1285,7 +1286,7 @@
 }
 
 static unsigned int simple_typing(PurpleConnection *gc, const char *name, PurpleTypingState state) {
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 
 	gchar *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
 			"<isComposing xmlns=\"urn:ietf:params:xml:ns:im-iscomposing\"\n"
@@ -1661,7 +1662,7 @@
 
 static void simple_udp_process(gpointer data, gint source, PurpleInputCondition con) {
 	PurpleConnection *gc = data;
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	struct sipmsg *msg;
 	int len;
 	time_t currtime = time(NULL);
@@ -1681,7 +1682,7 @@
 static void simple_input_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = data;
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	int len;
 	struct sip_connection *conn = connection_find(sip, source);
 	if(!conn) {
@@ -1714,7 +1715,7 @@
 /* Callback for new connections on incoming TCP port */
 static void simple_newconn_cb(gpointer data, gint source, PurpleInputCondition cond) {
 	PurpleConnection *gc = data;
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 	struct sip_connection *conn;
 	int newfd, flags;
 
@@ -1745,7 +1746,7 @@
 		return;
 	}
 
-	sip = gc->proto_data;
+	sip = purple_connection_get_protocol_data(gc);
 	sip->fd = source;
 
 	conn = connection_create(sip, source);
@@ -1822,7 +1823,7 @@
 	}
 
 	/* create socket for incoming connections */
-	sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_DGRAM,
+	sip->listen_data = purple_network_listen_range(5060, 5160, AF_UNSPEC, SOCK_DGRAM, TRUE,
 				simple_udp_host_resolved_listen_cb, sip);
 	if (sip->listen_data == NULL) {
 		purple_connection_error(sip->gc,
@@ -1892,7 +1893,7 @@
 	/* TCP case */
 	if(!sip->udp) {
 		/* create socket for incoming connections */
-		sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
+		sip->listen_data = purple_network_listen_range(5060, 5160, AF_UNSPEC, SOCK_STREAM, TRUE,
 					simple_tcp_connect_listen_cb, sip);
 		if (sip->listen_data == NULL) {
 			purple_connection_error(sip->gc,
@@ -1903,7 +1904,7 @@
 	} else { /* UDP */
 		purple_debug_info("simple", "using udp with server %s and port %d\n", hostname, port);
 
-		sip->query_data = purple_dnsquery_a_account(sip->account, hostname,
+		sip->query_data = purple_dnsquery_a(sip->account, hostname,
 			port, simple_udp_host_resolved, sip);
 		if (sip->query_data == NULL) {
 			purple_connection_error(sip->gc,
@@ -1930,7 +1931,8 @@
 		return;
 	}
 
-	gc->proto_data = sip = g_new0(struct simple_account_data, 1);
+	sip = g_new0(struct simple_account_data, 1);
+	purple_connection_set_protocol_data(gc, sip);
 	sip->gc = gc;
 	sip->fd = -1;
 	sip->listenfd = -1;
@@ -1968,13 +1970,13 @@
 		hosttoconnect = purple_account_get_string(account, "proxy", sip->servername);
 	}
 
-	sip->srv_query_data = purple_srv_resolve_account(account, "sip",
+	sip->srv_query_data = purple_srv_resolve(account, "sip",
 			sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
 }
 
 static void simple_close(PurpleConnection *gc)
 {
-	struct simple_account_data *sip = gc->proto_data;
+	struct simple_account_data *sip = purple_connection_get_protocol_data(gc);
 
 	if (!sip)
 		return;
@@ -2005,7 +2007,7 @@
 		purple_dnsquery_destroy(sip->query_data);
 
 	if (sip->srv_query_data != NULL)
-		purple_srv_cancel(sip->srv_query_data);
+		purple_srv_txt_query_destroy(sip->srv_query_data);
 
 	if (sip->listen_data != NULL)
 		purple_network_listen_cancel(sip->listen_data);
@@ -2039,11 +2041,12 @@
 	g_free(sip->realhostname);
 
 	g_free(sip);
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 }
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	0,
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
@@ -2084,7 +2087,6 @@
 	simple_keep_alive,		/* keepalive */
 	NULL,					/* register_user */
 	NULL,					/* get_cb_info */
-	NULL,					/* get_cb_away */
 	NULL,					/* alias_buddy */
 	NULL,					/* group_buddy */
 	NULL,					/* rename_group */
@@ -2109,15 +2111,12 @@
 	NULL,					/* unregister_user */
 	NULL,					/* send_attention */
 	NULL,					/* get_attention_types */
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,					/* get_account_text_table */
 	NULL,					/* initiate_media */
 	NULL,					/* get_media_caps */
 	NULL,					/* get_moods */
 	NULL,					/* set_public_alias */
-	NULL,					/* get_public_alias */
-	NULL,					/* add_buddy_with_invite */
-	NULL					/* add_buddies_with_invite */
+	NULL					/* get_public_alias */
 };
 
 
--- a/libpurple/protocols/yahoo/libyahoo.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/libyahoo.c	Mon Sep 26 14:57:21 2011 +0900
@@ -195,6 +195,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC,
 	NULL, /* user_splits */
 	NULL, /* protocol_options */
@@ -235,7 +236,6 @@
 	yahoo_keepalive,
 	NULL, /* register_user */
 	NULL, /* get_cb_info */
-	NULL, /* get_cb_away */
 	yahoo_update_alias, /* alias_buddy */
 	yahoo_change_buddys_group,
 	yahoo_rename_group,
@@ -258,19 +258,14 @@
 	NULL, /* send_raw */
 	NULL, /* roomlist_room_serialize */
 	NULL, /* unregister_user */
-
 	yahoo_send_attention,
 	yahoo_attention_types,
-
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	yahoo_get_account_text_table,    /* get_account_text_table */
 	NULL, /* initiate_media */
 	NULL,  /* get_media_caps */
 	NULL,  /* get_moods */
 	NULL,  /* set_public_alias */
-	NULL,  /* get_public_alias */
-	NULL,  /* add_buddy_with_invite */
-	NULL   /* add_buddies_with_invite */
+	NULL   /* get_public_alias */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/yahoo/libyahoojp.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/libyahoojp.c	Mon Sep 26 14:57:21 2011 +0900
@@ -91,6 +91,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	OPT_PROTO_MAIL_CHECK | OPT_PROTO_CHAT_TOPIC,
 	NULL, /* user_splits */
 	NULL, /* protocol_options */
@@ -131,7 +132,6 @@
 	yahoo_keepalive,
 	NULL, /* register_user */
 	NULL, /* get_cb_info */
-	NULL, /* get_cb_away */
 	yahoo_update_alias, /* alias_buddy */
 	yahoo_change_buddys_group,
 	yahoo_rename_group,
@@ -158,15 +158,12 @@
 	yahoo_send_attention,
 	yahoo_attention_types,
 
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	yahoojp_get_account_text_table,    /* get_account_text_table */
 	NULL, /* initiate_media */
 	NULL, /* get_media_caps */
 	NULL, /* get_moods */
 	NULL, /* set_public_alias */
-	NULL, /* get_public_alias */
-	NULL, /* add_buddy_with_invite */
-	NULL  /* add_buddies_with_invite */
+	NULL  /* get_public_alias */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/yahoo/libymsg.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/libymsg.c	Mon Sep 26 14:57:21 2011 +0900
@@ -496,7 +496,7 @@
 	GSList *l = pkt->hash;
 
 	PurpleAccount *account = purple_connection_get_account(gc);
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	GHashTable *ht;
 	char *norm_bud = NULL;
 	char *temp = NULL;
@@ -638,7 +638,7 @@
 	gboolean got_serv_list = FALSE;
 	YahooFriend *f = NULL;
 	PurpleAccount *account = purple_connection_get_account(gc);
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	GHashTable *ht;
 
 	char **lines;
@@ -787,7 +787,7 @@
 	YahooFriend *f = NULL;
 	GSList *l = pkt->hash;
 	gint val_11 = 0;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	account = purple_connection_get_account(gc);
@@ -899,7 +899,7 @@
 	char *server_msg = NULL;
 	char *m;
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 
 	while (l != NULL) {
@@ -960,7 +960,7 @@
 static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type)
 {
 	PurpleAccount *account;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	GSList *l = pkt->hash;
 	GSList *list = NULL;
 	struct _yahoo_im *im = NULL;
@@ -1061,7 +1061,7 @@
 							doodle_session *ds;
 							wb = purple_whiteboard_create(account, im->from,
 											DOODLE_STATE_REQUESTED);
-							ds = wb->proto_data;
+							ds = purple_whiteboard_get_protocol_data(wb);
 							ds->imv_key = g_strdup(pair->value);
 
 							yahoo_doodle_command_send_request(gc, im->from, pair->value);
@@ -1202,7 +1202,7 @@
 {
 	struct yahoo_add_request *add_req = data;
 	struct yahoo_packet *pkt;
-	YahooData *yd = add_req->gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(add_req->gc);
 	const char *who = add_req->who;
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -1233,7 +1233,7 @@
 static void
 yahoo_buddy_add_deny_cb(struct yahoo_add_request *add_req, const char *msg)
 {
-	YahooData *yd = add_req->gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(add_req->gc);
 	struct yahoo_packet *pkt;
 	char *encoded_msg = NULL;
 	const char *who = add_req->who;
@@ -1295,7 +1295,7 @@
 static void yahoo_buddy_denied_our_add(PurpleConnection *gc, const char *who, const char *reason)
 {
 	char *notify_msg;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	if (who == NULL)
 		return;
@@ -1631,7 +1631,7 @@
 static void yahoo_process_mail(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	const char *who = NULL;
 	const char *email = NULL;
 	const char *subj = NULL;
@@ -1715,7 +1715,7 @@
 
 static void yahoo_auth16_stage3(PurpleConnection *gc, const char *crypt)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 	const char *name = purple_normalize(account, purple_account_get_username(account));
 	PurpleCipher *md5_cipher;
@@ -2020,7 +2020,7 @@
 			gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE);
 
 			url = g_strdup_printf(yahoojp ? YAHOOJP_LOGIN_URL : YAHOO_LOGIN_URL, token);
-			url_data = purple_util_fetch_url_request_len(
+			url_data = purple_util_fetch_url_request(
 					proxy_ssl ? account : NULL, url, TRUE, YAHOO_CLIENT_USERAGENT,
 					TRUE, NULL, TRUE, -1, yahoo_auth16_stage2, auth_data);
 			if (url_data)
@@ -2061,7 +2061,7 @@
 	g_free(encoded_password);
 	g_free(encoded_username);
 
-	url_data = purple_util_fetch_url_request_len(
+	url_data = purple_util_fetch_url_request(
 			proxy_ssl ? account : NULL, url, TRUE,
 			YAHOO_CLIENT_USERAGENT, TRUE, NULL, FALSE, -1,
 			yahoo_auth16_stage1_cb, auth_data);
@@ -2213,7 +2213,7 @@
 static void yahoo_process_authresp(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 #ifdef TRY_WEBMESSENGER_LOGIN
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 #endif /* TRY_WEBMESSENGER_LOGIN */
 	GSList *l = pkt->hash;
 	int err = 0;
@@ -2253,7 +2253,7 @@
 			if (gc->inpa)
 				purple_input_remove(gc->inpa);
 			url_data = purple_util_fetch_url(WEBMESSENGER_URL, TRUE,
-					"Purple/" VERSION, FALSE, yahoo_login_page_cb, gc);
+					"Purple/" VERSION, FALSE, -1, yahoo_login_page_cb, gc);
 			if (url_data != NULL)
 				yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
 			return;
@@ -2306,7 +2306,7 @@
 	char *buf;
 	YahooFriend *f;
 	GSList *l = pkt->hash;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
 	while (l) {
@@ -2400,7 +2400,7 @@
 	PurpleConnection *gc = user_data;
 	struct yahoo_packet *pkt_to_send;
 	PurpleAccount *account;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	account = purple_connection_get_account(gc);
 
@@ -2419,7 +2419,7 @@
 static gboolean yahoo_p2p_keepalive(gpointer data)
 {
 	PurpleConnection *gc = data;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc);
 
@@ -2466,7 +2466,7 @@
 	if(!(p2p_data = data))
 		return ;
 
-	yd = p2p_data->gc->proto_data;
+	yd = purple_connection_get_protocol_data(p2p_data->gc);
 
 	/* lets see whats in the packet */
 	while (l) {
@@ -2551,7 +2551,7 @@
 
 	if(!(p2p_data = data))
 		return ;
-	yd = p2p_data->gc->proto_data;
+	yd = purple_connection_get_protocol_data(p2p_data->gc);
 
 	len = read(source, buf, sizeof(buf));
 	if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
@@ -2623,7 +2623,7 @@
 
 	if(!(p2p_data = data))
 		return ;
-	yd = p2p_data->gc->proto_data;
+	yd = purple_connection_get_protocol_data(p2p_data->gc);
 
 	acceptfd = accept(source, NULL, 0);
 	if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
@@ -2663,7 +2663,7 @@
 	if(!(p2p_data = data))
 		return FALSE;
 
-	yd = p2p_data->gc->proto_data;
+	yd = purple_connection_get_protocol_data(p2p_data->gc);
 
 	purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect\n");
 	yahoo_p2p_disconnect_destroy_data(data);
@@ -2684,7 +2684,7 @@
 	if(!(p2p_data = data))
 		return ;
 
-	yd = p2p_data->gc->proto_data;
+	yd = purple_connection_get_protocol_data(p2p_data->gc);
 	yd->listen_data = NULL;
 
 	if(listenfd == -1) {
@@ -2712,7 +2712,7 @@
 	YahooFriend *f;
 	struct yahoo_packet *pkt;
 	PurpleAccount *account;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_p2p_data *p2p_data;
 	const char *norm_username;
 
@@ -2776,7 +2776,7 @@
 	if (yd->listen_data)
 		purple_debug_warning("yahoo","p2p: Failed to create p2p server - server already exists\n");
 	else {
-		yd->listen_data = purple_network_listen(YAHOO_PAGER_PORT_P2P, SOCK_STREAM, yahoo_p2p_server_listen_cb, p2p_data);
+		yd->listen_data = purple_network_listen(YAHOO_PAGER_PORT_P2P, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_server_listen_cb, p2p_data);
 		if (yd->listen_data == NULL)
 			purple_debug_warning("yahoo","p2p: Failed to created p2p server\n");
 	}
@@ -2793,7 +2793,7 @@
 	YahooData *yd;
 
 	p2p_data = data;
-	yd = p2p_data->gc->proto_data;
+	yd = purple_connection_get_protocol_data(p2p_data->gc);
 
 	if(error_message != NULL) {
 		purple_debug_warning("yahoo","p2p: %s\n",error_message);
@@ -3138,7 +3138,7 @@
 static void yahoo_pending(gpointer data, gint source, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = data;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	char buf[1024];
 	int len;
 
@@ -3246,7 +3246,7 @@
 		return;
 	}
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	yd->fd = source;
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, yd->current_status, yd->session_id);
@@ -3272,7 +3272,7 @@
 		return;
 	}
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	yd->fd = source;
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_WEBLOGIN, YAHOO_STATUS_WEBLOGIN, yd->session_id);
@@ -3291,7 +3291,7 @@
 {
 	PurpleConnection *gc = data;
 	PurpleAccount *account = purple_connection_get_account(gc);
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	char bufread[2048], *i = bufread, *buf = bufread;
 	int len;
 	GString *s;
@@ -3370,7 +3370,7 @@
 	int written, remaining;
 
 	gc = data;
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	remaining = strlen(yd->auth) - yd->auth_written;
 	written = write(source, yd->auth + yd->auth_written, remaining);
@@ -3480,7 +3480,7 @@
 {
 	PurpleConnection *gc = (PurpleConnection *)user_data;
 	PurpleAccount *account = purple_connection_get_account(gc);
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	const char *sn = purple_account_get_username(account);
 	const char *pass = purple_connection_get_password(gc);
 	GHashTable *hash = yahoo_login_page_hash(url_text, len);
@@ -3677,12 +3677,13 @@
 
 void yahoo_login(PurpleAccount *account) {
 	PurpleConnection *gc = purple_account_get_connection(account);
-	YahooData *yd = gc->proto_data = g_new0(YahooData, 1);
+	YahooData *yd = g_new0(YahooData, 1);
 	PurpleStatus *status = purple_account_get_active_status(account);
 	gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
 	gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE);
 	PurpleUtilFetchUrlData *url_data;
 
+	purple_connection_set_protocol_data(gc, yd);
 	gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC;
 
 	purple_connection_update_progress(gc, _("Connecting"), 1, 2);
@@ -3714,7 +3715,7 @@
 
 	/* Get the pager server.  Actually start connecting in the callback since we
 	 * must have the contents of the HTTP response to proceed. */
-	url_data = purple_util_fetch_url_request_len(
+	url_data = purple_util_fetch_url_request(
 			proxy_ssl ? purple_connection_get_account(gc) : NULL,
 			yd->jp ? YAHOOJP_PAGER_HOST_REQ_URL : YAHOO_PAGER_HOST_REQ_URL,
 			use_whole_url ? TRUE : FALSE,
@@ -3727,7 +3728,7 @@
 }
 
 void yahoo_close(PurpleConnection *gc) {
-	YahooData *yd = (YahooData *)gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	GSList *l;
 
 	if (gc->inpa)
@@ -3816,7 +3817,7 @@
 	g_free(yd->current_list15_grp);
 
 	g_free(yd);
-	gc->proto_data = NULL;
+	purple_connection_set_protocol_data(gc, NULL);
 }
 
 const char *yahoo_list_icon(PurpleAccount *a, PurpleBuddy *b)
@@ -3833,7 +3834,7 @@
 
 	if (!b || !(account = purple_buddy_get_account(b)) ||
 			!(gc = purple_account_get_connection(account)) ||
-			!gc->proto_data)
+			!purple_connection_get_protocol_data(gc))
 		return NULL;
 
 	f = yahoo_friend_find(gc, purple_buddy_get_name(b));
@@ -3898,7 +3899,7 @@
 
 	buddy = (PurpleBuddy *) node;
 	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	id = yd->conf_id;
 
 	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
@@ -4061,7 +4062,7 @@
 	buddy = (PurpleBuddy *) node;
 	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
 
-	yahoo_add_buddy(gc, buddy, NULL);
+	yahoo_add_buddy(gc, buddy, NULL, NULL);
 }
 
 
@@ -4081,7 +4082,7 @@
 static GList *build_presence_submenu(YahooFriend *f, PurpleConnection *gc) {
 	GList *m = NULL;
 	PurpleMenuAction *act;
-	YahooData *yd = (YahooData *) gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
 		if (f->presence != YAHOO_PRESENCE_ONLINE) {
@@ -4141,7 +4142,7 @@
 	PurpleMenuAction *act;
 
 	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	static char buf2[1024];
 	YahooFriend *f;
 
@@ -4224,7 +4225,7 @@
 
 static void yahoo_act_id(PurpleConnection *gc, PurpleRequestFields *fields)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	const char *name = yd->profiles[purple_request_fields_get_choice(fields, "id")];
 
 	struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -4241,7 +4242,7 @@
 	PurpleConnection *gc = user_data;
 	gboolean set_cookie = FALSE;
 	gchar *url;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
 
@@ -4277,7 +4278,7 @@
 	/* XXX I have no idea how this will work with Yahoo! Japan. */
 
 	PurpleConnection *gc = action->context;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	PurpleUtilFetchUrlData *url_data;
 	const char* base_url = "http://login.yahoo.com";
@@ -4292,7 +4293,7 @@
 		use_whole_url ? base_url : "",
 		yd->cookie_t, yd->cookie_y);
 
-	url_data = purple_util_fetch_url_request_len(
+	url_data = purple_util_fetch_url_request(
 			purple_connection_get_account(gc), base_url, use_whole_url,
 			YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
 			yahoo_get_inbox_token_cb, gc);
@@ -4391,7 +4392,7 @@
 {
 	struct yahoo_sms_carrier_cb_data *sms_cb_data = user_data;
 	PurpleConnection *gc = sms_cb_data->gc;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	char *status = NULL;
 	char *carrier = NULL;
 	PurpleAccount *account = purple_connection_get_account(gc);
@@ -4446,7 +4447,7 @@
 
 static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	PurpleUtilFetchUrlData *url_data;
 	struct yahoo_sms_carrier_cb_data *sms_cb_data;
 	char *validate_request_str = NULL;
@@ -4481,10 +4482,10 @@
 		YAHOO_CLIENT_VERSION, yd->cookie_t, yd->cookie_y, strlen(validate_request_str), validate_request_str);
 
 	/* use whole URL if using HTTP Proxy */
-	if ((gc->account->proxy_info) && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP))
+	if ((gc->account->proxy_info) && (purple_proxy_info_get_type(gc->account->proxy_info) == PURPLE_PROXY_HTTP))
 	    use_whole_url = TRUE;
 
-	url_data = purple_util_fetch_url_request_len(
+	url_data = purple_util_fetch_url_request(
 			purple_connection_get_account(gc), YAHOO_SMS_CARRIER_URL, use_whole_url,
 			YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
 			yahoo_get_sms_carrier_cb, data);
@@ -4506,7 +4507,7 @@
 
 int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt = NULL;
 	char *msg = yahoo_html_to_codes(what);
 	char *msg2;
@@ -4663,7 +4664,7 @@
 
 unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_p2p_data *p2p_data;
 	YahooFederation fed = YAHOO_FEDERATION_NONE;
 	struct yahoo_packet *pkt = NULL;
@@ -4734,7 +4735,7 @@
 
 	gc = purple_account_get_connection(account);
 	presence = purple_status_get_presence(status);
-	yd = (YahooData *)gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	old_status = yd->current_status;
 
 	yd->current_status = get_yahoo_status_from_purple_status(status);
@@ -4800,7 +4801,7 @@
 
 void yahoo_set_idle(PurpleConnection *gc, int idle)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt = NULL;
 	char *msg = NULL, *msg2 = NULL;
 	PurpleStatus *status = NULL;
@@ -4920,7 +4921,7 @@
 void yahoo_keepalive(PurpleConnection *gc)
 {
 	struct yahoo_packet *pkt;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	time_t now = time(NULL);
 
 	/* We're only allowed to send a ping once an hour or the servers will boot us */
@@ -4951,9 +4952,9 @@
 
 }
 
-void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g)
+void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message)
 {
-	YahooData *yd = (YahooData *)gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	const char *group = NULL;
 	char *group2;
@@ -5016,7 +5017,7 @@
 
 void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
-	YahooData *yd = (YahooData *)gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	GSList *buddies, *l;
 	PurpleGroup *g;
@@ -5072,7 +5073,7 @@
 }
 
 void yahoo_add_deny(PurpleConnection *gc, const char *who) {
-	YahooData *yd = (YahooData *)gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
@@ -5095,7 +5096,7 @@
 }
 
 void yahoo_rem_deny(PurpleConnection *gc, const char *who) {
-	YahooData *yd = (YahooData *)gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	YahooFederation fed = YAHOO_FEDERATION_NONE;
 
@@ -5143,7 +5144,7 @@
 void yahoo_change_buddys_group(PurpleConnection *gc, const char *who,
 				   const char *old_group, const char *new_group)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	char *gpn, *gpo;
 	YahooFriend *f = yahoo_friend_find(gc, who);
@@ -5190,7 +5191,7 @@
 void yahoo_rename_group(PurpleConnection *gc, const char *old_name,
 							   PurpleGroup *group, GList *moved_buddies)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	char *gpn, *gpo;
 
--- a/libpurple/protocols/yahoo/libymsg.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/libymsg.h	Mon Sep 26 14:57:21 2011 +0900
@@ -371,7 +371,7 @@
 unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state);
 void yahoo_set_status(PurpleAccount *account, PurpleStatus *status);
 void yahoo_set_idle(PurpleConnection *gc, int idle);
-void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g);
+void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g, const char *message);
 void yahoo_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 void yahoo_add_deny(PurpleConnection *gc, const char *who);
 void yahoo_rem_deny(PurpleConnection *gc, const char *who);
--- a/libpurple/protocols/yahoo/util.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/util.c	Mon Sep 26 14:57:21 2011 +0900
@@ -64,8 +64,9 @@
 	char firstflag = 1;
 	gchar *t1,*t2,*t3;
 	GSList *tmp;
-	GSList *cookies;
-	cookies = ((YahooData*)(gc->proto_data))->cookies;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
+	GSList *cookies = yd->cookies;
+
 	tmp = cookies;
 	while(tmp)
 	{
@@ -130,7 +131,7 @@
  */
 char *yahoo_string_encode(PurpleConnection *gc, const char *str, gboolean *utf8)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	char *ret = NULL;
 	gsize newlen;
 	const char *to_codeset;
@@ -158,7 +159,7 @@
  */
 char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	char *ret, *tmp;
 	const char *from_codeset;
 	gsize newlen;
--- a/libpurple/protocols/yahoo/yahoo_aliases.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Mon Sep 26 14:57:21 2011 +0900
@@ -74,7 +74,7 @@
 yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message)
 {
 	PurpleConnection *gc = user_data;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
 
@@ -186,7 +186,7 @@
 void
 yahoo_fetch_aliases(PurpleConnection *gc)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	const char *url;
 	gchar *request, *webpage, *webaddress;
 	PurpleUtilFetchUrlData *url_data;
@@ -207,7 +207,7 @@
 				  webaddress);
 
 	/* We have a URL and some header information, let's connect and get some aliases  */
-	url_data = purple_util_fetch_url_request_len(purple_connection_get_account(gc),
+	url_data = purple_util_fetch_url_request(purple_connection_get_account(gc),
 				url, use_whole_url, NULL, TRUE, request, FALSE, -1,
 				yahoo_fetch_aliases_cb, gc);
 	if (url_data != NULL)
@@ -230,7 +230,7 @@
 	PurpleConnection *gc = cb->gc;
 	YahooData *yd;
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
 
 	if (len == 0 || error_message != NULL) {
@@ -310,7 +310,7 @@
 		return;
 	}
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	/* Using callback_data so I have access to gc in the callback function */
 	cb = g_new0(struct callback_data, 1);
@@ -379,7 +379,7 @@
 				  content);
 
 	/* We have a URL and some header information, let's connect and update the alias  */
-	url_data = purple_util_fetch_url_request_len(
+	url_data = purple_util_fetch_url_request(
 			purple_connection_get_account(gc), url, use_whole_url, NULL, TRUE,
 			request, FALSE, -1, yahoo_update_alias_cb, cb);
 	if (url_data != NULL)
@@ -517,7 +517,7 @@
 	}
 #endif
 
-	url_data = purple_util_fetch_url_request_len(account, webaddress, FALSE,
+	url_data = purple_util_fetch_url_request(account, webaddress, FALSE,
 			YAHOO_CLIENT_USERAGENT, TRUE, request, FALSE, -1,
 			yahoo_fetch_aliases_cb, gc);
 	if (url_data != NULL)
--- a/libpurple/protocols/yahoo/yahoo_doodle.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/yahoo_doodle.c	Mon Sep 26 14:57:21 2011 +0900
@@ -161,7 +161,7 @@
 		*/
 
 		wb = purple_whiteboard_create(account, from, DOODLE_STATE_REQUESTED);
-		ds = wb->proto_data;
+		ds = purple_whiteboard_get_protocol_data(wb);
 		ds->imv_key = g_strdup(imv_key);
 
 		yahoo_doodle_command_send_ready(gc, from, imv_key);
@@ -189,19 +189,19 @@
 	if(wb == NULL)
 		return;
 
-	if(wb->state == DOODLE_STATE_REQUESTING)
+	if(purple_whiteboard_get_state(wb) == DOODLE_STATE_REQUESTING)
 	{
-		doodle_session *ds = wb->proto_data;
+		doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
 		purple_whiteboard_start(wb);
 
-		wb->state = DOODLE_STATE_ESTABLISHED;
+		purple_whiteboard_set_state(wb, DOODLE_STATE_ESTABLISHED);
 
 		yahoo_doodle_command_send_confirm(gc, from, imv_key);
 		/* Let's steal the imv_key and reuse it */
 		g_free(ds->imv_key);
 		ds->imv_key = g_strdup(imv_key);
 	}
-	else if(wb->state == DOODLE_STATE_ESTABLISHED)
+	else if(purple_whiteboard_get_state(wb) == DOODLE_STATE_ESTABLISHED)
 	{
 		/* TODO Ask whether to save picture too */
 		purple_whiteboard_clear(wb);
@@ -211,7 +211,7 @@
 	 * already thinks we're in a session with them (when their chat message
 	 * contains the doodle imv key)
 	 */
-	else if(wb->state == DOODLE_STATE_REQUESTED)
+	else if(purple_whiteboard_get_state(wb) == DOODLE_STATE_REQUESTED)
 	{
 		/* purple_whiteboard_start(wb); */
 		yahoo_doodle_command_send_ready(gc, from, imv_key);
@@ -292,7 +292,7 @@
 	if(wb == NULL)
 		return;
 
-	if(wb->state == DOODLE_STATE_ESTABLISHED)
+	if(purple_whiteboard_get_state(wb) == DOODLE_STATE_ESTABLISHED)
 	{
 		/* TODO Ask user whether to save the image before clearing it */
 
@@ -333,9 +333,9 @@
 	/* TODO Combine the following IF's? */
 
 	/* Check if we requested a doodle session */
-	/*if(wb->state == DOODLE_STATE_REQUESTING)
+	/*if(purple_whiteboard_get_state(wb) == DOODLE_STATE_REQUESTING)
 	{
-		wb->state = DOODLE_STATE_ESTABLISHED;
+		purple_whiteboard_set_state(wb, DOODLE_STATE_ESTABLISHED);
 
 		purple_whiteboard_start(wb);
 
@@ -343,9 +343,9 @@
 	}*/
 
 	/* Check if we accepted a request for a doodle session */
-	if(wb->state == DOODLE_STATE_REQUESTED)
+	if(purple_whiteboard_get_state(wb) == DOODLE_STATE_REQUESTED)
 	{
-		wb->state = DOODLE_STATE_ESTABLISHED;
+		purple_whiteboard_set_state(wb, DOODLE_STATE_ESTABLISHED);
 
 		purple_whiteboard_start(wb);
 	}
@@ -372,7 +372,7 @@
 
 	/* TODO Ask if user wants to save picture before the session is closed */
 
-	wb->state = DOODLE_STATE_CANCELLED;
+	purple_whiteboard_set_state(wb, DOODLE_STATE_CANCELLED);
 	purple_whiteboard_destroy(wb);
 }
 
@@ -389,7 +389,7 @@
 
 	purple_debug_info("yahoo", "doodle: Sent %s (%s)\n", type, to);
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	/* Make and send an acknowledge (ready) Doodle packet */
 	pkt = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -450,21 +450,23 @@
 	ds->brush_size  = DOODLE_BRUSH_SMALL;
 	ds->brush_color = DOODLE_COLOR_RED;
 
-	wb->proto_data = ds;
+	purple_whiteboard_set_protocol_data(wb, ds);
 }
 
 void yahoo_doodle_end(PurpleWhiteboard *wb)
 {
-	PurpleConnection *gc = purple_account_get_connection(wb->account);
-	doodle_session *ds = wb->proto_data;
+	PurpleAccount *account = purple_whiteboard_get_account(wb);
+	PurpleConnection *gc = purple_account_get_connection(account);
+	doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
 
 	/* g_debug_debug("yahoo", "doodle: yahoo_doodle_end()\n"); */
 
-	if (gc && wb->state != DOODLE_STATE_CANCELLED)
-		yahoo_doodle_command_send_shutdown(gc, wb->who);
+	if (gc && (purple_whiteboard_get_state(wb) != DOODLE_STATE_CANCELLED))
+		yahoo_doodle_command_send_shutdown(gc, purple_whiteboard_get_who(wb));
 
 	g_free(ds->imv_key);
-	g_free(wb->proto_data);
+	g_free(ds);
+	purple_whiteboard_set_protocol_data(wb, NULL);
 }
 
 void yahoo_doodle_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height)
@@ -494,20 +496,25 @@
 
 void yahoo_doodle_send_draw_list(PurpleWhiteboard *wb, GList *draw_list)
 {
-	doodle_session *ds = wb->proto_data;
+	PurpleAccount *account = purple_whiteboard_get_account(wb);
+	PurpleConnection *gc = purple_account_get_connection(account);
+	doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
 	char *message;
 
 	g_return_if_fail(draw_list != NULL);
 
 	message = yahoo_doodle_build_draw_string(ds, draw_list);
-	yahoo_doodle_command_send_draw(wb->account->gc, wb->who, message, ds->imv_key);
+	yahoo_doodle_command_send_draw(gc, purple_whiteboard_get_who(wb), message, ds->imv_key);
 	g_free(message);
 }
 
 void yahoo_doodle_clear(PurpleWhiteboard *wb)
 {
-	doodle_session *ds = wb->proto_data;
-	yahoo_doodle_command_send_clear(wb->account->gc, wb->who, ds->imv_key);
+	PurpleAccount *account = purple_whiteboard_get_account(wb);
+	PurpleConnection *gc = purple_account_get_connection(account);
+	doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
+
+	yahoo_doodle_command_send_clear(gc, purple_whiteboard_get_who(wb), ds->imv_key);
 }
 
 
@@ -560,14 +567,14 @@
 
 void yahoo_doodle_get_brush(const PurpleWhiteboard *wb, int *size, int *color)
 {
-	doodle_session *ds = wb->proto_data;
+	doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
 	*size = ds->brush_size;
 	*color = ds->brush_color;
 }
 
 void yahoo_doodle_set_brush(PurpleWhiteboard *wb, int size, int color)
 {
-	doodle_session *ds = wb->proto_data;
+	doodle_session *ds = purple_whiteboard_get_protocol_data(wb);
 	ds->brush_size = size;
 	ds->brush_color = color;
 
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Mon Sep 26 14:57:21 2011 +0900
@@ -43,7 +43,7 @@
 	PurpleConnection *gc;
 	long expires;
 	gboolean started;
-	gchar *txbuf;
+	guchar *txbuf;
 	gsize txbuflen;
 	gsize txbuf_written;
 	guint tx_handler;
@@ -85,7 +85,7 @@
 	GSList *l;
 
 	gc = xd->gc;
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	/* remove entry from map */
 	if(xd->xfer_peer_idstring) {
@@ -120,13 +120,13 @@
 {
 	PurpleXfer *xfer;
 	struct yahoo_xfer_data *xd;
-	int remaining, written;
+	gssize remaining, written;
 
 	xfer = data;
 	xd = purple_xfer_get_protocol_data(xfer);
 
 	remaining = xd->txbuflen - xd->txbuf_written;
-	written = write(xfer->fd, xd->txbuf + xd->txbuf_written, remaining);
+	written = purple_xfer_write(xfer, xd->txbuf + xd->txbuf_written, remaining);
 
 	if (written < 0 && errno == EAGAIN)
 		written = 0;
@@ -164,7 +164,7 @@
 		return;
 	if ((source < 0) || (xd->path == NULL) || (xd->host == NULL)) {
 		purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer),
-				xfer->who, _("Unable to connect."));
+				purple_xfer_get_remote_user(xfer), _("Unable to connect."));
 		purple_xfer_cancel_remote(xfer);
 		return;
 	}
@@ -173,9 +173,10 @@
 
 	/* The first time we get here, assemble the tx buffer */
 	if (xd->txbuflen == 0) {
-		xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
+		gchar *header = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
 			      xd->path, xd->host);
-		xd->txbuflen = strlen(xd->txbuf);
+		xd->txbuf = (guchar*) header;
+		xd->txbuflen = strlen(header);
 		xd->txbuf_written = 0;
 	}
 
@@ -191,13 +192,13 @@
 {
 	PurpleXfer *xfer;
 	struct yahoo_xfer_data *xd;
-	int written, remaining;
+	gssize written, remaining;
 
 	xfer = data;
 	xd = purple_xfer_get_protocol_data(xfer);
 
 	remaining = xd->txbuflen - xd->txbuf_written;
-	written = write(xfer->fd, xd->txbuf + xd->txbuf_written, remaining);
+	written = purple_xfer_write(xfer, xd->txbuf + xd->txbuf_written, remaining);
 
 	if (written < 0 && errno == EAGAIN)
 		written = 0;
@@ -245,7 +246,7 @@
 
 	if (source < 0) {
 		purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer),
-				xfer->who, _("Unable to connect."));
+				purple_xfer_get_remote_user(xfer), _("Unable to connect."));
 		purple_xfer_cancel_remote(xfer);
 		return;
 	}
@@ -255,7 +256,7 @@
 	/* Assemble the tx buffer */
 	gc = xd->gc;
 	account = purple_connection_get_account(gc);
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER,
 		YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -269,7 +270,7 @@
 	encoded_filename = yahoo_string_encode(gc, filename, NULL); // this takes utf8 as input. --yaz
 
 	yahoo_packet_hash(pkt, "sssss", 0, purple_connection_get_display_name(gc),
-	  5, xfer->who, 14, "", 27, encoded_filename, 28, size);
+	  5, purple_xfer_get_remote_user(xfer), 14, "", 27, encoded_filename, 28, size);
 	g_free(size);
 	g_free(encoded_filename);
 	g_free(filename);
@@ -320,7 +321,7 @@
 
 	xfer_data = purple_xfer_get_protocol_data(xfer);
 	gc = xfer_data->gc;
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 
 	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
@@ -364,7 +365,7 @@
 
 	xfer_data = purple_xfer_get_protocol_data(xfer);
 	gc = xfer_data->gc;
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
 
 	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)	{
@@ -375,14 +376,14 @@
 							   yd->session_id);
 		yahoo_packet_hash(pkt, "sssiiiisiii",
 			1, purple_normalize(account, purple_account_get_username(account)),
-			5, xfer->who,
+			5, purple_xfer_get_remote_user(xfer),
 			265, xfer_data->xfer_peer_idstring,
 			222, 1,
 			266, 1,
 			302, 268,
 			300, 268,
 			27,  filename,
-			28,  xfer->size,
+			28,  purple_xfer_get_size(xfer),
 			301, 268,
 			303, 268);
 		g_free(filename);
@@ -393,7 +394,7 @@
 
 			yahoo_packet_hash(pkt, "sssi",
 				1, purple_normalize(account, purple_account_get_username(account)),
-				5, xfer->who,
+				5, purple_xfer_get_remote_user(xfer),
 				265, xfer_data->xfer_peer_idstring,
 				222, 3);
 		} else {
@@ -402,7 +403,7 @@
 
 			yahoo_packet_hash(pkt, "sssi",
 				1, purple_normalize(account, purple_account_get_username(account)),
-				5, xfer->who,
+				5, purple_xfer_get_remote_user(xfer),
 				265, xfer_data->xfer_peer_idstring,
 				271, 1);
 		}
@@ -531,7 +532,7 @@
 		struct yahoo_packet *pkt;
 
 		gc = xfer_data->gc;
-		yd = gc->proto_data;
+		yd = purple_connection_get_protocol_data(gc);
 		account = purple_connection_get_account(gc);
 		if(xfer_data->xfer_idstring_for_relay) /* hack to see if file trans acc/info packet has been received */
 		{
@@ -540,7 +541,7 @@
 								   yd->session_id);
 			yahoo_packet_hash(pkt, "sssi",
 				1, purple_normalize(account, purple_account_get_username(account)),
-				5, xfer->who,
+				5, purple_xfer_get_remote_user(xfer),
 				265, xfer_data->xfer_peer_idstring,
 				66, -1);
 		}
@@ -551,7 +552,7 @@
 								   yd->session_id);
 			yahoo_packet_hash(pkt, "sssi",
 				1, purple_normalize(account, purple_account_get_username(account)),
-				5, xfer->who,
+				5, purple_xfer_get_remote_user(xfer),
 				265, xfer_data->xfer_peer_idstring,
 				222, 2);
 		}
@@ -579,7 +580,7 @@
 		struct yahoo_packet *pkt;
 
 		gc = xfer_data->gc;
-		yd = gc->proto_data;
+		yd = purple_connection_get_protocol_data(gc);
 		account = purple_connection_get_account(gc);
 		if(!xfer_data->xfer_idstring_for_relay) /* hack to see if file trans acc/info packet has been received */
 		{
@@ -588,7 +589,7 @@
 								   yd->session_id);
 			yahoo_packet_hash(pkt, "sssi",
 				1, purple_normalize(account, purple_account_get_username(account)),
-				5, xfer->who,
+				5, purple_xfer_get_remote_user(xfer),
 				265, xfer_data->xfer_peer_idstring,
 				222, 4);
 		}
@@ -599,7 +600,7 @@
 								   yd->session_id);
 			yahoo_packet_hash(pkt, "sssi",
 				1, purple_normalize(account, purple_account_get_username(account)),
-				5, xfer->who,
+				5, purple_xfer_get_remote_user(xfer),
 				265, xfer_data->xfer_peer_idstring,
 				66, -1);
 		}
@@ -617,7 +618,7 @@
 	char *tx = NULL;
 	int written;
 
-	tx = g_strdup_printf("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nContent-Type: application/octet-stream\r\nConnection: close\r\n\r\n");
+	tx = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\nContent-Type: application/octet-stream\r\nConnection: close\r\n\r\n";
 	written = write(xfer->fd, tx, strlen(tx));
 
 	if (written < 0 && errno == EAGAIN)
@@ -628,7 +629,6 @@
 	/* close connection */
 	close(xfer->fd);
 	xfer->fd = -1;
-	g_free(tx);
 }
 
 static void yahoo_xfer_end(PurpleXfer *xfer_old)
@@ -667,7 +667,7 @@
 			filesize = atol( xfer_data->size_list->data );
 
 			gc = xfer_data->gc;
-			yd = gc->proto_data;
+			yd = purple_connection_get_protocol_data(gc);
 
 			/* setting up xfer_data for next file's tranfer */
 			g_free(xfer_data->host);
@@ -697,7 +697,7 @@
 			purple_xfer_set_protocol_data(xfer_old, NULL);
 
 			/* Build the file transfer handle. */
-			xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, xfer_old->who);
+			xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, purple_xfer_get_remote_user(xfer_old));
 
 
 			if (xfer) {
@@ -810,7 +810,7 @@
 	unsigned long filesize = 0L;
 	GSList *l;
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	for (l = pkt->hash; l; l = l->next) {
 		struct yahoo_pair *pair = l->data;
@@ -1001,7 +1001,7 @@
 		return;
 	gc = xd->gc;
 	account = purple_connection_get_account(gc);
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	if(!hosts)
 	{
@@ -1061,7 +1061,7 @@
 
 	yahoo_packet_hash(pkt, "ssssis",
 		1, purple_normalize(account, purple_account_get_username(account)),
-		5, xfer->who,
+		5, purple_xfer_get_remote_user(xfer),
 		265, xd->xfer_peer_idstring,
 		27,  filename,
 		249, 3,
@@ -1081,7 +1081,7 @@
 void yahoo_send_file(PurpleConnection *gc, const char *who, const char *file)
 {
 	struct yahoo_xfer_data *xfer_data;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	PurpleXfer *xfer = yahoo_new_xfer(gc, who);
 
 	g_return_if_fail(xfer != NULL);
@@ -1112,8 +1112,7 @@
 	PurpleXfer *xfer;
 	struct yahoo_xfer_data *xd;
 	int did;
-	gchar* buf;
-	gchar* t;
+	guchar buf[1000];
 	PurpleAccount *account;
 	PurpleConnection *gc;
 
@@ -1122,16 +1121,13 @@
 	account = purple_connection_get_account(xd->gc);
 	gc = xd->gc;
 
-	buf=g_strnfill(1000, 0);
-	while((did = read(source, buf, 998)) > 0)
+	while((did = read(source, buf, sizeof(buf))) > 0)
 	{
+		/* TODO: Convert to circ buffer, this all is pretty horrible */
+		xd->txbuf = g_realloc(xd->txbuf, xd->txbuflen + did);
+		g_memmove(xd->txbuf + xd->txbuflen, buf, did);
 		xd->txbuflen += did;
-		buf[did] = '\0';
-		t = xd->txbuf;
-		xd->txbuf = g_strconcat(t,buf,NULL);
-		g_free(t);
 	}
-	g_free(buf);
 
 	if (did < 0 && errno == EAGAIN)
 		return;
@@ -1225,7 +1221,7 @@
 		close(source);
 		xfer->fd = -1;
 		/* start local server, listen for connections */
-		purple_network_listen(xd->yahoo_local_p2p_ft_server_port, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer);
+		purple_network_listen(xd->yahoo_local_p2p_ft_server_port, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_ft_server_listen_cb, xfer);
 	}
 	else
 	{
@@ -1249,7 +1245,7 @@
 	account = purple_connection_get_account(gc);
 	if ((source < 0) || (xd->path == NULL) || (xd->host == NULL)) {
 		purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer),
-			xfer->who, _("Unable to connect."));
+			purple_xfer_get_remote_user(xfer), _("Unable to connect."));
 		purple_xfer_cancel_remote(xfer);
 		return;
 	}
@@ -1257,7 +1253,8 @@
 	if (xd->txbuflen == 0)
 	{
 		gchar* cookies;
-		YahooData *yd = gc->proto_data;
+		gchar* initial_buffer;
+		YahooData *yd = purple_connection_get_protocol_data(gc);
 
 		/* cookies = yahoo_get_cookies(gc);
 		 * This doesn't seem to be working. The function is returning NULL, which yahoo servers don't like
@@ -1270,30 +1267,30 @@
 			if(xd->info_val_249 == 2)
 				{
 				/* sending file via p2p, we are connected as client */
-				xd->txbuf = g_strdup_printf("POST /%s HTTP/1.1\r\n"
+				initial_buffer = g_strdup_printf("POST /%s HTTP/1.1\r\n"
 						"User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
 						"Host: %s\r\n"
-						"Content-Length: %ld\r\n"
+						"Content-Length: %" G_GSIZE_FORMAT "\r\n"
 						"Cache-Control: no-cache\r\n\r\n",
 										xd->path,
 										xd->host,
-										(long int)xfer->size);	/* to do, add Referer */
+										purple_xfer_get_size(xfer));	/* to do, add Referer */
 				}
 			else
 				{
 				/* sending file via relaying */
-				xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
+				initial_buffer = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
 						"Cookie:%s\r\n"
 						"User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
 						"Host: %s\r\n"
-						"Content-Length: %ld\r\n"
+						"Content-Length: %" G_GSIZE_FORMAT "\r\n"
 						"Cache-Control: no-cache\r\n\r\n",
 										purple_url_encode(xd->xfer_idstring_for_relay),
 										purple_normalize(account, purple_account_get_username(account)),
-										xfer->who,
+										purple_xfer_get_remote_user(xfer),
 										cookies,
 										xd->host,
-										(long int)xfer->size);
+										purple_xfer_get_size(xfer));	/* to do, add Referer */
 				}
 		}
 		else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED)
@@ -1301,7 +1298,7 @@
 			if(xd->info_val_249 == 1)
 				{
 				/* receiving file via p2p, connected as client */
-				xd->txbuf = g_strdup_printf("HEAD /%s HTTP/1.1\r\n"
+				initial_buffer = g_strdup_printf("HEAD /%s HTTP/1.1\r\n"
 						"Accept: */*\r\n"
 						"User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
 						"Host: %s\r\n"
@@ -1312,7 +1309,7 @@
 			else
 				{
 				/* receiving file via relaying */
-				xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
+				initial_buffer = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
 						"Accept: */*\r\n"
 						"Cookie: %s\r\n"
 						"User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
@@ -1321,7 +1318,7 @@
 						"Cache-Control: no-cache\r\n\r\n",
 										purple_url_encode(xd->xfer_idstring_for_relay),
 										purple_normalize(account, purple_account_get_username(account)),
-										xfer->who,
+										purple_xfer_get_remote_user(xfer),
 										cookies,
 										xd->host);
 			}
@@ -1331,7 +1328,7 @@
 			if(xd->info_val_249 == 1)
 				{
 				/* receiving file via p2p, connected as client */
-				xd->txbuf = g_strdup_printf("GET /%s HTTP/1.1\r\n"
+				initial_buffer = g_strdup_printf("GET /%s HTTP/1.1\r\n"
 						"User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
 						"Host: %s\r\n"
 						"Connection: Keep-Alive\r\n\r\n",
@@ -1340,14 +1337,14 @@
 			else
 				{
 				/* receiving file via relaying */
-				xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
+				initial_buffer = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\n"
 						"Cookie: %s\r\n"
 						"User-Agent: " YAHOO_CLIENT_USERAGENT "\r\n"
 						"Host: %s\r\n"
 						"Connection: Keep-Alive\r\n\r\n",
 										purple_url_encode(xd->xfer_idstring_for_relay),
 										purple_normalize(account, purple_account_get_username(account)),
-										xfer->who,
+										purple_xfer_get_remote_user(xfer),
 										cookies,
 										xd->host);
 			}
@@ -1358,7 +1355,8 @@
 			g_free(cookies);
 			return;
 		}
-		xd->txbuflen = strlen(xd->txbuf);
+		xd->txbuf = (guchar*) initial_buffer;
+		xd->txbuflen = strlen(initial_buffer);
 		xd->txbuf_written = 0;
 		g_free(cookies);
 	}
@@ -1435,14 +1433,15 @@
 	time_str[strlen(time_str) - 1] = '\0';
 
 	if (xd->txbuflen == 0)	{
-		xd->txbuf = g_strdup_printf("HTTP/1.0 200 OK\r\n"
+		gchar *initial_buffer = g_strdup_printf("HTTP/1.0 200 OK\r\n"
 		                            "Date: %s GMT\r\n"
 		                            "Server: Y!/1.0\r\n"
 		                            "MIME-version: 1.0\r\n"
 		                            "Last-modified: %s GMT\r\n"
 		                            "Content-length: %" G_GSIZE_FORMAT "\r\n\r\n",
-		                            time_str, time_str, xfer->size);
-		xd->txbuflen = strlen(xd->txbuf);
+		                            time_str, time_str, purple_xfer_get_size(xfer));
+		xd->txbuf = (guchar *)initial_buffer;
+		xd->txbuflen = strlen(initial_buffer);
 		xd->txbuf_written = 0;
 	}
 
@@ -1485,7 +1484,7 @@
 
 	/* Add an Input Read event to the file descriptor */
 	xfer->fd = acceptfd;
-	if(xfer->type == PURPLE_XFER_RECEIVE)
+	if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE)
 		xd->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_ft_POST_cb, data);
 	else
 		xd->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_ft_HEAD_GET_cb, data);
@@ -1510,8 +1509,8 @@
 		return;
 	}
 
-	if( (xfer->type == PURPLE_XFER_RECEIVE) || (xd->status_15 != P2P_HEAD_REPLIED) )	{
-		yd = xd->gc->proto_data;
+	if( (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) || (xd->status_15 != P2P_HEAD_REPLIED) )	{
+		yd = purple_connection_get_protocol_data(xd->gc);
 		account = purple_connection_get_account(xd->gc);
 		local_ip = purple_network_get_my_ip(listenfd);
 		xd->yahoo_local_p2p_ft_server_port = purple_network_get_port_from_fd(listenfd);
@@ -1519,18 +1518,18 @@
 		filename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
 		filename_without_spaces = g_strdup(filename);
 		purple_util_chrreplace(filename_without_spaces, ' ', '+');
-		xd->xfer_url = g_strdup_printf("/Messenger.%s.%d000%s?AppID=Messenger&UserID=%s&K=lc9lu2u89gz1llmplwksajkjx", xfer->who, (int)time(NULL), filename_without_spaces, xfer->who);
+		xd->xfer_url = g_strdup_printf("/Messenger.%s.%d000%s?AppID=Messenger&UserID=%s&K=lc9lu2u89gz1llmplwksajkjx", purple_xfer_get_remote_user(xfer), (int)time(NULL), filename_without_spaces, purple_xfer_get_remote_user(xfer));
 		url_to_send = g_strdup_printf("http://%s:%d%s", local_ip, xd->yahoo_local_p2p_ft_server_port, xd->xfer_url);
 
-		if(xfer->type == PURPLE_XFER_RECEIVE)	{
+		if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
 			xd->info_val_249 = 2;	/* 249=2: we are p2p server, and receiving file */
 			pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15,
 				YAHOO_STATUS_AVAILABLE, yd->session_id);
 			yahoo_packet_hash(pkt, "ssssis",
 				1, purple_normalize(account, purple_account_get_username(account)),
-				5, xfer->who,
+				5, purple_xfer_get_remote_user(xfer),
 				265, xd->xfer_peer_idstring,
-				27, xfer->filename,
+				27, purple_xfer_get_filename(xfer),
 				249, 2,
 				250, url_to_send);
 		}
@@ -1539,7 +1538,7 @@
 			pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
 			yahoo_packet_hash(pkt, "ssssis",
 				1, purple_normalize(account, purple_account_get_username(account)),
-				5, xfer->who,
+				5, purple_xfer_get_remote_user(xfer),
 				265, xd->xfer_peer_idstring,
 				27,  filename,
 				249, 1,
@@ -1572,11 +1571,11 @@
 		return;
 
 	account = purple_connection_get_account(gc);
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
-	p2p_data = g_hash_table_lookup(yd->peers, xfer->who);
+	p2p_data = g_hash_table_lookup(yd->peers, purple_xfer_get_remote_user(xfer));
 	if( p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER )
-		if(purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer))
+		if(purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_ft_server_listen_cb, xfer))
 			return;
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -1584,7 +1583,7 @@
 
 	yahoo_packet_hash(pkt, "ssssi",
 		1, purple_normalize(account, purple_account_get_username(account)),
-		5, xfer->who,
+		5, purple_xfer_get_remote_user(xfer),
 		265, xd->xfer_peer_idstring,
 		27,  filename,
 		249, 2);	/* 249=2: we are p2p client */
@@ -1613,7 +1612,7 @@
 	GSList *size_list = NULL;
 	int nooffiles = 0;
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	for (l = pkt->hash; l; l = l->next) {
 		struct yahoo_pair *pair = l->data;
@@ -1687,13 +1686,13 @@
 		account = purple_connection_get_account(gc);
 		if (yd->jp)
 		{
-			purple_dnsquery_a_account(account, YAHOOJP_XFER_RELAY_HOST,
+			purple_dnsquery_a(account, YAHOOJP_XFER_RELAY_HOST,
 							YAHOOJP_XFER_RELAY_PORT,
 							yahoo_xfer_dns_connected_15, xfer);
 		}
 		else
 		{
-			purple_dnsquery_a_account(account, YAHOO_XFER_RELAY_HOST,
+			purple_dnsquery_a(account, YAHOO_XFER_RELAY_HOST,
 							YAHOO_XFER_RELAY_PORT,
 							yahoo_xfer_dns_connected_15, xfer);
 		}
@@ -1743,8 +1742,6 @@
 		g_return_if_reached();
 	}
 
-	xfer->message = NULL;
-
 	/* Set the info about the incoming file. */
 	utf8_filename = yahoo_string_decode(gc, filename, TRUE);
 	purple_xfer_set_filename(xfer, utf8_filename);
@@ -1769,7 +1766,7 @@
 
 	if(nooffiles > 1) {
 		gchar* message;
-		message = g_strdup_printf(_("%s is trying to send you a group of %d files.\n"), xfer->who, nooffiles);
+		message = g_strdup_printf(_("%s is trying to send you a group of %d files.\n"), purple_xfer_get_remote_user(xfer), nooffiles);
 		purple_xfer_conversation_write(xfer, message, FALSE);
 		g_free(message);
 	}
@@ -1794,7 +1791,7 @@
 	struct yahoo_packet *pkt_to_send;
 	struct yahoo_p2p_data *p2p_data;
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	for (l = pkt->hash; l; l = l->next) {
 		struct yahoo_pair *pair = l->data;
@@ -1858,9 +1855,9 @@
 			YAHOO_STATUS_AVAILABLE, yd->session_id);
 		yahoo_packet_hash(pkt_to_send, "ssssis",
 			1, purple_normalize(account, purple_account_get_username(account)),
-			5, xfer->who,
+			5, purple_xfer_get_remote_user(xfer),
 			265, xfer_data->xfer_peer_idstring,
-			27, xfer->filename,
+			27, purple_xfer_get_filename(xfer),
 			249, xfer_data->info_val_249,
 			251, xfer_data->xfer_idstring_for_relay);
 
@@ -1874,12 +1871,12 @@
 		}
 	}
 	else if(val_249 == 2)	{
-		p2p_data = g_hash_table_lookup(yd->peers, xfer->who);
+		p2p_data = g_hash_table_lookup(yd->peers, purple_xfer_get_remote_user(xfer));
 		if( !( p2p_data && (p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) ) )	{
 			purple_xfer_cancel_remote(xfer);
 			return;
 		}
-		if(!purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer)) {
+		if(!purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, TRUE, yahoo_p2p_ft_server_listen_cb, xfer)) {
 			purple_xfer_cancel_remote(xfer);
 			return;
 		}
@@ -1900,7 +1897,7 @@
 	gchar *url = NULL;
 	int val_249 = 0;
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	for (l = pkt->hash; l; l = l->next) {
 		struct yahoo_pair *pair = l->data;
 
--- a/libpurple/protocols/yahoo/yahoo_friend.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/yahoo_friend.c	Mon Sep 26 14:57:21 2011 +0900
@@ -46,9 +46,10 @@
 	const char *norm;
 
 	g_return_val_if_fail(gc != NULL, NULL);
-	g_return_val_if_fail(gc->proto_data != NULL, NULL);
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
+	g_return_val_if_fail(yd != NULL, NULL);
+
 	norm = purple_normalize(purple_connection_get_account(gc), name);
 
 	return g_hash_table_lookup(yd->friends, norm);
@@ -61,9 +62,10 @@
 	const char *norm;
 
 	g_return_val_if_fail(gc != NULL, NULL);
-	g_return_val_if_fail(gc->proto_data != NULL, NULL);
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
+	g_return_val_if_fail(yd != NULL, NULL);
+
 	norm = purple_normalize(purple_connection_get_account(gc), name);
 
 	f = g_hash_table_lookup(yd->friends, norm);
@@ -225,7 +227,7 @@
 void yahoo_friend_update_presence(PurpleConnection *gc, const char *name,
 		YahooPresenceVisibility presence)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt = NULL;
 	YahooFriend *f;
 	const char *thirtyone, *thirteen;
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Mon Sep 26 14:57:21 2011 +0900
@@ -52,7 +52,7 @@
 	YahooData *yd;
 
 	d = user_data;
-	yd = d->gc->proto_data;
+	yd = purple_connection_get_protocol_data(d->gc);
 	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
 
 	if (error_message != NULL) {
@@ -123,26 +123,19 @@
 		/* TODO: make this work p2p, try p2p before the url */
 		PurpleUtilFetchUrlData *url_data;
 		struct yahoo_fetch_picture_data *data;
-		PurpleBuddy *b = purple_find_buddy(gc->account, who);
-		const char *locksum = NULL;
 		/* use whole URL if using HTTP Proxy */
 		gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
 
-		/* FIXME: Cleanup this strtol() stuff if possible. */
-		if (b && (locksum = purple_buddy_icons_get_checksum_for_user(b)) != NULL &&
-				(checksum == strtol(locksum, NULL, 10)))
-			return;
-
 		data = g_new0(struct yahoo_fetch_picture_data, 1);
 		data->gc = gc;
 		data->who = g_strdup(who);
 		data->checksum = checksum;
 		/* TODO: Does this need to be MSIE 5.0? */
 		url_data = purple_util_fetch_url(url, use_whole_url,
-				"Mozilla/4.0 (compatible; MSIE 5.5)", FALSE,
+				"Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, -1,
 				yahoo_fetch_picture_cb, data);
 		if (url_data != NULL) {
-			yd = gc->proto_data;
+			yd = purple_connection_get_protocol_data(gc);
 			yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
 		} else {
 			g_free(data->who);
@@ -192,7 +185,7 @@
 void yahoo_process_picture_upload(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	GSList *l = pkt->hash;
 	char *url = NULL;
 
@@ -268,7 +261,7 @@
 
 void yahoo_send_picture_info(PurpleConnection *gc, const char *who)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 
 	if (!yd->picture_url) {
@@ -285,7 +278,7 @@
 
 void yahoo_send_picture_request(PurpleConnection *gc, const char *who)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -297,7 +290,7 @@
 
 void yahoo_send_picture_checksum(PurpleConnection *gc)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_CHECKSUM, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -308,7 +301,7 @@
 
 void yahoo_send_picture_update_to_user(PurpleConnection *gc, const char *who, int type)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 
 	pkt = yahoo_packet_new(YAHOO_SERVICE_AVATAR_UPDATE, YAHOO_STATUS_AVAILABLE, yd->session_id);
@@ -333,7 +326,7 @@
 
 void yahoo_send_picture_update(PurpleConnection *gc, int type)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yspufe data;
 
 	data.gc = gc;
@@ -432,7 +425,7 @@
 	gboolean use_whole_url = yahoo_account_use_http_proxy(gc);
 
 	account = purple_connection_get_account(gc);
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	/* Buddy icon connect is now complete; clear the PurpleProxyConnectData */
 	yd->buddy_icon_connect_data = NULL;
@@ -497,7 +490,7 @@
 void yahoo_buddy_icon_upload(PurpleConnection *gc, struct yahoo_buddy_icon_upload_data *d)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	if (yd->buddy_icon_connect_data != NULL) {
 		/* Cancel any in-progress buddy icon upload */
@@ -542,7 +535,7 @@
 
 void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = gc->account;
 
 	if (img == NULL) {
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Mon Sep 26 14:57:21 2011 +0900
@@ -793,7 +793,7 @@
 
 	purple_debug_info("yahoo", "In yahoo_got_info\n");
 
-	yd = info_data->gc->proto_data;
+	yd = purple_connection_get_protocol_data(info_data->gc);
 	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
 
 	user_info = purple_notify_user_info_new();
@@ -944,7 +944,7 @@
 		 * we specify HTTP 1.1. So we have to specify 1.0 & fix purple_util_fetch_url
 		 */
 		url_data = purple_util_fetch_url(photo_url_text, use_whole_url, NULL,
-				FALSE, yahoo_got_photo, info2_data);
+				FALSE, -1, yahoo_got_photo, info2_data);
 		if (url_data != NULL)
 			yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
 	} else {
@@ -987,7 +987,7 @@
 	/* in to purple_markup_strip_html*/
 	char *fudged_buffer;
 
-	yd = info_data->gc->proto_data;
+	yd = purple_connection_get_protocol_data(info_data->gc);
 	yd->url_datas = g_slist_remove(yd->url_datas, url_data);
 
 	fudged_buffer = purple_strcasereplace(url_buffer, "</dd>", "</dd><br>");
@@ -1271,7 +1271,7 @@
 
 void yahoo_get_info(PurpleConnection *gc, const char *name)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	YahooGetInfoData *data;
 	char *url;
 	PurpleUtilFetchUrlData *url_data;
@@ -1283,7 +1283,7 @@
 	url = g_strdup_printf("%s%s",
 			(yd->jp ? YAHOOJP_PROFILE_URL : YAHOO_PROFILE_URL), name);
 
-	url_data = purple_util_fetch_url(url, TRUE, NULL, FALSE, yahoo_got_info, data);
+	url_data = purple_util_fetch_url(url, TRUE, NULL, FALSE, -1, yahoo_got_info, data);
 	if (url_data != NULL)
 		yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
 	else {
--- a/libpurple/protocols/yahoo/yahoochat.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/yahoochat.c	Mon Sep 26 14:57:21 2011 +0900
@@ -53,7 +53,7 @@
 /* special function to log us on to the yahoo chat service */
 static void yahoo_chat_online(PurpleConnection *gc)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	const char *rll;
 
@@ -101,7 +101,7 @@
 	YahooData *yd;
 	GSList *l;
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	for (l = yd->confs; l; l = l->next) {
 		PurpleConversation *c = l->data;
@@ -372,7 +372,7 @@
 
 static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *room, const char *topic, const char *id)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	char *room2;
 	gboolean utf8 = TRUE;
@@ -400,7 +400,7 @@
 /* this is a confirmation of yahoo_chat_online(); */
 void yahoo_process_chat_online(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
-	YahooData *yd = (YahooData *) gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	if (pkt->status == 1) {
 		yd->chat_online = TRUE;
@@ -432,7 +432,7 @@
 /* this is basicly the opposite of chat_online */
 void yahoo_process_chat_logout(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
-	YahooData *yd = (YahooData *) gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	GSList *l;
 
 	for (l = pkt->hash; l; l = l->next) {
@@ -462,7 +462,7 @@
 void yahoo_process_chat_join(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
-	YahooData *yd = (YahooData *) gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	PurpleConversation *c = NULL;
 	GSList *l;
 	GList *members = NULL;
@@ -786,7 +786,7 @@
 static int yahoo_conf_send(PurpleConnection *gc, const char *dn, const char *room,
 							GList *members, const char *what)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	GList *who;
 	char *msg, *msg2;
@@ -843,7 +843,7 @@
 static void yahoo_conf_invite(PurpleConnection *gc, PurpleConversation *c,
 		const char *dn, const char *buddy, const char *room, const char *msg)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	GList *members;
 	char *msg2 = NULL;
@@ -873,7 +873,7 @@
 
 static void yahoo_chat_leave(PurpleConnection *gc, const char *room, const char *dn, gboolean logout)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 
 	char *eroom;
@@ -924,7 +924,7 @@
 
 static int yahoo_chat_send(PurpleConnection *gc, const char *dn, const char *room, const char *what, PurpleMessageFlags flags)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	int me = 0;
 	char *msg1, *msg2, *room2;
@@ -969,7 +969,7 @@
 static void yahoo_chat_invite(PurpleConnection *gc, const char *dn, const char *buddy,
 							const char *room, const char *msg)
 {
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	struct yahoo_packet *pkt;
 	char *room2, *msg2 = NULL;
 	gboolean utf8 = TRUE;
@@ -997,7 +997,7 @@
 	YahooData *yd;
 	struct yahoo_packet *pkt;
 
-	yd = gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 
 	if (yd->wm) {
 		g_return_if_fail(yd->ycht != NULL);
@@ -1029,7 +1029,7 @@
 
 void yahoo_c_leave(PurpleConnection *gc, int id)
 {
-	YahooData *yd = (YahooData *) gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	PurpleConversation *c;
 
 	if (!yd)
@@ -1056,7 +1056,7 @@
 	int ret;
 	YahooData *yd;
 
-	yd = (YahooData *) gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	if (!yd)
 		return -1;
 
@@ -1114,7 +1114,7 @@
 	char *room, *topic, *type;
 	PurpleConversation *c;
 
-	yd = (YahooData *) gc->proto_data;
+	yd = purple_connection_get_protocol_data(gc);
 	if (!yd)
 		return;
 
@@ -1478,7 +1478,7 @@
 	PurpleRoomlist *list = yrl->list;
 	PurpleAccount *account = purple_roomlist_get_account(list);
 	PurpleConnection *pc = purple_account_get_connection(account);
-	YahooData *yd = pc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(pc);
 
 	if (source < 0) {
 		purple_notify_error(pc, NULL, _("Unable to connect"), _("Fetching the room list failed."));
--- a/libpurple/protocols/yahoo/ycht.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/yahoo/ycht.c	Mon Sep 26 14:57:21 2011 +0900
@@ -53,7 +53,7 @@
 static void ycht_process_login(YchtConn *ycht, YchtPkt *pkt)
 {
 	PurpleConnection *gc = ycht->gc;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	if (ycht->logged_in)
 		return;
@@ -68,7 +68,7 @@
 static void ycht_process_logout(YchtConn *ycht, YchtPkt *pkt)
 {
 	PurpleConnection *gc = ycht->gc;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	yd->chat_online = FALSE;
 	ycht->logged_in = FALSE;
@@ -173,7 +173,7 @@
 {
 #if 0
 	PurpleConnection *gc = ycht->gc;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 
 	if (ycht->logged_in)
 		return;
@@ -429,7 +429,7 @@
 
 void ycht_connection_close(YchtConn *ycht)
 {
-	YahooData *yd = ycht->gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(ycht->gc);
 
 	if (yd) {
 		yd->ycht = NULL;
@@ -541,7 +541,7 @@
 {
 	YchtConn *ycht = data;
 	PurpleConnection *gc = ycht->gc;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	YchtPkt *pkt;
 	char *buf;
 
@@ -568,7 +568,7 @@
 void ycht_connection_open(PurpleConnection *gc)
 {
 	YchtConn *ycht;
-	YahooData *yd = gc->proto_data;
+	YahooData *yd = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 
 	ycht = g_new0(YchtConn, 1);
--- a/libpurple/protocols/zephyr/zephyr.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/protocols/zephyr/zephyr.c	Mon Sep 26 14:57:21 2011 +0900
@@ -347,7 +347,7 @@
 {
 	gchar *utf8;
 	GError *err = NULL;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	if (g_utf8_validate(string, -1, NULL)) {
 		return g_strdup(string);
 	} else {
@@ -762,7 +762,7 @@
 
 static void handle_message(PurpleConnection *gc,ZNotice_t notice)
 {
-	zephyr_account* zephyr = gc->proto_data;
+	zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
 
 	if (!g_ascii_strcasecmp(notice.z_class, LOGIN_CLASS)) {
 		/* well, we'll be updating in 20 seconds anyway, might as well ignore this. */
@@ -872,7 +872,7 @@
 		} else {
 			zephyr_triple *zt1, *zt2;
 			gchar *send_inst_utf8;
-			zephyr_account *zephyr = gc->proto_data;
+			zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 			zt1 = new_triple(zephyr,notice.z_class, notice.z_class_inst, notice.z_recipient);
 			zt2 = find_sub_by_triple(zephyr,zt1);
 			if (!zt2) {
@@ -1109,7 +1109,7 @@
 static gint check_notify_tzc(gpointer data)
 {
 	PurpleConnection *gc = (PurpleConnection *)data;
-	zephyr_account* zephyr = gc->proto_data;
+	zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
 	parse_tree *newparsetree = read_from_tzc(zephyr);
 	if (newparsetree != NULL) {
 		gchar *spewtype;
@@ -1265,7 +1265,7 @@
 	GSList *buddies;
 	ZLocations_t locations;
 	PurpleConnection *gc = data;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 	int numlocs;
 	int one = 1;
@@ -1296,7 +1296,7 @@
 	GSList *buddies;
 	ZAsyncLocateData_t ald;
 	PurpleConnection *gc = (PurpleConnection *)data;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 
 	if (use_zeph02(zephyr)) {
@@ -1572,7 +1572,8 @@
 	username = purple_account_get_username(account);
 #endif
 	gc->flags |= PURPLE_CONNECTION_AUTO_RESP | PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC;
-	gc->proto_data = zephyr=g_new0(zephyr_account,1);
+	zephyr = g_new0(zephyr_account, 1);
+	purple_connection_set_protocol_data(gc, zephyr);
 
 	zephyr->account = account;
 
@@ -1961,7 +1962,7 @@
 {
 	GList *l;
 	GSList *s;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	pid_t tzc_pid = zephyr->tzc_pid;
 
 	l = zephyr->pending_zloc_names;
@@ -2036,7 +2037,7 @@
 	PurpleConvChat *gcc;
 	char *inst;
 	char *recipient;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 
 	zt = find_sub_by_id(zephyr,id);
 	if (!zt)
@@ -2065,7 +2066,7 @@
 static int zephyr_send_im(PurpleConnection * gc, const char *who, const char *im, PurpleMessageFlags flags)
 {
 	const char *sig;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	if (flags & PURPLE_MESSAGE_AUTO_RESP)
 		sig = "Automated reply:";
 	else {
@@ -2234,7 +2235,7 @@
 	if (gc == NULL)
 		return NULL;
 
-	tmp = local_zephyr_normalize(gc->proto_data, who);
+	tmp = local_zephyr_normalize(purple_connection_get_protocol_data(gc), who);
 
 	if (strlen(tmp) >= sizeof(buf)) {
 		g_free(tmp);
@@ -2250,7 +2251,7 @@
 static void zephyr_zloc(PurpleConnection *gc, const char *who)
 {
 	ZAsyncLocateData_t ald;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	gchar* normalized_who = local_zephyr_normalize(zephyr,who);
 
 	if (use_zeph02(zephyr)) {
@@ -2277,7 +2278,8 @@
 static void zephyr_set_status(PurpleAccount *account, PurpleStatus *status) {
 	size_t len;
 	size_t result;
-	zephyr_account *zephyr = purple_account_get_connection(account)->proto_data;
+	PurpleConnection *gc = purple_account_get_connection(account);
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status));
 
 	if (zephyr->away) {
@@ -2411,7 +2413,7 @@
 	const char *classname;
 	const char *instname;
 	const char *recip;
-	zephyr_account *zephyr=gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	classname = g_hash_table_lookup(data, "class");
 	instname = g_hash_table_lookup(data, "instance");
 	recip = g_hash_table_lookup(data, "recipient");
@@ -2474,7 +2476,7 @@
 static void zephyr_chat_leave(PurpleConnection * gc, int id)
 {
 	zephyr_triple *zt;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	zt = find_sub_by_id(zephyr,id);
 
 	if (zt) {
@@ -2524,7 +2526,7 @@
 
 static unsigned int zephyr_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state) {
 	gchar *recipient;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	if (use_tzc(zephyr))
 		return 0;
 
@@ -2566,7 +2568,7 @@
 	PurpleConversation *gconv;
 	PurpleConvChat *gcc;
 	gchar *topic_utf8;
-	zephyr_account* zephyr = gc->proto_data;
+	zephyr_account* zephyr = purple_connection_get_protocol_data(gc);
 	char *sender = (char *)zephyr->username;
 
 	zt = find_sub_by_id(zephyr,id);
@@ -2589,7 +2591,8 @@
 				      const char *cmd, char **args, char **error, void *data)
 {
 	char *recipient;
-	zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);;
 	if (!g_ascii_strcasecmp(args[0],"*"))
 		return PURPLE_CMD_RET_FAILED;  /* "*" is not a valid argument */
 	else
@@ -2641,7 +2644,8 @@
 				     const char *cmd, char **args, char **error, void *data)
 {
 	/* args = instance, message */
-	zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	if ( zephyr_send_message(zephyr,"message",args[0],"",args[1],zephyr_get_signature(),""))
 		return PURPLE_CMD_RET_OK;
 	else
@@ -2652,7 +2656,8 @@
 				      const char *cmd, char **args, char **error, void *data)
 {
 	/* args = class, instance, message */
-	zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	if ( zephyr_send_message(zephyr,args[0],args[1],"",args[2],zephyr_get_signature(),""))
 		return PURPLE_CMD_RET_OK;
 	else
@@ -2663,7 +2668,8 @@
 				       const char *cmd, char **args, char **error, void *data)
 {
 	/* args = class, instance, recipient, message */
-	zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	if ( zephyr_send_message(zephyr,args[0],args[1],args[2],args[3],zephyr_get_signature(),""))
 		return PURPLE_CMD_RET_OK;
 	else
@@ -2674,7 +2680,8 @@
 				      const char *cmd, char **args, char **error, void *data)
 {
 	/* args = instance, recipient, message */
-	zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	if ( zephyr_send_message(zephyr,"message",args[0],args[1],args[2],zephyr_get_signature(),""))
 		return PURPLE_CMD_RET_OK;
 	else
@@ -2685,7 +2692,8 @@
 				     const char *cmd, char **args, char **error, void *data)
 {
 	/* args = class, message */
-	zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data;
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	if ( zephyr_send_message(zephyr,args[0],"PERSONAL","",args[1],zephyr_get_signature(),""))
 		return PURPLE_CMD_RET_OK;
 	else
@@ -2766,7 +2774,7 @@
 {
 	/* Resubscribe to the in-memory list of subscriptions and also
 	   unsubscriptions*/
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	GSList *s = zephyr->subscrips;
 	zephyr_triple *zt;
 	while (s) {
@@ -2791,7 +2799,7 @@
 static void zephyr_action_get_subs_from_server(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *) action->context;
-	zephyr_account *zephyr = gc->proto_data;
+	zephyr_account *zephyr = purple_connection_get_protocol_data(gc);
 	gchar *title;
 	int retval, nsubs, one,i;
 	ZSubscription_t subs;
@@ -2845,6 +2853,7 @@
 static PurplePlugin *my_protocol = NULL;
 
 static PurplePluginProtocolInfo prpl_info = {
+	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD,
 	NULL,					/* ??? user_splits */
 	NULL,					/* ??? protocol_options */
@@ -2885,7 +2894,6 @@
 	NULL,					/* keepalive -- Not necessary*/
 	NULL,					/* register_user -- Not supported*/
 	NULL,					/* XXX get_cb_info */
-	NULL,					/* get_cb_away */
 	NULL,					/* alias_buddy */
 	NULL,					/* group_buddy */
 	NULL,					/* rename_group */
@@ -2911,15 +2919,12 @@
 	NULL,
 	NULL,
 	NULL,
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,					/* get_account_text_table */
 	NULL,					/* initate_media */
 	NULL,					/* get_media_caps */
 	NULL,					/* get_moods */
 	NULL,					/* set_public_alias */
-	NULL,					/* get_public_alias */
-	NULL,					/* add_buddy_with_invite */
-	NULL					/* add_buddies_with_invite */
+	NULL					/* get_public_alias */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/proxy.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/proxy.c	Mon Sep 26 14:57:21 2011 +0900
@@ -41,6 +41,16 @@
 #include "proxy.h"
 #include "util.h"
 
+struct _PurpleProxyInfo
+{
+	PurpleProxyType type;   /**< The proxy type.  */
+
+	char *host;           /**< The host.        */
+	int   port;           /**< The port number. */
+	char *username;       /**< The username.    */
+	char *password;       /**< The password.    */
+};
+
 struct _PurpleProxyConnectData {
 	void *handle;
 	PurpleProxyConnectFunction connect_cb;
@@ -1369,7 +1379,7 @@
 
 		proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
 	} else {
-		connect_data->query_data = purple_dnsquery_a_account(
+		connect_data->query_data = purple_dnsquery_a(
 				connect_data->account, connect_data->host,
 				connect_data->port, s4_host_resolved, connect_data);
 
@@ -2088,7 +2098,7 @@
 
 /**
  * This function attempts to connect to the next IP address in the list
- * of IP addresses returned to us by purple_dnsquery_a() and attemps
+ * of IP addresses returned to us by purple_dnsquery_a() and attempts
  * to connect to each one.  This is called after the hostname is
  * resolved, and each time a connection attempt fails (assuming there
  * is another IP address to try).
@@ -2317,7 +2327,7 @@
 			return NULL;
 	}
 
-	connect_data->query_data = purple_dnsquery_a_account(account, connecthost,
+	connect_data->query_data = purple_dnsquery_a(account, connecthost,
 			connectport, connection_host_resolved, connect_data);
 	if (connect_data->query_data == NULL)
 	{
@@ -2385,7 +2395,7 @@
 			return NULL;
 	}
 
-	connect_data->query_data = purple_dnsquery_a_account(account, connecthost,
+	connect_data->query_data = purple_dnsquery_a(account, connecthost,
 			connectport, connection_host_resolved, connect_data);
 	if (connect_data->query_data == NULL)
 	{
@@ -2398,17 +2408,6 @@
 	return connect_data;
 }
 
-PurpleProxyConnectData *
-purple_proxy_connect_socks5(void *handle, PurpleProxyInfo *gpi,
-						  const char *host, int port,
-						  PurpleProxyConnectFunction connect_cb,
-						  gpointer data)
-{
-	return purple_proxy_connect_socks5_account(NULL, handle, gpi,
-						  host, port, connect_cb, data);
-}
-
-
 /* This is called when we connect to the SOCKS5 proxy server (through any
  * relevant account proxy)
  */
--- a/libpurple/proxy.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/proxy.h	Mon Sep 26 14:57:21 2011 +0900
@@ -47,16 +47,7 @@
 /**
  * Information on proxy settings.
  */
-typedef struct
-{
-	PurpleProxyType type;   /**< The proxy type.  */
-
-	char *host;           /**< The host.        */
-	int   port;           /**< The port number. */
-	char *username;       /**< The username.    */
-	char *password;       /**< The password.    */
-
-} PurpleProxyInfo;
+typedef struct _PurpleProxyInfo PurpleProxyInfo;
 
 typedef struct _PurpleProxyConnectData PurpleProxyConnectData;
 
@@ -191,7 +182,6 @@
  * Set purple's global proxy information.
  *
  * @param info     The proxy information.
- * @since 2.6.0
  */
 void purple_global_proxy_set_info(PurpleProxyInfo *info);
 
@@ -316,35 +306,6 @@
 			const char *host, int port,
 			PurpleProxyConnectFunction connect_cb, gpointer data);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PROXY_C_)
-/**
- * Makes a connection through a SOCKS5 proxy.
- *
- * @param handle     A handle that should be associated with this
- *                   connection attempt.  The handle can be used
- *                   to cancel the connection attempt using the
- *                   purple_proxy_connect_cancel_with_handle()
- *                   function.
- * @param gpi        The PurpleProxyInfo specifying the proxy settings
- * @param host       The destination host.
- * @param port       The destination port.
- * @param connect_cb The function to call when the connection is
- *                   established.  If the connection failed then
- *                   fd will be -1 and error message will be set
- *                   to something descriptive (hopefully).
- * @param data       User-defined data.
- *
- * @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.
- * @deprecated Use purple_proxy_connect_socks5_account instead
- */
-PurpleProxyConnectData *purple_proxy_connect_socks5(void *handle,
-			PurpleProxyInfo *gpi,
-			const char *host, int port,
-			PurpleProxyConnectFunction connect_cb, gpointer data);
-#endif
-
 /**
  * Cancel an in-progress connection attempt.  This should be called
  * by the PRPL if the user disables an account while it is still
--- a/libpurple/prpl.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/prpl.c	Mon Sep 26 14:57:21 2011 +0900
@@ -32,6 +32,17 @@
 /**************************************************************************/
 /** @name Attention Type API                                              */
 /**************************************************************************/
+
+struct _PurpleAttentionType
+{
+	const char *name;                  /**< Shown in GUI elements */
+	const char *incoming_description;  /**< Shown when sent */
+	const char *outgoing_description;  /**< Shown when receied */
+	const char *icon_name;             /**< Icon to display (optional) */
+	const char *unlocalized_name;      /**< Unlocalized name for UIs needing it */
+};
+
+
 PurpleAttentionType *
 purple_attention_type_new(const char *ulname, const char *name,
 						const char *inc_desc, const char *out_desc)
--- a/libpurple/prpl.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/prpl.h	Mon Sep 26 14:57:21 2011 +0900
@@ -31,7 +31,10 @@
 #define _PURPLE_PRPL_H_
 
 typedef struct _PurplePluginProtocolInfo PurplePluginProtocolInfo;
-/** @copydoc _PurpleAttentionType */
+
+/** Represents "nudges" and "buzzes" that you may send to a buddy to attract
+ *  their attention (or vice-versa).
+ */
 typedef struct _PurpleAttentionType PurpleAttentionType;
 
 /**************************************************************************/
@@ -111,23 +114,6 @@
 	gboolean secret;         /**< True if the entry is secret (password) */
 };
 
-/** 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 */
-	const char *incoming_description;  /**< Shown when sent */
-	const char *outgoing_description;  /**< Shown when receied */
-	const char *icon_name;             /**< Icon to display (optional) */
-	const char *unlocalized_name;      /**< Unlocalized name for UIs needing it */
-
-	/* Reserved fields for future purposes */
-	gpointer _reserved2;
-	gpointer _reserved3;
-	gpointer _reserved4;
-};
-
 /**
  * Protocol options
  *
@@ -200,14 +186,12 @@
 	/**
 	 * Indicates that slash commands are native to this protocol.
 	 * Used as a hint that unknown commands should not be sent as messages.
-	 * @since 2.1.0
 	 */
 	OPT_PROTO_SLASH_COMMANDS_NATIVE = 0x00000400,
 
 	/**
 	 * Indicates that this protocol supports sending a user-supplied message
 	 * along with an invitation.
-	 * @since 2.8.0
 	 */
 	OPT_PROTO_INVITE_MESSAGE = 0x00000800
 
@@ -222,6 +206,27 @@
  */
 struct _PurplePluginProtocolInfo
 {
+	/**
+	 * The size of the PurplePluginProtocolInfo. This should always be sizeof(PurplePluginProtocolInfo).
+	 * This allows adding more functions to this struct without requiring a major version bump.
+	 */
+	unsigned long struct_size;
+
+	/* NOTE:
+	 * If more functions are added, they should accessed using the following syntax:
+	 *
+	 *		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, new_function))
+	 *			prpl->new_function(...);
+	 *
+	 * instead of
+	 *
+	 *		if (prpl->new_function != NULL)
+	 *			prpl->new_function(...);
+	 *
+	 * The PURPLE_PROTOCOL_PLUGIN_HAS_FUNC macro can be used for the older member
+	 * functions (e.g. login, send_im etc.) too.
+	 */
+
 	PurpleProtocolOptions options;  /**< Protocol options.          */
 
 	GList *user_splits;      /**< A GList of PurpleAccountUserSplit */
@@ -333,6 +338,7 @@
 	void (*set_idle)(PurpleConnection *, int idletime);
 	void (*change_passwd)(PurpleConnection *, const char *old_pass,
 						  const char *new_pass);
+
 	/**
 	 * Add a buddy to a group on the server.
 	 *
@@ -341,11 +347,10 @@
 	 * authorization and the user is not already authorized to see the
 	 * status of \a buddy, \a add_buddy should request authorization.
 	 *
-	 * @deprecated Since 2.8.0, add_buddy_with_invite is preferred.
-	 * @see add_buddy_with_invite
+	 * If authorization is required, then use the supplied invite message.
 	 */
-	void (*add_buddy)(PurpleConnection *, PurpleBuddy *buddy, PurpleGroup *group);
-	void (*add_buddies)(PurpleConnection *, GList *buddies, GList *groups);
+	void (*add_buddy)(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message);
+	void (*add_buddies)(PurpleConnection *pc, GList *buddies, GList *groups, const char *message);
 	void (*remove_buddy)(PurpleConnection *, PurpleBuddy *buddy, PurpleGroup *group);
 	void (*remove_buddies)(PurpleConnection *, GList *buddies, GList *groups);
 	void (*add_permit)(PurpleConnection *, const char *name);
@@ -444,11 +449,6 @@
 	 * @deprecated Use #PurplePluginProtocolInfo.get_info instead.
 	 */
 	void (*get_cb_info)(PurpleConnection *, int, const char *who);
-	/**
-	 * @deprecated Use #PurplePluginProtocolInfo.get_cb_real_name and
-	 *             #PurplePluginProtocolInfo.status_text instead.
-	 */
-	void (*get_cb_away)(PurpleConnection *, int, const char *who);
 
 	/** save/store buddy's alias on server list/roster */
 	void (*alias_buddy)(PurpleConnection *, const char *who,
@@ -534,27 +534,6 @@
 	gboolean (*send_attention)(PurpleConnection *gc, const char *username, guint type);
 	GList *(*get_attention_types)(PurpleAccount *acct);
 
-	/**
-	 * The size of the PurplePluginProtocolInfo. This should always be sizeof(PurplePluginProtocolInfo).
-	 * This allows adding more functions to this struct without requiring a major version bump.
-	 */
-	unsigned long struct_size;
-
-	/* NOTE:
-	 * If more functions are added, they should accessed using the following syntax:
-	 *
-	 *		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, new_function))
-	 *			prpl->new_function(...);
-	 *
-	 * instead of
-	 *
-	 *		if (prpl->new_function != NULL)
-	 *			prpl->new_function(...);
-	 *
-	 * The PURPLE_PROTOCOL_PLUGIN_HAS_FUNC macro can be used for the older member
-	 * functions (e.g. login, send_im etc.) too.
-	 */
-
 	/** This allows protocols to specify additional strings to be used for
 	 * various purposes.  The idea is to stuff a bunch of strings in this hash
 	 * table instead of expanding the struct for every addition.  This hash
@@ -590,7 +569,6 @@
 	/**
 	 * Returns an array of "PurpleMood"s, with the last one having
 	 * "mood" set to @c NULL.
-	 * @since 2.7.0
 	 */
 	PurpleMood *(*get_moods)(PurpleAccount *account);
 
@@ -610,7 +588,6 @@
 	 * @param failure_cb Callback to be called if setting the public alias
 	 *                   fails
 	 * @see purple_account_set_public_alias
-	 * @since 2.7.0
 	 */
 	void (*set_public_alias)(PurpleConnection *gc, const char *alias,
 	                         PurpleSetPublicAliasSuccessCallback success_cb,
@@ -627,31 +604,14 @@
 	 * @param failure_cb Callback to be called if the prpl is unable to
 	 *                   retrieve the alias
 	 * @see purple_account_get_public_alias
-	 * @since 2.7.0
 	 */
 	void (*get_public_alias)(PurpleConnection *gc,
 	                         PurpleGetPublicAliasSuccessCallback success_cb,
 	                         PurpleGetPublicAliasFailureCallback failure_cb);
-
-	/**
-	 * Add a buddy to a group on the server.
-	 *
-	 * This PRPL function may be called in situations in which the buddy is
-	 * already in the specified group. If the protocol supports
-	 * authorization and the user is not already authorized to see the
-	 * status of \a buddy, \a add_buddy should request authorization.
-	 *
-	 * If authorization is required, then use the supplied invite message.
-	 *
-	 * @since 2.8.0
-	 */
-	void (*add_buddy_with_invite)(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message);
-	void (*add_buddies_with_invite)(PurpleConnection *pc, GList *buddies, GList *groups, const char *message);
 };
 
 #define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \
-	(((G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < G_STRUCT_OFFSET(PurplePluginProtocolInfo, struct_size)) \
-	  || (G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < prpl->struct_size)) && \
+	(G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < prpl->struct_size && \
 	 prpl->member != NULL)
 
 
@@ -680,8 +640,8 @@
  *             should be the same string as @a ulname, with localization.
  * @param inc_desc A localized description shown when the event is received.
  * @param out_desc A localized description shown when the event is sent.
+ *
  * @return A pointer to the new object.
- * @since 2.4.0
  */
 PurpleAttentionType *purple_attention_type_new(const char *ulname, const char *name,
 								const char *inc_desc, const char *out_desc);
@@ -693,7 +653,6 @@
  * @param name The localized name that will be displayed by UIs. This should be
  *             the same string given as the unlocalized name, but with
  *             localization.
- * @since 2.4.0
  */
 void purple_attention_type_set_name(PurpleAttentionType *type, const char *name);
 
@@ -703,7 +662,6 @@
  *
  * @param type The attention type.
  * @param desc The localized description for incoming events.
- * @since 2.4.0
  */
 void purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc);
 
@@ -713,7 +671,6 @@
  *
  * @param type The attention type.
  * @param desc The localized description for outgoing events.
- * @since 2.4.0
  */
 void purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc);
 
@@ -723,7 +680,6 @@
  * @param type The attention type.
  * @param name The icon's name.
  * @note Icons are optional for attention events.
- * @since 2.4.0
  */
 void purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name);
 
@@ -734,7 +690,6 @@
  * @param type The attention type.
  * @param ulname The unlocalized name.  This should be the same string given as
  *               the localized name, but without localization.
- * @since 2.4.0
  */
 void purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname);
 
@@ -742,8 +697,8 @@
  * Get the attention type's name as displayed by the UI.
  *
  * @param type The attention type.
+ *
  * @return The name.
- * @since 2.4.0
  */
 const char *purple_attention_type_get_name(const PurpleAttentionType *type);
 
@@ -752,7 +707,6 @@
  *
  * @param type The attention type.
  * @return The description.
- * @since 2.4.0
  */
 const char *purple_attention_type_get_incoming_desc(const PurpleAttentionType *type);
 
@@ -761,7 +715,6 @@
  *
  * @param type The attention type.
  * @return The description.
- * @since 2.4.0
  */
 const char *purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type);
 
@@ -771,7 +724,6 @@
  * @param type The attention type.
  * @return The icon name or @c NULL if unset/empty.
  * @note Icons are optional for attention events.
- * @since 2.4.0
  */
 const char *purple_attention_type_get_icon_name(const PurpleAttentionType *type);
 
@@ -780,7 +732,6 @@
  *
  * @param type The attention type
  * @return The unlocalized name.
- * @since 2.4.0
  */
 const char *purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type);
 
@@ -836,7 +787,6 @@
  * @param account   The account.
  *
  * @see account-actions-changed
- * @since 2.6.0
  */
 void purple_prpl_got_account_actions(PurpleAccount *account);
 
@@ -927,8 +877,6 @@
  *
  * Note that you can't send arbitrary PurpleAttentionType's, because there is
  * only a fixed set of attention commands.
- *
- * @since 2.5.0
  */
 void purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code);
 
@@ -939,8 +887,6 @@
  * @param who Who requested your attention.
  * @param type_code An index into the prpl's attention_types list determining the type
  *        of the attention request command to send.
- *
- * @since 2.5.0
  */
 void purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code);
 
@@ -952,8 +898,6 @@
  * @param who Who requested your attention.
  * @param type_code An index into the prpl's attention_types list determining the type
  *        of the attention request command to send.
- *
- * @since 2.5.0
  */
 void purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code);
 
@@ -988,7 +932,6 @@
  *
  * @param account The account the user is on.
  * @param who The name of the contact for which capabilities have been received.
- * @since 2.7.0
  */
 void purple_prpl_got_media_caps(PurpleAccount *account, const char *who);
 
--- a/libpurple/purple.h.in	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/purple.h.in	Mon Sep 26 14:57:21 2011 +0900
@@ -6,7 +6,6 @@
  * include any other libpurple files.
  *
  * @ingroup core libpurple
- * @since 2.3.0
  */
 
 /* purple
--- a/libpurple/request.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/request.c	Mon Sep 26 14:57:21 2011 +0900
@@ -42,9 +42,6 @@
 
 } PurpleRequestInfo;
 
-/**
- * A request field.
- */
 struct _PurpleRequestField
 {
 	PurpleRequestFieldType type;
@@ -126,7 +123,26 @@
 
 	void *ui_data;
 	char *tooltip;
+};
 
+struct _PurpleRequestFields
+{
+	GList *groups;
+
+	GHashTable *fields;
+
+	GList *required_fields;
+
+	void *ui_data;
+};
+
+struct _PurpleRequestFieldGroup
+{
+	PurpleRequestFields *fields_list;
+
+	char *title;
+
+	GList *fields;
 };
 
 PurpleRequestFields *
@@ -357,6 +373,20 @@
 	return purple_request_field_account_get_value(field);
 }
 
+gpointer purple_request_fields_get_ui_data(const PurpleRequestFields *fields)
+{
+	g_return_val_if_fail(fields != NULL, NULL);
+
+	return fields->ui_data;
+}
+
+void purple_request_fields_set_ui_data(PurpleRequestFields *fields, gpointer ui_data)
+{
+	g_return_if_fail(fields != NULL);
+
+	fields->ui_data = ui_data;
+}
+
 PurpleRequestFieldGroup *
 purple_request_field_group_new(const char *title)
 {
@@ -423,6 +453,14 @@
 	return group->fields;
 }
 
+PurpleRequestFields *
+purple_request_field_group_get_fields_list(const PurpleRequestFieldGroup *group)
+{
+	g_return_val_if_fail(group != NULL, NULL);
+
+	return group->fields_list;
+}
+
 PurpleRequestField *
 purple_request_field_new(const char *id, const char *text,
 					   PurpleRequestFieldType type)
@@ -951,13 +989,6 @@
 }
 
 void
-purple_request_field_list_add(PurpleRequestField *field, const char *item,
-							void *data)
-{
-	purple_request_field_list_add_icon(field, item, NULL, data);
-}
-
-void
 purple_request_field_list_add_icon(PurpleRequestField *field, const char *item, const char* icon_path,
 							void *data)
 {
--- a/libpurple/request.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/request.h	Mon Sep 26 14:57:21 2011 +0900
@@ -30,9 +30,21 @@
 #include <glib-object.h>
 #include <glib.h>
 
-/** @copydoc _PurpleRequestField */
+/**
+ * A request field.
+ */
 typedef struct _PurpleRequestField PurpleRequestField;
 
+/**
+ * Multiple fields request data.
+ */
+typedef struct _PurpleRequestFields PurpleRequestFields;
+
+/**
+ * A group of fields with a title.
+ */
+typedef struct _PurpleRequestFieldGroup PurpleRequestFieldGroup;
+
 #include "account.h"
 
 #define PURPLE_DEFAULT_ACTION_NONE	-1
@@ -69,34 +81,6 @@
 } PurpleRequestFieldType;
 
 /**
- * Multiple fields request data.
- */
-typedef struct
-{
-	GList *groups;
-
-	GHashTable *fields;
-
-	GList *required_fields;
-
-	void *ui_data;
-
-} PurpleRequestFields;
-
-/**
- * A group of fields with a title.
- */
-typedef struct
-{
-	PurpleRequestFields *fields_list;
-
-	char *title;
-
-	GList *fields;
-
-} PurpleRequestFieldGroup;
-
-/**
  * Request UI operations.
  */
 typedef struct
@@ -322,6 +306,25 @@
 PurpleAccount *purple_request_fields_get_account(const PurpleRequestFields *fields,
 											 const char *id);
 
+/**
+ * Returns the UI data associated with this object.
+ *
+ * @param fields The fields list.
+ *
+ * @return The UI data associated with this object.  This is a
+ *         convenience field provided to the UIs--it is not
+ *         used by the libuprple core.
+ */
+gpointer purple_request_fields_get_ui_data(const PurpleRequestFields *fields);
+
+/**
+ * Set the UI data associated with this object.
+ *
+ * @param fields The fields list.
+ * @param ui_data A pointer to associate with this object.
+ */
+void purple_request_fields_set_ui_data(PurpleRequestFields *fields, gpointer ui_data);
+
 /*@}*/
 
 /**************************************************************************/
@@ -374,6 +377,16 @@
 GList *purple_request_field_group_get_fields(
 		const PurpleRequestFieldGroup *group);
 
+/**
+ * Returns a list of all fields in a group.
+ *
+ * @param group The group.
+ *
+ * @constreturn The list of fields in the group.
+ */
+PurpleRequestFields *purple_request_field_group_get_fields_list(
+		const PurpleRequestFieldGroup *group);
+
 /*@}*/
 
 /**************************************************************************/
@@ -464,8 +477,6 @@
  * @param field The field.
  *
  * @return The UI data.
- *
- * @since 2.6.0
  */
 PurpleRequestFieldGroup *purple_request_field_get_group(const PurpleRequestField *field);
 
@@ -529,8 +540,6 @@
  * @param field The field.
  *
  * @return The UI data.
- *
- * @since 2.6.0
  */
 gpointer purple_request_field_get_ui_data(const PurpleRequestField *field);
 
@@ -541,8 +550,6 @@
  * @param ui_data The UI data.
  *
  * @return The UI data.
- *
- * @since 2.6.0
  */
 void purple_request_field_set_ui_data(PurpleRequestField *field,
                                       gpointer ui_data);
@@ -894,18 +901,6 @@
  *
  * @param field The list field.
  * @param item  The list item.
- * @param data  The associated data.
- *
- * @deprecated Use purple_request_field_list_add_icon() instead.
- */
-void purple_request_field_list_add(PurpleRequestField *field,
-								 const char *item, void *data);
-
-/**
- * Adds an item to a list field.
- *
- * @param field The list field.
- * @param item  The list item.
  * @param icon_path The path to icon file, or @c NULL for no icon.
  * @param data  The associated data.
  */
@@ -1335,8 +1330,7 @@
 
 /**
  * Version of purple_request_action() supplying an image for the UI to
- * optionally display as an icon in the dialog; see its documentation
- * @since 2.7.0
+ * optionally display as an icon in the dialog; see its documentation.
  */
 void *purple_request_action_with_icon(void *handle, const char *title,
 	const char *primary, const char *secondary, int default_action,
@@ -1347,7 +1341,6 @@
 /**
  * <tt>va_list</tt> version of purple_request_action_with_icon();
  * see its documentation.
- * @since 2.7.0
  */
 void *purple_request_action_with_icon_varg(void *handle, const char *title,
 	const char *primary, const char *secondary, int default_action,
--- a/libpurple/roomlist.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/roomlist.h	Mon Sep 26 14:57:21 2011 +0900
@@ -217,14 +217,13 @@
  *
  * @param roomlist The roomlist, which must not be @c NULL.
  * @constreturn A list of fields
- * @since 2.4.0
  */
 GList *purple_roomlist_get_fields(PurpleRoomlist *roomlist);
 
 /**
  * Get the protocol data associated with this room list.
  *
- * @param roomlist The roomlist, which must not be @c NULL.
+ * @param list The roomlist, which must not be @c NULL.
  *
  * @return The protocol data associated with this room list.  This is a
  *         convenience field provided to the protocol plugin--it is not
@@ -243,7 +242,7 @@
 /**
  * Get the UI data associated with this room list.
  *
- * @param roomlist The roomlist, which must not be @c NULL.
+ * @param list The roomlist, which must not be @c NULL.
  *
  * @return The UI data associated with this room list.  This is a
  *         convenience field provided to the UIs--it is not
@@ -255,7 +254,7 @@
  * Set the UI data associated with this room list.
  *
  * @param list The roomlist, which must not be @c NULL.
- * @param UI_data A pointer to associate with this room list.
+ * @param ui_data A pointer to associate with this room list.
  */
 void purple_roomlist_set_ui_data(PurpleRoomlist *list, gpointer ui_data);
 
@@ -299,7 +298,6 @@
  * Get the type of a room.
  * @param room  The room, which must not be @c NULL.
  * @return The type of the room.
- * @since 2.4.0
  */
 PurpleRoomlistRoomType purple_roomlist_room_get_type(PurpleRoomlistRoom *room);
 
@@ -307,7 +305,6 @@
  * Get the name of a room.
  * @param room  The room, which must not be @c NULL.
  * @return The name of the room.
- * @since 2.4.0
  */
 const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room);
 
@@ -315,7 +312,6 @@
  * Get the parent of a room.
  * @param room  The room, which must not be @c NULL.
  * @return The parent of the room, which can be @c NULL.
- * @since 2.4.0
  */
 PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room);
 
@@ -341,7 +337,6 @@
  *
  * @param room  The room, which must not be @c NULL.
  * @constreturn A list of fields
- * @since 2.4.0
  */
 GList * purple_roomlist_room_get_fields(PurpleRoomlistRoom *room);
 
@@ -373,7 +368,6 @@
  * @param field  A PurpleRoomlistField, which must not be @c NULL.
  *
  * @return  The type of the field.
- * @since 2.4.0
  */
 PurpleRoomlistFieldType purple_roomlist_field_get_type(PurpleRoomlistField *field);
 
@@ -383,7 +377,6 @@
  * @param field  A PurpleRoomlistField, which must not be @c NULL.
  *
  * @return  The label of the field.
- * @since 2.4.0
  */
 const char * purple_roomlist_field_get_label(PurpleRoomlistField *field);
 
@@ -392,7 +385,6 @@
  * @param field  A PurpleRoomlistField, which must not be @c NULL.
  *
  * @return  @c TRUE if the field is hidden, @c FALSE otherwise.
- * @since 2.4.0
  */
 gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field);
 
--- a/libpurple/server.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/server.c	Mon Sep 26 14:57:21 2011 +0900
@@ -336,18 +336,6 @@
 	return attn;
 }
 
-void
-serv_send_attention(PurpleConnection *gc, const char *who, guint type_code)
-{
-	purple_prpl_send_attention(gc, who, type_code);
-}
-
-void
-serv_got_attention(PurpleConnection *gc, const char *who, guint type_code)
-{
-	purple_prpl_got_attention(gc, who, type_code);
-}
-
 
 /*
  * Move a buddy from one group to another on server.
--- a/libpurple/server.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/server.h	Mon Sep 26 14:57:21 2011 +0900
@@ -61,32 +61,6 @@
  */
 PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account, guint type_code);
 
-/** Send an attention request message.
- *
- * @deprecated Use purple_prpl_send_attention() instead.
- *
- * @param gc The connection to send the message on.
- * @param who Whose attention to request.
- * @param type_code An index into the prpl's attention_types list determining the type
- * 	of the attention request command to send. 0 if prpl only defines one
- * 	(for example, Yahoo and MSN), but some protocols define more (MySpaceIM).
- *
- * Note that you can't send arbitrary PurpleAttentionType's, because there is
- * only a fixed set of attention commands.
- */
-void serv_send_attention(PurpleConnection *gc, const char *who, guint type_code);
-
-/** Process an incoming attention message.
- *
- * @deprecated Use purple_prpl_got_attention() instead.
- *
- * @param gc The connection that received the attention message.
- * @param who Who requested your attention.
- * @param type_code An index into the prpl's attention_types list determining the type
- * 	of the attention request command to send.
- */
-void serv_got_attention(PurpleConnection *gc, const char *who, guint type_code);
-
 void serv_get_info(PurpleConnection *, const char *);
 void serv_set_info(PurpleConnection *, const char *);
 
--- a/libpurple/smiley.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/smiley.h	Mon Sep 26 14:57:21 2011 +0900
@@ -1,7 +1,6 @@
 /**
  * @file smiley.h Smiley API
  * @ingroup core
- * @since 2.5.0
  */
 
 /* purple
--- a/libpurple/sound-theme.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/sound-theme.c	Mon Sep 26 14:57:21 2011 +0900
@@ -24,7 +24,7 @@
 #include "sound-theme.h"
 
 #define PURPLE_SOUND_THEME_GET_PRIVATE(Gobject) \
-	((PurpleSoundThemePrivate *) ((PURPLE_SOUND_THEME(Gobject))->priv))
+	(G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PURPLE_TYPE_SOUND_THEME, PurpleSoundThemePrivate))
 
 /******************************************************************************
  * Structs
@@ -55,8 +55,6 @@
 {
 	PurpleSoundThemePrivate *priv;
 
-	(PURPLE_SOUND_THEME(instance))->priv = g_new0(PurpleSoundThemePrivate, 1);
-
 	priv = PURPLE_SOUND_THEME_GET_PRIVATE(instance);
 
 	priv->sound_files = g_hash_table_new_full(g_str_hash,
@@ -82,6 +80,8 @@
 
 	parent_class = g_type_class_peek_parent(klass);
 
+	g_type_class_add_private(klass, sizeof(PurpleSoundThemePrivate));
+
 	obj_class->finalize = purple_sound_theme_finalize;
 }
 
--- a/libpurple/sound-theme.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/sound-theme.h	Mon Sep 26 14:57:21 2011 +0900
@@ -51,7 +51,6 @@
 struct _PurpleSoundTheme
 {
 	PurpleTheme parent;
-	gpointer priv;
 };
 
 struct _PurpleSoundThemeClass
--- a/libpurple/sslconn.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/sslconn.c	Mon Sep 26 14:57:21 2011 +0900
@@ -185,15 +185,6 @@
 }
 
 PurpleSslConnection *
-purple_ssl_connect_fd(PurpleAccount *account, int fd,
-					PurpleSslInputFunction func,
-					PurpleSslErrorFunction error_func,
-                    void *data)
-{
-    return purple_ssl_connect_with_host_fd(account, fd, func, error_func, NULL, data);
-}
-
-PurpleSslConnection *
 purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd,
                       PurpleSslInputFunction func,
                       PurpleSslErrorFunction error_func,
--- a/libpurple/sslconn.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/sslconn.h	Mon Sep 26 14:57:21 2011 +0900
@@ -203,7 +203,6 @@
  * @param data       User-defined data.
  *
  * @return The SSL connection handle.
- * @since 2.6.0
  */
 PurpleSslConnection *purple_ssl_connect_with_ssl_cn(PurpleAccount *account, const char *host,
 									int port, PurpleSslInputFunction func,
@@ -211,26 +210,6 @@
 									const char *ssl_host,
 									void *data);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_SSLCONN_C_)
-/**
- * Makes a SSL connection using an already open file descriptor.
- *
- * @deprecated Use purple_ssl_connect_with_host_fd() instead.
- *
- * @param account    The account making the connection.
- * @param fd         The file descriptor.
- * @param func       The SSL input handler function.
- * @param error_func The SSL error handler function.
- * @param data       User-defined data.
- *
- * @return The SSL connection handle.
- */
-PurpleSslConnection *purple_ssl_connect_fd(PurpleAccount *account, int fd,
-									   PurpleSslInputFunction func,
-									   PurpleSslErrorFunction error_func,
- 									   void *data);
-#endif
-
 /**
  * Makes a SSL connection using an already open file descriptor.
  *
@@ -242,8 +221,6 @@
  * @param data       User-defined data.
  *
  * @return The SSL connection handle.
- *
- * @since 2.2.0
  */
 PurpleSslConnection *purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd,
                                            PurpleSslInputFunction func,
@@ -298,8 +275,6 @@
  *
  * @return The peer certificate chain, in the order of certificate, issuer,
  *         issuer's issuer, etc. @a NULL if no certificates have been provided,
- *
- * @since 2.2.0
  */
 GList * purple_ssl_get_peer_certificates(PurpleSslConnection *gsc);
 
--- a/libpurple/status.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/status.c	Mon Sep 26 14:57:21 2011 +0900
@@ -257,6 +257,42 @@
 			user_settable, FALSE);
 }
 
+static void
+status_type_add_attr(PurpleStatusType *status_type, const char *id,
+		const char *name, PurpleValue *value)
+{
+	PurpleStatusAttr *attr;
+
+	g_return_if_fail(status_type != NULL);
+	g_return_if_fail(id          != NULL);
+	g_return_if_fail(name        != NULL);
+	g_return_if_fail(value       != NULL);
+
+	attr = purple_status_attr_new(id, name, value);
+
+	status_type->attrs = g_list_append(status_type->attrs, attr);
+}
+
+static void
+status_type_add_attrs_vargs(PurpleStatusType *status_type, va_list args)
+{
+	const char *id, *name;
+	PurpleValue *value;
+
+	g_return_if_fail(status_type != NULL);
+
+	while ((id = va_arg(args, const char *)) != NULL)
+	{
+		name = va_arg(args, const char *);
+		g_return_if_fail(name != NULL);
+
+		value = va_arg(args, PurpleValue *);
+		g_return_if_fail(value != NULL);
+
+		status_type_add_attr(status_type, id, name, value);
+	}
+}
+
 PurpleStatusType *
 purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive,
 		const char *id, const char *name,
@@ -277,10 +313,10 @@
 			user_settable, independent);
 
 	/* Add the first attribute */
-	purple_status_type_add_attr(status_type, attr_id, attr_name, attr_value);
+	status_type_add_attr(status_type, attr_id, attr_name, attr_value);
 
 	va_start(args, attr_value);
-	purple_status_type_add_attrs_vargs(status_type, args);
+	status_type_add_attrs_vargs(status_type, args);
 	va_end(args);
 
 	return status_type;
@@ -301,61 +337,6 @@
 	g_free(status_type);
 }
 
-void
-purple_status_type_add_attr(PurpleStatusType *status_type, const char *id,
-		const char *name, PurpleValue *value)
-{
-	PurpleStatusAttr *attr;
-
-	g_return_if_fail(status_type != NULL);
-	g_return_if_fail(id          != NULL);
-	g_return_if_fail(name        != NULL);
-	g_return_if_fail(value       != NULL);
-
-	attr = purple_status_attr_new(id, name, value);
-
-	status_type->attrs = g_list_append(status_type->attrs, attr);
-}
-
-void
-purple_status_type_add_attrs_vargs(PurpleStatusType *status_type, va_list args)
-{
-	const char *id, *name;
-	PurpleValue *value;
-
-	g_return_if_fail(status_type != NULL);
-
-	while ((id = va_arg(args, const char *)) != NULL)
-	{
-		name = va_arg(args, const char *);
-		g_return_if_fail(name != NULL);
-
-		value = va_arg(args, PurpleValue *);
-		g_return_if_fail(value != NULL);
-
-		purple_status_type_add_attr(status_type, id, name, value);
-	}
-}
-
-void
-purple_status_type_add_attrs(PurpleStatusType *status_type, const char *id,
-		const char *name, PurpleValue *value, ...)
-{
-	va_list args;
-
-	g_return_if_fail(status_type != NULL);
-	g_return_if_fail(id          != NULL);
-	g_return_if_fail(name        != NULL);
-	g_return_if_fail(value       != NULL);
-
-	/* Add the first attribute */
-	purple_status_type_add_attr(status_type, id, name, value);
-
-	va_start(args, value);
-	purple_status_type_add_attrs_vargs(status_type, args);
-	va_end(args);
-}
-
 PurpleStatusPrimitive
 purple_status_type_get_primitive(const PurpleStatusType *status_type)
 {
@@ -687,6 +668,68 @@
 	notify_status_update(presence, old_status, status);
 }
 
+static void
+status_set_attr_boolean(PurpleStatus *status, const char *id,
+		gboolean value)
+{
+	PurpleValue *attr_value;
+
+	g_return_if_fail(status != NULL);
+	g_return_if_fail(id     != NULL);
+
+	/* Make sure this attribute exists and is the correct type. */
+	attr_value = purple_status_get_attr_value(status, id);
+	g_return_if_fail(attr_value != NULL);
+	g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_BOOLEAN);
+
+	purple_value_set_boolean(attr_value, value);
+}
+
+static void
+status_set_attr_int(PurpleStatus *status, const char *id, int value)
+{
+	PurpleValue *attr_value;
+
+	g_return_if_fail(status != NULL);
+	g_return_if_fail(id     != NULL);
+
+	/* Make sure this attribute exists and is the correct type. */
+	attr_value = purple_status_get_attr_value(status, id);
+	g_return_if_fail(attr_value != NULL);
+	g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_INT);
+
+	purple_value_set_int(attr_value, value);
+}
+
+static void
+status_set_attr_string(PurpleStatus *status, const char *id,
+		const char *value)
+{
+	PurpleValue *attr_value;
+
+	g_return_if_fail(status != NULL);
+	g_return_if_fail(id     != NULL);
+
+	/* Make sure this attribute exists and is the correct type. */
+	attr_value = purple_status_get_attr_value(status, id);
+	/* This used to be g_return_if_fail, but it's failing a LOT, so
+	 * let's generate a log error for now. */
+	/* g_return_if_fail(attr_value != NULL); */
+	if (attr_value == NULL) {
+		purple_debug_error("status",
+				 "Attempted to set status attribute '%s' for "
+				 "status '%s', which is not legal.  Fix "
+                                 "this!\n", id,
+				 purple_status_type_get_name(purple_status_get_type(status)));
+		return;
+	}
+	g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_STRING);
+
+	/* XXX: Check if the value has actually changed. If it has, and the status
+	 * is active, should this trigger 'status_has_changed'? */
+	purple_value_set_string(attr_value, value);
+}
+
 void
 purple_status_set_active(PurpleStatus *status, gboolean active)
 {
@@ -769,7 +812,7 @@
 			l = l->next;
 			if (purple_strequal(string_data, purple_value_get_string(value)))
 				continue;
-			purple_status_set_attr_string(status, id, string_data);
+			status_set_attr_string(status, id, string_data);
 			changed = TRUE;
 		}
 		else if (purple_value_get_type(value) == PURPLE_TYPE_INT)
@@ -778,7 +821,7 @@
 			l = l->next;
 			if (int_data == purple_value_get_int(value))
 				continue;
-			purple_status_set_attr_int(status, id, int_data);
+			status_set_attr_int(status, id, int_data);
 			changed = TRUE;
 		}
 		else if (purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN)
@@ -787,7 +830,7 @@
 			l = l->next;
 			if (boolean_data == purple_value_get_boolean(value))
 				continue;
-			purple_status_set_attr_boolean(status, id, boolean_data);
+			status_set_attr_boolean(status, id, boolean_data);
 			changed = TRUE;
 		}
 		else
@@ -818,21 +861,21 @@
 					continue;
 				}
 
-				purple_status_set_attr_string(status, attr->id, def);
+				status_set_attr_string(status, attr->id, def);
 			} else if (purple_value_get_type(default_value) == PURPLE_TYPE_INT) {
 				int cur = purple_status_get_attr_int(status, attr->id);
 				int def = purple_value_get_int(default_value);
 				if (cur == def)
 					continue;
 
-				purple_status_set_attr_int(status, attr->id, def);
+				status_set_attr_int(status, attr->id, def);
 			} else if (purple_value_get_type(default_value) == PURPLE_TYPE_BOOLEAN) {
 				gboolean cur = purple_status_get_attr_boolean(status, attr->id);
 				gboolean def = purple_value_get_boolean(default_value);
 				if (cur == def)
 					continue;
 
-				purple_status_set_attr_boolean(status, attr->id, def);
+				status_set_attr_boolean(status, attr->id, def);
 			}
 			changed = TRUE;
 		}
@@ -844,68 +887,6 @@
 	status_has_changed(status);
 }
 
-void
-purple_status_set_attr_boolean(PurpleStatus *status, const char *id,
-		gboolean value)
-{
-	PurpleValue *attr_value;
-
-	g_return_if_fail(status != NULL);
-	g_return_if_fail(id     != NULL);
-
-	/* Make sure this attribute exists and is the correct type. */
-	attr_value = purple_status_get_attr_value(status, id);
-	g_return_if_fail(attr_value != NULL);
-	g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_BOOLEAN);
-
-	purple_value_set_boolean(attr_value, value);
-}
-
-void
-purple_status_set_attr_int(PurpleStatus *status, const char *id, int value)
-{
-	PurpleValue *attr_value;
-
-	g_return_if_fail(status != NULL);
-	g_return_if_fail(id     != NULL);
-
-	/* Make sure this attribute exists and is the correct type. */
-	attr_value = purple_status_get_attr_value(status, id);
-	g_return_if_fail(attr_value != NULL);
-	g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_INT);
-
-	purple_value_set_int(attr_value, value);
-}
-
-void
-purple_status_set_attr_string(PurpleStatus *status, const char *id,
-		const char *value)
-{
-	PurpleValue *attr_value;
-
-	g_return_if_fail(status != NULL);
-	g_return_if_fail(id     != NULL);
-
-	/* Make sure this attribute exists and is the correct type. */
-	attr_value = purple_status_get_attr_value(status, id);
-	/* This used to be g_return_if_fail, but it's failing a LOT, so
-	 * let's generate a log error for now. */
-	/* g_return_if_fail(attr_value != NULL); */
-	if (attr_value == NULL) {
-		purple_debug_error("status",
-				 "Attempted to set status attribute '%s' for "
-				 "status '%s', which is not legal.  Fix "
-                                 "this!\n", id,
-				 purple_status_type_get_name(purple_status_get_type(status)));
-		return;
-	}
-	g_return_if_fail(purple_value_get_type(attr_value) == PURPLE_TYPE_STRING);
-
-	/* XXX: Check if the value has actually changed. If it has, and the status
-	 * is active, should this trigger 'status_has_changed'? */
-	purple_value_set_string(attr_value, value);
-}
-
 PurpleStatusType *
 purple_status_get_type(const PurpleStatus *status)
 {
@@ -1167,30 +1148,6 @@
 }
 
 void
-purple_presence_add_status(PurplePresence *presence, PurpleStatus *status)
-{
-	g_return_if_fail(presence != NULL);
-	g_return_if_fail(status   != NULL);
-
-	presence->statuses = g_list_append(presence->statuses, status);
-
-	g_hash_table_insert(presence->status_table,
-	g_strdup(purple_status_get_id(status)), status);
-}
-
-void
-purple_presence_add_list(PurplePresence *presence, GList *source_list)
-{
-	GList *l;
-
-	g_return_if_fail(presence    != NULL);
-	g_return_if_fail(source_list != NULL);
-
-	for (l = source_list; l != NULL; l = l->next)
-		purple_presence_add_status(presence, (PurpleStatus *)l->data);
-}
-
-void
 purple_presence_set_status_active(PurplePresence *presence, const char *status_id,
 		gboolean active)
 {
--- a/libpurple/status.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/status.h	Mon Sep 26 14:57:21 2011 +0900
@@ -271,56 +271,6 @@
  */
 void purple_status_type_destroy(PurpleStatusType *status_type);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds an attribute to a status type.
- *
- * @param status_type The status type to add the attribute to.
- * @param id          The ID of the attribute.
- * @param name        The name presented to the user.
- * @param value       The value type of this attribute.
- *
- * @deprecated This function isn't needed and should be removed in 3.0.0.
- *             Status type attributes should be set when the status type
- *             is created, in the call to purple_status_type_new_with_attrs.
- */
-void purple_status_type_add_attr(PurpleStatusType *status_type, const char *id,
-							   const char *name, PurpleValue *value);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds multiple attributes to a status type.
- *
- * @param status_type The status type to add the attribute to.
- * @param id          The ID of the first attribute.
- * @param name        The description of the first attribute.
- * @param value       The value type of the first attribute attribute.
- * @param ...         Additional attribute information.
- *
- * @deprecated This function isn't needed and should be removed in 3.0.0.
- *             Status type attributes should be set when the status type
- *             is created, in the call to purple_status_type_new_with_attrs.
- */
-void purple_status_type_add_attrs(PurpleStatusType *status_type, const char *id,
-								const char *name, PurpleValue *value, ...) G_GNUC_NULL_TERMINATED;
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds multiple attributes to a status type using a va_list.
- *
- * @param status_type The status type to add the attribute to.
- * @param args        The va_list of attributes.
- *
- * @deprecated This function isn't needed and should be removed in 3.0.0.
- *             Status type attributes should be set when the status type
- *             is created, in the call to purple_status_type_new_with_attrs.
- */
-void purple_status_type_add_attrs_vargs(PurpleStatusType *status_type,
-									  va_list args);
-#endif
-
 /**
  * Returns the primitive type of a status type.
  *
@@ -552,51 +502,6 @@
 void purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
 											GList *attrs);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Sets the boolean value of an attribute in a status with the specified ID.
- *
- * @param status The status.
- * @param id     The attribute ID.
- * @param value  The boolean value.
- *
- * @deprecated This function is only used by status.c and should be made
- *             static in 3.0.0.
- */
-void purple_status_set_attr_boolean(PurpleStatus *status, const char *id,
-								  gboolean value);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Sets the integer value of an attribute in a status with the specified ID.
- *
- * @param status The status.
- * @param id     The attribute ID.
- * @param value  The integer value.
- *
- * @deprecated This function is only used by status.c and should be made
- *             static in 3.0.0.
- */
-void purple_status_set_attr_int(PurpleStatus *status, const char *id,
-							  int value);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Sets the string value of an attribute in a status with the specified ID.
- *
- * @param status The status.
- * @param id     The attribute ID.
- * @param value  The string value.
- *
- * @deprecated This function is only used by status.c and should be made
- *             static in 3.0.0.
- */
-void purple_status_set_attr_string(PurpleStatus *status, const char *id,
-								 const char *value);
-#endif
-
 /**
  * Returns the status's type.
  *
@@ -803,32 +708,6 @@
  */
 void purple_presence_destroy(PurplePresence *presence);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds a status to a presence.
- *
- * @param presence The presence.
- * @param status   The status to add.
- *
- * @deprecated This function is only used by purple_presence_add_list,
- *             and both should be removed in 3.0.0.
- */
-void purple_presence_add_status(PurplePresence *presence, PurpleStatus *status);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_STATUS_C_)
-/**
- * Adds a list of statuses to the presence.
- *
- * @param presence    The presence.
- * @param source_list The source list of statuses to add, which is not
- *                    modified or freed by this function.
- *
- * @deprecated This function isn't used and should be removed in 3.0.0.
- */
-void purple_presence_add_list(PurplePresence *presence, GList *source_list);
-#endif
-
 /**
  * Sets the active state of a status in a presence.
  *
--- a/libpurple/stun.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/stun.c	Mon Sep 26 14:57:21 2011 +0900
@@ -338,7 +338,7 @@
 		return;
 	}
 
-	if (!purple_network_listen_range(12108, 12208, SOCK_DGRAM, hbn_listen_cb, hosts)) {
+	if (!purple_network_listen_range(12108, 12208, AF_UNSPEC, SOCK_DGRAM, TRUE, hbn_listen_cb, hosts)) {
 		while (hosts) {
 			hosts = g_slist_delete_link(hosts, hosts);
 			g_free(hosts->data);
@@ -365,7 +365,7 @@
 	purple_debug_info("stun", "got %d SRV responses, server: %s, port: %d\n",
 		results, servername, port);
 
-	purple_dnsquery_a_account(NULL, servername, port, hbn_cb, NULL);
+	purple_dnsquery_a(NULL, servername, port, hbn_cb, NULL);
 	g_free(resp);
 }
 
@@ -424,7 +424,7 @@
 	nattype.servername = g_strdup(servername);
 
 	callbacks = g_slist_append(callbacks, cb);
-	purple_srv_resolve_account(NULL, "stun", "udp", servername, do_test1,
+	purple_srv_resolve(NULL, "stun", "udp", servername, do_test1,
 		(gpointer) servername);
 
 	return &nattype;
--- a/libpurple/tests/test_xmlnode.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/tests/test_xmlnode.c	Mon Sep 26 14:57:21 2011 +0900
@@ -21,6 +21,101 @@
 }
 END_TEST
 
+#define check_doc_structure(x) { \
+	xmlnode *ping, *child1, *child2; \
+	fail_if(x == NULL, "Failed to parse document"); \
+	ping = xmlnode_get_child(x, "ping"); \
+	fail_if(ping == NULL, "Failed to find 'ping' child"); \
+	child1 = xmlnode_get_child(ping, "child1"); \
+	fail_if(child1 == NULL, "Failed to find 'child1'"); \
+	child2 = xmlnode_get_child(child1, "child2"); \
+	fail_if(child2 == NULL, "Failed to find 'child2'"); \
+	xmlnode_new_child(child2, "a"); \
+	\
+	assert_string_equal("jabber:client", xmlnode_get_namespace(x)); \
+	/* NOTE: xmlnode_get_namespace() returns the namespace of the element, not the
+	 * current default namespace.  See http://www.w3.org/TR/xml-names/#defaulting and
+	 * http://www.w3.org/TR/xml-names/#dt-defaultNS.
+	 */ \
+	assert_string_equal("urn:xmpp:ping", xmlnode_get_namespace(ping)); \
+	assert_string_equal("jabber:client", xmlnode_get_namespace(child1)); \
+	assert_string_equal("urn:xmpp:ping", xmlnode_get_namespace(child2)); \
+	/*
+	 * This fails (well, actually crashes [the ns is NULL]) unless
+	 * xmlnode_new_child() actually sets the element namespace.
+	assert_string_equal("jabber:client", xmlnode_get_namespace(xmlnode_get_child(child2, "a")));
+	 */ \
+	\
+	assert_string_equal("jabber:client", xmlnode_get_default_namespace(x)); \
+	assert_string_equal("jabber:client", xmlnode_get_default_namespace(ping)); \
+	assert_string_equal("jabber:client", xmlnode_get_default_namespace(child1)); \
+	assert_string_equal("jabber:client", xmlnode_get_default_namespace(child2)); \
+}
+
+START_TEST(test_xmlnode_prefixes)
+{
+	const char *xml_doc =
+		"<iq type='get' xmlns='jabber:client' xmlns:ping='urn:xmpp:ping'>"
+			"<ping:ping>"
+				"<child1>"
+					"<ping:child2></ping:child2>" /* xmlns='jabber:child' */
+				"</child1>"
+			"</ping:ping>"
+		"</iq>";
+	char *str;
+	xmlnode *xml, *reparsed;
+
+	xml = xmlnode_from_str(xml_doc, -1);
+	check_doc_structure(xml);
+
+	/* Check that xmlnode_from_str(xmlnode_to_str(xml, NULL), -1) is idempotent. */
+	str = xmlnode_to_str(xml, NULL);
+	fail_if(str == NULL, "Failed to serialize XMLnode");
+	reparsed = xmlnode_from_str(str, -1);
+	fail_if(reparsed == NULL, "Failed to reparse xml document");
+	check_doc_structure(reparsed);
+
+	g_free(str);
+	xmlnode_free(xml);
+	xmlnode_free(reparsed);
+}
+END_TEST
+
+
+START_TEST(test_strip_prefixes)
+{
+	const char *xml_doc = "<message xmlns='jabber:client' from='user@gmail.com/resource' to='another_user@darkrain42.org' type='chat' id='purple'>"
+		"<cha:active xmlns:cha='http://jabber.org/protocol/chatstates'/>"
+		"<body>xvlc xvlc</body>"
+		"<im:html xmlns:im='http://jabber.org/protocol/xhtml-im'>"
+			"<xht:body xmlns:xht='http://www.w3.org/1999/xhtml'>"
+				"<xht:p>xvlc <xht:span style='font-weight: bold;'>xvlc</xht:span></xht:p>"
+			"</xht:body>"
+		"</im:html>"
+	"</message>";
+	const char *out = "<message xmlns='jabber:client' from='user@gmail.com/resource' to='another_user@darkrain42.org' type='chat' id='purple'>"
+		"<active xmlns:cha='http://jabber.org/protocol/chatstates' xmlns='http://jabber.org/protocol/chatstates'/>"
+		"<body>xvlc xvlc</body>"
+		"<html xmlns:im='http://jabber.org/protocol/xhtml-im' xmlns='http://jabber.org/protocol/xhtml-im'>"
+			"<body xmlns:xht='http://www.w3.org/1999/xhtml' xmlns='http://www.w3.org/1999/xhtml'>"
+				"<p>xvlc <span style='font-weight: bold;'>xvlc</span></p>"
+			"</body>"
+		"</html>"
+	"</message>";
+	char *str;
+	xmlnode *xml;
+
+	xml = xmlnode_from_str(xml_doc, -1);
+	fail_if(xml == NULL, "Failed to parse XML");
+
+	xmlnode_strip_prefixes(xml);
+	str = xmlnode_to_str(xml, NULL);
+	assert_string_equal_free(out, str);
+
+	xmlnode_free(xml);
+}
+END_TEST
+
 Suite *
 xmlnode_suite(void)
 {
@@ -28,6 +123,9 @@
 
 	TCase *tc = tcase_create("xmlnode");
 	tcase_add_test(tc, test_xmlnode_billion_laughs_attack);
+	tcase_add_test(tc, test_xmlnode_prefixes);
+	tcase_add_test(tc, test_strip_prefixes);
+
 	suite_add_tcase(s, tc);
 
 	return s;
--- a/libpurple/theme-loader.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/theme-loader.c	Mon Sep 26 14:57:21 2011 +0900
@@ -24,7 +24,7 @@
 #include "theme-loader.h"
 
 #define PURPLE_THEME_LOADER_GET_PRIVATE(PurpleThemeLoader) \
-	((PurpleThemeLoaderPrivate *) ((PurpleThemeLoader)->priv))
+	(G_TYPE_INSTANCE_GET_PRIVATE((PurpleThemeLoader), PURPLE_TYPE_THEME_LOADER, PurpleThemeLoaderPrivate))
 
 void purple_theme_loader_set_type_string(PurpleThemeLoader *loader, const gchar *type);
 
@@ -86,21 +86,12 @@
 }
 
 static void
-purple_theme_loader_init(GTypeInstance *instance,
-			gpointer klass)
-{
-	PurpleThemeLoader *loader = PURPLE_THEME_LOADER(instance);
-	loader->priv = g_new0(PurpleThemeLoaderPrivate, 1);
-}
-
-static void
 purple_theme_loader_finalize(GObject *obj)
 {
 	PurpleThemeLoader *loader = PURPLE_THEME_LOADER(obj);
 	PurpleThemeLoaderPrivate *priv = PURPLE_THEME_LOADER_GET_PRIVATE(loader);
 
 	g_free(priv->type);
-	g_free(priv);
 
 	parent_class->finalize(obj);
 }
@@ -113,6 +104,8 @@
 
 	parent_class = g_type_class_peek_parent(klass);
 
+	g_type_class_add_private(klass, sizeof(PurpleThemeLoaderPrivate));
+
 	obj_class->get_property = purple_theme_loader_get_property;
 	obj_class->set_property = purple_theme_loader_set_property;
 	obj_class->finalize = purple_theme_loader_finalize;
@@ -139,7 +132,7 @@
 			NULL, /* class_data */
 			sizeof(PurpleThemeLoader),
 			0, /* n_preallocs */
-			purple_theme_loader_init, /* instance_init */
+			NULL, /* instance_init */
 			NULL, /* value table */
 		};
 		type = g_type_register_static(G_TYPE_OBJECT,
--- a/libpurple/theme-loader.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/theme-loader.h	Mon Sep 26 14:57:21 2011 +0900
@@ -50,7 +50,6 @@
 struct _PurpleThemeLoader
 {
 	GObject parent;
-	gpointer priv;
 };
 
 struct _PurpleThemeLoaderClass
--- a/libpurple/theme.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/theme.c	Mon Sep 26 14:57:21 2011 +0900
@@ -25,7 +25,7 @@
 #include "util.h"
 
 #define PURPLE_THEME_GET_PRIVATE(PurpleTheme) \
-	((PurpleThemePrivate *) ((PurpleTheme)->priv))
+	(G_TYPE_INSTANCE_GET_PRIVATE((PurpleTheme), PURPLE_TYPE_THEME, PurpleThemePrivate))
 
 void purple_theme_set_type_string(PurpleTheme *theme, const gchar *type);
 
@@ -129,14 +129,6 @@
 }
 
 static void
-purple_theme_init(GTypeInstance *instance,
-		gpointer klass)
-{
-	PurpleTheme *theme = PURPLE_THEME(instance);
-	theme->priv = g_new0(PurpleThemePrivate, 1);
-}
-
-static void
 purple_theme_finalize(GObject *obj)
 {
 	PurpleTheme *theme = PURPLE_THEME(obj);
@@ -160,6 +152,8 @@
 
 	parent_class = g_type_class_peek_parent(klass);
 
+	g_type_class_add_private(klass, sizeof(PurpleThemePrivate));
+
 	obj_class->get_property = purple_theme_get_property;
 	obj_class->set_property = purple_theme_set_property;
 	obj_class->finalize = purple_theme_finalize;
@@ -222,7 +216,7 @@
 			NULL, /* class_data */
 			sizeof(PurpleTheme),
 			0, /* n_preallocs */
-			purple_theme_init, /* instance_init */
+			NULL, /* instance_init */
 			NULL, /* value table */
 		};
 		type = g_type_register_static (G_TYPE_OBJECT,
--- a/libpurple/theme.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/theme.h	Mon Sep 26 14:57:21 2011 +0900
@@ -49,7 +49,6 @@
 struct _PurpleTheme
 {
 	GObject parent;
-	gpointer priv;
 };
 
 struct _PurpleThemeClass
--- a/libpurple/upnp.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/upnp.c	Mon Sep 26 14:57:21 2011 +0900
@@ -464,7 +464,7 @@
 	purple_timeout_remove(dd->tima);
 	dd->tima = 0;
 
-	purple_util_fetch_url_request_len(NULL, descriptionURL, TRUE, NULL, TRUE, httpRequest,
+	purple_util_fetch_url_request(NULL, descriptionURL, TRUE, NULL, TRUE, httpRequest,
 			TRUE, MAX_UPNP_DOWNLOAD, upnp_parse_description_cb, dd);
 
 	g_free(httpRequest);
@@ -730,7 +730,7 @@
 	g_free(pathOfControl);
 	g_free(soapMessage);
 
-	gfud = purple_util_fetch_url_request_len(NULL, control_info.control_url, FALSE, NULL, TRUE,
+	gfud = purple_util_fetch_url_request(NULL, control_info.control_url, FALSE, NULL, TRUE,
 				totalSendMessage, TRUE, MAX_UPNP_DOWNLOAD, cb, cb_data);
 
 	g_free(totalSendMessage);
--- a/libpurple/util.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/util.c	Mon Sep 26 14:57:21 2011 +0900
@@ -73,6 +73,14 @@
 	PurpleAccount *account;
 };
 
+struct _PurpleMenuAction
+{
+	char *label;
+	PurpleCallback callback;
+	gpointer data;
+	GList *children;
+};
+
 static char *custom_user_dir = NULL;
 static char *user_dir = NULL;
 
@@ -98,6 +106,62 @@
 	g_free(act);
 }
 
+char * purple_menu_action_get_label(const PurpleMenuAction *act)
+{
+	g_return_val_if_fail(act != NULL, NULL);
+
+	return act->label;
+}
+
+PurpleCallback purple_menu_action_get_callback(const PurpleMenuAction *act)
+{
+	g_return_val_if_fail(act != NULL, NULL);
+
+	return act->callback;
+}
+
+gpointer purple_menu_action_get_data(const PurpleMenuAction *act)
+{
+	g_return_val_if_fail(act != NULL, NULL);
+
+	return act->data;
+}
+
+GList* purple_menu_action_get_children(const PurpleMenuAction *act)
+{
+	g_return_val_if_fail(act != NULL, NULL);
+
+	return act->children;
+}
+
+void purple_menu_action_set_label(PurpleMenuAction *act, char *label)
+{
+	g_return_if_fail(act != NULL);
+
+	act-> label = label;
+}
+
+void purple_menu_action_set_callback(PurpleMenuAction *act, PurpleCallback callback)
+{
+	g_return_if_fail(act != NULL);
+
+	act->callback = callback;
+}
+
+void purple_menu_action_set_data(PurpleMenuAction *act, gpointer data)
+{
+	g_return_if_fail(act != NULL);
+
+	act->data = data;
+}
+
+void purple_menu_action_set_children(PurpleMenuAction *act, GList *children)
+{
+	g_return_if_fail(act != NULL);
+
+	act->children = children;
+}
+
 void
 purple_util_init(void)
 {
@@ -4131,19 +4195,7 @@
 }
 
 PurpleUtilFetchUrlData *
-purple_util_fetch_url_request(const char *url, gboolean full,
-		const char *user_agent, gboolean http11,
-		const char *request, gboolean include_headers,
-		PurpleUtilFetchUrlCallback callback, void *user_data)
-{
-	return purple_util_fetch_url_request_len(NULL, url, full,
-					     user_agent, http11,
-					     request, include_headers, -1,
-					     callback, user_data);
-}
-
-PurpleUtilFetchUrlData *
-purple_util_fetch_url_request_len(PurpleAccount *account,
+purple_util_fetch_url_request(PurpleAccount *account,
 		const char *url, gboolean full,	const char *user_agent, gboolean http11,
 		const char *request, gboolean include_headers, gssize max_len,
 		PurpleUtilFetchUrlCallback callback, void *user_data)
@@ -4430,11 +4482,10 @@
 	return (double_colon && chunks < 8) || (!double_colon && chunks == 8);
 }
 
-/* TODO 3.0.0: Add ipv6 check, too */
 gboolean
 purple_ip_address_is_valid(const char *ip)
 {
-	return purple_ipv4_address_is_valid(ip);
+	return (purple_ipv4_address_is_valid(ip) || purple_ipv6_address_is_valid(ip));
 }
 
 /* Stolen from gnome_uri_list_extract_uris */
--- a/libpurple/util.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/util.h	Mon Sep 26 14:57:21 2011 +0900
@@ -36,9 +36,20 @@
   * the request.
   */
 typedef struct _PurpleUtilFetchUrlData PurpleUtilFetchUrlData;
-/** @copydoc _PurpleMenuAction */
+
+/**
+ * A generic structure that contains information about an "action."  One
+ * place this is is used is by PRPLs to tell the core the list of available
+ * right-click actions for a buddy list row.
+ */
 typedef struct _PurpleMenuAction PurpleMenuAction;
-/** @copydoc _PurpleKeyValuePair */
+
+/**
+ * A key-value pair.
+ *
+ * This is used by, among other things, purple_gtk_combo* functions to pass in a
+ * list of key-value pairs so it can display a user-friendly value.
+ */
 typedef struct _PurpleKeyValuePair PurpleKeyValuePair;
 
 #include "account.h"
@@ -51,22 +62,8 @@
 extern "C" {
 #endif
 
-struct _PurpleMenuAction
-{
-	char *label;
-	PurpleCallback callback;
-	gpointer data;
-	GList *children;
-};
-
 typedef char *(*PurpleInfoFieldFormatCallback)(const char *field, size_t len);
 
-/**
- * A key-value pair.
- *
- * This is used by, among other things, purple_gtk_combo* functions to pass in a
- * list of key-value pairs so it can display a user-friendly value.
- */
 struct _PurpleKeyValuePair
 {
 	gchar *key;
@@ -96,12 +93,79 @@
 void purple_menu_action_free(PurpleMenuAction *act);
 
 /**
+ * Returns the label of the PurpleMenuAction.
+ *
+ * @param act	The PurpleMenuAction.
+ *
+ * @return The label string.
+ */
+char * purple_menu_action_get_label(const PurpleMenuAction *act);
+
+/**
+ * Returns the callback of the PurpleMenuAction.
+ *
+ * @param act	The PurpleMenuAction.
+ *
+ * @return The callback function.
+ */
+PurpleCallback purple_menu_action_get_callback(const PurpleMenuAction *act);
+
+/**
+ * Returns the data stored in the PurpleMenuAction.
+ *
+ * @param act	The PurpleMenuAction.
+ *
+ * @return The data.
+ */
+gpointer purple_menu_action_get_data(const PurpleMenuAction *act);
+
+/**
+ * Returns the children of the PurpleMenuAction.
+ *
+ * @param act	The PurpleMenuAction.
+ *
+ * @return The  GList of children.
+ */
+GList* purple_menu_action_get_children(const PurpleMenuAction *act);
+
+/**
+ * Set the label to the PurpleMenuAction.
+ *
+ * @param act   The menu action.
+ * @param label The label for the menu action.
+ */
+void purple_menu_action_set_label(PurpleMenuAction *act, char *label);
+
+/**
+ * Set the callback that will be used by the PurpleMenuAction.
+ *
+ * @param act        The menu action.
+ * @param callback   The callback.
+ */
+void purple_menu_action_set_callback(PurpleMenuAction *act, PurpleCallback callback);
+
+/**
+ * Set the label to the PurpleMenuAction.
+ *
+ * @param act   The menu action.
+ * @param data  The data used by this PurpleMenuAction
+ */
+void purple_menu_action_set_data(PurpleMenuAction *act, gpointer data);
+
+/**
+ * Set the children of the PurpleMenuAction.
+ *
+ * @param act       The menu action.
+ * @param children  The PurpleMenuAtion children
+ */
+void purple_menu_action_set_children(PurpleMenuAction *act, GList *children);
+
+/**
  * Set the appropriate presence values for the currently playing song.
  *
  * @param title     The title of the song, @c NULL to unset the value.
  * @param artist    The artist of the song, can be @c NULL.
  * @param album     The album of the song, can be @c NULL.
- * @since 2.4.0
  */
 void purple_util_set_current_song(const char *title, const char *artist,
 		const char *album);
@@ -115,7 +179,6 @@
  * @param unused    Currently unused, must be @c NULL.
  *
  * @return   The formatted string. The caller must g_free the returned string.
- * @since 2.4.0
  */
 char * purple_util_format_song_info(const char *title, const char *artist,
 		const char *album, gpointer unused);
@@ -127,15 +190,11 @@
 
 /**
  * Initializes the utility subsystem.
- *
- * @since 2.3.0
  */
 void purple_util_init(void);
 
 /**
  * Uninitializes the util subsystem.
- *
- * @since 2.3.0
  */
 void purple_util_uninit(void);
 
@@ -429,8 +488,6 @@
  * This is exactly the same as g_markup_escape_text(), except that it
  * does not change ' to &apos; because &apos; is not a valid HTML 4 entity,
  * and is displayed literally in IE7.
- *
- * @since 2.6.0
  */
 gchar *purple_markup_escape_text(const gchar *text, gssize length);
 
@@ -531,7 +588,6 @@
  *         this string when finished with it.
  *
  * @see purple_unescape_html()
- * @since 2.7.0
  */
 char *purple_unescape_text(const char *text);
 
@@ -624,8 +680,6 @@
  * @param html  The HTML text.
  *
  * @return  TRUE if the text contains RTL text, FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_markup_is_rtl(const char *html);
 
@@ -826,7 +880,6 @@
  *
  * @return The address family of the socket (AF_INET, AF_INET6, etc) or -1
  *         on error.
- * @since 2.7.0
  */
 int purple_socket_get_family(int fd);
 
@@ -838,7 +891,6 @@
  *
  * @param fd The socket file descriptor
  * @return TRUE if a socket can speak IPv4.
- * @since 2.7.0
  */
 gboolean purple_socket_speaks_ipv4(int fd);
 
@@ -860,8 +912,6 @@
  * @param right A string to compare with left
  *
  * @return @c TRUE if the strings are the same, else @c FALSE.
- *
- * @since 2.6.0
  */
 gboolean purple_strequal(const gchar *left, const gchar *right);
 
@@ -1117,53 +1167,17 @@
  *                   partial URL.
  * @param user_agent The user agent field to use, or NULL.
  * @param http11     TRUE if HTTP/1.1 should be used to download the file.
- * @param cb         The callback function.
- * @param data       The user data to pass to the callback function.
- */
-#define purple_util_fetch_url(url, full, user_agent, http11, cb, data) \
-	purple_util_fetch_url_request(url, full, user_agent, http11, NULL, \
-		FALSE, cb, data);
-
-/**
- * Fetches the data from a URL, and passes it to a callback function.
- *
- * @param url        The URL.
- * @param full       TRUE if this is the full URL, or FALSE if it's a
- *                   partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11     TRUE if HTTP/1.1 should be used to download the file.
  * @param max_len    The maximum number of bytes to retrieve (-1 for unlimited)
  * @param cb         The callback function.
  * @param data       The user data to pass to the callback function.
- * @deprecated       In 3.0.0, we'll rename this to "purple_util_fetch_url" and get rid of the old one
  */
-#define purple_util_fetch_url_len(url, full, user_agent, http11, max_len, cb, data) \
-	purple_util_fetch_url_request_len(NULL, url, full, user_agent, http11, NULL, \
+#define purple_util_fetch_url(url, full, user_agent, http11, max_len, cb, data) \
+	purple_util_fetch_url_request(NULL, url, full, user_agent, http11, NULL, \
 		FALSE, max_len, cb, data);
 
 /**
  * Fetches the data from a URL, and passes it to a callback function.
  *
- * @param url        The URL.
- * @param full       TRUE if this is the full URL, or FALSE if it's a
- *                   partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11     TRUE if HTTP/1.1 should be used to download the file.
- * @param request    A HTTP request to send to the server instead of the
- *                   standard GET
- * @param include_headers
- *                   If TRUE, include the HTTP headers in the response.
- * @param callback   The callback function.
- * @param data       The user data to pass to the callback function.
- */
-PurpleUtilFetchUrlData *purple_util_fetch_url_request(const gchar *url,
-		gboolean full, const gchar *user_agent, gboolean http11,
-		const gchar *request, gboolean include_headers,
-		PurpleUtilFetchUrlCallback callback, gpointer data);
-
-/**
- * Fetches the data from a URL, and passes it to a callback function.
- *
  * @param account    The account for which the request is needed, or NULL.
  * @param url        The URL.
  * @param full       TRUE if this is the full URL, or FALSE if it's a
@@ -1177,9 +1191,8 @@
  * @param max_len    The maximum number of bytes to retrieve (-1 for unlimited)
  * @param callback   The callback function.
  * @param data       The user data to pass to the callback function.
- * @deprecated       In 3.0.0, we'll rename this to "purple_util_fetch_url_request" and get rid of the old one
  */
-PurpleUtilFetchUrlData *purple_util_fetch_url_request_len(
+PurpleUtilFetchUrlData *purple_util_fetch_url_request(
 		PurpleAccount *account, const gchar *url,
 		gboolean full, const gchar *user_agent, gboolean http11,
 		const gchar *request, gboolean include_headers, gssize max_len,
@@ -1225,14 +1238,15 @@
 gboolean purple_email_is_valid(const char *address);
 
 /**
- * Checks if the given IP address is a syntactically valid IPv4 address.
+ * Checks if the given IP address is a syntactically valid IPv4 or
+ * IPv6 address.
+ * If you specifically want to check for an IPv4 address use
+ * purple_ipv4_address_is_valid(), or for an IPv6 address use
+ * purple_ipv6_address_is_valid().
  *
  * @param ip The IP address to validate.
  *
  * @return True if the IP address is syntactically correct.
- * @deprecated This function will be replaced with one that validates
- *             as either IPv4 or IPv6 in 3.0.0. If you don't want this,
- *             behavior, use one of the more specific functions.
  */
 gboolean purple_ip_address_is_valid(const char *ip);
 
@@ -1242,7 +1256,6 @@
  * @param ip The IP address to validate.
  *
  * @return True if the IP address is syntactically correct.
- * @since 2.6.0
  */
 gboolean purple_ipv4_address_is_valid(const char *ip);
 
@@ -1252,7 +1265,6 @@
  * @param ip The IP address to validate.
  *
  * @return True if the IP address is syntactically correct.
- * @since 2.6.0
  */
 gboolean purple_ipv6_address_is_valid(const char *ip);
 
@@ -1320,7 +1332,6 @@
  * @param str A valid UTF-8 string.
  *
  * @return A newly allocated UTF-8 string without the unprintable characters.
- * @since 2.6.0
  */
 gchar *purple_utf8_strip_unprintables(const gchar *str);
 
@@ -1332,7 +1343,6 @@
  * @param errnum The error code.
  *
  * @return The UTF-8 error message.
- * @since 2.4.0
  */
 G_CONST_RETURN gchar *purple_gai_strerror(gint errnum);
 
@@ -1446,7 +1456,6 @@
  * Returns a type 4 (random) UUID
  *
  * @return A UUID, caller is responsible for freeing it
- * @since 2.7.0
  */
 gchar *purple_uuid_random(void);
 
--- a/libpurple/version.h.in	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/version.h.in	Mon Sep 26 14:57:21 2011 +0900
@@ -56,8 +56,6 @@
  * The major version of the running libpurple.  Contrast with
  * #PURPLE_MAJOR_VERSION, which expands at compile time to the major version of
  * libpurple being compiled against.
- *
- * @since 2.4.0
  */
 extern const guint purple_major_version;
 
@@ -65,8 +63,6 @@
  * The minor version of the running libpurple.  Contrast with
  * #PURPLE_MINOR_VERSION, which expands at compile time to the minor version of
  * libpurple being compiled against.
- *
- * @since 2.4.0
  */
 extern const guint purple_minor_version;
 
@@ -75,8 +71,6 @@
  * The micro version of the running libpurple.  Contrast with
  * #PURPLE_MICRO_VERSION, which expands at compile time to the micro version of
  * libpurple being compiled against.
- *
- * @since 2.4.0
  */
 extern const guint purple_micro_version;
 
--- a/libpurple/whiteboard.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/whiteboard.c	Mon Sep 26 14:57:21 2011 +0900
@@ -90,6 +90,34 @@
 	g_free(wb);
 }
 
+PurpleAccount *purple_whiteboard_get_account(const PurpleWhiteboard *wb)
+{
+	g_return_val_if_fail(wb != NULL, NULL);
+
+	return wb->account;
+}
+
+const char *purple_whiteboard_get_who(const PurpleWhiteboard *wb)
+{
+	g_return_val_if_fail(wb != NULL, NULL);
+
+	return wb->who;	
+}
+
+void purple_whiteboard_set_state(PurpleWhiteboard *wb, int state)
+{
+	g_return_if_fail(wb != NULL);
+
+	wb->state = state;
+}
+
+int purple_whiteboard_get_state(const PurpleWhiteboard *wb)
+{
+	g_return_val_if_fail(wb != NULL, -1);
+
+	return wb->state;
+}
+
 void purple_whiteboard_start(PurpleWhiteboard *wb)
 {
 	/* Create frontend for whiteboard */
@@ -206,3 +234,44 @@
 		whiteboard_ui_ops->set_brush(wb, size, color);
 }
 
+GList *purple_whiteboard_get_draw_list(const PurpleWhiteboard *wb)
+{
+	g_return_val_if_fail(wb != NULL, NULL);
+
+	return wb->draw_list;
+}
+
+void purple_whiteboard_set_draw_list(PurpleWhiteboard *wb, GList* draw_list)
+{
+	g_return_if_fail(wb != NULL);
+
+	wb->draw_list = draw_list;
+}
+
+void purple_whiteboard_set_protocol_data(PurpleWhiteboard *wb, gpointer proto_data)
+{
+	g_return_if_fail(wb != NULL);
+
+	wb->proto_data = proto_data;
+}
+
+gpointer purple_whiteboard_get_protocol_data(const PurpleWhiteboard *wb)
+{
+	g_return_val_if_fail(wb != NULL, NULL);
+
+	return wb->proto_data;
+}
+
+void purple_whiteboard_set_ui_data(PurpleWhiteboard *wb, gpointer ui_data)
+{
+	g_return_if_fail(wb != NULL);
+
+	wb->ui_data = ui_data;
+}
+
+gpointer purple_whiteboard_get_ui_data(const PurpleWhiteboard *wb)
+{
+	g_return_val_if_fail(wb != NULL, NULL);
+
+	return wb->ui_data;
+}
--- a/libpurple/whiteboard.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/whiteboard.h	Mon Sep 26 14:57:21 2011 +0900
@@ -135,6 +135,41 @@
 void purple_whiteboard_destroy(PurpleWhiteboard *wb);
 
 /**
+ * Returns the whiteboard's account.
+ *
+ * @param wb		The whiteboard.
+ *
+ * @return The whiteboard's account.
+ */
+PurpleAccount *purple_whiteboard_get_account(const PurpleWhiteboard *wb);
+
+/**
+ * Return who you're drawing with.
+ *
+ * @param wb		The whiteboard
+ *
+ * @return Who you're drawing with.
+ */
+const char *purple_whiteboard_get_who(const PurpleWhiteboard *wb);
+
+/**
+ * Set the state of the whiteboard.
+ *
+ * @param wb		The whiteboard.
+ * @param state		The state
+ */
+void purple_whiteboard_set_state(PurpleWhiteboard *wb, int state);
+
+/**
+ * Return the state of the whiteboard.
+ *
+ * @param wb		The whiteboard.
+ *
+ * @return The state of the whiteboard.
+ */
+int purple_whiteboard_get_state(const PurpleWhiteboard *wb);
+
+/**
  * Starts a whiteboard
  *
  * @param wb The whiteboard.
@@ -253,6 +288,59 @@
  */
 void purple_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color);
 
+/**
+ * Return the drawing list.
+ *
+ * @param wb			The whiteboard.
+ *
+ * @return The drawing list
+ */
+GList *purple_whiteboard_get_draw_list(const PurpleWhiteboard *wb);
+
+/**
+ * Set the drawing list.
+ *
+ * @param wb			The whiteboard
+ * @param draw_list		The drawing list.
+ */
+void purple_whiteboard_set_draw_list(PurpleWhiteboard *wb, GList* draw_list);
+
+/**
+ * Sets the protocol data for a whiteboard.
+ *
+ * @param wb			The whiteboard.
+ * @param proto_data	The protocol data to set for the whiteboard.
+ */
+void purple_whiteboard_set_protocol_data(PurpleWhiteboard *wb, gpointer proto_data);
+
+/**
+ * Gets the protocol data for a whiteboard.
+ *
+ * @param wb			The whiteboard.
+ *
+ * @return The protocol data for the whiteboard.
+ */
+gpointer purple_whiteboard_get_protocol_data(const PurpleWhiteboard *wb);
+
+/**
+ * Set the UI data associated with this whiteboard.
+ *
+ * @param wb			The whiteboard.
+ * @param ui_data		A pointer to associate with this whiteboard.
+ */
+void purple_whiteboard_set_ui_data(PurpleWhiteboard *wb, gpointer ui_data);
+
+/**
+ * Get the UI data associated with this whiteboard.
+ *
+ * @param wb			The whiteboard..
+ *
+ * @return The UI data associated with this whiteboard.  This is a
+ *         convenience field provided to the UIs--it is not
+ *         used by the libpurple core.
+ */
+gpointer purple_whiteboard_get_ui_data(const PurpleWhiteboard *wb);
+
 /*@}*/
 
 #ifdef __cplusplus
--- a/libpurple/win32/libc_interface.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/win32/libc_interface.c	Mon Sep 26 14:57:21 2011 +0900
@@ -1041,15 +1041,3 @@
 	purple_debug_warning("wpurple", "could not find a match for Windows timezone \"%s\"\n", tzname);
 	return "";
 }
-
-int wpurple_g_access (const gchar *filename, int mode);
-/**
- * @deprecated - remove for 3.0.0
- */
-int
-wpurple_g_access (const gchar *filename, int mode)
-{
-	return g_access(filename, mode);
-}
-
-
--- a/libpurple/xmlnode.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/xmlnode.c	Mon Sep 26 14:57:21 2011 +0900
@@ -78,6 +78,19 @@
 	node = new_node(name, XMLNODE_TYPE_TAG);
 
 	xmlnode_insert_child(parent, node);
+#if 0
+	/* This would give xmlnodes more appropriate namespacing
+	 * when creating them.  Otherwise, unless an explicit namespace
+	 * is set, xmlnode_get_namespace() will return NULL, when
+	 * there may be a default namespace.
+	 *
+	 * I'm unconvinced that it's useful, and concerned it may break things.
+	 *
+	 * _insert_child would need the same thing, probably (assuming
+	 * xmlns->node == NULL)
+	 */
+	xmlnode_set_namespace(node, xmlnode_get_default_namespace(node))
+#endif
 
 	return node;
 }
@@ -191,18 +204,6 @@
 }
 
 void
-xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value)
-{
-	xmlnode_set_attrib_full(node, attr, xmlns, NULL, value);
-}
-
-void
-xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value)
-{
-	xmlnode_set_attrib_full(node, attr, NULL, prefix, value);
-}
-
-void
 xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns, const char *prefix, const char *value)
 {
 	xmlnode *attrib_node;
@@ -261,19 +262,53 @@
 
 void xmlnode_set_namespace(xmlnode *node, const char *xmlns)
 {
+	char *tmp;
 	g_return_if_fail(node != NULL);
 
-	g_free(node->xmlns);
+	tmp = node->xmlns;
 	node->xmlns = g_strdup(xmlns);
+
+	if (node->namespace_map) {
+		g_hash_table_insert(node->namespace_map,
+			g_strdup(""), g_strdup(xmlns));
+	}
+
+	g_free(tmp);
 }
 
-const char *xmlnode_get_namespace(xmlnode *node)
+const char *xmlnode_get_namespace(const xmlnode *node)
 {
 	g_return_val_if_fail(node != NULL, NULL);
 
 	return node->xmlns;
 }
 
+const char *xmlnode_get_default_namespace(const xmlnode *node)
+{
+	const xmlnode *current_node;
+	const char *ns = NULL;
+
+	g_return_val_if_fail(node != NULL, NULL);
+
+	current_node = node;
+	while (current_node) {
+		/* If this node does *not* have a prefix, node->xmlns is the default
+		 * namespace.  Otherwise, it's the prefix namespace.
+		 */
+		if (!current_node->prefix && current_node->xmlns) {
+			return current_node->xmlns;
+		} else if (current_node->namespace_map) {
+			ns = g_hash_table_lookup(current_node->namespace_map, "");
+			if (ns && *ns)
+				return ns;
+		}
+
+		current_node = current_node->parent;
+	}
+
+	return ns;
+}
+
 void xmlnode_set_prefix(xmlnode *node, const char *prefix)
 {
 	g_return_if_fail(node != NULL);
@@ -288,6 +323,53 @@
 	return node->prefix;
 }
 
+const char *xmlnode_get_prefix_namespace(const xmlnode *node, const char *prefix)
+{
+	const xmlnode *current_node;
+
+	g_return_val_if_fail(node != NULL, NULL);
+	g_return_val_if_fail(prefix != NULL, xmlnode_get_default_namespace(node));
+
+	current_node = node;
+	while (current_node) {
+		if (current_node->prefix && g_str_equal(prefix, current_node->prefix) &&
+				current_node->xmlns) {
+			return current_node->xmlns;
+		} else if (current_node->namespace_map) {
+			const char *ns = g_hash_table_lookup(current_node->namespace_map, prefix);
+			if (ns && *ns) {
+				return ns;
+			}
+		}
+
+		current_node = current_node->parent;
+	}
+
+	return NULL;
+}
+
+void xmlnode_strip_prefixes(xmlnode *node)
+{
+	xmlnode *child;
+	const char *prefix;
+
+	g_return_if_fail(node != NULL);
+
+	for (child = node->child; child; child = child->next) {
+		if (child->type == XMLNODE_TYPE_TAG)
+			xmlnode_strip_prefixes(child);
+	}
+
+	prefix = xmlnode_get_prefix(node);
+	if (prefix) {
+		const char *ns = xmlnode_get_prefix_namespace(node, prefix);
+		xmlnode_set_namespace(node, ns);
+		xmlnode_set_prefix(node, NULL);
+	} else {
+		xmlnode_set_namespace(node, xmlnode_get_default_namespace(node));
+	}
+}
+
 xmlnode *xmlnode_get_parent(const xmlnode *child)
 {
 	g_return_val_if_fail(child != NULL, NULL);
@@ -455,12 +537,22 @@
 	if (node->namespace_map) {
 		g_hash_table_foreach(node->namespace_map,
 			(GHFunc)xmlnode_to_str_foreach_append_ns, text);
-	} else if (node->xmlns) {
-		if(!node->parent || !purple_strequal(node->xmlns, node->parent->xmlns))
+	} else {
+		/* Figure out if this node has a different default namespace from parent */
+		const char *xmlns = NULL;
+		const char *parent_xmlns = NULL;
+		if (!prefix)
+			xmlns = node->xmlns;
+
+		if (!xmlns)
+			xmlns = xmlnode_get_default_namespace(node);
+		if (node->parent)
+			parent_xmlns = xmlnode_get_default_namespace(node->parent);
+		if (!purple_strequal(xmlns, parent_xmlns))
 		{
-			char *xmlns = g_markup_escape_text(node->xmlns, -1);
-			g_string_append_printf(text, " xmlns='%s'", xmlns);
-			g_free(xmlns);
+			char *escaped_xmlns = g_markup_escape_text(xmlns, -1);
+			g_string_append_printf(text, " xmlns='%s'", escaped_xmlns);
+			g_free(escaped_xmlns);
 		}
 	}
 	for(c = node->child; c; c = c->next)
--- a/libpurple/xmlnode.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/libpurple/xmlnode.h	Mon Sep 26 14:57:21 2011 +0900
@@ -157,42 +157,14 @@
  */
 void xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_XMLNODE_C_)
-/**
- * Sets a prefixed attribute for a node
- *
- * @param node   The node to set an attribute for.
- * @param attr   The name of the attribute to set
- * @param prefix The prefix of the attribute to ste
- * @param value  The value of the attribute
- *
- * @deprecated Use xmlnode_set_attrib_full instead.
- */
-void xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value);
-
-/**
- * Sets a namespaced attribute for a node
- *
- * @param node  The node to set an attribute for.
- * @param attr  The name of the attribute to set
- * @param xmlns The namespace of the attribute to ste
- * @param value The value of the attribute
- *
- * @deprecated Use xmlnode_set_attrib_full instead.
- */
-void xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value);
-#endif /* PURPLE_DISABLE_DEPRECATED */
-
 /**
  * Sets a namespaced attribute for a node
  *
  * @param node   The node to set an attribute for.
  * @param attr   The name of the attribute to set
- * @param xmlns  The namespace of the attribute to ste
- * @param prefix The prefix of the attribute to ste
+ * @param xmlns  The namespace of the attribute to set
+ * @param prefix The prefix of the attribute to set
  * @param value  The value of the attribute
- *
- * @since 2.6.0
  */
 void xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns,
 	const char *prefix, const char *value);
@@ -249,7 +221,37 @@
  * @param node The node to get the namepsace from
  * @return The namespace of this node
  */
-const char *xmlnode_get_namespace(xmlnode *node);
+const char *xmlnode_get_namespace(const xmlnode *node);
+
+/**
+ * Returns the current default namespace.  The default
+ * namespace is the current namespace which applies to child
+ * elements which are unprefixed and which do not contain their
+ * own namespace.
+ *
+ * For example, given:
+ * \verbatim
+ * <iq type='get' xmlns='jabber:client' xmlns:ns1='http://example.org/ns1'>
+ *     <ns1:element><child1/></ns1:element>
+ * </iq>
+ * \endverbatim
+ *
+ * The default namespace of all nodes (including 'child1') is "jabber:client",
+ * though the namespace for 'element' is "http://example.org/ns1".
+ *
+ * @param node The node for which to return the default namespace
+ * @return The default namespace of this node
+ */
+const char *xmlnode_get_default_namespace(const xmlnode *node);
+
+/**
+ * Returns the defined namespace for a prefix.
+ *
+ * @param node The node from which to start the search.
+ * @param prefix The prefix for which to return the associated namespace.
+ * @return The namespace for this prefix.
+ */
+const char *xmlnode_get_prefix_namespace(const xmlnode *node, const char *prefix);
 
 /**
  * Sets the prefix of a node
@@ -268,13 +270,24 @@
 const char *xmlnode_get_prefix(const xmlnode *node);
 
 /**
+ * Remove all element prefixes from an xmlnode tree.  The prefix's
+ * namespace is transformed into the default namespace for an element.
+ *
+ * Note that this will not necessarily remove all prefixes in use
+ * (prefixed attributes may still exist), and that this usage may
+ * break some applications (SOAP / XPath apparently often rely on
+ * the prefixes having the same name.
+ *
+ * @param node The node from which to strip prefixes
+ */
+void xmlnode_strip_prefixes(xmlnode *node);
+
+/**
  * Gets the parent node.
  *
  * @param child The child node.
  *
  * @return The parent or NULL.
- *
- * @since 2.6.0
  */
 xmlnode *xmlnode_get_parent(const xmlnode *child);
 
@@ -343,8 +356,6 @@
  * 			the category for debugging.
  *
  * @return The new node or NULL if an error occurred.
- *
- * @since 2.6.0
  */
 xmlnode *xmlnode_from_file(const char *dir, const char *filename,
 			   const char *description, const char *process);
@@ -354,3 +365,4 @@
 #endif
 
 #endif /* _PURPLE_XMLNODE_H_ */
+
--- a/pidgin/Makefile.am	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/Makefile.am	Mon Sep 26 14:57:21 2011 +0900
@@ -48,11 +48,12 @@
 	gtkcertmgr.c \
 	gtkconn.c \
 	gtkconv.c \
+	gtkconv-theme.c \
+	gtkconv-theme-loader.c \
 	gtkdebug.c \
 	gtkdialogs.c \
 	gtkdnd-hints.c \
 	gtkdocklet.c \
-	gtkdocklet-gtk.c \
 	gtkeventloop.c \
 	gtkft.c \
 	gtkicon-theme.c \
@@ -84,9 +85,11 @@
 	gtkstatusbox.c \
 	gtkthemes.c \
 	gtkutils.c \
+	gtkwebview.c \
 	gtkwhiteboard.c \
 	minidialog.c \
-	pidgintooltip.c
+	pidgintooltip.c \
+	smileyparser.c
 
 pidgin_headers = \
 	gtkaccount.h \
@@ -98,6 +101,8 @@
 	gtkconn.h \
 	gtkconv.h \
 	gtkconvwin.h \
+	gtkconv-theme.h \
+	gtkconv-theme-loader.h \
 	gtkdebug.h \
 	gtkdialogs.h \
 	gtkdnd-hints.h \
@@ -134,10 +139,12 @@
 	pidginstock.h \
 	gtkthemes.h \
 	gtkutils.h \
+	gtkwebview.h \
 	gtkwhiteboard.h \
 	minidialog.h \
 	pidgintooltip.h \
-	pidgin.h
+	pidgin.h \
+	smileyparser.h
 
 pidginincludedir=$(includedir)/pidgin
 pidgininclude_HEADERS = \
@@ -156,6 +163,7 @@
 	$(INTLLIBS) \
 	$(GTKSPELL_LIBS) \
 	$(LIBXML_LIBS) \
+	$(WEBKIT_LIBS) \
 	$(GTK_LIBS) \
 	$(top_builddir)/libpurple/libpurple.la
 
@@ -179,5 +187,7 @@
 	$(DBUS_CFLAGS) \
 	$(GTKSPELL_CFLAGS) \
 	$(LIBXML_CFLAGS) \
+	$(WEBKIT_CFLAGS) \
 	$(INTGG_CFLAGS)
 endif  # ENABLE_GTK
+
--- a/pidgin/Makefile.mingw	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/Makefile.mingw	Mon Sep 26 14:57:21 2011 +0900
@@ -58,6 +58,8 @@
 			gtkcertmgr.c \
 			gtkconn.c \
 			gtkconv.c \
+			gtkconv-theme.c \
+			gtkconv-theme-loader.c \
 			gtkdebug.c \
 			gtkdialogs.c \
 			gtkdnd-hints.c \
@@ -91,12 +93,12 @@
 			gtkstatusbox.c \
 			gtkthemes.c \
 			gtkutils.c \
+			gtkwebview.c \
 			gtkwhiteboard.c \
 			minidialog.c \
 			pidginstock.c \
 			pidgintooltip.c \
-			win32/MinimizeToTray.c \
-			win32/gtkdocklet-win32.c \
+			smileyparser.c \
 			win32/gtkwin32dep.c \
 			win32/untar.c \
 			win32/wspell.c
--- a/pidgin/gtkblist-theme.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkblist-theme.c	Mon Sep 26 14:57:21 2011 +0900
@@ -24,7 +24,7 @@
 #include "gtkblist-theme.h"
 
 #define PIDGIN_BLIST_THEME_GET_PRIVATE(Gobject) \
-	((PidginBlistThemePrivate *) ((PIDGIN_BLIST_THEME(Gobject))->priv))
+	(G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PIDGIN_TYPE_BLIST_THEME, PidginBlistThemePrivate))
 
 /******************************************************************************
  * Structs
@@ -189,13 +189,6 @@
  *****************************************************************************/
 
 static void
-pidgin_blist_theme_init(GTypeInstance *instance,
-		gpointer klass)
-{
-	(PIDGIN_BLIST_THEME(instance))->priv = g_new0(PidginBlistThemePrivate, 1);
-}
-
-static void
 pidgin_blist_theme_get_property(GObject *obj, guint param_id, GValue *value,
 		GParamSpec *psec)
 {
@@ -349,8 +342,6 @@
 	pidgin_theme_font_free(priv->message_nick_said);
 	pidgin_theme_font_free(priv->status);
 
-	g_free(priv);
-
 	parent_class->finalize (obj);
 }
 
@@ -362,6 +353,8 @@
 
 	parent_class = g_type_class_peek_parent (klass);
 
+	g_type_class_add_private(klass, sizeof(PidginBlistThemePrivate));
+
 	obj_class->get_property = pidgin_blist_theme_get_property;
 	obj_class->set_property = pidgin_blist_theme_set_property;
 	obj_class->finalize = pidgin_blist_theme_finalize;
@@ -484,7 +477,7 @@
 			NULL, /* class_data */
 			sizeof(PidginBlistTheme),
 			0, /* n_preallocs */
-			pidgin_blist_theme_init, /* instance_init */
+			NULL, /* instance_init */
 			NULL, /* value table */
 		};
 		type = g_type_register_static (PURPLE_TYPE_THEME,
@@ -507,7 +500,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->bgcolor;
 }
@@ -519,7 +512,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), 1.0);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->opacity;
 }
@@ -531,7 +524,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->layout;
 }
@@ -543,7 +536,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->expanded_color;
 }
@@ -555,7 +548,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->expanded;
 }
@@ -567,7 +560,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->collapsed_color;
 }
@@ -579,7 +572,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->collapsed;
 }
@@ -591,7 +584,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->contact_color;
 }
@@ -603,7 +596,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->contact;
 }
@@ -615,7 +608,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->online;
 }
@@ -627,7 +620,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->away;
 }
@@ -639,7 +632,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->offline;
 }
@@ -651,7 +644,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->idle;
 }
@@ -663,7 +656,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->message;
 }
@@ -675,7 +668,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->message_nick_said;
 }
@@ -687,7 +680,7 @@
 
 	g_return_val_if_fail(PIDGIN_IS_BLIST_THEME(theme), NULL);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	return priv->status;
 }
@@ -700,7 +693,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	if (priv->bgcolor)
 		gdk_color_free(priv->bgcolor);
@@ -714,7 +707,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme) || opacity < 0.0 || opacity > 1.0);
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	priv->opacity = opacity;
 }
@@ -726,7 +719,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	g_free(priv->layout);
 	priv->layout = g_memdup(layout, sizeof(PidginBlistLayout));
@@ -739,7 +732,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	if (priv->expanded_color)
 		gdk_color_free(priv->expanded_color);
@@ -753,7 +746,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->expanded);
 	priv->expanded = copy_font_and_color(pair);
@@ -766,7 +759,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	if (priv->collapsed_color)
 		gdk_color_free(priv->collapsed_color);
@@ -780,7 +773,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->collapsed);
 	priv->collapsed = copy_font_and_color(pair);
@@ -793,7 +786,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	if (priv->contact_color)
 		gdk_color_free(priv->contact_color);
@@ -807,7 +800,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->contact);
 	priv->contact = copy_font_and_color(pair);
@@ -820,7 +813,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->online);
 	priv->online = copy_font_and_color(pair);
@@ -833,7 +826,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->away);
 	priv->away = copy_font_and_color(pair);
@@ -846,7 +839,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->offline);
 	priv->offline = copy_font_and_color(pair);
@@ -859,7 +852,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->idle);
 	priv->idle = copy_font_and_color(pair);
@@ -872,7 +865,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->message);
 	priv->message = copy_font_and_color(pair);
@@ -885,7 +878,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->message_nick_said);
 	priv->message_nick_said = copy_font_and_color(pair);
@@ -898,7 +891,7 @@
 
 	g_return_if_fail(PIDGIN_IS_BLIST_THEME(theme));
 
-	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme));
+	priv = PIDGIN_BLIST_THEME_GET_PRIVATE(theme);
 
 	pidgin_theme_font_free(priv->status);
 	priv->status = copy_font_and_color(pair);
--- a/pidgin/gtkblist-theme.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkblist-theme.h	Mon Sep 26 14:57:21 2011 +0900
@@ -51,7 +51,6 @@
 struct _PidginBlistTheme
 {
 	PurpleTheme parent;
-	gpointer priv;
 };
 
 struct _PidginBlistThemeClass
--- a/pidgin/gtkblist.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkblist.c	Mon Sep 26 14:57:21 2011 +0900
@@ -3544,7 +3544,7 @@
 	g = purple_request_field_group_new(NULL);
 	f = purple_request_field_list_new("mood", _("Please select your mood from the list"));
 
-	purple_request_field_list_add(f, _("None"), "");
+	purple_request_field_list_add_icon(f, _("None"), NULL, "");
 	if (current_mood == NULL)
 		purple_request_field_list_add_selected(f, _("None"));
 
@@ -4769,8 +4769,6 @@
 	PidginBuddyList *gtkblist;
 
 	gtkblist = g_new0(PidginBuddyList, 1);
-	gtkblist->connection_errors = g_hash_table_new_full(g_direct_hash,
-												g_direct_equal, NULL, g_free);
 	gtkblist->priv = g_new0(PidginBuddyListPrivate, 1);
 
 	blist->ui_data = gtkblist;
@@ -5084,7 +5082,6 @@
 generic_error_destroy_cb(GtkObject *dialog,
                          PurpleAccount *account)
 {
-	g_hash_table_remove(gtkblist->connection_errors, account);
 	/* If the error dialog is being destroyed in response to the
 	 * account-error-changed signal, we don't want to clear the current
 	 * error.
@@ -5342,28 +5339,6 @@
 }
 
 
-/**
- * Was used by the connection API to tell the blist if an account has a
- * connection error or no longer has a connection error, but the blist now does
- * this itself with the @ref account-error-changed signal.
- *
- * @param account The account that either has a connection error
- *        or no longer has a connection error.
- * @param message The connection error message, or NULL if this
- *        account is no longer in an error state.
- */
-static void
-pidgin_blist_update_account_error_state(PurpleAccount *account, const char *text)
-{
-	/* connection_errors isn't actually used anywhere; it's just kept in
-	 * sync with reality in case a plugin uses it.
-	 */
-	if (text == NULL)
-		g_hash_table_remove(gtkblist->connection_errors, account);
-	else
-		g_hash_table_insert(gtkblist->connection_errors, account, g_strdup(text));
-}
-
 /* Call appropriate error notification code based on error types */
 static void
 update_account_error_state(PurpleAccount *account,
@@ -5377,12 +5352,6 @@
 	if (old == NULL && new == NULL)
 		return;
 
-	/* For backwards compatibility: */
-	if (new)
-		pidgin_blist_update_account_error_state(account, new->description);
-	else
-		pidgin_blist_update_account_error_state(account, NULL);
-
 	if (new != NULL)
 		pidgin_blist_select_notebook_page(gtkblist);
 
@@ -6890,7 +6859,6 @@
 	if (gtkblist->drag_timeout)
 		g_source_remove(gtkblist->drag_timeout);
 
-	g_hash_table_destroy(gtkblist->connection_errors);
 	gtkblist->refresh_timer = 0;
 	gtkblist->timeout = 0;
 	gtkblist->drag_timeout = 0;
@@ -7046,7 +7014,7 @@
 			purple_blist_add_buddy(b, NULL, g, NULL);
 		}
 
-		purple_account_add_buddy_with_invite(account, b, invite);
+		purple_account_add_buddy(account, b, invite);
 
 		/* Offer to merge people with the same alias. */
 		if (whoalias != NULL && g != NULL)
--- a/pidgin/gtkblist.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkblist.h	Mon Sep 26 14:57:21 2011 +0900
@@ -86,15 +86,6 @@
 	GtkWidget *menutray;            /**< The menu tray widget. */
 	GtkWidget *menutrayicon;        /**< The menu tray icon. */
 
-	/** Caches connection error messages; keys are #PurpleAccount and
-	 *  values are non-@c NULL <tt>const char *</tt>s containing localised
-	 *  error messages.  (If an account does not have an error, it will not
-	 *  appear in the table.)
-	 *  @deprecated in favour of purple_account_get_current_error(), which also
-	 *              gives you the #PurpleConnectionError value.
-	 */
-	GHashTable *connection_errors;
-
 	guint refresh_timer;            /**< The timer for refreshing every 30 seconds */
 
 	guint      timeout;              /**< The timeout for the tooltip. */
@@ -259,8 +250,6 @@
  * Sets the current theme for Pidgin to use
  *
  * @param theme	the new theme to use
- *
- * @since 2.6.0
  */
 void pidgin_blist_set_theme(PidginBlistTheme *theme);
 
@@ -268,8 +257,6 @@
  * Gets Pidgin's current buddy list theme
  *
  * @returns	the current theme
- *
- * @since 2.6.0
  */
 PidginBlistTheme *pidgin_blist_get_theme(void);
 
@@ -391,8 +378,6 @@
  * @param selected  Whether this buddy is selected. If TRUE, the markup will not change the color.
  * @param aliased  TRUE to return the appropriate alias of this buddy, FALSE to return its username and status information
  * @return The markup for this buddy
- *
- * @since 2.1.0
  */
 gchar *pidgin_blist_get_name_markup(PurpleBuddy *buddy, gboolean selected, gboolean aliased);
 
@@ -404,15 +389,11 @@
  *
  * @param node The buddy list node to show a tooltip for
  * @param widget The widget to draw the tooltip on
- *
- * @since 2.1.0
  */
 void pidgin_blist_draw_tooltip(PurpleBlistNode *node, GtkWidget *widget);
 
 /**
  * Destroys the current (if any) Buddy List tooltip
- *
- * @since 2.1.0
  */
 void pidgin_blist_tooltip_destroy(void);
 
--- a/pidgin/gtkcertmgr.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkcertmgr.c	Mon Sep 26 14:57:21 2011 +0900
@@ -333,6 +333,12 @@
 }
 
 static void
+tls_peers_mgmt_activated_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
+{
+	tls_peers_mgmt_info_cb(NULL, NULL);
+}
+
+static void
 tls_peers_mgmt_delete_confirm_cb(gchar *id, gint choice)
 {
 	if (1 == choice) {
@@ -452,6 +458,9 @@
 	g_signal_connect(G_OBJECT(select), "changed",
 			 G_CALLBACK(tls_peers_mgmt_select_chg_cb), NULL);
 
+	g_signal_connect(G_OBJECT(listview), "row-activated",
+			 G_CALLBACK(tls_peers_mgmt_activated_cb), NULL);
+
 	gtk_box_pack_start(GTK_BOX(mgmt_widget), 
 			pidgin_make_scrollable(GTK_WIDGET(listview), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
 			TRUE, TRUE, /* Take up lots of space */
@@ -471,9 +480,8 @@
 	gtk_widget_show(bbox);
 
 	/* Import button */
-	/* TODO: This is the wrong stock button */
 	tpm_dat->importbutton = importbutton =
-		gtk_button_new_from_stock(GTK_STOCK_ADD);
+		gtk_button_new_from_stock(GTK_STOCK_OPEN);
 	gtk_box_pack_start(GTK_BOX(bbox), importbutton, FALSE, FALSE, 0);
 	gtk_widget_show(importbutton);
 	g_signal_connect(G_OBJECT(importbutton), "clicked",
@@ -481,9 +489,8 @@
 
 
 	/* Export button */
-	/* TODO: This is the wrong stock button */
 	tpm_dat->exportbutton = exportbutton =
-		gtk_button_new_from_stock(GTK_STOCK_SAVE);
+		gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
 	gtk_box_pack_start(GTK_BOX(bbox), exportbutton, FALSE, FALSE, 0);
 	gtk_widget_show(exportbutton);
 	g_signal_connect(G_OBJECT(exportbutton), "clicked",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkconv-theme-loader.c	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,280 @@
+/*
+ * PidginConvThemeLoader for Pidgin
+ *
+ * Pidgin 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 "pidgin.h"
+#include "gtkconv-theme-loader.h"
+#include "gtkconv-theme.h"
+
+#include "xmlnode.h"
+#include "debug.h"
+
+/*****************************************************************************
+ * Conversation Theme Builder
+ *****************************************************************************/
+
+static GHashTable *
+read_info_plist(xmlnode *plist)
+{
+	GHashTable *info;
+	xmlnode *key, *value;
+	gboolean fail = FALSE;
+
+	info = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+	for (key = xmlnode_get_child(plist, "dict/key");
+	     key;
+	     key = xmlnode_get_next_twin(key)) {
+		char *keyname;
+		GValue *val;
+
+		;
+		for (value = key->next; value && value->type != XMLNODE_TYPE_TAG; value = value->next)
+			;
+		if (!value) {
+			fail = TRUE;
+			break;
+		}
+
+		val = g_new0(GValue, 1);
+		if (g_str_equal(value->name, "string")) {
+			g_value_init(val, G_TYPE_STRING);
+			g_value_take_string(val, xmlnode_get_data_unescaped(value));
+
+		} else if (g_str_equal(value->name, "true")) {
+			g_value_init(val, G_TYPE_BOOLEAN);
+			g_value_set_boolean(val, TRUE);
+
+		} else if (g_str_equal(value->name, "false")) {
+			g_value_init(val, G_TYPE_BOOLEAN);
+			g_value_set_boolean(val, FALSE);
+
+		} else if (g_str_equal(value->name, "real")) {
+			char *temp = xmlnode_get_data_unescaped(value);
+			g_value_init(val, G_TYPE_FLOAT);
+			g_value_set_float(val, atof(temp));
+			g_free(temp);
+
+		} else if (g_str_equal(value->name, "integer")) {
+			char *temp = xmlnode_get_data_unescaped(value);
+			g_value_init(val, G_TYPE_INT);
+			g_value_set_int(val, atoi(temp));
+			g_free(temp);
+
+		} else {
+			/* NOTE: We don't support array, data, date, or dict as values,
+			   since they don't seem to be needed for styles. */
+			g_free(val);
+			fail = TRUE;
+			break;
+		}
+
+		keyname = xmlnode_get_data_unescaped(key);
+		g_hash_table_insert(info, keyname, val);
+	}
+
+	if (fail) {
+		g_hash_table_destroy(info);
+		info = NULL;
+	}
+
+	return info;
+}
+
+static PurpleTheme *
+pidgin_conv_loader_build(const gchar *dir)
+{
+	PidginConvTheme *theme = NULL;
+	char *contents;
+	xmlnode *plist;
+	GHashTable *info;
+	GValue *val;
+	int MessageViewVersion;
+	const char *CFBundleName;
+	const char *CFBundleIdentifier;
+	GDir *variants;
+	char *variant_dir;
+
+	g_return_val_if_fail(dir != NULL, NULL);
+
+	/* Load Info.plist for theme information */
+	contents = g_build_filename(dir, "Contents", NULL);
+	plist = xmlnode_from_file(contents, "Info.plist", "Info.plist", "gtkconv-theme-loader");
+	g_free(contents);
+	if (plist == NULL) {
+		purple_debug_error("gtkconv-theme-loader",
+		                   "Failed to load Contents/Info.plist in %s\n", dir);
+		return NULL;
+	}
+
+	info = read_info_plist(plist);
+	xmlnode_free(plist);
+	if (info == NULL) {
+		purple_debug_error("gtkconv-theme-loader",
+		                   "Failed to load Contents/Info.plist in %s\n", dir);
+		return NULL;
+	}
+
+	/* Check for required keys: CFBundleName */
+	val = g_hash_table_lookup(info, "CFBundleName");
+	if (!val || !G_VALUE_HOLDS_STRING(val)) {
+		purple_debug_error("gtkconv-theme-loader",
+		                   "%s/Contents/Info.plist missing required string key CFBundleName.\n",
+		                   dir);
+		g_hash_table_destroy(info);
+		return NULL;
+	}
+	CFBundleName = g_value_get_string(val);
+
+	/* Check for required keys: CFBundleIdentifier */
+	val = g_hash_table_lookup(info, "CFBundleIdentifier");
+	if (!val || !G_VALUE_HOLDS_STRING(val)) {
+		purple_debug_error("gtkconv-theme-loader",
+		                   "%s/Contents/Info.plist missing required string key CFBundleIdentifier.\n",
+		                   dir);
+		g_hash_table_destroy(info);
+		return NULL;
+	}
+	CFBundleIdentifier = g_value_get_string(val);
+
+	/* Check for required keys: MessageViewVersion */
+	val = g_hash_table_lookup(info, "MessageViewVersion");
+	if (!val || !G_VALUE_HOLDS_INT(val)) {
+		purple_debug_error("gtkconv-theme-loader",
+		                   "%s/Contents/Info.plist missing required integer key MessageViewVersion.\n",
+		                   dir);
+		g_hash_table_destroy(info);
+		return NULL;
+	}
+
+	MessageViewVersion = g_value_get_int(val);
+	if (MessageViewVersion < 3) {
+		purple_debug_error("gtkconv-theme-loader",
+		                   "%s is a legacy style (version %d) and will not be loaded.\n",
+		                   CFBundleName, MessageViewVersion);
+		g_hash_table_destroy(info);
+		return NULL;
+	}
+
+	theme = g_object_new(PIDGIN_TYPE_CONV_THEME,
+	                     "type", "conversation",
+	                     "name", CFBundleName,
+	                     "directory", dir,
+	                     "info", info, NULL);
+
+	/* Read list of variants */
+	variant_dir = g_build_filename(dir, "Contents", "Resources", "Variants", NULL);
+	variants = g_dir_open(variant_dir, 0, NULL);
+	g_free(variant_dir);
+
+	if (variants) {
+		char *prefname;
+		const char *default_variant = NULL;
+		const char *file;
+
+		/* Make sure prefs exist */
+		prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s",
+		                           CFBundleIdentifier);
+		purple_prefs_add_none(prefname);
+		g_free(prefname);
+
+		/* Try user-set variant */
+		prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s/variant",
+		                           CFBundleIdentifier);
+		if (purple_prefs_exists(prefname))
+			default_variant = purple_prefs_get_string(prefname);
+		g_free(prefname);
+
+		if (default_variant && *default_variant) {
+			pidgin_conversation_theme_set_variant(theme, default_variant);
+
+		} else {
+			/* Try theme default */
+			val = g_hash_table_lookup(info, "DefaultVariant");
+			if (val && G_VALUE_HOLDS_STRING(val)) {
+				default_variant = g_value_get_string(val);
+				if (default_variant && *default_variant)
+					pidgin_conversation_theme_set_variant(theme, default_variant);
+				else
+					default_variant = NULL;
+			} else
+				default_variant = NULL;
+		}
+
+		while ((file = g_dir_read_name(variants)) != NULL) {
+			const char *end = g_strrstr(file, ".css");
+			char *name;
+
+			if ((end == NULL) || (*(end + 4) != '\0'))
+				continue;
+
+			name = g_strndup(file, end - file);
+			pidgin_conversation_theme_add_variant(theme, name);
+
+			/* Set variant with first found */
+			if (!default_variant) {
+				pidgin_conversation_theme_set_variant(theme, name);
+				default_variant = name;
+			}
+		}
+
+		g_dir_close(variants);
+	}
+
+	return PURPLE_THEME(theme);
+}
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+pidgin_conv_theme_loader_class_init(PidginConvThemeLoaderClass *klass)
+{
+	PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass);
+
+	loader_klass->purple_theme_loader_build = pidgin_conv_loader_build;
+}
+
+
+GType
+pidgin_conversation_theme_loader_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PidginConvThemeLoaderClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)pidgin_conv_theme_loader_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (PidginConvThemeLoader),
+			0, /* n_preallocs */
+			NULL, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static(PURPLE_TYPE_THEME_LOADER,
+				"PidginConvThemeLoader", &info, 0);
+	}
+	return type;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkconv-theme-loader.h	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,72 @@
+/**
+ * @file gtkconv-theme-loader.h  Pidgin Conversation Theme Loader Class API
+ */
+
+/* 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 PIDGIN_CONV_THEME_LOADER_H
+#define PIDGIN_CONV_THEME_LOADER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "theme-loader.h"
+
+/**
+ * A pidgin conversation theme loader. Extends PurpleThemeLoader (theme-loader.h)
+ * This is a class designed to build conversation themes
+ *
+ * PidginConvThemeLoader is a GObject.
+ */
+typedef struct _PidginConvThemeLoader       PidginConvThemeLoader;
+typedef struct _PidginConvThemeLoaderClass  PidginConvThemeLoaderClass;
+
+#define PIDGIN_TYPE_CONV_THEME_LOADER            (pidgin_conversation_theme_loader_get_type ())
+#define PIDGIN_CONV_THEME_LOADER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoader))
+#define PIDGIN_CONV_THEME_LOADER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoaderClass))
+#define PIDGIN_IS_CONV_THEME_LOADER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_CONV_THEME_LOADER))
+#define PIDGIN_IS_CONV_THEME_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_CONV_THEME_LOADER))
+#define PIDGIN_CONV_THEME_LOADER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_CONV_THEME_LOADER, PidginConvThemeLoaderClass))
+
+struct _PidginConvThemeLoader
+{
+	PurpleThemeLoader parent;
+};
+
+struct _PidginConvThemeLoaderClass
+{
+	PurpleThemeLoaderClass parent_class;
+};
+
+/**************************************************************************/
+/** @name Pidgin Conversation Theme-Loader API                            */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType pidgin_conversation_theme_loader_get_type(void);
+
+G_END_DECLS
+#endif /* PIDGIN_CONV_THEME_LOADER_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkconv-theme.c	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,737 @@
+/*
+ * Conversation Themes for Pidgin
+ *
+ * Pidgin 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 "gtkconv-theme.h"
+
+#include "conversation.h"
+#include "debug.h"
+#include "prefs.h"
+#include "xmlnode.h"
+
+#include "pidgin.h"
+#include "gtkconv.h"
+#include "gtkwebview.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define PIDGIN_CONV_THEME_GET_PRIVATE(Gobject) \
+	(G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PIDGIN_TYPE_CONV_THEME, PidginConvThemePrivate))
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+
+typedef struct {
+	/* current config options */
+	char     *variant; /* allowed to be NULL if there are no variants */
+	GList    *variants;
+
+	/* Info.plist keys/values */
+	GHashTable *info;
+
+	/* caches */
+	char    *template_html;
+	char    *header_html;
+	char    *footer_html;
+	char    *topic_html;
+	char    *status_html;
+	char    *content_html;
+	char    *incoming_content_html;
+	char    *outgoing_content_html;
+	char    *incoming_next_content_html;
+	char    *outgoing_next_content_html;
+	char    *incoming_context_html;
+	char    *outgoing_context_html;
+	char    *incoming_next_context_html;
+	char    *outgoing_next_context_html;
+	char    *basestyle_css;
+} PidginConvThemePrivate;
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+
+enum {
+	PROP_ZERO = 0,
+	PROP_INFO,
+	PROP_VARIANT,
+	PROP_LAST
+};
+
+/******************************************************************************
+ * Globals
+ *****************************************************************************/
+
+static GObjectClass *parent_class = NULL;
+static GParamSpec *properties[PROP_LAST];
+
+/******************************************************************************
+ * Helper Functions
+ *****************************************************************************/
+
+static const GValue *
+get_key(PidginConvThemePrivate *priv, const char *key, gboolean specific)
+{
+	GValue *val = NULL;
+
+	/* Try variant-specific key */
+	if (specific && priv->variant) {
+		char *name = g_strdup_printf("%s:%s", key, priv->variant);
+		val = g_hash_table_lookup(priv->info, name);
+		g_free(name);
+	}
+
+	/* Try generic key */
+	if (!val) {
+		val = g_hash_table_lookup(priv->info, key);
+	}
+
+	return val;
+}
+
+static const char *
+get_template_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->template_html)
+		return priv->template_html;
+
+	/* The template path can either come from the theme, or can
+	 * be stock Template.html that comes with the plugin */
+	file = g_build_filename(dir, "Contents", "Resources", "Template.html", NULL);
+
+	if (!g_file_test(file, G_FILE_TEST_EXISTS)) {
+		g_free(file);
+		file = g_build_filename(DATADIR, "pidgin", "webkit", "Template.html", NULL);
+	}
+
+	if (!g_file_get_contents(file, &priv->template_html, NULL, NULL)) {
+		purple_debug_error("webkit", "Could not locate a Template.html (%s)\n", file);
+		priv->template_html = g_strdup("");
+	}
+	g_free(file);
+
+	return priv->template_html;
+}
+
+static const char *
+get_basestyle_css(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->basestyle_css)
+		return priv->basestyle_css;
+
+	file = g_build_filename(dir, "Contents", "Resources", "main.css", NULL);
+	if (!g_file_get_contents(file, &priv->basestyle_css, NULL, NULL))
+		priv->basestyle_css = g_strdup("");
+	g_free(file);
+
+	return priv->basestyle_css;
+}
+
+static const char *
+get_header_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->header_html)
+		return priv->header_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Header.html", NULL);
+	if (!g_file_get_contents(file, &priv->header_html, NULL, NULL))
+		priv->header_html = g_strdup("");
+	g_free(file);
+
+	return priv->header_html;
+}
+
+static const char *
+get_footer_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->footer_html)
+		return priv->footer_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Footer.html", NULL);
+	if (!g_file_get_contents(file, &priv->footer_html, NULL, NULL))
+		priv->footer_html = g_strdup("");
+	g_free(file);
+
+	return priv->footer_html;
+}
+
+static const char *
+get_topic_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->topic_html)
+		return priv->topic_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Topic.html", NULL);
+	if (!g_file_get_contents(file, &priv->topic_html, NULL, NULL)) {
+		purple_debug_info("webkit", "%s could not find Resources/Topic.html\n", dir);
+		priv->topic_html = g_strdup("");
+	}
+	g_free(file);
+
+	return priv->topic_html;
+}
+
+static const char *
+get_content_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->content_html)
+		return priv->content_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Content.html", NULL);
+	if (!g_file_get_contents(file, &priv->content_html, NULL, NULL)) {
+		purple_debug_info("webkit", "%s did not have a Content.html\n", dir);
+		priv->content_html = g_strdup("");
+	}
+	g_free(file);
+
+	return priv->content_html;
+}
+
+static const char *
+get_status_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->status_html)
+		return priv->status_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Status.html", NULL);
+	if (!g_file_get_contents(file, &priv->status_html, NULL, NULL)) {
+		purple_debug_info("webkit", "%s could not find Resources/Status.html\n", dir);
+		priv->status_html = g_strdup(get_content_html(priv, dir));
+	}
+	g_free(file);
+
+	return priv->status_html;
+}
+
+static const char *
+get_incoming_content_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->incoming_content_html)
+		return priv->incoming_content_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Incoming", "Content.html", NULL);
+	if (!g_file_get_contents(file, &priv->incoming_content_html, NULL, NULL)) {
+		purple_debug_info("webkit", "%s did not have a Incoming/Content.html\n", dir);
+		priv->incoming_content_html = g_strdup(get_content_html(priv, dir));
+	}
+	g_free(file);
+
+	return priv->incoming_content_html;
+}
+
+static const char *
+get_incoming_next_content_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->incoming_next_content_html)
+		return priv->incoming_next_content_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Incoming", "NextContent.html", NULL);
+	if (!g_file_get_contents(file, &priv->incoming_next_content_html, NULL, NULL)) {
+		priv->incoming_next_content_html = g_strdup(get_incoming_content_html(priv, dir));
+	}
+	g_free(file);
+
+	return priv->incoming_next_content_html;
+}
+
+static const char *
+get_incoming_context_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->incoming_context_html)
+		return priv->incoming_context_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Incoming", "Context.html", NULL);
+	if (!g_file_get_contents(file, &priv->incoming_context_html, NULL, NULL)) {
+		purple_debug_info("webkit", "%s did not have a Incoming/Context.html\n", dir);
+		priv->incoming_context_html = g_strdup(get_incoming_content_html(priv, dir));
+	}
+	g_free(file);
+
+	return priv->incoming_context_html;
+}
+
+static const char *
+get_incoming_next_context_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->incoming_next_context_html)
+		return priv->incoming_next_context_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Incoming", "NextContext.html", NULL);
+	if (!g_file_get_contents(file, &priv->incoming_next_context_html, NULL, NULL)) {
+		priv->incoming_next_context_html = g_strdup(get_incoming_context_html(priv, dir));
+	}
+	g_free(file);
+
+	return priv->incoming_next_context_html;
+}
+
+static const char *
+get_outgoing_content_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->outgoing_content_html)
+		return priv->outgoing_content_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "Content.html", NULL);
+	if (!g_file_get_contents(file, &priv->outgoing_content_html, NULL, NULL)) {
+		priv->outgoing_content_html = g_strdup(get_incoming_content_html(priv, dir));
+	}
+	g_free(file);
+
+	return priv->outgoing_content_html;
+}
+
+static const char *
+get_outgoing_next_content_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->outgoing_next_content_html)
+		return priv->outgoing_next_content_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "NextContent.html", NULL);
+	if (!g_file_get_contents(file, &priv->outgoing_next_content_html, NULL, NULL)) {
+		priv->outgoing_next_content_html = g_strdup(get_outgoing_content_html(priv, dir));
+	}
+
+	return priv->outgoing_next_content_html;
+}
+
+static const char *
+get_outgoing_context_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->outgoing_context_html)
+		return priv->outgoing_context_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "Context.html", NULL);
+	if (!g_file_get_contents(file, &priv->outgoing_context_html, NULL, NULL)) {
+		priv->outgoing_context_html = g_strdup(get_incoming_context_html(priv, dir));
+	}
+	g_free(file);
+
+	return priv->outgoing_context_html;
+}
+
+static const char *
+get_outgoing_next_context_html(PidginConvThemePrivate *priv, const char *dir)
+{
+	char *file;
+
+	if (priv->outgoing_next_context_html)
+		return priv->outgoing_next_context_html;
+
+	file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "NextContext.html", NULL);
+	if (!g_file_get_contents(file, &priv->outgoing_next_context_html, NULL, NULL)) {
+		priv->outgoing_next_context_html = g_strdup(get_outgoing_context_html(priv, dir));
+	}
+
+	return priv->outgoing_next_context_html;
+}
+
+static void
+_set_variant(PidginConvTheme *theme, const char *variant)
+{
+	PidginConvThemePrivate *priv;
+	const GValue *val;
+	char *prefname;
+
+	g_return_if_fail(theme != NULL);
+	g_return_if_fail(variant != NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+
+	g_free(priv->variant);
+	priv->variant = g_strdup(variant);
+
+	val = get_key(priv, "CFBundleIdentifier", FALSE);
+	prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s/variant",
+	                           g_value_get_string(val));
+	purple_prefs_set_string(prefname, variant);
+	g_free(prefname);
+}
+
+/******************************************************************************
+ * GObject Stuff
+ *****************************************************************************/
+
+static void
+pidgin_conv_theme_get_property(GObject *obj, guint param_id, GValue *value,
+		GParamSpec *psec)
+{
+	PidginConvTheme *theme = PIDGIN_CONV_THEME(obj);
+
+	switch (param_id) {
+		case PROP_INFO:
+			g_value_set_boxed(value, (gpointer)pidgin_conversation_theme_get_info(theme));
+			break;
+
+		case PROP_VARIANT:
+			g_value_set_string(value, pidgin_conversation_theme_get_variant(theme));
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+pidgin_conv_theme_set_property(GObject *obj, guint param_id, const GValue *value,
+		GParamSpec *psec)
+{
+	PidginConvTheme *theme = PIDGIN_CONV_THEME(obj);
+
+	switch (param_id) {
+		case PROP_INFO:
+			pidgin_conversation_theme_set_info(theme, g_value_get_boxed(value));
+			break;
+
+		case PROP_VARIANT:
+			_set_variant(theme, g_value_get_string(value));
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
+			break;
+	}
+}
+
+static void
+pidgin_conv_theme_init(GTypeInstance *instance,
+		gpointer klass)
+{
+	PidginConvThemePrivate *priv;
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(instance);
+}
+
+static void
+pidgin_conv_theme_finalize(GObject *obj)
+{
+	PidginConvThemePrivate *priv;
+	GList *list;
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(obj);
+
+	g_free(priv->template_html);
+	g_free(priv->header_html);
+	g_free(priv->footer_html);
+	g_free(priv->topic_html);
+	g_free(priv->status_html);
+	g_free(priv->content_html);
+	g_free(priv->incoming_content_html);
+	g_free(priv->outgoing_content_html);
+	g_free(priv->incoming_next_content_html);
+	g_free(priv->outgoing_next_content_html);
+	g_free(priv->incoming_context_html);
+	g_free(priv->outgoing_context_html);
+	g_free(priv->incoming_next_context_html);
+	g_free(priv->outgoing_next_context_html);
+	g_free(priv->basestyle_css);
+
+	if (priv->info)
+		g_hash_table_destroy(priv->info);
+
+	list = priv->variants;
+	while (list) {
+		g_free(list->data);
+		list = g_list_delete_link(list, list);
+	}
+	g_free(priv->variant);
+
+	parent_class->finalize(obj);
+}
+
+static void
+pidgin_conv_theme_class_init(PidginConvThemeClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GParamSpec *pspec;
+
+	parent_class = g_type_class_peek_parent(klass);
+
+	g_type_class_add_private(klass, sizeof(PidginConvThemePrivate));
+
+	obj_class->get_property = pidgin_conv_theme_get_property;
+	obj_class->set_property = pidgin_conv_theme_set_property;
+	obj_class->finalize = pidgin_conv_theme_finalize;
+
+	/* INFO */
+	pspec = g_param_spec_boxed("info", "Info",
+			"The information about this theme",
+			G_TYPE_HASH_TABLE,
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property(obj_class, PROP_INFO, pspec);
+	properties[PROP_INFO] = pspec;
+
+	/* VARIANT */
+	pspec = g_param_spec_string("variant", "Variant",
+			"The current variant for this theme",
+			NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property(obj_class, PROP_VARIANT, pspec);
+	properties[PROP_VARIANT] = pspec;
+
+}
+
+GType
+pidgin_conversation_theme_get_type(void)
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PidginConvThemeClass),
+			NULL, /* base_init */
+			NULL, /* base_finalize */
+			(GClassInitFunc)pidgin_conv_theme_class_init, /* class_init */
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof(PidginConvTheme),
+			0, /* n_preallocs */
+			pidgin_conv_theme_init, /* instance_init */
+			NULL, /* value table */
+		};
+		type = g_type_register_static(PURPLE_TYPE_THEME,
+				"PidginConvTheme", &info, 0);
+	}
+	return type;
+}
+
+/*****************************************************************************
+ * Public API functions
+ *****************************************************************************/
+
+const GHashTable *
+pidgin_conversation_theme_get_info(const PidginConvTheme *theme)
+{
+	PidginConvThemePrivate *priv;
+
+	g_return_val_if_fail(theme != NULL, NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+	return priv->info;
+}
+
+void
+pidgin_conversation_theme_set_info(PidginConvTheme *theme, GHashTable *info)
+{
+	PidginConvThemePrivate *priv;
+
+	g_return_if_fail(theme != NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+
+	if (priv->info)
+		g_hash_table_destroy(priv->info);
+
+	priv->info = info;
+}
+
+const GValue *
+pidgin_conversation_theme_lookup(PidginConvTheme *theme, const char *key, gboolean specific)
+{
+	PidginConvThemePrivate *priv;
+
+	g_return_val_if_fail(theme != NULL, NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+
+	return get_key(priv, key, specific);
+}
+
+const char *
+pidgin_conversation_theme_get_template(PidginConvTheme *theme, PidginConvThemeTemplateType type)
+{
+	PidginConvThemePrivate *priv;
+	const char *dir;
+	const char *html;
+
+	g_return_val_if_fail(theme != NULL, NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+	dir = purple_theme_get_dir(PURPLE_THEME(theme));
+
+	switch (type) {
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN:
+			html = get_template_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER:
+			html = get_header_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER:
+			html = get_footer_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_TOPIC:
+			html = get_topic_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS:
+			html = get_status_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_CONTENT:
+			html = get_content_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT:
+			html = get_incoming_content_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT:
+			html = get_incoming_next_content_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTEXT:
+			html = get_incoming_context_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTEXT:
+			html = get_incoming_next_context_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT:
+			html = get_outgoing_content_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT:
+			html = get_outgoing_next_content_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTEXT:
+			html = get_outgoing_context_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTEXT:
+			html = get_outgoing_next_context_html(priv, dir);
+			break;
+		case PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS:
+			html = get_basestyle_css(priv, dir);
+			break;
+		default:
+			purple_debug_error("gtkconv-theme",
+			                   "Requested invalid template type (%d) for theme %s.\n",
+			                   type, purple_theme_get_name(PURPLE_THEME(theme)));
+			html = NULL;
+	}
+
+	return html;
+}
+
+void
+pidgin_conversation_theme_add_variant(PidginConvTheme *theme, char *variant)
+{
+	PidginConvThemePrivate *priv;
+
+	g_return_if_fail(theme != NULL);
+	g_return_if_fail(variant != NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+
+	priv->variants = g_list_prepend(priv->variants, variant);
+}
+
+const char *
+pidgin_conversation_theme_get_variant(PidginConvTheme *theme)
+{
+	PidginConvThemePrivate *priv;
+
+	g_return_val_if_fail(theme != NULL, NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+
+	return priv->variant;
+}
+
+void
+pidgin_conversation_theme_set_variant(PidginConvTheme *theme, const char *variant)
+{
+	_set_variant(theme, variant);
+	g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_VARIANT]);
+}
+
+const GList *
+pidgin_conversation_theme_get_variants(PidginConvTheme *theme)
+{
+	PidginConvThemePrivate *priv;
+
+	g_return_val_if_fail(theme != NULL, NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+
+	return priv->variants;
+}
+
+char *
+pidgin_conversation_theme_get_template_path(PidginConvTheme *theme)
+{
+	const char *dir;
+	char *filename;
+
+	g_return_val_if_fail(theme != NULL, NULL);
+
+	dir = purple_theme_get_dir(PURPLE_THEME(theme));
+	filename = g_build_filename(dir, "Contents", "Resources", "Template.html", NULL);
+
+	if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
+		g_free(filename);
+		filename = g_build_filename(DATADIR, "pidgin", "webkit", "Template.html", NULL);
+	}
+
+	return filename;
+}
+
+char *
+pidgin_conversation_theme_get_css_path(PidginConvTheme *theme)
+{
+	PidginConvThemePrivate *priv;
+	const char *dir;
+
+	g_return_val_if_fail(theme != NULL, NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+
+	dir = purple_theme_get_dir(PURPLE_THEME(theme));
+	if (!priv->variant) {
+		return g_build_filename(dir, "Contents", "Resources", "main.css", NULL);
+	} else {
+		char *file = g_strdup_printf("%s.css", priv->variant);
+		char *ret = g_build_filename(dir, "Contents", "Resources", "Variants",  file, NULL);
+		g_free(file);
+		return ret;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkconv-theme.h	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,196 @@
+/**
+ * @file gtkconv-theme.h  Pidgin Conversation Theme  Class API
+ */
+
+/* pidgin
+ *
+ * Pidgin 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 PIDGIN_CONV_THEME_H
+#define PIDGIN_CONV_THEME_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "conversation.h"
+#include "theme.h"
+
+/**
+ * extends PurpleTheme (theme.h)
+ * A pidgin icon theme.
+ * This object represents a Pidgin icon theme.
+ *
+ * PidginConvTheme is a PurpleTheme Object.
+ */
+typedef struct _PidginConvTheme        PidginConvTheme;
+typedef struct _PidginConvThemeClass   PidginConvThemeClass;
+
+#define PIDGIN_TYPE_CONV_THEME            (pidgin_conversation_theme_get_type ())
+#define PIDGIN_CONV_THEME(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_CONV_THEME, PidginConvTheme))
+#define PIDGIN_CONV_THEME_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_CONV_THEME, PidginConvThemeClass))
+#define PIDGIN_IS_CONV_THEME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_CONV_THEME))
+#define PIDGIN_IS_CONV_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_CONV_THEME))
+#define PIDGIN_CONV_THEME_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_CONV_THEME, PidginConvThemeClass))
+
+struct _PidginConvTheme
+{
+	PurpleTheme parent;
+};
+
+struct _PidginConvThemeClass
+{
+	PurpleThemeClass parent_class;
+};
+
+typedef enum {
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_TOPIC,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_CONTENT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTEXT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTEXT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTEXT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTEXT,
+	PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS
+
+} PidginConvThemeTemplateType;
+
+/**************************************************************************/
+/** @name Pidgin Conversation Theme API                                   */
+/**************************************************************************/
+G_BEGIN_DECLS
+
+/**
+ * GObject foo.
+ * @internal.
+ */
+GType pidgin_conversation_theme_get_type(void);
+
+/**
+ * Get the Info.plist hash table from a conversation theme.
+ *
+ * @param theme The conversation theme
+ *
+ * @return The hash table. Keys are strings as outlined for message styles,
+ *         values are GValue*s. This is an internal structure. Take a ref if
+ *         necessary, but don't destroy it yourself.
+ */
+const GHashTable *pidgin_conversation_theme_get_info(const PidginConvTheme *theme);
+
+/**
+ * Set the Info.plist hash table for a conversation theme.
+ *
+ * @param theme The conversation theme
+ * @param info  The new hash table. The theme will take ownership of this hash
+ *              table. Do not use it yourself afterwards with holding a ref.
+ *              For key and value specifications, @see pidgin_conversation_theme_get_info.
+ *
+ */
+void pidgin_conversation_theme_set_info(PidginConvTheme *theme, GHashTable *info);
+
+/**
+ * Lookup a key in a theme
+ *
+ * @param theme    The conversation theme
+ * @param key      The key to find
+ * @param specific Whether to search variant-specific keys
+ *
+ * @return The key information. If @a specific is @c TRUE, then keys are first
+ *         searched by variant, then by general ones. Otherwise, only general
+ *         key values are returned.
+ */
+const GValue *pidgin_conversation_theme_lookup(PidginConvTheme *theme, const char *key, gboolean specific);
+
+/**
+ * Get the template data from a conversation theme.
+ *
+ * @param theme The conversation theme
+ * @param type  The type of template data
+ *
+ * @return The template data requested. Fallback is made as required by styles.
+ *         Subsequent calls to this function will return cached values.
+ */
+const char *pidgin_conversation_theme_get_template(PidginConvTheme *theme, PidginConvThemeTemplateType type);
+
+/**
+ * Add an available variant name to a conversation theme.
+ *
+ * @param theme   The conversation theme
+ * @param variant The name of the variant
+ *
+ * @Note The conversation theme will take ownership of the variant name string.
+ *       This function should normally only be called by the theme loader.
+ */
+void pidgin_conversation_theme_add_variant(PidginConvTheme *theme, char *variant);
+
+/**
+ * Get the currently set variant name for a conversation theme.
+ *
+ * @param theme The conversation theme
+ *
+ * @return The current variant name.
+ */
+const char *pidgin_conversation_theme_get_variant(PidginConvTheme *theme);
+
+/**
+ * Set the variant name for a conversation theme.
+ *
+ * @param theme   The conversation theme
+ * @param variant The name of the variant
+ *
+ */
+void pidgin_conversation_theme_set_variant(PidginConvTheme *theme, const char *variant);
+
+/**
+ * Get a list of available variants for a conversation theme.
+ *
+ * @param theme The conversation theme
+ *
+ * @return The list of variants. This GList and the string data are owned by
+ *         the theme and should not be freed by the caller.
+ */
+const GList *pidgin_conversation_theme_get_variants(PidginConvTheme *theme);
+
+/**
+ * Get the path to the template HTML file.
+ *
+ * @param theme The conversation theme
+ *
+ * @return The path to the HTML file.
+ */
+char *pidgin_conversation_theme_get_template_path(PidginConvTheme *theme);
+
+/**
+ * Get the path to the current variant CSS file.
+ *
+ * @param theme The conversation theme
+ *
+ * @return The path to the CSS file.
+ */
+char *pidgin_conversation_theme_get_css_path(PidginConvTheme *theme);
+
+G_END_DECLS
+#endif /* PIDGIN_CONV_THEME_H */
+
--- a/pidgin/gtkconv.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkconv.c	Mon Sep 26 14:57:21 2011 +0900
@@ -53,6 +53,8 @@
 #include "notify.h"
 #include "prpl.h"
 #include "request.h"
+#include "theme-loader.h"
+#include "theme-manager.h"
 #include "util.h"
 #include "version.h"
 
@@ -60,6 +62,8 @@
 #include "gtkblist.h"
 #include "gtkconv.h"
 #include "gtkconvwin.h"
+#include "gtkconv-theme.h"
+#include "gtkconv-theme-loader.h"
 #include "gtkdialogs.h"
 #include "gtkimhtml.h"
 #include "gtkimhtmltoolbar.h"
@@ -70,11 +74,46 @@
 #include "gtkprivacy.h"
 #include "gtkthemes.h"
 #include "gtkutils.h"
+#include "gtkwebview.h"
 #include "pidginstock.h"
 #include "pidgintooltip.h"
+#include "smileyparser.h"
 
 #include "gtknickcolors.h"
 
+/**
+ * A GTK+ Instant Message pane.
+ */
+struct _PidginImPane
+{
+	GtkWidget *block;
+	GtkWidget *send_file;
+	GtkWidget *sep1;
+	GtkWidget *sep2;
+	GtkWidget *check;
+	GtkWidget *progress;
+	guint32 typing_timer;
+
+	/* Buddy icon stuff */
+	GtkWidget *icon_container;
+	GtkWidget *icon;
+	gboolean show_icon;
+	gboolean animate;
+	GdkPixbufAnimation *anim;
+	GdkPixbufAnimationIter *iter;
+	guint32 icon_timer;
+};
+
+/**
+ * GTK+ Chat panes.
+ */
+struct _PidginChatPane
+{
+	GtkWidget *count;
+	GtkWidget *list;
+	GtkWidget *topic_text;
+};
+
 #define CLOSE_CONV_TIMEOUT_SECS  (10 * 60)
 
 #define AUTO_RESPONSE "&lt;AUTO-REPLY&gt; : "
@@ -181,7 +220,7 @@
 static const GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name)
 {
 	static GdkColor col;
-	GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml);
+	GtkStyle *style = gtk_widget_get_style(gtkconv->webview);
 	float scale;
 
 	col = nick_colors[g_str_hash(name) % nbr_nick_colors];
@@ -445,8 +484,9 @@
 	PidginConversation *gtkconv = NULL;
 
 	gtkconv = PIDGIN_CONVERSATION(conv);
-	if (gtkconv)
-		gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml));
+
+	if (PIDGIN_CONVERSATION(conv))
+		webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(gtkconv->webview), "", "");
 }
 
 static PurpleCmdRet
@@ -1068,6 +1108,8 @@
 static void
 savelog_writefile_cb(void *user_data, const char *filename)
 {
+	/* TODO WEBKIT: I don't know how to support this using webkit yet. */
+#if 0
 	PurpleConversation *conv = (PurpleConversation *)user_data;
 	FILE *fp;
 	const char *name;
@@ -1094,6 +1136,7 @@
 
 	fprintf(fp, "\n</body>\n</html>\n");
 	fclose(fp);
+#endif /* if 0 */
 }
 
 /*
@@ -1602,32 +1645,6 @@
 }
 
 static void
-menu_chat_get_away_cb(GtkWidget *w, PidginConversation *gtkconv)
-{
-	PurpleConversation *conv = gtkconv->active_conv;
-	PurplePluginProtocolInfo *prpl_info = NULL;
-	PurpleConnection *gc;
-	char *who;
-
-	gc  = purple_conversation_get_gc(conv);
-	who = g_object_get_data(G_OBJECT(w), "user_data");
-
-	if (gc != NULL) {
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
-		/*
-		 * May want to expand this to work similarly to menu_info_cb?
-		 */
-
-		if (prpl_info->get_cb_away != NULL)
-		{
-			prpl_info->get_cb_away(gc,
-				purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
-		}
-	}
-}
-
-static void
 menu_chat_add_remove_cb(GtkWidget *w, PidginConversation *gtkconv)
 {
 	PurpleConversation *conv = gtkconv->active_conv;
@@ -1650,7 +1667,7 @@
 static GtkTextMark *
 get_mark_for_user(PidginConversation *gtkconv, const char *who)
 {
-	GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml));
+	GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->webview));
 	char *tmp = g_strconcat("user:", who, NULL);
 	GtkTextMark *mark = gtk_text_buffer_get_mark(buf, tmp);
 
@@ -1661,6 +1678,8 @@
 static void
 menu_last_said_cb(GtkWidget *w, PidginConversation *gtkconv)
 {
+/* TODO WEBKIT: This doesn't work yet, of course... */
+#if 0
 	GtkTextMark *mark;
 	const char *who;
 
@@ -1671,6 +1690,7 @@
 		gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0);
 	else
 		g_return_if_reached();
+#endif /* if 0 */
 }
 
 static GtkWidget *
@@ -1758,16 +1778,6 @@
 			g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
 	}
 
-	if (prpl_info && prpl_info->get_cb_away) {
-		button = pidgin_new_item_from_stock(menu, _("Get Away Message"), PIDGIN_STOCK_AWAY,
-					G_CALLBACK(menu_chat_get_away_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL);
-
-		if (gc == NULL)
-			gtk_widget_set_sensitive(button, FALSE);
-		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)) {
 		if ((buddy = purple_find_buddy(conv->account, who)) != NULL)
 			button = pidgin_new_item_from_stock(menu, _("Remove"), GTK_STOCK_REMOVE,
@@ -1885,10 +1895,13 @@
 		chat_do_im(gtkconv, who);
 	} else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) {
 		/* Move to user's anchor */
+/* TODO WEBKIT: This isn't implemented yet. */
+#if 0
 		GtkTextMark *mark = get_mark_for_user(gtkconv, who);
 
 		if(mark != NULL)
 			gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0);
+#endif /* if 0 */
 	} else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
 		GtkWidget *menu = create_chat_menu (conv, who, gc);
 		gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
@@ -1965,8 +1978,8 @@
 		GtkWidget *from;
 		GtkWidget *to;
 	} transitions[] = {
-		{gtkconv->entry, gtkconv->imhtml},
-		{gtkconv->imhtml, chat ? gtkconv->u.chat->list : gtkconv->entry},
+		{gtkconv->entry, gtkconv->webview},
+		{gtkconv->webview, chat ? gtkconv->u.chat->list : gtkconv->entry},
 		{chat ? gtkconv->u.chat->list : NULL, gtkconv->entry},
 		{NULL, NULL}
 	}, *ptr;
@@ -2222,14 +2235,20 @@
 			break;
 
 		case GDK_Page_Up:
- 		case GDK_KP_Page_Up:
+		case GDK_KP_Page_Up:
+/* TODO WEBKIT: Write this. */
+#if 0
 			gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml));
+#endif /* if 0 */
 			return TRUE;
 			break;
 
 		case GDK_Page_Down:
- 		case GDK_KP_Page_Down:
+		case GDK_KP_Page_Down:
+/* TODO WEBKIT: Write this. */
+#if 0
 			gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml));
+#endif /* if 0 */
 			return TRUE;
 			break;
 
@@ -2339,7 +2358,7 @@
 	entry = GTK_IMHTML(gtkconv->entry);
 	protocol_name = purple_account_get_protocol_name(conv->account);
 	gtk_imhtml_set_protocol_name(entry, protocol_name);
-	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol_name);
+	/* TODO WEBKIT: gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol_name); */
 
 	if (!(conv->features & PURPLE_CONNECTION_HTML))
 		gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry));
@@ -3288,11 +3307,11 @@
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		chat = purple_blist_find_chat(conv->account, conv->name);
 
-		if ((chat == NULL) && (gtkconv->imhtml != NULL)) {
-			chat = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat");
-		}
-
-		if ((chat == NULL) && (gtkconv->imhtml != NULL)) {
+		if ((chat == NULL) && (gtkconv->webview != NULL)) {
+			chat = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat");
+		}
+
+		if ((chat == NULL) && (gtkconv->webview != NULL)) {
 			GHashTable *components;
 			PurpleAccount *account = purple_conversation_get_account(conv);
 			PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
@@ -3310,7 +3329,7 @@
 			chat = purple_chat_new(conv->account, NULL, components);
 			purple_blist_node_set_flags((PurpleBlistNode *)chat,
 					PURPLE_BLIST_NODE_FLAG_NO_SAVE);
-			g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_chat",
+			g_object_set_data_full(G_OBJECT(gtkconv->webview), "transient_chat",
 					chat, (GDestroyNotify)purple_blist_remove_chat);
 		}
 	} else {
@@ -3322,15 +3341,15 @@
 		/* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle
 		 * removing "isolated" buddy nodes well */
 		if (purple_version_check(2, 0, 2) == NULL) {
-			if ((buddy == NULL) && (gtkconv->imhtml != NULL)) {
-				buddy = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy");
+			if ((buddy == NULL) && (gtkconv->webview != NULL)) {
+				buddy = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy");
 			}
 
-			if ((buddy == NULL) && (gtkconv->imhtml != NULL)) {
+			if ((buddy == NULL) && (gtkconv->webview != NULL)) {
 				buddy = purple_buddy_new(conv->account, conv->name, NULL);
 				purple_blist_node_set_flags((PurpleBlistNode *)buddy,
 						PURPLE_BLIST_NODE_FLAG_NO_SAVE);
-				g_object_set_data_full(G_OBJECT(gtkconv->imhtml), "transient_buddy",
+				g_object_set_data_full(G_OBJECT(gtkconv->webview), "transient_buddy",
 						buddy, (GDestroyNotify)purple_buddy_destroy);
 			}
 		}
@@ -3741,6 +3760,8 @@
 static void
 update_typing_message(PidginConversation *gtkconv, const char *message)
 {
+	/* TODO WEBKIT: this is not handled at all */
+#if 0
 	GtkTextBuffer *buffer;
 	GtkTextMark *stmark, *enmark;
 
@@ -3773,6 +3794,7 @@
 		gtk_text_buffer_get_end_iter(buffer, &iter);
 		gtk_text_buffer_create_mark(buffer, "typing-notification-end", &iter, TRUE);
 	}
+#endif /* if 0 */
 }
 
 static void
@@ -4018,7 +4040,8 @@
 						continue;
 
 					account = purple_buddy_get_account(buddy);
-					if (purple_account_is_connected(account) || account == gtkconv->active_conv->account)
+					/* TODO WEBKIT: (I'm not actually sure if this is webkit-related --Mark Doliner) */
+					if (purple_account_is_connected(account) /*|| account == gtkconv->active_conv->account*/)
 					{
 						/* Use the PurplePresence to get unique buddies. */
 						PurplePresence *presence = purple_buddy_get_presence(buddy);
@@ -4130,10 +4153,13 @@
 	g_free(tmp);
 
 	if (is_me) {
+#if 0
+		/* TODO WEBKIT: No tags in webkit stuff, yet. */
 		GtkTextTag *tag = gtk_text_tag_table_lookup(
-				gtk_text_buffer_get_tag_table(GTK_IMHTML(gtkconv->imhtml)->text_buffer),
+				gtk_text_buffer_get_tag_table(GTK_IMHTML(gtkconv->webview)->text_buffer),
 				"send-name");
 		g_object_get(tag, "foreground-gdk", &color, NULL);
+#endif /* if 0 */
 	} else {
 		GtkTextTag *tag;
 		if ((tag = get_buddy_tag(conv, name, 0, FALSE)))
@@ -4697,7 +4723,7 @@
 	GdkRectangle oneline;
 	int height, diff;
 	int pad_top, pad_inside, pad_bottom;
-	int total_height = (gtkconv->imhtml->allocation.height + gtkconv->entry->allocation.height);
+	int total_height = (gtkconv->webview->allocation.height + gtkconv->entry->allocation.height);
 	int max_height = total_height / 2;
 	int min_lines = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines");
 	int min_height;
@@ -4919,7 +4945,7 @@
 
 	gtkchat->list = list;
 
-	gtk_box_pack_start(GTK_BOX(lbox), 
+	gtk_box_pack_start(GTK_BOX(lbox),
 		pidgin_make_scrollable(list, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1),
 		TRUE, TRUE, 0);
 }
@@ -4935,13 +4961,13 @@
 	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 		node = (PurpleBlistNode*)(purple_blist_find_chat(conv->account, conv->name));
 		if (!node)
-			node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_chat");
+			node = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat");
 	} else {
 		node = (PurpleBlistNode*)(purple_find_buddy(conv->account, conv->name));
 #if 0
 		/* Using the transient blist nodes to show the tooltip doesn't quite work yet. */
 		if (!node)
-			node = g_object_get_data(G_OBJECT(gtkconv->imhtml), "transient_buddy");
+			node = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy");
 #endif
 	}
 
@@ -4956,7 +4982,7 @@
 {
 	gtk_widget_modify_base(gtkconv->quickfind.entry, GTK_STATE_NORMAL, NULL);
 
-	gtk_imhtml_search_clear(GTK_IMHTML(gtkconv->imhtml));
+	webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(gtkconv->webview));
 	gtk_widget_hide_all(gtkconv->quickfind.container);
 
 	gtk_widget_grab_focus(gtkconv->entry);
@@ -4973,7 +4999,7 @@
 				GTK_ENTRY(entry)->need_im_reset = TRUE;
 				return TRUE;
 			}
-			if (gtk_imhtml_search_find(GTK_IMHTML(gtkconv->imhtml), gtk_entry_get_text(GTK_ENTRY(entry)))) {
+			if (webkit_web_view_search_text(WEBKIT_WEB_VIEW(gtkconv->webview), gtk_entry_get_text(GTK_ENTRY(entry)), FALSE, TRUE, TRUE)) {
 				gtk_widget_modify_base(gtkconv->quickfind.entry, GTK_STATE_NORMAL, NULL);
 			} else {
 				GdkColor col;
@@ -5023,16 +5049,167 @@
 
 /* }}} */
 
+static char *
+replace_header_tokens(PurpleConversation *conv, const char *text)
+{
+	GString *str;
+	const char *cur = text;
+	const char *prev = cur;
+
+	if (text == NULL || *text == '\0')
+		return NULL;
+
+	str = g_string_new(NULL);
+	while ((cur = strchr(cur, '%'))) {
+		const char *replace = NULL;
+		const char *fin = NULL;
+
+		if (g_str_has_prefix(cur, "%chatName%")) {
+			replace = conv->name;
+
+		} else if (g_str_has_prefix(cur, "%sourceName%")) {
+			replace = purple_account_get_alias(conv->account);
+			if (replace == NULL)
+				replace = purple_account_get_username(conv->account);
+
+		} else if (g_str_has_prefix(cur, "%destinationName%")) {
+			PurpleBuddy *buddy = purple_find_buddy(conv->account, conv->name);
+			if (buddy) {
+				replace = purple_buddy_get_alias(buddy);
+			} else {
+				replace = conv->name;
+			}
+
+		} else if (g_str_has_prefix(cur, "%incomingIconPath%")) {
+			PurpleBuddyIcon *icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
+			if (icon)
+				replace = purple_buddy_icon_get_full_path(icon);
+
+		} else if (g_str_has_prefix(cur, "%outgoingIconPath%")) {
+			replace = purple_account_get_buddy_icon_path(conv->account);
+
+		} else if (g_str_has_prefix(cur, "%timeOpened")) {
+			const char *tmp = cur + strlen("%timeOpened");
+			char *format = NULL;
+			if (*tmp == '{') {
+				const char *end;
+				tmp++;
+				end = strstr(tmp, "}%");
+				if (!end) /* Invalid string */
+					continue;
+				format = g_strndup(tmp, end - tmp);
+				fin = end + 1;
+			}
+			replace = purple_utf8_strftime(format ? format : "%X", NULL);
+			g_free(format);
+
+		} else {
+			continue;
+		}
+
+		/* Here we have a replacement to make */
+		g_string_append_len(str, prev, cur - prev);
+		if (replace)
+			g_string_append(str, replace);
+
+		/* And update the pointers */
+		if (fin) {
+			prev = cur = fin + 1;
+		} else {
+			prev = cur = strchr(cur + 1, '%') + 1;
+		}
+	}
+
+	/* And wrap it up */
+	g_string_append(str, prev);
+	return g_string_free(str, FALSE);
+}
+
+static char *
+replace_template_tokens(PidginConvTheme *theme, const char *header, const char *footer)
+{
+	GString *str;
+	const char *text;
+	char **ms;
+	char *path;
+
+	text = pidgin_conversation_theme_get_template(theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN);
+	if (text == NULL)
+		return NULL;
+
+	ms = g_strsplit(text, "%@", 6);
+	if (ms[0] == NULL || ms[1] == NULL || ms[2] == NULL || ms[3] == NULL || ms[4] == NULL || ms[5] == NULL) {
+		g_strfreev(ms);
+		return NULL;
+	}
+
+	str = g_string_new(NULL);
+
+	g_string_append(str, ms[0]);
+	g_string_append(str, "file://");
+	path = pidgin_conversation_theme_get_template_path(theme);
+	g_string_append(str, path);
+	g_free(path);
+
+	g_string_append(str, ms[1]);
+
+	text = pidgin_conversation_theme_get_template(theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS);
+	g_string_append(str, text);
+
+	g_string_append(str, ms[2]);
+
+	g_string_append(str, "file://");
+	path = pidgin_conversation_theme_get_css_path(theme);
+	g_string_append(str, path);
+	g_free(path);
+
+	g_string_append(str, ms[3]);
+	if (header)
+		g_string_append(str, header);
+	g_string_append(str, ms[4]);
+	if (footer)
+		g_string_append(str, footer);
+	g_string_append(str, ms[5]);
+
+	g_strfreev(ms);
+
+	return g_string_free(str, FALSE);
+}
+
+static void
+set_theme_webkit_settings(WebKitWebView *webview, PidginConvTheme *theme)
+{
+	WebKitWebSettings *settings;
+	const GValue *val;
+
+	g_object_get(G_OBJECT(webview), "settings", &settings, NULL);
+
+	val = pidgin_conversation_theme_lookup(theme, "DefaultFontFamily", TRUE);
+	if (val && G_VALUE_HOLDS_STRING(val))
+		g_object_set(G_OBJECT(settings), "default-font-family", g_value_get_string(val), NULL);
+
+	val = pidgin_conversation_theme_lookup(theme, "DefaultFontSize", TRUE);
+	if (val && G_VALUE_HOLDS_INT(val))
+		g_object_set(G_OBJECT(settings), "default-font-size", GINT_TO_POINTER(g_value_get_int(val)), NULL);
+
+	val = pidgin_conversation_theme_lookup(theme, "DefaultBackgroundIsTransparent", TRUE);
+	if (val && G_VALUE_HOLDS_BOOLEAN(val))
+		/* this does not work :( */
+		webkit_web_view_set_transparent(webview, g_value_get_boolean(val));
+}
+
 static GtkWidget *
 setup_common_pane(PidginConversation *gtkconv)
 {
-	GtkWidget *paned, *vbox, *frame, *imhtml_sw, *event_box;
+	GtkWidget *paned, *vbox, *frame, *imhtml_sw, *webview_sw, *event_box;
 	GtkCellRenderer *rend;
 	GtkTreePath *path;
 	PurpleConversation *conv = gtkconv->active_conv;
 	PurpleBuddy *buddy;
 	gboolean chat = (conv->type == PURPLE_CONV_TYPE_CHAT);
 	int buddyicon_size = 0;
+	char *header, *footer;
+	char *template;
 
 	paned = gtk_vpaned_new();
 	gtk_widget_show(paned);
@@ -5126,9 +5303,38 @@
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_EMBLEM_COLUMN, NULL);
 	g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
 
-	/* Setup the gtkimhtml widget */
-	frame = pidgin_create_imhtml(FALSE, &gtkconv->imhtml, NULL, &imhtml_sw);
-	gtk_widget_set_size_request(gtkconv->imhtml, -1, 0);
+	/* Setup the webkit widget */
+	frame = pidgin_create_webview(FALSE, &gtkconv->webview, NULL, &webview_sw);
+	gtk_widget_set_size_request(gtkconv->webview, -1, 0);
+
+	header = replace_header_tokens(conv,
+		pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER));
+	footer = replace_header_tokens(conv,
+		pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER));
+	template = replace_template_tokens(gtkconv->theme, header, footer);
+	g_free(header);
+	g_free(footer);
+
+	if (template != NULL) {
+		char *basedir;
+		char *baseuri;
+
+		purple_debug_info("webkit", "template: %s\n", template);
+
+		set_theme_webkit_settings(WEBKIT_WEB_VIEW(gtkconv->webview), gtkconv->theme);
+
+		basedir = pidgin_conversation_theme_get_template_path(gtkconv->theme);
+		baseuri = g_strdup_printf("file://%s", basedir);
+		webkit_web_view_load_string(WEBKIT_WEB_VIEW(gtkconv->webview), template, "text/html", "UTF-8", baseuri);
+
+		if (chat)
+			gtk_webview_safe_execute_script(GTK_WEBVIEW(gtkconv->webview), "document.getElementById('Chat').className = 'groupchat'");
+
+		g_free(basedir);
+		g_free(baseuri);
+		g_free(template);
+	}
+
 	if (chat) {
 		GtkWidget *hpaned;
 
@@ -5146,19 +5352,16 @@
 	} else {
 		gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 	}
-	gtk_widget_show(frame);
-
-	gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
-	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
-	g_object_set_data(G_OBJECT(gtkconv->imhtml), "gtkconv", gtkconv);
-
-	g_object_set(G_OBJECT(imhtml_sw), "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
-
-	g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event",
+	gtk_widget_show_all(frame);
+
+	gtk_widget_set_name(gtkconv->webview, "pidgin_conv_webview");
+	g_object_set_data(G_OBJECT(gtkconv->webview), "gtkconv", gtkconv);
+
+	g_signal_connect_after(G_OBJECT(gtkconv->webview), "button_press_event",
 	                       G_CALLBACK(entry_stop_rclick_cb), NULL);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event",
+	g_signal_connect(G_OBJECT(gtkconv->webview), "key_press_event",
 	                 G_CALLBACK(refocus_entry_cb), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
+	g_signal_connect(G_OBJECT(gtkconv->webview), "key_release_event",
 	                 G_CALLBACK(refocus_entry_cb), gtkconv);
 
 	/* Setup the bottom half of the conversation window */
@@ -5413,6 +5616,8 @@
 
 static void set_typing_font(GtkWidget *widget, GtkStyle *style, PidginConversation *gtkconv)
 {
+/* TODO WEBKIT */
+#if 0
 	static PangoFontDescription *font_desc = NULL;
 	static GdkColor *color = NULL;
 	static gboolean enable = TRUE;
@@ -5443,6 +5648,7 @@
 	}
 
 	g_signal_handlers_disconnect_by_func(G_OBJECT(widget), set_typing_font, gtkconv);
+#endif /* if 0 */
 }
 
 /**************************************************************************
@@ -5452,6 +5658,7 @@
 private_gtkconv_new(PurpleConversation *conv, gboolean hidden)
 {
 	PidginConversation *gtkconv;
+	PurpleTheme *theme;
 	PurpleConversationType conv_type = purple_conversation_get_type(conv);
 	GtkWidget *pane = NULL;
 	GtkWidget *tab_cont;
@@ -5476,6 +5683,11 @@
 	gtkconv->tooltips = gtk_tooltips_new();
 	gtkconv->unseen_state = PIDGIN_UNSEEN_NONE;
 	gtkconv->unseen_count = 0;
+	theme = purple_theme_manager_find_theme(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/theme"), "conversation");
+	if (!theme)
+		theme = purple_theme_manager_find_theme("Default", "conversation");
+	gtkconv->theme = PIDGIN_CONV_THEME(g_object_ref(theme));
+	gtkconv->last_flags = 0;
 
 	if (conv_type == PURPLE_CONV_TYPE_IM) {
 		gtkconv->u.im = g_malloc0(sizeof(PidginImPane));
@@ -5484,9 +5696,6 @@
 	}
 	pane = setup_common_pane(gtkconv);
 
-	gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml),
-			gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE);
-
 	if (pane == NULL) {
 		if (conv_type == PURPLE_CONV_TYPE_CHAT)
 			g_free(gtkconv->u.chat);
@@ -5509,7 +5718,7 @@
 	                  GTK_DEST_DEFAULT_DROP,
 	                  te, sizeof(te) / sizeof(GtkTargetEntry),
 	                  GDK_ACTION_COPY);
-	gtk_drag_dest_set(gtkconv->imhtml, 0,
+	gtk_drag_dest_set(gtkconv->webview, 0,
 	                  te, sizeof(te) / sizeof(GtkTargetEntry),
 	                  GDK_ACTION_COPY);
 
@@ -5521,12 +5730,12 @@
 	                 G_CALLBACK(ignore_middle_click), NULL);
 	g_signal_connect(G_OBJECT(pane), "drag_data_received",
 	                 G_CALLBACK(conv_dnd_recv), gtkconv);
-	g_signal_connect(G_OBJECT(gtkconv->imhtml), "drag_data_received",
+	g_signal_connect(G_OBJECT(gtkconv->webview), "drag_data_received",
 	                 G_CALLBACK(conv_dnd_recv), gtkconv);
 	g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received",
 	                 G_CALLBACK(conv_dnd_recv), gtkconv);
 
-	g_signal_connect(gtkconv->imhtml, "style-set", G_CALLBACK(set_typing_font), gtkconv);
+	g_signal_connect(gtkconv->webview, "style-set", G_CALLBACK(set_typing_font), gtkconv);
 
 	/* Setup the container for the tab. */
 	gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -5556,10 +5765,6 @@
 	else
 		gtk_widget_hide(gtkconv->infopane_hbox);
 
-	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
-		purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_timestamps"));
-	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
-								 purple_account_get_protocol_name(conv->account));
 
 	g_signal_connect_swapped(G_OBJECT(pane), "focus",
 	                         G_CALLBACK(gtk_widget_grab_focus),
@@ -5572,7 +5777,7 @@
 
 	if (nick_colors == NULL) {
 		nbr_nick_colors = NUM_NICK_COLORS;
-		nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->imhtml)->base[GTK_STATE_NORMAL]);
+		nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->webview)->base[GTK_STATE_NORMAL]);
 	}
 
 	if (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
@@ -5686,6 +5891,8 @@
 		g_source_remove(gtkconv->attach.timer);
 	}
 
+	g_object_unref(gtkconv->theme);
+
 	g_free(gtkconv);
 }
 
@@ -5713,6 +5920,7 @@
 	purple_conversation_write(conv, who, message, flags, mtime);
 }
 
+#if 0
 static const char *
 get_text_tag_color(GtkTextTag *tag)
 {
@@ -5793,10 +6001,13 @@
 
 	return FALSE;
 }
+#endif
 
 static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, PurpleMessageFlags flag,
 		gboolean create)
 {
+/* TODO WEBKIT */
+#if 0
 	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 	GtkTextTag *buddytag;
 	gchar *str;
@@ -5830,8 +6041,11 @@
 	g_free(str);
 
 	return buddytag;
-}
-
+#endif /* if 0 */
+	return NULL;
+}
+
+#if 0
 static void pidgin_conv_calculate_newday(PidginConversation *gtkconv, time_t mtime)
 {
 	struct tm *tm = localtime(&mtime);
@@ -5877,6 +6091,105 @@
 	*str = ret;
 #endif
 }
+#endif
+
+static char *
+replace_message_tokens(
+	const char *text,
+	PurpleConversation *conv,
+	const char *name,
+	const char *alias,
+	const char *message,
+	PurpleMessageFlags flags,
+	time_t mtime)
+{
+	GString *str;
+	const char *cur = text;
+	const char *prev = cur;
+
+	if (text == NULL)
+		return g_strdup("");
+
+	str = g_string_new(NULL);
+	while ((cur = strchr(cur, '%'))) {
+		const char *replace = NULL;
+		const char *fin = NULL;
+
+		if (g_str_has_prefix(cur, "%message%")) {
+			replace = message;
+
+		} else if (g_str_has_prefix(cur, "%messageClasses%")) {
+			replace = flags & PURPLE_MESSAGE_SEND ? "outgoing" :
+				  flags & PURPLE_MESSAGE_RECV ? "incoming" : "event";
+
+		} else if (g_str_has_prefix(cur, "%time")) {
+			const char *tmp = cur + strlen("%time");
+			char *format = NULL;
+			if (*tmp == '{') {
+				char *end;
+				tmp++;
+				end = strstr(tmp, "}%");
+				if (!end) /* Invalid string */
+					continue;
+				format = g_strndup(tmp, end - tmp);
+				fin = end + 1;
+			}
+			replace = purple_utf8_strftime(format ? format : "%X", NULL);
+			g_free(format);
+
+		} else if (g_str_has_prefix(cur, "%userIconPath%")) {
+			if (flags & PURPLE_MESSAGE_SEND) {
+				if (purple_account_get_bool(conv->account, "use-global-buddyicon", TRUE)) {
+					replace = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
+				} else {
+					PurpleStoredImage *img = purple_buddy_icons_find_account_icon(conv->account);
+					replace = purple_imgstore_get_filename(img);
+				}
+				if (replace == NULL || !g_file_test(replace, G_FILE_TEST_EXISTS)) {
+					replace = g_build_filename("Outgoing", "buddy_icon.png", NULL);
+				}
+			} else if (flags & PURPLE_MESSAGE_RECV) {
+				PurpleBuddyIcon *icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
+				if (icon)
+					replace = purple_buddy_icon_get_full_path(icon);
+				if (replace == NULL || !g_file_test(replace, G_FILE_TEST_EXISTS)) {
+					replace = g_build_filename("Incoming", "buddy_icon.png", NULL);
+				}
+			}
+
+		} else if (g_str_has_prefix(cur, "%senderScreenName%")) {
+			replace = name;
+
+		} else if (g_str_has_prefix(cur, "%sender%")) {
+			replace = alias;
+
+		} else if (g_str_has_prefix(cur, "%service%")) {
+			replace = purple_account_get_protocol_name(conv->account);
+
+		} else {
+			cur++;
+			continue;
+		}
+
+		/* Here we have a replacement to make */
+		g_string_append_len(str, prev, cur - prev);
+		if (replace)
+			g_string_append(str, replace);
+
+		/* And update the pointers */
+		if (fin) {
+			prev = cur = fin + 1;
+		} else {
+			prev = cur = strchr(cur + 1, '%') + 1;
+		}
+
+	}
+
+	/* And wrap it up */
+	g_string_append(str, prev);
+
+	return g_string_free(str, FALSE);
+}
 
 static void
 pidgin_conv_write_conv(PurpleConversation *conv, const char *name, const char *alias,
@@ -5886,23 +6199,31 @@
 	PidginConversation *gtkconv;
 	PurpleConnection *gc;
 	PurpleAccount *account;
+#if 0
 	int gtk_font_options = 0;
 	int gtk_font_options_all = 0;
-	int max_scrollback_lines;
-	int line_count;
 	char buf2[BUF_LONG];
 	gboolean show_date;
 	char *mdate;
 	char *str;
 	char *with_font_tag;
 	char *sml_attrib = NULL;
+#endif
 	size_t length;
 	PurpleConversationType type;
 	char *displaying;
 	gboolean plugin_return;
-	char *bracket;
-	int tag_count = 0;
+#if 0
 	gboolean is_rtl_message = FALSE;
+#endif
+
+	const char *message_html;
+	char *msg;
+	char *escape;
+	char *script;
+	char *smileyed;
+	PurpleMessageFlags old_flags;
+	const char *func = "appendMessage";
 
 	g_return_if_fail(conv != NULL);
 	gtkconv = PIDGIN_CONVERSATION(conv);
@@ -5959,56 +6280,39 @@
 	}
 	length = strlen(displaying) + 1;
 
-	/* Awful hack to work around GtkIMHtml's inefficient rendering of messages with lots of formatting changes.
-	 * If a message has over 100 '<' characters, strip formatting before appending it. Hopefully nobody actually
-	 * needs that much formatting, anyway.
-	 */
-	for (bracket = strchr(displaying, '<'); bracket && *(bracket + 1); bracket = strchr(bracket + 1, '<'))
-		tag_count++;
-
-	if (tag_count > 100) {
-		char *tmp = displaying;
-		displaying = purple_markup_strip_html(tmp);
-		g_free(tmp);
-	}
-
-	line_count = gtk_text_buffer_get_line_count(
-			gtk_text_view_get_buffer(GTK_TEXT_VIEW(
-				gtkconv->imhtml)));
-
-	max_scrollback_lines = purple_prefs_get_int(
-		PIDGIN_PREFS_ROOT "/conversations/scrollback_lines");
-	/* If we're sitting at more than 100 lines more than the
-	   max scrollback, trim down to max scrollback */
-	if (max_scrollback_lines > 0
-			&& line_count > (max_scrollback_lines + 100)) {
-		GtkTextBuffer *text_buffer = gtk_text_view_get_buffer(
-			GTK_TEXT_VIEW(gtkconv->imhtml));
-		GtkTextIter start, end;
-
-		gtk_text_buffer_get_start_iter(text_buffer, &start);
-		gtk_text_buffer_get_iter_at_line(text_buffer, &end,
-			(line_count - max_scrollback_lines));
-		gtk_imhtml_delete(GTK_IMHTML(gtkconv->imhtml), &start, &end);
-	}
-
-	if (type == PURPLE_CONV_TYPE_CHAT)
-	{
-		/* Create anchor for user */
-		GtkTextIter iter;
-		char *tmp = g_strconcat("user:", name, NULL);
-
-		gtk_text_buffer_get_end_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)), &iter);
-		gtk_text_buffer_create_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)),
-								tmp, &iter, TRUE);
-		g_free(tmp);
-	}
-
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling"))
-		gtk_font_options_all |= GTK_IMHTML_USE_SMOOTHSCROLLING;
-
-	if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
+	old_flags = gtkconv->last_flags;
+	if ((flags & PURPLE_MESSAGE_SEND) && (old_flags & PURPLE_MESSAGE_SEND)) {
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT);
+		func = "appendNextMessage";
+	} else if (flags & PURPLE_MESSAGE_SEND) {
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT);
+	} else if ((flags & PURPLE_MESSAGE_RECV) && (old_flags & PURPLE_MESSAGE_RECV)) {
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT);
+		func = "appendNextMessage";
+	} else if (flags & PURPLE_MESSAGE_RECV) {
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT);
+	} else {
+		message_html = pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS);
+	}
+	gtkconv->last_flags = flags;
+
+	smileyed = smiley_parse_markup(message, purple_account_get_protocol_id(account));
+	msg = replace_message_tokens(message_html, conv, name, alias, smileyed, flags, mtime);
+	escape = gtk_webview_quote_js_string(msg);
+	script = g_strdup_printf("%s(%s)", func, escape);
+
+	purple_debug_info("webkit", "JS: %s\n", script);
+	gtk_webview_safe_execute_script(GTK_WEBVIEW(gtkconv->webview), script);
+
+	g_free(script);
+	g_free(smileyed);
+	g_free(msg);
+	g_free(escape);
+
+#if 0
+	/* if the buffer is not empty add a <br> */
+	if (!gtk_webview_is_empty(GTK_WEBVIEW(gtkconv->webview)))
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<br />");
 
 	/* First message in a conversation. */
 	if (gtkconv->newday == 0)
@@ -6059,32 +6363,32 @@
 	if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY))
 	{
 		/* We want to see our own smileys. Need to revert it after send*/
-		pidgin_themes_smiley_themeize_custom(gtkconv->imhtml);
+		pidgin_themes_smiley_themeize_custom(gtkconv->webview);
 	}
 
 	/* TODO: These colors should not be hardcoded so log.c can use them */
 	if (flags & PURPLE_MESSAGE_RAW) {
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), message, gtk_font_options_all);
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), message);
 	} else if (flags & PURPLE_MESSAGE_SYSTEM) {
 		g_snprintf(buf2, sizeof(buf2),
-			   "<FONT %s><FONT SIZE=\"2\"><!--%s --></FONT><B>%s</B></FONT>",
+			   "<font %s><font size=\"2\"><span class='timestamp'>%s</span></font><b>%s</b></font>",
 			   sml_attrib ? sml_attrib : "", mdate, displaying);
 
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all);
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2);
 
 	} else if (flags & PURPLE_MESSAGE_ERROR) {
 		g_snprintf(buf2, sizeof(buf2),
-			   "<FONT COLOR=\"#ff0000\"><FONT %s><FONT SIZE=\"2\"><!--%s --></FONT><B>%s</B></FONT></FONT>",
+			   "<font color=\"#ff0000\"><font %s><font size=\"2\"><span class='timestamp'>%s</span> </font><b>%s</b></font></font>",
 			   sml_attrib ? sml_attrib : "", mdate, displaying);
 
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all);
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2);
 
 	} else if (flags & PURPLE_MESSAGE_NO_LOG) {
 		g_snprintf(buf2, BUF_LONG,
-			   "<B><FONT %s COLOR=\"#777777\">%s</FONT></B>",
+			   "<b><font %s color=\"#777777\">%s</font></b>",
 			   sml_attrib ? sml_attrib : "", displaying);
 
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all);
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2);
 	} else {
 		char *new_message = g_memdup(displaying, length);
 		char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup(""));
@@ -6094,11 +6398,6 @@
 		int tag_end_offset = 0;
 		const char *tagname = NULL;
 
-		GtkTextIter start, end;
-		GtkTextMark *mark;
-		GtkTextTag *tag;
-		GtkTextBuffer *buffer = GTK_IMHTML(gtkconv->imhtml)->text_buffer;
-
 		/* Enforce direction on alias */
 		if (is_rtl_message)
 			str_embed_direction_chars(&alias_escaped);
@@ -6159,55 +6458,41 @@
 
 		g_free(alias_escaped);
 
+		/* TODO WEBKIT */
+#if 0
 		if (tagname)
 			tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tagname);
 		else
 			tag = get_buddy_tag(conv, name, flags, TRUE);
 
 		if (GTK_IMHTML(gtkconv->imhtml)->show_comments) {
+		{
 			/* The color for the timestamp has to be set in the font-tags, unfortunately.
 			 * Applying the nick-tag to timestamps would work, but that can make it
 			 * bold. I thought applying the "comment" tag again, which has "weight" set
 			 * to PANGO_WEIGHT_NORMAL, would remove the boldness. But it doesn't. So
 			 * this will have to do. I don't terribly like it.  -- sadrul */
-			const char *color = get_text_tag_color(tag);
+			/* const char *color = get_text_tag_color(tag); */
 			g_snprintf(buf2, BUF_LONG, "<FONT %s%s%s SIZE=\"2\"><!--%s --></FONT>",
 					color ? "COLOR=\"" : "", color ? color : "", color ? "\"" : "", mdate);
-			gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
-		}
-
-		gtk_text_buffer_get_end_iter(buffer, &end);
-		mark = gtk_text_buffer_create_mark(buffer, NULL, &end, TRUE);
-
-		g_snprintf(buf2, BUF_LONG, "<FONT %s>%s</FONT> ", sml_attrib ? sml_attrib : "", str);
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL);
-
-		gtk_text_buffer_get_end_iter(buffer, &end);
-		gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
-		gtk_text_buffer_apply_tag(buffer, tag, &start, &end);
-		gtk_text_buffer_delete_mark(buffer, mark);
+			gtk_webview_append_html (GTK_WEBVIEW(gtkconv->webview), buf2);
+		}
+#endif /* if 0 */
+		g_snprintf(buf2, BUF_LONG, "<font %s>%s</font> ", sml_attrib ? sml_attrib : "", str);
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), buf2);
 
 		g_free(str);
 
-		if(gc){
+		if (gc) {
 			char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : "");
 			char *post = "</font>";
-			int pre_len = strlen(pre);
-			int post_len = strlen(post);
-
-			with_font_tag = g_malloc(length + pre_len + post_len + 1);
-
-			strcpy(with_font_tag, pre);
-			memcpy(with_font_tag + pre_len, new_message, length);
-			strcpy(with_font_tag + pre_len + length, post);
-
-			length += pre_len + post_len;
+			with_font_tag = g_strdup_printf("%s%s%s", pre, new_message, post);
 			g_free(pre);
 		} else
 			with_font_tag = g_memdup(new_message, length);
 
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml),
-							 with_font_tag, gtk_font_options | gtk_font_options_all);
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview),
+							 with_font_tag);
 
 		g_free(with_font_tag);
 		g_free(new_message);
@@ -6216,6 +6501,8 @@
 	g_free(mdate);
 	g_free(sml_attrib);
 
+#endif
+
 	/* Tab highlighting stuff */
 	if (!(flags & PURPLE_MESSAGE_SEND) && !pidgin_conv_has_focus(conv))
 	{
@@ -6234,11 +6521,13 @@
 		gtkconv_set_unseen(gtkconv, unseen);
 	}
 
+#if 0
 	if (!(flags & PURPLE_MESSAGE_RECV) && (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY))
 	{
 		/* Restore the smiley-data */
-		pidgin_themes_smiley_themeize(gtkconv->imhtml);
-	}
+		pidgin_themes_smiley_themeize(gtkconv->webview);
+	}
+#endif
 
 	purple_signal_emit(pidgin_conversations_get_handle(),
 		(type == PURPLE_CONV_TYPE_IM ? "displayed-im-msg" : "displayed-chat-msg"),
@@ -6483,6 +6772,13 @@
 }
 
 static gboolean
+add_custom_smiley_for_webview(GtkWebView *webview, const char *sml, const char *smile)
+{
+	/* TODO WEBKIT: Smileys need to be added to webkit stuff */
+	return TRUE;
+}
+
+static gboolean
 pidgin_conv_custom_smiley_add(PurpleConversation *conv, const char *smile, gboolean remote)
 {
 	PidginConversation *gtkconv;
@@ -6509,7 +6805,7 @@
 		}
 	}
 
-	if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->imhtml), sml, smile))
+	if (!add_custom_smiley_for_webview(GTK_WEBVIEW(gtkconv->webview), sml, smile))
 		return FALSE;
 
 	if (!remote)	/* If it's a local custom smiley, then add it for the entry */
@@ -6523,6 +6819,8 @@
 pidgin_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
                                       const guchar *data, gsize size)
 {
+/* TODO WEBKIT */
+#if 0
 	PidginConversation *gtkconv;
 	GtkIMHtmlSmiley *smiley;
 	const char *sml;
@@ -6544,7 +6842,7 @@
 
 	if (!gdk_pixbuf_loader_write(smiley->loader, data, size, &error) || error) {
 		purple_debug_warning("gtkconv", "gdk_pixbuf_loader_write() "
-				"failed with size=%zu: %s\n", size,
+				"failed with size=%" G_GSIZE_FORMAT ": %s\n", size,
 				error ? error->message : "(no error message)");
 		if (error)
 			g_error_free(error);
@@ -6557,11 +6855,14 @@
 		g_object_unref(G_OBJECT(smiley->loader));
 		smiley->loader = gdk_pixbuf_loader_new();
 	}
+#endif /* if 0 */
 }
 
 static void
 pidgin_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
 {
+/* TODO WEBKIT */
+#if 0
 	PidginConversation *gtkconv;
 	GtkIMHtmlSmiley *smiley;
 	const char *sml;
@@ -6598,6 +6899,7 @@
 		g_object_unref(G_OBJECT(smiley->loader));
 		smiley->loader = gdk_pixbuf_loader_new();
 	}
+#endif /* if 0 */
 }
 
 static void
@@ -6750,7 +7052,7 @@
 
 		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
 		{
-			gtk_widget_set_sensitive(win->menu.add, (prpl_info->add_buddy != NULL) || (prpl_info->add_buddy_with_invite != NULL));
+			gtk_widget_set_sensitive(win->menu.add, (prpl_info->add_buddy != NULL));
 			gtk_widget_set_sensitive(win->menu.remove, (prpl_info->remove_buddy != NULL));
 			gtk_widget_set_sensitive(win->menu.send_file,
 									 (prpl_info->send_file != NULL && (!prpl_info->can_receive_file ||
@@ -6870,8 +7172,10 @@
 		}
 	}
 
+#if 0
 	if (fields & PIDGIN_CONV_SMILEY_THEME)
-		pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml);
+		pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->webview);
+#endif
 
 	if ((fields & PIDGIN_CONV_COLORIZE_TITLE) ||
 			(fields & PIDGIN_CONV_SET_TITLE) ||
@@ -7489,8 +7793,11 @@
 		        GTK_CHECK_MENU_ITEM(win->menu.show_timestamps),
 		        (gboolean)GPOINTER_TO_INT(value));
 
+/* TODO WEBKIT: Use WebKit version of this. */
+#if 0
 		gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),
 			(gboolean)GPOINTER_TO_INT(value));
+#endif /* if 0 */
 	}
 }
 
@@ -7891,7 +8198,7 @@
 	while (gtkconv->attach.current && count < 100) {  /* XXX: 100 is a random value here */
 		PurpleConvMessage *msg = gtkconv->attach.current->data;
 		if (!im && when && when < msg->when) {
-			gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0);
+			gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR><HR>");
 			g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
 		}
 		pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when);
@@ -7926,7 +8233,7 @@
 			PurpleConvMessage *msg = msgs->data;
 			pidgin_conv_write_conv(msg->conv, msg->who, msg->alias, msg->what, msg->flags, msg->when);
 		}
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR><HR>", 0);
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR><HR>");
 		g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
 	}
 
@@ -8033,6 +8340,7 @@
 
 	/* Conversations */
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations");
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/themes");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", TRUE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs", TRUE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/trim_vertical_tabs", FALSE);
@@ -8319,6 +8627,8 @@
 	purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle,
 			PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
 
+	purple_theme_manager_register_type(g_object_new(PIDGIN_TYPE_CONV_THEME_LOADER, "type", "conversation", NULL));
+
 	{
 		/* Set default tab colors */
 		GString *str = g_string_new(NULL);
--- a/pidgin/gtkconv.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkconv.h	Mon Sep 26 14:57:21 2011 +0900
@@ -51,7 +51,7 @@
 	CHAT_USERS_FLAGS_COLUMN,
 	CHAT_USERS_COLOR_COLUMN,
 	CHAT_USERS_WEIGHT_COLUMN,
-	CHAT_USERS_ICON_STOCK_COLUMN,   /** @since 2.6.0 */
+	CHAT_USERS_ICON_STOCK_COLUMN,
 	CHAT_USERS_COLUMNS
 };
 
@@ -65,6 +65,7 @@
 #include "pidgin.h"
 #include "conversation.h"
 #include "gtkconvwin.h"
+#include "gtkconv-theme.h"
 
 /**************************************************************************
  * @name Structures
@@ -77,39 +78,6 @@
  */
 
 /**
- * A GTK+ Instant Message pane.
- */
-struct _PidginImPane
-{
-	GtkWidget *block;
-	GtkWidget *send_file;
-	GtkWidget *sep1;
-	GtkWidget *sep2;
-	GtkWidget *check;
-	GtkWidget *progress;
-	guint32 typing_timer;
-
-	/* Buddy icon stuff */
-	GtkWidget *icon_container;
-	GtkWidget *icon;
-	gboolean show_icon;
-	gboolean animate;
-	GdkPixbufAnimation *anim;
-	GdkPixbufAnimationIter *iter;
-	guint32 icon_timer;
-};
-
-/**
- * GTK+ Chat panes.
- */
-struct _PidginChatPane
-{
-	GtkWidget *count;
-	GtkWidget *list;
-	GtkWidget *topic_text;
-};
-
-/**
  * A GTK+ conversation pane.
  */
 struct _PidginConversation
@@ -128,7 +96,9 @@
 	GtkWidget *tabby;
 	GtkWidget *menu_tabby;
 
-	GtkWidget *imhtml;
+	PidginConvTheme *theme;
+	PurpleMessageFlags last_flags;
+	GtkWidget *webview;
 	GtkTextBuffer *entry_buffer;
 	GtkWidget *entry;
 	gboolean auto_resize;   /* this is set to TRUE if the conversation
@@ -174,8 +144,6 @@
 
 	/**
 	 * Quick Find.
-	 *
-	 * @since 2.7.0
 	 */
 	struct {
 		GtkWidget *entry;
@@ -263,8 +231,6 @@
  * @param conv  The conversation.
  *
  * @return  Wheter Pidgin UI was successfully attached.
- *
- * @since 2.2.0
  */
 gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv);
 
--- a/pidgin/gtkdialogs.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkdialogs.c	Mon Sep 26 14:57:21 2011 +0900
@@ -39,10 +39,9 @@
 
 #include "gtkblist.h"
 #include "gtkdialogs.h"
-#include "gtkimhtml.h"
-#include "gtkimhtmltoolbar.h"
 #include "gtklog.h"
 #include "gtkutils.h"
+#include "gtkwebview.h"
 #include "pidginstock.h"
 
 static GList *dialogwindows = NULL;
@@ -151,7 +150,7 @@
 	{N_("Assamese"),            "as", "Amitakhya Phukan", "aphukan@fedoraproject.org"},
 	{N_("Belarusian Latin"),    "be@latin", "Ihar Hrachyshka", "ihar.hrachyshka@gmail.com"},
 	{N_("Bulgarian"),           "bg", "Vladimira Girginova", "missing@here.is"},
-	{N_("Bulgarian"),           "bg", "Vladimir (Kaladan) Petkov", "vpetkov@i-space.org"},
+	{N_("Bulgarian"),           "bg", "Vladimir (Kaladan) Petkov", "kaladan@gmail.com"},
 	{N_("Bengali"),             "bn", "Israt Jahan", "israt@ankur.org.bd"},
 	{N_("Bengali"),             "bn", "Jamil Ahmed", "jamil@bengalinux.org"},
 	{N_("Bengali"),             "bn", "Samia Nimatullah", "mailsamia2001@yahoo.com"},
@@ -222,7 +221,6 @@
 	{N_("Oriya"),               "or", "Manoj Kumar Giri", "giri.manojkr@gmail.com"},
 	{N_("Punjabi"),             "pa", "Amanpreet Singh Alam", "aalam@users.sf.net"},
 	{N_("Polish"),              "pl", "Piotr DrÄ…g", "piotrdrag@gmail.com"},
-	{N_("Polish"),              "pl", "Piotr Makowski", "pmakowski@aviary.pl"},
 	{N_("Portuguese"),          "pt", "Duarte Henriques", "duarte_henriques@myrealbox.com"},
 	{N_("Portuguese-Brazil"),   "pt_BR", "Rodrigo Luiz Marques Flores", "rodrigomarquesflores@gmail.com"},
 	{N_("Pashto"),              "ps", "Kashif Masood", "masudmails@yahoo.com"},
@@ -304,6 +302,7 @@
 	{N_("Polish"),              "pl", "Emil Nowak", "emil5@go2.pl"},
 	{N_("Polish"),              "pl", "Paweł Godlewski", "pawel@bajk.pl"},
 	{N_("Polish"),              "pl", "Krzysztof Foltman", "krzysztof@foltman.com"},
+	{N_("Polish"),              "pl", "Piotr Makowski", NULL},
 	{N_("Polish"),              "pl", "Przemysław Sułek", NULL},
 	{N_("Portuguese-Brazil"),   "pt_BR", "Maurício de Lemos Rodrigues Collares Neto", "mauricioc@gmail.com"},
 	{N_("Russian"),             "ru", "Dmitry Beloglazov", "dmaa@users.sf.net"},
@@ -422,9 +421,8 @@
 static GtkWidget *
 pidgin_build_help_dialog(const char *title, const char *role, GString *string)
 {
-	GtkWidget *win, *vbox, *frame, *logo, *imhtml, *button;
+	GtkWidget *win, *vbox, *frame, *logo, *webview, *button;
 	GdkPixbuf *pixbuf;
-	GtkTextIter iter;
 	AtkObject *obj;
 	char *filename, *tmp;
 
@@ -451,13 +449,15 @@
 	g_free(tmp);
 	gtk_box_pack_start(GTK_BOX(vbox), logo, FALSE, FALSE, 0);
 
-	frame = pidgin_create_imhtml(FALSE, &imhtml, NULL, NULL);
+	frame = pidgin_create_webview(FALSE, &webview, NULL, NULL);
+	/* TODO WEBKIT: Compile now and fix it later when we have a proper replacement for this function
 	gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml), GTK_IMHTML_ALL ^ GTK_IMHTML_SMILEY);
+	*/
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 
-	gtk_imhtml_append_text(GTK_IMHTML(imhtml), string->str, GTK_IMHTML_NO_SCROLL);
-	gtk_text_buffer_get_start_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)), &iter);
-	gtk_text_buffer_place_cursor(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)), &iter);
+	gtk_webview_append_html(GTK_WEBVIEW(webview), string->str);
+	/* TODO WEBKIT: This doesn't seem to stay at the top. */
+	webkit_web_view_move_cursor(WEBKIT_WEB_VIEW(webview), GTK_MOVEMENT_BUFFER_ENDS, -1);
 
 	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
 	                G_CALLBACK(destroy_win), win);
--- a/pidgin/gtkdocklet-gtk.c	Mon Aug 29 12:59:57 2011 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,296 +0,0 @@
-/*
- * System tray icon (aka docklet) plugin for Purple
- *
- * Copyright (C) 2007 Anders Hasselqvist
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#include "internal.h"
-#include "pidgin.h"
-#include "debug.h"
-#include "prefs.h"
-#include "pidginstock.h"
-#include "gtkdocklet.h"
-
-#define SHORT_EMBED_TIMEOUT 5
-#define LONG_EMBED_TIMEOUT 15
-
-/* globals */
-static GtkStatusIcon *docklet = NULL;
-static guint embed_timeout = 0;
-
-/* protos */
-static void docklet_gtk_status_create(gboolean);
-
-static gboolean
-docklet_gtk_recreate_cb(gpointer data)
-{
-	docklet_gtk_status_create(TRUE);
-
-	return FALSE;
-}
-
-static gboolean
-docklet_gtk_embed_timeout_cb(gpointer data)
-{
-#if !GTK_CHECK_VERSION(2,12,0)
-	if (gtk_status_icon_is_embedded(docklet)) {
-		/* Older GTK+ (<2.12) don't implement the embedded signal, but the
-		   information is still accessable through the above function. */
-		purple_debug_info("docklet", "embedded\n");
-
-		pidgin_docklet_embedded();
-		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
-	}
-	else
-#endif
-	{
-		/* The docklet was not embedded within the timeout.
-		 * Remove it as a visibility manager, but leave the plugin
-		 * loaded so that it can embed automatically if/when a notification
-		 * area becomes available.
-		 */
-		purple_debug_info("docklet", "failed to embed within timeout\n");
-		pidgin_docklet_remove();
-		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
-	}
-
-#if GTK_CHECK_VERSION(2,12,0)
-	embed_timeout = 0;
-	return FALSE;
-#else
-	return TRUE;
-#endif
-}
-
-#if GTK_CHECK_VERSION(2,12,0)
-static gboolean
-docklet_gtk_embedded_cb(GtkWidget *widget, gpointer data)
-{
-	if (embed_timeout) {
-		purple_timeout_remove(embed_timeout);
-		embed_timeout = 0;
-	}
-
-	if (gtk_status_icon_is_embedded(docklet)) {
-		purple_debug_info("docklet", "embedded\n");
-
-		pidgin_docklet_embedded();
-		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
-	} else {
-		purple_debug_info("docklet", "detached\n");
-
-		pidgin_docklet_remove();
-		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
-	}
-
-	return TRUE;
-}
-#endif
-
-static void
-docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data)
-{
-	purple_debug_info("docklet", "destroyed\n");
-
-	pidgin_docklet_remove();
-
-	g_object_unref(G_OBJECT(docklet));
-	docklet = NULL;
-
-	g_idle_add(docklet_gtk_recreate_cb, NULL);
-}
-
-static void
-docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data)
-{
-	pidgin_docklet_clicked(1);
-}
-
-static void
-docklet_gtk_status_clicked_cb(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
-{
-	purple_debug_info("docklet", "The button is %u\n", button);
-#ifdef GDK_WINDOWING_QUARTZ
-	/* You can only click left mouse button on MacOSX native GTK. Let that be the menu */
-	pidgin_docklet_clicked(3);
-#else
-	pidgin_docklet_clicked(button);
-#endif
-}
-
-static void
-docklet_gtk_status_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
-{
-	const gchar *icon_name = NULL;
-
-	switch (status) {
-		case PURPLE_STATUS_OFFLINE:
-			icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
-			break;
-		case PURPLE_STATUS_AWAY:
-			icon_name = PIDGIN_STOCK_TRAY_AWAY;
-			break;
-		case PURPLE_STATUS_UNAVAILABLE:
-			icon_name = PIDGIN_STOCK_TRAY_BUSY;
-			break;
-		case PURPLE_STATUS_EXTENDED_AWAY:
-			icon_name = PIDGIN_STOCK_TRAY_XA;
-			break;
-		case PURPLE_STATUS_INVISIBLE:
-			icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
-			break;
-		default:
-			icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
-			break;
-	}
-
-	if (pending)
-		icon_name = PIDGIN_STOCK_TRAY_PENDING;
-	if (connecting)
-		icon_name = PIDGIN_STOCK_TRAY_CONNECT;
-
-	if (icon_name) {
-		gtk_status_icon_set_from_stock(docklet, icon_name);
-	}
-
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")) {
-		gtk_status_icon_set_blinking(docklet, (pending && !connecting));
-	} else if (gtk_status_icon_get_blinking(docklet)) {
-		gtk_status_icon_set_blinking(docklet, FALSE);
-	}
-}
-
-static void
-docklet_gtk_status_set_tooltip(gchar *tooltip)
-{
-	gtk_status_icon_set_tooltip(docklet, tooltip);
-}
-
-static void
-docklet_gtk_status_position_menu(GtkMenu *menu,
-                                 int *x, int *y, gboolean *push_in,
-                                 gpointer user_data)
-{
-	gtk_status_icon_position_menu(menu, x, y, push_in, docklet);
-}
-
-static void
-docklet_gtk_status_destroy(void)
-{
-	g_return_if_fail(docklet != NULL);
-
-	pidgin_docklet_remove();
-
-	if (embed_timeout) {
-		purple_timeout_remove(embed_timeout);
-		embed_timeout = 0;
-	}
-
-	gtk_status_icon_set_visible(docklet, FALSE);
-	g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
-	g_object_unref(G_OBJECT(docklet));
-	docklet = NULL;
-
-	purple_debug_info("docklet", "GTK+ destroyed\n");
-}
-
-static void
-docklet_gtk_status_create(gboolean recreate)
-{
-	if (docklet) {
-		/* if this is being called when a tray icon exists, it's because
-		   something messed up. try destroying it before we proceed,
-		   although docklet_refcount may be all hosed. hopefully won't happen. */
-		purple_debug_warning("docklet", "trying to create icon but it already exists?\n");
-		docklet_gtk_status_destroy();
-	}
-
-	docklet = gtk_status_icon_new();
-	g_return_if_fail(docklet != NULL);
-
-	g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL);
-	g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL);
-#if GTK_CHECK_VERSION(2,12,0)
-	g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL);
-#endif
-	g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
-
-	gtk_status_icon_set_visible(docklet, TRUE);
-
-	/* This is a hack to avoid a race condition between the docklet getting
-	 * embedded in the notification area and the gtkblist restoring its
-	 * previous visibility state.  If the docklet does not get embedded within
-	 * the timeout, it will be removed as a visibility manager until it does
-	 * get embedded.  Ideally, we would only call docklet_embedded() when the
-	 * icon was actually embedded. This only happens when the docklet is first
-	 * created, not when being recreated.
-	 *
-	 * The gtk docklet tracks whether it successfully embedded in a pref and
-	 * allows for a longer timeout period if it successfully embedded the last
-	 * time it was run. This should hopefully solve problems with the buddy
-	 * list not properly starting hidden when Pidgin is started on login.
-	 */
-	if (!recreate) {
-		pidgin_docklet_embedded();
-#if GTK_CHECK_VERSION(2,12,0)
-		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded")) {
-			embed_timeout = purple_timeout_add_seconds(LONG_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
-		} else {
-			embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
-		}
-#else
-		embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
-#endif
-	}
-
-	purple_debug_info("docklet", "GTK+ created\n");
-}
-
-static void
-docklet_gtk_status_create_ui_op(void)
-{
-	docklet_gtk_status_create(FALSE);
-}
-
-static struct docklet_ui_ops ui_ops =
-{
-	docklet_gtk_status_create_ui_op,
-	docklet_gtk_status_destroy,
-	docklet_gtk_status_update_icon,
-	NULL,
-	docklet_gtk_status_set_tooltip,
-	docklet_gtk_status_position_menu
-};
-
-void
-docklet_ui_init(void)
-{
-	pidgin_docklet_set_ui_ops(&ui_ops);
-
-	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/gtk");
-	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
-		purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
-		purple_prefs_remove(PIDGIN_PREFS_ROOT "/docklet/x11/embedded");
-	} else {
-		purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
-	}
-
-	gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
-		DATADIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S "pidgin" G_DIR_SEPARATOR_S "tray");
-}
-
--- a/pidgin/gtkdocklet.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkdocklet.c	Mon Sep 26 14:57:21 2011 +0900
@@ -30,6 +30,7 @@
 #include "prefs.h"
 #include "signals.h"
 #include "sound.h"
+#include "status.h"
 
 #include "gtkaccount.h"
 #include "gtkblist.h"
@@ -48,8 +49,12 @@
 #define DOCKLET_TOOLTIP_LINE_LIMIT 5
 #endif
 
+#define SHORT_EMBED_TIMEOUT 5
+#define LONG_EMBED_TIMEOUT 15
+
 /* globals */
-static struct docklet_ui_ops *ui_ops = NULL;
+static GtkStatusIcon *docklet = NULL;
+static guint embed_timeout = 0;
 static PurpleStatusPrimitive status = PURPLE_STATUS_OFFLINE;
 static gboolean pending = FALSE;
 static gboolean connecting = FALSE;
@@ -58,9 +63,55 @@
 static gboolean visible = FALSE;
 static gboolean visibility_manager = FALSE;
 
+/* protos */
+static void docklet_gtk_status_create(gboolean);
+static void docklet_gtk_status_destroy(void);
+
 /**************************************************************************
  * docklet status and utility functions
  **************************************************************************/
+static void
+docklet_gtk_status_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
+{
+	const gchar *icon_name = NULL;
+
+	switch (status) {
+		case PURPLE_STATUS_OFFLINE:
+			icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
+			break;
+		case PURPLE_STATUS_AWAY:
+			icon_name = PIDGIN_STOCK_TRAY_AWAY;
+			break;
+		case PURPLE_STATUS_UNAVAILABLE:
+			icon_name = PIDGIN_STOCK_TRAY_BUSY;
+			break;
+		case PURPLE_STATUS_EXTENDED_AWAY:
+			icon_name = PIDGIN_STOCK_TRAY_XA;
+			break;
+		case PURPLE_STATUS_INVISIBLE:
+			icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
+			break;
+		default:
+			icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
+			break;
+	}
+
+	if (pending)
+		icon_name = PIDGIN_STOCK_TRAY_PENDING;
+	if (connecting)
+		icon_name = PIDGIN_STOCK_TRAY_CONNECT;
+
+	if (icon_name) {
+		gtk_status_icon_set_from_icon_name(docklet, icon_name);
+	}
+
+	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")) {
+		gtk_status_icon_set_blinking(docklet, (pending && !connecting));
+	} else if (gtk_status_icon_get_blinking(docklet)) {
+		gtk_status_icon_set_blinking(docklet, FALSE);
+	}
+}
+
 static gboolean
 docklet_blink_icon(gpointer data)
 {
@@ -70,11 +121,8 @@
 	blinked = !blinked;
 
 	if(pending && !connecting) {
-		if (blinked) {
-			if (ui_ops && ui_ops->blank_icon)
-				ui_ops->blank_icon();
-		} else {
-			pidgin_docklet_update_icon();
+		if (!blinked) {
+			docklet_gtk_status_update_icon(status, connecting, pending);
 		}
 		ret = TRUE; /* keep blinking */
 	} else {
@@ -126,12 +174,12 @@
 	convs = get_pending_list(DOCKLET_TOOLTIP_LINE_LIMIT);
 
 	if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "pending")) {
-		if (convs && ui_ops->create && !visible) {
+		if (convs && !visible) {
 			g_list_free(convs);
-			ui_ops->create();
+			docklet_gtk_status_create(FALSE);
 			return FALSE;
-		} else if (!convs && ui_ops->destroy && visible) {
-			ui_ops->destroy();
+		} else if (!convs && visible) {
+			docklet_gtk_status_destroy();
 			return FALSE;
 		}
 	}
@@ -142,46 +190,43 @@
 	}
 
 	if (convs != NULL) {
+		/* set tooltip if messages are pending */
+		GString *tooltip_text = g_string_new("");
 		newpending = TRUE;
 
-		/* set tooltip if messages are pending */
-		if (ui_ops->set_tooltip) {
-			GString *tooltip_text = g_string_new("");
-			for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) {
-				PurpleConversation *conv = (PurpleConversation *)l->data;
-				PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+		for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) {
+			PurpleConversation *conv = (PurpleConversation *)l->data;
+			PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
-				if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) {
-					g_string_append(tooltip_text, _("Right-click for more unread messages...\n"));
-				} else if(gtkconv) {
-					g_string_append_printf(tooltip_text,
-						ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
-						gtkconv->unseen_count,
-						purple_conversation_get_title(conv));
-				} else {
-					g_string_append_printf(tooltip_text,
-						ngettext("%d unread message from %s\n", "%d unread messages from %s\n",
-						GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))),
-						GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")),
-						purple_conversation_get_title(conv));
-				}
+			if (count == DOCKLET_TOOLTIP_LINE_LIMIT - 1) {
+				g_string_append(tooltip_text, _("Right-click for more unread messages...\n"));
+			} else if(gtkconv) {
+				g_string_append_printf(tooltip_text,
+					ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count),
+					gtkconv->unseen_count,
+					purple_conversation_get_title(conv));
+			} else {
+				g_string_append_printf(tooltip_text,
+					ngettext("%d unread message from %s\n", "%d unread messages from %s\n",
+					GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))),
+					GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")),
+					purple_conversation_get_title(conv));
 			}
-
-			/* get rid of the last newline */
-			if (tooltip_text->len > 0)
-				tooltip_text = g_string_truncate(tooltip_text, tooltip_text->len - 1);
-
-			ui_ops->set_tooltip(tooltip_text->str);
-
-			g_string_free(tooltip_text, TRUE);
 		}
 
+		/* get rid of the last newline */
+		if (tooltip_text->len > 0)
+			tooltip_text = g_string_truncate(tooltip_text, tooltip_text->len - 1);
+
+		gtk_status_icon_set_tooltip(docklet, tooltip_text->str);
+
+		g_string_free(tooltip_text, TRUE);
 		g_list_free(convs);
 
-	} else if (ui_ops->set_tooltip) {
+	} else {
 		char *tooltip_text = g_strconcat(PIDGIN_NAME, " - ",
 			purple_savedstatus_get_title(saved_status), NULL);
-		ui_ops->set_tooltip(tooltip_text);
+		gtk_status_icon_set_tooltip(docklet, tooltip_text);
 		g_free(tooltip_text);
 	}
 
@@ -207,7 +252,7 @@
 		pending = newpending;
 		connecting = newconnecting;
 
-		pidgin_docklet_update_icon();
+		docklet_gtk_status_update_icon(status, connecting, pending);
 
 		/* and schedule the blinker function if messages are pending */
 		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")
@@ -286,17 +331,15 @@
 {
 	const char *val = value;
 	if (!strcmp(val, "always")) {
-		if (ui_ops->create) {
-			if (!visible)
-				ui_ops->create();
-			else if (!visibility_manager) {
-				pidgin_blist_visibility_manager_add();
-				visibility_manager = TRUE;
-			}
+		if (!visible)
+			docklet_gtk_status_create(FALSE);
+		else if (!visibility_manager) {
+			pidgin_blist_visibility_manager_add();
+			visibility_manager = TRUE;
 		}
 	} else if (!strcmp(val, "never")) {
-		if (visible && ui_ops->destroy)
-			ui_ops->destroy();
+		if (visible)
+			docklet_gtk_status_destroy();
 	} else {
 		if (visibility_manager) {
 			pidgin_blist_visibility_manager_remove();
@@ -672,6 +715,7 @@
 {
 	static GtkWidget *menu = NULL;
 	GtkWidget *menuitem;
+	GtkMenuPositionFunc pos_func = gtk_status_icon_position_menu;
 
 	if (menu) {
 		gtk_widget_destroy(menu);
@@ -747,24 +791,15 @@
 #ifdef _WIN32
 	g_signal_connect(menu, "leave-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL);
 	g_signal_connect(menu, "enter-notify-event", G_CALLBACK(docklet_menu_leave_enter), NULL);
+	pos_func = NULL;
 #endif
 	gtk_widget_show_all(menu);
 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
-		       ui_ops->position_menu,
-		       NULL, 0, gtk_get_current_event_time());
+		       pos_func,
+		       docklet, 0, gtk_get_current_event_time());
 }
 
-/**************************************************************************
- * public api for ui_ops
- **************************************************************************/
-void
-pidgin_docklet_update_icon()
-{
-	if (ui_ops && ui_ops->update_icon)
-		ui_ops->update_icon(status, connecting, pending);
-}
-
-void
+static void
 pidgin_docklet_clicked(int button_type)
 {
 	switch (button_type) {
@@ -785,8 +820,8 @@
 	}
 }
 
-void
-pidgin_docklet_embedded()
+static void
+pidgin_docklet_embedded(void)
 {
 	if (!visibility_manager
 	    && strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "pending")) {
@@ -795,11 +830,11 @@
 	}
 	visible = TRUE;
 	docklet_update_status();
-	pidgin_docklet_update_icon();
+	docklet_gtk_status_update_icon(status, connecting, pending);
 }
 
-void
-pidgin_docklet_remove()
+static void
+pidgin_docklet_remove(void)
 {
 	if (visible) {
 		if (visibility_manager) {
@@ -815,12 +850,183 @@
 	}
 }
 
-void
-pidgin_docklet_set_ui_ops(struct docklet_ui_ops *ops)
+static gboolean
+docklet_gtk_recreate_cb(gpointer data)
+{
+	docklet_gtk_status_create(TRUE);
+
+	return FALSE;
+}
+
+#ifndef _WIN32
+static gboolean
+docklet_gtk_embed_timeout_cb(gpointer data)
 {
-	ui_ops = ops;
+#if !GTK_CHECK_VERSION(2,12,0)
+	if (gtk_status_icon_is_embedded(docklet)) {
+		/* Older GTK+ (<2.12) don't implement the embedded signal, but the
+		   information is still accessible through the above function. */
+		purple_debug_info("docklet", "embedded\n");
+
+		pidgin_docklet_embedded();
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
+	}
+	else
+#endif
+	{
+		/* The docklet was not embedded within the timeout.
+		 * Remove it as a visibility manager, but leave the plugin
+		 * loaded so that it can embed automatically if/when a notification
+		 * area becomes available.
+		 */
+		purple_debug_info("docklet", "failed to embed within timeout\n");
+		pidgin_docklet_remove();
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
+	}
+
+#if GTK_CHECK_VERSION(2,12,0)
+	embed_timeout = 0;
+	return FALSE;
+#else
+	return TRUE;
+#endif
+}
+#endif
+
+#if GTK_CHECK_VERSION(2,12,0)
+static gboolean
+docklet_gtk_embedded_cb(GtkWidget *widget, gpointer data)
+{
+	if (embed_timeout) {
+		purple_timeout_remove(embed_timeout);
+		embed_timeout = 0;
+	}
+
+	if (gtk_status_icon_is_embedded(docklet)) {
+		purple_debug_info("docklet", "embedded\n");
+
+		pidgin_docklet_embedded();
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
+	} else {
+		purple_debug_info("docklet", "detached\n");
+
+		pidgin_docklet_remove();
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
+	}
+
+	return TRUE;
+}
+#endif
+
+static void
+docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data)
+{
+	purple_debug_info("docklet", "destroyed\n");
+
+	pidgin_docklet_remove();
+
+	g_object_unref(G_OBJECT(docklet));
+	docklet = NULL;
+
+	g_idle_add(docklet_gtk_recreate_cb, NULL);
+}
+
+static void
+docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data)
+{
+	pidgin_docklet_clicked(1);
 }
 
+static void
+docklet_gtk_status_clicked_cb(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
+{
+	purple_debug_info("docklet", "The button is %u\n", button);
+#ifdef GDK_WINDOWING_QUARTZ
+	/* You can only click left mouse button on MacOSX native GTK. Let that be the menu */
+	pidgin_docklet_clicked(3);
+#else
+	pidgin_docklet_clicked(button);
+#endif
+}
+
+static void
+docklet_gtk_status_destroy(void)
+{
+	g_return_if_fail(docklet != NULL);
+
+	pidgin_docklet_remove();
+
+	if (embed_timeout) {
+		purple_timeout_remove(embed_timeout);
+		embed_timeout = 0;
+	}
+
+	gtk_status_icon_set_visible(docklet, FALSE);
+	g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
+	g_object_unref(G_OBJECT(docklet));
+	docklet = NULL;
+
+	purple_debug_info("docklet", "GTK+ destroyed\n");
+}
+
+static void
+docklet_gtk_status_create(gboolean recreate)
+{
+	if (docklet) {
+		/* if this is being called when a tray icon exists, it's because
+		   something messed up. try destroying it before we proceed,
+		   although docklet_refcount may be all hosed. hopefully won't happen. */
+		purple_debug_warning("docklet", "trying to create icon but it already exists?\n");
+		docklet_gtk_status_destroy();
+	}
+
+	docklet = gtk_status_icon_new();
+	g_return_if_fail(docklet != NULL);
+
+	g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL);
+	g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL);
+#if GTK_CHECK_VERSION(2,12,0)
+	g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL);
+#endif
+	g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
+
+	gtk_status_icon_set_visible(docklet, TRUE);
+
+	/* This is a hack to avoid a race condition between the docklet getting
+	 * embedded in the notification area and the gtkblist restoring its
+	 * previous visibility state.  If the docklet does not get embedded within
+	 * the timeout, it will be removed as a visibility manager until it does
+	 * get embedded.  Ideally, we would only call docklet_embedded() when the
+	 * icon was actually embedded. This only happens when the docklet is first
+	 * created, not when being recreated.
+	 *
+	 * The gtk docklet tracks whether it successfully embedded in a pref and
+	 * allows for a longer timeout period if it successfully embedded the last
+	 * time it was run. This should hopefully solve problems with the buddy
+	 * list not properly starting hidden when Pidgin is started on login.
+	 */
+	if (!recreate) {
+		pidgin_docklet_embedded();
+#ifndef _WIN32
+#if GTK_CHECK_VERSION(2,12,0)
+		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded")) {
+			embed_timeout = purple_timeout_add_seconds(LONG_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
+		} else {
+			embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
+		}
+#else
+		embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
+#endif
+#endif
+	}
+
+	purple_debug_info("docklet", "GTK+ created\n");
+}
+
+/**************************************************************************
+ * public api
+ **************************************************************************/
+ 
 void*
 pidgin_docklet_get_handle()
 {
@@ -836,6 +1042,7 @@
 	void *accounts_handle = purple_accounts_get_handle();
 	void *status_handle = purple_savedstatuses_get_handle();
 	void *docklet_handle = pidgin_docklet_get_handle();
+	gchar *tmp;
 
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/blink", FALSE);
@@ -843,9 +1050,20 @@
 	purple_prefs_connect_callback(docklet_handle, PIDGIN_PREFS_ROOT "/docklet/show",
 				    docklet_show_pref_changed_cb, NULL);
 
-	docklet_ui_init();
-	if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "always") && ui_ops && ui_ops->create)
-		ui_ops->create();
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/gtk");
+	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
+		purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
+		purple_prefs_remove(PIDGIN_PREFS_ROOT "/docklet/x11/embedded");
+	} else {
+		purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
+	}
+
+	tmp = g_build_path(G_DIR_SEPARATOR_S, DATADIR, "pixmaps", "pidgin", "tray", NULL);
+	gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), tmp);
+	g_free(tmp);
+
+	if (!strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/docklet/show"), "always"))
+		docklet_gtk_status_create(FALSE);
 
 	purple_signal_connect(conn_handle, "signed-on",
 			    docklet_handle, PURPLE_CALLBACK(docklet_signed_on_cb), NULL);
@@ -874,6 +1092,7 @@
 void
 pidgin_docklet_uninit()
 {
-	if (visible && ui_ops && ui_ops->destroy)
-		ui_ops->destroy();
+	if (visible)
+		docklet_gtk_status_destroy();
 }
+
--- a/pidgin/gtkdocklet.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkdocklet.h	Mon Sep 26 14:57:21 2011 +0900
@@ -25,31 +25,9 @@
 #ifndef _GTKDOCKLET_H_
 #define _GTKDOCKLET_H_
 
-#include "status.h"
-
-struct docklet_ui_ops
-{
-	void (*create)(void);
-	void (*destroy)(void);
-	void (*update_icon)(PurpleStatusPrimitive, gboolean, gboolean);
-	void (*blank_icon)(void);
-	void (*set_tooltip)(gchar *);
-	GtkMenuPositionFunc position_menu;
-};
-
-
-/* functions in gtkdocklet.c */
-void pidgin_docklet_update_icon(void);
-void pidgin_docklet_clicked(int);
-void pidgin_docklet_embedded(void);
-void pidgin_docklet_remove(void);
-void pidgin_docklet_set_ui_ops(struct docklet_ui_ops *);
-void pidgin_docklet_unload(void);
 void pidgin_docklet_init(void);
 void pidgin_docklet_uninit(void);
 void*pidgin_docklet_get_handle(void);
 
-/* function in gtkdocklet-{gtk,x11,win32}.c */
-void docklet_ui_init(void);
+#endif /* _GTKDOCKLET_H_ */
 
-#endif /* _GTKDOCKLET_H_ */
--- a/pidgin/gtkft.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkft.c	Mon Sep 26 14:57:21 2011 +0900
@@ -113,14 +113,17 @@
 	double kbps = 0.0;
 	time_t elapsed, now;
 
-	if (xfer->end_time != 0)
-		now = xfer->end_time;
-	else
+	now = purple_xfer_get_end_time(xfer);
+	if (now == 0)
 		now = time(NULL);
 
 	kb_sent = purple_xfer_get_bytes_sent(xfer) / 1024.0;
 	kb_rem  = purple_xfer_get_bytes_remaining(xfer) / 1024.0;
-	elapsed = (xfer->start_time > 0 ? now - xfer->start_time : 0);
+	elapsed = purple_xfer_get_start_time(xfer);
+	if (elapsed > 0)
+		elapsed = now - elapsed;
+	else
+		elapsed = 0;
 	kbps    = (elapsed > 0 ? (kb_sent / elapsed) : 0);
 
 	if (kbsec != NULL) {
@@ -132,9 +135,9 @@
 		int h, m, s;
 		int secs_elapsed;
 
-		if (xfer->start_time > 0)
+		if (purple_xfer_get_start_time(xfer) > 0)
 		{
-			secs_elapsed = now - xfer->start_time;
+			secs_elapsed = now - purple_xfer_get_start_time(xfer);
 
 			h = secs_elapsed / 3600;
 			m = (secs_elapsed % 3600) / 60;
@@ -152,7 +155,7 @@
 		if (purple_xfer_is_completed(xfer)) {
 			*time_remaining = g_strdup(_("Finished"));
 		}
-		else if (purple_xfer_is_canceled(xfer)) {
+		else if (purple_xfer_is_cancelled(xfer)) {
 			*time_remaining = g_strdup(_("Cancelled"));
 		}
 		else if (purple_xfer_get_size(xfer) == 0 || (kb_sent > 0 && kbps == 0)) {
@@ -278,10 +281,10 @@
 	}
 
 	gtk_label_set_text(GTK_LABEL(dialog->local_user_label),
-								 purple_account_get_username(xfer->account));
-	gtk_label_set_text(GTK_LABEL(dialog->remote_user_label), xfer->who);
+								 purple_account_get_username(purple_xfer_get_account(xfer)));
+	gtk_label_set_text(GTK_LABEL(dialog->remote_user_label), purple_xfer_get_remote_user(xfer));
 	gtk_label_set_text(GTK_LABEL(dialog->protocol_label),
-								 purple_account_get_protocol_name(xfer->account));
+								 purple_account_get_protocol_name(purple_xfer_get_account(xfer)));
 
 	if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
 		gtk_label_set_text(GTK_LABEL(dialog->filename_label),
@@ -354,7 +357,7 @@
 #endif
 
 		gtk_widget_set_sensitive(dialog->remove_button, TRUE);
-	} else if (purple_xfer_is_canceled(xfer)) {
+	} else if (purple_xfer_is_cancelled(xfer)) {
 		gtk_widget_hide(dialog->stop_button);
 		gtk_widget_show(dialog->remove_button);
 
@@ -729,8 +732,8 @@
 	gtk_widget_show(vbox2);
 
 	/* Setup the listbox */
-	gtk_box_pack_start(GTK_BOX(vbox2), 
-		pidgin_make_scrollable(setup_tree(dialog), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 140), 
+	gtk_box_pack_start(GTK_BOX(vbox2),
+		pidgin_make_scrollable(setup_tree(dialog), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 140),
 		TRUE, TRUE, 0);
 
 	/* "Close this window when all transfers finish" */
@@ -976,7 +979,7 @@
 									PIDGIN_STOCK_FILE_CANCELED,
 									GTK_ICON_SIZE_MENU, NULL);
 
-	if (purple_xfer_is_canceled(xfer))
+	if (purple_xfer_is_cancelled(xfer))
 		status = _("Cancelled");
 	else
 		status = _("Failed");
--- a/pidgin/gtkicon-theme.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkicon-theme.c	Mon Sep 26 14:57:21 2011 +0900
@@ -26,7 +26,7 @@
 #include <gtk/gtk.h>
 
 #define PIDGIN_ICON_THEME_GET_PRIVATE(Gobject) \
-	((PidginIconThemePrivate *) ((PIDGIN_ICON_THEME(Gobject))->priv))
+	(G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PIDGIN_TYPE_ICON_THEME, PidginIconThemePrivate))
 
 /******************************************************************************
  * Structs
@@ -53,8 +53,6 @@
 {
 	PidginIconThemePrivate *priv;
 
-	(PIDGIN_ICON_THEME(instance))->priv = g_new0(PidginIconThemePrivate, 1);
-
 	priv = PIDGIN_ICON_THEME_GET_PRIVATE(instance);
 
 	priv->icon_files = g_hash_table_new_full(g_str_hash,
@@ -69,7 +67,6 @@
 	priv = PIDGIN_ICON_THEME_GET_PRIVATE(obj);
 
 	g_hash_table_destroy(priv->icon_files);
-	g_free(priv);
 
 	parent_class->finalize(obj);
 }
@@ -82,6 +79,8 @@
 	parent_class = g_type_class_peek_parent(klass);
 
 	obj_class->finalize = pidgin_icon_theme_finalize;
+
+	g_type_class_add_private(klass, sizeof(PidginIconThemePrivate));
 }
 
 GType
--- a/pidgin/gtkicon-theme.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkicon-theme.h	Mon Sep 26 14:57:21 2011 +0900
@@ -50,7 +50,6 @@
 struct _PidginIconTheme
 {
 	PurpleTheme parent;
-	gpointer priv;
 };
 
 struct _PidginIconThemeClass
--- a/pidgin/gtkimhtml.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkimhtml.c	Mon Sep 26 14:57:21 2011 +0900
@@ -90,8 +90,7 @@
 	GtkTextTag *tag;
 };
 
-typedef struct _GtkIMHtmlProtocol
-{
+typedef struct {
 	char *name;
 	int length;
 
@@ -99,7 +98,8 @@
 	gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu);
 } GtkIMHtmlProtocol;
 
-typedef struct _GtkIMHtmlFontDetail {
+/* The five elements contained in a FONT tag */
+typedef struct {
 	gushort size;
 	gchar *face;
 	gchar *fore;
@@ -3609,125 +3609,38 @@
 	gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0);
 }
 
-/* GtkIMHtmlScalable, gtk_imhtml_image, gtk_imhtml_hr */
-GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id)
-{
-	GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage));
-
-	GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
-	GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
-	GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_image_free;
-
-	im_image->pixbuf = img;
-	im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
-	im_image->width = gdk_pixbuf_get_width(img);
-	im_image->height = gdk_pixbuf_get_height(img);
-	im_image->mark = NULL;
-	im_image->filename = g_strdup(filename);
-	im_image->id = id;
-	im_image->filesel = NULL;
-
-	g_object_ref(img);
-	return GTK_IMHTML_SCALABLE(im_image);
-}
-
-static gboolean
-animate_image_cb(gpointer data)
-{
-	GtkIMHtmlImage *im_image;
-	int width, height;
-	int delay;
-
-	im_image = data;
-
-	/* Update the pointer to this GdkPixbuf frame of the animation */
-	if (gdk_pixbuf_animation_iter_advance(GTK_IMHTML_ANIMATION(im_image)->iter, NULL)) {
-		GdkPixbuf *pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
-		g_object_unref(G_OBJECT(im_image->pixbuf));
-		im_image->pixbuf = gdk_pixbuf_copy(pb);
-
-		/* Update the displayed GtkImage */
-		width = gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image));
-		height = gdk_pixbuf_get_height(gtk_image_get_pixbuf(im_image->image));
-		if (width > 0 && height > 0)
-		{
-			/* Need to scale the new frame to the same size as the old frame */
-			GdkPixbuf *tmp;
-			tmp = gdk_pixbuf_scale_simple(im_image->pixbuf, width, height, GDK_INTERP_BILINEAR);
-			gtk_image_set_from_pixbuf(im_image->image, tmp);
-			g_object_unref(G_OBJECT(tmp));
-		} else {
-			/* Display at full-size */
-			gtk_image_set_from_pixbuf(im_image->image, im_image->pixbuf);
-		}
-	}
-
-	delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
-	GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
-
-	return FALSE;
-}
-
-GtkIMHtmlScalable *gtk_imhtml_animation_new(GdkPixbufAnimation *anim, const gchar *filename, int id)
-{
-	GtkIMHtmlImage *im_image = (GtkIMHtmlImage *) g_new0(GtkIMHtmlAnimation, 1);
-
-	GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
-	GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
-	GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_animation_free;
-
-	GTK_IMHTML_ANIMATION(im_image)->anim = anim;
-	if (gdk_pixbuf_animation_is_static_image(anim)) {
-		im_image->pixbuf = gdk_pixbuf_animation_get_static_image(anim);
-		g_object_ref(im_image->pixbuf);
-	} else {
-		int delay;
-		GdkPixbuf *pb;
-		GTK_IMHTML_ANIMATION(im_image)->iter = gdk_pixbuf_animation_get_iter(anim, NULL);
-		pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
-		im_image->pixbuf = gdk_pixbuf_copy(pb);
-		delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
-		GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
-	}
-	im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
-	im_image->width = gdk_pixbuf_animation_get_width(anim);
-	im_image->height = gdk_pixbuf_animation_get_height(anim);
-	im_image->filename = g_strdup(filename);
-	im_image->id = id;
-
-	g_object_ref(anim);
-
-	return GTK_IMHTML_SCALABLE(im_image);
-}
-
-void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height)
-{
-	GtkIMHtmlImage *im_image = (GtkIMHtmlImage *)scale;
-
-	if (im_image->width > width || im_image->height > height) {
-		double ratio_w, ratio_h, ratio;
-		int new_h, new_w;
-		GdkPixbuf *new_image = NULL;
-
-		ratio_w = ((double)width - 2) / im_image->width;
-		ratio_h = ((double)height - 2) / im_image->height;
-
-		ratio = (ratio_w < ratio_h) ? ratio_w : ratio_h;
-
-		new_w = (int)(im_image->width * ratio);
-		new_h = (int)(im_image->height * ratio);
-
-		new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, new_w, new_h, GDK_INTERP_BILINEAR);
-		gtk_image_set_from_pixbuf(im_image->image, new_image);
-		g_object_unref(G_OBJECT(new_image));
-	} else if (gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image)) != im_image->width) {
-		/* Enough space to show the full-size of the image. */
-		GdkPixbuf *new_image;
-
-		new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, im_image->width, im_image->height, GDK_INTERP_BILINEAR);
-		gtk_image_set_from_pixbuf(im_image->image, new_image);
-		g_object_unref(G_OBJECT(new_image));
-	}
+/**
+ * Destroys and frees a GTK+ IM/HTML scalable image.
+ *
+ * @param scale The GTK+ IM/HTML scalable.
+ */
+static void gtk_imhtml_image_free(GtkIMHtmlScalable *scale)
+{
+	GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
+
+	g_object_unref(image->pixbuf);
+	g_free(image->filename);
+	if (image->filesel)
+		gtk_widget_destroy(image->filesel);
+	g_free(scale);
+}
+
+/**
+ * Destroys and frees a GTK+ IM/HTML scalable animation.
+ *
+ * @param scale The GTK+ IM/HTML scalable.
+ */
+static void gtk_imhtml_animation_free(GtkIMHtmlScalable *scale)
+{
+	GtkIMHtmlAnimation *animation = (GtkIMHtmlAnimation *)scale;
+
+	if (animation->timer > 0)
+		g_source_remove(animation->timer);
+	if (animation->iter != NULL)
+		g_object_unref(animation->iter);
+	g_object_unref(animation->anim);
+
+	gtk_imhtml_image_free(scale);
 }
 
 static void
@@ -3934,55 +3847,51 @@
 
 }
 
-static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley)
-{
-	GdkPixbufAnimation *anim = NULL;
-	GtkIMHtmlImageSave *save = NULL;
-	gboolean ret;
-
-	if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3)
-		return FALSE;
-
-	anim = gtk_smiley_get_image(smiley);
-	if (!anim)
-		return FALSE;
-
-	save = g_new0(GtkIMHtmlImageSave, 1);
-	save->image = (GtkIMHtmlScalable *)gtk_imhtml_animation_new(anim, smiley->smile, 0);
-	save->data = smiley->data;        /* Do not need to memdup here, since the smiley is not
-	                                     destroyed before this GtkIMHtmlImageSave */
-	save->datasize = smiley->datasize;
-	ret = gtk_imhtml_image_clicked(w, event, save);
-	g_object_set_data_full(G_OBJECT(w), "image-data", save->image, (GDestroyNotify)gtk_imhtml_animation_free);
-	g_object_set_data_full(G_OBJECT(w), "image-save-data", save, (GDestroyNotify)g_free);
-	return ret;
-}
-
-void gtk_imhtml_image_free(GtkIMHtmlScalable *scale)
-{
-	GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
-
-	g_object_unref(image->pixbuf);
-	g_free(image->filename);
-	if (image->filesel)
-		gtk_widget_destroy(image->filesel);
-	g_free(scale);
-}
-
-void gtk_imhtml_animation_free(GtkIMHtmlScalable *scale)
-{
-	GtkIMHtmlAnimation *animation = (GtkIMHtmlAnimation *)scale;
-
-	if (animation->timer > 0)
-		g_source_remove(animation->timer);
-	if (animation->iter != NULL)
-		g_object_unref(animation->iter);
-	g_object_unref(animation->anim);
-
-	gtk_imhtml_image_free(scale);
-}
-
-void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
+/**
+ * Rescales a GTK+ IM/HTML scalable image to a given size.
+ *
+ * @param scale  The GTK+ IM/HTML scalable.
+ * @param width  The new width.
+ * @param height The new height.
+ */
+static void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height)
+{
+	GtkIMHtmlImage *im_image = (GtkIMHtmlImage *)scale;
+
+	if (im_image->width > width || im_image->height > height) {
+		double ratio_w, ratio_h, ratio;
+		int new_h, new_w;
+		GdkPixbuf *new_image = NULL;
+
+		ratio_w = ((double)width - 2) / im_image->width;
+		ratio_h = ((double)height - 2) / im_image->height;
+
+		ratio = (ratio_w < ratio_h) ? ratio_w : ratio_h;
+
+		new_w = (int)(im_image->width * ratio);
+		new_h = (int)(im_image->height * ratio);
+
+		new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, new_w, new_h, GDK_INTERP_BILINEAR);
+		gtk_image_set_from_pixbuf(im_image->image, new_image);
+		g_object_unref(G_OBJECT(new_image));
+	} else if (gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image)) != im_image->width) {
+		/* Enough space to show the full-size of the image. */
+		GdkPixbuf *new_image;
+
+		new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, im_image->width, im_image->height, GDK_INTERP_BILINEAR);
+		gtk_image_set_from_pixbuf(im_image->image, new_image);
+		g_object_unref(G_OBJECT(new_image));
+	}
+}
+
+/**
+ * Adds a GTK+ IM/HTML scalable image to a given GTK+ IM/HTML at a given iter.
+ *
+ * @param scale  The GTK+ IM/HTML scalable.
+ * @param imhtml The GTK+ IM/HTML.
+ * @param iter   The GtkTextIter at which to add the scalable.
+ */
+static void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
 {
 	GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
 	GtkWidget *box = gtk_event_box_new();
@@ -4010,6 +3919,121 @@
 	g_object_set_data_full(G_OBJECT(box), "image-save-data", save, (GDestroyNotify)g_free);
 }
 
+/**
+ * Creates and returns a new GTK+ IM/HTML scalable object with an image.
+ *
+ * @param img      A GdkPixbuf of the image to add.
+ * @param filename The filename to associate with the image.
+ * @param id       The id to associate with the image.
+ *
+ * @return A new IM/HTML Scalable object with an image.
+ */
+static GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id)
+{
+	GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage));
+
+	GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
+	GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
+	GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_image_free;
+
+	im_image->pixbuf = img;
+	im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
+	im_image->width = gdk_pixbuf_get_width(img);
+	im_image->height = gdk_pixbuf_get_height(img);
+	im_image->mark = NULL;
+	im_image->filename = g_strdup(filename);
+	im_image->id = id;
+	im_image->filesel = NULL;
+
+	g_object_ref(img);
+	return GTK_IMHTML_SCALABLE(im_image);
+}
+
+static gboolean
+animate_image_cb(gpointer data)
+{
+	GtkIMHtmlImage *im_image;
+	int width, height;
+	int delay;
+
+	im_image = data;
+
+	/* Update the pointer to this GdkPixbuf frame of the animation */
+	if (gdk_pixbuf_animation_iter_advance(GTK_IMHTML_ANIMATION(im_image)->iter, NULL)) {
+		GdkPixbuf *pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
+		g_object_unref(G_OBJECT(im_image->pixbuf));
+		im_image->pixbuf = gdk_pixbuf_copy(pb);
+
+		/* Update the displayed GtkImage */
+		width = gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image));
+		height = gdk_pixbuf_get_height(gtk_image_get_pixbuf(im_image->image));
+		if (width > 0 && height > 0)
+		{
+			/* Need to scale the new frame to the same size as the old frame */
+			GdkPixbuf *tmp;
+			tmp = gdk_pixbuf_scale_simple(im_image->pixbuf, width, height, GDK_INTERP_BILINEAR);
+			gtk_image_set_from_pixbuf(im_image->image, tmp);
+			g_object_unref(G_OBJECT(tmp));
+		} else {
+			/* Display at full-size */
+			gtk_image_set_from_pixbuf(im_image->image, im_image->pixbuf);
+		}
+	}
+
+	delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
+	GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
+
+	return FALSE;
+}
+
+/**
+ * Creates and returns a new GTK+ IM/HTML scalable object with an
+ * animated image.
+ *
+ * @param img      A GdkPixbufAnimation of the image to add.
+ * @param filename The filename to associate with the image.
+ * @param id       The id to associate with the image.
+ *
+ * @return A new IM/HTML Scalable object with an image.
+ */
+/*
+ * TODO: All this animation code could be combined much better with
+ *       the image code.  It couldn't be done when it was written
+ *       because it requires breaking backward compatibility.  It
+ *       would be good to do it for 3.0.0.
+ */
+static GtkIMHtmlScalable *gtk_imhtml_animation_new(GdkPixbufAnimation *anim, const gchar *filename, int id)
+{
+	GtkIMHtmlImage *im_image = (GtkIMHtmlImage *) g_new0(GtkIMHtmlAnimation, 1);
+
+	GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
+	GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
+	GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_animation_free;
+
+	GTK_IMHTML_ANIMATION(im_image)->anim = anim;
+	if (gdk_pixbuf_animation_is_static_image(anim)) {
+		im_image->pixbuf = gdk_pixbuf_animation_get_static_image(anim);
+		g_object_ref(im_image->pixbuf);
+	} else {
+		int delay;
+		GdkPixbuf *pb;
+		GTK_IMHTML_ANIMATION(im_image)->iter = gdk_pixbuf_animation_get_iter(anim, NULL);
+		pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
+		im_image->pixbuf = gdk_pixbuf_copy(pb);
+		delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
+		GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
+	}
+	im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
+	im_image->width = gdk_pixbuf_animation_get_width(anim);
+	im_image->height = gdk_pixbuf_animation_get_height(anim);
+	im_image->filename = g_strdup(filename);
+	im_image->id = id;
+
+	g_object_ref(anim);
+
+	return GTK_IMHTML_SCALABLE(im_image);
+}
+
 GtkIMHtmlScalable *gtk_imhtml_hr_new()
 {
 	GtkIMHtmlHr *hr = g_malloc(sizeof(GtkIMHtmlHr));
@@ -4934,6 +4958,30 @@
 	}
 }
 
+static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley)
+{
+	GdkPixbufAnimation *anim = NULL;
+	GtkIMHtmlImageSave *save = NULL;
+	gboolean ret;
+
+	if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3)
+		return FALSE;
+
+	anim = gtk_smiley_get_image(smiley);
+	if (!anim)
+		return FALSE;
+
+	save = g_new0(GtkIMHtmlImageSave, 1);
+	save->image = (GtkIMHtmlScalable *)gtk_imhtml_animation_new(anim, smiley->smile, 0);
+	save->data = smiley->data;        /* Do not need to memdup here, since the smiley is not
+	                                     destroyed before this GtkIMHtmlImageSave */
+	save->datasize = smiley->datasize;
+	ret = gtk_imhtml_image_clicked(w, event, save);
+	g_object_set_data_full(G_OBJECT(w), "image-data", save->image, (GDestroyNotify)gtk_imhtml_animation_free);
+	g_object_set_data_full(G_OBJECT(w), "image-save-data", save, (GDestroyNotify)g_free);
+	return ret;
+}
+
 void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *smiley, GtkTextIter *iter)
 {
 	GdkPixbuf *pixbuf = NULL;
--- a/pidgin/gtkimhtml.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkimhtml.h	Mon Sep 26 14:57:21 2011 +0900
@@ -50,9 +50,6 @@
 
 typedef struct _GtkIMHtml			GtkIMHtml;
 typedef struct _GtkIMHtmlClass		GtkIMHtmlClass;
-#if !(defined PIDGIN_DISABLE_DEPRECATED) && !(defined _PIDGIN_GTKIMHTML_C_)
-typedef struct _GtkIMHtmlFontDetail	GtkIMHtmlFontDetail;	/* The five elements contained in a FONT tag */
-#endif
 typedef struct _GtkSmileyTree		GtkSmileyTree;
 typedef struct _GtkIMHtmlSmiley		GtkIMHtmlSmiley;
 typedef struct _GtkIMHtmlScalable	GtkIMHtmlScalable;
@@ -61,9 +58,6 @@
 typedef struct _GtkIMHtmlHr			GtkIMHtmlHr;
 typedef struct _GtkIMHtmlFuncs		GtkIMHtmlFuncs;
 
-/**
- * @since 2.6.0
- */
 typedef struct _GtkIMHtmlLink       GtkIMHtmlLink;
 
 typedef enum {
@@ -81,7 +75,7 @@
 	GTK_IMHTML_SMILEY =     1 << 11,
 	GTK_IMHTML_LINKDESC =   1 << 12,
 	GTK_IMHTML_STRIKE =     1 << 13,
-	/** Show custom smileys when appropriate. @since 2.5.0 */
+	/** Show custom smileys when appropriate. */
 	GTK_IMHTML_CUSTOM_SMILEY = 1 << 14,
 	GTK_IMHTML_ALL =       -1
 } GtkIMHtmlButtons;
@@ -169,8 +163,8 @@
 	GSList *anchors;
 	GtkIMHtmlSmileyFlags flags;
 	GtkIMHtml *imhtml;
-	gpointer data;       /** @since 2.6.0 */
-	gsize datasize;      /** @since 2.6.0 */
+	gpointer data;
+	gsize datasize;
 };
 
 struct _GtkIMHtmlScalable {
@@ -419,73 +413,6 @@
 GtkIMHtmlScalable *gtk_imhtml_scalable_new(void);
 
 /**
- * Creates and returns a new GTK+ IM/HTML scalable object with an image.
- *
- * @param img      A GdkPixbuf of the image to add.
- * @param filename The filename to associate with the image.
- * @param id       The id to associate with the image.
- *
- * @return A new IM/HTML Scalable object with an image.
- */
-GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id);
-
-/**
- * Creates and returns a new GTK+ IM/HTML scalable object with an
- * animated image.
- *
- * @param img      A GdkPixbufAnimation of the image to add.
- * @param filename The filename to associate with the image.
- * @param id       The id to associate with the image.
- *
- * @return A new IM/HTML Scalable object with an image.
- *
- * @since 2.1.0
- */
-/*
- * TODO: All this animation code could be combined much better with
- *       the image code.  It couldn't be done when it was written
- *       because it requires breaking backward compatibility.  It
- *       would be good to do it for 3.0.0.
- */
-GtkIMHtmlScalable *gtk_imhtml_animation_new(GdkPixbufAnimation *img, const gchar *filename, int id);
-
-/**
- * Destroys and frees a GTK+ IM/HTML scalable image.
- *
- * @param scale The GTK+ IM/HTML scalable.
- */
-/* TODO: Is there any reason this isn't private? */
-void gtk_imhtml_image_free(GtkIMHtmlScalable *scale);
-
-/**
- * Destroys and frees a GTK+ IM/HTML scalable animation.
- *
- * @param scale The GTK+ IM/HTML scalable.
- */
-/* TODO: Is there any reason this isn't private? */
-void gtk_imhtml_animation_free(GtkIMHtmlScalable *scale);
-
-/**
- * Rescales a GTK+ IM/HTML scalable image to a given size.
- *
- * @param scale  The GTK+ IM/HTML scalable.
- * @param width  The new width.
- * @param height The new height.
- */
-/* TODO: Is there any reason this isn't private? */
-void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height);
-
-/**
- * Adds a GTK+ IM/HTML scalable image to a given GTK+ IM/HTML at a given iter.
- *
- * @param scale  The GTK+ IM/HTML scalable.
- * @param imhtml The GTK+ IM/HTML.
- * @param iter   The GtkTextIter at which to add the scalable.
- */
-/* TODO: Is there any reason this isn't private? */
-void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter);
-
-/**
  * Creates and returns an new GTK+ IM/HTML scalable with a horizontal rule.
  *
  * @return A new IM/HTML Scalable object with an image.
@@ -836,8 +763,6 @@
  *
  * @param imhtml  The GTK+ IM/HTML.
  * @param flags   The connection flag which describes the allowed types of formatting.
- *
- * @since 2.1.0
  */
 void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
 
@@ -850,7 +775,6 @@
  * @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);
@@ -859,8 +783,6 @@
  * Reload the image data for the smiley.
  *
  * @param smiley   The smiley to reload
- *
- * @since 2.5.0
  */
 void gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley);
 
@@ -868,8 +790,6 @@
  * Destroy a GtkIMHtmlSmiley.
  *
  * @param smiley   The smiley to destroy
- *
- * @since 2.5.0
  */
 void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley);
 
@@ -888,8 +808,6 @@
  *                      successfully, @c FALSE otherwise.
  *
  * @return  @c TRUE if the protocol was successfully registered (or unregistered, when \a activate is @c NULL)
- *
- * @since 2.6.0
  */
 gboolean gtk_imhtml_class_register_protocol(const char *name,
 		gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link),
@@ -901,8 +819,6 @@
  * @param link   The GtkIMHtmlLink object sent to the callback functions
  *
  * @return  The URL
- *
- * @since 2.6.0
  */
 const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link);
 
@@ -912,8 +828,6 @@
  * @param link   The GtkIMHtmlLink object sent to the callback functions
  *
  * @return  The GtkTextTag object, or @c NULL
- *
- * @since 2.6.0
  */
 const GtkTextTag *gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link);
 
@@ -924,8 +838,6 @@
  * @param link   The GtkIMHtmlLink object sent to the callback functions
  *
  * @return  @c TRUE if 'url-clicked' signal was emitted, @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link);
 
--- a/pidgin/gtkimhtmltoolbar.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkimhtmltoolbar.h	Mon Sep 26 14:57:21 2011 +0900
@@ -90,9 +90,6 @@
 void gtk_imhtmltoolbar_attach    (GtkIMHtmlToolbar *toolbar, GtkWidget *imhtml);
 void gtk_imhtmltoolbar_associate_smileys (GtkIMHtmlToolbar *toolbar, const char *proto_id);
 
-/**
- * @since 2.7.0
- */
 void gtk_imhtmltoolbar_switch_active_conversation(GtkIMHtmlToolbar *toolbar,
 	PurpleConversation *conv);
 
--- a/pidgin/gtklog.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtklog.c	Mon Sep 26 14:57:21 2011 +0900
@@ -35,9 +35,9 @@
 
 #include "pidginstock.h"
 #include "gtkblist.h"
-#include "gtkimhtml.h"
 #include "gtklog.h"
 #include "gtkutils.h"
+#include "gtkwebview.h"
 
 static GHashTable *log_viewers = NULL;
 static void populate_log_tree(PidginLogViewer *lv);
@@ -130,7 +130,7 @@
 		populate_log_tree(lv);
 		g_free(lv->search);
 		lv->search = NULL;
-		gtk_imhtml_search_clear(GTK_IMHTML(lv->imhtml));
+		webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(lv->web_view));
 		select_first_log(lv);
 		return;
 	}
@@ -138,7 +138,7 @@
 	if (lv->search != NULL && !strcmp(lv->search, search_term))
 	{
 		/* Searching for the same term acts as "Find Next" */
-		gtk_imhtml_search_find(GTK_IMHTML(lv->imhtml), lv->search);
+		webkit_web_view_search_text(WEBKIT_WEB_VIEW(lv->web_view), lv->search, FALSE, TRUE, TRUE);
 		return;
 	}
 
@@ -148,7 +148,7 @@
 	lv->search = g_strdup(search_term);
 
 	gtk_tree_store_clear(lv->treestore);
-	gtk_imhtml_clear(GTK_IMHTML(lv->imhtml));
+	webkit_web_view_open(WEBKIT_WEB_VIEW(lv->web_view), "about:blank"); /* clear the view */
 
 	for (logs = lv->logs; logs != NULL; logs = logs->next) {
 		char *read = purple_log_read((PurpleLog*)logs->data, NULL);
@@ -419,8 +419,9 @@
 static gboolean search_find_cb(gpointer data)
 {
 	PidginLogViewer *viewer = data;
-	gtk_imhtml_search_find(GTK_IMHTML(viewer->imhtml), viewer->search);
-	g_object_steal_data(G_OBJECT(viewer->entry), "search-find-cb");
+	webkit_web_view_mark_text_matches(WEBKIT_WEB_VIEW(viewer->web_view), viewer->search, FALSE, 0);
+	webkit_web_view_set_highlight_text_matches(WEBKIT_WEB_VIEW(viewer->web_view), TRUE);
+	webkit_web_view_search_text(WEBKIT_WEB_VIEW(viewer->web_view), viewer->search, FALSE, TRUE, TRUE);
 	return FALSE;
 }
 
@@ -461,23 +462,16 @@
 	read = purple_log_read(log, &flags);
 	viewer->flags = flags;
 
-	gtk_imhtml_clear(GTK_IMHTML(viewer->imhtml));
-	gtk_imhtml_set_protocol_name(GTK_IMHTML(viewer->imhtml),
-	                            purple_account_get_protocol_name(log->account));
+	webkit_web_view_open(WEBKIT_WEB_VIEW(viewer->web_view), "about:blank");
 
 	purple_signal_emit(pidgin_log_get_handle(), "log-displaying", viewer, log);
 
-	gtk_imhtml_append_text(GTK_IMHTML(viewer->imhtml), read,
-			       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_TITLE | GTK_IMHTML_NO_SCROLL |
-			       ((flags & PURPLE_LOG_READ_NO_NEWLINE) ? GTK_IMHTML_NO_NEWLINE : 0));
+	webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(viewer->web_view), read, "");
 	g_free(read);
 
 	if (viewer->search != NULL) {
-		guint source;
-		gtk_imhtml_search_clear(GTK_IMHTML(viewer->imhtml));
-		source = g_idle_add(search_find_cb, viewer);
-		g_object_set_data_full(G_OBJECT(viewer->entry), "search-find-cb",
-		                       GINT_TO_POINTER(source), (GDestroyNotify)g_source_remove);
+		webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(viewer->web_view));
+		g_idle_add(search_find_cb, viewer);
 	}
 
 	pidgin_clear_cursor(viewer->window);
@@ -620,7 +614,7 @@
 	col = gtk_tree_view_column_new_with_attributes ("time", rend, "markup", 0, NULL);
 	gtk_tree_view_append_column (GTK_TREE_VIEW(lv->treeview), col);
 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (lv->treeview), FALSE);
-	gtk_paned_add1(GTK_PANED(pane), 
+	gtk_paned_add1(GTK_PANED(pane),
 		pidgin_make_scrollable(lv->treeview, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1));
 
 	populate_log_tree(lv);
@@ -655,9 +649,9 @@
 	gtk_paned_add2(GTK_PANED(pane), vbox);
 
 	/* Viewer ************/
-	frame = pidgin_create_imhtml(FALSE, &lv->imhtml, NULL, NULL);
-	gtk_widget_set_name(lv->imhtml, "pidgin_log_imhtml");
-	gtk_widget_set_size_request(lv->imhtml, 320, 200);
+	frame = pidgin_create_webview(FALSE, &lv->web_view, NULL, NULL);
+	gtk_widget_set_name(lv->web_view, "pidgin_log_web_view");
+	gtk_widget_set_size_request(lv->web_view, 320, 200);
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
--- a/pidgin/gtklog.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtklog.h	Mon Sep 26 14:57:21 2011 +0900
@@ -43,7 +43,7 @@
 	GtkWidget        *window;    /**< The viewer's window                      */
 	GtkTreeStore     *treestore; /**< The treestore containing said logs       */
 	GtkWidget        *treeview;  /**< The treeview representing said treestore */
-	GtkWidget        *imhtml;    /**< The imhtml to display said logs          */
+	GtkWidget        *web_view;  /**< The webkit web view to display said logs */
 	GtkWidget        *entry;     /**< The search entry, in which search terms
 	                              *   are entered                              */
 	PurpleLogReadFlags flags;      /**< The most recently used log flags         */
--- a/pidgin/gtknotify.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtknotify.c	Mon Sep 26 14:57:21 2011 +0900
@@ -36,10 +36,10 @@
 #include "util.h"
 
 #include "gtkblist.h"
-#include "gtkimhtml.h"
 #include "gtknotify.h"
 #include "gtkpounce.h"
 #include "gtkutils.h"
+#include "gtkwebview.h"
 
 typedef struct
 {
@@ -810,21 +810,6 @@
 	return FALSE;
 }
 
-static GtkIMHtmlOptions
-notify_imhtml_options(void)
-{
-	GtkIMHtmlOptions options = 0;
-
-	if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting"))
-		options |= GTK_IMHTML_NO_COLOURS | GTK_IMHTML_NO_FONTS | GTK_IMHTML_NO_SIZES;
-
-	options |= GTK_IMHTML_NO_COMMENTS;
-	options |= GTK_IMHTML_NO_TITLE;
-	options |= GTK_IMHTML_NO_NEWLINE;
-	options |= GTK_IMHTML_NO_SCROLL;
-	return options;
-}
-
 static void *
 pidgin_notify_formatted(const char *title, const char *primary,
 						  const char *secondary, const char *text)
@@ -833,7 +818,7 @@
 	GtkWidget *vbox;
 	GtkWidget *label;
 	GtkWidget *button;
-	GtkWidget *imhtml;
+	GtkWidget *web_view;
 	GtkWidget *frame;
 	char label_text[2048];
 	char *linked_text, *primary_esc, *secondary_esc;
@@ -869,12 +854,10 @@
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 	gtk_widget_show(label);
 
-	/* Add the imhtml */
-	frame = pidgin_create_imhtml(FALSE, &imhtml, NULL, NULL);
-	gtk_widget_set_name(imhtml, "pidgin_notify_imhtml");
-	gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml),
-			gtk_imhtml_get_format_functions(GTK_IMHTML(imhtml)) | GTK_IMHTML_IMAGE);
-	gtk_widget_set_size_request(imhtml, 300, 250);
+	/* Add the webview */
+	frame = pidgin_create_webview(FALSE, &web_view, NULL, NULL);
+	gtk_widget_set_name(web_view, "pidgin_notify_webview");
+	gtk_widget_set_size_request(web_view, 300, 250);
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
@@ -889,10 +872,10 @@
 
 	/* Make sure URLs are clickable */
 	linked_text = purple_markup_linkify(text);
-	gtk_imhtml_append_text(GTK_IMHTML(imhtml), linked_text, notify_imhtml_options());
+	webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(web_view), linked_text, "");
 	g_free(linked_text);
 
-	g_object_set_data(G_OBJECT(window), "info-widget", imhtml);
+	g_object_set_data(G_OBJECT(window), "webview-widget", web_view);
 
 	/* Show the window */
 	pidgin_auto_parent_window(window);
@@ -1147,10 +1130,9 @@
 	info = purple_notify_user_info_get_text_with_newline(user_info, "<br />");
 	pinfo = g_hash_table_lookup(userinfo, key);
 	if (pinfo != NULL) {
-		GtkIMHtml *imhtml = g_object_get_data(G_OBJECT(pinfo->window), "info-widget");
+		GtkWidget *webview = g_object_get_data(G_OBJECT(pinfo->window), "webview-widget");
 		char *linked_text = purple_markup_linkify(info);
-		gtk_imhtml_clear(imhtml);
-		gtk_imhtml_append_text(imhtml, linked_text, notify_imhtml_options());
+		gtk_webview_load_html_string_with_imgstore(GTK_WEBVIEW(webview), linked_text);
 		g_free(linked_text);
 		g_free(key);
 		ui_handle = pinfo->window;
@@ -1653,7 +1635,7 @@
 	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), 
+	gtk_box_pack_start(GTK_BOX(vbox),
 		pidgin_make_scrollable(spec_dialog->treeview, GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, -1),
 		TRUE, TRUE, 2);
 
--- a/pidgin/gtkprefs.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkprefs.c	Mon Sep 26 14:57:21 2011 +0900
@@ -45,6 +45,7 @@
 
 #include "gtkblist.h"
 #include "gtkconv.h"
+#include "gtkconv-theme.h"
 #include "gtkdebug.h"
 #include "gtkdialogs.h"
 #include "gtkimhtml.h"
@@ -83,6 +84,8 @@
 /* Themes page */
 static GtkWidget *prefs_sound_themes_combo_box;
 static GtkWidget *prefs_blist_themes_combo_box;
+static GtkWidget *prefs_conv_themes_combo_box;
+static GtkWidget *prefs_conv_variants_combo_box;
 static GtkWidget *prefs_status_themes_combo_box;
 static GtkWidget *prefs_smiley_themes_combo_box;
 
@@ -94,6 +97,8 @@
 /* These exist outside the lifetime of the prefs dialog */
 static GtkListStore *prefs_sound_themes;
 static GtkListStore *prefs_blist_themes;
+static GtkListStore *prefs_conv_themes;
+static GtkListStore *prefs_conv_variants;
 static GtkListStore *prefs_status_icon_themes;
 static GtkListStore *prefs_smiley_themes;
 
@@ -339,6 +344,8 @@
 
 	prefs_sound_themes_combo_box = NULL;
 	prefs_blist_themes_combo_box = NULL;
+	prefs_conv_themes_combo_box = NULL;
+	prefs_conv_variants_combo_box = NULL;
 	prefs_status_themes_combo_box = NULL;
 	prefs_smiley_themes_combo_box = NULL;
 
@@ -490,6 +497,18 @@
 		g_free(markup);
 		if (pixbuf != NULL)
 			g_object_unref(G_OBJECT(pixbuf));
+
+	} else if (PIDGIN_IS_CONV_THEME(theme)) {
+		/* No image available? */
+
+		name = purple_theme_get_name(theme);
+		/* No author available */
+		/* No description available */
+
+		markup = get_theme_markup(name, FALSE, NULL, NULL);
+
+		gtk_list_store_append(prefs_conv_themes, &iter);
+		gtk_list_store_set(prefs_conv_themes, &iter, 1, markup, 2, name, -1);
 	}
 }
 
@@ -545,6 +564,17 @@
 	gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1);
 	g_free(tmp);
 
+	/* conversation themes */
+	gtk_list_store_clear(prefs_conv_themes);
+	gtk_list_store_append(prefs_conv_themes, &iter);
+	tmp = get_theme_markup(_("Default"), FALSE, _("Penguin Pimps"),
+		_("The default Pidgin conversation theme"));
+	gtk_list_store_set(prefs_conv_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1);
+	g_free(tmp);
+
+	/* conversation theme variants */
+	gtk_list_store_clear(prefs_conv_variants);
+
 	/* status icon themes */
 	gtk_list_store_clear(prefs_status_icon_themes);
 	gtk_list_store_append(prefs_status_icon_themes, &iter);
@@ -565,6 +595,7 @@
 	/* set active */
 	prefs_set_active_theme_combo(prefs_sound_themes_combo_box, prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"));
 	prefs_set_active_theme_combo(prefs_blist_themes_combo_box, prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"));
+	prefs_set_active_theme_combo(prefs_conv_themes_combo_box, prefs_conv_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/theme"));
 	prefs_set_active_theme_combo(prefs_status_themes_combo_box, prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"));
 	prefs_set_active_theme_combo(prefs_smiley_themes_combo_box, prefs_smiley_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/smileys/theme"));
 	prefs_sound_themes_loading = FALSE;
@@ -578,6 +609,10 @@
 
 	prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
 
+	prefs_conv_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	prefs_conv_variants = gtk_list_store_new(1, G_TYPE_STRING);
+
 	prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
 
 	prefs_smiley_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
@@ -856,7 +891,7 @@
 		} else if (!g_ascii_strncasecmp(name, "http://", 7)) {
 			/* Oo, a web drag and drop. This is where things
 			 * will start to get interesting */
-			purple_util_fetch_url(name, TRUE, NULL, FALSE, theme_got_url, info);
+			purple_util_fetch_url(name, TRUE, NULL, FALSE, -1, theme_got_url, info);
 		} else if (!g_ascii_strncasecmp(name, "https://", 8)) {
 			/* purple_util_fetch_url() doesn't support HTTPS, but we want users
 			 * to be able to drag and drop links from the SF trackers, so
@@ -867,7 +902,7 @@
 			tmp[2] = 't';
 			tmp[3] = 'p';
 
-			purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, info);
+			purple_util_fetch_url(tmp, TRUE, NULL, FALSE, -1, theme_got_url, info);
 			g_free(tmp);
 		} else
 			free_theme_info(info);
@@ -1019,6 +1054,72 @@
 	}
 }
 
+/* sets the current conversation theme */
+static void
+prefs_set_conv_theme_cb(GtkComboBox *combo_box, gpointer user_data)
+{
+	PidginConvTheme *theme =  NULL;
+	GtkTreeIter iter;
+	gchar *name = NULL;
+
+	if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
+		const GList *variants;
+		const char *current_variant;
+		gboolean unset = TRUE;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(prefs_conv_themes), &iter, 2, &name, -1);
+		if (!name || !*name) {
+			g_free(name);
+			return;
+		}
+
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/theme", name);
+
+		/* Update list of variants */
+		gtk_list_store_clear(prefs_conv_variants);
+
+		theme = PIDGIN_CONV_THEME(purple_theme_manager_find_theme(name, "conversation"));
+		current_variant = pidgin_conversation_theme_get_variant(theme);
+
+		variants = pidgin_conversation_theme_get_variants(theme);
+		for (; variants && current_variant; variants = g_list_next(variants)) {
+			gtk_list_store_append(prefs_conv_variants, &iter);
+			gtk_list_store_set(prefs_conv_variants, &iter, 0, variants->data, -1);
+
+			if (g_str_equal(variants->data, current_variant)) {
+				gtk_combo_box_set_active_iter(GTK_COMBO_BOX(prefs_conv_variants_combo_box), &iter);
+				unset = FALSE;
+			}
+		}
+
+		if (unset)
+			gtk_combo_box_set_active(GTK_COMBO_BOX(prefs_conv_variants_combo_box), 0);
+
+		g_free(name);
+	}
+}
+
+/* sets the current conversation theme variant */
+static void
+prefs_set_conv_variant_cb(GtkComboBox *combo_box, gpointer user_data)
+{
+	PidginConvTheme *theme =  NULL;
+	GtkTreeIter iter;
+	gchar *name = NULL;
+
+	if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(prefs_conv_themes_combo_box), &iter)) {
+		gtk_tree_model_get(GTK_TREE_MODEL(prefs_conv_themes), &iter, 2, &name, -1);
+		theme = PIDGIN_CONV_THEME(purple_theme_manager_find_theme(name, "conversation"));
+		g_free(name);
+
+		if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
+			gtk_tree_model_get(GTK_TREE_MODEL(prefs_conv_variants), &iter, 0, &name, -1);
+			pidgin_conversation_theme_set_variant(theme, name);
+			g_free(name);
+		}
+	}
+}
+
 /* sets the current icon theme */
 static void
 prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data)
@@ -1072,6 +1173,40 @@
 }
 
 static GtkWidget *
+add_child_theme_prefs_combo(GtkWidget *vbox, GtkSizeGroup *combo_sg,
+                             GtkSizeGroup *label_sg, GtkListStore *theme_store,
+                             GCallback combo_box_cb, gpointer combo_box_cb_user_data,
+                             const char *label_str)
+{
+	GtkWidget *label;
+	GtkWidget *combo_box;
+	GtkWidget *themesel_hbox;
+	GtkCellRenderer *cell_rend;
+
+	themesel_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new(label_str);
+	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+	gtk_size_group_add_widget(label_sg, label);
+	gtk_box_pack_start(GTK_BOX(themesel_hbox), label, FALSE, FALSE, 0);
+
+	combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(theme_store));
+
+	cell_rend = gtk_cell_renderer_text_new();
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), cell_rend, TRUE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "text", 0, NULL);
+	g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+	g_signal_connect(G_OBJECT(combo_box), "changed",
+						(GCallback)combo_box_cb, combo_box_cb_user_data);
+	gtk_size_group_add_widget(combo_sg, combo_box);
+	gtk_box_pack_start(GTK_BOX(themesel_hbox), combo_box, TRUE, TRUE, 0);
+
+	return combo_box;
+}
+
+static GtkWidget *
 theme_page(void)
 {
 	GtkWidget *label;
@@ -1101,6 +1236,20 @@
 		(GCallback)prefs_set_blist_theme_cb, NULL,
 		_("Buddy List Theme:"), PIDGIN_PREFS_ROOT "/blist/theme", "blist");
 
+	/* Conversation Themes */
+	prefs_conv_themes_combo_box = add_theme_prefs_combo(
+		vbox, combo_sg, label_sg, prefs_conv_themes,
+		(GCallback)prefs_set_conv_theme_cb, NULL,
+		_("Conversation Theme:"), PIDGIN_PREFS_ROOT "/conversations/theme", "conversation");
+
+	/* Conversation Theme Variants */
+	prefs_conv_variants_combo_box = add_child_theme_prefs_combo(
+		vbox, combo_sg, label_sg, prefs_conv_variants,
+		(GCallback)prefs_set_conv_variant_cb, NULL, _("\tVariant:"));
+
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(prefs_conv_variants),
+	                                     0, GTK_SORT_ASCENDING);
+
 	/* Status Icon Themes */
 	prefs_status_themes_combo_box = add_theme_prefs_combo(
 		vbox, combo_sg, label_sg, prefs_status_icon_themes,
@@ -2869,6 +3018,10 @@
 	/* Themes */
 	prefs_themes_init();
 
+	/* Conversation Themes */
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/theme", "Default");
+
 	/* Smiley Themes */
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys");
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
--- a/pidgin/gtkprefs.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkprefs.h	Mon Sep 26 14:57:21 2011 +0900
@@ -92,8 +92,6 @@
  * @return      An hbox containing both the label and the entry.  Can be used to set
  *               the widgets to sensitive or insensitive based on the value of a
  *               checkbox.
- *
- * @since 2.6.0
  */
 GtkWidget *pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title,
 										const char *key, GtkSizeGroup *sg);
--- a/pidgin/gtkrequest.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkrequest.c	Mon Sep 26 14:57:21 2011 +0900
@@ -749,6 +749,7 @@
 req_entry_field_changed_cb(GtkWidget *entry, PurpleRequestField *field)
 {
 	PurpleRequestFieldGroup *group;
+	PurpleRequestFields *fields;
 	PidginRequestData *req_data;
 
 	if (purple_request_field_string_is_multiline(field))
@@ -771,10 +772,11 @@
 	}
 
 	group = purple_request_field_get_group(field);
-	req_data = (PidginRequestData *)group->fields_list->ui_data;
+	fields = purple_request_field_group_get_fields_list(group);
+	req_data = purple_request_fields_get_ui_data(fields);
 
 	gtk_widget_set_sensitive(req_data->ok_button,
-		purple_request_fields_all_required_filled(group->fields_list));
+		purple_request_fields_all_required_filled(fields));
 }
 
 static void
@@ -796,7 +798,7 @@
 		{
 			GtkWidget *optmenu = NULL;
 			PurpleRequestFieldGroup *group = purple_request_field_get_group(field);
-			GList *fields = group->fields;
+			GList *fields = purple_request_field_group_get_fields(group);
 
 			/* Ensure the account option menu is created (if the widget hasn't
 			 * been initialized already) for username auto-completion. */
@@ -1220,7 +1222,7 @@
 	data->user_data = user_data;
 	data->u.multifield.fields = fields;
 
-	fields->ui_data = data;
+	purple_request_fields_set_ui_data(fields, data);
 
 	data->cb_count = 2;
 	data->cbs = g_new0(GCallback, 2);
--- a/pidgin/gtksmiley.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtksmiley.c	Mon Sep 26 14:57:21 2011 +0900
@@ -47,9 +47,9 @@
 	GtkWidget *smiley_image;
 	gchar *filename;
 	GdkPixbuf *custom_pixbuf;
-	gpointer data; /** @since 2.6.0 */
-	gsize datasize; /** @since 2.6.0 */
-	gint entry_len; /** @since 2.6.0 */
+	gpointer data;
+	gsize datasize;
+	gint entry_len;
 };
 
 typedef struct
@@ -750,7 +750,7 @@
 		} else if (!g_ascii_strncasecmp(name, "http://", 7)) {
 			/* Oo, a web drag and drop. This is where things
 			 * will start to get interesting */
-			purple_util_fetch_url(name, TRUE, NULL, FALSE, smiley_got_url, dialog);
+			purple_util_fetch_url(name, TRUE, NULL, FALSE, -1, smiley_got_url, dialog);
 		} else if (!g_ascii_strncasecmp(name, "https://", 8)) {
 			/* purple_util_fetch_url() doesn't support HTTPS */
 			char *tmp = g_strdup(name + 1);
@@ -759,7 +759,7 @@
 			tmp[2] = 't';
 			tmp[3] = 'p';
 
-			purple_util_fetch_url(tmp, TRUE, NULL, FALSE, smiley_got_url, dialog);
+			purple_util_fetch_url(tmp, TRUE, NULL, FALSE, -1, smiley_got_url, dialog);
 			g_free(tmp);
 		}
 
--- a/pidgin/gtksmiley.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtksmiley.h	Mon Sep 26 14:57:21 2011 +0900
@@ -1,7 +1,6 @@
 /**
  * @file gtksmiley.h GTK+ Custom Smiley API
  * @ingroup pidgin
- * @since 2.5.0
  */
 
 /* pidgin
@@ -106,8 +105,6 @@
  * @param editor A smiley editor dialog
  * @param data A pointer to smiley's data
  * @param datasize The size of smiley's data
- *
- * @since 2.6.0
  */
 void pidgin_smiley_editor_set_data(PidginSmiley *editor, gpointer data, gsize datasize);
 
--- a/pidgin/gtksound.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtksound.h	Mon Sep 26 14:57:21 2011 +0900
@@ -67,8 +67,6 @@
  * Returns true Pidgin is using customized sounds
  *
  * @return TRUE if non default sounds are used.
- *
- * @since 2.6.0
  */
 gboolean pidgin_sound_is_customized(void);
 
--- a/pidgin/gtkstatusbox.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkstatusbox.c	Mon Sep 26 14:57:21 2011 +0900
@@ -412,7 +412,7 @@
 	if (src == NULL)
 		return FALSE;
 
-	purple_util_fetch_url(src, TRUE, NULL, FALSE, statusbox_got_url, data);
+	purple_util_fetch_url(src, TRUE, NULL, FALSE, -1, statusbox_got_url, data);
 	return TRUE;
 }
 
--- a/pidgin/gtkthemes.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkthemes.c	Mon Sep 26 14:57:21 2011 +0900
@@ -122,6 +122,8 @@
 
 static void _pidgin_themes_smiley_themeize(GtkWidget *imhtml, gboolean custom)
 {
+	/* TODO WEBKIT: move imhtml dependency to use webview. */
+#if 0
 	struct smiley_list *list;
 	if (!current_smiley_theme)
 		return;
@@ -147,6 +149,7 @@
 
 		list = list->next;
 	}
+#endif /* if 0 */
 }
 
 void pidgin_themes_smiley_themeize(GtkWidget *imhtml)
@@ -278,6 +281,8 @@
 		if (*i == '[' && strchr(i, ']') && load) {
 			struct smiley_list *child = g_new0(struct smiley_list, 1);
 			child->sml = g_strndup(i+1, strchr(i, ']') - i - 1);
+			child->files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
 			if (theme->list)
 				list->next = child;
 			else
@@ -320,6 +325,7 @@
 				} else {
 					GtkIMHtmlSmiley *smiley = gtk_imhtml_smiley_create(sfile, l, hidden, 0);
 					list->smileys = g_slist_prepend(list->smileys, smiley);
+					g_hash_table_insert (list->files, g_strdup(l), g_strdup(sfile));
 				}
 				while (isspace(*i))
 					i++;
@@ -358,7 +364,6 @@
 
 			if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
 				/* We want to see our custom smileys on our entry if we write the shortcut */
-				pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml);
 				pidgin_themes_smiley_themeize_custom(PIDGIN_CONVERSATION(conv)->entry);
 			}
 		}
--- a/pidgin/gtkthemes.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkthemes.h	Mon Sep 26 14:57:21 2011 +0900
@@ -29,6 +29,7 @@
 struct smiley_list {
 	char *sml;
 	GSList *smileys;
+	GHashTable *files; /**< map from smiley shortcut to filename */
 	struct smiley_list *next;
 };
 
@@ -51,18 +52,12 @@
 
 void pidgin_themes_smiley_themeize(GtkWidget *);
 
-/**
- * @since 2.5.0
- */
 void pidgin_themes_smiley_themeize_custom(GtkWidget *);
 
 void pidgin_themes_smiley_theme_probe(void);
 
 void pidgin_themes_load_smiley_theme(const char *file, gboolean load);
 
-/**
- * @since 2.1.0
- */
 void pidgin_themes_remove_smiley_theme(const char *file);
 
 GSList *pidgin_themes_get_proto_smileys(const char *id);
--- a/pidgin/gtkutils.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkutils.c	Mon Sep 26 14:57:21 2011 +0900
@@ -67,6 +67,7 @@
 #include "pidginstock.h"
 #include "gtkthemes.h"
 #include "gtkutils.h"
+#include "gtkwebview.h"
 #include "pidgin/minidialog.h"
 
 typedef struct {
@@ -276,6 +277,69 @@
 	return frame;
 }
 
+GtkWidget *
+pidgin_create_webview(gboolean editable, GtkWidget **webview_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret)
+{
+	GtkWidget *frame;
+	GtkWidget *webview;
+	GtkWidget *sep;
+	GtkWidget *sw;
+	GtkWidget *toolbar = NULL;
+	GtkWidget *vbox;
+
+	frame = gtk_frame_new(NULL);
+	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(frame), vbox);
+	gtk_widget_show(vbox);
+
+	if (editable) {
+		toolbar = gtk_imhtmltoolbar_new();
+		gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
+		gtk_widget_show(toolbar);
+
+		sep = gtk_hseparator_new();
+		gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
+		g_signal_connect_swapped(G_OBJECT(toolbar), "show", G_CALLBACK(gtk_widget_show), sep);
+		g_signal_connect_swapped(G_OBJECT(toolbar), "hide", G_CALLBACK(gtk_widget_hide), sep);
+		gtk_widget_show(sep);
+	}
+
+	webview = gtk_webview_new();
+#if 0
+	/* TODO WEBKIT: Don't have editable webview yet. */
+	gtk_webview_set_editable(GTK_WEBVIEW(webview), editable);
+#endif /* if 0 */
+#ifdef USE_GTKSPELL
+	if (editable && purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
+		pidgin_setup_gtkspell(GTK_TEXT_VIEW(webview));
+#endif
+	gtk_widget_show(webview);
+
+	if (editable) {
+		gtk_imhtmltoolbar_attach(GTK_IMHTMLTOOLBAR(toolbar), webview);
+		gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(toolbar), "default");
+	}
+
+	sw = pidgin_make_scrollable(webview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1);
+	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
+
+	gtk_webview_set_vadjustment(GTK_WEBVIEW(webview),
+			gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
+
+	if (webview_ret != NULL)
+		*webview_ret = webview;
+
+	if (editable && (toolbar_ret != NULL))
+		*toolbar_ret = toolbar;
+
+	if (sw_ret != NULL)
+		*sw_ret = sw;
+
+	return frame;
+}
+
 void
 pidgin_set_sensitive_if_input(GtkWidget *entry, GtkWidget *dialog)
 {
@@ -1816,21 +1880,27 @@
                             gpointer object)
 {
 	GtkWidget *menuitem;
+	GList *list;
 
 	if (act == NULL) {
 		return pidgin_separator(menu);
 	}
 
-	if (act->children == NULL) {
-		menuitem = gtk_menu_item_new_with_mnemonic(act->label);
-
-		if (act->callback != NULL) {
+	list = purple_menu_action_get_children(act);
+	menuitem = gtk_menu_item_new_with_mnemonic(purple_menu_action_get_label(act));
+
+	if (list == NULL) {
+		PurpleCallback callback;
+
+		callback = purple_menu_action_get_callback(act);
+
+		if (callback != NULL) {
 			g_object_set_data(G_OBJECT(menuitem),
 							  "purplecallback",
-							  act->callback);
+							  callback);
 			g_object_set_data(G_OBJECT(menuitem),
 							  "purplecallbackdata",
-							  act->data);
+							  purple_menu_action_get_data(act));
 			g_signal_connect(G_OBJECT(menuitem), "activate",
 							 G_CALLBACK(menu_action_cb),
 							 object);
@@ -1844,7 +1914,6 @@
 		GtkWidget *submenu = NULL;
 		GtkAccelGroup *group;
 
-		menuitem = gtk_menu_item_new_with_mnemonic(act->label);
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
 		submenu = gtk_menu_new();
@@ -1852,19 +1921,20 @@
 
 		group = gtk_menu_get_accel_group(GTK_MENU(menu));
 		if (group) {
-			char *path = g_strdup_printf("%s/%s", GTK_MENU_ITEM(menuitem)->accel_path, act->label);
+			char *path = g_strdup_printf("%s/%s", GTK_MENU_ITEM(menuitem)->accel_path,
+					purple_menu_action_get_label(act));
 			gtk_menu_set_accel_path(GTK_MENU(submenu), path);
 			g_free(path);
 			gtk_menu_set_accel_group(GTK_MENU(submenu), group);
 		}
 
-		for (l = act->children; l; l = l->next) {
+		for (l = list; l; l = l->next) {
 			PurpleMenuAction *act = (PurpleMenuAction *)l->data;
 
 			pidgin_append_menu_action(submenu, act, object);
 		}
-		g_list_free(act->children);
-		act->children = NULL;
+		g_list_free(list);
+		purple_menu_action_set_children(act, NULL);
 	}
 	purple_menu_action_free(act);
 	return menuitem;
--- a/pidgin/gtkutils.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkutils.h	Mon Sep 26 14:57:21 2011 +0900
@@ -109,12 +109,34 @@
 GtkWidget *pidgin_create_imhtml(gboolean editable, GtkWidget **imhtml_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret);
 
 /**
+ * Create an GtkWebView widget and associated GtkIMHtmlToolbar widget.  This
+ * function puts both widgets in a nice GtkFrame.  They're separated by an
+ * attractive GtkSeparator.
+ * TODO WEBKIT: editable isn't supported yet
+ *
+ * @param editable @c TRUE if this webview should be editable.  If this is
+ *        @c FALSE, then the toolbar will NOT be created.  If this webview
+ *        should be read-only at first, but may become editable later, then
+ *        pass in @c TRUE here and then manually call gtk_webview_set_editable()
+ *        later.
+ * @param webview_ret A pointer to a pointer to a GtkWidget.  This pointer
+ *        will be set to the webview when this function exits.
+ * @param toolbar_ret A pointer to a pointer to a GtkWidget.  If editable is
+ *        TRUE then this will be set to the toolbar when this function exits.
+ *        Otherwise this will be set to @c NULL.
+ * @param sw_ret This will be filled with a pointer to the scrolled window
+ *        widget which contains the webview.
+ *
+ * @return The GtkFrame containing the toolbar and webview.
+ */
+GtkWidget *pidgin_create_webview(gboolean editable, GtkWidget **webview_ret, GtkWidget **toolbar_ret, GtkWidget **sw_ret);
+
+/**
  * Creates a small button
  *
  * @param  image   A button image.
  *
  * @return   A GtkButton created from the image.
- * @since 2.7.0
  */
 GtkWidget *pidgin_create_small_button(GtkWidget *image);
 
@@ -125,8 +147,6 @@
  * @param border_width The window's desired border width
  * @param role         A string indicating what the window is responsible for doing, or @c NULL
  * @param resizable    Whether the window should be resizable (@c TRUE) or not (@c FALSE)
- *
- * @since 2.1.0
  */
 GtkWidget *pidgin_create_window(const char *title, guint border_width, const char *role, gboolean resizable);
 
@@ -137,8 +157,6 @@
  * @param border_width The window's desired border width
  * @param role         A string indicating what the window is responsible for doing, or @c NULL
  * @param resizable    Whether the window should be resizable (@c TRUE) or not (@c FALSE)
- *
- * @since 2.4.0
  */
 GtkWidget *pidgin_create_dialog(const char *title, guint border_width, const char *role, gboolean resizable);
 
@@ -148,8 +166,6 @@
  * @param dialog       The dialog window
  * @param homogeneous  TRUE if all children are to be given equal space allotments.
  * @param spacing      the number of pixels to place by default between children
- *
- * @since 2.4.0
  */
 GtkWidget *pidgin_dialog_get_vbox_with_properties(GtkDialog *dialog, gboolean homogeneous, gint spacing);
 
@@ -157,8 +173,6 @@
  * Retrieves the main content box (vbox) from a pidgin dialog window
  *
  * @param dialog       The dialog window
- *
- * @since 2.4.0
  */
 GtkWidget *pidgin_dialog_get_vbox(GtkDialog *dialog);
 
@@ -171,7 +185,6 @@
  * @param callbackdata   The user data for the callback function
  *
  * @return The created button.
- * @since 2.4.0
  */
 GtkWidget *pidgin_dialog_add_button(GtkDialog *dialog, const char *label,
 		GCallback callback, gpointer callbackdata);
@@ -180,8 +193,6 @@
  * Retrieves the action area (button box) from a pidgin dialog window
  *
  * @param dialog       The dialog window
- *
- * @since 2.4.0
  */
 GtkWidget *pidgin_dialog_get_action_area(GtkDialog *dialog);
 
@@ -415,8 +426,6 @@
  *
  * @param conn   The connection to get information from.
  * @param name   The user to get information about.
- *
- * @since 2.1.0
  */
 void pidgin_retrieve_user_info(PurpleConnection *conn, const char *name);
 
@@ -426,8 +435,6 @@
  * @param conn   The connection to get information from.
  * @param name   The user to get information about.
  * @param chatid The chat id.
- *
- * @since 2.1.0
  */
 void pidgin_retrieve_user_info_in_chat(PurpleConnection *conn, const char *name, int chatid);
 
@@ -466,8 +473,6 @@
  *
  * @param w The widget that we want to label.
  * @param l A GtkLabel that we want to use as the label for the widget.
- *
- * @since 2.2.0
  */
 void pidgin_set_accessible_relations(GtkWidget *w, GtkWidget *l);
 
@@ -482,8 +487,6 @@
  *        where the menu shall be drawn. This is an output parameter.
  * @param push_in This is an output parameter?
  * @param data Not used by this particular position function.
- *
- * @since 2.1.0
  */
 void pidgin_menu_position_func_helper(GtkMenu *menu, gint *x, gint *y,
 										gboolean *push_in, gpointer data);
@@ -555,8 +558,6 @@
  * @param prim   The status primitive
  *
  * @return The stock-id
- *
- * @since 2.6.0
  */
 const char *pidgin_stock_id_from_status_primitive(PurpleStatusPrimitive prim);
 
@@ -566,8 +567,6 @@
  * @param presence   The presence.
  *
  * @return The stock-id
- *
- * @since 2.6.0
  */
 const char *pidgin_stock_id_from_presence(PurplePresence *presence);
 
@@ -735,8 +734,6 @@
  *
  * @return               A newly created text GtkComboBox containing a GtkEntry
  *                       child.
- *
- * @since 2.2.0
  */
 GtkWidget *pidgin_text_combo_box_entry_new(const char *default_item, GList *items);
 
@@ -746,8 +743,6 @@
  * @param widget         The simple text GtkComboBoxEntry equivalent widget
  *
  * @return               The text in the widget's entry. It must not be freed
- *
- * @since 2.2.0
  */
 const char *pidgin_text_combo_box_entry_get_text(GtkWidget *widget);
 
@@ -756,8 +751,6 @@
  *
  * @param widget         The simple text GtkComboBoxEntry equivalent widget
  * @param text           The text to set
- *
- * @since 2.2.0
  */
 void pidgin_text_combo_box_entry_set_text(GtkWidget *widget, const char *text);
 
@@ -767,8 +760,6 @@
  * @param window    The window to make transient.
  *
  * @return Whether the window was made transient or not.
- *
- * @since 2.4.0
  */
 gboolean pidgin_auto_parent_window(GtkWidget *window);
 
@@ -783,7 +774,6 @@
  * @param p_label      Place to store a pointer to the GtkLabel, or @c NULL if you don't care.
  *
  * @return  A GtkHBox already added to the GtkVBox containing the GtkLabel and the GtkWidget.
- * @since 2.4.0
  */
 GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label);
 
@@ -795,8 +785,6 @@
  *
  * @return A GdkPixbuf created from the image data, or NULL if
  *         there was an error parsing the data.
- *
- * @since 2.9.0
  */
 GdkPixbuf *pidgin_pixbuf_from_data(const guchar *buf, gsize count);
 
@@ -808,8 +796,6 @@
  *
  * @return A GdkPixbufAnimation created from the image data, or NULL if
  *         there was an error parsing the data.
- *
- * @since 2.9.0
  */
 GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count);
 
@@ -819,8 +805,6 @@
  * @param  image   A PurpleStoredImage.
  *
  * @return   A GdkPixbuf created from the stored image.
- *
- * @since 2.5.0
  */
 GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image);
 
@@ -844,8 +828,6 @@
  *
  * @return The GdkPixbuf if successful.  Otherwise NULL is returned and
  *         a warning is logged.
- *
- * @since 2.9.0
  */
 GdkPixbuf *pidgin_pixbuf_new_from_file(const char *filename);
 
@@ -871,8 +853,6 @@
  *
  * @return The GdkPixbuf if successful.  Otherwise NULL is returned and
  *         a warning is logged.
- *
- * @since 2.9.0
  */
 GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width, int height);
 
@@ -899,35 +879,27 @@
  *
  * @return The GdkPixbuf if successful.  Otherwise NULL is returned and
  *         a warning is logged.
- *
- * @since 2.9.0
  */
 GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width, int height, gboolean preserve_aspect_ratio);
 
 /**
  * Add scrollbars to a widget
- * @param widget      The child widget
- * @hscrollbar_policy Horizontal scrolling policy
- * @vscrollbar_policy Vertical scrolling policy
- * @shadow            Shadow type
- * @width             Desired widget width, or -1 for default
- * @height            Desired widget height, or -1 for default
- *
- * @since 2.8.0
+ * @param child              The child widget
+ * @param hscrollbar_policy  Horizontal scrolling policy
+ * @param vscrollbar_policy  Vertical scrolling policy
+ * @param shadow_type        Shadow type
+ * @param width              Desired widget width, or -1 for default
+ * @param height             Desired widget height, or -1 for default
  */
 GtkWidget *pidgin_make_scrollable(GtkWidget *child, GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy, GtkShadowType shadow_type, int width, int height);
 
 /**
  * Initialize some utility functions.
- *
- * @since 2.6.0
  */
 void pidgin_utils_init(void);
 
 /**
  * Uninitialize some utility functions.
- *
- * @since 2.6.0
  */
 void pidgin_utils_uninit(void);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkwebview.c	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,419 @@
+/*
+ * @file gtkwebview.c GTK+ WebKitWebView wrapper class.
+ * @ingroup pidgin
+ */
+
+/* pidgin
+ *
+ * Pidgin 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <JavaScriptCore/JavaScript.h>
+
+#include "util.h"
+#include "gtkwebview.h"
+#include "imgstore.h"
+
+static WebKitWebViewClass *parent_class = NULL;
+
+struct GtkWebViewPriv {
+	GHashTable *images; /**< a map from id to temporary file for the image */
+	gboolean empty;  /**< whether anything has been appended **/
+
+	/* JS execute queue */
+	GQueue *js_queue;
+	gboolean is_loading;
+	GtkAdjustment *vadj;
+	guint scroll_src;
+	GTimer *scroll_time;
+};
+
+GtkWidget *
+gtk_webview_new(void)
+{
+	GtkWebView* ret = GTK_WEBVIEW(g_object_new(gtk_webview_get_type(), NULL));
+	return GTK_WIDGET(ret);
+}
+
+static char *
+get_image_filename_from_id(GtkWebView* view, int id)
+{
+	char *filename = NULL;
+	FILE *file;
+	PurpleStoredImage* img;
+
+	if (!view->priv->images)
+		view->priv->images = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+
+	filename = (char *)g_hash_table_lookup(view->priv->images, GINT_TO_POINTER(id));
+	if (filename)
+		return filename;
+
+	/* else get from img store */
+	file = purple_mkstemp(&filename, TRUE);
+
+	img = purple_imgstore_find_by_id(id);
+
+	fwrite(purple_imgstore_get_data(img), purple_imgstore_get_size(img), 1, file);
+	g_hash_table_insert(view->priv->images, GINT_TO_POINTER(id), filename);
+	fclose(file);
+	return filename;
+}
+
+static void
+clear_single_image(gpointer key, gpointer value, gpointer userdata)
+{
+	g_unlink((char *)value);
+}
+
+static void
+clear_images(GtkWebView *view)
+{
+	if (!view->priv->images)
+		return;
+	g_hash_table_foreach(view->priv->images, clear_single_image, NULL);
+	g_hash_table_unref(view->priv->images);
+}
+
+/*
+ * Replace all <img id=""> tags with <img src="">. I hoped to never
+ * write any HTML parsing code, but I'm forced to do this, until
+ * purple changes the way it works.
+ */
+static char *
+replace_img_id_with_src(GtkWebView *view, const char *html)
+{
+	GString *buffer = g_string_sized_new(strlen(html));
+	const char* cur = html;
+	char *id;
+	int nid;
+
+	while (*cur) {
+		const char *img = strstr(cur, "<img");
+		if (!img) {
+			g_string_append(buffer, cur);
+			break;
+		} else
+			g_string_append_len(buffer, cur, img - cur);
+
+		cur = strstr(img, "/>");
+		if (!cur)
+			cur = strstr(img, ">");
+
+		if (!cur) { /* invalid html? */
+			g_string_printf(buffer, "%s", html);
+			break;
+		}
+
+		if (strstr(img, "src=") || !strstr(img, "id=")) {
+			g_string_printf(buffer, "%s", html);
+			break;
+		}
+
+		/*
+		 * if this is valid HTML, then I can be sure that it
+		 * has an id= and does not have an src=, since
+		 * '=' cannot appear in parameters.
+		 */
+
+		id = strstr(img, "id=") + 3;
+
+		/* *id can't be \0, since a ">" appears after this */
+		if (isdigit(*id))
+			nid = atoi(id);
+		else
+			nid = atoi(id + 1);
+
+		/* let's dump this, tag and then dump the src information */
+		g_string_append_len(buffer, img, cur - img);
+
+		g_string_append_printf(buffer, " src='file://%s' ", get_image_filename_from_id(view, nid));
+	}
+
+	return g_string_free(buffer, FALSE);
+}
+
+static void
+gtk_webview_finalize(GObject *view)
+{
+	gpointer temp;
+
+	while ((temp = g_queue_pop_head(GTK_WEBVIEW(view)->priv->js_queue)))
+		g_free(temp);
+	g_queue_free(GTK_WEBVIEW(view)->priv->js_queue);
+
+	clear_images(GTK_WEBVIEW(view));
+	g_free(GTK_WEBVIEW(view)->priv);
+	G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(view));
+}
+
+static void
+gtk_webview_class_init(GtkWebViewClass *klass, gpointer userdata)
+{
+	parent_class = g_type_class_ref(webkit_web_view_get_type());
+	G_OBJECT_CLASS(klass)->finalize = gtk_webview_finalize;
+}
+
+static gboolean
+webview_link_clicked(WebKitWebView *view,
+		      WebKitWebFrame *frame,
+		      WebKitNetworkRequest *request,
+		      WebKitWebNavigationAction *navigation_action,
+		      WebKitWebPolicyDecision *policy_decision)
+{
+	const gchar *uri;
+	WebKitWebNavigationReason reason;
+
+	uri = webkit_network_request_get_uri(request);
+	reason = webkit_web_navigation_action_get_reason(navigation_action);
+
+	if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
+		/* the gtk imhtml way was to create an idle cb, not sure
+		 * why, so right now just using purple_notify_uri directly */
+		purple_notify_uri(NULL, uri);
+	} else
+		webkit_web_policy_decision_use(policy_decision);
+
+	return TRUE;
+}
+
+static gboolean
+process_js_script_queue(GtkWebView *view)
+{
+	char *script;
+	if (view->priv->is_loading)
+		return FALSE; /* we will be called when loaded */
+	if (!view->priv->js_queue || g_queue_is_empty(view->priv->js_queue))
+		return FALSE; /* nothing to do! */
+
+	script = g_queue_pop_head(view->priv->js_queue);
+	webkit_web_view_execute_script(WEBKIT_WEB_VIEW(view), script);
+	g_free(script);
+
+	return TRUE; /* there may be more for now */
+}
+
+static void
+webview_load_started(WebKitWebView *view,
+		      WebKitWebFrame *frame,
+		      gpointer userdata)
+{
+	/* is there a better way to test for is_loading? */
+	GTK_WEBVIEW(view)->priv->is_loading = TRUE;
+}
+
+static void
+webview_load_finished(WebKitWebView *view,
+		       WebKitWebFrame *frame,
+		       gpointer userdata)
+{
+	GTK_WEBVIEW(view)->priv->is_loading = FALSE;
+	g_idle_add((GSourceFunc)process_js_script_queue, view);
+}
+
+void
+gtk_webview_safe_execute_script(GtkWebView *view, const char *script)
+{
+	g_queue_push_tail(view->priv->js_queue, g_strdup(script));
+	g_idle_add((GSourceFunc)process_js_script_queue, view);
+}
+
+static void
+gtk_webview_init(GtkWebView *view, gpointer userdata)
+{
+	view->priv = g_new0(struct GtkWebViewPriv, 1);
+	g_signal_connect(view, "navigation-policy-decision-requested",
+			  G_CALLBACK(webview_link_clicked),
+			  view);
+
+	g_signal_connect(view, "load-started",
+			  G_CALLBACK(webview_load_started),
+			  view);
+
+	g_signal_connect(view, "load-finished",
+			  G_CALLBACK(webview_load_finished),
+			  view);
+
+	view->priv->empty = TRUE;
+	view->priv->js_queue = g_queue_new();
+}
+
+
+void
+gtk_webview_load_html_string_with_imgstore(GtkWebView *view, const char *html)
+{
+	char *html_imged;
+
+	clear_images(view);
+	html_imged = replace_img_id_with_src(view, html);
+	webkit_web_view_load_html_string(WEBKIT_WEB_VIEW(view), html_imged, "file:///");
+	g_free(html_imged);
+}
+
+char *
+gtk_webview_quote_js_string(const char *text)
+{
+	GString *str = g_string_new("\"");
+	const char *cur = text;
+
+	while (cur && *cur) {
+		switch (*cur) {
+			case '\\':
+				g_string_append(str, "\\\\");
+				break;
+			case '\"':
+				g_string_append(str, "\\\"");
+				break;
+			case '\r':
+				g_string_append(str, "<br/>");
+				break;
+			case '\n':
+				break;
+			default:
+				g_string_append_c(str, *cur);
+		}
+		cur++;
+	}
+	g_string_append_c(str, '"');
+	return g_string_free(str, FALSE);
+}
+
+void
+gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj)
+{
+	webview->priv->vadj = vadj;
+}
+
+/* this is a "hack", my plan is to eventually handle this
+ * correctly using a signals and a plugin: the plugin will have
+ * the information as to what javascript function to call. It seems
+ * wrong to hardcode that here.
+ */
+void
+gtk_webview_append_html(GtkWebView *view, const char *html)
+{
+	char *escaped = gtk_webview_quote_js_string(html);
+	char *script = g_strdup_printf("document.write(%s)", escaped);
+	webkit_web_view_execute_script(WEBKIT_WEB_VIEW(view), script);
+	view->priv->empty = FALSE;
+	gtk_webview_scroll_to_end(view, TRUE);
+	g_free(script);
+	g_free(escaped);
+}
+
+gboolean
+gtk_webview_is_empty(GtkWebView *view)
+{
+	return view->priv->empty;
+}
+
+#define MAX_SCROLL_TIME 0.4 /* seconds */
+#define SCROLL_DELAY 33 /* milliseconds */
+
+/*
+ * Smoothly scroll a WebView.
+ *
+ * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom.
+ */
+static gboolean
+smooth_scroll_cb(gpointer data)
+{
+	struct GtkWebViewPriv *priv = data;
+	GtkAdjustment *adj = priv->vadj;
+	gdouble max_val = adj->upper - adj->page_size;
+	gdouble scroll_val = gtk_adjustment_get_value(adj) + ((max_val - gtk_adjustment_get_value(adj)) / 3);
+
+	g_return_val_if_fail(priv->scroll_time != NULL, FALSE);
+
+	if (g_timer_elapsed(priv->scroll_time, NULL) > MAX_SCROLL_TIME || scroll_val >= max_val) {
+		/* time's up. jump to the end and kill the timer */
+		gtk_adjustment_set_value(adj, max_val);
+		g_timer_destroy(priv->scroll_time);
+		priv->scroll_time = NULL;
+		g_source_remove(priv->scroll_src);
+		priv->scroll_src = 0;
+		return FALSE;
+	}
+
+	/* scroll by 1/3rd the remaining distance */
+	gtk_adjustment_set_value(adj, scroll_val);
+	return TRUE;
+}
+
+static gboolean
+scroll_idle_cb(gpointer data)
+{
+	struct GtkWebViewPriv *priv = data;
+	GtkAdjustment *adj = priv->vadj;
+	if (adj) {
+		gtk_adjustment_set_value(adj, adj->upper - adj->page_size);
+	}
+	priv->scroll_src = 0;
+	return FALSE;
+}
+
+void
+gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth)
+{
+	struct GtkWebViewPriv *priv = webview->priv;
+	if (priv->scroll_time)
+		g_timer_destroy(priv->scroll_time);
+	if (priv->scroll_src)
+		g_source_remove(priv->scroll_src);
+	if(smooth) {
+		priv->scroll_time = g_timer_new();
+		priv->scroll_src = g_timeout_add_full(G_PRIORITY_LOW, SCROLL_DELAY, smooth_scroll_cb, priv, NULL);
+	} else {
+		priv->scroll_time = NULL;
+		priv->scroll_src = g_idle_add_full(G_PRIORITY_LOW, scroll_idle_cb, priv, NULL);
+	}
+}
+
+GType
+gtk_webview_get_type(void)
+{
+	static GType mview_type = 0;
+	if (G_UNLIKELY(mview_type == 0)) {
+		static const GTypeInfo mview_info = {
+			sizeof(GtkWebViewClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gtk_webview_class_init,
+			NULL,
+			NULL,
+			sizeof(GtkWebView),
+			0,
+			(GInstanceInitFunc) gtk_webview_init,
+			NULL
+		};
+		mview_type = g_type_register_static(webkit_web_view_get_type(),
+				"GtkWebView", &mview_info, 0);
+	}
+	return mview_type;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkwebview.h	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,147 @@
+/**
+ * @file gtkwebview.h Wrapper over the Gtk WebKitWebView component
+ * @ingroup pidgin
+ */
+
+/* pidgin
+ *
+ * Pidgin 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 _PIDGIN_WEBVIEW_H_
+#define _PIDGIN_WEBVIEW_H_
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit/webkit.h>
+
+#include "notify.h"
+
+#define GTK_TYPE_WEBVIEW            (gtk_webview_get_type())
+#define GTK_WEBVIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_WEBVIEW, GtkWebView))
+#define GTK_WEBVIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_WEBVIEW, GtkWebViewClass))
+#define GTK_IS_WEBVIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_WEBVIEW))
+#define GTK_IS_WEBVIEW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_WEBVIEW))
+
+
+struct GtkWebViewPriv;
+
+struct _GtkWebView
+{
+	WebKitWebView webkit_web_view;
+
+	/*< private >*/
+	struct GtkWebViewPriv *priv;
+};
+
+typedef struct _GtkWebView GtkWebView;
+
+struct _GtkWebViewClass
+{
+	WebKitWebViewClass parent;
+};
+
+typedef struct _GtkWebViewClass GtkWebViewClass;
+
+
+/**
+ * Returns the GType for a GtkWebView widget
+ *
+ * @return the GType for GtkWebView widget
+ */
+GType gtk_webview_get_type(void);
+
+/**
+ * Create a new GtkWebView object
+ *
+ * @return a GtkWidget corresponding to the GtkWebView object
+ */
+GtkWidget *gtk_webview_new(void);
+
+/**
+ * Set the vertical adjustment for the GtkWebView.
+ *
+ * @param webview  The GtkWebView.
+ * @param vadj     The GtkAdjustment that control the webview.
+ */
+void gtk_webview_set_vadjustment(GtkWebView *webview, GtkAdjustment *vadj);
+
+/**
+ * A very basic routine to append html, which can be considered
+ * equivalent to a "document.write" using JavaScript.
+ *
+ * @param webview The GtkWebView object
+ * @param markup  The html markup to append
+ */
+void gtk_webview_append_html(GtkWebView *webview, const char *markup);
+
+/**
+ * Rather than use webkit_webview_load_string, this routine
+ * parses and displays the \<img id=?\> tags that make use of the
+ * Pidgin imgstore.
+ *
+ * @param webview The GtkWebView object
+ * @param html    The HTML content to load
+ */
+void gtk_webview_load_html_string_with_imgstore(GtkWebView *webview, const char *html);
+
+/**
+ * TODO WEBKIT: Right now this just tests whether an append has been called
+ * since the last clear or since the Widget was created.  So it does not
+ * test for load_string's called in between.
+ *
+ * @param webview The GtkWebView object
+ *
+ * @return gboolean indicating whether the webview is empty.
+ */
+gboolean gtk_webview_is_empty(GtkWebView *webview);
+
+/**
+ * Execute the JavaScript only after the webkit_webview_load_string
+ * loads completely. We also guarantee that the scripts are executed
+ * in the order they are called here. This is useful to avoid race
+ * conditions when calling JS functions immediately after opening the
+ * page.
+ *
+ * @param webview the GtkWebView object
+ * @param script   the script to execute
+ */
+void gtk_webview_safe_execute_script(GtkWebView *webview, const char *script);
+
+/**
+ * A convenience routine to quote a string for use as a JavaScript
+ * string. For instance, "hello 'world'" becomes "'hello \\'world\\''"
+ *
+ * @param str The string to escape and quote
+ *
+ * @return the quoted string.
+ */
+char *gtk_webview_quote_js_string(const char *str);
+
+/**
+ * Scrolls the Webview to the end of its contents.
+ *
+ * @param webview The GtkWebView.
+ * @param smooth   A boolean indicating if smooth scrolling should be used.
+ */
+void gtk_webview_scroll_to_end(GtkWebView *webview, gboolean smooth);
+
+#endif /* _PIDGIN_WEBVIEW_H_ */
+
--- a/pidgin/gtkwhiteboard.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/gtkwhiteboard.c	Mon Sep 26 14:57:21 2011 +0900
@@ -125,7 +125,7 @@
 	PidginWhiteboard *gtkwb = g_new0(PidginWhiteboard, 1);
 
 	gtkwb->wb = wb;
-	wb->ui_data = gtkwb;
+	purple_whiteboard_set_ui_data(wb, gtkwb);
 
 	/* Get dimensions (default?) for the whiteboard canvas */
 	if (!purple_whiteboard_get_dimensions(wb, &gtkwb->width, &gtkwb->height))
@@ -145,11 +145,11 @@
 	/* Try and set window title as the name of the buddy, else just use their
 	 * username
 	 */
-	buddy = purple_find_buddy(wb->account, wb->who);
+	buddy = purple_find_buddy(purple_whiteboard_get_account(wb), purple_whiteboard_get_who(wb));
 
-	window = pidgin_create_window(buddy != NULL ? purple_buddy_get_contact_alias(buddy) : wb->who, 0, NULL, FALSE);
+	window = pidgin_create_window(buddy != NULL ? purple_buddy_get_contact_alias(buddy) : purple_whiteboard_get_who(wb), 0, NULL, FALSE);
 	gtkwb->window = window;
-	gtk_widget_set_name(window, wb->who);
+	gtk_widget_set_name(window, purple_whiteboard_get_who(wb));
 
 	g_signal_connect(G_OBJECT(window), "delete_event",
 					 G_CALLBACK(whiteboard_close_cb), gtkwb);
@@ -274,7 +274,7 @@
 	GtkWidget *colour_dialog;
 
 	g_return_if_fail(wb != NULL);
-	gtkwb = wb->ui_data;
+	gtkwb = purple_whiteboard_get_ui_data(wb);
 	g_return_if_fail(gtkwb != NULL);
 
 	/* TODO Ask if user wants to save picture before the session is closed */
@@ -301,7 +301,7 @@
 		gtkwb->window = NULL;
 	}
 	g_free(gtkwb);
-	wb->ui_data = NULL;
+	purple_whiteboard_set_ui_data(wb, NULL);
 }
 
 static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb)
@@ -406,7 +406,7 @@
 	GdkPixmap *pixmap = gtkwb->pixmap;
 
 	PurpleWhiteboard *wb = gtkwb->wb;
-	GList *draw_list = wb->draw_list;
+	GList *draw_list = purple_whiteboard_get_draw_list(wb);
 
 	if(BrushState != BRUSH_STATE_UP)
 	{
@@ -441,7 +441,7 @@
 											 gtkwb->brush_color, gtkwb->brush_size);
 	}
 
-	wb->draw_list = draw_list;
+	purple_whiteboard_set_draw_list(wb, draw_list);
 
 	return TRUE;
 }
@@ -459,7 +459,7 @@
 	GdkPixmap *pixmap = gtkwb->pixmap;
 
 	PurpleWhiteboard *wb = gtkwb->wb;
-	GList *draw_list = wb->draw_list;
+	GList *draw_list = purple_whiteboard_get_draw_list(wb);
 
 	if(event->is_hint)
 		gdk_window_get_pointer(event->window, &x, &y, &state);
@@ -528,7 +528,7 @@
 		LastY = y;
 	}
 
-	wb->draw_list = draw_list;
+	purple_whiteboard_set_draw_list(wb, draw_list);
 
 	return TRUE;
 }
@@ -539,7 +539,7 @@
 	GdkPixmap *pixmap = gtkwb->pixmap;
 
 	PurpleWhiteboard *wb = gtkwb->wb;
-	GList *draw_list = wb->draw_list;
+	GList *draw_list = purple_whiteboard_get_draw_list(wb);
 
 	if((BrushState != BRUSH_STATE_DOWN) && (BrushState != BRUSH_STATE_MOTION))
 	{
@@ -583,7 +583,7 @@
 		if(draw_list)
 			purple_whiteboard_draw_list_destroy(draw_list);
 
-		wb->draw_list = NULL;
+		purple_whiteboard_set_draw_list(wb, NULL);
 	}
 
 	return TRUE;
@@ -591,7 +591,7 @@
 
 static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, int x, int y, int color, int size)
 {
-	PidginWhiteboard *gtkwb = wb->ui_data;
+	PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
 	GtkWidget *widget = gtkwb->drawing_area;
 	GdkPixmap *pixmap = gtkwb->pixmap;
 
@@ -684,7 +684,7 @@
 
 static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height)
 {
-	PidginWhiteboard *gtkwb = wb->ui_data;
+	PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
 
 	gtkwb->width = width;
 	gtkwb->height = height;
@@ -692,7 +692,7 @@
 
 static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color)
 {
-	PidginWhiteboard *gtkwb = wb->ui_data;
+	PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
 
 	gtkwb->brush_size = size;
 	gtkwb->brush_color = color;
@@ -700,7 +700,7 @@
 
 static void pidgin_whiteboard_clear(PurpleWhiteboard *wb)
 {
-	PidginWhiteboard *gtkwb = wb->ui_data;
+	PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
 	GdkPixmap *pixmap = gtkwb->pixmap;
 	GtkWidget *drawing_area = gtkwb->drawing_area;
 	cairo_t *cr = g_object_get_data(G_OBJECT(pixmap), "cairo-context");
--- a/pidgin/pidgintooltip.h	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/pidgintooltip.h	Mon Sep 26 14:57:21 2011 +0900
@@ -36,7 +36,6 @@
  * @param h          The value of this should be set to the desired height of the tooltip window.
  *
  * @return  @c TRUE if the tooltip was created correctly, @c FALSE otherwise.
- * @since 2.4.0
  */
 typedef gboolean (*PidginTooltipCreateForTree)(GtkWidget *tipwindow,
 			GtkTreePath *path, gpointer userdata, int *w, int *h);
@@ -48,7 +47,6 @@
  * @param h          The value of this should be set to the desired height of the tooltip window.
  *
  * @return  @c TRUE if the tooltip was created correctly, @c FALSE otherwise.
- * @since 2.4.0
  */
 typedef gboolean (*PidginTooltipCreate)(GtkWidget *tipwindow,
 			gpointer userdata, int *w, int *h);
@@ -58,7 +56,6 @@
  * @param  userdata    The userdata set during pidgin_tooltip_setup_for_treeview or pidgin_tooltip_show.
  *
  * @return  @c TRUE if the tooltip was painted correctly, @c FALSE otherwise.
- * @since 2.4.0
  */
 typedef gboolean (*PidginTooltipPaint)(GtkWidget *tipwindow, gpointer userdata);
 
@@ -71,7 +68,6 @@
  * @param paint_cb     Callback function to paint the tooltip
  *
  * @return   @c TRUE if the tooltip callbacks were setup correctly.
- * @since 2.4.0
  */
 gboolean pidgin_tooltip_setup_for_treeview(GtkWidget *tree, gpointer userdata,
 		PidginTooltipCreateForTree create_cb, PidginTooltipPaint paint_cb);
@@ -85,14 +81,12 @@
  * @param paint_cb     Callback function to paint the tooltip
  *
  * @return   @c TRUE if the tooltip callbacks were setup correctly.
- * @since 2.4.0
  */
 gboolean pidgin_tooltip_setup_for_widget(GtkWidget *widget, gpointer userdata,
 		PidginTooltipCreate create_cb, PidginTooltipPaint paint_cb);
 
 /**
  * Destroy the tooltip.
- * @since 2.4.0
  */
 void pidgin_tooltip_destroy(void);
 
@@ -103,8 +97,6 @@
  * @param userdata    The userdata to send to the callback functions
  * @param create_cb    Callback function to create the tooltip from the GtkTreePath
  * @param paint_cb     Callback function to paint the tooltip
- *
- * @since 2.4.0
  */
 void pidgin_tooltip_show(GtkWidget *widget, gpointer userdata,
 		PidginTooltipCreate create_cb, PidginTooltipPaint paint_cb);
--- a/pidgin/plugins/Makefile.am	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/plugins/Makefile.am	Mon Sep 26 14:57:21 2011 +0900
@@ -1,4 +1,4 @@
-DIST_SUBDIRS = cap disco gestures gevolution musicmessaging perl ticker
+DIST_SUBDIRS = adiumthemes cap disco gestures gevolution musicmessaging perl ticker
 
 if BUILD_GEVOLUTION
 GEVOLUTION_DIR = gevolution
@@ -27,7 +27,8 @@
 	$(MUSICMESSAGING_DIR) \
 	$(PERL_DIR) \
 	disco \
-	ticker
+	ticker \
+	adiumthemes
 
 plugindir = $(libdir)/pidgin
 
@@ -38,16 +39,14 @@
 gtkbuddynote_la_LDFLAGS     = -module -avoid-version
 history_la_LDFLAGS          = -module -avoid-version
 iconaway_la_LDFLAGS         = -module -avoid-version
-markerline_la_LDFLAGS       = -module -avoid-version
 notify_la_LDFLAGS           = -module -avoid-version
 pidginrc_la_LDFLAGS         = -module -avoid-version
 relnot_la_LDFLAGS           = -module -avoid-version
 sendbutton_la_LDFLAGS       = -module -avoid-version
 spellchk_la_LDFLAGS         = -module -avoid-version
 themeedit_la_LDFLAGS        = -module -avoid-version
-timestamp_la_LDFLAGS        = -module -avoid-version
-timestamp_format_la_LDFLAGS = -module -avoid-version
 vvconfig_la_LDFLAGS         = -module -avoid-version
+webkit_la_LDFLAGS           = -module -avoid-version
 xmppconsole_la_LDFLAGS      = -module -avoid-version
 
 if PLUGINS
@@ -58,15 +57,13 @@
 	gtkbuddynote.la     \
 	history.la          \
 	iconaway.la         \
-	markerline.la       \
 	notify.la           \
 	pidginrc.la         \
 	relnot.la           \
 	sendbutton.la       \
 	spellchk.la         \
-	themeedit.la         \
-	timestamp.la        \
-	timestamp_format.la \
+	themeedit.la        \
+	webkit.la           \
 	xmppconsole.la
 
 if USE_VV
@@ -84,16 +81,13 @@
 gtkbuddynote_la_SOURCES     = gtkbuddynote.c
 history_la_SOURCES          = history.c
 iconaway_la_SOURCES         = iconaway.c
-markerline_la_SOURCES       = markerline.c
 notify_la_SOURCES           = notify.c
 pidginrc_la_SOURCES         = pidginrc.c
 relnot_la_SOURCES           = relnot.c
 sendbutton_la_SOURCES       = sendbutton.c
 spellchk_la_SOURCES         = spellchk.c
 themeedit_la_SOURCES        = themeedit.c themeedit-icon.c themeedit-icon.h
-timestamp_la_SOURCES        = timestamp.c
-timestamp_format_la_SOURCES = timestamp_format.c
-vvconfig_la_SOURCES         = vvconfig.c
+webkit_la_SOURCES           = webkit.c
 xmppconsole_la_SOURCES      = xmppconsole.c
 
 convcolors_la_LIBADD        = $(GTK_LIBS)
@@ -103,16 +97,13 @@
 gtkbuddynote_la_LIBADD      = $(GTK_LIBS)
 history_la_LIBADD           = $(GTK_LIBS)
 iconaway_la_LIBADD          = $(GTK_LIBS)
-markerline_la_LIBADD        = $(GTK_LIBS)
 notify_la_LIBADD            = $(GTK_LIBS)
 pidginrc_la_LIBADD          = $(GTK_LIBS)
 relnot_la_LIBADD            = $(GLIB_LIBS)
 sendbutton_la_LIBADD        = $(GTK_LIBS)
 spellchk_la_LIBADD          = $(GTK_LIBS)
 themeedit_la_LIBADD         = $(GTK_LIBS)
-timestamp_la_LIBADD         = $(GTK_LIBS)
-timestamp_format_la_LIBADD  = $(GTK_LIBS)
-vvconfig_la_LIBADD          = $(GTK_LIBS) $(GSTREAMER_LIBS)
+webkit_la_LIBADD            = $(GTK_LIBS) $(WEBKIT_LIBS)
 xmppconsole_la_LIBADD       = $(GTK_LIBS)
 
 endif # PLUGINS
@@ -136,6 +127,7 @@
 	-I$(top_srcdir)/pidgin \
 	$(DEBUG_CFLAGS) \
 	$(GTK_CFLAGS) \
+	$(WEBKIT_CFLAGS) \
 	$(GSTREAMER_CFLAGS) \
 	$(PLUGIN_CFLAGS)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/adiumthemes/Makefile.am	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,30 @@
+
+adiumtemplatedir = $(datadir)/pidgin/webkit
+adiumtemplate_DATA = Template.html
+
+adiumdir = $(libdir)/pidgin
+
+adium_la_LDFLAGS = -module -avoid-version
+
+EXTRA_DIST = $(webkittemplate_DATA)
+
+if PLUGINS
+
+adium_LTLIBRARIES = adium.la
+
+adium_la_SOURCES = webkit.c \
+	message-style.h \
+	message-style.c
+
+endif
+
+adium_la_LIBADD = $(GTK_LIBS) $(WEBKIT_LIBS)
+
+AM_CPPFLAGS = \
+	-DDATADIR=\"$(datadir)\" \
+	-I$(top_srcdir)/libpurple \
+	-I$(top_builddir)/libpurple \
+	-I$(top_srcdir)/pidgin \
+	$(DEBUG_CFLAGS) \
+	$(GTK_CFLAGS) \
+	$(WEBKIT_CFLAGS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/adiumthemes/Template.html	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+	<base href="%@">
+	<script type="text/ecmascript" defer="defer">
+	
+		//Appending new content to the message view
+		function appendMessage(html) {
+			shouldScroll = nearBottom();
+		
+			//Remove any existing insertion point
+			insert = document.getElementById("insert");
+			if(insert) insert.parentNode.removeChild(insert);
+
+			//Append the new message to the bottom of our chat block
+			chat = document.getElementById("Chat");
+			range = document.createRange();
+			range.selectNode(chat);
+			documentFragment = range.createContextualFragment(html);
+			chat.appendChild(documentFragment);
+			
+			alignChat(shouldScroll);
+		}
+		function appendMessageNoScroll(html) {
+			//Remove any existing insertion point
+			insert = document.getElementById("insert");
+			if(insert) insert.parentNode.removeChild(insert);
+
+			//Append the new message to the bottom of our chat block
+			chat = document.getElementById("Chat");
+			range = document.createRange();
+			range.selectNode(chat);
+			documentFragment = range.createContextualFragment(html);
+			chat.appendChild(documentFragment);
+		}
+		function appendNextMessage(html){
+			shouldScroll = nearBottom();
+
+			//Locate the insertion point
+			insert = document.getElementById("insert");
+		
+			//make new node
+			range = document.createRange();
+			range.selectNode(insert.parentNode);
+			newNode = range.createContextualFragment(html);
+
+			//swap
+			insert.parentNode.replaceChild(newNode,insert);
+			
+			alignChat(shouldScroll);
+		}
+		function appendNextMessageNoScroll(html){
+			//Locate the insertion point
+			insert = document.getElementById("insert");
+		
+			//make new node
+			range = document.createRange();
+			range.selectNode(insert.parentNode);
+			newNode = range.createContextualFragment(html);
+
+			//swap
+			insert.parentNode.replaceChild(newNode,insert);
+		}
+		
+		//Auto-scroll to bottom.  Use nearBottom to determine if a scrollToBottom is desired.
+		function nearBottom() {
+			return ( document.body.scrollTop >= ( document.body.offsetHeight - ( window.innerHeight * 1.2 ) ) );
+		}
+		function scrollToBottom() {
+			document.body.scrollTop = document.body.offsetHeight;
+		}
+
+		//Dynamically exchange the active stylesheet
+		function setStylesheet( id, url ) {
+			code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">";
+			if( url.length ) code += "@import url( \"" + url + "\" );";
+			code += "</style>";
+			range = document.createRange();
+			head = document.getElementsByTagName( "head" ).item(0);
+			range.selectNode( head );
+			documentFragment = range.createContextualFragment( code );
+			head.removeChild( document.getElementById( id ) );
+			head.appendChild( documentFragment );
+		}
+		
+		//Swap an image with its alt-tag text on click, or expand/unexpand an attached image
+		document.onclick = imageCheck;
+		function imageCheck() {		
+			node = event.target;
+			if(node.tagName == 'IMG' && !client.zoomImage(node) && node.alt) {
+				a = document.createElement('a');
+				a.setAttribute('onclick', 'imageSwap(this)');
+				a.setAttribute('src', node.getAttribute('src'));
+				a.className = node.className;
+				text = document.createTextNode(node.alt);
+				a.appendChild(text);
+				node.parentNode.replaceChild(a, node);
+			}
+		}
+
+		function imageSwap(node) {
+			shouldScroll = nearBottom();
+
+			//Swap the image/text
+			img = document.createElement('img');
+			img.setAttribute('src', node.getAttribute('src'));
+			img.setAttribute('alt', node.firstChild.nodeValue);
+			img.className = node.className;
+			node.parentNode.replaceChild(img, node);
+			
+			alignChat(shouldScroll);
+		}
+		
+		//Align our chat to the bottom of the window.  If true is passed, view will also be scrolled down
+		function alignChat(shouldScroll) {
+			var windowHeight = window.innerHeight;
+			
+			if (windowHeight > 0) {
+				var contentElement = document.getElementById('Chat');
+				var contentHeight = contentElement.offsetHeight;
+				if (windowHeight - contentHeight > 0) {
+					contentElement.style.position = 'relative';
+					contentElement.style.top = (windowHeight - contentHeight) + 'px';
+				} else {
+					contentElement.style.position = 'static';
+				}
+			}
+			
+			if (shouldScroll) scrollToBottom();
+		}
+		
+		function windowDidResize(){
+			alignChat(true/*nearBottom()*/); //nearBottom buggy with inactive tabs
+		}
+		
+		window.onresize = windowDidResize;
+	</script>
+	
+	<style type="text/css">
+		.actionMessageUserName:before { content:"*"; }
+		.actionMessageBody:after { content:"*"; }
+		*{ word-wrap:break-word; }
+		img.scaledToFitImage { height:auto; width:100%; }
+	</style>
+	
+	<!-- This style is shared by all variants. !-->
+	<style id="baseStyle" type="text/css" media="screen,print">	
+		%@
+	</style>
+	
+	<!-- Although we call this mainStyle for legacy reasons, it's actually the variant style !-->
+	<style id="mainStyle" type="text/css" media="screen,print">	
+		@import url( "%@" );
+	</style>
+
+</head>
+<body onload="alignChat(true);" style="==bodyBackground==">
+%@
+<div id="Chat">
+</div>
+%@
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/adiumthemes/message-style.c	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,429 @@
+/* pidgin
+ *
+ * Pidgin 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 "message-style.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#include <debug.h>
+#include <util.h>
+
+static void
+glist_free_all_string(GList *list)
+{
+	for (; list; list = g_list_delete_link(list, list))
+		g_free(list->data);
+}
+
+static PidginMessageStyle *
+pidgin_message_style_new(const char *styledir)
+{
+	PidginMessageStyle *ret = g_new0(PidginMessageStyle, 1);
+
+	ret->ref_counter = 1;
+	ret->style_dir = g_strdup(styledir);
+
+	return ret;
+}
+
+void
+pidgin_message_style_unref(PidginMessageStyle *style)
+{
+	if (!style)
+		return;
+	g_assert (style->ref_counter > 0);
+
+	style->ref_counter--;
+	if (style->ref_counter)
+		return;
+
+	g_free(style->cf_bundle_name);
+	g_free(style->cf_bundle_identifier);
+	g_free(style->cf_bundle_get_info_string);
+	g_free(style->default_font_family);
+	g_free(style->default_background_color);
+	g_free(style->image_mask);
+	g_free(style->default_variant);
+
+	g_free(style->style_dir);
+	g_free(style->template_path);
+
+	g_free(style->template_html);
+	g_free(style->incoming_content_html);
+	g_free(style->outgoing_content_html);
+	g_free(style->outgoing_next_content_html);
+	g_free(style->status_html);
+	g_free(style->basestyle_css);
+
+	g_free(style);
+}
+
+void
+pidgin_message_style_save_state(const PidginMessageStyle *style)
+{
+	char *prefname = g_strdup_printf("/plugins/gtk/adiumthemes/%s", style->cf_bundle_identifier);
+	char *variant = g_strdup_printf("%s/variant", prefname);
+
+	purple_debug_info("webkit", "saving state with variant %s\n", style->variant);
+	purple_prefs_add_none(prefname);
+	purple_prefs_add_string(variant, "");
+	purple_prefs_set_string(variant, style->variant);
+
+	g_free(prefname);
+	g_free(variant);
+}
+
+static void
+pidgin_message_style_load_state(PidginMessageStyle *style)
+{
+	char *prefname = g_strdup_printf("/plugins/gtk/adiumthemes/%s", style->cf_bundle_identifier);
+	char *variant = g_strdup_printf("%s/variant", prefname);
+
+	const char* value = purple_prefs_get_string(variant);
+	gboolean changed = !style->variant || !g_str_equal(style->variant, value);
+
+	g_free(style->variant);
+	style->variant = g_strdup(value);
+
+	if (changed)
+		pidgin_message_style_read_info_plist(style, style->variant);
+
+	g_free(prefname);
+	g_free(variant);
+}
+
+
+static gboolean
+parse_info_plist_key_value(xmlnode* key, gpointer destination, const char* expected)
+{
+	xmlnode *val = key->next;
+
+	for (; val && val->type != XMLNODE_TYPE_TAG; val = val->next)
+		;
+	if (!val)
+		return FALSE;
+
+	if (expected == NULL || g_str_equal(expected, "string")) {
+		char **dest = (char **)destination;
+		if (!g_str_equal(val->name, "string"))
+			return FALSE;
+		if (*dest)
+			g_free(*dest);
+		*dest = xmlnode_get_data_unescaped(val);
+	} else if (g_str_equal(expected, "integer")) {
+		int *dest = (int *)destination;
+		char *value = xmlnode_get_data_unescaped(val);
+
+		if (!g_str_equal(val->name, "integer"))
+			return FALSE;
+		*dest = atoi(value);
+		g_free(value);
+	} else if (g_str_equal(expected, "boolean")) {
+		gboolean *dest = (gboolean *)destination;
+		if (g_str_equal(val->name, "true"))
+			*dest = TRUE;
+		else if (g_str_equal(val->name, "false"))
+			*dest = FALSE;
+		else
+			return FALSE;
+	} else return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+str_for_key(const char *key, const char *found, const char *variant)
+{
+	if (g_str_equal(key, found))
+		return TRUE;
+	if (!variant)
+		return FALSE;
+	return (g_str_has_prefix(found, key)
+		&& g_str_has_suffix(found, variant)
+		&& strlen(found) == strlen(key) + strlen(variant) + 1);
+}
+
+/**
+ * Info.plist should be re-read every time the variant changes, this is because
+ * the keys that take precedence depend on the value of the current variant.
+ */
+void
+pidgin_message_style_read_info_plist(PidginMessageStyle *style, const char *variant)
+{
+	/* note that if a variant is used the option:VARIANTNAME takes precedence */
+	char *contents = g_build_filename(style->style_dir, "Contents", NULL);
+	xmlnode *plist = xmlnode_from_file(contents, "Info.plist", "Info.plist", "webkit"), *iter;
+	xmlnode *dict = xmlnode_get_child(plist, "dict");
+
+	g_assert (dict);
+	for (iter = xmlnode_get_child(dict, "key"); iter; iter = xmlnode_get_next_twin(iter)) {
+		char* key = xmlnode_get_data_unescaped(iter);
+		gboolean pr = TRUE;
+
+		if (g_str_equal("MessageViewVersion", key))
+			pr = parse_info_plist_key_value(iter, &style->message_view_version, "integer");
+		else if (g_str_equal("CFBundleName", key))
+			pr = parse_info_plist_key_value(iter, &style->cf_bundle_name, "string");
+		else if (g_str_equal("CFBundleIdentifier", key))
+			pr = parse_info_plist_key_value(iter, &style->cf_bundle_identifier, "string");
+		else if (g_str_equal("CFBundleGetInfoString", key))
+			pr = parse_info_plist_key_value(iter, &style->cf_bundle_get_info_string, "string");
+		else if (str_for_key("DefaultFontFamily", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->default_font_family, "string");
+		else if (str_for_key("DefaultFontSize", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->default_font_size, "integer");
+		else if (str_for_key("ShowsUserIcons", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->shows_user_icons, "boolean");
+		else if (str_for_key("DisableCombineConsecutive", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->disable_combine_consecutive, "boolean");
+		else if (str_for_key("DefaultBackgroundIsTransparent", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->default_background_is_transparent, "boolean");
+		else if (str_for_key("DisableCustomBackground", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->disable_custom_background, "boolean");
+		else if (str_for_key("DefaultBackgroundColor", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->default_background_color, "string");
+		else if (str_for_key("AllowTextColors", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->allow_text_colors, "integer");
+		else if (str_for_key("ImageMask", key, variant))
+			pr = parse_info_plist_key_value(iter, &style->image_mask, "string");
+
+		if (!pr)
+			purple_debug_warning("webkit", "Failed to parse key %s\n", key);
+		g_free(key);
+	}
+
+	xmlnode_free(plist);
+}
+
+PidginMessageStyle *
+pidgin_message_style_load(const char *styledir)
+{
+	/*
+	 * the loading process described:
+	 *
+	 * First we load all the style .html files, etc.
+	 * The we load any config options that have been stored for
+	 * this variant.
+	 * Then we load the Info.plist, for the currently decided variant.
+	 * At this point, if we find that variants exist, yet
+	 * we don't have a variant selected, we choose DefaultVariant
+	 * and if that does not exist, we choose the first one in the
+	 * directory.
+	 */
+	char *file;
+	PidginMessageStyle *style = NULL;
+
+	style = pidgin_message_style_new(styledir);
+
+	/* load all other files */
+
+	/* The template path can either come from the theme, or can
+	 * be stock Template.html that comes with the plugin */
+	style->template_path = g_build_filename(styledir, "Contents", "Resources", "Template.html", NULL);
+
+	if (!g_file_test(style->template_path, G_FILE_TEST_EXISTS)) {
+		g_free(style->template_path);
+		style->template_path = g_build_filename(DATADIR, "pidgin", "webkit", "Template.html", NULL);
+	}
+
+	if (!g_file_get_contents(style->template_path, &style->template_html, NULL, NULL)) {
+		purple_debug_error("webkit", "Could not locate a Template.html (%s)\n", style->template_path);
+		pidgin_message_style_unref(style);
+		return NULL;
+	}
+
+	file = g_build_filename(styledir, "Contents", "Resources", "Status.html", NULL);
+	if (!g_file_get_contents(file, &style->status_html, NULL, NULL)) {
+		purple_debug_info("webkit", "%s could not find Resources/Status.html", styledir);
+		pidgin_message_style_unref(style);
+		g_free(file);
+		return NULL;
+	}
+	g_free(file);
+
+	file = g_build_filename(styledir, "Contents", "Resources", "main.css", NULL);
+	if (!g_file_get_contents(file, &style->basestyle_css, NULL, NULL))
+		style->basestyle_css = g_strdup("");
+	g_free(file);
+
+	file = g_build_filename(styledir, "Contents", "Resources", "Header.html", NULL);
+	if (!g_file_get_contents(file, &style->header_html, NULL, NULL))
+		style->header_html = g_strdup("");
+	g_free(file);
+
+	file = g_build_filename(styledir, "Contents", "Resources", "Footer.html", NULL);
+	if (!g_file_get_contents(file, &style->footer_html, NULL, NULL))
+		style->footer_html = g_strdup("");
+	g_free(file);
+
+	file = g_build_filename(styledir, "Contents", "Resources", "Incoming", "Content.html", NULL);
+	if (!g_file_get_contents(file, &style->incoming_content_html, NULL, NULL)) {
+		purple_debug_info("webkit", "%s did not have a Incoming/Content.html\n", styledir);
+		pidgin_message_style_unref(style);
+		g_free(file);
+		return NULL;
+	}
+	g_free(file);
+
+
+	/* according to the spec, the following are optional files */
+	file = g_build_filename(styledir, "Contents", "Resources", "Incoming", "NextContent.html", NULL);
+	if (!g_file_get_contents(file, &style->incoming_next_content_html, NULL, NULL)) {
+		style->incoming_next_content_html = g_strdup(style->incoming_content_html);
+	}
+	g_free(file);
+
+	file = g_build_filename(styledir, "Contents", "Resources", "Outgoing", "Content.html", NULL);
+	if (!g_file_get_contents(file, &style->outgoing_content_html, NULL, NULL)) {
+		style->outgoing_content_html = g_strdup(style->incoming_content_html);
+	}
+	g_free(file);
+
+	file = g_build_filename(styledir, "Contents", "Resources", "Outgoing", "NextContent.html", NULL);
+	if (!g_file_get_contents(file, &style->outgoing_next_content_html, NULL, NULL)) {
+		style->outgoing_next_content_html = g_strdup(style->outgoing_content_html);
+	}
+
+	pidgin_message_style_read_info_plist(style, NULL);
+	pidgin_message_style_load_state(style);
+
+	/* non variant dependent Info.plist checks */
+	if (style->message_view_version < 3) {
+		purple_debug_info("webkit", "%s is a legacy style (version %d) and will not be loaded\n", style->cf_bundle_name, style->message_view_version);
+		pidgin_message_style_unref(style);
+		return NULL;
+	}
+
+	if (!style->variant)
+	{
+		GList *variants = pidgin_message_style_get_variants(style);
+
+		if (variants)
+			pidgin_message_style_set_variant(style, variants->data);
+
+		glist_free_all_string(variants);
+	}
+
+	return style;
+}
+
+PidginMessageStyle *
+pidgin_message_style_copy(const PidginMessageStyle *style)
+{
+	PidginMessageStyle *ret = pidgin_message_style_new(style->style_dir);
+
+	ret->variant = g_strdup(style->variant);
+	ret->message_view_version = style->message_view_version;
+	ret->cf_bundle_name = g_strdup(style->cf_bundle_name);
+	ret->cf_bundle_identifier = g_strdup(style->cf_bundle_identifier);
+	ret->cf_bundle_get_info_string = g_strdup(style->cf_bundle_get_info_string);
+	ret->default_font_family = g_strdup(style->default_font_family);
+	ret->default_font_size = style->default_font_size;
+	ret->shows_user_icons = style->shows_user_icons;
+	ret->disable_combine_consecutive = style->disable_combine_consecutive;
+	ret->default_background_is_transparent = style->default_background_is_transparent;
+	ret->disable_custom_background = style->disable_custom_background;
+	ret->default_background_color = g_strdup(style->default_background_color);
+	ret->allow_text_colors = style->allow_text_colors;
+	ret->image_mask = g_strdup(style->image_mask);
+	ret->default_variant = g_strdup(style->default_variant);
+
+	ret->template_path = g_strdup(style->template_path);
+	ret->template_html = g_strdup(style->template_html);
+	ret->header_html = g_strdup(style->header_html);
+	ret->footer_html = g_strdup(style->footer_html);
+	ret->incoming_content_html = g_strdup(style->incoming_content_html);
+	ret->outgoing_content_html = g_strdup(style->outgoing_content_html);
+	ret->incoming_next_content_html = g_strdup(style->incoming_next_content_html);
+	ret->outgoing_next_content_html = g_strdup(style->outgoing_next_content_html);
+	ret->status_html = g_strdup(style->status_html);
+	ret->basestyle_css = g_strdup(style->basestyle_css);
+	return ret;
+}
+
+void
+pidgin_message_style_set_variant(PidginMessageStyle *style, const char *variant)
+{
+	/* I'm not going to test whether this variant is valid! */
+	g_free(style->variant);
+	style->variant = g_strdup(variant);
+
+	pidgin_message_style_read_info_plist(style, variant);
+
+	/* todo, the style has "changed". Ideally, I would like to use signals at this point. */
+}
+
+char *
+pidgin_message_style_get_variant(PidginMessageStyle *style)
+{
+	return g_strdup(style->variant);
+}
+
+/**
+ * Get a list of variants supported by the style.
+ */
+GList*
+pidgin_message_style_get_variants(PidginMessageStyle *style)
+{
+	GList *ret = NULL;
+	GDir *variants;
+	const char *css_file;
+	char *css;
+	char *variant_dir;
+
+	g_assert(style->style_dir);
+	variant_dir = g_build_filename(style->style_dir, "Contents", "Resources", "Variants", NULL);
+
+	variants = g_dir_open(variant_dir, 0, NULL);
+	if (!variants)
+		return NULL;
+
+	while ((css_file = g_dir_read_name(variants)) != NULL) {
+		if (!g_str_has_suffix(css_file, ".css"))
+			continue;
+
+		css = g_strndup(css_file, strlen(css_file) - 4);
+		ret = g_list_append(ret, css);
+	}
+
+	g_dir_close(variants);
+	g_free(variant_dir);
+
+	ret = g_list_sort(ret, (GCompareFunc)g_strcmp0);
+	return ret;
+}
+
+char *
+pidgin_message_style_get_css(PidginMessageStyle *style)
+{
+	if (!style->variant) {
+		return g_build_filename(style->style_dir, "Contents", "Resources", "main.css", NULL);
+	} else {
+		char *file = g_strdup_printf("%s.css", style->variant);
+		char *ret = g_build_filename(style->style_dir, "Contents", "Resources", "Variants",  file, NULL);
+		g_free(file);
+		return ret;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/adiumthemes/message-style.h	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,81 @@
+/* pidgin
+ *
+ * Pidgin 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 <glib.h>
+
+/*
+ * I'm going to allow a different style for each PidginConversation.
+ * This way I can do two things: 1) change the theme on the fly and not
+ * change existing themes, and 2) Use a different theme for IMs and
+ * chats.
+ */
+typedef struct _PidginMessageStyle {
+	int     ref_counter;
+
+	/* current config options */
+	char     *variant; /* allowed to be NULL if there are no variants */
+
+	/* Info.plist keys that change with Variant */
+
+	/* Static Info.plist keys */
+	int      message_view_version;
+	char     *cf_bundle_name;
+	char     *cf_bundle_identifier;
+	char     *cf_bundle_get_info_string;
+	char     *default_font_family;
+	int      default_font_size;
+	gboolean shows_user_icons;
+	gboolean disable_combine_consecutive;
+	gboolean default_background_is_transparent;
+	gboolean disable_custom_background;
+	char     *default_background_color;
+	gboolean allow_text_colors;
+	char     *image_mask;
+	char     *default_variant;
+
+	/* paths */
+	char    *style_dir;
+	char    *template_path;
+
+	/* caches */
+	char    *template_html;
+	char    *header_html;
+	char    *footer_html;
+	char    *incoming_content_html;
+	char    *outgoing_content_html;
+	char    *incoming_next_content_html;
+	char    *outgoing_next_content_html;
+	char    *status_html;
+	char    *basestyle_css;
+} PidginMessageStyle;
+
+PidginMessageStyle *pidgin_message_style_load(const char *styledir);
+PidginMessageStyle *pidgin_message_style_copy(const PidginMessageStyle *style);
+void pidgin_message_style_save_state(const PidginMessageStyle *style);
+void pidgin_message_style_unref(PidginMessageStyle *style);
+void pidgin_message_style_read_info_plist(PidginMessageStyle *style, const char *variant);
+char *pidgin_message_style_get_variant(PidginMessageStyle *style);
+GList *pidgin_message_style_get_variants(PidginMessageStyle *style);
+void pidgin_message_style_set_variant(PidginMessageStyle *style, const char *variant);
+
+char *pidgin_message_style_get_css(PidginMessageStyle *style);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/adiumthemes/webkit.c	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,856 @@
+/* pidgin
+ *
+ * Pidgin 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
+ *
+ */
+
+#define PLUGIN_ID		"gtk-webview-adium-ims"
+#define PLUGIN_NAME		"webview-adium-ims"
+
+/*
+ * A lot of this was originally written by Sean Egan, but I think I've
+ * rewrote enough to replace the author for now.
+ */
+#define PLUGIN_AUTHOR		"Arnold Noronha <arnstein87@gmail.com>"
+#define PURPLE_PLUGINS		"Hell yeah"
+
+/* System headers */
+#include <string.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include <webkit/webkit.h>
+
+/* Purple headers */
+#include <conversation.h>
+#include <debug.h>
+#include <internal.h>
+#include <notify.h>
+#include <util.h>
+#include <version.h>
+
+/* Pidgin headers */
+#include <gtkconv.h>
+#include <gtkplugin.h>
+#include <gtkwebview.h>
+#include <smileyparser.h>
+
+#include <libxml/xmlreader.h>
+
+#include "message-style.h"
+/* GObject data keys */
+#define MESSAGE_STYLE_KEY "message-style"
+
+static char  *cur_style_dir = NULL;
+static void  *handle = NULL;
+
+static inline char *
+get_absolute_path(const char *path)
+{
+	if (g_path_is_absolute(path))
+		return g_strdup(path);
+	else {
+		char *cwd, *ret;
+		cwd = g_get_current_dir();
+		ret = g_build_filename(cwd, path, NULL);
+		g_free(cwd);
+		return ret;
+	}
+}
+
+static void webkit_on_webview_destroy(GtkObject* obj, gpointer data);
+
+static void *
+webkit_plugin_get_handle(void)
+{
+	if (handle)
+		return handle;
+	else
+		return (handle = g_malloc(1));
+}
+
+static void
+webkit_plugin_free_handle(void)
+{
+	purple_signals_disconnect_by_handle(handle);
+	g_free(handle);
+}
+
+static char *
+replace_message_tokens(
+	const char *text,
+	PurpleConversation *conv,
+	const char *name,
+	const char *alias,
+	const char *message,
+	PurpleMessageFlags flags,
+	time_t mtime)
+{
+	GString *str = g_string_new(NULL);
+	const char *cur = text;
+	const char *prev = cur;
+
+	while ((cur = strchr(cur, '%'))) {
+		const char *replace = NULL;
+		char *fin = NULL;
+
+		if (!strncmp(cur, "%message%", strlen("%message%"))) {
+			replace = message;
+		} else if (!strncmp(cur, "%messageClasses%", strlen("%messageClasses%"))) {
+			replace = flags & PURPLE_MESSAGE_SEND ? "outgoing" :
+				  flags & PURPLE_MESSAGE_RECV ? "incoming" : "event";
+		} else if (!strncmp(cur, "%time", strlen("%time"))) {
+			char *format = NULL;
+			if (*(cur + strlen("%time")) == '{') {
+				const char *start = cur + strlen("%time") + 1;
+				char *end = strstr(start, "}%");
+				if (!end) /* Invalid string */
+					continue;
+				format = g_strndup(start, end - start);
+				fin = end + 1;
+			}
+			replace = purple_utf8_strftime(format ? format : "%X", NULL);
+			g_free(format);
+		} else if (!strncmp(cur, "%userIconPath%", strlen("%userIconPath%"))) {
+			if (flags & PURPLE_MESSAGE_SEND) {
+				if (purple_account_get_bool(conv->account, "use-global-buddyicon", TRUE)) {
+					replace = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
+				} else {
+					PurpleStoredImage *img = purple_buddy_icons_find_account_icon(conv->account);
+					replace = purple_imgstore_get_filename(img);
+				}
+				if (replace == NULL || !g_file_test(replace, G_FILE_TEST_EXISTS)) {
+					replace = g_build_filename("Outgoing", "buddy_icon.png", NULL);
+				}
+			} else if (flags & PURPLE_MESSAGE_RECV) {
+				PurpleBuddyIcon *icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
+				replace = purple_buddy_icon_get_full_path(icon);
+				if (replace == NULL || !g_file_test(replace, G_FILE_TEST_EXISTS)) {
+					replace = g_build_filename("Incoming", "buddy_icon.png", NULL);
+				}
+			}
+
+		} else if (!strncmp(cur, "%senderScreenName%", strlen("%senderScreenName%"))) {
+			replace = name;
+		} else if (!strncmp(cur, "%sender%", strlen("%sender%"))) {
+			replace = alias;
+		} else if (!strncmp(cur, "%service%", strlen("%service%"))) {
+			replace = purple_account_get_protocol_name(conv->account);
+		} else {
+			cur++;
+			continue;
+		}
+
+		/* Here we have a replacement to make */
+		g_string_append_len(str, prev, cur - prev);
+		g_string_append(str, replace);
+
+		/* And update the pointers */
+		if (fin) {
+			prev = cur = fin + 1;
+		} else {
+			prev = cur = strchr(cur + 1, '%') + 1;
+		}
+
+	}
+
+	/* And wrap it up */
+	g_string_append(str, prev);
+	return g_string_free(str, FALSE);
+}
+
+static char *
+replace_header_tokens(char *text, PurpleConversation *conv)
+{
+	GString *str = g_string_new(NULL);
+	char *cur = text;
+	char *prev = cur;
+
+	if (text == NULL)
+		return NULL;
+
+	while ((cur = strchr(cur, '%'))) {
+		const char *replace = NULL;
+		char *fin = NULL;
+
+		if (!strncmp(cur, "%chatName%", strlen("%chatName%"))) {
+			replace = conv->name;
+		} else if (!strncmp(cur, "%sourceName%", strlen("%sourceName%"))) {
+			replace = purple_account_get_alias(conv->account);
+			if (replace == NULL)
+				replace = purple_account_get_username(conv->account);
+		} else if (!strncmp(cur, "%destinationName%", strlen("%destinationName%"))) {
+			PurpleBuddy *buddy = purple_find_buddy(conv->account, conv->name);
+			if (buddy) {
+				replace = purple_buddy_get_alias(buddy);
+			} else {
+				replace = conv->name;
+			}
+		} else if (!strncmp(cur, "%incomingIconPath%", strlen("%incomingIconPath%"))) {
+			PurpleBuddyIcon *icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
+			replace = purple_buddy_icon_get_full_path(icon);
+		} else if (!strncmp(cur, "%outgoingIconPath%", strlen("%outgoingIconPath%"))) {
+		} else if (!strncmp(cur, "%timeOpened", strlen("%timeOpened"))) {
+			char *format = NULL;
+			if (*(cur + strlen("%timeOpened")) == '{') {
+				char *start = cur + strlen("%timeOpened") + 1;
+				char *end = strstr(start, "}%");
+				if (!end) /* Invalid string */
+					continue;
+				format = g_strndup(start, end - start);
+				fin = end + 1;
+			}
+			replace = purple_utf8_strftime(format ? format : "%X", NULL);
+			g_free(format);
+		} else {
+			continue;
+		}
+
+		/* Here we have a replacement to make */
+		g_string_append_len(str, prev, cur - prev);
+		g_string_append(str, replace);
+
+		/* And update the pointers */
+		if (fin) {
+			prev = cur = fin + 1;
+		} else {
+			prev = cur = strchr(cur + 1, '%') + 1;
+		}
+	}
+
+	/* And wrap it up */
+	g_string_append(str, prev);
+	return g_string_free(str, FALSE);
+}
+
+static char *
+replace_template_tokens(PidginMessageStyle *style, char *text, char *header, char *footer)
+{
+	GString *str = g_string_new(NULL);
+
+	char **ms = g_strsplit(text, "%@", 6);
+	char *base = NULL;
+	char *csspath = pidgin_message_style_get_css(style);
+	if (ms[0] == NULL || ms[1] == NULL || ms[2] == NULL || ms[3] == NULL || ms[4] == NULL || ms[5] == NULL) {
+		g_strfreev(ms);
+		g_string_free(str, TRUE);
+		return NULL;
+	}
+
+	g_string_append(str, ms[0]);
+	g_string_append(str, "file://");
+	base = g_build_filename(style->style_dir, "Contents", "Resources", "Template.html", NULL);
+	g_string_append(str, base);
+	g_free(base);
+
+	g_string_append(str, ms[1]);
+
+	g_string_append(str, style->basestyle_css);
+
+	g_string_append(str, ms[2]);
+
+	g_string_append(str, "file://");
+	g_string_append(str, csspath);
+
+	g_string_append(str, ms[3]);
+	if (header)
+		g_string_append(str, header);
+	g_string_append(str, ms[4]);
+	if (footer)
+		g_string_append(str, footer);
+	g_string_append(str, ms[5]);
+
+	g_strfreev(ms);
+	g_free(csspath);
+	return g_string_free(str, FALSE);
+}
+
+static GtkWidget *
+get_webkit(PurpleConversation *conv)
+{
+	PidginConversation *gtkconv;
+	gtkconv = PIDGIN_CONVERSATION(conv);
+	if (!gtkconv)
+		return NULL;
+	else
+		return gtkconv->webview;
+}
+
+static void
+set_theme_webkit_settings(WebKitWebView *webview, PidginMessageStyle *style)
+{
+	WebKitWebSettings *settings;
+
+	g_object_get(G_OBJECT(webview), "settings", &settings, NULL);
+	if (style->default_font_family)
+		g_object_set(G_OBJECT(settings), "default-font-family", style->default_font_family, NULL);
+
+	if (style->default_font_size)
+		g_object_set(G_OBJECT(settings), "default-font-size", GINT_TO_POINTER(style->default_font_size), NULL);
+
+	/* this does not work :( */
+	webkit_web_view_set_transparent(webview, style->default_background_is_transparent);
+}
+
+/*
+ * The style specification says that if the conversation is a group
+ * chat then the <div id="Chat"> element will be given a class
+ * 'groupchat'. I can't add another '%@' in Template.html because
+ * that breaks style-specific Template.html's. I have to either use libxml
+ * or conveniently play with WebKit's javascript engine. The javascript
+ * engine should work, but it's not an identical behavior.
+ */
+static void
+webkit_set_groupchat(GtkWebView *webview)
+{
+	gtk_webview_safe_execute_script(webview, "document.getElementById('Chat').className = 'groupchat'");
+}
+
+
+/**
+ * Called when either a new PurpleConversation is created
+ * or when a PidginConversation changes its active PurpleConversation
+ * This will not change the theme if the theme is already set.
+ * (This is to prevent accidental theme changes if a new
+ * PurpleConversation gets added.
+ *
+ * FIXME: it's not at all clear to me as to how
+ * Adium themes handle the case when the PurpleConversation
+ * changes.
+ */
+static void
+init_theme_for_webkit(PurpleConversation *conv, char *style_dir)
+{
+	GtkWidget *webkit = PIDGIN_CONVERSATION(conv)->webview;
+	char *header, *footer;
+	char *template;
+
+	char* basedir;
+	char* baseuri;
+	PidginMessageStyle *style, *oldStyle;
+	PidginMessageStyle *copy;
+
+	oldStyle = g_object_get_data(G_OBJECT(webkit), MESSAGE_STYLE_KEY);
+	if (oldStyle)
+		return;
+
+	purple_debug_info("webkit", "loading %s\n", style_dir);
+	style = pidgin_message_style_load(style_dir);
+	g_assert(style);
+	g_assert(style->template_html); /* debugging test? */
+
+	basedir = g_build_filename(style->style_dir, "Contents", "Resources", "Template.html", NULL);
+	baseuri = g_strdup_printf("file://%s", basedir);
+	header = replace_header_tokens(style->header_html, conv);
+	g_assert(style);
+	footer = replace_header_tokens(style->footer_html, conv);
+	template = replace_template_tokens(style, style->template_html, header, footer);
+
+	g_assert(template);
+
+	purple_debug_info("webkit", "template: %s\n", template);
+
+	set_theme_webkit_settings(WEBKIT_WEB_VIEW(webkit), style);
+	webkit_web_view_load_string(WEBKIT_WEB_VIEW(webkit), template, "text/html", "UTF-8", baseuri);
+
+	copy = pidgin_message_style_copy(style);
+	g_object_set_data(G_OBJECT(webkit), MESSAGE_STYLE_KEY, copy);
+
+	pidgin_message_style_unref(style);
+	/* I need to unref this style when the webkit object destroys */
+	g_signal_connect(G_OBJECT(webkit), "destroy", G_CALLBACK(webkit_on_webview_destroy), copy);
+
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+		webkit_set_groupchat(GTK_WEBVIEW(webkit));
+	g_free(basedir);
+	g_free(baseuri);
+	g_free(header);
+	g_free(footer);
+	g_free(template);
+}
+
+
+/* restore the non theme version of the conversation window */
+static void
+finalize_theme_for_webkit(PurpleConversation *conv)
+{
+	GtkWidget *webview = PIDGIN_CONVERSATION(conv)->webview;
+	PidginMessageStyle *style = g_object_get_data(G_OBJECT(webview), MESSAGE_STYLE_KEY);
+
+	webkit_web_view_load_string(WEBKIT_WEB_VIEW(webview), "", "text/html", "UTF-8", "");
+
+	g_object_set_data(G_OBJECT(webview), MESSAGE_STYLE_KEY, NULL);
+	pidgin_message_style_unref(style);
+}
+
+static void
+webkit_on_webview_destroy(GtkObject *object, gpointer data)
+{
+	pidgin_message_style_unref((PidginMessageStyle *)data);
+	g_object_set_data(G_OBJECT(object), MESSAGE_STYLE_KEY, NULL);
+}
+
+static gboolean
+webkit_on_displaying_im_msg(PurpleAccount *account,
+						 const char* name,
+						 char **pmessage,
+						 PurpleConversation *conv,
+						 PurpleMessageFlags flags,
+						 gpointer data)
+{
+	GtkWidget *webkit;
+	char *message = *pmessage;
+	const char *alias = name; /* FIXME: signal doesn't give me alias */
+	char *stripped;
+	char *message_html;
+	char *msg;
+	char *escape;
+	char *script;
+	char *func = "appendMessage";
+	char *smileyed;
+	time_t mtime = time(NULL); /* FIXME: this should come from the write_conv calback, but the signal doesn't pass this to me */
+
+	PurpleMessageFlags old_flags = GPOINTER_TO_INT(purple_conversation_get_data(conv, "webkit-lastflags"));
+	PidginMessageStyle *style;
+
+	webkit = get_webkit(conv);
+	stripped = g_strdup(message);
+
+	style = g_object_get_data(G_OBJECT(webkit), MESSAGE_STYLE_KEY);
+	g_assert(style);
+
+	if (flags & PURPLE_MESSAGE_SEND && old_flags & PURPLE_MESSAGE_SEND) {
+		message_html = style->outgoing_next_content_html;
+		func = "appendNextMessage";
+	} else if (flags & PURPLE_MESSAGE_SEND) {
+		message_html = style->outgoing_content_html;
+	} else if (flags & PURPLE_MESSAGE_RECV && old_flags & PURPLE_MESSAGE_RECV) {
+		message_html = style->incoming_next_content_html;
+		func = "appendNextMessage";
+	} else if (flags & PURPLE_MESSAGE_RECV) {
+		message_html = style->incoming_content_html;
+	} else {
+		message_html = style->status_html;
+	}
+	purple_conversation_set_data(conv, "webkit-lastflags", GINT_TO_POINTER(flags));
+
+	smileyed = smiley_parse_markup(stripped, conv->account->protocol_id);
+	msg = replace_message_tokens(message_html, conv, name, alias, smileyed, flags, mtime);
+	escape = gtk_webview_quote_js_string(msg);
+	script = g_strdup_printf("%s(%s)", func, escape);
+
+	purple_debug_info("webkit", "JS: %s\n", script);
+	gtk_webview_safe_execute_script(GTK_WEBVIEW(webkit), script);
+
+	g_free(script);
+	g_free(smileyed);
+	g_free(msg);
+	g_free(stripped);
+	g_free(escape);
+
+	return TRUE; /* GtkConv should not handle this IM */
+}
+
+static gboolean
+webkit_on_displaying_chat_msg(PurpleAccount *account,
+					       const char *who,
+					       char **message,
+					       PurpleConversation *conv,
+					       PurpleMessageFlags flags,
+					       gpointer userdata)
+{
+	/* handle exactly like an IM message for now */
+	return webkit_on_displaying_im_msg(account, who, message, conv, flags, NULL);
+}
+
+static void
+webkit_on_conversation_displayed(PidginConversation *gtkconv, gpointer data)
+{
+	init_theme_for_webkit(gtkconv->active_conv, cur_style_dir);
+}
+
+static void
+webkit_on_conversation_switched(PurpleConversation *conv, gpointer data)
+{
+	init_theme_for_webkit(conv, cur_style_dir);
+}
+
+static void
+webkit_on_conversation_hiding(PidginConversation *gtkconv, gpointer data)
+{
+	/*
+	 * I'm not sure if I need to do anything here, but let's keep
+	 * this anyway.
+	 */
+}
+
+static GList *
+get_dir_dir_list(const char *dirname)
+{
+	GList *ret = NULL;
+	GDir  *dir = g_dir_open(dirname, 0, NULL);
+	const char* subdir;
+
+	if (!dir) return NULL;
+	while ((subdir = g_dir_read_name(dir))) {
+		ret = g_list_append(ret, g_build_filename(dirname, subdir, NULL));
+	}
+
+	g_dir_close(dir);
+	return ret;
+}
+
+/**
+ * Get me a list of all the available themes specified by their
+ * directories. I don't guarrantee that these are valid themes, just
+ * that they are in the directories for themes.
+ */
+static GList *
+get_style_directory_list(void)
+{
+	char *user_dir, *user_style_dir, *global_style_dir;
+	GList *list1, *list2;
+
+	user_dir = get_absolute_path(purple_user_dir());
+
+	user_style_dir = g_build_filename(user_dir, "styles", NULL);
+	global_style_dir = g_build_filename(DATADIR, "pidgin", "styles", NULL);
+
+	list1 = get_dir_dir_list(user_style_dir);
+	list2 = get_dir_dir_list(global_style_dir);
+
+	g_free(global_style_dir);
+	g_free(user_style_dir);
+	g_free(user_dir);
+
+	return g_list_concat(list1, list2);
+}
+
+/**
+ * use heuristics or previous user options to figure out what
+ * theme to use as default in this Pidgin instance.
+ */
+static void
+style_set_default(void)
+{
+	GList *styles = get_style_directory_list(), *iter;
+	const char *stylepath = purple_prefs_get_string("/plugins/gtk/adiumthemes/stylepath");
+	g_assert(cur_style_dir == NULL);
+
+	if (stylepath && *stylepath)
+		styles = g_list_prepend(styles, g_strdup(stylepath));
+
+	/* pick any one that works. Note that we have first preference
+	 * for the one in the userdir */
+	for (iter = styles; iter; iter = g_list_next(iter)) {
+		PidginMessageStyle *style = pidgin_message_style_load(iter->data);
+		if (style) {
+			cur_style_dir = (char *)g_strdup(iter->data);
+			pidgin_message_style_unref(style);
+			break;
+		}
+		purple_debug_info("webkit", "Style %s is invalid\n", (char *)iter->data);
+	}
+
+	for (iter = styles; iter; iter = g_list_next(iter))
+		g_free(iter->data);
+	g_list_free(styles);
+}
+
+static gboolean
+plugin_load(PurplePlugin *plugin)
+{
+	style_set_default();
+	if (!cur_style_dir)
+		return FALSE; /* couldn't find a style */
+
+	purple_signal_connect(pidgin_conversations_get_handle(),
+			       "displaying-im-msg",
+			       webkit_plugin_get_handle(),
+			       PURPLE_CALLBACK(webkit_on_displaying_im_msg),
+			       NULL);
+
+	purple_signal_connect(pidgin_conversations_get_handle(),
+			       "displaying-chat-msg",
+			       webkit_plugin_get_handle(),
+			       PURPLE_CALLBACK(webkit_on_displaying_chat_msg),
+			       NULL);
+
+	purple_signal_connect(pidgin_conversations_get_handle(),
+			       "conversation-displayed",
+			       webkit_plugin_get_handle(),
+			       PURPLE_CALLBACK(webkit_on_conversation_displayed),
+			       NULL);
+
+	purple_signal_connect(pidgin_conversations_get_handle(),
+			       "conversation-switched",
+			       webkit_plugin_get_handle(),
+			       PURPLE_CALLBACK(webkit_on_conversation_switched),
+			       NULL);
+
+	purple_signal_connect(pidgin_conversations_get_handle(),
+			       "conversation-hiding",
+			       webkit_plugin_get_handle(),
+			       PURPLE_CALLBACK(webkit_on_conversation_hiding),
+			       NULL);
+
+	/* finally update each of the existing conversation windows */
+	{
+		GList *list = purple_get_conversations();
+		for (;list; list = g_list_next(list))
+			init_theme_for_webkit(list->data, cur_style_dir);
+
+	}
+	return TRUE;
+}
+
+static gboolean
+plugin_unload(PurplePlugin *plugin)
+{
+	GList *list;
+
+	webkit_plugin_free_handle();
+	cur_style_dir = NULL;
+	list = purple_get_conversations();
+	while (list) {
+		finalize_theme_for_webkit(list->data);
+		list = g_list_next(list);
+	}
+
+	return TRUE;
+}
+
+/*
+ * UI config code
+ */
+
+static void
+style_changed(GtkWidget *combobox, gpointer null)
+{
+	char *name = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combobox));
+	GtkWidget *dialog;
+	GList *styles = get_style_directory_list(), *iter;
+
+	/* find the full path for this name, I wish I could store this info in the combobox itself. :( */
+	for (iter = styles; iter; iter = g_list_next(iter)) {
+		char *basename = g_path_get_basename(iter->data);
+		if (g_str_equal(basename, name)) {
+			g_free(basename);
+			break;
+		}
+		g_free(basename);
+	}
+
+	g_assert(iter);
+	g_free(name);
+	g_free(cur_style_dir);
+	cur_style_dir = g_strdup(iter->data);;
+	purple_prefs_set_string("/plugins/gtk/adiumthemes/stylepath", cur_style_dir);
+
+	/* inform the user that existing conversations haven't changed */
+	dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "The style for existing conversations have not been changed. Please close and re-open the conversation for the changes to take effect.");
+	g_assert(dialog);
+	gtk_widget_show(dialog);
+	g_signal_connect_swapped(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog);
+}
+
+static GtkWidget *
+get_style_config_frame(void)
+{
+	GtkWidget *combobox = gtk_combo_box_new_text();
+	GList *styles = get_style_directory_list(), *iter;
+	int index = 0, selected = 0;
+
+	for (iter = styles; iter; iter = g_list_next(iter)) {
+		PidginMessageStyle *style = pidgin_message_style_load(iter->data);
+
+		if (style) {
+			char *text = g_path_get_basename(iter->data);
+			gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), text);
+			g_free(text);
+
+			if (g_str_equal(iter->data, cur_style_dir))
+				selected = index;
+			index++;
+			pidgin_message_style_unref(style);
+		}
+	}
+	gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), selected);
+	g_signal_connect_after(G_OBJECT(combobox), "changed", G_CALLBACK(style_changed), NULL);
+	return combobox;
+}
+
+static void
+variant_update_conversation(PurpleConversation *conv)
+{
+	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+	WebKitWebView *webview = WEBKIT_WEB_VIEW(gtkconv->webview);
+	PidginMessageStyle *style = (PidginMessageStyle *)g_object_get_data(G_OBJECT(webview), MESSAGE_STYLE_KEY);
+	char *script;
+
+	g_assert(style);
+
+	script = g_strdup_printf("setStylesheet(\"mainStyle\",\"%s\")", pidgin_message_style_get_css(style));
+	gtk_webview_safe_execute_script(GTK_WEBVIEW(webview), script);
+
+	set_theme_webkit_settings(WEBKIT_WEB_VIEW(gtkconv->webview), style);
+	g_free(script);
+}
+
+static void
+variant_changed(GtkWidget* combobox, gpointer null)
+{
+	char *name;
+	GList *list;
+	PidginMessageStyle *style = pidgin_message_style_load(cur_style_dir);
+
+	g_assert(style);
+	name = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combobox));
+	pidgin_message_style_set_variant(style, name);
+	pidgin_message_style_save_state(style);
+
+	/* update conversations */
+	list = purple_get_conversations();
+	while (list) {
+		variant_update_conversation(list->data);
+		list = g_list_next(list);
+	}
+
+	g_free(name);
+	pidgin_message_style_unref(style);
+}
+
+static GtkWidget *
+get_variant_config_frame()
+{
+	PidginMessageStyle *style = pidgin_message_style_load(cur_style_dir);
+	GList *variants = pidgin_message_style_get_variants(style), *iter;
+	char *cur_variant = pidgin_message_style_get_variant(style);
+	GtkWidget *combobox = gtk_combo_box_new_text();
+	int def = -1, index = 0;
+
+	pidgin_message_style_unref(style);
+
+	for (iter = variants; iter; iter = g_list_next(iter)) {
+		gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), iter->data);
+
+		if (g_str_equal(cur_variant, iter->data))
+			def = index;
+		index ++;
+
+	}
+
+	gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), def);
+	g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(variant_changed), NULL);
+
+	return combobox;
+}
+
+static void
+style_changed_reset_variants(GtkWidget* combobox, gpointer table)
+{
+	/* I hate to do this, I swear. But I don't know how to cleanly clean an existing combobox */
+	GtkWidget* variants = g_object_get_data(G_OBJECT(table), "variants-cbox");
+	gtk_widget_destroy(variants);
+	variants = get_variant_config_frame();
+	gtk_table_attach_defaults(GTK_TABLE(table), variants, 1, 2, 1, 2);
+	gtk_widget_show_all(GTK_WIDGET(table));
+
+	g_object_set_data(G_OBJECT(table), "variants-cbox", variants);
+}
+
+static GtkWidget*
+get_config_frame(PurplePlugin* plugin)
+{
+	GtkWidget *table = gtk_table_new(2, 2, FALSE);
+	GtkWidget *style_config = get_style_config_frame();
+	GtkWidget *variant_config = get_variant_config_frame();
+
+	gtk_table_attach_defaults(GTK_TABLE(table), gtk_label_new("Message Style"), 0, 1, 0, 1);
+	gtk_table_attach_defaults(GTK_TABLE(table), style_config, 1, 2, 0, 1);
+	gtk_table_attach_defaults(GTK_TABLE(table), gtk_label_new("Style Variant"), 0, 1, 1, 2);
+	gtk_table_attach_defaults(GTK_TABLE(table), variant_config, 1, 2, 1, 2);
+
+	g_object_set_data(G_OBJECT(table), "variants-cbox", variant_config);
+	/* to clarify, this is a second signal connected on style config */
+	g_signal_connect_after(G_OBJECT(style_config), "changed", G_CALLBACK(style_changed_reset_variants), table);
+
+	return table;
+}
+
+PidginPluginUiInfo ui_info =
+{
+        get_config_frame,
+        0, /* page_num (Reserved) */
+
+        /* padding */
+        NULL,
+        NULL,
+        NULL,
+        NULL
+};
+
+
+static PurplePluginInfo info =
+{
+	PURPLE_PLUGIN_MAGIC,		/* Magic				*/
+	PURPLE_MAJOR_VERSION,		/* Purple Major Version	*/
+	PURPLE_MINOR_VERSION,		/* Purple Minor Version	*/
+	PURPLE_PLUGIN_STANDARD,		/* plugin type			*/
+	PIDGIN_PLUGIN_TYPE,			/* ui requirement		*/
+	0,							/* flags				*/
+	NULL,						/* dependencies			*/
+	PURPLE_PRIORITY_DEFAULT,	/* priority				*/
+
+	PLUGIN_ID,					/* plugin id			*/
+	NULL,						/* name					*/
+	"0.1",					/* version				*/
+	NULL,						/* summary				*/
+	NULL,						/* description			*/
+	PLUGIN_AUTHOR,				/* author				*/
+	"http://pidgin.im",					/* website				*/
+
+	plugin_load,				/* load					*/
+	plugin_unload,				/* unload				*/
+	NULL,						/* destroy				*/
+
+	&ui_info,						/* ui_info				*/
+	NULL,						/* extra_info			*/
+	NULL,						/* prefs_info			*/
+	NULL,						/* actions				*/
+	NULL,						/* reserved 1			*/
+	NULL,						/* reserved 2			*/
+	NULL,						/* reserved 3			*/
+	NULL						/* reserved 4			*/
+};
+
+static void
+init_plugin(PurplePlugin *plugin) {
+	info.name = "Adium IMs";
+	info.summary = "Adium-like IMs with Pidgin";
+	info.description = "You can chat in Pidgin using Adium's WebKit view.";
+
+	purple_prefs_add_none("/plugins");
+	purple_prefs_add_none("/plugins/gtk");
+	purple_prefs_add_none("/plugins/gtk/adiumthemes");
+	purple_prefs_add_string("/plugins/gtk/adiumthemes/stylepath", "");
+}
+
+PURPLE_INIT_PLUGIN(webkit, init_plugin, info)
+
--- a/pidgin/plugins/gestures/gestures.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/plugins/gestures/gestures.c	Mon Sep 26 14:57:21 2011 +0900
@@ -48,7 +48,7 @@
 
 	gtkconv = PIDGIN_CONVERSATION(conv);
 
-	gstroke_cleanup(gtkconv->imhtml);
+	gstroke_cleanup(gtkconv->webview);
 	purple_conversation_destroy(conv);
 }
 
@@ -126,15 +126,15 @@
 
 	gtkconv = PIDGIN_CONVERSATION(conv);
 
-	gstroke_enable(gtkconv->imhtml);
-	gstroke_signal_connect(gtkconv->imhtml, "14789",  stroke_close,    conv);
-	gstroke_signal_connect(gtkconv->imhtml, "1456",   stroke_close,    conv);
-	gstroke_signal_connect(gtkconv->imhtml, "1489",   stroke_close,    conv);
-	gstroke_signal_connect(gtkconv->imhtml, "74123",  stroke_next_tab, conv);
-	gstroke_signal_connect(gtkconv->imhtml, "7456",   stroke_next_tab, conv);
-	gstroke_signal_connect(gtkconv->imhtml, "96321",  stroke_prev_tab, conv);
-	gstroke_signal_connect(gtkconv->imhtml, "9654",   stroke_prev_tab, conv);
-	gstroke_signal_connect(gtkconv->imhtml, "25852",  stroke_new_win,  conv);
+	gstroke_enable(gtkconv->webview);
+	gstroke_signal_connect(gtkconv->webview, "14789",  stroke_close,    conv);
+	gstroke_signal_connect(gtkconv->webview, "1456",   stroke_close,    conv);
+	gstroke_signal_connect(gtkconv->webview, "1489",   stroke_close,    conv);
+	gstroke_signal_connect(gtkconv->webview, "74123",  stroke_next_tab, conv);
+	gstroke_signal_connect(gtkconv->webview, "7456",   stroke_next_tab, conv);
+	gstroke_signal_connect(gtkconv->webview, "96321",  stroke_prev_tab, conv);
+	gstroke_signal_connect(gtkconv->webview, "9654",   stroke_prev_tab, conv);
+	gstroke_signal_connect(gtkconv->webview, "25852",  stroke_new_win,  conv);
 }
 
 static void
@@ -215,8 +215,8 @@
 
 		gtkconv = PIDGIN_CONVERSATION(conv);
 
-		gstroke_cleanup(gtkconv->imhtml);
-		gstroke_disable(gtkconv->imhtml);
+		gstroke_cleanup(gtkconv->webview);
+		gstroke_disable(gtkconv->webview);
 	}
 
 	return TRUE;
--- a/pidgin/plugins/gevolution/gevo-util.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/plugins/gevolution/gevo-util.c	Mon Sep 26 14:57:21 2011 +0900
@@ -49,7 +49,7 @@
 		purple_blist_add_buddy(buddy, NULL, group, NULL);
 	}
 
-	purple_account_add_buddy(account, buddy);
+	purple_account_add_buddy(account, buddy, NULL);
 
 	if (conv != NULL)
 	{
--- a/pidgin/plugins/history.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/plugins/history.c	Mon Sep 26 14:57:21 2011 +0900
@@ -16,16 +16,17 @@
 #include "gtkconv.h"
 #include "gtkimhtml.h"
 #include "gtkplugin.h"
+#include "gtkwebview.h"
 
 #define HISTORY_PLUGIN_ID "gtk-history"
 
 #define HISTORY_SIZE (4 * 1024)
 
-static gboolean _scroll_imhtml_to_end(gpointer data)
+static gboolean _scroll_webview_to_end(gpointer data)
 {
-	GtkIMHtml *imhtml = data;
-	gtk_imhtml_scroll_to_end(GTK_IMHTML(imhtml), FALSE);
-	g_object_unref(G_OBJECT(imhtml));
+	GtkWebView *webview = data;
+	gtk_webview_scroll_to_end(GTK_WEBVIEW(webview), FALSE);
+	g_object_unref(G_OBJECT(webview));
 	return FALSE;
 }
 
@@ -39,9 +40,15 @@
 	guint flags;
 	char *history;
 	PidginConversation *gtkconv;
+#if 0
+	/* FIXME: WebView has no options */
 	GtkIMHtmlOptions options = GTK_IMHTML_NO_COLOURS;
+#endif
 	char *header;
+#if 0
+	/* FIXME: WebView has no protocol setting */
 	char *protocol;
+#endif
 	char *escaped_alias;
 	const char *header_date;
 
@@ -116,15 +123,21 @@
 
 	history = purple_log_read((PurpleLog*)logs->data, &flags);
 	gtkconv = PIDGIN_CONVERSATION(c);
+#if 0
+	/* FIXME: WebView has no options */
 	if (flags & PURPLE_LOG_READ_NO_NEWLINE)
 		options |= GTK_IMHTML_NO_NEWLINE;
+#endif
 
+#if 0
+	/* FIXME: WebView has no protocol setting */
 	protocol = g_strdup(gtk_imhtml_get_protocol_name(GTK_IMHTML(gtkconv->imhtml)));
 	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml),
 			purple_account_get_protocol_name(((PurpleLog*)logs->data)->account));
+#endif
 
-	if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml))))
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", options);
+	if (!gtk_webview_is_empty(GTK_WEBVIEW(gtkconv->webview)))
+		gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<BR>");
 
 	escaped_alias = g_markup_escape_text(alias, -1);
 
@@ -134,21 +147,24 @@
 		header_date = purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time));
 
 	header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), escaped_alias, header_date);
-	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), header, options);
+	gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), header);
 	g_free(header);
 	g_free(escaped_alias);
 
 	g_strchomp(history);
-	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), history, options);
+	gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), history);
 	g_free(history);
 
-	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<hr>", options);
+	gtk_webview_append_html(GTK_WEBVIEW(gtkconv->webview), "<hr>");
 
+#if 0
+	/* FIXME: WebView has no protocol setting */
 	gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol);
 	g_free(protocol);
+#endif
 
-	g_object_ref(G_OBJECT(gtkconv->imhtml));
-	g_idle_add(_scroll_imhtml_to_end, gtkconv->imhtml);
+	g_object_ref(G_OBJECT(gtkconv->webview));
+	g_idle_add(_scroll_webview_to_end, gtkconv->webview);
 
 	g_list_foreach(logs, (GFunc)purple_log_free, NULL);
 	g_list_free(logs);
--- a/pidgin/plugins/notify.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/plugins/notify.c	Mon Sep 26 14:57:21 2011 +0900
@@ -303,7 +303,7 @@
 attach_signals(PurpleConversation *conv)
 {
 	PidginConversation *gtkconv = NULL;
-	GSList *imhtml_ids = NULL, *entry_ids = NULL;
+	GSList *webview_ids = NULL, *entry_ids = NULL;
 	guint id;
 
 	gtkconv = PIDGIN_CONVERSATION(conv);
@@ -322,9 +322,9 @@
 		                      G_CALLBACK(unnotify_cb), conv);
 		entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
 
-		id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "focus-in-event",
+		id = g_signal_connect(G_OBJECT(gtkconv->webview), "focus-in-event",
 		                      G_CALLBACK(unnotify_cb), conv);
-		imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id));
+		webview_ids = g_slist_append(webview_ids, GUINT_TO_POINTER(id));
 	}
 
 	if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_click")) {
@@ -334,9 +334,9 @@
 		                      G_CALLBACK(unnotify_cb), conv);
 		entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
 
-		id = g_signal_connect(G_OBJECT(gtkconv->imhtml), "button-press-event",
+		id = g_signal_connect(G_OBJECT(gtkconv->webview), "button-press-event",
 		                      G_CALLBACK(unnotify_cb), conv);
-		imhtml_ids = g_slist_append(imhtml_ids, GUINT_TO_POINTER(id));
+		webview_ids = g_slist_append(webview_ids, GUINT_TO_POINTER(id));
 	}
 
 	if (purple_prefs_get_bool("/plugins/gtk/X11/notify/notify_type")) {
@@ -345,7 +345,7 @@
 		entry_ids = g_slist_append(entry_ids, GUINT_TO_POINTER(id));
 	}
 
-	purple_conversation_set_data(conv, "notify-imhtml-signals", imhtml_ids);
+	purple_conversation_set_data(conv, "notify-webview-signals", webview_ids);
 	purple_conversation_set_data(conv, "notify-entry-signals", entry_ids);
 
 	return 0;
@@ -361,9 +361,9 @@
 	if (!gtkconv)
 		return;
 
-	ids = purple_conversation_get_data(conv, "notify-imhtml-signals");
+	ids = purple_conversation_get_data(conv, "notify-webview-signals");
 	for (l = ids; l != NULL; l = l->next)
-		g_signal_handler_disconnect(gtkconv->imhtml, GPOINTER_TO_INT(l->data));
+		g_signal_handler_disconnect(gtkconv->webview, GPOINTER_TO_INT(l->data));
 	g_slist_free(ids);
 
 	ids = purple_conversation_get_data(conv, "notify-entry-signals");
@@ -373,7 +373,7 @@
 
 	purple_conversation_set_data(conv, "notify-message-count", GINT_TO_POINTER(0));
 
-	purple_conversation_set_data(conv, "notify-imhtml-signals", NULL);
+	purple_conversation_set_data(conv, "notify-webview-signals", NULL);
 	purple_conversation_set_data(conv, "notify-entry-signals", NULL);
 }
 
--- a/pidgin/plugins/perl/common/GtkIMHtml.xs	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/plugins/perl/common/GtkIMHtml.xs	Mon Sep 26 14:57:21 2011 +0900
@@ -77,26 +77,6 @@
 	Gtk::TextIter end
 */
 
-/* This can't work at the moment since I don't have a typemap for Gdk::Pixbuf.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-Pidgin::IMHtml::Scalable
-gtk_imhtml_image_new(img, filename, id)
-	Gdk::Pixbuf img
-	const gchar * filename
-	int id
-*/
-
-/* This can't work at the moment since I don't have a typemap for Gtk::Widget.
- * I thought about using the one from libgtk2-perl but wasn't sure how to go
- * about doing that.
-void
-gtk_imhtml_image_add_to(scale, imhtml, iter)
-	Pidgin::IMHtml::Scalable scale
-	Pidgin::IMHtml imhtml
-	Gtk::TextIter iter
-*/
-
 /* This can't work at the moment since I don't have a typemap for Gtk::Widget.
  * I thought about using the one from libgtk2-perl but wasn't sure how to go
  * about doing that.
@@ -323,19 +303,6 @@
 	}
 	XPUSHs(sv_2mortal(newRV_noinc((SV *)lines)));
 
-MODULE = Pidgin::IMHtml  PACKAGE = Pidgin::IMHtml::Scalable  PREFIX = gtk_imhtml_image_
-PROTOTYPES: ENABLE
-
-void
-gtk_imhtml_image_free(scale)
-	Pidgin::IMHtml::Scalable scale
-
-void
-gtk_imhtml_image_scale(scale, width, height)
-	Pidgin::IMHtml::Scalable scale
-	int width
-	int height
-
 MODULE = Pidgin::IMHtml  PACKAGE = Pidgin::IMHtml::Hr  PREFIX = gtk_imhtml_hr_
 PROTOTYPES: ENABLE
 
--- a/pidgin/plugins/relnot.c	Mon Aug 29 12:59:57 2011 +0900
+++ b/pidgin/plugins/relnot.c	Mon Sep 26 14:57:21 2011 +0900
@@ -152,7 +152,7 @@
 				url,
 				host);
 
-		purple_util_fetch_url_request_len(NULL, url, TRUE, NULL, FALSE,
+		purple_util_fetch_url_request(NULL, url, TRUE, NULL, FALSE,
 			request, TRUE, -1, version_fetch_cb, NULL);
 
 		g_free(request);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/plugins/webkit.c	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,201 @@
+/*
+ * WebKit - Open the inspector on any WebKit views.
+ * Copyright (C) 2011 Elliott Sales de Andrade <qulogic@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "internal.h"
+
+#include "version.h"
+
+#include "pidgin.h"
+
+#include "gtkconv.h"
+#include "gtkplugin.h"
+#include "gtkwebview.h"
+
+static WebKitWebView *
+create_gtk_window_around_it(WebKitWebInspector *inspector,
+                            WebKitWebView      *webview,
+                            PidginConversation *gtkconv)
+{
+	GtkWidget *win;
+	GtkWidget *view;
+	char *title;
+
+	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	title = g_strdup_printf(_("%s - Inspector"),
+	                        gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)));
+	gtk_window_set_title(GTK_WINDOW(win), title);
+	g_free(title);
+	gtk_window_set_default_size(GTK_WINDOW(win), 600, 400);
+	g_signal_connect_swapped(G_OBJECT(gtkconv->tab_cont), "destroy", G_CALLBACK(gtk_widget_destroy), win);
+
+	view = webkit_web_view_new();
+	gtk_container_add(GTK_CONTAINER(win), view);
+	g_object_set_data(G_OBJECT(webview), "inspector-window", win);
+
+	return WEBKIT_WEB_VIEW(view);
+}
+
+static gboolean
+show_inspector_window(WebKitWebInspector *inspector,
+                      GtkWidget          *webview)
+{
+	GtkWidget *win;
+
+	win = g_object_get_data(G_OBJECT(webview), "inspector-window");
+
+	gtk_widget_show_all(win);
+
+	return TRUE;
+}
+
+static void
+setup_inspector(PidginConversation *gtkconv)
+{
+	GtkWidget *webview = gtkconv->webview;
+	WebKitWebSettings *settings;
+	WebKitWebInspector *inspector;
+
+	settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
+	inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
+
+	g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
+
+	g_signal_connect(G_OBJECT(inspector), "inspect-web-view",
+	                 G_CALLBACK(create_gtk_window_around_it), gtkconv);
+	g_signal_connect(G_OBJECT(inspector), "show-window",
+	                 G_CALLBACK(show_inspector_window), webview);
+}
+
+static void
+remove_inspector(PidginConversation *gtkconv)
+{
+	GtkWidget *webview = gtkconv->webview;
+	GtkWidget *win;
+	WebKitWebSettings *settings;
+
+	win = g_object_get_data(G_OBJECT(webview), "inspector-window");
+	gtk_widget_destroy(win);
+	g_object_set_data(G_OBJECT(webview), "inspector-window", NULL);
+
+	settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
+
+	g_object_set(G_OBJECT(settings), "enable-developer-extras", FALSE, NULL);
+}
+
+static void
+conversation_displayed_cb(PidginConversation *gtkconv)
+{
+	GtkWidget *inspect = NULL;
+
+	inspect = g_object_get_data(G_OBJECT(gtkconv->webview),
+	                            "inspector-window");
+	if (inspect == NULL) {
+		setup_inspector(gtkconv);
+	}
+}
+
+static gboolean
+plugin_load(PurplePlugin *plugin)
+{
+	GList *convs = purple_get_conversations();
+	void *gtk_conv_handle = pidgin_conversations_get_handle();
+
+	purple_signal_connect(gtk_conv_handle, "conversation-displayed", plugin,
+	                      PURPLE_CALLBACK(conversation_displayed_cb), NULL);
+
+	while (convs) {
+		PurpleConversation *conv = (PurpleConversation *)convs->data;
+
+		/* Setup WebKit Inspector */
+		if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
+			setup_inspector(PIDGIN_CONVERSATION(conv));
+		}
+
+		convs = convs->next;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+plugin_unload(PurplePlugin *plugin)
+{
+	GList *convs = purple_get_conversations();
+
+	while (convs) {
+		PurpleConversation *conv = (PurpleConversation *)convs->data;
+
+		/* Remove WebKit Inspector */
+		if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
+			remove_inspector(PIDGIN_CONVERSATION(conv));
+		}
+
+		convs = convs->next;
+	}
+
+	return TRUE;
+}
+
+static PurplePluginInfo info =
+{
+	PURPLE_PLUGIN_MAGIC,
+	PURPLE_MAJOR_VERSION,                           /**< major version */
+	PURPLE_MINOR_VERSION,                           /**< minor version */
+	PURPLE_PLUGIN_STANDARD,                         /**< type */
+	PIDGIN_PLUGIN_TYPE,                             /**< ui_requirement */
+	0,                                              /**< flags */
+	NULL,                                           /**< dependencies */
+	PURPLE_PRIORITY_DEFAULT,                        /**< priority */
+
+	"gtkwebkit-inspect",                            /**< id */
+	N_("WebKit Development"),                       /**< name */
+	DISPLAY_VERSION,                                /**< version */
+	N_("Enables WebKit Inspector."),                /**< summary */
+	N_("Enables WebKit's built-in inspector in a "
+	   "conversation window. This may be viewed "
+	   "by right-clicking a WebKit widget and "
+	   "selecting 'Inspect Element'."),             /**< description */
+	"Elliott Sales de Andrade <qulogic@pidgin.im>", /**< author */
+	PURPLE_WEBSITE,                                 /**< homepage */
+	plugin_load,                                    /**< load */
+	plugin_unload,                                  /**< unload */
+	NULL,                                           /**< destroy */
+	NULL,                                           /**< ui_info */
+	NULL,                                           /**< extra_info */
+	NULL,                                           /**< prefs_info */
+	NULL,                                           /**< actions */
+
+	/* padding */
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(webkit-devel, init_plugin, info)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/smileyparser.c	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,169 @@
+/* pidgin
+ *
+ * Pidgin 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 <gtk/gtk.h>
+#include <debug.h>
+#include "smileyparser.h"
+#include <smiley.h>
+#include <string.h>
+#include "gtkthemes.h"
+
+static char *
+get_fullpath(const char *filename)
+{
+	if (g_path_is_absolute(filename))
+		return g_strdup(filename);
+	else
+		return g_build_path(g_get_current_dir(), filename, NULL);
+}
+
+static void
+parse_for_shortcut_plaintext(const char *text, const char *shortcut, const char *file, GString *ret)
+{
+	const char *tmp = text;
+
+	for (;*tmp;) {
+		const char *end = strstr(tmp, shortcut);
+		char *path;
+		char *escaped_path;
+
+		if (end == NULL) {
+			g_string_append(ret, tmp);
+			break;
+		}
+		path = get_fullpath(file);
+		escaped_path = g_markup_escape_text(path, -1);
+
+		g_string_append_len(ret, tmp, end-tmp);
+		g_string_append_printf(ret,"<img alt='%s' src='%s' />",
+					shortcut, escaped_path);
+		g_free(path);
+		g_free(escaped_path);
+		g_assert(strlen(tmp) >= strlen(shortcut));
+		tmp = end + strlen(shortcut);
+	}
+}
+
+static char *
+parse_for_shortcut(const char *markup, const char *shortcut, const char *file)
+{
+	GString* ret = g_string_new("");
+	char *local_markup = g_strdup(markup);
+	char *escaped_shortcut = g_markup_escape_text(shortcut, -1);
+
+	char *temp = local_markup;
+
+	for (;*temp;) {
+		char *end = strchr(temp, '<');
+		char *end_of_tag;
+
+		if (!end) {
+			parse_for_shortcut_plaintext(temp, escaped_shortcut, file, ret);
+			break;
+		}
+
+		*end = 0;
+		parse_for_shortcut_plaintext(temp, escaped_shortcut, file, ret);
+		*end = '<';
+
+		/* if this is well-formed, then there should be no '>' within
+		 * the tag. TODO: handle a comment tag better :( */
+		end_of_tag = strchr(end, '>');
+		if (!end_of_tag) {
+			g_string_append(ret, end);
+			break;
+		}
+
+		g_string_append_len(ret, end, end_of_tag - end + 1);
+
+		temp = end_of_tag + 1;
+	}
+	g_free(local_markup);
+	g_free(escaped_shortcut);
+	return g_string_free(ret, FALSE);
+}
+
+static char *
+parse_for_purple_smiley(const char *markup, PurpleSmiley *smiley)
+{
+	char *file = purple_smiley_get_full_path(smiley);
+	char *ret = parse_for_shortcut(markup, purple_smiley_get_shortcut(smiley), file);
+	g_free(file);
+	return ret;
+}
+
+static char *
+parse_for_smiley_list(const char *markup, GHashTable *smileys)
+{
+	GHashTableIter iter;
+	char *key, *value;
+	char *ret = g_strdup(markup);
+
+	g_hash_table_iter_init(&iter, smileys);
+	while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
+	{
+		char *temp = parse_for_shortcut(ret, key, value);
+		g_free(ret);
+		ret = temp;
+	}
+
+	return ret;
+}
+
+char *
+smiley_parse_markup(const char *markup, const char *proto_id)
+{
+	GList *smileys = purple_smileys_get_all();
+	char *temp = g_strdup(markup), *temp2;
+	struct smiley_list *list;
+	const char *proto_name = "default";
+
+	if (proto_id != NULL) {
+		PurplePlugin *proto;
+		proto = purple_find_prpl(proto_id);
+		proto_name = proto->info->name;
+	}
+
+	/* unnecessarily slow, but lets manage for now. */
+	for (; smileys; smileys = g_list_next(smileys)) {
+		temp2 = parse_for_purple_smiley(temp, PURPLE_SMILEY(smileys->data));
+		g_free(temp);
+		temp = temp2;
+	}
+
+	/* now for each theme smiley, observe that this does look nasty */
+	if (!current_smiley_theme || !(current_smiley_theme->list)) {
+		purple_debug_warning("smiley", "theme does not exist\n");
+		return temp;
+	}
+
+	for (list = current_smiley_theme->list; list; list = list->next) {
+		if (g_str_equal(list->sml, proto_name)) {
+			temp2 = parse_for_smiley_list(temp, list->files);
+			g_free(temp);
+			temp = temp2;
+		}
+	}
+
+	return temp;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/smileyparser.h	Mon Sep 26 14:57:21 2011 +0900
@@ -0,0 +1,25 @@
+/* pidgin
+ *
+ * Pidgin 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
+ *
+ */
+
+char *
+smiley_parse_markup(const char *markup, const char *sml);
+