# HG changeset patch # User masca@cpw.pidgin.im # Date 1279573892 0 # Node ID 47b6eda877236e826558421bcb7afb9d8ffa3d44 # Parent e874875a74a7273703bcb2282cc52a02f7061079# Parent c7fa7c7aca7d194a1072b844c0bb366b9efb9a7b propagate from branch 'im.pidgin.pidgin' (head 07d0765c444a097af45c2650f54323afb900a07b) to branch 'im.pidgin.soc.2010.msn-tlc' (head f3998422a4724ab424e4e2328f58fc0504856557) diff -r c7fa7c7aca7d -r 47b6eda87723 ChangeLog --- a/ChangeLog Mon Jul 19 21:05:06 2010 +0000 +++ b/ChangeLog Mon Jul 19 21:11:32 2010 +0000 @@ -1,11 +1,17 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul version 2.7.2 (??/??/????): + General: + * Use silent build rules for automake >1.11. You can enable verbose + builds with the --disable-silent-rules configure option, or using + make V=1. + libpurple: * Fix the TURN server settings (broken in 2.7.0). Pidgin: * Re-focus the input area after clicking the attention toolbar button. + * Re-arrange media window to make it more netbook-friendly. Finch: * Rebindable 'suggest-next-page' and 'suggest-prev-page' actions for @@ -21,17 +27,24 @@ a fallback to legacy IQ authentication (broken in 2.7.0). * Fix a crash when receiving custom emoticons that don't adhere to the specification. + * When initiating a file transfer, don't show resources that are certain + to not support file transfers in the resource selection dialog. + * Fix connecting to servers using BOSH and authenticating with + DIGEST-MD5 when libpurple was built with Cyrus SASL support. Yahoo/Yahoo JAPAN: * Renamed "Use account proxy for SSL connections" to "Use account proxy for HTTP and HTTPS requests" and tied the option to HTTP requests too. - * Properly detect HTTP proxy server use when the HTTP proxy is the global - proxy server, an account-level non-HTTP proxy server is configured, and - the "Use account proxy for HTTP and HTTPS requests" account option is - turned off. This fixes connecting for some HTTP proxy servers. - * Fall back to connecting to scsa.msg.yahoo.com (not configurable) if the - HTTP-based connect server lookup fails. This does not work for Yahoo - JAPAN accounts. + * Properly detect HTTP proxy server use when the HTTP proxy is the + global proxy server, an account-level non-HTTP proxy server is + configured, and the "Use account proxy for HTTP and HTTPS requests" + account option is turned off. This fixes connecting for some HTTP + proxy servers. + * Fall back to connecting to scsa.msg.yahoo.com (not configurable) if + the HTTP-based connect server lookup fails. This does not work for + Yahoo JAPAN accounts. + * Fix file transfers that get stuck with "Waiting for transfer to + begin". version 2.7.1 (05/29/2010): General: diff -r c7fa7c7aca7d -r 47b6eda87723 ChangeLog.API --- a/ChangeLog.API Mon Jul 19 21:05:06 2010 +0000 +++ b/ChangeLog.API Mon Jul 19 21:11:32 2010 +0000 @@ -7,6 +7,23 @@ called for a protocol that doesn't support the underlying calls and the caller does not specify a failure callback. + Perl: + Added: + * Exposed log-subsystem signals. + + Pidgin: + Changed: + * Changing the visibility (gtk_widget_hide/show) of + the widgets in the GtkIMHtmlToolbar should now affect + the visibility of the entries in the 'lean' view + (the default toolbar view). + + libgnt: + Added: + * gnt_tree_row_get_key, gnt_tree_row_get_next, + gnt_tree_row_get_prev, gnt_tree_row_get_child and + gnt_tree_row_get_parent. + version 2.7.1 (05/29/2010): * No changes diff -r c7fa7c7aca7d -r 47b6eda87723 Makefile.am --- a/Makefile.am Mon Jul 19 21:05:06 2010 +0000 +++ b/Makefile.am Mon Jul 19 21:11:32 2010 +0000 @@ -100,18 +100,18 @@ # creates, and also make sure that the shell command exits # successfully; the rm -f ensures both package_revision_raw.txt: - REAL_BLDDIR=$$PWD/$(top_builddir); \ + $(AM_V_GEN)REAL_BLDDIR=$$PWD/$(top_builddir); \ (cd $(srcdir) && $$REAL_BLDDIR/mtn --root=. automate get_base_revision_id) 2>/dev/null >$@ \ || (cd $(srcdir) && mtn --root=. automate get_base_revision_id) 2>/dev/null >$@ \ || rm -f $@ package_revision.h: package_revision_raw.txt - if test -f $<; then \ + $(AM_V_GEN)if test -f $<; then \ echo "#define REVISION \"`cat $<`\"" > $@; \ fi - if test ! -f $@ -a -f $(srcdir)/$@; then \ + $(AM_V_at)if test ! -f $@ -a -f $(srcdir)/$@; then \ cp $(srcdir)/$@ $@; \ fi - test -f $@ || echo "#define REVISION \"unknown\"" > $@ + $(AM_V_at)test -f $@ || echo "#define REVISION \"unknown\"" > $@ # This is a magic directive copy-and-pasted, then modified, from the # automake 1.9 manual, section 13.4, "Checking the distribution". diff -r c7fa7c7aca7d -r 47b6eda87723 configure.ac --- a/configure.ac Mon Jul 19 21:05:06 2010 +0000 +++ b/configure.ac Mon Jul 19 21:11:32 2010 +0000 @@ -52,10 +52,10 @@ [purple_major_version.purple_minor_version.purple_micro_version]) m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix])) -m4_define([gnt_lt_current], [7]) +m4_define([gnt_lt_current], [8]) m4_define([gnt_major_version], [2]) -m4_define([gnt_minor_version], [7]) -m4_define([gnt_micro_version], [2]) +m4_define([gnt_minor_version], [8]) +m4_define([gnt_micro_version], [0]) m4_define([gnt_version_suffix], [devel]) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version]) @@ -75,6 +75,8 @@ AC_CANONICAL_HOST AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([1.9 -Wno-portability dist-bzip2]) +dnl TODO: Always use AM_SILENT_RULES when we depend on automake >= 1.11 +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) PURPLE_MAJOR_VERSION=purple_major_version PURPLE_MINOR_VERSION=purple_minor_version @@ -660,6 +662,10 @@ for location in $ac_ncurses_includes $NCURSES_HEADERS /usr/include/ncursesw /usr/include do f="$location/ncurses.h" + orig_CFLAGS="$CFLAGS" + orig_CPPFLAGS="$CPPFLAGS" + CFLAGS="$CFLAGS -I$location" + CPPFLAGS="$CPPFLAGS -I$location" AC_CHECK_HEADER($f,[ AC_MSG_CHECKING([if $f supports wide characters]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @@ -678,9 +684,13 @@ fi found_ncurses_h=yes + CFLAGS="$orig_CFLAGS" + CPPFLAGS="$orig_CPPFLAGS" AC_MSG_RESULT([yes]) break ], [ + CFLAGS="$orig_CFLAGS" + CPPFLAGS="$orig_CPPFLAGS" AC_MSG_RESULT([no]) ]) ]) diff -r c7fa7c7aca7d -r 47b6eda87723 doc/finch.1.in --- a/doc/finch.1.in Mon Jul 19 21:05:06 2010 +0000 +++ b/doc/finch.1.in Mon Jul 19 21:11:32 2010 +0000 @@ -59,7 +59,7 @@ Display the version information window. .SH GNT Shortcuts -You can use the following shortcuts (see the "\*QWidget Actions\*U" section for a more complete list): +You can use the following shortcuts (see the "Widget Actions" section for a more complete list): .TP .B Alt \+ a Bring up a list of available actions. You can use this list to access the @@ -507,6 +507,10 @@ .br a-/ = help-for-widget .br +a-c-j = window-scroll-down +.br +a-c-k = window-scroll-up +.br # The following action is still incomplete, and doesn't have a default binding .br # switch-window-n diff -r c7fa7c7aca7d -r 47b6eda87723 finch/finch.c --- a/finch/finch.c Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/finch.c Mon Jul 19 21:11:32 2010 +0000 @@ -252,6 +252,7 @@ gboolean opt_version = FALSE; char *opt_config_dir_arg = NULL; gboolean debug_enabled = FALSE; + struct stat st; struct option long_options[] = { {"config", required_argument, NULL, 'c'}, @@ -361,6 +362,8 @@ purple_idle_set_ui_ops(finch_idle_get_ui_ops()); path = g_build_filename(purple_user_dir(), "plugins", NULL); + if (!g_stat(path, &st)) + g_mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR); purple_plugins_add_search_path(path); g_free(path); diff -r c7fa7c7aca7d -r 47b6eda87723 finch/gntaccount.c --- a/finch/gntaccount.c Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/gntaccount.c Mon Jul 19 21:11:32 2010 +0000 @@ -125,7 +125,8 @@ if (value == NULL || *value == '\0') { - purple_notify_error(NULL, _("Error"), _("Account was not added"), + purple_notify_error(NULL, _("Error"), + dialog->account ? _("Account was not modified") : _("Account was not added"), _("Username of an account must be non-empty.")); return; } @@ -160,8 +161,28 @@ account = dialog->account; /* Protocol */ - purple_account_set_protocol_id(account, purple_plugin_get_id(plugin)); - purple_account_set_username(account, username->str); + if (purple_account_is_disconnected(account)) { + purple_account_set_protocol_id(account, purple_plugin_get_id(plugin)); + purple_account_set_username(account, username->str); + } else { + const char *old = purple_account_get_protocol_id(account); + char *oldprpl; + if (strcmp(old, purple_plugin_get_id(plugin))) { + purple_notify_error(NULL, _("Error"), _("Account was not modified"), + _("The account's protocol cannot be changed while it is connected to the server.")); + return; + } + + oldprpl = g_strdup(purple_normalize(account, purple_account_get_username(account))); + if (g_utf8_collate(oldprpl, purple_normalize(account, username->str))) { + purple_notify_error(NULL, _("Error"), _("Account was not modified"), + _("The account's username cannot be changed while it is connected to the server.")); + g_free(oldprpl); + return; + } + g_free(oldprpl); + purple_account_set_username(account, username->str); + } } g_string_free(username, TRUE); @@ -245,6 +266,17 @@ } } + /* In case of a new account, the 'Accounts' window is updated from the account-added + * callback. In case of changes in an existing account, we need to explicitly do it + * here. + */ + if (dialog->account != NULL && accounts.window) { + gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account, + 0, purple_account_get_username(dialog->account)); + gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account, + 1, purple_account_get_protocol_name(dialog->account)); + } + gnt_widget_destroy(dialog->window); } @@ -603,7 +635,7 @@ button = gnt_button_new(_("Cancel")); gnt_box_add_widget(GNT_BOX(hbox), button); g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window); - + button = gnt_button_new(_("Save")); gnt_box_add_widget(GNT_BOX(hbox), button); g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(save_account_cb), dialog); diff -r c7fa7c7aca7d -r 47b6eda87723 finch/gntdebug.c --- a/finch/gntdebug.c Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/gntdebug.c Mon Jul 19 21:11:32 2010 +0000 @@ -77,7 +77,10 @@ } return; } - pipe(pipes); + if (pipe(pipes)) { + readhandle = -1; + return; + }; dup2(pipes[1], STDERR_FILENO); stderrch = g_io_channel_unix_new(pipes[0]); diff -r c7fa7c7aca7d -r 47b6eda87723 finch/gntrequest.c --- a/finch/gntrequest.c Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/gntrequest.c Mon Jul 19 21:11:32 2010 +0000 @@ -349,10 +349,9 @@ } else if (type == PURPLE_REQUEST_FIELD_LIST) { - GList *list = NULL; + GList *list = NULL, *iter; if (purple_request_field_list_get_multi_select(field)) { - GList *iter; GntWidget *tree = FINCH_GET_DATA(field); iter = purple_request_field_list_get_items(field); @@ -361,14 +360,23 @@ const char *text = iter->data; gpointer key = purple_request_field_list_get_data(field, text); if (gnt_tree_get_choice(GNT_TREE(tree), key)) - list = g_list_prepend(list, key); + list = g_list_prepend(list, (gpointer)text); } } else { GntWidget *combo = FINCH_GET_DATA(field); gpointer data = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)); - list = g_list_append(list, data); + + iter = purple_request_field_list_get_items(field); + for (; iter; iter = iter->next) { + const char *text = iter->data; + gpointer key = purple_request_field_list_get_data(field, text); + if (key == data) { + list = g_list_prepend(list, (gpointer)text); + break; + } + } } purple_request_field_list_set_selected(field, list); @@ -815,7 +823,7 @@ for (list = purple_request_fields_get_groups(allfields); list; list = list->next) { PurpleRequestFieldGroup *group = list->data; GList *fields = purple_request_field_group_get_fields(group); - + for (; fields ; fields = fields->next) { PurpleRequestField *field = fields->data; PurpleRequestFieldType type = purple_request_field_get_type(field); @@ -826,6 +834,7 @@ switch (type) { case PURPLE_REQUEST_FIELD_LIST: val = purple_request_field_list_get_selected(field)->data; + val = purple_request_field_list_get_data(field, val); break; case PURPLE_REQUEST_FIELD_BOOLEAN: val = GINT_TO_POINTER(purple_request_field_bool_get_value(field)); diff -r c7fa7c7aca7d -r 47b6eda87723 finch/gntsound.c --- a/finch/gntsound.c Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/gntsound.c Mon Jul 19 21:11:32 2010 +0000 @@ -104,7 +104,8 @@ {PURPLE_SOUND_CHAT_YOU_SAY, N_("You talk in chat"), "send_chat_msg", "send.wav", NULL}, {PURPLE_SOUND_CHAT_SAY, N_("Others talk in chat"), "chat_msg_recv", "receive.wav", NULL}, {PURPLE_SOUND_POUNCE_DEFAULT, NULL, "pounce_default", "alert.wav", NULL}, - {PURPLE_SOUND_CHAT_NICK, N_("Someone says your username in chat"), "nick_said", "alert.wav", NULL} + {PURPLE_SOUND_CHAT_NICK, N_("Someone says your username in chat"), "nick_said", "alert.wav", NULL}, + {PURPLE_SOUND_GOT_ATTENTION, N_("Attention received"), "got_attention", "alert.wav", NULL} }; const char * @@ -273,6 +274,14 @@ play_conv_event(conv, event); } +static void +got_attention_cb(PurpleAccount *account, const char *who, + PurpleConversation *conv, guint type, PurpleSoundEventID event) +{ + play_conv_event(conv, event); +} + + /* * We mute sounds for the 10 seconds after you log in so that * you don't get flooded with sounds when the blist shows all @@ -300,34 +309,22 @@ static void initialize_profile(const char *name, PurplePrefType type, gconstpointer val, gpointer null) { + FinchSoundEvent *event; if (purple_prefs_exists(make_pref(""))) return; purple_prefs_add_none(make_pref("")); purple_prefs_add_none(make_pref("/enabled")); purple_prefs_add_none(make_pref("/file")); - purple_prefs_add_bool(make_pref("/enabled/login"), FALSE); - purple_prefs_add_path(make_pref("/file/login"), ""); - purple_prefs_add_bool(make_pref("/enabled/logout"), FALSE); - purple_prefs_add_path(make_pref("/file/logout"), ""); - purple_prefs_add_bool(make_pref("/enabled/im_recv"), FALSE); - purple_prefs_add_path(make_pref("/file/im_recv"), ""); - purple_prefs_add_bool(make_pref("/enabled/first_im_recv"), FALSE); - purple_prefs_add_path(make_pref("/file/first_im_recv"), ""); - purple_prefs_add_bool(make_pref("/enabled/send_im"), FALSE); - purple_prefs_add_path(make_pref("/file/send_im"), ""); - purple_prefs_add_bool(make_pref("/enabled/join_chat"), FALSE); - purple_prefs_add_path(make_pref("/file/join_chat"), ""); - purple_prefs_add_bool(make_pref("/enabled/left_chat"), FALSE); - purple_prefs_add_path(make_pref("/file/left_chat"), ""); - purple_prefs_add_bool(make_pref("/enabled/send_chat_msg"), FALSE); - purple_prefs_add_path(make_pref("/file/send_chat_msg"), ""); - purple_prefs_add_bool(make_pref("/enabled/chat_msg_recv"), FALSE); - purple_prefs_add_path(make_pref("/file/chat_msg_recv"), ""); - purple_prefs_add_bool(make_pref("/enabled/nick_said"), FALSE); - purple_prefs_add_path(make_pref("/file/nick_said"), ""); - purple_prefs_add_bool(make_pref("/enabled/pounce_default"), FALSE); - purple_prefs_add_path(make_pref("/file/pounce_default"), ""); + + for (event = sounds; event - sounds < PURPLE_NUM_SOUNDS; event++) { + char pref[512]; + g_snprintf(pref, sizeof(pref), "/enabled/%s", event->pref); + purple_prefs_add_bool(make_pref(pref), FALSE); + g_snprintf(pref, sizeof(pref), "/file/%s", event->pref); + purple_prefs_add_path(make_pref(pref), ""); + } + purple_prefs_add_bool(make_pref("/conv_focus"), FALSE); purple_prefs_add_bool(make_pref("/mute"), FALSE); purple_prefs_add_path(make_pref("/command"), ""); @@ -336,6 +333,25 @@ } static void +update_profiles(void) +{ + GList *list = finch_sound_get_profiles(); + for (; list; list = g_list_delete_link(list, list)) { + char pname[512]; + + /* got_attention was added in libpurple 2.7.0 */ + g_snprintf(pname, sizeof(pname), FINCH_PREFS_ROOT "/sound/profiles/%s%s", + (char *)list->data, "/enabled/got_attention"); + purple_prefs_add_bool(pname, FALSE); + g_snprintf(pname, sizeof(pname), FINCH_PREFS_ROOT "/sound/profiles/%s%s", + (char *)list->data, "/file/got_attention"); + purple_prefs_add_path(pname, ""); + + g_free(list->data); + } +} + +static void finch_sound_init(void) { void *gnt_sound_handle = finch_sound_get_handle(); @@ -356,7 +372,7 @@ purple_prefs_connect_callback(gnt_sound_handle, FINCH_PREFS_ROOT "/sound/actprofile", initialize_profile, NULL); purple_prefs_trigger_callback(FINCH_PREFS_ROOT "/sound/actprofile"); - + #ifdef USE_GSTREAMER purple_debug_info("sound", "Initializing sound output drivers.\n"); #if (GST_VERSION_MAJOR > 0 || \ @@ -399,6 +415,11 @@ purple_signal_connect(conv_handle, "received-chat-msg", gnt_sound_handle, PURPLE_CALLBACK(chat_msg_received_cb), GINT_TO_POINTER(PURPLE_SOUND_CHAT_SAY)); + purple_signal_connect(conv_handle, "got-attention", + gnt_sound_handle, PURPLE_CALLBACK(got_attention_cb), + GINT_TO_POINTER(PURPLE_SOUND_GOT_ATTENTION)); + + update_profiles(); } static void @@ -577,7 +598,8 @@ if ((event == PURPLE_SOUND_BUDDY_ARRIVE) && mute_login_sounds) return; - if (event >= PURPLE_NUM_SOUNDS) { + if (event >= PURPLE_NUM_SOUNDS || + event >= G_N_ELEMENTS(sounds)) { purple_debug_error("sound", "got request for unknown sound: %d\n", event); return; } diff -r c7fa7c7aca7d -r 47b6eda87723 finch/libgnt/configure.ac --- a/finch/libgnt/configure.ac Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/libgnt/configure.ac Mon Jul 19 21:11:32 2010 +0000 @@ -24,9 +24,9 @@ # Make sure to update ../../configure.ac with libgnt version changes. # -m4_define([gnt_lt_current], [7]) +m4_define([gnt_lt_current], [8]) m4_define([gnt_major_version], [2]) -m4_define([gnt_minor_version], [7]) +m4_define([gnt_minor_version], [8]) m4_define([gnt_micro_version], [0]) m4_define([gnt_version_suffix], [devel]) m4_define([gnt_version], @@ -265,6 +265,10 @@ for location in $ac_ncurses_includes /usr/include/ncursesw /usr/include do f="$location/ncurses.h" + orig_CFLAGS="$CFLAGS" + orig_CPPFLAGS="$CPPFLAGS" + CFLAGS="$CFLAGS -I$location" + CPPFLAGS="$CPPFLAGS -I$location" AC_CHECK_HEADER($f,[ AC_MSG_CHECKING([if $f supports wide characters]) AC_TRY_COMPILE([ @@ -283,9 +287,13 @@ fi found_ncurses_h=yes + CFLAGS="$orig_CFLAGS" + CPPFLAGS="$orig_CPPFLAGS" AC_MSG_RESULT([yes]) break ], [ + CFLAGS="$orig_CFLAGS" + CPPFLAGS="$orig_CPPFLAGS" AC_MSG_RESULT([no]) ]) ]) diff -r c7fa7c7aca7d -r 47b6eda87723 finch/libgnt/gntcombobox.c --- a/finch/libgnt/gntcombobox.c Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/libgnt/gntcombobox.c Mon Jul 19 21:11:32 2010 +0000 @@ -150,12 +150,11 @@ gnt_combo_box_key_pressed(GntWidget *widget, const char *text) { GntComboBox *box = GNT_COMBO_BOX(widget); - if (GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED)) - { - if (text[1] == 0) - { - switch (text[0]) - { + gboolean showing = !!GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED); + + if (showing) { + if (text[1] == 0) { + switch (text[0]) { case '\r': case '\t': case '\n': @@ -166,8 +165,42 @@ return TRUE; } } - if (gnt_widget_key_pressed(box->dropdown, text)) - return TRUE; + } + + if (gnt_widget_key_pressed(box->dropdown, text)) { + if (!showing) + popup_dropdown(box); + return TRUE; + } + + { +#define SEARCH_IN_RANGE(start, end) do { \ + GntTreeRow *row; \ + for (row = start; row != end; \ + row = gnt_tree_row_get_next(tree, row)) { \ + gpointer key = gnt_tree_row_get_key(tree, row); \ + GList *list = gnt_tree_get_row_text_list(tree, key); \ + gboolean found = FALSE; \ + found = (list->data && g_ascii_strncasecmp(text, list->data, len) == 0); \ + g_list_foreach(list, (GFunc)g_free, NULL); \ + g_list_free(list); \ + if (found) { \ + if (!showing) \ + popup_dropdown(box); \ + gnt_tree_set_selected(tree, key); \ + return TRUE; \ + } \ + } \ +} while (0) + + int len = strlen(text); + GntTree *tree = GNT_TREE(box->dropdown); + GntTreeRow *current = tree->current; + + SEARCH_IN_RANGE(gnt_tree_row_get_next(tree, current), NULL); + SEARCH_IN_RANGE(tree->top, current); + +#undef SEARCH_IN_RANGE } return FALSE; diff -r c7fa7c7aca7d -r 47b6eda87723 finch/libgnt/gnttree.c --- a/finch/libgnt/gnttree.c Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/libgnt/gnttree.c Mon Jul 19 21:11:32 2010 +0000 @@ -1926,3 +1926,33 @@ return (row && row->parent) ? row->parent->key : NULL; } +gpointer gnt_tree_row_get_key(GntTree *tree, GntTreeRow *row) +{ + g_return_val_if_fail(row && row->tree == tree, NULL); + return row->key; +} + +GntTreeRow * gnt_tree_row_get_next(GntTree *tree, GntTreeRow *row) +{ + g_return_val_if_fail(row && row->tree == tree, NULL); + return row->next; +} + +GntTreeRow * gnt_tree_row_get_prev(GntTree *tree, GntTreeRow *row) +{ + g_return_val_if_fail(row && row->tree == tree, NULL); + return row->prev; +} + +GntTreeRow * gnt_tree_row_get_child(GntTree *tree, GntTreeRow *row) +{ + g_return_val_if_fail(row && row->tree == tree, NULL); + return row->child; +} + +GntTreeRow * gnt_tree_row_get_parent(GntTree *tree, GntTreeRow *row) +{ + g_return_val_if_fail(row && row->tree == tree, NULL); + return row->parent; +} + diff -r c7fa7c7aca7d -r 47b6eda87723 finch/libgnt/gnttree.h --- a/finch/libgnt/gnttree.h Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/libgnt/gnttree.h Mon Jul 19 21:11:32 2010 +0000 @@ -222,6 +222,61 @@ GList * gnt_tree_get_row_text_list(GntTree *tree, gpointer key); /** + * Get the key of a row. + * + * @param tree The tree + * @param row The GntTreeRow object + * + * @return The key of the row. + * @since 2.8.0 (gnt), 2.7.2 (pidgin) + */ +gpointer gnt_tree_row_get_key(GntTree *tree, GntTreeRow *row); + +/** + * Get the next row. + * + * @param tree The tree + * @param row The GntTreeRow object + * + * @return The next row. + * @since 2.8.0 (gnt), 2.7.2 (pidgin) + */ +GntTreeRow * gnt_tree_row_get_next(GntTree *tree, GntTreeRow *row); + +/** + * Get the previous row. + * + * @param tree The tree + * @param row The GntTreeRow object + * + * @return The previous row. + * @since 2.8.0 (gnt), 2.7.2 (pidgin) + */ +GntTreeRow * gnt_tree_row_get_prev(GntTree *tree, GntTreeRow *row); + +/** + * Get the child row. + * + * @param tree The tree + * @param row The GntTreeRow object + * + * @return The child row. + * @since 2.8.0 (gnt), 2.7.2 (pidgin) + */ +GntTreeRow * gnt_tree_row_get_child(GntTree *tree, GntTreeRow *row); + +/** + * Get the parent row. + * + * @param tree The tree + * @param row The GntTreeRow object + * + * @return The parent row. + * @since 2.8.0 (gnt), 2.7.2 (pidgin) + */ +GntTreeRow * gnt_tree_row_get_parent(GntTree *tree, GntTreeRow *row); + +/** * Get a list of text of the current row. * * @param tree The tree diff -r c7fa7c7aca7d -r 47b6eda87723 finch/libgnt/gntutils.c --- a/finch/libgnt/gntutils.c Mon Jul 19 21:05:06 2010 +0000 +++ b/finch/libgnt/gntutils.c Mon Jul 19 21:11:32 2010 +0000 @@ -385,7 +385,6 @@ const char *name; char *content; xmlNode *ch; - gboolean processed = FALSE; char *url = NULL; gboolean insert_nl_s = FALSE, insert_nl_e = FALSE; @@ -426,17 +425,14 @@ for (ch = node->children; ch; ch = ch->next) { if (ch->type == XML_ELEMENT_NODE) { - processed = TRUE; util_parse_html_to_tv(ch, tv, flag); + } else if (ch->type == XML_TEXT_NODE) { + content = (char*)xmlNodeGetContent(ch); + gnt_text_view_append_text_with_flags(tv, content, flag); + xmlFree(content); } } - if (!processed) { - content = (char*)xmlNodeGetContent(node); - gnt_text_view_append_text_with_flags(tv, content, flag); - xmlFree(content); - } - if (url) { char *href = g_strdup_printf(" (%s)", url); gnt_text_view_append_text_with_flags(tv, href, flag); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/Makefile.am --- a/libpurple/Makefile.am Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/Makefile.am Mon Jul 19 21:11:32 2010 +0000 @@ -170,13 +170,11 @@ purple_builtheaders = purple.h version.h marshallers.h marshallers.h: marshallers.list - @echo "Generating marshallers.h" - $(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h + $(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h marshallers.c: marshallers.list marshallers.h - @echo "Generating marshallers.c" - echo "#include \"marshallers.h\"" > marshallers.c - $(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c + $(AM_V_GEN)echo "#include \"marshallers.h\"" > marshallers.c + $(AM_V_at)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c if ENABLE_DBUS @@ -212,16 +210,16 @@ $(srcdir)/protocols/jabber/libxmpp.c dbus-types.c: dbus-analyze-types.py $(purple_build_coreheaders) - cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@ + $(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@ dbus-types.h: dbus-analyze-types.py $(purple_build_coreheaders) - cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@ + $(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@ dbus-bindings.c: dbus-analyze-functions.py $(dbus_exported) - cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@ + $(AM_V_GEN)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@ dbus-signals.c: dbus-analyze-signals.py $(dbus_signals) - cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@ + $(AM_V_GEN)cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@ dbus-server.$(OBJEXT): dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h dbus-server.lo: dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h @@ -236,11 +234,11 @@ libpurple_client_la_LIBADD = $(DBUS_LIBS) purple-client-bindings.c: dbus-analyze-functions.py $(dbus_exported) - cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@ + $(AM_V_GEN)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@ purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(addprefix media/, $(purple_mediaheaders)) $(purple_builtheaders) $(dbus_exported) - cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@ - cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@ + $(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@ + $(AM_V_at)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@ $(libpurple_client_la_OBJECTS): purple-client-bindings.h purple-client-bindings.c diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/certificate.c --- a/libpurple/certificate.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/certificate.c Mon Jul 19 21:11:32 2010 +0000 @@ -99,7 +99,8 @@ "that can verify it is currently trusted."); break; case PURPLE_CERTIFICATE_NOT_ACTIVATED: - return _("The certificate is not valid yet."); + return _("The certificate is not valid yet. Check that your " + "computer's date and time are accurate."); break; case PURPLE_CERTIFICATE_EXPIRED: return _("The certificate has expired and should not be " diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/connection.c --- a/libpurple/connection.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/connection.c Mon Jul 19 21:11:32 2010 +0000 @@ -516,7 +516,8 @@ account = data; gc = purple_account_get_connection(account); - gc->disconnect_timeout = 0; + if (gc != NULL) + gc->disconnect_timeout = 0; password = g_strdup(purple_account_get_password(account)); purple_account_disconnect(account); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/plugins/Makefile.mingw --- a/libpurple/plugins/Makefile.mingw Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/plugins/Makefile.mingw Mon Jul 19 21:11:32 2010 +0000 @@ -55,7 +55,7 @@ $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) install cp *.dll $(PURPLE_INSTALL_PLUGINS_DIR) -.c.dll: +%.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H) $(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@.o -c $< $(CC) -shared $@.o $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@ diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/plugins/perl/common/Log.xs --- a/libpurple/plugins/perl/common/Log.xs Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/plugins/perl/common/Log.xs Mon Jul 19 21:11:32 2010 +0000 @@ -27,6 +27,9 @@ newCONSTSUB(flags_stash, (char *)civ->name, newSViv(civ->iv)); } +Purple::Handle +purple_log_get_handle() + int purple_log_common_sizer(log) Purple::Log log diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/auth_cyrus.c --- a/libpurple/protocols/jabber/auth_cyrus.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/auth_cyrus.c Mon Jul 19 21:11:32 2010 +0000 @@ -36,7 +36,7 @@ { purple_connection_error_reason(purple_account_get_connection(account), PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Server requires plaintext authentication over an unencrypted stream")); + _("Server may require plaintext authentication over an unencrypted stream")); } static void start_cyrus_wrapper(JabberStream *js) @@ -240,8 +240,9 @@ * it in plaintext, see if we can turn on * plaintext auth */ + /* XXX Should we just check for PLAIN/LOGIN being offered mechanisms? */ } else if (!plaintext) { - char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), + char *msg = g_strdup_printf(_("%s may require plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), purple_account_get_username(account)); purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/bosh.c --- a/libpurple/protocols/jabber/bosh.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/bosh.c Mon Jul 19 21:11:32 2010 +0000 @@ -195,6 +195,11 @@ g_free(path); conn->pipelining = TRUE; + if (purple_ip_address_is_valid(host)) + js->serverFQDN = g_strdup(js->user->domain); + else + js->serverFQDN = g_strdup(host); + if ((user && user[0] != '\0') || (passwd && passwd[0] != '\0')) { purple_debug_info("jabber", "Ignoring unexpected username and password " "in BOSH URL.\n"); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/caps.c --- a/libpurple/protocols/jabber/caps.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/caps.c Mon Jul 19 21:11:32 2010 +0000 @@ -29,6 +29,7 @@ #include "iq.h" #include "presence.h" #include "util.h" +#include "xdata.h" #define JABBER_CAPS_FILENAME "xmpp-caps.xml" @@ -41,20 +42,14 @@ static GHashTable *nodetable = NULL; /* char *node -> JabberCapsNodeExts */ static guint save_timer = 0; -/** - * Processes a query-node and returns a JabberCapsClientInfo object with all relevant info. - * - * @param query A query object. - * @return A JabberCapsClientInfo object. - */ -static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query); - /* Free a GList of allocated char* */ static void free_string_glist(GList *list) { - g_list_foreach(list, (GFunc)g_free, NULL); - g_list_free(list); + while (list) { + g_free(list->data); + list = g_list_delete_link(list, list); + } } static JabberCapsNodeExts* @@ -238,15 +233,15 @@ if(!capsdata) return; - if (strcmp(capsdata->name, "capabilities") != 0) { + if (!g_str_equal(capsdata->name, "capabilities")) { xmlnode_free(capsdata); return; } - for(client = capsdata->child; client; client = client->next) { - if(client->type != XMLNODE_TYPE_TAG) + for (client = capsdata->child; client; client = client->next) { + if (client->type != XMLNODE_TYPE_TAG) continue; - if(!strcmp(client->name, "client")) { + if (g_str_equal(client->name, "client")) { JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1); JabberCapsTuple *key = (JabberCapsTuple*)&value->tuple; xmlnode *child; @@ -259,15 +254,15 @@ if (key->hash == NULL) exts = jabber_caps_find_exts_by_node(key->node); - for(child = client->child; child; child = child->next) { - if(child->type != XMLNODE_TYPE_TAG) + for (child = client->child; child; child = child->next) { + if (child->type != XMLNODE_TYPE_TAG) continue; - if(!strcmp(child->name,"feature")) { + if (g_str_equal(child->name, "feature")) { const char *var = xmlnode_get_attrib(child, "var"); if(!var) continue; value->features = g_list_append(value->features,g_strdup(var)); - } else if(!strcmp(child->name,"identity")) { + } else if (g_str_equal(child->name, "identity")) { const char *category = xmlnode_get_attrib(child, "category"); const char *type = xmlnode_get_attrib(child, "type"); const char *name = xmlnode_get_attrib(child, "name"); @@ -284,15 +279,15 @@ id->lang = g_strdup(lang); value->identities = g_list_append(value->identities,id); - } else if(!strcmp(child->name,"x")) { + } else if (g_str_equal(child->name, "x")) { /* TODO: See #7814 -- this might cause problems if anyone * ever actually specifies forms. In fact, for this to * work properly, that bug needs to be fixed in * xmlnode_from_str, not the output version... */ value->forms = g_list_append(value->forms, xmlnode_copy(child)); - } else if (!strcmp(child->name, "ext") && key->hash != NULL) { + } else if (g_str_equal(child->name, "ext") && key->hash != NULL) { purple_debug_warning("jabber", "Ignoring exts when reading new-style caps\n"); - } else if (!strcmp(child->name, "ext")) { + } else if (g_str_equal(child->name, "ext")) { /* TODO: Do we care about reading in the identities listed here? */ const char *identifier = xmlnode_get_attrib(child, "identifier"); xmlnode *node; @@ -304,7 +299,7 @@ for (node = child->child; node; node = node->next) { if (node->type != XMLNODE_TYPE_TAG) continue; - if (!strcmp(node->name, "feature")) { + if (g_str_equal(node->name, "feature")) { const char *var = xmlnode_get_attrib(node, "var"); if (!var) continue; @@ -463,13 +458,13 @@ * size in jabber_caps_calculate_hash is large enough. The cipher API * doesn't seem to offer a "Get the hash size" function(?). */ - if (!strcmp(userdata->hash, "sha-1")) { + if (g_str_equal(userdata->hash, "sha-1")) { hash = jabber_caps_calculate_hash(info, "sha1"); - } else if (!strcmp(userdata->hash, "md5")) { + } else if (g_str_equal(userdata->hash, "md5")) { hash = jabber_caps_calculate_hash(info, "md5"); } - if (!hash || strcmp(hash, userdata->ver)) { + if (!hash || !g_str_equal(hash, userdata->ver)) { purple_debug_warning("jabber", "Could not validate caps info from " "%s. Expected %s, got %s\n", xmlnode_get_attrib(packet, "from"), @@ -709,44 +704,6 @@ } static gint -jabber_identity_compare(gconstpointer a, gconstpointer b) -{ - const JabberIdentity *ac; - const JabberIdentity *bc; - gint cat_cmp; - gint typ_cmp; - - ac = a; - bc = b; - - if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) { - if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) { - if (!ac->lang && !bc->lang) { - return 0; - } else if (ac->lang && !bc->lang) { - return 1; - } else if (!ac->lang && bc->lang) { - return -1; - } else { - return strcmp(ac->lang, bc->lang); - } - } else { - return typ_cmp; - } - } else { - return cat_cmp; - } -} - -static gchar *jabber_caps_get_formtype(const xmlnode *x) { - xmlnode *formtypefield; - formtypefield = xmlnode_get_child(x, "field"); - while (formtypefield && strcmp(xmlnode_get_attrib(formtypefield, "var"), "FORM_TYPE")) formtypefield = xmlnode_get_next_twin(formtypefield); - formtypefield = xmlnode_get_child(formtypefield, "value"); - return xmlnode_get_data(formtypefield);; -} - -static gint jabber_xdata_compare(gconstpointer a, gconstpointer b) { const xmlnode *aformtypefield = a; @@ -755,8 +712,8 @@ char *bformtype; int result; - aformtype = jabber_caps_get_formtype(aformtypefield); - bformtype = jabber_caps_get_formtype(bformtypefield); + aformtype = jabber_x_data_get_formtype(aformtypefield); + bformtype = jabber_x_data_get_formtype(bformtypefield); result = strcmp(aformtype, bformtype); g_free(aformtype); @@ -764,20 +721,21 @@ return result; } -static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) +JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) { xmlnode *child; JabberCapsClientInfo *info; - if (!query || strcmp(query->xmlns, NS_DISCO_INFO)) - return 0; + if (!query || !g_str_equal(query->name, "query") || + !purple_strequal(query->xmlns, NS_DISCO_INFO)) + return NULL; info = g_new0(JabberCapsClientInfo, 1); for(child = query->child; child; child = child->next) { if (child->type != XMLNODE_TYPE_TAG) continue; - if (!strcmp(child->name,"identity")) { + if (g_str_equal(child->name, "identity")) { /* parse identity */ const char *category = xmlnode_get_attrib(child, "category"); const char *type = xmlnode_get_attrib(child, "type"); @@ -795,13 +753,13 @@ id->lang = g_strdup(lang); info->identities = g_list_append(info->identities, id); - } else if (!strcmp(child->name, "feature")) { + } else if (g_str_equal(child->name, "feature")) { /* parse feature */ const char *var = xmlnode_get_attrib(child, "var"); if (var) info->features = g_list_prepend(info->features, g_strdup(var)); - } else if (!strcmp(child->name, "x")) { - if (child->xmlns && !strcmp(child->xmlns, "jabber:x:data")) { + } else if (g_str_equal(child->name, "x")) { + if (purple_strequal(child->xmlns, "jabber:x:data")) { /* x-data form */ xmlnode *dataform = xmlnode_copy(child); info->forms = g_list_append(info->forms, dataform); @@ -848,9 +806,12 @@ static void append_escaped_string(PurpleCipherContext *context, const gchar *str) { - char *tmp = g_markup_escape_text(str, -1); - purple_cipher_context_append(context, (const guchar *)tmp, strlen(tmp)); - g_free(tmp); + if (str && *str) { + char *tmp = g_markup_escape_text(str, -1); + purple_cipher_context_append(context, (const guchar *)tmp, strlen(tmp)); + g_free(tmp); + } + purple_cipher_context_append(context, (const guchar *)"<", 1); } @@ -904,7 +865,7 @@ /* concat x-data forms to the verification string */ for(node = info->forms; node; node = node->next) { xmlnode *data = (xmlnode *)node->data; - gchar *formtype = jabber_caps_get_formtype(data); + gchar *formtype = jabber_x_data_get_formtype(data); GList *fields = jabber_caps_xdata_get_fields(data); /* append FORM_TYPE's field value to the verification string */ @@ -965,6 +926,10 @@ } info.features = features; + /* TODO: This copy can go away, I think, since jabber_identities + * is pre-sorted, so the sort in calculate_hash should be idempotent. + * However, I want to test that. --darkrain + */ info.identities = g_list_copy(jabber_identities); info.forms = NULL; @@ -989,7 +954,7 @@ for (node = accounts; node; node = node->next) { PurpleAccount *account = node->data; const char *prpl_id = purple_account_get_protocol_id(account); - if (!strcmp("prpl-jabber", prpl_id) && purple_account_is_connected(account)) { + if (g_str_equal("prpl-jabber", prpl_id) && purple_account_is_connected(account)) { PurpleConnection *gc = purple_account_get_connection(account); jabber_presence_send(gc->proto_data, TRUE); } diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/caps.h --- a/libpurple/protocols/jabber/caps.h Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/caps.h Mon Jul 19 21:11:32 2010 +0000 @@ -115,4 +115,15 @@ */ void jabber_caps_broadcast_change(void); +/** + * Parse the element from an IQ stanza into a JabberCapsClientInfo + * struct. + * + * Exposed for tests + * + * @param query The 'query' element from an IQ reply stanza. + * @returns A JabberCapsClientInfo struct, or NULL on error + */ +JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query); + #endif /* PURPLE_JABBER_CAPS_H_ */ diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/jabber.c Mon Jul 19 21:11:32 2010 +0000 @@ -1962,20 +1962,53 @@ } } -void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name) { +gint +jabber_identity_compare(gconstpointer a, gconstpointer b) +{ + const JabberIdentity *ac; + const JabberIdentity *bc; + gint cat_cmp; + gint typ_cmp; + + ac = a; + bc = b; + + if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) { + if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) { + if (!ac->lang && !bc->lang) { + return 0; + } else if (ac->lang && !bc->lang) { + return 1; + } else if (!ac->lang && bc->lang) { + return -1; + } else { + return strcmp(ac->lang, bc->lang); + } + } else { + return typ_cmp; + } + } else { + return cat_cmp; + } +} + +void jabber_add_identity(const gchar *category, const gchar *type, + const gchar *lang, const gchar *name) +{ GList *identity; JabberIdentity *ident; + /* both required according to XEP-0030 */ g_return_if_fail(category != NULL); g_return_if_fail(type != NULL); - for(identity = jabber_identities; identity; identity = identity->next) { - JabberIdentity *ident = (JabberIdentity*)identity->data; - if (!strcmp(ident->category, category) && - !strcmp(ident->type, type) && - ((!ident->lang && !lang) || (ident->lang && lang && !strcmp(ident->lang, lang)))) { + /* Check if this identity is already there... */ + for (identity = jabber_identities; identity; identity = identity->next) { + JabberIdentity *id = identity->data; + if (g_str_equal(id->category, category) && + g_str_equal(id->type, type) && + purple_strequal(id->lang, lang)) return; - } } ident = g_new0(JabberIdentity, 1); @@ -1983,7 +2016,8 @@ ident->type = g_strdup(type); ident->lang = g_strdup(lang); ident->name = g_strdup(name); - jabber_identities = g_list_prepend(jabber_identities, ident); + jabber_identities = g_list_insert_sorted(jabber_identities, ident, + jabber_identity_compare); } static void jabber_identities_destroy(void) @@ -3440,8 +3474,7 @@ for (iter = jb->resources; iter ; iter = g_list_next(iter)) { JabberBuddyResource *jbr = (JabberBuddyResource *) iter->data; - if (jabber_resource_has_capability(jbr, - "http://jabber.org/protocol/si/profile/file-transfer") + if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER) && (jabber_resource_has_capability(jbr, NS_BYTESTREAMS) || jabber_resource_has_capability(jbr, NS_IBB))) { @@ -3743,7 +3776,7 @@ jabber_add_feature("http://jabber.org/protocol/muc", 0); jabber_add_feature("http://jabber.org/protocol/muc#user", 0); jabber_add_feature("http://jabber.org/protocol/si", 0); - jabber_add_feature("http://jabber.org/protocol/si/profile/file-transfer", 0); + jabber_add_feature(NS_SI_FILE_TRANSFER, 0); jabber_add_feature(NS_XHTML_IM, 0); jabber_add_feature(NS_PING, 0); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/jabber.h Mon Jul 19 21:11:32 2010 +0000 @@ -303,6 +303,9 @@ /* what kind of additional features as returned from disco#info are supported? */ extern GList *jabber_features; +/* A sorted list of identities advertised. Use jabber_add_identity to add + * so it remains sorted. + */ extern GList *jabber_identities; void jabber_stream_features_parse(JabberStream *js, xmlnode *packet); @@ -342,6 +345,11 @@ void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name); /** + * GCompareFunc for JabberIdentity structs. + */ +gint jabber_identity_compare(gconstpointer a, gconstpointer b); + +/** * Returns true if this connection is over a secure (SSL) stream. Use this * instead of checking js->gsc because BOSH stores its PurpleSslConnection * members in its own data structure. diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/namespaces.h --- a/libpurple/protocols/jabber/namespaces.h Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/namespaces.h Mon Jul 19 21:11:32 2010 +0000 @@ -61,6 +61,9 @@ #define NS_AVATAR_1_1_DATA "urn:xmpp:avatar:data" #define NS_AVATAR_1_1_METADATA "urn:xmpp:avatar:metadata" +/* XEP-0096 SI File Transfer */ +#define NS_SI_FILE_TRANSFER "http://jabber.org/protocol/si/profile/file-transfer" + /* XEP-0124 Bidirectional-streams Over Synchronous HTTP (BOSH) */ #define NS_BOSH "http://jabber.org/protocol/httpbind" diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/si.c --- a/libpurple/protocols/jabber/si.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/si.c Mon Jul 19 21:11:32 2010 +0000 @@ -1262,12 +1262,10 @@ xmlnode_set_namespace(si, "http://jabber.org/protocol/si"); jsx->stream_id = jabber_get_next_id(jsx->js); xmlnode_set_attrib(si, "id", jsx->stream_id); - xmlnode_set_attrib(si, "profile", - "http://jabber.org/protocol/si/profile/file-transfer"); + xmlnode_set_attrib(si, "profile", NS_SI_FILE_TRANSFER); file = xmlnode_new_child(si, "file"); - xmlnode_set_namespace(file, - "http://jabber.org/protocol/si/profile/file-transfer"); + xmlnode_set_namespace(file, NS_SI_FILE_TRANSFER); xmlnode_set_attrib(file, "name", xfer->filename); g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size); xmlnode_set_attrib(file, "size", buf); @@ -1488,7 +1486,7 @@ if (jabber_resource_has_capability(jbr, NS_IBB)) jsx->stream_method |= STREAM_METHOD_IBB; - if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/si/profile/file-transfer")) { + if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)) { jabber_si_xfer_send_request(xfer); return; } @@ -1523,7 +1521,8 @@ JabberBuddy *jb; JabberBuddyResource *jbr = NULL; char *resource; - + GList *resources = NULL; + if(NULL != (resource = jabber_get_resource(xfer->who))) { /* they've specified a resource, no need to ask or * default or anything, just do it */ @@ -1535,7 +1534,22 @@ jb = jabber_buddy_find(jsx->js, xfer->who, TRUE); - if(!jb || !jb->resources) { + if (jb) { + GList *l; + + for (l = jb->resources ; l ; l = g_list_next(l)) { + jbr = l->data; + + if (!jabber_resource_know_capabilities(jbr) || + (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER) + && (jabber_resource_has_capability(jbr, NS_BYTESTREAMS) + || jabber_resource_has_capability(jbr, NS_IBB)))) { + resources = g_list_append(resources, jbr); + } + } + } + + if (!resources) { /* no resources online, we're trying to send to someone * whose presence we're not subscribed to, or * someone who is offline. Let's inform the user */ @@ -1551,13 +1565,11 @@ purple_notify_error(jsx->js->gc, _("File Send Failed"), _("File Send Failed"), msg); g_free(msg); - } else if(!jb->resources->next) { + } else if (g_list_length(resources) == 1) { /* only 1 resource online (probably our most common case) * so no need to ask who to send to */ - jbr = jb->resources->data; - + jbr = resources->data; do_transfer_send(xfer, jbr->name); - } else { /* we've got multiple resources, we need to pick one to send to */ GList *l; @@ -1565,11 +1577,9 @@ PurpleRequestFields *fields = purple_request_fields_new(); PurpleRequestField *field = purple_request_field_choice_new("resource", _("Resource"), 0); PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL); - - for(l = jb->resources; l; l = l->next) - { + + for(l = resources; l; l = l->next) { jbr = l->data; - purple_request_field_choice_add(field, jbr->name); } @@ -1583,6 +1593,8 @@ g_free(msg); } + + g_list_free(resources); } else { xmlnode *si, *feature, *x, *field, *value; @@ -1695,7 +1707,7 @@ size_t filesize = 0; if(!(profile = xmlnode_get_attrib(si, "profile")) || - strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) + strcmp(profile, NS_SI_FILE_TRANSFER)) return; if(!(stream_id = xmlnode_get_attrib(si, "id"))) diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/xdata.c --- a/libpurple/protocols/jabber/xdata.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/xdata.c Mon Jul 19 21:11:32 2010 +0000 @@ -411,4 +411,30 @@ return handle; } +gchar * +jabber_x_data_get_formtype(const xmlnode *form) +{ + xmlnode *field; + g_return_val_if_fail(form != NULL, NULL); + + for (field = xmlnode_get_child((xmlnode *)form, "field"); field; + field = xmlnode_get_next_twin(field)) { + const char *var = xmlnode_get_attrib(field, "var"); + if (purple_strequal(var, "FORM_TYPE")) { + xmlnode *value = xmlnode_get_child(field, "value"); + if (value) + return xmlnode_get_data(value); + else + /* An interesting corner case... Looking for a second + * FORM_TYPE would be more considerate, but I'm in favor + * of not helping broken clients. + */ + return NULL; + } + } + + /* Erm, none found :( */ + return NULL; +} + diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/jabber/xdata.h --- a/libpurple/protocols/jabber/xdata.h Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/jabber/xdata.h Mon Jul 19 21:11:32 2010 +0000 @@ -37,4 +37,19 @@ void *jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data); void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data); +/* + * Return the form type (the CDATA of the value child of the FORM_TYPE + * field entry. + * E.g., for the following, "http://jabber.org/protocol/muc#roominfo". + * + * + * http://jabber.org/protocol/muc#roominfo + * + * + * + * @param form The xmlnode for the form (the 'x' element) + * @returns The FORM_TYPE. Must be freed by caller. + */ +gchar *jabber_x_data_get_formtype(const xmlnode *form); + #endif /* PURPLE_JABBER_XDATA_H_ */ diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/msn/notification.c Mon Jul 19 21:11:32 2010 +0000 @@ -431,7 +431,7 @@ /* add contact to xmlnode */ static void -msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnNetwork networkId) +msn_add_contact_xml(xmlnode *mlNode, const char *passport, MsnListOp list_op, MsnNetwork networkId) { xmlnode *d_node,*c_node; char **tokens; @@ -565,8 +565,8 @@ adl_node = xmlnode_new("ml"); xmlnode_set_attrib(adl_node, "l", "1"); - msn_add_contact_xml(session, adl_node, passport, - user->list_op & MSN_LIST_OP_MASK, network); + msn_add_contact_xml(adl_node, passport, + user->list_op & MSN_LIST_OP_MASK, network); payload = xmlnode_to_str(adl_node, &payload_len); msn_notification_post_adl(session->notification->cmdproc, payload, payload_len); g_free(payload); @@ -622,8 +622,9 @@ } if (user->networkid != MSN_NETWORK_UNKNOWN) { - msn_add_contact_xml(session, adl_node, user->passport, - user->list_op & MSN_LIST_OP_MASK, user->networkid); + msn_add_contact_xml(adl_node, user->passport, + user->list_op & MSN_LIST_OP_MASK, + user->networkid); /* each ADL command may contain up to 150 contacts */ if (++adl_count % 150 == 0) { @@ -652,8 +653,7 @@ purple_debug_info("msn", "Adding FQY address, count is %d\n", session->adl_fqy); - msn_add_contact_xml(session, fqy_node, user->passport, - 0, user->networkid); + msn_add_contact_xml(fqy_node, user->passport, 0, user->networkid); /* each FQY command may contain up to 150 contacts, probably */ if (++fqy_count % 150 == 0) { @@ -696,6 +696,8 @@ xmlnode_free(adl_node); xmlnode_free(fqy_node); + msn_session_activate_login_timeout(session); + pc = purple_account_get_connection(session->account); display_name = purple_connection_get_display_name(pc); if (display_name @@ -2255,8 +2257,7 @@ node = xmlnode_new("ml"); node->child = NULL; - msn_add_contact_xml(session, node, passport, - addrem->list_op, network); + msn_add_contact_xml(node, passport, addrem->list_op, network); payload = xmlnode_to_str(node, &payload_len); xmlnode_free(node); @@ -2286,8 +2287,7 @@ adl_node = xmlnode_new("ml"); adl_node->child = NULL; - msn_add_contact_xml(notification->session, adl_node, user->passport, - list_op, user->networkid); + msn_add_contact_xml(adl_node, user->passport, list_op, user->networkid); payload = xmlnode_to_str(adl_node, &payload_len); xmlnode_free(adl_node); @@ -2325,8 +2325,7 @@ rml_node = xmlnode_new("ml"); rml_node->child = NULL; - msn_add_contact_xml(notification->session, rml_node, user->passport, - list_op, user->networkid); + msn_add_contact_xml(rml_node, user->passport, list_op, user->networkid); payload = xmlnode_to_str(rml_node, &payload_len); xmlnode_free(rml_node); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/msn/session.c --- a/libpurple/protocols/msn/session.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/msn/session.c Mon Jul 19 21:11:32 2010 +0000 @@ -134,6 +134,11 @@ if (!session->connected) return; + if (session->login_timeout) { + purple_timeout_remove(session->login_timeout); + session->login_timeout = 0; + } + session->connected = FALSE; while (session->switches != NULL) @@ -261,6 +266,28 @@ return swboard; } +static gboolean +msn_login_timeout_cb(gpointer data) +{ + MsnSession *session = data; + /* This forces the login process to finish, even though we haven't heard + a response for our FQY requests yet. We'll at least end up online to the + people we've already added. The rest will follow later. */ + msn_session_finish_login(session); + session->login_timeout = 0; + return FALSE; +} + +void +msn_session_activate_login_timeout(MsnSession *session) +{ + if (!session->logged_in) { + session->login_timeout = + purple_timeout_add_seconds(MSN_LOGIN_FQY_TIMEOUT, + msn_login_timeout_cb, session); + } +} + static void msn_session_sync_users(MsnSession *session) { @@ -365,6 +392,9 @@ msg = g_strdup_printf(_("Unable to authenticate: %s"), (info == NULL ) ? _("Unknown error") : info); + /* Clear the password if it isn't being saved */ + if (!purple_account_get_remember_password(session->account)) + purple_account_set_password(session->account, NULL); break; case MSN_ERROR_BAD_BLIST: reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/msn/session.h --- a/libpurple/protocols/msn/session.h Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/msn/session.h Mon Jul 19 21:11:32 2010 +0000 @@ -59,6 +59,8 @@ #define MSN_LOGIN_STEPS MSN_LOGIN_STEP_END +#define MSN_LOGIN_FQY_TIMEOUT 30 + #include "nexus.h" #include "notification.h" #include "oim.h" @@ -78,6 +80,7 @@ gboolean connected; gboolean logged_in; /**< A temporal flag to ignore local buddy list adds. */ int adl_fqy; /**< A count of ADL/FQY so status is only changed once. */ + guint login_timeout; /**< Timeout to force status change if ADL/FQY fail. */ gboolean destroying; /**< A flag that states if the session is being destroyed. */ gboolean http_method; @@ -208,7 +211,16 @@ const char *info); /** - * Sets the current step in the login proccess. + * Starts a timeout to initiate finishing login. Sometimes the server ignores + * our FQY requests, so this forces ourselves online eventually. + * + * @param session The MSN session. + */ +void +msn_session_activate_login_timeout(MsnSession *session); + +/** + * Sets the current step in the login process. * * @param session The MSN session. * @param step The current step. diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/msn/slplink.c --- a/libpurple/protocols/msn/slplink.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/msn/slplink.c Mon Jul 19 21:11:32 2010 +0000 @@ -221,11 +221,16 @@ slplink->swboard = NULL; } - /* The slplink has no slpcalls in it, release it from the DC. */ - if (slplink->slp_calls == NULL && slplink->dc != NULL) { - slplink->dc->slplink = NULL; - msn_dc_destroy(slplink->dc); - slplink->dc = NULL; + if (slplink->dc != NULL) { + if ((slplink->dc->state != DC_STATE_ESTABLISHED && slplink->dc->slpcall == slpcall) + || (slplink->slp_calls == NULL)) { + /* The DC is not established and its corresponding slpcall is dead, + * or the slplink has no slpcalls in it and no longer needs the DC. + */ + slplink->dc->slplink = NULL; + msn_dc_destroy(slplink->dc); + slplink->dc = NULL; + } } } diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/msn/switchboard.c --- a/libpurple/protocols/msn/switchboard.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/msn/switchboard.c Mon Jul 19 21:11:32 2010 +0000 @@ -647,8 +647,13 @@ else if ((swboard->current_users > 1) || (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) { + GList *passport; /* This is a switchboard used for a chat */ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL); + + passport = g_list_find_custom(swboard->users, user, (GCompareFunc)strcmp); + g_free(passport->data); + swboard->users = g_list_delete_link(swboard->users, passport); swboard->current_users--; if (swboard->current_users == 0) msn_switchboard_destroy(swboard); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/oscar/oscar.c Mon Jul 19 21:11:32 2010 +0000 @@ -2667,10 +2667,10 @@ g_free(rtfmsg); } } - } else if (args->info.rtfmsg.msgtype == 26) + } else if (args->info.rtfmsg.msgtype == 26) { purple_debug_info("oscar", "Sending X-Status Reply\n"); icq_relay_xstatus(od, userinfo->bn, args->cookie); - + } } else { diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/oscar/util.c --- a/libpurple/protocols/oscar/util.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/oscar/util.c Mon Jul 19 21:11:32 2010 +0000 @@ -38,8 +38,8 @@ static const char * const msgerrreason[] = { N_("Invalid error"), N_("Invalid SNAC"), - N_("Rate to host"), - N_("Rate to client"), + N_("Server rate limit exceeded"), + N_("Client rate limit exceeded"), N_("Not logged in"), N_("Service unavailable"), N_("Service not defined"), diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/protocols/yahoo/yahoo_filexfer.c --- a/libpurple/protocols/yahoo/yahoo_filexfer.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Mon Jul 19 21:11:32 2010 +0000 @@ -983,8 +983,8 @@ struct yahoo_xfer_data *xd; struct sockaddr_in *addr; struct yahoo_packet *pkt; - long actaddr; - long a,b,c,d; + unsigned long actaddr; + unsigned char a,b,c,d; PurpleConnection *gc; PurpleAccount *account; YahooData *yd; @@ -1018,19 +1018,19 @@ /* TODO:actually, u must try with addr no.1 , if its not working addr no.2 ..... */ addr = hosts->data; actaddr = addr->sin_addr.s_addr; - d = actaddr % 256; - actaddr = (actaddr - d) / 256; - c = actaddr % 256; - actaddr = (actaddr - c) / 256; - b = actaddr % 256; - actaddr = (actaddr - b) / 256; - a = actaddr; + d = actaddr & 0xff; + actaddr >>= 8; + c = actaddr & 0xff; + actaddr >>= 8; + b = actaddr & 0xff; + actaddr >>= 8; + a = actaddr & 0xff; if(yd->jp) xd->port = YAHOOJP_XFER_RELAY_PORT; else xd->port = YAHOO_XFER_RELAY_PORT; - url = g_strdup_printf("%ld.%ld.%ld.%ld", d, c, b, a); + url = g_strdup_printf("%u.%u.%u.%u", d, c, b, a); /* Free the address... */ g_free(hosts->data); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/tests/Makefile.am --- a/libpurple/tests/Makefile.am Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/tests/Makefile.am Mon Jul 19 21:11:32 2010 +0000 @@ -10,6 +10,7 @@ check_libpurple.c \ tests.h \ test_cipher.c \ + test_jabber_caps.c \ test_jabber_jutil.c \ test_jabber_scram.c \ test_qq.c \ diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/tests/check_libpurple.c --- a/libpurple/tests/check_libpurple.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/tests/check_libpurple.c Mon Jul 19 21:11:32 2010 +0000 @@ -84,6 +84,7 @@ sr = srunner_create (master_suite()); srunner_add_suite(sr, cipher_suite()); + srunner_add_suite(sr, jabber_caps_suite()); srunner_add_suite(sr, jabber_jutil_suite()); srunner_add_suite(sr, jabber_scram_suite()); srunner_add_suite(sr, qq_suite()); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/tests/test_jabber_caps.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/tests/test_jabber_caps.c Mon Jul 19 21:11:32 2010 +0000 @@ -0,0 +1,54 @@ +#include + +#include "tests.h" +#include "../xmlnode.h" +#include "../protocols/jabber/caps.h" + +START_TEST(test_parse_invalid) +{ + xmlnode *query; + + fail_unless(NULL == jabber_caps_parse_client_info(NULL)); + + /* Something other than a disco#info query */ + query = xmlnode_new("foo"); + fail_unless(NULL == jabber_caps_parse_client_info(query)); + xmlnode_free(query); + + query = xmlnode_new("query"); + fail_unless(NULL == jabber_caps_parse_client_info(query)); + xmlnode_set_namespace(query, "jabber:iq:last"); + fail_unless(NULL == jabber_caps_parse_client_info(query)); + xmlnode_free(query); +} +END_TEST + +#define assert_caps_calculate_match(hash_func, hash, str) { \ + xmlnode *query = xmlnode_from_str((str), -1); \ + JabberCapsClientInfo *info = jabber_caps_parse_client_info(query); \ + gchar *got_hash = jabber_caps_calculate_hash(info, (hash_func)); \ + assert_string_equal_free((hash), got_hash); \ +} + +START_TEST(test_calculate_caps) +{ + assert_caps_calculate_match("sha1", "GNjxthSckUNvAIoCCJFttjl6VL8=", + "urn:xmpp:dataforms:softwareinfoTkabber ( 8.5.5 )ATmega640-16AU"); +} +END_TEST + +Suite * +jabber_caps_suite(void) +{ + Suite *s = suite_create("Jabber Caps Functions"); + + TCase *tc = tcase_create("Parsing invalid ndoes"); + tcase_add_test(tc, test_parse_invalid); + suite_add_tcase(s, tc); + + tc = tcase_create("Calculating from XMLnode"); + tcase_add_test(tc, test_calculate_caps); + suite_add_tcase(s, tc); + + return s; +} diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/tests/tests.h --- a/libpurple/tests/tests.h Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/tests/tests.h Mon Jul 19 21:11:32 2010 +0000 @@ -9,6 +9,7 @@ /* remember to add the suite to the runner in check_libpurple.c */ Suite * master_suite(void); Suite * cipher_suite(void); +Suite * jabber_caps_suite(void); Suite * jabber_jutil_suite(void); Suite * jabber_scram_suite(void); Suite * qq_suite(void); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/upnp.c --- a/libpurple/upnp.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/upnp.c Mon Jul 19 21:11:32 2010 +0000 @@ -403,6 +403,11 @@ lookup_internal_ip(); } + if (dd->inpa > 0) + purple_input_remove(dd->inpa); + if (dd->tima > 0) + purple_timeout_remove(dd->tima); + g_free(dd); } @@ -506,6 +511,8 @@ if (dd->inpa) purple_input_remove(dd->inpa); + if (dd->tima > 0) + purple_timeout_remove(dd->tima); dd->inpa = 0; dd->tima = 0; @@ -610,7 +617,7 @@ /* We have already done all our retries. Make sure that the callback * doesn't get called before the original function returns */ - purple_timeout_add(10, purple_upnp_discover_timeout, dd); + dd->tima = purple_timeout_add(10, purple_upnp_discover_timeout, dd); } void @@ -647,7 +654,7 @@ "purple_upnp_discover(): Failed In sock creation\n"); /* Short circuit the retry attempts */ dd->retry_count = NUM_UDP_ATTEMPTS; - purple_timeout_add(10, purple_upnp_discover_timeout, dd); + dd->tima = purple_timeout_add(10, purple_upnp_discover_timeout, dd); return; } @@ -659,7 +666,7 @@ "purple_upnp_discover(): Failed In gethostbyname\n"); /* Short circuit the retry attempts */ dd->retry_count = NUM_UDP_ATTEMPTS; - purple_timeout_add(10, purple_upnp_discover_timeout, dd); + dd->tima = purple_timeout_add(10, purple_upnp_discover_timeout, dd); return; } diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/util.c --- a/libpurple/util.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/util.c Mon Jul 19 21:11:32 2010 +0000 @@ -3423,7 +3423,7 @@ char **ret_path, char **ret_user, char **ret_passwd) { gboolean is_https = FALSE; - char scan_info[255]; + const char * scan_info; char port_str[6]; int f; const char *at, *slash; @@ -3431,11 +3431,12 @@ char host[256], path[256], user[256], passwd[256]; int port = 0; /* hyphen at end includes it in control set */ - static const char addr_ctrl[] = "A-Za-z0-9.-"; - static const char port_ctrl[] = "0-9"; - static const char page_ctrl[] = "A-Za-z0-9.~_/:*!@&%%?=+^-"; - static const char user_ctrl[] = "A-Za-z0-9.~_/*!&%%?=+^-"; - static const char passwd_ctrl[] = "A-Za-z0-9.~_/*!&%%?=+^-"; + +#define ADDR_CTRL "A-Za-z0-9.-" +#define PORT_CTRL "0-9" +#define PAGE_CTRL "A-Za-z0-9.~_/:*!@&%%?=+^-" +#define USER_CTRL "A-Za-z0-9.~_/*!&%%?=+^-" +#define PASSWD_CTRL "A-Za-z0-9.~_/*!&%%?=+^-" g_return_val_if_fail(url != NULL, FALSE); @@ -3455,37 +3456,32 @@ /* Only care about @ char BEFORE the first / */ at = strchr(url, '@'); slash = strchr(url, '/'); - if ((at != NULL) && - (((slash != NULL) && (strlen(at) > strlen(slash))) || - (slash == NULL))) { - g_snprintf(scan_info, sizeof(scan_info), - "%%255[%s]:%%255[%s]^@", user_ctrl, passwd_ctrl); + f = 0; + if (at && (!slash || at < slash)) { + scan_info = "%255[" USER_CTRL "]:%255[" PASSWD_CTRL "]^@"; f = sscanf(url, scan_info, user, passwd); - if (f ==1 ) { + if (f == 1) { /* No passwd, possibly just username supplied */ - g_snprintf(scan_info, sizeof(scan_info), - "%%255[%s]^@", user_ctrl); + scan_info = "%255[" USER_CTRL "]^@"; f = sscanf(url, scan_info, user); - *passwd = '\0'; } url = at+1; /* move pointer after the @ char */ - } else { + } + + if (f < 1) { *user = '\0'; *passwd = '\0'; - } - - g_snprintf(scan_info, sizeof(scan_info), - "%%255[%s]:%%5[%s]/%%255[%s]", addr_ctrl, port_ctrl, page_ctrl); - + } else if (f == 1) + *passwd = '\0'; + + scan_info = "%255[" ADDR_CTRL "]:%5[" PORT_CTRL "]/%255[" PAGE_CTRL "]"; f = sscanf(url, scan_info, host, port_str, path); if (f == 1) { - g_snprintf(scan_info, sizeof(scan_info), - "%%255[%s]/%%255[%s]", - addr_ctrl, page_ctrl); + scan_info = "%255[" ADDR_CTRL "]/%255[" PAGE_CTRL "]"; f = sscanf(url, scan_info, host, path); /* Use the default port */ if (is_https) @@ -3509,6 +3505,12 @@ if (ret_passwd != NULL) *ret_passwd = g_strdup(passwd); return ((*host != '\0') ? TRUE : FALSE); + +#undef ADDR_CTRL +#undef PORT_CTRL +#undef PAGE_CTRL +#undef USER_CTRL +#undef PASSWD_CTRL } /** diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/xmlnode.c --- a/libpurple/xmlnode.c Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/xmlnode.c Mon Jul 19 21:11:32 2010 +0000 @@ -223,7 +223,7 @@ const char * -xmlnode_get_attrib(xmlnode *node, const char *attr) +xmlnode_get_attrib(const xmlnode *node, const char *attr) { xmlnode *x; @@ -240,9 +240,9 @@ } const char * -xmlnode_get_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns) +xmlnode_get_attrib_with_namespace(const xmlnode *node, const char *attr, const char *xmlns) { - xmlnode *x; + const xmlnode *x; g_return_val_if_fail(node != NULL, NULL); g_return_val_if_fail(attr != NULL, NULL); diff -r c7fa7c7aca7d -r 47b6eda87723 libpurple/xmlnode.h --- a/libpurple/xmlnode.h Mon Jul 19 21:05:06 2010 +0000 +++ b/libpurple/xmlnode.h Mon Jul 19 21:11:32 2010 +0000 @@ -205,7 +205,7 @@ * * @return The value of the attribute. */ -const char *xmlnode_get_attrib(xmlnode *node, const char *attr); +const char *xmlnode_get_attrib(const xmlnode *node, const char *attr); /** * Gets a namespaced attribute from a node @@ -216,7 +216,7 @@ * * @return The value of the attribute/ */ -const char *xmlnode_get_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns); +const char *xmlnode_get_attrib_with_namespace(const xmlnode *node, const char *attr, const char *xmlns); /** * Removes an attribute from a node. diff -r c7fa7c7aca7d -r 47b6eda87723 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Mon Jul 19 21:05:06 2010 +0000 +++ b/pidgin/gtkblist.c Mon Jul 19 21:11:32 2010 +0000 @@ -3601,11 +3601,12 @@ purple_request_field_list_add_selected(f, _("None")); /* TODO: rlaager wants this sorted. */ - /* The connection is checked for PURPLE_CONNECTION_SUPPORT_MOODS flag before - * this function is called for a non-null account. So using - * PURPLE_PROTOCOL_PLUGIN_HAS_FUNC isn't necessary here */ - for (mood = account ? prpl_info->get_moods(account) : global_moods; - mood->mood != NULL ; mood++) { + /* TODO: darkrain wants it sorted post-translation */ + if (account && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods)) + mood = prpl_info->get_moods(account); + else + mood = global_moods; + for ( ; mood->mood != NULL ; mood++) { char *path; if (mood->mood == NULL || mood->description == NULL) diff -r c7fa7c7aca7d -r 47b6eda87723 pidgin/gtkimhtmltoolbar.c --- a/pidgin/gtkimhtmltoolbar.c Mon Jul 19 21:05:06 2010 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Mon Jul 19 21:11:32 2010 +0000 @@ -1276,6 +1276,15 @@ } static void +button_visibility_changed(GtkWidget *button, gpointer dontcare, GtkWidget *item) +{ + if (GTK_WIDGET_VISIBLE(button)) + gtk_widget_hide(item); + else + gtk_widget_show(item); +} + +static void button_sensitiveness_changed(GtkWidget *button, gpointer dontcare, GtkWidget *item) { gtk_widget_set_sensitive(item, GTK_WIDGET_IS_SENSITIVE(button)); @@ -1323,6 +1332,7 @@ GtkWidget *insert_menu; GtkWidget *menuitem; GtkWidget *sep; + GObject *wide_attention_button; int i; struct { const char *label; @@ -1393,6 +1403,8 @@ gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), menuitem); g_signal_connect(G_OBJECT(old), "notify::sensitive", G_CALLBACK(button_sensitiveness_changed), menuitem); + g_signal_connect(G_OBJECT(old), "notify::visible", + G_CALLBACK(button_visibility_changed), menuitem); gtk_container_foreach(GTK_CONTAINER(menuitem), (GtkCallback)enable_markup, NULL); } @@ -1425,12 +1437,16 @@ gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem); g_signal_connect(G_OBJECT(toolbar->image), "notify::sensitive", G_CALLBACK(button_sensitiveness_changed), menuitem); + g_signal_connect(G_OBJECT(toolbar->image), "notify::visible", + G_CALLBACK(button_visibility_changed), menuitem); menuitem = gtk_menu_item_new_with_mnemonic(_("_Link")); g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->link); gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem); g_signal_connect(G_OBJECT(toolbar->link), "notify::sensitive", G_CALLBACK(button_sensitiveness_changed), menuitem); + g_signal_connect(G_OBJECT(toolbar->link), "notify::visible", + G_CALLBACK(button_visibility_changed), menuitem); menuitem = gtk_menu_item_new_with_mnemonic(_("_Horizontal rule")); g_signal_connect(G_OBJECT(menuitem), "activate" , G_CALLBACK(insert_hr_cb), toolbar); @@ -1467,29 +1483,30 @@ gtk_widget_show_all(sep); /* Attention */ + wide_attention_button = g_object_get_data(G_OBJECT(toolbar), "attention"); + attention_button = gtk_button_new(); gtk_button_set_relief(GTK_BUTTON(attention_button), GTK_RELIEF_NONE); bbox = gtk_hbox_new(FALSE, 3); gtk_container_add(GTK_CONTAINER(attention_button), bbox); - image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, + image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)); gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0); label = gtk_label_new_with_mnemonic(_("_Attention!")); gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box), attention_button, FALSE, FALSE, 0); - g_signal_connect_swapped(G_OBJECT(attention_button), "clicked", - G_CALLBACK(gtk_button_clicked), - g_object_get_data(G_OBJECT(toolbar), "attention")); + g_signal_connect_swapped(G_OBJECT(attention_button), "clicked", + G_CALLBACK(gtk_button_clicked), wide_attention_button); gtk_widget_show_all(attention_button); - - g_signal_connect(G_OBJECT(g_object_get_data(G_OBJECT(toolbar), "attention")), - "notify::sensitive", + + g_signal_connect(wide_attention_button, "notify::sensitive", G_CALLBACK(button_sensitiveness_changed), attention_button); + g_signal_connect(wide_attention_button, "notify::visible", + G_CALLBACK(button_visibility_changed), attention_button); /* set attention button to be greyed out until we get a conversation */ - gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(toolbar), "attention"), - FALSE); - + gtk_widget_set_sensitive(GTK_WIDGET(wide_attention_button), FALSE); + gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0); g_object_set_data(G_OBJECT(hbox), "lean-view", box); gtk_widget_show(box); diff -r c7fa7c7aca7d -r 47b6eda87723 pidgin/gtkmain.c --- a/pidgin/gtkmain.c Mon Jul 19 21:05:06 2010 +0000 +++ b/pidgin/gtkmain.c Mon Jul 19 21:11:32 2010 +0000 @@ -97,7 +97,6 @@ */ static const int catch_sig_list[] = { SIGSEGV, - SIGHUP, SIGINT, SIGTERM, SIGQUIT, @@ -223,9 +222,6 @@ } switch (sig) { - case SIGHUP: - purple_debug_warning("sighandler", "Caught signal %d\n", sig); - break; #if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING) /* By default, gstreamer forks when you initialize it, and waitpids for the * child. But if libpurple reaps the child rather than leaving it to @@ -550,6 +546,7 @@ gboolean debug_enabled; gboolean migration_failed = FALSE; GList *active_accounts; + struct stat st; struct option long_options[] = { {"config", required_argument, NULL, 'c'}, @@ -853,6 +850,8 @@ * in user's home directory. */ search_path = g_build_filename(purple_user_dir(), "plugins", NULL); + if (!g_stat(search_path, &st)) + g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR); purple_plugins_add_search_path(search_path); g_free(search_path); purple_plugins_add_search_path(LIBDIR); diff -r c7fa7c7aca7d -r 47b6eda87723 pidgin/gtkmedia.c --- a/pidgin/gtkmedia.c Mon Jul 19 21:05:06 2010 +0000 +++ b/pidgin/gtkmedia.c Mon Jul 19 21:11:32 2010 +0000 @@ -333,7 +333,7 @@ gtk_box_pack_start(GTK_BOX(vbox), media->priv->menubar, FALSE, TRUE, 0); - media->priv->display = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + media->priv->display = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_container_set_border_width(GTK_CONTAINER(media->priv->display), PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(vbox), media->priv->display, @@ -629,29 +629,29 @@ gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display), recv_widget, TRUE, TRUE, 0); gtk_widget_show(recv_widget); - } else + } else { recv_widget = gtkmedia->priv->recv_widget; + } if (gtkmedia->priv->send_widget == NULL && type & (PURPLE_MEDIA_SEND_VIDEO | PURPLE_MEDIA_SEND_AUDIO)) { send_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display), - send_widget, TRUE, TRUE, 0); + send_widget, FALSE, TRUE, 0); button_widget = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_end(GTK_BOX(send_widget), button_widget, - FALSE, FALSE, 0); - gtk_widget_show(GTK_WIDGET(button_widget)); + gtk_box_pack_end(GTK_BOX(recv_widget), button_widget, + FALSE, TRUE, 0); gtk_widget_show(send_widget); /* Hold button */ gtkmedia->priv->hold = gtk_toggle_button_new_with_mnemonic("_Hold"); + gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->hold, + FALSE, FALSE, 0); + gtk_widget_show(gtkmedia->priv->hold); g_signal_connect(gtkmedia->priv->hold, "toggled", G_CALLBACK(pidgin_media_hold_toggled), gtkmedia); - gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->hold, - FALSE, FALSE, 0); - gtk_widget_show(gtkmedia->priv->hold); } else { send_widget = gtkmedia->priv->send_widget; button_widget = gtkmedia->priv->button_widget; @@ -663,7 +663,7 @@ GtkWidget *remote_video; GdkColor color = {0, 0, 0, 0}; - aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); + aspect = gtk_aspect_frame_new(NULL, 0, 0, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(recv_widget), aspect, TRUE, TRUE, 0); @@ -683,15 +683,16 @@ gtkmedia->priv->remote_video = remote_video; } + if (type & PURPLE_MEDIA_SEND_VIDEO) { PidginMediaRealizeData *data; GtkWidget *aspect; GtkWidget *local_video; GdkColor color = {0, 0, 0, 0}; - aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); + aspect = gtk_aspect_frame_new(NULL, 0, 0, 4.0/3.0, TRUE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); - gtk_box_pack_start(GTK_BOX(send_widget), aspect, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(send_widget), aspect, FALSE, TRUE, 0); data = g_new0(PidginMediaRealizeData, 1); data->gtkmedia = gtkmedia; @@ -703,44 +704,43 @@ g_signal_connect(G_OBJECT(local_video), "realize", G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(aspect), local_video); - gtk_widget_set_size_request (GTK_WIDGET(local_video), 160, 120); + gtk_widget_set_size_request (GTK_WIDGET(local_video), 80, 60); gtk_widget_show(local_video); gtk_widget_show(aspect); gtkmedia->priv->pause = gtk_toggle_button_new_with_mnemonic(_("_Pause")); + gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->pause, + FALSE, FALSE, 0); + gtk_widget_show(gtkmedia->priv->pause); g_signal_connect(gtkmedia->priv->pause, "toggled", G_CALLBACK(pidgin_media_pause_toggled), gtkmedia); - gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->pause, - FALSE, FALSE, 0); - gtk_widget_show(gtkmedia->priv->pause); gtkmedia->priv->local_video = local_video; } - if (type & PURPLE_MEDIA_RECV_AUDIO) { gtk_box_pack_end(GTK_BOX(recv_widget), pidgin_media_add_audio_widget(gtkmedia, PURPLE_MEDIA_RECV_AUDIO), FALSE, FALSE, 0); } + if (type & PURPLE_MEDIA_SEND_AUDIO) { gtkmedia->priv->mute = gtk_toggle_button_new_with_mnemonic("_Mute"); + gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->mute, + FALSE, FALSE, 0); + gtk_widget_show(gtkmedia->priv->mute); g_signal_connect(gtkmedia->priv->mute, "toggled", G_CALLBACK(pidgin_media_mute_toggled), gtkmedia); - gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->mute, - FALSE, FALSE, 0); - gtk_widget_show(gtkmedia->priv->mute); - gtk_box_pack_end(GTK_BOX(send_widget), + gtk_box_pack_end(GTK_BOX(recv_widget), pidgin_media_add_audio_widget(gtkmedia, PURPLE_MEDIA_SEND_AUDIO), FALSE, FALSE, 0); } - if (type & PURPLE_MEDIA_AUDIO && gtkmedia->priv->level_handler_id == 0) { gtkmedia->priv->level_handler_id = g_signal_connect( @@ -752,8 +752,10 @@ gtkmedia->priv->send_widget = send_widget; if (recv_widget != NULL) gtkmedia->priv->recv_widget = recv_widget; - if (button_widget != NULL) + if (button_widget != NULL) { gtkmedia->priv->button_widget = button_widget; + gtk_widget_show(GTK_WIDGET(button_widget)); + } if (purple_media_is_initiator(media, sid, NULL) == FALSE) { if (gtkmedia->priv->timeout_id != 0) diff -r c7fa7c7aca7d -r 47b6eda87723 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Mon Jul 19 21:05:06 2010 +0000 +++ b/pidgin/gtkprefs.c Mon Jul 19 21:11:32 2010 +0000 @@ -2223,6 +2223,7 @@ gtk_widget_set_sensitive(hbox, !strcmp(method, "automatic") || + !strcmp(method, "alsa") || !strcmp(method, "esd")); } #endif /* USE_GSTREAMER */ diff -r c7fa7c7aca7d -r 47b6eda87723 pidgin/plugins/Makefile.mingw --- a/pidgin/plugins/Makefile.mingw Mon Jul 19 21:05:06 2010 +0000 +++ b/pidgin/plugins/Makefile.mingw Mon Jul 19 21:11:32 2010 +0000 @@ -73,7 +73,7 @@ $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) install cp *.dll $(PIDGIN_INSTALL_PLUGINS_DIR) -.c.dll: +%.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H) $(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@.o -c $< $(CC) -shared $@.o $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@