# HG changeset patch # User Yoshiki Yazawa # Date 1222522087 0 # Node ID d477b5b536607a52d1bf4c15e6f2dfda3bb931da # Parent 43164dadf9225756688ab84762f51cf110355a30# Parent b041a72f8878093a6e5c5f4455dbc1e6d1dfeb4d propagate from branch 'im.pidgin.pidgin' (head a77a8a85e623af608434736b9e63cc17507ec60c) to branch 'im.pidgin.pidgin.yaz' (head b6620638db9843c51a3140a4bc83482b0a69bd11) diff -r b041a72f8878 -r d477b5b53660 COPYRIGHT --- a/COPYRIGHT Mon Sep 22 06:17:11 2008 +0000 +++ b/COPYRIGHT Sat Sep 27 13:28:07 2008 +0000 @@ -425,6 +425,7 @@ Gal Topper Chris Toshok Ken Tossell +Marcus Trautwig Tom Tromey Todd Troxell Brad Turcotte diff -r b041a72f8878 -r d477b5b53660 ChangeLog --- a/ChangeLog Mon Sep 22 06:17:11 2008 +0000 +++ b/ChangeLog Sat Sep 27 13:28:07 2008 +0000 @@ -5,6 +5,8 @@ * Restored the "Has You" feature to the MSN protocol tooltips. * Fixed a crash on removing a custom buddy icon on a buddy. * Fixed a crash caused by certain self-signed SSL certificates. + * Enable a number of strong ciphers which were previously disabled + when using NSS. (Thanks to Marcus Trautwig.) Pidgin: * The status selector now saves your message when changing status. @@ -13,6 +15,8 @@ help with discoverability. CTRL+S is no longer bound to mute. * Added ability to change the color of visited links (using the theme control plugin, or setting the color in ~/.gtkrc-2.0) + * Fix a crash occuring when a custom smiley is deleted and re-added and + used in an open conversation after being re-added Finch: * A new 'Nested Grouping' option in the 'Grouping' plugin. Group @@ -20,6 +24,14 @@ * A bug was fixed where some key-bindings wouldn't work with some TERMs (e.g. xterm-color, screen-linux etc.) + XMPP: + * Sending and receiving custom smileys using the specification in + XEP-0231 (bits of binary) and XHTML-IM + + Yahoo: + * Only send a Ping once every hour. This prevents the account from + being disconnected from the server periodically. + version 2.5.1 (08/30/2008): libpurple: * In the Join/Part plugin, add the ability to apply the rules to diff -r b041a72f8878 -r d477b5b53660 libpurple/dnsquery.c --- a/libpurple/dnsquery.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/dnsquery.c Sat Sep 27 13:28:07 2008 +0000 @@ -461,8 +461,8 @@ return FALSE; } if (rc < sizeof(dns_params)) { - purple_debug_error("dns", "Tried to read %" G_GSSIZE_FORMAT - " bytes from child but only read %" G_GSSIZE_FORMAT "\n", + purple_debug_error("dns", "Tried to write %" G_GSSIZE_FORMAT + " bytes to child but only wrote %" G_GSSIZE_FORMAT "\n", sizeof(dns_params), rc); purple_dnsquery_resolver_destroy(resolver); return FALSE; diff -r b041a72f8878 -r d477b5b53660 libpurple/plugins/ssl/ssl-nss.c --- a/libpurple/plugins/ssl/ssl-nss.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/plugins/ssl/ssl-nss.c Sat Sep 27 13:28:07 2008 +0000 @@ -141,6 +141,19 @@ g_free(lib); NSS_SetDomesticPolicy(); + SSL_CipherPrefSetDefault(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 1); + SSL_CipherPrefSetDefault(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, 1); + SSL_CipherPrefSetDefault(TLS_RSA_WITH_AES_256_CBC_SHA, 1); + SSL_CipherPrefSetDefault(TLS_DHE_DSS_WITH_RC4_128_SHA, 1); + SSL_CipherPrefSetDefault(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, 1); + SSL_CipherPrefSetDefault(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, 1); + SSL_CipherPrefSetDefault(SSL_RSA_WITH_RC4_128_SHA, 1); + SSL_CipherPrefSetDefault(TLS_RSA_WITH_AES_128_CBC_SHA, 1); + SSL_CipherPrefSetDefault(SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 1); + SSL_CipherPrefSetDefault(SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, 1); + SSL_CipherPrefSetDefault(SSL_DHE_RSA_WITH_DES_CBC_SHA, 1); + SSL_CipherPrefSetDefault(SSL_DHE_DSS_WITH_DES_CBC_SHA, 1); + _identity = PR_GetUniqueIdentity("Purple"); _nss_methods = PR_GetDefaultIOMethods(); } diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/Makefile.am --- a/libpurple/protocols/jabber/Makefile.am Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/Makefile.am Sat Sep 27 13:28:07 2008 +0000 @@ -11,6 +11,8 @@ buddy.h \ chat.c \ chat.h \ + data.c \ + data.h \ disco.c \ disco.h \ google.c \ diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/Makefile.mingw --- a/libpurple/protocols/jabber/Makefile.mingw Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/Makefile.mingw Sat Sep 27 13:28:07 2008 +0000 @@ -48,6 +48,7 @@ buddy.c \ caps.c \ chat.c \ + data.c \ disco.c \ google.c \ iq.c \ diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/buddy.c --- a/libpurple/protocols/jabber/buddy.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/buddy.c Sat Sep 27 13:28:07 2008 +0000 @@ -2498,5 +2498,38 @@ js); } +gboolean +jabber_resource_has_capability(const JabberBuddyResource *jbr, const gchar *cap) +{ + const GList *iter = NULL; + if (!jbr->caps) { + purple_debug_error("jabber", + "Unable to find caps: nothing known about buddy\n"); + return FALSE; + } + for (iter = jbr->caps->features ; iter ; iter = g_list_next(iter)) { + purple_debug_info("jabber", "Found cap: %s\n", (char *)iter->data); + if (strcmp(iter->data, cap) == 0) { + return TRUE; + } + } + + return FALSE; +} + +gboolean +jabber_buddy_has_capability(const JabberBuddy *jb, const gchar *cap) +{ + JabberBuddyResource *jbr = jabber_buddy_find_resource((JabberBuddy*)jb, NULL); + + if (!jbr) { + purple_debug_error("jabber", + "Unable to find caps: buddy might be offline\n"); + return FALSE; + } + + return jabber_resource_has_capability(jbr, cap); +} + diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/buddy.h --- a/libpurple/protocols/jabber/buddy.h Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/buddy.h Sat Sep 27 13:28:07 2008 +0000 @@ -117,4 +117,8 @@ void jabber_vcard_fetch_mine(JabberStream *js); +gboolean jabber_resource_has_capability(const JabberBuddyResource *jbr, + const gchar *cap); +gboolean jabber_buddy_has_capability(const JabberBuddy *jb, const gchar *cap); + #endif /* _PURPLE_JABBER_BUDDY_H_ */ diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/chat.c --- a/libpurple/protocols/jabber/chat.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/chat.c Sat Sep 27 13:28:07 2008 +0000 @@ -31,6 +31,7 @@ #include "message.h" #include "presence.h" #include "xdata.h" +#include "data.h" GList *jabber_chat_info(PurpleConnection *gc) { @@ -684,6 +685,7 @@ xmlnode_insert_data(status, msg, -1); } jabber_send(chat->js, presence); + xmlnode_free(presence); g_free(room_jid); } diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/data.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/data.c Sat Sep 27 13:28:07 2008 +0000 @@ -0,0 +1,247 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include "internal.h" +#include "data.h" +#include "debug.h" +#include "xmlnode.h" +#include "util.h" +#include "iq.h" + +static GHashTable *local_data_by_alt = NULL; +static GHashTable *local_data_by_cid = NULL; +static GHashTable *remote_data_by_cid = NULL; + +JabberData * +jabber_data_create_from_data(gconstpointer rawdata, gsize size, const char *type, + JabberStream *js) +{ + JabberData *data = g_new0(JabberData, 1); + gchar *checksum = purple_util_get_image_checksum(rawdata, size); + gchar cid[256]; + + g_snprintf(cid, sizeof(cid), "sha1+%s@bob.xmpp.org", checksum); + g_free(checksum); + + data->cid = g_strdup(cid); + data->type = g_strdup(type); + data->size = size; + + data->data = g_memdup(rawdata, size); + + return data; +} + +JabberData * +jabber_data_create_from_xml(xmlnode *tag) +{ + JabberData *data = g_new0(JabberData, 1); + gsize size; + gpointer raw_data = NULL; + + if (data == NULL) { + purple_debug_error("jabber", "Could not allocate data object\n"); + g_free(data); + return NULL; + } + + /* check if this is a "data" tag */ + if (strcmp(tag->name, "data") != 0) { + purple_debug_error("jabber", "Invalid data element"); + g_free(data); + return NULL; + } + + data->cid = g_strdup(xmlnode_get_attrib(tag, "cid")); + data->type = g_strdup(xmlnode_get_attrib(tag, "type")); + + raw_data = xmlnode_get_data(tag); + data->data = purple_base64_decode(raw_data, &size); + data->size = size; + + g_free(raw_data); + + return data; +} + + +static void +jabber_data_delete(gpointer cbdata) +{ + JabberData *data = cbdata; + g_free(data->cid); + g_free(data->type); + g_free(data->data); + g_free(data); +} + +const char * +jabber_data_get_cid(const JabberData *data) +{ + return data->cid; +} + + +const char * +jabber_data_get_type(const JabberData *data) +{ + return data->type; +} + +gsize +jabber_data_get_size(const JabberData *data) +{ + return data->size; +} + +gpointer +jabber_data_get_data(const JabberData *data) +{ + return data->data; +} + +xmlnode * +jabber_data_get_xml_definition(const JabberData *data) +{ + xmlnode *tag = xmlnode_new("data"); + char *base64data = purple_base64_encode(data->data, data->size); + + xmlnode_set_namespace(tag, XEP_0231_NAMESPACE); + xmlnode_set_attrib(tag, "cid", data->cid); + xmlnode_set_attrib(tag, "type", data->type); + + xmlnode_insert_data(tag, base64data, -1); + + g_free(base64data); + + return tag; +} + +xmlnode * +jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt) +{ + xmlnode *img = xmlnode_new("img"); + char src[128]; + + xmlnode_set_attrib(img, "alt", alt); + g_snprintf(src, sizeof(src), "cid:%s", data->cid); + xmlnode_set_attrib(img, "src", src); + + return img; +} + +xmlnode * +jabber_data_get_xml_request(const gchar *cid) +{ + xmlnode *tag = xmlnode_new("data"); + + xmlnode_set_namespace(tag, XEP_0231_NAMESPACE); + xmlnode_set_attrib(tag, "cid", cid); + + return tag; +} + +const JabberData * +jabber_data_find_local_by_alt(const gchar *alt) +{ + purple_debug_info("jabber", "looking up local smiley with alt = %s\n", alt); + return g_hash_table_lookup(local_data_by_alt, alt); +} + +const JabberData * +jabber_data_find_local_by_cid(const gchar *cid) +{ + purple_debug_info("jabber", "lookup local smiley with cid = %s\n", cid); + return g_hash_table_lookup(local_data_by_cid, cid); +} + +const JabberData * +jabber_data_find_remote_by_cid(const gchar *cid) +{ + purple_debug_info("jabber", "lookup remote smiley with cid = %s\n", cid); + + return g_hash_table_lookup(remote_data_by_cid, cid); +} + +void +jabber_data_associate_local(JabberData *data, const gchar *alt) +{ + purple_debug_info("jabber", "associating local smiley\n alt = %s, cid = %s\n", + alt, jabber_data_get_cid(data)); + g_hash_table_insert(local_data_by_alt, g_strdup(alt), data); + g_hash_table_insert(local_data_by_cid, g_strdup(jabber_data_get_cid(data)), + data); +} + +void +jabber_data_associate_remote(JabberData *data) +{ + purple_debug_info("jabber", "associating remote smiley, cid = %s\n", + jabber_data_get_cid(data)); + g_hash_table_insert(remote_data_by_cid, g_strdup(jabber_data_get_cid(data)), + data); +} + +void +jabber_data_parse(JabberStream *js, xmlnode *packet) +{ + JabberIq *result = NULL; + const char *who = xmlnode_get_attrib(packet, "from"); + xmlnode *data_node = xmlnode_get_child(packet, "data"); + const JabberData *data = + jabber_data_find_local_by_cid(xmlnode_get_attrib(data_node, "cid")); + + if (!data) { + xmlnode *item_not_found = xmlnode_new("item-not-found"); + + result = jabber_iq_new(js, JABBER_IQ_ERROR); + xmlnode_set_attrib(result->node, "to", who); + xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + xmlnode_insert_child(result->node, item_not_found); + } else { + result = jabber_iq_new(js, JABBER_IQ_RESULT); + xmlnode_set_attrib(result->node, "to", who); + xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + xmlnode_insert_child(result->node, + jabber_data_get_xml_definition(data)); + } + jabber_iq_send(result); +} + +void +jabber_data_init(void) +{ + purple_debug_info("jabber", "creating hash tables for data objects\n"); + local_data_by_alt = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + local_data_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, jabber_data_delete); + remote_data_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, jabber_data_delete); +} + +void +jabber_data_uninit(void) +{ + purple_debug_info("jabber", "destroying hash tables for data objects\n"); + g_hash_table_destroy(local_data_by_alt); + g_hash_table_destroy(local_data_by_cid); + g_hash_table_destroy(remote_data_by_cid); +} diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/data.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/data.h Sat Sep 27 13:28:07 2008 +0000 @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA + */ + +#ifndef JABBER_DATA_H +#define JABBER_DATA_H + +#include "xmlnode.h" +#include "jabber.h" + +#define XEP_0231_NAMESPACE "urn:xmpp:bob" + +#include + +typedef struct { + char *cid; + char *type; + gsize size; + gpointer data; +} JabberData; + +/* creates a JabberData instance from raw data */ +JabberData *jabber_data_create_from_data(gconstpointer data, gsize size, + const char *type, JabberStream *js); + +/* create a JabberData instance from an XML "data" element (as defined by + XEP 0231 */ +JabberData *jabber_data_create_from_xml(xmlnode *tag); + +const char *jabber_data_get_cid(const JabberData *data); +const char *jabber_data_get_type(const JabberData *data); + +gsize jabber_data_get_size(const JabberData *data); +gpointer jabber_data_get_data(const JabberData *data); + +/* returns the XML definition for the data element */ +xmlnode *jabber_data_get_xml_definition(const JabberData *data); + +/* returns an XHTML-IM "img" tag given a data instance */ +xmlnode *jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt); + +/* returns a data request element (to be included in an iq stanza) for requesting + data */ +xmlnode *jabber_data_get_xml_request(const gchar *cid); + +/* lookup functions */ +const JabberData *jabber_data_find_local_by_alt(const gchar *alt); +const JabberData *jabber_data_find_local_by_cid(const gchar *cid); +const JabberData *jabber_data_find_remote_by_cid(const gchar *cid); + +/* store data objects */ +void jabber_data_associate_local(JabberData *data, const gchar *alt); +void jabber_data_associate_remote(JabberData *data); + +/* handles iq requests */ +void jabber_data_parse(JabberStream *js, xmlnode *packet); + +void jabber_data_init(void); +void jabber_data_uninit(void); + +#endif /* JABBER_DATA_H */ diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/iq.c --- a/libpurple/protocols/jabber/iq.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/iq.c Sat Sep 27 13:28:07 2008 +0000 @@ -33,6 +33,7 @@ #include "si.h" #include "ping.h" #include "adhoccommands.h" +#include "data.h" #ifdef _WIN32 #include "utsname.h" @@ -355,6 +356,11 @@ return; } + if (xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE)) { + jabber_data_parse(js, packet); + return; + } + /* If we get here, send the default error reply mandated by XMPP-CORE */ if(type && (!strcmp(type, "set") || !strcmp(type, "get"))) { JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sat Sep 27 13:28:07 2008 +0000 @@ -42,6 +42,7 @@ #include "auth.h" #include "buddy.h" #include "chat.h" +#include "data.h" #include "disco.h" #include "google.h" #include "iq.h" @@ -57,6 +58,7 @@ #include "pep.h" #include "adhoccommands.h" + #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) static PurplePlugin *my_protocol = NULL; @@ -642,7 +644,8 @@ JabberStream *js; JabberBuddy *my_jb = NULL; - gc->flags |= PURPLE_CONNECTION_HTML; + gc->flags |= PURPLE_CONNECTION_HTML | + PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY; js = gc->proto_data = g_new0(JabberStream, 1); js->gc = gc; js->fd = -1; @@ -1901,7 +1904,7 @@ JabberID *jid; JabberBuddy *jb; JabberBuddyResource *jbr; - + if(!(jid = jabber_id_new(who))) return; diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Sat Sep 27 13:28:07 2008 +0000 @@ -43,6 +43,7 @@ #include "pep.h" #include "usertune.h" #include "caps.h" +#include "data.h" static PurplePluginProtocolInfo prpl_info = { @@ -136,8 +137,7 @@ purple_marshal_VOID__POINTER_POINTER, NULL, 2, purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), purple_value_new_outgoing(PURPLE_TYPE_STRING)); - - + return TRUE; } @@ -149,6 +149,8 @@ purple_signal_unregister(plugin, "jabber-sending-text"); + jabber_data_uninit(); + return TRUE; } @@ -241,6 +243,13 @@ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + /* this should probably be part of global smiley theme settings later on, + shared with MSN */ + option = purple_account_option_bool_new(_("Show Custom Smileys"), + "custom_smileys", TRUE); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + option); + jabber_init_plugin(plugin); purple_prefs_remove("/plugins/prpl/jabber"); @@ -269,11 +278,16 @@ jabber_tune_init(); jabber_caps_init(); - + + jabber_data_init(); + jabber_add_feature("avatarmeta", AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb); jabber_add_feature("avatardata", AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_add_feature("buzz", "http://www.xmpp.org/extensions/xep-0224.html#ns", jabber_buzz_isenabled); - + jabber_add_feature("buzz", "http://www.xmpp.org/extensions/xep-0224.html#ns", + jabber_buzz_isenabled); + jabber_add_feature("bob", XEP_0231_NAMESPACE, + jabber_custom_smileys_isenabled); + jabber_pep_register_handler("avatar", AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata); } diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/message.c --- a/libpurple/protocols/jabber/message.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/message.c Sat Sep 27 13:28:07 2008 +0000 @@ -24,13 +24,17 @@ #include "notify.h" #include "server.h" #include "util.h" - #include "buddy.h" #include "chat.h" +#include "data.h" #include "google.h" #include "message.h" #include "xmlnode.h" #include "pep.h" +#include "smiley.h" +#include "iq.h" + +#include void jabber_message_free(JabberMessage *jm) { @@ -312,6 +316,225 @@ g_free(str); } +/* used internally by the functions below */ +typedef struct { + gchar *cid; + gchar *alt; +} JabberSmileyRef; + + +static void +jabber_message_get_refs_from_xmlnode_internal(const xmlnode *message, + GHashTable *table) +{ + xmlnode *child; + + for (child = xmlnode_get_child(message, "img") ; child ; + child = xmlnode_get_next_twin(child)) { + const gchar *src = xmlnode_get_attrib(child, "src"); + + if (g_str_has_prefix(src, "cid:")) { + const gchar *cid = src + 4; + + /* if we haven't "fetched" this yet... */ + if (!g_hash_table_lookup(table, cid)) { + /* take a copy of the cid and let the SmileyRef own it... */ + gchar *temp_cid = g_strdup(cid); + JabberSmileyRef *ref = g_new0(JabberSmileyRef, 1); + const gchar *alt = xmlnode_get_attrib(child, "alt"); + ref->cid = temp_cid; + /* if there is no "alt" string, use the cid... + include the entire src, eg. "cid:.." to avoid linkification */ + if (alt && alt[0] != '\0') { + /* workaround for when "alt" is set to the value of the + CID (which Jabbim seems to do), to avoid it showing up + as an mailto: link */ + if (purple_email_is_valid(alt)) { + ref->alt = g_strdup_printf("smiley:%s", alt); + } else { + ref->alt = g_strdup(alt); + } + } else { + ref->alt = g_strdup(src); + } + g_hash_table_insert(table, temp_cid, ref); + } + } + } + + for (child = message->child ; child ; child = child->next) { + jabber_message_get_refs_from_xmlnode_internal(child, table); + } +} + +static gboolean +jabber_message_get_refs_steal(gpointer key, gpointer value, gpointer user_data) +{ + GList **refs = (GList **) user_data; + JabberSmileyRef *ref = (JabberSmileyRef *) value; + + *refs = g_list_append(*refs, ref); + + return TRUE; +} + +static GList * +jabber_message_get_refs_from_xmlnode(const xmlnode *message) +{ + GList *refs = NULL; + GHashTable *unique_refs = g_hash_table_new(g_str_hash, g_str_equal); + + jabber_message_get_refs_from_xmlnode_internal(message, unique_refs); + (void) g_hash_table_foreach_steal(unique_refs, + jabber_message_get_refs_steal, (gpointer) &refs); + g_hash_table_destroy(unique_refs); + return refs; +} + +static gchar * +jabber_message_xml_to_string_strip_img_smileys(xmlnode *xhtml) +{ + gchar *markup = xmlnode_to_str(xhtml, NULL); + int len = strlen(markup); + int pos = 0; + GString *out = g_string_new(NULL); + + while (pos < len) { + /* this is a bit cludgy, maybe there is a better way to do this... + we need to find all tags within the XHTML and replace those + tags with the value of their "alt" attributes */ + if (g_str_has_prefix(&(markup[pos]), "")) { + pos2 += 2; + break; + } else if (g_str_has_prefix(&(markup[pos2]), "")) { + pos2 += 5; + break; + } + } + + /* note, if the above loop didn't find the end of the tag, + it the parsed string will be until the end of the input string, + in which case xmlnode_from_str will bail out and return NULL, + in this case the "if" statement below doesn't trigger and the + text is copied unchanged */ + img = xmlnode_from_str(&(markup[pos]), pos2 - pos); + src = xmlnode_get_attrib(img, "src"); + + if (g_str_has_prefix(src, "cid:")) { + const gchar *alt = xmlnode_get_attrib(img, "alt"); + /* if the "alt" attribute is empty, put the cid as smiley string */ + if (alt && alt[0] != '\0') { + /* if the "alt" is the same as the CID, as Jabbim does, + this prevents linkification... */ + if (purple_email_is_valid(alt)) { + gchar *safe_alt = g_strdup_printf("smiley:%s", alt); + out = g_string_append(out, safe_alt); + g_free(safe_alt); + } else { + out = g_string_append(out, alt); + } + } else { + out = g_string_append(out, src); + } + pos += pos2 - pos; + } else { + out = g_string_append_c(out, markup[pos]); + pos++; + } + + xmlnode_free(img); + + } else { + out = g_string_append_c(out, markup[pos]); + pos++; + } + } + + g_free(markup); + return g_string_free(out, FALSE); +} + +static void +jabber_message_add_remote_smileys(const xmlnode *message) +{ + xmlnode *data_tag; + for (data_tag = xmlnode_get_child_with_namespace(message, "data", XEP_0231_NAMESPACE) ; + data_tag ; + data_tag = xmlnode_get_next_twin(data_tag)) { + const gchar *cid = xmlnode_get_attrib(data_tag, "cid"); + const JabberData *data = jabber_data_find_remote_by_cid(cid); + + if (!data && cid != NULL) { + /* we haven't cached this already, let's add it */ + JabberData *new_data = jabber_data_create_from_xml(data_tag); + jabber_data_associate_remote(new_data); + } + } +} + +/* used in the function below to supply a conversation and shortcut for a + smiley */ +typedef struct { + PurpleConversation *conv; + gchar *alt; +} JabberDataRef; + +static void +jabber_message_get_data_cb(JabberStream *js, xmlnode *packet, gpointer data) +{ + JabberDataRef *ref = (JabberDataRef *) data; + PurpleConversation *conv = ref->conv; + const gchar *alt = ref->alt; + xmlnode *data_element = xmlnode_get_child(packet, "data"); + xmlnode *item_not_found = xmlnode_get_child(packet, "item-not-found"); + + /* did we get a data element as result? */ + if (data_element) { + JabberData *data = jabber_data_create_from_xml(data_element); + + if (data) { + jabber_data_associate_remote(data); + purple_conv_custom_smiley_write(conv, alt, + jabber_data_get_data(data), + jabber_data_get_size(data)); + purple_conv_custom_smiley_close(conv, alt); + } + + } else if (item_not_found) { + purple_debug_info("jabber", + "Responder didn't recognize requested data\n"); + } else { + purple_debug_error("jabber", "Unknown response to data request\n"); + } + g_free(ref->alt); + g_free(ref); +} + +static void +jabber_message_send_data_request(JabberStream *js, PurpleConversation *conv, + const gchar *cid, const gchar *who, + const gchar *alt) +{ + JabberIq *request = jabber_iq_new(js, JABBER_IQ_GET); + JabberDataRef *ref = g_new0(JabberDataRef, 1); + xmlnode *data_request = jabber_data_get_xml_request(cid); + + xmlnode_set_attrib(request->node, "to", who); + ref->conv = conv; + ref->alt = g_strdup(alt); + jabber_iq_set_callback(request, jabber_message_get_data_cb, ref); + xmlnode_insert_child(request->node, data_request); + + jabber_iq_send(request); +} + + void jabber_message_parse(JabberStream *js, xmlnode *packet) { JabberMessage *jm; @@ -369,16 +592,101 @@ g_free(msg); g_free(tmp); } } else if(!strcmp(child->name, "html") && !strcmp(xmlns,"http://jabber.org/protocol/xhtml-im")) { - if(!jm->xhtml && xmlnode_get_child(child, "body")){ - char *tmp, *c; + if(!jm->xhtml && xmlnode_get_child(child, "body")) { + char *c; gsize len; - tmp = xmlnode_to_str(child, NULL); - jm->xhtml = sanitize_utf(tmp, strlen(tmp), &len); - /* Convert all newlines to whitespace. Technically, even regular, non-XML HTML is supposed to ignore newlines, but Pidgin has, as convention - * treated \n as a newline for compatibility with other protocols + + const PurpleConnection *gc = js->gc; + const gchar *who = xmlnode_get_attrib(packet, "from"); + PurpleAccount *account = purple_connection_get_account(gc); + PurpleConversation *conv = NULL; + GList *smiley_refs = NULL; + gchar *reformatted_xhtml; + + if (purple_account_get_bool(account, "custom_smileys", TRUE)) { + /* find a list of smileys ("cid" and "alt" text pairs) + occuring in the message */ + smiley_refs = jabber_message_get_refs_from_xmlnode(child); + purple_debug_info("jabber", "found %d smileys\n", + g_list_length(smiley_refs)); + + if (jm->type == JABBER_MESSAGE_GROUPCHAT) { + JabberID *jid = jabber_id_new(jm->from); + JabberChat *chat = NULL; + + if (jid) { + chat = jabber_chat_find(js, jid->node, jid->domain); + conv = chat->conv; + } + + jabber_id_free(jid); + } else { + conv = + purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, + who, account); + if (!conv) { + /* we need to create the conversation here */ + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, + account, who); + } + } + + /* process any newly provided smileys */ + jabber_message_add_remote_smileys(packet); + } + + /* reformat xhtml so that img tags with a "cid:" src gets + translated to the bare text of the emoticon (the "alt" attrib) */ + /* this is done also when custom smiley retrieval is turned off, + this way the receiver always sees the shortcut instead */ + reformatted_xhtml = + jabber_message_xml_to_string_strip_img_smileys(child); + +// jm->xhtml = reformatted_xhtml; + jm->xhtml = sanitize_utf(reformatted_xhtml, + strlen(reformatted_xhtml), &len); + + /* add known custom emoticons to the conversation */ + /* note: if there were no smileys in the incoming message, or + if receiving custom smileys is turned off, smiley_refs will + be NULL */ + for (; smiley_refs ; smiley_refs = g_list_delete_link(smiley_refs, smiley_refs)) { + JabberSmileyRef *ref = (JabberSmileyRef *) smiley_refs->data; + const gchar *cid = ref->cid; + const gchar *alt = ref->alt; + + purple_debug_info("jabber", + "about to add custom smiley %s to the conv\n", alt); + if (purple_conv_custom_smiley_add(conv, alt, "cid", cid, + TRUE)) { + const JabberData *data = + jabber_data_find_remote_by_cid(cid); + /* if data is already known, we add write it immediatly */ + if (data) { + purple_debug_info("jabber", + "data is already known\n"); + purple_conv_custom_smiley_write(conv, alt, + jabber_data_get_data(data), + jabber_data_get_size(data)); + purple_conv_custom_smiley_close(conv, alt); + } else { + /* we need to request the smiley (data) */ + purple_debug_info("jabber", + "data is unknown, need to request it\n"); + jabber_message_send_data_request(js, conv, cid, who, + alt); + } + } + g_free(ref->cid); + g_free(ref->alt); + g_free(ref); + } + + /* Convert all newlines to whitespace. Technically, even regular, non-XML HTML is supposed to ignore newlines, but Pidgin has, as convention + * treated \n as a newline for compatibility with other protocols */ for (c = jm->xhtml; *c != '\0'; c++) { - if (*c == '\n') + if (*c == '\n') *c = ' '; } g_free(tmp); @@ -509,6 +817,129 @@ jabber_message_free(jm); } +static const gchar * +jabber_message_get_mimetype_from_ext(const gchar *ext) +{ + if (strcmp(ext, "png") == 0) { + return "image/png"; + } else if (strcmp(ext, "gif") == 0) { + return "image/gif"; + } else if (strcmp(ext, "jpg") == 0) { + return "image/jpeg"; + } else if (strcmp(ext, "tif") == 0) { + return "image/tif"; + } else { + return "image/x-icon"; /* or something... */ + } +} + +static GList * +jabber_message_xhtml_find_smileys(const char *xhtml) +{ + GList *smileys = purple_smileys_get_all(); + GList *found_smileys = NULL; + + for (; smileys ; smileys = g_list_delete_link(smileys, smileys)) { + PurpleSmiley *smiley = (PurpleSmiley *) smileys->data; + const gchar *shortcut = purple_smiley_get_shortcut(smiley); + const gssize len = strlen(shortcut); + + gchar *escaped = g_markup_escape_text(shortcut, len); + const char *pos = strstr(xhtml, escaped); + + if (pos) { + found_smileys = g_list_append(found_smileys, smiley); + } + + g_free(escaped); + } + + return found_smileys; +} + +static gchar * +jabber_message_get_smileyfied_xhtml(const gchar *xhtml, const GList *smileys) +{ + /* create XML element for all smileys (img tags) */ + GString *result = g_string_new(NULL); + int pos = 0; + int length = strlen(xhtml); + + while (pos < length) { + const GList *iterator; + gboolean found_smiley = FALSE; + + for (iterator = smileys ; iterator ; + iterator = g_list_next(iterator)) { + const PurpleSmiley *smiley = (PurpleSmiley *) iterator->data; + const gchar *shortcut = purple_smiley_get_shortcut(smiley); + const gssize len = strlen(shortcut); + gchar *escaped = g_markup_escape_text(shortcut, len); + + if (g_str_has_prefix(&(xhtml[pos]), escaped)) { + /* we found the current smiley at this position */ + const JabberData *data = + jabber_data_find_local_by_alt(shortcut); + xmlnode *img = jabber_data_get_xhtml_im(data, shortcut); + int len; + gchar *img_text = xmlnode_to_str(img, &len); + + found_smiley = TRUE; + result = g_string_append(result, img_text); + g_free(img_text); + pos += strlen(escaped); + g_free(escaped); + xmlnode_free(img); + break; + } else { + /* cleanup from the before the next round... */ + g_free(escaped); + } + } + if (!found_smiley) { + /* there was no smiley here, just copy one byte */ + result = g_string_append_c(result, xhtml[pos]); + pos++; + } + } + + return g_string_free(result, FALSE); +} + +static gboolean +jabber_conv_support_custom_smileys(const PurpleConnection *gc, + const PurpleConversation *conv, + const gchar *who) +{ + JabberStream *js = (JabberStream *) gc->proto_data; + JabberBuddy *jb; + + if (!js) { + purple_debug_error("jabber", + "jabber_conv_support_custom_smileys: could not find stream\n"); + return FALSE; + } + + jb = jabber_buddy_find(js, who, FALSE); + if (!jb) { + purple_debug_error("jabber", + "jabber_conv_support_custom smileys: could not find buddy\n"); + return FALSE; + } + + + + switch (purple_conversation_get_type(conv)) { + /* for the time being, we will not support custom smileys in MUCs */ + case PURPLE_CONV_TYPE_IM: + return jabber_buddy_has_capability(jb, XEP_0231_NAMESPACE); + break; + default: + return FALSE; + break; + } +} + void jabber_message_send(JabberMessage *jm) { xmlnode *message, *child; @@ -594,7 +1025,55 @@ } if(jm->xhtml) { - child = xmlnode_from_str(jm->xhtml, -1); + 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) { xmlnode_insert_child(message, child); } else { @@ -768,3 +1247,11 @@ return js->allowBuzz; } +gboolean jabber_custom_smileys_isenabled(JabberStream *js, const gchar *shortname, + const gchar *namespace) +{ + const PurpleConnection *gc = js->gc; + PurpleAccount *account = purple_connection_get_account(gc); + + return purple_account_get_bool(account, "custom_smileys", TRUE); +} diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/jabber/message.h --- a/libpurple/protocols/jabber/message.h Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/jabber/message.h Sat Sep 27 13:28:07 2008 +0000 @@ -80,4 +80,7 @@ gboolean jabber_buzz_isenabled(JabberStream *js, const gchar *shortname, const gchar *namespace); +gboolean jabber_custom_smileys_isenabled(JabberStream *js, const gchar *shortname, + const gchar *namespace); + #endif /* _PURPLE_JABBER_MESSAGE_H_ */ diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/msn/group.h --- a/libpurple/protocols/msn/group.h Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/msn/group.h Sat Sep 27 13:28:07 2008 +0000 @@ -33,10 +33,10 @@ #include "userlist.h" #define MSN_INDIVIDUALS_GROUP_ID "1983" -#define MSN_INDIVIDUALS_GROUP_NAME "Other Contacts" +#define MSN_INDIVIDUALS_GROUP_NAME _("Other Contacts") #define MSN_NON_IM_GROUP_ID "email" -#define MSN_NON_IM_GROUP_NAME "Non-IM Contacts" +#define MSN_NON_IM_GROUP_NAME _("Non-IM Contacts") /** * A group. diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/msn/notification.c Sat Sep 27 13:28:07 2008 +0000 @@ -613,16 +613,13 @@ xmlnode_set_attrib(adl_node, "l", "1"); /*get the userlist*/ - for (l = session->userlist->users; l != NULL; l = l->next) { + for (l = session->userlist->users; l != NULL; l = l->next){ user = l->data; /* skip RL & PL during initial dump */ if (!(user->list_op & MSN_LIST_OP_MASK)) continue; - if (!strcmp(user->passport, "messenger@microsoft.com")) - continue; - msn_add_contact_xml(session, adl_node, user->passport, user->list_op & MSN_LIST_OP_MASK, user->networkid); @@ -774,6 +771,53 @@ } static void +adl_241_error_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + /* khc: some googling suggests that error 241 means the buddy is somehow + in the local list, but not the server list, and that we should add + those buddies to the addressbook. For now I will just notify the user + about the raw payload, because I am lazy */ + MsnSession *session; + PurpleAccount *account; + PurpleConnection *gc; + xmlnode *adl; + xmlnode *domain; + GString *emails; + + session = cmdproc->session; + account = session->account; + gc = purple_account_get_connection(account); + + adl = xmlnode_from_str(payload, len); + emails = g_string_new(NULL); + + domain = xmlnode_get_child(adl, "d"); + while (domain) { + const char *domain_str = xmlnode_get_attrib(domain, "n"); + xmlnode *contact = xmlnode_get_child(domain, "c"); + while (contact) { + g_string_append_printf(emails, "%s@%s\n", + xmlnode_get_attrib(contact, "n"), domain_str); + contact = xmlnode_get_next_twin(contact); + } + domain = xmlnode_get_next_twin(domain); + } + + purple_notify_error(gc, NULL, + _("The following users are missing from your addressbook"), emails->str); + g_string_free(emails, TRUE); + xmlnode_free(adl); +} + +static void +adl_241_error_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + cmdproc->last_cmd->payload_cb = adl_241_error_cmd_post; + cmd->payload_len = atoi(cmd->params[1]); +} + +static void fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) { @@ -1962,6 +2006,8 @@ msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd); + msn_table_add_cmd(cbs_table, NULL, "241", adl_241_error_cmd); + msn_table_add_error(cbs_table, "ADD", add_error); msn_table_add_error(cbs_table, "ADL", adl_error); msn_table_add_error(cbs_table, "REG", reg_error); diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/msn/session.c --- a/libpurple/protocols/msn/session.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/msn/session.c Sat Sep 27 13:28:07 2008 +0000 @@ -453,6 +453,8 @@ PurpleConnection *gc; PurpleStoredImage *img; + msn_change_status(session); + if (session->logged_in) return; @@ -465,8 +467,6 @@ session->logged_in = TRUE; - msn_change_status(session); - purple_connection_set_state(gc, PURPLE_CONNECTED); /* Sync users */ diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Sat Sep 27 13:28:07 2008 +0000 @@ -55,6 +55,12 @@ /* #define TRY_WEBMESSENGER_LOGIN 0 */ +/* One hour */ +#define PING_TIMEOUT 3600 + +/* One minute */ +#define KEEPALIVE_TIMEOUT 60 + static void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *, PurpleGroup *); #ifdef TRY_WEBMESSENGER_LOGIN static void yahoo_login_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message); @@ -3009,6 +3015,7 @@ yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); yd->confs = NULL; yd->conf_id = 2; + yd->last_keepalive = yd->last_ping = time(NULL); yd->current_status = get_yahoo_status_from_purple_status(status); @@ -3067,7 +3074,7 @@ } g_slist_free(yd->cookies); - yd->chat_online = 0; + yd->chat_online = FALSE; if (yd->in_chat) yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */ @@ -3894,21 +3901,36 @@ static void yahoo_keepalive(PurpleConnection *gc) { + struct yahoo_packet *pkt; struct yahoo_data *yd = gc->proto_data; - struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_send_and_free(pkt, yd); - - if (!yd->chat_online) - return; - - if (yd->wm) { - ycht_chat_send_keepalive(yd->ycht); - return; + time_t now = time(NULL); + + /* We're only allowed to send a ping once an hour or the servers will boot us */ + if ((now - yd->last_ping) >= PING_TIMEOUT) { + yd->last_ping = now; + + /* The native client will only send PING or CHATPING */ + if (yd->chat_online) { + if (yd->wm) { + ycht_chat_send_keepalive(yd->ycht); + } else { + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc)); + yahoo_packet_send_and_free(pkt, yd); + } + } else { + pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_send_and_free(pkt, yd); + } } - pkt = yahoo_packet_new(YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash_str(pkt, 109, purple_connection_get_display_name(gc)); - yahoo_packet_send_and_free(pkt, yd); + if ((now - yd->last_keepalive) >= KEEPALIVE_TIMEOUT) { + yd->last_keepalive = now; + pkt = yahoo_packet_new(YAHOO_SERVICE_KEEPALIVE, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc)); + yahoo_packet_send_and_free(pkt, yd); + } + } static void yahoo_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *g) diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/yahoo/yahoo.h --- a/libpurple/protocols/yahoo/yahoo.h Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Sat Sep 27 13:28:07 2008 +0000 @@ -176,6 +176,8 @@ * the server expects us to keep track of the group for which it is sending us contact names. */ char *current_list15_grp; + time_t last_ping; + time_t last_keepalive; }; #define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255) diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/yahoo/yahoo_packet.h --- a/libpurple/protocols/yahoo/yahoo_packet.h Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.h Sat Sep 27 13:28:07 2008 +0000 @@ -76,7 +76,7 @@ YAHOO_SERVICE_IGNORECONTACT, /* > 1, 7, 13 < 1, 66, 13, 0*/ YAHOO_SERVICE_REJECTCONTACT, YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */ - /* YAHOO_SERVICE_??? = 0x8A, */ + YAHOO_SERVICE_KEEPALIVE = 0x8A, YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/ YAHOO_SERVICE_CHATGOTO, YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */ diff -r b041a72f8878 -r d477b5b53660 libpurple/protocols/yahoo/yahoochat.c --- a/libpurple/protocols/yahoo/yahoochat.c Mon Sep 22 06:17:11 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoochat.c Sat Sep 27 13:28:07 2008 +0000 @@ -369,7 +369,7 @@ struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data; if (pkt->status == 1) { - yd->chat_online = 1; + yd->chat_online = TRUE; /* We need to goto a user in chat */ if (yd->pending_chat_goto) { @@ -411,7 +411,7 @@ } if (pkt->status == 1) { - yd->chat_online = 0; + yd->chat_online = FALSE; g_free(yd->pending_chat_room); yd->pending_chat_room = NULL; g_free(yd->pending_chat_id); @@ -881,7 +881,7 @@ yahoo_packet_hash_str(pkt, 1, dn); yahoo_packet_send_and_free(pkt, yd); - yd->chat_online = 0; + yd->chat_online = FALSE; g_free(yd->pending_chat_room); yd->pending_chat_room = NULL; g_free(yd->pending_chat_id); diff -r b041a72f8878 -r d477b5b53660 pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Mon Sep 22 06:17:11 2008 +0000 +++ b/pidgin/gtkimhtml.c Sat Sep 27 13:28:07 2008 +0000 @@ -352,6 +352,9 @@ g_string_free (t->values, TRUE); g_free (t->children); } + if (t && t->image) { + t->image->imhtml = NULL; + } g_free (t); } } @@ -1972,7 +1975,6 @@ } } -/* this isn't used yet static void gtk_smiley_tree_remove (GtkSmileyTree *tree, GtkIMHtmlSmiley *smiley) { @@ -1988,7 +1990,7 @@ pos = strchr (t->values->str, *x); if (pos) - t = t->children [(int) pos - (int) t->values->str]; + t = t->children [(unsigned int) pos - (unsigned int) t->values->str]; else return; @@ -1999,8 +2001,6 @@ t->image = NULL; } } -*/ - static gint gtk_smiley_tree_lookup (GtkSmileyTree *tree, @@ -2060,6 +2060,25 @@ return 0; } +static void +gtk_imhtml_disassociate_smiley_foreach(gpointer key, gpointer value, + gpointer user_data) +{ + GtkSmileyTree *tree = (GtkSmileyTree *) value; + GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) user_data; + gtk_smiley_tree_remove(tree, smiley); +} + +static void +gtk_imhtml_disassociate_smiley(GtkIMHtmlSmiley *smiley) +{ + if (smiley->imhtml) { + gtk_smiley_tree_remove(smiley->imhtml->default_smilies, smiley); + g_hash_table_foreach(smiley->imhtml->smiley_data, + gtk_imhtml_disassociate_smiley_foreach, smiley); + } +} + void gtk_imhtml_associate_smiley (GtkIMHtml *imhtml, const gchar *sml, @@ -5632,12 +5651,14 @@ smiley->smile = g_strdup(shortcut); smiley->hidden = hide; smiley->flags = flags; + smiley->imhtml = NULL; gtk_imhtml_smiley_reload(smiley); return smiley; } void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley) { + gtk_imhtml_disassociate_smiley(smiley); g_free(smiley->smile); g_free(smiley->file); if (smiley->icon) diff -r b041a72f8878 -r d477b5b53660 pidgin/gtksmiley.c --- a/pidgin/gtksmiley.c Mon Sep 22 06:17:11 2008 +0000 +++ b/pidgin/gtksmiley.c Sat Sep 27 13:28:07 2008 +0000 @@ -273,8 +273,8 @@ gsize size = 0; gchar *filename; - gdk_pixbuf_save_to_bufferv(s->custom_pixbuf, &buffer, &size, - "png", NULL, NULL, NULL); + gdk_pixbuf_save_to_buffer(s->custom_pixbuf, &buffer, &size, + "png", NULL, "compression", "9", NULL, NULL); filename = purple_util_get_image_filename(buffer, size); s->filename = g_build_filename(purple_smileys_get_storing_dir(), filename, NULL); purple_util_write_data_to_file_absolute(s->filename, buffer, size); diff -r b041a72f8878 -r d477b5b53660 po/POTFILES.in --- a/po/POTFILES.in Mon Sep 22 06:17:11 2008 +0000 +++ b/po/POTFILES.in Sat Sep 27 13:28:07 2008 +0000 @@ -98,6 +98,7 @@ libpurple/protocols/msn/contact.c libpurple/protocols/msn/dialog.c libpurple/protocols/msn/error.c +libpurple/protocols/msn/group.h libpurple/protocols/msn/msn.c libpurple/protocols/msn/nexus.c libpurple/protocols/msn/notification.c diff -r b041a72f8878 -r d477b5b53660 po/de.po --- a/po/de.po Mon Sep 22 06:17:11 2008 +0000 +++ b/po/de.po Sat Sep 27 13:28:07 2008 +0000 @@ -11,9 +11,9 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-09-01 10:30+0200\n" -"PO-Revision-Date: 2008-09-01 10:30+0200\n" -"Last-Translator: Jochen Kemnade \n" +"POT-Creation-Date: 2008-09-27 13:06+0200\n" +"PO-Revision-Date: 2008-09-27 13:02+0200\n" +"Last-Translator: Björn Voigt \n" "Language-Team: Deutsch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -76,7 +76,7 @@ msgid "Remember password" msgstr "Passwort speichern" -msgid "There's no protocol plugins installed." +msgid "There are no protocol plugins installed." msgstr "Es sind keine Protokoll-Plugins installiert." msgid "(You probably forgot to 'make install'.)" @@ -1128,6 +1128,7 @@ msgid "%s has sent you a message. (%s)" msgstr "%s hat Ihnen eine Nachricht gesendet. (%s)" +#, c-format msgid "Unknown pounce event. Please report this!" msgstr "Unbekanntes Alarm-Ereignis. Bitte berichten Sie dieses Problem!" @@ -1497,6 +1498,7 @@ "Wenn eine neue Unterhaltung eröffnet wird, fügt dieses Plugin die letzte " "Unterhaltung in die aktuelle Unterhaltung ein." +#, c-format msgid "Online" msgstr "Online" @@ -1839,8 +1841,9 @@ "Fehler beim Lesen vom Auflösungsprozess:\n" "%s" -msgid "EOF while reading from resolver process" -msgstr "EOF während vom Resolver-Prozess gelesen wurde" +#, c-format +msgid "Resolver process exited without answering our request" +msgstr "Auflösungsprozess hat sich beendet ohne die Anfrage zu beantworten" #, c-format msgid "Thread creation failure: %s" @@ -1929,6 +1932,7 @@ msgid "Transfer of file %s complete" msgstr "Übertragung der Datei %s ist komplett" +#, c-format msgid "File transfer complete" msgstr "Dateiübertragung ist komplett" @@ -1936,6 +1940,7 @@ msgid "You canceled the transfer of %s" msgstr "Sie haben die Dateiübertragung von %s abgebrochen" +#, c-format msgid "File transfer cancelled" msgstr "Dateiübertragung wurde abgebrochen" @@ -2143,6 +2148,7 @@ msgid "You are using %s, but this plugin requires %s." msgstr "Sie benutzen %s, aber dieses Plugin benötigt %s." +#, c-format msgid "This plugin has not defined an ID." msgstr "Dieses Plugin hat keine ID definiert." @@ -3038,6 +3044,7 @@ #. get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for #. * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message #. Away stuff +#, c-format msgid "Away" msgstr "Abwesend" @@ -3929,6 +3936,7 @@ msgid "Extended Away" msgstr "Abwesend (erweitert)" +#, c-format msgid "Do Not Disturb" msgstr "Nicht stören" @@ -4554,6 +4562,11 @@ msgid "File transfer proxies" msgstr "Proxys für Dateiübertragungen" +#. this should probably be part of global smiley theme settings later on, +#. shared with MSN +msgid "Show Custom Smileys" +msgstr "Zeige benutzerdefinierte Smileys" + #, c-format msgid "%s has left the conversation." msgstr "%s hat das Gespräch verlassen." @@ -4698,166 +4711,220 @@ "%s ist auf der lokalen Liste, aber nicht auf der Serverliste. Möchten Sie, " "dass der Buddy hinzugefügt wird?" +#, c-format msgid "Unable to parse message" msgstr "Kann die Nachricht nicht parsen" +#, c-format msgid "Syntax Error (probably a client bug)" msgstr "Syntaxfehler (wahrscheinlich ein Client-Bug)" +#, c-format msgid "Invalid email address" msgstr "Ungültige E-Mail-Adresse" +#, c-format msgid "User does not exist" msgstr "Benutzer existiert nicht" +#, c-format msgid "Fully qualified domain name missing" msgstr "Der Fully Qualified Domain Name fehlt" +#, c-format msgid "Already logged in" msgstr "Schon angemeldet" +#, c-format msgid "Invalid username" msgstr "Ungültiger Benutzername" +#, c-format msgid "Invalid friendly name" msgstr "Ungültiger Freundesname" +#, c-format msgid "List full" msgstr "Liste voll" +#, c-format msgid "Already there" msgstr "Schon da" +#, c-format msgid "Not on list" msgstr "Nicht auf der Liste" +#, c-format msgid "User is offline" msgstr "Benutzer ist offline" +#, c-format msgid "Already in the mode" msgstr "Bereits in diesem Modus" +#, c-format msgid "Already in opposite list" msgstr "Bereits in der „Gegenteil-Liste“" +#, c-format msgid "Too many groups" msgstr "Zu viele Gruppen" +#, c-format msgid "Invalid group" msgstr "Ungültige Gruppe" +#, c-format msgid "User not in group" msgstr "Benutzer ist nicht in der Gruppe" +#, c-format msgid "Group name too long" msgstr "Name der Gruppe ist zu lang" +#, c-format msgid "Cannot remove group zero" msgstr "Kann die Gruppe „Null“ nicht entfernen" +#, c-format msgid "Tried to add a user to a group that doesn't exist" msgstr "" "Versuchte einen Benutzer zu einer nichtexistierenden Gruppe hinzuzufügen" +#, c-format msgid "Switchboard failed" msgstr "Vermittlung gescheitert" +#, c-format msgid "Notify transfer failed" msgstr "Übertragung der Benachrichtigung gescheitert" +#, c-format msgid "Required fields missing" msgstr "Notwendige Felder fehlen" +#, c-format msgid "Too many hits to a FND" msgstr "Zu viele Treffer zu einem FND" +#, c-format msgid "Not logged in" msgstr "Nicht angemeldet" +#, c-format msgid "Service temporarily unavailable" msgstr "Dienst momentan nicht verfügbar" +#, c-format msgid "Database server error" msgstr "Fehler des Datenbank-Servers" +#, c-format msgid "Command disabled" msgstr "Kommando abgeschaltet" +#, c-format msgid "File operation error" msgstr "Dateiverarbeitungsfehler" +#, c-format msgid "Memory allocation error" msgstr "Fehler bei der Speicheranforderung" +#, c-format msgid "Wrong CHL value sent to server" msgstr "Falscher CHL-Wert zum Server gesendet" +#, c-format msgid "Server busy" msgstr "Server beschäftigt" +#, c-format msgid "Server unavailable" msgstr "Server unerreichbar" +#, c-format msgid "Peer notification server down" msgstr "Peer-Benachrichtigungsserver nicht erreichbar" +#, c-format msgid "Database connect error" msgstr "Datenbank-Verbindungsfehler" +#, c-format msgid "Server is going down (abandon ship)" msgstr "Server fährt runter (melden Sie sich ab)" +#, c-format msgid "Error creating connection" msgstr "Fehler beim Herstellen der Verbindung" +#, c-format msgid "CVR parameters are either unknown or not allowed" msgstr "CVR-Parameter sind entweder unbekannt oder nicht erlaubt" +#, c-format msgid "Unable to write" msgstr "Schreiben nicht möglich" +#, c-format msgid "Session overload" msgstr "Sitzung überlastet" +#, c-format msgid "User is too active" msgstr "Benutzer ist zu aktiv" +#, c-format msgid "Too many sessions" msgstr "Zu viele Sitzungen" +#, c-format msgid "Passport not verified" msgstr "Passport (MSN Benutzerausweis) wurde nicht überprüft" +#, c-format msgid "Bad friend file" msgstr "Falsche Friends-Datei" +#, c-format msgid "Not expected" msgstr "Nicht erwartet" +#, c-format msgid "Friendly name changes too rapidly" msgstr "Benutzernamen werden zu oft geändert" +#, c-format msgid "Server too busy" msgstr "Server ist zu beschäftigt" +#, c-format msgid "Authentication failed" msgstr "Authentifizierung fehlgeschlagen" +#, c-format msgid "Not allowed when offline" msgstr "Nicht erlaubt im Offline-Modus" +#, c-format msgid "Not accepting new users" msgstr "Akzeptiert keine neuen Benutzer" +#, c-format msgid "Kids Passport without parental consent" msgstr "Kinder-Passwort ohne die Zustimmung der Eltern" +#, c-format msgid "Passport account not yet verified" msgstr "Passport-Konto wurde noch nicht überprüft" +#, c-format msgid "Passport account suspended" msgstr "Passport-Konto gesperrt" +#, c-format msgid "Bad ticket" msgstr "Falsches Ticket" @@ -4869,6 +4936,12 @@ msgid "MSN Error: %s\n" msgstr "MSN-Fehler: %s\n" +msgid "Other Contacts" +msgstr "Andere Kontakte" + +msgid "Non-IM Contacts" +msgstr "Nicht-IM-Kontakte" + msgid "Nudge" msgstr "Anstoßen" @@ -4939,6 +5012,9 @@ msgid "Page" msgstr "Nachricht" +msgid "Has you" +msgstr "Hat Sie" + msgid "Home Phone Number" msgstr "Private Telefonnummer" @@ -5221,6 +5297,9 @@ msgid "Unable to add user" msgstr "Kann den Benutzer nicht hinzufügen" +msgid "The following users are missing from your addressbook" +msgstr "Die folgenden Benutzer fehlen in Ihrem Adressbuch" + #, c-format msgid "Unable to add user on %s (%s)" msgstr "Kann den Benutzer nicht zu %s (%s) hinzufügen" @@ -5437,9 +5516,6 @@ msgid "This Hotmail account may not be active." msgstr "Dieses Hotmail-Konto ist vielleicht nicht aktiv." -msgid "Has you" -msgstr "Hat Sie" - #. *< type #. *< ui_requirement #. *< flags @@ -6073,6 +6149,7 @@ msgid "Error. SSL support is not installed." msgstr "Fehler. SSL ist nicht installiert." +#, c-format msgid "This conference has been closed. No more messages can be sent." msgstr "" "Diese Konferenz wurde geschlossen. Es können keine Nachrichten mehr gesendet " @@ -6130,6 +6207,9 @@ msgid "AIM Protocol Plugin" msgstr "AIM-Protokoll-Plugin" +msgid "ICQ UIN..." +msgstr "ICQ-UIN..." + #. *< type #. *< ui_requirement #. *< flags @@ -6193,7 +6273,7 @@ msgstr "Bewertung zum Client" msgid "Service unavailable" -msgstr "Server unerreichbar" +msgstr "Dienst nicht unerreichbar" msgid "Service not defined" msgstr "Dienst nicht definiert" @@ -6339,18 +6419,23 @@ msgid "Screen Sharing" msgstr "Gemeinsamer Bildschirm" +#, c-format msgid "Free For Chat" msgstr "Bereit zum Chatten" +#, c-format msgid "Not Available" msgstr "Nicht verfügbar" +#, c-format msgid "Occupied" msgstr "Beschäftigt" +#, c-format msgid "Web Aware" msgstr "In Web" +#, c-format msgid "Invisible" msgstr "Unsichtbar" @@ -7046,6 +7131,7 @@ msgid "Attempting to connect to %s:%hu." msgstr "Verbindungsversuch mit %s:%hu." +#, c-format msgid "Attempting to connect via proxy server." msgstr "Verbindungsversuch über einen Proxyserver." @@ -7173,14 +7259,23 @@ msgid "Other" msgstr "Andere" -msgid "Modify my information" -msgstr "Meine Informationen bearbeiten" - -msgid "Update my information" -msgstr "Meine Informationen aktualisieren" - -msgid "Your information has been updated" -msgstr "Ihre Informationen wurden aktualisiert" +msgid "Modify information" +msgstr "Informationen bearbeiten" + +msgid "Update information" +msgstr "Informationen aktualisieren" + +#. TODO: We don't really need to notify the user about this, do we? +#. TODO: Does the user really need to be notified about this? +msgid "QQ Buddy" +msgstr "QQ-Buddy" + +#, fuzzy +msgid "Successed:" +msgstr "Geschwindigkeit:" + +msgid "Change buddy information." +msgstr "Buddy-Informationen bearbeiten" #, c-format msgid "" @@ -7198,30 +7293,29 @@ msgid "You rejected %d's request" msgstr "Sie haben die Anfrage von %d abgelehnt" -msgid "Input your reason:" -msgstr "Geben Sie Ihren Grund an:" - msgid "Reject request" msgstr "Anfrage ablehnen" #. title -msgid "Sorry, you are not my type..." +msgid "Sorry, you are not my style..." msgstr "Tut mir Leid, du bist nicht mein Typ..." msgid "Add buddy with auth request failed" msgstr "Benutzer hinzufügen, wenn Autorisierungsanfrage fehlschlug" -#. TODO: We don't really need to notify the user about this, do we? -msgid "You have successfully removed a buddy" -msgstr "Sie haben einen Buddy entfernt" - -#. TODO: Does the user really need to be notified about this? -msgid "You have successfully removed yourself from your friend's buddy list" -msgstr "Sie haben sich erfolgreich von der Kontaktliste Ihres Freunds entfernt" - -#, c-format -msgid "User %d needs authentication" -msgstr "Benutzer %d benötigt Authentifizierung" +msgid "Failed:" +msgstr "Gescheitert:" + +msgid "Remove buddy" +msgstr "Buddy entfernen" + +#, fuzzy +msgid "Remove from other's buddy list" +msgstr "Von seiner/ihrer Buddy-Liste entfernen" + +#, c-format +msgid "%d needs authentication" +msgstr "%d benötigt Authentifizierung" msgid "Input request here" msgstr "Anfrage hier eingeben" @@ -7237,14 +7331,14 @@ msgstr "Senden" #, c-format -msgid "You have added %d to buddy list" -msgstr "Sie haben %d zu Ihrer Buddy-Liste hinzugefügt" - -msgid "QQid Error" -msgstr "QQid-Fehler" - -msgid "Invalid QQid" -msgstr "Ungültige QQid" +msgid "Add into %d's buddy list" +msgstr "Zu %ds Buddy-Liste hinzufügen" + +msgid "QQ Number Error" +msgstr "Fehler in QQ-Nummer" + +msgid "Invalid QQ Number" +msgstr "Ungültige QQ-Nummer" msgid "ID: " msgstr "ID: " @@ -7264,19 +7358,19 @@ msgid "QQ Qun" msgstr "QQ-Qun" -msgid "Please enter external group ID" -msgstr "Bitte geben Sie die externe Gruppen-ID an" - -msgid "You can only search for permanent QQ groups\n" -msgstr "Sie können nur nach permanenten QQ-Gruppen suchen\n" - -#, c-format -msgid "User %d requested to join group %d" -msgstr "Benutzer %d möchte der Gruppe %d beitreten" - -#, c-format -msgid "Reason: %s" -msgstr "Grund: %s" +msgid "Please enter Qun number" +msgstr "Bitte geben Sie die Qun-Nummer ein" + +msgid "You can only search for permanent Qun\n" +msgstr "Sie können nur nach permanenten Qun suchen\n" + +#, c-format +msgid "%d request to join Qun %d" +msgstr "%d möchte dem Qun %d beitreten" + +#, c-format +msgid "Message: %s" +msgstr "Nachricht: %s" msgid "QQ Qun Operation" msgstr "QQ-Qun-Operation" @@ -7284,24 +7378,24 @@ msgid "Approve" msgstr "Akzeptieren" -#, c-format -msgid "Your request to join group %d has been rejected by admin %d" +#, fuzzy, c-format +msgid "Failed to join Qun %d, operated by admin %d" +msgstr "Dem Qun %d, moderiert von admin %d, konnte nicht beigetreten werden" + +#, fuzzy, c-format +msgid "Successed to join Qun %d, operated by admin %d" msgstr "Ihre Anfrage, der Gruppe %d beizutreten wurde von Admin %d abgelehnt" #, c-format -msgid "Your request to join group %d has been approved by admin %d" -msgstr "Ihre Anfrage, der Gruppe %d beizutreten wurde von Admin %d akzeptiert" - -#, c-format -msgid "You [%d] have left group \"%d\"" -msgstr "Sie [%d] haben die Gruppe „%d“ verlassen" - -#, c-format -msgid "You [%d] have been added to group \"%d\"" -msgstr "Sie [%d] wurden der Gruppe „%d“ hinzugefügt" - -msgid "This group has been added to your buddy list" -msgstr "Diese Gruppe wurde Ihrer Buddy-Liste hinzugefügt" +msgid "[%d] removed from Qun \"%d\"" +msgstr "[%d] vom Qun „%d“ entfernt" + +msgid "Notice:" +msgstr "Bemerkung:" + +#, c-format +msgid "[%d] added to Qun \"%d\"" +msgstr "[%d] zum Qun „%d“ hinzugefügt" msgid "I am not a member" msgstr "Ich bin kein Mitglied" @@ -7309,8 +7403,9 @@ msgid "I am a member" msgstr "Ich bin Mitglied" -msgid "I am applying to join" -msgstr "Ich möchte beitreten" +#, fuzzy +msgid "I am requesting" +msgstr "Ich frage an" msgid "I am the admin" msgstr "Ich bin der Admin" @@ -7318,17 +7413,22 @@ msgid "Unknown status" msgstr "Unbekannter Status" -msgid "This group does not allow others to join" -msgstr "Dieser Gruppe können andere nicht beitreten" - -msgid "You have successfully left the group" -msgstr "Sie haben die Gruppe erfolgreich verlassen" - -msgid "QQ Group Auth" -msgstr "QQ-Gruppenauthentifikation" - -msgid "Your authorization request has been accepted by the QQ server" -msgstr "Ihre Autorisierungsanfrage wurde vom QQ-Server akzeptiert" +msgid "The Qun does not allow others to join" +msgstr "Diesen Qun können andere nicht beitreten" + +msgid "Remove from Qun" +msgstr "vom Qun entfernen" + +#, fuzzy +msgid "Join to Qun" +msgstr "Chat betreten" + +#, c-format +msgid "Qun %d denied to join" +msgstr "" + +msgid "Join Qun, Unknow Reply" +msgstr "" msgid "You entered a group ID outside the acceptable range" msgstr "Sie haben eine Gruppen-ID außerhalb des erlaubten Bereichs angegeben" @@ -7347,24 +7447,36 @@ msgid "Do you want to approve the request?" msgstr "Wollen sie die Anfrage akzeptieren?" -msgid "Enter your reason:" -msgstr "Geben Sie Ihren Grund an:" - -msgid "You have successfully modified Qun member" -msgstr "Sie haben die Qun-Mitgliedschaft erfolgreich modifiziert" - -msgid "You have successfully modified Qun information" -msgstr "Sie haben die Qun-Information erfolgreich modifiziert" +#, fuzzy +msgid "Change Qun member" +msgstr "Telefonnummer" + +msgid "Change Qun information" +msgstr "Qun-Informationen bearbeiten" msgid "You have successfully created a Qun" msgstr "Sie haben einen Qun angelegt" -msgid "Would you like to set up the Qun details now?" +#, fuzzy +msgid "Would you like to set up the detail information now?" msgstr "Möchten Sie jetzt die Qun-Details einstellen?" msgid "Setup" msgstr "Setup" +#, fuzzy, c-format +msgid "" +"%s\n" +"\n" +"%s" +msgstr "" +"%s\n" +"\n" +"%s" + +msgid "QQ Server News" +msgstr "QQ-Server-News" + msgid "System Message" msgstr "Systemnachricht" @@ -7396,8 +7508,9 @@ msgid " Video" msgstr " Video" -msgid " Space" -msgstr " Raum" +#, fuzzy +msgid " Zone" +msgstr " Zone" msgid "Flag" msgstr "Flagge" @@ -7417,20 +7530,36 @@ msgstr "Letzte Aktualisierung: %s
\n" #, c-format -msgid "Server: %s: %d
\n" -msgstr "Server: %s: %d
\n" +msgid "Server: %s
\n" +msgstr "Server: %s
\n" #, c-format msgid "Connection Mode: %s
\n" msgstr "Verbindungsmodus: %s
\n" #, c-format -msgid "Real hostname: %s: %d
\n" -msgstr "Wirklicher Hostname: %s: %d
\n" - -#, c-format -msgid "My Public IP: %s
\n" -msgstr "Meine öffentlich IP: %s
\n" +msgid "My Internet Address: %s
\n" +msgstr "Meine Internet-Adresse: %s
\n" + +#, c-format +msgid "Sent: %lu
\n" +msgstr "Gesendet: %lu
\n" + +#, c-format +msgid "Resend: %lu
\n" +msgstr "Erneut senden: %lu
\n" + +#, c-format +msgid "Lost: %lu
\n" +msgstr "Verloren: %lu
\n" + +#, c-format +msgid "Received: %lu
\n" +msgstr "Empfangen: %lu
\n" + +#, c-format +msgid "Received Duplicate: %lu
\n" +msgstr "Duplikat empfangen: %lu
\n" #, c-format msgid "Login Time: %s
\n" @@ -7453,11 +7582,11 @@ msgid "Change Password" msgstr "Passwort ändern" -msgid "Show Login Information" -msgstr "Login-Informationen anzeigen" - -msgid "Leave this QQ Qun" -msgstr "Dieses QQ-Qun verlassen" +msgid "Account Information" +msgstr "Kontoinformationen" + +msgid "Leave the QQ Qun" +msgstr "Diesen QQ-Qun verlassen" msgid "Block this buddy" msgstr "Diesen Buddy blockieren" @@ -7475,11 +7604,18 @@ msgid "QQ Protocol\tPlugin" msgstr "QQ-Protokoll-Plugin" -msgid "Connect using TCP" +msgid "Auto" +msgstr "Auto" + +#, fuzzy +msgid "Connect by TCP" msgstr "Über TCP verbinden" -msgid "resend interval(s)" -msgstr "Neusenden-Intervall(e)" +msgid "Show server notice" +msgstr "Server-Nachricht anzeigen" + +msgid "Show server news" +msgstr "Server-News anzeigen" msgid "Keep alive interval(s)" msgstr "Intervall(e) zum Aufrechterhalten der Verbindung (Keep alive)" @@ -7488,15 +7624,39 @@ msgstr "Aktualisierungsintervall(e)" #, c-format +msgid "Invalid token reply code, 0x%02X" +msgstr "Ungültiger Token-Antwort-Code, 0x%02X" + +#, c-format msgid "Invalid token len, %d" msgstr "Ungültige Länge des Tokens, %d" +msgid "Unable login for not support Redirect_EX now" +msgstr "" + +#, fuzzy, c-format +msgid "Error password: %s" +msgstr "Fehler beim Ändern des Passworts" + +#, c-format +msgid "Need active: %s" +msgstr "" + +#, fuzzy, c-format +msgid "Unable login for unknow reply code 0x%02X" +msgstr "Ungültiger Token-Antwort-Code, 0x%02X" + msgid "Keep alive error" msgstr "Fehler beim Aufrechterhalten der Verbindung (Keep alive)" -msgid "Failed to connect server" +#, fuzzy +msgid "Failed to connect all servers" msgstr "Verbinden zum Server fehlgeschlagen" +#. we didn't successfully connect. tdt->toc_fd is valid here +msgid "Unable to connect." +msgstr "Verbindung nicht möglich." + msgid "Socket error" msgstr "Socket-Fehler" @@ -7517,54 +7677,50 @@ msgid "Connection lost" msgstr "Verbindung verloren" +#. Update the login progress status display +#, fuzzy, c-format +msgid "Request token" +msgstr "Anfrage verweigert" + msgid "Couldn't resolve host" msgstr "Kann den Hostnamen nicht auflösen" -msgid "hostname is NULL or port is 0" -msgstr "Hostname ist NULL oder Port ist 0" +msgid "Invalid server or port" +msgstr "Ungültiger Server oder Port" #, c-format msgid "Connecting server %s, retries %d" msgstr "Verbinde zu Server %s, %d Wiederholungen" -#. we didn't successfully connect. tdt->toc_fd is valid here -msgid "Unable to connect." -msgstr "Verbindung nicht möglich." - -msgid "Could not resolve hostname" -msgstr "Konnte den Hostnamen nicht auflösen" - -msgid "Unable to login. Check debug log." -msgstr "Anmeldung fehlgeschlagen, Debugmitschnitt beachten." - -msgid "Unable to login" -msgstr "Anmeldung fehlgeschlagen" - -#, c-format -msgid "" -"Reply %s(0x%02X )\n" -"Sent %s(0x%02X )\n" -"Room id %d, reply [0x%02X]: \n" -"%s" +msgid "QQ Error" +msgstr "QQ-Fehler" + +msgid "Unknow SERVER CMD" +msgstr "" + +#, fuzzy, c-format +msgid "" +"Error reply of %s(0x%02X)\n" +"Room %d, reply 0x%02X" msgstr "" "Antwort %s(0x%02X )\n" "Gesendet %s(0x%02X )\n" "Raum-ID %d, Antwort [0x%02X]: \n" "%s" -msgid "Failed room reply" -msgstr "Antwort vom Raum gescheitert" - -#, c-format -msgid "You are not a member of group \"%s\"\n" -msgstr "Sie sind ein Mitglied der Gruppe „%s“\n" +msgid "QQ Qun Command" +msgstr "QQ-Qun-Kommando" + +#, c-format +msgid "You are not a member of QQ Qun \"%s\"\n" +msgstr "Sie sind kein Mitglied des Qun „%s“\n" msgid "Can not decrypt login reply" msgstr "Kann die Antwort der Anmeldung nicht entschlüsseln" -#, c-format -msgid "Invalid token reply code, 0x%02X" -msgstr "Ungültiger Token-Antwort-Code, 0x%02X" +#, fuzzy +msgid "Unknow reply CMD" +msgstr "Unbekannter Grund" #, c-format msgid "%d has declined the file %s" @@ -7577,7 +7733,12 @@ msgid "%d canceled the transfer of %s" msgstr "%d hat die Übertragung von %s abgebrochen" -msgid "Do you want to add this buddy?" +#, fuzzy +msgid "Do you approve the requestion?" +msgstr "Wollen sie die Anfrage akzeptieren?" + +#, fuzzy +msgid "Do you add the buddy?" msgstr "Möchten Sie diesen Buddy hinzufügen?" #. only need to get value @@ -7588,39 +7749,46 @@ msgid "Would you like to add him?" msgstr "Möchten Sie ihn hinzufügen?" -#, c-format -msgid "%s has added you [%s] to his or her buddy list" +#, fuzzy, c-format +msgid "%s added you [%s] to buddy list" msgstr "%s hat Sie [%s] zu seiner Buddy-Liste hinzugefügt" -#, c-format -msgid "User %s rejected your request" -msgstr "Benutzer %s hat Ihre Anfrage abgelehnt" - -#, c-format -msgid "User %s approved your request" -msgstr "Benutzer %s hat Ihre Anfrage akzeptiert" +#, fuzzy +msgid "QQ Budy" +msgstr "Buddy" + +#, c-format +msgid "Requestion rejected by %s" +msgstr "" + +#, c-format +msgid "Requestion approved by %s" +msgstr "" #. TODO: this should go through purple_account_request_authorization() #, c-format msgid "%s wants to add you [%s] as a friend" msgstr "%s möchte Sie [%s] als Freund hinzufügen" -#, c-format -msgid "Message: %s" -msgstr "Nachricht: %s" - -#, c-format -msgid "%s is not in your buddy list" +#, fuzzy, c-format +msgid "%s is not in buddy list" msgstr "%s ist nicht in Ihrer Buddy-Liste" -#, c-format -msgid "Notice from: %s" -msgstr "Ankündigung von %s" +#, fuzzy +msgid "Would you add?" +msgstr "Möchten Sie ihn hinzufügen?" + +#, c-format +msgid "From %s:" +msgstr "Von %s:" #, c-format msgid "%s" msgstr "%s" +msgid "QQ Server Notice" +msgstr "QQ-Server-Nachricht" + msgid "Connection closed (writing)" msgstr "Verbindung geschlossen (schreibend)" @@ -8221,6 +8389,7 @@ msgid "
Channel Topic:
%s" msgstr "
Thema des Kanals:
%s" +#, c-format msgid "
Channel Modes: " msgstr "
Kanal-Modi: " @@ -8245,6 +8414,7 @@ msgid "Channel Public Keys List" msgstr "Liste der öffentlichen Schlüssel des Kanals" +#, c-format msgid "" "Channel authentication is used to secure the channel from unauthorized " "access. The authentication may be based on passphrase and digital " @@ -8649,6 +8819,7 @@ msgid "Your Current Mood" msgstr "Ihre momentane Stimmung" +#, c-format msgid "Normal" msgstr "Normal" @@ -9034,37 +9205,47 @@ msgid "No server statistics available" msgstr "Keine Serverstatistik verfügbar" +#, c-format msgid "Failure: Version mismatch, upgrade your client" msgstr "Fehler: Unterschiedliche Version, aktualisieren Sie Ihren Client" +#, c-format msgid "Failure: Remote does not trust/support your public key" msgstr "" "Fehler: Die entfernte Seite vertraut Ihrem öffentlichen Schlüssel nicht" +#, c-format msgid "Failure: Remote does not support proposed KE group" msgstr "" "Fehler: Entferntes Programm unterstützt nicht die vorgeschlagen KE-Gruppe" +#, c-format msgid "Failure: Remote does not support proposed cipher" msgstr "" "Fehler: Entferntes Programm unterstützt die vorgeschlagene Cipher nicht" +#, c-format msgid "Failure: Remote does not support proposed PKCS" msgstr "Fehler: Entferntes Programm unterstützt die vorgeschlagene PKCS nicht" +#, c-format msgid "Failure: Remote does not support proposed hash function" msgstr "" "Fehler: Entferntes Programm unterstützt die vorgeschlagen Hashfunktion nicht" +#, c-format msgid "Failure: Remote does not support proposed HMAC" msgstr "Fehler: Entferntes Programm unterstützt das vorgeschlagene HMAC nicht" +#, c-format msgid "Failure: Incorrect signature" msgstr "Fehler: Falsche Signatur" +#, c-format msgid "Failure: Invalid cookie" msgstr "Fehler: Ungültiger Cookie" +#, c-format msgid "Failure: Authentication failed" msgstr "Fehler: Authentifizierung fehlgeschlagen" @@ -9090,6 +9271,9 @@ msgid "Could not create listen socket" msgstr "Kann Listen-Socket nicht erstellen" +msgid "Could not resolve hostname" +msgstr "Konnte den Hostnamen nicht auflösen" + msgid "SIP usernames may not contain whitespaces or @ symbols" msgstr "SIP-Benutzernamen dürfen keine Leerzeichen oder @-Symbole enthalten" @@ -9158,6 +9342,7 @@ msgid "Warning of %s not allowed." msgstr "Verwarnung von %s nicht erlaubt." +#, c-format msgid "A message has been dropped, you are exceeding the server speed limit." msgstr "" "Eine Nachricht ging verloren. Sie überschreiten die Geschwindigkeitsgrenze " @@ -9181,30 +9366,39 @@ "Eine Nachricht von %s hat Sie nicht erreicht, da sie zu schnell gesendet " "wurde." +#, c-format msgid "Failure." msgstr "Fehler." +#, c-format msgid "Too many matches." msgstr "Zu viele Übereinstimmungen." +#, c-format msgid "Need more qualifiers." msgstr "Benötige mehr Angaben." +#, c-format msgid "Dir service temporarily unavailable." msgstr "Verzeichnis-Dienst ist zur Zeit nicht verfügbar." +#, c-format msgid "Email lookup restricted." msgstr "E-Mail-Suche eingeschränkt." +#, c-format msgid "Keyword ignored." msgstr "Stichwort ignoriert." +#, c-format msgid "No keywords." msgstr "Keine Stichwörter." +#, c-format msgid "User has no directory information." msgstr "Der Benutzer hat kein Profil." +#, c-format msgid "Country not supported." msgstr "Land nicht unterstützt." @@ -9212,15 +9406,19 @@ msgid "Failure unknown: %s." msgstr "Unbekannter Fehler: %s." +#, c-format msgid "Incorrect username or password." msgstr "Ungültiger Benutzername oder Passwort." +#, c-format msgid "The service is temporarily unavailable." msgstr "Der Dienst ist zur Zeit nicht verfügbar." +#, c-format msgid "Your warning level is currently too high to log in." msgstr "Ihre Warnstufe ist zur Zeit zu hoch, um sich anzumelden." +#, 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." @@ -9474,6 +9672,9 @@ msgid "doodle: Request user to start a Doodle session" msgstr "doodle: Einen Benutzer auffordern, eine Mal-Sitzung zu starten" +msgid "Yahoo ID..." +msgstr "Yahoo-ID..." + #. *< type #. *< ui_requirement #. *< flags @@ -10029,6 +10230,10 @@ msgstr "Verbindung zu %s nicht möglich" #, c-format +msgid "Error reading from %s: response too long (%d bytes limit)" +msgstr "Fehler beim Lesen von %s: Antwort zu lang (%d Bytes maximal)" + +#, c-format msgid "" "Unable to allocate enough memory to hold the contents from %s. The web " "server may be trying something malicious." @@ -10057,24 +10262,29 @@ msgstr " (%s)" #. 10053 +#, c-format msgid "Connection interrupted by other software on your computer." msgstr "" "Die Verbindung wurde von einer anderen Software auf ihrem Computer " "unterbrochen." #. 10054 +#, c-format msgid "Remote host closed connection." msgstr "Der entfernte Host hat die Verbindung beendet." #. 10060 +#, c-format msgid "Connection timed out." msgstr "Verbindungsabbruch wegen Zeitüberschreitung." #. 10061 +#, c-format msgid "Connection refused." msgstr "Verbindung abgelehnt." #. 10048 +#, c-format msgid "Address already in use." msgstr "Adresse wird bereits benutzt." @@ -11305,6 +11515,7 @@ "geschützt. Die Datei 'COPYRIGHT' enthält die komplette Liste der " "Mitwirkenden. Wir übernehmen keine Haftung für dieses Programm.

" +#, c-format msgid "IRC: #pidgin on irc.freenode.net

" msgstr "IRC: #pidgin auf irc.freenode.net

" @@ -11559,6 +11770,13 @@ msgid "Color to draw hyperlinks." msgstr "Farbe zum Darstellen von Hyperlinks." +msgid "Hyperlink visited color" +msgstr "Farbe für besuchte Hyperlinks" + +msgid "Color to draw hyperlinks after it has been visited (or activated)." +msgstr "" +"Farbe zum Darstellen von Hyperlinks, wenn sie besucht (oder aktiviert) wurden" + msgid "Hyperlink prelight color" msgstr "Hyperlink-Farbe" @@ -11667,9 +11885,11 @@ msgid "Save Image" msgstr "Bild speichern" +#, c-format msgid "_Save Image..." msgstr "Bild _speichern..." +#, c-format msgid "_Add Custom Smiley..." msgstr "Benutzerdefinierten Smiley _hinzufügen..." @@ -12394,21 +12614,27 @@ msgid "Sound Selection" msgstr "Klang-Auswahl" +#, c-format msgid "Quietest" msgstr "Am leisesten" +#, c-format msgid "Quieter" msgstr "Leiser" +#, c-format msgid "Quiet" msgstr "Leise" +#, c-format msgid "Loud" msgstr "Laut" +#, c-format msgid "Louder" msgstr "Lauter" +#, c-format msgid "Loudest" msgstr "Am lautesten" @@ -12429,13 +12655,19 @@ "Klang-_Abspielbefehl:\n" "(%s für den Dateinamen)" +#, fuzzy +msgid "M_ute sounds" +msgstr "Stu_mmschalten" + msgid "Sounds when conversation has _focus" msgstr "Klang, wenn das Gespräch den _Fokus hat" -msgid "Enable sounds:" +#, fuzzy +msgid "_Enable sounds:" msgstr "Klänge aktivieren:" -msgid "Volume:" +#, fuzzy +msgid "V_olume:" msgstr "Lautstärke:" msgid "Play" @@ -13385,6 +13617,9 @@ msgid "Hyperlink Color" msgstr "Hyperlink-Farbe" +msgid "Visited Hyperlink Color" +msgstr "Farbe für besuchte Hyperlinks" + msgid "Highlighted Message Name Color" msgstr "Farbe des Absendernamens für hervorgehobene Nachrichten" @@ -13403,6 +13638,7 @@ msgid "Select Color" msgstr "Farbe auswählen" +#, c-format msgid "Select Interface Font" msgstr "Schriftart wählen" @@ -13629,6 +13865,7 @@ msgid "Timestamp Format Options" msgstr "Zeitstempelformat-Optionen" +#, c-format msgid "_Force 24-hour time format" msgstr "_Erzwinge 24-Stunden Zeitformat"