# HG changeset patch # User Yoshiki Yazawa # Date 1309022821 -32400 # Node ID accce7b797372a74fe535d5cc7b097bdae53d20c # Parent fa9d736e9937395a6458074749b447d257c25bb2# Parent a99a4f9b7171c426cb9f3d856e45bdec9a9d05ab merged from im.pidgin.pidgin diff -r fa9d736e9937 -r accce7b79737 ChangeLog --- a/ChangeLog Sun Jun 12 01:39:24 2011 +0900 +++ b/ChangeLog Sun Jun 26 02:27:01 2011 +0900 @@ -1,6 +1,34 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.8.1 (MM/DD/YYYY): +version 2.9.1 (MM/DD/YYYY): + Gadu-Gadu: + * Fixed searching for buddies in public directory. (Tomasz Wasilczyk) + (#5242) + * Better status message handling. (Tomasz Wasilczyk) (#14314) + +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: + * 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. version 2.8.0 (06/07/2011): General: @@ -84,7 +112,7 @@ birthdays. (Dustin Gathmann) (#13533) * It is now possible to specify multiple encodings on the Advanced tab of an ICQ account's settings by using a comma-delimited list. - (Dmitry Utkin (#13496) + (Dmitry Utkin) (#13496) IRC: * Add "authserv" service command. (tomos) (#13337) diff -r fa9d736e9937 -r accce7b79737 ChangeLog.API --- a/ChangeLog.API Sun Jun 12 01:39:24 2011 +0900 +++ b/ChangeLog.API Sun Jun 26 02:27:01 2011 +0900 @@ -1,5 +1,22 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +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 fa9d736e9937 -r accce7b79737 configure.ac --- a/configure.ac Sun Jun 12 01:39:24 2011 +0900 +++ b/configure.ac Sun Jun 26 02:27:01 2011 +0900 @@ -43,9 +43,9 @@ # # Make sure to update finch/libgnt/configure.ac with libgnt version changes. # -m4_define([purple_lt_current], [8]) +m4_define([purple_lt_current], [9]) m4_define([purple_major_version], [2]) -m4_define([purple_minor_version], [8]) +m4_define([purple_minor_version], [9]) m4_define([purple_micro_version], [1]) m4_define([purple_version_suffix], [devel]) m4_define([purple_version], @@ -565,15 +565,12 @@ if test "x$enable_gevolution" = "xyes"; then evo_deps="libebook-1.2 libedata-book-1.2" PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, , [ - AC_MSG_RESULT(yes) enable_gevolution="no" ]) if test "x$enable_gevolution" = "xno"; then evo_deps="libebook-1.0 libedata-book-1.0" PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, [ enable_gevolution="yes" - ], [ - AC_MSG_RESULT(yes) ]) fi if test "x$enable_gevolution" = "xyes"; then diff -r fa9d736e9937 -r accce7b79737 finch/finch.c --- a/finch/finch.c Sun Jun 12 01:39:24 2011 +0900 +++ b/finch/finch.c Sun Jun 26 02:27:01 2011 +0900 @@ -74,11 +74,28 @@ * account "markdoliner." Please don't use this key for other * applications. You can either not specify a client key, in * which case the default "libpurple" key will be used, or you - * can register for your own client key at - * http://developer.aim.com/manageKeys.jsp + * can try to register your own at the AIM or ICQ web sites + * (although this functionality was removed at some point, it's + * possible it has been re-added). AOL's old key management + * page is http://developer.aim.com/manageKeys.jsp */ g_hash_table_insert(ui_info, "prpl-aim-clientkey", "ma19sqWV9ymU6UYc"); - g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma19sqWV9ymU6UYc"); + + /* + * This is the client key for "Pidgin." It is owned by the AIM + * account "markdoliner." Please don't use this key for other + * applications. You can either not specify a client key, in + * which case the default "libpurple" key will be used, or you + * can try to register your own at the AIM or ICQ web sites + * (although this functionality was removed at some point, it's + * possible it has been re-added). AOL's old key management + * page is http://developer.aim.com/manageKeys.jsp + * + * We used to have a Finch-specific devId/clientkey + * (ma19sqWV9ymU6UYc), but it stopped working, so we switched + * to this one. + */ + g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma1cSASNCKFtrdv9"); /* * This is the distid for Finch, given to us by AOL. Please diff -r fa9d736e9937 -r accce7b79737 libpurple/Makefile.am --- a/libpurple/Makefile.am Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/Makefile.am Sun Jun 26 02:27:01 2011 +0900 @@ -156,7 +156,6 @@ theme-manager.h \ upnp.h \ util.h \ - valgrind.h \ value.h \ xmlnode.h \ whiteboard.h diff -r fa9d736e9937 -r accce7b79737 libpurple/account.c diff -r fa9d736e9937 -r accce7b79737 libpurple/conversation.c --- a/libpurple/conversation.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/conversation.c Sun Jun 26 02:27:01 2011 +0900 @@ -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); @@ -1691,9 +1712,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); @@ -1785,8 +1806,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); @@ -1794,8 +1816,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); } @@ -1888,8 +1910,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); } @@ -1969,8 +1991,11 @@ purple_conv_chat_cb_destroy(cb); } + g_hash_table_remove_all(chat->users); + chat->users = NULL; + g_list_free(users); - purple_conv_chat_set_users(chat, NULL); + chat->in_room = NULL; } @@ -2160,19 +2185,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 @@ -2181,6 +2197,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); @@ -2587,6 +2606,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 fa9d736e9937 -r accce7b79737 libpurple/conversation.h --- a/libpurple/conversation.h Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/conversation.h Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 libpurple/media/backend-fs2.c --- a/libpurple/media/backend-fs2.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/media/backend-fs2.c Sun Jun 26 02:27:01 2011 +0900 @@ -1823,7 +1823,7 @@ port = purple_prefs_get_int("/purple/network/turn_port_tcp"); if (port > 0) { relay_info = append_relay_info(relay_info, turn_ip, port, username, - password, "udp"); + password, "tcp"); } /* TURN over SSL is only supported by libnice for Google's "psuedo" SSL mode diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/gg/gg.c --- a/libpurple/protocols/gg/gg.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/gg/gg.c Sun Jun 26 02:27:01 2011 +0900 @@ -487,15 +487,16 @@ GGPSearchForm *form = user_data; guint32 seq; - g_free(form->offset); - form->offset = g_strdup(form->last_uin); + form->page_number++; ggp_search_remove(info->searches, form->seq); - purple_debug_info("gg", "ggp_callback_show_next(): Removed seq %u", form->seq); + purple_debug_info("gg", "ggp_callback_show_next(): Removed seq %u\n", + form->seq); seq = ggp_search_start(gc, form); ggp_search_add(info->searches, seq, form); - purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u", seq); + purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u\n", + seq); } static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data) @@ -526,21 +527,16 @@ form = ggp_search_form_new(GGP_SEARCH_TYPE_FULL); form->user_data = info; - form->lastname = charset_convert( - purple_request_fields_get_string(fields, "lastname"), - "UTF-8", "CP1250"); - form->firstname = charset_convert( - purple_request_fields_get_string(fields, "firstname"), - "UTF-8", "CP1250"); - form->nickname = charset_convert( - purple_request_fields_get_string(fields, "nickname"), - "UTF-8", "CP1250"); - form->city = charset_convert( - purple_request_fields_get_string(fields, "city"), - "UTF-8", "CP1250"); - form->birthyear = charset_convert( - purple_request_fields_get_string(fields, "year"), - "UTF-8", "CP1250"); + form->lastname = g_strdup( + purple_request_fields_get_string(fields, "lastname")); + form->firstname = g_strdup( + purple_request_fields_get_string(fields, "firstname")); + form->nickname = g_strdup( + purple_request_fields_get_string(fields, "nickname")); + form->city = g_strdup( + purple_request_fields_get_string(fields, "city")); + form->birthyear = g_strdup( + purple_request_fields_get_string(fields, "year")); switch (purple_request_fields_get_choice(fields, "gender")) { case 1: @@ -557,11 +553,10 @@ form->active = purple_request_fields_get_bool(fields, "active") ? g_strdup(GG_PUBDIR50_ACTIVE_TRUE) : NULL; - form->offset = g_strdup("0"); - seq = ggp_search_start(gc, form); ggp_search_add(info->searches, seq, form); - purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u", seq); + purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u\n", + seq); } static void ggp_find_buddies(PurplePluginAction *action) @@ -1076,6 +1071,7 @@ { gchar *from; const char *st; + char *status_msg = NULL; ggp_update_buddy_avatar(gc, uin); @@ -1113,14 +1109,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); } @@ -1131,7 +1137,8 @@ GGPInfo *info = form->user_data; ggp_search_remove(info->searches, form->seq); - purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u", form->seq); + purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u\n", + form->seq); ggp_search_form_destroy(form); } @@ -1246,6 +1253,8 @@ res_count = gg_pubdir50_count(req); res_count = (res_count > PUBDIR_RESULTS_MAX) ? PUBDIR_RESULTS_MAX : res_count; + if (form->page_size == 0) + form->page_size = res_count; results = purple_notify_searchresults_new(); @@ -1255,7 +1264,8 @@ purple_notify_error(gc, NULL, _("Unable to display the search results."), NULL); - ggp_sr_close_cb(form); + if (form->window == NULL) + ggp_sr_close_cb(form); return; } @@ -1297,11 +1307,6 @@ (birth && strncmp(birth, "0", 1)) ? birth : g_strdup("-")); purple_notify_searchresults_row_add(results, row); - - if (i == res_count - 1) { - g_free(form->last_uin); - form->last_uin = ggp_search_get_result(req, i, GG_PUBDIR50_UIN); - } } purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_CONTINUE, @@ -1342,7 +1347,8 @@ seq = gg_pubdir50_seq(req); form = ggp_search_get(info->searches, seq); - purple_debug_info("gg", "ggp_pubdir_reply_handler(): seq %u --> form %p", seq, form); + purple_debug_info("gg", + "ggp_pubdir_reply_handler(): seq %u --> form %p\n", seq, form); /* * this can happen when user will request more results * and close the results window before they arrive. @@ -1355,7 +1361,8 @@ purple_notify_error(gc, NULL, _("No matching users found"), _("There are no users matching your search criteria.")); - ggp_sr_close_cb(form); + if (form->window == NULL) + ggp_sr_close_cb(form); return; } @@ -1914,6 +1921,17 @@ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection failed")); break; + case GG_EVENT_MSG: + if (ev->event.msg.sender == 0) + /* system messages are mostly ads */ + purple_debug_info("gg", "System message:\n%s\n", + ev->event.msg.message); + else + purple_debug_warning("gg", "GG_EVENT_MSG: message from user %u " + "unexpected while connecting:\n%s\n", + ev->event.msg.sender, + ev->event.msg.message); + break; default: purple_debug_error("gg", "strange event: %d\n", ev->type); break; @@ -1938,23 +1956,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) @@ -2371,8 +2384,6 @@ form->user_data = info; form->uin = g_strdup(name); - form->offset = g_strdup("0"); - form->last_uin = g_strdup("0"); seq = ggp_search_start(gc, form); ggp_search_add(info->searches, seq, form); diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/gg/lib/libgadu.h --- a/libpurple/protocols/gg/lib/libgadu.h Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/gg/lib/libgadu.h Sun Jun 26 02:27:01 2011 +0900 @@ -1442,6 +1442,7 @@ int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED; +#undef printf #ifdef __GNUC__ char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED; #else diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/gg/lib/resolver.c --- a/libpurple/protocols/gg/lib/resolver.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/gg/lib/resolver.c Sun Jun 26 02:27:01 2011 +0900 @@ -249,6 +249,7 @@ #endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ } +#if defined(GG_CONFIG_HAVE_PTHREAD) || !defined(_WIN32) /** * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora. * @@ -286,6 +287,7 @@ return res; } +#endif /** * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/gg/search.c --- a/libpurple/protocols/gg/search.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/gg/search.c Sun Jun 26 02:27:01 2011 +0900 @@ -38,6 +38,8 @@ form->window = NULL; form->user_data = NULL; form->seq = 0; + form->page_number = 0; + form->page_size = 0; form->uin = NULL; form->lastname = NULL; @@ -47,8 +49,6 @@ form->birthyear = NULL; form->gender = NULL; form->active = NULL; - form->offset = NULL; - form->last_uin = NULL; return form; } @@ -62,6 +62,8 @@ form->window = NULL; form->user_data = NULL; form->seq = 0; + form->page_number = 0; + form->page_size = 0; g_free(form->uin); g_free(form->lastname); @@ -71,8 +73,6 @@ g_free(form->birthyear); g_free(form->gender); g_free(form->active); - g_free(form->offset); - g_free(form->last_uin); g_free(form); } /* }}} */ @@ -137,7 +137,7 @@ { GGPInfo *info = gc->proto_data; gg_pubdir50_t req; - guint seq; + guint seq, offset; purple_debug_info("gg", "It's time to perform a search...\n"); @@ -187,8 +187,10 @@ } } - purple_debug_info("gg", "offset: %s\n", form->offset); - gg_pubdir50_add(req, GG_PUBDIR50_START, g_strdup(form->offset)); + offset = form->page_size * form->page_number; + purple_debug_info("gg", "page number: %u, page size: %u, offset: %u\n", + form->page_number, form->page_size, offset); + gg_pubdir50_add(req, GG_PUBDIR50_START, g_strdup_printf("%u", offset)); if ((seq = gg_pubdir50(info->session, req)) == 0) { purple_debug_warning("gg", "ggp_bmenu_show_details: Search failed.\n"); diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/gg/search.h --- a/libpurple/protocols/gg/search.h Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/gg/search.h Sun Jun 26 02:27:01 2011 +0900 @@ -46,12 +46,11 @@ char *birthyear; char *gender; char *active; - char *offset; - - char *last_uin; GGPSearchType search_type; guint32 seq; + guint16 page_number; + guint16 page_size; /* how many contacts fits into one page of results */ void *user_data; void *window; diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/irc/irc.c --- a/libpurple/protocols/irc/irc.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/irc/irc.c Sun Jun 26 02:27:01 2011 +0900 @@ -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); diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/irc/irc.h --- a/libpurple/protocols/irc/irc.h Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/irc/irc.h Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/jabber/jabber.c Sun Jun 26 02:27:01 2011 +0900 @@ -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, diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/mxit/profile.c --- a/libpurple/protocols/mxit/profile.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/mxit/profile.c Sun Jun 26 02:27:01 2011 +0900 @@ -322,9 +322,9 @@ purple_notify_searchresults_button_add( results, PURPLE_NOTIFY_BUTTON_INVITE, mxit_search_results_add_cb ); if ( searchType == CP_SUGGEST_FRIENDS ) - text = g_strdup_printf( _( "You have %i suggested friends." ), maxResults ); + text = g_strdup_printf( dngettext( PACKAGE, "You have %i suggested friend.", "You have %i suggested friends.", maxResults ), maxResults ); else - text = g_strdup_printf( _( "We found %i contacts that match your search." ), maxResults ); + text = g_strdup_printf( dngettext( PACKAGE, "We found %i contact that matches your search.", "We found %i contacts that match your search.", maxResults ), maxResults ); purple_notify_searchresults( session->con, NULL, text, NULL, results, NULL, NULL ); diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/oscar/family_feedbag.c --- a/libpurple/protocols/oscar/family_feedbag.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/oscar/family_feedbag.c Sun Jun 26 02:27:01 2011 +0900 @@ -100,7 +100,8 @@ { g_string_append_printf(str, "%s gid=0x%04hx, bid=0x%04hx, list_type=0x%04hx [%s], name=%s.\n", - prefix, item->gid, item->bid, item->type, aim_ssi_type_to_string(item->type), item->name); + prefix, item->gid, item->bid, item->type, aim_ssi_type_to_string(item->type), + item->name ? item->name : "(null)"); } /** diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/oscar/oscar.c Sun Jun 26 02:27:01 2011 +0900 @@ -1465,10 +1465,10 @@ } else if (previous_status != NULL && purple_status_is_available(previous_status)) { itmsurl = g_strdup(purple_status_get_attr_string(previous_status, "itmsurl")); } - purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s', itmsurl = '%s'\n", status_id, info->bn, message, itmsurl); + purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s', itmsurl = '%s'\n", status_id, info->bn, message ? message : "(null)", itmsurl ? itmsurl : "(null)"); purple_prpl_got_user_status(account, info->bn, status_id, "message", message, "itmsurl", itmsurl, NULL); } else { - purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s'\n", status_id, info->bn, message); + purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s'\n", status_id, info->bn, message ? message : "(null)"); purple_prpl_got_user_status(account, info->bn, status_id, "message", message, NULL); } diff -r fa9d736e9937 -r accce7b79737 libpurple/protocols/yahoo/libymsg.c --- a/libpurple/protocols/yahoo/libymsg.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/protocols/yahoo/libymsg.c Sun Jun 26 02:27:01 2011 +0900 @@ -3573,11 +3573,10 @@ status_id = purple_status_get_id(status); msg = purple_status_get_attr_string(status, "message"); - if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) { - if ((msg != NULL) && (*msg != '\0')) - return YAHOO_STATUS_CUSTOM; - else - return YAHOO_STATUS_AVAILABLE; + if ((msg != NULL) && (*msg != '\0')) { + return YAHOO_STATUS_CUSTOM; + } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_AVAILABLE)) { + return YAHOO_STATUS_AVAILABLE; } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BRB)) { return YAHOO_STATUS_BRB; } else if (!strcmp(status_id, YAHOO_STATUS_TYPE_BUSY)) { @@ -4856,6 +4855,11 @@ if (idle) yahoo_packet_hash_str(pkt, 47, "2"); + else if (yd->current_status == YAHOO_STATUS_CUSTOM && + !purple_status_is_available(status)) + /* We are still unavailable in this case. + * Make sure Yahoo knows that */ + yahoo_packet_hash_str(pkt, 47, "1"); yahoo_packet_send_and_free(pkt, yd); @@ -4883,7 +4887,10 @@ type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_BRB, _("Be Right Back"), TRUE); types = g_list_append(types, type); - type = purple_status_type_new(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY, _("Busy"), TRUE); + type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, YAHOO_STATUS_TYPE_BUSY, + _("Busy"), TRUE, TRUE, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, type); type = purple_status_type_new(PURPLE_STATUS_AWAY, YAHOO_STATUS_TYPE_NOTATHOME, _("Not at Home"), TRUE); diff -r fa9d736e9937 -r accce7b79737 libpurple/tests/Makefile.am --- a/libpurple/tests/Makefile.am Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/tests/Makefile.am Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 libpurple/tests/check_libpurple.c --- a/libpurple/tests/check_libpurple.c Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/tests/check_libpurple.c Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 libpurple/tests/test_xmlnode.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/tests/test_xmlnode.c Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 libpurple/tests/tests.h --- a/libpurple/tests/tests.h Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/tests/tests.h Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 libpurple/value.h --- a/libpurple/value.h Sun Jun 12 01:39:24 2011 +0900 +++ b/libpurple/value.h Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkaccount.c Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkblist.c Sun Jun 26 02:27:01 2011 +0900 @@ -2648,7 +2648,6 @@ gboolean scaled, gboolean greyed) { gsize len; - GdkPixbufLoader *loader; PurpleBuddy *buddy = NULL; PurpleGroup *group = NULL; const guchar *data = NULL; @@ -2715,21 +2714,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; @@ -3958,7 +3956,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 fa9d736e9937 -r accce7b79737 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkconv.c Sun Jun 26 02:27:01 2011 +0900 @@ -4051,6 +4051,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; @@ -4058,18 +4068,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); @@ -4079,7 +4091,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); @@ -4124,6 +4137,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); @@ -4419,6 +4441,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; @@ -6195,6 +6223,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) { @@ -6245,12 +6295,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); @@ -6261,20 +6309,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))) @@ -6282,14 +6323,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 @@ -6316,6 +6357,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 { @@ -6355,8 +6397,6 @@ PidginChatPane *gtkchat; GtkTreeIter iter; GtkTreeModel *model; - int f = 1; - char *alias = NULL; chat = PURPLE_CONV_CHAT(conv); gtkconv = PIDGIN_CONVERSATION(conv); @@ -6367,35 +6407,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 @@ -6480,8 +6501,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); @@ -6494,11 +6515,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 @@ -6506,8 +6540,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); @@ -6519,17 +6553,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 @@ -6682,7 +6726,7 @@ if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { - gtk_widget_set_sensitive(win->menu.add, (prpl_info->add_buddy != NULL)); + gtk_widget_set_sensitive(win->menu.add, (prpl_info->add_buddy != NULL) || (prpl_info->add_buddy_with_invite != NULL)); gtk_widget_set_sensitive(win->menu.remove, (prpl_info->remove_buddy != NULL)); gtk_widget_set_sensitive(win->menu.send_file, (prpl_info->send_file != NULL && (!prpl_info->can_receive_file || @@ -7050,10 +7094,6 @@ PurpleBuddy *buddy; - GdkPixbufLoader *loader; - GdkPixbufAnimation *anim; - GError *err = NULL; - PurpleStoredImage *custom_img = NULL; gconstpointer data = NULL; size_t len; @@ -7131,7 +7171,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, @@ -7140,7 +7179,6 @@ } data = purple_buddy_icon_get_data(icon, &len); - if (data == NULL) { gtk_widget_set_size_request(gtkconv->u.im->icon_container, @@ -7149,25 +7187,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)) { @@ -8219,6 +8245,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 fa9d736e9937 -r accce7b79737 pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkdialogs.c Sun Jun 26 02:27:01 2011 +0900 @@ -433,7 +433,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 */ diff -r fa9d736e9937 -r accce7b79737 pidgin/gtkft.c --- a/pidgin/gtkft.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkft.c Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkimhtml.c Sun Jun 26 02:27:01 2011 +0900 @@ -5073,16 +5073,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); } diff -r fa9d736e9937 -r accce7b79737 pidgin/gtkmain.c --- a/pidgin/gtkmain.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkmain.c Sun Jun 26 02:27:01 2011 +0900 @@ -270,7 +270,7 @@ /* use the nice PNG icon for all the windows */ for(i=0; iicon ? 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 */ diff -r fa9d736e9937 -r accce7b79737 pidgin/gtkrequest.c --- a/pidgin/gtkrequest.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkrequest.c Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 pidgin/gtksmiley.c --- a/pidgin/gtksmiley.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtksmiley.c Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 pidgin/gtkstatusbox.c --- a/pidgin/gtkstatusbox.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkstatusbox.c Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 pidgin/gtkutils.c --- a/pidgin/gtkutils.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkutils.c Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 pidgin/gtkutils.h --- a/pidgin/gtkutils.h Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/gtkutils.h Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 pidgin/plugins/gevolution/gevolution.c --- a/pidgin/plugins/gevolution/gevolution.c Sun Jun 12 01:39:24 2011 +0900 +++ b/pidgin/plugins/gevolution/gevolution.c Sun Jun 26 02:27:01 2011 +0900 @@ -447,8 +447,8 @@ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); gtk_box_pack_start(GTK_BOX(vbox), - pidgin_make_scrollable(treeview, GTK_POLICY_AUTO, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, 300, 300), - TRUE, TRUE, 0); + pidgin_make_scrollable(treeview, GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS, GTK_SHADOW_IN, 300, 300), TRUE, TRUE, 0); gtk_widget_show(treeview); /* Setup the column */ diff -r fa9d736e9937 -r accce7b79737 po/ChangeLog --- a/po/ChangeLog Sun Jun 12 01:39:24 2011 +0900 +++ b/po/ChangeLog Sun Jun 26 02:27:01 2011 +0900 @@ -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 fa9d736e9937 -r accce7b79737 po/de.po --- a/po/de.po Sun Jun 12 01:39:24 2011 +0900 +++ b/po/de.po Sun Jun 26 02:27:01 2011 +0900 @@ -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-06-21 09:12+0200\n" +"PO-Revision-Date: 2011-06-21 09:11+0200\n" "Last-Translator: Björn Voigt \n" "Language-Team: German \n" "Language: de\n" @@ -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." @@ -13182,6 +13185,9 @@ msgid "_UDP Port:" msgstr "_UDP-Port:" +msgid "T_CP Port:" +msgstr "T_CP-Port:" + msgid "Use_rname:" msgstr "_Benutzername:"