# HG changeset patch # User Will Thompson # Date 1191171319 0 # Node ID 2bb82b05d30af015185ae0662a2141a9d45dcf22 # Parent 2e5ee1bf530080acfcaea0e1308f8fb42f7d3640# Parent 3e569b1b037118036a0622c13fdae5f821b5c0c0 merge of '07d5798f8a0b64518b4697e0376f5ceeddbb61e2' and 'da7520dee75fbe063aa7747c5452551d0030abee' diff -r 3e569b1b0371 -r 2bb82b05d30a COPYRIGHT diff -r 3e569b1b0371 -r 2bb82b05d30a ChangeLog --- a/ChangeLog Sun Sep 30 16:53:34 2007 +0000 +++ b/ChangeLog Sun Sep 30 16:55:19 2007 +0000 @@ -1,6 +1,6 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -Version 2.2.2: +version 2.2.2: http://developer.pidgin.im/query?status=closed&milestone=2.2.2 NOTE: Due to the backporting that happened for the 2.2.1 release, it is possible bugs this release fixes bugs @@ -30,9 +30,9 @@ * The "Smiley" menu has been moved to the top-level of the toolbar. * Pidgin's display is now saved with the command line for session restoration. (David Mohr) - * ICQ Birthday notifications are shown as buddy list emblems + * ICQ Birthday notifications are shown as buddy list emblems. -Version 2.2.1: +version 2.2.1 (09/29/2007): http://developer.pidgin.im/query?status=closed&milestone=2.2.1 NOTE: Due to the backporting that happened for the actual release, it is possible bugs marked as fixed in 2.2.1 @@ -43,24 +43,28 @@ * Cancelling the password prompt for an account will no longer leave it in an ambiguous state. (It will be disabled.) * Fixed an erroneous size display for MSN file transfers. (galt) + * Fixed multiple memory leaks, particularly in XMPP and MySpace + protocols + * Fixed remembering proxy preferences and status scores * Gmail notifications are better tracked Pidgin: * Fixed keyboard tab reordering to move tabs one step instead of two. * You should no longer lose proxy settings when Pidgin is restarted. + * Fixed detection of X11 when compiling Finch: * Pressing 'Insert' in the buddylist will bring up the 'Add Buddy' dialog. -Version 2.2.0 (09/13/2007): +version 2.2.0 (09/13/2007): http://developer.pidgin.im/query?status=closed&milestone=2.2.0 Libpurple: * New protocol plugin: MySpaceIM (Jeff Connelly, Google Summer of Code) * XMPP enhancements. See - http://www.adiumx.com/blog/2007/07/soc-xmpp-update.php (Andreas + http://www.adiumx.com/blog/2007/07/soc-xmpp-update.php (Andreas Monitzer, Google Summer of Code for Adium) * Certificate management. Libpurple will validate certificates on SSL-encrypted protocols (William Ehlhardt, Google Summer of Code) @@ -2296,4 +2300,4 @@ * Fixed WindowMaker Appicon * Version Number in About Box * Gaim Slogan in about box :) - * Created Changelog File :) \ No newline at end of file + * Created Changelog File :) diff -r 3e569b1b0371 -r 2bb82b05d30a finch/gntconn.c diff -r 3e569b1b0371 -r 2bb82b05d30a finch/gntconv.c --- a/finch/gntconv.c Sun Sep 30 16:53:34 2007 +0000 +++ b/finch/gntconv.c Sun Sep 30 16:55:19 2007 +0000 @@ -959,7 +959,7 @@ { FinchConv *fc = FINCH_CONV(conv); if (fc && fc->window) - return gnt_window_present(fc->window); + gnt_window_present(fc->window); } static gboolean diff -r 3e569b1b0371 -r 2bb82b05d30a finch/gntplugin.c --- a/finch/gntplugin.c Sun Sep 30 16:53:34 2007 +0000 +++ b/finch/gntplugin.c Sun Sep 30 16:55:19 2007 +0000 @@ -32,6 +32,7 @@ #include "finch.h" +#include "debug.h" #include "notify.h" #include "request.h" @@ -127,8 +128,8 @@ /* XXX: Use formatting and stuff */ gnt_text_view_clear(GNT_TEXT_VIEW(plugins.aboot)); text = g_strdup_printf(_("Name: %s\nVersion: %s\nDescription: %s\nAuthor: %s\nWebsite: %s\nFilename: %s\n"), - SAFE(plugin->info->name), SAFE(plugin->info->version), SAFE(plugin->info->description), - SAFE(plugin->info->author), SAFE(plugin->info->homepage), SAFE(plugin->path)); + SAFE(_(plugin->info->name)), SAFE(_(plugin->info->version)), SAFE(_(plugin->info->description)), + SAFE(_(plugin->info->author)), SAFE(_(plugin->info->homepage)), SAFE(plugin->path)); gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(plugins.aboot), text, GNT_TEXT_FLAG_NORMAL); gnt_text_view_scroll(GNT_TEXT_VIEW(plugins.aboot), 0); @@ -237,6 +238,93 @@ } } +static void +install_selected_file_cb(gpointer handle, const char *filename) +{ + /* Try to init the selected file. + * If it succeeds, try to make a copy of the file in $USERDIR/plugins/. + * If the copy succeeds, unload and destroy the plugin in the original + * location and init+load the new one. + * Select the plugin in the plugin list. + */ + char *path; + PurplePlugin *plugin; + + g_return_if_fail(plugins.window); + + plugin = purple_plugin_probe(filename); + if (!plugin) { + purple_notify_error(handle, _("Error loading plugin"), + _("The selected file is not a valid plugin."), + _("Please open the debug window and try again to see the exact error message.")); + return; + } + if (g_list_find(gnt_tree_get_rows(GNT_TREE(plugins.tree)), plugin)) { + purple_plugin_load(plugin); + gnt_tree_set_choice(GNT_TREE(plugins.tree), plugin, purple_plugin_is_loaded(plugin)); + gnt_tree_set_selected(GNT_TREE(plugins.tree), plugin); + return; + } + + path = g_build_filename(purple_user_dir(), "plugins", NULL); + if (purple_build_dir(path, S_IRUSR | S_IWUSR | S_IXUSR) == 0) { + char *content = NULL; + gsize length = 0; + + if (g_file_get_contents(filename, &content, &length, NULL)) { + char *file = g_path_get_basename(filename); + g_free(path); + path = g_build_filename(purple_user_dir(), "plugins", file, NULL); + if (purple_util_write_data_to_file_absolute(path, content, length)) { + purple_plugin_destroy(plugin); + plugin = purple_plugin_probe(path); + if (!plugin) { + purple_debug_warning("gntplugin", "This is really strange. %s can be loaded, but %s can't!\n", + filename, path); + g_unlink(path); + plugin = purple_plugin_probe(filename); + } + } else { + } + } + g_free(content); + } + g_free(path); + + purple_plugin_load(plugin); + + if (plugin->info->type == PURPLE_PLUGIN_LOADER) { + GList *cur; + for (cur = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts; cur != NULL; + cur = cur->next) + purple_plugins_probe(cur->data); + return; + } + + if (plugin->info->type != PURPLE_PLUGIN_STANDARD || + (plugin->info->flags & PURPLE_PLUGIN_FLAG_INVISIBLE) || + plugin->error) + return; + + gnt_tree_add_choice(GNT_TREE(plugins.tree), plugin, + gnt_tree_create_row(GNT_TREE(plugins.tree), plugin->info->name), NULL, NULL); + gnt_tree_set_choice(GNT_TREE(plugins.tree), plugin, purple_plugin_is_loaded(plugin)); + gnt_tree_set_row_flags(GNT_TREE(plugins.tree), plugin, GNT_TEXT_FLAG_BOLD); + gnt_tree_set_selected(GNT_TREE(plugins.tree), plugin); +} + +static void +install_plugin_cb(GntWidget *w, gpointer null) +{ + static int handle; + + purple_request_close_with_handle(&handle); + purple_request_file(&handle, _("Select plugin to install"), NULL, + FALSE, G_CALLBACK(install_selected_file_cb), NULL, + NULL, NULL, NULL, &handle); + g_signal_connect_swapped(G_OBJECT(w), "destroy", G_CALLBACK(purple_request_close_with_handle), &handle); +} + void finch_plugins_show_all() { GntWidget *window, *tree, *box, *aboot, *button; @@ -272,6 +360,7 @@ gnt_box_add_widget(GNT_BOX(box), gnt_vline_new()); plugins.aboot = aboot = gnt_text_view_new(); + gnt_text_view_set_flag(GNT_TEXT_VIEW(aboot), GNT_TEXT_VIEW_TOP_ALIGN); gnt_widget_set_size(aboot, 40, 20); gnt_box_add_widget(GNT_BOX(box), aboot); @@ -307,6 +396,10 @@ box = gnt_hbox_new(FALSE); gnt_box_add_widget(GNT_BOX(window), box); + button = gnt_button_new(_("Install Plugin...")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(install_plugin_cb), NULL); + button = gnt_button_new(_("Close")); gnt_box_add_widget(GNT_BOX(box), button); g_signal_connect_swapped(G_OBJECT(button), "activate", diff -r 3e569b1b0371 -r 2bb82b05d30a finch/gntpounce.c --- a/finch/gntpounce.c Sun Sep 30 16:53:34 2007 +0000 +++ b/finch/gntpounce.c Sun Sep 30 16:55:19 2007 +0000 @@ -964,10 +964,15 @@ PURPLE_CALLBACK(signed_on_off_cb), NULL); } +static void +dummy_pounce_cb(PurplePounce *pounce, PurplePounceEvent events, void *data) +{ +} + /* XXX: There's no such thing in pidgin. Perhaps there should be? */ void finch_pounces_uninit() { - purple_pounces_register_handler(FINCH_UI, NULL, NULL, NULL); + purple_pounces_register_handler(FINCH_UI, dummy_pounce_cb, NULL, NULL); purple_signals_disconnect_by_handle(finch_pounces_get_handle()); } diff -r 3e569b1b0371 -r 2bb82b05d30a finch/libgnt/pygnt/gntmodule.c --- a/finch/libgnt/pygnt/gntmodule.c Sun Sep 30 16:53:34 2007 +0000 +++ b/finch/libgnt/pygnt/gntmodule.c Sun Sep 30 16:55:19 2007 +0000 @@ -1,7 +1,74 @@ #include +#include "gnt.h" void gnt_register_classes (PyObject *d); extern PyMethodDef gnt_functions[]; + +static void +gnt_add_string_constants(PyObject *module) +{ +#define define_key(x) if (GNT_KEY_##x && *(GNT_KEY_##x)) PyModule_AddStringConstant(module, "KEY_" #x, GNT_KEY_##x) + + define_key(POPUP); + + define_key(LEFT); + define_key(RIGHT); + define_key(UP); + define_key(DOWN); + + define_key(CTRL_UP); + define_key(CTRL_DOWN); + define_key(CTRL_RIGHT); + define_key(CTRL_LEFT); + + define_key(PGUP); + define_key(PGDOWN); + define_key(HOME); + define_key(END); + + define_key(ENTER); + + define_key(BACKSPACE); + define_key(DEL); + define_key(INS); + define_key(BACK_TAB); + + define_key(CTRL_A); + define_key(CTRL_B); + define_key(CTRL_D); + define_key(CTRL_E); + define_key(CTRL_F); + define_key(CTRL_G); + define_key(CTRL_H); + define_key(CTRL_I); + define_key(CTRL_J); + define_key(CTRL_K); + define_key(CTRL_L); + define_key(CTRL_M); + define_key(CTRL_N); + define_key(CTRL_O); + define_key(CTRL_P); + define_key(CTRL_R); + define_key(CTRL_T); + define_key(CTRL_U); + define_key(CTRL_V); + define_key(CTRL_W); + define_key(CTRL_X); + define_key(CTRL_Y); + + define_key(F1); + define_key(F2); + define_key(F3); + define_key(F4); + define_key(F5); + define_key(F6); + define_key(F7); + define_key(F8); + define_key(F9); + define_key(F10); + define_key(F11); + define_key(F12); +} DL_EXPORT(void) initgnt(void) @@ -19,5 +86,8 @@ if (PyErr_Occurred ()) { Py_FatalError ("can't initialise module sad"); } + + gnt_init(); + gnt_add_string_constants(m); } diff -r 3e569b1b0371 -r 2bb82b05d30a finch/libgnt/pygnt/gnttree.override --- a/finch/libgnt/pygnt/gnttree.override Sun Sep 30 16:53:34 2007 +0000 +++ b/finch/libgnt/pygnt/gnttree.override Sun Sep 30 16:55:19 2007 +0000 @@ -62,13 +62,14 @@ { static char *kwlist[] = {"key", "row", "parent", "bigbro", NULL}; PyObject *py_list; - gpointer key, parent, bigbro; + gpointer key, parent, bigbro = NULL; int len, i; GList *list = NULL; GntTreeRow *row; + gboolean insert_last = FALSE; if (!PyArg_ParseTuple(args, - "OOOO:GntTree.add_row_after", + "OOO|O:GntTree.add_row_after", &key, &py_list, &parent, @@ -92,13 +93,20 @@ parent = NULL; if (bigbro == Py_None) bigbro = NULL; + else if (bigbro == NULL) + insert_last = TRUE; + + Py_INCREF((PyObject*)key); list = g_list_reverse(list); row = gnt_tree_create_row_from_list(GNT_TREE(self->obj), list); - gnt_tree_add_row_after(GNT_TREE(self->obj), - key, - row, - parent, bigbro); + if (insert_last) + gnt_tree_add_row_last(GNT_TREE(self->obj), + key, row, parent); + else + gnt_tree_add_row_after(GNT_TREE(self->obj), + key, row, + parent, bigbro); g_list_free(list); Py_INCREF(Py_None); @@ -179,4 +187,27 @@ Py_INCREF(Py_None); return Py_None; } +%% +override gnt_tree_set_compare_func +static PyObject * +_wrap_gnt_tree_set_compare_func(PyGObject *self, PyObject *args) +{ + static char *kwlist[] = {"compare_func", NULL}; + PyGObject *compare; + if (!PyArg_ParseTuple(args, "O:GntTree.set_compare_func", &compare)) { + return NULL; + } + + if (!PyCallable_Check(compare)) { + PyErr_SetString(PyExc_TypeError, "the callback must be callable ... doh!"); + return NULL; + } + + Py_INCREF(compare); + gnt_tree_set_compare_func(GNT_TREE(self->obj), (GCompareFunc)compare->obj); + + Py_INCREF(Py_None); + return Py_None; +} + diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/account.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/dnssrv.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/ft.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/notify.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/plugin.c --- a/libpurple/plugin.c Sun Sep 30 16:53:34 2007 +0000 +++ b/libpurple/plugin.c Sun Sep 30 16:55:19 2007 +0000 @@ -1275,13 +1275,9 @@ if (basename) basename = purple_plugin_get_basename(filename); - if ((plugin = purple_plugins_find_with_filename(filename)) != NULL) - { - purple_debug_info("plugins", "Loading saved plugin %s\n", - plugin->path); - purple_plugin_load(plugin); - } - else if (basename && (plugin = purple_plugins_find_with_basename(basename)) != NULL) + if (((plugin = purple_plugins_find_with_filename(filename)) != NULL) || + (basename && (plugin = purple_plugins_find_with_basename(basename)) != NULL) || + ((plugin = purple_plugin_probe(filename)) != NULL)) { purple_debug_info("plugins", "Loading saved plugin %s\n", plugin->path); diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/jabber/buddy.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/jabber/jabber.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/jabber/message.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/msn/directconn.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/msn/msn.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/msn/servconn.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/myspace/markup.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/myspace/markup.h diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/myspace/session.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/myspace/session.h diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/myspace/user.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/myspace/user.h diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/myspace/zap.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/myspace/zap.h diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/oscar/odc.c --- a/libpurple/protocols/oscar/odc.c Sun Sep 30 16:53:34 2007 +0000 +++ b/libpurple/protocols/oscar/odc.c Sun Sep 30 16:55:19 2007 +0000 @@ -27,6 +27,8 @@ #include "imgstore.h" #include "util.h" +#define DIRECTIM_MAX_FILESIZE 52428800 + /** * Free any ODC related data and print a message to the conversation * window based on conn->disconnect_reason. @@ -587,6 +589,27 @@ if (frame->payload.len > 0) { + if (frame->payload.len > DIRECTIM_MAX_FILESIZE) + { + gchar *tmp, *size1, *size2; + PurpleAccount *account; + PurpleConversation *conv; + + size1 = purple_str_size_to_units(frame->payload.len); + size2 = purple_str_size_to_units(DIRECTIM_MAX_FILESIZE); + tmp = g_strdup_printf(_("%s tried to send you a %s file, but we only allow files up to %s over Direct IM. Try using file transfer instead.\n"), conn->sn, size1, size2); + g_free(size1); + g_free(size2); + + account = purple_connection_get_account(conn->od->gc); + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->sn); + purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); + g_free(tmp); + + peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL); + return; + } + /* We have payload data! Switch to the ODC watcher to read it. */ frame->payload.data = g_new(guint8, frame->payload.len); frame->payload.offset = 0; diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Sun Sep 30 16:53:34 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.c Sun Sep 30 16:55:19 2007 +0000 @@ -2184,12 +2184,14 @@ { PurpleConnection *gc; OscarData *od; + PurpleAccount *account; PurpleBuddy *buddy; PurpleGroup *group; gc = data->gc; od = gc->proto_data; - buddy = purple_find_buddy(purple_connection_get_account(gc), data->name); + account = purple_connection_get_account(gc); + buddy = purple_find_buddy(account, data->name); if (buddy != NULL) group = purple_buddy_get_group(buddy); else @@ -2201,7 +2203,19 @@ buddy->name, group->name); aim_ssi_sendauthrequest(od, data->name, msg ? msg : _("Please authorize me so I can add you to my buddy list.")); if (!aim_ssi_itemlist_finditem(od->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY)) + { aim_ssi_addbuddy(od, buddy->name, group->name, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, TRUE); + + /* Mobile users should always be online */ + if (buddy->name[0] == '+') { + purple_prpl_got_user_status(account, + purple_buddy_get_name(buddy), + OSCAR_STATUS_ID_AVAILABLE, NULL); + purple_prpl_got_user_status(account, + purple_buddy_get_name(buddy), + OSCAR_STATUS_ID_MOBILE, NULL); + } + } } } @@ -4621,12 +4635,16 @@ void oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { - OscarData *od = (OscarData *)gc->proto_data; + OscarData *od; + PurpleAccount *account; + + od = (OscarData *)gc->proto_data; + account = purple_connection_get_account(gc); if (!aim_snvalid(buddy->name)) { gchar *buf; buf = g_strdup_printf(_("Could not add the buddy %s because the screen name is invalid. Screen names must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), buddy->name); - if (!purple_conv_present_error(buddy->name, purple_connection_get_account(gc), buf)) + if (!purple_conv_present_error(buddy->name, account, buf)) purple_notify_error(gc, NULL, _("Unable To Add"), buf); g_free(buf); @@ -4640,6 +4658,16 @@ purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n", buddy->name, group->name); aim_ssi_addbuddy(od, buddy->name, group->name, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0); + + /* Mobile users should always be online */ + if (buddy->name[0] == '+') { + purple_prpl_got_user_status(account, + purple_buddy_get_name(buddy), + OSCAR_STATUS_ID_AVAILABLE, NULL); + purple_prpl_got_user_status(account, + purple_buddy_get_name(buddy), + OSCAR_STATUS_ID_MOBILE, NULL); + } } /* XXX - Should this be done from AIM accounts, as well? */ @@ -4955,6 +4983,17 @@ g_free(comment); } } + + /* Mobile users should always be online */ + if (b->name[0] == '+') { + purple_prpl_got_user_status(account, + purple_buddy_get_name(b), + OSCAR_STATUS_ID_AVAILABLE, NULL); + purple_prpl_got_user_status(account, + purple_buddy_get_name(b), + OSCAR_STATUS_ID_MOBILE, NULL); + } + g_free(gname_utf8); g_free(alias_utf8); } @@ -5145,6 +5184,17 @@ purple_debug_info("oscar", "ssi: adding buddy %s to group %s to local list\n", name, gname_utf8 ? gname_utf8 : _("Orphans")); purple_blist_add_buddy(b, NULL, g, NULL); + + /* Mobile users should always be online */ + if (b->name[0] == '+') { + purple_prpl_got_user_status(account, + purple_buddy_get_name(b), + OSCAR_STATUS_ID_AVAILABLE, NULL); + purple_prpl_got_user_status(account, + purple_buddy_get_name(b), + OSCAR_STATUS_ID_MOBILE, NULL); + } + } g_free(gname_utf8); diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/upnp.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/util.c diff -r 3e569b1b0371 -r 2bb82b05d30a libpurple/win32/win32dep.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkaccount.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkblist.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkcellrendererexpander.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkconv.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkdialogs.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkimhtml.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkimhtmltoolbar.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkmain.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkprefs.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtksavedstatuses.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/gtkutils.c diff -r 3e569b1b0371 -r 2bb82b05d30a pidgin/pidgin.h