changeset 27845:0aa090fde749

propagate from branch 'im.pidgin.pidgin' (head eeed2c960a1dbe213de63e3115629056ac809beb) to branch 'im.pidgin.pidgin.yaz' (head d545b8061fc6864f28add1244ef187cd2131985e)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Fri, 30 May 2008 14:29:33 +0000
parents f7944b0ffe46 (current diff) 996c80ab3dbc (diff)
children 9f91d6e0983c
files libpurple/protocols/irc/parse.c libpurple/protocols/oscar/oscar.c libpurple/util.c pidgin/gtkimhtml.c pidgin/gtkimhtmltoolbar.c
diffstat 16 files changed, 466 insertions(+), 333 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue May 27 14:14:40 2008 +0000
+++ b/COPYRIGHT	Fri May 30 14:29:33 2008 +0000
@@ -388,6 +388,7 @@
 Richard Stellingwerff
 Charlie Stockman
 David Stoddard
+Adam Strzelecki
 Andreas Stührk
 Oleg Sukhodolsky
 Sun Microsystems
--- a/ChangeLog	Tue May 27 14:14:40 2008 +0000
+++ b/ChangeLog	Fri May 30 14:29:33 2008 +0000
@@ -18,6 +18,9 @@
 	* Group and Chat buddy list entries can now be given custom buddy
 	  icons.
 
+	Finch:
+	* Added "Invite..." menu to chats.
+
 version 2.4.2 (05/17/2008):
 	libpurple:
 	* In MySpaceIM, messages from spambots are discarded (Justin Williams)
--- a/finch/gntconv.c	Tue May 27 14:14:40 2008 +0000
+++ b/finch/gntconv.c	Fri May 30 14:29:33 2008 +0000
@@ -38,10 +38,11 @@
 #include "gntdebug.h"
 #include "gntlog.h"
 #include "gntplugin.h"
+#include "gntpounce.h"
 #include "gntprefs.h"
+#include "gntrequest.h"
 #include "gntsound.h"
 #include "gntstatus.h"
-#include "gntpounce.h"
 
 #include "gnt.h"
 #include "gntbox.h"
@@ -557,6 +558,47 @@
 }
 
 static void
+invite_select_cb(FinchConv *fc, PurpleRequestFields *fields)
+{
+	PurpleConversation *conv = fc->active_conv;
+	const char *buddy = purple_request_fields_get_string(fields,  "screenname");
+	const char *message = purple_request_fields_get_string(fields,  "message");
+	serv_chat_invite(purple_conversation_get_gc(conv),
+		purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
+		message, buddy);
+
+}
+
+static void
+invite_cb(GntMenuItem *item, gpointer ggconv)
+{
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+
+	fields = purple_request_fields_new();
+
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+
+	field = purple_request_field_string_new("screenname", _("Name"), NULL, FALSE);
+	purple_request_field_set_type_hint(field, "screenname");
+	purple_request_field_set_required(field, TRUE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("message", _("Invite message"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+	purple_request_fields(finch_conv_get_handle(), _("Invite"),
+						NULL,
+						_("Please enter the name of the user "
+						  "you wish to invite,\nalong with an optional invite message."),
+						fields,
+						_("OK"), G_CALLBACK(invite_select_cb),
+						_("Cancel"), NULL,
+						NULL, NULL, NULL,
+						ggconv);
+}
+
+static void
 gg_create_menu(FinchConv *ggc)
 {
 	GntWidget *menu, *sub;
@@ -606,6 +648,10 @@
 		}
 
 		generate_send_to_menu(ggc);
+	} else if (purple_conversation_get_type(ggc->active_conv) == PURPLE_CONV_TYPE_CHAT) {
+		item = gnt_menuitem_new(_("Invite..."));
+		gnt_menu_add_item(GNT_MENU(sub), item);
+		gnt_menuitem_set_callback(item, invite_cb, ggc);
 	}
 
 	item = gnt_menuitem_new(_("View Log..."));
--- a/libpurple/protocols/gg/gg.c	Tue May 27 14:14:40 2008 +0000
+++ b/libpurple/protocols/gg/gg.c	Fri May 30 14:29:33 2008 +0000
@@ -343,7 +343,8 @@
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
 
-	purple_request_file(action, "Load buddylist from file...", NULL, FALSE,
+	purple_request_file(action, _("Load buddylist from file..."), NULL,
+			FALSE,
 			G_CALLBACK(ggp_callback_buddylist_load_ok), NULL,
 			purple_connection_get_account(gc), NULL, NULL,
 			gc);
@@ -926,8 +927,10 @@
 /* ----- INTERNAL CALLBACKS --------------------------------------------- */
 /* ---------------------------------------------------------------------- */
 
-/* just a prototype */
+/* Prototypes */
 static void ggp_set_status(PurpleAccount *account, PurpleStatus *status);
+static int ggp_to_gg_status(PurpleStatus *status, char **msg);
+
 
 /**
  * Handle change of the status of the buddy.
@@ -1488,23 +1491,12 @@
 			break;
 		case GG_EVENT_CONN_SUCCESS:
 			{
-				PurpleAccount *account;
-				PurplePresence *presence;
-				PurpleStatus *status;
-
 				purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS\n");
 				purple_input_remove(gc->inpa);
 				gc->inpa = purple_input_add(info->session->fd,
 							  PURPLE_INPUT_READ,
 							  ggp_callback_recv, gc);
 
-				/* gg_change_status(info->session, GG_STATUS_AVAIL); */
-
-				account = purple_connection_get_account(gc);
-				presence = purple_account_get_presence(account);
-				status = purple_presence_get_active_status(presence);
-
-				ggp_set_status(account, status);
 				purple_connection_set_state(gc, PURPLE_CONNECTED);
 				ggp_buddylist_send(gc);
 			}
@@ -1692,6 +1684,8 @@
 static void ggp_login(PurpleAccount *account)
 {
 	PurpleConnection *gc;
+	PurplePresence *presence;
+	PurpleStatus *status;
 	struct gg_login_params *glp;
 	GGPInfo *info;
 
@@ -1714,8 +1708,11 @@
 	glp->uin = ggp_get_uin(account);
 	glp->password = (char *)purple_account_get_password(account);
 
+	presence = purple_account_get_presence(account);
+	status = purple_presence_get_active_status(presence);
+
 	glp->async = 1;
-	glp->status = GG_STATUS_AVAIL;
+	glp->status = ggp_to_gg_status(status, &glp->status_descr);
 	glp->tls = 0;
 
 	info->session = gg_login(glp);
@@ -1826,22 +1823,15 @@
 /* }}} */
 
 /* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
-static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
+static int ggp_to_gg_status(PurpleStatus *status, char **msg)
 {
-	PurpleConnection *gc;
-	GGPInfo *info;
-	const char *status_id, *msg;
+	const char *status_id = purple_status_get_id(status);
 	int new_status, new_status_descr;
+	const char *new_msg;
 
-	if (!purple_status_is_active(status))
-		return;
+	g_return_val_if_fail(msg == NULL, 0);
 
-	gc = purple_account_get_connection(account);
-	info = gc->proto_data;
-
-	status_id = purple_status_get_id(status);
-
-	purple_debug_info("gg", "ggp_set_status: Requested status = %s\n",
+	purple_debug_info("gg", "ggp_to_gg_status: Requested status = %s\n",
 			status_id);
 
 	if (strcmp(status_id, "available") == 0) {
@@ -1860,22 +1850,45 @@
 		new_status = GG_STATUS_AVAIL;
 		new_status_descr = GG_STATUS_AVAIL_DESCR;
 		purple_debug_info("gg",
-			"ggp_set_status: uknown status requested (status_id=%s)\n",
+			"ggp_set_status: unknown status requested (status_id=%s)\n",
 			status_id);
 	}
 
-	msg = purple_status_get_attr_string(status, "message");
+	new_msg = purple_status_get_attr_string(status, "message");
+
+	if(new_msg) {
+		char *tmp = purple_markup_strip_html(new_msg);
+		*msg = charset_convert(tmp, "UTF-8", "CP1250");
+		g_free(tmp);
+
+		return new_status_descr;
+	} else {
+		*msg = NULL;
+		return new_status;
+	}
+}
+/* }}} */
 
-	if (msg == NULL) {
+/* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
+static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
+{
+	PurpleConnection *gc;
+	GGPInfo *info;
+	int new_status;
+	char *new_msg = NULL;
+
+	if (!purple_status_is_active(status))
+		return;
+
+	gc = purple_account_get_connection(account);
+	info = gc->proto_data;
+
+	new_status = ggp_to_gg_status(status, &new_msg);
+
+	if (new_msg == NULL) {
 		gg_change_status(info->session, new_status);
 	} else {
-		gchar *tmp, *new_msg;
-
-		tmp = charset_convert(msg, "UTF-8", "CP1250");
-		new_msg = purple_markup_strip_html(tmp);
-		g_free(tmp);
-
-		gg_change_status_descr(info->session, new_status_descr, new_msg);
+		gg_change_status_descr(info->session, new_status, new_msg);
 		g_free(new_msg);
 	}
 
--- a/libpurple/protocols/irc/parse.c	Tue May 27 14:14:40 2008 +0000
+++ b/libpurple/protocols/irc/parse.c	Fri May 30 14:29:33 2008 +0000
@@ -256,7 +256,7 @@
 
 	if (encodings[0] == NULL || !g_ascii_strcasecmp("UTF-8", encodings[0])) {
 		g_strfreev(encodings);
-		return g_strdup(string);
+		return NULL;
 	}
 
 	strtmp  = botch_utf(string, strlen(string), &strtmp_len);
@@ -798,7 +798,7 @@
 		case 'n':
 		case 'c':
 			tmp = irc_send_convert(irc, tok);
-			g_string_append(string, tmp);
+			g_string_append(string, tmp ? tmp : tok);
 			g_free(tmp);
 			break;
 		default:
--- a/libpurple/protocols/oscar/oscar.c	Tue May 27 14:14:40 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Fri May 30 14:29:33 2008 +0000
@@ -789,18 +789,6 @@
 }
 
 static void
-oscar_string_convert_and_append(PurpleAccount *account, GString *str, const char *newline,
-					const char *name, const char *value)
-{
-	gchar *utf8;
-
-	if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
-		g_string_append_printf(str, "%s<b>%s:</b> %s", newline, name, utf8);
-		g_free(utf8);
-	}
-}
-
-static void
 oscar_user_info_convert_and_add(PurpleAccount *account, PurpleNotifyUserInfo *user_info,
 								const char *name, const char *value)
 {
@@ -812,7 +800,114 @@
 	}
 }
 
-static void oscar_string_append_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
+static void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
+{
+	PurpleAccount *account = purple_connection_get_account(gc);
+	OscarData *od;
+	PurplePresence *presence = NULL;
+	PurpleStatus *status = NULL;
+	gchar *message = NULL, *itmsurl = NULL, *tmp;
+
+	od = gc->proto_data;
+
+	if (userinfo == NULL)
+		userinfo = aim_locate_finduserinfo(od, b->name);
+
+	if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
+		return;
+
+	if (b == NULL)
+		b = purple_find_buddy(purple_connection_get_account(gc), userinfo->sn);
+	
+	if (b) {
+		presence = purple_buddy_get_presence(b);
+		status = purple_presence_get_active_status(presence);
+		
+		message = g_strdup(purple_status_get_attr_string(status, "message"));
+		itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
+
+	} else {
+		/* This is an OSCAR contact for whom we don't have a PurpleBuddy but do have information. */
+		if ((userinfo->flags & AIM_FLAG_AWAY)) {
+			/* Away message? */
+			if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
+				gchar *away_utf8;
+				
+				tmp = oscar_encoding_extract(userinfo->away_encoding);
+				away_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->away,
+												   userinfo->away_len);
+				g_free(tmp);
+				if (away_utf8 != NULL) {
+					message = purple_str_sub_away_formatters(away_utf8, purple_account_get_username(account));
+					g_free(away_utf8);
+				}
+			}
+		} else {
+			/* Available message? */
+			if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
+				tmp = oscar_encoding_to_utf8(account, userinfo->status_encoding,
+											 userinfo->status, userinfo->status_len);
+				/* Available messages are plain text */
+				message = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
+			}
+#if defined (_WIN32) || defined (__APPLE__)
+			if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0'))
+				itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
+												 userinfo->itmsurl, userinfo->itmsurl_len);
+#endif
+		}
+	}
+	
+	if (itmsurl) {
+		tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
+							  itmsurl, message);
+		g_free(itmsurl);
+		g_free(message);
+		message = tmp;
+	}
+
+	if (b) {
+		if (purple_presence_is_online(presence)) {
+			if (aim_snvalid_icq(b->name) || !message || !(*message)) {
+				/* Append the status name for online ICQ statuses and for all buddies with no message.
+				 * If the status name and the message are the same, only show one. */
+				const char *status_name = purple_status_get_name(status);
+				if (status_name && message && !strcmp(status_name, message))
+					status_name = NULL;
+
+				tmp = g_strdup_printf("%s%s%s",
+									   status_name,
+									   ((status_name && message) && *message) ? ": " : "",
+									   (message && *message) ? message : "");
+				g_free(message);
+				message = tmp;
+			}
+
+		} else {
+			if (aim_ssi_waitingforauth(od->ssi.local,
+									   aim_ssi_itemlist_findparentname(od->ssi.local, b->name),
+									   b->name)) {
+				/* Note if an offline buddy is not authorized */
+				tmp = g_strdup_printf("%s%s%s",
+									  _("Not Authorized"),
+									  (message && *message) ? ": " : "",
+									  (message && *message) ? message : "");
+				g_free(message);
+				message = tmp;
+			} else {
+				g_free(message);
+				message = g_strdup(_("Offline"));
+			}
+		}
+
+	}
+
+	purple_notify_user_info_add_pair(user_info, _("Status"), message);
+	g_free(message);
+}
+
+static void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
 {
 	OscarData *od;
 	PurpleAccount *account;
@@ -842,22 +937,7 @@
 
 	if (userinfo != NULL)
 		bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->sn));
-
-	if (b != NULL) {
-		if (purple_presence_is_online(presence)) {
-			if (aim_snvalid_icq(b->name)) {
-				PurpleStatus *status = purple_presence_get_active_status(presence);
-				oscar_user_info_add_pair(user_info, _("Status"),	purple_status_get_name(status));
-			}
-		} else {
-			tmp = aim_ssi_itemlist_findparentname(od->ssi.local, b->name);
-			if (aim_ssi_waitingforauth(od->ssi.local, tmp, b->name))
-				oscar_user_info_add_pair(user_info, _("Status"),	_("Not Authorized"));
-			else
-				oscar_user_info_add_pair(user_info, _("Status"),	_("Offline"));
-		}
-	}
-
+								  
 	if ((bi != NULL) && (bi->ipaddr != 0)) {
 		tmp =  g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
 						(bi->ipaddr & 0xff000000) >> 24,
@@ -868,7 +948,6 @@
 		g_free(tmp);
 	}
 
-
 	if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
 		tmp = g_strdup_printf("%d", (int)(userinfo->warnlevel/10.0 + .5));
 		oscar_user_info_add_pair(user_info, _("Warning Level"), tmp);
@@ -1016,8 +1095,8 @@
 
 	if (source < 0)
 	{
-		purple_debug_error("oscar", "unable to connect FLAP server "
-				"of type 0x%04hx\n", conn->type);
+		purple_debug_error("oscar", "unable to connect to FLAP "
+				"server of type 0x%04hx\n", conn->type);
 		if (conn->type == SNAC_FAMILY_AUTH)
 		{
 			gchar *msg;
@@ -2965,7 +3044,7 @@
 	PurpleConnection *gc = od->gc;
 	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleNotifyUserInfo *user_info;
-	gchar *tmp = NULL, *info_utf8 = NULL, *away_utf8 = NULL;
+	gchar *tmp = NULL, *info_utf8 = NULL;
 	va_list ap;
 	aim_userinfo_t *userinfo;
 
@@ -2974,23 +3053,8 @@
 	va_end(ap);
 
 	user_info = purple_notify_user_info_new();
-	purple_notify_user_info_add_pair(user_info, _("Username"), userinfo->sn);
-
-	if (userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) {
-		time_t t = userinfo->onlinesince;
-		oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
-	}
-
-	if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
-		time_t t = userinfo->membersince;
-		oscar_user_info_add_pair(user_info, _("Member Since"), purple_date_format_full(localtime(&t)));
-	}
-
-	if (userinfo->capabilities != 0) {
-		tmp = oscar_caps_to_string(userinfo->capabilities);
-		oscar_user_info_add_pair(user_info, _("Capabilities"), tmp);
-		g_free(tmp);
-	}
+
+	oscar_user_info_append_status(gc, user_info, NULL, userinfo);
 
 	if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) {
 		tmp = purple_str_seconds_to_string(userinfo->idletime*60);
@@ -2998,44 +3062,25 @@
 		g_free(tmp);
 	}
 
-	oscar_string_append_info(gc, user_info, NULL, userinfo);
-
-	/* Available message */
-	if ((userinfo->status != NULL) && !(userinfo->flags & AIM_FLAG_AWAY))
-	{
-		if (userinfo->status[0] != '\0')
-			tmp = oscar_encoding_to_utf8(account, userinfo->status_encoding,
-											 userinfo->status, userinfo->status_len);
-#if defined (_WIN32) || defined (__APPLE__)
-		if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
-			gchar *itmsurl, *tmp2;
-			itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
-					userinfo->itmsurl, userinfo->itmsurl_len);
-			tmp2 = g_strdup_printf("<a href=\"%s\">%s</a>",
-					itmsurl, tmp);
-			g_free(tmp);
-			tmp = tmp2;
-			g_free(itmsurl);
-		}
-#endif
-		oscar_user_info_add_pair(user_info, _("Available Message"), tmp);
+	oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo);
+
+	if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !aim_snvalid_sms(userinfo->sn)) {
+		/* An SMS contact is always online; its Online Since valid is not useful */
+		time_t t = userinfo->onlinesince;
+		oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
+	}
+	
+	if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
+		time_t t = userinfo->membersince;
+		oscar_user_info_add_pair(user_info, _("Member Since"), purple_date_format_full(localtime(&t)));
+	}
+	
+	if (userinfo->capabilities != 0) {
+		tmp = oscar_caps_to_string(userinfo->capabilities);
+		oscar_user_info_add_pair(user_info, _("Capabilities"), tmp);
 		g_free(tmp);
 	}
 
-	/* Away message */
-	if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
-		tmp = oscar_encoding_extract(userinfo->away_encoding);
-		away_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->away,
-		                                   userinfo->away_len);
-		g_free(tmp);
-		if (away_utf8 != NULL) {
-			tmp = purple_str_sub_away_formatters(away_utf8, purple_account_get_username(account));
-			oscar_user_info_add_pair(user_info, _("Away Message"), tmp);
-			g_free(tmp);
-			g_free(away_utf8);
-		}
-	}
-
 	/* Info */
 	if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
 		tmp = oscar_encoding_extract(userinfo->info_encoding);
@@ -3732,12 +3777,9 @@
 	PurpleConnection *gc;
 	PurpleAccount *account;
 	PurpleBuddy *buddy;
-	PurplePresence *presence;
-	PurpleStatus *status;
 	struct buddyinfo *bi;
 	gchar who[16];
 	PurpleNotifyUserInfo *user_info;
-	GString *tmp;
 	gchar *utf8;
 	gchar *buf;
 	const gchar *alias;
@@ -3818,8 +3860,7 @@
 	if ((info->age > 0) && (info->age < 255)) {
 		char age[5];
 		snprintf(age, sizeof(age), "%hhd", info->age);
-		purple_notify_user_info_add_pair(user_info,
-													_("Age"), age);
+		purple_notify_user_info_add_pair(user_info, _("Age"), age);
 	}
 	if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->personalwebpage))) {
 		buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
@@ -3828,65 +3869,41 @@
 		g_free(utf8);
 	}
 
-	if (buddy != NULL) {
-		const gchar *message;
-		gchar *utf8, *tmp;
-
-		presence = purple_buddy_get_presence(buddy);
-		status = purple_presence_get_active_status(presence);
-		message = purple_status_get_attr_string(status, "message");
-
-		utf8 = message && message[0] ? oscar_utf8_try_convert(account, message) : NULL;
-		tmp = g_strdup_printf("%s%s%s",
-				purple_status_get_name(status),
-				utf8 && *utf8 ? ": " : "",
-				utf8 && *utf8 ? utf8 : "");
-		g_free(utf8);
-
-		oscar_user_info_convert_and_add(account,
-				user_info, _("Status"), tmp);
-	}
+	if (buddy != NULL)
+		oscar_user_info_append_status(gc, user_info, buddy, NULL);
 
 	oscar_user_info_convert_and_add(account, user_info, _("Additional Information"), info->info);
 	purple_notify_user_info_add_section_break(user_info);
 
 	if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
-		tmp = g_string_sized_new(100);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Address"), info->homeaddr);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("City"), info->homecity);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("State"), info->homestate);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Zip Code"), info->homezip);
-		
-		purple_notify_user_info_add_pair(user_info, _("Home Address"), tmp->str);
-		purple_notify_user_info_add_section_break(user_info);
-
-		g_string_free(tmp, TRUE);
+		purple_notify_user_info_add_section_header(user_info, _("Home Address"));
+
+		oscar_user_info_convert_and_add(account, user_info, _("Address"), info->homeaddr);
+		oscar_user_info_convert_and_add(account, user_info, _("City"), info->homecity);
+		oscar_user_info_convert_and_add(account, user_info, _("State"), info->homestate);
+		oscar_user_info_convert_and_add(account, user_info, _("Zip Code"), info->homezip);
 	}
 	if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
-		tmp = g_string_sized_new(100);
-
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Address"), info->workaddr);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("City"), info->workcity);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("State"), info->workstate);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Zip Code"), info->workzip);
-
-		purple_notify_user_info_add_pair(user_info, _("Work Address"), tmp->str);
-		purple_notify_user_info_add_section_break(user_info);
-
-		g_string_free(tmp, TRUE);
+		purple_notify_user_info_add_section_header(user_info, _("Work Address"));
+		
+		oscar_user_info_convert_and_add(account, user_info, _("Address"), info->workaddr);
+		oscar_user_info_convert_and_add(account, user_info, _("City"), info->workcity);
+		oscar_user_info_convert_and_add(account, user_info, _("State"), info->workstate);
+		oscar_user_info_convert_and_add(account, user_info, _("Zip Code"), info->workzip);
 	}
 	if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
-		tmp = g_string_sized_new(100);
+		purple_notify_user_info_add_section_header(user_info, _("Work Information"));
 		
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Company"), info->workcompany);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Division"), info->workdivision);
-		oscar_string_convert_and_append(account, tmp, "\n<br>", _("Position"), info->workposition);
+		oscar_user_info_convert_and_add(account, user_info, _("Company"), info->workcompany);
+		oscar_user_info_convert_and_add(account, user_info, _("Division"), info->workdivision);
+		oscar_user_info_convert_and_add(account, user_info, _("Position"), info->workposition);
+		
 		if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->workwebpage))) {
-			g_string_append_printf(tmp, "\n<br><b>%s:</b> <a href=\"%s\">%s</a>", _("Web Page"), utf8, utf8);
+			char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
+			purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage);
+			g_free(webpage);
 			g_free(utf8);
 		}
-		purple_notify_user_info_add_pair(user_info, _("Work Information"), tmp->str);
-		g_string_free(tmp, TRUE);
 	}
 
 	if (buddy != NULL)
@@ -5671,47 +5688,10 @@
 	aim_userinfo_t *userinfo = aim_locate_finduserinfo(od, b->name);
 
 	if (PURPLE_BUDDY_IS_ONLINE(b)) {
-		PurplePresence *presence;
-		PurpleStatus *status;
-		const char *message;
+		oscar_user_info_append_status(gc, user_info, b, userinfo);
 
 		if (full)
-			oscar_string_append_info(gc, user_info, b, userinfo);
-
-		presence = purple_buddy_get_presence(b);
-		status = purple_presence_get_active_status(presence);
-		message = purple_status_get_attr_string(status, "message");
-
-		if (purple_status_is_available(status))
-		{
-			if (message != NULL)
-			{
-				/* Available status messages are plain text */
-				gchar *tmp;
-				tmp = g_markup_escape_text(message, -1);
-				purple_notify_user_info_add_pair(user_info, _("Message"), tmp);
-				g_free(tmp);
-			}
-		}
-		else
-		{
-			if (message != NULL)
-			{
-				/* Away messages are HTML */
-				gchar *tmp1, *tmp2;
-				tmp2 = purple_markup_strip_html(message);
-				tmp1 = g_markup_escape_text(tmp2, -1);
-				g_free(tmp2);
-				tmp2 = purple_str_sub_away_formatters(tmp1, purple_account_get_username(purple_connection_get_account(gc)));
-				g_free(tmp1);
-				purple_notify_user_info_add_pair(user_info, _("Away Message"), tmp2);
-				g_free(tmp2);
-			}
-			else
-			{
-				purple_notify_user_info_add_pair(user_info, _("Away Message"), _("<i>(retrieving)</i>"));
-			}
-		}
+			oscar_user_info_append_extra_info(gc, user_info, b, userinfo);
 	}
 }
 
--- a/libpurple/smiley.c	Tue May 27 14:14:40 2008 +0000
+++ b/libpurple/smiley.c	Fri May 30 14:29:33 2008 +0000
@@ -641,10 +641,9 @@
 	old_filename = purple_imgstore_get_filename(old_img);
 	new_filename = purple_imgstore_get_filename(smiley->img);
 
-	if (g_ascii_strcasecmp(old_filename, new_filename)) {
+	if (g_ascii_strcasecmp(old_filename, new_filename))
 		purple_smiley_data_unstore(old_filename);
-		purple_imgstore_unref(old_img);
-	}
+	purple_imgstore_unref(old_img);
 }
 
 
--- a/libpurple/status.c	Tue May 27 14:14:40 2008 +0000
+++ b/libpurple/status.c	Fri May 30 14:29:33 2008 +0000
@@ -107,8 +107,6 @@
 	PurpleStatusType *type;
 	PurplePresence *presence;
 
-	const char *title;
-
 	gboolean active;
 
 	/*
--- a/libpurple/util.c	Tue May 27 14:14:40 2008 +0000
+++ b/libpurple/util.c	Fri May 30 14:29:33 2008 +0000
@@ -1359,6 +1359,14 @@
 	GString *cdata = NULL;
 	GList *tags = NULL, *tag;
 	const char *c = html;
+	char quote = '\0';
+
+#define CHECK_QUOTE(ptr) if (*(ptr) == '\'' || *(ptr) == '\"') \
+			quote = *(ptr++); \
+		else \
+			quote = '\0';
+
+#define VALID_CHAR(ptr) (*(ptr) && *(ptr) != quote && (quote || (*(ptr) != ' ' && *(ptr) != '>')))
 
 	g_return_if_fail(xhtml_out != NULL || plain_out != NULL);
 
@@ -1516,38 +1524,37 @@
 						xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>");
 					continue;
 				}
-				if(!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) {
-					const char *p = c;
+				if (!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) {
+					const char *p = c + 4;
 					GString *src = NULL, *alt = NULL;
-					while(*p && *p != '>') {
-						if(!g_ascii_strncasecmp(p, "src=", strlen("src="))) {
-							const char *q = p + strlen("src=");
+					while (*p && *p != '>') {
+						if (!g_ascii_strncasecmp(p, "src=", 4)) {
+							const char *q = p + 4;
 							if (src)
 								g_string_free(src, TRUE);
 							src = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								src = g_string_append_c(src, *q);
 								q++;
 							}
 							p = q;
-						} else if(!g_ascii_strncasecmp(p, "alt=", strlen("alt="))) {
-							const char *q = p + strlen("alt=");
+						} else if (!g_ascii_strncasecmp(p, "alt=", 4)) {
+							const char *q = p + 4;
 							if (alt)
 								g_string_free(alt, TRUE);
 							alt = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								alt = g_string_append_c(alt, *q);
 								q++;
 							}
 							p = q;
+						} else {
+							p++;
 						}
-						p++;
 					}
-					if ((c = strchr(c, '>')) != NULL)
+					if ((c = strchr(p, '>')) != NULL)
 						c++;
 					else
 						c = p;
@@ -1564,21 +1571,20 @@
 					g_string_free(src, TRUE);
 					continue;
 				}
-				if(!g_ascii_strncasecmp(c, "<a", 2) && (*(c+2) == '>' || *(c+2) == ' ')) {
-					const char *p = c;
+				if (!g_ascii_strncasecmp(c, "<a", 2) && (*(c+2) == '>' || *(c+2) == ' ')) {
+					const char *p = c + 2;
 					struct purple_parse_tag *pt;
-					while(*p && *p != '>') {
-						if(!g_ascii_strncasecmp(p, "href=", strlen("href="))) {
-							const char *q = p + strlen("href=");
+					while (*p && *p != '>') {
+						if (!g_ascii_strncasecmp(p, "href=", 5)) {
+							const char *q = p + 5;
 							if (url)
 								g_string_free(url, TRUE);
 							url = g_string_new("");
 							if (cdata)
 								g_string_free(cdata, TRUE);
 							cdata = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								int len;
 								if ((*q == '&') && (purple_markup_unescape_entity(q, &len) == NULL))
 									url = g_string_append(url, "&amp;");
@@ -1587,10 +1593,11 @@
 								q++;
 							}
 							p = q;
+						} else {
+							p++;
 						}
-						p++;
 					}
-					if ((c = strchr(c, '>')) != NULL)
+					if ((c = strchr(p, '>')) != NULL)
 						c++;
 					else
 						c = p;
@@ -1603,55 +1610,48 @@
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) {
-					const char *p = c;
+					const char *p = c + 5;
 					GString *style = g_string_new("");
 					struct purple_parse_tag *pt;
-					while(*p && *p != '>') {
-						if(!g_ascii_strncasecmp(p, "back=", strlen("back="))) {
-							const char *q = p + strlen("back=");
+					while (*p && *p != '>') {
+						if (!g_ascii_strncasecmp(p, "back=", 5)) {
+							const char *q = p + 5;
 							GString *color = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								color = g_string_append_c(color, *q);
 								q++;
 							}
 							g_string_append_printf(style, "background: %s; ", color->str);
 							g_string_free(color, TRUE);
 							p = q;
-						} else if(!g_ascii_strncasecmp(p, "color=", strlen("color="))) {
-							const char *q = p + strlen("color=");
+						} else if (!g_ascii_strncasecmp(p, "color=", 6)) {
+							const char *q = p + 6;
 							GString *color = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								color = g_string_append_c(color, *q);
 								q++;
 							}
 							g_string_append_printf(style, "color: %s; ", color->str);
 							g_string_free(color, TRUE);
 							p = q;
-						} else if(!g_ascii_strncasecmp(p, "face=", strlen("face="))) {
-							const char *q = p + strlen("face=");
-							gboolean space_allowed = FALSE;
+						} else if (!g_ascii_strncasecmp(p, "face=", 5)) {
+							const char *q = p + 5;
 							GString *face = g_string_new("");
-							if(*q == '\'' || *q == '\"') {
-								space_allowed = TRUE;
-								q++;
-							}
-							while(*q && *q != '\"' && *q != '\'' && (space_allowed || *q != ' ')) {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								face = g_string_append_c(face, *q);
 								q++;
 							}
 							g_string_append_printf(style, "font-family: %s; ", g_strstrip(face->str));
 							g_string_free(face, TRUE);
 							p = q;
-						} else if(!g_ascii_strncasecmp(p, "size=", strlen("size="))) {
-							const char *q = p + strlen("size=");
+						} else if (!g_ascii_strncasecmp(p, "size=", 5)) {
+							const char *q = p + 5;
 							int sz;
 							const char *size = "medium";
-							if(*q == '\'' || *q == '\"')
-								q++;
+							CHECK_QUOTE(q);
 							sz = atoi(q);
 							switch (sz)
 							{
@@ -1681,10 +1681,11 @@
 							}
 							g_string_append_printf(style, "font-size: %s; ", size);
 							p = q;
+						} else {
+							p++;
 						}
-						p++;
 					}
-					if ((c = strchr(c, '>')) != NULL)
+					if ((c = strchr(p, '>')) != NULL)
 						c++;
 					else
 						c = p;
@@ -1699,24 +1700,23 @@
 					g_string_free(style, TRUE);
 					continue;
 				}
-				if(!g_ascii_strncasecmp(c, "<body ", 6)) {
-					const char *p = c;
+				if (!g_ascii_strncasecmp(c, "<body ", 6)) {
+					const char *p = c + 6;
 					gboolean did_something = FALSE;
-					while(*p && *p != '>') {
-						if(!g_ascii_strncasecmp(p, "bgcolor=", strlen("bgcolor="))) {
-							const char *q = p + strlen("bgcolor=");
+					while (*p && *p != '>') {
+						if (!g_ascii_strncasecmp(p, "bgcolor=", 8)) {
+							const char *q = p + 8;
 							struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1);
 							GString *color = g_string_new("");
-							if(*q == '\'' || *q == '\"')
-								q++;
-							while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+							CHECK_QUOTE(q);
+							while (VALID_CHAR(q)) {
 								color = g_string_append_c(color, *q);
 								q++;
 							}
-							if(xhtml)
+							if (xhtml)
 								g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str));
 							g_string_free(color, TRUE);
-							if ((c = strchr(c, '>')) != NULL)
+							if ((c = strchr(p, '>')) != NULL)
 								c++;
 							else
 								c = p;
@@ -1728,7 +1728,7 @@
 						}
 						p++;
 					}
-					if(did_something) continue;
+					if (did_something) continue;
 				}
 				/* this has to come after the special case for bgcolor */
 				ALLOW_TAG("body");
@@ -1791,6 +1791,8 @@
 		g_string_free(url, TRUE);
 	if (cdata)
 		g_string_free(cdata, TRUE);
+#undef CHECK_QUOTE
+#undef VALID_CHAR
 }
 
 /* The following are probably reasonable changes:
--- a/pidgin/gtkaccount.c	Tue May 27 14:14:40 2008 +0000
+++ b/pidgin/gtkaccount.c	Fri May 30 14:29:33 2008 +0000
@@ -2191,8 +2191,7 @@
 					G_TYPE_STRING,     /* COLUMN_SCREENNAME */
 					G_TYPE_BOOLEAN,    /* COLUMN_ENABLED */
 					G_TYPE_STRING,     /* COLUMN_PROTOCOL */
-					G_TYPE_POINTER,    /* COLUMN_DATA */
-					G_TYPE_POINTER     /* COLUMN_PULSE_DATA */
+					G_TYPE_POINTER     /* COLUMN_DATA */
 					);
 
 	/* And now the actual treeview */
--- a/pidgin/gtkimhtml.c	Tue May 27 14:14:40 2008 +0000
+++ b/pidgin/gtkimhtml.c	Fri May 30 14:29:33 2008 +0000
@@ -519,6 +519,7 @@
 			tmp);
 		g_free(tmp);
 
+		g_object_unref(layout);
 		return FALSE;
 	}
 
@@ -558,6 +559,7 @@
 	gtk_widget_show (imhtml->tip_window);
 
 	pango_font_metrics_unref(font_metrics);
+	g_object_unref(font);
 	g_object_unref(layout);
 
 	return FALSE;
@@ -5061,6 +5063,7 @@
 					break;
 				default:
 					str += g_snprintf(str, sizeof(buf) - (str - buf), "text-decoration: underline;");
+					empty = FALSE;
 			}
 		}
 
@@ -5112,6 +5115,38 @@
 	}
 }
 
+typedef struct {
+	GtkTextTag *tag;
+	char *end;
+	char *start;
+} PidginTextTagData;
+
+static PidginTextTagData *text_tag_data_new(GtkTextTag *tag)
+{
+	const char *start, *end;
+	PidginTextTagData *ret = NULL;
+
+	start = tag_to_html_start(tag);
+	if (!start || !*start)
+		return NULL;
+	end = tag_to_html_end(tag);
+	if (!end || !*end)
+		return NULL;
+
+	ret = g_new0(PidginTextTagData, 1);
+	ret->start = g_strdup(start);
+	ret->end = g_strdup(end);
+	ret->tag = tag;
+	return ret;
+}
+
+static void text_tag_data_destroy(PidginTextTagData *data)
+{
+	g_free(data->start);
+	g_free(data->end);
+	g_free(data);
+}
+
 static gboolean tag_ends_here(GtkTextTag *tag, GtkTextIter *iter, GtkTextIter *niter)
 {
 	return ((gtk_text_iter_has_tag(iter, GTK_TEXT_TAG(tag)) &&
@@ -5132,12 +5167,11 @@
 	gboolean is_rtl_message = FALSE;
 	GString *str = g_string_new("");
 	GSList *tags, *sl;
-	GQueue *q, *r;
+	GQueue *q;
 	GtkTextTag *tag;
+	PidginTextTagData *tagdata;
 
 	q = g_queue_new();
-	r = g_queue_new();
-
 
 	gtk_text_iter_order(start, end);
 	non_neutral_iter = next_iter = iter = *start;
@@ -5160,9 +5194,11 @@
 	for (sl = tags; sl; sl = sl->next) {
 		tag = sl->data;
 		if (!gtk_text_iter_toggles_tag(start, GTK_TEXT_TAG(tag))) {
-			if (strlen(tag_to_html_end(tag)) > 0)
-				g_string_append(str, tag_to_html_start(tag));
-			g_queue_push_tail(q, tag);
+			PidginTextTagData *data = text_tag_data_new(tag);
+			if (data) {
+				g_string_append(str, data->start);
+				g_queue_push_tail(q, data);
+			}
 		}
 	}
 	g_slist_free(tags);
@@ -5174,13 +5210,14 @@
 		for (sl = tags; sl; sl = sl->next) {
 			tag = sl->data;
 			if (gtk_text_iter_begins_tag(&iter, GTK_TEXT_TAG(tag))) {
-				if (strlen(tag_to_html_end(tag)) > 0)
-					g_string_append(str, tag_to_html_start(tag));
-				g_queue_push_tail(q, tag);
+				PidginTextTagData *data = text_tag_data_new(tag);
+				if (data) {
+					g_string_append(str, data->start);
+					g_queue_push_tail(q, data);
+				}
 			}
 		}
 
-
 		if (c == 0xFFFC) {
 			GtkTextChildAnchor* anchor = gtk_text_iter_get_child_anchor(&iter);
 			if (anchor) {
@@ -5206,28 +5243,31 @@
 		for (sl = tags; sl; sl = sl->next) {
 			tag = sl->data;
 			/** don't worry about non-printing tags ending */
-			if (tag_ends_here(tag, &iter, &next_iter) && strlen(tag_to_html_end(tag)) > 0) {
-
-				GtkTextTag *tmp;
-
-				while ((tmp = g_queue_pop_tail(q)) != tag) {
-					if (tmp == NULL)
-						break;
-
-					if (!tag_ends_here(tmp, &iter, &next_iter) && strlen(tag_to_html_end(tmp)) > 0)
+			if (tag_ends_here(tag, &iter, &next_iter) &&
+					strlen(tag_to_html_end(tag)) > 0 &&
+					strlen(tag_to_html_start(tag)) > 0) {
+
+				PidginTextTagData *tmp;
+				GQueue *r = g_queue_new();
+
+				while ((tmp = g_queue_pop_tail(q)) && tmp->tag != tag) {
+					g_string_append(str, tmp->end);
+					if (!tag_ends_here(tmp->tag, &iter, &next_iter))
 						g_queue_push_tail(r, tmp);
-					g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tmp)));
+					else
+						text_tag_data_destroy(tmp);
 				}
 
 				if (tmp == NULL)
 					purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n");
 				else
-					g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tag)));
+					g_string_append(str, tmp->end);
 
 				while ((tmp = g_queue_pop_head(r))) {
-					g_string_append(str, tag_to_html_start(GTK_TEXT_TAG(tmp)));
+					g_string_append(str, tmp->start);
 					g_queue_push_tail(q, tmp);
 				}
+				g_queue_free(r);
 			}
 		}
 
@@ -5236,15 +5276,16 @@
 		gtk_text_iter_forward_char(&next_iter);
 	}
 
-	while ((tag = g_queue_pop_tail(q)))
-		g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tag)));
+	while ((tagdata = g_queue_pop_tail(q))) {
+		g_string_append(str, tagdata->end);
+		text_tag_data_destroy(tagdata);
+	}
 
 	/* Bi-directional text support - close tags */
 	if (is_rtl_message)
 		g_string_append(str, "</SPAN>");
 
 	g_queue_free(q);
-	g_queue_free(r);
 	return g_string_free(str, FALSE);
 }
 
--- a/pidgin/gtkimhtml.h	Tue May 27 14:14:40 2008 +0000
+++ b/pidgin/gtkimhtml.h	Fri May 30 14:29:33 2008 +0000
@@ -854,11 +854,36 @@
  */
 void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
 
+/**
+ * Create a new GtkIMHtmlSmiley.
+ *
+ * @param file       The image file for the smiley
+ * @param shortcut   The key shortcut for the smiley
+ * @param hide       @c TRUE if the smiley should be hidden in the smiley dialog, @c FALSE otherwise
+ * @param flags      The smiley flags
+ *
+ * @return The newly created smiley
+ * @since 2.5.0
+ */
 GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide,
 		GtkIMHtmlSmileyFlags flags);
 
+/**
+ * Reload the image data for the smiley.
+ *
+ * @param smiley   The smiley to reload
+ *
+ * @since 2.5.0
+ */
 void gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley);
 
+/**
+ * Destroy a GtkIMHtmlSmiley.
+ *
+ * @param smiley   The smiley to destroy
+ *
+ * @since 2.5.0
+ */
 void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley);
 /*@}*/
 
--- a/pidgin/gtkimhtmltoolbar.c	Tue May 27 14:14:40 2008 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Fri May 30 14:29:33 2008 +0000
@@ -1122,7 +1122,10 @@
 	}
 
 	destroy_toolbar_font(NULL, NULL, toolbar);
-	destroy_smiley_dialog(toolbar);
+	if (toolbar->smiley_dialog != NULL) {
+		g_signal_handlers_disconnect_by_func(G_OBJECT(toolbar->smiley_dialog), close_smiley_dialog, toolbar);
+		destroy_smiley_dialog(toolbar);
+	}
 	destroy_toolbar_bgcolor(NULL, NULL, toolbar);
 	destroy_toolbar_fgcolor(NULL, NULL, toolbar);
 	close_link_dialog(toolbar);
--- a/pidgin/gtksmiley.c	Tue May 27 14:14:40 2008 +0000
+++ b/pidgin/gtksmiley.c	Fri May 30 14:29:33 2008 +0000
@@ -96,6 +96,18 @@
 	gtksmiley->smile = g_strdup(purple_smiley_get_shortcut(smiley));
 }
 
+static void
+image_changed_cb(PurpleSmiley *smiley, gpointer dontcare, GtkIMHtmlSmiley *gtksmiley)
+{
+	const char *file;
+
+	g_free(gtksmiley->file);
+
+	file = purple_imgstore_get_filename(purple_smiley_get_stored_image(smiley));
+	gtksmiley->file = g_build_filename(purple_smileys_get_storing_dir(), file, NULL);
+	gtk_imhtml_smiley_reload(gtksmiley);
+}
+
 static GtkIMHtmlSmiley *smiley_purple_to_gtkimhtml(PurpleSmiley *smiley)
 {
 	GtkIMHtmlSmiley *gtksmiley;
@@ -114,6 +126,10 @@
 	g_signal_connect(G_OBJECT(smiley), "notify::shortcut",
 			G_CALLBACK(shortcut_changed_cb), gtksmiley);
 
+	/* And update the pixbuf too when the image is changed */
+	g_signal_connect(G_OBJECT(smiley), "notify::image",
+			G_CALLBACK(image_changed_cb), gtksmiley);
+
 	return gtksmiley;
 }
 
--- a/po/de.po	Tue May 27 14:14:40 2008 +0000
+++ b/po/de.po	Fri May 30 14:29:33 2008 +0000
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-26 21:14+0200\n"
-"PO-Revision-Date: 2008-05-26 21:14+0200\n"
+"POT-Creation-Date: 2008-05-29 17:29+0200\n"
+"PO-Revision-Date: 2008-05-29 17:28+0200\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -612,6 +612,20 @@
 msgid "Send To"
 msgstr "Senden an"
 
+msgid "Invite message"
+msgstr "Einladungsnachricht"
+
+msgid "Invite"
+msgstr "Einladen"
+
+#, fuzzy
+msgid ""
+"Please enter the name of the user you wish to invite,\n"
+"along with an optional invite message."
+msgstr ""
+"Bitte geben Sie den Benutzernamen der Person ein, die Sie einladen möchten "
+"zusammen mit einer optionalen Einladungsnachricht."
+
 msgid "Conversation"
 msgstr "Unterhaltung"
 
@@ -624,6 +638,9 @@
 msgid "Add Buddy Pounce..."
 msgstr "Buddy-Alarm hinzufügen..."
 
+msgid "Invite..."
+msgstr "Einladen..."
+
 msgid "Enable Logging"
 msgstr "Mitschnitt einschalten"
 
@@ -870,9 +887,6 @@
 msgid "IM"
 msgstr "Nachricht"
 
-msgid "Invite"
-msgstr "Einladen"
-
 msgid "(none)"
 msgstr "(kein)"
 
@@ -2875,6 +2889,9 @@
 msgid "Save buddylist..."
 msgstr "Buddy-Liste speichern..."
 
+msgid "Load buddylist from file..."
+msgstr "Buddy-Liste aus Datei laden..."
+
 msgid "Fill in the registration fields."
 msgstr "Füllen Sie die Registrierungsfelder aus."
 
@@ -3051,9 +3068,6 @@
 msgid "Save buddylist to file..."
 msgstr "Buddy-Liste in Datei speichern..."
 
-msgid "Load buddylist from file..."
-msgstr "Buddy-Liste aus Datei laden..."
-
 #. magic
 #. major_version
 #. minor_version
@@ -3620,6 +3634,8 @@
 msgid "Country"
 msgstr "Land"
 
+#. lots of clients (including purple) do this, but it's
+#. * out of spec
 msgid "Telephone"
 msgstr "Telefon"
 
@@ -3807,12 +3823,12 @@
 msgid "Capabilities"
 msgstr "Fähigkeiten"
 
+msgid "Priority"
+msgstr "Priorität"
+
 msgid "Resource"
 msgstr "Ressource"
 
-msgid "Priority"
-msgstr "Priorität"
-
 msgid "Middle Name"
 msgstr "Zweiter Name"
 
@@ -6514,9 +6530,6 @@
 msgid "Member Since"
 msgstr "Mitglied seit"
 
-msgid "Available Message"
-msgstr "Verfügbarkeitsnachricht"
-
 msgid "Your AIM connection may be lost."
 msgstr "Ihre AIM-Verbindung könnte unterbrochen sein."
 
@@ -6552,6 +6565,9 @@
 msgid "Zip Code"
 msgstr "PLZ"
 
+msgid "Work Information"
+msgstr "Information (Arbeit)"
+
 msgid "Division"
 msgstr "Abteilung"
 
@@ -6561,9 +6577,6 @@
 msgid "Web Page"
 msgstr "Webseite"
 
-msgid "Work Information"
-msgstr "Information (Arbeit)"
-
 msgid "Pop-Up Message"
 msgstr "Pop-Up Nachricht"
 
@@ -6784,12 +6797,6 @@
 "Ihr IM-Bild wurde nicht gesendet. Sie können keine IM-Bilder in AIM-Chats "
 "senden."
 
-msgid "Away Message"
-msgstr "Abwesenheitsnachricht"
-
-msgid "<i>(retrieving)</i>"
-msgstr "<i>(empfange)</i>"
-
 msgid "iTunes Music Store Link"
 msgstr "iTunes Music Store Link"
 
--- a/po/nl.po	Tue May 27 14:14:40 2008 +0000
+++ b/po/nl.po	Fri May 30 14:29:33 2008 +0000
@@ -14558,7 +14558,7 @@
 "<b>Occupants:</b> %d"
 msgstr ""
 "\n"
-"<b>Deelnemers:</b> %s"
+"<b>Deelnemers:</b> %d"
 
 #: ../pidgin/gtkblist.c:3253
 #, c-format