changeset 26977:6ddb4932772a

propagate from branch 'im.pidgin.cpw.sulabh.yahoo_16' (head c1ede5faf776dc1cc966dabb42c1ef0dbaea31db) to branch 'im.pidgin.pidgin' (head 2a5f44f787052657964361388082569b98a37290)
author John Bailey <rekkanoryo@rekkanoryo.org>
date Sun, 17 May 2009 01:55:15 +0000
parents aa341c72f29f (diff) e4ab3d5362df (current diff)
children 12d1711e1667 a1e92a4d99b9
files libpurple/protocols/msn/soap.c libpurple/protocols/yahoo/yahoo.h libpurple/protocols/yahoo/yahoo_auth.c libpurple/protocols/yahoo/yahoo_auth.h libpurple/protocols/yahoo/yahoo_crypt.c libpurple/protocols/yahoo/yahoo_crypt.h libpurple/util.c
diffstat 31 files changed, 624 insertions(+), 751 deletions(-) [+]
line wrap: on
line diff
--- a/.mtn-ignore	Thu May 14 02:12:40 2009 +0000
+++ b/.mtn-ignore	Sun May 17 01:55:15 2009 +0000
@@ -52,6 +52,7 @@
 libpurple/dbus-types.h
 libpurple/example/nullclient
 libpurple/gconf/purple.schemas$
+libpurple/marshallers.[ch]
 libpurple/plugins/dbus-example-bindings.c
 libpurple/plugins/perl/common/Makefile.PL$
 libpurple/plugins/perl/common/Makefile.old
--- a/ChangeLog	Thu May 14 02:12:40 2009 +0000
+++ b/ChangeLog	Sun May 17 01:55:15 2009 +0000
@@ -38,6 +38,9 @@
 	  contains formatting.
 	* Show when the user was last logged in when doing "Get Info" on an offline
 	  buddy, provided the server supports it.
+	* Support custom smileys in MUCs (only when all participants supports the
+	  "Bits of Binary" extension, and a maximum of 10 participants are in the
+	  chat (to avoid getting too many fetch requests).
 
 	Yahoo:
 	* P2P file transfers. (Sulabh Mahajan)
--- a/libpurple/account.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/account.c	Sun May 17 01:55:15 2009 +0000
@@ -99,7 +99,7 @@
 	const char *name;
 	PurpleAccountSetting *setting;
 	xmlnode *node, *child;
-	char buf[20];
+	char buf[21];
 
 	name    = (const char *)key;
 	setting = (PurpleAccountSetting *)value;
@@ -110,7 +110,7 @@
 
 	if (setting->type == PURPLE_PREF_INT) {
 		xmlnode_set_attrib(child, "type", "int");
-		snprintf(buf, sizeof(buf), "%d", setting->value.integer);
+		g_snprintf(buf, sizeof(buf), "%d", setting->value.integer);
 		xmlnode_insert_data(child, buf, -1);
 	}
 	else if (setting->type == PURPLE_PREF_STRING && setting->value.string != NULL) {
@@ -119,7 +119,7 @@
 	}
 	else if (setting->type == PURPLE_PREF_BOOLEAN) {
 		xmlnode_set_attrib(child, "type", "bool");
-		snprintf(buf, sizeof(buf), "%d", setting->value.boolean);
+		g_snprintf(buf, sizeof(buf), "%d", setting->value.boolean);
 		xmlnode_insert_data(child, buf, -1);
 	}
 }
@@ -281,7 +281,7 @@
 	PurpleProxyType proxy_type;
 	const char *value;
 	int int_value;
-	char buf[20];
+	char buf[21];
 
 	proxy_type = purple_proxy_info_get_type(proxy_info);
 
@@ -304,7 +304,7 @@
 
 	if ((int_value = purple_proxy_info_get_port(proxy_info)) != 0)
 	{
-		snprintf(buf, sizeof(buf), "%d", int_value);
+		g_snprintf(buf, sizeof(buf), "%d", int_value);
 		child = xmlnode_new_child(node, "port");
 		xmlnode_insert_data(child, buf, -1);
 	}
@@ -342,7 +342,7 @@
 		return node;
 
 	child = xmlnode_new_child(node, "type");
-	snprintf(type_str, sizeof(type_str), "%u", err->type);
+	g_snprintf(type_str, sizeof(type_str), "%u", err->type);
 	xmlnode_insert_data(child, type_str, -1);
 
 	child = xmlnode_new_child(node, "description");
--- a/libpurple/blist.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/blist.c	Sun May 17 01:55:15 2009 +0000
@@ -125,7 +125,7 @@
 	const char *name;
 	PurpleValue *value;
 	xmlnode *node, *child;
-	char buf[20];
+	char buf[21];
 
 	name    = (const char *)key;
 	value   = (PurpleValue *)hvalue;
@@ -138,7 +138,7 @@
 
 	if (purple_value_get_type(value) == PURPLE_TYPE_INT) {
 		xmlnode_set_attrib(child, "type", "int");
-		snprintf(buf, sizeof(buf), "%d", purple_value_get_int(value));
+		g_snprintf(buf, sizeof(buf), "%d", purple_value_get_int(value));
 		xmlnode_insert_data(child, buf, -1);
 	}
 	else if (purple_value_get_type(value) == PURPLE_TYPE_STRING) {
@@ -147,7 +147,7 @@
 	}
 	else if (purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN) {
 		xmlnode_set_attrib(child, "type", "bool");
-		snprintf(buf, sizeof(buf), "%d", purple_value_get_boolean(value));
+		g_snprintf(buf, sizeof(buf), "%d", purple_value_get_boolean(value));
 		xmlnode_insert_data(child, buf, -1);
 	}
 }
@@ -303,7 +303,7 @@
 	node = xmlnode_new("account");
 	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
 	xmlnode_set_attrib(node, "name", purple_account_get_username(account));
-	snprintf(buf, sizeof(buf), "%d", account->perm_deny);
+	g_snprintf(buf, sizeof(buf), "%d", account->perm_deny);
 	xmlnode_set_attrib(node, "mode", buf);
 
 	for (cur = account->permit; cur; cur = cur->next)
--- a/libpurple/dnsquery.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/dnsquery.c	Sun May 17 01:55:15 2009 +0000
@@ -325,7 +325,7 @@
 		return;
 	already_done = TRUE;
 	ppid = getppid();
-	snprintf(s, sizeof(s), "/proc/%d/exe", ppid);
+	g_snprintf(s, sizeof(s), "/proc/%d/exe", ppid);
 	n = readlink(s, e, sizeof(e));
 	if(n < 0)
 		return;
--- a/libpurple/network.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/network.c	Sun May 17 01:55:15 2009 +0000
@@ -303,7 +303,7 @@
 	/*
 	 * Get a list of addresses on this machine.
 	 */
-	snprintf(serv, sizeof(serv), "%hu", port);
+	g_snprintf(serv, sizeof(serv), "%hu", port);
 	memset(&hints, 0, sizeof(struct addrinfo));
 	hints.ai_flags = AI_PASSIVE;
 	hints.ai_family = AF_UNSPEC;
--- a/libpurple/plugins/perl/perl-handlers.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/plugins/perl/perl-handlers.c	Sun May 17 01:55:15 2009 +0000
@@ -284,6 +284,8 @@
 	DATATYPE **copy_args;
 
 	dSP;
+	PERL_SET_CONTEXT(my_perl);
+	SPAGAIN;
 	ENTER;
 	SAVETMPS;
 	PUSHMARK(sp);
@@ -364,7 +366,8 @@
 					break;
 
 				case PURPLE_TYPE_STRING:
-					if (strcmp(*((char **)copy_args[i]), SvPVX(sv_args[i]))) {
+					if (!*((char **)copy_args[i]) || !SvPVX(sv_args[i]) ||
+							strcmp(*((char **)copy_args[i]), SvPVX(sv_args[i]))) {
 						g_free(*((char **)copy_args[i]));
 						*((char **)copy_args[i]) =
 							g_strdup(SvPVutf8_nolen(sv_args[i]));
--- a/libpurple/prefs.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/prefs.c	Sun May 17 01:55:15 2009 +0000
@@ -118,7 +118,7 @@
 {
 	xmlnode *node, *childnode;
 	struct purple_pref *child;
-	char buf[20];
+	char buf[21];
 	GList *cur;
 
 	/* Create a new node */
@@ -128,7 +128,7 @@
 	/* Set the type of this node (if type == PURPLE_PREF_NONE then do nothing) */
 	if (pref->type == PURPLE_PREF_INT) {
 		xmlnode_set_attrib(node, "type", "int");
-		snprintf(buf, sizeof(buf), "%d", pref->value.integer);
+		g_snprintf(buf, sizeof(buf), "%d", pref->value.integer);
 		xmlnode_set_attrib(node, "value", buf);
 	}
 	else if (pref->type == PURPLE_PREF_STRING) {
@@ -161,7 +161,7 @@
 	}
 	else if (pref->type == PURPLE_PREF_BOOLEAN) {
 		xmlnode_set_attrib(node, "type", "bool");
-		snprintf(buf, sizeof(buf), "%d", pref->value.boolean);
+		g_snprintf(buf, sizeof(buf), "%d", pref->value.boolean);
 		xmlnode_set_attrib(node, "value", buf);
 	}
 
--- a/libpurple/protocols/jabber/buddy.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Sun May 17 01:55:15 2009 +0000
@@ -74,7 +74,7 @@
 	if (js->buddies == NULL)
 		return NULL;
 
-	if(!(realname = jabber_normalize(js->gc->account, name)))
+	if(!(realname = jabber_get_bare_jid(name)))
 		return NULL;
 
 	jb = g_hash_table_lookup(js->buddies, realname);
--- a/libpurple/protocols/jabber/chat.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/jabber/chat.c	Sun May 17 01:55:15 2009 +0000
@@ -1162,5 +1162,59 @@
 	g_free(room_jid);
 }
 
+typedef struct {
+	const gchar *cap;
+	gboolean *all_support;
+	JabberBuddy *jb;
+} JabberChatCapsData;
 
+static void
+jabber_chat_all_participants_have_capability_foreach(gpointer key,
+                                                     gpointer value,
+                                                     gpointer user_data)
+{
+	const gchar *cap = ((JabberChatCapsData *) user_data)->cap;
+	gboolean *all_support = ((JabberChatCapsData *) user_data)->all_support;
+	JabberBuddy *jb = ((JabberChatCapsData *) user_data)->jb;
+	JabberChatMember *member = (JabberChatMember *) value;
+	const gchar *resource = member->handle;
+	JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, resource);
 
+	if (jbr) {
+		*all_support &= jabber_resource_has_capability(jbr, cap);
+	} else {
+		*all_support = FALSE;
+	}
+}
+
+gboolean
+jabber_chat_all_participants_have_capability(const JabberChat *chat,
+	const gchar *cap)
+{
+	gchar *chat_jid = NULL;
+	JabberBuddy *jb = NULL;
+	gboolean all_support = TRUE;
+	JabberChatCapsData data;
+
+	chat_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
+	jb = jabber_buddy_find(chat->js, chat_jid, FALSE);
+
+	if (jb) {
+		data.cap = cap;
+		data.all_support = &all_support;
+		data.jb = jb;
+
+		g_hash_table_foreach(chat->members, 
+			jabber_chat_all_participants_have_capability_foreach, &data);
+	} else {
+		all_support = FALSE;
+	}
+	g_free(chat_jid);
+	return all_support;
+}
+
+guint
+jabber_chat_get_num_participants(const JabberChat *chat)
+{
+	return g_hash_table_size(chat->members);
+}
--- a/libpurple/protocols/jabber/chat.h	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/jabber/chat.h	Sun May 17 01:55:15 2009 +0000
@@ -95,5 +95,8 @@
 
 char *jabber_roomlist_room_serialize(PurpleRoomlistRoom *room);
 
+gboolean jabber_chat_all_participants_have_capability(const JabberChat *chat,
+	const gchar *cap);
+guint jabber_chat_get_num_participants(const JabberChat *chat);
 
 #endif /* PURPLE_JABBER_CHAT_H_ */
--- a/libpurple/protocols/jabber/jabber.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sun May 17 01:55:15 2009 +0000
@@ -3359,7 +3359,7 @@
 	jabber_add_feature(XEP_0224_NAMESPACE, jabber_buzz_isenabled);
 
 	/* Bits Of Binary */
-	jabber_add_feature(XEP_0231_NAMESPACE, jabber_custom_smileys_isenabled);
+	jabber_add_feature(XEP_0231_NAMESPACE, 0);
 
 	/* Jingle features! */
 	jabber_add_feature(JINGLE, 0);
--- a/libpurple/protocols/jabber/message.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/jabber/message.c	Sun May 17 01:55:15 2009 +0000
@@ -921,11 +921,12 @@
 
 static gboolean
 jabber_conv_support_custom_smileys(const PurpleConnection *gc,
-								   const PurpleConversation *conv,
+								   PurpleConversation *conv,
 								   const gchar *who)
 {
 	JabberStream *js = (JabberStream *) gc->proto_data;
 	JabberBuddy *jb;
+	JabberChat *chat;
 
 	if (!js) {
 		purple_debug_error("jabber",
@@ -934,7 +935,6 @@
 	}
 
 	switch (purple_conversation_get_type(conv)) {
-		/* for the time being, we will not support custom smileys in MUCs */
 		case PURPLE_CONV_TYPE_IM:
 			jb = jabber_buddy_find(js, who, FALSE);
 			if (jb) {
@@ -943,6 +943,18 @@
 				return FALSE;
 			}
 			break;
+		case PURPLE_CONV_TYPE_CHAT:
+			chat = jabber_chat_find_by_conv(conv);
+			if (chat) {
+				/* do not attempt to send custom smileys in a MUC with more than
+				 10 people, to avoid getting too many BoB requests */
+				return jabber_chat_get_num_participants(chat) <= 10 &&
+					jabber_chat_all_participants_have_capability(chat, 
+						XEP_0231_NAMESPACE);
+			} else {
+				return FALSE;
+			}
+			break;
 		default:
 			return FALSE;
 			break;
@@ -1203,6 +1215,7 @@
 	JabberMessage *jm;
 	JabberStream *js;
 	char *xhtml;
+	char *tmp;
 
 	if(!msg || !gc)
 		return 0;
@@ -1220,6 +1233,11 @@
 	jm->id = jabber_get_next_id(jm->js);
 
 	purple_markup_html_to_xhtml(msg, &xhtml, &jm->body);
+	tmp = jabber_message_smileyfy_xhtml(jm, xhtml);
+	if (tmp) {
+		g_free(xhtml);
+		xhtml = tmp;
+	}
 
 	if (chat->xhtml && !jabber_xhtml_plain_equal(xhtml, jm->body))
 		jm->xhtml = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", xhtml);
--- a/libpurple/protocols/jabber/si.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/jabber/si.c	Sun May 17 01:55:15 2009 +0000
@@ -407,7 +407,7 @@
 {
 	PurpleXfer *xfer = data;
 	JabberSIXfer *jsx = xfer->data;
-	char buffer[256];
+	char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
 	int len;
 	char *dstaddr, *hash;
 	const char *host;
@@ -431,16 +431,19 @@
 		jsx->rxlen += len;
 		return;
 	} else if(jsx->rxqueue[0] != 0x05 || jsx->rxqueue[1] != 0x01 ||
-			jsx->rxqueue[3] != 0x03) {
-		purple_debug_info("jabber", "invalid socks5 stuff\n");
+			jsx->rxqueue[3] != 0x03 || jsx->rxqueue[4] != 40) {
+		purple_debug_info("jabber", "Invalid socks5 conn req. header[0x%x,0x%x,0x%x,0x%x,0x%x]\n",
+				  jsx->rxqueue[0], jsx->rxqueue[1], jsx->rxqueue[2],
+				  jsx->rxqueue[3], jsx->rxqueue[4]);
 		purple_input_remove(xfer->watcher);
 		xfer->watcher = 0;
 		close(source);
 		purple_xfer_cancel_remote(xfer);
 		return;
 	} else if(jsx->rxlen - 5 <  jsx->rxqueue[4] + 2) {
-		purple_debug_info("jabber", "reading umpteen more bytes\n");
-		len = read(source, buffer, jsx->rxqueue[4] + 5 + 2 - jsx->rxlen);
+		purple_debug_info("jabber", "reading %u bytes for DST.ADDR + port num (trying to read %u now)\n",
+				  jsx->rxqueue[4] + 2, jsx->rxqueue[4] + 2 - (jsx->rxlen - 5));
+		len = read(source, buffer, jsx->rxqueue[4] + 2 - (jsx->rxlen - 5));
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0) {
@@ -455,6 +458,7 @@
 		jsx->rxlen += len;
 	}
 
+	/* Have we not read all of DST.ADDR and the following 2-byte port number? */
 	if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2)
 		return;
 
@@ -468,9 +472,16 @@
 	/* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
 	hash = jabber_calculate_data_sha1sum(dstaddr, strlen(dstaddr));
 
-	if(jsx->rxqueue[4] != 40 || strncmp(hash, jsx->rxqueue+5, 40) ||
+	if(strncmp(hash, jsx->rxqueue + 5, 40) ||
 			jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
-		purple_debug_error("jabber", "someone connected with the wrong info!\n");
+		if (jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00)
+			purple_debug_error("jabber", "Got SOCKS5 BS conn with the wrong DST.PORT"
+						     " (must be 0 - got[0x%x,0x%x]).\n",
+						     jsx->rxqueue[45], jsx->rxqueue[46]);
+		else
+			purple_debug_error("jabber", "Got SOCKS5 BS conn with the wrong DST.ADDR"
+						     " (expected '%s' - got '%.40s').\n",
+						     hash, jsx->rxqueue + 5);
 		close(source);
 		purple_xfer_cancel_remote(xfer);
 		g_free(hash);
@@ -531,11 +542,13 @@
 	purple_input_remove(xfer->watcher);
 	xfer->watcher = 0;
 
+	/* If we sent a "Success", wait for a response, otherwise give up and cancel */
 	if (jsx->rxqueue[1] == 0x00) {
 		xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
 			jabber_si_xfer_bytestreams_send_read_again_cb, xfer);
 		g_free(jsx->rxqueue);
 		jsx->rxqueue = NULL;
+		jsx->rxlen = 0;
 	} else {
 		close(source);
 		purple_xfer_cancel_remote(xfer);
@@ -556,6 +569,7 @@
 
 	xfer->fd = source;
 
+	/** Try to read the SOCKS5 header */
 	if(jsx->rxlen < 2) {
 		purple_debug_info("jabber", "reading those first two bytes\n");
 		len = read(source, buffer, 2 - jsx->rxlen);
@@ -573,8 +587,9 @@
 		jsx->rxlen += len;
 		return;
 	} else if(jsx->rxlen - 2 <  jsx->rxqueue[1]) {
-		purple_debug_info("jabber", "reading the next umpteen bytes\n");
-		len = read(source, buffer, jsx->rxqueue[1] + 2 - jsx->rxlen);
+		purple_debug_info("jabber", "reading %u bytes for auth methods (trying to read %u now)\n",
+				  jsx->rxqueue[1], jsx->rxqueue[1] - (jsx->rxlen - 2));
+		len = read(source, buffer, jsx->rxqueue[1] - (jsx->rxlen - 2));
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0) {
@@ -589,6 +604,7 @@
 		jsx->rxlen += len;
 	}
 
+	/* Have we not read all the auth. method bytes? */
 	if(jsx->rxlen -2 < jsx->rxqueue[1])
 		return;
 
--- a/libpurple/protocols/msn/httpconn.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/msn/httpconn.c	Sun May 17 01:55:15 2009 +0000
@@ -219,7 +219,13 @@
 		g_free(tmp);
 
 		t = strchr(full_session_id, '.');
-		session_id = g_strndup(full_session_id, t - full_session_id);
+		if (t != NULL)
+			session_id = g_strndup(full_session_id, t - full_session_id);
+		else {
+			purple_debug_error("msn", "Malformed full_session_id[%s]\n",
+					   full_session_id ? full_session_id : NULL);
+			session_id = g_strdup(full_session_id);
+		}
 
 		if (session_action == NULL || strcmp(session_action, "close") != 0)
 		{
--- a/libpurple/protocols/msn/oim.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/msn/oim.c	Sun May 17 01:55:15 2009 +0000
@@ -668,9 +668,15 @@
 		if (tokens[1] != NULL)
 			from = (const char *)tokens[1];
 
-		start = strchr(from, '<') + 1;
-		end = strchr(from, '>');
-		passport = g_strndup(start, end - start);
+		start = strchr(from, '<');
+		if (start != NULL) {
+			start++;
+			end = strchr(from, '>');
+			if (end != NULL)
+				passport = g_strndup(start, end - start);
+		}
+		if (passport == NULL)
+			passport = g_strdup(_("Unknown"));
 
 		g_strfreev(tokens);
 	}
--- a/libpurple/protocols/msn/soap.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/msn/soap.c	Sun May 17 01:55:15 2009 +0000
@@ -434,7 +434,7 @@
 					g_free(line);
 					return;
 				} else if (strcmp(key, "Content-Length") == 0) {
-					conn->body_len = atoi(value);
+					sscanf(value, "%" G_GSIZE_FORMAT, &(conn->body_len));
 				} else if (strcmp(key, "Connection") == 0) {
 					if (strcmp(value, "close") == 0) {
 						conn->close_when_done = TRUE;
@@ -667,7 +667,8 @@
 			conn->handled_len = 0;
 			conn->current_request = req;
 
-			purple_input_remove(conn->event_handle);
+			if (conn->event_handle)
+				purple_input_remove(conn->event_handle);
 			conn->event_handle = purple_input_add(conn->ssl->fd,
 				PURPLE_INPUT_WRITE, msn_soap_write_cb, conn);
 			if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) {
--- a/libpurple/protocols/oscar/family_locate.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Sun May 17 01:55:15 2009 +0000
@@ -68,12 +68,8 @@
 	 {0x09, 0x46, 0x00, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
+	/* "Live Video" (SIP/RTC Video) support in Windows AIM 5.5.3501 and newer */
 	{OSCAR_CAPABILITY_VIDEO,
-	 {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
-	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
-
-	/* "Live Video" (SIP/RTC Video) support in Windows AIM 5.5.3501 and newer */
-	{OSCAR_CAPABILITY_LIVEVIDEO,
 	 {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
--- a/libpurple/protocols/oscar/family_oservice.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Sun May 17 01:55:15 2009 +0000
@@ -151,7 +151,7 @@
 
 	memset(&csi, 0, sizeof(csi));
 	csi.exchange = exchange;
-	strncpy(csi.name, roomname, sizeof(csi.name));
+	g_strlcpy(csi.name, roomname, sizeof(csi.name));
 	csi.instance = instance;
 
 	/*
--- a/libpurple/protocols/oscar/oscar.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Sun May 17 01:55:15 2009 +0000
@@ -714,9 +714,6 @@
 			case OSCAR_CAPABILITY_ICHATAV:
 				tmp = _("iChat AV");
 				break;
-			case OSCAR_CAPABILITY_LIVEVIDEO:
-				tmp = _("Live Video");
-				break;
 			case OSCAR_CAPABILITY_CAMERA:
 				tmp = _("Camera");
 				break;
--- a/libpurple/protocols/oscar/oscar.h	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Sun May 17 01:55:15 2009 +0000
@@ -369,12 +369,11 @@
 	OSCAR_CAPABILITY_SMS                  = 0x00400000,
 	OSCAR_CAPABILITY_VIDEO                = 0x00800000,
 	OSCAR_CAPABILITY_ICHATAV              = 0x01000000,
-	OSCAR_CAPABILITY_LIVEVIDEO            = 0x02000000,
-	OSCAR_CAPABILITY_CAMERA               = 0x04000000,
-	OSCAR_CAPABILITY_ICHAT_SCREENSHARE    = 0x08000000,
-	OSCAR_CAPABILITY_TYPING               = 0x10000000,
-	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x20000000,
-	OSCAR_CAPABILITY_LAST                 = 0x40000000
+	OSCAR_CAPABILITY_CAMERA               = 0x02000000,
+	OSCAR_CAPABILITY_ICHAT_SCREENSHARE    = 0x04000000,
+	OSCAR_CAPABILITY_TYPING               = 0x08000000,
+	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x10000000,
+	OSCAR_CAPABILITY_LAST                 = 0x20000000
 } OscarCapability;
 
 /*
--- a/libpurple/protocols/qq/utils.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/qq/utils.c	Sun May 17 01:55:15 2009 +0000
@@ -222,7 +222,8 @@
  * The return should be freed later. */
 guint8 *hex_str_to_bytes(const gchar *const buffer, gint *out_len)
 {
-	gchar *hex_str, *hex_buffer, *cursor, tmp;
+	gchar *hex_str, *hex_buffer, *cursor;
+	gchar tmp[2];
 	guint8 *bytes, nibble1, nibble2;
 	gint index;
 
@@ -242,7 +243,9 @@
 	index = 0;
 	for (cursor = hex_str; cursor < hex_str + sizeof(gchar) * (strlen(hex_str)) - 1; cursor++) {
 		if (g_ascii_isdigit(*cursor)) {
-			tmp = *cursor; nibble1 = atoi(&tmp);
+			tmp[0] = *cursor;
+			tmp[1] = '\0';
+			nibble1 = atoi(tmp);
 		} else if (g_ascii_isalpha(*cursor) && (gint) *cursor - 87 < 16) {
 			nibble1 = (gint) *cursor - 87;
 		} else {
@@ -254,7 +257,9 @@
 		nibble1 = nibble1 << 4;
 		cursor++;
 		if (g_ascii_isdigit(*cursor)) {
-			tmp = *cursor; nibble2 = atoi(&tmp);
+			tmp[0] = *cursor;
+			tmp[1] = '\0';
+			nibble2 = atoi(tmp);
 		} else if (g_ascii_isalpha(*cursor) && (gint) (*cursor - 87) < 16) {
 			nibble2 = (gint) *cursor - 87;
 		} else {
--- a/libpurple/protocols/yahoo/yahoo.h	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Sun May 17 01:55:15 2009 +0000
@@ -36,7 +36,7 @@
 #define YAHOO_P2P_KEEPALIVE_SECS 300
 #define YAHOO_P2P_SERVER_TIMEOUT 10
 #define YAHOO_PROFILE_URL "http://profiles.yahoo.com/"
-#define YAHOO_MAIL_URL "https://login.yahoo.com/config/login?.src=ym"
+#define YAHOO_MAIL_URL "http://rd.yahoo.com/messenger/client/?http://mail.yahoo.com/"
 #define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com"
 #define YAHOO_XFER_PORT 80
 #define YAHOO_XFER_RELAY_HOST "relay.msg.yahoo.com"
--- a/libpurple/savedstatuses.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/savedstatuses.c	Sun May 17 01:55:15 2009 +0000
@@ -285,13 +285,13 @@
 		xmlnode_set_attrib(node, "transient", "true");
 	}
 
-	snprintf(buf, sizeof(buf), "%lu", status->creation_time);
+	g_snprintf(buf, sizeof(buf), "%lu", status->creation_time);
 	xmlnode_set_attrib(node, "created", buf);
 
-	snprintf(buf, sizeof(buf), "%lu", status->lastused);
+	g_snprintf(buf, sizeof(buf), "%lu", status->lastused);
 	xmlnode_set_attrib(node, "lastused", buf);
 
-	snprintf(buf, sizeof(buf), "%u", status->usage_count);
+	g_snprintf(buf, sizeof(buf), "%u", status->usage_count);
 	xmlnode_set_attrib(node, "usage_count", buf);
 
 	child = xmlnode_new_child(node, "state");
--- a/libpurple/util.c	Thu May 14 02:12:40 2009 +0000
+++ b/libpurple/util.c	Sun May 17 01:55:15 2009 +0000
@@ -129,7 +129,7 @@
 	ascii = g_malloc(len * 2 + 1);
 
 	for (i = 0; i < len; i++)
-		snprintf(&ascii[i * 2], 3, "%02hhx", data[i]);
+		g_snprintf(&ascii[i * 2], 3, "%02hhx", data[i]);
 
 	return ascii;
 }
@@ -1623,7 +1623,7 @@
 					pt->dest_tag = "a";
 					tags = g_list_prepend(tags, pt);
 					if(xhtml)
-						g_string_append_printf(xhtml, "<a href='%s'>", url ? g_strstrip(url->str) : "");
+						g_string_append_printf(xhtml, "<a href=\"%s\">", url ? g_strstrip(url->str) : "");
 					continue;
 				}
 				if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) {
@@ -2017,7 +2017,6 @@
 	case '<':
 	case '>':
 	case '"':
-	case '\'':
 		return TRUE;
 	default:
 		return FALSE;
--- a/pidgin/gtkblist.c	Thu May 14 02:12:40 2009 +0000
+++ b/pidgin/gtkblist.c	Sun May 17 01:55:15 2009 +0000
@@ -75,47 +75,40 @@
 typedef struct
 {
 	PurpleAccount *account;
-
 	GtkWidget *window;
+	GtkBox *vbox;
+	GtkWidget *account_menu;
+	GtkSizeGroup *sg;
+} PidginBlistRequestData;
+
+typedef struct
+{
+	PidginBlistRequestData rq_data;
 	GtkWidget *combo;
 	GtkWidget *entry;
 	GtkWidget *entry_for_alias;
-	GtkWidget *account_box;
 
 } PidginAddBuddyData;
 
 typedef struct
 {
-	PurpleAccount *account;
+	PidginBlistRequestData rq_data;
 	gchar *default_chat_name;
-
-	GtkWidget *window;
-	GtkWidget *account_menu;
+	GList *entries;
+} PidginChatData;
+
+typedef struct
+{
+	PidginChatData chat_data;
+
 	GtkWidget *alias_entry;
 	GtkWidget *group_combo;
-	GtkWidget *entries_box;
-	GtkSizeGroup *sg;
 	GtkWidget *autojoin;
 	GtkWidget *persistent;
-
-	GList *entries;
-
 } PidginAddChatData;
 
 typedef struct
 {
-	PurpleAccount *account;
-
-	GtkWidget *window;
-	GtkWidget *account_menu;
-	GtkWidget *entries_box;
-	GtkSizeGroup *sg;
-
-	GList *entries;
-} PidginJoinChatData;
-
-typedef struct
-{
 	/** Used to hold error minidialogs.  Gets packed
 	 *  inside PidginBuddyList.error_buttons
 	 */
@@ -868,7 +861,7 @@
 }
 
 static void
-do_join_chat(PidginJoinChatData *data)
+do_join_chat(PidginChatData *data)
 {
 	if (data)
 	{
@@ -894,14 +887,14 @@
 			}
 		}
 
-		chat = purple_chat_new(data->account, NULL, components);
+		chat = purple_chat_new(data->rq_data.account, NULL, components);
 		gtk_blist_join_chat(chat);
 		purple_blist_remove_chat(chat);
 	}
 }
 
 static void
-do_joinchat(GtkWidget *dialog, int id, PidginJoinChatData *info)
+do_joinchat(GtkWidget *dialog, int id, PidginChatData *info)
 {
 	switch(id)
 	{
@@ -910,7 +903,7 @@
 			break;
 
 		case 1:
-			pidgin_roomlist_dialog_show_with_account(info->account);
+			pidgin_roomlist_dialog_show_with_account(info->rq_data.account);
 			return;
 
 		break;
@@ -926,11 +919,11 @@
  * strings are empty then don't allow the user to click on "OK."
  */
 static void
-joinchat_set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data)
+set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data)
 {
 	PurplePluginProtocolInfo *prpl_info;
 	PurpleConnection *gc;
-	PidginJoinChatData *data;
+	PidginChatData *data;
 	GList *tmp;
 	const char *text;
 	gboolean required;
@@ -949,13 +942,13 @@
 		}
 	}
 
-	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), GTK_RESPONSE_OK, sensitive);
-
-	gc = purple_account_get_connection(data->account);
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), GTK_RESPONSE_OK, sensitive);
+
+	gc = purple_account_get_connection(data->rq_data.account);
 	prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL;
 	sensitive = (prpl_info != NULL && prpl_info->roomlist_get_list != NULL);
 
-	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), 1, sensitive);
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), 1, sensitive);
 }
 
 static void
@@ -967,97 +960,6 @@
 	pidgin_blist_update_buddy(purple_get_blist(), PURPLE_BLIST_NODE(buddy), TRUE);
 }
 
-static void
-rebuild_joinchat_entries(PidginJoinChatData *data)
-{
-	PurpleConnection *gc;
-	GList *list = NULL, *tmp;
-	GHashTable *defaults = NULL;
-	struct proto_chat_entry *pce;
-	gboolean focus = TRUE;
-
-	g_return_if_fail(data->account != NULL);
-
-	gc = purple_account_get_connection(data->account);
-
-	gtk_container_foreach(GTK_CONTAINER(data->entries_box), (GtkCallback)gtk_widget_destroy, NULL);
-
-	g_list_free(data->entries);
-	data->entries = NULL;
-
-	if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL)
-		list = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc);
-
-	if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL)
-		defaults = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, NULL);
-
-	for (tmp = list; tmp; tmp = tmp->next)
-	{
-		GtkWidget *input;
-
-		pce = tmp->data;
-
-		if (pce->is_int)
-		{
-			GtkObject *adjust;
-			adjust = gtk_adjustment_new(pce->min, pce->min, pce->max,
-										1, 10, 10);
-			input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
-			gtk_widget_set_size_request(input, 50, -1);
-			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, FALSE, NULL);
-		}
-		else
-		{
-			char *value;
-			input = gtk_entry_new();
-			gtk_entry_set_activates_default(GTK_ENTRY(input), TRUE);
-			value = g_hash_table_lookup(defaults, pce->identifier);
-			if (value != NULL)
-				gtk_entry_set_text(GTK_ENTRY(input), value);
-			if (pce->secret)
-			{
-				gtk_entry_set_visibility(GTK_ENTRY(input), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
-				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
-					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
-			}
-			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL);
-			g_signal_connect(G_OBJECT(input), "changed",
-							 G_CALLBACK(joinchat_set_sensitive_if_input_cb), data);
-		}
-
-		/* Do the following for any type of input widget */
-		if (focus)
-		{
-			gtk_widget_grab_focus(input);
-			focus = FALSE;
-		}
-		g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier);
-		g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int));
-		g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required));
-		data->entries = g_list_append(data->entries, input);
-
-		g_free(pce);
-	}
-
-	g_list_free(list);
-	g_hash_table_destroy(defaults);
-
-	/* Set whether the "OK" button should be clickable initially */
-	joinchat_set_sensitive_if_input_cb(NULL, data);
-
-	gtk_widget_show_all(data->entries_box);
-}
-
-static void
-joinchat_select_account_cb(GObject *w, PurpleAccount *account,
-                           PidginJoinChatData *data)
-{
-    data->account = account;
-    rebuild_joinchat_entries(data);
-}
-
 static gboolean
 chat_account_filter_func(PurpleAccount *account)
 {
@@ -1085,32 +987,38 @@
 	return FALSE;
 }
 
-void
-pidgin_blist_joinchat_show(void)
-{
-	GtkWidget *hbox, *vbox;
+static GtkWidget *
+make_blist_request_dialog(PidginBlistRequestData *data, PurpleAccount *account,
+	const char *title, const char *window_role, const char *label_text, 
+	GCallback callback_func, PurpleFilterAccountFunc filter_func,
+	GCallback response_cb)
+{
 	GtkWidget *label;
+	GtkWidget *img;
+	GtkWidget *hbox;
+	GtkWidget *vbox;
+	GtkWindow *blist_window;
 	PidginBuddyList *gtkblist;
-	GtkWidget *img = NULL;
-	PidginJoinChatData *data = NULL;
-
-	gtkblist = purple_blist_get_ui_data();
+
+	data->account = account;
+
 	img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
-					gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
-	data = g_new0(PidginJoinChatData, 1);
-
-	data->window = gtk_dialog_new_with_buttons(_("Join a Chat"),
-		NULL, GTK_DIALOG_NO_SEPARATOR,
-		_("Room _List"), 1,
-		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-		PIDGIN_STOCK_CHAT, GTK_RESPONSE_OK, NULL);
+		gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
+
+	gtkblist = PIDGIN_BLIST(purple_get_blist());
+	blist_window = gtkblist ? GTK_WINDOW(gtkblist->window) : NULL;
+
+	data->window = gtk_dialog_new_with_buttons(title,
+		blist_window, GTK_DIALOG_NO_SEPARATOR,
+		NULL);
+
+	gtk_window_set_transient_for(GTK_WINDOW(data->window), blist_window);
 	gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
 	gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE);
 	gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE);
 	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BORDER);
-	gtk_container_set_border_width(
-		GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_window_set_role(GTK_WINDOW(data->window), "join_chat");
+	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BOX_SPACE);
+	gtk_window_set_role(GTK_WINDOW(data->window), window_role);
 
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox);
@@ -1118,37 +1026,154 @@
 	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
 
 	vbox = gtk_vbox_new(FALSE, 5);
-	gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
 	gtk_container_add(GTK_CONTAINER(hbox), vbox);
 
-	label = gtk_label_new(_("Please enter the appropriate information "
-							"about the chat you would like to join.\n"));
+	label = gtk_label_new(label_text);
+
+	gtk_widget_set_size_request(label, 400, -1);
 	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 
 	data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
-	data->account_menu = pidgin_account_option_menu_new(NULL, FALSE,
-			G_CALLBACK(joinchat_select_account_cb),
-			chat_account_filter_func, data);
-
-	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL);
-
-	data->entries_box = gtk_vbox_new(FALSE, 5);
-	gtk_container_add(GTK_CONTAINER(vbox), data->entries_box);
-	gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0);
-
-	data->account =	pidgin_account_option_menu_get_selected(data->account_menu);
-
-	rebuild_joinchat_entries(data);
-
-	g_signal_connect(G_OBJECT(data->window), "response",
-					 G_CALLBACK(do_joinchat), data);
+	data->account_menu = pidgin_account_option_menu_new(account, FALSE,
+			callback_func, filter_func, data);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_ccount"), data->sg, data->account_menu, TRUE, NULL);
+
+	data->vbox = GTK_BOX(gtk_vbox_new(FALSE, 5));
+	gtk_container_set_border_width(GTK_CONTAINER(data->vbox), 0);
+	gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(data->vbox), FALSE, FALSE, 0);
+
+	g_signal_connect(G_OBJECT(data->window), "response", response_cb, data);
 
 	g_object_unref(data->sg);
 
-	gtk_widget_show_all(data->window);
+	return vbox;
+}
+
+static void
+rebuild_chat_entries(PidginChatData *data, const char *default_chat_name)
+{
+	PurpleConnection *gc;
+	GList *list = NULL, *tmp;
+	GHashTable *defaults = NULL;
+	struct proto_chat_entry *pce;
+	gboolean focus = TRUE;
+
+	g_return_if_fail(data->rq_data.account != NULL);
+
+	gc = purple_account_get_connection(data->rq_data.account);
+
+	gtk_container_foreach(GTK_CONTAINER(data->rq_data.vbox), (GtkCallback)gtk_widget_destroy, NULL);
+
+	g_list_free(data->entries);
+	data->entries = NULL;
+
+	if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL)
+		list = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc);
+
+	if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL)
+		defaults = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, default_chat_name);
+
+	for (tmp = list; tmp; tmp = tmp->next)
+	{
+		GtkWidget *input;
+
+		pce = tmp->data;
+
+		if (pce->is_int)
+		{
+			GtkObject *adjust;
+			adjust = gtk_adjustment_new(pce->min, pce->min, pce->max,
+										1, 10, 10);
+			input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
+			gtk_widget_set_size_request(input, 50, -1);
+			pidgin_add_widget_to_vbox(GTK_BOX(data->rq_data.vbox), pce->label, data->rq_data.sg, input, FALSE, NULL);
+		}
+		else
+		{
+			char *value;
+			input = gtk_entry_new();
+			gtk_entry_set_activates_default(GTK_ENTRY(input), TRUE);
+			value = g_hash_table_lookup(defaults, pce->identifier);
+			if (value != NULL)
+				gtk_entry_set_text(GTK_ENTRY(input), value);
+			if (pce->secret)
+			{
+				gtk_entry_set_visibility(GTK_ENTRY(input), FALSE);
+#if !GTK_CHECK_VERSION(2,16,0)
+				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
+					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
+#endif /* Less than GTK+ 2.16 */
+			}
+			pidgin_add_widget_to_vbox(data->rq_data.vbox, pce->label, data->rq_data.sg, input, TRUE, NULL);
+			g_signal_connect(G_OBJECT(input), "changed",
+							 G_CALLBACK(set_sensitive_if_input_cb), data);
+		}
+
+		/* Do the following for any type of input widget */
+		if (focus)
+		{
+			gtk_widget_grab_focus(input);
+			focus = FALSE;
+		}
+		g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier);
+		g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int));
+		g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required));
+		data->entries = g_list_append(data->entries, input);
+
+		g_free(pce);
+	}
+
+	g_list_free(list);
+	g_hash_table_destroy(defaults);
+
+	/* Set whether the "OK" button should be clickable initially */
+	set_sensitive_if_input_cb(NULL, data);
+
+	gtk_widget_show_all(GTK_WIDGET(data->rq_data.vbox));
+}
+
+static void
+chat_select_account_cb(GObject *w, PurpleAccount *account,
+                       PidginChatData *data)
+{
+	if (strcmp(purple_account_get_protocol_id(data->rq_data.account),
+	           purple_account_get_protocol_id(account)) == 0)
+	{
+		data->rq_data.account = account;
+	}
+	else
+	{
+		data->rq_data.account = account;
+		rebuild_chat_entries(data, data->default_chat_name);
+	}
+}
+
+void
+pidgin_blist_joinchat_show(void)
+{
+	PidginChatData *data = NULL;
+
+	data = g_new0(PidginChatData, 1);
+
+	make_blist_request_dialog((PidginBlistRequestData *)data, NULL,
+		_("Join a Chat"), "join_chat",
+		_("Please enter the appropriate information about the chat "
+			"you would like to join.\n"),
+		G_CALLBACK(chat_select_account_cb),
+		chat_account_filter_func, (GCallback)do_joinchat);
+	gtk_dialog_add_buttons(GTK_DIALOG(data->rq_data.window),
+		_("Room _List"), 1,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		PIDGIN_STOCK_CHAT, GTK_RESPONSE_OK, NULL);
+	data->default_chat_name = NULL;
+	data->rq_data.account = pidgin_account_option_menu_get_selected(data->rq_data.account_menu);
+
+	rebuild_chat_entries(data, NULL);
+
+	gtk_widget_show_all(data->rq_data.window);
 }
 
 static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data)
@@ -6734,7 +6759,7 @@
 							PidginAddBuddyData *data)
 {
 	/* Save our account */
-	data->account = account;
+	data->rq_data.account = account;
 }
 
 static void
@@ -6747,6 +6772,7 @@
 add_buddy_cb(GtkWidget *w, int resp, PidginAddBuddyData *data)
 {
 	const char *grp, *who, *whoalias;
+	PurpleAccount *account;
 	PurpleGroup *g;
 	PurpleBuddy *b;
 	PurpleConversation *c;
@@ -6760,6 +6786,8 @@
 		if (*whoalias == '\0')
 			whoalias = NULL;
 
+		account = data->rq_data.account;
+
 		g = NULL;
 		if ((grp != NULL) && (*grp != '\0'))
 		{
@@ -6769,20 +6797,20 @@
 				purple_blist_add_group(g, NULL);
 			}
 
-			b = purple_find_buddy_in_group(data->account, who, g);
+			b = purple_find_buddy_in_group(account, who, g);
 		}
-		else if ((b = purple_find_buddy(data->account, who)) != NULL)
+		else if ((b = purple_find_buddy(account, who)) != NULL)
 		{
 			g = purple_buddy_get_group(b);
 		}
 
 		if (b == NULL)
 		{
-			b = purple_buddy_new(data->account, who, whoalias);
+			b = purple_buddy_new(account, who, whoalias);
 			purple_blist_add_buddy(b, NULL, g, NULL);
 		}
 
-		purple_account_add_buddy(data->account, b);
+		purple_account_add_buddy(account, b);
 
 		/* Offer to merge people with the same alias. */
 		if (whoalias != NULL && g != NULL)
@@ -6803,7 +6831,7 @@
 		 * Or something.  --Mark
 		 */
 
-		c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, data->account);
+		c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, data->rq_data.account);
 		if (c != NULL) {
 			icon = purple_conv_im_get_icon(PURPLE_CONV_IM(c));
 			if (icon != NULL)
@@ -6811,103 +6839,52 @@
 		}
 	}
 
-	gtk_widget_destroy(data->window);
+	gtk_widget_destroy(data->rq_data.window);
 }
 
 static void
 pidgin_blist_request_add_buddy(PurpleAccount *account, const char *username,
 								 const char *group, const char *alias)
 {
-	GtkWidget *table;
-	GtkWidget *label;
-	GtkWidget *hbox;
-	GtkWidget *vbox;
-	GtkWidget *img;
-	PidginBuddyList *gtkblist;
 	PidginAddBuddyData *data = g_new0(PidginAddBuddyData, 1);
 
-	data->account =
+	make_blist_request_dialog((PidginBlistRequestData *)data,
 		(account != NULL
-		 ? account
-		 : purple_connection_get_account(purple_connections_get_all()->data));
-
-	img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
-					gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
-
-	gtkblist = PIDGIN_BLIST(purple_get_blist());
-
-	data->window = gtk_dialog_new_with_buttons(_("Add Buddy"),
-			gtkblist ? GTK_WINDOW(gtkblist->window) : NULL, GTK_DIALOG_NO_SEPARATOR,
+			? account : purple_connection_get_account(purple_connections_get_all()->data)),
+		_("Add Buddy"), "add_buddy",
+		_("Add a buddy.\n"),
+		G_CALLBACK(add_buddy_select_account_cb), NULL,
+		G_CALLBACK(add_buddy_cb));
+	gtk_dialog_add_buttons(GTK_DIALOG(data->rq_data.window),
 			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 			GTK_STOCK_ADD, GTK_RESPONSE_OK,
 			NULL);
 
-	if (gtkblist)
-		gtk_window_set_transient_for(GTK_WINDOW(data->window), GTK_WINDOW(gtkblist->window));
-	gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
-	gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE);
-	gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE);
-	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BORDER);
-	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_window_set_role(GTK_WINDOW(data->window), "add_buddy");
-	gtk_window_set_type_hint(GTK_WINDOW(data->window),
-							 GDK_WINDOW_TYPE_HINT_DIALOG);
-
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox);
-	gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
-	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
-
-	vbox = gtk_vbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(hbox), vbox);
-
-	g_signal_connect(G_OBJECT(data->window), "destroy",
+	g_signal_connect(G_OBJECT(data->rq_data.window), "destroy",
 	                 G_CALLBACK(destroy_add_buddy_dialog_cb), data);
 
-	label = gtk_label_new(_("Add a buddy.\n"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-
-	table = gtk_table_new(4, 2, FALSE);
-	gtk_table_set_row_spacings(GTK_TABLE(table), 5);
-	gtk_table_set_col_spacings(GTK_TABLE(table), 5);
-	gtk_container_set_border_width(GTK_CONTAINER(table), 0);
-	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
-
-	data->account_box = pidgin_account_option_menu_new(account, FALSE,
-			G_CALLBACK(add_buddy_select_account_cb), NULL, data);
-
-	gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 0, 2, 0, 1);
-
-	label = gtk_label_new_with_mnemonic(_("Buddy's _username:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
-
 	data->entry = gtk_entry_new();
-	gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 1, 2);
+
+	pidgin_add_widget_to_vbox(data->rq_data.vbox, _("Buddy's _username:"),
+		data->rq_data.sg, data->entry, TRUE, NULL);
 	gtk_widget_grab_focus(data->entry);
 
 	if (username != NULL)
 		gtk_entry_set_text(GTK_ENTRY(data->entry), username);
 	else
-		gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window),
+		gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window),
 		                                  GTK_RESPONSE_OK, FALSE);
 
 	gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->entry);
-	pidgin_set_accessible_label (data->entry, label);
 
 	g_signal_connect(G_OBJECT(data->entry), "changed",
 	                 G_CALLBACK(pidgin_set_sensitive_if_input),
-	                 data->window);
-
-	label = gtk_label_new_with_mnemonic(_("(Optional) A_lias:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
+	                 data->rq_data.window);
 
 	data->entry_for_alias = gtk_entry_new();
-	gtk_table_attach_defaults(GTK_TABLE(table),
-	                          data->entry_for_alias, 1, 2, 2, 3);
+	pidgin_add_widget_to_vbox(data->rq_data.vbox, _("(Optional) A_lias:"),
+	                          data->rq_data.sg, data->entry_for_alias, TRUE,
+	                          NULL);
 
 	if (alias != NULL)
 		gtk_entry_set_text(GTK_ENTRY(data->entry_for_alias), alias);
@@ -6915,23 +6892,11 @@
 	if (username != NULL)
 		gtk_widget_grab_focus(GTK_WIDGET(data->entry_for_alias));
 
-	gtk_entry_set_activates_default (GTK_ENTRY(data->entry_for_alias), TRUE);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->entry_for_alias);
-	pidgin_set_accessible_label (data->entry_for_alias, label);
-
-	label = gtk_label_new_with_mnemonic(_("Add buddy to _group:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
-
 	data->combo = pidgin_text_combo_box_entry_new(group, groups_tree());
-	gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 3, 4);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_BIN(data->combo)->child);
-	pidgin_set_accessible_label (data->combo, label);
-
-	g_signal_connect(G_OBJECT(data->window), "response",
-					 G_CALLBACK(add_buddy_cb), data);
-
-	gtk_widget_show_all(data->window);
+	pidgin_add_widget_to_vbox(data->rq_data.vbox, _("Add buddy to _group:"),
+	                          data->rq_data.sg, data->combo, TRUE, NULL);
+
+	gtk_widget_show_all(data->rq_data.window);
 }
 
 static void
@@ -6947,7 +6912,7 @@
 	components = g_hash_table_new_full(g_str_hash, g_str_equal,
 									   g_free, g_free);
 
-	for (tmp = data->entries; tmp; tmp = tmp->next)
+	for (tmp = data->chat_data.entries; tmp; tmp = tmp->next)
 	{
 		if (g_object_get_data(tmp->data, "is_spin"))
 		{
@@ -6966,7 +6931,7 @@
 		}
 	}
 
-	chat = purple_chat_new(data->account,
+	chat = purple_chat_new(data->chat_data.rq_data.account,
 							   gtk_entry_get_text(GTK_ENTRY(data->alias_entry)),
 							   components);
 
@@ -6990,9 +6955,9 @@
 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->persistent)))
 		purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent", TRUE);
 
-	gtk_widget_destroy(data->window);
-	g_free(data->default_chat_name);
-	g_list_free(data->entries);
+	gtk_widget_destroy(data->chat_data.rq_data.window);
+	g_free(data->chat_data.default_chat_name);
+	g_list_free(data->chat_data.entries);
 	g_free(data);
 }
 
@@ -7005,166 +6970,25 @@
 	}
 	else if (resp == 1)
 	{
-		pidgin_roomlist_dialog_show_with_account(data->account);
+		pidgin_roomlist_dialog_show_with_account(data->chat_data.rq_data.account);
 	}
 	else
 	{
-		gtk_widget_destroy(data->window);
-		g_free(data->default_chat_name);
-		g_list_free(data->entries);
+		gtk_widget_destroy(data->chat_data.rq_data.window);
+		g_free(data->chat_data.default_chat_name);
+		g_list_free(data->chat_data.entries);
 		g_free(data);
 	}
 }
 
-/*
- * Check the values of all the text entry boxes.  If any required input
- * strings are empty then don't allow the user to click on "OK."
- */
-static void
-addchat_set_sensitive_if_input_cb(GtkWidget *entry, gpointer user_data)
-{
-	PurplePluginProtocolInfo *prpl_info;
-	PurpleConnection *gc;
-	PidginAddChatData *data;
-	GList *tmp;
-	const char *text;
-	gboolean required;
-	gboolean sensitive = TRUE;
-
-	data = user_data;
-
-	for (tmp = data->entries; tmp != NULL; tmp = tmp->next)
-	{
-		if (!g_object_get_data(tmp->data, "is_spin"))
-		{
-			required = GPOINTER_TO_INT(g_object_get_data(tmp->data, "required"));
-			text = gtk_entry_get_text(tmp->data);
-			if (required && (*text == '\0'))
-				sensitive = FALSE;
-		}
-	}
-
-	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), GTK_RESPONSE_OK, sensitive);
-
-	gc = purple_account_get_connection(data->account);
-	prpl_info = (gc != NULL) ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL;
-	sensitive = (prpl_info != NULL && prpl_info->roomlist_get_list != NULL);
-
-	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->window), 1, sensitive);
-}
-
-static void
-rebuild_addchat_entries(PidginAddChatData *data)
-{
-	PurpleConnection *gc;
-	GList *list = NULL, *tmp;
-	GHashTable *defaults = NULL;
-	struct proto_chat_entry *pce;
-	gboolean focus = TRUE;
-
-	g_return_if_fail(data->account != NULL);
-
-	gc = purple_account_get_connection(data->account);
-
-	gtk_container_foreach(GTK_CONTAINER(data->entries_box), (GtkCallback)gtk_widget_destroy, NULL);
-
-	g_list_free(data->entries);
-
-	data->entries = NULL;
-
-	if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info != NULL)
-		list = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info(gc);
-
-	if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL)
-		defaults = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, data->default_chat_name);
-
-	for (tmp = list; tmp; tmp = tmp->next)
-	{
-		GtkWidget *input;
-
-		pce = tmp->data;
-
-		if (pce->is_int)
-		{
-			GtkObject *adjust;
-			adjust = gtk_adjustment_new(pce->min, pce->min, pce->max,
-										1, 10, 10);
-			input = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0);
-			gtk_widget_set_size_request(input, 50, -1);
-			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, FALSE, NULL);
-		}
-		else
-		{
-			char *value;
-			input = gtk_entry_new();
-			gtk_entry_set_activates_default(GTK_ENTRY(input), TRUE);
-			value = g_hash_table_lookup(defaults, pce->identifier);
-			if (value != NULL)
-				gtk_entry_set_text(GTK_ENTRY(input), value);
-			if (pce->secret)
-			{
-				gtk_entry_set_visibility(GTK_ENTRY(input), FALSE);
-#if !GTK_CHECK_VERSION(2,16,0)
-				if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*')
-					gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR);
-#endif /* Less than GTK+ 2.16 */
-			}
-			pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL);
-			g_signal_connect(G_OBJECT(input), "changed",
-							 G_CALLBACK(addchat_set_sensitive_if_input_cb), data);
-		}
-
-		/* Do the following for any type of input widget */
-		if (focus)
-		{
-			gtk_widget_grab_focus(input);
-			focus = FALSE;
-		}
-		g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier);
-		g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int));
-		g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required));
-		data->entries = g_list_append(data->entries, input);
-
-		g_free(pce);
-	}
-
-	g_list_free(list);
-	g_hash_table_destroy(defaults);
-
-	/* Set whether the "OK" button should be clickable initially */
-	addchat_set_sensitive_if_input_cb(NULL, data);
-
-	gtk_widget_show_all(data->entries_box);
-}
-
-static void
-addchat_select_account_cb(GObject *w, PurpleAccount *account,
-						   PidginAddChatData *data)
-{
-	if (strcmp(purple_account_get_protocol_id(data->account),
-		purple_account_get_protocol_id(account)) == 0)
-	{
-		data->account = account;
-	}
-	else
-	{
-		data->account = account;
-		rebuild_addchat_entries(data);
-	}
-}
-
 static void
 pidgin_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
 								const char *alias, const char *name)
 {
 	PidginAddChatData *data;
-	PidginBuddyList *gtkblist;
 	GList *l;
 	PurpleConnection *gc;
-	GtkWidget *label;
-	GtkWidget *hbox;
-	GtkWidget *vbox;
-	GtkWidget *img;
+	GtkBox *vbox;
 
 	if (account != NULL) {
 		gc = purple_account_get_connection(account);
@@ -7193,82 +7017,44 @@
 	}
 
 	data = g_new0(PidginAddChatData, 1);
-	data->account = account;
-	data->default_chat_name = g_strdup(name);
-
-	img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
-					gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
-
-	gtkblist = PIDGIN_BLIST(purple_get_blist());
-
-	data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
-
-	data->window = gtk_dialog_new_with_buttons(_("Add Chat"),
-		gtkblist ? GTK_WINDOW(gtkblist->window) : NULL, GTK_DIALOG_NO_SEPARATOR,
-		_("Room _List"), 1,
+	vbox = GTK_BOX(make_blist_request_dialog((PidginBlistRequestData *)data, account,
+			_("Add Chat"), "add_chat",
+			_("Please enter an alias, and the appropriate information "
+			  "about the chat you would like to add to your buddy list.\n"),
+			G_CALLBACK(chat_select_account_cb), chat_account_filter_func,
+			G_CALLBACK(add_chat_resp_cb)));
+	gtk_dialog_add_buttons(GTK_DIALOG(data->chat_data.rq_data.window),
+		_("Room List"), 1,
 		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 		GTK_STOCK_ADD, GTK_RESPONSE_OK,
 		NULL);
 
-	if (gtkblist)
-		gtk_window_set_transient_for(GTK_WINDOW(data->window), GTK_WINDOW(gtkblist->window));
-	gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
-	gtk_container_set_border_width(GTK_CONTAINER(data->window), PIDGIN_HIG_BOX_SPACE);
-	gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE);
-	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BORDER);
-	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), PIDGIN_HIG_BOX_SPACE);
-	gtk_window_set_role(GTK_WINDOW(data->window), "add_chat");
-	gtk_window_set_type_hint(GTK_WINDOW(data->window),
-							 GDK_WINDOW_TYPE_HINT_DIALOG);
-
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(data->window)->vbox), hbox);
-	gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
-	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
-
-	vbox = gtk_vbox_new(FALSE, 5);
-	gtk_container_add(GTK_CONTAINER(hbox), vbox);
-
-	label = gtk_label_new(
-		_("Please enter an alias, and the appropriate information "
-		  "about the chat you would like to add to your buddy list.\n"));
-	gtk_widget_set_size_request(label, 400, -1);
-	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-
-	data->account_menu = pidgin_account_option_menu_new(account, FALSE,
-			G_CALLBACK(addchat_select_account_cb),
-			chat_account_filter_func, data);
-	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL);
-
-	data->entries_box = gtk_vbox_new(FALSE, 5);
-	gtk_container_set_border_width(GTK_CONTAINER(data->entries_box), 0);
-	gtk_box_pack_start(GTK_BOX(vbox), data->entries_box, TRUE, TRUE, 0);
-
-	rebuild_addchat_entries(data);
+	data->chat_data.default_chat_name = g_strdup(name);
+
+	rebuild_chat_entries((PidginChatData *)data, name);
 
 	data->alias_entry = gtk_entry_new();
 	if (alias != NULL)
 		gtk_entry_set_text(GTK_ENTRY(data->alias_entry), alias);
 	gtk_entry_set_activates_default(GTK_ENTRY(data->alias_entry), TRUE);
 
-	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_lias:"), data->sg, data->alias_entry, TRUE, NULL);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_lias:"),
+	                          data->chat_data.rq_data.sg, data->alias_entry,
+	                          TRUE, NULL);
 	if (name != NULL)
 		gtk_widget_grab_focus(data->alias_entry);
 
 	data->group_combo = pidgin_text_combo_box_entry_new(group ? group->name : NULL, groups_tree());
-	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Group:"), data->sg, data->group_combo, TRUE, NULL);
+	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Group:"),
+	                          data->chat_data.rq_data.sg, data->group_combo,
+	                          TRUE, NULL);
 
 	data->autojoin = gtk_check_button_new_with_mnemonic(_("Auto_join when account becomes online."));
 	data->persistent = gtk_check_button_new_with_mnemonic(_("_Remain in chat after window is closed."));
 	gtk_box_pack_start(GTK_BOX(vbox), data->autojoin, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), data->persistent, FALSE, FALSE, 0);
 
-	g_signal_connect(G_OBJECT(data->window), "response",
-					 G_CALLBACK(add_chat_resp_cb), data);
-
-	gtk_widget_show_all(data->window);
+	gtk_widget_show_all(data->chat_data.rq_data.window);
 }
 
 static void
@@ -8195,4 +7981,3 @@
 	if (activeitem)
 		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(activeitem), TRUE);
 }
-
--- a/pidgin/gtkconv.c	Thu May 14 02:12:40 2009 +0000
+++ b/pidgin/gtkconv.c	Sun May 17 01:55:15 2009 +0000
@@ -6309,6 +6309,10 @@
 	if (!smiley)
 		return;
 
+	smiley->data = g_realloc(smiley->data, smiley->datasize + size);
+	g_memmove(smiley->data + smiley->datasize, data, size);
+	smiley->datasize += size;
+
 	loader = smiley->loader;
 	if (!loader)
 		return;
--- a/pidgin/gtkimhtml.c	Thu May 14 02:12:40 2009 +0000
+++ b/pidgin/gtkimhtml.c	Sun May 17 01:55:15 2009 +0000
@@ -82,6 +82,11 @@
 	GtkTextMark *mark;
 };
 
+typedef struct {
+	GtkIMHtmlScalable *image;
+	gpointer data;
+	gsize datasize;
+} GtkIMHtmlImageSave;
 
 struct im_image_data {
 	int id;
@@ -3641,96 +3646,109 @@
 }
 
 static void
-image_save_yes_cb(GtkIMHtmlImage *image, const char *filename)
-{
-	gchar *type = NULL;
+image_save_yes_cb(GtkIMHtmlImageSave *save, const char *filename)
+{
 	GError *error = NULL;
-#if GTK_CHECK_VERSION(2,2,0)
-	GSList *formats = gdk_pixbuf_get_formats();
-#else
-	char *basename = g_path_get_basename(filename);
-	char *ext = strrchr(basename, '.');
-#endif
-	char *newfilename;
+	GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
 
 	gtk_widget_destroy(image->filesel);
 	image->filesel = NULL;
 
+	if (save->data && save->datasize) {
+#if GLIB_CHECK_VERSION(2,8,0)
+		g_file_set_contents(filename, save->data, save->datasize, &error);
+#else
+		purple_util_write_data_to_file_absolute(filename, save->data, save->datasize);
+#endif
+	} else {
+		gchar *type = NULL;
 #if GTK_CHECK_VERSION(2,2,0)
-	while (formats) {
-		GdkPixbufFormat *format = formats->data;
-		gchar **extensions = gdk_pixbuf_format_get_extensions(format);
-		gpointer p = extensions;
-
-		while(gdk_pixbuf_format_is_writable(format) && extensions && extensions[0]){
-			gchar *fmt_ext = extensions[0];
-			const gchar* file_ext = filename + strlen(filename) - strlen(fmt_ext);
-
-			if(!g_ascii_strcasecmp(fmt_ext, file_ext)){
-				type = gdk_pixbuf_format_get_name(format);
+		GSList *formats = gdk_pixbuf_get_formats();
+#else
+		char *basename = g_path_get_basename(filename);
+		char *ext = strrchr(basename, '.');
+#endif
+		char *newfilename;
+
+#if GTK_CHECK_VERSION(2,2,0)
+		while (formats) {
+			GdkPixbufFormat *format = formats->data;
+			gchar **extensions = gdk_pixbuf_format_get_extensions(format);
+			gpointer p = extensions;
+
+			while(gdk_pixbuf_format_is_writable(format) && extensions && extensions[0]){
+				gchar *fmt_ext = extensions[0];
+				const gchar* file_ext = filename + strlen(filename) - strlen(fmt_ext);
+
+				if(!g_ascii_strcasecmp(fmt_ext, file_ext)){
+					type = gdk_pixbuf_format_get_name(format);
+					break;
+				}
+
+				extensions++;
+			}
+
+			g_strfreev(p);
+
+			if (type)
 				break;
-			}
-
-			extensions++;
+
+			formats = formats->next;
 		}
 
-		g_strfreev(p);
-
-		if (type)
-			break;
-
-		formats = formats->next;
-	}
-
-	g_slist_free(formats);
+		g_slist_free(formats);
+#else
+		/* this is really ugly code, but I think it will work */
+		if (ext) {
+			ext++;
+			if (!g_ascii_strcasecmp(ext, "jpeg") || !g_ascii_strcasecmp(ext, "jpg"))
+				type = g_strdup("jpeg");
+			else if (!g_ascii_strcasecmp(ext, "png"))
+				type = g_strdup("png");
+		}
+
+		g_free(basename);
+#endif
+
+		/* If I can't find a valid type, I will just tell the user about it and then assume
+		   it's a png */
+		if (!type){
+			char *basename, *tmp;
+			char *dirname;
+#if GTK_CHECK_VERSION(2,4,0)
+			GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+							_("<span size='larger' weight='bold'>Unrecognized file type</span>\n\nDefaulting to PNG."));
 #else
-	/* this is really ugly code, but I think it will work */
-	if (ext) {
-		ext++;
-		if (!g_ascii_strcasecmp(ext, "jpeg") || !g_ascii_strcasecmp(ext, "jpg"))
-			type = g_strdup("jpeg");
-		else if (!g_ascii_strcasecmp(ext, "png"))
+			GtkWidget *dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+							_("Unrecognized file type\n\nDefaulting to PNG."));
+#endif
+
+			g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
+			gtk_widget_show(dialog);
+
 			type = g_strdup("png");
+			dirname = g_path_get_dirname(filename);
+			basename = g_path_get_basename(filename);
+			tmp = strrchr(basename, '.');
+			if (tmp != NULL)
+				tmp[0] = '\0';
+			newfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S  "%s.png", dirname, basename);
+			g_free(dirname);
+			g_free(basename);
+		} else {
+			/*
+			 * We're able to save the file in it's original format, so we
+			 * can use the original file name.
+			 */
+			newfilename = g_strdup(filename);
+		}
+
+		gdk_pixbuf_save(image->pixbuf, newfilename, type, &error, NULL);
+
+		g_free(newfilename);
+		g_free(type);
 	}
 
-	g_free(basename);
-#endif
-
-	/* If I can't find a valid type, I will just tell the user about it and then assume
-	   it's a png */
-	if (!type){
-		char *basename, *tmp;
-#if GTK_CHECK_VERSION(2,4,0)
-		GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
-						_("<span size='larger' weight='bold'>Unrecognized file type</span>\n\nDefaulting to PNG."));
-#else
-		GtkWidget *dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
-						_("Unrecognized file type\n\nDefaulting to PNG."));
-#endif
-
-		g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
-		gtk_widget_show(dialog);
-
-		type = g_strdup("png");
-		basename = g_path_get_basename(filename);
-		tmp = strrchr(basename, '.');
-		if (tmp != NULL)
-			tmp[0] = '\0';
-		newfilename = g_strdup_printf("%s.png", basename);
-		g_free(basename);
-	} else {
-		/*
-		 * We're able to save the file in it's original format, so we
-		 * can use the original file name.
-		 */
-		newfilename = g_strdup(filename);
-	}
-
-	gdk_pixbuf_save(image->pixbuf, newfilename, type, &error, NULL);
-
-	g_free(newfilename);
-	g_free(type);
-
 	if (error){
 #if GTK_CHECK_VERSION(2,4,0)
 		GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
@@ -3747,9 +3765,10 @@
 
 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 static void
-image_save_check_if_exists_cb(GtkWidget *widget, gint response, GtkIMHtmlImage *image)
+image_save_check_if_exists_cb(GtkWidget *widget, gint response, GtkIMHtmlImageSave *save)
 {
 	gchar *filename;
+	GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
 
 	if (response != GTK_RESPONSE_ACCEPT) {
 		gtk_widget_destroy(widget);
@@ -3760,9 +3779,10 @@
 	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
 #else /* FILECHOOSER */
 static void
-image_save_check_if_exists_cb(GtkWidget *button, GtkIMHtmlImage *image)
+image_save_check_if_exists_cb(GtkWidget *button, GtkIMHtmlImageSave *save)
 {
 	gchar *filename;
+	GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
 
 	filename = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(image->filesel)));
 
@@ -3795,7 +3815,7 @@
 		image_save_yes_cb(image, filename);
 	*/
 
-	image_save_yes_cb(image, filename);
+	image_save_yes_cb(save, filename);
 
 	g_free(filename);
 }
@@ -3810,8 +3830,10 @@
 #endif /* FILECHOOSER */
 
 static void
-gtk_imhtml_image_save(GtkWidget *w, GtkIMHtmlImage *image)
-{
+gtk_imhtml_image_save(GtkWidget *w, GtkIMHtmlImageSave *save)
+{
+	GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
+
 	if (image->filesel != NULL) {
 		gtk_window_present(GTK_WINDOW(image->filesel));
 		return;
@@ -3828,7 +3850,7 @@
 	if (image->filename != NULL)
 		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(image->filesel), image->filename);
 	g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(image->filesel)), "response",
-					 G_CALLBACK(image_save_check_if_exists_cb), image);
+					 G_CALLBACK(image_save_check_if_exists_cb), save);
 #else /* FILECHOOSER */
 	image->filesel = gtk_file_selection_new(_("Save Image"));
 	if (image->filename != NULL)
@@ -3838,7 +3860,7 @@
 	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(image->filesel)->cancel_button),
 							 "clicked", G_CALLBACK(image_save_cancel_cb), image);
 	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(image->filesel)->ok_button), "clicked",
-					 G_CALLBACK(image_save_check_if_exists_cb), image);
+					 G_CALLBACK(image_save_check_if_exists_cb), save);
 #endif /* FILECHOOSER */
 
 	gtk_widget_show(image->filesel);
@@ -3860,30 +3882,29 @@
  * embedded in the conversation.  Someone should make the Purple core handle
  * all of that.
  */
-static gboolean gtk_imhtml_image_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlImage *image)
+static gboolean gtk_imhtml_image_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlImageSave *save)
 {
 	GdkEventButton *event_button = (GdkEventButton *) event;
+	GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
 
 	if (event->type == GDK_BUTTON_RELEASE) {
 		if(event_button->button == 3) {
 			GtkWidget *img, *item, *menu;
-			gchar *text = g_strdup_printf(_("_Save Image..."));
 			menu = gtk_menu_new();
 
 			/* buttons and such */
 			img = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU);
-			item = gtk_image_menu_item_new_with_mnemonic(text);
+			item = gtk_image_menu_item_new_with_mnemonic(_("_Save Image..."));
 			gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
-			g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_image_save), image);
+			g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_image_save), save);
 			gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 
 			/* Add menu item for adding custom smiley to local smileys */
 			/* we only add the menu if the image is of "custom smiley size"
 			  <= 96x96 pixels */
 			if (image->width <= 96 && image->height <= 96) {
-				text = g_strdup_printf(_("_Add Custom Smiley..."));
 				img = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
-				item = gtk_image_menu_item_new_with_mnemonic(text);
+				item = gtk_image_menu_item_new_with_mnemonic(_("_Add Custom Smiley..."));
 				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
 				g_signal_connect(G_OBJECT(item), "activate",
 								 G_CALLBACK(gtk_imhtml_custom_smiley_save), image);
@@ -3894,7 +3915,6 @@
 			gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
 							event_button->button, event_button->time);
 
-			g_free(text);
 			return TRUE;
 		}
 	}
@@ -3909,7 +3929,7 @@
 static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley)
 {
 	GdkPixbufAnimation *anim = NULL;
-	GtkIMHtmlScalable *image = NULL;
+	GtkIMHtmlImageSave *save = NULL;
 	gboolean ret;
 
 	if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3)
@@ -3919,9 +3939,14 @@
 	if (!anim)
 		return FALSE;
 
-	image = gtk_imhtml_animation_new(anim, smiley->smile, 0);
-	ret = gtk_imhtml_image_clicked(w, event, (GtkIMHtmlImage*)image);
-	g_object_set_data_full(G_OBJECT(w), "image-data", image, (GDestroyNotify)gtk_imhtml_animation_free);
+	save = g_new0(GtkIMHtmlImageSave, 1);
+	save->image = (GtkIMHtmlScalable *)gtk_imhtml_animation_new(anim, smiley->smile, 0);
+	save->data = smiley->data;        /* Do not need to memdup here, since the smiley is not
+	                                     destroyed before this GtkIMHtmlImageSave */
+	save->datasize = smiley->datasize;
+	ret = gtk_imhtml_image_clicked(w, event, save);
+	g_object_set_data_full(G_OBJECT(w), "image-data", save->image, (GDestroyNotify)gtk_imhtml_animation_free);
+	g_object_set_data_full(G_OBJECT(w), "image-save-data", save, (GDestroyNotify)g_free);
 	return ret;
 }
 
@@ -3955,6 +3980,7 @@
 	GtkWidget *box = gtk_event_box_new();
 	char *tag;
 	GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
+	GtkIMHtmlImageSave *save;
 
 	gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(image->image));
 
@@ -3969,7 +3995,11 @@
 	g_object_set_data(G_OBJECT(anchor), "gtkimhtml_plaintext", "[Image]");
 
 	gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), box, anchor);
-	g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gtk_imhtml_image_clicked), image);
+
+	save = g_new0(GtkIMHtmlImageSave, 1);
+	save->image = scale;
+	g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gtk_imhtml_image_clicked), save);
+	g_object_set_data_full(G_OBJECT(box), "image-save-data", save, (GDestroyNotify)g_free);
 }
 
 GtkIMHtmlScalable *gtk_imhtml_hr_new()
@@ -5773,6 +5803,7 @@
 		g_object_unref(smiley->icon);
 	if (smiley->loader)
 		g_object_unref(smiley->loader);
+	g_free(smiley->data);
 	g_free(smiley);
 }
 
@@ -5887,13 +5918,25 @@
 
 void gtk_imhtml_set_populate_primary_clipboard(GtkIMHtml *imhtml, gboolean populate)
 {
+	gulong signal_id;
+	signal_id = g_signal_handler_find(imhtml->text_buffer,
+			G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_UNBLOCKED, 0, 0, NULL,
+			mark_set_so_update_selection_cb, NULL);
 	if (populate) {
-		g_signal_handlers_unblock_matched(imhtml->text_buffer,
-				G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
-				mark_set_so_update_selection_cb, NULL);
+		if (!signal_id) {
+			/* We didn't find an unblocked signal handler, which means there
+			   is a blocked handler. Now unblock it.
+			   This is necessary to avoid a mutex-lock when the debug message
+			   saying 'no handler is blocked' is printed in the debug window.
+				-- sad
+			 */
+			g_signal_handlers_unblock_matched(imhtml->text_buffer,
+					G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+					mark_set_so_update_selection_cb, NULL);
+		}
 	} else {
-		g_signal_handlers_block_matched(imhtml->text_buffer,
-				G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
-				mark_set_so_update_selection_cb, NULL);
+		/* Block only if we found an unblocked handler */
+		if (signal_id)
+			g_signal_handler_block(imhtml->text_buffer, signal_id);
 	}
 }
--- a/pidgin/gtkimhtml.h	Thu May 14 02:12:40 2009 +0000
+++ b/pidgin/gtkimhtml.h	Sun May 17 01:55:15 2009 +0000
@@ -188,6 +188,8 @@
 	GSList *anchors;
 	GtkIMHtmlSmileyFlags flags;
 	GtkIMHtml *imhtml;
+	gpointer data;       /** @Since 2.6.0 */
+	gsize datasize;      /** @Since 2.6.0 */
 };
 
 struct _GtkIMHtmlScalable {
--- a/pidgin/gtkstatusbox.c	Thu May 14 02:12:40 2009 +0000
+++ b/pidgin/gtkstatusbox.c	Sun May 17 01:55:15 2009 +0000
@@ -93,6 +93,7 @@
 static void do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift);
 static void icon_choose_cb(const char *filename, gpointer data);
 static void remove_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box);
+static void choose_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box);
 
 enum {
 	/** A PidginStatusBoxItemType */
@@ -317,6 +318,10 @@
 
 		box->icon_box_menu = gtk_menu_new();
 
+		menu_item = pidgin_new_item_from_stock(box->icon_box_menu, _("Select Buddy Icon"), GTK_STOCK_ADD,
+						     G_CALLBACK(choose_buddy_icon_cb),
+						     box, 0, 0, NULL);
+
 		menu_item = pidgin_new_item_from_stock(box->icon_box_menu, _("Remove"), GTK_STOCK_REMOVE,
 						     G_CALLBACK(remove_buddy_icon_cb),
 						     box, 0, 0, NULL);
@@ -327,13 +332,7 @@
 			       event->button, event->time);
 
 	} else {
-		if (box->buddy_icon_sel) {
-			gtk_window_present(GTK_WINDOW(box->buddy_icon_sel));
-			return FALSE;
-		}
-
-		box->buddy_icon_sel = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)), icon_choose_cb, box);
-		gtk_widget_show_all(box->buddy_icon_sel);
+		choose_buddy_icon_cb(widget, box);
 	}
 	return FALSE;
 }
@@ -1529,6 +1528,17 @@
 }
 
 static void
+choose_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box)
+{
+	if (box->buddy_icon_sel) {
+		gtk_window_present(GTK_WINDOW(box->buddy_icon_sel));
+	} else {
+		box->buddy_icon_sel = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(w)), icon_choose_cb, box);
+		gtk_widget_show_all(box->buddy_icon_sel);
+	}
+}
+
+static void
 icon_choose_cb(const char *filename, gpointer data)
 {
 	PidginStatusBox *box = data;
--- a/po/de.po	Thu May 14 02:12:40 2009 +0000
+++ b/po/de.po	Sun May 17 01:55:15 2009 +0000
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-04-30 10:37-0400\n"
-"PO-Revision-Date: 2009-04-26 12:11+0200\n"
+"POT-Creation-Date: 2009-05-15 19:56+0200\n"
+"PO-Revision-Date: 2009-05-15 19:56+0200\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -1562,7 +1562,6 @@
 msgid "Lastlog plugin."
 msgstr "Verlauf-Plugin."
 
-#, c-format
 msgid ""
 "\n"
 "Fetching TinyURL..."
@@ -1890,7 +1889,6 @@
 "Fehler beim Lesen vom Auflösungsprozess:\n"
 "%s"
 
-#, c-format
 msgid "Resolver process exited without answering our request"
 msgstr "Auflösungsprozess hat sich beendet ohne die Anfrage zu beantworten"
 
@@ -3760,11 +3758,8 @@
 msgid "Unsupported version of BOSH protocol"
 msgstr "Nicht-unterstützte Version"
 
-#, fuzzy
 msgid "Unable to establish a connection with the server"
-msgstr ""
-"Die Verbindung mit dem Server konnte nicht hergestellt werden:\n"
-"%s"
+msgstr "Die Verbindung mit dem Server konnte nicht hergestellt werden"
 
 #, c-format
 msgid ""
@@ -3774,9 +3769,8 @@
 "Die Verbindung mit dem Server konnte nicht hergestellt werden:\n"
 "%s"
 
-#, fuzzy
 msgid "Unable to establish SSL connection"
-msgstr "Kann Verbindung nicht erstellen"
+msgstr "Kann SSL-Verbindung nicht erstellen"
 
 msgid "Unable to create socket"
 msgstr "Kann Socket nicht erstellen"
@@ -4012,6 +4006,14 @@
 msgid "Resource"
 msgstr "Ressource"
 
+#, fuzzy, c-format
+msgid "%s ago"
+msgstr "vor %s"
+
+#, fuzzy
+msgid "Logged off"
+msgstr "Abgemeldet"
+
 msgid "Middle Name"
 msgstr "Zweiter Name"
 
@@ -4181,6 +4183,17 @@
 msgid "Find Rooms"
 msgstr "Finde Räume"
 
+#, fuzzy
+msgid "Affiliations:"
+msgstr "Alias:"
+
+msgid "No users found"
+msgstr "Keine Benutzer gefunden"
+
+#, fuzzy
+msgid "Roles:"
+msgstr "Funktion"
+
 msgid "You require encryption, but it is not available on this server."
 msgstr ""
 "Sie fordern Verschlüsselung, aber diese ist auf dem Server nicht verfügbar."
@@ -4191,7 +4204,6 @@
 msgid "Read Error"
 msgstr "Fehler beim Lesen"
 
-#, c-format
 msgid ""
 "Could not find alternative XMPP connection methods after failing to connect "
 "directly.\n"
@@ -4645,16 +4657,18 @@
 msgid "ban &lt;user&gt; [reason]:  Ban a user from the room."
 msgstr "ban &lt;Benutzer&gt; [Grund]:  Verbanne einen Benutzer aus dem Raum."
 
-msgid ""
-"affiliate &lt;user&gt; &lt;owner|admin|member|outcast|none&gt;: Set a user's "
-"affiliation with the room."
+#, fuzzy
+msgid ""
+"affiliate &lt;owner|admin|member|outcast|none&gt; [nick1] [nick2] ...: Get "
+"the users with an affiliation or set users' affiliation with the room."
 msgstr ""
 "affiliate &lt;Benutzer&gt; &lt;owner|admin|member|outcast|none&gt;: Setze "
 "eine Benutzerzugehörigkeit für den Raum."
 
-msgid ""
-"role &lt;user&gt; &lt;moderator|participant|visitor|none&gt;: Set a user's "
-"role in the room."
+#, fuzzy
+msgid ""
+"role &lt;moderator|participant|visitor|none&gt; [nick1] [nick2] ...: Get the "
+"users with an role or set users' role with the room."
 msgstr ""
 "role &lt;Benutzer&gt; &lt;moderator|participant|visitor|none&gt;: Setze eine "
 "Rolle für den Benutzer im Raum."
@@ -4888,219 +4902,166 @@
 "%s ist auf der lokalen Liste, aber nicht auf der Serverliste. Möchten Sie, "
 "dass der Buddy hinzugefügt wird?"
 
-#, c-format
 msgid "Unable to parse message"
 msgstr "Kann die Nachricht nicht parsen"
 
-#, c-format
 msgid "Syntax Error (probably a client bug)"
 msgstr "Syntaxfehler (wahrscheinlich ein Client-Bug)"
 
-#, c-format
 msgid "Invalid email address"
 msgstr "Ungültige E-Mail-Adresse"
 
-#, c-format
 msgid "User does not exist"
 msgstr "Benutzer existiert nicht"
 
-#, c-format
 msgid "Fully qualified domain name missing"
 msgstr "Der Fully Qualified Domain Name fehlt"
 
-#, c-format
 msgid "Already logged in"
 msgstr "Schon angemeldet"
 
-#, c-format
 msgid "Invalid username"
 msgstr "Ungültiger Benutzername"
 
-#, c-format
 msgid "Invalid friendly name"
 msgstr "Ungültiger Freundesname"
 
-#, c-format
 msgid "List full"
 msgstr "Liste voll"
 
-#, c-format
 msgid "Already there"
 msgstr "Schon da"
 
-#, c-format
 msgid "Not on list"
 msgstr "Nicht auf der Liste"
 
-#, c-format
 msgid "User is offline"
 msgstr "Benutzer ist offline"
 
-#, c-format
 msgid "Already in the mode"
 msgstr "Bereits in diesem Modus"
 
-#, c-format
 msgid "Already in opposite list"
 msgstr "Bereits in der „Gegenteil-Liste“"
 
-#, c-format
 msgid "Too many groups"
 msgstr "Zu viele Gruppen"
 
-#, c-format
 msgid "Invalid group"
 msgstr "Ungültige Gruppe"
 
-#, c-format
 msgid "User not in group"
 msgstr "Benutzer ist nicht in der Gruppe"
 
-#, c-format
 msgid "Group name too long"
 msgstr "Name der Gruppe ist zu lang"
 
-#, c-format
 msgid "Cannot remove group zero"
 msgstr "Kann die Gruppe „Null“ nicht entfernen"
 
-#, c-format
 msgid "Tried to add a user to a group that doesn't exist"
 msgstr ""
 "Versuchte einen Benutzer zu einer nichtexistierenden Gruppe hinzuzufügen"
 
-#, c-format
 msgid "Switchboard failed"
 msgstr "Vermittlung gescheitert"
 
-#, c-format
 msgid "Notify transfer failed"
 msgstr "Übertragung der Benachrichtigung gescheitert"
 
-#, c-format
 msgid "Required fields missing"
 msgstr "Notwendige Felder fehlen"
 
-#, c-format
 msgid "Too many hits to a FND"
 msgstr "Zu viele Treffer zu einem FND"
 
-#, c-format
 msgid "Not logged in"
 msgstr "Nicht angemeldet"
 
-#, c-format
 msgid "Service temporarily unavailable"
 msgstr "Dienst momentan nicht verfügbar"
 
-#, c-format
 msgid "Database server error"
 msgstr "Fehler des Datenbank-Servers"
 
-#, c-format
 msgid "Command disabled"
 msgstr "Kommando abgeschaltet"
 
-#, c-format
 msgid "File operation error"
 msgstr "Dateiverarbeitungsfehler"
 
-#, c-format
 msgid "Memory allocation error"
 msgstr "Fehler bei der Speicheranforderung"
 
-#, c-format
 msgid "Wrong CHL value sent to server"
 msgstr "Falscher CHL-Wert zum Server gesendet"
 
-#, c-format
 msgid "Server busy"
 msgstr "Server beschäftigt"
 
-#, c-format
 msgid "Server unavailable"
 msgstr "Server unerreichbar"
 
-#, c-format
 msgid "Peer notification server down"
 msgstr "Peer-Benachrichtigungsserver nicht erreichbar"
 
-#, c-format
 msgid "Database connect error"
 msgstr "Datenbank-Verbindungsfehler"
 
-#, c-format
 msgid "Server is going down (abandon ship)"
 msgstr "Server fährt runter (melden Sie sich ab)"
 
-#, c-format
 msgid "Error creating connection"
 msgstr "Fehler beim Herstellen der Verbindung"
 
-#, c-format
 msgid "CVR parameters are either unknown or not allowed"
 msgstr "CVR-Parameter sind entweder unbekannt oder nicht erlaubt"
 
-#, c-format
 msgid "Unable to write"
 msgstr "Schreiben nicht möglich"
 
-#, c-format
 msgid "Session overload"
 msgstr "Sitzung überlastet"
 
-#, c-format
 msgid "User is too active"
 msgstr "Benutzer ist zu aktiv"
 
-#, c-format
 msgid "Too many sessions"
 msgstr "Zu viele Sitzungen"
 
-#, c-format
 msgid "Passport not verified"
 msgstr "Passport (MSN Benutzerausweis) wurde nicht überprüft"
 
-#, c-format
 msgid "Bad friend file"
 msgstr "Falsche Friends-Datei"
 
-#, c-format
 msgid "Not expected"
 msgstr "Nicht erwartet"
 
-#, c-format
 msgid "Friendly name changes too rapidly"
 msgstr "Benutzernamen werden zu oft geändert"
 
-#, c-format
 msgid "Server too busy"
 msgstr "Server ist zu beschäftigt"
 
-#, c-format
 msgid "Authentication failed"
 msgstr "Authentifizierung fehlgeschlagen"
 
-#, c-format
 msgid "Not allowed when offline"
 msgstr "Nicht erlaubt im Offline-Modus"
 
-#, c-format
 msgid "Not accepting new users"
 msgstr "Akzeptiert keine neuen Benutzer"
 
-#, c-format
 msgid "Kids Passport without parental consent"
 msgstr "Kinder-Passwort ohne die Zustimmung der Eltern"
 
-#, c-format
 msgid "Passport account not yet verified"
 msgstr "Passport-Konto wurde noch nicht überprüft"
 
 msgid "Passport account suspended"
 msgstr "Passport-Konto gesperrt"
 
-#, c-format
 msgid "Bad ticket"
 msgstr "Falsches Ticket"
 
@@ -5477,14 +5438,17 @@
 msgid "%s just sent you a Nudge!"
 msgstr "%s hat Sie gerade angestoßen!"
 
-#. char *adl = g_strndup(payload, len);
+#, c-format
+msgid "Unknown error (%d): %s"
+msgstr "Unbekannter Fehler (%d): %s"
+
+msgid "Unable to add user"
+msgstr "Kann den Benutzer nicht hinzufügen"
+
 #, c-format
 msgid "Unknown error (%d)"
 msgstr "Unbekannter Fehler (%d)"
 
-msgid "Unable to add user"
-msgstr "Kann den Benutzer nicht hinzufügen"
-
 msgid "The following users are missing from your addressbook"
 msgstr "Die folgenden Benutzer fehlen in Ihrem Adressbuch"
 
@@ -5749,6 +5713,8 @@
 msgstr "Verbindung zum Server verloren"
 
 #. Can't write _()'d strings in array initializers. Workaround.
+#. khc: then use N_() in the array initializer and use _() when they are
+#. used
 msgid "New mail messages"
 msgstr "Neue Mail-Nachrichten"
 
@@ -6591,9 +6557,6 @@
 msgid "iChat AV"
 msgstr "iChat AV"
 
-msgid "Live Video"
-msgstr "Live-Video"
-
 msgid "Camera"
 msgstr "Kamera"
 
@@ -8653,7 +8616,6 @@
 msgid "<br><b>Channel Topic:</b><br>%s"
 msgstr "<br><b>Thema des Kanals:</b><br>%s"
 
-#, c-format
 msgid "<br><b>Channel Modes:</b> "
 msgstr "<br><b>Kanal-Modi:</b> "
 
@@ -8678,7 +8640,6 @@
 msgid "Channel Public Keys List"
 msgstr "Liste der öffentlichen Schlüssel des Kanals"
 
-#, c-format
 msgid ""
 "Channel authentication is used to secure the channel from unauthorized "
 "access. The authentication may be based on passphrase and digital "
@@ -9080,7 +9041,6 @@
 msgid "Your Current Mood"
 msgstr "Ihre momentane Stimmung"
 
-#, c-format
 msgid "Normal"
 msgstr "Normal"
 
@@ -9466,47 +9426,37 @@
 msgid "No server statistics available"
 msgstr "Keine Serverstatistik verfügbar"
 
-#, c-format
 msgid "Failure: Version mismatch, upgrade your client"
 msgstr "Fehler: Unterschiedliche Version, aktualisieren Sie Ihren Client"
 
-#, c-format
 msgid "Failure: Remote does not trust/support your public key"
 msgstr ""
 "Fehler: Die entfernte Seite vertraut Ihrem öffentlichen Schlüssel nicht"
 
-#, c-format
 msgid "Failure: Remote does not support proposed KE group"
 msgstr ""
 "Fehler: Entferntes Programm unterstützt nicht die vorgeschlagen KE-Gruppe"
 
-#, c-format
 msgid "Failure: Remote does not support proposed cipher"
 msgstr ""
 "Fehler: Entferntes Programm unterstützt die vorgeschlagene Cipher nicht"
 
-#, c-format
 msgid "Failure: Remote does not support proposed PKCS"
 msgstr "Fehler: Entferntes Programm unterstützt die vorgeschlagene PKCS nicht"
 
-#, c-format
 msgid "Failure: Remote does not support proposed hash function"
 msgstr ""
 "Fehler: Entferntes Programm unterstützt die vorgeschlagen Hashfunktion nicht"
 
-#, c-format
 msgid "Failure: Remote does not support proposed HMAC"
 msgstr "Fehler: Entferntes Programm unterstützt das vorgeschlagene HMAC nicht"
 
-#, c-format
 msgid "Failure: Incorrect signature"
 msgstr "Fehler: Falsche Signatur"
 
-#, c-format
 msgid "Failure: Invalid cookie"
 msgstr "Fehler: Ungültiger Cookie"
 
-#, c-format
 msgid "Failure: Authentication failed"
 msgstr "Fehler: Authentifizierung fehlgeschlagen"
 
@@ -10322,29 +10272,24 @@
 msgstr " (%s)"
 
 #. 10053
-#, c-format
 msgid "Connection interrupted by other software on your computer."
 msgstr ""
 "Die Verbindung wurde von einer anderen Software auf ihrem Computer "
 "unterbrochen."
 
 #. 10054
-#, c-format
 msgid "Remote host closed connection."
 msgstr "Der entfernte Host hat die Verbindung beendet."
 
 #. 10060
-#, c-format
 msgid "Connection timed out."
 msgstr "Verbindungsabbruch wegen Zeitüberschreitung."
 
 #. 10061
-#, c-format
 msgid "Connection refused."
 msgstr "Verbindung abgelehnt."
 
 #. 10048
-#, c-format
 msgid "Address already in use."
 msgstr "Adresse wird bereits benutzt."
 
@@ -11620,7 +11565,6 @@
 "geschützt.  Die Datei 'COPYRIGHT' enthält die komplette Liste der "
 "Mitwirkenden.  Wir übernehmen keine Haftung für dieses Programm.<BR><BR>"
 
-#, c-format
 msgid ""
 "<FONT SIZE=\"4\">FAQ:</FONT> <A HREF=\"http://developer.pidgin.im/wiki/FAQ"
 "\">http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"
@@ -11628,7 +11572,6 @@
 "<FONT SIZE=\"4\">FAQ:</FONT> <A HREF=\"http://developer.pidgin.im/wiki/FAQ"
 "\">http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"
 
-#, c-format
 msgid ""
 "<FONT SIZE=\"4\">Help via e-mail:</FONT> <A HREF=\"mailto:support@pidgin.im"
 "\">support@pidgin.im</A><BR/><BR/>"
@@ -11636,13 +11579,11 @@
 "<FONT SIZE=\"4\">Hilfe per E-Mail:</FONT> <A HREF=\"mailto:support@pidgin.im"
 "\">support@pidgin.im</A><BR/><BR/>"
 
-#, c-format
 msgid ""
 "<FONT SIZE=\"4\">IRC Channel:</FONT> #pidgin on irc.freenode.net<BR><BR>"
 msgstr ""
 "<FONT SIZE=\"4\">IRC-Kanal:</FONT> #pidgin auf irc.freenode.net<BR><BR>"
 
-#, c-format
 msgid "<FONT SIZE=\"4\">XMPP MUC:</FONT> devel@conference.pidgin.im<BR><BR>"
 msgstr "<FONT SIZE=\"4\">XMPP-MUC:</FONT> devel@conference.pidgin.im<BR><BR>"
 
@@ -12001,11 +11942,9 @@
 msgid "Save Image"
 msgstr "Bild speichern"
 
-#, c-format
 msgid "_Save Image..."
 msgstr "Bild _speichern..."
 
-#, c-format
 msgid "_Add Custom Smiley..."
 msgstr "Benutzerdefinierten Smiley _hinzufügen..."
 
@@ -12305,7 +12244,6 @@
 msgid "Pidgin"
 msgstr "Pidgin"
 
-#, c-format
 msgid "Exiting because another libpurple client is already running.\n"
 msgstr "Wird geschlossen, da bereits ein anderer libpurple-Client läuft\n"
 
@@ -12493,47 +12431,36 @@
 msgid "Pounce Target"
 msgstr "Alarm-Ziel"
 
-#, c-format
 msgid "Started typing"
 msgstr "Beginnt zu tippen"
 
-#, c-format
 msgid "Paused while typing"
 msgstr "Hat beim Tippen angehalten"
 
-#, c-format
 msgid "Signed on"
 msgstr "Hat sich anmeldet"
 
-#, c-format
 msgid "Returned from being idle"
 msgstr "Ist nicht mehr inaktiv"
 
-#, c-format
 msgid "Returned from being away"
 msgstr "Ist wieder anwesend"
 
-#, c-format
 msgid "Stopped typing"
 msgstr "Hat das Tippen gestoppt"
 
-#, c-format
 msgid "Signed off"
 msgstr "Hat sich abmeldet"
 
-#, c-format
 msgid "Became idle"
 msgstr "Wurde untätig"
 
-#, c-format
 msgid "Went away"
 msgstr "Ging hinaus"
 
-#, c-format
 msgid "Sent a message"
 msgstr "Eine Nachricht senden"
 
-#, c-format
 msgid "Unknown.... Please report this!"
 msgstr "Unbekannt.... Bitte berichten Sie dieses Problem!"
 
@@ -12814,27 +12741,21 @@
 msgid "Sound Selection"
 msgstr "Klang-Auswahl"
 
-#, c-format
 msgid "Quietest"
 msgstr "Am leisesten"
 
-#, c-format
 msgid "Quieter"
 msgstr "Leiser"
 
-#, c-format
 msgid "Quiet"
 msgstr "Leise"
 
-#, c-format
 msgid "Loud"
 msgstr "Laut"
 
-#, c-format
 msgid "Louder"
 msgstr "Lauter"
 
-#, c-format
 msgid "Loudest"
 msgstr "Am lautesten"
 
@@ -13871,7 +13792,6 @@
 msgid "Select Color"
 msgstr "Farbe auswählen"
 
-#, c-format
 msgid "Select Interface Font"
 msgstr "Schriftart wählen"
 
@@ -14096,7 +14016,6 @@
 msgid "Timestamp Format Options"
 msgstr "Zeitstempelformat-Optionen"
 
-#, c-format
 msgid "_Force 24-hour time format"
 msgstr "_Erzwinge 24-Stunden Zeitformat"
 
@@ -14265,6 +14184,9 @@
 msgstr ""
 "Dieses Plugin ist nützlich zur Fehlersuche in XMPP-Servern oder -Clients."
 
+#~ msgid "Live Video"
+#~ msgstr "Live-Video"
+
 #~ msgid "Invite message"
 #~ msgstr "Einladungsnachricht"