changeset 26975:37436c98201e

propagate from branch 'im.pidgin.pidgin' (head de00ba4608f6410625b80dbfb07287b668eee937) to branch 'im.pidgin.cpw.sulabh.yahoo_16' (head 162a734100aafd62b7338baa81b04510c6b66921)
author Paul Aurich <paul@darkrain42.org>
date Wed, 13 May 2009 18:42:52 +0000
parents 572a0b620d4a (current diff) 727d960a75a4 (diff)
children e4ab3d5362df
files libpurple/protocols/yahoo/yahoo.c libpurple/protocols/yahoo/yahoo_auth.c libpurple/protocols/yahoo/yahoo_auth.h libpurple/protocols/yahoo/yahoo_crypt.c libpurple/protocols/yahoo/yahoo_crypt.h
diffstat 37 files changed, 1016 insertions(+), 495 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Wed May 13 18:42:05 2009 +0000
+++ b/COPYRIGHT	Wed May 13 18:42:52 2009 +0000
@@ -424,6 +424,7 @@
 Lex Spoon
 Chris Stafford
 Kevin Stange
+Joshua Stein
 Jakub Steiner
 Richard Stellingwerff
 Charlie Stockman
--- a/ChangeLog	Wed May 13 18:42:05 2009 +0000
+++ b/ChangeLog	Wed May 13 18:42:52 2009 +0000
@@ -33,6 +33,11 @@
 	* Better support for receiving remote users' nicknames.
 	* /affiliate and /role will now list the room members with the specified
 	  affiliation/role if possible. (Andrei Mozzhuhin)
+	* Put section breaks between resources in "Get Info" to improve readability.
+	* XHTML markup is only included in outgoing messages when the message
+	  contains formatting.
+	* Show when the user was last logged in when doing "Get Info" on an offline
+	  buddy, provided the server supports it.
 
 	Yahoo:
 	* P2P file transfers. (Sulabh Mahajan)
@@ -60,6 +65,8 @@
 	* The nicks of the persons who leave the chatroom are italicized in the
 	  chat's conversation history. The nicks are un-italicized when they
 	  rejoin.
+	* Always set unseen-count and unseen-state on conversations.
+	  (Joshua Stein)
 
 	Finch:
 	* The hardware cursor is updated correctly. This will be useful
--- a/ChangeLog.API	Wed May 13 18:42:05 2009 +0000
+++ b/ChangeLog.API	Wed May 13 18:42:52 2009 +0000
@@ -15,6 +15,7 @@
 			* account-destroying
 		* blist-node-added and blist-node-removed signals (see
 		  blist-signals.dox)
+		* Jabber plugin signals (see jabber-signals.dox)
 		* purple_buddy_destroy
 		* purple_buddy_get_protocol_data
 		* purple_buddy_set_protocol_data
--- a/Makefile.mingw	Wed May 13 18:42:05 2009 +0000
+++ b/Makefile.mingw	Wed May 13 18:42:52 2009 +0000
@@ -96,15 +96,15 @@
 	 -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) -exec $(STRIP) --strip-unneeded {} ';'
 
 installer: create_release_install_dir
-	$(MAKENSIS) /V3 /DPIDGIN_VERSION="$(PIDGIN_VERSION)" /DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" /DWITH_GTK /DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" /DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
+	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DWITH_GTK $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
 	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe ./
 
 installer_nogtk: create_release_install_dir
-	$(MAKENSIS) /V3 /DPIDGIN_VERSION="$(PIDGIN_VERSION)" /DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" /DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" /DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
+	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
 	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-no-gtk.exe ./
 
 installer_debug: install
-	$(MAKENSIS) /V3 /DPIDGIN_VERSION="$(PIDGIN_VERSION)" /DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" /DPIDGIN_INSTALL_DIR="$(PIDGIN_INSTALL_DIR)" /DDEBUG /DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
+	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(PIDGIN_INSTALL_DIR)" $(MAKENSISOPT)DDEBUG $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
 	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-debug.exe ./
 
 installer_zip: create_release_install_dir
--- a/libpurple/dbus-analyze-functions.py	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/dbus-analyze-functions.py	Wed May 13 18:42:52 2009 +0000
@@ -31,6 +31,15 @@
     "purple_account_unregister",
     "purple_connection_new_unregister",
 
+    # These functions are excluded because they involve setting arbitrary
+    # data via pointers for protocols and UIs.  This just won't work.
+    "purple_blist_get_ui_data",
+    "purple_blist_set_ui_data",
+    "purple_blist_node_get_ui_data",
+    "purple_blist_node_set_ui_data",
+    "purple_buddy_get_protocol_data",
+    "purple_buddy_set_protocol_data",
+
     # This is excluded because this script treats PurpleLogReadFlags*
     # as pointer to a struct, instead of a pointer to an enum.  This
     # causes a compilation error. Someone should fix this script.
--- a/libpurple/protocols/jabber/buddy.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Wed May 13 18:42:52 2009 +0000
@@ -50,6 +50,8 @@
 	int timeout_handle;
 	GSList *vcard_imgids;
 	PurpleNotifyUserInfo *user_info;
+	long last_seconds;
+	gchar *last_message;
 } JabberBuddyInfo;
 
 void jabber_buddy_free(JabberBuddy *jb)
@@ -642,6 +644,7 @@
 
 	g_free(jbi->jid);
 	g_hash_table_destroy(jbi->resources);
+	g_free(jbi->last_message);
 	purple_notify_user_info_destroy(jbi->user_info);
 	g_free(jbi);
 }
@@ -852,6 +855,13 @@
 
 			jbr = resources->data;
 
+			/* put a section break between resources, this is not needed if
+			 we are at the first, because one was already added for the vcard
+			 section */
+			if (resources != jbi->jb->resources) {
+				purple_notify_user_info_prepend_section_break(user_info);
+			}
+
 			if(jbr->client.name) {
 				tmp = g_strdup_printf("%s%s%s", jbr->client.name,
 									  (jbr->client.version ? " " : ""),
@@ -1032,6 +1042,24 @@
 		}
 	}
 
+	if (!jbi->jb->resources) {
+		/* the buddy is offline */
+		gchar *status = 
+			g_strdup_printf("%s%s%s",	_("Offline"), 
+				jbi->last_message ? ": " : "",
+				jbi->last_message ? jbi->last_message : "");
+		if (jbi->last_seconds > 0) {
+			char *last = purple_str_seconds_to_string(jbi->last_seconds);
+			gchar *message = g_strdup_printf(_("%s ago"), last);
+			purple_notify_user_info_prepend_pair(user_info, 
+				_("Logged off"), message);
+			g_free(last);
+			g_free(message);
+		}
+		purple_notify_user_info_prepend_pair(user_info, _("Status"), status);
+		g_free(status);
+	}
+
 	g_free(resource_name);
 
 	purple_notify_userinfo(jbi->js->gc, jbi->jid, user_info, NULL, NULL);
@@ -1484,6 +1512,38 @@
 	jabber_buddy_info_show_if_ready(jbi);
 }
 
+static void jabber_last_offline_parse(JabberStream *js, const char *from,
+									  JabberIqType type, const char *id,
+									  xmlnode *packet, gpointer data)
+{
+	JabberBuddyInfo *jbi = data;
+	xmlnode *query;
+	const char *seconds;
+
+	g_return_if_fail(jbi != NULL);
+
+	jabber_buddy_info_remove_id(jbi, id);
+
+	if(!from)
+		return;
+
+	if (type == JABBER_IQ_RESULT) {
+		if((query = xmlnode_get_child(packet, "query"))) {
+			seconds = xmlnode_get_attrib(query, "seconds");
+			if(seconds) {
+				char *end = NULL;
+				long sec = strtol(seconds, &end, 10);
+				if(end != seconds) {
+					jbi->last_seconds = sec;
+				}
+			}
+			jbi->last_message = xmlnode_get_data(query);
+		}
+	}
+
+	jabber_buddy_info_show_if_ready(jbi);
+}
+
 static void jabber_time_parse(JabberStream *js, const char *from,
                               JabberIqType type, const char *id,
                               xmlnode *packet, gpointer data)
@@ -1681,6 +1741,15 @@
 		g_free(full_jid);
 	}
 
+	if (!jb->resources && strchr(jid, '/') == NULL) {
+		/* user is offline, send a jabber:iq:last to find out last time online */
+		iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:last");
+		xmlnode_set_attrib(iq->node, "to", jid);
+		jabber_iq_set_callback(iq, jabber_last_offline_parse, jbi);
+		jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
+		jabber_iq_send(iq);
+	}
+	
 	js->pending_buddy_info_requests = g_slist_prepend(js->pending_buddy_info_requests, jbi);
 	jbi->timeout_handle = purple_timeout_add_seconds(30, jabber_buddy_get_info_timeout, jbi);
 }
--- a/libpurple/protocols/jabber/buddy.h	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.h	Wed May 13 18:42:52 2009 +0000
@@ -86,6 +86,7 @@
 		GList *exts;
 	} caps;
 	GList *commands;
+	gboolean commands_fetched;
 } JabberBuddyResource;
 
 void jabber_buddy_free(JabberBuddy *jb);
--- a/libpurple/protocols/jabber/caps.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/caps.c	Wed May 13 18:42:52 2009 +0000
@@ -35,13 +35,7 @@
 	GList *values;
 } JabberDataFormField;
 
-typedef struct _JabberCapsKey {
-	char *node;
-	char *ver;
-	char *hash;
-} JabberCapsKey;
-
-static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsClientInfo */
+static GHashTable *capstable = NULL; /* JabberCapsTuple -> JabberCapsClientInfo */
 static GHashTable *nodetable = NULL; /* char *node -> JabberCapsNodeExts */
 static guint       save_timer = 0;
 
@@ -86,7 +80,7 @@
 }
 
 static guint jabber_caps_hash(gconstpointer data) {
-	const JabberCapsKey *key = data;
+	const JabberCapsTuple *key = data;
 	guint nodehash = g_str_hash(key->node);
 	guint verhash  = g_str_hash(key->ver);
 	/*
@@ -99,22 +93,14 @@
 }
 
 static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) {
-	const JabberCapsKey *name1 = v1;
-	const JabberCapsKey *name2 = v2;
+	const JabberCapsTuple *name1 = v1;
+	const JabberCapsTuple *name2 = v2;
 
 	return g_str_equal(name1->node, name2->node) &&
 	       g_str_equal(name1->ver, name2->ver) &&
 	       purple_strequal(name1->hash, name2->hash);
 }
 
-void jabber_caps_destroy_key(gpointer data) {
-	JabberCapsKey *key = data;
-	g_free(key->node);
-	g_free(key->ver);
-	g_free(key->hash);
-	g_free(key);
-}
-
 static void
 jabber_caps_client_info_destroy(JabberCapsClientInfo *info)
 {
@@ -140,6 +126,10 @@
 
 	jabber_caps_node_exts_unref(info->exts);
 
+	g_free((char *)info->tuple.node);
+	g_free((char *)info->tuple.ver);
+	g_free((char *)info->tuple.hash);
+
 	g_free(info);
 }
 
@@ -176,16 +166,16 @@
 }
 
 static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) {
-	JabberCapsKey *clientinfo = key;
-	JabberCapsClientInfo *props = value;
+	const JabberCapsTuple *tuple = key;
+	const JabberCapsClientInfo *props = value;
 	xmlnode *root = user_data;
 	xmlnode *client = xmlnode_new_child(root, "client");
 	GList *iter;
 
-	xmlnode_set_attrib(client, "node", clientinfo->node);
-	xmlnode_set_attrib(client, "ver", clientinfo->ver);
-	if (clientinfo->hash)
-		xmlnode_set_attrib(client, "hash", clientinfo->hash);
+	xmlnode_set_attrib(client, "node", tuple->node);
+	xmlnode_set_attrib(client, "ver", tuple->ver);
+	if (tuple->hash)
+		xmlnode_set_attrib(client, "hash", tuple->hash);
 	for(iter = props->identities; iter; iter = g_list_next(iter)) {
 		JabberIdentity *id = iter->data;
 		xmlnode *identity = xmlnode_new_child(client, "identity");
@@ -255,8 +245,8 @@
 		if(client->type != XMLNODE_TYPE_TAG)
 			continue;
 		if(!strcmp(client->name, "client")) {
-			JabberCapsKey *key = g_new0(JabberCapsKey, 1);
 			JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1);
+			JabberCapsTuple *key = (JabberCapsTuple*)&value->tuple;
 			xmlnode *child;
 			JabberCapsNodeExts *exts = NULL;
 			key->node = g_strdup(xmlnode_get_attrib(client,"node"));
@@ -340,7 +330,7 @@
 void jabber_caps_init(void)
 {
 	nodetable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)jabber_caps_node_exts_unref); 
-	capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, (GDestroyNotify)jabber_caps_client_info_destroy);
+	capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, NULL, (GDestroyNotify)jabber_caps_client_info_destroy);
 	jabber_caps_load();
 }
 
@@ -356,6 +346,28 @@
 	capstable = nodetable = NULL;
 }
 
+gboolean jabber_caps_exts_known(const JabberCapsClientInfo *info,
+                                char **exts)
+{
+	int i;
+	g_return_val_if_fail(info != NULL, FALSE);
+
+	if (!exts)
+		return TRUE;
+
+	for (i = 0; exts[i]; ++i) {
+		/* Hack since we advertise the ext along with v1.5 caps but don't
+		 * store any exts */
+		if (g_str_equal(exts[i], "voice-v1") && !info->exts)
+			continue;
+		if (!info->exts ||
+				!g_hash_table_lookup(info->exts->exts, exts[i]))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
 typedef struct _jabber_caps_cbplususerdata {
 	guint ref;
 
@@ -429,7 +441,7 @@
 		"http://jabber.org/protocol/disco#info");
 	jabber_caps_cbplususerdata *userdata = data;
 	JabberCapsClientInfo *info = NULL, *value;
-	JabberCapsKey key;
+	JabberCapsTuple key;
 
 	if (!query || type == JABBER_IQ_ERROR) {
 		/* Any outstanding exts will be dealt with via ref-counting */
@@ -481,7 +493,7 @@
 		jabber_caps_client_info_destroy(info);
 		info = value;
 	} else {
-		JabberCapsKey *n_key = g_new(JabberCapsKey, 1);
+		JabberCapsTuple *n_key = (JabberCapsTuple *)&info->tuple;
 		n_key->node = userdata->node;
 		n_key->ver  = userdata->ver;
 		n_key->hash = userdata->hash;
@@ -549,16 +561,19 @@
 }
 
 void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
-        const char *ver, const char *hash, const char *ext,
+        const char *ver, const char *hash, char **exts,
         jabber_caps_get_info_cb cb, gpointer user_data)
 {
 	JabberCapsClientInfo *info;
-	JabberCapsKey key;
+	JabberCapsTuple key;
 	jabber_caps_cbplususerdata *userdata;
 
-	if (ext && *ext && hash)
+	if (exts && hash) {
 		purple_debug_info("jabber", "Ignoring exts in new-style caps from %s\n",
 		                     who);
+		g_strfreev(exts);
+		exts = NULL;
+	}
 
 	/* Using this in a read-only fashion, so the cast is OK */
 	key.node = (char *)node;
@@ -607,9 +622,8 @@
 	}
 
 	/* Are there any exts that we don't recognize? */
-	if (ext && *ext && !hash) {
+	if (exts) {
 		JabberCapsNodeExts *node_exts;
-		gchar **splat = g_strsplit(ext, " ", 0);
 		int i;
 
 		if (info) {
@@ -621,23 +635,23 @@
 			/* We'll put it in later once we have the client info */
 			node_exts = userdata->node_exts = jabber_caps_find_exts_by_node(node);
 
-		for (i = 0; splat[i]; ++i) {
-			userdata->exts = g_list_prepend(userdata->exts, splat[i]);
+		for (i = 0; exts[i]; ++i) {
+			userdata->exts = g_list_prepend(userdata->exts, exts[i]);
 			/* Look it up if we don't already know what it means */
-			if (!g_hash_table_lookup(node_exts->exts, splat[i])) {
+			if (!g_hash_table_lookup(node_exts->exts, exts[i])) {
 				JabberIq *iq;
 				xmlnode *query;
 				char *nodeext;
 				ext_iq_data *cbdata = g_new(ext_iq_data, 1);
 
-				cbdata->name = splat[i];
+				cbdata->name = exts[i];
 				cbdata->data = cbplususerdata_ref(userdata);
 
 				iq = jabber_iq_new_query(js, JABBER_IQ_GET,
 				            "http://jabber.org/protocol/disco#info");
 				query = xmlnode_get_child_with_namespace(iq->node, "query",
 				            "http://jabber.org/protocol/disco#info");
-				nodeext = g_strdup_printf("%s#%s", node, splat[i]);
+				nodeext = g_strdup_printf("%s#%s", node, exts[i]);
 				xmlnode_set_attrib(query, "node", nodeext);
 				g_free(nodeext);
 				xmlnode_set_attrib(iq->node, "to", who);
@@ -647,11 +661,11 @@
 
 				++userdata->extOutstanding;	
 			}
-			splat[i] = NULL;
+			exts[i] = NULL;
 		}
 		/* All the strings are now part of the GList, so don't need
 		 * g_strfreev. */
-		g_free(splat);
+		g_free(exts);
 	}
 
 	if (userdata->info && userdata->extOutstanding == 0) {
--- a/libpurple/protocols/jabber/caps.h	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/caps.h	Wed May 13 18:42:52 2009 +0000
@@ -30,11 +30,19 @@
 
 typedef struct _JabberCapsNodeExts JabberCapsNodeExts;
 
+typedef struct _JabberCapsTuple {
+	const char *node;
+	const char *ver;
+	const char *hash;
+} JabberCapsTuple;
+
 struct _JabberCapsClientInfo {
 	GList *identities; /* JabberIdentity */
 	GList *features; /* char * */
 	GList *forms; /* xmlnode * */
 	JabberCapsNodeExts *exts;
+
+	const JabberCapsTuple tuple;
 };
 
 /*
@@ -60,7 +68,10 @@
 void jabber_caps_init(void);
 void jabber_caps_uninit(void);
 
-void jabber_caps_destroy_key(gpointer value);
+/**
+ * Check whether all of the exts in a char* array are known to the given info.
+ */
+gboolean jabber_caps_exts_known(const JabberCapsClientInfo *info, char **exts);
 
 /**
  * Main entity capabilites function to get the capabilities of a contact.
@@ -68,10 +79,13 @@
  * The callback will be called synchronously if we already have the
  * capabilities for the specified (node,ver,hash) (and, if exts are specified,
  * if we know what each means)
+ *
+ * @param exts A g_strsplit'd (NULL-terminated) array of strings. This
+ *             function is responsible for freeing it.
  */
 void jabber_caps_get_info(JabberStream *js, const char *who, const char *node,
                           const char *ver, const char *hash,
-                          const char *ext, jabber_caps_get_info_cb cb,
+                          char **exts, jabber_caps_get_info_cb cb,
                           gpointer user_data);
 
 /**
--- a/libpurple/protocols/jabber/disco.h	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/disco.h	Wed May 13 18:42:52 2009 +0000
@@ -1,5 +1,5 @@
 /**
- * @file iq.h JabberID handlers
+ * @file disco.h service discovery handlers
  *
  * purple
  *
--- a/libpurple/protocols/jabber/google.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Wed May 13 18:42:52 2009 +0000
@@ -638,7 +638,6 @@
 	const char *to, *url;
 	const char *in_str;
 	char *to_name;
-	char *default_tos[1];
 
 	int i, count = 1, returned_count;
 
@@ -658,14 +657,20 @@
 
 	/* If Gmail doesn't tell us who the mail is to, let's use our JID */
 	to = xmlnode_get_attrib(packet, "to");
-	default_tos[0] = jabber_get_bare_jid(to);
 
 	message = xmlnode_get_child(child, "mail-thread-info");
 
 	if (count == 0 || !message) {
-		if (count > 0)
-			purple_notify_emails(js->gc, count, FALSE, NULL, NULL, (const char**) default_tos, NULL, NULL, NULL);
-		g_free(default_tos[0]);
+		if (count > 0) {
+			char *bare_jid = jabber_get_bare_jid(to);
+			const char *default_tos[2] = { bare_jid };
+
+			purple_notify_emails(js->gc, count, FALSE, NULL, NULL, default_tos, NULL, NULL, NULL);
+			g_free(bare_jid);
+		} else {
+			purple_notify_emails(js->gc, count, FALSE, NULL, NULL, NULL, NULL, NULL, NULL);
+		}
+
 		return;
 	}
 
@@ -673,10 +678,10 @@
 	 * accordingly */
 	for (returned_count = 0; message; returned_count++, message=xmlnode_get_next_twin(message));
 
-	froms    = g_new0(const char* , returned_count);
-	tos      = g_new0(const char* , returned_count);
-	subjects = g_new0(char* , returned_count);
-	urls     = g_new0(const char* , returned_count);
+	froms    = g_new0(const char* , returned_count + 1);
+	tos      = g_new0(const char* , returned_count + 1);
+	subjects = g_new0(char* , returned_count + 1);
+	urls     = g_new0(const char* , returned_count + 1);
 
 	to = xmlnode_get_attrib(packet, "to");
 	to_name = jabber_get_bare_jid(to);
@@ -726,16 +731,12 @@
 	if (i>0)
 		purple_notify_emails(js->gc, count, count == i, (const char**) subjects, froms, tos,
 				urls, NULL, NULL);
-	else
-		purple_notify_emails(js->gc, count, FALSE, NULL, NULL, (const char**) default_tos, NULL, NULL, NULL);
-
 
 	g_free(to_name);
 	g_free(tos);
-	g_free(default_tos[0]);
 	g_free(froms);
-	for (; i > 0; i--)
-		g_free(subjects[i - 1]);
+	for (i = 0; i < returned_count; i++)
+		g_free(subjects[i]);
 	g_free(subjects);
 	g_free(urls);
 
@@ -761,6 +762,13 @@
 	if (type != JABBER_IQ_SET)
 		return;
 
+	/* Acknowledge the notification */
+	iq = jabber_iq_new(js, JABBER_IQ_RESULT);
+	if (from)
+		xmlnode_set_attrib(iq->node, "to", from);
+	xmlnode_set_attrib(iq->node, "id", id);
+	jabber_iq_send(iq);
+
 	purple_debug(PURPLE_DEBUG_MISC, "jabber",
 		   "Got new mail notification. Sending request for more info\n");
 
--- a/libpurple/protocols/jabber/iq.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/iq.c	Wed May 13 18:42:52 2009 +0000
@@ -42,7 +42,7 @@
 #endif
 
 GHashTable *iq_handlers = NULL;
-
+GHashTable *signal_iq_handlers = NULL;
 
 JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type)
 {
@@ -289,6 +289,16 @@
 	const char *xmlns;
 	const char *iq_type, *id, *from;
 	JabberIqType type = JABBER_IQ_NONE;
+	gboolean signal_return;
+
+	from = xmlnode_get_attrib(packet, "from");
+	id = xmlnode_get_attrib(packet, "id");
+	iq_type = xmlnode_get_attrib(packet, "type");
+
+	signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin,
+			"jabber-receiving-iq", js->gc, iq_type, id, from, packet));
+	if (signal_return)
+		return;
 
 	/*
 	 * child will be either the first tag child or NULL if there is no child.
@@ -301,10 +311,6 @@
 			break;
 	}
 
-	iq_type = xmlnode_get_attrib(packet, "type");
-	from = xmlnode_get_attrib(packet, "from");
-	id = xmlnode_get_attrib(packet, "id");
-
 	if (iq_type) {
 		if (!strcmp(iq_type, "get"))
 			type = JABBER_IQ_GET;
@@ -361,12 +367,23 @@
 		}
 	}
 
-	/* Apparently not, so lets see if we have a pre-defined handler */
+	/*
+	 * Apparently not, so let's see if we have a pre-defined handler
+	 * or if an outside plugin is interested.
+	 */
 	if(child && (xmlns = xmlnode_get_namespace(child))) {
 		char *key = g_strdup_printf("%s %s", child->name, xmlns);
 		JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key);
+		int signal_ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
 		g_free(key);
 
+		if (signal_ref > 0) {
+			signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin, "jabber-watched-iq",
+					js->gc, iq_type, id, from, child));
+			if (signal_return)
+				return;
+		}
+
 		if(jih) {
 			jih(js, from, type, id, child);
 			return;
@@ -408,9 +425,48 @@
 	g_hash_table_replace(iq_handlers, key, handlerfunc);
 }
 
+void jabber_iq_signal_register(const gchar *node, const gchar *xmlns)
+{
+	gchar *key;
+	int ref;
+
+	g_return_if_fail(node != NULL && *node != '\0');
+	g_return_if_fail(xmlns != NULL && *xmlns != '\0');
+
+	key = g_strdup_printf("%s %s", node, xmlns);
+	ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
+	if (ref == 0) {
+		g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(1));
+	} else {
+		g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(ref + 1));
+		g_free(key);
+	}
+}
+
+void jabber_iq_signal_unregister(const gchar *node, const gchar *xmlns)
+{
+	gchar *key;
+	int ref;
+
+	g_return_if_fail(node != NULL && *node != '\0');
+	g_return_if_fail(xmlns != NULL && *xmlns != '\0');
+
+	key = g_strdup_printf("%s %s", node, xmlns);
+	ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
+
+	if (ref == 1) {
+		g_hash_table_remove(signal_iq_handlers, key);
+	} else if (ref > 1) {
+		g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(ref - 1));
+	}
+
+	g_free(key);
+}
+
 void jabber_iq_init(void)
 {
 	iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+	signal_iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
 
 	jabber_iq_register_handler("jingle", JINGLE, jingle_parse);
 	jabber_iq_register_handler("mailbox", "google:mail:notify",
@@ -446,5 +502,6 @@
 void jabber_iq_uninit(void)
 {
 	g_hash_table_destroy(iq_handlers);
-	iq_handlers = NULL;
+	g_hash_table_destroy(signal_iq_handlers);
+	iq_handlers = signal_iq_handlers = NULL;
 }
--- a/libpurple/protocols/jabber/iq.h	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/iq.h	Wed May 13 18:42:52 2009 +0000
@@ -31,6 +31,7 @@
 } JabberIqType;
 
 #include "jabber.h"
+#include "connection.h"
 
 typedef struct _JabberIq JabberIq;
 
@@ -106,4 +107,8 @@
 void jabber_iq_register_handler(const char *node, const char *xmlns,
                                 JabberIqHandler *func);
 
+/* Connected to namespace-handler registration signals */
+void jabber_iq_signal_register(const gchar *node, const gchar *xmlns);
+void jabber_iq_signal_unregister(const gchar *node, const gchar *xmlns);
+
 #endif /* PURPLE_JABBER_IQ_H_ */
--- a/libpurple/protocols/jabber/jabber.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Wed May 13 18:42:52 2009 +0000
@@ -3380,6 +3380,7 @@
 							 purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT),
 							 purple_value_new(PURPLE_TYPE_STRING),
 							 purple_value_new(PURPLE_TYPE_STRING));
+
 	purple_plugin_ipc_register(plugin, "add_feature", PURPLE_CALLBACK(jabber_ipc_add_feature),
 							 purple_marshal_VOID__POINTER,
 							 NULL, 1,
@@ -3389,6 +3390,8 @@
 void
 jabber_uninit_plugin(void)
 {
+	purple_plugin_ipc_unregister_all(jabber_plugin);
+
 	jabber_features_destroy();
 	jabber_identities_destroy();
 }
--- a/libpurple/protocols/jabber/jabber.h	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Wed May 13 18:42:52 2009 +0000
@@ -75,6 +75,8 @@
 /* Index into attention_types list */
 #define JABBER_BUZZ 0
 
+PurplePlugin *jabber_plugin;
+
 typedef enum {
 	JABBER_STREAM_OFFLINE,
 	JABBER_STREAM_CONNECTING,
--- a/libpurple/protocols/jabber/libxmpp.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Wed May 13 18:42:52 2009 +0000
@@ -46,6 +46,8 @@
 #include "data.h"
 #include "ibb.h"
 
+PurplePlugin *jabber_plugin = NULL;
+
 static PurplePluginProtocolInfo prpl_info =
 {
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_MAIL_CHECK |
@@ -125,6 +127,8 @@
 
 static gboolean load_plugin(PurplePlugin *plugin)
 {
+	jabber_plugin = plugin;
+
 	purple_signal_register(plugin, "jabber-receiving-xmlnode",
 			purple_marshal_VOID__POINTER_POINTER, NULL, 2,
 			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
@@ -140,16 +144,65 @@
 			     purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
 			     purple_value_new_outgoing(PURPLE_TYPE_STRING));
 
+	purple_signal_register(plugin, "jabber-receiving-message",
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
+			purple_value_new(PURPLE_TYPE_BOOLEAN), 6,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new(PURPLE_TYPE_STRING), /* type */
+			purple_value_new(PURPLE_TYPE_STRING), /* id */
+			purple_value_new(PURPLE_TYPE_STRING), /* from */
+			purple_value_new(PURPLE_TYPE_STRING), /* to */
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+
+	purple_signal_register(plugin, "jabber-receiving-iq",
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
+			purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new(PURPLE_TYPE_STRING), /* type */
+			purple_value_new(PURPLE_TYPE_STRING), /* id */
+			purple_value_new(PURPLE_TYPE_STRING), /* from */
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+
+	purple_signal_register(plugin, "jabber-watched-iq",
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
+			purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new(PURPLE_TYPE_STRING), /* type */
+			purple_value_new(PURPLE_TYPE_STRING), /* id */
+			purple_value_new(PURPLE_TYPE_STRING), /* from */
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */
+
+	purple_signal_register(plugin, "jabber-register-namespace-watcher",
+			purple_marshal_VOID__POINTER_POINTER_POINTER,
+			NULL, 2,
+			purple_value_new(PURPLE_TYPE_STRING),  /* node */
+			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+
+	purple_signal_register(plugin, "jabber-unregister-namespace-watcher",
+			purple_marshal_VOID__POINTER_POINTER_POINTER,
+			NULL, 2,
+			purple_value_new(PURPLE_TYPE_STRING),  /* node */
+			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+
+	purple_signal_connect(plugin, "jabber-register-namespace-watcher",
+			plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL);
+	purple_signal_connect(plugin, "jabber-unregister-namespace-watcher",
+			plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL);
+
+	purple_signal_register(plugin, "jabber-receiving-presence",
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER,
+			purple_value_new(PURPLE_TYPE_BOOLEAN), 4,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new(PURPLE_TYPE_STRING), /* type */
+			purple_value_new(PURPLE_TYPE_STRING), /* from */
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+
 	return TRUE;
 }
 
 static gboolean unload_plugin(PurplePlugin *plugin)
 {
-	purple_signal_unregister(plugin, "jabber-receiving-xmlnode");
-
-	purple_signal_unregister(plugin, "jabber-sending-xmlnode");
-
-	purple_signal_unregister(plugin, "jabber-sending-text");
+	purple_signals_unregister_by_instance(plugin);
 
 	/* reverse order of init_plugin */
 	jabber_bosh_uninit();
@@ -166,6 +219,8 @@
 	/* Stay on target...stay on target... Almost there... */
 	jabber_uninit_plugin();
 
+	jabber_plugin = NULL;
+
 	return TRUE;
 }
 
@@ -253,8 +308,9 @@
 
 	option = purple_account_option_string_new(_("File transfer proxies"),
 						  "ft_proxies",
-						/* TODO: Is this an acceptable default? */
-						  "proxy.jabber.org");
+						/* TODO: Is this an acceptable default?
+						 * Also, keep this in sync as they add more servers */
+						  "proxy.eu.jabber.org");
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 						  option);
 
--- a/libpurple/protocols/jabber/message.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/message.c	Wed May 13 18:42:52 2009 +0000
@@ -532,16 +532,25 @@
 void jabber_message_parse(JabberStream *js, xmlnode *packet)
 {
 	JabberMessage *jm;
-	const char *type;
+	const char *id, *from, *to, *type;
 	xmlnode *child;
+	gboolean signal_return;
+
+	from = xmlnode_get_attrib(packet, "from");
+	id   = xmlnode_get_attrib(packet, "id");
+	to   = xmlnode_get_attrib(packet, "to");
+	type = xmlnode_get_attrib(packet, "type");
+
+	signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin,
+			"jabber-receiving-message", js->gc, type, id, from, to, packet));
+	if (signal_return)
+		return;
 
 	jm = g_new0(JabberMessage, 1);
 	jm->js = js;
 	jm->sent = time(NULL);
 	jm->delayed = FALSE;
 
-	type = xmlnode_get_attrib(packet, "type");
-
 	if(type) {
 		if(!strcmp(type, "normal"))
 			jm->type = JABBER_MESSAGE_NORMAL;
@@ -559,9 +568,9 @@
 		jm->type = JABBER_MESSAGE_NORMAL;
 	}
 
-	jm->from = g_strdup(xmlnode_get_attrib(packet, "from"));
-	jm->to = g_strdup(xmlnode_get_attrib(packet, "to"));
-	jm->id = g_strdup(xmlnode_get_attrib(packet, "id"));
+	jm->from = g_strdup(from);
+	jm->to   = g_strdup(to);
+	jm->id   = g_strdup(id);
 
 	for(child = packet->child; child; child = child->next) {
 		const char *xmlns = xmlnode_get_namespace(child);
@@ -940,6 +949,58 @@
 	}
 }
 
+static char *
+jabber_message_smileyfy_xhtml(JabberMessage *jm, const char *xhtml)
+{
+	PurpleAccount *account = purple_connection_get_account(jm->js->gc);
+	PurpleConversation *conv =
+		purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, jm->to,
+			account);
+
+	if (jabber_conv_support_custom_smileys(jm->js->gc, conv, jm->to)) {
+		GList *found_smileys = jabber_message_xhtml_find_smileys(xhtml);
+
+		if (found_smileys) {
+			gchar *smileyfied_xhtml = NULL;
+			const GList *iterator;
+
+			for (iterator = found_smileys; iterator ;
+				iterator = g_list_next(iterator)) {
+				const PurpleSmiley *smiley =
+						(PurpleSmiley *) iterator->data;
+				const gchar *shortcut = purple_smiley_get_shortcut(smiley);
+				const JabberData *data =
+					jabber_data_find_local_by_alt(shortcut);
+
+				/* the object has not been sent before */
+				if (!data) {
+					PurpleStoredImage *image =
+							purple_smiley_get_stored_image(smiley);
+					const gchar *ext = purple_imgstore_get_extension(image);
+					JabberStream *js = jm->js;
+
+					JabberData *new_data =
+						jabber_data_create_from_data(purple_imgstore_get_data(image),
+							purple_imgstore_get_size(image),
+							jabber_message_get_mimetype_from_ext(ext), js);
+					purple_debug_info("jabber",
+						"cache local smiley alt = %s, cid = %s\n",
+						shortcut, jabber_data_get_cid(new_data));
+					jabber_data_associate_local(new_data, shortcut);
+				}
+			}
+
+			smileyfied_xhtml =
+				jabber_message_get_smileyfied_xhtml(xhtml, found_smileys);
+			g_list_free(found_smileys);
+
+			return smileyfied_xhtml;
+		}
+	}
+
+	return NULL;
+}
+
 void jabber_message_send(JabberMessage *jm)
 {
 	xmlnode *message, *child;
@@ -1025,56 +1086,7 @@
 	}
 
 	if(jm->xhtml) {
-		PurpleAccount *account = purple_connection_get_account(jm->js->gc);
-		PurpleConversation *conv =
-			purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, jm->to,
-				account);
-
-		if (jabber_conv_support_custom_smileys(jm->js->gc, conv, jm->to)) {
-			GList *found_smileys = jabber_message_xhtml_find_smileys(jm->xhtml);
-
-			if (found_smileys) {
-				gchar *smileyfied_xhtml = NULL;
-				const GList *iterator;
-
-				for (iterator = found_smileys; iterator ;
-					iterator = g_list_next(iterator)) {
-					const PurpleSmiley *smiley =
-							(PurpleSmiley *) iterator->data;
-					const gchar *shortcut = purple_smiley_get_shortcut(smiley);
-					const JabberData *data =
-						jabber_data_find_local_by_alt(shortcut);
-
-					/* the object has not been sent before */
-					if (!data) {
-						PurpleStoredImage *image =
-								purple_smiley_get_stored_image(smiley);
-						const gchar *ext = purple_imgstore_get_extension(image);
-						JabberStream *js = jm->js;
-
-						JabberData *new_data =
-							jabber_data_create_from_data(purple_imgstore_get_data(image),
-								purple_imgstore_get_size(image),
-								jabber_message_get_mimetype_from_ext(ext), js);
-						purple_debug_info("jabber",
-							"cache local smiley alt = %s, cid = %s\n",
-							shortcut, jabber_data_get_cid(new_data));
-						jabber_data_associate_local(new_data, shortcut);
-					}
-				}
-
-				smileyfied_xhtml =
-					jabber_message_get_smileyfied_xhtml(jm->xhtml, found_smileys);
-				child = xmlnode_from_str(smileyfied_xhtml, -1);
-				g_free(smileyfied_xhtml);
-				g_list_free(found_smileys);
-			} else {
-				child = xmlnode_from_str(jm->xhtml, -1);
-			}
-		} else {
-			child = xmlnode_from_str(jm->xhtml, -1);
-		}
-		if(child) {
+		if ((child = xmlnode_from_str(jm->xhtml, -1))) {
 			xmlnode_insert_child(message, child);
 		} else {
 			purple_debug(PURPLE_DEBUG_ERROR, "jabber",
@@ -1088,14 +1100,51 @@
 	xmlnode_free(message);
 }
 
+/*
+ * Compare the XHTML and plain strings passed in for "equality". Any HTML markup
+ * other than <br/> (matches a newline) in the XHTML will cause this to return
+ * FALSE.
+ */
+static gboolean
+jabber_xhtml_plain_equal(const char *xhtml_escaped,
+                         const char *plain)
+{
+	int i = 0;
+	int j = 0;
+	gboolean ret;
+	char *xhtml = purple_unescape_html(xhtml_escaped);
+
+	while (xhtml[i] && plain[j]) {
+		if (xhtml[i] == plain[j]) {
+			i += 1;
+			j += 1;
+			continue;
+		}
+
+		if (plain[j] == '\n' && !strncmp(xhtml+i, "<br/>", 5)) {
+			i += 5;
+			j += 1;
+			continue;
+		}
+
+		g_free(xhtml);
+		return FALSE;
+	}
+
+	/* Are we at the end of both strings? */
+	ret = (xhtml[i] == plain[j]) && (xhtml[i] == '\0');
+	g_free(xhtml);
+	return ret;
+}
+
 int jabber_message_send_im(PurpleConnection *gc, const char *who, const char *msg,
 		PurpleMessageFlags flags)
 {
 	JabberMessage *jm;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
-	char *buf;
 	char *xhtml;
+	char *tmp;
 	char *resource;
 
 	if(!who || !msg)
@@ -1130,15 +1179,18 @@
 			jm->typing_style |= JM_TS_JEP_0022;
 	}
 
-	buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg);
-
-	purple_markup_html_to_xhtml(buf, &xhtml, &jm->body);
-	g_free(buf);
+	purple_markup_html_to_xhtml(msg, &xhtml, &jm->body);
+	tmp = jabber_message_smileyfy_xhtml(jm, xhtml);
+	if (tmp) {
+		g_free(xhtml);
+		xhtml = tmp;
+	}
 
-	if(!jbr || jbr->capabilities & JABBER_CAP_XHTML)
-		jm->xhtml = xhtml;
-	else
-		g_free(xhtml);
+	if ((!jbr || jbr->capabilities & JABBER_CAP_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);
+
+	g_free(xhtml);
 
 	jabber_message_send(jm);
 	jabber_message_free(jm);
@@ -1150,7 +1202,7 @@
 	JabberChat *chat;
 	JabberMessage *jm;
 	JabberStream *js;
-	char *buf;
+	char *xhtml;
 
 	if(!msg || !gc)
 		return 0;
@@ -1167,14 +1219,12 @@
 	jm->to = g_strdup_printf("%s@%s", chat->room, chat->server);
 	jm->id = jabber_get_next_id(jm->js);
 
-	buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg);
-	purple_markup_html_to_xhtml(buf, &jm->xhtml, &jm->body);
-	g_free(buf);
+	purple_markup_html_to_xhtml(msg, &xhtml, &jm->body);
 
-	if(!chat->xhtml) {
-		g_free(jm->xhtml);
-		jm->xhtml = NULL;
-	}
+	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);
+
+	g_free(xhtml);
 
 	jabber_message_send(jm);
 	jabber_message_free(jm);
--- a/libpurple/protocols/jabber/oob.h	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/oob.h	Wed May 13 18:42:52 2009 +0000
@@ -1,5 +1,5 @@
 /**
- * @file jutil.h utility functions
+ * @file oob.h out-of-band transfer functions
  *
  * purple
  *
--- a/libpurple/protocols/jabber/ping.h	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/ping.h	Wed May 13 18:42:52 2009 +0000
@@ -1,5 +1,5 @@
 /**
- * @file ping.h utility functions
+ * @file ping.h ping functions
  *
  * purple
  *
--- a/libpurple/protocols/jabber/presence.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/presence.c	Wed May 13 18:42:52 2009 +0000
@@ -404,8 +404,10 @@
                                  JabberPresenceCapabilities *userdata)
 {
 	JabberBuddyResource *jbr;
-	char *resource = g_utf8_strrchr(userdata->from, -1, '/');
-	resource += 1;
+	char *resource = g_utf8_strchr(userdata->from, -1, '/');
+
+	if (resource)
+		resource += 1;
 
 	jbr = jabber_buddy_find_resource(userdata->jb, resource);
 	if (!jbr) {
@@ -427,13 +429,15 @@
 	jbr->caps.info = info;
 	jbr->caps.exts = exts;
 
-	if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) {
+	if (!jbr->commands_fetched && jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) {
 		JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
 		xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", "http://jabber.org/protocol/disco#items");
 		xmlnode_set_attrib(iq->node, "to", userdata->from);
 		xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
 		jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
 		jabber_iq_send(iq);
+
+		jbr->commands_fetched = TRUE;
 	}
 
 	g_free(userdata->from);
@@ -442,8 +446,8 @@
 
 void jabber_presence_parse(JabberStream *js, xmlnode *packet)
 {
-	const char *from = xmlnode_get_attrib(packet, "from");
-	const char *type = xmlnode_get_attrib(packet, "type");
+	const char *from;
+	const char *type;
 	const char *real_jid = NULL;
 	const char *affiliation = NULL;
 	const char *role = NULL;
@@ -465,10 +469,19 @@
 	xmlnode *caps = NULL;
 	int idle = 0;
 	gchar *nickname = NULL;
+	gboolean signal_return;
+
+	from = xmlnode_get_attrib(packet, "from");
+	type = xmlnode_get_attrib(packet, "type");
 
 	if(!(jb = jabber_buddy_find(js, from, TRUE)))
 		return;
 
+	signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin,
+			"jabber-receiving-presence", js->gc, type, from, packet));
+	if (signal_return)
+		return;
+
 	if(!(jid = jabber_id_new(from)))
 		return;
 
@@ -852,13 +865,26 @@
 		/* v1.3 uses: node, ver, and optionally ext.
 		 * v1.5 uses: node, ver, and hash. */
 		if (node && *node && ver && *ver) {
-			JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
-			userdata->js = js;
-			userdata->jb = jb;
-			userdata->from = g_strdup(from);
-			jabber_caps_get_info(js, from, node, ver, hash, ext,
-			    (jabber_caps_get_info_cb)jabber_presence_set_capabilities,
-			    userdata);
+			gchar **exts = ext && *ext ? g_strsplit(ext, " ", -1) : NULL;
+			jbr = jabber_buddy_find_resource(jb, jid->resource);
+
+			/* Look it up if we don't already have all this information */
+			if (!jbr || !jbr->caps.info ||
+					!g_str_equal(node, jbr->caps.info->tuple.node) ||
+					!g_str_equal(ver, jbr->caps.info->tuple.ver) ||
+					!purple_strequal(hash, jbr->caps.info->tuple.hash) ||
+					!jabber_caps_exts_known(jbr->caps.info, (gchar **)exts)) {
+				JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
+				userdata->js = js;
+				userdata->jb = jb;
+				userdata->from = g_strdup(from);
+				jabber_caps_get_info(js, from, node, ver, hash, exts,
+				    (jabber_caps_get_info_cb)jabber_presence_set_capabilities,
+				    userdata);
+			} else {
+				if (exts)
+					g_strfreev(exts);
+			}
 		}
 	}
 
--- a/libpurple/protocols/jabber/si.h	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/jabber/si.h	Wed May 13 18:42:52 2009 +0000
@@ -1,5 +1,5 @@
 /**
- * @file jutil.h utility functions
+ * @file si.h SI transfer functions
  *
  * purple
  *
--- a/libpurple/protocols/msn/notification.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Wed May 13 18:42:52 2009 +0000
@@ -1891,14 +1891,11 @@
 
 		if (count > 0)
 		{
-			const char *passport;
-			const char *url;
-
-			passport = msn_user_get_passport(session->user);
-			url = session->passport_info.mail_url;
+			const char *passports[2] = { msn_user_get_passport(session->user) };
+			const char *urls[2] = { session->passport_info.mail_url };
 
 			purple_notify_emails(gc, count, FALSE, NULL, NULL,
-							   &passport, &url, NULL, NULL);
+							   passports, urls, NULL, NULL);
 		}
 	}
 
@@ -1960,14 +1957,11 @@
 
 		if (count > 0)
 		{
-			const char *passport;
-			const char *url;
-
-			passport = msn_user_get_passport(session->user);
-			url = session->passport_info.mail_url;
+			const char *passports[2] = { msn_user_get_passport(session->user) };
+			const char *urls[2] = { session->passport_info.mail_url };
 
 			purple_notify_emails(gc, count, FALSE, NULL, NULL,
-							   &passport, &url, NULL, NULL);
+							   passports, urls, NULL, NULL);
 		}
 	}
 
--- a/libpurple/protocols/msn/oim.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/msn/oim.c	Wed May 13 18:42:52 2009 +0000
@@ -764,14 +764,14 @@
 	if (iu_node != NULL && purple_account_get_check_mail(session->account))
 	{
 		char *unread = xmlnode_get_data(iu_node);
-		const char *passport = msn_user_get_passport(session->user);
-		const char *url = session->passport_info.mail_url;
+		const char *passports[2] = { msn_user_get_passport(session->user) };
+		const char *urls[2] = { session->passport_info.mail_url };
 		int count = atoi(unread);
 
 		/* XXX/khc: pretty sure this is wrong */
 		if (count > 0)
 			purple_notify_emails(session->account->gc, count, FALSE, NULL,
-				NULL, &passport, &url, NULL, NULL);
+				NULL, passports, urls, NULL, NULL);
 		g_free(unread);
 	}
 
--- a/libpurple/protocols/msn/slplink.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/msn/slplink.c	Wed May 13 18:42:52 2009 +0000
@@ -683,9 +683,9 @@
 		size = st.st_size;
 
 	if(!file_name) {
-		base = g_path_get_basename(file_path);
-		u8 = purple_utf8_try_convert(base);
-		g_free(base);
+		gchar *basename = g_path_get_basename(file_path);
+		u8 = purple_utf8_try_convert(basename);
+		g_free(basename);
 		file_name = u8;
 	}
 
--- a/libpurple/protocols/msnp9/notification.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/msnp9/notification.c	Wed May 13 18:42:52 2009 +0000
@@ -1280,14 +1280,11 @@
 
 		if (count > 0)
 		{
-			const char *passport;
-			const char *url;
+			const char *passports[2] = { msn_user_get_passport(session->user) };
+			const char *urls[2] = { session->passport_info.file };
 
-			passport = msn_user_get_passport(session->user);
-			url = session->passport_info.file;
-
-			purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL,
-							   &passport, &url, NULL, NULL);
+			purple_notify_emails(gc, count, FALSE, NULL, NULL,
+							   passports, urls, NULL, NULL);
 		}
 	}
 
--- a/libpurple/protocols/myspace/myspace.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Wed May 13 18:42:52 2009 +0000
@@ -847,8 +847,6 @@
 	MsimMessage *body;
 	guint old_inbox_status;
 	guint i, n;
-	const gchar *froms[5], *tos[5], *urls[5], *subjects[5];
-
 	/* Information for each new inbox message type. */
 	static struct
 	{
@@ -863,16 +861,22 @@
 		{ "FriendRequest", MSIM_INBOX_FRIEND_REQUEST, "http://messaging.myspace.com/index.cfm?fuseaction=mail.friendRequests", NULL },
 		{ "PictureComment", MSIM_INBOX_PICTURE_COMMENT, "http://home.myspace.com/index.cfm?fuseaction=user", NULL }
 	};
+	const gchar *froms[G_N_ELEMENTS(message_types) + 1] = { "" },
+		*tos[G_N_ELEMENTS(message_types) + 1] = { "" },
+		*urls[G_N_ELEMENTS(message_types) + 1] = { "" },
+		*subjects[G_N_ELEMENTS(message_types) + 1] = { "" };
+
+	g_return_if_fail(reply != NULL);
 
 	/* Can't write _()'d strings in array initializers. Workaround. */
+	/* khc: then use N_() in the array initializer and use _() when they are
+	   used */
 	message_types[0].text = _("New mail messages");
 	message_types[1].text = _("New blog comments");
 	message_types[2].text = _("New profile comments");
 	message_types[3].text = _("New friend requests!");
 	message_types[4].text = _("New picture comments");
 
-	g_return_if_fail(reply != NULL);
-
 	body = msim_msg_get_dictionary(reply, "body");
 
 	if (body == NULL)
@@ -882,7 +886,7 @@
 
 	n = 0;
 
-	for (i = 0; i < sizeof(message_types) / sizeof(message_types[0]); ++i) {
+	for (i = 0; i < G_N_ELEMENTS(message_types); ++i) {
 		const gchar *key;
 		guint bit;
 
--- a/libpurple/protocols/null/nullprpl.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Wed May 13 18:42:52 2009 +0000
@@ -909,7 +909,8 @@
 static void nullprpl_set_buddy_icon(PurpleConnection *gc,
                                     PurpleStoredImage *img) {
  purple_debug_info("nullprpl", "setting %s's buddy icon to %s\n",
-                   gc->account->username, purple_imgstore_get_filename(img));
+                   gc->account->username,
+                   img ? purple_imgstore_get_filename(img) : "(null)");
 }
 
 static void nullprpl_remove_group(PurpleConnection *gc, PurpleGroup *group) {
--- a/libpurple/protocols/oscar/oscar.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Wed May 13 18:42:52 2009 +0000
@@ -930,7 +930,7 @@
 	PurpleGroup *g = NULL;
 	struct buddyinfo *bi = NULL;
 	char *tmp;
-	const char *bname, *gname = NULL;
+	const char *bname = NULL, *gname = NULL;
 
 	od = purple_connection_get_protocol_data(gc);
 	account = purple_connection_get_account(gc);
@@ -938,14 +938,14 @@
 	if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
 		return;
 
-	bname = purple_buddy_get_name(b);
 	if (userinfo == NULL)
-		userinfo = aim_locate_finduserinfo(od, bname);
+		userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
 
 	if (b == NULL)
 		b = purple_find_buddy(account, userinfo->bn);
 
 	if (b != NULL) {
+		bname = purple_buddy_get_name(b);
 		g = purple_buddy_get_group(b);
 		gname = purple_group_get_name(g);
 		presence = purple_buddy_get_presence(b);
@@ -3571,8 +3571,10 @@
 				purple_account_get_username(account),
 				emailinfo->domain ? "@" : "",
 				emailinfo->domain ? emailinfo->domain : "");
+		const char *tos[2] = { to };
+		const char *urls[2] = { emailinfo->url };
 		purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL,
-				(const char **)&to, (const char **)&emailinfo->url, NULL, NULL);
+				tos, urls, NULL, NULL);
 		g_free(to);
 	}
 
--- a/libpurple/protocols/qq/qq_crypt.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/qq/qq_crypt.c	Wed May 13 18:42:52 2009 +0000
@@ -163,9 +163,11 @@
 		c32_prev[0] = crypted32[0]; c32_prev[1] = crypted32[1];
 		
 		/* set next 64 bits want to crypt*/
-		crypted_ptr += 8;
-		memcpy(crypted32, crypted_ptr, sizeof(crypted32));
-		plain32[0] = crypted32[0] ^ c32_prev[0]; plain32[1] = crypted32[1] ^ c32_prev[1];
+		if (count64 > 0) {
+			crypted_ptr += 8;
+			memcpy(crypted32, crypted_ptr, sizeof(crypted32));
+			plain32[0] = crypted32[0] ^ c32_prev[0]; plain32[1] = crypted32[1] ^ c32_prev[1];
+		}
 	}
 }
 
--- a/libpurple/protocols/yahoo/yahoo.c	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Wed May 13 18:42:52 2009 +0000
@@ -1518,10 +1518,10 @@
 		g_free(dec_subj);
 		g_free(from);
 	} else if (count > 0) {
-		const char *to = purple_account_get_username(account);
-		const char *url = yahoo_mail_url;
-
-		purple_notify_emails(gc, count, FALSE, NULL, NULL, &to, &url,
+		const char *tos[2] = { purple_account_get_username(account) };
+		const char *urls[2] = { yahoo_mail_url };
+
+		purple_notify_emails(gc, count, FALSE, NULL, NULL, tos, urls,
 						   NULL, NULL);
 	}
 }
@@ -4423,8 +4423,10 @@
 			status = purple_presence_get_active_status(purple_account_get_presence(purple_connection_get_account(gc)));
 		tmp = purple_status_get_attr_string(status, "message");
 		if (tmp != NULL) {
-			msg = yahoo_string_encode(gc, tmp, NULL);
+			gboolean utf8 = TRUE;
+			msg = yahoo_string_encode(gc, tmp, &utf8);
 			msg2 = purple_markup_strip_html(msg);
+			yahoo_packet_hash_str(pkt, 97, utf8 ? "1" : 0);
 			yahoo_packet_hash_str(pkt, 19, msg2);
 		} else {
 			/* get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
--- a/libpurple/win32/global.mak	Wed May 13 18:42:05 2009 +0000
+++ b/libpurple/win32/global.mak	Wed May 13 18:42:52 2009 +0000
@@ -103,6 +103,7 @@
 endif
 GMSGFMT ?= $(GTK_BIN)/msgfmt
 MAKENSIS ?= makensis.exe
+MAKENSISOPT ?= /
 PERL ?= /cygdrive/c/perl/bin/perl
 WINDRES ?= windres
 STRIP ?= strip
--- a/pidgin/gtkconv.c	Wed May 13 18:42:05 2009 +0000
+++ b/pidgin/gtkconv.c	Wed May 13 18:42:52 2009 +0000
@@ -8324,6 +8324,9 @@
 			gtkconv->unseen_state = state;
 	}
 
+	purple_conversation_set_data(gtkconv->active_conv, "unseen-count", GINT_TO_POINTER(gtkconv->unseen_count));
+	purple_conversation_set_data(gtkconv->active_conv, "unseen-state", GINT_TO_POINTER(gtkconv->unseen_state));
+
 	purple_conversation_update(gtkconv->active_conv, PURPLE_CONV_UPDATE_UNSEEN);
 }
 
--- a/pidgin/gtknotify.c	Wed May 13 18:42:05 2009 +0000
+++ b/pidgin/gtknotify.c	Wed May 13 18:42:52 2009 +0000
@@ -1104,6 +1104,9 @@
 	{
 		PidginNotifyMailData *data = (PidginNotifyMailData *)ui_handle;
 
+		/* Close the notification dialog */
+		pidgin_notify_emails(purple_account_get_connection(data->account),
+		                     0, FALSE, NULL, NULL, NULL, NULL);
 		if (data) {
 			g_free(data->url);
 			g_free(data);
--- a/pidgin/gtkstatusbox.c	Wed May 13 18:42:05 2009 +0000
+++ b/pidgin/gtkstatusbox.c	Wed May 13 18:42:52 2009 +0000
@@ -471,6 +471,12 @@
 			img = purple_imgstore_new_from_file(filename);
 
 		pidgin_status_box_set_buddy_icon(status_box, img);
+		if (img)
+			/*
+			 * purple_imgstore_new gives us a reference and
+			 * pidgin_status_box_set_buddy_icon also takes one.
+			 */
+			purple_imgstore_unref(img);
 	}
 
 	status_box->hand_cursor = gdk_cursor_new (GDK_HAND2);
@@ -1467,6 +1473,13 @@
 				if (filename)
 					data = pidgin_convert_buddy_icon(plug, filename, &len);
 				img = purple_buddy_icons_set_account_icon(box->account, data, len);
+				if (img)
+					/*
+					 * set_account_icon doesn't give us a reference, but we
+					 * unref one below (for the other code path)
+					 */
+					purple_imgstore_ref(img);
+
 				purple_account_set_buddy_icon_path(box->account, filename);
 
 				purple_account_set_bool(box->account, "use-global-buddyicon", (filename != NULL));
@@ -1486,7 +1499,7 @@
 					size_t len = 0;
 					if (filename)
 						data = pidgin_convert_buddy_icon(plug, filename, &len);
-					img = purple_buddy_icons_set_account_icon(account, data, len);
+					purple_buddy_icons_set_account_icon(account, data, len);
 					purple_account_set_buddy_icon_path(account, filename);
 				}
 			}
@@ -1498,6 +1511,8 @@
 	}
 
 	pidgin_status_box_set_buddy_icon(box, img);
+	if (img)
+		purple_imgstore_unref(img);
 }
 
 static void
--- a/pidgin/win32/pidgin_dll_rc.rc.in	Wed May 13 18:42:05 2009 +0000
+++ b/pidgin/win32/pidgin_dll_rc.rc.in	Wed May 13 18:42:52 2009 +0000
@@ -2,8 +2,6 @@
 #include "version.h"
 #include "resource.h"
 
-#define PIXMAPDIR "pixmaps/tray/16/"
-
 VS_VERSION_INFO VERSIONINFO
   FILEVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
   PRODUCTVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
@@ -33,11 +31,11 @@
     END
   END
 
-PIDGIN_TRAY_AVAILABLE_4BIT	ICON PIXMAPDIR "available_4bit.ico"
-PIDGIN_TRAY_AWAY_4BIT		ICON PIXMAPDIR "away_4bit.ico"
-PIDGIN_TRAY_BUSY_4BIT		ICON PIXMAPDIR "busy_4bit.ico"
-PIDGIN_TRAY_XA_4BIT		ICON PIXMAPDIR "extended-away_4bit.ico"
-PIDGIN_TRAY_OFFLINE_4BIT	ICON PIXMAPDIR "offline_4bit.ico"
-PIDGIN_TRAY_CONNECTING_4BIT	ICON PIXMAPDIR "connecting_4bit.ico"
-PIDGIN_TRAY_PENDING_4BIT	ICON PIXMAPDIR "message_4bit.ico"
-PIDGIN_TRAY_INVISIBLE_4BIT	ICON PIXMAPDIR "invisible_4bit.ico"
+PIDGIN_TRAY_AVAILABLE_4BIT	ICON "pixmaps/tray/16/available_4bit.ico"
+PIDGIN_TRAY_AWAY_4BIT		ICON "pixmaps/tray/16/away_4bit.ico"
+PIDGIN_TRAY_BUSY_4BIT		ICON "pixmaps/tray/16/busy_4bit.ico"
+PIDGIN_TRAY_XA_4BIT		ICON "pixmaps/tray/16/extended-away_4bit.ico"
+PIDGIN_TRAY_OFFLINE_4BIT	ICON "pixmaps/tray/16/offline_4bit.ico"
+PIDGIN_TRAY_CONNECTING_4BIT	ICON "pixmaps/tray/16/connecting_4bit.ico"
+PIDGIN_TRAY_PENDING_4BIT	ICON "pixmaps/tray/16/message_4bit.ico"
+PIDGIN_TRAY_INVISIBLE_4BIT	ICON "pixmaps/tray/16/invisible_4bit.ico"
--- a/pidgin/win32/pidgin_exe_rc.rc.in	Wed May 13 18:42:05 2009 +0000
+++ b/pidgin/win32/pidgin_exe_rc.rc.in	Wed May 13 18:42:52 2009 +0000
@@ -2,8 +2,6 @@
 #include "resource.h"
 #include "version.h"
 
-#define PIXMAPDIR "pixmaps/"
-
 VS_VERSION_INFO VERSIONINFO
   FILEVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
   PRODUCTVERSION PURPLE_MAJOR_VERSION,PURPLE_MINOR_VERSION,PURPLE_MICRO_VERSION,0
@@ -33,4 +31,4 @@
     END
   END
 
-PIDGIN_ICON			ICON PIXMAPDIR "pidgin.ico"
+PIDGIN_ICON			ICON "pixmaps/pidgin.ico"
--- a/po/ca.po	Wed May 13 18:42:05 2009 +0000
+++ b/po/ca.po	Wed May 13 18:42:52 2009 +0000
@@ -33,8 +33,8 @@
 msgstr ""
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-05-03 18:12+0200\n"
-"PO-Revision-Date: 2009-05-03 18:15+0200\n"
+"POT-Creation-Date: 2009-05-06 19:49+0200\n"
+"PO-Revision-Date: 2009-05-06 23:20+0200\n"
 "Last-Translator: Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>\n"
 "Language-Team: Catalan <tradgnome@softcatala.net>\n"
 "MIME-Version: 1.0\n"
@@ -639,19 +639,6 @@
 msgid "Send To"
 msgstr "Envia a"
 
-msgid "Invite message"
-msgstr "Missatge d'invitació"
-
-msgid "Invite"
-msgstr "Convida"
-
-msgid ""
-"Please enter the name of the user you wish to invite,\n"
-"along with an optional invite message."
-msgstr ""
-"Introduïu el nom de l'usuari que vulgueu convidar,\n"
-"així com un missatge d'invitació opcional."
-
 msgid "Conversation"
 msgstr "Conversa"
 
@@ -915,6 +902,41 @@
 msgid "System Log"
 msgstr "Registre del sistema"
 
+msgid "Calling ... "
+msgstr "S'està trucant..."
+
+msgid "Hangup"
+msgstr "Penja"
+
+#. Number of actions
+msgid "Accept"
+msgstr "Accepta"
+
+msgid "Reject"
+msgstr "Rebutja"
+
+msgid "Call in progress."
+msgstr "S'està fent una trucada."
+
+msgid "The call has been terminated."
+msgstr "Ha finalitzat la trucada."
+
+#, c-format
+msgid "%s wishes to start an audio session with you."
+msgstr "%s vol iniciar una sessió d'àudio."
+
+#, c-format
+msgid "%s is trying to start an unsupported media session type with you."
+msgstr ""
+"%s està intentant iniciar una sessió amb medi d'un tipus que no està "
+"implementat."
+
+msgid "You have rejected the call."
+msgstr "Heu rebutjat la trucada."
+
+msgid "call: Make an audio call."
+msgstr "call: fa una trucada d'àudio."
+
 msgid "Emails"
 msgstr "Correus electrònics"
 
@@ -949,6 +971,9 @@
 msgid "IM"
 msgstr "MI"
 
+msgid "Invite"
+msgstr "Convida"
+
 msgid "(none)"
 msgstr "(cap)"
 
@@ -1564,6 +1589,30 @@
 msgid "Lastlog plugin."
 msgstr "Connector lastlog."
 
+#, c-format
+msgid ""
+"\n"
+"Fetching TinyURL..."
+msgstr ""
+"\n"
+"S'està aconseguint una TinyURL..."
+
+msgid "Only create TinyURL for urls of this length or greater"
+msgstr "Crea TinyURL per a URL així de llargues o més"
+
+msgid "TinyURL (or other) address prefix"
+msgstr "Prefix de l'adreça TinyURL (o altra)"
+
+msgid "TinyURL"
+msgstr "TinyURL"
+
+msgid "TinyURL plugin"
+msgstr "Connector TinyURL"
+
+msgid "When receiving a message with URL(s), TinyURL for easier copying"
+msgstr ""
+"Quan rebeu missagtes amb URL, feu servir TinyURL per a copiar més fàcilment"
+
 msgid "accounts"
 msgstr "comptes"
 
@@ -1665,13 +1714,6 @@
 msgid "SSL Certificate Verification"
 msgstr "Verificació d'un certificat SSL"
 
-#. Number of actions
-msgid "Accept"
-msgstr "Accepta"
-
-msgid "Reject"
-msgstr "Rebutja"
-
 msgid "_View Certificate..."
 msgstr "_Mostra el certificat..."
 
@@ -1820,6 +1862,17 @@
 msgid "%s left the room (%s)."
 msgstr "%s ha sortit de la sala (%s)."
 
+msgid "Invite to chat"
+msgstr "Convida al xat"
+
+#. Put our happy label in it.
+msgid ""
+"Please enter the name of the user you wish to invite, along with an optional "
+"invite message."
+msgstr ""
+"Introduïu el nom de l'usuari que vulgueu convidar, així com un missatge "
+"d'invitació opcional."
+
 #, c-format
 msgid "Failed to get connection: %s"
 msgstr "No s'ha pogut obtenir la connexió: %s"
@@ -2653,6 +2706,36 @@
 msgid "Do not ask. Always save in pounce."
 msgstr "No ho demanis, desa-ho sempre en un avís."
 
+msgid "One Time Password"
+msgstr "Contrasenya d'un sol ús"
+
+# FIXME ?
+#. *< type
+#. *< ui_requirement
+#. *< flags
+#. *< dependencies
+#. *< priority
+#. *< id
+msgid "One Time Password Support"
+msgstr "Contrasenyes d'un sol us"
+
+#. *< name
+#. *< version
+#. *  summary
+msgid "Enforce that passwords are used only once."
+msgstr "Força que les contrasenyes només s'emprin una vegada."
+
+#. *  description
+msgid ""
+"Allows you to enforce on a per-account basis that passwords not being saved "
+"are only used in a single successful connection.\n"
+"Note: The account password must not be saved for this to work."
+msgstr ""
+"Us permet forçar que les contrasenyes siguin d'un sol us per a comptes dels "
+"quals no es desin les contrasenyes.\n"
+"Nota: per poder fer servir això, cal que no es desi la contrasenya del "
+"compte."
+
 #. *< type
 #. *< ui_requirement
 #. *< flags
@@ -3055,6 +3138,7 @@
 msgid "Add to chat..."
 msgstr "Afegeix al xat..."
 
+#. Global
 msgid "Available"
 msgstr "Disponible"
 
@@ -3401,6 +3485,16 @@
 "El servidor ha rebutjat el nom del compte que heu triat. Possiblement conté "
 "caràcters invàlids."
 
+#. We only want to do the following dance if the connection
+#. has not been successfully completed.  If it has, just
+#. notify the user that their /nick command didn't go.
+#, c-format
+msgid "The nickname \"%s\" is already being used."
+msgstr "Ja hi ha algú amb el sobrenom «%s»."
+
+msgid "Nickname in use"
+msgstr "El sobrenom ja s'està fent servir"
+
 msgid "Cannot change nick"
 msgstr "No es pot canviar el sobrenom"
 
@@ -3673,6 +3767,35 @@
 msgid "SASL error"
 msgstr "Error en el SASL"
 
+msgid "The BOSH connection manager terminated your session."
+msgstr "El gestor de connexions BOSH ha tancat la connexió."
+
+msgid "No session ID given"
+msgstr "No s'ha indicat cap ID"
+
+msgid "Unsupported version of BOSH protocol"
+msgstr "Aquesta versió del protocol BOSH no està implementada"
+
+msgid "Unable to establish a connection with the server"
+msgstr "No s'ha pogut establir una connexió amb al servidor"
+
+#, c-format
+msgid ""
+"Could not establish a connection with the server:\n"
+"%s"
+msgstr ""
+"No s'ha pogut establir una connexió amb al servidor:\n"
+"%s"
+
+msgid "Unable to establish SSL connection"
+msgstr "No s'ha pogut establir una connexió SSL"
+
+msgid "Unable to create socket"
+msgstr "No s'ha pogut crear el sòcol"
+
+msgid "Write error"
+msgstr "Error d'escriptura"
+
 msgid "Full Name"
 msgstr "Nom"
 
@@ -3739,6 +3862,9 @@
 msgid "Operating System"
 msgstr "Sistema operatiu"
 
+msgid "Local Time"
+msgstr "Hora local"
+
 msgid "Last Activity"
 msgstr "Darrera activitat"
 
@@ -4079,12 +4205,18 @@
 msgid "Find Rooms"
 msgstr "Cerca sales"
 
+msgid "Affiliations:"
+msgstr "Afiliacions:"
+
+msgid "No users found"
+msgstr "No s'ha trobat cap usuari"
+
+msgid "Roles:"
+msgstr "Rols:"
+
 msgid "You require encryption, but it is not available on this server."
 msgstr "Requeriu xifratge, però no està disponible en aquest servidor."
 
-msgid "Write error"
-msgstr "Error d'escriptura"
-
 msgid "Ping timeout"
 msgstr "Temps d'espera del ping"
 
@@ -4093,14 +4225,11 @@
 
 #, c-format
 msgid ""
-"Could not establish a connection with the server:\n"
-"%s"
-msgstr ""
-"No s'ha pogut establir una connexió amb al servidor:\n"
-"%s"
-
-msgid "Unable to create socket"
-msgstr "No s'ha pogut crear el sòcol"
+"Could not find alternative XMPP connection methods after failing to connect "
+"directly.\n"
+msgstr ""
+"No s'ha pogut trobar cap mètode alternatiu de connexió XMPP després de no "
+"haver pogut connectar directament.\n"
 
 msgid "Invalid XMPP ID"
 msgstr "ID de l'XMPP invàlid"
@@ -4108,6 +4237,9 @@
 msgid "Invalid XMPP ID. Domain must be set."
 msgstr "L'ID de l'XMPP no és vàlid. Cal especificar un domini."
 
+msgid "Malformed BOSH Connect Server"
+msgstr ""
+
 #, c-format
 msgid "Registration of %s@%s successful"
 msgstr "S'ha registrat %s a %s amb èxit"
@@ -4198,6 +4330,13 @@
 msgid "Not Authorized"
 msgstr "No autoritzat"
 
+msgid "Mood"
+msgstr "Estat d'ànim"
+
+# FIXME?
+msgid "Now Listening"
+msgstr "Ara escoltant"
+
 msgid "Both"
 msgstr "Ambdós"
 
@@ -4219,13 +4358,6 @@
 msgid "Subscription"
 msgstr "Subscripció"
 
-msgid "Mood"
-msgstr "Estat d'ànim"
-
-# FIXME?
-msgid "Now Listening"
-msgstr "Ara escoltant"
-
 msgid "Mood Text"
 msgstr "Text sobre l'estat d'ànim"
 
@@ -4496,6 +4628,37 @@
 msgid "%s has buzzed you!"
 msgstr "%s us ha botzinat!"
 
+#, c-format
+msgid "Unable to initiate media with %s: invalid JID"
+msgstr "No s'ha pogut iniciar el medi amb %s: el JID no és vàlid"
+
+#, c-format
+msgid "Unable to initiate media with %s: user is not online"
+msgstr "No s'ha pogut iniciar el medi amb %s: l'usuari no està connectat"
+
+#, c-format
+msgid "Unable to initiate media with %s: not subscribed to user presence"
+msgstr ""
+"No s'ha pogut iniciar el medi amb %s: no esteu subscrit a la presència de "
+"l'usuari"
+
+msgid "Media Initiation Failed"
+msgstr "Ha fallat la iniciació del medi"
+
+# FIXME: "media session" -> "sessió amb medi"?
+#, c-format
+msgid ""
+"Please select the resource of %s with which you would like to start a media "
+"session."
+msgstr ""
+"Escolliu el recurs de %s amb el qual voleu iniciar una sessió amb medi."
+
+msgid "Select a Resource"
+msgstr "Seleccioneu un recurs"
+
+msgid "Initiate Media"
+msgstr "Inicia el medi"
+
 msgid "config:  Configure a chat room."
 msgstr "config:  configura la sala de xat."
 
@@ -4515,18 +4678,18 @@
 msgstr "ban &lt;user&gt; [motiu]:  bandeja de la sala un usuari."
 
 msgid ""
-"affiliate &lt;user&gt; &lt;owner|admin|member|outcast|none&gt;: Set a user's "
-"affiliation with the room."
-msgstr ""
-"affiliate &lt;usuari&gt; &lt;owner|admin|member|outcast|none&gt;: estableix "
-"l'afiliació de l'usuari a la sala."
-
-msgid ""
-"role &lt;user&gt; &lt;moderator|participant|visitor|none&gt;: Set a user's "
-"role in the room."
-msgstr ""
-"role &lt;usuari&gt; &lt;moderator|participant|visitor|none&gt;: estableix el "
-"rol d'un usuari en una sala."
+"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;owner|admin|member|outcast|none&gt; [sobrenom1] "
+"[sobrenom2] ...: obtén els usuaris amb una afiliació, o els l'estableix."
+
+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;usuari&gt; &lt;moderator|participant|visitor|none&gt; [sobrenom1] "
+"[sobrenom2] ...: obtén els usuaris amb el rol especificat, o els l'estableix."
 
 msgid "invite &lt;user&gt; [message]:  Invite a user to the room."
 msgstr "invite &lt;usuari&gt; [sala]:  convida un usuari a la sala."
@@ -4690,9 +4853,6 @@
 msgid "Please select the resource of %s to which you would like to send a file"
 msgstr "Escolliu a quin recurs de %s voleu enviar un fitxer"
 
-msgid "Select a Resource"
-msgstr "Seleccioneu un recurs"
-
 msgid "Edit User Mood"
 msgstr "Edita l'estat d'ànim"
 
@@ -5345,14 +5505,17 @@
 msgid "%s just sent you a Nudge!"
 msgstr "%s us ha donat un cop de colze!"
 
-#. char *adl = g_strndup(payload, len);
+#, c-format
+msgid "Unknown error (%d): %s"
+msgstr "Error desconegut (%d): %s"
+
+msgid "Unable to add user"
+msgstr "No s'ha pogut afegir l'usuari"
+
 #, c-format
 msgid "Unknown error (%d)"
 msgstr "Error desconegut (%d)"
 
-msgid "Unable to add user"
-msgstr "No s'ha pogut afegir l'usuari"
-
 msgid "The following users are missing from your addressbook"
 msgstr "Manquen aquests usuaris a la vostra llista d'amics"
 
@@ -7665,6 +7828,9 @@
 msgid "<p><b>Scrupulous Testers</b>:<br>\n"
 msgstr "<p><b>Comprovadors del codi</b>:<br>\n"
 
+msgid "and more, please let me know... thank you!))"
+msgstr "i més, si us plau feu-m'ho saber... gràcies!!!))"
+
 # FIXME: ush... traducció lliure... 
 msgid "<p><i>And, all the boys in the backroom...</i><br>\n"
 msgstr "<p><i>I tothom que ho ha fet possible...<i><br>\n"
@@ -7804,7 +7970,6 @@
 "No s'ha reconegut el codi de resposta en entrar (0x%02X):\n"
 "%s"
 
-#. we didn't successfully connect. tdt->toc_fd is valid here
 msgid "Unable to connect."
 msgstr "No s'ha pogut connectar."
 
@@ -9406,195 +9571,6 @@
 msgstr "Domini Auth"
 
 #, c-format
-msgid "Looking up %s"
-msgstr "S'està cercant %s"
-
-#, c-format
-msgid "Connect to %s failed"
-msgstr "Ha fallat la connexió a %s"
-
-#, c-format
-msgid "Signon: %s"
-msgstr "Entrada: %s"
-
-#, c-format
-msgid "Unable to write file %s."
-msgstr "No s'ha pogut escriure el fitxer %s."
-
-#, c-format
-msgid "Unable to read file %s."
-msgstr "No s'ha pogut llegir el fitxer %s."
-
-#, c-format
-msgid "Message too long, last %s bytes truncated."
-msgstr "El missatge és massa llarg, s'han retallat els darrers %s octets."
-
-#, c-format
-msgid "%s not currently logged in."
-msgstr "%s no està connectat."
-
-#, c-format
-msgid "Warning of %s not allowed."
-msgstr "Avís de %s no permès."
-
-#, c-format
-msgid "A message has been dropped, you are exceeding the server speed limit."
-msgstr ""
-"S'ha ignorat un missatge. Esteu excedint el límit de velocitat del servidor."
-
-#, c-format
-msgid "Chat in %s is not available."
-msgstr "El xat a %s no està disponible."
-
-#, c-format
-msgid "You are sending messages too fast to %s."
-msgstr "Esteu enviant missatges massa de pressa a %s."
-
-#, c-format
-msgid "You missed an IM from %s because it was too big."
-msgstr "Us heu perdut un missatge instantani de %s perquè era massa gran."
-
-#, c-format
-msgid "You missed an IM from %s because it was sent too fast."
-msgstr ""
-"Heu perdut un missatge instantani de %s perquè s'ha enviat massa de pressa."
-
-#, c-format
-msgid "Failure."
-msgstr "Fallada."
-
-#, c-format
-msgid "Too many matches."
-msgstr "Massa coincidències."
-
-#, c-format
-msgid "Need more qualifiers."
-msgstr "Es necessiten més qualificadors."
-
-#, c-format
-msgid "Dir service temporarily unavailable."
-msgstr "Servei de directori no disponible temporalment."
-
-#, c-format
-msgid "Email lookup restricted."
-msgstr "Recerca per adreça de correu electrònic restringida."
-
-#, c-format
-msgid "Keyword ignored."
-msgstr "S'ha ignorat la paraula clau."
-
-#, c-format
-msgid "No keywords."
-msgstr "No hi ha paraules clau."
-
-#, c-format
-msgid "User has no directory information."
-msgstr "L'usuari no té informació al directori."
-
-# FIXME
-#, c-format
-msgid "Country not supported."
-msgstr "Aquest país no està disponible."
-
-#, c-format
-msgid "Failure unknown: %s."
-msgstr "Fallada desconeguda: %s."
-
-#, c-format
-msgid "Incorrect username or password."
-msgstr "El nom d'usuari o la contrasenya no són correctes."
-
-#, c-format
-msgid "The service is temporarily unavailable."
-msgstr "El servei està temporalment no disponible."
-
-#, c-format
-msgid "Your warning level is currently too high to log in."
-msgstr "El vostre nivell d'avisos és massa alt per a connectar-se."
-
-#, c-format
-msgid ""
-"You have been connecting and disconnecting too frequently.  Wait ten minutes "
-"and try again.  If you continue to try, you will need to wait even longer."
-msgstr ""
-"Us heu estat connectant i desconnectant amb massa freqüència. Espereu deu "
-"minuts i torneu-ho a provar. Si continueu intentant-ho, haureu d'esperar "
-"encara més."
-
-#, c-format
-msgid "An unknown signon error has occurred: %s."
-msgstr "Hi ha hagut un error de connexió desconegut: %s."
-
-#, c-format
-msgid "An unknown error, %d, has occurred.  Info: %s"
-msgstr "S'ha produït un error desconegut, %d. Informació: %s"
-
-msgid "Invalid Groupname"
-msgstr "El nom del grup no és vàlid"
-
-msgid "Connection Closed"
-msgstr "Connexió tancada"
-
-msgid "Waiting for reply..."
-msgstr "S'està esperant una resposta..."
-
-msgid "TOC has come back from its pause. You may now send messages again."
-msgstr "TOC ha tornat de la pausa. Ja podeu enviar missatges de nou."
-
-msgid "Password Change Successful"
-msgstr "S'ha canviat la contrasenya amb èxit"
-
-msgid "_Group:"
-msgstr "_Grup:"
-
-msgid "Get Dir Info"
-msgstr "Aconsegueix informació del directori"
-
-msgid "Set Dir Info"
-msgstr "Estableix informació del directori"
-
-#, c-format
-msgid "Could not open %s for writing!"
-msgstr "No s'ha pogut obrir %s per a escriure-hi."
-
-msgid "File transfer failed; other side probably canceled."
-msgstr ""
-"Ha fallat la transferència de fitxers. Probablement s'ha cancel·lat a "
-"l'altra banda."
-
-msgid "Could not connect for transfer."
-msgstr "No s'ha pogut connectar per realitzar la transferència."
-
-msgid "Could not write file header.  The file will not be transferred."
-msgstr "No s'ha pogut escriure la capçalera del fitxer. No s'enviarà."
-
-msgid "Save As..."
-msgstr "Anomena i desa..."
-
-#, c-format
-msgid "%s requests %s to accept %d file: %s (%.2f %s)%s%s"
-msgid_plural "%s requests %s to accept %d files: %s (%.2f %s)%s%s"
-msgstr[0] "%s demana a %s que accepti %d fitxer: %s (%.2f %s)%s%s"
-msgstr[1] "%s demana a %s que accepti %d fitxers: %s (%.2f %s)%s%s"
-
-#, c-format
-msgid "%s requests you to send them a file"
-msgstr "%s us demana que li envieu un fitxer"
-
-#. *< type
-#. *< ui_requirement
-#. *< flags
-#. *< dependencies
-#. *< priority
-#. *< id
-#. *< name
-#. *< version
-#. *  summary
-#. *  description
-msgid "TOC Protocol Plugin"
-msgstr "Connector per al protocol TOC"
-
-#, c-format
 msgid "%s has sent you a webcam invite, which is not yet supported."
 msgstr ""
 "%s us ha enviat una invitació a la seva càmera web, però això encara no està "
@@ -10427,10 +10403,8 @@
 msgid "Use this buddy _icon for this account:"
 msgstr "Utilitza aquesta _icona d'amic per a aquest compte:"
 
-#. Build the protocol options frame.
-#, c-format
-msgid "%s Options"
-msgstr "Opcions de %s"
+msgid "_Advanced"
+msgstr "_Avançat"
 
 msgid "Use GNOME Proxy Settings"
 msgstr "Empra la configuració del servidor intermediari del Gnome"
@@ -10465,9 +10439,6 @@
 msgid "you can see the butterflies mating"
 msgstr "podreu veure les papallones aparellant-se"
 
-msgid "Proxy Options"
-msgstr "Opcions del servidor intermediari"
-
 msgid "Proxy _type:"
 msgstr "_Tipus de servidor intermediari"
 
@@ -10495,8 +10466,8 @@
 msgid "Create _this new account on the server"
 msgstr "Crea _aquest compte nou al servidor"
 
-msgid "_Advanced"
-msgstr "_Avançat"
+msgid "_Proxy"
+msgstr "Servidor _intermediari"
 
 msgid "Enabled"
 msgstr "Habilitat"
@@ -10575,6 +10546,15 @@
 msgid "I_M"
 msgstr "_MI"
 
+msgid "_Audio Call"
+msgstr "_Trucada amb àudio"
+
+msgid "Audio/_Video Call"
+msgstr "Trucada d'àudio i _vídeo"
+
+msgid "_Video Call"
+msgstr "Trucada de _vídeo"
+
 msgid "_Send File..."
 msgstr "_Envia un fitxer..."
 
@@ -10947,6 +10927,9 @@
 msgid "A_lias:"
 msgstr "Àl_ies:"
 
+msgid "_Group:"
+msgstr "_Grup:"
+
 msgid "Auto_join when account becomes online."
 msgstr "_Entra automàticament quant el compte estigui connectat."
 
@@ -10997,14 +10980,6 @@
 msgid "Invite Buddy Into Chat Room"
 msgstr "Convida l'amic a una sala de xat"
 
-#. Put our happy label in it.
-msgid ""
-"Please enter the name of the user you wish to invite, along with an optional "
-"invite message."
-msgstr ""
-"Introduïu el nom de l'usuari que vulgueu convidar, així com un missatge "
-"d'invitació opcional."
-
 msgid "_Buddy:"
 msgstr "_Amic:"
 
@@ -11080,6 +11055,18 @@
 msgid "/Conversation/Clea_r Scrollback"
 msgstr "/Conversa/_Neteja la finestra"
 
+msgid "/Conversation/M_edia"
+msgstr "/Conversa/M_edi"
+
+msgid "/Conversation/Media/_Audio Call"
+msgstr "/Conversa/Medi/_Trucada d'àudio"
+
+msgid "/Conversation/Media/_Video Call"
+msgstr "/Conversa/Medi/Trucada de _vídeo"
+
+msgid "/Conversation/Media/Audio\\/Video _Call"
+msgstr "/Conversa/Medi/Tru_cada d'àudio i vídeo"
+
 msgid "/Conversation/Se_nd File..."
 msgstr "/Conversa/Envia un _fitxer..."
 
@@ -11152,6 +11139,15 @@
 msgid "/Conversation/View Log"
 msgstr "/Conversa/Visualitza el registre"
 
+msgid "/Conversation/Media/Audio Call"
+msgstr "/Conversa/Medi/Trucada d'àudio"
+
+msgid "/Conversation/Media/Video Call"
+msgstr "/Conversa/Medi/Trucada de vídeo"
+
+msgid "/Conversation/Media/Audio\\/Video Call"
+msgstr "/Conversa/Medi/Trucada d'àudio i vídeo"
+
 msgid "/Conversation/Send File..."
 msgstr "/Conversa/Envia un fitxer..."
 
@@ -11338,6 +11334,9 @@
 msgid "Ka-Hing Cheung"
 msgstr "Ka-Hing Cheung"
 
+msgid "voice and video"
+msgstr "veu i vídeo"
+
 msgid "support"
 msgstr "suport"
 
@@ -12322,6 +12321,23 @@
 "Ara se sortirà atès que ja hi ha un altre client del libpurple executant-"
 "se.\n"
 
+msgid "/_Media"
+msgstr "/_Medi"
+
+msgid "/Media/_Hangup"
+msgstr "/Medi/_Penja"
+
+msgid "Calling..."
+msgstr "S'està trucant..."
+
+#, c-format
+msgid "%s wishes to start an audio/video session with you."
+msgstr "%s vol iniciar una sessió d'àudio/vídeo."
+
+#, c-format
+msgid "%s wishes to start a video session with you."
+msgstr "%s vol iniciar una sessió de vídeo."
+
 #, c-format
 msgid "%s has %d new message."
 msgid_plural "%s has %d new messages."
@@ -12679,9 +12695,6 @@
 msgid "Cannot start browser configuration program."
 msgstr "No s'ha pogut iniciar el programa de configuració del navegador."
 
-msgid "ST_UN server:"
-msgstr "Servidor ST_UN:"
-
 msgid "<span style=\"italic\">Example: stunserver.org</span>"
 msgstr "<span style=\"italic\">Exemple: stunserver.org</span>"
 
@@ -12706,6 +12719,10 @@
 msgid "_End port:"
 msgstr "Port _final:"
 
+#. TURN server
+msgid "Relay Server (TURN)"
+msgstr ""
+
 msgid "Proxy Server &amp; Browser"
 msgstr "Servidor intermediari i navegador"
 
@@ -14267,6 +14284,173 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr "Aquest connector és útil per a depurar servidors i clients XMPP."
 
+#~ msgid "Invite message"
+#~ msgstr "Missatge d'invitació"
+
+#~ msgid ""
+#~ "Please enter the name of the user you wish to invite,\n"
+#~ "along with an optional invite message."
+#~ msgstr ""
+#~ "Introduïu el nom de l'usuari que vulgueu convidar,\n"
+#~ "així com un missatge d'invitació opcional."
+
+#~ msgid "Looking up %s"
+#~ msgstr "S'està cercant %s"
+
+#~ msgid "Connect to %s failed"
+#~ msgstr "Ha fallat la connexió a %s"
+
+#~ msgid "Signon: %s"
+#~ msgstr "Entrada: %s"
+
+#~ msgid "Unable to write file %s."
+#~ msgstr "No s'ha pogut escriure el fitxer %s."
+
+#~ msgid "Unable to read file %s."
+#~ msgstr "No s'ha pogut llegir el fitxer %s."
+
+#~ msgid "Message too long, last %s bytes truncated."
+#~ msgstr "El missatge és massa llarg, s'han retallat els darrers %s octets."
+
+#~ msgid "%s not currently logged in."
+#~ msgstr "%s no està connectat."
+
+#~ msgid "Warning of %s not allowed."
+#~ msgstr "Avís de %s no permès."
+
+#~ msgid ""
+#~ "A message has been dropped, you are exceeding the server speed limit."
+#~ msgstr ""
+#~ "S'ha ignorat un missatge. Esteu excedint el límit de velocitat del "
+#~ "servidor."
+
+#~ msgid "Chat in %s is not available."
+#~ msgstr "El xat a %s no està disponible."
+
+#~ msgid "You are sending messages too fast to %s."
+#~ msgstr "Esteu enviant missatges massa de pressa a %s."
+
+#~ msgid "You missed an IM from %s because it was too big."
+#~ msgstr "Us heu perdut un missatge instantani de %s perquè era massa gran."
+
+#~ msgid "You missed an IM from %s because it was sent too fast."
+#~ msgstr ""
+#~ "Heu perdut un missatge instantani de %s perquè s'ha enviat massa de "
+#~ "pressa."
+
+#~ msgid "Failure."
+#~ msgstr "Fallada."
+
+#~ msgid "Too many matches."
+#~ msgstr "Massa coincidències."
+
+#~ msgid "Need more qualifiers."
+#~ msgstr "Es necessiten més qualificadors."
+
+#~ msgid "Dir service temporarily unavailable."
+#~ msgstr "Servei de directori no disponible temporalment."
+
+#~ msgid "Email lookup restricted."
+#~ msgstr "Recerca per adreça de correu electrònic restringida."
+
+#~ msgid "Keyword ignored."
+#~ msgstr "S'ha ignorat la paraula clau."
+
+#~ msgid "No keywords."
+#~ msgstr "No hi ha paraules clau."
+
+#~ msgid "User has no directory information."
+#~ msgstr "L'usuari no té informació al directori."
+
+# FIXME
+#~ msgid "Country not supported."
+#~ msgstr "Aquest país no està disponible."
+
+#~ msgid "Failure unknown: %s."
+#~ msgstr "Fallada desconeguda: %s."
+
+#~ msgid "Incorrect username or password."
+#~ msgstr "El nom d'usuari o la contrasenya no són correctes."
+
+#~ msgid "The service is temporarily unavailable."
+#~ msgstr "El servei està temporalment no disponible."
+
+#~ msgid "Your warning level is currently too high to log in."
+#~ msgstr "El vostre nivell d'avisos és massa alt per a connectar-se."
+
+#~ msgid ""
+#~ "You have been connecting and disconnecting too frequently.  Wait ten "
+#~ "minutes and try again.  If you continue to try, you will need to wait "
+#~ "even longer."
+#~ msgstr ""
+#~ "Us heu estat connectant i desconnectant amb massa freqüència. Espereu deu "
+#~ "minuts i torneu-ho a provar. Si continueu intentant-ho, haureu d'esperar "
+#~ "encara més."
+
+#~ msgid "An unknown signon error has occurred: %s."
+#~ msgstr "Hi ha hagut un error de connexió desconegut: %s."
+
+#~ msgid "An unknown error, %d, has occurred.  Info: %s"
+#~ msgstr "S'ha produït un error desconegut, %d. Informació: %s"
+
+#~ msgid "Invalid Groupname"
+#~ msgstr "El nom del grup no és vàlid"
+
+#~ msgid "Connection Closed"
+#~ msgstr "Connexió tancada"
+
+#~ msgid "Waiting for reply..."
+#~ msgstr "S'està esperant una resposta..."
+
+#~ msgid "TOC has come back from its pause. You may now send messages again."
+#~ msgstr "TOC ha tornat de la pausa. Ja podeu enviar missatges de nou."
+
+#~ msgid "Password Change Successful"
+#~ msgstr "S'ha canviat la contrasenya amb èxit"
+
+#~ msgid "Get Dir Info"
+#~ msgstr "Aconsegueix informació del directori"
+
+#~ msgid "Set Dir Info"
+#~ msgstr "Estableix informació del directori"
+
+#~ msgid "Could not open %s for writing!"
+#~ msgstr "No s'ha pogut obrir %s per a escriure-hi."
+
+#~ msgid "File transfer failed; other side probably canceled."
+#~ msgstr ""
+#~ "Ha fallat la transferència de fitxers. Probablement s'ha cancel·lat a "
+#~ "l'altra banda."
+
+#~ msgid "Could not connect for transfer."
+#~ msgstr "No s'ha pogut connectar per realitzar la transferència."
+
+#~ msgid "Could not write file header.  The file will not be transferred."
+#~ msgstr "No s'ha pogut escriure la capçalera del fitxer. No s'enviarà."
+
+#~ msgid "Save As..."
+#~ msgstr "Anomena i desa..."
+
+#~ msgid "%s requests %s to accept %d file: %s (%.2f %s)%s%s"
+#~ msgid_plural "%s requests %s to accept %d files: %s (%.2f %s)%s%s"
+#~ msgstr[0] "%s demana a %s que accepti %d fitxer: %s (%.2f %s)%s%s"
+#~ msgstr[1] "%s demana a %s que accepti %d fitxers: %s (%.2f %s)%s%s"
+
+#~ msgid "%s requests you to send them a file"
+#~ msgstr "%s us demana que li envieu un fitxer"
+
+#~ msgid "TOC Protocol Plugin"
+#~ msgstr "Connector per al protocol TOC"
+
+#~ msgid "%s Options"
+#~ msgstr "Opcions de %s"
+
+#~ msgid "Proxy Options"
+#~ msgstr "Opcions del servidor intermediari"
+
+#~ msgid "ST_UN server:"
+#~ msgstr "Servidor ST_UN:"
+
 #~ msgid "By log size"
 #~ msgstr "Per la mida del registre"
 
@@ -14986,9 +15170,6 @@
 #~ "\n"
 #~ "Inactiu: %s"
 
-#~ msgid "Nickname: %s\n"
-#~ msgstr "Sobrenom: %s\n"
-
 #~ msgid ""
 #~ "\n"
 #~ "<b>Nickname:</b> %s"
@@ -15460,9 +15641,6 @@
 #~ msgid "Call ended."
 #~ msgstr "Ha finalitzat la trucada."
 
-#~ msgid "End Call"
-#~ msgstr "Finalitza la trucada"
-
 #~ msgid "Receiving call from %s"
 #~ msgstr "S'està rebent una trucada de %s"