Mercurial > pidgin.yaz
changeset 27275:916f266aca98
merge of '1356d70e521b4cb5ab8ea10f0927063b5c62394a'
and '6e02b385a7decbbfdad9bc91c80b0bc86fbd8129'
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sat, 27 Jun 2009 17:50:49 +0000 |
parents | cb3e89f5a2d5 (current diff) 291724375feb (diff) |
children | 7f43d6779764 |
files | |
diffstat | 82 files changed, 2662 insertions(+), 850 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Sat Jun 27 17:50:35 2009 +0000 +++ b/COPYRIGHT Sat Jun 27 17:50:49 2009 +0000 @@ -224,6 +224,7 @@ Jochen Kemnade Yann Kerherve Gordian Klein +Krzysztof Klinikowski Akuke Kok Kir Kolyshkin F.W. Kong @@ -332,6 +333,7 @@ Luke Petre Diego Petten Nathan Peterson +Dmitry Petroff Sebastián E. Peyrott Andrea Piccinelli Celso Pinto @@ -510,6 +512,7 @@ Ma Xuan Jared Yanovich Timmy Yee +Li Yuan Nickolai Zeldovich Tom Zickel Marco Ziech
--- a/ChangeLog Sat Jun 27 17:50:35 2009 +0000 +++ b/ChangeLog Sat Jun 27 17:50:49 2009 +0000 @@ -18,11 +18,17 @@ from you on MSN. * DNS servers are re-read when DNS queries fail in case the system has moved to a new network and the old servers are not accessible. - * Gadu-Gadu accounts can specify a server to which to connect. + * GnuTLS logging (disabled by default) can be controlled through the + PURPLE_GNUTLS_DEBUG environment variable, which is an integer between + 0 and 9 (higher is more verbose). Higher values may reveal sensitive + information. + + Gadu-Gadu: + * Accounts can specify a server to which to connect. (Krzysztof "kreez" Tobola) - * Modifying the MSN privacy list for buddies not added by you (i.e. - spammers and other generally unwanted users) should no longer cause - a 240 error and disconnection. + * Correctly show tooltip status for contacts with status messages. + (Krzysztof "kkszysiu" Klinikowski) + * Support for fetching buddy icons. (Krzysztof "kkszysiu" Klinikowski) XMPP: * Voice & Video support with Jingle (XEP-0166, 0167, 0176, & 0177), voice @@ -60,6 +66,8 @@ * Fix an issue where Cyrus SASL DIGEST MD5 authentication might fail if the username, password, or realm (the JID domain) contain non-ASCII characters. + * Show emblem for mobile, handheld, and web clients and bots (if the other + client supports it). Yahoo: * P2P file transfers. (Sulabh Mahajan) @@ -67,9 +75,6 @@ (Sulabh Mahajan) * Addition of MSN buddies to Yahoo accounts by adding them as 'msn/buddy@somedomain.com' is now supported. (Sulabh Mahajan) - * Yahoo Protocol 16 support, including new HTTPS login method; this should - fix a number of login problems that have recently cropped up. (Sulabh - Mahajan, Mike "Maiku" Ruprecht) Pidgin: * Added -f command line option to tell Pidgin to ignore NetworkManager @@ -97,6 +102,12 @@ collapse buddy groups or contacts. (Peter Ruibal) * Support saving animated custom smileys as animated images or animated custom smileys. (Andrea Piccinelli) + * Support for keyboard navigation on the status icon. (Li Yuan) + * IMG tags without 'id' attributes are turned into links to the image URL. + (Dmitry Petroff) + * Draw the user's buddy icon at the bottom of the Buddy List with rounded + corners for visual consistency with the actual icons in the Buddy List. + (Kosta Arvanitis) Finch: * The hardware cursor is updated correctly. This will be useful @@ -110,6 +121,16 @@ * Preferences have been reorganized into three tabs for Colors, Fonts, and Miscellaneous categories. +version 2.5.7 (06/20/2009): + * Yahoo Protocol 16 support, including new HTTPS login method; this should + fix a number of login problems that have recently cropped up. (Sulabh + Mahajan, Mike "Maiku" Ruprecht) + * Only display the AIM "Unable to Retrieve Buddy List" message once per + connection. (Rob Taft) + * Blocking MSN users not on your buddy list no longer disconnects you. + * When performing operations on MSN, assume users are on the MSN/Passport + network if we don't get network ID's for them. + version 2.5.6 (05/19/2009): libpurple: * Improve sleep behavior by aggregation of longer timeouts on second
--- a/ChangeLog.API Sat Jun 27 17:50:35 2009 +0000 +++ b/ChangeLog.API Sat Jun 27 17:50:49 2009 +0000 @@ -39,6 +39,7 @@ * purple_network_set_turn_server * purple_network_get_stun_ip * purple_network_get_turn_ip + * purple_network_remove_port_mapping * purple_proxy_connect_udp * purple_prpl_get_media_caps * purple_prpl_got_account_actions @@ -127,6 +128,12 @@ * Purple::Request::Field::string_new * Purple::Request::Field::group_new +version 2.5.7 (06/20/2009): + No changes + +version 2.5.6 (05/19/2009): + No changes + version 2.5.5 (03/01/2009): libpurple: Changed:
--- a/ChangeLog.win32 Sat Jun 27 17:50:35 2009 +0000 +++ b/ChangeLog.win32 Sat Jun 27 17:50:49 2009 +0000 @@ -1,5 +1,11 @@ version 2.6.0 (??/??/2009): +version 2.5.7 (06/20/2009): + * No changes + +version 2.5.6 (05/19/2009): + * No changes + version 2.5.5 (03/01/2009): * Remove the "Flash window when chat messages are received" pref from the Windows Pidgin Options plugin - the Message Notification plugin
--- a/NEWS Sat Jun 27 17:50:35 2009 +0000 +++ b/NEWS Sat Jun 27 17:50:49 2009 +0000 @@ -2,6 +2,33 @@ Our development blog is available at: http://planet.pidgin.im +2.5.7 (06/20/2009): + John: This release is really just a rushed fix for the broken Yahoo + protocol plugin. I spent way more time on this release than I care + to admit, so I hope that time is well spent and this fixes the issues + people have been having. + +2.5.6 (05/19/2009): + Ka-Hing: Many much bugfixes. Hooray. (Paul told me to say that) + Oh, no one has met Paul yet? He's awesome, he backported my fixes + to the release branch so I didn't have to checkout a + workspace... except I just did to NEWS to tell you all about + that. Oh and I actually did do something for this release, none of + which is user visible though. This basically applies to the rest + of the release as well, nothing exciting, but you definitely want + it. + + Daniel: This should fix a number of annoying issues that some users + have encountered. We also would like to thank Veracode + (http://www.veracode.com) who performed a code analysis and found some + bugs that were addressed in this release. + + Elliott: I feel like I'm repeating myself, but there are some more MSN + fixes that should make things better behaved at login as well, and + maybe you'll stop getting some of those annoying errors (though not all + are fixed yet). Some other bugfixes, plus the craziness that is the + libxml "structured error handler" make up the rest of this release. + 2.5.5 (03/01/2009): John: Well, yet another release with bug fixing and patches. Hopefully one of the fixed bugs is one that irritated you. Also, thank Dimmuxx
--- a/configure.ac Sat Jun 27 17:50:35 2009 +0000 +++ b/configure.ac Sat Jun 27 17:50:49 2009 +0000 @@ -1663,7 +1663,7 @@ [enable_nss="$enableval"], [enable_nss="yes"]) -msg_ssl="None. MSN, Novell Groupwise, Yahoo! and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable!" +msg_ssl="None. MSN, Yahoo!, Novell Groupwise and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable!" looked_for_gnutls="no" dnl # dnl # Check for GnuTLS if it's specified. @@ -2057,19 +2057,19 @@ AC_MSG_ERROR([ Neither GnuTLS or NSS SSL development headers found. Use --disable-nss --disable-gnutls if you do not need SSL support. -MSN, Novell Groupwise and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable! +MSN, Yahoo!, Novell Groupwise and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable! ]) elif test "x$looked_for_gnutls" = "xyes" -a "x$force_deps" = "xyes" ; then AC_MSG_ERROR([ GnuTLS SSL development headers not found. Use --disable-gnutls if you do not need SSL support. -MSN, Novell Groupwise and Google Talk will not work without SSL support. +MSN, Yahoo!, Novell Groupwise and Google Talk will not work without SSL support. ]) elif test "x$looked_for_nss" = "xyes" -a "x$force_deps" = "xyes" ; then AC_MSG_ERROR([ NSS SSL development headers not found. Use --disable-nss if you do not need SSL support. -MSN, Novell Groupwise and Google Talk will not work without SSL support. +MSN, Yahoo!, Novell Groupwise and Google Talk will not work without SSL support. ]) fi
--- a/finch/plugins/gnthistory.c Sat Jun 27 17:50:35 2009 +0000 +++ b/finch/plugins/gnthistory.c Sat Jun 27 17:50:49 2009 +0000 @@ -31,6 +31,7 @@ #include "util.h" #include "version.h" +#include "gntconv.h" #include "gntplugin.h" #include "gntrequest.h" @@ -54,6 +55,9 @@ if (convtype == PURPLE_CONV_TYPE_IM) { GSList *buddies; GSList *cur; + FinchConv *fc = FINCH_CONV(c); + if (fc->list && fc->list->next) /* We were already in the middle of a conversation. */ + return; /* If we're not logging, don't show anything. * Otherwise, we might show a very old log. */
--- a/libpurple/cipher.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/cipher.c Sat Jun 27 17:50:49 2009 +0000 @@ -1862,6 +1862,261 @@ }; /******************************************************************************* + * SHA-256 + ******************************************************************************/ +#define SHA256_HMAC_BLOCK_SIZE 64 +#define SHA256_ROTR(X,n) ((((X) >> (n)) | ((X) << (32-(n)))) & 0xFFFFFFFF) + +static const guint32 sha256_K[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +struct SHA256Context { + guint32 H[8]; + guint32 W[64]; + + gint lenW; + + guint32 sizeHi; + guint32 sizeLo; +}; + +static void +sha256_hash_block(struct SHA256Context *sha256_ctx) { + gint i; + guint32 A, B, C, D, E, F, G, H, T1, T2; + + for(i = 16; i < 64; i++) { + sha256_ctx->W[i] = + (SHA256_ROTR(sha256_ctx->W[i-2], 17) ^ SHA256_ROTR(sha256_ctx->W[i-2], 19) ^ (sha256_ctx->W[i-2] >> 10)) + + sha256_ctx->W[i-7] + + (SHA256_ROTR(sha256_ctx->W[i-15], 7) ^ SHA256_ROTR(sha256_ctx->W[i-15], 18) ^ (sha256_ctx->W[i-15] >> 3)) + + sha256_ctx->W[i-16]; + } + + A = sha256_ctx->H[0]; + B = sha256_ctx->H[1]; + C = sha256_ctx->H[2]; + D = sha256_ctx->H[3]; + E = sha256_ctx->H[4]; + F = sha256_ctx->H[5]; + G = sha256_ctx->H[6]; + H = sha256_ctx->H[7]; + + for(i = 0; i < 64; i++) { + T1 = H + + (SHA256_ROTR(E, 6) ^ SHA256_ROTR(E, 11) ^ SHA256_ROTR(E, 25)) + + ((E & F) ^ ((~E) & G)) + + sha256_K[i] + sha256_ctx->W[i]; + T2 = (SHA256_ROTR(A, 2) ^ SHA256_ROTR(A, 13) ^ SHA256_ROTR(A, 22)) + + ((A & B) ^ (A & C) ^ (B & C)); + H = G; + G = F; + F = E; + E = D + T1; + D = C; + C = B; + B = A; + A = T1 + T2; + } + + sha256_ctx->H[0] += A; + sha256_ctx->H[1] += B; + sha256_ctx->H[2] += C; + sha256_ctx->H[3] += D; + sha256_ctx->H[4] += E; + sha256_ctx->H[5] += F; + sha256_ctx->H[6] += G; + sha256_ctx->H[7] += H; +} + +static void +sha256_set_opt(PurpleCipherContext *context, const gchar *name, void *value) { + struct SHA256Context *ctx; + + ctx = purple_cipher_context_get_data(context); + + if(!strcmp(name, "sizeHi")) { + ctx->sizeHi = GPOINTER_TO_INT(value); + } else if(!strcmp(name, "sizeLo")) { + ctx->sizeLo = GPOINTER_TO_INT(value); + } else if(!strcmp(name, "lenW")) { + ctx->lenW = GPOINTER_TO_INT(value); + } +} + +static void * +sha256_get_opt(PurpleCipherContext *context, const gchar *name) { + struct SHA256Context *ctx; + + ctx = purple_cipher_context_get_data(context); + + if(!strcmp(name, "sizeHi")) { + return GINT_TO_POINTER(ctx->sizeHi); + } else if(!strcmp(name, "sizeLo")) { + return GINT_TO_POINTER(ctx->sizeLo); + } else if(!strcmp(name, "lenW")) { + return GINT_TO_POINTER(ctx->lenW); + } + + return NULL; +} + +static void +sha256_init(PurpleCipherContext *context, void *extra) { + struct SHA256Context *sha256_ctx; + + sha256_ctx = g_new0(struct SHA256Context, 1); + + purple_cipher_context_set_data(context, sha256_ctx); + + purple_cipher_context_reset(context, extra); +} + +static void +sha256_reset(PurpleCipherContext *context, void *extra) { + struct SHA256Context *sha256_ctx; + gint i; + + sha256_ctx = purple_cipher_context_get_data(context); + + g_return_if_fail(sha256_ctx); + + sha256_ctx->lenW = 0; + sha256_ctx->sizeHi = 0; + sha256_ctx->sizeLo = 0; + + sha256_ctx->H[0] = 0x6a09e667; + sha256_ctx->H[1] = 0xbb67ae85; + sha256_ctx->H[2] = 0x3c6ef372; + sha256_ctx->H[3] = 0xa54ff53a; + sha256_ctx->H[4] = 0x510e527f; + sha256_ctx->H[5] = 0x9b05688c; + sha256_ctx->H[6] = 0x1f83d9ab; + sha256_ctx->H[7] = 0x5be0cd19; + + for(i = 0; i < 64; i++) + sha256_ctx->W[i] = 0; +} + +static void +sha256_uninit(PurpleCipherContext *context) { + struct SHA256Context *sha256_ctx; + + purple_cipher_context_reset(context, NULL); + + sha256_ctx = purple_cipher_context_get_data(context); + + memset(sha256_ctx, 0, sizeof(struct SHA256Context)); + + g_free(sha256_ctx); + sha256_ctx = NULL; +} + + +static void +sha256_append(PurpleCipherContext *context, const guchar *data, size_t len) { + struct SHA256Context *sha256_ctx; + gint i; + + sha256_ctx = purple_cipher_context_get_data(context); + + g_return_if_fail(sha256_ctx); + + for(i = 0; i < len; i++) { + sha256_ctx->W[sha256_ctx->lenW / 4] <<= 8; + sha256_ctx->W[sha256_ctx->lenW / 4] |= data[i]; + + if((++sha256_ctx->lenW) % 64 == 0) { + sha256_hash_block(sha256_ctx); + sha256_ctx->lenW = 0; + } + + sha256_ctx->sizeLo += 8; + sha256_ctx->sizeHi += (sha256_ctx->sizeLo < 8); + } +} + +static gboolean +sha256_digest(PurpleCipherContext *context, size_t in_len, guchar digest[32], + size_t *out_len) +{ + struct SHA256Context *sha256_ctx; + guchar pad0x80 = 0x80, pad0x00 = 0x00; + guchar padlen[8]; + gint i; + + g_return_val_if_fail(in_len >= 32, FALSE); + + sha256_ctx = purple_cipher_context_get_data(context); + + g_return_val_if_fail(sha256_ctx, FALSE); + + padlen[0] = (guchar)((sha256_ctx->sizeHi >> 24) & 255); + padlen[1] = (guchar)((sha256_ctx->sizeHi >> 16) & 255); + padlen[2] = (guchar)((sha256_ctx->sizeHi >> 8) & 255); + padlen[3] = (guchar)((sha256_ctx->sizeHi >> 0) & 255); + padlen[4] = (guchar)((sha256_ctx->sizeLo >> 24) & 255); + padlen[5] = (guchar)((sha256_ctx->sizeLo >> 16) & 255); + padlen[6] = (guchar)((sha256_ctx->sizeLo >> 8) & 255); + padlen[7] = (guchar)((sha256_ctx->sizeLo >> 0) & 255); + + /* pad with a 1, then zeroes, then length */ + purple_cipher_context_append(context, &pad0x80, 1); + while(sha256_ctx->lenW != 56) + purple_cipher_context_append(context, &pad0x00, 1); + purple_cipher_context_append(context, padlen, 8); + + for(i = 0; i < 32; i++) { + digest[i] = (guchar)(sha256_ctx->H[i / 4] >> 24); + sha256_ctx->H[i / 4] <<= 8; + } + + purple_cipher_context_reset(context, NULL); + + if(out_len) + *out_len = 32; + + return TRUE; +} + +static size_t +sha256_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return SHA256_HMAC_BLOCK_SIZE; +} + +static PurpleCipherOps SHA256Ops = { + sha256_set_opt, /* Set Option */ + sha256_get_opt, /* Get Option */ + sha256_init, /* init */ + sha256_reset, /* reset */ + sha256_uninit, /* uninit */ + NULL, /* set iv */ + sha256_append, /* append */ + sha256_digest, /* digest */ + NULL, /* encrypt */ + NULL, /* decrypt */ + NULL, /* set salt */ + NULL, /* get salt size */ + NULL, /* set key */ + NULL, /* get key size */ + NULL, /* set batch mode */ + NULL, /* get batch mode */ + sha256_get_block_size, /* get block size */ + NULL /* set key with len */ +}; + +/******************************************************************************* * RC4 ******************************************************************************/ @@ -2228,6 +2483,7 @@ purple_ciphers_register_cipher("md5", &MD5Ops); purple_ciphers_register_cipher("sha1", &SHA1Ops); + purple_ciphers_register_cipher("sha256", &SHA256Ops); purple_ciphers_register_cipher("md4", &MD4Ops); purple_ciphers_register_cipher("hmac", &HMACOps); purple_ciphers_register_cipher("des", &DESOps);
--- a/libpurple/connection.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/connection.c Sat Jun 27 17:50:49 2009 +0000 @@ -309,7 +309,7 @@ g_free(gc->password); g_free(gc->display_name); - if (gc->disconnect_timeout) + if (gc->disconnect_timeout > 0) purple_timeout_remove(gc->disconnect_timeout); PURPLE_DBUS_UNREGISTER_POINTER(gc); @@ -515,11 +515,20 @@ static gboolean purple_connection_disconnect_cb(gpointer data) { - PurpleAccount *account = data; - char *password = g_strdup(purple_account_get_password(account)); + PurpleAccount *account; + PurpleConnection *gc; + char *password; + + account = data; + gc = purple_account_get_connection(account); + + gc->disconnect_timeout = 0; + + password = g_strdup(purple_account_get_password(account)); purple_account_disconnect(account); purple_account_set_password(account, password); g_free(password); + return FALSE; } @@ -564,7 +573,7 @@ } /* If we've already got one error, we don't need any more */ - if (gc->disconnect_timeout) + if (gc->disconnect_timeout > 0) return; gc->wants_to_die = purple_connection_error_is_fatal (reason);
--- a/libpurple/connection.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/connection.h Sat Jun 27 17:50:49 2009 +0000 @@ -560,6 +560,9 @@ * Checks if gc is still a valid pointer to a gc. * * @return @c TRUE if gc is valid. + * + * @deprecated Do not use this. Instead, cancel your asynchronous request + * when the PurpleConnection is destroyed. */ /* * TODO: Eventually this bad boy will be removed, because it is
--- a/libpurple/core.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/core.h Sat Jun 27 17:50:49 2009 +0000 @@ -188,7 +188,9 @@ * <dd>the UI's development/support website, such as http://developer.pidgin.im.</dd> * * <dt><tt>client_type</tt></dt> - * <dd>the type of UI (pc, console, phone, handheld, web, bot)</dd> + * <dd>the type of UI. Possible values include 'pc', 'console', 'phone', + * 'handheld', 'web', and 'bot'. These values are compared + * programmatically and should not be localized.</dd> * * </dl> *
--- a/libpurple/network.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/network.c Sat Jun 27 17:50:49 2009 +0000 @@ -105,6 +105,10 @@ static gchar *stun_ip = NULL; static gchar *turn_ip = NULL; +/* Keep track of port mappings done with UPnP and NAT-PMP */ +static GHashTable *upnp_port_mappings = NULL; +static GHashTable *nat_pmp_port_mappings = NULL; + const unsigned char * purple_network_ip_atoi(const char *ip) { @@ -257,6 +261,15 @@ return; } + if (success) { + /* add port mapping to hash table */ + gint *key = g_new(gint, 1); + gint *value = g_new(gint, 1); + *key = purple_network_get_port_from_fd(listen_data->listenfd); + *value = listen_data->socket_type; + g_hash_table_insert(upnp_port_mappings, key, value); + } + if (listen_data->cb) listen_data->cb(listen_data->listenfd, listen_data->cb_data); @@ -270,9 +283,16 @@ purple_network_finish_pmp_map_cb(gpointer data) { PurpleNetworkListenData *listen_data; + gint *key = g_new(gint, 1); + gint *value = g_new(gint, 1); listen_data = data; + /* add port mapping to hash table */ + *key = purple_network_get_port_from_fd(listen_data->listenfd); + *value = listen_data->socket_type; + g_hash_table_insert(nat_pmp_port_mappings, key, value); + if (listen_data->cb) listen_data->cb(listen_data->listenfd, listen_data->cb_data); @@ -892,6 +912,61 @@ return &handle; } +static void +purple_network_upnp_mapping_remove_cb(gboolean sucess, gpointer data) +{ + purple_debug_info("network", "done removing UPnP port mapping\n"); +} + +/* the reason for these functions to have these signatures is to be able to + use them for g_hash_table_foreach to clean remaining port mappings, which is + not yet done */ +static void +purple_network_upnp_mapping_remove(gpointer key, gpointer value, + gpointer user_data) +{ + gint port = (gint) *((gint *) key); + gint protocol = (gint) *((gint *) value); + purple_debug_info("network", "removing UPnP port mapping for port %d\n", + port); + purple_upnp_remove_port_mapping(port, + protocol == SOCK_STREAM ? "TCP" : "UDP", + purple_network_upnp_mapping_remove_cb, NULL); + g_hash_table_remove(upnp_port_mappings, key); +} + +static void +purple_network_nat_pmp_mapping_remove(gpointer key, gpointer value, + gpointer user_data) +{ + gint port = (gint) *((gint *) key); + gint protocol = (gint) *((gint *) value); + purple_debug_info("network", "removing NAT-PMP port mapping for port %d\n", + port); + purple_pmp_destroy_map( + protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP, + port); + g_hash_table_remove(nat_pmp_port_mappings, key); +} + +void +purple_network_remove_port_mapping(gint fd) +{ + int port = purple_network_get_port_from_fd(fd); + gint *protocol = g_hash_table_lookup(upnp_port_mappings, &port); + + if (protocol) { + purple_network_upnp_mapping_remove(&port, protocol, NULL); + g_hash_table_remove(upnp_port_mappings, protocol); + } else { + protocol = g_hash_table_lookup(nat_pmp_port_mappings, &port); + if (protocol) { + purple_network_nat_pmp_mapping_remove(&port, protocol, NULL); + g_hash_table_remove(nat_pmp_port_mappings, protocol); + } + } +} + void purple_network_init(void) { @@ -964,8 +1039,15 @@ purple_prefs_get_string("/purple/network/stun_server")); purple_network_set_turn_server( purple_prefs_get_string("/purple/network/turn_server")); + + upnp_port_mappings = + g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + nat_pmp_port_mappings = + g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); } + + void purple_network_uninit(void) { @@ -1008,4 +1090,10 @@ if (stun_ip) g_free(stun_ip); + + g_hash_table_destroy(upnp_port_mappings); + g_hash_table_destroy(nat_pmp_port_mappings); + + /* TODO: clean up remaining port mappings, note calling + purple_upnp_remove_port_mapping from here doesn't quite work... */ }
--- a/libpurple/network.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/network.h Sat Jun 27 17:50:49 2009 +0000 @@ -246,10 +246,10 @@ * Update the TURN server IP given the host name * Will result in a DNS query being executed asynchronous * - * @param stun_server The host name of the STUN server to set + * @param turn_server The host name of the STUN server to set * @since 2.6.0 */ -void purple_network_set_turn_server(const gchar *stun_server); +void purple_network_set_turn_server(const gchar *turn_server); /** * Get the IP address of the STUN server as a string representation @@ -259,7 +259,14 @@ */ const gchar *purple_network_get_turn_ip(void); - +/** + * Remove a port mapping (UPnP or NAT-PMP) associated with listening socket + * + * @param fd Socket to remove the port mapping for + * @since 2.6.0 + */ +void purple_network_remove_port_mapping(gint fd); + /** * Initializes the network subsystem. */
--- a/libpurple/notify.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/notify.h Sat Jun 27 17:50:49 2009 +0000 @@ -427,7 +427,9 @@ * Displays a notification for multiple emails to the user. * * @param handle The plugin or connection handle. - * @param count The number of emails. + * @param count The number of emails. '0' can be used to signify that + * the user has no unread emails and the UI should remove + * the mail notification. * @param detailed @c TRUE if there is information for each email in the * arrays. * @param subjects The array of subjects.
--- a/libpurple/plugins/ssl/ssl-gnutls.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/plugins/ssl/ssl-gnutls.c Sat Jun 27 17:50:49 2009 +0000 @@ -43,8 +43,17 @@ static gnutls_certificate_client_credentials xcred; static void +ssl_gnutls_log(int level, const char *str) +{ + /* GnuTLS log messages include the '\n' */ + purple_debug_misc("gnutls", "lvl %d: %s", level, str); +} + +static void ssl_gnutls_init_gnutls(void) { + const char *debug_level; + /* Configure GnuTLS to use glib memory management */ /* I expect that this isn't really necessary, but it may prevent some bugs */ @@ -59,6 +68,20 @@ (gnutls_free_function) g_free /* free */ ); + debug_level = g_getenv("PURPLE_GNUTLS_DEBUG"); + if (debug_level) { + int level = atoi(debug_level); + if (level < 0) { + purple_debug_warning("gnutls", "Assuming log level 0 instead of %d\n", + level); + level = 0; + } + + /* "The level is an integer between 0 and 9. Higher values mean more verbosity." */ + gnutls_global_set_log_level(level); + gnutls_global_set_log_function(ssl_gnutls_log); + } + gnutls_global_init(); gnutls_certificate_allocate_credentials(&xcred); @@ -576,7 +599,6 @@ out_buf, out_size); g_free(out_buf); - g_return_val_if_fail(success, FALSE); return success; }
--- a/libpurple/protocols/bonjour/bonjour_ft.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Sat Jun 27 17:50:49 2009 +0000 @@ -880,7 +880,9 @@ purple_proxy_info_set_type(xf->proxy_info, PURPLE_PROXY_SOCKS5); purple_proxy_info_set_host(xf->proxy_info, xf->proxy_host); purple_proxy_info_set_port(xf->proxy_info, xf->proxy_port); - xf->proxy_connection = purple_proxy_connect_socks5(NULL, xf->proxy_info, + xf->proxy_connection = purple_proxy_connect_socks5( + purple_account_get_connection(account), + xf->proxy_info, dstaddr, 0, bonjour_bytestreams_connect_cb, xfer);
--- a/libpurple/protocols/bonjour/jabber.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Sat Jun 27 17:50:49 2009 +0000 @@ -926,7 +926,9 @@ } purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE); - connect_data = purple_proxy_connect(NULL, jdata->account, + connect_data = purple_proxy_connect( + purple_account_get_connection(jdata->account), + jdata->account, ip, bb->port_p2pj, _connected_to_buddy, pb); if (connect_data == NULL) {
--- a/libpurple/protocols/gg/gg.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/gg/gg.c Sat Jun 27 17:50:49 2009 +0000 @@ -7,6 +7,7 @@ * * Some parts of the code are adapted or taken from the previous implementation * of this plugin written by Arkadiusz Miskiewicz <misiek@pld.org.pl> + * Some parts Copyright (C) 2009 Krzysztof Klinikowski <grommasher@gmail.com> * * Thanks to Google's Summer of Code Program. * @@ -36,6 +37,7 @@ #include "debug.h" #include "util.h" #include "request.h" +#include "xmlnode.h" #include <libgadu.h> @@ -858,6 +860,133 @@ static void ggp_set_status(PurpleAccount *account, PurpleStatus *status); static int ggp_to_gg_status(PurpleStatus *status, char **msg); +struct gg_fetch_avatar_data +{ + PurpleConnection *gc; + gchar *uin; + gchar *avatar_url; +}; + + +static void gg_fetch_avatar_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, + const gchar *data, size_t len, const gchar *error_message) { + struct gg_fetch_avatar_data *d = user_data; + PurpleAccount *account; + PurpleBuddy *buddy; + gpointer buddy_icon_data; + + /* FIXME: This shouldn't be necessary */ + if (!PURPLE_CONNECTION_IS_VALID(d->gc)) { + g_free(d->uin); + g_free(d->avatar_url); + g_free(d); + g_return_if_reached(); + } + + account = purple_connection_get_account(d->gc); + buddy = purple_find_buddy(account, d->uin); + + if (buddy == NULL) + goto out; + + buddy_icon_data = g_memdup(data, len); + + purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy), + buddy_icon_data, len, d->avatar_url); + purple_debug_info("gg", "UIN: %s should have avatar now\n", d->uin); + +out: + g_free(d->uin); + g_free(d->avatar_url); + g_free(d); +} + +static void gg_get_avatar_url_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, + const gchar *url_text, size_t len, const gchar *error_message) { + struct gg_fetch_avatar_data *data; + PurpleConnection *gc = user_data; + PurpleAccount *account; + PurpleBuddy *buddy; + const char *uin; + const char *is_blank; + const char *checksum; + + gchar *bigavatar = NULL; + xmlnode *xml = NULL; + xmlnode *xmlnode_users; + xmlnode *xmlnode_user; + xmlnode *xmlnode_avatars; + xmlnode *xmlnode_avatar; + xmlnode *xmlnode_bigavatar; + + g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); + account = purple_connection_get_account(gc); + + if (error_message != NULL) + purple_debug_error("gg", "gg_get_avatars_cb error: %s\n", error_message); + else if (len > 0 && url_text && *url_text) { + xml = xmlnode_from_str(url_text, -1); + if (xml == NULL) + goto out; + + xmlnode_users = xmlnode_get_child(xml, "users"); + if (xmlnode_users == NULL) + goto out; + + xmlnode_user = xmlnode_get_child(xmlnode_users, "user"); + if (xmlnode_user == NULL) + goto out; + + uin = xmlnode_get_attrib(xmlnode_user, "uin"); + + xmlnode_avatars = xmlnode_get_child(xmlnode_user, "avatars"); + if (xmlnode_avatars == NULL) + goto out; + + xmlnode_avatar = xmlnode_get_child(xmlnode_avatars, "avatar"); + if (xmlnode_avatar == NULL) + goto out; + + xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "bigAvatar"); + if (xmlnode_bigavatar == NULL) + goto out; + + is_blank = xmlnode_get_attrib(xmlnode_avatar, "blank"); + bigavatar = xmlnode_get_data(xmlnode_bigavatar); + + purple_debug_info("gg", "gg_get_avatar_url_cb: UIN %s, IS_BLANK %s, " + "URL %s\n", + uin ? uin : "(null)", is_blank ? is_blank : "(null)", + bigavatar ? bigavatar : "(null)"); + + if (uin != NULL && bigavatar != NULL) { + buddy = purple_find_buddy(account, uin); + if (buddy == NULL) + goto out; + + checksum = purple_buddy_icons_get_checksum_for_user(buddy); + + if (purple_strequal(is_blank, "1")) { + purple_buddy_icons_set_for_user(account, + purple_buddy_get_name(buddy), NULL, 0, NULL); + } else if (!purple_strequal(checksum, bigavatar)) { + data = g_new0(struct gg_fetch_avatar_data, 1); + data->gc = gc; + data->uin = g_strdup(uin); + data->avatar_url = g_strdup(bigavatar); + + url_data = purple_util_fetch_url_request_len_with_account(account, + bigavatar, TRUE, "Mozilla/4.0 (compatible; MSIE 5.0)", + FALSE, NULL, FALSE, -1, gg_fetch_avatar_cb, data); + } + } + } + +out: + if (xml) + xmlnode_free(xml); + g_free(bigavatar); +} /** * Handle change of the status of the buddy. @@ -873,8 +1002,19 @@ gchar *from; const char *st; gchar *msg; - - from = g_strdup_printf("%ld", (unsigned long int)uin); + gchar *avatarurl; + PurpleUtilFetchUrlData *url_data; + + from = g_strdup_printf("%u", uin); + avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%s/0.xml", from); + + url_data = purple_util_fetch_url_request_len_with_account( + purple_connection_get_account(gc), avatarurl, TRUE, + "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1, + gg_get_avatar_url_cb, gc); + + g_free(avatarurl); + switch (status) { case GG_STATUS_NOT_AVAIL: case GG_STATUS_NOT_AVAIL_DESCR: @@ -931,12 +1071,15 @@ purple_debug_info("gg", "ggp_status_by_id: %d\n", id); switch (id) { case GG_STATUS_NOT_AVAIL: + case GG_STATUS_NOT_AVAIL_DESCR: st = _("Offline"); break; case GG_STATUS_AVAIL: + case GG_STATUS_AVAIL_DESCR: st = _("Available"); break; case GG_STATUS_BUSY: + case GG_STATUS_BUSY_DESCR: st = _("Away"); break; default:
--- a/libpurple/protocols/jabber/auth.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/auth.c Sat Jun 27 17:50:49 2009 +0000 @@ -990,12 +990,16 @@ xmlnode_set_namespace(response, "urn:ietf:params:xml:ns:xmpp-sasl"); if (clen > 0) { /* Cyrus SASL 2.1.22 appears to contain code to add the charset - * to the response but there is no possibility it will be executed. + * to the response for DIGEST-MD5 but there is no possibility + * it will be executed. + * * My reading of the digestmd5 plugin indicates the username and * realm are always encoded in UTF-8 (they seem to be the values * we pass in), so we need to ensure charset=utf-8 is set. */ - if (strstr(c_out, ",charset=")) + if (!js->current_mech || !g_str_equal(js->current_mech, "DIGEST-MD5") || + strstr(c_out, ",charset=")) + /* If we're not using DIGEST-MD5 or Cyrus SASL is fixed */ enc_out = purple_base64_encode((unsigned char*)c_out, clen); else { char *tmp = g_strdup_printf("%s,charset=utf-8", c_out);
--- a/libpurple/protocols/jabber/bosh.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/bosh.c Sat Jun 27 17:50:49 2009 +0000 @@ -243,7 +243,7 @@ /* Easy solution: Does everyone involved support pipelining? Hooray! Just use * one TCP connection! */ if (conn->pipelining) - return conn->connections[0]; + return conn->connections[0]->ready ? conn->connections[0] : NULL; /* First loop, look for a connection that's ready */ for (i = 0; i < MAX_HTTP_CONNECTIONS; ++i) { @@ -267,8 +267,8 @@ } static void -jabber_bosh_connection_send(PurpleBOSHConnection *conn, PurpleBOSHPacketType type, - const char *data) +jabber_bosh_connection_send(PurpleBOSHConnection *conn, + PurpleBOSHPacketType type, const char *data) { PurpleHTTPConnection *chosen; GString *packet = NULL; @@ -277,14 +277,16 @@ if (type != PACKET_NORMAL && !chosen) { /* - * For non-ordinary traffic, we don't want to 'buffer' it, so use the first - * connection. + * For non-ordinary traffic, we don't want to 'buffer' it, so use the + * first connection. */ chosen = conn->connections[0]; - if (!chosen->ready) - purple_debug_warning("jabber", "First BOSH connection wasn't ready. Bad " - "things may happen.\n"); + if (!chosen->ready) { + purple_debug_info("jabber", "Unable to find a ready BOSH " + "connection. Ignoring send of type 0x%02x.\n", type); + return; + } } if (type == PACKET_NORMAL && (!chosen ||
--- a/libpurple/protocols/jabber/buddy.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/buddy.c Sat Jun 27 17:50:49 2009 +0000 @@ -1102,8 +1102,12 @@ char *txt, *vcard_hash = NULL; if (type == JABBER_IQ_ERROR) { - purple_debug_warning("jabber", "Server returned error while retrieving vCard"); - return; + xmlnode *error; + purple_debug_warning("jabber", "Server returned error while retrieving vCard\n"); + + error = xmlnode_get_child(packet, "error"); + if (!error || !xmlnode_get_child(error, "item-not-found")) + return; } if((vcard = xmlnode_get_child(packet, "vCard")) ||
--- a/libpurple/protocols/jabber/caps.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/caps.c Sat Jun 27 17:50:49 2009 +0000 @@ -534,6 +534,31 @@ return; } + node_exts = (userdata->data->info ? userdata->data->info->exts : + userdata->data->node_exts); + + /* TODO: I don't see how this can actually happen, but it crashed khc. */ + if (!node_exts) { + purple_debug_error("jabber", "Couldn't find JabberCapsNodeExts. If you " + "see this, please tell darkrain42 and save your debug log.\n" + "JabberCapsClientInfo = %p\n", userdata->data->info); + + + /* Try once more to find the exts and then fail */ + node_exts = jabber_caps_find_exts_by_node(userdata->data->node); + if (node_exts) { + purple_debug_info("jabber", "Found the exts on the second try.\n"); + if (userdata->data->info) + userdata->data->info->exts = node_exts; + else + userdata->data->node_exts = node_exts; + } else { + cbplususerdata_unref(userdata->data); + g_free(userdata); + g_return_if_reached(); + } + } + /* So, we decrement this after checking for an error, which means that * if there *is* an error, we'll never call the callback passed to * jabber_caps_get_info. We will still free all of our data, though. @@ -547,8 +572,6 @@ features = g_list_prepend(features, g_strdup(var)); } - node_exts = (userdata->data->info ? userdata->data->info->exts : - userdata->data->node_exts); g_hash_table_insert(node_exts->exts, g_strdup(userdata->name), features); schedule_caps_save(); @@ -589,9 +612,7 @@ } userdata = g_new0(jabber_caps_cbplususerdata, 1); - /* This ref is given to fetching the basic node#ver info if we need it - * or unrefed at the bottom of this function */ - cbplususerdata_ref(userdata); + /* We start out with 0 references. Every query takes one */ userdata->cb = cb; userdata->cb_data = user_data; userdata->who = g_strdup(who); @@ -617,6 +638,8 @@ g_free(nodever); xmlnode_set_attrib(iq->node, "to", who); + cbplususerdata_ref(userdata); + jabber_iq_set_callback(iq, jabber_caps_client_iqcb, userdata); jabber_iq_send(iq); } @@ -669,6 +692,10 @@ } if (userdata->info && userdata->extOutstanding == 0) { + /* Start with 1 ref so the below functions are happy */ + userdata->ref = 1; + + /* We have everything we need right now */ jabber_caps_get_info_complete(userdata); cbplususerdata_unref(userdata); }
--- a/libpurple/protocols/jabber/jabber.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sat Jun 27 17:50:49 2009 +0000 @@ -758,6 +758,8 @@ PurpleConnection *gc = purple_account_get_connection(account); const char *connect_server = purple_account_get_string(account, "connect_server", ""); + const char *bosh_url = purple_account_get_string(account, + "bosh_url", ""); JabberStream *js; PurplePresence *presence; PurpleStoredImage *image; @@ -824,21 +826,25 @@ jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); /* TODO: Just use purple_url_parse? */ - if (!g_ascii_strncasecmp(connect_server, "http://", 7) || !g_ascii_strncasecmp(connect_server, "https://", 8)) { + /* If both BOSH and a Connect Server are specified, we prefer BOSH. I'm not + * attached to that choice, though. + */ + if (*bosh_url) { js->use_bosh = TRUE; - js->bosh = jabber_bosh_connection_init(js, connect_server); - if (!js->bosh) { + js->bosh = jabber_bosh_connection_init(js, bosh_url); + if (js->bosh) + jabber_bosh_connection_connect(js->bosh); + else { purple_connection_error_reason (js->gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("Malformed BOSH Connect Server")); - return; + _("Malformed BOSH URL")); } - jabber_bosh_connection_connect(js->bosh); + return; - } else { - js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain); } + js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain); + /* if they've got old-ssl mode going, we probably want to ignore SRV lookups */ if(purple_account_get_bool(js->gc->account, "old_ssl", FALSE)) { if(purple_ssl_is_supported()) {
--- a/libpurple/protocols/jabber/jabber.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.h Sat Jun 27 17:50:49 2009 +0000 @@ -325,8 +325,8 @@ void jabber_add_feature(const gchar *namespace, JabberFeatureEnabled cb); /* cb may be NULL */ void jabber_remove_feature(const gchar *namespace); -/** Adds an identitiy to this jabber library instance. For list of valid values vistit the - * webiste of the XMPP Registrar ( http://www.xmpp.org/registrar/disco-categories.html#client ). +/** Adds an identity to this jabber library instance. For list of valid values visit the + * website of the XMPP Registrar ( http://www.xmpp.org/registrar/disco-categories.html#client ). * @param category the category of the identity. * @param type the type of the identity. * @param language the language localization of the name. Can be NULL.
--- a/libpurple/protocols/jabber/libxmpp.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Sat Jun 27 17:50:49 2009 +0000 @@ -319,6 +319,11 @@ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = purple_account_option_string_new(_("BOSH URL"), + "bosh_url", NULL); + 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"),
--- a/libpurple/protocols/jabber/presence.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/presence.c Sat Jun 27 17:50:49 2009 +0000 @@ -245,7 +245,9 @@ { xmlnode *show, *status, *presence, *pri, *c; const char *show_string = NULL; +#ifdef USE_VV gboolean audio_enabled, video_enabled; +#endif presence = xmlnode_new("presence");
--- a/libpurple/protocols/jabber/si.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/si.c Sat Jun 27 17:50:49 2009 +0000 @@ -1321,6 +1321,11 @@ jabber_iq_remove_callback_by_id(js, jsx->iq_id); if (jsx->local_streamhost_fd >= 0) close(jsx->local_streamhost_fd); + if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && + xfer->fd >= 0) { + purple_debug_info("jabber", "remove port mapping\n"); + purple_network_remove_port_mapping(xfer->fd); + } if (jsx->connect_timeout > 0) purple_timeout_remove(jsx->connect_timeout); if (jsx->ibb_timeout_handle > 0)
--- a/libpurple/protocols/jabber/useravatar.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/jabber/useravatar.c Sat Jun 27 17:50:49 2009 +0000 @@ -257,6 +257,8 @@ gsize len, const gchar *error_message) { JabberBuddyAvatarUpdateURLInfo *info = user_data; + gpointer icon_data; + if(!url_text) { purple_debug(PURPLE_DEBUG_ERROR, "jabber", "do_buddy_avatar_update_fromurl got error \"%s\"", @@ -264,7 +266,8 @@ goto out; } - purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, (void*)url_text, len, info->id); + icon_data = g_memdup(url_text, len); + purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, icon_data, len, info->id); out: g_free(info->from);
--- a/libpurple/protocols/msn/cmdproc.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/msn/cmdproc.c Sat Jun 27 17:50:49 2009 +0000 @@ -210,6 +210,7 @@ trans = g_new0(MsnTransaction, 1); + trans->cmdproc = cmdproc; trans->command = g_strdup(command); if (format != NULL)
--- a/libpurple/protocols/msn/history.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/msn/history.c Sat Jun 27 17:50:49 2009 +0000 @@ -68,6 +68,7 @@ msn_history_add(MsnHistory *history, MsnTransaction *trans) { GQueue *queue; + int max_elems; g_return_if_fail(history != NULL); g_return_if_fail(trans != NULL); @@ -78,7 +79,12 @@ g_queue_push_tail(queue, trans); - if (queue->length > MSN_HIST_ELEMS) + if (trans->cmdproc->servconn->type == MSN_SERVCONN_NS) + max_elems = MSN_NS_HIST_ELEMS; + else + max_elems = MSN_SB_HIST_ELEMS; + + if (queue->length > max_elems) { trans = g_queue_pop_head(queue); msn_transaction_destroy(trans);
--- a/libpurple/protocols/msn/history.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/msn/history.h Sat Jun 27 17:50:49 2009 +0000 @@ -24,7 +24,8 @@ #ifndef _MSN_HISTORY_H #define _MSN_HISTORY_H -#define MSN_HIST_ELEMS 0x30 +#define MSN_NS_HIST_ELEMS 0x300 +#define MSN_SB_HIST_ELEMS 0x30 typedef struct _MsnHistory MsnHistory;
--- a/libpurple/protocols/msn/notification.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/msn/notification.c Sat Jun 27 17:50:49 2009 +0000 @@ -327,14 +327,13 @@ /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued * command and we are processing it */ if (cmd->payload == NULL) { - cmdproc->last_cmd->payload_cb = msg_cmd_post; + cmdproc->last_cmd->payload_cb = msg_cmd_post; cmd->payload_len = atoi(cmd->params[2]); - } else { g_return_if_fail(cmd->payload_cb != NULL); #if 0 /* glib on win32 doesn't correctly support precision modifiers for a string */ - purple_debug_info("msn", "MSG payload:{%.*s}\n", cmd->payload_len, cmd->payload); + purple_debug_info("msn", "MSG payload:{%.*s}\n", (guint)cmd->payload_len, cmd->payload); #endif cmd->payload_cb(cmdproc, cmd, cmd->payload, cmd->payload_len); }
--- a/libpurple/protocols/myspace/markup.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/myspace/markup.c Sat Jun 27 17:50:49 2009 +0000 @@ -112,8 +112,10 @@ } } -/** Convert typographical font point size to HTML font size. - * Based on libpurple/gtkimhtml.c */ +/** + * Convert typographical font point size to HTML font size. + * Based on libpurple/gtkimhtml.c + */ static guint msim_point_to_purple_size(MsimSession *session, guint point) { @@ -135,7 +137,9 @@ return this_point; } -/** Convert HTML font size to point size. */ +/** + * Convert HTML font size to point size. + */ static guint msim_purple_size_to_point(MsimSession *session, guint size) { @@ -155,7 +159,9 @@ return point; } -/** Convert a msim markup font pixel height to the more usual point size, for incoming messages. */ +/** + * Convert a msim markup font pixel height to the more usual point size, for incoming messages. + */ static guint msim_height_to_point(MsimSession *session, guint height) { @@ -169,7 +175,9 @@ * _font_size_ichat_to_purple */ } -/** Convert point size to msim pixel height font size specification, for outgoing messages. */ +/** + * Convert point size to msim pixel height font size specification, for outgoing messages. + */ static guint msim_point_to_height(MsimSession *session, guint point) { @@ -180,7 +188,9 @@ return (guint)msim_round((dpi * 1. / POINTS_PER_INCH) * point); } -/** Convert the msim markup <f> (font) tag into HTML. */ +/** + * Convert the msim markup <f> (font) tag into HTML. + */ static void msim_markup_f_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) { @@ -244,7 +254,8 @@ *end = g_string_free(gs_end, FALSE); } -/** Convert a msim markup color to a color suitable for libpurple. +/** + * Convert a msim markup color to a color suitable for libpurple. * * @param msim Either a color name, or an rgb(x,y,z) code. * @@ -268,7 +279,9 @@ return g_strdup_printf("#%.2x%.2x%.2x", red, green, blue); } -/** Convert the msim markup <a> (anchor) tag into HTML. */ +/** + * Convert the msim markup <a> (anchor) tag into HTML. + */ static void msim_markup_a_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) { @@ -283,7 +296,9 @@ *end = g_strdup("</a>"); } -/** Convert the msim markup <p> (paragraph) tag into HTML. */ +/** + * Convert the msim markup <p> (paragraph) tag into HTML. + */ static void msim_markup_p_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) { @@ -356,7 +371,9 @@ g_free(purple_color); } -/** Convert the msim markup <i> tag (emoticon image) into HTML. */ +/** + * Convert the msim markup <i> tag (emoticon image) into HTML. + */ static void msim_markup_i_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) { @@ -387,7 +404,9 @@ *end = g_strdup(""); } -/** Convert an individual msim markup tag to HTML. */ +/** + * Convert an individual msim markup tag to HTML. + */ static int msim_markup_tag_to_html(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) @@ -416,7 +435,9 @@ return 0; } -/** Convert an individual HTML tag to msim markup. */ +/** + * Convert an individual HTML tag to msim markup. + */ static int html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) @@ -568,7 +589,8 @@ return ret; } -/** Convert an xmlnode of msim markup or HTML to an HTML string or msim markup. +/** + * Convert an xmlnode of msim markup or HTML to an HTML string or msim markup. * * @param f Function to convert tags. * @@ -635,7 +657,9 @@ g_free(end); } -/** Convert XML to something based on MSIM_XMLNODE_CONVERT. */ +/** + * Convert XML to something based on MSIM_XMLNODE_CONVERT. + */ static gchar * msim_convert_xml(MsimSession *session, const gchar *raw, MSIM_XMLNODE_CONVERT f) { @@ -669,7 +693,8 @@ return g_string_free(str, FALSE); } -/** Convert plaintext smileys to <i> markup tags. +/** + * Convert plaintext smileys to <i> markup tags. * * @param before Original text with ASCII smileys. Will be freed. * @return A new string with <i> tags, if applicable. Must be g_free()'d.
--- a/libpurple/protocols/myspace/myspace.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/myspace/myspace.c Sat Jun 27 17:50:49 2009 +0000 @@ -519,6 +519,18 @@ return types; } +/* + * TODO: This define is stolen from oscar.h. + * It's also in yahoo.h. + * It should be in libpurple/util.c + */ +#define msim_put32(buf, data) ( \ + (*((buf)) = (unsigned char)((data)>>24)&0xff), \ + (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \ + (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \ + (*((buf)+3) = (unsigned char)(data)&0xff), \ + 4) + /** * Compute the base64'd login challenge response based on username, password, nonce, and IPs. * @@ -619,15 +631,27 @@ purple_cipher_context_set_option(rc4, "key_len", (gpointer)0x10); purple_cipher_context_set_key(rc4, key); - /* TODO: obtain IPs of network interfaces */ - /* rc4 encrypt: * nonce1+email+IP list */ data = g_string_new(NULL); g_string_append_len(data, nonce, NONCE_SIZE); - g_string_append(data, email); + + /* Include the null terminator */ + g_string_append_len(data, email, strlen(email) + 1); + + while (data->len % 4 != 0) + g_string_append_c(data, 0xfb); + +#ifdef SEND_OUR_IP_ADDRESSES + /* TODO: Obtain IPs of network interfaces instead of using this hardcoded value */ + g_string_set_size(data, data->len + 4); + msim_put32(data->str + data->len - 4, MSIM_LOGIN_IP_LIST_LEN); g_string_append_len(data, MSIM_LOGIN_IP_LIST, MSIM_LOGIN_IP_LIST_LEN); +#else + g_string_set_size(data, data->len + 4); + msim_put32(data->str + data->len - 4, 0); +#endif /* !SEND_OUR_IP_ADDRESSES */ data_out = g_new0(guchar, data->len); @@ -1661,8 +1685,8 @@ switch (bm) { case MSIM_BM_STATUS: return msim_incoming_status(session, msg); - case MSIM_BM_INSTANT_ACTION_OR_IM: - case MSIM_BM_DELAYABLE_ACTION_OR_IM: + case MSIM_BM_ACTION_OR_IM_DELAYABLE: + case MSIM_BM_ACTION_OR_IM_INSTANT: return msim_incoming_action_or_im(session, msg); case MSIM_BM_MEDIA: return msim_incoming_media(session, msg); @@ -1824,17 +1848,19 @@ gchar *suggestion; suggestion = g_strdup_printf(_("%s Your password is " - "%d characters, greater than the " - "expected maximum length of %d for " - "MySpaceIM. Please shorten your " + "%zu characters, which is longer than the " + "maximum length of %d. Please shorten your " "password at http://profileedit.myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try again."), - full_errmsg, (int) + full_errmsg, strlen(session->account->password), MSIM_MAX_PASSWORD_LENGTH); /* Replace full_errmsg. */ g_free(full_errmsg); full_errmsg = suggestion; + } else { + g_free(full_errmsg); + full_errmsg = g_strdup(_("Incorrect username or password")); } #endif break; @@ -2285,7 +2311,7 @@ message_msim = html_to_msim_markup(session, message); - if (msim_send_bm(session, who, message_msim, MSIM_BM_DELAYABLE_ACTION_OR_IM)) { + if (msim_send_bm(session, who, message_msim, MSIM_BM_ACTION_OR_IM_DELAYABLE)) { /* Return 1 to have Purple show this IM as being sent, 0 to not. I always * return 1 even if the message could not be sent, since I don't know if * it has failed yet--because the IM is only sent after the userid is @@ -2338,7 +2364,7 @@ } purple_debug_info("msim", "msim_send_typing(%s): %d (%s)\n", name, state, typing_str); - msim_send_bm(session, name, typing_str, MSIM_BM_INSTANT_ACTION_OR_IM); + msim_send_bm(session, name, typing_str, MSIM_BM_ACTION_OR_IM_INSTANT); return 0; }
--- a/libpurple/protocols/myspace/myspace.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/myspace/myspace.h Sat Jun 27 17:50:49 2009 +0000 @@ -127,9 +127,9 @@ #define MSIM_FINAL_STRING "\\final\\" /**< Message end marker */ /* Messages */ -#define MSIM_BM_DELAYABLE_ACTION_OR_IM 1 +#define MSIM_BM_ACTION_OR_IM_DELAYABLE 1 #define MSIM_BM_STATUS 100 -#define MSIM_BM_INSTANT_ACTION_OR_IM 121 +#define MSIM_BM_ACTION_OR_IM_INSTANT 121 #define MSIM_BM_MEDIA 122 #define MSIM_BM_PROFILE 124 #define MSIM_BM_UNOFFICIAL_CLIENT 200 @@ -140,6 +140,7 @@ /* Recognized challenge length */ #define MSIM_AUTH_CHALLENGE_LENGTH 0x40 +#ifdef SEND_OUR_IP_ADDRESSES /* TODO: obtain IPs of network interfaces from user's machine, instead of * hardcoding these values below (used in msim_compute_login_response). * This is not immediately @@ -152,6 +153,7 @@ #define MSIM_LOGIN_IP_LIST "\x00\x00\x00\x00\x05\x7f\x00\x00\x01\x00\x00\x00\x00\x0a\x00\x00\x40\xc0\xa8\x58\x01\xc0\xa8\x3c\x01" #define MSIM_LOGIN_IP_LIST_LEN 25 +#endif /* SEND_OUR_IP_ADDRESSES */ /* Indexes into status string (0|1|2|3|..., but 0 always empty) */ #define MSIM_STATUS_ORDINAL_EMPTY 0
--- a/libpurple/protocols/myspace/user.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/myspace/user.c Sat Jun 27 17:50:49 2009 +0000 @@ -58,6 +58,7 @@ user = g_new0(MsimUser, 1); user->buddy = buddy; + user->id = purple_blist_node_get_int(&buddy->node, "UserID"); purple_buddy_set_protocol_data(buddy, user); } @@ -109,7 +110,6 @@ { PurplePresence *presence; gchar *str; - guint uid; guint cv; /* Useful to identify the account the tooltip refers to. @@ -118,8 +118,6 @@ purple_notify_user_info_add_pair(user_info, _("User"), user->username); } - uid = purple_blist_node_get_int((PurpleBlistNode *)user->buddy, "UserID"); - /* a/s/l...the vitals */ if (user->age) { char age[16];
--- a/libpurple/protocols/myspace/zap.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/myspace/zap.c Sat Jun 27 17:50:49 2009 +0000 @@ -109,7 +109,7 @@ /* Construct and send the actual zap command. */ zap_string = g_strdup_printf("!!!ZAP_SEND!!!=RTE_BTN_ZAPS_%d", code); - if (!msim_send_bm(session, username, zap_string, MSIM_BM_INSTANT_ACTION_OR_IM)) { + if (!msim_send_bm(session, username, zap_string, MSIM_BM_ACTION_OR_IM_INSTANT)) { purple_debug_info("msim_send_zap", "msim_send_bm failed: zapping %s with %s\n", username, zap_string);
--- a/libpurple/protocols/oscar/Makefile.am Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/oscar/Makefile.am Sat Jun 27 17:50:49 2009 +0000 @@ -7,6 +7,7 @@ OSCARSOURCES = \ bstream.c \ + clientlogin.c \ family_admin.c \ family_advert.c \ family_alert.c \
--- a/libpurple/protocols/oscar/Makefile.mingw Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/oscar/Makefile.mingw Sat Jun 27 17:50:49 2009 +0000 @@ -42,6 +42,7 @@ ## C_SRC = \ bstream.c \ + clientlogin.c \ family_admin.c \ family_advert.c \ family_alert.c \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/oscar/clientlogin.c Sat Jun 27 17:50:49 2009 +0000 @@ -0,0 +1,530 @@ +/* + * Purple's oscar protocol plugin + * This file is the legal property of its developers. + * Please see the AUTHORS file distributed alongside this file. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA +*/ + +/** + * This file implements AIM's clientLogin procedure for authenticating + * users. This replaces the older MD5-based and XOR-based + * authentication methods that use SNAC family 0x0017. + * + * This doesn't use SNACs or FLAPs at all. It makes http and https + * POSTs to AOL to validate the user based on the password they + * provided to us. Upon successful authentication we request a + * connection to the BOS server by calling startOSCARsession. The + * AOL server gives us the hostname and port number to use, as well + * as the cookie to use to authenticate to the BOS server. And then + * everything else is the same as with BUCP. + * + * For details, see: + * http://dev.aol.com/aim/oscar/#AUTH + * http://dev.aol.com/authentication_for_clients + */ + +#include "cipher.h" +#include "core.h" + +#include "oscar.h" + +#define URL_CLIENT_LOGIN "https://api.screenname.aol.com/auth/clientLogin" +#define URL_START_OSCAR_SESSION "http://api.oscar.aol.com/aim/startOSCARSession" + +/* + * Using clientLogin requires a developer ID. This dev ID is owned by + * the AIM account "markdoliner" + */ +#define CLIENT_KEY "ma15d7JTxbmVG-RP" + +/** + * This is similar to purple_url_encode() except that it follows + * RFC3986 a little more closely by not encoding - . _ and ~ + * It also uses capital letters as hex characters because capital + * letters are required by AOL. The RFC says that capital letters + * are a SHOULD and that URLs that use capital letters are + * equivalent to URLs that use small letters. + * + * TODO: Check if purple_url_encode() can be replaced with this + * version without breaking anything. + */ +static const char *oscar_auth_url_encode(const char *str) +{ + const char *iter; + static char buf[BUF_LEN]; + char utf_char[6]; + guint i, j = 0; + + g_return_val_if_fail(str != NULL, NULL); + g_return_val_if_fail(g_utf8_validate(str, -1, NULL), NULL); + + iter = str; + for (; *iter && j < (BUF_LEN - 1) ; iter = g_utf8_next_char(iter)) { + gunichar c = g_utf8_get_char(iter); + /* If the character is an ASCII character and is alphanumeric + * no need to escape */ + if ((c < 128 && isalnum(c)) || c =='-' || c == '.' || c == '_' || c == '~') { + buf[j++] = c; + } else { + int bytes = g_unichar_to_utf8(c, utf_char); + for (i = 0; i < bytes; i++) { + if (j > (BUF_LEN - 4)) + break; + sprintf(buf + j, "%%%02X", utf_char[i] & 0xff); + j += 3; + } + } + } + + buf[j] = '\0'; + + return buf; +} + +/** + * @return A null-terminated base64 encoded version of the HMAC + * calculated using the given key and data. + */ +static gchar *hmac_sha256(const char *key, const char *message) +{ + PurpleCipherContext *context; + guchar digest[32]; + + context = purple_cipher_context_new_by_name("hmac", NULL); + purple_cipher_context_set_option(context, "hash", "sha256"); + purple_cipher_context_set_key(context, (guchar *)key); + purple_cipher_context_append(context, (guchar *)message, strlen(message)); + purple_cipher_context_digest(context, sizeof(digest), digest, NULL); + purple_cipher_context_destroy(context); + + return purple_base64_encode(digest, sizeof(digest)); +} + +/** + * @return A base-64 encoded HMAC-SHA256 signature created using the + * technique documented at + * http://dev.aol.com/authentication_for_clients#signing + */ +static gchar *generate_signature(const char *method, const char *url, const char *parameters, const char *session_key) +{ + char *encoded_url, *signature_base_string, *signature; + const char *encoded_parameters; + + encoded_url = g_strdup(oscar_auth_url_encode(url)); + encoded_parameters = oscar_auth_url_encode(parameters); + signature_base_string = g_strdup_printf("%s&%s&%s", + method, encoded_url, encoded_parameters); + g_free(encoded_url); + + signature = hmac_sha256(session_key, signature_base_string); + g_free(signature_base_string); + + return signature; +} + +static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **host, unsigned short *port, char **cookie) +{ + xmlnode *response_node, *tmp_node, *data_node; + xmlnode *host_node, *port_node, *cookie_node; + char *tmp; + + /* Parse the response as XML */ + response_node = xmlnode_from_str(response, response_len); + if (response_node == NULL) + { + purple_debug_error("oscar", "startOSCARSession could not parse " + "response as XML: %s\n", response); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Received unexpected response from " URL_START_OSCAR_SESSION)); + return FALSE; + } + + /* Grab the necessary XML nodes */ + tmp_node = xmlnode_get_child(response_node, "statusCode"); + data_node = xmlnode_get_child(response_node, "data"); + if (data_node != NULL) { + host_node = xmlnode_get_child(data_node, "host"); + port_node = xmlnode_get_child(data_node, "port"); + cookie_node = xmlnode_get_child(data_node, "cookie"); + } + + /* Make sure we have a status code */ + if (tmp_node == NULL || (tmp = xmlnode_get_data_unescaped(tmp_node)) == NULL) { + purple_debug_error("oscar", "startOSCARSession response was " + "missing statusCode: %s\n", response); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Received unexpected response from " URL_START_OSCAR_SESSION)); + xmlnode_free(response_node); + return FALSE; + } + + /* Make sure the status code was 200 */ + if (strcmp(tmp, "200") != 0) + { + purple_debug_error("oscar", "startOSCARSession response statusCode " + "was %s: %s\n", tmp, response); + + if (strcmp(tmp, "401") == 0) + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_OTHER_ERROR, + _("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.")); + else + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_OTHER_ERROR, + _("Received unexpected response from " URL_START_OSCAR_SESSION)); + + g_free(tmp); + xmlnode_free(response_node); + return FALSE; + } + g_free(tmp); + + /* Make sure we have everything else */ + if (data_node == NULL || host_node == NULL || + port_node == NULL || cookie_node == NULL) + { + purple_debug_error("oscar", "startOSCARSession response was missing " + "something: %s\n", response); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Received unexpected response from " URL_START_OSCAR_SESSION)); + xmlnode_free(response_node); + return FALSE; + } + + /* Extract data from the XML */ + *host = xmlnode_get_data_unescaped(host_node); + tmp = xmlnode_get_data_unescaped(port_node); + *cookie = xmlnode_get_data_unescaped(cookie_node); + if (*host == NULL || **host == '\0' || tmp == NULL || *tmp == '\0' || cookie == NULL || *cookie == '\0') + { + purple_debug_error("oscar", "startOSCARSession response was missing " + "something: %s\n", response); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Received unexpected response from " URL_START_OSCAR_SESSION)); + g_free(*host); + g_free(tmp); + g_free(*cookie); + xmlnode_free(response_node); + return FALSE; + } + + *port = atoi(tmp); + g_free(tmp); + + return TRUE; +} + +static void start_oscar_session_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) +{ + OscarData *od; + PurpleConnection *gc; + char *host, *cookie; + unsigned short port; + guint8 *cookiedata; + gsize cookiedata_len; + + od = user_data; + gc = od->gc; + + od->url_data = NULL; + + if (error_message != NULL || len == 0) { + gchar *tmp; + tmp = g_strdup_printf(_("Error requesting " URL_START_OSCAR_SESSION + ": %s"), error_message); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); + g_free(tmp); + return; + } + + if (!parse_start_oscar_session_response(gc, url_text, len, &host, &port, &cookie)) + return; + + cookiedata = purple_base64_decode(cookie, &cookiedata_len); + oscar_connect_to_bos(gc, od, host, port, cookiedata, cookiedata_len); + g_free(cookiedata); + + g_free(host); + g_free(cookie); +} + +static void send_start_oscar_session(OscarData *od, const char *token, const char *session_key, time_t hosttime) +{ + char *query_string, *signature, *url; + + /* Construct the GET parameters */ + query_string = g_strdup_printf("a=%s" + "&f=xml" + "&k=" CLIENT_KEY + "&ts=%zu" + "&useTLS=0", + oscar_auth_url_encode(token), hosttime); + signature = generate_signature("GET", URL_START_OSCAR_SESSION, + query_string, session_key); + url = g_strdup_printf(URL_START_OSCAR_SESSION "?%s&sig_sha256=%s", + query_string, signature); + g_free(query_string); + g_free(signature); + + /* Make the request */ + od->url_data = purple_util_fetch_url(url, TRUE, NULL, FALSE, + start_oscar_session_cb, od); + g_free(url); +} + +/** + * This function parses the given response from a clientLogin request + * and extracts the useful information. + * + * @param gc The PurpleConnection. If the response data does + * not indicate then purple_connection_error_reason() + * will be called to close this connection. + * @param response The response data from the clientLogin request. + * @param response_len The length of the above response, or -1 if + * @response is NUL terminated. + * @param token If parsing was successful then this will be set to + * a newly allocated string containing the token. The + * caller should g_free this string when it is finished + * with it. On failure this value will be untouched. + * @param secret If parsing was successful then this will be set to + * a newly allocated string containing the secret. The + * caller should g_free this string when it is finished + * with it. On failure this value will be untouched. + * @param hosttime If parsing was successful then this will be set to + * the time on the OpenAuth Server in seconds since the + * Unix epoch. On failure this value will be untouched. + * + * @return TRUE if the request was successful and we were able to + * extract all info we need. Otherwise FALSE. + */ +static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **token, char **secret, time_t *hosttime) +{ + xmlnode *response_node, *tmp_node, *data_node; + xmlnode *secret_node, *hosttime_node, *token_node, *tokena_node; + char *tmp; + + /* Parse the response as XML */ + response_node = xmlnode_from_str(response, response_len); + if (response_node == NULL) + { + purple_debug_error("oscar", "clientLogin could not parse " + "response as XML: %s\n", response); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Received unexpected response from " URL_CLIENT_LOGIN)); + return FALSE; + } + + /* Grab the necessary XML nodes */ + tmp_node = xmlnode_get_child(response_node, "statusCode"); + data_node = xmlnode_get_child(response_node, "data"); + if (data_node != NULL) { + secret_node = xmlnode_get_child(data_node, "sessionSecret"); + hosttime_node = xmlnode_get_child(data_node, "hostTime"); + token_node = xmlnode_get_child(data_node, "token"); + if (token_node != NULL) + tokena_node = xmlnode_get_child(token_node, "a"); + } + + /* Make sure we have a status code */ + if (tmp_node == NULL || (tmp = xmlnode_get_data_unescaped(tmp_node)) == NULL) { + purple_debug_error("oscar", "clientLogin response was " + "missing statusCode: %s\n", response); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Received unexpected response from " URL_CLIENT_LOGIN)); + xmlnode_free(response_node); + return FALSE; + } + + /* Make sure the status code was 200 */ + if (strcmp(tmp, "200") != 0) + { + int status_code, status_detail_code = 0; + + status_code = atoi(tmp); + g_free(tmp); + tmp_node = xmlnode_get_child(response_node, "statusDetailCode"); + if (tmp_node != NULL && (tmp = xmlnode_get_data_unescaped(tmp_node)) != NULL) { + status_detail_code = atoi(tmp); + g_free(tmp); + } + + purple_debug_error("oscar", "clientLogin response statusCode " + "was %d (%d): %s\n", status_code, status_detail_code, response); + + if (status_code == 330 && status_detail_code == 3011) { + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, + _("Incorrect password.")); + } else if (status_code == 401 && status_detail_code == 3019) { + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_OTHER_ERROR, + _("AOL does not allow your screen name to authenticate via this site.")); + } else + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_OTHER_ERROR, + _("Received unexpected response from " URL_CLIENT_LOGIN)); + + xmlnode_free(response_node); + return FALSE; + } + g_free(tmp); + + /* Make sure we have everything else */ + if (data_node == NULL || secret_node == NULL || + token_node == NULL || tokena_node == NULL) + { + purple_debug_error("oscar", "clientLogin response was missing " + "something: %s\n", response); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Received unexpected response from " URL_CLIENT_LOGIN)); + xmlnode_free(response_node); + return FALSE; + } + + /* Extract data from the XML */ + *token = xmlnode_get_data_unescaped(tokena_node); + *secret = xmlnode_get_data_unescaped(secret_node); + tmp = xmlnode_get_data_unescaped(hosttime_node); + if (*token == NULL || **token == '\0' || *secret == NULL || **secret == '\0' || tmp == NULL || *tmp == '\0') + { + purple_debug_error("oscar", "clientLogin response was missing " + "something: %s\n", response); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Received unexpected response from " URL_CLIENT_LOGIN)); + g_free(*token); + g_free(*secret); + g_free(tmp); + xmlnode_free(response_node); + return FALSE; + } + + *hosttime = strtol(tmp, NULL, 10); + g_free(tmp); + + xmlnode_free(response_node); + + return TRUE; +} + +static void client_login_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) +{ + OscarData *od; + PurpleConnection *gc; + char *token, *secret, *session_key; + time_t hosttime; + int password_len; + char *password; + + od = user_data; + gc = od->gc; + + od->url_data = NULL; + + if (error_message != NULL || len == 0) { + gchar *tmp; + tmp = g_strdup_printf(_("Error requesting " URL_CLIENT_LOGIN + ": %s"), error_message); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); + g_free(tmp); + return; + } + + if (!parse_client_login_response(gc, url_text, len, &token, &secret, &hosttime)) + return; + + password_len = strlen(purple_connection_get_password(gc)); + password = g_strdup_printf("%.*s", + od->icq ? MIN(password_len, MAXICQPASSLEN) : password_len, + purple_connection_get_password(gc)); + session_key = hmac_sha256(password, secret); + g_free(password); + g_free(secret); + + send_start_oscar_session(od, token, session_key, hosttime); + + g_free(token); + g_free(session_key); +} + +/** + * This function sends a request to + * https://api.screenname.aol.com/auth/clientLogin with the user's + * username and password and receives the user's session key, which is + * used to request a connection to the BOSS server. + */ +void send_client_login(OscarData *od, const char *username) +{ + PurpleConnection *gc; + GString *request, *body; + const char *tmp; + char *password; + int password_len; + + gc = od->gc; + + /* + * We truncate ICQ passwords to 8 characters. There is probably a + * limit for AIM passwords, too, but we really only need to do + * this for ICQ because older ICQ clients let you enter a password + * as long as you wanted and then they truncated it silently. + * + * And we can truncate based on the number of bytes and not the + * number of characters because passwords for AIM and ICQ are + * supposed to be plain ASCII (I don't know if this has always been + * the case, though). + */ + tmp = purple_connection_get_password(gc); + password_len = strlen(tmp); + password = g_strndup(tmp, od->icq ? MIN(password_len, MAXICQPASSLEN) : password_len); + + /* Construct the body of the HTTP POST request */ + body = g_string_new(""); + g_string_append_printf(body, "devId=" CLIENT_KEY); + g_string_append_printf(body, "&f=xml"); + g_string_append_printf(body, "&pwd=%s", oscar_auth_url_encode(password)); + g_string_append_printf(body, "&s=%s", oscar_auth_url_encode(username)); + g_free(password); + + /* Construct an HTTP POST request */ + request = g_string_new("POST /auth/clientLogin HTTP/1.0\r\n" + "Connection: close\r\n" + "Accept: */*\r\n"); + + /* Tack on the body */ + g_string_append_printf(request, "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"); + g_string_append_printf(request, "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n", body->len); + g_string_append_len(request, body->str, body->len); + g_string_free(body, TRUE); + + /* Send the POST request */ + od->url_data = purple_util_fetch_url_request(URL_CLIENT_LOGIN, + TRUE, NULL, FALSE, request->str, FALSE, + client_login_cb, od); + g_string_free(request, TRUE); +}
--- a/libpurple/protocols/oscar/family_auth.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/oscar/family_auth.c Sat Jun 27 17:50:49 2009 +0000 @@ -26,11 +26,11 @@ * */ -#include "oscar.h" +#include <ctype.h> #include "cipher.h" -#include <ctype.h> +#include "oscar.h" /* #define USE_XOR_FOR_ICQ */ @@ -129,6 +129,8 @@ GSList *tlvlist = NULL; int passwdlen; guint8 *password_encoded; + const char *clientstring; + guint32 distrib; passwdlen = strlen(password); password_encoded = (guint8 *)g_malloc(passwdlen+1); @@ -139,18 +141,25 @@ aim_encode_password(password, password_encoded); + clientstring = purple_prefs_get_string("/plugins/prpl/oscar/clientstring"); + if (clientstring == NULL) + clientstring = ci->clientstring; + distrib = purple_prefs_get_int("/plugins/prpl/oscar/distid"); + if ((gint32)distrib == -1) + distrib = ci->distrib; + byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */ aim_tlvlist_add_str(&tlvlist, 0x0001, sn); aim_tlvlist_add_raw(&tlvlist, 0x0002, passwdlen, password_encoded); - if (ci->clientstring) - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring); + if (clientstring) + aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring); aim_tlvlist_add_16(&tlvlist, 0x0016, (guint16)ci->clientid); aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major); aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor); aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point); aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build); - aim_tlvlist_add_32(&tlvlist, 0x0014, (guint32)ci->distrib); /* distribution chan */ + aim_tlvlist_add_32(&tlvlist, 0x0014, distrib); /* distribution chan */ aim_tlvlist_add_str(&tlvlist, 0x000f, ci->lang); aim_tlvlist_add_str(&tlvlist, 0x000e, ci->country); @@ -210,6 +219,8 @@ guint8 digest[16]; aim_snacid_t snacid; size_t password_len; + const char *clientstring; + guint32 distrib; if (!ci || !sn || !password) return -EINVAL; @@ -236,20 +247,27 @@ aim_encode_password_md5(password, password_len, key, digest); + clientstring = purple_prefs_get_string("/plugins/prpl/oscar/clientstring"); + if (clientstring == NULL) + clientstring = ci->clientstring; + distrib = purple_prefs_get_int("/plugins/prpl/oscar/distid"); + if ((gint32)distrib == -1) + distrib = ci->distrib; + aim_tlvlist_add_raw(&tlvlist, 0x0025, 16, digest); #ifndef USE_OLD_MD5 aim_tlvlist_add_noval(&tlvlist, 0x004c); #endif - if (ci->clientstring) - aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring); + if (clientstring) + aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring); aim_tlvlist_add_16(&tlvlist, 0x0016, (guint16)ci->clientid); aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major); aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor); aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point); aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build); - aim_tlvlist_add_32(&tlvlist, 0x0014, (guint32)ci->distrib); + aim_tlvlist_add_32(&tlvlist, 0x0014, distrib); aim_tlvlist_add_str(&tlvlist, 0x000f, ci->lang); aim_tlvlist_add_str(&tlvlist, 0x000e, ci->country);
--- a/libpurple/protocols/oscar/flap_connection.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Sat Jun 27 17:50:49 2009 +0000 @@ -46,7 +46,7 @@ FlapFrame *frame; frame = flap_frame_new(od, 0x01, 4); - byte_stream_put32(&frame->data, 0x00000001); + byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */ flap_connection_send(conn, frame); } @@ -64,7 +64,7 @@ GSList *tlvlist = NULL; frame = flap_frame_new(od, 0x01, 4 + 2 + 2 + length); - byte_stream_put32(&frame->data, 0x00000001); + byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */ aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy); aim_tlvlist_write(&frame->data, &tlvlist); aim_tlvlist_free(tlvlist); @@ -72,6 +72,32 @@ flap_connection_send(conn, frame); } +void +flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci) +{ + FlapFrame *frame; + GSList *tlvlist = NULL; + + frame = flap_frame_new(od, 0x01, 1152 + length); + + byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */ + aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy); + + if (ci->clientstring) + aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring); + aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major); + aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor); + aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point); + aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build); + aim_tlvlist_add_8(&tlvlist, 0x004a, 0x01); + + aim_tlvlist_write(&frame->data, &tlvlist); + + aim_tlvlist_free(tlvlist); + + flap_connection_send(conn, frame); +} + static struct rateclass * flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype) { @@ -355,23 +381,9 @@ } } - if (conn->fd >= 0) - { - if (conn->type == SNAC_FAMILY_LOCATE) - flap_connection_send_close(od, conn); - - close(conn->fd); - conn->fd = -1; - } - - if (conn->gsc != NULL) - { - if (conn->type == SNAC_FAMILY_LOCATE) - flap_connection_send_close(od, conn); - - purple_ssl_close(conn->gsc); - conn->gsc = NULL; - } + if ((conn->fd >= 0 || conn->gsc != NULL) + && conn->type == SNAC_FAMILY_LOCATE) + flap_connection_send_close(od, conn); if (conn->watcher_incoming != 0) { @@ -385,6 +397,18 @@ conn->watcher_outgoing = 0; } + if (conn->fd >= 0) + { + close(conn->fd); + conn->fd = -1; + } + + if (conn->gsc != NULL) + { + purple_ssl_close(conn->gsc); + conn->gsc = NULL; + } + g_free(conn->buffer_incoming.data.data); conn->buffer_incoming.data.data = NULL;
--- a/libpurple/protocols/oscar/oscar.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Sat Jun 27 17:50:49 2009 +0000 @@ -145,9 +145,12 @@ static const int msgerrreasonlen = G_N_ELEMENTS(msgerrreason); /* All the libfaim->purple callback functions */ + +/* Only used when connecting with the old-style BUCP login */ static int purple_parse_auth_resp (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_parse_login (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_parse_auth_securid_request(OscarData *, FlapConnection *, FlapFrame *, ...); + static int purple_handle_redirect (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_info_change (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_account_confirm (OscarData *, FlapConnection *, FlapFrame *, ...); @@ -204,7 +207,6 @@ void oscar_set_info(PurpleConnection *gc, const char *info); static void oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, PurpleStatus *status); static void oscar_set_extendedstatus(PurpleConnection *gc); -static void oscar_format_username(PurpleConnection *gc, const char *nick); static gboolean purple_ssi_rerequestdata(gpointer data); static void oscar_free_name_data(struct name_data *data) { @@ -1107,6 +1109,7 @@ if (conn->type == SNAC_FAMILY_AUTH) { + /* This only happens when connecting with the old-style BUCP login */ gchar *msg; msg = g_strdup_printf(_("Could not connect to authentication server:\n%s"), error_message); @@ -1152,14 +1155,26 @@ flap_connection_send_version(od, conn); else { - flap_connection_send_version_with_cookie(od, conn, - conn->cookielen, conn->cookie); + if (purple_account_get_bool(account, "use_clientlogin", OSCAR_DEFAULT_USE_CLIENTLOGIN)) + { + ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM; + ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ; + flap_connection_send_version_with_cookie_and_clientinfo(od, + conn, conn->cookielen, conn->cookie, + od->icq ? &icqinfo : &aiminfo); + } else { + flap_connection_send_version_with_cookie(od, conn, + conn->cookielen, conn->cookie); + } + + g_free(conn->cookie); conn->cookie = NULL; } if (conn->type == SNAC_FAMILY_AUTH) { + /* This only happens when connecting with the old-style BUCP login */ aim_request_login(od, conn, purple_account_get_username(account)); purple_debug_info("oscar", "Username sent, waiting for response\n"); purple_connection_update_progress(gc, _("Username sent"), 1, OSCAR_CONNECT_STEPS); @@ -1430,7 +1445,6 @@ { PurpleConnection *gc; OscarData *od; - FlapConnection *newconn; gc = purple_account_get_connection(account); od = oscar_data_new(); @@ -1445,9 +1459,12 @@ oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0007, purple_account_confirm, 0); oscar_data_addhandler(od, SNAC_FAMILY_ALERT, 0x0001, purple_parse_genericerr, 0); oscar_data_addhandler(od, SNAC_FAMILY_ALERT, SNAC_SUBTYPE_ALERT_MAILSTATUS, purple_email_parseupdate, 0); + + /* These are only needed when connecting with the old-style BUCP login */ oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0003, purple_parse_auth_resp, 0); oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0007, purple_parse_login, 0); oscar_data_addhandler(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_REQUEST, purple_parse_auth_securid_request, 0); + oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_RESPONSE, purple_icon_parseicon, 0); oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0001, purple_parse_genericerr, 0); oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0003, purple_bosrights, 0); @@ -1523,10 +1540,34 @@ purple_prefs_connect_callback(gc, "/purple/away/idle_reporting", idle_reporting_pref_cb, gc); purple_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc); - newconn = flap_connection_new(od, SNAC_FAMILY_AUTH); - if (od->use_ssl) { - if (purple_ssl_is_supported()) { - const char *server = purple_account_get_string(account, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER); + /* + * On 2008-03-05 AOL released some documentation on the OSCAR protocol + * which includes a new login method called clientLogin. It is similar + * (though not the same?) as what the AIM 6.0 series uses to + * authenticate. + * + * AIM 5.9 and lower use an MD5-based login procedure called "BUCP". + * Note that some people were unable to log in to ICQ using the MD5 + * method, and so ICQ, when not using clientLogin, is still using a + * very insecure XOR-based login scheme. + */ + if (purple_account_get_bool(account, "use_clientlogin", OSCAR_DEFAULT_USE_CLIENTLOGIN)) { + send_client_login(od, purple_account_get_username(account)); + } else { + FlapConnection *newconn; + const char *server; + + newconn = flap_connection_new(od, SNAC_FAMILY_AUTH); + + if (od->use_ssl) { + if (!purple_ssl_is_supported()) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, + _("SSL support unavailable")); + return; + } + + server = purple_account_get_string(account, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER); + /* * If the account's server is what the oscar prpl has offered as * the default login server through the vast eons (all two of @@ -1544,32 +1585,29 @@ purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), ssl_connection_established_cb, ssl_connection_error_cb, newconn); } else { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - _("SSL support unavailable")); + server = purple_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER); + + /* + * See the comment above. We do the reverse here. If they don't want + * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER, + * set it back to the default. + */ + if (!strcmp(server, OSCAR_DEFAULT_SSL_LOGIN_SERVER)) { + purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n"); + purple_account_set_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER); + server = OSCAR_DEFAULT_LOGIN_SERVER; + } + + newconn->connect_data = purple_proxy_connect(NULL, account, server, + purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), + connection_established_cb, newconn); } - } else { - const char *server = purple_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER); - - /* - * See the comment above. We do the reverse here. If they don't want - * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER, - * set it back to the default. - */ - if (!strcmp(server, OSCAR_DEFAULT_SSL_LOGIN_SERVER)) { - purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n"); - purple_account_set_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER); - server = OSCAR_DEFAULT_LOGIN_SERVER; + + if (newconn->gsc == NULL && newconn->connect_data == NULL) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Couldn't connect to host")); + return; } - - newconn->connect_data = purple_proxy_connect(NULL, account, server, - purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), - connection_established_cb, newconn); - } - - if (newconn->gsc == NULL && newconn->connect_data == NULL) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Couldn't connect to host")); - return; } purple_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS); @@ -1604,165 +1642,6 @@ purple_debug_info("oscar", "Signed off.\n"); } -static int -purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - char *host; int port; - int i; - FlapConnection *newconn; - va_list ap; - struct aim_authresp_info *info; - - port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT); - - va_start(ap, fr); - info = va_arg(ap, struct aim_authresp_info *); - va_end(ap); - - purple_debug_info("oscar", - "inside auth_resp (Username: %s)\n", info->bn); - - if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) { - char buf[256]; - switch (info->errorcode) { - case 0x01: - /* Unregistered username */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Invalid username.")); - break; - case 0x05: - /* Incorrect password */ - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password.")); - break; - case 0x11: - /* Suspended account */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended.")); - break; - case 0x02: - case 0x14: - /* service temporarily unavailable */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable.")); - break; - case 0x18: - /* username connecting too frequently */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("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.")); - break; - case 0x1c: - { - /* client too old */ - GHashTable *ui_info = purple_core_get_ui_info(); - g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), - ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf); - break; - } - case 0x1d: - /* IP address connecting too frequently */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("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.")); - break; - default: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Authentication failed")); - break; - } - purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode); - purple_debug_info("oscar", "Error URL: %s\n", info->errorurl ? info->errorurl : ""); - return 1; - } - - purple_debug_misc("oscar", "Reg status: %hu\n" - "Email: %s\n" - "BOSIP: %s\n", - info->regstatus, - info->email ? info->email : "null", - info->bosip ? info->bosip : "null"); - purple_debug_info("oscar", "Closing auth connection...\n"); - flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL); - - for (i = 0; i < strlen(info->bosip); i++) { - if (info->bosip[i] == ':') { - port = atoi(&(info->bosip[i+1])); - break; - } - } - host = g_strndup(info->bosip, i); - newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE); - newconn->cookielen = info->cookielen; - newconn->cookie = g_memdup(info->cookie, info->cookielen); - - if (od->use_ssl) - { - /* - * This shouldn't be hardcoded except that the server isn't sending - * us a name to use for comparing the certificate common name. - */ - newconn->ssl_cert_cn = g_strdup("bos.oscar.aol.com"); - newconn->connect_data = purple_proxy_connect(NULL, account, host, port, - ssl_proxy_conn_established_cb, newconn); - } - else - { - newconn->connect_data = purple_proxy_connect(NULL, account, host, port, - connection_established_cb, newconn); - } - - g_free(host); - if (newconn->connect_data == NULL) - { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Could Not Connect")); - return 0; - } - - purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS); - ck[3] = 0x64; - - return 1; -} - -static void -purple_parse_auth_securid_request_yes_cb(gpointer user_data, const char *msg) -{ - PurpleConnection *gc = user_data; - OscarData *od = purple_connection_get_protocol_data(gc); - - aim_auth_securid_send(od, msg); -} - -static void -purple_parse_auth_securid_request_no_cb(gpointer user_data, const char *value) -{ - PurpleConnection *gc = user_data; - - /* Disconnect */ - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("The SecurID key entered is invalid.")); -} - -static int -purple_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) -{ - PurpleConnection *gc = od->gc; - PurpleAccount *account = purple_connection_get_account(gc); - gchar *primary; - - purple_debug_info("oscar", "Got SecurID request\n"); - - primary = g_strdup_printf("Enter the SecurID key for %s.", purple_account_get_username(account)); - purple_request_input(gc, NULL, _("Enter SecurID"), primary, - _("Enter the 6 digit number from the digital display."), - FALSE, FALSE, NULL, - _("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb), - _("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb), - account, NULL, NULL, - gc); - g_free(primary); - - return 1; -} - /* XXX - Should use purple_util_fetch_url for the below stuff */ struct pieceofcrap { PurpleConnection *gc; @@ -1833,13 +1712,6 @@ gchar *buf; gssize result; - if (!PURPLE_CONNECTION_IS_VALID(pos->gc)) - { - g_free(pos->modname); - g_free(pos); - return; - } - pos->fd = source; if (source < 0) { @@ -1879,7 +1751,8 @@ /* size of icbmui.ocm, the largest module in AIM 3.5 */ #define AIM_MAX_FILE_SIZE 98304 -int purple_memrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { +static int purple_memrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) +{ va_list ap; struct pieceofcrap *pos; guint32 offset, len; @@ -1937,8 +1810,7 @@ pos->len = len; pos->modname = g_strdup(modname); - /* TODO: Keep track of this return value. */ - if (purple_proxy_connect(NULL, pos->gc->account, "pidgin.im", 80, + if (purple_proxy_connect(pos->gc, pos->gc->account, "pidgin.im", 80, straight_to_hell, pos) == NULL) { char buf[256]; @@ -1957,6 +1829,204 @@ return 1; } +int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen) +{ + FlapConnection *conn; + + conn = flap_connection_new(od, SNAC_FAMILY_LOCATE); + conn->cookielen = cookielen; + conn->cookie = g_memdup(cookie, cookielen); + conn->connect_data = purple_proxy_connect(NULL, + purple_connection_get_account(gc), host, port, + connection_established_cb, conn); + if (conn->connect_data == NULL) + { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Could Not Connect")); + return 0; + } + + od->default_port = port; + + purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS); + ck[3] = 0x64; + + return 1; +} + +/** + * Only used when connecting with the old-style BUCP login. + */ +static int +purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) +{ + PurpleConnection *gc = od->gc; + PurpleAccount *account = purple_connection_get_account(gc); + char *host; int port; + int i; + FlapConnection *newconn; + va_list ap; + struct aim_authresp_info *info; + + port = purple_account_get_int(account, "port", od->default_port); + + va_start(ap, fr); + info = va_arg(ap, struct aim_authresp_info *); + va_end(ap); + + purple_debug_info("oscar", + "inside auth_resp (Username: %s)\n", info->bn); + + if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) { + char buf[256]; + switch (info->errorcode) { + case 0x01: + /* Unregistered username */ + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Invalid username.")); + break; + case 0x05: + /* Incorrect password */ + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password.")); + break; + case 0x11: + /* Suspended account */ + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended.")); + break; + case 0x02: + case 0x14: + /* service temporarily unavailable */ + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable.")); + break; + case 0x18: + /* username connecting too frequently */ + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("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.")); + break; + case 0x1c: + { + /* client too old */ + GHashTable *ui_info = purple_core_get_ui_info(); + g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), + ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf); + break; + } + case 0x1d: + /* IP address connecting too frequently */ + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("You have been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer.")); + break; + default: + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Authentication failed")); + break; + } + purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode); + purple_debug_info("oscar", "Error URL: %s\n", info->errorurl ? info->errorurl : ""); + return 1; + } + + purple_debug_misc("oscar", "Reg status: %hu\n" + "Email: %s\n" + "BOSIP: %s\n", + info->regstatus, + info->email ? info->email : "null", + info->bosip ? info->bosip : "null"); + purple_debug_info("oscar", "Closing auth connection...\n"); + flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL); + + for (i = 0; i < strlen(info->bosip); i++) { + if (info->bosip[i] == ':') { + port = atoi(&(info->bosip[i+1])); + break; + } + } + host = g_strndup(info->bosip, i); + newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE); + newconn->cookielen = info->cookielen; + newconn->cookie = g_memdup(info->cookie, info->cookielen); + + if (od->use_ssl) + { + /* + * This shouldn't be hardcoded except that the server isn't sending + * us a name to use for comparing the certificate common name. + */ + newconn->ssl_cert_cn = g_strdup("bos.oscar.aol.com"); + newconn->connect_data = purple_proxy_connect(NULL, account, host, port, + ssl_proxy_conn_established_cb, newconn); + } + else + { + newconn->connect_data = purple_proxy_connect(NULL, account, host, port, + connection_established_cb, newconn); + } + + g_free(host); + if (newconn->connect_data == NULL) + { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Could Not Connect")); + return 0; + } + + purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS); + ck[3] = 0x64; + + return 1; +} + +/** + * Only used when connecting with the old-style BUCP login. + */ +static void +purple_parse_auth_securid_request_yes_cb(gpointer user_data, const char *msg) +{ + PurpleConnection *gc = user_data; + OscarData *od = purple_connection_get_protocol_data(gc); + + aim_auth_securid_send(od, msg); +} + +/** + * Only used when connecting with the old-style BUCP login. + */ +static void +purple_parse_auth_securid_request_no_cb(gpointer user_data, const char *value) +{ + PurpleConnection *gc = user_data; + + /* Disconnect */ + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, + _("The SecurID key entered is invalid.")); +} + +/** + * Only used when connecting with the old-style BUCP login. + */ +static int +purple_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) +{ + PurpleConnection *gc = od->gc; + PurpleAccount *account = purple_connection_get_account(gc); + gchar *primary; + + purple_debug_info("oscar", "Got SecurID request\n"); + + primary = g_strdup_printf("Enter the SecurID key for %s.", purple_account_get_username(account)); + purple_request_input(gc, NULL, _("Enter SecurID"), primary, + _("Enter the 6 digit number from the digital display."), + FALSE, FALSE, NULL, + _("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb), + _("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb), + account, NULL, NULL, + gc); + g_free(primary); + + return 1; +} + +/** + * Only used when connecting with the old-style BUCP login. + */ static int purple_parse_login(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { @@ -2002,7 +2072,7 @@ redir = va_arg(ap, struct aim_redirect_data *); va_end(ap); - port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT); + port = od->default_port; separator = strchr(redir->ip, ':'); if (separator != NULL) { @@ -3920,20 +3990,9 @@ purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE)); } + aim_srv_requestnew(od, SNAC_FAMILY_ALERT); aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV); - /* - * The "if" statement here is a pathetic attempt to not attempt to - * connect to the alerts servce (aka email notification) if this - * username does not support it. I think mail notification - * works for @mac.com accounts but does not work for the newer - * @anythingelse.com accounts. If that's true then this change - * breaks mail notification for @mac.com accounts, but it gets rid - * of an annoying error at signon for @anythingelse.com accounts. - */ - if (od->authinfo->email != NULL && strchr(username, '@') == NULL) - aim_srv_requestnew(od, SNAC_FAMILY_ALERT); - return 1; } @@ -4436,7 +4495,8 @@ } g_string_free(data, TRUE); - peer_odc_send_im(conn, msg->str, msg->len, charset, (imflags & PURPLE_MESSAGE_AUTO_RESP)); + peer_odc_send_im(conn, msg->str, msg->len, charset, + imflags & PURPLE_MESSAGE_AUTO_RESP); g_string_free(msg, TRUE); } @@ -6384,6 +6444,10 @@ if (od->ssi.received_data && purple_buddy_get_group(buddy) != NULL) { + /* + * We only do this if the user is in our buddy list and we're + * waiting for authorization. + */ char *gname; gname = aim_ssi_itemlist_findparentname(od->ssi.local, bname); if (gname && aim_ssi_waitingforauth(od->ssi.local, gname, bname)) @@ -6463,7 +6527,7 @@ gc); } -static void oscar_format_username(PurpleConnection *gc, const char *nick) { +void oscar_format_username(PurpleConnection *gc, const char *nick) { OscarData *od = purple_connection_get_protocol_data(gc); if (!oscar_util_name_compare(purple_account_get_username(purple_connection_get_account(gc)), nick)) { if (!flap_connection_getbytype(od, SNAC_FAMILY_ADMIN)) { @@ -6624,6 +6688,9 @@ purple_account_request_change_password(purple_connection_get_account(gc)); } +/** + * Only used when connecting with the old-style BUCP login. + */ static void oscar_show_chpassurl(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection *) action->context; @@ -6764,12 +6831,16 @@ oscar_change_pass); menu = g_list_prepend(menu, act); - if (od->authinfo->chpassurl != NULL) + if (od->authinfo != NULL && od->authinfo->chpassurl != NULL) { + /* This only happens when connecting with the old-style BUCP login */ act = purple_plugin_action_new(_("Change Password (web)"), oscar_show_chpassurl); menu = g_list_prepend(menu, act); - + } + + if (!od->icq) + { act = purple_plugin_action_new(_("Configure IM Forwarding (web)"), oscar_show_imforwardingurl); menu = g_list_prepend(menu, act); @@ -7006,6 +7077,10 @@ OSCAR_DEFAULT_USE_SSL); prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + option = purple_account_option_bool_new(_("Use clientLogin"), "use_clientlogin", + OSCAR_DEFAULT_USE_CLIENTLOGIN); + prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + option = purple_account_option_bool_new( _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy", OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY); @@ -7022,6 +7097,16 @@ /* Preferences */ purple_prefs_add_none("/plugins/prpl/oscar"); purple_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE); + + /* + * These two preferences will normally not be changed. UIs can optionally + * use them to override these two version fields which are sent to the + * server when logging in. AOL requested this change to allow clients to + * use custom values. + */ + purple_prefs_add_string("/plugins/prpl/oscar/clientstring", NULL); + purple_prefs_add_int("/plugins/prpl/oscar/distid", -1); + purple_prefs_remove("/plugins/prpl/oscar/show_idle"); purple_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy");
--- a/libpurple/protocols/oscar/oscar.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.h Sat Jun 27 17:50:49 2009 +0000 @@ -469,6 +469,9 @@ */ struct _OscarData { + /** Only used when connecting with clientLogin */ + PurpleUtilFetchUrlData *url_data; + gboolean iconconnecting; gboolean set_icon; @@ -522,6 +525,8 @@ IcbmCookie *msgcookies; struct aim_icq_info *icq_info; + + /** Only used when connecting with the old-style BUCP login. */ struct aim_authresp_info *authinfo; struct aim_emailinfo *emailinfo; @@ -547,6 +552,7 @@ /** A linked list containing FlapConnections. */ GSList *oscar_connections; + guint16 default_port; /** A linked list containing PeerConnections. */ GSList *peer_connections; @@ -568,10 +574,9 @@ #define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000 #define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000 -typedef int (*aim_rxcallback_t)(OscarData *od, FlapConnection *conn, FlapFrame *frame, ...); - - -/* family_auth.c */ +/** + * Only used when connecting with the old-style BUCP login. + */ struct aim_clientrelease { char *name; @@ -580,6 +585,9 @@ char *info; }; +/** + * Only used when connecting with the old-style BUCP login. + */ struct aim_authresp_info { char *bn; @@ -611,12 +619,29 @@ } chat; }; +int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen); + +/* family_auth.c */ + +/** + * Only used when connecting with the old-style BUCP login. + */ int aim_request_login(OscarData *od, FlapConnection *conn, const char *bn); + +/** + * Only used when connecting with the old-style BUCP login. + */ int aim_send_login(OscarData *od, FlapConnection *conn, const char *bn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins); + +/** + * Only used when connecting with the old-style BUCP login. + */ /* 0x000b */ int aim_auth_securid_send(OscarData *od, const char *securid); -void oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags); -aim_rxcallback_t aim_callhandler(OscarData *od, guint16 family, guint16 subtype); +/** + * Only used when connecting with clientLogin. + */ +void send_client_login(OscarData *od, const char *username); /* flap_connection.c */ FlapConnection *flap_connection_new(OscarData *, int type); @@ -632,13 +657,19 @@ void flap_connection_send(FlapConnection *conn, FlapFrame *frame); void flap_connection_send_version(OscarData *od, FlapConnection *conn); void flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy); +void flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci); void flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data); void flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data, gboolean high_priority); void flap_connection_send_keepalive(OscarData *od, FlapConnection *conn); FlapFrame *flap_frame_new(OscarData *od, guint16 channel, int datalen); +/* oscar_data.c */ +typedef int (*aim_rxcallback_t)(OscarData *od, FlapConnection *conn, FlapFrame *frame, ...); + OscarData *oscar_data_new(void); void oscar_data_destroy(OscarData *); +void oscar_data_addhandler(OscarData *od, guint16 family, guint16 subtype, aim_rxcallback_t newhandler, guint16 flags); +aim_rxcallback_t aim_callhandler(OscarData *od, guint16 family, guint16 subtype); /* misc.c */ #define AIM_VISIBILITYCHANGE_PERMITADD 0x05
--- a/libpurple/protocols/oscar/oscar_data.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/oscar/oscar_data.c Sat Jun 27 17:50:49 2009 +0000 @@ -70,6 +70,7 @@ /* missing 0x14 */ aim__registermodule(od, icq_modfirst); /* missing 0x16 */ + /* auth_modfirst is only needed if we're connecting with the old-style BUCP login */ aim__registermodule(od, auth_modfirst); aim__registermodule(od, email_modfirst); @@ -86,6 +87,10 @@ { aim_cleansnacs(od, -1); + /* Only used when connecting with clientLogin */ + if (od->url_data != NULL) + purple_util_fetch_url_cancel(od->url_data); + while (od->requesticon) { g_free(od->requesticon->data);
--- a/libpurple/protocols/oscar/oscarcommon.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/oscar/oscarcommon.h Sat Jun 27 17:50:49 2009 +0000 @@ -45,6 +45,7 @@ #define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE #define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE #define OSCAR_DEFAULT_USE_SSL FALSE +#define OSCAR_DEFAULT_USE_CLIENTLOGIN FALSE #ifdef _WIN32 const char *oscar_get_locale_charset(void); @@ -91,5 +92,6 @@ void oscar_send_file(PurpleConnection *gc, const char *who, const char *file); PurpleXfer *oscar_new_xfer(PurpleConnection *gc, const char *who); gboolean oscar_offline_message(const PurpleBuddy *buddy); +void oscar_format_username(PurpleConnection *gc, const char *nick); GList *oscar_actions(PurplePlugin *plugin, gpointer context); void oscar_init(PurplePluginProtocolInfo *prpl_info);
--- a/libpurple/protocols/sametime/sametime.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/sametime/sametime.c Sat Jun 27 17:50:49 2009 +0000 @@ -1466,7 +1466,7 @@ if(purple_account_get_bool(account, MW_KEY_FORCE, FALSE) || !host || (! strcmp(current_host, host)) || - (purple_proxy_connect(NULL, account, host, port, connect_cb, pd) == NULL)) { + (purple_proxy_connect(gc, account, host, port, connect_cb, pd) == NULL)) { /* if we're configured to force logins, or if we're being redirected to the already configured host, or if we couldn't
--- a/libpurple/protocols/simple/simple.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/simple/simple.c Sat Jun 27 17:50:49 2009 +0000 @@ -446,13 +446,6 @@ struct simple_account_data *sip; struct sip_connection *conn; - if (!PURPLE_CONNECTION_IS_VALID(gc)) - { - if (source >= 0) - close(source); - return; - } - if(source < 0) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, @@ -1735,13 +1728,6 @@ struct simple_account_data *sip; struct sip_connection *conn; - if (!PURPLE_CONNECTION_IS_VALID(gc)) - { - if (source >= 0) - close(source); - return; - } - if(source < 0) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
--- a/libpurple/protocols/yahoo/util.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/util.c Sat Jun 27 17:50:49 2009 +0000 @@ -22,7 +22,7 @@ #ifdef HAVE_CONFIG_H #include "config.h" -#endif +#endif /* HAVE_CONFIG_H */ #include "debug.h" #include "internal.h" @@ -42,7 +42,7 @@ /* * Returns cookies formatted as a null terminated string for the given connection. * Must g_free return value. - * + * * TODO:will work, but must test for strict correctness */ gchar* yahoo_get_cookies(PurpleConnection *gc) @@ -191,7 +191,7 @@ /* * I found these on some website but i don't know that they actually * work (or are supposed to work). I didn't implement them yet. - * + * * [0;30m ---black * [1;37m ---white * [0;37m ---tan
--- a/libpurple/protocols/yahoo/yahoo.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Sat Jun 27 17:50:49 2009 +0000 @@ -64,7 +64,7 @@ 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); -#endif +#endif /* TRY_WEBMESSENGER_LOGIN */ static void yahoo_set_status(PurpleAccount *account, PurpleStatus *status); static void yahoo_update_status(PurpleConnection *gc, const char *name, YahooFriend *f) @@ -74,12 +74,10 @@ if (!gc || !name || !f || !purple_find_buddy(purple_connection_get_account(gc), name)) return; - if (f->status == YAHOO_STATUS_OFFLINE) - { - return; - } - switch (f->status) { + case YAHOO_STATUS_OFFLINE: + status = YAHOO_STATUS_TYPE_OFFLINE; + break; case YAHOO_STATUS_AVAILABLE: status = YAHOO_STATUS_TYPE_AVAILABLE; break; @@ -182,8 +180,28 @@ name = message = NULL; f = NULL; if (pair->value && g_utf8_validate(pair->value, -1, NULL)) { + GSList *tmplist; + int protocol = 0; + name = pair->value; + + /* Look ahead to see if we have the protocol info about the buddy */ + for (tmplist = l->next; tmplist; tmplist = tmplist->next) { + struct yahoo_pair *p = tmplist->data; + if (p->key == 7) + break; + if (p->key == 241) { + if(strtol(p->value, NULL, 10) == 2) { + g_free(msn_name); + msn_name = g_strconcat("msn/", name, NULL); + name = msn_name; + protocol = 2; + } + break; + } + } f = yahoo_friend_find_or_new(gc, name); + f->protocol = protocol; } break; case 10: /* state */ @@ -331,11 +349,7 @@ f->version_id = strtol(pair->value, NULL, 10); break; case 241: /* protocol buddy belongs to */ - if(strtol(pair->value, NULL, 10) == 2) { - msn_name = g_strconcat("msn/", name, NULL); - name = msn_name; - } - break; + break; /* We process this when get '7' */ default: purple_debug_warning("yahoo", "Unknown status key %d\n", pair->key); @@ -345,11 +359,16 @@ l = l->next; } - if (message && f) - yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode)); - - if (name && f) /* update the last buddy */ - yahoo_update_status(gc, name, f); + if (f) { + if (pkt->service == YAHOO_SERVICE_LOGOFF) + f->status = YAHOO_STATUS_OFFLINE; + if (message) + yahoo_friend_set_status_message(f, yahoo_string_decode(gc, message, unicode)); + + if (name) /* update the last buddy */ + yahoo_update_status(gc, name, f); + } + g_free(msn_name); } static void yahoo_do_group_check(PurpleAccount *account, GHashTable *ht, const char *name, const char *group) @@ -500,30 +519,30 @@ if (!(g = purple_find_group(yd->current_list15_grp))) { g = purple_group_new(yd->current_list15_grp); purple_blist_add_group(g, NULL); + } + b = purple_buddy_new(account, norm_bud, NULL); + purple_blist_add_buddy(b, NULL, g, NULL); } - b = purple_buddy_new(account, norm_bud, NULL); - purple_blist_add_buddy(b, NULL, g, NULL); - } - yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp); - if(protocol != 0) { - f->protocol = protocol; - purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol); - } - if(stealth == 2) - f->presence = YAHOO_PRESENCE_PERM_OFFLINE; - - /* set p2p status not connected and no p2p packet sent */ - if(protocol == 0) { - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); - f->p2p_packet_sent = 0; - } else - yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT); + yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp); + if(protocol != 0) { + f->protocol = protocol; + purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol); + } + if(stealth == 2) + f->presence = YAHOO_PRESENCE_PERM_OFFLINE; + + /* set p2p status not connected and no p2p packet sent */ + if(protocol == 0) { + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + f->p2p_packet_sent = 0; + } else + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT); } else { /* This buddy is on the ignore list (and therefore in no group) */ purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n",account->username, norm_bud); purple_privacy_deny_add(account, norm_bud, 1); } - + protocol = 0; stealth = 0; norm_bud = NULL; @@ -554,7 +573,7 @@ } g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL); - + /* Now that we have processed the buddy list, we can say yahoo has connected */ purple_connection_set_display_name(gc, purple_normalize(account, purple_account_get_username(account))); purple_connection_set_state(gc, PURPLE_CONNECTED); @@ -726,7 +745,6 @@ gint val_11 = 0; struct yahoo_data *yd = gc->proto_data; gboolean msn = FALSE; - char *msn_from = NULL; account = purple_connection_get_account(gc); @@ -759,17 +777,16 @@ return; } - if(msn) - msn_from = g_strconcat("msn/", from, NULL); - if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING")) && (purple_privacy_check(account, from))) { if(msn) { + char *msn_from = g_strconcat("msn/", from, NULL); if (*stat == '1') serv_got_typing(gc, msn_from, 0, PURPLE_TYPING); else serv_got_typing_stopped(gc, msn_from); + g_free(msn_from); } else { if (*stat == '1') @@ -803,8 +820,6 @@ purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL)); g_free(buf); } - - g_free(msn_from); } @@ -813,6 +828,7 @@ int time; int utf8; int buddy_icon; + char *id; char *msg; }; @@ -824,8 +840,8 @@ struct yahoo_data *yd; char *server_msg = NULL; char *m; - - yd = gc->proto_data; + + yd = gc->proto_data; account = purple_connection_get_account(gc); while (l != NULL) { @@ -858,7 +874,7 @@ } else purple_notify_error(gc, NULL, _("Your SMS was not delivered"), NULL); - + g_free(sms->from); g_free(sms); return ; @@ -871,7 +887,7 @@ m = yahoo_string_decode(gc, sms->msg, sms->utf8); serv_got_im(gc, sms->from, m, 0, sms->time); - + g_free(m); g_free(sms->from); g_free(sms); @@ -930,6 +946,9 @@ { imv = pair->value; } + if (pair->key == 429) + if (im) + im->id = pair->value; l = l->next; } } else if (pkt->status == 2) { @@ -997,6 +1016,28 @@ return; } + /* + * TODO: Is there anything else we should check when determining whether + * we should send an acknowledgement? + */ + if (im->id != NULL) { + /* Send acknowledgement. If we don't do this then the official + * Yahoo Messenger client for Windows will send us the same + * message 7 seconds later as an offline message. This is true + * for at least version 9.0.0.2162 on Windows XP. */ + struct yahoo_packet *pkt2; + pkt2 = yahoo_packet_new(YAHOO_SERVICE_MESSAGE_ACK, + YAHOO_STATUS_AVAILABLE, pkt->id); + yahoo_packet_hash(pkt2, "ssisii", + 1, purple_connection_get_display_name(gc), + 5, im->from, + 302, 430, + 430, im->id, + 303, 430, + 450, 0); + yahoo_packet_send_and_free(pkt2, yd); + } + m = yahoo_string_decode(gc, im->msg, im->utf8); /* This may actually not be necessary, but it appears * that at least at one point some clients were sending @@ -1024,7 +1065,7 @@ username = g_markup_escape_text(msn_from, -1); else username = g_markup_escape_text(im->from, -1); - + purple_prpl_got_attention(gc, username, YAHOO_BUZZ); g_free(username); g_free(m); @@ -1043,7 +1084,7 @@ g_free(m2); - /* laters : implement buddy icon for msn friends */ + /* laters : implement buddy icon for msn friends */ if(!msn) { if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) { if (yahoo_friend_get_buddy_icon_need_request(f)) { @@ -1097,11 +1138,15 @@ struct yahoo_add_request *add_req = data; struct yahoo_packet *pkt; struct yahoo_data *yd = add_req->gc->proto_data; + const char *who = add_req->who; + + if (add_req->protocol == 2) + who += 4; pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH_REQ_15, YAHOO_STATUS_AVAILABLE, 0); yahoo_packet_hash(pkt, "ssiii", 1, add_req->id, - 5, add_req->who, + 5, who, 241, add_req->protocol, 13, 1, 334, 0); @@ -1625,16 +1670,21 @@ purple_debug_error("yahoo", "Login Failed, unable to retrieve stage 2 url: %s\n", error_message); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); g_free(auth_data->seed); - g_free(auth_data); + g_free(auth_data); return; } else if (len > 0 && ret_data && *ret_data) { gchar **split_data = g_strsplit(ret_data, "\r\n", -1); - int totalelements = g_strv_length(split_data); + int totalelements = 0; int response_no = -1; char *crumb = NULL; char *crypt = NULL; +#if GLIB_CHECK_VERSION(2,6,0) + totalelements = g_strv_length(split_data); +#else + while (split_data[++totalelements] != NULL); +#endif if (totalelements >= 5) { response_no = strtol(split_data[1], NULL, 10); crumb = g_strdup(split_data[2] + strlen("crumb=")); @@ -1712,10 +1762,15 @@ } else if (len > 0 && ret_data && *ret_data) { gchar **split_data = g_strsplit(ret_data, "\r\n", -1); - int totalelements = g_strv_length(split_data); + int totalelements = 0; int response_no = -1; char *token = NULL; +#if GLIB_CHECK_VERSION(2,6,0) + totalelements = g_strv_length(split_data); +#else + while (split_data[++totalelements] != NULL); +#endif if(totalelements >= 5) { response_no = strtol(split_data[1], NULL, 10); token = g_strdup(split_data[2] + strlen("ymsgr=")); @@ -1972,7 +2027,7 @@ { #ifdef TRY_WEBMESSENGER_LOGIN struct yahoo_data *yd = gc->proto_data; -#endif +#endif /* TRY_WEBMESSENGER_LOGIN */ GSList *l = pkt->hash; int err = 0; char *msg; @@ -2016,7 +2071,7 @@ yd->url_datas = g_slist_prepend(yd->url_datas, url_data); return; } -#endif +#endif /* TRY_WEBMESSENGER_LOGIN */ if (!purple_account_get_remember_password(account)) purple_account_set_password(account, NULL); @@ -2082,7 +2137,7 @@ return; if (!group) group = ""; - + if(msn) who = g_strconcat("msn/", temp, NULL); else @@ -2123,7 +2178,7 @@ { size_t pkt_len; guchar *raw_packet; - + /*build the raw packet and send it to the host*/ pkt_len = yahoo_packet_build(pkt, 0, 0, 0, &raw_packet); if(write(source, raw_packet, pkt_len) != pkt_len) @@ -2302,7 +2357,7 @@ yahoo_p2p_disconnect_destroy_data(data); return; } - + if(len < YAHOO_PACKET_HDRLEN) return; @@ -2311,13 +2366,11 @@ purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n"); start = memchr(buf + 1, 'Y', len - 1); - if(start) { - g_memmove(buf, start, len - (start - buf)); - len -= start - buf; - } else { - g_free(buf); + if (start == NULL) return; - } + + g_memmove(buf, start, len - (start - buf)); + len -= start - buf; } pos += 4; /* YMSG */ @@ -2442,7 +2495,7 @@ void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13) { const char *public_ip; - guint32 temp[4]; + guint32 temp[4]; guint32 ip; char temp_str[100]; gchar *base64_ip = NULL; @@ -2463,7 +2516,7 @@ if( strcmp(purple_normalize(account, purple_account_get_username(account)), who) == 0) return; - /* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */ + /* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */ if( !( f && (yahoo_friend_get_p2p_status(f) == YAHOO_P2PSTATUS_NOT_CONNECTED) && (f->p2p_packet_sent == 0)) ) return; @@ -2526,7 +2579,7 @@ if(error_message != NULL) { purple_debug_warning("yahoo","p2p: %s\n",error_message); yahoo_send_p2p_pkt(p2p_data->gc, p2p_data->host_username, 2);/* send p2p init packet with val_13=2 */ - + yahoo_p2p_disconnect_destroy_data(p2p_data); return; } @@ -2647,7 +2700,7 @@ p2p_data->source = -1; /* connect to host */ - if((purple_proxy_connect(NULL, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) { + if((purple_proxy_connect(gc, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) { purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip); g_free(p2p_data->host_ip); g_free(p2p_data->host_username); @@ -2965,11 +3018,6 @@ struct yahoo_data *yd; struct yahoo_packet *pkt; - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - close(source); - return; - } - if (source < 0) { gchar *tmp; tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), @@ -2997,11 +3045,6 @@ struct yahoo_data *yd; struct yahoo_packet *pkt; - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - close(source); - return; - } - if (source < 0) { gchar *tmp; tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), @@ -3291,7 +3334,7 @@ purple_cipher_context_destroy(context); } -#endif +#endif /* TRY_WEBMESSENGER_LOGIN */ static void yahoo_server_check(PurpleAccount *account) { @@ -3299,7 +3342,8 @@ server = purple_account_get_string(account, "server", YAHOO_PAGER_HOST); - if (strcmp(server, "scs.yahoo.com") == 0) + if (*server == '\0' || g_str_equal(server, "scs.yahoo.com") || + g_str_equal(server, "scs.msg.yahoo.com")) purple_account_set_string(account, "server", YAHOO_PAGER_HOST); } @@ -3898,7 +3942,7 @@ struct yahoo_data *yd = gc->proto_data; g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); - + yd->url_datas = g_slist_remove(yd->url_datas, url_data); if (error_message != NULL) @@ -4039,7 +4083,7 @@ xmlnode *validate_data_root = xmlnode_from_str(webdata, -1); xmlnode *validate_data_child = xmlnode_get_child(validate_data_root, "mobile_no"); mobile_no = (char *)xmlnode_get_attrib(validate_data_child, "msisdn"); - + validate_data_root = xmlnode_copy(validate_data_child); validate_data_child = xmlnode_get_child(validate_data_root, "status"); status = xmlnode_get_data(validate_data_child); @@ -4142,7 +4186,7 @@ struct yahoo_p2p_data *p2p_data; gboolean msn = FALSE; msg2 = yahoo_string_encode(gc, msg, &utf8); - + if(msg2) { lenb = strlen(msg2); lenc = g_utf8_strlen(msg2, -1); @@ -4172,13 +4216,11 @@ struct yahoo_sms_carrier_cb_data *sms_cb_data; sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data)); sms_cb_data->gc = gc; - sms_cb_data->who = g_malloc(strlen(who)); - sms_cb_data->what = g_malloc(strlen(what)); - strcpy(sms_cb_data->who, who); - strcpy(sms_cb_data->what, what); + sms_cb_data->who = g_strdup(who); + sms_cb_data->what = g_strdup(what); purple_conversation_write(conv, NULL, "Getting mobile carrier to send the sms", PURPLE_MESSAGE_SYSTEM, time(NULL)); - + yahoo_get_sms_carrier(gc, sms_cb_data); g_free(msg); @@ -4297,7 +4339,7 @@ if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !msn ) { yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc), 14, " ", 13, state == PURPLE_TYPING ? "1" : "0", - 5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */ + 5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */ yahoo_p2p_write_pkt(p2p_data->source, pkt); yahoo_packet_free(pkt); }
--- a/libpurple/protocols/yahoo/yahoo.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Sat Jun 27 17:50:49 2009 +0000 @@ -53,7 +53,7 @@ #define YAHOOJP_XFER_HOST "filetransfer.msg.yahoo.co.jp" #define YAHOOJP_WEBCAM_HOST "wc.yahoo.co.jp" /* not sure, must test: */ -#define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.co.jp" +#define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.co.jp" #define YAHOOJP_XFER_RELAY_PORT 80 #define YAHOOJP_ROOMLIST_URL "http://insider.msg.yahoo.co.jp/ycontent/" #define YAHOOJP_ROOMLIST_LOCALE "ja" @@ -89,7 +89,7 @@ #define YAHOOJP_CLIENT_VERSION_ID "4194239" #define YAHOOJP_CLIENT_VERSION "9.0.0.2152" -#define YAHOO_CLIENT_USERAGENT "Mozilla/4.0 (compatible; MSIE 5.5)" +#define YAHOO_CLIENT_USERAGENT "Mozilla/5.0" /* Index into attention types list. */ #define YAHOO_BUZZ 0 @@ -201,7 +201,7 @@ GSList *url_datas; GHashTable *xfer_peer_idstring_map;/* Hey, i dont know, but putting this HashTable next to friends gives a run time fault... */ GSList *cookies;/* contains all cookies, including _y and _t */ - + /** * We may receive a list15 in multiple packets with no prior warning as to how many we'll be getting; * the server expects us to keep track of the group for which it is sending us contact names.
--- a/libpurple/protocols/yahoo/yahoo_aliases.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_aliases.h Sat Jun 27 17:50:49 2009 +0000 @@ -1,38 +1,38 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 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 02111-1301 USA - * - */ - - -#include "internal.h" - -#include "account.h" -#include "accountopt.h" -#include "blist.h" -#include "debug.h" -#include "util.h" -#include "version.h" -#include "yahoo.h" -#include "yahoo_packet.h" - -void yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias); -void yahoo_fetch_aliases(PurpleConnection *gc); - +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 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 02111-1301 USA + * + */ + + +#include "internal.h" + +#include "account.h" +#include "accountopt.h" +#include "blist.h" +#include "debug.h" +#include "util.h" +#include "version.h" +#include "yahoo.h" +#include "yahoo_packet.h" + +void yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias); +void yahoo_fetch_aliases(PurpleConnection *gc); +
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Sat Jun 27 17:50:49 2009 +0000 @@ -320,7 +320,7 @@ if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) { if (yd->jp) { - if (purple_proxy_connect(NULL, account, purple_account_get_string(account, "xferjp_host", YAHOOJP_XFER_HOST), + if (purple_proxy_connect(gc, account, purple_account_get_string(account, "xferjp_host", YAHOOJP_XFER_HOST), purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT), yahoo_sendfile_connected, xfer) == NULL) { @@ -329,7 +329,7 @@ purple_xfer_cancel_remote(xfer); } } else { - if (purple_proxy_connect(NULL, account, purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST), + if (purple_proxy_connect(gc, account, purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST), purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT), yahoo_sendfile_connected, xfer) == NULL) { @@ -340,7 +340,7 @@ } } else { xfer->fd = -1; - if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, + if (purple_proxy_connect(gc, account, xfer_data->host, xfer_data->port, yahoo_receivefile_connected, xfer) == NULL) { purple_notify_error(gc, NULL, _("File Transfer Failed"), _("Unable to establish file descriptor.")); @@ -380,12 +380,12 @@ 28, xfer->size, 301, 268, 303, 268); - g_free(filename); + g_free(filename); } else { if(xfer_data->firstoflist == TRUE) { pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, YAHOO_STATUS_AVAILABLE, yd->session_id); - + yahoo_packet_hash(pkt, "sssi", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, @@ -394,7 +394,7 @@ } else { pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, YAHOO_STATUS_AVAILABLE, yd->session_id); - + yahoo_packet_hash(pkt, "sssi", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, @@ -623,7 +623,7 @@ else if (written <= 0) purple_debug_info("yahoo", "p2p filetransfer: Unable to write HTTP OK"); - /* close connection */ + /* close connection */ close(xfer->fd); xfer->fd = -1; g_free(tx); @@ -644,14 +644,14 @@ /* Send HTTP OK in case of p2p transfer, when we act as server */ if((xfer_data->xfer_url != NULL) && (xfer_old->fd >=0) && (purple_xfer_get_status(xfer_old) == PURPLE_XFER_STATUS_DONE)) yahoo_p2p_ft_server_send_OK(xfer_old); - + /* removing top of filename & size list completely */ g_free( xfer_data->filename_list->data ); g_free( xfer_data->size_list->data ); - + xfer_data->filename_list->data = NULL; xfer_data->size_list->data = NULL; - + xfer_data->filename_list = g_slist_delete_link(xfer_data->filename_list, xfer_data->filename_list); xfer_data->size_list = g_slist_delete_link(xfer_data->size_list, xfer_data->size_list); @@ -697,16 +697,16 @@ /* Build the file transfer handle. */ xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, xfer_old->who); - + if (xfer) { /* Set the info about the incoming file. */ char *utf8_filename = yahoo_string_decode(gc, filename, TRUE); purple_xfer_set_filename(xfer, utf8_filename); g_free(utf8_filename); purple_xfer_set_size(xfer, filesize); - + xfer->data = xfer_data; - + /* Setup our I/O op functions */ purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15); purple_xfer_set_start_fnc(xfer, yahoo_xfer_start); @@ -1003,7 +1003,7 @@ purple_xfer_cancel_remote(xfer); return; } - + /* Discard the length... */ hosts = g_slist_remove(hosts, hosts->data); if(!hosts) @@ -1012,7 +1012,7 @@ purple_xfer_cancel_remote(xfer); return; } - + /* TODO:actually, u must try with addr no.1 , if its not working addr no.2 ..... */ addr = hosts->data; actaddr = addr->sin_addr.s_addr; @@ -1113,14 +1113,15 @@ while((did = read(source, buf, 998)) > 0) { xd->txbuflen += did; - buf[did] = '\0'; + buf[did] = '\0'; t = xd->txbuf; xd->txbuf = g_strconcat(t,buf,NULL); g_free(t); } g_free(buf); - if (did < 0 && errno == EAGAIN) return; + if (did < 0 && errno == EAGAIN) + return; else if (did < 0) { purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); purple_xfer_cancel_remote(xfer); @@ -1136,7 +1137,7 @@ close(source);/* Is this required? */ g_free(xd->txbuf); xd->txbuf = NULL; - if (purple_proxy_connect(NULL, account, xd->host, xd->port, yahoo_xfer_connected_15, xfer) == NULL) + if (purple_proxy_connect(gc, account, xd->host, xd->port, yahoo_xfer_connected_15, xfer) == NULL) { purple_notify_error(gc, NULL, _("File Transfer Failed"), _("Unable to establish file descriptor.")); @@ -1276,7 +1277,7 @@ } } else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED) - { + { if(xd->info_val_249 == 1) { /* receiving file via p2p, connected as client */ @@ -1592,7 +1593,7 @@ GSList *filename_list = NULL; GSList *size_list = NULL; int nooffiles = 0; - + yd = gc->proto_data; for (l = pkt->hash; l; l = l->next) { @@ -1617,7 +1618,7 @@ break; case 222: val_222 = atol(pair->value); - /* 1=send, 2=cancel, 3=accept, 4=reject */ + /* 1=send, 2=cancel, 3=accept, 4=reject */ break; /* check for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it. */ @@ -1655,7 +1656,7 @@ * so, purple dnsquery is used... but retries, trying with next ip * address etc. is not implemented..TODO */ - + /* To send through p2p */ if( g_hash_table_lookup(yd->peers, from) ) { /* send p2p file transfer information */ @@ -1685,7 +1686,7 @@ g_hash_table_replace(yd->imvironments, g_strdup(from), g_strdup(imv)); return; } - + if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) { if (service && (strcmp("FILEXFER", service) != 0)) { purple_debug_misc("yahoo", "unhandled service 0x%02x\n", pkt->service); @@ -1710,7 +1711,7 @@ xfer_data->xfer_peer_idstring = g_strdup(xfer_peer_idstring); xfer_data->filename_list = filename_list; xfer_data->size_list = size_list; - + /* Build the file transfer handle. */ xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, from); xfer->message = NULL; @@ -1739,7 +1740,7 @@ g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer); - + if(nooffiles > 1) { gchar* message; message = g_strdup_printf(_("%s is trying to send you a group of %d files.\n"), xfer->who, nooffiles); @@ -1825,23 +1826,22 @@ purple_xfer_cancel_remote(xfer); return; } - + account = purple_connection_get_account(xfer_data->gc); pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt_to_send, "ssssisi", + yahoo_packet_hash(pkt_to_send, "ssssis", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xfer_data->xfer_peer_idstring, 27, xfer->filename, 249, xfer_data->info_val_249, - 251, xfer_data->xfer_idstring_for_relay, - 222, 3); + 251, xfer_data->xfer_idstring_for_relay); yahoo_packet_send_and_free(pkt_to_send, yd); - if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, + if (purple_proxy_connect(gc, account, xfer_data->host, xfer_data->port, yahoo_xfer_connected_15, xfer) == NULL) { purple_notify_error(gc, NULL, _("File Transfer Failed"), _("Unable to establish file descriptor.")); @@ -1916,12 +1916,12 @@ xfer_data = xfer->data; if(url) purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL); - + xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay); xfer_data->status_15 = ACCEPTED; account = purple_connection_get_account(gc); - if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, + if (purple_proxy_connect(gc, account, xfer_data->host, xfer_data->port, yahoo_xfer_connected_15, xfer) == NULL) { purple_notify_error(gc, NULL, _("File Transfer Failed"),_("Unable to connect"));
--- a/libpurple/protocols/yahoo/yahoo_friend.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_friend.c Sat Jun 27 17:50:49 2009 +0000 @@ -172,16 +172,16 @@ l = l->next; } + if (value != 1 && value != 2) { + purple_debug_error("yahoo", "Received unknown value for presence key: %d\n", value); + return; + } + if(msn) who = g_strconcat("msn/", temp, NULL); else who = g_strdup(temp); - if (value != 1 && value != 2) { - purple_debug_error("yahoo", "Received unknown value for presence key: %d\n", value); - return; - } - g_return_if_fail(who != NULL); f = yahoo_friend_find(gc, who);
--- a/libpurple/protocols/yahoo/yahoo_packet.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.c Sat Jun 27 17:50:49 2009 +0000 @@ -195,7 +195,7 @@ "Key: %d \tValue: %s\n", pair->key, esc); g_free(esc); } -#endif +#endif /* DEBUG */ } else { g_free(pair); } @@ -282,7 +282,7 @@ } purple_debug(PURPLE_DEBUG_MISC, NULL, "\n"); -#endif +#endif /* YAHOO_DEBUG */ } static void @@ -329,7 +329,7 @@ if (wm) pos += yahoo_put16(data + pos, YAHOO_WEBMESSENGER_PROTO_VER); else if (jp) - pos += yahoo_put16(data + pos, YAHOO_PROTO_VER_JAPAN); + pos += yahoo_put16(data + pos, YAHOO_PROTO_VER_JAPAN); else pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); pos += yahoo_put16(data + pos, 0x0000);
--- a/libpurple/protocols/yahoo/yahoo_packet.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.h Sat Jun 27 17:50:49 2009 +0000 @@ -107,6 +107,7 @@ YAHOO_SERVICE_CHGRP_15 = 0xe7, YAHOO_SERVICE_STATUS_15 = 0xf0, YAHOO_SERVICE_LIST_15 = 0xf1, + YAHOO_SERVICE_MESSAGE_ACK = 0xfb, YAHOO_SERVICE_WEBLOGIN = 0x0226, YAHOO_SERVICE_SMS_MSG = 0x02ea /* YAHOO_SERVICE_DISCONNECT = 0x07d1 Server forces us to disconnect. Is sent with TCP FIN flag set */
--- a/libpurple/protocols/yahoo/yahoo_picture.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_picture.c Sat Jun 27 17:50:49 2009 +0000 @@ -110,6 +110,9 @@ l = l->next; } + if (!who) + return; + if (!purple_privacy_check(purple_connection_get_account(gc), who)) { purple_debug_info("yahoo", "Picture packet from %s dropped.\n", who); return; @@ -126,7 +129,7 @@ gboolean use_whole_url = yahoo_account_use_http_proxy(gc); /* FIXME: Cleanup this strtol() stuff if possible. */ - if (b && (locksum = purple_buddy_icons_get_checksum_for_user(b)) != NULL && + if (b && (locksum = purple_buddy_icons_get_checksum_for_user(b)) != NULL && (checksum == strtol(locksum, NULL, 10))) return; @@ -136,7 +139,7 @@ data->checksum = checksum; /* TODO: Does this need to be MSIE 5.0? */ url_data = purple_util_fetch_url(url, use_whole_url, - "Mozilla/4.0 (compatible; MSIE 5.0)", FALSE, + "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, yahoo_fetch_picture_cb, data); if (url_data != NULL) { yd = gc->proto_data; @@ -312,8 +315,8 @@ } pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pkt, "sssssi", 1, purple_connection_get_display_name(gc), - 4, purple_connection_get_display_name(gc), 5, who, + yahoo_packet_hash(pkt, "ssssi", 1, purple_connection_get_display_name(gc), + 5, who, 13, "2", 20, yd->picture_url, 192, yd->picture_checksum); yahoo_packet_send_and_free(pkt, yd); } @@ -324,7 +327,7 @@ struct yahoo_packet *pkt; pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash_str(pkt, 4, purple_connection_get_display_name(gc)); /* me */ + yahoo_packet_hash_str(pkt, 1, purple_connection_get_display_name(gc)); /* me */ yahoo_packet_hash_str(pkt, 5, who); /* the other guy */ yahoo_packet_hash_str(pkt, 13, "1"); /* 1 = request, 2 = reply */ yahoo_packet_send_and_free(pkt, yd); @@ -506,7 +509,7 @@ "Content-Length: %" G_GSIZE_FORMAT "\r\n" "Cache-Control: no-cache\r\n\r\n", use_whole_url ? "http://" : "", use_whole_url ? tmp : "", - yd->cookie_t, yd->cookie_y, + yd->cookie_t, yd->cookie_y, tmp, pkt_buf_len + 4 + d->str->len); g_free(tmp); @@ -573,7 +576,7 @@ purple_debug_misc("yahoo", "Calculated buddy icon checksum: %d\n", checksum); return checksum; -} +} void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) {
--- a/libpurple/protocols/yahoo/yahoo_profile.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_profile.c Sat Jun 27 17:50:49 2009 +0000 @@ -29,7 +29,7 @@ #include "util.h" #if PHOTO_SUPPORT #include "imgstore.h" -#endif +#endif /* PHOTO_SUPPORT */ #include "yahoo.h" #include "yahoo_friend.h" @@ -40,11 +40,11 @@ } YahooGetInfoData; typedef enum profile_lang_id { - XX, DA, DE, EL, - EN, EN_GB, + XX, DA, DE, EL, + EN, EN_GB, ES_AR, ES_ES, ES_MX, ES_US, - FR_CA, FR_FR, - IT, JA, KO, NO, PT, SV, + FR_CA, FR_FR, + IT, JA, KO, NO, PT, SV, ZH_CN, ZH_HK, ZH_TW, ZH_US, PT_BR } profile_lang_id_t; @@ -702,7 +702,7 @@ const char *balias = purple_buddy_get_local_buddy_alias(b); if(balias && balias[0]) { char *aliastext = g_markup_escape_text(balias, -1); - purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext); + purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext); g_free(aliastext); } #if 0 @@ -777,7 +777,7 @@ char *stripped; int stripped_len; char *last_updated_utf8_string = NULL; -#endif +#endif /* !PHOTO_SUPPORT */ const char *last_updated_string = NULL; char *url_buffer; GString *s; @@ -899,7 +899,7 @@ #if PHOTO_SUPPORT photo_url_text = yahoo_get_photo_url(url_text, info_data->name); -#endif +#endif /* PHOTO_SUPPORT */ url_buffer = g_strdup(url_text); @@ -1048,7 +1048,7 @@ purple_debug_info("yahoo", "%s is %" G_GSIZE_FORMAT " bytes\n", photo_url_text, len); id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL); - + tmp = g_strdup_printf("<img id=\"%d\"><br>", id); purple_notify_user_info_add_pair(user_info, NULL, tmp); g_free(tmp); @@ -1259,7 +1259,7 @@ g_free(info2_data); if (id != -1) purple_imgstore_unref_by_id(id); -#endif +#endif /* PHOTO_SUPPORT */ } void yahoo_get_info(PurpleConnection *gc, const char *name)
--- a/libpurple/protocols/yahoo/yahoochat.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoochat.c Sat Jun 27 17:50:49 2009 +0000 @@ -30,7 +30,7 @@ #ifdef HAVE_CONFIG_H #include "config.h" -#endif +#endif /* HAVE_CONFIG_H */ #include "debug.h" #include "privacy.h" @@ -1519,7 +1519,7 @@ purple_roomlist_set_fields(rl, fields); - if (purple_proxy_connect(NULL, account, yrl->host, 80, + if (purple_proxy_connect(gc, account, yrl->host, 80, yahoo_roomlist_got_connected, yrl) == NULL) { purple_notify_error(gc, NULL, _("Connection problem"), _("Unable to fetch room list.")); @@ -1588,8 +1588,9 @@ yrl->ucat = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_CATEGORY, _("User Rooms"), yrl->cat); purple_roomlist_room_add(list, yrl->ucat); - if (purple_proxy_connect(NULL, list->account, yrl->host, 80, - yahoo_roomlist_got_connected, yrl) == NULL) + if (purple_proxy_connect(purple_account_get_connection(list->account), + list->account, yrl->host, 80, + yahoo_roomlist_got_connected, yrl) == NULL) { purple_notify_error(purple_account_get_connection(list->account), NULL, _("Connection problem"), _("Unable to fetch room list."));
--- a/libpurple/protocols/yahoo/ycht.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/protocols/yahoo/ycht.c Sat Jun 27 17:50:49 2009 +0000 @@ -225,7 +225,7 @@ } purple_debug(PURPLE_DEBUG_MISC, NULL, "\n"); -#endif +#endif /* YAHOO_YCHT_DEBUG */ } static YchtPkt *ycht_packet_new(guint version, guint service, int status) @@ -578,7 +578,7 @@ yd->ycht = ycht; - if (purple_proxy_connect(NULL, account, + if (purple_proxy_connect(gc, account, purple_account_get_string(account, "ycht-server", YAHOO_YCHT_HOST), purple_account_get_int(account, "ycht-port", YAHOO_YCHT_PORT), ycht_got_connected, ycht) == NULL)
--- a/libpurple/util.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/util.c Sat Jun 27 17:50:49 2009 +0000 @@ -3898,7 +3898,7 @@ url_fetch_recv_cb(data, -1, cond); } -/* +/** * This function is called when the socket is available to be written * to. *
--- a/libpurple/xmlnode.c Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/xmlnode.c Sat Jun 27 17:50:49 2009 +0000 @@ -382,7 +382,7 @@ } char * -xmlnode_get_data(xmlnode *node) +xmlnode_get_data(const xmlnode *node) { GString *str = NULL; xmlnode *c; @@ -405,7 +405,7 @@ } char * -xmlnode_get_data_unescaped(xmlnode *node) +xmlnode_get_data_unescaped(const xmlnode *node) { char *escaped = xmlnode_get_data(node);
--- a/libpurple/xmlnode.h Sat Jun 27 17:50:35 2009 +0000 +++ b/libpurple/xmlnode.h Sat Jun 27 17:50:49 2009 +0000 @@ -136,7 +136,7 @@ * @return The data from the node or NULL. This data is in raw escaped format. * You must g_free this string when finished using it. */ -char *xmlnode_get_data(xmlnode *node); +char *xmlnode_get_data(const xmlnode *node); /** * Gets unescaped data from a node. @@ -146,7 +146,7 @@ * @return The data from the node, in unescaped form. You must g_free * this string when finished using it. */ -char *xmlnode_get_data_unescaped(xmlnode *node); +char *xmlnode_get_data_unescaped(const xmlnode *node); /** * Sets an attribute for a node.
--- a/pidgin/eggtrayicon.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/eggtrayicon.c Sat Jun 27 17:50:49 2009 +0000 @@ -400,9 +400,35 @@ static gboolean transparent_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { - gdk_window_clear_area (widget->window, event->area.x, event->area.y, - event->area.width, event->area.height); - return FALSE; + GtkWidget *focus_child = NULL; + gint border_width, x, y, width, height; + gboolean retval = FALSE; + + gdk_window_clear_area (widget->window, event->area.x, event->area.y, + event->area.width, event->area.height); + + if (GTK_WIDGET_CLASS (parent_class)->expose_event) + retval = GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + + if (GTK_CONTAINER (widget)->focus_child) + focus_child = GTK_CONTAINER (GTK_CONTAINER (widget)->focus_child)->focus_child; + if (focus_child && GTK_WIDGET_HAS_FOCUS (focus_child)) + { + border_width = GTK_CONTAINER (widget)->border_width; + + x = widget->allocation.x + border_width; + y = widget->allocation.y + border_width; + + width = widget->allocation.width - 2 * border_width; + height = widget->allocation.height - 2 * border_width; + + gtk_paint_focus (widget->style, widget->window, + GTK_WIDGET_STATE (widget), + &event->area, widget, "tray_icon", + x, y, width, height); + } + + return retval; } static void
--- a/pidgin/gtkdocklet-x11.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/gtkdocklet-x11.c Sat Jun 27 17:50:49 2009 +0000 @@ -32,6 +32,7 @@ #include "eggtrayicon.h" #include "gtkdocklet.h" +#include <gdk/gdkkeysyms.h> #define SHORT_EMBED_TIMEOUT 5000 #define LONG_EMBED_TIMEOUT 15000 @@ -89,6 +90,33 @@ return TRUE; } +static gboolean +docklet_x11_pressed_cb(GtkWidget *button, GdkEventKey *event) +{ + guint state, keyval; + + state = event->state & gtk_accelerator_get_default_mod_mask(); + keyval = event->keyval; + if (state == 0 && + (keyval == GDK_Return || + keyval == GDK_KP_Enter || + keyval == GDK_ISO_Enter || + keyval == GDK_space || + keyval == GDK_KP_Space)) + { + pidgin_docklet_clicked(1); + return TRUE; + } + + return FALSE; +} + +static void +docklet_x11_popup_cb(GtkWidget *button) +{ + pidgin_docklet_clicked(3); +} + static void docklet_x11_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending) { @@ -259,11 +287,14 @@ docklet = egg_tray_icon_new(PIDGIN_NAME); box = gtk_event_box_new(); image = gtk_image_new(); + GTK_WIDGET_SET_FLAGS (image, GTK_CAN_FOCUS); g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_x11_embedded_cb), NULL); g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_x11_destroyed_cb), NULL); g_signal_connect(G_OBJECT(docklet), "size-allocate", G_CALLBACK(docklet_x11_resize_icon), NULL); g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_x11_clicked_cb), NULL); + g_signal_connect(G_OBJECT(box), "key-press-event", G_CALLBACK(docklet_x11_pressed_cb), NULL); + g_signal_connect(G_OBJECT(box), "popup-menu", G_CALLBACK(docklet_x11_popup_cb), NULL); gtk_container_add(GTK_CONTAINER(box), image); gtk_container_add(GTK_CONTAINER(docklet), box);
--- a/pidgin/gtkft.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/gtkft.c Sat Jun 27 17:50:49 2009 +0000 @@ -705,12 +705,12 @@ }; /* Setup the initial table */ - dialog->table = table = gtk_table_new(9, 2, FALSE); + dialog->table = table = gtk_table_new(G_N_ELEMENTS(labels) + 1, 2, FALSE); gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE); gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE); /* Setup the labels */ - for (i = 0; i < sizeof(labels) / sizeof(*labels); i++) { + for (i = 0; i < G_N_ELEMENTS(labels); i++) { GtkWidget *label; char buf[256]; @@ -734,7 +734,9 @@ /* Setup the progress bar */ dialog->progress = gtk_progress_bar_new(); - gtk_table_attach(GTK_TABLE(table), dialog->progress, 0, 2, 8, 9, + gtk_table_attach(GTK_TABLE(table), dialog->progress, + 0, 2, + G_N_ELEMENTS(labels), G_N_ELEMENTS(labels) + 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show(dialog->progress);
--- a/pidgin/gtkicon-theme-loader.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/gtkicon-theme-loader.c Sat Jun 27 17:50:49 2009 +0000 @@ -41,7 +41,7 @@ filename_full = g_build_filename(dir, "theme.xml", NULL); if (g_file_test(filename_full, G_FILE_TEST_IS_REGULAR)) - root_node = xmlnode_from_file(dir, "theme.xml", "sound themes", "sound-theme-loader"); + root_node = xmlnode_from_file(dir, "theme.xml", "icon themes", "icon-theme-loader"); g_free(filename_full); g_return_val_if_fail(root_node != NULL, NULL);
--- a/pidgin/gtkimhtml.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/gtkimhtml.c Sat Jun 27 17:50:49 2009 +0000 @@ -2996,10 +2996,21 @@ break; id = gtk_imhtml_get_html_opt(tag, "ID="); - if (!id) - break; - gtk_imhtml_insert_image_at_iter(imhtml, atoi(id), iter); - g_free(id); + if (id) { + gtk_imhtml_insert_image_at_iter(imhtml, atoi(id), iter); + g_free(id); + } else { + char *src, *alt; + src = gtk_imhtml_get_html_opt(tag, "SRC="); + alt = gtk_imhtml_get_html_opt(tag, "ALT="); + if (src) { + gtk_imhtml_toggle_link(imhtml, src); + gtk_text_buffer_insert(imhtml->text_buffer, iter, alt ? alt : src, -1); + gtk_imhtml_toggle_link(imhtml, NULL); + } + g_free (src); + g_free (alt); + } break; } case 47: /* P (opt) */
--- a/pidgin/gtknotify.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/gtknotify.c Sat Jun 27 17:50:49 2009 +0000 @@ -557,7 +557,10 @@ gtk_tree_store_remove(treemodel, &iter); advanced = (iter.stamp == 0) ? FALSE : TRUE; #endif - purple_notify_close(PURPLE_NOTIFY_EMAILS, data); + if (data->purple_has_handle) + purple_notify_close(PURPLE_NOTIFY_EMAILS, data); + else + pidgin_close_notify(PURPLE_NOTIFY_EMAILS, data); /* We're completely done if we've processed all entries */ if (!advanced) return NULL; @@ -612,7 +615,7 @@ char *notification; PurpleAccount *account; PidginNotifyMailData *data = NULL, *data2; - gboolean new_data; + gboolean new_data = FALSE; /* Don't bother updating if there aren't new emails and we don't have any displayed currently */ if (count == 0 && mail_dialog == NULL) @@ -660,7 +663,7 @@ /* If we don't keep track of this, will leak "data" for each of the notifications except the last */ data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, 0, FALSE, &new_data); - if (new_data) { + if (data2 && new_data) { if (data) data->purple_has_handle = FALSE; data = data2; @@ -677,7 +680,7 @@ (int)count), *tos, (int)count); data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, count, FALSE, &new_data); - if (new_data) { + if (data2 && new_data) { if (data) data->purple_has_handle = FALSE; data = data2;
--- a/pidgin/gtkstatusbox.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/gtkstatusbox.c Sat Jun 27 17:50:49 2009 +0000 @@ -2045,17 +2045,11 @@ if (status_box->icon_box) { - GtkTextDirection dir = gtk_widget_get_direction(widget); parent_alc.width -= (parent_alc.height + border_width); icon_alc = parent_alc; icon_alc.height = MAX(1, icon_alc.height) - 2; icon_alc.width = icon_alc.height; - if (dir == GTK_TEXT_DIR_RTL) { - icon_alc.x = parent_alc.x; - parent_alc.x += icon_alc.width + border_width; - } else { - icon_alc.x = allocation->width - (icon_alc.width + border_width + 1); - } + icon_alc.x = allocation->width - (icon_alc.width + border_width + 1); icon_alc.y += 1; if (status_box->icon_size != icon_alc.height) @@ -2264,14 +2258,22 @@ if (status_box->buddy_icon_img != NULL) { + GdkPixbuf *buf, *scale; + int scale_width, scale_height; GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); g_signal_connect(G_OBJECT(loader), "size-prepared", G_CALLBACK(pixbuf_size_prepared_cb), NULL); gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(status_box->buddy_icon_img), purple_imgstore_get_size(status_box->buddy_icon_img), NULL); gdk_pixbuf_loader_close(loader, NULL); - status_box->buddy_icon = gdk_pixbuf_loader_get_pixbuf(loader); - if (status_box->buddy_icon) - g_object_ref(status_box->buddy_icon); + buf = gdk_pixbuf_loader_get_pixbuf(loader); + scale_width = gdk_pixbuf_get_width(buf); + scale_height = gdk_pixbuf_get_height(buf); + scale = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height); + gdk_pixbuf_fill(scale, 0x00000000); + gdk_pixbuf_copy_area(buf, 0, 0, scale_width, scale_height, scale, 0, 0); + if (pidgin_gdk_pixbuf_is_opaque(scale)) + pidgin_gdk_pixbuf_make_round(scale); + status_box->buddy_icon = scale; g_object_unref(loader); }
--- a/pidgin/plugins/Makefile.mingw Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/plugins/Makefile.mingw Sat Jun 27 17:50:49 2009 +0000 @@ -7,6 +7,7 @@ PIDGIN_TREE_TOP := ../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak +DISCO_PLUGIN := ./disco GTKPERL_PLUGIN := ./perl TICKER_PLUGIN := ./ticker TRANSPARENCY_PLUGIN := ./win32/transparency @@ -58,12 +59,14 @@ .PHONY: all clean plugins install all: plugins + $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) install: all $(PIDGIN_INSTALL_PLUGINS_DIR) + $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) install $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) install $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) install $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) install @@ -95,6 +98,7 @@ ## clean: rm -f *.o *.dll + $(MAKE) -C $(DISCO_PLUGIN) -f $(MINGW_MAKEFILE) clean $(MAKE) -C $(GTKPERL_PLUGIN) -f $(MINGW_MAKEFILE) clean $(MAKE) -C $(TICKER_PLUGIN) -f $(MINGW_MAKEFILE) clean $(MAKE) -C $(TRANSPARENCY_PLUGIN) -f $(MINGW_MAKEFILE) clean
--- a/pidgin/plugins/disco/Makefile.am Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/plugins/disco/Makefile.am Sat Jun 27 17:50:49 2009 +0000 @@ -8,7 +8,9 @@ xmppdisco_la_SOURCES = \ gtkdisco.c \ - xmppdisco.c + gtkdisco.h \ + xmppdisco.c \ + xmppdisco.h xmppdisco_la_LIBADD = $(GTK_LIBS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/disco/Makefile.mingw Sat Jun 27 17:50:49 2009 +0000 @@ -0,0 +1,79 @@ +# +# Makefile.mingw +# +# Description: Makefile for xmppdisco plugin. +# + +PIDGIN_TREE_TOP := ../../.. +include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak + +TARGET = xmppdisco + +## +## INCLUDE PATHS +## +INCLUDE_PATHS += -I. \ + -I$(GTK_TOP)/include \ + -I$(GTK_TOP)/include/gtk-2.0 \ + -I$(GTK_TOP)/include/glib-2.0 \ + -I$(GTK_TOP)/include/pango-1.0 \ + -I$(GTK_TOP)/include/atk-1.0 \ + -I$(GTK_TOP)/include/cairo \ + -I$(GTK_TOP)/lib/glib-2.0/include \ + -I$(GTK_TOP)/lib/gtk-2.0/include \ + -I$(PURPLE_TOP) \ + -I$(PURPLE_TOP)/win32 \ + -I$(PIDGIN_TOP) \ + -I$(PIDGIN_TOP)/win32 \ + -I$(PIDGIN_TREE_TOP) + +LIB_PATHS += -L$(GTK_TOP)/lib \ + -L$(PURPLE_TOP) \ + -L$(PIDGIN_TOP) + +## +## SOURCES, OBJECTS +## +C_SRC = xmppdisco.c \ + gtkdisco.c + +OBJECTS = $(C_SRC:%.c=%.o) + +## +## LIBRARIES +## +LIBS = -lgtk-win32-2.0 \ + -lglib-2.0 \ + -lgdk-win32-2.0 \ + -lgobject-2.0 \ + -lpango-1.0 \ + -lgdk_pixbuf-2.0 \ + -lintl \ + -lpurple \ + -lpidgin + +include $(PIDGIN_COMMON_RULES) + +## +## TARGET DEFINITIONS +## +.PHONY: all install clean + +all: $(TARGET).dll + +install: $(PIDGIN_INSTALL_PLUGINS_DIR) all + cp $(TARGET).dll $(PIDGIN_INSTALL_PLUGINS_DIR) + +$(OBJECTS): $(PIDGIN_CONFIG_H) + +$(TARGET).dll: $(PURPLE_DLL).a $(PIDGIN_DLL).a $(OBJECTS) + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll + +## +## CLEAN RULES +## +clean: + rm -rf $(OBJECTS) + rm -rf $(TARGET).dll + +include $(PIDGIN_COMMON_TARGETS)
--- a/pidgin/plugins/disco/gtkdisco.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/plugins/disco/gtkdisco.c Sat Jun 27 17:50:49 2009 +0000 @@ -29,17 +29,13 @@ #include "gtkutils.h" #include "pidgin.h" #include "request.h" +#include "pidgintooltip.h" #include "gtkdisco.h" #include "xmppdisco.h" GList *dialogs = NULL; -struct _menu_cb_info { - PidginDiscoList *list; - XmppDiscoService *service; -}; - enum { PIXBUF_COLUMN = 0, NAME_COLUMN, @@ -69,7 +65,7 @@ g_return_val_if_fail(list != NULL, NULL); ++list->ref; - purple_debug_misc("xmppdisco", "reffing list, ref count now %d\n", list->ref); + purple_debug_misc("xmppdisco", "reffing list, ref count now %d\n", list->ref); return list; } @@ -112,20 +108,46 @@ } } +static GdkPixbuf * +pidgin_disco_load_icon(XmppDiscoService *service, const char *size) +{ + GdkPixbuf *pixbuf = NULL; + char *filename; + + g_return_val_if_fail(service != NULL, NULL); + g_return_val_if_fail(size != NULL, NULL); + + if (service->type == XMPP_DISCO_SERVICE_TYPE_GATEWAY && service->gateway_type) { + char *tmp = g_strconcat(service->gateway_type, ".png", NULL); + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", size, tmp, NULL); + g_free(tmp); +#if 0 + } else if (service->type == XMPP_DISCO_SERVICE_TYPE_USER) { + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", size, "person.png", NULL); +#endif + } else if (service->type == XMPP_DISCO_SERVICE_TYPE_CHAT) + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", size, "chat.png", NULL); + + if (filename) { + pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + g_free(filename); + } + + return pixbuf; +} + static void pidgin_disco_create_tree(PidginDiscoList *pdl); static void dialog_select_account_cb(GObject *w, PurpleAccount *account, - PidginDiscoDialog *dialog) + PidginDiscoDialog *dialog) { dialog->account = account; gtk_widget_set_sensitive(dialog->browse_button, account != NULL); } -static void register_button_cb(GtkButton *button, PidginDiscoDialog *dialog) +static void register_button_cb(GtkWidget *unused, PidginDiscoDialog *dialog) { - struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info"); - - xmpp_disco_service_register(info->service); + xmpp_disco_service_register(dialog->selected); } static void discolist_cancel_cb(PidginDiscoList *pdl, const char *server) @@ -152,7 +174,7 @@ xmpp_disco_start(pdl); } -static void browse_button_cb(GtkButton *button, PidginDiscoDialog *dialog) +static void browse_button_cb(GtkWidget *button, PidginDiscoDialog *dialog) { PurpleConnection *pc; PidginDiscoList *pdl; @@ -212,52 +234,91 @@ g_free(server); } -static void add_room_to_blist_cb(GtkButton *button, PidginDiscoDialog *dialog) +static void add_to_blist_cb(GtkWidget *unused, PidginDiscoDialog *dialog) { - struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info"); + XmppDiscoService *service = dialog->selected; PurpleAccount *account; - const char *name; + const char *jid; + + g_return_if_fail(service != NULL); + + account = purple_connection_get_account(service->list->pc); + jid = service->jid; + + if (service->type == XMPP_DISCO_SERVICE_TYPE_CHAT) + purple_blist_request_add_chat(account, NULL, NULL, jid); + else + purple_blist_request_add_buddy(account, jid, NULL, NULL); +} - g_return_if_fail(info != NULL); +static gboolean +service_click_cb(GtkTreeView *tree, GdkEventButton *event, gpointer user_data) +{ + PidginDiscoList *pdl; + XmppDiscoService *service; + GtkWidget *menu; + + GtkTreePath *path; + GtkTreeIter iter; + GValue val; + + if (event->button != 3 || event->type != GDK_BUTTON_PRESS) + return FALSE; - account = purple_connection_get_account(info->list->pc); - name = info->service->name; + pdl = user_data; + + /* Figure out what was clicked */ + if (!gtk_tree_view_get_path_at_pos(tree, event->x, event->y, &path, + NULL, NULL, NULL)) + return FALSE; + gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &iter, path); + gtk_tree_path_free(path); + val.g_type = 0; + gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN, + &val); + service = g_value_get_pointer(&val); + + if (!service) + return FALSE; - if (info->service->type == XMPP_DISCO_SERVICE_TYPE_CHAT) - purple_blist_request_add_chat(account, NULL, NULL, name); - else - purple_blist_request_add_buddy(account, name, NULL, NULL); + menu = gtk_menu_new(); + + if (service->flags & XMPP_DISCO_ADD) + pidgin_new_item_from_stock(menu, _("Add to Buddy List"), GTK_STOCK_ADD, + G_CALLBACK(add_to_blist_cb), pdl->dialog, + 0, 0, NULL); + + if (service->flags & XMPP_DISCO_REGISTER) { + GtkWidget *item = pidgin_new_item(menu, _("Register")); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(register_button_cb), pdl->dialog); + } + + gtk_widget_show_all(menu); + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, + event->time); + return FALSE; } static void selection_changed_cb(GtkTreeSelection *selection, PidginDiscoList *pdl) { - XmppDiscoService *service; GtkTreeIter iter; GValue val; - static struct _menu_cb_info *info; PidginDiscoDialog *dialog = pdl->dialog; if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { val.g_type = 0; gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN, &val); - service = g_value_get_pointer(&val); - if (!service) { + dialog->selected = g_value_get_pointer(&val); + if (!dialog->selected) { gtk_widget_set_sensitive(dialog->add_button, FALSE); gtk_widget_set_sensitive(dialog->register_button, FALSE); return; } - info = g_new0(struct _menu_cb_info, 1); - info->list = dialog->discolist; - info->service = service; - - g_object_set_data_full(G_OBJECT(dialog->add_button), "disco-info", - info, g_free); - g_object_set_data(G_OBJECT(dialog->register_button), "disco-info", info); - - gtk_widget_set_sensitive(dialog->add_button, service->flags & XMPP_DISCO_ADD); - gtk_widget_set_sensitive(dialog->register_button, service->flags & XMPP_DISCO_REGISTER); + gtk_widget_set_sensitive(dialog->add_button, dialog->selected->flags & XMPP_DISCO_ADD); + gtk_widget_set_sensitive(dialog->register_button, dialog->selected->flags & XMPP_DISCO_REGISTER); } else { gtk_widget_set_sensitive(dialog->add_button, FALSE); gtk_widget_set_sensitive(dialog->register_button, FALSE); @@ -282,6 +343,36 @@ } static void +row_activated_cb(GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + PidginDiscoList *pdl = user_data; + GtkTreeIter iter; + XmppDiscoService *service; + GValue val; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &iter, path)) + return; + + val.g_type = 0; + gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN, + &val); + service = g_value_get_pointer(&val); + + if (service->flags & XMPP_DISCO_BROWSE) + if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(pdl->tree), path)) + gtk_tree_view_collapse_row(GTK_TREE_VIEW(pdl->tree), path); + else + gtk_tree_view_expand_row(GTK_TREE_VIEW(pdl->tree), path, FALSE); + else if (service->flags & XMPP_DISCO_REGISTER) + register_button_cb(NULL, pdl->dialog); + else if (service->flags & XMPP_DISCO_ADD) + add_to_blist_cb(NULL, pdl->dialog); +} + +static void destroy_win_cb(GtkWidget *window, gpointer d) { PidginDiscoDialog *dialog = d; @@ -317,6 +408,94 @@ return purple_strequal(purple_account_get_protocol_id(account), XMPP_PLUGIN_ID); } +static gboolean +disco_paint_tooltip(GtkWidget *tipwindow, gpointer data) +{ + PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin"); + gtk_paint_layout(tipwindow->style, tipwindow->window, GTK_STATE_NORMAL, FALSE, + NULL, tipwindow, "tooltip", + 6, 6, layout); + return TRUE; +} + +static gboolean +disco_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path, + gpointer data, int *w, int *h) +{ + PidginDiscoList *pdl = data; + GtkTreeIter iter; + PangoLayout *layout; + int width, height; + XmppDiscoService *service; + GValue val; + const char *type = NULL; + char *markup, *jid, *name, *desc = NULL; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &iter, path)) + return FALSE; + + val.g_type = 0; + gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN, + &val); + service = g_value_get_pointer(&val); + + switch (service->type) { + case XMPP_DISCO_SERVICE_TYPE_UNSET: + type = _("Unknown"); + break; + + case XMPP_DISCO_SERVICE_TYPE_GATEWAY: + type = _("Gateway"); + break; + + case XMPP_DISCO_SERVICE_TYPE_DIRECTORY: + type = _("Directory"); + break; + + case XMPP_DISCO_SERVICE_TYPE_CHAT: + type = _("Chat"); + break; + + case XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION: + type = _("PubSub Collection"); + break; + + case XMPP_DISCO_SERVICE_TYPE_PUBSUB_LEAF: + type = _("PubSub Leaf"); + break; + + case XMPP_DISCO_SERVICE_TYPE_OTHER: + type = _("Other"); + break; + } + + markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s%s%s", + name = g_markup_escape_text(service->name, -1), + type, + jid = g_markup_escape_text(service->jid, -1), + service->description ? _("\n<b>Description:</b> ") : "", + service->description ? desc = g_markup_escape_text(service->description, -1) : ""); + + layout = gtk_widget_create_pango_layout(tipwindow, NULL); + pango_layout_set_markup(layout, markup, -1); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD); + pango_layout_set_width(layout, 500000); + pango_layout_get_size(layout, &width, &height); + g_object_set_data_full(G_OBJECT(tipwindow), "tooltip-plugin", layout, g_object_unref); + + if (w) + *w = PANGO_PIXELS(width) + 12; + if (h) + *h = PANGO_PIXELS(height) + 12; + + g_free(markup); + g_free(jid); + g_free(name); + g_free(desc); + + return TRUE; +} + static void pidgin_disco_create_tree(PidginDiscoList *pdl) { GtkCellRenderer *text_renderer, *pixbuf_renderer; @@ -348,7 +527,7 @@ column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, _("Name")); - gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE); + gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE); gtk_tree_view_column_set_attributes(column, pixbuf_renderer, "pixbuf", PIXBUF_COLUMN, NULL); @@ -372,7 +551,13 @@ gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(pdl->tree), column); + g_signal_connect(G_OBJECT(pdl->tree), "button-press-event", G_CALLBACK(service_click_cb), pdl); g_signal_connect(G_OBJECT(pdl->tree), "row-expanded", G_CALLBACK(row_expanded_cb), pdl); + g_signal_connect(G_OBJECT(pdl->tree), "row-activated", G_CALLBACK(row_activated_cb), pdl); + + pidgin_tooltip_setup_for_treeview(pdl->tree, pdl, + disco_create_tooltip, + disco_paint_tooltip); } void pidgin_disco_signed_off_cb(PurpleConnection *pc) @@ -464,13 +649,15 @@ gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); /* stop button */ - dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, - G_CALLBACK(stop_button_cb), dialog); + dialog->stop_button = + pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, + G_CALLBACK(stop_button_cb), dialog); gtk_widget_set_sensitive(dialog->stop_button, FALSE); /* browse button */ - dialog->browse_button = pidgin_pixbuf_button_from_stock(_("_Browse"), GTK_STOCK_REFRESH, - PIDGIN_BUTTON_HORIZONTAL); + dialog->browse_button = + pidgin_pixbuf_button_from_stock(_("_Browse"), GTK_STOCK_REFRESH, + PIDGIN_BUTTON_HORIZONTAL); gtk_box_pack_start(GTK_BOX(bbox), dialog->browse_button, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(dialog->browse_button), "clicked", G_CALLBACK(browse_button_cb), dialog); @@ -478,22 +665,25 @@ gtk_widget_show(dialog->browse_button); /* register button */ - dialog->register_button = pidgin_dialog_add_button(GTK_DIALOG(dialog->window), _("Register"), - G_CALLBACK(register_button_cb), dialog); + dialog->register_button = + pidgin_dialog_add_button(GTK_DIALOG(dialog->window), _("Register"), + G_CALLBACK(register_button_cb), dialog); gtk_widget_set_sensitive(dialog->register_button, FALSE); /* add button */ - dialog->add_button = pidgin_pixbuf_button_from_stock(_("_Add"), GTK_STOCK_ADD, - PIDGIN_BUTTON_HORIZONTAL); + dialog->add_button = + pidgin_pixbuf_button_from_stock(_("_Add"), GTK_STOCK_ADD, + PIDGIN_BUTTON_HORIZONTAL); gtk_box_pack_start(GTK_BOX(bbox), dialog->add_button, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(dialog->add_button), "clicked", - G_CALLBACK(add_room_to_blist_cb), dialog); + G_CALLBACK(add_to_blist_cb), dialog); gtk_widget_set_sensitive(dialog->add_button, FALSE); gtk_widget_show(dialog->add_button); /* close button */ - dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, - G_CALLBACK(close_button_cb), dialog); + dialog->close_button = + pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, + G_CALLBACK(close_button_cb), dialog); /* show the dialog window and return the dialog */ gtk_widget_show(dialog->window); @@ -505,7 +695,6 @@ { PidginDiscoDialog *dialog; GtkTreeIter iter, parent_iter, child; - char *filename = NULL; GdkPixbuf *pixbuf = NULL; gboolean append = TRUE; @@ -563,21 +752,7 @@ gtk_tree_path_free(path); } - if (service->type == XMPP_DISCO_SERVICE_TYPE_GATEWAY && service->gateway_type) { - char *tmp = g_strconcat(service->gateway_type, ".png", NULL); - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "16", tmp, NULL); - g_free(tmp); -#if 0 - } else if (service->type == XMPP_DISCO_SERVICE_TYPE_USER) { - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "16", "person.png", NULL); -#endif - } else if (service->type == XMPP_DISCO_SERVICE_TYPE_CHAT) - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "16", "chat.png", NULL); - - if (filename) { - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - g_free(filename); - } + pixbuf = pidgin_disco_load_icon(service, "16"); gtk_tree_store_set(pdl->model, &iter, PIXBUF_COLUMN, pixbuf,
--- a/pidgin/plugins/disco/gtkdisco.h Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/plugins/disco/gtkdisco.h Sat Jun 27 17:50:49 2009 +0000 @@ -39,6 +39,7 @@ GtkWidget *register_button; GtkWidget *add_button; GtkWidget *close_button; + XmppDiscoService *selected; PurpleAccount *account; PidginDiscoList *discolist;
--- a/pidgin/plugins/disco/xmppdisco.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/plugins/disco/xmppdisco.c Sat Jun 27 17:50:49 2009 +0000 @@ -56,8 +56,8 @@ static gboolean iq_listening = FALSE; typedef void (*XmppIqCallback)(PurpleConnection *pc, const char *type, - const char *id, const char *from, xmlnode *iq, - gpointer data); + const char *id, const char *from, xmlnode *iq, + gpointer data); struct xmpp_iq_cb_data { @@ -149,7 +149,7 @@ g_hash_table_insert(iq_callbacks, id, cbdata); if (!iq_listening) { - PurplePlugin *prpl = purple_plugins_find_with_id(XMPP_PLUGIN_ID); + PurplePlugin *prpl = purple_plugins_find_with_id(XMPP_PLUGIN_ID); iq_listening = TRUE; purple_signal_connect(prpl, "jabber-receiving-iq", my_plugin, PURPLE_CALLBACK(xmpp_iq_received), NULL); @@ -245,30 +245,30 @@ } static const struct { - const char *from; - const char *to; + const char *from; + const char *to; } disco_type_mappings[] = { - { "gadu-gadu", "gadu-gadu" }, /* the prpl is prpl-gg, but list_icon returns "gadu-gadu" */ - { "sametime", "meanwhile" }, - { "myspaceim", "myspace" }, - { "xmpp", "jabber" }, /* prpl-jabber (mentioned in case the prpl is renamed so this line will match) */ - { NULL, NULL } + { "gadu-gadu", "gadu-gadu" }, /* the prpl is prpl-gg, but list_icon returns "gadu-gadu" */ + { "sametime", "meanwhile" }, + { "myspaceim", "myspace" }, + { "xmpp", "jabber" }, /* prpl-jabber (mentioned in case the prpl is renamed so this line will match) */ + { NULL, NULL } }; static const gchar * disco_type_from_string(const gchar *str) { - int i = 0; + int i = 0; - g_return_val_if_fail(str != NULL, ""); + g_return_val_if_fail(str != NULL, ""); - for ( ; disco_type_mappings[i].from; ++i) { - if (!strcasecmp(str, disco_type_mappings[i].from)) - return disco_type_mappings[i].to; - } + for ( ; disco_type_mappings[i].from; ++i) { + if (!strcasecmp(str, disco_type_mappings[i].from)) + return disco_type_mappings[i].to; + } - /* fallback to the string itself */ - return str; + /* fallback to the string itself */ + return str; } static void @@ -294,7 +294,7 @@ service->list = item_data->list; purple_debug_info("xmppdisco", "parent for %s is %p\n", from, item_data->parent); service->parent = item_data->parent; - service->flags = XMPP_DISCO_ADD; + service->flags = 0; service->type = disco_service_type_from_identity(identity); if (item_data->node) { @@ -312,6 +312,10 @@ } else service->name = g_strdup(from); + if (!service->node) + /* Only support adding JIDs, not JID+node combos */ + service->flags |= XMPP_DISCO_ADD; + if (item_data->name) { service->description = item_data->name; item_data->name = NULL; @@ -607,13 +611,13 @@ static gboolean plugin_load(PurplePlugin *plugin) { - PurplePlugin *xmpp_prpl; + PurplePlugin *xmpp_prpl; my_plugin = plugin; - xmpp_prpl = purple_plugins_find_with_id(XMPP_PLUGIN_ID); - if (NULL == xmpp_prpl) - return FALSE; + xmpp_prpl = purple_plugins_find_with_id(XMPP_PLUGIN_ID); + if (NULL == xmpp_prpl) + return FALSE; purple_signal_connect(purple_connections_get_handle(), "signing-off", plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
--- a/pidgin/plugins/disco/xmppdisco.h Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/plugins/disco/xmppdisco.h Sat Jun 27 17:50:49 2009 +0000 @@ -40,22 +40,22 @@ */ typedef enum { - XMPP_DISCO_SERVICE_TYPE_UNSET, - /** - * A registerable gateway to another protocol. An example would be - * XMPP legacy transports. - */ - XMPP_DISCO_SERVICE_TYPE_GATEWAY, + XMPP_DISCO_SERVICE_TYPE_UNSET, + /** + * A registerable gateway to another protocol. An example would be + * XMPP legacy transports. + */ + XMPP_DISCO_SERVICE_TYPE_GATEWAY, - /** - * A directory (e.g. allows the user to search for other users). - */ - XMPP_DISCO_SERVICE_TYPE_DIRECTORY, + /** + * A directory (e.g. allows the user to search for other users). + */ + XMPP_DISCO_SERVICE_TYPE_DIRECTORY, - /** - * A chat (multi-user conversation). - */ - XMPP_DISCO_SERVICE_TYPE_CHAT, + /** + * A chat (multi-user conversation). + */ + XMPP_DISCO_SERVICE_TYPE_CHAT, /** * A pubsub collection (contains nodes) @@ -68,9 +68,9 @@ XMPP_DISCO_SERVICE_TYPE_PUBSUB_LEAF, /** - * Something else. Do we need more categories? - */ - XMPP_DISCO_SERVICE_TYPE_OTHER + * Something else. Do we need more categories? + */ + XMPP_DISCO_SERVICE_TYPE_OTHER } XmppDiscoServiceType; /** @@ -78,10 +78,10 @@ */ typedef enum { - XMPP_DISCO_NONE = 0x0000, - XMPP_DISCO_ADD = 0x0001, /**< Supports an 'add' operation */ - XMPP_DISCO_BROWSE = 0x0002, /**< Supports browsing */ - XMPP_DISCO_REGISTER = 0x0004 /**< Supports a 'register' operation */ + XMPP_DISCO_NONE = 0x0000, + XMPP_DISCO_ADD = 0x0001, /**< Supports an 'add' operation */ + XMPP_DISCO_BROWSE = 0x0002, /**< Supports browsing */ + XMPP_DISCO_REGISTER = 0x0004 /**< Supports a 'register' operation */ } XmppDiscoServiceFlags; struct _XmppDiscoService {
--- a/pidgin/plugins/gevolution/gevolution.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/plugins/gevolution/gevolution.c Sat Jun 27 17:50:49 2009 +0000 @@ -39,6 +39,7 @@ #include <libedata-book/Evolution-DataServer-Addressbook.h> #include <libedata-book/e-data-book-factory.h> +/* TODO: bonobo is going away eventually, we'll need to find an alternative */ #include <bonobo/bonobo-main.h> #include <glib.h>
--- a/pidgin/plugins/pidginrc.c Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/plugins/pidginrc.c Sat Jun 27 17:50:49 2009 +0000 @@ -34,7 +34,7 @@ "/plugins/gtk/purplerc/color/GtkIMHtml::receive-name-color", "/plugins/gtk/purplerc/color/GtkIMHtml::highlight-name-color", "/plugins/gtk/purplerc/color/GtkIMHtml::action-name-color", - "/plugins/gtk/purplerc/color/GtkIMHtml::typing-ntofication-color" + "/plugins/gtk/purplerc/color/GtkIMHtml::typing-notification-color" }; static const gchar *color_prefs_set[] = { "/plugins/gtk/purplerc/set/color/GtkIMHtml::hyperlink-color",
--- a/pidgin/win32/nsis/pidgin-installer.nsi Sat Jun 27 17:50:35 2009 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Sat Jun 27 17:50:49 2009 +0000 @@ -773,6 +773,7 @@ Delete "$INSTDIR\plugins\win2ktrans.dll" Delete "$INSTDIR\plugins\winprefs.dll" Delete "$INSTDIR\plugins\xmppconsole.dll" + Delete "$INSTDIR\plugins\xmppdisco.dll" RMDir "$INSTDIR\plugins" RMDir /r "$INSTDIR\sasl2" Delete "$INSTDIR\sounds\purple\alert.wav"
--- a/po/ca.po Sat Jun 27 17:50:35 2009 +0000 +++ b/po/ca.po Sat Jun 27 17:50:49 2009 +0000 @@ -33,8 +33,8 @@ msgstr "" "Project-Id-Version: Pidgin\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-06-06 22:44+0200\n" -"PO-Revision-Date: 2009-06-06 22:51+0200\n" +"POT-Creation-Date: 2009-06-25 21:33+0200\n" +"PO-Revision-Date: 2009-06-25 21:44+0200\n" "Last-Translator: Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>\n" "Language-Team: Catalan <tradgnome@softcatala.net>\n" "MIME-Version: 1.0\n" @@ -4254,8 +4254,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 "" +# FIX +msgid "Malformed BOSH URL" +msgstr "L'URL BOSH està malmès" #, c-format msgid "Registration of %s@%s successful" @@ -4323,6 +4324,9 @@ msgid "Change Registration" msgstr "Canvia el registre" +msgid "Malformed BOSH Connect Server" +msgstr "La connexió al servidor BOSH està malmesa" + msgid "Error unregistering account" msgstr "S'ha produït un error en cancel·lar el registre" @@ -4460,7 +4464,7 @@ # FIX msgid "Malformed XMPP ID" -msgstr "l'ID de l'XMPP està malmès" +msgstr "L'ID de l'XMPP està malmès" msgid "Not Acceptable" msgstr "No és acceptable" @@ -4767,6 +4771,9 @@ msgid "File transfer proxies" msgstr "Servidors intermediari per a la transferència de fitxers" +msgid "BOSH URL" +msgstr "URL BOSH" + #. this should probably be part of global smiley theme settings later on, #. shared with MSN msgid "Show Custom Smileys" @@ -5524,32 +5531,6 @@ msgid "The following users are missing from your addressbook" msgstr "Manquen aquests usuaris a la vostra llista d'amics" -#, c-format -msgid "Unable to add user on %s (%s)" -msgstr "No s'ha pogut afegir l'usuari a %s (%s)" - -#, c-format -msgid "Unable to block user on %s (%s)" -msgstr "No s'ha pogut blocar l'usuari %s (%s)" - -#, c-format -msgid "Unable to permit user on %s (%s)" -msgstr "No s'ha pogut permetre l'usuari %s (%s)" - -#, c-format -msgid "%s could not be added because your buddy list is full." -msgstr "No s'ha pogut afegir %s perquè la llista és plena." - -#, c-format -msgid "%s is not a valid passport account." -msgstr "%s no és un compte de passaport vàlid." - -msgid "Service Temporarily Unavailable." -msgstr "El servei no està disponible temporalment." - -msgid "Unknown error." -msgstr "Error desconegut." - msgid "Mobile message was not sent because it was too long." msgstr "No s'ha enviat el missatge al mòbil perquè era massa llarg." @@ -5645,6 +5626,9 @@ "La llista d'amics MSN està temporalment no disponible. Espereu i proveu-ho " "més tard." +msgid "Unknown error." +msgstr "Error desconegut." + msgid "Handshaking" msgstr "S'està comprovant la conformitat de connexió" @@ -5746,6 +5730,29 @@ msgid "%s on %s (%s)" msgstr "%s a %s (%s)" +#, c-format +msgid "Unable to add user on %s (%s)" +msgstr "No s'ha pogut afegir l'usuari a %s (%s)" + +#, c-format +msgid "Unable to block user on %s (%s)" +msgstr "No s'ha pogut blocar l'usuari %s (%s)" + +#, c-format +msgid "Unable to permit user on %s (%s)" +msgstr "No s'ha pogut permetre l'usuari %s (%s)" + +#, c-format +msgid "%s could not be added because your buddy list is full." +msgstr "No s'ha pogut afegir %s perquè la llista és plena." + +#, c-format +msgid "%s is not a valid passport account." +msgstr "%s no és un compte de passaport vàlid." + +msgid "Service Temporarily Unavailable." +msgstr "El servei no està disponible temporalment." + msgid "Unable to rename group" msgstr "No s'ha pogut canviar el nom del grup" @@ -5843,14 +5850,16 @@ #, c-format msgid "" -"%s Your password is %d characters, greater than the expected maximum length " -"of %d for MySpaceIM. Please shorten your password at http://profileedit." -"myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try " -"again." -msgstr "" -"%s La vostra contrasenya conté %d caràcters, més del màxim de %d que permet " -"MySpaceIM. Escolliu una contrasenya més curta a http://profileedit.myspace." -"com/index.cfm?fuseaction=accountSettings.changePassword i proveu-ho de nou." +"%s Your password is %zu characters, which is longer than the maximum length " +"of %d. Please shorten your password at http://profileedit.myspace.com/index." +"cfm?fuseaction=accountSettings.changePassword and try again." +msgstr "" +"%s La vostra contrasenya conté %zu caràcters, més %d que és el màxim permès. " +"Escolliu una contrasenya més curta a http://profileedit.myspace.com/index." +"cfm?fuseaction=accountSettings.changePassword i proveu-ho de nou." + +msgid "Incorrect username or password" +msgstr "El sobrenom o la contrasenya no són correctes" msgid "MySpaceIM Error" msgstr "S'ha produït un error en el MySpaceIM" @@ -6174,9 +6183,6 @@ msgid "Master archive is misconfigured" msgstr "L'arxiu mestre està desconfigurat" -msgid "Incorrect username or password" -msgstr "El sobrenom o la contrasenya no són correctes" - msgid "Could not recognize the host of the username you entered" msgstr "No s'ha pogut reconèixer l'ordinador del nom d'usuari que heu entrat" @@ -6707,6 +6713,31 @@ "començar amb una lletra i contenir només lletres, nombres o espais, o només " "nombres." +#, c-format +msgid "You may be disconnected shortly. If so, check %s for updates." +msgstr "" +"Pot ser que es desconnecti d'aquí a poc. Si això passés, comproveu si hi ha " +"actualitzacions a %s." + +# FIXME: hash (josep) +msgid "Unable to get a valid AIM login hash." +msgstr "No s'ha pogut obtenir un hash d'AIM d'entrada vàlid." + +#, c-format +msgid "You may be disconnected shortly. Check %s for updates." +msgstr "" +"Se us pot desconnectar d'aquí a poc temps. Comproveu si hi ha " +"actualitzacions a %s." + +msgid "Unable to get a valid login hash." +msgstr "No s'ha pogut obtenir un hash d'entrada vàlid." + +msgid "Could Not Connect" +msgstr "No s'ha pogut connectar" + +msgid "Received authorization" +msgstr "S'ha rebut l'autorització" + #. Unregistered username #. uid is not exist msgid "Invalid username." @@ -6725,7 +6756,6 @@ "El servei de missatges instantanis d'AOL no està disponible temporalment." #. username connecting too frequently -#. IP address connecting too frequently 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." @@ -6738,11 +6768,14 @@ msgid "The client version you are using is too old. Please upgrade at %s" msgstr "La versió del client que useu és massa antiga, actualitzeu-la a %s" -msgid "Could Not Connect" -msgstr "No s'ha pogut connectar" - -msgid "Received authorization" -msgstr "S'ha rebut l'autorització" +#. IP address connecting too frequently +msgid "" +"You have been connecting and disconnecting too frequently. Wait a minute and " +"try again. If you continue to try, you will need to wait even longer." +msgstr "" +"Heu estat connectant-vos i desconnectat-vos amb massa freqüència. Espereu un " +"minut i intenteu-ho de nou. Si continueu intentant-ho, haureu d'esperar " +"encara més temps." msgid "The SecurID key entered is invalid." msgstr "La clau SecurID que heu entrat no és vàlida." @@ -6759,25 +6792,6 @@ msgid "_OK" msgstr "_D'acord" -#, c-format -msgid "You may be disconnected shortly. If so, check %s for updates." -msgstr "" -"Pot ser que es desconnecti d'aquí a poc. Si això passés, comproveu si hi ha " -"actualitzacions a %s." - -# FIXME: hash (josep) -msgid "Unable to get a valid AIM login hash." -msgstr "No s'ha pogut obtenir un hash d'AIM d'entrada vàlid." - -#, c-format -msgid "You may be disconnected shortly. Check %s for updates." -msgstr "" -"Se us pot desconnectar d'aquí a poc temps. Comproveu si hi ha " -"actualitzacions a %s." - -msgid "Unable to get a valid login hash." -msgstr "No s'ha pogut obtenir un hash d'entrada vàlid." - msgid "Password sent" msgstr "S'ha enviat la contrasenya" @@ -7282,6 +7296,7 @@ msgid "Set User Info (web)..." msgstr "Estableix informació d'usuari (web)..." +#. This only happens when connecting with the old-style BUCP login msgid "Change Password (web)" msgstr "Canvia la contrasenya (web)" @@ -7311,6 +7326,9 @@ msgid "Search for Buddy by Information" msgstr "Cerca un amic per la informació" +msgid "Use clientLogin" +msgstr "Empra clientLogin" + msgid "" "Always use AIM/ICQ proxy server for\n" "file transfers and direct IM (slower,\n"
--- a/po/de.po Sat Jun 27 17:50:35 2009 +0000 +++ b/po/de.po Sat Jun 27 17:50:49 2009 +0000 @@ -11,9 +11,9 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-06-01 12:31+0200\n" -"PO-Revision-Date: 2009-06-01 12:31+0200\n" -"Last-Translator: Björn Voigt <bjoern@cs.tu-berlin.de>\n" +"POT-Creation-Date: 2009-06-11 12:42+0200\n" +"PO-Revision-Date: 2009-06-11 12:40+0200\n" +"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n" "Language-Team: Deutsch <de@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -877,7 +877,7 @@ msgstr "System-Mitschnitt" msgid "Calling ... " -msgstr "Anrufen..." +msgstr "Anrufen ... " msgid "Hangup" msgstr "Auflegen" @@ -3169,6 +3169,10 @@ msgid "Chat _name:" msgstr "Chat_name:" +#. should this be a settings error? +msgid "Unable to resolve server" +msgstr "Verbindung zum Server nicht möglich" + msgid "Chat error" msgstr "Chatfehler" @@ -3217,6 +3221,10 @@ msgid "Gadu-Gadu User" msgstr "Gadu-Gadu-Benutzer" +#, fuzzy +msgid "GG server" +msgstr "Hole server" + #, c-format msgid "Unknown command: %s" msgstr "Unbekanntes Kommando: %s" @@ -3463,7 +3471,7 @@ #. notify the user that their /nick command didn't go. #, c-format msgid "The nickname \"%s\" is already being used." -msgstr "Der Spitzname \"%s\" existiert bereits." +msgstr "Der Spitzname „%s“ existiert bereits." #, fuzzy msgid "Nickname in use" @@ -4215,9 +4223,8 @@ msgid "Invalid XMPP ID. Domain must be set." msgstr "Falsche XMPP-ID. Die Domain muss gesetzt werden." -#, fuzzy msgid "Malformed BOSH Connect Server" -msgstr "Verbindung zum Server nicht möglich." +msgstr "" #, c-format msgid "Registration of %s@%s successful" @@ -4243,9 +4250,6 @@ msgid "Unregistration Failed" msgstr "Aufheben der Registrierung gescheitert" -msgid "Already Registered" -msgstr "Schon registriert" - msgid "State" msgstr "Provinz/Bundesland" @@ -4258,6 +4262,9 @@ msgid "Date" msgstr "Datum" +msgid "Already Registered" +msgstr "Schon registriert" + msgid "Unregister" msgstr "Aufheben der Registrierung" @@ -4631,7 +4638,7 @@ "session." msgstr "" "Bitte wählen Sie die Ressource von %s, mir der Sie eine Medien-Sitzung " -"starten möchten" +"starten möchten." msgid "Select a Resource" msgstr "Wählen Sie eine Ressource" @@ -4795,12 +4802,11 @@ msgid "Error in chat %s" msgstr "Fehler im Chat %s" -#, fuzzy msgid "An error occured on the in-band bytestream transfer\n" -msgstr "Beim Öffnen der Datei trat ein Fehler auf." +msgstr "Beim Übertragen der Datei trat ein Fehler auf\n" msgid "Transfer was closed." -msgstr "Übertragung wurde geschlossen" +msgstr "Übertragung wurde geschlossen." msgid "Failed to open the file" msgstr "Öffnen der Datei fehlgeschlagen" @@ -5441,32 +5447,6 @@ 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" - -#, c-format -msgid "Unable to block user on %s (%s)" -msgstr "Kann den Benutzer nicht für %s (%s) blockieren" - -#, c-format -msgid "Unable to permit user on %s (%s)" -msgstr "Kann den Benutzer nicht für %s (%s) erlauben" - -#, c-format -msgid "%s could not be added because your buddy list is full." -msgstr "%s konnte nicht hinzugefügt werden, da Ihre Buddy-Liste voll ist." - -#, c-format -msgid "%s is not a valid passport account." -msgstr "%s ist kein gültiges Passport-Konto." - -msgid "Service Temporarily Unavailable." -msgstr "Dienst momentan nicht verfügbar." - -msgid "Unknown error." -msgstr "Unbekannter Fehler." - msgid "Mobile message was not sent because it was too long." msgstr "Mobile Nachricht wurde nicht gesendet, da sie zu lang war." @@ -5566,6 +5546,9 @@ "Ihre MSN-Buddy-Liste ist temporär nicht verfügbar. Bitte warten Sie und " "versuchen Sie es später nochmal." +msgid "Unknown error." +msgstr "Unbekannter Fehler." + msgid "Handshaking" msgstr "Abgleich" @@ -5669,6 +5652,29 @@ msgid "%s on %s (%s)" msgstr "%s auf %s (%s)" +#, c-format +msgid "Unable to add user on %s (%s)" +msgstr "Kann den Benutzer nicht zu %s (%s) hinzufügen" + +#, c-format +msgid "Unable to block user on %s (%s)" +msgstr "Kann den Benutzer nicht für %s (%s) blockieren" + +#, c-format +msgid "Unable to permit user on %s (%s)" +msgstr "Kann den Benutzer nicht für %s (%s) erlauben" + +#, c-format +msgid "%s could not be added because your buddy list is full." +msgstr "%s konnte nicht hinzugefügt werden, da Ihre Buddy-Liste voll ist." + +#, c-format +msgid "%s is not a valid passport account." +msgstr "%s ist kein gültiges Passport-Konto." + +msgid "Service Temporarily Unavailable." +msgstr "Dienst momentan nicht verfügbar." + msgid "Unable to rename group" msgstr "Kann die Gruppe nicht umbenennen" @@ -6783,7 +6789,7 @@ msgstr "Möchten Sie diesen Buddy zu Ihrer Buddy-Liste hinzufügen?" msgid "_Add" -msgstr "_Hinzufügen" +msgstr "Hinzu_fügen" msgid "_Decline" msgstr "_Ablehnen" @@ -9568,16 +9574,26 @@ msgstr "Falsches Passwort" #. security lock from too many failed login attempts -msgid "Account locked: Too many failed login attempts" -msgstr "Konto gesperrt: Zu viele erfolglose Login-Versuche" +#, fuzzy +msgid "" +"Account locked: Too many failed login attempts.\n" +"Logging into the Yahoo! website may fix this." +msgstr "" +"Unbekannte Fehlernummer %d. Vielleicht kann dies repariert werden, wenn Sie " +"sich auf der Yahoo! Webseite anmelden." #. the username does not exist msgid "Username does not exist" msgstr "Benutzername existiert nicht" #. indicates a lock of some description -msgid "Account locked: See the debug log" -msgstr "Konto gesperrt: Sehen Sie in den Debug-Mitschnitt" +#, fuzzy +msgid "" +"Account locked: Unknown reason.\n" +"Logging into the Yahoo! website may fix this." +msgstr "" +"Unbekannte Fehlernummer %d. Vielleicht kann dies repariert werden, wenn Sie " +"sich auf der Yahoo! Webseite anmelden." #. username or password missing msgid "Username or password missing" @@ -10492,9 +10508,8 @@ msgid "Please update the necessary fields." msgstr "Bitte aktualisieren Sie die erforderlichen Felder." -#, fuzzy msgid "A_ccount" -msgstr "Konto" +msgstr "K_onto" msgid "" "Please enter the appropriate information about the chat you would like to " @@ -11646,7 +11661,7 @@ msgstr "_Name" msgid "_Account" -msgstr "_Konto" +msgstr "K_onto" msgid "Get User Info" msgstr "Benutzer-Info abrufen" @@ -12400,19 +12415,19 @@ msgstr "Bei wem alarmieren" msgid "_Account:" -msgstr "_Konto:" +msgstr "K_onto:" msgid "_Buddy name:" -msgstr "_Buddy-Name:" +msgstr "Budd_y-Name:" msgid "Si_gns on" -msgstr "_sich anmeldet" +msgstr "si_ch anmeldet" msgid "Signs o_ff" msgstr "sich abmel_det" msgid "Goes a_way" -msgstr "_hinausgeht" +msgstr "hinausgeh_t" msgid "Ret_urns from away" msgstr "wi_eder anwesend ist" @@ -12421,22 +12436,22 @@ msgstr "_untätig wird" msgid "Is no longer i_dle" -msgstr "nicht meh_r untätig ist" +msgstr "nicht _mehr untätig ist" msgid "Starts _typing" -msgstr "zu _tippen beginnt" +msgstr "zu tippen _beginnt" msgid "P_auses while typing" -msgstr "beim Tippen an_hält" +msgstr "beim Tippen anh_ält" msgid "Stops t_yping" msgstr "aufhört _zu tippen" msgid "Sends a _message" -msgstr "eine _Nachricht sendet" +msgstr "eine Nachr_icht sendet" msgid "Ope_n an IM window" -msgstr "Gesprächsfenster ö_ffnen" +msgstr "_Gesprächsfenster öffnen" msgid "_Pop up a notification" msgstr "_Popup-Benachrichtigung" @@ -12445,22 +12460,22 @@ msgstr "_Nachricht senden" msgid "E_xecute a command" -msgstr "Befeh_l ausführen" +msgstr "Befehl ausfüh_ren" msgid "P_lay a sound" -msgstr "einen _Klang abspielen" +msgstr "Einen _Klang abspielen" msgid "Brows_e..." -msgstr "Aus_wählen..." +msgstr "Au_swählen..." msgid "Br_owse..." -msgstr "Aus_wählen..." +msgstr "Auswäh_len..." msgid "Pre_view" msgstr "_Vorschau" msgid "P_ounce only when my status is not Available" -msgstr "Nur _alarmieren, wenn ich nicht verfügbar bin" +msgstr "Nur alarmieren, wenn ich nicht ver_fügbar bin" msgid "_Recurring" msgstr "_Wiederkehrend" @@ -13802,12 +13817,6 @@ "- Es sendet eine Nachricht zu Leuten in Ihrer Buddy Liste, direkt wenn Sie " "sich angemeldet haben" -msgid "Cursor Color" -msgstr "Cursor-Farbe" - -msgid "Secondary Cursor Color" -msgstr "Sekundäre Cursor-Farbe" - msgid "Hyperlink Color" msgstr "Hyperlink-Farbe" @@ -13817,6 +13826,10 @@ msgid "Highlighted Message Name Color" msgstr "Farbe des Absendernamens für hervorgehobene Nachrichten" +#, fuzzy +msgid "Typing Notification Color" +msgstr "Farbe der Tipp-Benachrichtigung" + msgid "GtkTreeView Horizontal Separation" msgstr "GtkTreeview horizontaler Abstand" @@ -13845,35 +13858,24 @@ msgid "GTK+ Text Shortcut Theme" msgstr "GTK+ Text Shortcut-Thema" -#. -#. for (i = 0; i < G_N_ELEMENTS(widget_bool_prefs); i++) { -#. hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); -#. gtk_box_pack_start(GTK_BOX(frame), hbox, FALSE, FALSE, 0); -#. -#. check = pidgin_prefs_checkbox(_(widget_bool_names[i]), -#. widget_bool_prefs_set[i], hbox); -#. gtk_size_group_add_widget(labelsg, check); -#. -#. widget_bool_widgets[i] = pidgin_prefs_checkbox("", widget_bool_prefs[i], hbox); -#. * -#. gtk_size_group_add_widget(widgetsb, widget_bool_widgets[i]); -#. * -#. gtk_widget_set_sensitive(widget_bool_widgets[i], -#. purple_prefs_get_bool(widget_bool_prefs_set[i])); -#. g_signal_connect(G_OBJECT(check), "toggled", -#. G_CALLBACK(pidgin_toggle_sensitive), -#. widget_bool_widgets[i]); -#. } -#. -msgid "Interface colors" -msgstr "UI-Farben" - -msgid "Widget Sizes" -msgstr "Widget-Größen" +#, fuzzy +msgid "Disable Typing Notification Text" +msgstr "Tipp-Benachrichtigung aktivieren" + +#, fuzzy +msgid "GTK+ Theme Control Settings" +msgstr "Pidgin GTK+ Themenkontrolle" + +#, fuzzy +msgid "Colors" +msgstr "Schließen" msgid "Fonts" msgstr "Schrift" +msgid "Miscellaneous" +msgstr "" + msgid "Gtkrc File Tools" msgstr "Gtkrc-Datei-Werkzeuge"