# HG changeset patch # User Paul Aurich # Date 1242240172 0 # Node ID 37436c98201e5fa5e1e2877e9e2b36cb33a7b924 # Parent 572a0b620d4af4e3e6be6735f14f9d917423a4f5# Parent 727d960a75a452f2b599ea001045da5e8b743d18 propagate from branch 'im.pidgin.pidgin' (head de00ba4608f6410625b80dbfb07287b668eee937) to branch 'im.pidgin.cpw.sulabh.yahoo_16' (head 162a734100aafd62b7338baa81b04510c6b66921) diff -r 572a0b620d4a -r 37436c98201e COPYRIGHT --- 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 diff -r 572a0b620d4a -r 37436c98201e ChangeLog --- 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 diff -r 572a0b620d4a -r 37436c98201e ChangeLog.API --- 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 diff -r 572a0b620d4a -r 37436c98201e Makefile.mingw --- 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 diff -r 572a0b620d4a -r 37436c98201e libpurple/dbus-analyze-functions.py --- 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. diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/buddy.c --- 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); } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/buddy.h --- 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); diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/caps.c --- 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) { diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/caps.h --- 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); /** diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/disco.h --- 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 * diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/google.c --- 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"); diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/iq.c --- 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; } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/iq.h --- 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_ */ diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/jabber.c --- 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(); } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/jabber.h --- 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, diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/libxmpp.c --- 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); diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/message.c --- 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
(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, "
", 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("%s", 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("%s", 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("%s", 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("%s", xhtml); + + g_free(xhtml); jabber_message_send(jm); jabber_message_free(jm); diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/oob.h --- 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 * diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/ping.h --- 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 * diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/presence.c --- 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); + } } } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/jabber/si.h --- 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 * diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/msn/notification.c --- 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); } } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/msn/oim.c --- 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); } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/msn/slplink.c --- 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; } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/msnp9/notification.c --- 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); } } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/myspace/myspace.c --- 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; diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/null/nullprpl.c --- 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) { diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/oscar/oscar.c --- 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); } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/qq/qq_crypt.c --- 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]; + } } } diff -r 572a0b620d4a -r 37436c98201e libpurple/protocols/yahoo/yahoo.c --- 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 diff -r 572a0b620d4a -r 37436c98201e libpurple/win32/global.mak --- 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 diff -r 572a0b620d4a -r 37436c98201e pidgin/gtkconv.c --- 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); } diff -r 572a0b620d4a -r 37436c98201e pidgin/gtknotify.c --- 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); diff -r 572a0b620d4a -r 37436c98201e pidgin/gtkstatusbox.c --- 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 diff -r 572a0b620d4a -r 37436c98201e pidgin/win32/pidgin_dll_rc.rc.in --- 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" diff -r 572a0b620d4a -r 37436c98201e pidgin/win32/pidgin_exe_rc.rc.in --- 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" diff -r 572a0b620d4a -r 37436c98201e po/ca.po --- 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ó \n" "Language-Team: Catalan \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 <user> [motiu]: bandeja de la sala un usuari." msgid "" -"affiliate <user> <owner|admin|member|outcast|none>: Set a user's " -"affiliation with the room." -msgstr "" -"affiliate <usuari> <owner|admin|member|outcast|none>: estableix " -"l'afiliació de l'usuari a la sala." - -msgid "" -"role <user> <moderator|participant|visitor|none>: Set a user's " -"role in the room." -msgstr "" -"role <usuari> <moderator|participant|visitor|none>: estableix el " -"rol d'un usuari en una sala." +"affiliate <owner|admin|member|outcast|none> [nick1] [nick2] ...: Get " +"the users with an affiliation or set users' affiliation with the room." +msgstr "" +"affiliate <owner|admin|member|outcast|none> [sobrenom1] " +"[sobrenom2] ...: obtén els usuaris amb una afiliació, o els l'estableix." + +msgid "" +"role <moderator|participant|visitor|none> [nick1] [nick2] ...: Get the " +"users with an role or set users' role with the room." +msgstr "" +"role <usuari> <moderator|participant|visitor|none> [sobrenom1] " +"[sobrenom2] ...: obtén els usuaris amb el rol especificat, o els l'estableix." msgid "invite <user> [message]: Invite a user to the room." msgstr "invite <usuari> [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 "

Scrupulous Testers:
\n" msgstr "

Comprovadors del codi:
\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 "

And, all the boys in the backroom...
\n" msgstr "

I tothom que ho ha fet possible...
\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 "Example: stunserver.org" msgstr "Exemple: stunserver.org" @@ -12706,6 +12719,10 @@ msgid "_End port:" msgstr "Port _final:" +#. TURN server +msgid "Relay Server (TURN)" +msgstr "" + msgid "Proxy Server & 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" #~ "Nickname: %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"