# HG changeset patch # User pieter.loubser@mxit.com # Date 1313074894 0 # Node ID 98ae4b8b592ffae46413ef613fc428891434008d # Parent 3951afa56940a87baf32bc909acdad30eb597963# Parent 727edf2625f59b13db4897e9d963975657cc79a2 propagate from branch 'im.pidgin.pidgin' (head b55fa7e527d663bdb8c0320d6d035488e8446978) to branch 'im.pidgin.pidgin.mxit' (head 62c1402a9d54bdc07737fbefdfeb664eb5f01d81) diff -r 3951afa56940 -r 98ae4b8b592f AUTHORS --- a/AUTHORS Sun Jun 26 21:03:22 2011 +0000 +++ b/AUTHORS Thu Aug 11 15:01:34 2011 +0000 @@ -12,55 +12,58 @@ Paul 'darkrain42' Aurich - Developer John 'rekkanoryo' Bailey - Developer Ethan 'Paco-Paco' Blanton - Developer -Thomas Butter - Developer -Ka-Hing Cheung - Developer Sadrul Habib Chowdhury - Developer Mark 'KingAnt' Doliner - Developer -Sean Egan - Developer Casey Harkins - Developer Ivan Komarov - Developer Gary 'grim' Kramlich - Developer Richard 'rlaager' Laager - Developer +Marcus 'malu' Lundblad - Developer Sulabh 'sulabh_m' Mahajan - Developer Richard 'wabz' Nelson - Developer -Christopher 'siege' O'Brien - Developer -Bartosz Oler - Developer Etan 'deryni' Reisner - Developer -Tim 'marv' Ringenbach - Developer Michael 'Maiku' Ruprecht - Developer, voice and video Elliott 'QuLogic' Sales de Andrade - Developer Luke 'LSchiere' Schierer - Support -Megan 'Cae' Schneider - support/QA Evan Schoenberg - Developer Kevin 'SimGuy' Stange - Developer & Webmaster Will 'resiak' Thompson - Developer Stu 'nosnilmot' Tomlinson - Developer -Nathan 'faceprint' Walp - Developer +Jorge 'Masca' Villaseñor - Developer Crazy Patch Writers: ------------------- -Marcus 'malu' Lundblad -Dennis 'EvilDennisR' Ristuccia +Jakub 'haakon' Adam +Krzysztof Klinikowski Peter 'Fmoo' Ruibal Gabriel 'Nix' Schulhof -Jorge 'Masca' Villaseñor +Tomasz Wasilczyk Retired Developers: ------------------ Herman Bloggs - Win32 Port +Thomas Butter - Developer +Ka-Hing Cheung - Developer Jim Duchek - maintainer +Sean Egan - Developer Rob Flynn - maintainer Adam Fritzler - libfaim maintainer Christian 'ChipX86' Hammond - Developer & Webmaster Syd Logan - hacker and designated driver [lazy bum] +Christopher 'siege' O'Brien - Developer +Bartosz Oler - Developer +Tim 'marv' Ringenbach - Developer +Megan 'Cae' Schneider - support/QA Jim Seymour - XMPP developer Mark Spencer - original author +Nathan 'faceprint' Walp - Developer Eric Warmenhoven - lead developer Retired Crazy Patch Writers: --------------------------- Felipe 'shx' Contreras Decklin Foster +Dennis 'EvilDennisR' Ristuccia - Senior Contributor/QA Peter 'Bleeter' Lawler Robert 'Robot101' McQueen Benjamin Miller diff -r 3951afa56940 -r 98ae4b8b592f COPYRIGHT --- a/COPYRIGHT Sun Jun 26 21:03:22 2011 +0000 +++ b/COPYRIGHT Thu Aug 11 15:01:34 2011 +0000 @@ -15,7 +15,7 @@ piece of code, then that code should be traced through our version control system to see from where it came and who has modified it. -Copyright (C) 1998-2009 by the following: +Copyright (C) 1998-2011 by the following: Saleem Abdulrasool Jakub Adam @@ -71,6 +71,7 @@ Éric Boumaour Chris Boyle Stanislav Brabec +Quentin Brandon Derrick J Brashear Mauro Sérgio Ferreira Brasil Luke Bratch diff -r 3951afa56940 -r 98ae4b8b592f ChangeLog --- a/ChangeLog Sun Jun 26 21:03:22 2011 +0000 +++ b/ChangeLog Thu Aug 11 15:01:34 2011 +0000 @@ -1,23 +1,74 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.8.1 (MM/DD/YYYY): +version 2.10.0 (MM/DD/YYYY): Pidgin: + * Make the max size of incoming smileys a pref instead of hardcoding it. + (Quentin Brandon) (#5231) + * Added a plugin information dialog to show information for plugins + that aren't otherwise visible in the plugins dialog. + * Fix building with GTK+ earlier than 2.14.0 (GTK+ 2.10 is still the + minimum supported) (#14261) + + libpurple: + * Fix a potential crash in the Log Reader plugin when reading QIP logs. + * Fix a large number of strcpy() and strcat() invocations to use + strlcpy() and strlcat(), etc., forestalling an entire class of + string buffer overrun bugs. + (The Electronic Frontier Foundation, Dan Auerbach, Chris Palmer, + Jacob Appelbaum) + + Gadu-Gadu: + * Fixed searching for buddies in public directory. (Tomasz Wasilczyk) + (#5242) + * Better status message handling. (Tomasz Wasilczyk) (#14314) + * Merged two buddy blocking methods. (Tomasz Wasilczyk) (#5303) + * Fix building of the bundled libgadu library with older versions + of GnuTLS. (patch plucked from upstream) (#14365) + + ICQ: + * Fix crash selecting Tools->Set Mood when you're online with an + ICQ account that is configured as an AIM account. (#14437) + + IRC: + * Fix the handling of formatting following mIRC ^O (#14436) + + MSN: + * Fix seemingly random crashing. (#14307) + + XMPP: + * Do not generate malformed XML ("") when setting an empty mood. + (#14342) + * Fix the /join behavior. (Broken when adding support for + @) (#14205) + + Yahoo!/Yahoo! JAPAN: + * Fix coming out of idle while in an unavailable state + * Fix logging into Yahoo! JAPAN. (#14259) + +version 2.9.0 (06/23/2011): + Pidgin: + * Fix a potential remote denial-of-service bug related to displaying + buddy icons. + * Significantly improved performance of larger IRC channels (regression + introduced in 2.8.0). * Fix Conversation->Add on AIM and MSN. + * Entries in the chat user list are sorted properly again. This was + inadvertenly broken in 2.8.0. Finch: - * Fixed logging in to ICQ. + * Fix logging in to ICQ. libpurple: * media: Actually use the specified TCP port from the TURN configuration to create a TCP relay candidate. + AIM and ICQ: + * Fix crashes on some non-mainstream OSes when attempting to + printf("%s", NULL). (Clemens Huebner) (#14297) + Plugins: * The Evolution Integration plugin compiles again. - Gadu-Gadu: - * Fixed searching for buddies in public directory. (Tomasz Wasilczyk) - (#5242) - version 2.8.0 (06/07/2011): General: * Implement simple silence suppression for voice calls, preventing diff -r 3951afa56940 -r 98ae4b8b592f ChangeLog.API --- a/ChangeLog.API Sun Jun 26 21:03:22 2011 +0000 +++ b/ChangeLog.API Thu Aug 11 15:01:34 2011 +0000 @@ -1,5 +1,31 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.10.0: + libpurple: + Added: + * purple_srv_txt_query_destroy (accidentally left out of 2.8.0) + + Pidgin: + Added: + * pidgin_dialogs_plugins_info (should not be used by anything but Pidgin) + +version 2.9.0: + libpurple: + Added: + * Hash table to PurpleConvChat struct, used to make + purple_conv_chat_cb_find O(1). + * ui_data pointer to PurpleConvChatBuddy struct. + * deleting-chat-buddy signal (conversation signals) + * pidgin_pixbuf_from_data + * pidgin_pixbuf_anim_from_data + * pidgin_pixbuf_new_from_file + * pidgin_pixbuf_new_from_file_at_size + * pidgin_pixbuf_new_from_file_at_scale + + Deprecated: + * purple_conv_chat_set_users + * PurpleConvChat in_room list + version 2.8.0 (06/07/2011): libpurple: Added: diff -r 3951afa56940 -r 98ae4b8b592f configure.ac --- a/configure.ac Sun Jun 26 21:03:22 2011 +0000 +++ b/configure.ac Thu Aug 11 15:01:34 2011 +0000 @@ -43,10 +43,10 @@ # # Make sure to update finch/libgnt/configure.ac with libgnt version changes. # -m4_define([purple_lt_current], [8]) +m4_define([purple_lt_current], [10]) m4_define([purple_major_version], [2]) -m4_define([purple_minor_version], [8]) -m4_define([purple_micro_version], [1]) +m4_define([purple_minor_version], [10]) +m4_define([purple_micro_version], [0]) m4_define([purple_version_suffix], [devel]) m4_define([purple_version], [purple_major_version.purple_minor_version.purple_micro_version]) diff -r 3951afa56940 -r 98ae4b8b592f libpurple/conversation.c --- a/libpurple/conversation.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/conversation.c Thu Aug 11 15:01:34 2011 +0000 @@ -70,6 +70,23 @@ g_free(hc); } +static guint _purple_conversation_user_hash(gconstpointer data) +{ + const gchar *name = data; + gchar *collated; + guint hash; + + collated = g_utf8_collate_key(name, -1); + hash = g_str_hash(collated); + g_free(collated); + return hash; +} + +static gboolean _purple_conversation_user_equal(gconstpointer a, gconstpointer b) +{ + return !g_utf8_collate(a, b); +} + void purple_conversations_set_ui_ops(PurpleConversationUiOps *ops) { @@ -393,6 +410,8 @@ conv->u.chat = g_new0(PurpleConvChat, 1); conv->u.chat->conv = conv; + conv->u.chat->users = g_hash_table_new_full(_purple_conversation_user_hash, + _purple_conversation_user_equal, g_free, NULL); PURPLE_DBUS_REGISTER_POINTER(conv->u.chat, PurpleConvChat); chats = g_list_prepend(chats, conv); @@ -547,6 +566,8 @@ conv->u.im = NULL; } else if (conv->type == PURPLE_CONV_TYPE_CHAT) { + g_hash_table_destroy(conv->u.chat->users); + conv->u.chat->users = NULL; g_list_foreach(conv->u.chat->in_room, (GFunc)purple_conv_chat_cb_destroy, NULL); g_list_free(conv->u.chat->in_room); @@ -1677,9 +1698,9 @@ cbuddy = purple_conv_chat_cb_new(user, alias, flag); cbuddy->buddy = purple_find_buddy(conv->account, user) != NULL; - /* This seems dumb. Why should we set users thousands of times? */ - purple_conv_chat_set_users(chat, - g_list_prepend(chat->in_room, cbuddy)); + + chat->in_room = g_list_prepend(chat->in_room, cbuddy); + g_hash_table_replace(chat->users, g_strdup(cbuddy->name), cbuddy); cbuddies = g_list_prepend(cbuddies, cbuddy); @@ -1771,8 +1792,9 @@ flags = purple_conv_chat_user_get_flags(chat, old_user); cb = purple_conv_chat_cb_new(new_user, new_alias, flags); cb->buddy = purple_find_buddy(conv->account, new_user) != NULL; - purple_conv_chat_set_users(chat, - g_list_prepend(chat->in_room, cb)); + + chat->in_room = g_list_prepend(chat->in_room, cb); + g_hash_table_replace(chat->users, g_strdup(cb->name), cb); if (ops != NULL && ops->chat_rename_user != NULL) ops->chat_rename_user(conv, old_user, new_user, new_alias); @@ -1780,8 +1802,8 @@ cb = purple_conv_chat_cb_find(chat, old_user); if (cb) { - purple_conv_chat_set_users(chat, - g_list_remove(chat->in_room, cb)); + chat->in_room = g_list_remove(chat->in_room, cb); + g_hash_table_remove(chat->users, cb->name); purple_conv_chat_cb_destroy(cb); } @@ -1874,8 +1896,8 @@ cb = purple_conv_chat_cb_find(chat, user); if (cb) { - purple_conv_chat_set_users(chat, - g_list_remove(chat->in_room, cb)); + chat->in_room = g_list_remove(chat->in_room, cb); + g_hash_table_remove(chat->users, cb->name); purple_conv_chat_cb_destroy(cb); } @@ -1955,8 +1977,10 @@ purple_conv_chat_cb_destroy(cb); } + g_hash_table_remove_all(chat->users); + g_list_free(users); - purple_conv_chat_set_users(chat, NULL); + chat->in_room = NULL; } @@ -2146,19 +2170,10 @@ PurpleConvChatBuddy * purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name) { - GList *l; - PurpleConvChatBuddy *cb = NULL; - g_return_val_if_fail(chat != NULL, NULL); g_return_val_if_fail(name != NULL, NULL); - for (l = purple_conv_chat_get_users(chat); l; l = l->next) { - cb = l->data; - if (!g_utf8_collate(cb->name, name)) - return cb; - } - - return NULL; + return g_hash_table_lookup(chat->users, name); } void @@ -2167,6 +2182,9 @@ if (cb == NULL) return; + purple_signal_emit(purple_conversations_get_handle(), + "deleting-chat-buddy", cb); + g_free(cb->alias); g_free(cb->alias_key); g_free(cb->name); @@ -2573,6 +2591,11 @@ purple_value_new(PURPLE_TYPE_STRING), purple_value_new(PURPLE_TYPE_STRING)); + purple_signal_register(handle, "deleting-chat-buddy", + purple_marshal_VOID__POINTER, NULL, 1, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_CHATBUDDY)); + purple_signal_register(handle, "chat-inviting-user", purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3, purple_value_new(PURPLE_TYPE_SUBTYPE, diff -r 3951afa56940 -r 98ae4b8b592f libpurple/conversation.h --- a/libpurple/conversation.h Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/conversation.h Thu Aug 11 15:01:34 2011 +0000 @@ -271,7 +271,9 @@ { PurpleConversation *conv; /**< The parent conversation. */ - GList *in_room; /**< The users in the room. */ + GList *in_room; /**< The users in the room. + * @deprecated Will be removed in 3.0.0 + */ GList *ignored; /**< Ignored users. */ char *who; /**< The person who set the topic. */ char *topic; /**< The topic. */ @@ -279,6 +281,9 @@ char *nick; /**< Your nick in this chat. */ gboolean left; /**< We left the chat and kept the window open */ + GHashTable *users; /**< Hash table of the users in the room. + * @since 2.9.0 + */ }; /** @@ -304,6 +309,7 @@ GHashTable *attributes; /**< A hash table of attributes about the user, such as * real name, user@host, etc. */ + gpointer ui_data; /** < The UI can put whatever it wants here. */ }; /** @@ -1065,6 +1071,8 @@ * @param users The list of users. * * @return The list passed. + * + * @deprecated This function will be removed in 3.0.0. You shouldn't be using it anyway. */ GList *purple_conv_chat_set_users(PurpleConvChat *chat, GList *users); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/dnsquery.c --- a/libpurple/dnsquery.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/dnsquery.c Thu Aug 11 15:01:34 2011 +0000 @@ -154,8 +154,27 @@ static gboolean resolve_ip(PurpleDnsQueryData *query_data) { +#if defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST) + struct addrinfo hints, *res; + char servname[20]; + + g_snprintf(servname, sizeof(servname), "%d", query_data->port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags |= AI_NUMERICHOST; + + if (0 == getaddrinfo(query_data->hostname, servname, &hints, &res)) + { + GSList *hosts = NULL; + hosts = g_slist_append(hosts, GINT_TO_POINTER(res->ai_addrlen)); + hosts = g_slist_append(hosts, g_memdup(res->ai_addr, res->ai_addrlen)); + purple_dnsquery_resolved(query_data, hosts); + + freeaddrinfo(res); + return TRUE; + } +#else /* defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST) */ struct sockaddr_in sin; - /* TODO: Use inet_pton for IPv6 support */ if (inet_aton(query_data->hostname, &sin.sin_addr)) { /* @@ -171,6 +190,7 @@ return TRUE; } +#endif return FALSE; } diff -r 3951afa56940 -r 98ae4b8b592f libpurple/dnssrv.c --- a/libpurple/dnssrv.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/dnssrv.c Thu Aug 11 15:01:34 2011 +0000 @@ -250,6 +250,52 @@ return list; } +static PurpleSrvTxtQueryData * +query_data_new(int type, gchar *query, gpointer extradata) +{ + PurpleSrvTxtQueryData *query_data = g_new0(PurpleSrvTxtQueryData, 1); + query_data->type = type; + query_data->extradata = extradata; + query_data->query = query; +#ifndef _WIN32 + query_data->fd_in = -1; + query_data->fd_out = -1; +#endif + return query_data; +} + +void +purple_srv_txt_query_destroy(PurpleSrvTxtQueryData *query_data) +{ + PurpleSrvTxtQueryUiOps *ops = purple_srv_txt_query_get_ui_ops(); + + if (ops && ops->destroy) + ops->destroy(query_data); + + if (query_data->handle > 0) + purple_input_remove(query_data->handle); +#ifdef _WIN32 + if (query_data->resolver != NULL) + { + /* + * It's not really possible to kill a thread. So instead we + * just set the callback to NULL and let the DNS lookup + * finish. + */ + query_data->cb.srv = NULL; + return; + } + g_free(query_data->error_message); +#else + if (query_data->fd_out != -1) + close(query_data->fd_out); + if (query_data->fd_in != -1) + close(query_data->fd_in); +#endif + g_free(query_data->query); + g_free(query_data); +} + #ifdef USE_IDN static gboolean dns_str_is_ascii(const char *name) @@ -523,7 +569,7 @@ } waitpid(query_data->pid, &status, 0); - purple_srv_cancel(query_data); + purple_srv_txt_query_destroy(query_data); } #else /* _WIN32 */ @@ -583,7 +629,7 @@ query_data->resolver = NULL; query_data->handle = 0; - purple_srv_cancel(query_data); + purple_srv_txt_query_destroy(query_data); return FALSE; } @@ -730,13 +776,10 @@ purple_debug_info("dnssrv","querying SRV record for %s: %s\n", domain, query); g_free(hostname); - - query_data = g_new0(PurpleSrvTxtQueryData, 1); - query_data->type = PurpleDnsTypeSrv; + + query_data = query_data_new(PurpleDnsTypeSrv, query, extradata); query_data->cb.srv = cb; - query_data->extradata = extradata; - query_data->query = query; - + if (purple_srv_txt_query_ui_resolve(query_data)) { return query_data; @@ -746,6 +789,7 @@ if(pipe(in) || pipe(out)) { purple_debug_error("dnssrv", "Could not create pipe\n"); g_free(query); + g_free(query_data); cb(NULL, 0, extradata); return NULL; } @@ -753,8 +797,9 @@ pid = fork(); if (pid == -1) { purple_debug_error("dnssrv", "Could not create process!\n"); + g_free(query); + g_free(query_data); cb(NULL, 0, extradata); - g_free(query); return NULL; } @@ -762,6 +807,7 @@ if (pid == 0) { g_free(query); + g_free(query_data); close(out[0]); close(in[1]); @@ -784,8 +830,6 @@ query_data->fd_in = in[1]; query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data); - g_free(query); - return query_data; #else if (!initialized) { @@ -862,13 +906,10 @@ purple_debug_info("dnssrv","querying TXT record for %s: %s\n", domain, query); g_free(hostname); - - query_data = g_new0(PurpleSrvTxtQueryData, 1); - query_data->type = PurpleDnsTypeTxt; + + query_data = query_data_new(PurpleDnsTypeTxt, query, extradata); query_data->cb.txt = cb; - query_data->extradata = extradata; - query_data->query = query; - + if (purple_srv_txt_query_ui_resolve(query_data)) { /* query intentionally not freed */ @@ -879,6 +920,7 @@ if(pipe(in) || pipe(out)) { purple_debug_error("dnssrv", "Could not create pipe\n"); g_free(query); + g_free(query_data); cb(NULL, extradata); return NULL; } @@ -886,8 +928,9 @@ pid = fork(); if (pid == -1) { purple_debug_error("dnssrv", "Could not create process!\n"); + g_free(query); + g_free(query_data); cb(NULL, extradata); - g_free(query); return NULL; } @@ -895,6 +938,7 @@ if (pid == 0) { g_free(query); + g_free(query_data); close(out[0]); close(in[1]); @@ -911,14 +955,12 @@ if (write(in[1], &internal_query, sizeof(internal_query)) < 0) purple_debug_error("dnssrv", "Could not write to TXT resolver\n"); - + query_data->pid = pid; query_data->fd_out = out[0]; query_data->fd_in = in[1]; query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data); - g_free(query); - return query_data; #else if (!initialized) { @@ -949,39 +991,15 @@ } void -purple_srv_cancel(PurpleSrvTxtQueryData *query_data) +purple_txt_cancel(PurpleSrvTxtQueryData *query_data) { - PurpleSrvTxtQueryUiOps *ops = purple_srv_txt_query_get_ui_ops(); - - if (ops && ops->destroy) - ops->destroy(query_data); - - if (query_data->handle > 0) - purple_input_remove(query_data->handle); -#ifdef _WIN32 - if (query_data->resolver != NULL) - { - /* - * It's not really possible to kill a thread. So instead we - * just set the callback to NULL and let the DNS lookup - * finish. - */ - query_data->cb.srv = NULL; - return; - } - g_free(query_data->query); - g_free(query_data->error_message); -#else - close(query_data->fd_out); - close(query_data->fd_in); -#endif - g_free(query_data); + purple_srv_txt_query_destroy(query_data); } void -purple_txt_cancel(PurpleSrvTxtQueryData *query_data) +purple_srv_cancel(PurpleSrvTxtQueryData *query_data) { - purple_srv_cancel(query_data); + purple_srv_txt_query_destroy(query_data); } const gchar * @@ -1006,12 +1024,41 @@ static void purple_srv_query_resolved(PurpleSrvTxtQueryData *query_data, GList *records) { + GList *l; + PurpleSrvResponse *records_array; + int i = 0, length; + g_return_if_fail(records != NULL); - - purple_debug_info("dnssrv", "SRV records resolved for %s, count: %d\n", query_data->query, g_list_length(records)); - - if (query_data->cb.srv != NULL) - query_data->cb.srv(purple_srv_sort(records)->data, g_list_length(records), query_data->extradata); + + if (query_data->cb.srv == NULL) { + purple_srv_txt_query_destroy(query_data); + + while (records) { + g_free(records->data); + records = g_list_delete_link(records, records); + } + return; + } + + records = purple_srv_sort(records); + length = g_list_length(records); + + purple_debug_info("dnssrv", "SRV records resolved for %s, count: %d\n", + query_data->query, length); + + records_array = g_new(PurpleSrvResponse, length); + for (l = records; l; l = l->next, i++) { + records_array[i] = *(PurpleSrvResponse *)l->data; + } + + query_data->cb.srv(records_array, length, query_data->extradata); + + purple_srv_txt_query_destroy(query_data); + + while (records) { + g_free(records->data); + records = g_list_delete_link(records, records); + } } /* @@ -1024,19 +1071,29 @@ purple_debug_info("dnssrv", "TXT entries resolved for %s, count: %d\n", query_data->query, g_list_length(entries)); + /* the callback should g_free the entries. + */ if (query_data->cb.txt != NULL) query_data->cb.txt(entries, query_data->extradata); + else { + while (entries) { + g_free(entries->data); + entries = g_list_delete_link(entries, entries); + } + } + + purple_srv_txt_query_destroy(query_data); } static void purple_srv_query_failed(PurpleSrvTxtQueryData *query_data, const gchar *error_message) { purple_debug_error("dnssrv", "%s\n", error_message); - + if (query_data->cb.srv != NULL) query_data->cb.srv(NULL, 0, query_data->extradata); - - purple_srv_cancel(query_data); + + purple_srv_txt_query_destroy(query_data); } static gboolean @@ -1069,7 +1126,7 @@ purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data) { g_return_val_if_fail(query_data != NULL, NULL); - + return query_data->query; } @@ -1078,6 +1135,6 @@ purple_srv_txt_query_get_type(PurpleSrvTxtQueryData *query_data) { g_return_val_if_fail(query_data != NULL, 0); - + return query_data->type; } diff -r 3951afa56940 -r 98ae4b8b592f libpurple/dnssrv.h --- a/libpurple/dnssrv.h Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/dnssrv.h Thu Aug 11 15:01:34 2011 +0000 @@ -133,6 +133,8 @@ * Cancel an SRV or DNS query. * * @param query_data The request to cancel. + * + * @deprecated Use purple_srv_txt_query_destroy instead */ void purple_srv_cancel(PurpleSrvTxtQueryData *query_data); @@ -170,6 +172,8 @@ * * @param query_data The request to cancel. * @since 2.6.0 + * + * @deprecated Use purple_srv_txt_query_destroy instead */ void purple_txt_cancel(PurpleSrvTxtQueryData *query_data); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/plugins/log_reader.c --- a/libpurple/plugins/log_reader.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/plugins/log_reader.c Thu Aug 11 15:01:34 2011 +0000 @@ -1454,11 +1454,15 @@ const char *footer = NULL; GString *temp = NULL; - if ((c = strstr(c, "\n"))) - { - *c = '\0'; - c++; - } + /* There's always a trailing '\n' at the end of the file (see above), so + * just quit out if we don't find another, because we're at the end. + */ + c = strchr(c, '\n'); + if (!c) + break; + + *c = '\0'; + c++; /* Convert links. * @@ -1482,14 +1486,14 @@ char *end_paren; char *space; - if (!(end_paren = strstr(link, ")"))) + if (!(end_paren = strchr(link, ')'))) { /* Something is not as we expect. Bail out. */ break; } if (!temp) - temp = g_string_sized_new(c ? (c - 1 - line) : strlen(line)); + temp = g_string_sized_new(strlen(line)); g_string_append_len(temp, line, (tmp - line)); @@ -1504,7 +1508,7 @@ /* The \r is a bit of a hack to keep there from being a \r in * the link text, which may not matter. */ - if ((space = strstr(end_paren, " ")) || (space = strstr(end_paren, "\r"))) + if ((space = strchr(end_paren, ' ')) || (space = strchr(end_paren, '\r'))) { g_string_append_len(temp, end_paren + 1, space - end_paren - 1); @@ -1539,7 +1543,7 @@ if (*line == '[') { const char *timestamp; - if ((timestamp = strstr(line, "]"))) { + if ((timestamp = strchr(line, ']'))) { line++; /* TODO: Parse the timestamp and convert it to Purple's format. */ g_string_append(formatted, "("); @@ -1658,7 +1662,7 @@ } } } else { - const char *line2 = strstr(line, ":"); + const char *line2 = strchr(line, ':'); if (line2) { const char *acct_name; line2++; @@ -1819,7 +1823,7 @@ gboolean add_new_log = FALSE; - if (*c) { + if (c && *c) { if (purple_str_has_prefix(c, QIP_LOG_IN_MESSAGE) || purple_str_has_prefix(c, QIP_LOG_OUT_MESSAGE)) { @@ -1828,11 +1832,11 @@ new_line = c; /* find EOL */ - c = strstr(c, "\n"); + c = strchr(c, '\n'); c++; /* Find the last '(' character. */ - if ((tmp = strstr(c, "\n")) != NULL) { + if ((tmp = strchr(c, '\n')) != NULL) { while (*tmp && *tmp != '(') --tmp; c = tmp; } else { @@ -1902,10 +1906,10 @@ start_log = new_line; } - if (*c) { + if (c && *c) { /* find EOF */ - c = strstr(c, "\n"); - c++; + if ((c = strchr(c, '\n'))) + c++; } } @@ -1983,13 +1987,13 @@ is_in_message = purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC); /* find EOL */ - c = strstr(c, "\n"); + c = strchr(c, '\n'); /* XXX: Do we need buddy_name when we have buddy->alias? */ buddy_name = ++c; /* Find the last '(' character. */ - if ((tmp = strstr(c, "\n")) != NULL) { + if ((tmp = strchr(c, '\n')) != NULL) { while (*tmp && *tmp != '(') --tmp; c = tmp; } else { @@ -2042,12 +2046,12 @@ } /* find EOF */ - c = strstr(c, "\n"); + c = strchr(c, '\n'); line = ++c; } } } else { - if ((c = strstr(c, "\n"))) + if ((c = strchr(c, '\n'))) *c = '\0'; if (line[0] != '\n' && line[0] != '\r') { @@ -2186,7 +2190,7 @@ " length = (%d)\n", sn, data->path, data->offset, data->length); } - c = strstr(c, "\n"); + c = strchr(c, '\n'); c++; } @@ -2342,7 +2346,7 @@ char *end; char *old_tag; char *tag; - end = strstr(start, "\n"); + end = strchr(start, '\n'); if (!end) break; *end = '\0'; diff -r 3951afa56940 -r 98ae4b8b592f libpurple/plugins/tcl/tcl_ref.c --- a/libpurple/plugins/tcl/tcl_ref.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/plugins/tcl/tcl_ref.c Thu Aug 11 15:01:34 2011 +0000 @@ -92,6 +92,7 @@ static void purple_tcl_ref_update(Tcl_Obj *obj) { + size_t len; /* This is ugly on memory, but we pretty much have to either * do this or guesstimate lengths or introduce a varargs * function in here ... ugh. */ @@ -100,8 +101,9 @@ OBJ_REF_VALUE(obj)); obj->length = strlen(bytes); - obj->bytes = ckalloc(obj->length + 1); - strcpy(obj->bytes, bytes); + len = obj->length + 1; + obj->bytes = ckalloc(len); + g_strlcpy(obj->bytes, bytes, len); g_free(bytes); } diff -r 3951afa56940 -r 98ae4b8b592f libpurple/plugins/tcl/tcl_signals.c --- a/libpurple/plugins/tcl/tcl_signals.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/plugins/tcl/tcl_signals.c Thu Aug 11 15:01:34 2011 +0000 @@ -259,8 +259,9 @@ vals[i] = ckalloc(1); *(char *)vals[i] = '\0'; } else { - vals[i] = ckalloc(strlen(*strs[i]) + 1); - strcpy(vals[i], *strs[i]); + size_t len = strlen(*strs[i]) + 1; + vals[i] = ckalloc(len); + g_strlcpy(vals[i], *strs[i], len); } Tcl_LinkVar(handler->interp, name->str, (char *)&vals[i], TCL_LINK_STRING); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/prefs.c --- a/libpurple/prefs.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/prefs.c Thu Aug 11 15:01:34 2011 +0000 @@ -277,6 +277,12 @@ } } + if ((pref_type == PURPLE_PREF_BOOLEAN || pref_type == PURPLE_PREF_INT) && + pref_value == NULL) { + /* Missing a value attribute */ + return; + } + if(purple_strequal(element_name, "item")) { struct purple_pref *pref; diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/gg/gg.c --- a/libpurple/protocols/gg/gg.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/gg/gg.c Thu Aug 11 15:01:34 2011 +0000 @@ -877,30 +877,26 @@ /* ----- BLOCK BUDDIES -------------------------------------------------- */ -static void ggp_bmenu_block(PurpleBlistNode *node, gpointer ignored) +static void ggp_add_deny(PurpleConnection *gc, const char *who) { - PurpleConnection *gc; - PurpleBuddy *buddy; - GGPInfo *info; - uin_t uin; - - buddy = (PurpleBuddy *)node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - info = gc->proto_data; - - uin = ggp_str_to_uin(purple_buddy_get_name(buddy)); - - if (purple_blist_node_get_bool(node, "blocked")) { - purple_blist_node_set_bool(node, "blocked", FALSE); - gg_remove_notify_ex(info->session, uin, GG_USER_BLOCKED); - gg_add_notify_ex(info->session, uin, GG_USER_NORMAL); - purple_debug_info("gg", "send: uin=%d; mode=NORMAL\n", uin); - } else { - purple_blist_node_set_bool(node, "blocked", TRUE); - gg_remove_notify_ex(info->session, uin, GG_USER_NORMAL); - gg_add_notify_ex(info->session, uin, GG_USER_BLOCKED); - purple_debug_info("gg", "send: uin=%d; mode=BLOCKED\n", uin); - } + GGPInfo *info = gc->proto_data; + uin_t uin = ggp_str_to_uin(who); + + purple_debug_info("gg", "ggp_add_deny: %u\n", uin); + + gg_remove_notify_ex(info->session, uin, GG_USER_NORMAL); + gg_add_notify_ex(info->session, uin, GG_USER_BLOCKED); +} + +static void ggp_rem_deny(PurpleConnection *gc, const char *who) +{ + GGPInfo *info = gc->proto_data; + uin_t uin = ggp_str_to_uin(who); + + purple_debug_info("gg", "ggp_rem_deny: %u\n", uin); + + gg_remove_notify_ex(info->session, uin, GG_USER_BLOCKED); + gg_add_notify_ex(info->session, uin, GG_USER_NORMAL); } /* ---------------------------------------------------------------------- */ @@ -922,6 +918,9 @@ PurpleBuddy *buddy; gpointer buddy_icon_data; + purple_debug_info("gg", "gg_fetch_avatar_cb: got avatar image for %s\n", + d->uin); + /* FIXME: This shouldn't be necessary */ if (!PURPLE_CONNECTION_IS_VALID(d->gc)) { g_free(d->uin); @@ -940,7 +939,8 @@ 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); + purple_debug_info("gg", "gg_fetch_avatar_cb: UIN %s should have avatar " + "now\n", d->uin); out: g_free(d->uin); @@ -1022,6 +1022,8 @@ data->uin = g_strdup(uin); data->avatar_url = g_strdup(bigavatar); + purple_debug_info("gg", "gg_get_avatar_url_cb: " + "requesting avatar for %s\n", uin); 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); @@ -1071,6 +1073,7 @@ { gchar *from; const char *st; + char *status_msg = NULL; ggp_update_buddy_avatar(gc, uin); @@ -1108,14 +1111,24 @@ break; } - purple_debug_info("gg", "st = %s\n", st); - //msg = charset_convert(descr, "CP1250", "UTF-8"); - if (descr == NULL) { + if (descr != NULL) { + status_msg = g_strdup(descr); + g_strstrip(status_msg); + if (status_msg[0] == '\0') { + g_free(status_msg); + status_msg = NULL; + } + } + + purple_debug_info("gg", "status of %u is %s [%s]\n", uin, st, + status_msg ? status_msg : ""); + if (status_msg == NULL) { purple_prpl_got_user_status(purple_connection_get_account(gc), - from, st, NULL); + from, st, NULL); } else { purple_prpl_got_user_status(purple_connection_get_account(gc), - from, st, "message", descr, NULL); + from, st, "message", status_msg, NULL); + g_free(status_msg); } g_free(from); } @@ -1945,23 +1958,18 @@ char *text; char *tmp; - status = purple_presence_get_active_status(purple_buddy_get_presence(b)); - + status = purple_presence_get_active_status( + purple_buddy_get_presence(b)); msg = purple_status_get_attr_string(status, "message"); - if (msg != NULL) { - tmp = purple_markup_strip_html(msg); - text = g_markup_escape_text(tmp, -1); - g_free(tmp); - - return text; - } else { - tmp = purple_utf8_salvage(purple_status_get_name(status)); - text = g_markup_escape_text(tmp, -1); - g_free(tmp); - - return text; - } + if (msg == NULL) + return NULL; + + tmp = purple_markup_strip_html(msg); + text = g_markup_escape_text(tmp, -1); + g_free(tmp); + + return text; } static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) @@ -2068,20 +2076,6 @@ m = g_list_append(m, act); } - /* Using a blist node boolean here is also wrong. - * Once the Block and Unblock actions are added to the core, - * this will have to go. -- rlaager */ - if (purple_blist_node_get_bool(node, "blocked")) { - act = purple_menu_action_new(_("Unblock"), - PURPLE_CALLBACK(ggp_bmenu_block), - NULL, NULL); - } else { - act = purple_menu_action_new(_("Block"), - PURPLE_CALLBACK(ggp_bmenu_block), - NULL, NULL); - } - m = g_list_append(m, act); - return m; } @@ -2678,9 +2672,9 @@ ggp_remove_buddy, /* remove_buddy */ NULL, /* remove_buddies */ NULL, /* add_permit */ - NULL, /* add_deny */ + ggp_add_deny, /* add_deny */ NULL, /* rem_permit */ - NULL, /* rem_deny */ + ggp_rem_deny, /* rem_deny */ NULL, /* set_permit_deny */ ggp_join_chat, /* join_chat */ NULL, /* reject_chat */ diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/gg/lib/libgadu.c --- a/libpurple/protocols/gg/lib/libgadu.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/gg/lib/libgadu.c Thu Aug 11 15:01:34 2011 +0000 @@ -893,8 +893,7 @@ gnutls_global_init(); gnutls_certificate_allocate_credentials(&tmp->xcred); gnutls_init(&tmp->session, GNUTLS_CLIENT); - gnutls_priority_set_direct(tmp->session, "NORMAL:-VERS-TLS", NULL); -// gnutls_priority_set_direct(tmp->session, "NONE:+VERS-SSL3.0:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL", NULL); + gnutls_set_default_priority(tmp->session); gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred); #elif defined(GG_CONFIG_HAVE_OPENSSL) char buf[1024]; diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/irc/irc.c --- a/libpurple/protocols/irc/irc.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/irc/irc.c Thu Aug 11 15:01:34 2011 +0000 @@ -101,7 +101,11 @@ static int irc_send_raw(PurpleConnection *gc, const char *buf, int len) { struct irc_conn *irc = (struct irc_conn*)gc->proto_data; - return do_send(irc, buf, len); + if (len == -1) { + len = strlen(buf); + } + irc_send_len(irc, buf, len); + return len; } static void @@ -144,16 +148,18 @@ int irc_send(struct irc_conn *irc, const char *buf) { - int ret, buflen; + return irc_send_len(irc, buf, strlen(buf)); +} + +int irc_send_len(struct irc_conn *irc, const char *buf, int buflen) +{ + int ret; char *tosend= g_strdup(buf); purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend); if (tosend == NULL) return 0; - buflen = strlen(tosend); - - /* If we're not buffering writes, try to send immediately */ if (!irc->writeh) ret = do_send(irc, tosend, buflen); @@ -245,10 +251,14 @@ static void irc_who_channel(PurpleConversation *conv, struct irc_conn *irc) { - if (purple_conversation_get_account(conv) == irc->account && purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + if (purple_conversation_get_account(conv) == irc->account + && purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT + && !purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) { char *buf = irc_format(irc, "vc", "WHO", purple_conversation_get_name(conv)); - purple_debug(PURPLE_DEBUG_INFO, "irc", "Performing periodic who on %s", purple_conversation_get_name(conv)); + purple_debug(PURPLE_DEBUG_INFO, "irc", + "Performing periodic who on %s\n", + purple_conversation_get_name(conv)); irc_send(irc, buf); g_free(buf); } diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/irc/irc.h --- a/libpurple/protocols/irc/irc.h Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/irc/irc.h Thu Aug 11 15:01:34 2011 +0000 @@ -106,6 +106,7 @@ typedef int (*IRCCmdCallback) (struct irc_conn *irc, const char *cmd, const char *target, const char **args); int irc_send(struct irc_conn *irc, const char *buf); +int irc_send_len(struct irc_conn *irc, const char *buf, int len); gboolean irc_blist_timeout(struct irc_conn *irc); gboolean irc_who_channel_timeout(struct irc_conn *irc); void irc_buddy_query(struct irc_conn *irc); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/irc/msgs.c --- a/libpurple/protocols/irc/msgs.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/irc/msgs.c Thu Aug 11 15:01:34 2011 +0000 @@ -445,7 +445,7 @@ g_free(userhost); g_free(realname); - flags = purple_conv_chat_user_get_flags(chat, cb->name); + flags = cb->flags; if (args[6][0] == 'G' && !(flags & PURPLE_CBFLAGS_AWAY)) { purple_conv_chat_user_set_flags(chat, cb->name, flags | PURPLE_CBFLAGS_AWAY); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/irc/parse.c --- a/libpurple/protocols/irc/parse.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/irc/parse.c Thu Aug 11 15:01:34 2011 +0000 @@ -459,6 +459,7 @@ decoded = g_string_append(decoded, ""); if (font) decoded = g_string_append(decoded, ""); + bold = italic = underline = font = FALSE; break; default: purple_debug(PURPLE_DEBUG_ERROR, "irc", "Unexpected mIRC formatting character %d\n", *cur); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/jabber/jabber.c Thu Aug 11 15:01:34 2011 +0000 @@ -199,7 +199,7 @@ hostname, so maybe we want to detect that and use it instead */ - strcpy(hostname, "localhost"); + g_strlcpy(hostname, "localhost", sizeof(hostname)); } hostname[sizeof(hostname) - 1] = '\0'; @@ -579,7 +579,7 @@ */ jabber_send_raw(js, buf, len); - return len; + return (len < 0 ? strlen(buf) : len); } void jabber_send_signal_cb(PurpleConnection *pc, xmlnode **packet, @@ -3009,7 +3009,7 @@ { JabberChat *chat = jabber_chat_find_by_conv(conv); GHashTable *components; - JabberID *jid; + JabberID *jid = NULL; const char *room = NULL, *server = NULL, *handle = NULL; if (!chat || !args || !args[0]) @@ -3017,7 +3017,8 @@ components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - jid = jabber_id_new(args[0]); + if (strchr(args[0], '@')) + jid = jabber_id_new(args[0]); if (jid) { room = jid->node; server = jid->domain; @@ -3673,8 +3674,7 @@ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_join, - _("join: <room> [password]: Join a chat on this server."), - /* _("join: <room[@server]> [password]: Join a chat."), */ + _("join: <room[@server]> [password]: Join a chat."), NULL); commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/jabber/usermood.c --- a/libpurple/protocols/jabber/usermood.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/jabber/usermood.c Thu Aug 11 15:01:34 2011 +0000 @@ -177,12 +177,12 @@ xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood"); moodnode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "mood"); xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood"); - if (mood) { + if (mood && *mood) { /* if mood is NULL, set an empty mood node, meaning: unset mood */ xmlnode_new_child(moodnode, mood); } - if (text && text[0] != '\0') { + if (text && *text) { xmlnode *textnode = xmlnode_new_child(moodnode, "text"); xmlnode_insert_data(textnode, text, -1); } @@ -195,4 +195,4 @@ PurpleMood *jabber_get_moods(PurpleAccount *account) { return moods; -} \ No newline at end of file +} diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/jabber/win32/posix.uname.c --- a/libpurple/protocols/jabber/win32/posix.uname.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/jabber/win32/posix.uname.c Thu Aug 11 15:01:34 2011 +0000 @@ -54,32 +54,32 @@ GetVersionEx ( &OS_version ); GetSystemInfo ( &System_Info ); - strcpy( uts->sysname, "WIN32_" ); + g_strlcpy( uts->sysname, "WIN32_" , sizeof(uts->sysname)); switch( OS_version.dwPlatformId ) { case VER_PLATFORM_WIN32_NT: - strcat( uts->sysname, "WinNT" ); + g_strlcat( uts->sysname, "WinNT", sizeof(uts->sysname) ); MingwOS = WinNT; break; case VER_PLATFORM_WIN32_WINDOWS: switch ( OS_version.dwMinorVersion ) { case 0: - strcat( uts->sysname, "Win95" ); + g_strlcat( uts->sysname, "Win95", sizeof(uts->sysname) ); MingwOS = Win95; break; case 10: - strcat( uts->sysname, "Win98" ); + g_strlcat( uts->sysname, "Win98", sizeof(uts->sysname) ); MingwOS = Win98; break; default: - strcat( uts->sysname, "Win??" ); + g_strlcat( uts->sysname, "Win??", sizeof(uts->sysname) ); MingwOS = unknown; break; } break; default: - strcat( uts->sysname, "Win??" ); + g_strlcat( uts->sysname, "Win??", sizeof(uts->sysname) ); MingwOS = unknown; break; } @@ -92,13 +92,13 @@ switch( System_Info.wProcessorArchitecture ) { case PROCESSOR_ARCHITECTURE_PPC: - strcpy( uts->machine, "ppc" ); + g_strlcpy( uts->machine, "ppc" , sizeof( uts->machine ) ); break; case PROCESSOR_ARCHITECTURE_ALPHA: - strcpy( uts->machine, "alpha" ); + g_strlcpy( uts->machine, "alpha" , sizeof( uts->machine ) ); break; case PROCESSOR_ARCHITECTURE_MIPS: - strcpy( uts->machine, "mips" ); + g_strlcpy( uts->machine, "mips" , sizeof( uts->machine ) ); break; case PROCESSOR_ARCHITECTURE_INTEL: /* dwProcessorType is only valid in Win95 and Win98 @@ -115,7 +115,7 @@ sprintf( uts->machine, "i%ld", System_Info.dwProcessorType ); break; default: - strcpy( uts->machine, "i386" ); + g_strlcpy( uts->machine, "i386" , sizeof( uts->machine ) ); break; } break; @@ -123,12 +123,12 @@ sprintf( uts->machine, "i%d86", System_Info.wProcessorLevel ); break; default: - strcpy( uts->machine, "unknown" ); + g_strlcpy( uts->machine, "unknown" , sizeof( uts->machine ) ); break; } break; default: - strcpy( uts->machine, "unknown" ); + g_strlcpy( uts->machine, "unknown" , sizeof( uts->machine ) ); break; } diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/msn/contact.c --- a/libpurple/protocols/msn/contact.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/msn/contact.c Thu Aug 11 15:01:34 2011 +0000 @@ -474,7 +474,7 @@ } /*parse contact list*/ -static void +static gboolean msn_parse_contact_list(MsnSession *session, xmlnode *node) { xmlnode *fault, *faultnode; @@ -499,13 +499,14 @@ if (g_str_equal(errorcode, "ABDoesNotExist")) { msn_create_address_book(session); g_free(errorcode); - return; + return FALSE; } g_free(errorcode); } msn_get_contact_list(session, MSN_PS_INITIAL, NULL); + return FALSE; } else { xmlnode *service; @@ -514,6 +515,7 @@ service; service = xmlnode_get_next_twin(service)) { msn_parse_each_service(session, service); } + return TRUE; } } @@ -534,23 +536,24 @@ purple_debug_misc("msn", "Got the contact list!\n"); - msn_parse_contact_list(session, resp->xml); + if (msn_parse_contact_list(session, resp->xml)) { #ifdef MSN_PARTIAL_LISTS - abLastChange = purple_account_get_string(session->account, - "ablastChange", NULL); - dynamicItemLastChange = purple_account_get_string(session->account, - "DynamicItemLastChanged", NULL); + abLastChange = purple_account_get_string(session->account, + "ablastChange", NULL); + dynamicItemLastChange = purple_account_get_string(session->account, + "DynamicItemLastChanged", NULL); #endif - if (state->partner_scenario == MSN_PS_INITIAL) { + if (state->partner_scenario == MSN_PS_INITIAL) { #ifdef MSN_PARTIAL_LISTS - /* XXX: this should be enabled when we can correctly do partial - syncs with the server. Currently we need to retrieve the whole - list to detect sync issues */ - msn_get_address_book(session, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange); + /* XXX: this should be enabled when we can correctly do partial + syncs with the server. Currently we need to retrieve the whole + list to detect sync issues */ + msn_get_address_book(session, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange); #else - msn_get_address_book(session, MSN_PS_INITIAL, NULL, NULL); + msn_get_address_book(session, MSN_PS_INITIAL, NULL, NULL); #endif + } } } } diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/msn/msn.c Thu Aug 11 15:01:34 2011 +0000 @@ -265,9 +265,9 @@ } if (real_alias[0] == '\0') - strcpy(real_alias, purple_account_get_username(account)); + g_strlcpy(real_alias, purple_account_get_username(account), sizeof(real_alias)); } else - strcpy(real_alias, purple_account_get_username(account)); + g_strlcpy(real_alias, purple_account_get_username(account), sizeof(real_alias)); closure = g_new0(struct public_alias_closure, 1); closure->account = account; diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/msn/notification.c Thu Aug 11 15:01:34 2011 +0000 @@ -198,6 +198,9 @@ { /* RPS authentication */ + if (session->nexus) + msn_nexus_destroy(session->nexus); + session->nexus = msn_nexus_new(session); session->nexus->policy = g_strdup(cmd->params[3]); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/msn/session.c --- a/libpurple/protocols/msn/session.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/msn/session.c Thu Aug 11 15:01:34 2011 +0000 @@ -288,6 +288,8 @@ msn_session_activate_login_timeout(MsnSession *session) { if (!session->logged_in && session->connected) { + if (session->login_timeout) + purple_timeout_remove(session->login_timeout); session->login_timeout = purple_timeout_add_seconds(MSN_LOGIN_FQY_TIMEOUT, msn_login_timeout_cb, session); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/msn/slp.c --- a/libpurple/protocols/msn/slp.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/msn/slp.c Thu Aug 11 15:01:34 2011 +0000 @@ -284,7 +284,6 @@ purple_xfer_unref(xfer); slpmsg = msn_slpmsg_file_new(slpcall, purple_xfer_get_size(xfer)); - msn_slpmsg_set_slplink(slpmsg, slpcall->slplink); msn_slplink_send_slpmsg(slpcall->slplink, slpmsg); } diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/msn/slpcall.c --- a/libpurple/protocols/msn/slpcall.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/msn/slpcall.c Thu Aug 11 15:01:34 2011 +0000 @@ -490,12 +490,10 @@ if (img != NULL) { /* DATA PREP */ slpmsg = msn_slpmsg_dataprep_new(slpcall); - msn_slpmsg_set_slplink(slpmsg, slplink); msn_slplink_queue_slpmsg(slplink, slpmsg); /* DATA */ slpmsg = msn_slpmsg_obj_new(slpcall, img); - msn_slpmsg_set_slplink(slpmsg, slplink); msn_slplink_queue_slpmsg(slplink, slpmsg); purple_imgstore_unref(img); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/mxit/cipher.c --- a/libpurple/protocols/mxit/cipher.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/mxit/cipher.c Thu Aug 11 15:01:34 2011 +0000 @@ -85,12 +85,12 @@ pass[sizeof( pass ) - 1] = '\0'; /* build the custom AES encryption key */ - strcpy( key, INITIAL_KEY ); + g_strlcpy( key, INITIAL_KEY, sizeof( key ) ); memcpy( key, session->clientkey, strlen( session->clientkey ) ); ExpandKey( (unsigned char*) key, (unsigned char*) exkey ); /* build the custom data to be encrypted */ - strcpy( pass, SECRET_HEADER ); + g_strlcpy( pass, SECRET_HEADER, sizeof( pass ) ); strcat( pass, session->acc->password ); /* pad the secret data */ diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/oscar/oscar.c Thu Aug 11 15:01:34 2011 +0000 @@ -743,11 +743,15 @@ gc->flags |= PURPLE_CONNECTION_HTML; if (oscar_util_valid_name_icq((purple_account_get_username(account)))) { od->icq = TRUE; - gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS; } else { gc->flags |= PURPLE_CONNECTION_AUTO_RESP; } + /* Set this flag based on the protocol_id rather than the username, + because that is what's tied to the get_moods prpl callback. */ + if (g_str_equal(purple_account_get_protocol_id(account), "prpl-icq")) + gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS; + od->default_port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT); encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/sametime/sametime.c --- a/libpurple/protocols/sametime/sametime.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/sametime/sametime.c Thu Aug 11 15:01:34 2011 +0000 @@ -922,6 +922,11 @@ alias = mwSametimeGroup_getAlias(stgroup); type = mwSametimeGroup_getType(stgroup); + if (!name) { + DEBUG_WARN("Can't ensure a null group\n"); + return NULL; + } + DEBUG_INFO("attempting to ensure group %s, called %s\n", NSTR(name), NSTR(alias)); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/simple/simple.c --- a/libpurple/protocols/simple/simple.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/simple/simple.c Thu Aug 11 15:01:34 2011 +0000 @@ -1932,6 +1932,8 @@ gc->proto_data = sip = g_new0(struct simple_account_data, 1); sip->gc = gc; + sip->fd = -1; + sip->listenfd = -1; sip->account = account; sip->registerexpire = 900; sip->udp = purple_account_get_bool(account, "udp", FALSE); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/yahoo/libymsg.h --- a/libpurple/protocols/yahoo/libymsg.h Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/yahoo/libymsg.h Thu Aug 11 15:01:34 2011 +0000 @@ -48,7 +48,7 @@ #define YAHOO_ROOMLIST_LOCALE "us" /* Yahoo! JAPAN stuff */ -#define YAHOOJP_PAGER_HOST_REQ_URL "http://cs1.msg.vip.ogk.yahoo.co.jp/capacity" +#define YAHOOJP_PAGER_HOST_REQ_URL "http://cs1.yahoo.co.jp/capacity" #define YAHOOJP_TOKEN_URL "https://login.yahoo.co.jp/config/pwtoken_get?src=ymsgr&ts=&login=%s&passwd=%s&chal=%s" #define YAHOOJP_LOGIN_URL "https://login.yahoo.co.jp/config/pwtoken_login?src=ymsgr&ts=&token=%s" #define YAHOOJP_PROFILE_URL "http://profiles.yahoo.co.jp/" diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/zephyr/ZAsyncLocate.c --- a/libpurple/protocols/zephyr/ZAsyncLocate.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/zephyr/ZAsyncLocate.c Thu Aug 11 15:01:34 2011 +0000 @@ -18,6 +18,7 @@ { int retval; ZNotice_t notice; + size_t userlen, versionlen; if (ZGetFD() < 0) if ((retval = ZOpenPort((unsigned short *)0)) != ZERR_NONE) @@ -37,16 +38,18 @@ if ((retval = ZSendNotice(¬ice, auth)) != ZERR_NONE) return(retval); - if ((zald->user = (char *) malloc(strlen(user)+1)) == NULL) { + userlen = strlen(user) + 1; + versionlen = strlen(notice.z_version) + 1; + if ((zald->user = (char *) malloc(userlen)) == NULL) { return(ENOMEM); } - if ((zald->version = (char *) malloc(strlen(notice.z_version)+1)) == NULL) { + if ((zald->version = (char *) malloc(versionlen)) == NULL) { free(zald->user); return(ENOMEM); } zald->uid = notice.z_multiuid; - strcpy(zald->user,user); - strcpy(zald->version,notice.z_version); + g_strlcpy(zald->user,user,userlen); + g_strlcpy(zald->version,notice.z_version,versionlen); return(ZERR_NONE); } @@ -109,35 +112,38 @@ __locate_list[i].host = (char *) malloc(len); if (!__locate_list[i].host) return (ENOMEM); - (void) strcpy(__locate_list[i].host, ptr); + g_strlcpy(__locate_list[i].host, ptr,len); ptr += len; len = strlen (ptr) + 1; __locate_list[i].time = (char *) malloc(len); if (!__locate_list[i].time) return (ENOMEM); - (void) strcpy(__locate_list[i].time, ptr); + g_strlcpy(__locate_list[i].time, ptr,len); ptr += len; len = strlen (ptr) + 1; __locate_list[i].tty = (char *) malloc(len); if (!__locate_list[i].tty) return (ENOMEM); - (void) strcpy(__locate_list[i].tty, ptr); + g_strlcpy(__locate_list[i].tty, ptr,len); ptr += len; } __locate_next = 0; *nlocs = __locate_num; if (user) { + size_t len; if (zald) { - if ((*user = (char *) malloc(strlen(zald->user)+1)) == NULL) + len = strlen(zald->user) + 1; + if ((*user = (char *) malloc(len)) == NULL) return(ENOMEM); - strcpy(*user,zald->user); + g_strlcpy(*user,zald->user,len); } else { - if ((*user = (char *) malloc(strlen(notice->z_class_inst)+1)) == NULL) + len = strlen(notice->z_class_inst) + 1; + if ((*user = (char *) malloc(len)) == NULL) return(ENOMEM); - strcpy(*user,notice->z_class_inst); + g_strlcpy(*user,notice->z_class_inst,len); } } return (ZERR_NONE); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/zephyr/ZInit.c --- a/libpurple/protocols/zephyr/ZInit.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/zephyr/ZInit.c Thu Aug 11 15:01:34 2011 +0000 @@ -101,14 +101,14 @@ #ifdef ZEPHYR_USES_KERBEROS if (krealm) { - strcpy(__Zephyr_realm, krealm); + g_strlcpy(__Zephyr_realm, krealm, REALM_SZ); } else if ((krb_get_tf_fullname(TKT_FILE, d1, d2, __Zephyr_realm) != KSUCCESS) && ((krbval = krb_get_lrealm(__Zephyr_realm, 1)) != KSUCCESS)) { return (krbval); } #else - strcpy(__Zephyr_realm, "local-realm"); + g_strlcpy(__Zephyr_realm, "local-realm", 12); #endif __My_addr.s_addr = INADDR_NONE; diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/zephyr/ZRetSubs.c --- a/libpurple/protocols/zephyr/ZRetSubs.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/zephyr/ZRetSubs.c Thu Aug 11 15:01:34 2011 +0000 @@ -141,32 +141,37 @@ } for (ptr=retnotice.z_message,i = 0; i< __subscriptions_num; i++) { + size_t len; + + len = strlen(ptr) + 1; __subscriptions_list[i].zsub_class = (char *) - malloc((unsigned)strlen(ptr)+1); + malloc(len); if (!__subscriptions_list[i].zsub_class) { ZFreeNotice(&retnotice); return (ENOMEM); } - (void) strcpy(__subscriptions_list[i].zsub_class,ptr); - ptr += strlen(ptr)+1; + g_strlcpy(__subscriptions_list[i].zsub_class,ptr,len); + ptr += len; + len = strlen(ptr) + 1; __subscriptions_list[i].zsub_classinst = (char *) - malloc((unsigned)strlen(ptr)+1); + malloc(len); if (!__subscriptions_list[i].zsub_classinst) { ZFreeNotice(&retnotice); return (ENOMEM); } - (void) strcpy(__subscriptions_list[i].zsub_classinst,ptr); - ptr += strlen(ptr)+1; + g_strlcpy(__subscriptions_list[i].zsub_classinst,ptr,len); + ptr += len; ptr2 = ptr; if (!*ptr2) ptr2 = "*"; + len = strlen(ptr2) + 1; __subscriptions_list[i].zsub_recipient = (char *) - malloc((unsigned)strlen(ptr2)+1); + malloc(len); if (!__subscriptions_list[i].zsub_recipient) { ZFreeNotice(&retnotice); return (ENOMEM); } - (void) strcpy(__subscriptions_list[i].zsub_recipient,ptr2); + g_strlcpy(__subscriptions_list[i].zsub_recipient,ptr2,len); ptr += strlen(ptr)+1; } ZFreeNotice(&retnotice); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/zephyr/Zinternal.c --- a/libpurple/protocols/zephyr/Zinternal.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/zephyr/Zinternal.c Thu Aug 11 15:01:34 2011 +0000 @@ -677,7 +677,7 @@ if (buffer_len < strlen(notice->z_version)+1) return (ZERR_HEADERLEN); - (void) strcpy(ptr, notice->z_version); + g_strlcpy(ptr, notice->z_version, buffer_len); ptr += strlen(ptr)+1; if (ZMakeAscii32(ptr, end-ptr, Z_NUMFIELDS + notice->z_num_other_fields) @@ -767,9 +767,9 @@ if (*ptr+len > end) return 1; if (field) - (void) strcpy(*ptr, field); + g_strlcpy(*ptr, field, len); else - **ptr = '\0'; + **ptr = '\0'; *ptr += len; return 0; diff -r 3951afa56940 -r 98ae4b8b592f libpurple/protocols/zephyr/zephyr.c --- a/libpurple/protocols/zephyr/zephyr.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/protocols/zephyr/zephyr.c Thu Aug 11 15:01:34 2011 +0000 @@ -2235,7 +2235,7 @@ return NULL; } - strcpy(buf, tmp); + g_strlcpy(buf, tmp, sizeof(buf)); g_free(tmp); return buf; diff -r 3951afa56940 -r 98ae4b8b592f libpurple/proxy.c --- a/libpurple/proxy.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/proxy.c Thu Aug 11 15:01:34 2011 +0000 @@ -981,7 +981,7 @@ hostname[sizeof(hostname) - 1] = '\0'; if (ret < 0 || hostname[0] == '\0') { purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?"); - strcpy(hostname, "localhost"); + g_strlcpy(hostname, "localhost", sizeof(hostname)); } if (domain != NULL) @@ -1115,7 +1115,7 @@ hostname[sizeof(hostname) - 1] = '\0'; if (ret < 0 || hostname[0] == '\0') { purple_debug_warning("proxy", "gethostname() failed -- is your hostname set?"); - strcpy(hostname, "localhost"); + g_strlcpy(hostname, "localhost", sizeof(hostname)); } t1 = g_strdup_printf("%s:%s", diff -r 3951afa56940 -r 98ae4b8b592f libpurple/tests/Makefile.am --- a/libpurple/tests/Makefile.am Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/tests/Makefile.am Thu Aug 11 15:01:34 2011 +0000 @@ -17,6 +17,7 @@ test_oscar_util.c \ test_yahoo_util.c \ test_util.c \ + test_xmlnode.c \ $(top_builddir)/libpurple/util.h check_libpurple_CFLAGS=\ diff -r 3951afa56940 -r 98ae4b8b592f libpurple/tests/check_libpurple.c --- a/libpurple/tests/check_libpurple.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/tests/check_libpurple.c Thu Aug 11 15:01:34 2011 +0000 @@ -91,6 +91,7 @@ srunner_add_suite(sr, oscar_util_suite()); srunner_add_suite(sr, yahoo_util_suite()); srunner_add_suite(sr, util_suite()); + srunner_add_suite(sr, xmlnode_suite()); /* make this a libpurple "ui" */ purple_check_init(); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/tests/test_xmlnode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/tests/test_xmlnode.c Thu Aug 11 15:01:34 2011 +0000 @@ -0,0 +1,34 @@ +#include + +#include "tests.h" +#include "../xmlnode.h" + +/* + * If we really wanted to test the billion laughs attack we would + * need to have more than just 4 ha's. But as long as this shorter + * document fails to parse, the longer one should also fail to parse. + */ +START_TEST(test_xmlnode_billion_laughs_attack) +{ + const char *malicious_xml_doc = " ]>&ha3;"; + + /* Uncomment this line if you want to see the error message given by + the parser for the above XML document */ + /* purple_debug_set_enabled(TRUE); */ + + fail_if(xmlnode_from_str(malicious_xml_doc, -1), + "xmlnode_from_str() returned an XML tree, but we didn't want it to"); +} +END_TEST + +Suite * +xmlnode_suite(void) +{ + Suite *s = suite_create("Utility Functions"); + + TCase *tc = tcase_create("xmlnode"); + tcase_add_test(tc, test_xmlnode_billion_laughs_attack); + suite_add_tcase(s, tc); + + return s; +} diff -r 3951afa56940 -r 98ae4b8b592f libpurple/tests/tests.h --- a/libpurple/tests/tests.h Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/tests/tests.h Thu Aug 11 15:01:34 2011 +0000 @@ -16,6 +16,7 @@ Suite * oscar_util_suite(void); Suite * yahoo_util_suite(void); Suite * util_suite(void); +Suite * xmlnode_suite(void); /* helper macros */ #define assert_int_equal(expected, actual) { \ diff -r 3951afa56940 -r 98ae4b8b592f libpurple/upnp.c --- a/libpurple/upnp.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/upnp.c Thu Aug 11 15:01:34 2011 +0000 @@ -535,7 +535,7 @@ dd->retry_count++; purple_upnp_discover_send_broadcast(dd); } else { - if (dd->fd) + if (dd->fd != -1) close(dd->fd); control_info.status = PURPLE_UPNP_STATUS_UNABLE_TO_DISCOVER; @@ -662,7 +662,7 @@ } /* Set up the sockets */ - sock = socket(AF_INET, SOCK_DGRAM, 0); + dd->fd = sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock == -1) { purple_debug_error("upnp", "purple_upnp_discover(): Failed In sock creation\n"); @@ -672,8 +672,6 @@ return; } - dd->fd = sock; - /* TODO: Non-blocking! */ if((hp = gethostbyname(HTTPMU_HOST_ADDRESS)) == NULL) { purple_debug_error("upnp", @@ -820,7 +818,7 @@ static void looked_up_internal_ip_cb(gpointer data, gint source, const gchar *error_message) { - if (source) { + if (source != -1) { strncpy(control_info.internalip, purple_network_get_local_system_ip(source), sizeof(control_info.internalip)); diff -r 3951afa56940 -r 98ae4b8b592f libpurple/value.h --- a/libpurple/value.h Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/value.h Thu Aug 11 15:01:34 2011 +0000 @@ -79,7 +79,8 @@ PURPLE_SUBTYPE_XMLNODE, PURPLE_SUBTYPE_USERINFO, PURPLE_SUBTYPE_STORED_IMAGE, - PURPLE_SUBTYPE_CERTIFICATEPOOL + PURPLE_SUBTYPE_CERTIFICATEPOOL, + PURPLE_SUBTYPE_CHATBUDDY } PurpleSubType; /** diff -r 3951afa56940 -r 98ae4b8b592f libpurple/win32/libc_interface.c --- a/libpurple/win32/libc_interface.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/win32/libc_interface.c Thu Aug 11 15:01:34 2011 +0000 @@ -982,7 +982,7 @@ if (strcmp(tzname, zonename) == 0) { /* Matched zone */ - strcpy(localtzname, keyname); + g_strlcpy(localtzname, keyname, sizeof(localtzname)); RegCloseKey(key); break; } @@ -997,7 +997,7 @@ if (strcmp(tzname, zonename) == 0) { /* Matched DST zone */ - strcpy(localtzname, keyname); + g_strlcpy(localtzname, keyname, sizeof(localtzname)); RegCloseKey(key); break; } diff -r 3951afa56940 -r 98ae4b8b592f libpurple/xmlnode.c --- a/libpurple/xmlnode.c Sun Jun 26 21:03:22 2011 +0000 +++ b/libpurple/xmlnode.c Thu Aug 11 15:01:34 2011 +0000 @@ -62,7 +62,7 @@ xmlnode* xmlnode_new(const char *name) { - g_return_val_if_fail(name != NULL, NULL); + g_return_val_if_fail(name != NULL && *name != '\0', NULL); return new_node(name, XMLNODE_TYPE_TAG); } @@ -73,7 +73,7 @@ xmlnode *node; g_return_val_if_fail(parent != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); + g_return_val_if_fail(name != NULL && *name != '\0', NULL); node = new_node(name, XMLNODE_TYPE_TAG); diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkaccount.c Thu Aug 11 15:01:34 2011 +0000 @@ -2135,7 +2135,7 @@ gtk_list_store_clear(dialog->model); if ((path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon")) != NULL) { - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, NULL); + GdkPixbuf *pixbuf = pidgin_pixbuf_new_from_file(path); if (pixbuf != NULL) { global_buddyicon = gdk_pixbuf_scale_simple(pixbuf, 22, 22, GDK_INTERP_HYPER); g_object_unref(G_OBJECT(pixbuf)); diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkblist.c --- a/pidgin/gtkblist.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkblist.c Thu Aug 11 15:01:34 2011 +0000 @@ -2643,7 +2643,6 @@ gboolean scaled, gboolean greyed) { gsize len; - GdkPixbufLoader *loader; PurpleBuddy *buddy = NULL; PurpleGroup *group = NULL; const guchar *data = NULL; @@ -2710,21 +2709,20 @@ return NULL; } - loader = gdk_pixbuf_loader_new(); - gdk_pixbuf_loader_write(loader, data, len, NULL); - gdk_pixbuf_loader_close(loader, NULL); - - purple_imgstore_unref(custom_img); + buf = pidgin_pixbuf_from_data(data, len); purple_buddy_icon_unref(icon); - - buf = gdk_pixbuf_loader_get_pixbuf(loader); - if (buf) - g_object_ref(G_OBJECT(buf)); - g_object_unref(G_OBJECT(loader)); - if (!buf) { + purple_debug_warning("gtkblist", "Couldn't load buddy icon " + "on account %s (%s) buddyname=%s " + "custom_img_data=%p\n", + account ? purple_account_get_username(account) : "(no account)", + account ? purple_account_get_protocol_id(account) : "(no account)", + buddy ? purple_buddy_get_name(buddy) : "(no buddy)", + custom_img ? purple_imgstore_get_data(custom_img) : NULL); + purple_imgstore_unref(custom_img); return NULL; } + purple_imgstore_unref(custom_img); if (greyed) { gboolean offline = FALSE, idle = FALSE; @@ -3649,6 +3647,7 @@ { N_("/Help/_Build Information"), NULL, pidgin_dialogs_buildinfo, 0, "", NULL }, { N_("/Help/_Debug Window"), NULL, toggle_debug, 0, "", NULL }, { N_("/Help/De_veloper Information"), NULL, pidgin_dialogs_developers, 0, "", NULL }, + { N_("/Help/_Plugin Information"), NULL, pidgin_dialogs_plugins_info, 0, "", NULL }, { N_("/Help/_Translator Information"), NULL, pidgin_dialogs_translators, 0, "", NULL }, { "/Help/sep2", NULL, NULL, 0, "", NULL }, { N_("/Help/_About"), NULL, pidgin_dialogs_about, 4, "", GTK_STOCK_ABOUT }, @@ -3952,7 +3951,7 @@ g_object_ref(pb); g_free(path); } else { - pb = gdk_pixbuf_new_from_file(path, NULL); + pb = pidgin_pixbuf_new_from_file(path); if (pb != NULL) { /* We don't want to own a ref to the pixbuf, but we need to keep clean up. */ /* I'm not sure if it would be better to just keep our ref and not let the emblem ever be destroyed */ diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkconv.c --- a/pidgin/gtkconv.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkconv.c Thu Aug 11 15:01:34 2011 +0000 @@ -3978,6 +3978,16 @@ } static void +deleting_chat_buddy_cb(PurpleConvChatBuddy *cb) +{ + if (cb->ui_data) { + GtkTreeRowReference *ref = cb->ui_data; + gtk_tree_row_reference_free(ref); + cb->ui_data = NULL; + } +} + +static void add_chat_buddy_common(PurpleConversation *conv, PurpleConvChatBuddy *cb, const char *old_name) { PidginConversation *gtkconv; @@ -3985,18 +3995,20 @@ PurpleConvChat *chat; PurpleConnection *gc; PurplePluginProtocolInfo *prpl_info; + GtkTreeModel *tm; GtkListStore *ls; + GtkTreePath *newpath; const char *stock; GtkTreeIter iter; gboolean is_me = FALSE; gboolean is_buddy; gchar *tmp, *alias_key, *name, *alias; - int flags; + PurpleConvChatBuddyFlags flags; GdkColor *color = NULL; alias = cb->alias; name = cb->name; - flags = GPOINTER_TO_INT(cb->flags); + flags = cb->flags; chat = PURPLE_CONV_CHAT(conv); gtkconv = PIDGIN_CONVERSATION(conv); @@ -4006,7 +4018,8 @@ if (!gc || !(prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl))) return; - ls = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list))); + tm = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); + ls = GTK_LIST_STORE(tm); stock = get_chat_buddy_status_icon(chat, name, flags); @@ -4051,6 +4064,15 @@ CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, -1); + if (cb->ui_data) { + GtkTreeRowReference *ref = cb->ui_data; + gtk_tree_row_reference_free(ref); + } + + newpath = gtk_tree_model_get_path(tm, &iter); + cb->ui_data = gtk_tree_row_reference_new(tm, newpath); + gtk_tree_path_free(newpath); + if (is_me && color) gdk_color_free(color); g_free(alias_key); @@ -4346,6 +4368,12 @@ CHAT_USERS_WEIGHT_COLUMN, &buddy2, -1); + /* Only sort by membership levels */ + f1 &= PURPLE_CBFLAGS_VOICE | PURPLE_CBFLAGS_HALFOP | PURPLE_CBFLAGS_OP | + PURPLE_CBFLAGS_FOUNDER; + f2 &= PURPLE_CBFLAGS_VOICE | PURPLE_CBFLAGS_HALFOP | PURPLE_CBFLAGS_OP | + PURPLE_CBFLAGS_FOUNDER; + if (user1 == NULL || user2 == NULL) { if (!(user1 == NULL && user2 == NULL)) ret = (user1 == NULL) ? -1: 1; @@ -6102,6 +6130,28 @@ update_typing_message(gtkconv, NULL); } +static gboolean get_iter_from_chatbuddy(PurpleConvChatBuddy *cb, GtkTreeIter *iter) +{ + GtkTreeRowReference *ref = cb->ui_data; + GtkTreePath *path; + GtkTreeModel *model; + + if (!ref) + return FALSE; + + if ((path = gtk_tree_row_reference_get_path(ref)) == NULL) + return FALSE; + + model = gtk_tree_row_reference_get_model(ref); + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), iter, path)) { + gtk_tree_path_free(path); + return FALSE; + } + + gtk_tree_path_free(path); + return TRUE; +} + static void pidgin_conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals) { @@ -6152,12 +6202,10 @@ PurpleConvChat *chat; PidginConversation *gtkconv; PidginChatPane *gtkchat; - PurpleConvChatBuddyFlags flags; - PurpleConvChatBuddy *cbuddy; + PurpleConvChatBuddy *old_cbuddy, *new_cbuddy; GtkTreeIter iter; GtkTreeModel *model; GtkTextTag *tag; - int f = 1; chat = PURPLE_CONV_CHAT(conv); gtkconv = PIDGIN_CONVERSATION(conv); @@ -6168,20 +6216,13 @@ if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) return; - while (f != 0) { - char *val; - - gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &val, CHAT_USERS_FLAGS_COLUMN, &flags, -1); - - if (!purple_utf8_strcasecmp(old_name, val)) { - gtk_list_store_remove(GTK_LIST_STORE(model), &iter); - g_free(val); - break; - } - - f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter); - - g_free(val); + old_cbuddy = purple_conv_chat_cb_find(chat, old_name); + if (get_iter_from_chatbuddy(old_cbuddy, &iter)) { + GtkTreeRowReference *ref = old_cbuddy->ui_data; + + gtk_list_store_remove(GTK_LIST_STORE(model), &iter); + gtk_tree_row_reference_free(ref); + old_cbuddy->ui_data = NULL; } if ((tag = get_buddy_tag(conv, old_name, 0, FALSE))) @@ -6189,14 +6230,14 @@ if ((tag = get_buddy_tag(conv, old_name, PURPLE_MESSAGE_NICK, FALSE))) g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL); - if (!purple_conv_chat_find_user(chat, old_name)) + if (!old_cbuddy) return; g_return_if_fail(new_alias != NULL); - cbuddy = purple_conv_chat_cb_find(chat, new_name); - - add_chat_buddy_common(conv, cbuddy, old_name); + new_cbuddy = purple_conv_chat_cb_find(chat, new_name); + + add_chat_buddy_common(conv, new_cbuddy, old_name); } static void @@ -6223,6 +6264,7 @@ model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) + /* XXX: Break? */ continue; do { @@ -6262,8 +6304,6 @@ PidginChatPane *gtkchat; GtkTreeIter iter; GtkTreeModel *model; - int f = 1; - char *alias = NULL; chat = PURPLE_CONV_CHAT(conv); gtkconv = PIDGIN_CONVERSATION(conv); @@ -6274,35 +6314,16 @@ if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) return; - while (f != 0) { - char *val; - - gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &val, -1); - - if (!purple_utf8_strcasecmp(user, val)) { - gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_ALIAS_COLUMN, &alias, -1); - gtk_list_store_remove(GTK_LIST_STORE(model), &iter); - g_free(val); - break; - } - - f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter); - - g_free(val); - } - - if (!purple_conv_chat_find_user(chat, user)) - { - g_free(alias); - return; - } - - g_return_if_fail(alias != NULL); - cbuddy = purple_conv_chat_cb_find(chat, user); + if (get_iter_from_chatbuddy(cbuddy, &iter)) { + GtkTreeRowReference *ref = cbuddy->ui_data; + gtk_list_store_remove(GTK_LIST_STORE(model), &iter); + gtk_tree_row_reference_free(ref); + cbuddy->ui_data = NULL; + } + if (cbuddy) add_chat_buddy_common(conv, cbuddy, NULL); - g_free(alias); } gboolean @@ -6387,8 +6408,8 @@ { PidginConversation *gtkconv; GtkIMHtmlSmiley *smiley; - GdkPixbufLoader *loader; const char *sml; + GError *error = NULL; sml = purple_account_get_protocol_name(conv->account); gtkconv = PIDGIN_CONVERSATION(conv); @@ -6401,11 +6422,24 @@ g_memmove((guchar *)smiley->data + smiley->datasize, data, size); smiley->datasize += size; - loader = smiley->loader; - if (!loader) + if (!smiley->loader) return; - gdk_pixbuf_loader_write(loader, data, size, NULL); + if (!gdk_pixbuf_loader_write(smiley->loader, data, size, &error) || error) { + purple_debug_warning("gtkconv", "gdk_pixbuf_loader_write() " + "failed with size=%zu: %s\n", size, + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + /* We must stop using the GdkPixbufLoader because trying to load + certain invalid GIFs with at least gdk-pixbuf 2.23.3 can return + a GdkPixbuf that will cause some operations (like + gdk_pixbuf_scale_simple()) to consume memory in an infinite loop. + But we also don't want to set smiley->loader to NULL because our + code might expect it to be set. So create a new loader. */ + g_object_unref(G_OBJECT(smiley->loader)); + smiley->loader = gdk_pixbuf_loader_new(); + } } static void @@ -6413,8 +6447,8 @@ { PidginConversation *gtkconv; GtkIMHtmlSmiley *smiley; - GdkPixbufLoader *loader; const char *sml; + GError *error = NULL; g_return_if_fail(conv != NULL); g_return_if_fail(smile != NULL); @@ -6426,17 +6460,27 @@ if (!smiley) return; - loader = smiley->loader; - - if (!loader) + if (!smiley->loader) return; - - purple_debug_info("gtkconv", "About to close the smiley pixbuf\n"); - gdk_pixbuf_loader_close(loader, NULL); - + if (!gdk_pixbuf_loader_close(smiley->loader, &error) || error) { + purple_debug_warning("gtkconv", "gdk_pixbuf_loader_close() " + "failed: %s\n", + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + /* We must stop using the GdkPixbufLoader because if we tried to + load certain invalid GIFs with all current versions of GDK (as + of 2011-06-15) then it's possible the loader will contain data + that could cause some operations (like gdk_pixbuf_scale_simple()) + to consume memory in an infinite loop. But we also don't want + to set smiley->loader to NULL because our code might expect it + to be set. So create a new loader. */ + g_object_unref(G_OBJECT(smiley->loader)); + smiley->loader = gdk_pixbuf_loader_new(); + } } static void @@ -6936,10 +6980,6 @@ PurpleBuddy *buddy; - GdkPixbufLoader *loader; - GdkPixbufAnimation *anim; - GError *err = NULL; - PurpleStoredImage *custom_img = NULL; gconstpointer data = NULL; size_t len; @@ -7017,7 +7057,6 @@ if (data == NULL) { icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv)); - if (icon == NULL) { gtk_widget_set_size_request(gtkconv->u.im->icon_container, @@ -7026,7 +7065,6 @@ } data = purple_buddy_icon_get_data(icon, &len); - if (data == NULL) { gtk_widget_set_size_request(gtkconv->u.im->icon_container, @@ -7035,25 +7073,13 @@ } } - loader = gdk_pixbuf_loader_new(); - gdk_pixbuf_loader_write(loader, data, len, NULL); - gdk_pixbuf_loader_close(loader, &err); - + gtkconv->u.im->anim = pidgin_pixbuf_anim_from_data(data, len); purple_imgstore_unref(custom_img); - anim = gdk_pixbuf_loader_get_animation(loader); - if (anim) - g_object_ref(G_OBJECT(anim)); - g_object_unref(loader); - - if (!anim) + if (!gtkconv->u.im->anim) { + purple_debug_error("gtkconv", "Couldn't load icon for conv %s\n", + purple_conversation_get_name(conv)); return; - gtkconv->u.im->anim = anim; - - if (err) { - purple_debug(PURPLE_DEBUG_ERROR, "gtkconv", - "Buddy icon error: %s\n", err->message); - g_error_free(err); } if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) { @@ -7857,6 +7883,8 @@ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline", FALSE); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck", TRUE); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", TRUE); + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys", TRUE); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size", 96); purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines", 2); purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/show_timestamps", TRUE); @@ -8083,6 +8111,9 @@ purple_signal_connect(purple_conversations_get_handle(), "cleared-message-history", handle, G_CALLBACK(clear_conversation_scrollback_cb), NULL); + purple_signal_connect(purple_conversations_get_handle(), "deleting-chat-buddy", + handle, G_CALLBACK(deleting_chat_buddy_cb), NULL); + purple_conversations_set_ui_ops(&conversation_ui_ops); hidden_convwin = pidgin_conv_window_new(); diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkdialogs.c Thu Aug 11 15:01:34 2011 +0000 @@ -31,6 +31,7 @@ #include "debug.h" #include "notify.h" +#include "plugin.h" #include "prpl.h" #include "request.h" #include "util.h" @@ -433,7 +434,7 @@ /* Generate a logo with a version number */ filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "logo.png", NULL); - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + pixbuf = pidgin_pixbuf_new_from_file(filename); g_free(filename); #if 0 /* Don't versionize the logo when the logo has the version in it */ @@ -787,6 +788,52 @@ g_free(tmp); } +void pidgin_dialogs_plugins_info(void) +{ + GString *str; + GList *l = NULL; + PurplePlugin *plugin = NULL; + char *title = g_strdup_printf(_("%s Plugin Information"), PIDGIN_NAME); + char *pname = NULL, *pauthor = NULL; + const char *pver, *pwebsite, *pid; + gboolean ploaded, punloadable; + static GtkWidget *plugins_info = NULL; + + str = g_string_sized_new(4096); + + g_string_append_printf(str, "%s
", + _("Plugin Information")); + + for(l = purple_plugins_get_all(); l; l = l->next) { + plugin = (PurplePlugin *)l->data; + + pname = g_markup_escape_text(purple_plugin_get_name(plugin), -1); + pauthor = g_markup_escape_text(purple_plugin_get_author(plugin), -1); + pver = purple_plugin_get_version(plugin); + pwebsite = purple_plugin_get_homepage(plugin); + pid = purple_plugin_get_id(plugin); + punloadable = purple_plugin_is_unloadable(plugin); + ploaded = purple_plugin_is_loaded(plugin); + + g_string_append_printf(str, + "%s
" + "\tAuthor: %s
\tVersion: %s
" + "\tWebsite: %s
\tID String: %s
" + "\tLoadable: %s
\tLoaded: %s
" + "
", pname, pauthor ? pauthor : "(null)", + pver, pwebsite, pid, + punloadable ? "No" : "Yes", + ploaded ? "Yes" : "No"); + } + + plugins_info = pidgin_build_help_dialog(title, "plugins_info", str); + g_signal_connect(G_OBJECT(plugins_info), "destroy", + G_CALLBACK(gtk_widget_destroyed), &plugins_info); + g_free(title); + g_free(pname); + g_free(pauthor); +} + static void pidgin_dialogs_im_cb(gpointer data, PurpleRequestFields *fields) { diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkdialogs.h --- a/pidgin/gtkdialogs.h Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkdialogs.h Thu Aug 11 15:01:34 2011 +0000 @@ -36,6 +36,7 @@ void pidgin_dialogs_buildinfo(void); void pidgin_dialogs_developers(void); void pidgin_dialogs_translators(void); +void pidgin_dialogs_plugins_info(void); void pidgin_dialogs_im(void); void pidgin_dialogs_im_with_user(PurpleAccount *, const char *); void pidgin_dialogs_info(void); diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkft.c --- a/pidgin/gtkft.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkft.c Thu Aug 11 15:01:34 2011 +0000 @@ -1149,8 +1149,8 @@ if (purple_xfer_get_size(xfer) <= PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL) { GdkPixbuf *thumbnail = - gdk_pixbuf_new_from_file_at_size( - purple_xfer_get_local_filename(xfer), 128, 128, NULL); + pidgin_pixbuf_new_from_file_at_size( + purple_xfer_get_local_filename(xfer), 128, 128); if (thumbnail) { gchar **formats_split = g_strsplit(formats, ",", 0); diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkimhtml.c Thu Aug 11 15:01:34 2011 +0000 @@ -5057,16 +5057,8 @@ data = imhtml->funcs->image_get_data(image); len = imhtml->funcs->image_get_size(image); - - if (data && len) { - GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); - gdk_pixbuf_loader_write(loader, data, len, NULL); - gdk_pixbuf_loader_close(loader, NULL); - anim = gdk_pixbuf_loader_get_animation(loader); - if (anim) - g_object_ref(G_OBJECT(anim)); - g_object_unref(G_OBJECT(loader)); - } + if (data && len) + anim = pidgin_pixbuf_anim_from_data(data, len); } @@ -5699,18 +5691,19 @@ static void gtk_custom_smiley_size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer data) { -#define CUSTOM_SMILEY_SIZE 96 /* XXX: Should this be a theme setting? */ - if (width <= CUSTOM_SMILEY_SIZE && height <= CUSTOM_SMILEY_SIZE) - return; - - if (width >= height) { - height = height * CUSTOM_SMILEY_SIZE / width; - width = CUSTOM_SMILEY_SIZE; - } else { - width = width * CUSTOM_SMILEY_SIZE / height; - height = CUSTOM_SMILEY_SIZE; + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys")) { + int custom_smileys_size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size"); + if (width <= custom_smileys_size && height <= custom_smileys_size) + return; + + if (width >= height) { + height = height * custom_smileys_size / width; + width = custom_smileys_size; + } else { + width = width * custom_smileys_size / height; + height = custom_smileys_size; + } } - gdk_pixbuf_loader_set_size(loader, width, height); } diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkmain.c --- a/pidgin/gtkmain.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkmain.c Thu Aug 11 15:01:34 2011 +0000 @@ -270,7 +270,7 @@ /* use the nice PNG icon for all the windows */ for(i=0; iparticipant == NULL) +#if GTK_CHECK_VERSION(2, 14, 0) window = gtk_widget_get_window(priv->local_video); +#else + window = (priv->local_video)->window; +#endif else { GtkWidget *widget = pidgin_media_get_widget(data->gtkmedia, data->session_id, data->participant); if (widget) +#if GTK_CHECK_VERSION(2, 14, 0) window = gtk_widget_get_window(widget); +#else + window = widget->window; +#endif } if (window) { diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkprefs.c Thu Aug 11 15:01:34 2011 +0000 @@ -382,7 +382,7 @@ * LEAK - Gentoo memprof thinks pixbuf is leaking here... but it * looks like it should be ok to me. Anyone know what's up? --Mark */ - pixbuf = (theme->icon ? gdk_pixbuf_new_from_file(theme->icon, NULL) : NULL); + pixbuf = (theme->icon ? pidgin_pixbuf_new_from_file(theme->icon) : NULL); gtk_list_store_set(prefs_smiley_themes, &iter, 0, pixbuf, @@ -452,7 +452,7 @@ image_full = purple_theme_get_image_full(theme); if (image_full != NULL){ - pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); + pixbuf = pidgin_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE); g_free(image_full); } else pixbuf = NULL; @@ -473,7 +473,7 @@ image_full = purple_theme_get_image_full(theme); if (image_full != NULL){ - pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); + pixbuf = pidgin_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE); g_free(image_full); } else pixbuf = NULL; @@ -529,7 +529,7 @@ purple_theme_manager_refresh(); tmp = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL); - pixbuf = gdk_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL); + pixbuf = pidgin_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE); g_free(tmp); /* sound themes */ @@ -1448,6 +1448,9 @@ GtkWidget *iconpref2; GtkWidget *imhtml; GtkWidget *frame; + GtkWidget *hbox; + GtkWidget *checkbox; + GtkWidget *spin_button; ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER); @@ -1480,6 +1483,24 @@ #ifdef _WIN32 pidgin_prefs_checkbox(_("F_lash window when IMs are received"), PIDGIN_PREFS_ROOT "/win32/blink_im", vbox); #endif + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + + checkbox = pidgin_prefs_checkbox(_("Resize incoming custom smileys"), + PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys", hbox); + + spin_button = pidgin_prefs_labeled_spin_button(hbox, + _("Maximum size:"), + PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size", + 16, 512, NULL); + + if (!purple_prefs_get_bool( + PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys")) + gtk_widget_set_sensitive(GTK_WIDGET(spin_button), FALSE); + + g_signal_connect(G_OBJECT(checkbox), "clicked", + G_CALLBACK(pidgin_toggle_sensitive), spin_button); + + pidgin_add_widget_to_vbox(GTK_BOX(vbox), NULL, NULL, hbox, TRUE, NULL); pidgin_prefs_labeled_spin_button(vbox, _("Minimum input area height in lines:"), diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkrequest.c --- a/pidgin/gtkrequest.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkrequest.c Thu Aug 11 15:01:34 2011 +0000 @@ -653,35 +653,30 @@ /* Dialog icon. */ if (icon_data) { - GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); - GdkPixbuf *pixbuf = NULL; - if (gdk_pixbuf_loader_write(loader, icon_data, icon_size, NULL)) { - pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); - if (pixbuf) { - /* scale the image if it is too large */ - int width = gdk_pixbuf_get_width(pixbuf); - int height = gdk_pixbuf_get_height(pixbuf); - if (width > 128 || height > 128) { - int scaled_width = width > height ? 128 : (128 * width) / height; - int scaled_height = height > width ? 128 : (128 * height) / width; - GdkPixbuf *scaled = - gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height, - GDK_INTERP_BILINEAR); + GdkPixbuf *pixbuf = pidgin_pixbuf_from_data(icon_data, icon_size); + if (pixbuf) { + /* scale the image if it is too large */ + int width = gdk_pixbuf_get_width(pixbuf); + int height = gdk_pixbuf_get_height(pixbuf); + if (width > 128 || height > 128) { + int scaled_width = width > height ? 128 : (128 * width) / height; + int scaled_height = height > width ? 128 : (128 * height) / width; + GdkPixbuf *scaled = + gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height, + GDK_INTERP_BILINEAR); - purple_debug_info("pidgin", - "dialog icon was too large, scale it down\n"); - if (scaled) { - g_object_unref(pixbuf); - pixbuf = scaled; - } + purple_debug_info("pidgin", + "dialog icon was too large, scaled it down\n"); + if (scaled) { + g_object_unref(pixbuf); + pixbuf = scaled; } - img = gtk_image_new_from_pixbuf(pixbuf); } + img = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); } else { purple_debug_info("pidgin", "failed to parse dialog icon\n"); } - gdk_pixbuf_loader_close(loader, NULL); - g_object_unref(loader); } if (!img) { @@ -1016,22 +1011,17 @@ { GtkWidget *widget; GdkPixbuf *buf, *scale; - GdkPixbufLoader *loader; - loader = gdk_pixbuf_loader_new(); - gdk_pixbuf_loader_write(loader, - (const guchar *)purple_request_field_image_get_buffer(field), - purple_request_field_image_get_size(field), - NULL); - gdk_pixbuf_loader_close(loader, NULL); - buf = gdk_pixbuf_loader_get_pixbuf(loader); + buf = pidgin_pixbuf_from_data( + (const guchar *)purple_request_field_image_get_buffer(field), + purple_request_field_image_get_size(field)); scale = gdk_pixbuf_scale_simple(buf, purple_request_field_image_get_scale_x(field) * gdk_pixbuf_get_width(buf), purple_request_field_image_get_scale_y(field) * gdk_pixbuf_get_height(buf), GDK_INTERP_BILINEAR); widget = gtk_image_new_from_pixbuf(scale); - g_object_unref(G_OBJECT(loader)); + g_object_unref(G_OBJECT(buf)); g_object_unref(G_OBJECT(scale)); return widget; @@ -1132,7 +1122,7 @@ GdkPixbuf* pixbuf = NULL; if (icon_path) - pixbuf = gdk_pixbuf_new_from_file(icon_path, NULL); + pixbuf = pidgin_pixbuf_new_from_file(icon_path); gtk_list_store_set(store, &iter, 0, purple_request_field_list_get_data(field, text), diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtksmiley.c --- a/pidgin/gtksmiley.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtksmiley.c Thu Aug 11 15:01:34 2011 +0000 @@ -332,7 +332,7 @@ g_free(s->filename); s->filename = g_strdup(filename); - pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, 64, 64, FALSE, NULL); + pixbuf = pidgin_pixbuf_new_from_file_at_scale(filename, 64, 64, FALSE); gtk_image_set_from_pixbuf(GTK_IMAGE(s->smiley_image), pixbuf); if (pixbuf) g_object_unref(G_OBJECT(pixbuf)); @@ -690,7 +690,6 @@ FILE *f; gchar *path; size_t wc; - GError *err = NULL; PidginSmiley *ps; GdkPixbuf *image; @@ -709,13 +708,11 @@ } fclose(f); - image = gdk_pixbuf_new_from_file(path, &err); + image = pidgin_pixbuf_new_from_file(path); g_unlink(path); g_free(path); - if (err) { - g_error_free(err); + if (!image) return; - } ps = pidgin_smiley_edit(dialog->window, NULL); pidgin_smiley_editor_set_image(ps, image); diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkstatusbox.c --- a/pidgin/gtkstatusbox.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkstatusbox.c Thu Aug 11 15:01:34 2011 +0000 @@ -2225,22 +2225,45 @@ if (status_box->buddy_icon_img != NULL) { - GdkPixbuf *buf, *scale; - int scale_width, scale_height; - GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + GdkPixbufLoader *loader; + GError *error = NULL; + + 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); - 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; + if (!gdk_pixbuf_loader_write(loader, + purple_imgstore_get_data(status_box->buddy_icon_img), + purple_imgstore_get_size(status_box->buddy_icon_img), + &error) || error) + { + purple_debug_warning("gtkstatusbox", "gdk_pixbuf_loader_write() " + "failed with size=%zu: %s\n", + purple_imgstore_get_size(status_box->buddy_icon_img), + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + } else if (!gdk_pixbuf_loader_close(loader, &error) || error) { + purple_debug_warning("gtkstatusbox", "gdk_pixbuf_loader_close() " + "failed for image of size %zu: %s\n", + purple_imgstore_get_size(status_box->buddy_icon_img), + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + } else { + GdkPixbuf *buf, *scale; + int scale_width, scale_height; + + 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); } diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkutils.c --- a/pidgin/gtkutils.c Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkutils.c Thu Aug 11 15:01:34 2011 +0000 @@ -615,7 +615,7 @@ tmp, NULL); g_free(tmp); - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + pixbuf = pidgin_pixbuf_new_from_file(filename); g_free(filename); return pixbuf; @@ -704,7 +704,7 @@ "16", "google-talk.png", NULL); GtkWidget *item; - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + pixbuf = pidgin_pixbuf_new_from_file(filename); g_free(filename); gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu), @@ -723,7 +723,7 @@ "16", "facebook.png", NULL); GtkWidget *item; - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + pixbuf = pidgin_pixbuf_new_from_file(filename); g_free(filename); gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu), @@ -1593,7 +1593,7 @@ } /* Are we dealing with an image? */ - pb = gdk_pixbuf_new_from_file(filename, NULL); + pb = pidgin_pixbuf_new_from_file(filename); if (pb) { _DndData *data = g_malloc(sizeof(_DndData)); gboolean ft = FALSE, im = FALSE; @@ -2265,7 +2265,7 @@ filename = gtk_file_chooser_get_preview_filename( GTK_FILE_CHOOSER(dialog->icon_filesel)); - if (!filename || g_stat(filename, &st) || !(pixbuf = gdk_pixbuf_new_from_file(filename, NULL))) + if (!filename || g_stat(filename, &st) || !(pixbuf = pidgin_pixbuf_new_from_file(filename))) { gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL); gtk_label_set_markup(GTK_LABEL(dialog->icon_text), ""); @@ -3086,17 +3086,134 @@ #endif } -GdkPixbuf * pidgin_pixbuf_from_imgstore(PurpleStoredImage *image) +static GObject *pidgin_pixbuf_from_data_helper(const guchar *buf, gsize count, gboolean animated) +{ + GObject *pixbuf; + GdkPixbufLoader *loader; + GError *error = NULL; + + loader = gdk_pixbuf_loader_new(); + + if (!gdk_pixbuf_loader_write(loader, buf, count, &error) || error) { + purple_debug_warning("gtkutils", "gdk_pixbuf_loader_write() " + "failed with size=%zu: %s\n", count, + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + g_object_unref(G_OBJECT(loader)); + return NULL; + } + + if (!gdk_pixbuf_loader_close(loader, &error) || error) { + purple_debug_warning("gtkutils", "gdk_pixbuf_loader_close() " + "failed for image of size %zu: %s\n", count, + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + g_object_unref(G_OBJECT(loader)); + return NULL; + } + + if (animated) + pixbuf = G_OBJECT(gdk_pixbuf_loader_get_animation(loader)); + else + pixbuf = G_OBJECT(gdk_pixbuf_loader_get_pixbuf(loader)); + if (!pixbuf) { + purple_debug_warning("gtkutils", "%s() returned NULL for image " + "of size %zu\n", + animated ? "gdk_pixbuf_loader_get_animation" + : "gdk_pixbuf_loader_get_pixbuf", count); + g_object_unref(G_OBJECT(loader)); + return NULL; + } + + g_object_ref(pixbuf); + g_object_unref(G_OBJECT(loader)); + + return pixbuf; +} + +GdkPixbuf *pidgin_pixbuf_from_data(const guchar *buf, gsize count) +{ + return GDK_PIXBUF(pidgin_pixbuf_from_data_helper(buf, count, FALSE)); +} + +GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count) +{ + return GDK_PIXBUF_ANIMATION(pidgin_pixbuf_from_data_helper(buf, count, TRUE)); +} + +GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image) +{ + return pidgin_pixbuf_from_data(purple_imgstore_get_data(image), + purple_imgstore_get_size(image)); +} + +GdkPixbuf *pidgin_pixbuf_new_from_file(const gchar *filename) { GdkPixbuf *pixbuf; - GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); - gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(image), - purple_imgstore_get_size(image), NULL); - gdk_pixbuf_loader_close(loader, NULL); - pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); - if (pixbuf) - g_object_ref(pixbuf); - g_object_unref(loader); + GError *error = NULL; + + pixbuf = gdk_pixbuf_new_from_file(filename, &error); + if (!pixbuf || error) { + purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file() " + "returned %s for file %s: %s\n", + pixbuf ? "something" : "nothing", + filename, + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + if (pixbuf) + g_object_unref(G_OBJECT(pixbuf)); + return NULL; + } + + return pixbuf; +} + +GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width, int height) +{ + GdkPixbuf *pixbuf; + GError *error = NULL; + + pixbuf = gdk_pixbuf_new_from_file_at_size(filename, + width, height, &error); + if (!pixbuf || error) { + purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file_at_size() " + "returned %s for file %s: %s\n", + pixbuf ? "something" : "nothing", + filename, + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + if (pixbuf) + g_object_unref(G_OBJECT(pixbuf)); + return NULL; + } + + return pixbuf; +} + +GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width, int height, gboolean preserve_aspect_ratio) +{ + GdkPixbuf *pixbuf; + GError *error = NULL; + + pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, + width, height, preserve_aspect_ratio, &error); + if (!pixbuf || error) { + purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file_at_scale() " + "returned %s for file %s: %s\n", + pixbuf ? "something" : "nothing", + filename, + error ? error->message : "(no error message)"); + if (error) + g_error_free(error); + if (pixbuf) + g_object_unref(G_OBJECT(pixbuf)); + return NULL; + } + return pixbuf; } diff -r 3951afa56940 -r 98ae4b8b592f pidgin/gtkutils.h --- a/pidgin/gtkutils.h Sun Jun 26 21:03:22 2011 +0000 +++ b/pidgin/gtkutils.h Thu Aug 11 15:01:34 2011 +0000 @@ -834,6 +834,32 @@ GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label); /** + * Create a GdkPixbuf from a chunk of image data. + * + * @param buf The raw binary image data. + * @param count The length of buf in bytes. + * + * @return A GdkPixbuf created from the image data, or NULL if + * there was an error parsing the data. + * + * @since 2.9.0 + */ +GdkPixbuf *pidgin_pixbuf_from_data(const guchar *buf, gsize count); + +/** + * Create a GdkPixbufAnimation from a chunk of image data. + * + * @param buf The raw binary image data. + * @param count The length of buf in bytes. + * + * @return A GdkPixbufAnimation created from the image data, or NULL if + * there was an error parsing the data. + * + * @since 2.9.0 + */ +GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count); + +/** * Create a GdkPixbuf from a PurpleStoredImage. * * @param image A PurpleStoredImage. @@ -845,6 +871,86 @@ GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image); /** + * Helper function that calls gdk_pixbuf_new_from_file() and checks both + * the return code and the GError and returns NULL if either one failed. + * + * The gdk-pixbuf documentation implies that it is sufficient to check + * the return value of gdk_pixbuf_new_from_file() to determine + * whether the image was able to be loaded. However, this is not the case + * with gdk-pixbuf 2.23.3 and probably many earlier versions. In some + * cases a GdkPixbuf object is returned that will cause some operations + * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an + * infinite loop. + * + * This function shouldn't be necessary once Pidgin requires a version of + * gdk-pixbuf where the aforementioned bug is fixed. However, it might be + * nice to keep this function around for the debug message that it logs. + * + * @param filename Name of file to load, in the GLib file name encoding + * + * @return The GdkPixbuf if successful. Otherwise NULL is returned and + * a warning is logged. + * + * @since 2.9.0 + */ +GdkPixbuf *pidgin_pixbuf_new_from_file(const char *filename); + +/** + * Helper function that calls gdk_pixbuf_new_from_file_at_size() and checks + * both the return code and the GError and returns NULL if either one failed. + * + * The gdk-pixbuf documentation implies that it is sufficient to check + * the return value of gdk_pixbuf_new_from_file_at_size() to determine + * whether the image was able to be loaded. However, this is not the case + * with gdk-pixbuf 2.23.3 and probably many earlier versions. In some + * cases a GdkPixbuf object is returned that will cause some operations + * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an + * infinite loop. + * + * This function shouldn't be necessary once Pidgin requires a version of + * gdk-pixbuf where the aforementioned bug is fixed. However, it might be + * nice to keep this function around for the debug message that it logs. + * + * @param filename Name of file to load, in the GLib file name encoding + * @param width The width the image should have or -1 to not constrain the width + * @param height The height the image should have or -1 to not constrain the height + * + * @return The GdkPixbuf if successful. Otherwise NULL is returned and + * a warning is logged. + * + * @since 2.9.0 + */ +GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width, int height); + +/** + * Helper function that calls gdk_pixbuf_new_from_file_at_scale() and checks + * both the return code and the GError and returns NULL if either one failed. + * + * The gdk-pixbuf documentation implies that it is sufficient to check + * the return value of gdk_pixbuf_new_from_file_at_scale() to determine + * whether the image was able to be loaded. However, this is not the case + * with gdk-pixbuf 2.23.3 and probably many earlier versions. In some + * cases a GdkPixbuf object is returned that will cause some operations + * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an + * infinite loop. + * + * This function shouldn't be necessary once Pidgin requires a version of + * gdk-pixbuf where the aforementioned bug is fixed. However, it might be + * nice to keep this function around for the debug message that it logs. + * + * @param filename Name of file to load, in the GLib file name encoding + * @param width The width the image should have or -1 to not constrain the width + * @param height The height the image should have or -1 to not constrain the height + * @param preserve_aspect_ratio TRUE to preserve the image's aspect ratio + * + * @return The GdkPixbuf if successful. Otherwise NULL is returned and + * a warning is logged. + * + * @since 2.9.0 + */ +GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width, int height, gboolean preserve_aspect_ratio); + +/** * Add scrollbars to a widget * @param widget The child widget * @hscrollbar_policy Horizontal scrolling policy diff -r 3951afa56940 -r 98ae4b8b592f po/ChangeLog --- a/po/ChangeLog Sun Jun 26 21:03:22 2011 +0000 +++ b/po/ChangeLog Thu Aug 11 15:01:34 2011 +0000 @@ -1,5 +1,11 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.9.1 + * German translation updated (Björn Voigt, Jochen Kemnade) + +version 2.9.0 + * No changes + version 2.8.0 * Albanian translation updated (Besnik Bleta) * Bengali translation updated (Jamil Ahmed) diff -r 3951afa56940 -r 98ae4b8b592f po/de.po --- a/po/de.po Sun Jun 26 21:03:22 2011 +0000 +++ b/po/de.po Thu Aug 11 15:01:34 2011 +0000 @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-05-12 09:37+0200\n" -"PO-Revision-Date: 2011-05-12 09:37+0200\n" +"POT-Creation-Date: 2011-08-11 10:27+0200\n" +"PO-Revision-Date: 2011-08-11 10:12+0200\n" "Last-Translator: Björn Voigt \n" "Language-Team: German \n" "Language: de\n" @@ -1959,7 +1959,7 @@ msgstr "Unbekannter Grund" msgid "Aborting DNS lookup in Tor Proxy mode." -msgstr "DNS-Anfrage im Tor-Proxy-Modus abgebrochen" +msgstr "DNS-Anfrage im Tor-Proxy-Modus abgebrochen." #, c-format msgid "" @@ -4778,8 +4778,8 @@ msgstr "" "invite <Benutzer> [Nachricht]: Lade einen Benutzer in den Raum ein." -msgid "join: <room> [password]: Join a chat on this server." -msgstr "join: <Raum> [Passwort]: Betrete einen Chat auf diesem Server." +msgid "join: <room[@server]> [password]: Join a chat." +msgstr "join: <Raum[@Server]> [Passwort]: Betrete einen Chat." msgid "kick <user> [reason]: Kick a user from the room." msgstr "kick <Benutzer> [Grund]: Kickt einen Benutzer aus dem Raum." @@ -6361,13 +6361,16 @@ msgstr "Wo ich wohne" #, c-format -msgid "You have %i suggested friends." -msgstr "Sie haben %i vorgeschlagene(n) Freund(e)." - -#, c-format -msgid "We found %i contacts that match your search." -msgstr "" -"Wir fanden %i Kontakt(e), die Ihrer Suchanfrage entspricht/entsprechen." +msgid "You have %i suggested friend." +msgid_plural "You have %i suggested friends." +msgstr[0] "Sie haben %i vorgeschlagenen Freund." +msgstr[1] "Sie haben %i vorgeschlagene Freunde." + +#, c-format +msgid "We found %i contact that matches your search." +msgid_plural "We found %i contacts that match your search." +msgstr[0] "Wir fanden %i Kontakt, der Ihrer Suchanfrage entspricht." +msgstr[1] "Wir fanden %i Kontakte, die Ihrer Suchanfrage entsprechen." #. we must have lost the connection, so terminate it so that we can reconnect msgid "We have lost the connection to MXit. Please reconnect." @@ -11131,6 +11134,9 @@ msgid "/Help/De_veloper Information" msgstr "/Hilfe/_Entwickler-Informationen" +msgid "/Help/_Plugin Information" +msgstr "/Hilfe/_Plugin-Informationen" + msgid "/Help/_Translator Information" msgstr "/Hilfe/Über_setzer-Informationen" @@ -12146,6 +12152,13 @@ msgid "%s Translator Information" msgstr "%s-Übersetzer-Informationen" +#, c-format +msgid "%s Plugin Information" +msgstr "%s Plugin-Informationen" + +msgid "Plugin Information" +msgstr "Plugin-Informationen" + msgid "_Name" msgstr "_Name" @@ -12747,7 +12760,7 @@ #, c-format msgid "Exiting because another libpurple client is already running.\n" -msgstr "Wird geschlossen, da bereits ein anderer libpurple-Client läuft\n" +msgstr "Wird geschlossen, da bereits ein anderer libpurple-Client läuft.\n" msgid "_Media" msgstr "_Medien" @@ -13113,6 +13126,12 @@ msgid "F_lash window when IMs are received" msgstr "Fenster b_linkt, wenn IM-Nachrichten empfangen werden" +msgid "Resize incoming custom smileys" +msgstr "Erhaltene benutzerdefinierte Smileys skalieren" + +msgid "Maximum size:" +msgstr "Maximale Größe:" + msgid "Minimum input area height in lines:" msgstr "Minimale Höhe des Eingabefeldes in Zeilen:" @@ -13182,6 +13201,9 @@ msgid "_UDP Port:" msgstr "_UDP-Port:" +msgid "T_CP Port:" +msgstr "T_CP-Port:" + msgid "Use_rname:" msgstr "_Benutzername:"