changeset 27907:a64685485775

propagate from branch 'im.pidgin.pidgin' (head a4222a6a8081e1e41a29c5b01c09a31ed212eb60) to branch 'im.pidgin.pidgin.yaz' (head f037145e58bf0c248f0bfb71758c04c604be896b)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Sun, 23 Nov 2008 09:40:48 +0000
parents 3667342896ce (diff) 6ecfc6b9667c (current diff)
children 770f2f8a2c91
files libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/si.c libpurple/protocols/msn/msn.c libpurple/protocols/msn/switchboard.c pidgin/gtkblist.c
diffstat 18 files changed, 559 insertions(+), 387 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Mon Nov 17 06:15:33 2008 +0000
+++ b/COPYRIGHT	Sun Nov 23 09:40:48 2008 +0000
@@ -220,6 +220,7 @@
 Tuomas Kuosmanen
 Tero Kuusela
 Richard Laager
+Jacky Lam
 Scott Lamb
 Dennis Lambe Jr.
 Joe LaPenna
@@ -307,7 +308,7 @@
 Ted Percival
 Eduardo PĆ©rez
 Matt Perry
-Diego Pettenņ
+Diego Petten
 Nathan Peterson
 SebastiƔn E. Peyrott
 Celso Pinto
@@ -323,7 +324,7 @@
 Jory A. Pratt
 Brent Priddy
 Justin Pryzby
-Florian Qučze
+Florian QuĆØze
 Ignacio Casal Quinteiro
 Federicco Mena Quintero
 Yosef Radchenko
@@ -477,6 +478,7 @@
 Jared Yanovich
 Timmy Yee
 Nickolai Zeldovich
+Tom Zickel
 Marco Ziech
 Piotr Zielinski
 Jeroen Zwartepoorte
--- a/Makefile.am	Mon Nov 17 06:15:33 2008 +0000
+++ b/Makefile.am	Sun Nov 23 09:40:48 2008 +0000
@@ -34,6 +34,7 @@
 	(cd po ; intltool-update -m 2>&1 | grep -v '^mismatched quotes.*\.py$$' | sed "s|current directory|po directory|" | grep . ; if [ $$? = 0 ] ; then exit 1 ; else exit 0 ; fi)
 	LC_ALL=C sort -c -t/ -u po/POTFILES.in
 	LC_ALL=C sort -c -t/ -u po/POTFILES.skip
+	iconv -f utf8 -t utf8 COPYRIGHT | cmp COPYRIGHT -
 
 version-check: commit-check
 # We don't want to release development versions.
--- a/libpurple/dnsquery.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/dnsquery.c	Sun Nov 23 09:40:48 2008 +0000
@@ -345,6 +345,12 @@
 {
 	g_return_if_fail(resolver != NULL);
 
+	/* Keep this before the kill() call below. */
+	if (resolver->inpa != 0) {
+		purple_input_remove(resolver->inpa);
+		resolver->inpa = 0;
+	}
+
 	/*
 	 * We might as well attempt to kill our child process.  It really
 	 * doesn't matter if this fails, because children will expire on
@@ -353,9 +359,6 @@
 	if (resolver->dns_pid > 0)
 		kill(resolver->dns_pid, SIGKILL);
 
-	if (resolver->inpa != 0)
-		purple_input_remove(resolver->inpa);
-
 	close(resolver->fd_in);
 	close(resolver->fd_out);
 
--- a/libpurple/notify.h	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/notify.h	Sun Nov 23 09:40:48 2008 +0000
@@ -236,7 +236,8 @@
  * Adds a stock button that will be displayed in the search results dialog.
  *
  * @param results The search results object.
- * @param type    Type of the button. (TODO: Only one button of a given type can be displayed.)
+ * @param type    Type of the button. (TODO: Only one button of a given type
+ *                can be displayed.)
  * @param cb      Function that will be called on the click event.
  */
 void purple_notify_searchresults_button_add(PurpleNotifySearchResults *results,
@@ -245,8 +246,9 @@
 
 
 /**
- * Adds a plain labelled button that will be displayed in the search results dialog.
- * 
+ * Adds a plain labelled button that will be displayed in the search results
+ * dialog.
+ *
  * @param results The search results object
  * @param label   The label to display
  * @param cb      Function that will be called on the click event
@@ -267,7 +269,7 @@
  * Returns a newly created search result column object.
  *
  * @param title Title of the column. NOTE: Title will get g_strdup()ed.
- * 
+ *
  * @return The new search column object.
  */
 PurpleNotifySearchColumn *purple_notify_searchresults_column_new(const char *title);
@@ -470,53 +472,58 @@
  * The text is essentially a stripped-down format of HTML, the same that
  * IMs may send.
  *
- * @param gc		         The PurpleConnection handle associated with the information.
- * @param who				 The username associated with the information.
- * @param user_info          The PurpleNotifyUserInfo which contains the information
- * @param cb                 The callback to call when the user closes
- *                           the notification.
- * @param user_data          The data to pass to the callback.
+ * @param gc         The PurpleConnection handle associated with the information.
+ * @param who        The username associated with the information.
+ * @param user_info  The PurpleNotifyUserInfo which contains the information
+ * @param cb         The callback to call when the user closes the notification.
+ * @param user_data  The data to pass to the callback.
  *
- * @return A UI-specific handle.
+ * @return  A UI-specific handle.
  */
 void *purple_notify_userinfo(PurpleConnection *gc, const char *who,
 						   PurpleNotifyUserInfo *user_info, PurpleNotifyCloseCallback cb,
 						   gpointer user_data);
 
 /**
- * Create a new PurpleNotifyUserInfo which is suitable for passing to purple_notify_userinfo()
+ * Create a new PurpleNotifyUserInfo which is suitable for passing to
+ * purple_notify_userinfo()
  *
- * @return A new PurpleNotifyUserInfo, which the caller must destroy when done
+ * @return  A new PurpleNotifyUserInfo, which the caller must destroy when done
  */
 PurpleNotifyUserInfo *purple_notify_user_info_new(void);
 
 /**
  * Destroy a PurpleNotifyUserInfo
  *
- * @param user_info          The PurpleNotifyUserInfo
+ * @param user_info  The PurpleNotifyUserInfo
  */
 void purple_notify_user_info_destroy(PurpleNotifyUserInfo *user_info);
 
 /**
- * Retrieve the array of PurpleNotifyUserInfoEntry objects from a PurpleNotifyUserInfo
- *
- * This GList may be manipulated directly with normal GList functions such as g_list_insert(). Only 
- * PurpleNotifyUserInfoEntry are allowed in the list.  If a PurpleNotifyUserInfoEntry item is added to the list,
- * it should not be g_free()'d by the caller; PurpleNotifyUserInfo will g_free it when destroyed.
+ * Retrieve the array of PurpleNotifyUserInfoEntry objects from a
+ * PurpleNotifyUserInfo
  *
- * To remove a PurpleNotifyUserInfoEntry, use purple_notify_user_info_remove_entry(). Do not use the GList directly.
+ * This GList may be manipulated directly with normal GList functions such
+ * as g_list_insert(). Only PurpleNotifyUserInfoEntry are allowed in the
+ * list.  If a PurpleNotifyUserInfoEntry item is added to the list, it
+ * should not be g_free()'d by the caller; PurpleNotifyUserInfo will g_free
+ * it when destroyed.
  *
- * @param user_info          The PurpleNotifyUserInfo
+ * To remove a PurpleNotifyUserInfoEntry, use
+ * purple_notify_user_info_remove_entry(). Do not use the GList directly.
  *
- * @constreturn              A GList of PurpleNotifyUserInfoEntry objects
+ * @param user_info  The PurpleNotifyUserInfo
+ *
+ * @constreturn A GList of PurpleNotifyUserInfoEntry objects
  */
 GList *purple_notify_user_info_get_entries(PurpleNotifyUserInfo *user_info);
 
 /**
- * Create a textual representation of a PurpleNotifyUserInfo, separating entries with newline
+ * Create a textual representation of a PurpleNotifyUserInfo, separating
+ * entries with newline
  *
- * @param user_info          The PurpleNotifyUserInfo
- * @param newline            The separation character
+ * @param user_info  The PurpleNotifyUserInfo
+ * @param newline    The separation character
  */
 char *purple_notify_user_info_get_text_with_newline(PurpleNotifyUserInfo *user_info, const char *newline);
 
@@ -524,46 +531,58 @@
  * Add a label/value pair to a PurpleNotifyUserInfo object.
  * PurpleNotifyUserInfo keeps track of the order in which pairs are added.
  *
- * @param user_info          The PurpleNotifyUserInfo
- * @param label              A label, which for example might be displayed by a UI with a colon after it ("Status:"). Do not include a colon.
- *                           If NULL, value will be displayed without a label.
- * @param value              The value, which might be displayed by a UI after the label.
- *                           If NULL, label will still be displayed; the UI should then treat label as independent
- *                           and not include a colon if it would otherwise.
+ * @param user_info  The PurpleNotifyUserInfo
+ * @param label      A label, which for example might be displayed by a
+ *                   UI with a colon after it ("Status:"). Do not include
+ *                   a colon.  If NULL, value will be displayed without a
+ *                   label.
+ * @param value      The value, which might be displayed by a UI after
+ *                   the label.  If NULL, label will still be displayed;
+ *                   the UI should then treat label as independent and not
+ *                   include a colon if it would otherwise.
  */
 void purple_notify_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
 
 /**
  * Prepend a label/value pair to a PurpleNotifyUserInfo object
  *
- * @param user_info          The PurpleNotifyUserInfo
- * @param label              A label, which for example might be displayed by a UI with a colon after it ("Status:"). Do not include a colon.
- *                           If NULL, value will be displayed without a label.
- * @param value              The value, which might be displayed by a UI after the label.
- *                           If NULL, label will still be displayed; the UI should then treat label as independent
- *                           and not include a colon if it would otherwise.
+ * @param user_info  The PurpleNotifyUserInfo
+ * @param label      A label, which for example might be displayed by a
+ *                   UI with a colon after it ("Status:"). Do not include
+ *                   a colon.  If NULL, value will be displayed without a
+ *                   label.
+ * @param value      The value, which might be displayed by a UI after
+ *                   the label.  If NULL, label will still be displayed;
+ *                   the UI should then treat label as independent and not
+ *                   include a colon if it would otherwise.
  */
 void purple_notify_user_info_prepend_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
 
 /**
- * Remove a PurpleNotifyUserInfoEntry from a PurpleNotifyUserInfo object without freeing the entry.
+ * Remove a PurpleNotifyUserInfoEntry from a PurpleNotifyUserInfo object
+ * without freeing the entry.
  *
- * @param user_info          The PurpleNotifyUserInfo
- * @param user_info_entry    The PurpleNotifyUserInfoEntry
+ * @param user_info        The PurpleNotifyUserInfo
+ * @param user_info_entry  The PurpleNotifyUserInfoEntry
  */
 void purple_notify_user_info_remove_entry(PurpleNotifyUserInfo *user_info, PurpleNotifyUserInfoEntry *user_info_entry);
+
 /**
  * Create a new PurpleNotifyUserInfoEntry
  *
- * If added to a PurpleNotifyUserInfo object, this should not be free()'d, as PurpleNotifyUserInfo will do so
- * when destroyed.  purple_notify_user_info_add_pair() and purple_notify_user_info_prepend_pair() are convenience
- * methods for creating entries and adding them to a PurpleNotifyUserInfo.
+ * If added to a PurpleNotifyUserInfo object, this should not be free()'d,
+ * as PurpleNotifyUserInfo will do so when destroyed.
+ * purple_notify_user_info_add_pair() and
+ * purple_notify_user_info_prepend_pair() are convenience methods for
+ * creating entries and adding them to a PurpleNotifyUserInfo.
  *
- * @param label              A label, which for example might be displayed by a UI with a colon after it ("Status:"). Do not include a colon.
- *                           If NULL, value will be displayed without a label.
- * @param value              The value, which might be displayed by a UI after the label.
- *                           If NULL, label will still be displayed; the UI should then treat label as independent
- *                           and not include a colon if it would otherwise.
+ * @param label  A label, which for example might be displayed by a UI
+ *               with a colon after it ("Status:"). Do not include a
+ *               colon.  If NULL, value will be displayed without a label.
+ * @param value  The value, which might be displayed by a UI after the
+ *               label.  If NULL, label will still be displayed; the UI
+ *               should then treat label as independent and not include a
+ *               colon if it would otherwise.
  *
  * @result A new PurpleNotifyUserInfoEntry
  */
@@ -572,71 +591,74 @@
 /**
  * Add a section break.  A UI might display this as a horizontal line.
  *
- * @param user_info          The PurpleNotifyUserInfo
+ * @param user_info  The PurpleNotifyUserInfo
  */
 void purple_notify_user_info_add_section_break(PurpleNotifyUserInfo *user_info);
 
 /**
  * Prepend a section break.  A UI might display this as a horizontal line.
  *
- * @param user_info          The PurpleNotifyUserInfo
+ * @param user_info  The PurpleNotifyUserInfo
  * @since 2.5.0
  */
 void purple_notify_user_info_prepend_section_break(PurpleNotifyUserInfo *user_info);
-	
+
 /**
- * Add a section header.  A UI might display this in a different font from other text.
+ * Add a section header.  A UI might display this in a different font
+ * from other text.
  *
- * @param user_info          The PurpleNotifyUserInfo
- * @param label              The name of the section
+ * @param user_info  The PurpleNotifyUserInfo
+ * @param label      The name of the section
  */
 void purple_notify_user_info_add_section_header(PurpleNotifyUserInfo *user_info, const char *label);
-	
+
 /**
- * Prepend a section header.  A UI might display this in a different font from other text.
+ * Prepend a section header.  A UI might display this in a different font
+ * from other text.
  *
- * @param user_info          The PurpleNotifyUserInfo
- * @param label              The name of the section
+ * @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);
-	
+
 /**
- * Remove the last item which was added to a PurpleNotifyUserInfo. This could be used to remove a section header which is not needed.
+ * Remove the last item which was added to a PurpleNotifyUserInfo. This
+ * could be used to remove a section header which is not needed.
  */
 void purple_notify_user_info_remove_last_item(PurpleNotifyUserInfo *user_info);
 
 /**
  * Get the label for a PurpleNotifyUserInfoEntry
  *
- * @param user_info_entry     The PurpleNotifyUserInfoEntry
+ * @param user_info_entry  The PurpleNotifyUserInfoEntry
  *
- * @result                    The label
+ * @return  The label
  */
 const gchar *purple_notify_user_info_entry_get_label(PurpleNotifyUserInfoEntry *user_info_entry);
 
 /**
  * Set the label for a PurpleNotifyUserInfoEntry
  *
- * @param user_info_entry     The PurpleNotifyUserInfoEntry
- * @param label			      The label
+ * @param user_info_entry  The PurpleNotifyUserInfoEntry
+ * @param label            The label
  */
 void purple_notify_user_info_entry_set_label(PurpleNotifyUserInfoEntry *user_info_entry, const char *label);
 
 /**
  * Get the value for a PurpleNotifyUserInfoEntry
  *
- * @param user_info_entry     The PurpleNotifyUserInfoEntry
+ * @param user_info_entry  The PurpleNotifyUserInfoEntry
  *
- * @result                    The value
+ * @result  The value
  */
 const gchar *purple_notify_user_info_entry_get_value(PurpleNotifyUserInfoEntry *user_info_entry);
 
 /**
  * Set the value for a PurpleNotifyUserInfoEntry
  *
- * @param user_info_entry     The PurpleNotifyUserInfoEntry
- * @param value				  The value
+ * @param user_info_entry  The PurpleNotifyUserInfoEntry
+ * @param value            The value
  */
 void purple_notify_user_info_entry_set_value(PurpleNotifyUserInfoEntry *user_info_entry, const char *value);
 
@@ -644,17 +666,17 @@
 /**
  * Get the type of a PurpleNotifyUserInfoEntry
  *
- * @param user_info_entry     The PurpleNotifyUserInfoEntry
+ * @param user_info_entry  The PurpleNotifyUserInfoEntry
  *
- * @result					  The PurpleNotifyUserInfoEntryType
+ * @return  The PurpleNotifyUserInfoEntryType
  */
 PurpleNotifyUserInfoEntryType purple_notify_user_info_entry_get_type(PurpleNotifyUserInfoEntry *user_info_entry);
 
 /**
  * Set the type of a PurpleNotifyUserInfoEntry
  *
- * @param user_info_entry     The PurpleNotifyUserInfoEntry
- * @param type				  The PurpleNotifyUserInfoEntryType
+ * @param user_info_entry  The PurpleNotifyUserInfoEntry
+ * @param type             The PurpleNotifyUserInfoEntryType
  */
 void purple_notify_user_info_entry_set_type(PurpleNotifyUserInfoEntry *user_info_entry,
 										  PurpleNotifyUserInfoEntryType type);
--- a/libpurple/protocols/gg/gg.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/gg/gg.c	Sun Nov 23 09:40:48 2008 +0000
@@ -61,7 +61,6 @@
  *
  * @return Zero if proxy setup is valid, otherwise -1.
  */
-/* static int ggp_setup_proxy(PurpleAccount *account) {{{ */
 static int ggp_setup_proxy(PurpleAccount *account)
 {
 	PurpleProxyInfo *gpi;
@@ -88,11 +87,7 @@
 
 	return 0;
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_async_token_handler(gpointer _gc, gint fd, PurpleInputCondition cond) {{{ */
 static void ggp_async_token_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
@@ -157,11 +152,7 @@
 	token->cb = NULL;
 	cb(gc);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_token_request(PurpleConnection *gc, GGPTokenCallback cb) {{{ */
 static void ggp_token_request(PurpleConnection *gc, GGPTokenCallback cb)
 {
 	PurpleAccount *account;
@@ -199,7 +190,6 @@
  *
  * @param Current action handler.
  */
-/* static void ggp_action_buddylist_get(PurplePluginAction *action) {{{ */
 static void ggp_action_buddylist_get(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -209,14 +199,12 @@
 
 	gg_userlist_request(info->session, GG_USERLIST_GET, NULL);
 }
-/* }}} */
 
 /**
  * Upload the buddylist to the server.
  *
  * @param action Current action handler.
  */
-/* static void ggp_action_buddylist_put(PurplePluginAction *action) {{{ */
 static void ggp_action_buddylist_put(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -232,14 +220,12 @@
 	gg_userlist_request(info->session, GG_USERLIST_PUT, buddylist);
 	g_free(buddylist);
 }
-/* }}} */
 
 /**
  * Delete buddylist from the server.
  *
  * @param action Current action handler.
  */
-/* static void ggp_action_buddylist_delete(PurplePluginAction *action) {{{ */
 static void ggp_action_buddylist_delete(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -249,11 +235,7 @@
 
 	gg_userlist_request(info->session, GG_USERLIST_PUT, NULL);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *file) {{{ */
 static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *filename)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
@@ -284,11 +266,7 @@
 
 	g_free(buddylist);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_buddylist_load_ok(PurpleConnection *gc, gchar *file) {{{ */
 static void ggp_callback_buddylist_load_ok(PurpleConnection *gc, gchar *file)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
@@ -334,11 +312,7 @@
 			purple_connection_get_account(gc), NULL, NULL,
 			gc);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_action_buddylist_load(PurplePluginAction *action) {{{ */
 static void ggp_action_buddylist_load(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -349,11 +323,7 @@
 			purple_connection_get_account(gc), NULL, NULL,
 			gc);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_register_account_ok(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_register_account_ok(PurpleConnection *gc,
 					     PurpleRequestFields *fields)
 {
@@ -435,11 +405,7 @@
 	g_free(token->id);
 	g_free(token);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_register_account_cancel(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_register_account_cancel(PurpleConnection *gc,
 						 PurpleRequestFields *fields)
 {
@@ -453,11 +419,7 @@
 	g_free(token);
 
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_register_user_dialog(PurpleConnection *gc) {{{ */
 static void ggp_register_user_dialog(PurpleConnection *gc)
 {
 	PurpleAccount *account;
@@ -510,13 +472,9 @@
 		purple_connection_get_account(gc), NULL, NULL,
 		gc);
 }
-/* }}} */
 
 /* ----- PUBLIC DIRECTORY SEARCH ---------------------------------------- */
 
-/*
- */
-/* static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data) {{{ */
 static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data)
 {
 	GGPInfo *info = gc->proto_data;
@@ -533,21 +491,13 @@
 	ggp_search_add(info->searches, seq, form);
 	purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u", seq);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data) {{{ */
 static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data)
 {
 	purple_blist_request_add_buddy(purple_connection_get_account(gc),
 				     g_list_nth_data(row, 0), NULL, NULL);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_im(PurpleConnection *gc, GList *row, gpointer user_data) {{{ */
 static void ggp_callback_im(PurpleConnection *gc, GList *row, gpointer user_data)
 {
 	PurpleAccount *account;
@@ -560,11 +510,7 @@
 	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
 	purple_conversation_present(conv);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	GGPInfo *info = gc->proto_data;
@@ -611,11 +557,7 @@
 	ggp_search_add(info->searches, seq, form);
 	purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u", seq);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_find_buddies(PurplePluginAction *action) {{{ */
 static void ggp_find_buddies(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -672,13 +614,9 @@
 		purple_connection_get_account(gc), NULL, NULL,
 		gc);
 }
-/* }}} */
 
 /* ----- CHANGE PASSWORD ------------------------------------------------ */
 
-/*
- */
-/* static void ggp_callback_change_passwd_ok(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_change_passwd_ok(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	PurpleAccount *account;
@@ -750,11 +688,7 @@
 	g_free(info->token->data);
 	g_free(info->token);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_change_passwd_dialog(PurpleConnection *gc) {{{ */
 static void ggp_change_passwd_dialog(PurpleConnection *gc)
 {
 	PurpleRequestFields *fields;
@@ -811,24 +745,16 @@
 
 	g_free(msg);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_change_passwd(PurplePluginAction *action) {{{ */
 static void ggp_change_passwd(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
 
 	ggp_token_request(gc, ggp_change_passwd_dialog);
 }
-/* }}} */
 
 /* ----- CONFERENCES ---------------------------------------------------- */
 
-/*
- */
-/* static void ggp_callback_add_to_chat_ok(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_add_to_chat_ok(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	GGPInfo *info = gc->proto_data;
@@ -842,11 +768,7 @@
 	ggp_confer_participants_add_uin(gc, sel->data, info->tmp_buddy);
 	info->tmp_buddy = 0;
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored) {{{ */
 static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored)
 {
 	PurpleBuddy *buddy;
@@ -892,13 +814,9 @@
 			gc);
 	g_free(msg);
 }
-/* }}} */
 
 /* ----- BLOCK BUDDIES -------------------------------------------------- */
 
-/*
- */
-/* static void ggp_bmenu_block(PurpleBlistNode *node, gpointer ignored) {{{ */
 static void ggp_bmenu_block(PurpleBlistNode *node, gpointer ignored)
 {
 	PurpleConnection *gc;
@@ -924,7 +842,6 @@
 		purple_debug_info("gg", "send: uin=%d; mode=BLOCKED\n", uin);
 	}
 }
-/* }}} */
 
 /* ---------------------------------------------------------------------- */
 /* ----- INTERNAL CALLBACKS --------------------------------------------- */
@@ -943,7 +860,6 @@
  * @param status ID of the status.
  * @param descr  Description.
  */
-/* static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin, int status, const char *descr) {{{ */
 static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin,
 				       int status, const char *descr)
 {
@@ -983,11 +899,7 @@
 	g_free(from);
 	g_free(msg);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_sr_close_cb(gpointer user_data) {{{ */
 static void ggp_sr_close_cb(gpointer user_data)
 {
 	GGPSearchForm *form = user_data;
@@ -997,7 +909,6 @@
 	purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u", form->seq);
 	ggp_search_form_destroy(form);
 }
-/* }}} */
 
 /**
  * Translate a status' ID to a more user-friendly name.
@@ -1006,7 +917,6 @@
  *
  * @return The user-friendly name of the status.
  */
-/* static const char *ggp_status_by_id(unsigned int id) {{{ */
 static const char *ggp_status_by_id(unsigned int id)
 {
 	const char *st;
@@ -1029,11 +939,7 @@
 
 	return st;
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_pubdir_handle_info(PurpleConnection *gc, gg_pubdir50_t req, GGPSearchForm *form) {{{ */
 static void ggp_pubdir_handle_info(PurpleConnection *gc, gg_pubdir50_t req,
 				   GGPSearchForm *form)
 {
@@ -1092,11 +998,7 @@
 	g_free(who);
 	purple_notify_user_info_destroy(user_info);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_pubdir_handle_full(PurpleConnection *gc, gg_pubdir50_t req, GGPSearchForm *form) {{{ */
 static void ggp_pubdir_handle_full(PurpleConnection *gc, gg_pubdir50_t req,
 				   GGPSearchForm *form)
 {
@@ -1196,11 +1098,7 @@
 		purple_notify_searchresults_new_rows(gc, results, form->window);
 	}
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req) {{{ */
 static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req)
 {
 	GGPInfo *info = gc->proto_data;
@@ -1239,15 +1137,56 @@
 			break;
 	}
 }
-/* }}} */
+
+static void ggp_recv_image_handler(PurpleConnection *gc, const struct gg_event *ev)
+{
+	gint imgid = 0;
+	GGPInfo *info = gc->proto_data;
+	GList *entry = g_list_first(info->pending_richtext_messages);
+	gchar *handlerid = g_strdup_printf("IMGID_HANDLER-%i", ev->event.image_reply.crc32);
+
+	imgid = purple_imgstore_add_with_id(
+		g_memdup(ev->event.image_reply.image, ev->event.image_reply.size),
+		ev->event.image_reply.size,
+		ev->event.image_reply.filename);
+
+	purple_debug_info("gg", "ggp_recv_image_handler: got image with crc32: %u\n", ev->event.image_reply.crc32);
+
+	while(entry) {
+		if (strstr((gchar *)entry->data, handlerid) != NULL) {
+			gchar **split = g_strsplit((gchar *)entry->data, handlerid, 3);
+			gchar *text = g_strdup_printf("%s%i%s", split[0], imgid, split[1]);
+			purple_debug_info("gg", "ggp_recv_image_handler: found message matching crc32: %s\n", (gchar *)entry->data);
+			g_strfreev(split);
+			info->pending_richtext_messages = g_list_remove(info->pending_richtext_messages, entry->data);
+			/* We don't have any more images to download */
+			if (strstr(text, "<IMG ID=\"IMGID_HANDLER") == NULL) {
+				gchar *buf = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
+				serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, ev->event.msg.time);
+				g_free(buf);
+				purple_debug_info("gg", "ggp_recv_image_handler: richtext message: %s\n", text);
+				g_free(text);
+				break;
+			}
+			info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, text);
+			break;
+		}
+		entry = g_list_next(entry);
+	}
+	g_free(handlerid);
+
+	return;
+}
+
 
 /**
  * Dispatch a message received from a buddy.
  *
  * @param gc PurpleConnection.
  * @param ev Gadu-Gadu event structure.
+ *
+ * Image receiving, some code borrowed from Kadu http://www.kadu.net
  */
-/* static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev) {{{ */
 static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev)
 {
 	GGPInfo *info = gc->proto_data;
@@ -1264,7 +1203,109 @@
 	msg = g_markup_escape_text(tmp, -1);
 	g_free(tmp);
 
-	purple_debug_info("gg", "msg form (%s): %s (class = %d; rcpt_count = %d)\n",
+	/* We got richtext message */
+	if (ev->event.msg.formats_length)
+	{
+		gboolean got_image = FALSE, bold = FALSE, italic = FALSE, under = FALSE;
+		char *cformats = (char *)ev->event.msg.formats;
+		char *cformats_end = cformats + ev->event.msg.formats_length;
+		gint increased_len = 0;
+		struct gg_msg_richtext_format *actformat;
+		struct gg_msg_richtext_image *actimage;
+		GString *message = g_string_new(msg);
+		gchar *handlerid;
+
+		purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->event.msg.formats_length);
+
+		while (cformats < cformats_end)
+		{
+			gint byteoffset;
+			actformat = (struct gg_msg_richtext_format *)cformats;
+			cformats += sizeof(struct gg_msg_richtext_format);
+			byteoffset = g_utf8_offset_to_pointer(message->str, actformat->position + increased_len) - message->str;
+
+			if(actformat->position == 0 && actformat->font == 0) {
+				purple_debug_warning("gg", "ggp_recv_message_handler: bogus formatting (inc: %i)\n", increased_len);
+				continue;
+			}
+			purple_debug_info("gg", "ggp_recv_message_handler: format at pos: %i, image:%i, bold:%i, italic: %i, under:%i (inc: %i)\n",
+				actformat->position,
+				(actformat->font & GG_FONT_IMAGE) != 0,
+				(actformat->font & GG_FONT_BOLD) != 0,
+				(actformat->font & GG_FONT_ITALIC) != 0,
+				(actformat->font & GG_FONT_UNDERLINE) != 0,
+				increased_len);
+
+			if (actformat->font & GG_FONT_IMAGE) {
+				got_image = TRUE;
+				actimage = (struct gg_msg_richtext_image*)(cformats);
+				cformats += sizeof(struct gg_msg_richtext_image);
+				purple_debug_info("gg", "ggp_recv_message_handler: image received, size: %d, crc32: %i\n", actimage->size, actimage->crc32);
+
+				/* Checking for errors, image size shouldn't be
+				 * larger than 255.000 bytes */
+				if (actimage->size > 255000) {
+					purple_debug_warning("gg", "ggp_recv_message_handler: received image large than 255 kb\n");
+					continue;
+				}
+
+				gg_image_request(info->session, ev->event.msg.sender,
+					actimage->size, actimage->crc32);
+
+				handlerid = g_strdup_printf("<IMG ID=\"IMGID_HANDLER-%i\">", actimage->crc32);
+				g_string_insert(message, byteoffset, handlerid);
+				increased_len += strlen(handlerid);
+				g_free(handlerid);
+				continue;
+			}
+
+			if (actformat->font & GG_FONT_BOLD) {
+				if (bold == FALSE) {
+					g_string_insert(message, byteoffset, "<b>");
+					increased_len += 3;
+					bold = TRUE;
+				}
+			} else if (bold) {
+				g_string_insert(message, byteoffset, "</b>");
+				increased_len += 4;
+				bold = FALSE;
+			}
+
+			if (actformat->font & GG_FONT_ITALIC) {
+				if (italic == FALSE) {
+					g_string_insert(message, byteoffset, "<i>");
+					increased_len += 3;
+					italic = TRUE;
+				}
+			} else if (italic) {
+				g_string_insert(message, byteoffset, "</i>");
+				increased_len += 4;
+				italic = FALSE;
+			}
+
+			if (actformat->font & GG_FONT_UNDERLINE) {
+				if (under == FALSE) {
+					g_string_insert(message, byteoffset, "<u>");
+					increased_len += 3;
+					under = TRUE;
+				}
+			} else if (under) {
+				g_string_insert(message, byteoffset, "</u>");
+				increased_len += 4;
+				under = FALSE;
+			}
+		}
+
+		msg = message->str;
+		g_string_free(message, FALSE);
+
+		if (got_image) {
+			info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, msg);
+			return;
+		}
+	}
+
+	purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d)\n",
 			from, msg, ev->event.msg.msgclass,
 			ev->event.msg.recipients_count);
 
@@ -1301,11 +1342,32 @@
 	g_free(msg);
 	g_free(from);
 }
-/* }}} */
+
+static void ggp_send_image_handler(PurpleConnection *gc, const struct gg_event *ev)
+{
+	GGPInfo *info = gc->proto_data;
+	PurpleStoredImage *image;
+	gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, &ev->event.image_request.crc32));
+
+	purple_debug_info("gg", "ggp_send_image_handler: image request received, crc32: %u\n", ev->event.image_request.crc32);
 
-/*
- */
-/* static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond) {{{ */
+	if(imgid)
+	{
+		if((image = purple_imgstore_find_by_id(imgid))) {
+			gint image_size = purple_imgstore_get_size(image);
+			gconstpointer image_bin = purple_imgstore_get_data(image);
+			const char *image_filename = purple_imgstore_get_filename(image);
+
+			purple_debug_info("gg", "ggp_send_image_handler: sending image imgid: %i, crc: %u\n", imgid, ev->event.image_request.crc32);
+			gg_image_reply(info->session, (unsigned long int)ev->event.image_request.sender, image_filename, image_bin, image_size);
+			purple_imgstore_unref(image);
+		} else {
+			purple_debug_error("gg", "ggp_send_image_handler: image imgid: %i, crc: %u in hash but not found in imgstore!\n", imgid, ev->event.image_request.crc32);
+		}
+		g_hash_table_remove(info->pending_images, &ev->event.image_request.crc32);
+	}
+}
+
 static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
@@ -1330,11 +1392,18 @@
 			ggp_recv_message_handler(gc, ev);
 			break;
 		case GG_EVENT_ACK:
+			/* Changing %u to %i fixes compiler warning */
 			purple_debug_info("gg",
-				"message sent to: %u, delivery status=%d, seq=%d\n",
+				"ggp_callback_recv: message sent to: %i, delivery status=%d, seq=%d\n",
 				ev->event.ack.recipient, ev->event.ack.status,
 				ev->event.ack.seq);
 			break;
+		case GG_EVENT_IMAGE_REPLY:
+			ggp_recv_image_handler(gc, ev);
+			break;
+		case GG_EVENT_IMAGE_REQUEST:
+			ggp_send_image_handler(gc, ev);
+			break;
 		case GG_EVENT_NOTIFY:
 		case GG_EVENT_NOTIFY_DESCR:
 			{
@@ -1426,11 +1495,7 @@
 
 	gg_free_event(ev);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond) {{{ */
 static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
@@ -1519,20 +1584,16 @@
 
 	gg_free_event(ev);
 }
-/* }}} */
 
 /* ---------------------------------------------------------------------- */
 /* ----- PurplePluginProtocolInfo ----------------------------------------- */
 /* ---------------------------------------------------------------------- */
 
-/* static const char *ggp_list_icon(PurpleAccount *account, PurpleBuddy *buddy) {{{ */
 static const char *ggp_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
 {
 	return "gadu-gadu";
 }
-/* }}} */
 
-/* static char *ggp_status_text(PurpleBuddy *b) {{{ */
 static char *ggp_status_text(PurpleBuddy *b)
 {
 	PurpleStatus *status;
@@ -1558,20 +1619,21 @@
 		return text;
 	}
 }
-/* }}} */
 
-/* static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) {{{ */
 static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
 	PurpleStatus *status;
 	char *text, *tmp;
-	const char *msg, *name;
+	const char *msg, *name, *alias;
 
 	g_return_if_fail(b != NULL);
 
 	status = purple_presence_get_active_status(purple_buddy_get_presence(b));
 	msg = purple_status_get_attr_string(status, "message");
 	name = purple_status_get_name(status);
+	alias = purple_buddy_get_alias(b);
+
+	purple_notify_user_info_add_pair (user_info, _("Alias"), alias);
 
 	if (msg != NULL) {
 		text = g_markup_escape_text(msg, -1);
@@ -1588,9 +1650,7 @@
 		purple_notify_user_info_add_pair(user_info, _("Status"), name);
 	}
 }
-/* }}} */
 
-/* static GList *ggp_status_types(PurpleAccount *account) {{{ */
 static GList *ggp_status_types(PurpleAccount *account)
 {
 	PurpleStatusType *type;
@@ -1634,9 +1694,7 @@
 
 	return types;
 }
-/* }}} */
 
-/* static GList *ggp_blist_node_menu(PurpleBlistNode *node) {{{ */
 static GList *ggp_blist_node_menu(PurpleBlistNode *node)
 {
 	PurpleMenuAction *act;
@@ -1666,9 +1724,7 @@
 
 	return m;
 }
-/* }}} */
 
-/* static GList *ggp_chat_info(PurpleConnection *gc) {{{ */
 static GList *ggp_chat_info(PurpleConnection *gc)
 {
 	GList *m = NULL;
@@ -1682,9 +1738,7 @@
 
 	return m;
 }
-/* }}} */
 
-/* static void ggp_login(PurpleAccount *account) {{{ */
 static void ggp_login(PurpleAccount *account)
 {
 	PurpleConnection *gc;
@@ -1706,11 +1760,14 @@
 	info->chats_count = 0;
 	info->token = NULL;
 	info->searches = ggp_search_new();
+	info->pending_richtext_messages = NULL;
+	info->pending_images = g_hash_table_new(g_int_hash, g_int_equal);
 
 	gc->proto_data = info;
 
 	glp->uin = ggp_get_uin(account);
 	glp->password = (char *)purple_account_get_password(account);
+	glp->image_size = 255;
 
 	presence = purple_account_get_presence(account);
 	status = purple_presence_get_active_status(presence);
@@ -1730,9 +1787,7 @@
 	gc->inpa = purple_input_add(info->session->fd, PURPLE_INPUT_READ,
 				  ggp_async_login_handler, gc);
 }
-/* }}} */
 
-/* static void ggp_close(PurpleConnection *gc) {{{ */
 static void ggp_close(PurpleConnection *gc)
 {
 
@@ -1760,6 +1815,8 @@
 		purple_notify_close_with_handle(gc);
 
 		ggp_search_destroy(info->searches);
+		g_list_free(info->pending_richtext_messages);
+		g_hash_table_destroy(info->pending_images);
 		g_free(info);
 		gc->proto_data = NULL;
 	}
@@ -1771,25 +1828,108 @@
 
 	purple_debug_info("gg", "Connection closed.\n");
 }
-/* }}} */
 
-/* static int ggp_send_im(PurpleConnection *gc, const char *who, const char *msg, PurpleMessageFlags flags) {{{ */
 static int ggp_send_im(PurpleConnection *gc, const char *who, const char *msg,
 		       PurpleMessageFlags flags)
 {
 	GGPInfo *info = gc->proto_data;
 	char *tmp, *plain;
-	int ret = 0;
+	int ret = 1;
+	unsigned char format[1024];
+	unsigned int format_length = sizeof(struct gg_msg_richtext);
+	gint pos = 0;
+	GData *attribs;
+	const char *start, *end = NULL, *last;
 
-	if (strlen(msg) == 0) {
+	if (msg == NULL || *msg == '\0') {
 		return 0;
 	}
 
-	purple_debug_info("gg", "ggp_send_im: msg = %s\n", msg);
-	plain = purple_unescape_html(msg);
+	last = msg;
+
+	/* Check if the message is richtext */
+	/* TODO: Check formatting, too */
+	if(purple_markup_find_tag("img", last, &start, &end, &attribs)) {
+
+		GString *string_buffer = g_string_new(NULL);
+		struct gg_msg_richtext fmt;
+
+		do {
+			PurpleStoredImage *image;
+			const char *id;
+
+			/* Add text before the image */
+			if(start - last) {
+				pos = pos + g_utf8_strlen(last, start - last);
+				g_string_append_len(string_buffer, last, start - last);
+			}
+
+			if((id = g_datalist_get_data(&attribs, "id")) && (image = purple_imgstore_find_by_id(atoi(id)))) {
+				struct gg_msg_richtext_format actformat;
+				struct gg_msg_richtext_image actimage;
+				gint image_size = purple_imgstore_get_size(image);
+				gconstpointer image_bin = purple_imgstore_get_data(image);
+				const char *image_filename = purple_imgstore_get_filename(image);
+				uint32_t crc32 = gg_crc32(0, image_bin, image_size);
+
+				g_hash_table_insert(info->pending_images, &crc32, GINT_TO_POINTER(atoi(id)));
+				purple_imgstore_ref(image);
+				purple_debug_info("gg", "ggp_send_im_richtext: got crc: %i for imgid: %i\n", crc32, atoi(id));
+
+				actformat.font = GG_FONT_IMAGE;
+				actformat.position = pos;
+
+				actimage.unknown1 = 0x0109;
+				actimage.size = gg_fix32(image_size);
+				actimage.crc32 = gg_fix32(crc32);
+
+				if (actimage.size > 255000) {
+					purple_debug_warning("gg", "ggp_send_im_richtext: image over 255kb!\n");
+					continue;
+				}
+
+				purple_debug_info("gg", "ggp_send_im_richtext: adding images to richtext, size: %i, crc32: %u, name: %s\n", actimage.size, actimage.crc32, image_filename);
+
+				memcpy(format + format_length, &actformat, sizeof(actformat));
+				format_length += sizeof(actformat);
+				memcpy(format + format_length, &actimage, sizeof(actimage));
+				format_length += sizeof(actimage);
+			} else {
+				purple_debug_error("gg", "ggp_send_im_richtext: image not found in the image store!");
+			}
+
+			last = end + 1;
+			g_datalist_clear(&attribs);
+
+		} while(purple_markup_find_tag("img", last, &start, &end, &attribs));
+
+		/* Add text after the images */
+		if(last && *last) {
+			pos = pos + g_utf8_strlen(last, -1);
+			g_string_append(string_buffer, last);
+		}
+
+		fmt.flag = 2;
+		fmt.length = format_length - sizeof(fmt);
+		memcpy(format, &fmt, sizeof(fmt));
+
+		purple_debug_info("gg", "ggp_send_im: richtext msg = %s\n", string_buffer->str);
+		plain = purple_unescape_html(string_buffer->str);
+		g_string_free(string_buffer, TRUE);
+	} else {
+		purple_debug_info("gg", "ggp_send_im: msg = %s\n", msg);
+		plain = purple_unescape_html(msg);
+	}
+
 	tmp = charset_convert(plain, "UTF-8", "CP1250");
 
-	if (NULL == tmp || strlen(tmp) == 0) {
+	if (tmp && (format_length - sizeof(struct gg_msg_richtext))) {
+		if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) {
+			ret = -1;
+		} else {
+			ret = 1;
+		}
+	} else if (NULL == tmp || *tmp == 0) {
 		ret = 0;
 	} else if (strlen(tmp) > GG_MSG_MAXSIZE) {
 		ret = -E2BIG;
@@ -1805,9 +1945,7 @@
 
 	return ret;
 }
-/* }}} */
 
-/* static void ggp_get_info(PurpleConnection *gc, const char *name) { {{{ */
 static void ggp_get_info(PurpleConnection *gc, const char *name)
 {
 	GGPInfo *info = gc->proto_data;
@@ -1825,9 +1963,7 @@
 	ggp_search_add(info->searches, seq, form);
 	purple_debug_info("gg", "ggp_get_info(): Added seq %u", seq);
 }
-/* }}} */
 
-/* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
 static int ggp_to_gg_status(PurpleStatus *status, char **msg)
 {
 	const char *status_id = purple_status_get_id(status);
@@ -1872,9 +2008,7 @@
 		return new_status;
 	}
 }
-/* }}} */
 
-/* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
 static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
 {
 	PurpleConnection *gc;
@@ -1900,9 +2034,7 @@
 	ggp_status_fake_to_self(account);
 
 }
-/* }}} */
 
-/* static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {{{ */
 static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	PurpleAccount *account;
@@ -1915,9 +2047,7 @@
 		ggp_status_fake_to_self(account);
 	}
 }
-/* }}} */
 
-/* static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {{{ */
 static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
 						 PurpleGroup *group)
 {
@@ -1925,9 +2055,7 @@
 
 	gg_remove_notify(info->session, ggp_str_to_uin(buddy->name));
 }
-/* }}} */
 
-/* static void ggp_join_chat(PurpleConnection *gc, GHashTable *data) {{{ */
 static void ggp_join_chat(PurpleConnection *gc, GHashTable *data)
 {
 	GGPInfo *info = gc->proto_data;
@@ -1960,15 +2088,11 @@
 				purple_account_get_username(account), NULL,
 				PURPLE_CBFLAGS_NONE, TRUE);
 }
-/* }}} */
 
-/* static char *ggp_get_chat_name(GHashTable *data) { {{{ */
 static char *ggp_get_chat_name(GHashTable *data) {
 	return g_strdup(g_hash_table_lookup(data, "name"));
 }
-/* }}} */
 
-/* static int ggp_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) {{{ */
 static int ggp_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
 {
 	PurpleConversation *conv;
@@ -2020,9 +2144,7 @@
 
 	return 0;
 }
-/* }}} */
 
-/* static void ggp_keepalive(PurpleConnection *gc) {{{ */
 static void ggp_keepalive(PurpleConnection *gc)
 {
 	GGPInfo *info = gc->proto_data;
@@ -2037,9 +2159,7 @@
 			_("Not connected to the server."));
 	}
 }
-/* }}} */
 
-/* static void ggp_register_user(PurpleAccount *account) {{{ */
 static void ggp_register_user(PurpleAccount *account)
 {
 	PurpleConnection *gc = purple_account_get_connection(account);
@@ -2049,9 +2169,7 @@
 
 	ggp_token_request(gc, ggp_register_user_dialog);
 }
-/* }}} */
 
-/* static GList *ggp_actions(PurplePlugin *plugin, gpointer context) {{{ */
 static GList *ggp_actions(PurplePlugin *plugin, gpointer context)
 {
 	GList *m = NULL;
@@ -2091,19 +2209,15 @@
 
 	return m;
 }
-/* }}} */
 
-/* static gboolean ggp_offline_message(const PurpleBuddy *buddy) {{{ */
 static gboolean ggp_offline_message(const PurpleBuddy *buddy)
 {
 	return TRUE;
 }
-/* }}} */
 
-/* prpl_info setup {{{ */
 static PurplePluginProtocolInfo prpl_info =
 {
-	OPT_PROTO_REGISTER_NOSCREENNAME,
+	OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_IM_IMAGE,
 	NULL,				/* user_splits */
 	NULL,				/* protocol_options */
 	{"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY},	/* icon_spec */
@@ -2173,36 +2287,34 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL
 };
-/* }}} */
 
-/* PurplePluginInfo setup {{{ */
 static PurplePluginInfo info = {
-	PURPLE_PLUGIN_MAGIC,		/* magic */
-	PURPLE_MAJOR_VERSION,		/* major_version */
-	PURPLE_MINOR_VERSION,		/* minor_version */
-	PURPLE_PLUGIN_PROTOCOL,		/* plugin type */
-	NULL,				/* ui_requirement */
-	0,				/* flags */
-	NULL,				/* dependencies */
+	PURPLE_PLUGIN_MAGIC,			/* magic */
+	PURPLE_MAJOR_VERSION,			/* major_version */
+	PURPLE_MINOR_VERSION,			/* minor_version */
+	PURPLE_PLUGIN_PROTOCOL,			/* plugin type */
+	NULL,					/* ui_requirement */
+	0,					/* flags */
+	NULL,					/* dependencies */
 	PURPLE_PRIORITY_DEFAULT,		/* priority */
 
-	"prpl-gg",			/* id */
-	"Gadu-Gadu",			/* name */
-	DISPLAY_VERSION,		/* version */
+	"prpl-gg",				/* id */
+	"Gadu-Gadu",				/* name */
+	DISPLAY_VERSION,			/* version */
 
 	N_("Gadu-Gadu Protocol Plugin"),	/* summary */
 	N_("Polish popular IM"),		/* description */
-	"boler@sourceforge.net",	/* author */
-	PURPLE_WEBSITE,			/* homepage */
+	"boler@sourceforge.net",		/* author */
+	PURPLE_WEBSITE,				/* homepage */
 
-	NULL,				/* load */
-	NULL,				/* unload */
-	NULL,				/* destroy */
+	NULL,					/* load */
+	NULL,					/* unload */
+	NULL,					/* destroy */
 
-	NULL,				/* ui_info */
-	&prpl_info,			/* extra_info */
-	NULL,				/* prefs_info */
-	ggp_actions,			/* actions */
+	NULL,					/* ui_info */
+	&prpl_info,				/* extra_info */
+	NULL,					/* prefs_info */
+	ggp_actions,				/* actions */
 
 	/* padding */
 	NULL,
@@ -2210,9 +2322,7 @@
 	NULL,
 	NULL
 };
-/* }}} */
 
-/* static void purple_gg_debug_handler(int level, const char * format, va_list args) {{{ */
 static void purple_gg_debug_handler(int level, const char * format, va_list args) {
 	PurpleDebugLevel purple_level;
 	char *msg = g_strdup_vprintf(format, args);
@@ -2235,11 +2345,7 @@
 	purple_debug(purple_level, "gg", "%s", msg);
 	g_free(msg);
 }
-/* }}} */
 
-/*
- */
-/* static void init_plugin(PurplePlugin *plugin) {{{ */
 static void init_plugin(PurplePlugin *plugin)
 {
 	PurpleAccountOption *option;
@@ -2253,7 +2359,6 @@
 
 	gg_debug_handler = purple_gg_debug_handler;
 }
-/* }}} */
 
 PURPLE_INIT_PLUGIN(gg, init_plugin, info);
 
--- a/libpurple/protocols/gg/gg.h	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/gg/gg.h	Sun Nov 23 09:40:48 2008 +0000
@@ -65,9 +65,10 @@
 	uin_t tmp_buddy;
 	int chats_count;
 
+	GList *pending_richtext_messages;
+	GHashTable *pending_images;
 } GGPInfo;
 
-
 #endif /* _PURPLE_GG_H */
 
 /* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/jabber/jabber.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sun Nov 23 09:40:48 2008 +0000
@@ -1740,7 +1740,7 @@
 	types = g_list_append(types, type);
 
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
-			"tune", NULL, TRUE, TRUE, TRUE,
+			"tune", NULL, FALSE, TRUE, TRUE,
 			PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING),
 			PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING),
 			PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING),
--- a/libpurple/protocols/jabber/parser.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/jabber/parser.c	Sun Nov 23 09:40:48 2008 +0000
@@ -114,7 +114,8 @@
 		xmlnode *packet = js->current;
 		js->current = NULL;
 		jabber_process_packet(js, &packet);
-		xmlnode_free(packet);
+		if (packet != NULL)
+			xmlnode_free(packet);
 	}
 }
 
@@ -137,11 +138,19 @@
 {
 	JabberStream *js = user_data;
 
+	if (error->level == XML_ERR_WARNING && error->message != NULL
+			&& strcmp(error->message, "xmlns: URI vcard-temp is not absolute\n") == 0)
+		/*
+		 * This message happens when parsing vcards, and is normal, so don't
+		 * bother logging it because people scare easily.
+		 */
+		return;
+
 	purple_debug_error("jabber", "XML parser error for JabberStream %p: "
-								 "Domain %i, code %i, level %i: %s\n",
+								 "Domain %i, code %i, level %i: %s",
 					   js,
 					   error->domain, error->code, error->level,
-					   (error->message ? error->message : "(null)"));
+					   (error->message ? error->message : "(null)\n"));
 }
 
 static xmlSAXHandler jabber_parser_libxml = {
--- a/libpurple/protocols/jabber/presence.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/jabber/presence.c	Sun Nov 23 09:40:48 2008 +0000
@@ -459,7 +459,7 @@
 
 		if (buddy) {
 			jb = jabber_buddy_find(js, from, TRUE);
-			if ((jb->subscription & JABBER_SUB_TO))
+			if ((jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING)))
 				onlist = TRUE;
 		}
 
--- a/libpurple/protocols/jabber/si.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/jabber/si.c	Sun Nov 23 09:40:48 2008 +0000
@@ -800,7 +800,7 @@
 		if (!(sh->jid && sh->host && sh->port > 0))
 			continue;
 
-		purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and sh->jid %p",
+		purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and sh->jid %p\n",
 						  jsx, jsx->streamhosts, sh->jid);
 		if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL)
 			continue;
--- a/libpurple/protocols/msn/msn.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Sun Nov 23 09:40:48 2008 +0000
@@ -860,7 +860,7 @@
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
-			"tune", NULL, TRUE, TRUE, TRUE,
+			"tune", NULL, FALSE, TRUE, TRUE,
 			PURPLE_TUNE_ARTIST, _("Artist"), purple_value_new(PURPLE_TYPE_STRING),
 			PURPLE_TUNE_ALBUM, _("Album"), purple_value_new(PURPLE_TYPE_STRING),
 			PURPLE_TUNE_TITLE, _("Title"), purple_value_new(PURPLE_TYPE_STRING),
--- a/libpurple/protocols/msn/notification.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/msn/notification.c	Sun Nov 23 09:40:48 2008 +0000
@@ -491,7 +491,7 @@
 	 * command and we are processing it */
 	if (cmd->payload == NULL) {
 		cmdproc->last_cmd->payload_cb = msg_cmd_post;
-		cmd->payload_len = atoi(cmd->params[4]);
+		cmd->payload_len = atoi(cmd->params[3]);
 	} else {
 		g_return_if_fail(cmd->payload_cb != NULL);
 
@@ -1578,7 +1578,7 @@
 	MsnUser *user;
 	const char *passport;
 	char *psm_str, *str;
-	CurrentMedia media = {NULL, NULL, NULL};
+	CurrentMedia media = {CURRENT_MEDIA_UNKNOWN, NULL, NULL, NULL};
 
 	session = cmdproc->session;
 	account = session->account;
--- a/libpurple/protocols/msn/servconn.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/msn/servconn.c	Sun Nov 23 09:40:48 2008 +0000
@@ -69,8 +69,7 @@
 		return;
 	}
 
-	if (servconn->connected)
-		msn_servconn_disconnect(servconn);
+	msn_servconn_disconnect(servconn);
 
 	if (servconn->destroy_cb)
 		servconn->destroy_cb(servconn);
--- a/libpurple/protocols/msn/switchboard.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Sun Nov 23 09:40:48 2008 +0000
@@ -577,6 +577,7 @@
 	MsnTransaction *trans;
 	char *payload;
 	gsize payload_len;
+	char flag;
 
 	g_return_if_fail(swboard != NULL);
 	g_return_if_fail(msg     != NULL);
@@ -590,32 +591,35 @@
 	msn_message_show_readable(msg, "SB SEND", FALSE);
 #endif
 
+	flag = msn_message_get_flag(msg);
 	trans = msn_transaction_new(cmdproc, "MSG", "%c %" G_GSIZE_FORMAT,
-								msn_message_get_flag(msg), payload_len);
+								flag, payload_len);
 
 	/* Data for callbacks */
 	msn_transaction_set_data(trans, msg);
 
-	if (msg->type == MSN_MSG_TEXT)
-	{
-		msg->ack_ref = TRUE;
-		msn_message_ref(msg);
-		swboard->ack_list = g_list_append(swboard->ack_list, msg);
-		msn_transaction_set_timeout_cb(trans, msg_timeout);
-	}
-	else if (msg->type == MSN_MSG_SLP)
-	{
-		msg->ack_ref = TRUE;
-		msn_message_ref(msg);
-		swboard->ack_list = g_list_append(swboard->ack_list, msg);
-		msn_transaction_set_timeout_cb(trans, msg_timeout);
+	if (flag != 'U') {
+		if (msg->type == MSN_MSG_TEXT)
+		{
+			msg->ack_ref = TRUE;
+			msn_message_ref(msg);
+			swboard->ack_list = g_list_append(swboard->ack_list, msg);
+			msn_transaction_set_timeout_cb(trans, msg_timeout);
+		}
+		else if (msg->type == MSN_MSG_SLP)
+		{
+			msg->ack_ref = TRUE;
+			msn_message_ref(msg);
+			swboard->ack_list = g_list_append(swboard->ack_list, msg);
+			msn_transaction_set_timeout_cb(trans, msg_timeout);
 #if 0
-		if (msg->ack_cb != NULL)
-		{
-			msn_transaction_add_cb(trans, "ACK", msg_ack);
-			msn_transaction_add_cb(trans, "NAK", msg_nak);
+			if (msg->ack_cb != NULL)
+			{
+				msn_transaction_add_cb(trans, "ACK", msg_ack);
+				msn_transaction_add_cb(trans, "NAK", msg_nak);
+			}
+#endif
 		}
-#endif
 	}
 
 	trans->payload = payload;
@@ -806,7 +810,7 @@
 ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	purple_debug_misc("msn", "get UBM...\n");
-	cmd->payload_len = atoi(cmd->params[4]);
+	cmd->payload_len = atoi(cmd->params[3]);
 	cmdproc->last_cmd->payload_cb = msg_cmd_post;
 }
 
--- a/libpurple/protocols/myspace/myspace.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Sun Nov 23 09:40:48 2008 +0000
@@ -139,7 +139,7 @@
 	prim,   /* PurpleStatusPrimitive */                         \
 	NULL,   /* id - use default */                              \
 	NULL,   /* name - use default */                            \
-	TRUE,   /* savable */                                       \
+	TRUE,   /* saveable */                                      \
 	TRUE,   /* user_settable */                                 \
 	FALSE,  /* not independent */                               \
 	                                                            \
@@ -163,7 +163,7 @@
 			PURPLE_STATUS_TUNE,	/* primitive */
 			"tune",                 /* ID */
 			NULL,                   /* name - use default */
-			TRUE,                   /* savable */
+			FALSE,                  /* saveable */
 			TRUE,                   /* should be user_settable some day */
 			TRUE,                   /* independent */
 
--- a/libpurple/protocols/sametime/sametime.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Sun Nov 23 09:40:48 2008 +0000
@@ -1445,7 +1445,7 @@
 					 MW_PLUGIN_DEFAULT_HOST);
 
   if(purple_account_get_bool(account, MW_KEY_FORCE, FALSE) ||
-     (! strcmp(current_host, host)) ||
+     !host || (! strcmp(current_host, host)) ||
      (purple_proxy_connect(NULL, account, host, port, connect_cb, pd) == NULL)) {
 
     /* if we're configured to force logins, or if we're being
--- a/pidgin/gtkblist.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/pidgin/gtkblist.c	Sun Nov 23 09:40:48 2008 +0000
@@ -2984,6 +2984,22 @@
 	pidgin_tooltip_destroy();
 }
 
+static void
+pidgin_blist_align_tooltip(struct tooltip_data *td, GtkWidget *widget) 
+{ 
+	GtkTextDirection dir = gtk_widget_get_direction(widget); 
+
+	if (dir == GTK_TEXT_DIR_RTL) 
+	{
+		char* layout_name = purple_markup_strip_html(pango_layout_get_text(td->name_layout));
+		PangoDirection dir = pango_find_base_dir(layout_name, -1);
+		if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_NEUTRAL)
+			pango_layout_set_alignment(td->name_layout, PANGO_ALIGN_RIGHT); 
+		g_free(layout_name);
+		pango_layout_set_alignment(td->layout, PANGO_ALIGN_RIGHT); 
+	}
+}
+
 static gboolean
 pidgin_blist_create_tooltip_for_node(GtkWidget *widget, gpointer data, int *w, int *h)
 {
@@ -3003,11 +3019,13 @@
 	if (PURPLE_BLIST_NODE_IS_CHAT(node) ||
 	   PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		struct tooltip_data *td = create_tip_for_node(node, TRUE);
+		pidgin_blist_align_tooltip(td, gtkblist->tipwindow);
 		gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
 	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
 		PurpleGroup *group = (PurpleGroup*)node;
 		GSList *accounts;
 		struct tooltip_data *td = create_tip_for_node(node, TRUE);
+		pidgin_blist_align_tooltip(td, gtkblist->tipwindow);
 		gtkblist->tooltipdata = g_list_append(gtkblist->tooltipdata, td);
 
 		/* Accounts with buddies in group */
@@ -3027,6 +3045,7 @@
 		{
 			if(PURPLE_BLIST_NODE_IS_BUDDY(child) && buddy_is_displayable((PurpleBuddy*)child)) {
 				struct tooltip_data *td = create_tip_for_node(child, (b == (PurpleBuddy*)child));
+				pidgin_blist_align_tooltip(td, gtkblist->tipwindow);
 				if (b == (PurpleBuddy *)child) {
 					gtkblist->tooltipdata = g_list_prepend(gtkblist->tooltipdata, td);
 				} else {
--- a/pidgin/pidginstock.c	Mon Nov 17 06:15:33 2008 +0000
+++ b/pidgin/pidginstock.c	Sun Nov 23 09:40:48 2008 +0000
@@ -206,69 +206,64 @@
 	{ PIDGIN_STOCK_TRAY_EMAIL, "tray", "tray-message.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  }
 };
 
+static void
+add_sized_icon_common(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir,
+	       gboolean rtl, const char *size, const char *file,
+		   gboolean translucent);
+
+static gchar *
+find_file_common(const char *name)
+{
+	gchar *filename;
+#if GLIB_CHECK_VERSION(2,6,0)
+	const gchar *userdir;
+	const gchar * const *sysdirs;
+
+	userdir = g_get_user_data_dir();
+	filename = g_build_filename(userdir, name, NULL);
+	if (g_file_test(filename, G_FILE_TEST_EXISTS))
+		return filename;
+	g_free(filename);
+
+	sysdirs = g_get_system_data_dirs();
+	for (; *sysdirs; sysdirs++) {
+		filename = g_build_filename(*sysdirs, name, NULL);
+		if (g_file_test(filename, G_FILE_TEST_EXISTS))
+			return filename;
+		g_free(filename);
+	}
+#endif
+	filename = g_build_filename(DATADIR, name, NULL);
+	if (g_file_test(filename, G_FILE_TEST_EXISTS))
+		return filename;
+	g_free(filename);
+	return NULL;
+}
+
 static gchar *
 find_file(const char *dir, const char *base)
 {
 	char *filename;
+	char *ret;
 
 	if (base == NULL)
 		return NULL;
 
 	if (!strcmp(dir, "pidgin"))
-		filename = g_build_filename(DATADIR, "pixmaps", "pidgin", base, NULL);
+		filename = g_build_filename("pixmaps", "pidgin", base, NULL);
 	else
-	{
-		filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir,
-									base, NULL);
-	}
+		filename = g_build_filename("pixmaps", "pidgin", dir, base, NULL);
 
-	return filename;
+	ret = find_file_common(filename);
+	g_free(filename);
+	return ret;
 }
 
 static void
-add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir, 
+add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir,
 	       gboolean rtl, const char *size, const char *file)
 {
-	char *filename;
-	GtkIconSource *source;	
-
-	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL);
-	source = gtk_icon_source_new();
-        gtk_icon_source_set_filename(source, filename);
-	gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR);
-        gtk_icon_source_set_direction_wildcarded(source, !rtl);
-	gtk_icon_source_set_size(source, sizeid);
-        gtk_icon_source_set_size_wildcarded(source, FALSE);
-        gtk_icon_source_set_state_wildcarded(source, TRUE);
-        gtk_icon_set_add_source(iconset, source);
-	gtk_icon_source_free(source);
-
-	if (sizeid == gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)) {
-		source = gtk_icon_source_new();
-	        gtk_icon_source_set_filename(source, filename);
-        	gtk_icon_source_set_direction_wildcarded(source, TRUE);
-	        gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU);
-	        gtk_icon_source_set_size_wildcarded(source, FALSE);
-        	gtk_icon_source_set_state_wildcarded(source, TRUE);
-	        gtk_icon_set_add_source(iconset, source);
-	        gtk_icon_source_free(source);
-	}
-        g_free(filename);
-
-       if (rtl) {
-		filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL);
-                source = gtk_icon_source_new();
-                gtk_icon_source_set_filename(source, filename);
-                gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL);
-                gtk_icon_source_set_size(source, sizeid);
-                gtk_icon_source_set_size_wildcarded(source, FALSE);
-                gtk_icon_source_set_state_wildcarded(source, TRUE);
-                gtk_icon_set_add_source(iconset, source);
-		g_free(filename);
-		gtk_icon_source_free(source);
-        }
-
-
+	add_sized_icon_common(iconset, sizeid, dir, rtl, size, file, FALSE);
 }
 
 /* Altered from do_colorshift in gnome-panel */
@@ -307,64 +302,77 @@
         }
 }
 
-/* TODO: This is almost certainly not the best way to do this, but it's late, I'm tired,
- * we're a few hours from getting this thing out, and copy/paste is EASY.
- */
 static void
 add_translucent_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir,
 	       gboolean rtl, const char *size, const char *file)
 {
-	char *filename;
-	GtkIconSource *source;	
+	add_sized_icon_common(iconset, sizeid, dir, rtl, size, file, TRUE);
+}
+
+static void
+add_sized_icon_common(GtkIconSet *iconset, GtkIconSize sizeid, const char *dir,
+	       gboolean rtl, const char *size, const char *file,
+		   gboolean translucent)
+{
+	char *filename, *subpath;
+	GtkIconSource *source;
 	GdkPixbuf *pixbuf;
 
-	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL);
+	subpath = g_build_filename("pixmaps", "pidgin", dir, size, file, NULL);
+	filename = find_file_common(subpath);
+	g_free(subpath);
+	if (!filename)
+		return;
+
 	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
-	do_alphashift(pixbuf, pixbuf);
+	if (translucent)
+		do_alphashift(pixbuf, pixbuf);
 
 	source = gtk_icon_source_new();
-        gtk_icon_source_set_pixbuf(source, pixbuf);
+	gtk_icon_source_set_pixbuf(source, pixbuf);
 	gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR);
-        gtk_icon_source_set_direction_wildcarded(source, !rtl);
+	gtk_icon_source_set_direction_wildcarded(source, !rtl);
 	gtk_icon_source_set_size(source, sizeid);
-        gtk_icon_source_set_size_wildcarded(source, FALSE);
-        gtk_icon_source_set_state_wildcarded(source, TRUE);
-        gtk_icon_set_add_source(iconset, source);
+	gtk_icon_source_set_size_wildcarded(source, FALSE);
+	gtk_icon_source_set_state_wildcarded(source, TRUE);
+	gtk_icon_set_add_source(iconset, source);
 	gtk_icon_source_free(source);
 
 	if (sizeid == gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)) {
 		source = gtk_icon_source_new();
-	        gtk_icon_source_set_pixbuf(source, pixbuf);
-        	gtk_icon_source_set_direction_wildcarded(source, TRUE);
-	        gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU);
-	        gtk_icon_source_set_size_wildcarded(source, FALSE);
-        	gtk_icon_source_set_state_wildcarded(source, TRUE);
-	        gtk_icon_set_add_source(iconset, source);
-	        gtk_icon_source_free(source);
+		gtk_icon_source_set_pixbuf(source, pixbuf);
+		gtk_icon_source_set_direction_wildcarded(source, TRUE);
+		gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU);
+		gtk_icon_source_set_size_wildcarded(source, FALSE);
+		gtk_icon_source_set_state_wildcarded(source, TRUE);
+		gtk_icon_set_add_source(iconset, source);
+		gtk_icon_source_free(source);
 	}
-        g_free(filename);
+	g_free(filename);
 	g_object_unref(pixbuf);
 
-       if (rtl) {
-		filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL);
- 		pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
-		do_alphashift(pixbuf, pixbuf);
+	if (rtl) {
+		subpath = g_build_filename("pixmaps", "pidgin", dir, size, "rtl", file, NULL);
+		filename = find_file_common(subpath);
+		g_free(subpath);
+		if (!filename)
+			return;
+		pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+		if (translucent)
+			do_alphashift(pixbuf, pixbuf);
 		source = gtk_icon_source_new();
-                gtk_icon_source_set_pixbuf(source, pixbuf);
-                gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL);
-                gtk_icon_source_set_size(source, sizeid);
-                gtk_icon_source_set_size_wildcarded(source, FALSE);
-                gtk_icon_source_set_state_wildcarded(source, TRUE);
-                gtk_icon_set_add_source(iconset, source);
+		gtk_icon_source_set_pixbuf(source, pixbuf);
+		gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL);
+		gtk_icon_source_set_size(source, sizeid);
+		gtk_icon_source_set_size_wildcarded(source, FALSE);
+		gtk_icon_source_set_state_wildcarded(source, TRUE);
+		gtk_icon_set_add_source(iconset, source);
 		g_free(filename);
 		g_object_unref(pixbuf);
 		gtk_icon_source_free(source);
-        }
-
-
+	}
 }
 
-
 void
 pidgin_stock_init(void)
 {
@@ -413,7 +421,6 @@
 			gtk_icon_source_set_size_wildcarded(source, TRUE);
 			gtk_icon_source_set_state_wildcarded(source, TRUE);
 
-
 			iconset = gtk_icon_set_new();
 			gtk_icon_set_add_source(iconset, source);