Mon, 10 Jun 2013 01:22:52 +0530
merge default branch
--- a/.hgignore Mon Jun 03 15:26:52 2013 +0530 +++ b/.hgignore Mon Jun 10 01:22:52 2013 +0530 @@ -28,6 +28,7 @@ .*\.pyo$ .*\.rej$ .*\.so$ +.*\.moc$ Doxyfile(\.mingw)?$ VERSION$ aclocal.m4
--- a/COPYRIGHT Mon Jun 03 15:26:52 2013 +0530 +++ b/COPYRIGHT Mon Jun 10 01:22:52 2013 +0530 @@ -55,6 +55,7 @@ Igor Belyi David Benjamin Brian Bernas +Vivien Bernet-Rollande Paul Betts Runa Bhattacharjee Jonas Birmé
--- a/configure.ac Mon Jun 03 15:26:52 2013 +0530 +++ b/configure.ac Mon Jun 10 01:22:52 2013 +0530 @@ -110,6 +110,7 @@ dnl Checks for programs. AC_PROG_CC AM_PROG_CC_C_O +AC_PROG_CXX LT_PREREQ([2.2.6]) LT_INIT([disable-static]) LIBTOOL="$LIBTOOL --silent" @@ -1487,6 +1488,7 @@ DEBUG_CFLAGS="-Wall $DEBUG_CFLAGS" CFLAGS="-g $CFLAGS" fi +DEBUG_CPPFLAGS=`echo "$DEBUG_CFLAGS" | $sedpath 's/-Wdeclaration-after-statement//' | $sedpath 's/-Wmissing-prototypes//' | $sedpath 's/-Waggregate-return//'` if test "x$SUNCC" = "xyes"; then CFLAGS="$CFLAGS -features=extensions" @@ -1542,6 +1544,152 @@ fi dnl ####################################################################### +dnl # Check for Secret Service headers +dnl ####################################################################### + +# disabled - see secretservice.c +#AC_ARG_ENABLE(libsecret, [AC_HELP_STRING([--disable-libsecret], [enable Secret Service support])], enable_secret_service=no, enable_secret_service=yes) + +#if test "x$enable_secret_service" = "xyes" ; then +# PKG_CHECK_MODULES(SECRETSERVICE, [libsecret-1], [ +# AC_SUBST(SECRETSERVICE_CFLAGS) +# AC_SUBST(SECRETSERVICE_LIBS) +# AC_DEFINE(HAVE_SECRETSERVICE, 1, [Define if we have Secret Service.]) +# ]) +#fi + +#AM_CONDITIONAL(ENABLE_SECRETSERVICE, test "x$enable_secret_service" = "xyes") +AM_CONDITIONAL(ENABLE_SECRETSERVICE, test "x1" = "x2") + +dnl ####################################################################### +dnl # Check for GNOME Keyring headers +dnl ####################################################################### + +AC_ARG_ENABLE(gnome-keyring, [AC_HELP_STRING([--disable-gnome-keyring], [enable GNOME Keyring support])], enable_gnome_keyring=no, enable_gnome_keyring=yes) + +if test "x$enable_gnome_keyring" = "xyes" ; then + PKG_CHECK_MODULES(GNOMEKEYRING, [gnome-keyring-1], [ + AC_SUBST(GNOMEKEYRING_CFLAGS) + AC_SUBST(GNOMEKEYRING_LIBS) + AC_DEFINE(HAVE_GNOMEKEYRING, 1, [Define if we have GNOME Keyring.]) + ]) +fi + +AM_CONDITIONAL(ENABLE_GNOMEKEYRING, test "x$enable_gnome_keyring" = "xyes") + +dnl ####################################################################### +dnl # Check for KWallet headers +dnl ####################################################################### + +AC_ARG_ENABLE(kwallet, [AC_HELP_STRING([--disable-kwallet], [enable KWallet support])], enable_kwallet=no, enable_kwallet=yes) +AC_ARG_WITH(kwallet-includes, [AC_HELP_STRING([--with-kwallet-includes=DIR], [compile the KWallet plugin against includes in DIR])], [ac_kwallet_includes="$withval"], [ac_kwallet_includes="no"]) +AC_ARG_WITH(kwallet-libs, [AC_HELP_STRING([--with-kwallet-libs=DIR], [compile the KWallet plugin against the KWallet libs in DIR])], [ac_kwallet_libs="$withval"], [ac_kwallet_libs="no"]) + +if test "x$enable_kwallet" = "xyes"; then + KWALLET_CXXFLAGS="" + KWALLET_LIBS="" + if test -z "$with_kwallet_includes" || test -z "$with_kwallet_libs"; then + AC_CHECK_PROG(KDE4_CONFIG, kde4-config, kde4-config, no) + if test "x$KDE4_CONFIG" = "xno"; then + if test "x$force_deps" = "xyes"; then + AC_MSG_WARN([ +kde4-config not found. $KDE4_CONFIG +Use --disable-kwallet if you do not need KWallet support. +Use --with-kwallet-includes and --with-kwallet-libs to set up includes manually. +]) + enable_kwallet=no + fi + fi + fi +fi + +if test "x$enable_kwallet" = "xyes"; then + AC_LANG_PUSH([C++]) + CPPFLAGS_save="$CPPFLAGS" + + if test "$ac_kwallet_includes" != "no"; then + KWALLET_CXXFLAGS="-I$ac_kwallet_includes" + elif test "x$KDE4_CONFIG" != "xno"; then + KWALLET_CXXFLAGS="-I`$KDE4_CONFIG --path include`" + fi + CPPFLAGS="$CPPFLAGS $KWALLET_CXXFLAGS" + AC_CHECK_HEADER([kwallet.h], , [enable_kwallet=no]) + + CPPFLAGS="$CPPFLAGS_save" + AC_LANG_POP +fi + +if test "x$enable_kwallet" = "xyes"; then + dnl Ensure C++ compiler works + AC_CHECK_PROG(CXXTEST, [$CXX], [$CXX]) + if test "x$CXXTEST" = "x"; then + if test "x$force_deps" = "xyes"; then + AC_MSG_WARN([ +A C++ compiler was not found. +Use --disable-kwallet if you do not need KWallet support. +]) + enable_kwallet=no + fi + fi +fi + +AC_LANG_PUSH([C++]) +CPPFLAGS_save="$CPPFLAGS" +LDFLAGS_save="$LDFLAGS" +if test "x$enable_kwallet" = "xyes"; then + + PKG_CHECK_MODULES(QT4, [QtCore], [ + AC_SUBST(QT4_CFLAGS) + AC_SUBST(QT4_LIBS) + ], [ + AC_MSG_RESULT(no) + AC_MSG_WARN([ +Qt4 development headers not found. +Use --disable-kwallet if you do not need KWallet support. +]) + enable_kwallet=no + ]) +fi + +if test "x$enable_kwallet" = "xyes"; then + AC_MSG_CHECKING([for metaobject compiler]) + MOC=`$PKG_CONFIG --variable=moc_location QtCore` + AC_SUBST(MOC) + AC_MSG_RESULT([$MOC]) + + + AC_MSG_CHECKING([for KWallet libraries]) + if test "$ac_kwallet_libs" != "no"; then + KWALLET_LIBS="-L$ac_kwallet_libs -lkdeui" + elif test "x$KDE4_CONFIG" != "xno"; then + KWALLET_LIBS="-L`$KDE4_CONFIG --install lib`/kde4/devel -lkdeui" + else + KWALLET_LIBS="-lkdeui" + fi + KWALLET_LIBS="$KWALLET_LIBS" + CPPFLAGS="$CPPFLAGS $KWALLET_CXXFLAGS" + LDFLAGS="$LDFLAGS $KWALLET_LIBS $QT4_LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <kwallet.h>], + [KWallet::Wallet::LocalWallet();])], [AC_MSG_RESULT([yes])], + [if test "x$force_deps" = "xyes"; then +AC_MSG_ERROR([ +KWallet development libraries not found. +Use --disable-kwallet if you do not need KWallet support. +]) +fi +]) + +fi +CPPFLAGS="$CPPFLAGS_save" +LDFLAGS="$LDFLAGS_save" +AC_LANG_POP + +AC_SUBST(KWALLET_CXXFLAGS) +AC_SUBST(KWALLET_LIBS) + +AM_CONDITIONAL(ENABLE_KWALLET, test "x$enable_kwallet" = "xyes") + +dnl ####################################################################### dnl # Check for Python dnl ####################################################################### @@ -2437,6 +2585,7 @@ AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.]) fi +AC_SUBST(DEBUG_CPPFLAGS) AC_SUBST(DEBUG_CFLAGS) AC_SUBST(LDADD) AC_SUBST(LIBS) @@ -2715,6 +2864,7 @@ libpurple/purple-3.pc libpurple/purple-3-uninstalled.pc libpurple/plugins/Makefile + libpurple/plugins/keyrings/Makefile libpurple/plugins/mono/Makefile libpurple/plugins/mono/api/Makefile libpurple/plugins/mono/loader/Makefile @@ -2798,6 +2948,10 @@ echo Build with GtkSpell support... : $enable_gtkspell echo Build with GCR widgets........ : $enable_gcr echo +echo Build with GNOME Keyring...... : $enable_gnome_keyring +echo Build with KWallet............ : $enable_kwallet +#echo Build with Secret Service..... : $enable_secret_service +echo echo Build with plugin support..... : $enable_plugins echo Build with Mono support....... : $enable_mono echo Build with Perl support....... : $enable_perl
--- a/finch/gntaccount.c Mon Jun 03 15:26:52 2013 +0530 +++ b/finch/gntaccount.c Mon Jun 10 01:22:52 2013 +0530 @@ -195,9 +195,9 @@ gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->remember))); value = gnt_entry_get_text(GNT_ENTRY(dialog->password)); if (value && *value) - purple_account_set_password(account, value); + purple_account_set_password(account, value, NULL, NULL); else - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); /* Mail notification */ purple_account_set_check_mail(account, @@ -534,7 +534,8 @@ } static void -edit_account(PurpleAccount *account) +edit_account_continue(PurpleAccount *account, + const gchar *password, GError *error, gpointer user_data) { GntWidget *window, *hbox; GntWidget *combo, *button, *entry; @@ -617,7 +618,7 @@ gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Password:"))); gnt_box_add_widget(GNT_BOX(hbox), entry); if (account) - gnt_entry_set_text(GNT_ENTRY(entry), purple_account_get_password(account)); + gnt_entry_set_text(GNT_ENTRY(entry), password); hbox = gnt_hbox_new(TRUE); gnt_box_set_pad(GNT_BOX(hbox), 0); @@ -667,6 +668,12 @@ } static void +edit_account(PurpleAccount *account) +{ + purple_account_get_password(account, edit_account_continue, account); +} + +static void add_account_cb(GntWidget *widget, gpointer null) { edit_account(NULL);
--- a/finch/gntprefs.c Mon Jun 03 15:26:52 2013 +0530 +++ b/finch/gntprefs.c Mon Jun 10 01:22:52 2013 +0530 @@ -41,6 +41,7 @@ GList *freestrings; /* strings to be freed when the pref-window is closed */ gboolean showing; GntWidget *window; + GntWidget *keyring_window; } pref_request; void finch_prefs_init() @@ -195,6 +196,12 @@ {PURPLE_PREF_NONE, NULL, NULL, NULL}, }; +static Prefs keyring[] = +{ + {PURPLE_PREF_STRING, "/purple/keyring/active", N_("Active keyring"), purple_keyring_get_options}, + {PURPLE_PREF_NONE, NULL, NULL, NULL} +}; + static Prefs idle[] = { {PURPLE_PREF_STRING, "/purple/away/idle_reporting", N_("Report Idle time"), get_idle_options}, @@ -246,10 +253,15 @@ return; } + if (pref_request.keyring_window != NULL) + purple_request_close(PURPLE_REQUEST_FIELDS, + pref_request.keyring_window); + fields = purple_request_fields_new(); add_pref_group(fields, _("Buddy List"), blist); add_pref_group(fields, _("Conversations"), convs); + add_pref_group(fields, _("Keyring"), keyring); add_pref_group(fields, _("Logging"), logging); add_pref_group(fields, _("Idle"), idle); @@ -260,3 +272,40 @@ NULL); } +static void +finch_prefs_keyring_save(void *data, PurpleRequestFields *fields) +{ + pref_request.keyring_window = NULL; + + purple_keyring_apply_settings(NULL, fields); +} + +static void +finch_prefs_keyring_cancel(void) +{ + pref_request.keyring_window = NULL; +} + +void finch_prefs_show_keyring(void) +{ + PurpleRequestFields *fields; + + if (pref_request.keyring_window != NULL) { + gnt_window_present(pref_request.keyring_window); + return; + } + + fields = purple_keyring_read_settings(); + if (fields == NULL) { + purple_notify_info(NULL, _("Keyring settings"), + _("Selected keyring doesn't allow any configuration"), + NULL); + return; + } + + pref_request.keyring_window = purple_request_fields(NULL, + _("Keyring settings"), NULL, NULL, fields, + _("Save"), G_CALLBACK(finch_prefs_keyring_save), + _("Cancel"), G_CALLBACK(finch_prefs_keyring_cancel), + NULL, NULL, NULL, NULL); +}
--- a/finch/gntprefs.h Mon Jun 03 15:26:52 2013 +0530 +++ b/finch/gntprefs.h Mon Jun 10 01:22:52 2013 +0530 @@ -42,6 +42,11 @@ void finch_prefs_show_all(void); /** + * Show the preferences dialog for the selected keyring. + */ +void finch_prefs_show_keyring(void); + +/** * You don't need to know about this. */ void finch_prefs_update_old(void);
--- a/finch/gntui.c Mon Jun 03 15:26:52 2013 +0530 +++ b/finch/gntui.c Mon Jun 10 01:22:52 2013 +0530 @@ -106,6 +106,7 @@ gnt_register_action(_("Room List"), finch_roomlist_show_all); gnt_register_action(_("Sounds"), finch_sounds_show_all); gnt_register_action(_("Preferences"), finch_prefs_show_all); + gnt_register_action(_("Keyring settings"), finch_prefs_show_keyring); gnt_register_action(_("Statuses"), finch_savedstatus_show_all); #ifdef STANDALONE
--- a/libpurple/Makefile.am Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/Makefile.am Mon Jun 10 01:22:52 2013 +0530 @@ -55,6 +55,7 @@ http.c \ idle.c \ imgstore.c \ + keyring.c \ log.c \ media/backend-fs2.c \ media/backend-iface.c \ @@ -124,6 +125,7 @@ http.h \ idle.h \ imgstore.h \ + keyring.h \ log.h \ media.h \ media-gst.h \ @@ -317,6 +319,8 @@ $(GSTINTERFACES_LIBS) \ $(IDN_LIBS) \ $(JSON_LIBS) \ + $(GNUTLS_LIBS) \ + $(NSS_LIBS) \ ciphers/libpurple-ciphers.la \ -lm
--- a/libpurple/Makefile.mingw Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/Makefile.mingw Mon Jun 10 01:22:52 2013 +0530 @@ -34,11 +34,16 @@ -I$(GTK_TOP)/lib/glib-2.0/include \ -I$(JSON_GLIB_TOP)/include/json-glib-1.0 \ -I$(LIBXML2_TOP)/include/libxml2 \ + -I$(NSS_TOP)/include/nspr4 \ + -I$(NSS_TOP)/include/nss3 \ + -I$(GNUTLS_TOP)/include \ $(VV_INCLUDE_PATHS) LIB_PATHS += -L$(GTK_TOP)/lib \ -L$(JSON_GLIB_TOP)/lib \ -L$(LIBXML2_TOP)/lib \ + -L$(NSS_TOP)/lib \ + -L$(GNUTLS_TOP)/lib \ $(VV_LIB_PATHS) ## @@ -61,10 +66,12 @@ buddyicon.c \ certificate.c \ cipher.c \ + ciphers/aes.c \ ciphers/des.c \ ciphers/gchecksum.c \ ciphers/hmac.c \ ciphers/md4.c \ + ciphers/pbkdf2.c \ ciphers/rc4.c \ circbuffer.c \ cmds.c \ @@ -79,6 +86,7 @@ http.c \ idle.c \ imgstore.c \ + keyring.c \ log.c \ media/candidate.c \ media/enum-types.c \ @@ -141,6 +149,9 @@ -lws2_32 \ -lxml2 \ -ljson-glib-1.0 \ + -lnss3 \ + -lnspr4 \ + -lgnutls \ $(VV_LIBS) include $(PIDGIN_COMMON_RULES)
--- a/libpurple/account.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/account.c Mon Jun 10 01:22:52 2013 +0530 @@ -28,6 +28,7 @@ #include "core.h" #include "dbus-maybe.h" #include "debug.h" +#include "keyring.h" #include "network.h" #include "notify.h" #include "pounce.h" @@ -70,6 +71,12 @@ guint ref; } PurpleAccountRequestInfo; +typedef struct +{ + PurpleCallback cb; + gpointer data; +} PurpleCallbackBundle; + static PurpleAccountUiOps *account_ui_ops = NULL; static GList *accounts = NULL; @@ -366,11 +373,33 @@ child = xmlnode_new_child(node, "name"); xmlnode_insert_data(child, purple_account_get_username(account), -1); - if (purple_account_get_remember_password(account) && - ((tmp = purple_account_get_password(account)) != NULL)) + if (purple_account_get_remember_password(account)) { - child = xmlnode_new_child(node, "password"); - xmlnode_insert_data(child, tmp, -1); + const char *keyring_id = NULL; + const char *mode = NULL; + char *data = NULL; + GError *error = NULL; + GDestroyNotify destroy = NULL; + gboolean exported = purple_keyring_export_password(account, + &keyring_id, &mode, &data, &error, &destroy); + + if (error != NULL) { + purple_debug_error("account", + "Failed to export password for account %s: %s.\n", + purple_account_get_username(account), + error->message); + } else if (exported) { + child = xmlnode_new_child(node, "password"); + if (keyring_id != NULL) + xmlnode_set_attrib(child, "keyring_id", keyring_id); + if (mode != NULL) + xmlnode_set_attrib(child, "mode", mode); + if (data != NULL) + xmlnode_insert_data(child, data, -1); + + if (destroy != NULL) + destroy(data); + } } if ((tmp = purple_account_get_alias(account)) != NULL) @@ -868,15 +897,6 @@ g_free(name); g_free(protocol_id); - /* Read the password */ - child = xmlnode_get_child(node, "password"); - if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL)) - { - purple_account_set_remember_password(ret, TRUE); - purple_account_set_password(ret, data); - g_free(data); - } - /* Read the alias */ child = xmlnode_get_child(node, "alias"); if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL)) @@ -940,6 +960,25 @@ parse_current_error(child, ret); } + /* Read the password */ + child = xmlnode_get_child(node, "password"); + if (child != NULL) + { + const char *keyring_id = xmlnode_get_attrib(child, "keyring_id"); + const char *mode = xmlnode_get_attrib(child, "mode"); + gboolean result; + + data = xmlnode_get_data(child); + result = purple_keyring_import_password(ret, keyring_id, mode, data, NULL); + + if (result == TRUE || purple_keyring_get_inuse() == NULL) { + purple_account_set_remember_password(ret, TRUE); + } else { + purple_debug_error("account", "Failed to import password.\n"); + } + purple_str_wipe(data); + } + return ret; } @@ -1059,7 +1098,7 @@ g_free(account->username); g_free(account->alias); - g_free(account->password); + purple_str_wipe(account->password); g_free(account->user_info); g_free(account->buddy_icon_path); g_free(account->protocol_id); @@ -1107,6 +1146,15 @@ account->registration_cb_user_data = user_data; } +static void +purple_account_register_got_password_cb(PurpleAccount *account, + const gchar *password, GError *error, gpointer data) +{ + g_return_if_fail(account != NULL); + + _purple_connection_new(account, TRUE, password); +} + void purple_account_register(PurpleAccount *account) { @@ -1115,7 +1163,21 @@ purple_debug_info("account", "Registering account %s\n", purple_account_get_username(account)); - _purple_connection_new(account, TRUE, purple_account_get_password(account)); + purple_keyring_get_password(account, + purple_account_register_got_password_cb, NULL); +} + +static void +purple_account_unregister_got_password_cb(PurpleAccount *account, + const gchar *password, GError *error, gpointer data) +{ + PurpleCallbackBundle *cbb = data; + PurpleAccountUnregistrationCb cb; + + cb = (PurpleAccountUnregistrationCb)cbb->cb; + _purple_connection_new_unregister(account, password, cb, cbb->data); + + g_free(cbb); } void @@ -1130,12 +1192,19 @@ void purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data) { + PurpleCallbackBundle *cbb; + g_return_if_fail(account != NULL); purple_debug_info("account", "Unregistering account %s\n", purple_account_get_username(account)); - _purple_connection_new_unregister(account, purple_account_get_password(account), cb, user_data); + cbb = g_new0(PurpleCallbackBundle, 1); + cbb->cb = PURPLE_CALLBACK(cb); + cbb->data = user_data; + + purple_keyring_get_password(account, + purple_account_unregister_got_password_cb, cbb); } static void @@ -1153,11 +1222,9 @@ return; } - if(remember) - purple_account_set_remember_password(account, TRUE); - - purple_account_set_password(account, entry); - + purple_account_set_remember_password(account, remember); + + purple_account_set_password(account, entry, NULL, NULL); _purple_connection_new(account, FALSE, entry); } @@ -1210,11 +1277,27 @@ g_free(primary); } +static void +purple_account_connect_got_password_cb(PurpleAccount *account, + const gchar *password, GError *error, gpointer data) +{ + PurplePluginProtocolInfo *prpl_info = data; + + if ((password == NULL || *password == '\0') && + !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && + !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) + purple_account_request_password(account, + G_CALLBACK(request_password_ok_cb), + G_CALLBACK(request_password_cancel_cb), account); + else + _purple_connection_new(account, FALSE, password); +} + void purple_account_connect(PurpleAccount *account) { PurplePlugin *prpl; - const char *password, *username; + const char *username; PurplePluginProtocolInfo *prpl_info; g_return_if_fail(account != NULL); @@ -1241,13 +1324,13 @@ purple_debug_info("account", "Connecting to account %s.\n", username); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - password = purple_account_get_password(account); - if ((password == NULL) && - !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && - !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) - purple_account_request_password(account, G_CALLBACK(request_password_ok_cb), G_CALLBACK(request_password_cancel_cb), account); - else - _purple_connection_new(account, FALSE, password); + if (account->password != NULL) { + purple_account_connect_got_password_cb(account, + account->password, NULL, prpl_info); + } else { + purple_keyring_get_password(account, + purple_account_connect_got_password_cb, prpl_info); + } } void @@ -1267,8 +1350,6 @@ gc = purple_account_get_connection(account); _purple_connection_destroy(gc); - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); purple_account_set_connection(account, NULL); account->disconnecting = FALSE; @@ -1626,15 +1707,27 @@ blist_ops->save_account(account); } -void -purple_account_set_password(PurpleAccount *account, const char *password) +void +purple_account_set_password(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data) { g_return_if_fail(account != NULL); - g_free(account->password); + purple_str_wipe(account->password); account->password = g_strdup(password); schedule_accounts_save(); + + if (!purple_account_get_remember_password(account)) { + purple_debug_info("account", + "Password for %s set, not sent to keyring.\n", + purple_account_get_username(account)); + + if (cb != NULL) + cb(account, NULL, data); + } else { + purple_keyring_set_password(account, password, cb, data); + } } void @@ -2141,12 +2234,52 @@ return account->username; } -const char * -purple_account_get_password(const PurpleAccount *account) +static void +purple_account_get_password_got(PurpleAccount *account, + const gchar *password, GError *error, gpointer data) { - g_return_val_if_fail(account != NULL, NULL); - - return account->password; + PurpleCallbackBundle *cbb = data; + PurpleKeyringReadCallback cb; + + purple_debug_info("account", + "Read password for account %s from async keyring.\n", + purple_account_get_username(account)); + + purple_str_wipe(account->password); + account->password = g_strdup(password); + + cb = (PurpleKeyringReadCallback)cbb->cb; + if (cb != NULL) + cb(account, password, error, cbb->data); + + g_free(cbb); +} + +void +purple_account_get_password(PurpleAccount *account, + PurpleKeyringReadCallback cb, gpointer data) +{ + if (account == NULL) { + cb(NULL, NULL, NULL, data); + return; + } + + if (account->password != NULL) { + purple_debug_info("account", + "Reading password for account %s from cache.\n", + purple_account_get_username(account)); + cb(account, account->password, NULL, data); + } else { + PurpleCallbackBundle *cbb = g_new0(PurpleCallbackBundle, 1); + cbb->cb = PURPLE_CALLBACK(cb); + cbb->data = data; + + purple_debug_info("account", + "Reading password for account %s from async keyring.\n", + purple_account_get_username(account)); + purple_keyring_get_password(account, + purple_account_get_password_got, cbb); + } } const char * @@ -2661,7 +2794,7 @@ PurpleConnection *gc = purple_account_get_connection(account); PurplePlugin *prpl = NULL; - purple_account_set_password(account, new_pw); + purple_account_set_password(account, new_pw, NULL, NULL); if (gc != NULL) prpl = purple_connection_get_prpl(gc); @@ -2768,6 +2901,14 @@ account, type, description); } +static void +password_migration_cb(PurpleAccount *account) +{ + /* account may be NULL (means: all) */ + + schedule_accounts_save(); +} + const PurpleConnectionErrorInfo * purple_account_get_current_error(PurpleAccount *account) { @@ -2812,6 +2953,12 @@ purple_signal_emit(purple_accounts_get_handle(), "account-removed", account); } +static void +purple_accounts_delete_set(PurpleAccount *account, GError *error, gpointer data) +{ + purple_account_destroy(account); +} + void purple_accounts_delete(PurpleAccount *account) { @@ -2882,7 +3029,11 @@ /* This will cause the deletion of an old buddy icon. */ purple_buddy_icons_set_account_icon(account, NULL, 0); - purple_account_destroy(account); + /* This is async because we do not want the + * account being overwritten before we are done. + */ + purple_keyring_set_password(account, NULL, + purple_accounts_delete_set, NULL); } void @@ -3133,6 +3284,8 @@ PURPLE_CALLBACK(signed_off_cb), NULL); purple_signal_connect(conn_handle, "connection-error", handle, PURPLE_CALLBACK(connection_error_cb), NULL); + purple_signal_connect(purple_keyring_get_handle(), "password-migration", handle, + PURPLE_CALLBACK(password_migration_cb), NULL); load_accounts();
--- a/libpurple/account.h Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/account.h Mon Jun 10 01:22:52 2013 +0530 @@ -50,6 +50,7 @@ #include "proxy.h" #include "prpl.h" #include "status.h" +#include "keyring.h" /** * Account request types. @@ -364,10 +365,18 @@ /** * Sets the account's password. * + * The password in the keyring might not be immediately updated, but the cached + * version will be, and it is therefore safe to read the password back before + * the callback has been triggered. One can also set a NULL callback if + * notification of saving to the keyring is not required. + * * @param account The account. * @param password The password. + * @param cb A callback for once the password is saved. + * @param data A pointer to be passed to the callback. */ -void purple_account_set_password(PurpleAccount *account, const char *password); +void purple_account_set_password(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data); /** * Sets the account's alias. @@ -675,13 +684,19 @@ const char *purple_account_get_username(const PurpleAccount *account); /** - * Returns the account's password. + * Reads the password for the account. + * + * This is an asynchronous call, that will return the password in a callback + * once it has been read from the keyring. If the account is connected, and you + * require the password immediately, then consider using @ref + * purple_connection_get_password instead. * * @param account The account. - * - * @return The password. + * @param cb The callback to give the password. + * @param data A pointer passed to the callback. */ -const char *purple_account_get_password(const PurpleAccount *account); +void purple_account_get_password(PurpleAccount *account, + PurpleKeyringReadCallback cb, gpointer data); /** * Returns the account's alias.
--- a/libpurple/cipher.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/cipher.c Mon Jun 10 01:22:52 2013 +0530 @@ -248,14 +248,7 @@ purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CIPHER)); - purple_ciphers_register_cipher("md5", purple_md5_cipher_get_ops()); - purple_ciphers_register_cipher("sha1", purple_sha1_cipher_get_ops()); - purple_ciphers_register_cipher("sha256", purple_sha256_cipher_get_ops()); - purple_ciphers_register_cipher("md4", purple_md4_cipher_get_ops()); - purple_ciphers_register_cipher("hmac", purple_hmac_cipher_get_ops()); - purple_ciphers_register_cipher("des", purple_des_cipher_get_ops()); - purple_ciphers_register_cipher("des3", purple_des3_cipher_get_ops()); - purple_ciphers_register_cipher("rc4", purple_rc4_cipher_get_ops()); + purple_ciphers_register_all(); } void
--- a/libpurple/ciphers/Makefile.am Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/ciphers/Makefile.am Mon Jun 10 01:22:52 2013 +0530 @@ -1,10 +1,20 @@ noinst_LTLIBRARIES=libpurple-ciphers.la +# XXX: cipher.lo won't be updated after a change in cipher files + +if USE_NSS +AES_SOURCE = aes.c +endif +if USE_GNUTLS +AES_SOURCE = aes.c +endif libpurple_ciphers_la_SOURCES=\ + $(AES_SOURCE) \ des.c \ gchecksum.c \ hmac.c \ md4.c \ + pbkdf2.c \ rc4.c noinst_HEADERS =\ @@ -16,4 +26,6 @@ $(INTGG_CFLAGS) \ $(AM_CFLAGS) \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(DEBUG_CFLAGS) \ + $(GNUTLS_CFLAGS) \ + $(NSS_CFLAGS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/ciphers/aes.c Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,566 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + * Written by Tomek Wasilczyk <tomkiewicz@cpw.pidgin.im> + */ + +#include "internal.h" +#include "cipher.h" +#include "ciphers.h" +#include "debug.h" + +#if defined(HAVE_GNUTLS) +# define PURPLE_AES_USE_GNUTLS 1 +# include <gnutls/gnutls.h> +# include <gnutls/crypto.h> +#elif defined(HAVE_NSS) +# define PURPLE_AES_USE_NSS 1 +# include <nss.h> +# include <pk11pub.h> +# include <prerror.h> +#else +# error "No GnuTLS or NSS support" +#endif + +/* 128bit */ +#define PURPLE_AES_BLOCK_SIZE 16 + +typedef struct +{ + guchar iv[PURPLE_AES_BLOCK_SIZE]; + guchar key[32]; + guint key_size; + gboolean failure; +} AESContext; + +typedef gboolean (*purple_aes_crypt_func)( + const guchar *input, guchar *output, size_t len, + guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size); + +static void +purple_aes_init(PurpleCipherContext *context, void *extra) +{ + AESContext *ctx_data; + + ctx_data = g_new0(AESContext, 1); + purple_cipher_context_set_data(context, ctx_data); + + purple_cipher_context_reset(context, extra); +} + +static void +purple_aes_uninit(PurpleCipherContext *context) +{ + AESContext *ctx_data; + + purple_cipher_context_reset(context, NULL); + + ctx_data = purple_cipher_context_get_data(context); + g_free(ctx_data); + purple_cipher_context_set_data(context, NULL); +} + +static void +purple_aes_reset(PurpleCipherContext *context, void *extra) +{ + AESContext *ctx_data = purple_cipher_context_get_data(context); + + g_return_if_fail(ctx_data != NULL); + + memset(ctx_data->iv, 0, sizeof(ctx_data->iv)); + memset(ctx_data->key, 0, sizeof(ctx_data->key)); + ctx_data->key_size = 32; /* 256bit */ + ctx_data->failure = FALSE; +} + +static void +purple_aes_set_option(PurpleCipherContext *context, const gchar *name, + void *value) +{ + AESContext *ctx_data = purple_cipher_context_get_data(context); + + purple_debug_error("cipher-aes", "set_option not supported\n"); + ctx_data->failure = TRUE; +} + +static void +purple_aes_set_iv(PurpleCipherContext *context, guchar *iv, size_t len) +{ + AESContext *ctx_data = purple_cipher_context_get_data(context); + + if ((len > 0 && iv == NULL) || + (len != 0 && len != sizeof(ctx_data->iv))) { + purple_debug_error("cipher-aes", "invalid IV length\n"); + ctx_data->failure = TRUE; + return; + } + + if (len == 0) + memset(ctx_data->iv, 0, sizeof(ctx_data->iv)); + else + memcpy(ctx_data->iv, iv, len); +} + +static void +purple_aes_set_key(PurpleCipherContext *context, const guchar *key, size_t len) +{ + AESContext *ctx_data = purple_cipher_context_get_data(context); + + if ((len > 0 && key == NULL) || + (len != 0 && len != 16 && len != 24 && len != 32)) { + purple_debug_error("cipher-aes", "invalid key length\n"); + ctx_data->failure = TRUE; + return; + } + + ctx_data->key_size = len; + memset(ctx_data->key, 0, sizeof(ctx_data->key)); + if (len > 0) + memcpy(ctx_data->key, key, len); +} + +static guchar * +purple_aes_pad_pkcs7(const guchar input[], size_t in_len, size_t *out_len) +{ + int padding_len, total_len; + guchar *padded; + + g_return_val_if_fail(input != NULL, NULL); + g_return_val_if_fail(out_len != NULL, NULL); + + padding_len = PURPLE_AES_BLOCK_SIZE - (in_len % PURPLE_AES_BLOCK_SIZE); + total_len = in_len + padding_len; + g_assert((total_len % PURPLE_AES_BLOCK_SIZE) == 0); + + padded = g_new(guchar, total_len); + *out_len = total_len; + + memcpy(padded, input, in_len); + memset(padded + in_len, padding_len, padding_len); + + return padded; +} + +static ssize_t +purple_aes_unpad_pkcs7(guchar input[], size_t in_len) +{ + int padding_len, i; + size_t out_len; + + g_return_val_if_fail(input != NULL, -1); + g_return_val_if_fail(in_len > 0, -1); + + padding_len = input[in_len - 1]; + if (padding_len <= 0 || padding_len > PURPLE_AES_BLOCK_SIZE || + padding_len > in_len) { + purple_debug_warning("cipher-aes", + "Invalid padding length: %d (total %d) - " + "most probably, the key was invalid\n", + padding_len, in_len); + return -1; + } + + out_len = in_len - padding_len; + for (i = 0; i < padding_len; i++) { + if (input[out_len + i] != padding_len) { + purple_debug_warning("cipher-aes", + "Padding doesn't match at pos %d (found %02x, " + "expected %02x) - " + "most probably, the key was invalid\n", + i, input[out_len + i], padding_len); + return -1; + } + } + + memset(input + out_len, 0, padding_len); + return out_len; +} + +#ifdef PURPLE_AES_USE_GNUTLS + +static gnutls_cipher_hd_t +purple_aes_crypt_gnutls_init(guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], + guint key_size) +{ + gnutls_cipher_hd_t handle; + gnutls_cipher_algorithm_t algorithm; + gnutls_datum_t key_info, iv_info; + int ret; + + if (key_size == 16) + algorithm = GNUTLS_CIPHER_AES_128_CBC; + else if (key_size == 24) + algorithm = GNUTLS_CIPHER_AES_192_CBC; + else if (key_size == 32) + algorithm = GNUTLS_CIPHER_AES_256_CBC; + else + g_return_val_if_reached(NULL); + + key_info.data = key; + key_info.size = key_size; + + iv_info.data = iv; + iv_info.size = PURPLE_AES_BLOCK_SIZE; + + ret = gnutls_cipher_init(&handle, algorithm, &key_info, &iv_info); + if (ret != 0) { + purple_debug_error("cipher-aes", + "gnutls_cipher_init failed: %d\n", ret); + return NULL; + } + + return handle; +} + +static gboolean +purple_aes_encrypt_gnutls(const guchar *input, guchar *output, size_t len, + guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) +{ + gnutls_cipher_hd_t handle; + int ret; + + handle = purple_aes_crypt_gnutls_init(iv, key, key_size); + if (handle == NULL) + return FALSE; + + ret = gnutls_cipher_encrypt2(handle, input, len, output, len); + gnutls_cipher_deinit(handle); + + if (ret != 0) { + purple_debug_error("cipher-aes", + "gnutls_cipher_encrypt2 failed: %d\n", ret); + return FALSE; + } + + return TRUE; +} + +static gboolean +purple_aes_decrypt_gnutls(const guchar *input, guchar *output, size_t len, + guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) +{ + gnutls_cipher_hd_t handle; + int ret; + + handle = purple_aes_crypt_gnutls_init(iv, key, key_size); + if (handle == NULL) + return FALSE; + + ret = gnutls_cipher_decrypt2(handle, input, len, output, len); + gnutls_cipher_deinit(handle); + + if (ret != 0) { + purple_debug_error("cipher-aes", + "gnutls_cipher_decrypt2 failed: %d\n", ret); + return FALSE; + } + + return TRUE; +} + +#endif /* PURPLE_AES_USE_GNUTLS */ + +#ifdef PURPLE_AES_USE_NSS + +typedef struct +{ + PK11SlotInfo *slot; + PK11SymKey *sym_key; + SECItem *sec_param; + PK11Context *enc_context; +} purple_aes_encrypt_nss_context; + +static void +purple_aes_encrypt_nss_context_cleanup(purple_aes_encrypt_nss_context *ctx) +{ + g_return_if_fail(ctx != NULL); + + if (ctx->enc_context != NULL) + PK11_DestroyContext(ctx->enc_context, TRUE); + if (ctx->sec_param != NULL) + SECITEM_FreeItem(ctx->sec_param, TRUE); + if (ctx->sym_key != NULL) + PK11_FreeSymKey(ctx->sym_key); + if (ctx->slot != NULL) + PK11_FreeSlot(ctx->slot); + + memset(ctx, 0, sizeof(purple_aes_encrypt_nss_context)); +} + +static gboolean +purple_aes_crypt_nss(const guchar *input, guchar *output, size_t len, + guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size, + CK_ATTRIBUTE_TYPE operation) +{ + purple_aes_encrypt_nss_context ctx; + CK_MECHANISM_TYPE cipher_mech = CKM_AES_CBC; + SECItem key_item, iv_item; + SECStatus ret; + int outlen = 0; + unsigned int outlen_tmp = 0; + + memset(&ctx, 0, sizeof(purple_aes_encrypt_nss_context)); + + if (NSS_NoDB_Init(NULL) != SECSuccess) { + purple_debug_error("cipher-aes", + "NSS_NoDB_Init failed: %d\n", PR_GetError()); + return FALSE; + } + + ctx.slot = PK11_GetBestSlot(cipher_mech, NULL); + if (ctx.slot == NULL) { + purple_debug_error("cipher-aes", + "PK11_GetBestSlot failed: %d\n", PR_GetError()); + return FALSE; + } + + key_item.type = siBuffer; + key_item.data = key; + key_item.len = key_size; + ctx.sym_key = PK11_ImportSymKey(ctx.slot, cipher_mech, + PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); + if (ctx.sym_key == NULL) { + purple_debug_error("cipher-aes", + "PK11_ImportSymKey failed: %d\n", PR_GetError()); + purple_aes_encrypt_nss_context_cleanup(&ctx); + return FALSE; + } + + iv_item.type = siBuffer; + iv_item.data = iv; + iv_item.len = PURPLE_AES_BLOCK_SIZE; + ctx.sec_param = PK11_ParamFromIV(cipher_mech, &iv_item); + if (ctx.sec_param == NULL) { + purple_debug_error("cipher-aes", + "PK11_ParamFromIV failed: %d\n", PR_GetError()); + purple_aes_encrypt_nss_context_cleanup(&ctx); + return FALSE; + } + + ctx.enc_context = PK11_CreateContextBySymKey(cipher_mech, operation, + ctx.sym_key, ctx.sec_param); + if (ctx.enc_context == NULL) { + purple_debug_error("cipher-aes", + "PK11_CreateContextBySymKey failed: %d\n", + PR_GetError()); + purple_aes_encrypt_nss_context_cleanup(&ctx); + return FALSE; + } + + ret = PK11_CipherOp(ctx.enc_context, output, &outlen, len, input, len); + if (ret != SECSuccess) { + purple_debug_error("cipher-aes", + "PK11_CipherOp failed: %d\n", PR_GetError()); + purple_aes_encrypt_nss_context_cleanup(&ctx); + return FALSE; + } + + ret = PK11_DigestFinal(ctx.enc_context, output + outlen, &outlen_tmp, + len - outlen); + if (ret != SECSuccess) { + purple_debug_error("cipher-aes", + "PK11_DigestFinal failed: %d\n", PR_GetError()); + purple_aes_encrypt_nss_context_cleanup(&ctx); + return FALSE; + } + + purple_aes_encrypt_nss_context_cleanup(&ctx); + + outlen += outlen_tmp; + if (outlen != len) { + purple_debug_error("cipher-aes", + "resulting length doesn't match: %d (expected: %d)\n", + outlen, len); + return FALSE; + } + + return TRUE; +} + +static gboolean +purple_aes_encrypt_nss(const guchar *input, guchar *output, size_t len, + guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) +{ + return purple_aes_crypt_nss(input, output, len, iv, key, key_size, + CKA_ENCRYPT); +} + +static gboolean +purple_aes_decrypt_nss(const guchar *input, guchar *output, size_t len, + guchar iv[PURPLE_AES_BLOCK_SIZE], guchar key[32], guint key_size) +{ + return purple_aes_crypt_nss(input, output, len, iv, key, key_size, + CKA_DECRYPT); +} + +#endif /* PURPLE_AES_USE_NSS */ + +static ssize_t +purple_aes_encrypt(PurpleCipherContext *context, const guchar input[], + size_t in_len, guchar output[], size_t out_size) +{ + AESContext *ctx_data = purple_cipher_context_get_data(context); + purple_aes_crypt_func encrypt_func; + guchar *input_padded; + size_t out_len = 0; + gboolean succ; + + if (ctx_data->failure) + return -1; + + input_padded = purple_aes_pad_pkcs7(input, in_len, &out_len); + + if (out_len > out_size) { + purple_debug_error("cipher-aes", "Output buffer too small\n"); + memset(input_padded, 0, out_len); + g_free(input_padded); + return -1; + } + +#if defined(PURPLE_AES_USE_GNUTLS) + encrypt_func = purple_aes_encrypt_gnutls; +#elif defined(PURPLE_AES_USE_NSS) + encrypt_func = purple_aes_encrypt_nss; +#else +# error "No matching encrypt_func" +#endif + + succ = encrypt_func(input_padded, output, out_len, ctx_data->iv, + ctx_data->key, ctx_data->key_size); + + memset(input_padded, 0, out_len); + g_free(input_padded); + + if (!succ) { + memset(output, 0, out_len); + return -1; + } + + return out_len; +} + +static ssize_t +purple_aes_decrypt(PurpleCipherContext *context, const guchar input[], + size_t in_len, guchar output[], size_t out_size) +{ + AESContext *ctx_data = purple_cipher_context_get_data(context); + purple_aes_crypt_func decrypt_func; + gboolean succ; + ssize_t out_len; + + if (ctx_data->failure) + return -1; + + if (in_len > out_size) { + purple_debug_error("cipher-aes", "Output buffer too small\n"); + return -1; + } + + if ((in_len % PURPLE_AES_BLOCK_SIZE) != 0 || in_len == 0) { + purple_debug_error("cipher-aes", "Malformed data\n"); + return -1; + } + +#if defined(PURPLE_AES_USE_GNUTLS) + decrypt_func = purple_aes_decrypt_gnutls; +#elif defined(PURPLE_AES_USE_NSS) + decrypt_func = purple_aes_decrypt_nss; +#else +# error "No matching encrypt_func" +#endif + + succ = decrypt_func(input, output, in_len, ctx_data->iv, ctx_data->key, + ctx_data->key_size); + + if (!succ) { + memset(output, 0, in_len); + return -1; + } + + out_len = purple_aes_unpad_pkcs7(output, in_len); + if (out_len < 0) { + memset(output, 0, in_len); + return -1; + } + + return out_len; +} + +static size_t +purple_aes_get_key_size(PurpleCipherContext *context) +{ + AESContext *ctx_data = purple_cipher_context_get_data(context); + + return ctx_data->key_size; +} + +static void +purple_aes_set_batch_mode(PurpleCipherContext *context, + PurpleCipherBatchMode mode) +{ + AESContext *ctx_data = purple_cipher_context_get_data(context); + + if (mode == PURPLE_CIPHER_BATCH_MODE_CBC) + return; + + purple_debug_error("cipher-aes", "unsupported batch mode\n"); + ctx_data->failure = TRUE; +} + +static PurpleCipherBatchMode +purple_aes_get_batch_mode(PurpleCipherContext *context) +{ + return PURPLE_CIPHER_BATCH_MODE_CBC; +} + +static size_t +purple_aes_get_block_size(PurpleCipherContext *context) +{ + return PURPLE_AES_BLOCK_SIZE; +} + +static PurpleCipherOps AESOps = { + purple_aes_set_option, /* set_option */ + NULL, /* get_option */ + purple_aes_init, /* init */ + purple_aes_reset, /* reset */ + NULL, /* reset_state */ + purple_aes_uninit, /* uninit */ + purple_aes_set_iv, /* set_iv */ + NULL, /* append */ + NULL, /* digest */ + NULL, /* get_digest_size */ + purple_aes_encrypt, /* encrypt */ + purple_aes_decrypt, /* decrypt */ + NULL, /* set_salt */ + NULL, /* get_salt_size */ + purple_aes_set_key, /* set_key */ + purple_aes_get_key_size, /* get_key_size */ + purple_aes_set_batch_mode, /* set_batch_mode */ + purple_aes_get_batch_mode, /* get_batch_mode */ + purple_aes_get_block_size, /* get_block_size */ + NULL, NULL, NULL, NULL /* reserved */ +}; + +PurpleCipherOps * +purple_aes_cipher_get_ops(void) { + return &AESOps; +}
--- a/libpurple/ciphers/ciphers.h Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/ciphers/ciphers.h Mon Jun 10 01:22:52 2013 +0530 @@ -19,6 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +/* aes.c */ +PurpleCipherOps * purple_aes_cipher_get_ops(void); + /* des.c */ PurpleCipherOps * purple_des_cipher_get_ops(void); PurpleCipherOps * purple_des3_cipher_get_ops(void); @@ -34,5 +37,30 @@ /* md4.c */ PurpleCipherOps * purple_md4_cipher_get_ops(void); +/* pbkdf2.c */ +PurpleCipherOps * purple_pbkdf2_cipher_get_ops(void); + /* rc4.c */ PurpleCipherOps * purple_rc4_cipher_get_ops(void); + +static inline void purple_ciphers_register_all(void) +{ +#if defined(HAVE_GNUTLS) || defined(HAVE_NSS) + purple_ciphers_register_cipher("aes", purple_aes_cipher_get_ops()); +#endif + + purple_ciphers_register_cipher("des", purple_des_cipher_get_ops()); + purple_ciphers_register_cipher("des3", purple_des3_cipher_get_ops()); + + purple_ciphers_register_cipher("md5", purple_md5_cipher_get_ops()); + purple_ciphers_register_cipher("sha1", purple_sha1_cipher_get_ops()); + purple_ciphers_register_cipher("sha256", purple_sha256_cipher_get_ops()); + + purple_ciphers_register_cipher("hmac", purple_hmac_cipher_get_ops()); + + purple_ciphers_register_cipher("md4", purple_md4_cipher_get_ops()); + + purple_ciphers_register_cipher("pbkdf2", purple_pbkdf2_cipher_get_ops()); + + purple_ciphers_register_cipher("rc4", purple_rc4_cipher_get_ops()); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/ciphers/pbkdf2.c Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,323 @@ +/* + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + * Written by Tomek Wasilczyk <tomkiewicz@cpw.pidgin.im> + */ + +#include "internal.h" +#include "cipher.h" +#include "ciphers.h" +#include "debug.h" + +/* 1024bit */ +#define PBKDF2_HASH_MAX_LEN 128 + +typedef struct +{ + gchar *hash_func; + guint iter_count; + size_t out_len; + + guchar *salt; + size_t salt_len; + guchar *passphrase; + size_t passphrase_len; +} Pbkdf2Context; + +static void +purple_pbkdf2_init(PurpleCipherContext *context, void *extra) +{ + Pbkdf2Context *ctx_data; + + ctx_data = g_new0(Pbkdf2Context, 1); + purple_cipher_context_set_data(context, ctx_data); + + purple_cipher_context_reset(context, extra); +} + +static void +purple_pbkdf2_uninit(PurpleCipherContext *context) +{ + Pbkdf2Context *ctx_data; + + purple_cipher_context_reset(context, NULL); + + ctx_data = purple_cipher_context_get_data(context); + g_free(ctx_data); + purple_cipher_context_set_data(context, NULL); +} + +static void +purple_pbkdf2_reset(PurpleCipherContext *context, void *extra) +{ + Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context); + + g_return_if_fail(ctx_data != NULL); + + g_free(ctx_data->hash_func); + ctx_data->hash_func = NULL; + ctx_data->iter_count = 1; + ctx_data->out_len = 256; + + purple_cipher_context_reset_state(context, extra); +} + +static void +purple_pbkdf2_reset_state(PurpleCipherContext *context, void *extra) +{ + Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context); + + g_return_if_fail(ctx_data != NULL); + + purple_cipher_context_set_salt(context, NULL, 0); + purple_cipher_context_set_key(context, NULL, 0); +} + +static void +purple_pbkdf2_set_option(PurpleCipherContext *context, const gchar *name, + void *value) +{ + Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context); + + g_return_if_fail(ctx_data != NULL); + + if (g_strcmp0(name, "hash") == 0) { + g_free(ctx_data->hash_func); + ctx_data->hash_func = g_strdup(value); + return; + } + + if (g_strcmp0(name, "iter_count") == 0) { + ctx_data->iter_count = GPOINTER_TO_UINT(value); + return; + } + + if (g_strcmp0(name, "out_len") == 0) { + ctx_data->out_len = GPOINTER_TO_UINT(value); + return; + } + + purple_debug_warning("pbkdf2", "Unknown option: %s\n", + name ? name : "(null)"); +} + +static void * +purple_pbkdf2_get_option(PurpleCipherContext *context, const gchar *name) +{ + Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context); + + g_return_val_if_fail(ctx_data != NULL, NULL); + + if (g_strcmp0(name, "hash") == 0) + return ctx_data->hash_func; + + if (g_strcmp0(name, "iter_count") == 0) + return GUINT_TO_POINTER(ctx_data->iter_count); + + if (g_strcmp0(name, "out_len") == 0) + return GUINT_TO_POINTER(ctx_data->out_len); + + purple_debug_warning("pbkdf2", "Unknown option: %s\n", + name ? name : "(null)"); + return NULL; +} + +static size_t +purple_pbkdf2_get_digest_size(PurpleCipherContext *context) +{ + Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context); + + g_return_val_if_fail(ctx_data != NULL, 0); + + return ctx_data->out_len; +} + +static void +purple_pbkdf2_set_salt(PurpleCipherContext *context, const guchar *salt, size_t len) +{ + Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context); + + g_return_if_fail(ctx_data != NULL); + + g_free(ctx_data->salt); + ctx_data->salt = NULL; + ctx_data->salt_len = 0; + + if (len == 0) + return; + g_return_if_fail(salt != NULL); + + ctx_data->salt = g_memdup(salt, len); + ctx_data->salt_len = len; +} + +static void +purple_pbkdf2_set_key(PurpleCipherContext *context, const guchar *key, + size_t len) +{ + Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context); + + g_return_if_fail(ctx_data != NULL); + + if (ctx_data->passphrase != NULL) { + memset(ctx_data->passphrase, 0, ctx_data->passphrase_len); + g_free(ctx_data->passphrase); + ctx_data->passphrase = NULL; + } + ctx_data->passphrase_len = 0; + + if (len == 0) + return; + g_return_if_fail(key != NULL); + + ctx_data->passphrase = g_memdup(key, len); + ctx_data->passphrase_len = len; +} + +/* inspired by gnutls 3.1.10, pbkdf2-sha1.c */ +static gboolean +purple_pbkdf2_digest(PurpleCipherContext *context, guchar digest[], size_t len) +{ + Pbkdf2Context *ctx_data = purple_cipher_context_get_data(context); + guchar halfkey[PBKDF2_HASH_MAX_LEN], halfkey_hash[PBKDF2_HASH_MAX_LEN]; + guint halfkey_len, halfkey_count, halfkey_pad, halfkey_no; + guchar *salt_ext; + size_t salt_ext_len; + guint iter_no; + PurpleCipherContext *hash; + + g_return_val_if_fail(ctx_data != NULL, FALSE); + g_return_val_if_fail(digest != NULL, FALSE); + g_return_val_if_fail(len >= ctx_data->out_len, FALSE); + + g_return_val_if_fail(ctx_data->hash_func != NULL, FALSE); + g_return_val_if_fail(ctx_data->iter_count > 0, FALSE); + g_return_val_if_fail(ctx_data->passphrase != NULL || + ctx_data->passphrase_len == 0, FALSE); + g_return_val_if_fail(ctx_data->salt != NULL || ctx_data->salt_len == 0, + FALSE); + g_return_val_if_fail(ctx_data->out_len > 0, FALSE); + g_return_val_if_fail(ctx_data->out_len < 0xFFFFFFFFU, FALSE); + + salt_ext_len = ctx_data->salt_len + 4; + + hash = purple_cipher_context_new_by_name("hmac", NULL); + if (hash == NULL) { + purple_debug_error("pbkdf2", "Couldn't create new hmac " + "context\n"); + return FALSE; + } + purple_cipher_context_set_option(hash, "hash", + (void*)ctx_data->hash_func); + purple_cipher_context_set_key(hash, (const guchar*)ctx_data->passphrase, + ctx_data->passphrase_len); + + halfkey_len = purple_cipher_context_get_digest_size(hash); + if (halfkey_len <= 0 || halfkey_len > PBKDF2_HASH_MAX_LEN) { + purple_debug_error("pbkdf2", "Unsupported hash function: %s " + "(digest size: %d)\n", + ctx_data->hash_func ? ctx_data->hash_func : "(null)", + halfkey_len); + return FALSE; + } + + halfkey_count = ((ctx_data->out_len - 1) / halfkey_len) + 1; + halfkey_pad = ctx_data->out_len - (halfkey_count - 1) * halfkey_len; + + salt_ext = g_new(guchar, salt_ext_len); + memcpy(salt_ext, ctx_data->salt, ctx_data->salt_len); + + for (halfkey_no = 1; halfkey_no <= halfkey_count; halfkey_no++) { + memset(halfkey, 0, halfkey_len); + + for (iter_no = 1; iter_no <= ctx_data->iter_count; iter_no++) { + int i; + + purple_cipher_context_reset_state(hash, NULL); + + if (iter_no == 1) { + salt_ext[salt_ext_len - 4] = + (halfkey_no & 0xff000000) >> 24; + salt_ext[salt_ext_len - 3] = + (halfkey_no & 0x00ff0000) >> 16; + salt_ext[salt_ext_len - 2] = + (halfkey_no & 0x0000ff00) >> 8; + salt_ext[salt_ext_len - 1] = + (halfkey_no & 0x000000ff) >> 0; + + purple_cipher_context_append(hash, salt_ext, + salt_ext_len); + } + else + purple_cipher_context_append(hash, halfkey_hash, + halfkey_len); + + if (!purple_cipher_context_digest(hash, halfkey_hash, + halfkey_len)) { + purple_debug_error("pbkdf2", + "Couldn't retrieve a digest\n"); + g_free(salt_ext); + purple_cipher_context_destroy(hash); + return FALSE; + } + + for (i = 0; i < halfkey_len; i++) + halfkey[i] ^= halfkey_hash[i]; + } + + memcpy(digest + (halfkey_no - 1) * halfkey_len, halfkey, + (halfkey_no == halfkey_count) ? halfkey_pad : + halfkey_len); + } + + g_free(salt_ext); + purple_cipher_context_destroy(hash); + + return TRUE; +} + +static PurpleCipherOps PBKDF2Ops = { + purple_pbkdf2_set_option, /* set_option */ + purple_pbkdf2_get_option, /* get_option */ + purple_pbkdf2_init, /* init */ + purple_pbkdf2_reset, /* reset */ + purple_pbkdf2_reset_state, /* reset_state */ + purple_pbkdf2_uninit, /* uninit */ + NULL, /* set_iv */ + NULL, /* append */ + purple_pbkdf2_digest, /* digest */ + purple_pbkdf2_get_digest_size, /* get_digest_size */ + NULL, /* encrypt */ + NULL, /* decrypt */ + purple_pbkdf2_set_salt, /* set_salt */ + NULL, /* get_salt_size */ + purple_pbkdf2_set_key, /* set_key */ + NULL, /* get_key_size */ + NULL, /* set_batch_mode */ + NULL, /* get_batch_mode */ + NULL, /* get_block_size */ + NULL, NULL, NULL, NULL /* reserved */ +}; + +PurpleCipherOps * +purple_pbkdf2_cipher_get_ops(void) { + return &PBKDF2Ops; +}
--- a/libpurple/connection.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/connection.c Mon Jun 10 01:22:52 2013 +0530 @@ -284,7 +284,7 @@ purple_account_set_connection(account, NULL); - g_free(gc->password); + purple_str_wipe(gc->password); g_free(gc->display_name); if (gc->disconnect_timeout > 0) @@ -458,7 +458,7 @@ { g_return_val_if_fail(gc != NULL, NULL); - return gc->password ? gc->password : purple_account_get_password(gc->account); + return gc->password; } const char * @@ -512,7 +512,6 @@ { PurpleAccount *account; PurpleConnection *gc; - char *password; account = data; gc = purple_account_get_connection(account); @@ -520,11 +519,7 @@ if (gc != NULL) gc->disconnect_timeout = 0; - password = g_strdup(purple_account_get_password(account)); purple_account_disconnect(account); - purple_account_set_password(account, password); - g_free(password); - return FALSE; }
--- a/libpurple/core.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/core.c Mon Jun 10 01:22:52 2013 +0530 @@ -37,6 +37,7 @@ #include "http.h" #include "idle.h" #include "imgstore.h" +#include "keyring.h" #include "network.h" #include "notify.h" #include "plugin.h" @@ -117,6 +118,7 @@ purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *")); /* Parameters */ purple_signal_register(core, "quitting", purple_marshal_VOID, NULL, 0); + purple_signal_register(core, "core-initialized", purple_marshal_VOID, NULL, 0); /* The prefs subsystem needs to be initialized before static protocols * for protocol prefs to work. */ @@ -150,6 +152,7 @@ purple_plugins_probe(G_MODULE_SUFFIX); + purple_keyring_init(); /* before accounts */ purple_theme_manager_init(); /* The buddy icon code uses the imgstore, so init it early. */ @@ -196,6 +199,8 @@ /* Load the buddy list after UI init */ purple_blist_boot(); + purple_signal_emit(purple_get_core(), "core-initialized"); + return TRUE; } @@ -243,6 +248,7 @@ purple_savedstatuses_uninit(); purple_status_uninit(); purple_accounts_uninit(); + purple_keyring_uninit(); /* after accounts */ purple_sound_uninit(); purple_theme_manager_uninit(); purple_xfers_uninit();
--- a/libpurple/example/nullclient.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/example/nullclient.c Mon Jun 10 01:22:52 2013 +0530 @@ -298,7 +298,7 @@ /* Get the password for the account */ password = getpass("Password: "); - purple_account_set_password(account, password); + purple_account_set_password(account, password, NULL, NULL); /* It's necessary to enable the account first. */ purple_account_set_enabled(account, UI_ID, TRUE);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/keyring.c Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,1357 @@ +/** + * @file keyring.c Keyring API + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include <glib.h> +#include <string.h> +#include "account.h" +#include "keyring.h" +#include "signals.h" +#include "core.h" +#include "debug.h" +#include "internal.h" +#include "dbus-maybe.h" + +struct _PurpleKeyring +{ + gchar *name; + gchar *id; + PurpleKeyringRead read_password; + PurpleKeyringSave save_password; + PurpleKeyringCancelRequests cancel_requests; + PurpleKeyringClose close_keyring; + PurpleKeyringImportPassword import_password; + PurpleKeyringExportPassword export_password; + PurpleKeyringReadSettings read_settings; + PurpleKeyringApplySettings apply_settings; + + gboolean is_closing; + gboolean is_cancelling; + gboolean close_after_cancel; +}; + +typedef struct +{ + GError *error; + PurpleKeyringSetInUseCallback cb; + gpointer cb_data; + PurpleKeyring *new; + PurpleKeyring *old; + + /** + * We are done when finished is positive and read_outstanding is zero. + */ + gboolean finished; + int read_outstanding; + + gboolean abort; + gboolean force; + gboolean succeeded; +} PurpleKeyringChangeTracker; + +typedef void (*PurpleKeyringDropCallback)(gpointer data); + +typedef struct +{ + PurpleKeyringDropCallback cb; + gpointer cb_data; + + gboolean finished; + int drop_outstanding; +} PurpleKeyringDropTracker; + +typedef struct +{ + PurpleKeyringSaveCallback cb; + gpointer cb_data; +} PurpleKeyringSetPasswordData; + +typedef struct +{ + gchar *keyring_id; + gchar *mode; + gchar *data; +} PurpleKeyringFailedImport; + +static void +purple_keyring_change_tracker_free(PurpleKeyringChangeTracker *tracker) +{ + if (tracker->error) + g_error_free(tracker->error); + g_free(tracker); +} + +static void +purple_keyring_failed_import_free(PurpleKeyringFailedImport *import) +{ + g_return_if_fail(import != NULL); + + g_free(import->keyring_id); + g_free(import->mode); + purple_str_wipe(import->data); + g_free(import); +} + +static void +purple_keyring_close(PurpleKeyring *keyring); + +static void +purple_keyring_drop_passwords(PurpleKeyring *keyring, + PurpleKeyringDropCallback cb, gpointer data); + +/* A list of available keyrings */ +static GList *purple_keyring_keyrings = NULL; + +/* Keyring being used. */ +static PurpleKeyring *purple_keyring_inuse = NULL; + +/* Keyring id marked to use (may not be loadable). */ +static gchar *purple_keyring_to_use = NULL; + +static guint purple_keyring_pref_cbid = 0; +static GList *purple_keyring_loaded_plugins = NULL; +static PurpleKeyringChangeTracker *current_change_tracker = NULL; +static gboolean purple_keyring_is_quitting = FALSE; +static GHashTable *purple_keyring_failed_imports = NULL; + +static const gchar * +purple_keyring_print_account(PurpleAccount *account) +{ + static gchar print_buff[100]; + + if (account == NULL) { + g_snprintf(print_buff, 100, "(null)"); + return print_buff; + } + + g_snprintf(print_buff, 100, "%s:%s", + purple_account_get_protocol_id(account), + purple_account_get_username(account)); + return print_buff; +} + +/**************************************************************************/ +/* Setting used keyrings */ +/**************************************************************************/ + +PurpleKeyring * +purple_keyring_find_keyring_by_id(const gchar *id) +{ + GList *it; + + for (it = purple_keyring_keyrings; it != NULL; it = it->next) { + PurpleKeyring *keyring = it->data; + const gchar *curr_id = purple_keyring_get_id(keyring); + + if (g_strcmp0(id, curr_id) == 0) + return keyring; + } + + return NULL; +} + +static void +purple_keyring_pref_callback(const gchar *pref, PurplePrefType type, + gconstpointer id, gpointer data) +{ + PurpleKeyring *new_keyring; + + g_return_if_fail(g_strcmp0(pref, "/purple/keyring/active") == 0); + g_return_if_fail(type == PURPLE_PREF_STRING); + g_return_if_fail(id != NULL); + + new_keyring = purple_keyring_find_keyring_by_id(id); + g_return_if_fail(new_keyring != NULL); + + purple_keyring_set_inuse(new_keyring, FALSE, NULL, NULL); +} + +static void +purple_keyring_pref_connect(void) +{ + g_return_if_fail(purple_keyring_pref_cbid == 0); + + purple_keyring_pref_cbid = purple_prefs_connect_callback(NULL, + "/purple/keyring/active", purple_keyring_pref_callback, NULL); +} + +static void +purple_keyring_pref_disconnect(void) +{ + g_return_if_fail(purple_keyring_pref_cbid != 0); + + purple_prefs_disconnect_callback(purple_keyring_pref_cbid); + purple_keyring_pref_cbid = 0; +} + +PurpleKeyring * +purple_keyring_get_inuse(void) +{ + return purple_keyring_inuse; +} + +static void +purple_keyring_set_inuse_drop_cb(gpointer _tracker) +{ + PurpleKeyringChangeTracker *tracker = _tracker; + + g_return_if_fail(tracker != NULL); + + if (tracker->succeeded) { + purple_keyring_close(tracker->old); + + purple_debug_info("keyring", "Successfully changed keyring.\n"); + + purple_keyring_inuse = tracker->new; + current_change_tracker = NULL; + + if (tracker->cb != NULL) + tracker->cb(NULL, tracker->cb_data); + purple_keyring_change_tracker_free(tracker); + return; + } + + purple_debug_error("keyring", "Failed to change keyring, aborting.\n"); + + purple_keyring_close(tracker->new); + + purple_keyring_pref_disconnect(); + purple_prefs_set_string("/purple/keyring/active", + purple_keyring_get_id(tracker->old)); + purple_keyring_pref_connect(); + + current_change_tracker = NULL; + + if (tracker->error == NULL) { + tracker->error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_UNKNOWN, + _("An unknown error has occured.")); + } + + if (tracker->cb != NULL) + tracker->cb(tracker->error, tracker->cb_data); + + purple_keyring_change_tracker_free(tracker); +} + +static void +purple_keyring_set_inuse_save_cb(PurpleAccount *account, GError *error, + gpointer _tracker) +{ + PurpleKeyringChangeTracker *tracker = _tracker; + + g_return_if_fail(account != NULL); + g_return_if_fail(tracker != NULL); + + tracker->read_outstanding--; + + if (g_error_matches(error, PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOPASSWORD)) { + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring", "No password found while " + "changing keyring for account %s: %s.\n", + purple_keyring_print_account(account), + error->message); + } + } else if (g_error_matches(error, PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_ACCESSDENIED)) { + purple_debug_info("keyring", "Access denied while changing " + "keyring for account %s: %s.\n", + purple_keyring_print_account(account), error->message); + tracker->abort = TRUE; + if (tracker->error != NULL) + g_error_free(tracker->error); + tracker->error = g_error_copy(error); + } else if (g_error_matches(error, PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_CANCELLED)) { + purple_debug_info("keyring", "Operation cancelled while " + "changing keyring for account %s: %s.\n", + purple_keyring_print_account(account), error->message); + tracker->abort = TRUE; + if (tracker->error == NULL) + tracker->error = g_error_copy(error); + } else if (g_error_matches(error, PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL)) { + purple_debug_error("keyring", "Failed to communicate with " + "backend while changing keyring for account %s: %s. " + "Aborting changes.\n", + purple_keyring_print_account(account), error->message); + tracker->abort = TRUE; + if (tracker->error != NULL) + g_error_free(tracker->error); + tracker->error = g_error_copy(error); + } else if (error != NULL) { + purple_debug_error("keyring", "Unknown error while changing " + "keyring for account %s: %s. Aborting changes.\n", + purple_keyring_print_account(account), error->message); + tracker->abort = TRUE; + if (tracker->error == NULL) + tracker->error = g_error_copy(error); + } + + purple_signal_emit(purple_keyring_get_handle(), "password-migration", + account); + + if (!tracker->finished || tracker->read_outstanding > 0) + return; + + /* This was the last one. */ + if (tracker->abort && !tracker->force) { + tracker->succeeded = FALSE; + purple_keyring_drop_passwords(tracker->new, + purple_keyring_set_inuse_drop_cb, tracker); + } else { + tracker->succeeded = TRUE; + purple_keyring_drop_passwords(tracker->old, + purple_keyring_set_inuse_drop_cb, tracker); + } +} + +static void +purple_keyring_set_inuse_read_cb(PurpleAccount *account, const gchar *password, + GError *error, gpointer _tracker) +{ + PurpleKeyringChangeTracker *tracker = _tracker; + PurpleKeyringSave save_cb; + + g_return_if_fail(account != NULL); + g_return_if_fail(tracker != NULL); + + if (tracker->abort) { + purple_keyring_set_inuse_save_cb(account, NULL, tracker); + return; + } + + if (error != NULL) { + if (tracker->force == TRUE || g_error_matches(error, + PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOPASSWORD)) { + /* Don't save password, and ignore it. */ + } else { + tracker->abort = TRUE; + } + purple_keyring_set_inuse_save_cb(account, error, tracker); + return; + } + + save_cb = purple_keyring_get_save_password(tracker->new); + g_assert(save_cb != NULL); + + save_cb(account, password, purple_keyring_set_inuse_save_cb, tracker); +} + +void +purple_keyring_set_inuse(PurpleKeyring *newkeyring, gboolean force, + PurpleKeyringSetInUseCallback cb, gpointer data) +{ + PurpleKeyring *oldkeyring; + PurpleKeyringChangeTracker *tracker; + GList *it; + PurpleKeyringRead read_cb; + + if (current_change_tracker != NULL) { + GError *error; + purple_debug_error("keyring", "There is password migration " + "session already running.\n"); + if (cb == NULL) + return; + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_INTERNAL, + _("There is a password migration session already " + "running.")); + cb(error, data); + g_error_free(error); + return; + } + + oldkeyring = purple_keyring_get_inuse(); + + if (oldkeyring == newkeyring) { + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring", + "Old and new keyring are the same: %s.\n", + (newkeyring != NULL) ? + newkeyring->id : "(null)"); + } + if (cb != NULL) + cb(NULL, data); + return; + } + + purple_debug_info("keyring", "Attempting to set new keyring: %s.\n", + (newkeyring != NULL) ? newkeyring->id : "(null)"); + + if (oldkeyring == NULL) { /* No keyring was set before. */ + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring", "Setting keyring for the " + "first time: %s.\n", newkeyring->id); + } + + purple_keyring_inuse = newkeyring; + g_assert(current_change_tracker == NULL); + if (cb != NULL) + cb(NULL, data); + return; + } + + /* Starting a migration. */ + + read_cb = purple_keyring_get_read_password(oldkeyring); + g_assert(read_cb != NULL); + + purple_debug_misc("keyring", "Starting migration from: %s.\n", + oldkeyring->id); + + tracker = g_new0(PurpleKeyringChangeTracker, 1); + current_change_tracker = tracker; + + tracker->cb = cb; + tracker->cb_data = data; + tracker->new = newkeyring; + tracker->old = oldkeyring; + tracker->force = force; + + for (it = purple_accounts_get_all(); it != NULL; it = it->next) { + if (tracker->abort) { + tracker->finished = TRUE; + break; + } + tracker->read_outstanding++; + + if (it->next == NULL) + tracker->finished = TRUE; + + read_cb(it->data, purple_keyring_set_inuse_read_cb, tracker); + } +} + +void +purple_keyring_register(PurpleKeyring *keyring) +{ + const gchar *keyring_id; + + g_return_if_fail(keyring != NULL); + + keyring_id = purple_keyring_get_id(keyring); + + purple_debug_info("keyring", "Registering keyring: %s\n", + keyring_id ? keyring_id : "(null)"); + + if (purple_keyring_get_id(keyring) == NULL || + purple_keyring_get_name(keyring) == NULL || + purple_keyring_get_read_password(keyring) == NULL || + purple_keyring_get_save_password(keyring) == NULL) { + purple_debug_error("keyring", "Cannot register %s, some " + "required fields are missing.\n", + keyring_id ? keyring_id : "(null)"); + return; + } + + if (purple_keyring_find_keyring_by_id(keyring_id) != NULL) { + purple_debug_error("keyring", + "Keyring is already registered.\n"); + return; + } + + /* If this is the configured keyring, use it. */ + if (purple_keyring_inuse == NULL && + g_strcmp0(keyring_id, purple_keyring_to_use) == 0) { + purple_debug_info("keyring", "Keyring %s matches keyring to " + "use, using it.\n", keyring_id); + purple_keyring_set_inuse(keyring, TRUE, NULL, NULL); + } + + PURPLE_DBUS_REGISTER_POINTER(keyring, PurpleKeyring); + purple_signal_emit(purple_keyring_get_handle(), "keyring-register", + keyring_id, keyring); + if (purple_debug_is_verbose()) { + purple_debug_info("keyring", "Registered keyring: %s.\n", + keyring_id); + } + + purple_keyring_keyrings = g_list_prepend(purple_keyring_keyrings, + keyring); +} + +void +purple_keyring_unregister(PurpleKeyring *keyring) +{ + PurpleKeyring *inuse; + PurpleKeyring *fallback; + const gchar *keyring_id; + + g_return_if_fail(keyring != NULL); + + keyring_id = purple_keyring_get_id(keyring); + + purple_debug_info("keyring", "Unregistering keyring: %s.\n", + keyring_id); + + purple_signal_emit(purple_keyring_get_handle(), "keyring-unregister", + keyring_id, keyring); + PURPLE_DBUS_UNREGISTER_POINTER(keyring); + + inuse = purple_keyring_get_inuse(); + fallback = purple_keyring_find_keyring_by_id(PURPLE_DEFAULT_KEYRING); + + if (inuse == keyring) { + if (inuse != fallback) { + purple_keyring_set_inuse(fallback, TRUE, NULL, NULL); + } else { + fallback = NULL; + purple_keyring_set_inuse(NULL, TRUE, NULL, NULL); + } + } + + purple_keyring_keyrings = g_list_remove(purple_keyring_keyrings, + keyring); +} + +GList * +purple_keyring_get_options(void) +{ + GList *options = NULL; + GList *it; + static gchar currentDisabledName[40]; + + if (purple_keyring_get_inuse() == NULL && purple_keyring_to_use != NULL + && purple_keyring_to_use[0] != '\0') { + g_snprintf(currentDisabledName, sizeof(currentDisabledName), + _("%s (disabled)"), purple_keyring_to_use); + + options = g_list_append(options, currentDisabledName); + options = g_list_append(options, purple_keyring_to_use); + } + + for (it = purple_keyring_keyrings; it != NULL; it = it->next) { + PurpleKeyring *keyring = it->data; + + options = g_list_append(options, + (gpointer)purple_keyring_get_name(keyring)); + options = g_list_append(options, + (gpointer)purple_keyring_get_id(keyring)); + } + + return options; +} + + +/**************************************************************************/ +/* Keyring plugin wrappers */ +/**************************************************************************/ + +static void +purple_keyring_close(PurpleKeyring *keyring) +{ + PurpleKeyringClose close_cb; + + g_return_if_fail(keyring != NULL); + + if (keyring->is_cancelling) { + keyring->close_after_cancel = TRUE; + return; + } + if (keyring->is_closing) + return; + keyring->is_closing = TRUE; + + close_cb = purple_keyring_get_close_keyring(keyring); + + if (close_cb != NULL) + close_cb(); + + keyring->is_closing = FALSE; +} + +static void +purple_keyring_cancel_requests(PurpleKeyring *keyring) +{ + PurpleKeyringCancelRequests cancel_cb; + + g_return_if_fail(keyring != NULL); + + if (keyring->is_cancelling) + return; + keyring->is_cancelling = TRUE; + + cancel_cb = purple_keyring_get_cancel_requests(keyring); + + if (cancel_cb != NULL) + cancel_cb(); + + keyring->is_cancelling = FALSE; + + if (keyring->close_after_cancel) { + keyring->close_after_cancel = FALSE; + purple_keyring_close(keyring); + } +} + +static void +purple_keyring_drop_passwords_save_cb(PurpleAccount *account, GError *error, + gpointer _tracker) +{ + PurpleKeyringDropTracker *tracker = _tracker; + + tracker->drop_outstanding--; + + if (!tracker->finished || tracker->drop_outstanding > 0) + return; + + if (tracker->cb) + tracker->cb(tracker->cb_data); + g_free(tracker); +} + +static void +purple_keyring_drop_passwords(PurpleKeyring *keyring, + PurpleKeyringDropCallback cb, gpointer data) +{ + GList *it; + PurpleKeyringSave save_cb; + PurpleKeyringDropTracker *tracker; + + g_return_if_fail(keyring != NULL); + + save_cb = purple_keyring_get_save_password(keyring); + g_assert(save_cb != NULL); + + tracker = g_new0(PurpleKeyringDropTracker, 1); + tracker->cb = cb; + tracker->cb_data = data; + + for (it = purple_accounts_get_all(); it != NULL; it = it->next) { + PurpleAccount *account = it->data; + + tracker->drop_outstanding++; + if (it->next == NULL) + tracker->finished = TRUE; + + save_cb(account, NULL, purple_keyring_drop_passwords_save_cb, + tracker); + } +} + +gboolean +purple_keyring_import_password(PurpleAccount *account, const gchar *keyring_id, + const gchar *mode, const gchar *data, GError **error) +{ + PurpleKeyring *keyring; + PurpleKeyring *inuse; + PurpleKeyringImportPassword import; + + g_return_val_if_fail(account != NULL, FALSE); + + if (keyring_id == NULL) + keyring_id = PURPLE_DEFAULT_KEYRING; + + purple_debug_misc("keyring", "Importing password for account %s to " + "keyring %s.\n", purple_keyring_print_account(account), + keyring_id); + + keyring = purple_keyring_find_keyring_by_id(keyring_id); + if (keyring == NULL) { + if (error != NULL) { + *error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Specified keyring is not registered.")); + } + purple_debug_warning("Keyring", "Specified keyring is not " + "registered, cannot import password info for account " + "%s.\n", purple_keyring_print_account(account)); + return FALSE; + } + + inuse = purple_keyring_get_inuse(); + if (inuse == NULL) { + PurpleKeyringFailedImport *import; + if (error != NULL) { + *error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOKEYRING, + _("No keyring loaded, cannot import password " + "info.")); + } + purple_debug_warning("Keyring", + "No keyring loaded, cannot import password info for " + "account %s.\n", purple_keyring_print_account(account)); + + import = g_new0(PurpleKeyringFailedImport, 1); + import->keyring_id = g_strdup(keyring_id); + import->mode = g_strdup(mode); + import->data = g_strdup(data); + g_hash_table_insert(purple_keyring_failed_imports, account, + import); + return FALSE; + } + + if (inuse != keyring) { + if (error != NULL) { + *error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_INTERNAL, + _("Specified keyring id does not match the " + "loaded one.")); + } + purple_debug_error("keyring", + "Specified keyring %s is not currently used (%s). " + "Data will be lost.\n", keyring_id, + purple_keyring_get_id(inuse)); + return FALSE; + } + + import = purple_keyring_get_import_password(inuse); + if (import == NULL) { + if (purple_debug_is_verbose()) { + purple_debug_misc("Keyring", "Configured keyring " + "cannot import password info. This might be " + "normal.\n"); + } + return TRUE; + } + + return import(account, mode, data, error); +} + +gboolean +purple_keyring_export_password(PurpleAccount *account, const gchar **keyring_id, + const gchar **mode, gchar **data, GError **error, + GDestroyNotify *destroy) +{ + PurpleKeyring *inuse; + PurpleKeyringExportPassword export; + + g_return_val_if_fail(account != NULL, FALSE); + g_return_val_if_fail(keyring_id != NULL, FALSE); + g_return_val_if_fail(mode != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + g_return_val_if_fail(error != NULL, FALSE); + + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + PurpleKeyringFailedImport *import = g_hash_table_lookup( + purple_keyring_failed_imports, account); + + if (import == NULL) { + *error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOKEYRING, + _("No keyring configured, cannot export " + "password info.")); + purple_debug_warning("keyring", + "No keyring configured, cannot export password " + "info.\n"); + return FALSE; + } else { + purple_debug_info("keyring", "No keyring configured, " + "getting fallback export data for %s.\n", + purple_keyring_print_account(account)); + + *keyring_id = import->keyring_id; + *mode = import->mode; + *data = g_strdup(import->data); + *destroy = (GDestroyNotify)purple_str_wipe; + return TRUE; + } + } + + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring", + "Exporting password for account %s from keyring %s\n", + purple_keyring_print_account(account), + purple_keyring_get_id(inuse)); + } + + *keyring_id = purple_keyring_get_id(inuse); + + export = purple_keyring_get_export_password(inuse); + if (export == NULL) { + if (purple_debug_is_verbose()) { + purple_debug_misc("Keyring", "Configured keyring " + "cannot export password info. This might be " + "normal.\n"); + } + *mode = NULL; + *data = NULL; + *destroy = NULL; + return TRUE; + } + + return export(account, mode, data, error, destroy); +} + +void +purple_keyring_get_password(PurpleAccount *account, + PurpleKeyringReadCallback cb, gpointer data) +{ + GError *error; + PurpleKeyring *inuse; + PurpleKeyringRead read_cb; + + g_return_if_fail(account != NULL); + + if (purple_keyring_is_quitting) { + purple_debug_error("keyring", "Cannot request a password while " + "quitting.\n"); + if (cb == NULL) + return; + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_INTERNAL, + _("Cannot request a password while quitting.")); + cb(account, NULL, error, data); + g_error_free(error); + return; + } + + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + purple_debug_error("keyring", "No keyring configured.\n"); + if (cb == NULL) + return; + + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOKEYRING, + _("No keyring configured.")); + cb(account, NULL, error, data); + g_error_free(error); + } + + read_cb = purple_keyring_get_read_password(inuse); + g_assert(read_cb != NULL); + + purple_debug_info("keyring", "Reading password for account %s...\n", + purple_keyring_print_account(account)); + read_cb(account, cb, data); +} + +static void +purple_keyring_set_password_save_cb(PurpleAccount *account, GError *error, + gpointer _set_data) +{ + PurpleKeyringSetPasswordData *set_data = _set_data; + + g_return_if_fail(account != NULL); + g_return_if_fail(set_data != NULL); + + if (error == NULL && purple_debug_is_verbose()) { + purple_debug_misc("keyring", "Password for account %s " + "saved successfully.\n", + purple_keyring_print_account(account)); + } else if (purple_debug_is_verbose()) { + purple_debug_warning("keyring", "Password for account %s " + "not saved successfully.\n", + purple_keyring_print_account(account)); + } + + if (error != NULL) { + purple_notify_error(NULL, _("Keyrings"), + _("Failed to save a password in keyring."), + error->message); + } + + if (set_data->cb != NULL) + set_data->cb(account, error, set_data->cb_data); + g_free(set_data); +} + +void +purple_keyring_set_password(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data) +{ + GError *error; + PurpleKeyring *inuse; + PurpleKeyringSave save_cb; + PurpleKeyringSetPasswordData *set_data; + + g_return_if_fail(account != NULL); + + if (purple_keyring_is_quitting) { + purple_debug_error("keyring", "Cannot save a password while " + "quitting.\n"); + if (cb == NULL) + return; + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_INTERNAL, + _("Cannot save a password while quitting.")); + cb(account, error, data); + g_error_free(error); + return; + } + + if (current_change_tracker != NULL) { + purple_debug_error("keyring", "Cannot save a password during " + "password migration.\n"); + if (cb == NULL) + return; + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_INTERNAL, + _("Cannot save a password during password migration.")); + cb(account, error, data); + g_error_free(error); + return; + } + + inuse = purple_keyring_get_inuse(); + if (inuse == NULL) { + if (cb == NULL) + return; + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOKEYRING, + _("No keyring configured.")); + cb(account, error, data); + g_error_free(error); + return; + } + + save_cb = purple_keyring_get_save_password(inuse); + g_assert(save_cb != NULL); + + set_data = g_new(PurpleKeyringSetPasswordData, 1); + set_data->cb = cb; + set_data->cb_data = data; + purple_debug_info("keyring", "%s password for account %s...\n", + (password ? "Saving" : "Removing"), + purple_keyring_print_account(account)); + save_cb(account, password, purple_keyring_set_password_save_cb, set_data); +} + +PurpleRequestFields * +purple_keyring_read_settings(void) +{ + PurpleKeyring *inuse; + PurpleKeyringReadSettings read_settings; + + if (purple_keyring_is_quitting || current_change_tracker != NULL) { + purple_debug_error("keyring", "Cannot read settngs at the " + "moment.\n"); + return NULL; + } + + inuse = purple_keyring_get_inuse(); + if (inuse == NULL) { + purple_debug_error("keyring", "No keyring in use.\n"); + return NULL; + } + + read_settings = purple_keyring_get_read_settings(inuse); + if (read_settings == NULL) + return NULL; + return read_settings(); +} + +gboolean +purple_keyring_apply_settings(void *notify_handle, PurpleRequestFields *fields) +{ + PurpleKeyring *inuse; + PurpleKeyringApplySettings apply_settings; + + g_return_val_if_fail(fields != NULL, FALSE); + + if (purple_keyring_is_quitting || current_change_tracker != NULL) { + purple_debug_error("keyring", "Cannot apply settngs at the " + "moment.\n"); + return FALSE; + } + + inuse = purple_keyring_get_inuse(); + if (inuse == NULL) { + purple_debug_error("keyring", "No keyring in use.\n"); + return FALSE; + } + + apply_settings = purple_keyring_get_apply_settings(inuse); + if (apply_settings == NULL) { + purple_debug_warning("keyring", "Applying settings not " + "supported.\n"); + return FALSE; + } + return apply_settings(notify_handle, fields); +} + +/**************************************************************************/ +/* PurpleKeyring accessors */ +/**************************************************************************/ + +PurpleKeyring * +purple_keyring_new(void) +{ + return g_new0(PurpleKeyring, 1); +} + +void +purple_keyring_free(PurpleKeyring *keyring) +{ + g_return_if_fail(keyring != NULL); + + g_free(keyring->name); + g_free(keyring->id); + g_free(keyring); +} + +const gchar * +purple_keyring_get_name(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->name; +} + +const gchar * +purple_keyring_get_id(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->id; +} + +PurpleKeyringRead +purple_keyring_get_read_password(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->read_password; +} + +PurpleKeyringSave +purple_keyring_get_save_password(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->save_password; +} + +PurpleKeyringCancelRequests +purple_keyring_get_cancel_requests(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->cancel_requests; +} + +PurpleKeyringClose +purple_keyring_get_close_keyring(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->close_keyring; +} + +PurpleKeyringImportPassword +purple_keyring_get_import_password(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->import_password; +} + +PurpleKeyringExportPassword +purple_keyring_get_export_password(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->export_password; +} + +PurpleKeyringReadSettings +purple_keyring_get_read_settings(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->read_settings; +} + +PurpleKeyringApplySettings +purple_keyring_get_apply_settings(const PurpleKeyring *keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->apply_settings; +} + +void +purple_keyring_set_name(PurpleKeyring *keyring, const gchar *name) +{ + g_return_if_fail(keyring != NULL); + g_return_if_fail(name != NULL); + + g_free(keyring->name); + keyring->name = g_strdup(name); +} + +void +purple_keyring_set_id(PurpleKeyring *keyring, const gchar *id) +{ + g_return_if_fail(keyring != NULL); + g_return_if_fail(id != NULL); + + g_free(keyring->id); + keyring->id = g_strdup(id); +} + +void +purple_keyring_set_read_password(PurpleKeyring *keyring, + PurpleKeyringRead read_cb) +{ + g_return_if_fail(keyring != NULL); + g_return_if_fail(read_cb != NULL); + + keyring->read_password = read_cb; +} + +void +purple_keyring_set_save_password(PurpleKeyring *keyring, + PurpleKeyringSave save_cb) +{ + g_return_if_fail(keyring != NULL); + g_return_if_fail(save_cb != NULL); + + keyring->save_password = save_cb; +} + +void +purple_keyring_set_cancel_requests(PurpleKeyring *keyring, + PurpleKeyringCancelRequests cancel_requests) +{ + g_return_if_fail(keyring != NULL); + + keyring->cancel_requests = cancel_requests; +} + +void +purple_keyring_set_close_keyring(PurpleKeyring *keyring, + PurpleKeyringClose close_cb) +{ + g_return_if_fail(keyring != NULL); + + keyring->close_keyring = close_cb; +} + +void +purple_keyring_set_import_password(PurpleKeyring *keyring, + PurpleKeyringImportPassword import_password) +{ + g_return_if_fail(keyring != NULL); + + keyring->import_password = import_password; +} + +void +purple_keyring_set_export_password(PurpleKeyring *keyring, + PurpleKeyringExportPassword export_password) +{ + g_return_if_fail(keyring != NULL); + + keyring->export_password = export_password; +} + +void +purple_keyring_set_read_settings(PurpleKeyring *keyring, +PurpleKeyringReadSettings read_settings) +{ + g_return_if_fail(keyring != NULL); + + keyring->read_settings = read_settings; +} + +void +purple_keyring_set_apply_settings(PurpleKeyring *keyring, +PurpleKeyringApplySettings apply_settings) +{ + g_return_if_fail(keyring != NULL); + + keyring->apply_settings = apply_settings; +} + + +/**************************************************************************/ +/* Error Codes */ +/**************************************************************************/ + +GQuark purple_keyring_error_domain(void) +{ + return g_quark_from_static_string("libpurple keyring"); +} + +/**************************************************************************/ +/* Keyring Subsystem */ +/**************************************************************************/ + +static void purple_keyring_core_initialized_cb(void) +{ + if (purple_keyring_inuse == NULL) { + purple_notify_error(NULL, _("Keyrings"), + _("Failed to load selected keyring."), + _("Check your system configuration or select another " + "one in Preferences dialog.")); + } +} + +static void purple_keyring_core_quitting_cb() +{ + if (current_change_tracker != NULL) { + PurpleKeyringChangeTracker *tracker = current_change_tracker; + tracker->abort = TRUE; + if (tracker->old) + purple_keyring_cancel_requests(tracker->old); + if (current_change_tracker == tracker && tracker->new) + purple_keyring_cancel_requests(tracker->new); + } + + purple_keyring_is_quitting = TRUE; + if (purple_keyring_inuse != NULL) + purple_keyring_cancel_requests(purple_keyring_inuse); +} + +void +purple_keyring_init(void) +{ + const gchar *touse; + GList *it; + + purple_keyring_keyrings = NULL; + purple_keyring_inuse = NULL; + + purple_keyring_failed_imports = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, + (GDestroyNotify)purple_keyring_failed_import_free); + + /* void keyring_register(const char *keyring_id, + * PurpleKeyring * keyring); + * + * A signal called when keyring is registered. + * + * @param keyring_id The keyring ID. + * @param keyring The keyring. + */ + purple_signal_register(purple_keyring_get_handle(), + "keyring-register", + purple_marshal_VOID__POINTER_POINTER, + NULL, 2, + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_BOXED, "PurpleKeyring *")); + + /* void keyring_unregister(const char *keyring_id, + * PurpleKeyring * keyring); + * + * A signal called when keyring is unregistered. + * + * @param keyring_id The keyring ID. + * @param keyring The keyring. + */ + purple_signal_register(purple_keyring_get_handle(), + "keyring-unregister", + purple_marshal_VOID__POINTER_POINTER, + NULL, 2, + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_BOXED, "PurpleKeyring *")); + + /* void password_migration(PurpleAccount* account); + * + * A signal called, when a password for the account was moved to another + * keyring. + * + * @param account The account. + */ + purple_signal_register(purple_keyring_get_handle(), + "password-migration", + purple_marshal_VOID__POINTER, + NULL, 1, + purple_value_new(PURPLE_TYPE_BOXED, "PurpleAccount *")); + + touse = purple_prefs_get_string("/purple/keyring/active"); + if (touse == NULL) { + purple_prefs_add_none("/purple/keyring"); + purple_prefs_add_string("/purple/keyring/active", + PURPLE_DEFAULT_KEYRING); + purple_keyring_to_use = g_strdup(PURPLE_DEFAULT_KEYRING); + } else + purple_keyring_to_use = g_strdup(touse); + + purple_keyring_pref_connect(); + + for (it = purple_plugins_get_all(); it != NULL; it = it->next) + { + PurplePlugin *plugin = (PurplePlugin *)it->data; + + if (plugin->info == NULL || plugin->info->id == NULL) + continue; + if (strncmp(plugin->info->id, "keyring-", 8) != 0) + continue; + + if (purple_plugin_is_loaded(plugin)) + continue; + + if (purple_plugin_load(plugin)) + { + purple_keyring_loaded_plugins = g_list_append( + purple_keyring_loaded_plugins, plugin); + } + } + + if (purple_keyring_inuse == NULL) + purple_debug_error("keyring", "Selected keyring failed to load\n"); + + purple_signal_connect(purple_get_core(), "core-initialized", + purple_keyring_get_handle(), + PURPLE_CALLBACK(purple_keyring_core_initialized_cb), NULL); + purple_signal_connect(purple_get_core(), "quitting", + purple_keyring_get_handle(), + PURPLE_CALLBACK(purple_keyring_core_quitting_cb), NULL); +} + +void +purple_keyring_uninit(void) +{ + GList *it; + + purple_keyring_pref_disconnect(); + + g_free(purple_keyring_to_use); + purple_keyring_inuse = NULL; + + g_hash_table_destroy(purple_keyring_failed_imports); + purple_keyring_failed_imports = NULL; + + for (it = g_list_first(purple_keyring_loaded_plugins); it != NULL; + it = g_list_next(it)) + { + PurplePlugin *plugin = (PurplePlugin *)it->data; + if (g_list_find(purple_plugins_get_loaded(), plugin) == NULL) + continue; + purple_plugin_unload(plugin); + } + g_list_free(purple_keyring_loaded_plugins); + purple_keyring_loaded_plugins = NULL; + + purple_signals_unregister_by_instance(purple_keyring_get_handle()); + purple_signals_disconnect_by_handle(purple_keyring_get_handle()); +} + +void * +purple_keyring_get_handle(void) +{ + static int handle; + + return &handle; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/keyring.h Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,548 @@ +/** + * @file keyring.h Keyring API + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_KEYRING_H_ +#define _PURPLE_KEYRING_H_ + +#include "account.h" +#include "request.h" + +/** + * Default keyring ID. + */ +#define PURPLE_DEFAULT_KEYRING "keyring-internal" + +/** + * Keyring subsystem error domain. + */ +#define PURPLE_KEYRING_ERROR purple_keyring_error_domain() + +/**************************************************************************/ +/** @name Data structures and types */ +/**************************************************************************/ +/*@{*/ + +typedef struct _PurpleKeyring PurpleKeyring; + +/*@}*/ + +/**************************************************************************/ +/** @name Callbacks for keyrings access functions */ +/**************************************************************************/ +/*@{*/ + +/** + * Callback for once a password is read. + * + * If there was a problem, the password will be NULL, and the error set. + * + * @param account The account. + * @param password The password. + * @param error Error that may have occurred. + * @param data Data passed to the callback. + */ +typedef void (*PurpleKeyringReadCallback)(PurpleAccount *account, + const gchar *password, GError *error, gpointer data); + +/** + * Callback for once a password has been stored. + * + * If there was a problem, the error will be set. + * + * @param account The account. + * @param error Error that may have occurred. + * @param data Data passed to the callback. + */ +typedef void (*PurpleKeyringSaveCallback)(PurpleAccount *account, GError *error, + gpointer data); + +/** + * Callback for once the master password for a keyring has been changed. + * + * @param error Error that has occurred. + * @param data Data passed to the callback. + */ +typedef void (*PurpleKeyringChangeMasterCallback)(GError *error, gpointer data); + +/** + * Callback for when we change the keyring. + * + * @param error An error that might have occurred. + * @param data A pointer to user supplied data. + */ +typedef void (*PurpleKeyringSetInUseCallback)(GError *error, gpointer data); + +/*@}*/ + +/**************************************************************************/ +/** @name Keyrings access functions */ +/**************************************************************************/ +/*@{*/ + +/** + * Read the password for an account. + * + * @param account The account. + * @param cb A callback for once the password is found. + * @param data Data to be passed to the callback. + */ +typedef void (*PurpleKeyringRead)(PurpleAccount *account, + PurpleKeyringReadCallback cb, gpointer data); + +/** + * Store a password in the keyring. + * + * @param account The account. + * @param password The password to be stored. If the password is NULL, this + * means that the keyring should forget about that password. + * @param cb A callback for once the password is saved. + * @param data Data to be passed to the callback. + */ +typedef void (*PurpleKeyringSave)(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data); + +/** + * Cancel all running requests. + * + * After calling that, all queued requests should run their callbacks (most + * probably, with failure result). + */ +typedef void (*PurpleKeyringCancelRequests)(void); + +/** + * Close the keyring. + * + * This will be called so the keyring can do any cleanup it needs. + */ +typedef void (*PurpleKeyringClose)(void); + +/** + * Import serialized (and maybe encrypted) password. + * + * This is not async because it is not meant to prompt for a master password and + * decrypt passwords. + * + * @param account The account. + * @param mode A keyring specific option that was stored. Can be NULL. + * @param data Data that was stored. Can be NULL. + * + * @return TRUE on success, FALSE on failure. + */ +typedef gboolean (*PurpleKeyringImportPassword)(PurpleAccount *account, + const gchar *mode, const gchar *data, GError **error); + +/** + * Export serialized (and maybe encrypted) password. + * + * @param account The account. + * @param mode An option field that can be used by the plugin. This is + * expected to be a static string. + * @param data The data to be stored in the XML node. This string will be + * freed using destroy() once not needed anymore. + * @param error Will be set if a problem occured. + * @param destroy A function to be called, if non NULL, to free data. + * + * @return TRUE on success, FALSE on failure. + */ +typedef gboolean (*PurpleKeyringExportPassword)(PurpleAccount *account, + const gchar **mode, gchar **data, GError **error, + GDestroyNotify *destroy); + +/** + * Read keyring settings. + * + * @return New copy of current settings (must be free'd with + * purple_request_fields_destroy). + */ +typedef PurpleRequestFields * (*PurpleKeyringReadSettings)(void); + +/** + * Applies modified keyring settings. + * + * @param notify_handle A handle that can be passed to purple_notify_message. + * @param fields Modified settings (originally taken from + * PurpleKeyringReadSettings). + * @return TRUE, if succeeded, FALSE otherwise. + */ +typedef gboolean (*PurpleKeyringApplySettings)(void *notify_handle, + PurpleRequestFields *fields); + +/*@}*/ + +G_BEGIN_DECLS + +/**************************************************************************/ +/** @name Setting used keyrings */ +/**************************************************************************/ +/*@{*/ + +/** + * Find a keyring by an id. + * + * @param id The id for the keyring. + * + * @return The keyring, or NULL if not found. + */ +PurpleKeyring * +purple_keyring_find_keyring_by_id(const gchar *id); + +/** + * Get the keyring being used. + */ +PurpleKeyring * +purple_keyring_get_inuse(void); + +/** + * Set the keyring to use. This function will move all passwords from + * the old keyring to the new one. + * + * If it fails, it will cancel all changes, close the new keyring, and notify + * the callback. If it succeeds, it will remove all passwords from the old safe + * and close that safe. + * + * @param newkeyring The new keyring to use. + * @param force FALSE if the change can be cancelled. If this is TRUE and + * an error occurs, data might be lost. + * @param cb A callback for once the change is complete. + * @param data Data to be passed to the callback. + */ +void +purple_keyring_set_inuse(PurpleKeyring *newkeyring, gboolean force, + PurpleKeyringSetInUseCallback cb, gpointer data); + +/** + * Register a keyring plugin. + * + * @param keyring The keyring to register. + */ +void +purple_keyring_register(PurpleKeyring *keyring); + +/** + * Unregister a keyring plugin. + * + * In case the keyring is in use, passwords will be moved to a fallback safe, + * and the keyring to unregister will be properly closed. + * + * @param keyring The keyring to unregister. + */ +void +purple_keyring_unregister(PurpleKeyring *keyring); + +/** + * Returns a GList containing the IDs and names of the registered + * keyrings. + * + * @return The list of IDs and names. + */ +GList * +purple_keyring_get_options(void); + +/*@}*/ + +/**************************************************************************/ +/** @name Keyring plugin wrappers */ +/**************************************************************************/ +/*@{*/ + +/** + * Import serialized (and maybe encrypted) password into current keyring. + * + * It's used by account.c while reading a password from xml. + * + * @param account The account. + * @param keyring_id The plugin ID that was stored in the xml file. Can be NULL. + * @param mode A keyring specific option that was stored. Can be NULL. + * @param data Data that was stored, can be NULL. + * + * @return TRUE if the input was accepted, FALSE otherwise. + */ +gboolean +purple_keyring_import_password(PurpleAccount *account, const gchar *keyring_id, + const gchar *mode, const gchar *data, GError **error); + +/** + * Export serialized (and maybe encrypted) password out of current keyring. + * + * It's used by account.c while syncing accounts to xml. + * + * @param account The account for which we want the info. + * @param keyring_id The plugin id to be stored in the XML node. This will be + * NULL or a string that can be considered static. + * @param mode An option field that can be used by the plugin. This will + * be NULL or a string that can be considered static. + * @param data The data to be stored in the XML node. This string must be + * freed using destroy() once not needed anymore if it is not + * NULL. + * @param error Will be set if a problem occured. + * @param destroy A function to be called, if non NULL, to free data. + * + * @return TRUE if the info was exported successfully, FALSE otherwise. + */ +gboolean +purple_keyring_export_password(PurpleAccount *account, const gchar **keyring_id, + const gchar **mode, gchar **data, GError **error, + GDestroyNotify *destroy); + +/** + * Read a password from the current keyring. + * + * @param account The account. + * @param cb A callback for once the password is read. + * @param data Data passed to the callback. + */ +void +purple_keyring_get_password(PurpleAccount *account, + PurpleKeyringReadCallback cb, gpointer data); + +/** + * Save a password to the current keyring. + * + * @param account The account. + * @param password The password to save. + * @param cb A callback for once the password is saved. + * @param data Data to be passed to the callback. + */ +void +purple_keyring_set_password(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data); + +/** + * Reads settings from current keyring. + * + * @return New copy of current settings (must be free'd with + * purple_request_fields_destroy). + */ +PurpleRequestFields * +purple_keyring_read_settings(void); + +/** + * Applies modified settings to current keyring. + * + * @param notify_handle A handle that can be passed to purple_notify_message. + * @param fields Modified settings (originally taken from + * PurpleKeyringReadSettings). + * @return TRUE, if succeeded, FALSE otherwise. + */ +gboolean +purple_keyring_apply_settings(void *notify_handle, PurpleRequestFields *fields); + +/*@}*/ + +/**************************************************************************/ +/** @name PurpleKeyring accessors */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates a new keyring wrapper. + */ +PurpleKeyring * +purple_keyring_new(void); + +/** + * Frees all data allocated with purple_keyring_new. + * + * @param keyring Keyring wrapper struct. + */ +void +purple_keyring_free(PurpleKeyring *keyring); + +/** + * Gets friendly user name. + * + * @param keyring The keyring. + * @return Friendly user name. + */ +const gchar * +purple_keyring_get_name(const PurpleKeyring *keyring); + +/** + * Gets keyring ID. + * + * @param keyring The keyring. + * @return Keyring ID. + */ +const gchar * +purple_keyring_get_id(const PurpleKeyring *keyring); + +PurpleKeyringRead +purple_keyring_get_read_password(const PurpleKeyring *keyring); + +PurpleKeyringSave +purple_keyring_get_save_password(const PurpleKeyring *keyring); + +PurpleKeyringCancelRequests +purple_keyring_get_cancel_requests(const PurpleKeyring *keyring); + +PurpleKeyringClose +purple_keyring_get_close_keyring(const PurpleKeyring *keyring); + +PurpleKeyringImportPassword +purple_keyring_get_import_password(const PurpleKeyring *keyring); + +PurpleKeyringExportPassword +purple_keyring_get_export_password(const PurpleKeyring *keyring); + +PurpleKeyringReadSettings +purple_keyring_get_read_settings(const PurpleKeyring *keyring); + +PurpleKeyringApplySettings +purple_keyring_get_apply_settings(const PurpleKeyring *keyring); + +/** + * Sets friendly user name. + * + * This field is required. + * + * @param keyring The keyring. + * @param name Friendly user name. + */ +void +purple_keyring_set_name(PurpleKeyring *keyring, const gchar *name); + +/** + * Sets keyring ID. + * + * This field is required. + * + * @param keyring The keyring. + * @param name Keyring ID. + */ +void +purple_keyring_set_id(PurpleKeyring *keyring, const gchar *id); + +/** + * Sets read password method. + * + * This field is required. + * + * @param keyring The keyring. + * @param read_cb Read password method. + */ +void +purple_keyring_set_read_password(PurpleKeyring *keyring, + PurpleKeyringRead read_cb); + +/** + * Sets save password method. + * + * This field is required. + * + * @param keyring The keyring. + * @param save_cb Save password method. + */ +void +purple_keyring_set_save_password(PurpleKeyring *keyring, + PurpleKeyringSave save_cb); + +void +purple_keyring_set_cancel_requests(PurpleKeyring *keyring, + PurpleKeyringCancelRequests cancel_requests); + +void +purple_keyring_set_close_keyring(PurpleKeyring *keyring, + PurpleKeyringClose close_cb); + +void +purple_keyring_set_import_password(PurpleKeyring *keyring, + PurpleKeyringImportPassword import_password); + +void +purple_keyring_set_export_password(PurpleKeyring *keyring, + PurpleKeyringExportPassword export_password); + +void +purple_keyring_set_read_settings(PurpleKeyring *keyring, +PurpleKeyringReadSettings read_settings); + +void +purple_keyring_set_apply_settings(PurpleKeyring *keyring, +PurpleKeyringApplySettings apply_settings); + +/*@}*/ + +/**************************************************************************/ +/** @name Error Codes */ +/**************************************************************************/ +/*@{*/ + +/** + * Gets keyring subsystem error domain. + * + * @return keyring subsystem error domain. + */ +GQuark +purple_keyring_error_domain(void); + +/** + * Error codes for keyring subsystem. + */ +enum PurpleKeyringError +{ + PURPLE_KEYRING_ERROR_UNKNOWN = 0, /**< Unknown error. */ + + PURPLE_KEYRING_ERROR_NOKEYRING = 10, /**< No keyring configured. */ + PURPLE_KEYRING_ERROR_INTERNAL, /**< Internal keyring system error. */ + PURPLE_KEYRING_ERROR_BACKENDFAIL, /**< Failed to communicate with the backend or internal backend error. */ + + PURPLE_KEYRING_ERROR_NOPASSWORD = 20, /**< No password stored for the specified account. */ + PURPLE_KEYRING_ERROR_ACCESSDENIED, /**< Access denied for the specified keyring or entry. */ + PURPLE_KEYRING_ERROR_CANCELLED /**< Operation was cancelled. */ +}; + +/*}@*/ + +/**************************************************************************/ +/** @name Keyring Subsystem */ +/**************************************************************************/ +/*@{*/ + +/** + * Initializes the keyring subsystem. + */ +void +purple_keyring_init(void); + +/** + * Uninitializes the keyring subsystem. + */ +void +purple_keyring_uninit(void); + +/** + * Returns the keyring subsystem handle. + * + * @return The keyring subsystem handle. + */ +void * +purple_keyring_get_handle(void); + +/*}@*/ + +G_END_DECLS + +#endif /* _PURPLE_KEYRING_H_ */
--- a/libpurple/plugins/Makefile.am Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/plugins/Makefile.am Mon Jun 10 01:22:52 2013 +0530 @@ -1,4 +1,4 @@ -DIST_SUBDIRS = mono perl ssl tcl +DIST_SUBDIRS = mono perl ssl tcl keyrings if USE_PERL PERL_DIR = perl @@ -20,7 +20,8 @@ $(MONO_DIR) \ $(PERL_DIR) \ ssl \ - $(TCL_DIR) + $(TCL_DIR) \ + keyrings plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) @@ -146,7 +147,11 @@ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ $(PLUGIN_CFLAGS) \ - $(DBUS_CFLAGS) + $(DBUS_CFLAGS) \ + $(NSS_CFLAGS) + +PLUGIN_LIBS = \ + $(NSS_LIBS) # # This part allows people to build their own plugins in here.
--- a/libpurple/plugins/Makefile.mingw Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/plugins/Makefile.mingw Mon Jun 10 01:22:52 2013 +0530 @@ -10,6 +10,7 @@ PERL_PLUGIN := ./perl TCL_PLUGIN := ./tcl SSL_PLUGIN := ./ssl +KEYRING_PLUGIN := ./keyrings .SUFFIXES: .SUFFIXES: .c .dll @@ -48,11 +49,13 @@ $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) $(MAKE_at) $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE) $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) + $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE) install: all $(PURPLE_INSTALL_PLUGINS_DIR) $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) install $(MAKE_at) $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE) install $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) install + $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE) install cp *.dll $(PURPLE_INSTALL_PLUGINS_DIR) %.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H) @@ -78,5 +81,6 @@ $(MAKE_at) $(MAKE) -C $(PERL_PLUGIN) -f $(MINGW_MAKEFILE) clean $(MAKE_at) $(MAKE) -C $(TCL_PLUGIN) -f $(MINGW_MAKEFILE) clean $(MAKE_at) $(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) clean + $(MAKE_at) $(MAKE) -C $(KEYRING_PLUGIN) -f $(MINGW_MAKEFILE) clean include $(PIDGIN_COMMON_TARGETS)
--- a/libpurple/plugins/ciphertest.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/plugins/ciphertest.c Mon Jun 10 01:22:52 2013 +0530 @@ -231,6 +231,343 @@ } /************************************************************************** + * PBKDF2 stuff + **************************************************************************/ + +#ifdef HAVE_NSS +# include <nss.h> +# include <secmod.h> +# include <pk11func.h> +# include <prerror.h> +# include <secerr.h> +#endif + +typedef struct { + const gchar *hash; + const guint iter_count; + const gchar *passphrase; + const gchar *salt; + const guint out_len; + const gchar *answer; +} pbkdf2_test; + +pbkdf2_test pbkdf2_tests[] = { + { "sha256", 1, "password", "salt", 32, "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b"}, + { "sha1", 1, "password", "salt", 32, "0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164"}, + { "sha1", 1000, "ala ma kota", "", 16, "924dba137b5bcf6d0de84998f3d8e1f9"}, + { "sha1", 1, "", "", 32, "1e437a1c79d75be61e91141dae20affc4892cc99abcc3fe753887bccc8920176"}, + { "sha256", 100, "some password", "and salt", 1, "c7"}, + { "sha1", 10000, "pretty long password W Szczebrzeszynie chrzaszcz brzmi w trzcinie i Szczebrzeszyn z tego slynie", "Grzegorz Brzeczyszczykiewicz", 32, "8cb0cb164f2554733ae02f5751b0e84a88fb385446e85a3991bdcdf1ea11795c"}, + { NULL, 0, NULL, NULL, 0, NULL} +}; + +#ifdef HAVE_NSS + +static gchar* +cipher_pbkdf2_nss_sha1(const gchar *passphrase, const gchar *salt, + guint iter_count, guint out_len) +{ + PK11SlotInfo *slot; + SECAlgorithmID *algorithm = NULL; + PK11SymKey *symkey = NULL; + const SECItem *symkey_data = NULL; + SECItem salt_item, passphrase_item; + guchar *passphrase_buff, *salt_buff; + gchar *ret; + + g_return_val_if_fail(passphrase != NULL, NULL); + g_return_val_if_fail(iter_count > 0, NULL); + g_return_val_if_fail(out_len > 0, NULL); + + NSS_NoDB_Init(NULL); + + slot = PK11_GetBestSlot(PK11_AlgtagToMechanism(SEC_OID_PKCS5_PBKDF2), + NULL); + if (slot == NULL) { + purple_debug_error("cipher-test", "NSS: couldn't get slot: " + "%d\n", PR_GetError()); + return NULL; + } + + salt_buff = (guchar*)g_strdup(salt ? salt : ""); + salt_item.type = siBuffer; + salt_item.data = salt_buff; + salt_item.len = salt ? strlen(salt) : 0; + + algorithm = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, + SEC_OID_AES_256_CBC, SEC_OID_HMAC_SHA1, out_len, iter_count, + &salt_item); + if (algorithm == NULL) { + purple_debug_error("cipher-test", "NSS: couldn't create " + "algorithm ID: %d\n", PR_GetError()); + PK11_FreeSlot(slot); + g_free(salt_buff); + return NULL; + } + + passphrase_buff = (guchar*)g_strdup(passphrase); + passphrase_item.type = siBuffer; + passphrase_item.data = passphrase_buff; + passphrase_item.len = strlen(passphrase); + + symkey = PK11_PBEKeyGen(slot, algorithm, &passphrase_item, PR_FALSE, + NULL); + if (symkey == NULL) { + purple_debug_error("cipher-test", "NSS: Couldn't generate key: " + "%d\n", PR_GetError()); + SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); + PK11_FreeSlot(slot); + g_free(passphrase_buff); + g_free(salt_buff); + return NULL; + } + + if (PK11_ExtractKeyValue(symkey) == SECSuccess) + symkey_data = PK11_GetKeyData(symkey); + + if (symkey_data == NULL || symkey_data->data == NULL) { + purple_debug_error("cipher-test", "NSS: Couldn't extract key " + "value: %d\n", PR_GetError()); + PK11_FreeSymKey(symkey); + SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); + PK11_FreeSlot(slot); + g_free(passphrase_buff); + g_free(salt_buff); + return NULL; + } + + if (symkey_data->len != out_len) { + purple_debug_error("cipher-test", "NSS: Invalid key length: %d " + "(should be %d)\n", symkey_data->len, out_len); + PK11_FreeSymKey(symkey); + SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); + PK11_FreeSlot(slot); + g_free(passphrase_buff); + g_free(salt_buff); + return NULL; + } + + ret = purple_base16_encode(symkey_data->data, symkey_data->len); + + PK11_FreeSymKey(symkey); + SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); + PK11_FreeSlot(slot); + g_free(passphrase_buff); + g_free(salt_buff); + return ret; +} + +#endif /* HAVE_NSS */ + +static void +cipher_test_pbkdf2(void) +{ + PurpleCipherContext *context; + int i = 0; + gboolean fail = FALSE; + + purple_debug_info("cipher-test", "Running PBKDF2 tests\n"); + + context = purple_cipher_context_new_by_name("pbkdf2", NULL); + + while (!fail && pbkdf2_tests[i].answer) { + pbkdf2_test *test = &pbkdf2_tests[i]; + gchar digest[2 * 32 + 1 + 10]; + gchar *digest_nss = NULL; + gboolean ret, skip_nss = FALSE; + + i++; + + purple_debug_info("cipher-test", "Test %02d:\n", i); + purple_debug_info("cipher-test", + "\tTesting '%s' with salt:'%s' hash:%s iter_count:%d \n", + test->passphrase, test->salt, test->hash, + test->iter_count); + + purple_cipher_context_set_option(context, "hash", (gpointer)test->hash); + purple_cipher_context_set_option(context, "iter_count", GUINT_TO_POINTER(test->iter_count)); + purple_cipher_context_set_option(context, "out_len", GUINT_TO_POINTER(test->out_len)); + purple_cipher_context_set_salt(context, (const guchar*)test->salt, test->salt ? strlen(test->salt): 0); + purple_cipher_context_set_key(context, (const guchar*)test->passphrase, strlen(test->passphrase)); + + ret = purple_cipher_context_digest_to_str(context, digest, sizeof(digest)); + purple_cipher_context_reset(context, NULL); + + if (!ret) { + purple_debug_info("cipher-test", "\tfailed\n"); + fail = TRUE; + continue; + } + + if (g_strcmp0(test->hash, "sha1") != 0) + skip_nss = TRUE; + if (test->out_len != 16 && test->out_len != 32) + skip_nss = TRUE; + +#ifdef HAVE_NSS + if (!skip_nss) { + digest_nss = cipher_pbkdf2_nss_sha1(test->passphrase, + test->salt, test->iter_count, test->out_len); + } +#else + skip_nss = TRUE; +#endif + + if (!ret) { + purple_debug_info("cipher-test", "\tnss test failed\n"); + fail = TRUE; + } + + purple_debug_info("cipher-test", "\tGot: %s\n", digest); + if (digest_nss) + purple_debug_info("cipher-test", "\tGot from NSS: %s\n", digest_nss); + purple_debug_info("cipher-test", "\tWanted: %s\n", test->answer); + + if (g_strcmp0(digest, test->answer) == 0 && + (skip_nss || g_strcmp0(digest, digest_nss) == 0)) { + purple_debug_info("cipher-test", "\tTest OK\n"); + } + else { + purple_debug_info("cipher-test", "\twrong answer\n"); + fail = TRUE; + } + } + + purple_cipher_context_destroy(context); + + if (fail) + purple_debug_info("cipher-test", "PBKDF2 tests FAILED\n\n"); + else + purple_debug_info("cipher-test", "PBKDF2 tests completed successfully\n\n"); +} + +/************************************************************************** + * AES stuff + **************************************************************************/ + +typedef struct { + const gchar *iv; + const gchar *key; + const gchar *plaintext; + const gchar *cipher; +} aes_test; + +aes_test aes_tests[] = { + { NULL, "000102030405060708090a0b0c0d0e0f", "plaintext", "152e5b950e5f28fafadee9e96fcc59c9" }, + { NULL, "000102030405060708090a0b0c0d0e0f", NULL, "954f64f2e4e86e9eee82d20216684899" }, + { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f", NULL, "35d14e6d3e3a279cf01e343e34e7ded3" }, + { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f", "plaintext", "19d1996e8c098cf3c94bba5dcf5bc57e" }, + { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f1011121314151617", "plaintext", "e8fba0deae94f63fe72ae9b8ef128aed" }, + { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "plaintext", "e2dc50f6c60b33ac3b5953b6503cb684" }, + { "01010101010101010101010101010101", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "W Szczebrzeszynie chrzaszcz brzmi w trzcinie i Szczebrzeszyn z tego slynie", "8fcc068964e3505f0c2fac61c24592e5c8a9582d3a3264217cf605e9fd1cb056e679e159c4ac3110e8ce6c76c6630d42658c566ba3750c0e6da385b3a9baaa8b3a735b4c1ecaacf58037c8c281e523d2" }, + { NULL, NULL, NULL, NULL } +}; + +static void +cipher_test_aes(void) +{ + PurpleCipherContext *context; + int i = 0; + gboolean fail = FALSE; + + purple_debug_info("cipher-test", "Running AES tests\n"); + + context = purple_cipher_context_new_by_name("aes", NULL); + if (context == NULL) { + purple_debug_error("cipher-test", "AES cipher not found\n"); + fail = TRUE; + } + + while (!fail && aes_tests[i].cipher) { + aes_test *test = &aes_tests[i]; + gsize key_size; + guchar *key; + guchar cipher[1024], decipher[1024]; + ssize_t cipher_len, decipher_len; + gchar *cipher_b16, *deciphered; + + purple_debug_info("cipher-test", "Test %02d:\n", i); + purple_debug_info("cipher-test", "\tTesting '%s' (%dbit) \n", + test->plaintext ? test->plaintext : "(null)", + strlen(test->key) * 8 / 2); + + i++; + + purple_cipher_context_reset(context, NULL); + + if (test->iv) { + gsize iv_size; + guchar *iv = purple_base16_decode(test->iv, &iv_size); + g_assert(iv != NULL); + purple_cipher_context_set_iv(context, iv, iv_size); + g_free(iv); + } + + key = purple_base16_decode(test->key, &key_size); + g_assert(key != NULL); + purple_cipher_context_set_key(context, key, key_size); + g_free(key); + + if (purple_cipher_context_get_key_size(context) != key_size) { + purple_debug_info("cipher-test", "\tinvalid key size\n"); + fail = TRUE; + continue; + } + + cipher_len = purple_cipher_context_encrypt(context, + (const guchar*)(test->plaintext ? test->plaintext : ""), + test->plaintext ? (strlen(test->plaintext) + 1) : 0, + cipher, sizeof(cipher)); + if (cipher_len < 0) { + purple_debug_info("cipher-test", "\tencryption failed\n"); + fail = TRUE; + continue; + } + + cipher_b16 = purple_base16_encode(cipher, cipher_len); + + purple_debug_info("cipher-test", "\tGot: %s\n", cipher_b16); + purple_debug_info("cipher-test", "\tWanted: %s\n", test->cipher); + + if (g_strcmp0(cipher_b16, test->cipher) != 0) { + purple_debug_info("cipher-test", + "\tencrypted data doesn't match\n"); + g_free(cipher_b16); + fail = TRUE; + continue; + } + g_free(cipher_b16); + + decipher_len = purple_cipher_context_decrypt(context, + cipher, cipher_len, decipher, sizeof(decipher)); + if (decipher_len < 0) { + purple_debug_info("cipher-test", "\tdecryption failed\n"); + fail = TRUE; + continue; + } + + deciphered = (decipher_len > 0) ? (gchar*)decipher : NULL; + + if (g_strcmp0(deciphered, test->plaintext) != 0) { + purple_debug_info("cipher-test", + "\tdecrypted data doesn't match\n"); + fail = TRUE; + continue; + } + + purple_debug_info("cipher-test", "\tTest OK\n"); + } + + if (context != NULL) + purple_cipher_context_destroy(context); + + if (fail) + purple_debug_info("cipher-test", "AES tests FAILED\n\n"); + else + purple_debug_info("cipher-test", "AES tests completed successfully\n\n"); +} + +/************************************************************************** * Plugin stuff **************************************************************************/ static gboolean @@ -238,6 +575,8 @@ cipher_test_md5(); cipher_test_sha1(); cipher_test_digest(); + cipher_test_pbkdf2(); + cipher_test_aes(); return TRUE; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/Makefile.am Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,76 @@ +EXTRA_DIST = \ + Makefile.mingw \ + wincred.c +CLEANFILES = + +plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) + +internalkeyring_la_CFLAGS = $(AM_CPPFLAGS) +internalkeyring_la_LDFLAGS = -module -avoid-version +internalkeyring_la_SOURCES = internalkeyring.c +internalkeyring_la_LIBADD = $(GLIB_LIBS) + +if ENABLE_SECRETSERVICE + +secretservice_la_CFLAGS = $(AM_CPPFLAGS) $(SECRETSERVICE_CFLAGS) +secretservice_la_LDFLAGS = -module -avoid-version +secretservice_la_SOURCES = secretservice.c +secretservice_la_LIBADD = $(GLIB_LIBS) $(SECRETSERVICE_LIBS) + +endif + +if ENABLE_GNOMEKEYRING + +gnomekeyring_la_CFLAGS = $(AM_CPPFLAGS) $(GNOMEKEYRING_CFLAGS) +gnomekeyring_la_LDFLAGS = -module -avoid-version +gnomekeyring_la_SOURCES = gnomekeyring.c +gnomekeyring_la_LIBADD = $(GLIB_LIBS) $(GNOMEKEYRING_LIBS) + +endif + +if ENABLE_KWALLET + +kwallet_la_CXXFLAGS = $(KWALLET_CXXFLAGS) $(QT4_CFLAGS) +kwallet_la_LDFLAGS = -module -avoid-version +kwallet_la_SOURCES = kwallet.cpp +kwallet_la_BUILTSOURCES = kwallet.moc +kwallet_la_LIBADD = $(GLIB_LIBS) $(KWALLET_LIBS) $(QT4_LIBS) + +kwallet.cpp: kwallet.moc + +kwallet.moc: + $(AM_V_GEN)$(MOC) $(kwallet_la_CXXFLAGS) -i kwallet.cpp -o $@ + +CLEANFILES += kwallet.moc + +endif + +if PLUGINS + +plugin_LTLIBRARIES = \ + internalkeyring.la + +if ENABLE_SECRETSERVICE +plugin_LTLIBRARIES += \ + secretservice.la +endif + +if ENABLE_GNOMEKEYRING +plugin_LTLIBRARIES += \ + gnomekeyring.la +endif + +if ENABLE_KWALLET +plugin_LTLIBRARIES += \ + kwallet.la +endif + +endif + +#XXX: that might be done better than adding DEBUG_CPPFLAGS to all objects (not only C++ ones) +AM_CPPFLAGS = \ + -I$(top_srcdir)/libpurple \ + -I$(top_builddir)/libpurple \ + $(GLIB_CFLAGS) \ + $(DEBUG_CPPFLAGS) \ + $(PLUGIN_CFLAGS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/Makefile.mingw Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,81 @@ +# +# Makefile.mingw +# +# Description: Makefile for keyring plugins. +# + +PIDGIN_TREE_TOP := ../../.. +include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak + +## +## VARIABLE DEFINITIONS +## +TARGET_INTERNAL = internalkeyring +TARGET_WINCRED = wincred + +## +## INCLUDE PATHS +## +INCLUDE_PATHS += \ + -I. \ + -I$(GTK_TOP)/include \ + -I$(GTK_TOP)/include/glib-2.0 \ + -I$(GTK_TOP)/lib/glib-2.0/include \ + -I$(PURPLE_TOP) \ + -I$(PURPLE_TOP)/win32 \ + -I$(PIDGIN_TREE_TOP) + +LIB_PATHS += \ + -L$(GTK_TOP)/lib \ + -L$(PURPLE_TOP) + +## +## SOURCES, OBJECTS +## +C_SRC_INTERNAL = internalkeyring.c +OBJECTS_INTERNAL = $(C_SRC_INTERNAL:%.c=%.o) + +C_SRC_WINCRED = wincred.c +OBJECTS_WINCRED = $(C_SRC_WINCRED:%.c=%.o) + +## +## LIBRARIES +## +LIBS = \ + -lglib-2.0 \ + -lws2_32 \ + -lintl \ + -lpurple + +include $(PIDGIN_COMMON_RULES) + +## +## TARGET DEFINITIONS +## +.PHONY: all install clean + +all: $(TARGET_INTERNAL).dll $(TARGET_WINCRED).dll + +install: all $(PURPLE_INSTALL_PLUGINS_DIR) $(PURPLE_INSTALL_DIR) + cp $(TARGET_INTERNAL).dll $(PURPLE_INSTALL_PLUGINS_DIR) + cp $(TARGET_WINCRED).dll $(PURPLE_INSTALL_PLUGINS_DIR) + +$(OBJECTS_INTERNAL): $(PURPLE_CONFIG_H) + +## +## BUILD DLL +## +$(TARGET_INTERNAL).dll: $(PURPLE_DLL) $(OBJECTS_INTERNAL) + $(CC) -shared $(OBJECTS_INTERNAL) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_INTERNAL).dll + +$(TARGET_WINCRED).dll: $(PURPLE_DLL) $(OBJECTS_WINCRED) + $(CC) -shared $(OBJECTS_WINCRED) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_WINCRED).dll + +## +## CLEAN RULES +## +clean: + rm -f $(OBJECTS_INTERNAL) $(TARGET_INTERNAL).dll + rm -f $(OBJECTS_WINCRED) $(TARGET_WINCRED).dll + +include $(PIDGIN_COMMON_TARGETS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/gnomekeyring.c Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,454 @@ +/** + * @file gnomekeyring.c Gnome keyring password storage + * @ingroup plugins + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" +#include "account.h" +#include "debug.h" +#include "keyring.h" +#include "plugin.h" +#include "version.h" + +#include <gnome-keyring.h> +#include <gnome-keyring-memory.h> + +#define GNOMEKEYRING_NAME N_("GNOME Keyring") +#define GNOMEKEYRING_DESCRIPTION N_("This plugin will store passwords in " \ + "GNOME Keyring.") +#define GNOMEKEYRING_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)" +#define GNOMEKEYRING_ID "keyring-gnomekeyring" + +static PurpleKeyring *keyring_handler = NULL; +static GList *request_queue = NULL; +static gpointer current_request = NULL; + +typedef struct +{ + enum + { + GNOMEKEYRING_REQUEST_READ, + GNOMEKEYRING_REQUEST_SAVE + } type; + PurpleAccount *account; + gchar *password; + union + { + PurpleKeyringReadCallback read; + PurpleKeyringSaveCallback save; + } cb; + gpointer cb_data; + gboolean handled; +} gnomekeyring_request; + +static void gnomekeyring_cancel_queue(void); +static void gnomekeyring_process_queue(void); + +static void gnomekeyring_request_free(gnomekeyring_request *req) +{ + if (req->password != NULL) { + memset(req->password, 0, strlen(req->password)); + gnome_keyring_memory_free(req->password); + } + g_free(req); +} + +static void +gnomekeyring_enqueue(gnomekeyring_request *req) +{ + request_queue = g_list_append(request_queue, req); + gnomekeyring_process_queue(); +} + +static void +gnomekeyring_read_cb(GnomeKeyringResult result, const char *password, + gpointer _req) +{ + gnomekeyring_request *req = _req; + PurpleAccount *account; + GError *error = NULL; + + g_return_if_fail(req != NULL); + + current_request = NULL; + account = req->account; + + if (result == GNOME_KEYRING_RESULT_OK) { + error = NULL; + } else if (result == GNOME_KEYRING_RESULT_NO_MATCH) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOPASSWORD, + _("No password found for account.")); + } else if (result == GNOME_KEYRING_RESULT_DENIED || + result == GNOME_KEYRING_RESULT_CANCELLED) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_ACCESSDENIED, + _("Access denied.")); + gnomekeyring_cancel_queue(); + } else if (result == GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON || + GNOME_KEYRING_RESULT_IO_ERROR) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Communication with GNOME Keyring failed.")); + } else { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Unknown error (code: %d)."), result); + } + + if (error == NULL && password == NULL) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Unknown error (password empty).")); + } + + if (error == NULL) { + purple_debug_misc("keyring-gnome", + "Got password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } else if (result == GNOME_KEYRING_RESULT_NO_MATCH) { + if (purple_debug_is_verbose()) { + purple_debug_info("keyring-gnome", + "Password for account %s (%s) isn't stored.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } + } else { + password = NULL; + purple_debug_warning("keyring-gnome", "Failed to read " + "password for account %s (%s), code: %d.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account), + result); + } + + if (req->cb.read != NULL) + req->cb.read(account, password, error, req->cb_data); + req->handled = TRUE; + + if (error) + g_error_free(error); + + gnomekeyring_process_queue(); +} + +static void +gnomekeyring_save_cb(GnomeKeyringResult result, gpointer _req) +{ + gnomekeyring_request *req = _req; + PurpleAccount *account; + GError *error = NULL; + gboolean already_removed = FALSE; + + g_return_if_fail(req != NULL); + + current_request = NULL; + account = req->account; + + if (result == GNOME_KEYRING_RESULT_OK) { + error = NULL; + } else if (result == GNOME_KEYRING_RESULT_NO_MATCH && + req->password == NULL) { + error = NULL; + already_removed = TRUE; + } else if (result == GNOME_KEYRING_RESULT_DENIED || + result == GNOME_KEYRING_RESULT_CANCELLED) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_ACCESSDENIED, + _("Access denied.")); + gnomekeyring_cancel_queue(); + } else if (result == GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON || + GNOME_KEYRING_RESULT_IO_ERROR) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Communication with GNOME Keyring failed.")); + } else { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Unknown error (code: %d)."), result); + } + + if (already_removed) { + /* no operation */ + } else if (error == NULL) { + purple_debug_misc("keyring-gnome", + "Password %s for account %s (%s).\n", + req->password ? "saved" : "removed", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } else { + purple_debug_warning("keyring-gnome", "Failed updating " + "password for account %s (%s), code: %d.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account), + result); + } + + if (req->cb.save != NULL) + req->cb.save(account, error, req->cb_data); + req->handled = TRUE; + + if (error) + g_error_free(error); + + gnomekeyring_process_queue(); +} + +static void +gnomekeyring_request_cancel(gpointer _req) +{ + gnomekeyring_request *req = _req; + PurpleAccount *account; + GError *error; + + g_return_if_fail(req != NULL); + + if (req->handled) { + gnomekeyring_request_free(req); + return; + } + + purple_debug_warning("keyring-gnome", + "operation cancelled (%d %s:%s)\n", req->type, + purple_account_get_protocol_id(req->account), + purple_account_get_username(req->account)); + + account = req->account; + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_CANCELLED, + _("Operation cancelled.")); + if (req->type == GNOMEKEYRING_REQUEST_READ && req->cb.read) + req->cb.read(account, NULL, error, req->cb_data); + if (req->type == GNOMEKEYRING_REQUEST_SAVE && req->cb.save) + req->cb.save(account, error, req->cb_data); + g_error_free(error); + + gnomekeyring_request_free(req); + gnomekeyring_process_queue(); +} + +static void +gnomekeyring_cancel_queue(void) +{ + GList *cancel_list = request_queue; + + if (request_queue == NULL) + return; + + purple_debug_info("gnome-keyring", "cancelling all pending requests\n"); + request_queue = NULL; + + g_list_free_full(cancel_list, gnomekeyring_request_cancel); +} + +static void +gnomekeyring_process_queue(void) +{ + gnomekeyring_request *req; + PurpleAccount *account; + GList *first; + + if (request_queue == NULL) + return; + + if (current_request) { + if (purple_debug_is_verbose()) + purple_debug_misc("keyring-gnome", "busy...\n"); + return; + } + + first = g_list_first(request_queue); + req = first->data; + request_queue = g_list_delete_link(request_queue, first); + account = req->account; + + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring-gnome", + "%s password for account %s (%s)\n", + req->type == GNOMEKEYRING_REQUEST_READ ? "reading" : + (req->password == NULL ? "removing" : "updating"), + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } + + if (req->type == GNOMEKEYRING_REQUEST_READ) { + current_request = gnome_keyring_find_password( + GNOME_KEYRING_NETWORK_PASSWORD, gnomekeyring_read_cb, + req, gnomekeyring_request_cancel, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + } else if (req->type == GNOMEKEYRING_REQUEST_SAVE && + req->password != NULL) { + gchar *display_name = g_strdup_printf( + _("Pidgin IM password for account %s"), + purple_account_get_username(account)); + current_request = gnome_keyring_store_password( + GNOME_KEYRING_NETWORK_PASSWORD, GNOME_KEYRING_DEFAULT, + display_name, req->password, gnomekeyring_save_cb, req, + gnomekeyring_request_cancel, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + g_free(display_name); + } else if (req->type == GNOMEKEYRING_REQUEST_SAVE && + req->password == NULL) { + current_request = gnome_keyring_delete_password( + GNOME_KEYRING_NETWORK_PASSWORD, gnomekeyring_save_cb, + req, gnomekeyring_request_cancel, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + } else { + g_return_if_reached(); + } +} + +static void +gnomekeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb, + gpointer data) +{ + gnomekeyring_request *req; + + g_return_if_fail(account != NULL); + + req = g_new0(gnomekeyring_request, 1); + req->type = GNOMEKEYRING_REQUEST_READ; + req->account = account; + req->cb.read = cb; + req->cb_data = data; + + gnomekeyring_enqueue(req); +} + +static void +gnomekeyring_save(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data) +{ + gnomekeyring_request *req; + + g_return_if_fail(account != NULL); + + req = g_new0(gnomekeyring_request, 1); + req->type = GNOMEKEYRING_REQUEST_SAVE; + req->account = account; + req->password = gnome_keyring_memory_strdup(password); + req->cb.save = cb; + req->cb_data = data; + + gnomekeyring_enqueue(req); +} + +static void +gnomekeyring_cancel(void) +{ + gnomekeyring_cancel_queue(); + if (current_request) { + gnome_keyring_cancel_request(current_request); + while (g_main_iteration(FALSE)); + } +} + +static void +gnomekeyring_close(void) +{ + gnomekeyring_cancel(); +} + +static gboolean +gnomekeyring_load(PurplePlugin *plugin) +{ + if (!gnome_keyring_is_available()) { + purple_debug_info("keyring-gnome", "GNOME Keyring service is " + "disabled\n"); + return FALSE; + } + + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, GNOMEKEYRING_NAME); + purple_keyring_set_id(keyring_handler, GNOMEKEYRING_ID); + purple_keyring_set_read_password(keyring_handler, gnomekeyring_read); + purple_keyring_set_save_password(keyring_handler, gnomekeyring_save); + purple_keyring_set_cancel_requests(keyring_handler, + gnomekeyring_cancel); + purple_keyring_set_close_keyring(keyring_handler, gnomekeyring_close); + + purple_keyring_register(keyring_handler); + + return TRUE; +} + +static gboolean +gnomekeyring_unload(PurplePlugin *plugin) +{ + if (purple_keyring_get_inuse() == keyring_handler) { + purple_debug_warning("keyring-gnome", + "keyring in use, cannot unload\n"); + return FALSE; + } + + gnomekeyring_close(); + + purple_keyring_unregister(keyring_handler); + purple_keyring_free(keyring_handler); + keyring_handler = NULL; + + return TRUE; +} + +PurplePluginInfo plugininfo = +{ + PURPLE_PLUGIN_MAGIC, /* magic */ + PURPLE_MAJOR_VERSION, /* major_version */ + PURPLE_MINOR_VERSION, /* minor_version */ + PURPLE_PLUGIN_STANDARD, /* type */ + NULL, /* ui_requirement */ + PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + GNOMEKEYRING_ID, /* id */ + GNOMEKEYRING_NAME, /* name */ + DISPLAY_VERSION, /* version */ + "GNOME Keyring Plugin", /* summary */ + GNOMEKEYRING_DESCRIPTION, /* description */ + GNOMEKEYRING_AUTHOR, /* author */ + PURPLE_WEBSITE, /* homepage */ + gnomekeyring_load, /* load */ + gnomekeyring_unload, /* unload */ + NULL, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, NULL, NULL, NULL /* padding */ +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(gnome_keyring, init_plugin, plugininfo)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/internalkeyring.c Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,1043 @@ +/** + * @file internalkeyring.c internal keyring + * @ingroup plugins + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" +#include "account.h" +#include "cipher.h" +#include "debug.h" +#include "keyring.h" +#include "plugin.h" +#include "version.h" + +#define INTKEYRING_NAME N_("Internal keyring") +#define INTKEYRING_DESCRIPTION N_("This plugin provides the default password " \ + "storage behaviour for libpurple.") +#define INTKEYRING_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)" +#define INTKEYRING_ID PURPLE_DEFAULT_KEYRING + +#define INTKEYRING_VERIFY_STR "[verification-string]" +#define INTKEYRING_PBKDF2_ITERATIONS 10000 +#define INTKEYRING_PBKDF2_ITERATIONS_MIN 1000 +#define INTKEYRING_PBKDF2_ITERATIONS_MAX 1000000000 +#define INTKEYRING_KEY_LEN (256/8) +#define INTKEYRING_ENCRYPT_BUFF_LEN 1000 +#define INTKEYRING_ENCRYPTED_MIN_LEN 50 +#define INTKEYRING_ENCRYPTION_METHOD "pbkdf2-sha256-aes256" + +#define INTKEYRING_PREFS "/plugins/keyrings/internal/" + +/* win32 build defines such macro to override read() routine */ +#undef read + +typedef struct +{ + enum + { + INTKEYRING_REQUEST_READ, + INTKEYRING_REQUEST_SAVE + } type; + PurpleAccount *account; + gchar *password; + union + { + PurpleKeyringReadCallback read; + PurpleKeyringSaveCallback save; + } cb; + gpointer cb_data; +} intkeyring_request; + +typedef struct +{ + guchar *data; + size_t len; +} intkeyring_buff_t; + +static intkeyring_buff_t *intkeyring_key; +static GHashTable *intkeyring_passwords = NULL; +static GHashTable *intkeyring_ciphertexts = NULL; + +static gboolean intkeyring_opened = FALSE; +static gboolean intkeyring_unlocked = FALSE; + +static GList *intkeyring_pending_requests = NULL; +static void *intkeyring_masterpw_uirequest = NULL; + +static PurpleKeyring *keyring_handler = NULL; + +static void +intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb, + gpointer data); +static void +intkeyring_save(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data); +static void +intkeyring_reencrypt_passwords(void); +static void +intkeyring_unlock(const gchar *message); + +static void +intkeyring_request_free(intkeyring_request *req) +{ + g_return_if_fail(req != NULL); + + purple_str_wipe(req->password); + g_free(req); +} + +static intkeyring_buff_t * +intkeyring_buff_new(guchar *data, size_t len) +{ + intkeyring_buff_t *ret = g_new(intkeyring_buff_t, 1); + + ret->data = data; + ret->len = len; + + return ret; +} + +static void +intkeyring_buff_free(intkeyring_buff_t *buff) +{ + if (buff == NULL) + return; + + memset(buff->data, 0, buff->len); + g_free(buff->data); + g_free(buff); +} + +static intkeyring_buff_t * +intkeyring_buff_from_base64(const gchar *base64) +{ + guchar *data; + gsize len; + + data = purple_base64_decode(base64, &len); + + return intkeyring_buff_new(data, len); +} + +/************************************************************************/ +/* Generic encryption stuff */ +/************************************************************************/ + +static intkeyring_buff_t * +intkeyring_derive_key(const gchar *passphrase, intkeyring_buff_t *salt) +{ + PurpleCipherContext *context; + gboolean succ; + intkeyring_buff_t *ret; + + g_return_val_if_fail(passphrase != NULL, NULL); + + context = purple_cipher_context_new_by_name("pbkdf2", NULL); + g_return_val_if_fail(context != NULL, NULL); + + purple_cipher_context_set_option(context, "hash", "sha256"); + purple_cipher_context_set_option(context, "iter_count", + GUINT_TO_POINTER(purple_prefs_get_int(INTKEYRING_PREFS + "pbkdf2_iterations"))); + purple_cipher_context_set_option(context, "out_len", GUINT_TO_POINTER( + INTKEYRING_KEY_LEN)); + purple_cipher_context_set_salt(context, salt->data, salt->len); + purple_cipher_context_set_key(context, (const guchar*)passphrase, + strlen(passphrase)); + + ret = intkeyring_buff_new(g_new(guchar, INTKEYRING_KEY_LEN), + INTKEYRING_KEY_LEN); + succ = purple_cipher_context_digest(context, ret->data, ret->len); + + purple_cipher_context_destroy(context); + + if (!succ) { + intkeyring_buff_free(ret); + return NULL; + } + + return ret; +} + +static intkeyring_buff_t * +intkeyring_gen_salt(size_t len) +{ + intkeyring_buff_t *ret; + size_t filled = 0; + + g_return_val_if_fail(len > 0, NULL); + + ret = intkeyring_buff_new(g_new(guchar, len), len); + + while (filled < len) { + guint32 r = g_random_int(); + int i; + + for (i = 0; i < 4; i++) { + ret->data[filled++] = r & 0xFF; + if (filled >= len) + break; + r >>= 8; + } + } + + return ret; +} + +/** + * Encrypts a plaintext using the specified key. + * + * Random IV will be generated and stored with ciphertext. + * + * Encryption scheme: + * [ IV ] ++ AES( [ plaintext ] ++ [ min length padding ] ++ + * [ control string ] ++ [ pkcs7 padding ] ) + * where: + * IV: Random, 128bit IV. + * plaintext: The plaintext. + * min length padding: The padding used to hide the rough length of short + * plaintexts, may have a length of 0. + * control string: Constant string, verifies corectness of decryption. + * pkcs7 padding: The padding used to determine total length of encrypted + * content (also provides some verification). + * + * @param key The AES key. + * @param str The NUL-terminated plaintext. + * @return The ciphertext with IV, encoded as base64. Must be g_free'd. + */ +static gchar * +intkeyring_encrypt(intkeyring_buff_t *key, const gchar *str) +{ + PurpleCipherContext *context; + intkeyring_buff_t *iv; + guchar plaintext[INTKEYRING_ENCRYPT_BUFF_LEN]; + size_t plaintext_len, text_len, verify_len; + guchar encrypted_raw[INTKEYRING_ENCRYPT_BUFF_LEN]; + ssize_t encrypted_size; + + g_return_val_if_fail(key != NULL, NULL); + g_return_val_if_fail(str != NULL, NULL); + + text_len = strlen(str); + verify_len = strlen(INTKEYRING_VERIFY_STR); + plaintext_len = INTKEYRING_ENCRYPTED_MIN_LEN; + if (plaintext_len < text_len) + plaintext_len = text_len; + + g_return_val_if_fail(plaintext_len + verify_len <= sizeof(plaintext), + NULL); + + context = purple_cipher_context_new_by_name("aes", NULL); + g_return_val_if_fail(context != NULL, NULL); + + memset(plaintext, 0, plaintext_len); + memcpy(plaintext, str, text_len); + memcpy(plaintext + plaintext_len, INTKEYRING_VERIFY_STR, verify_len); + plaintext_len += verify_len; + + iv = intkeyring_gen_salt(purple_cipher_context_get_block_size(context)); + purple_cipher_context_set_iv(context, iv->data, iv->len); + purple_cipher_context_set_key(context, key->data, key->len); + purple_cipher_context_set_batch_mode(context, + PURPLE_CIPHER_BATCH_MODE_CBC); + + memcpy(encrypted_raw, iv->data, iv->len); + + encrypted_size = purple_cipher_context_encrypt(context, + plaintext, plaintext_len, encrypted_raw + iv->len, + sizeof(encrypted_raw) - iv->len); + encrypted_size += iv->len; + + memset(plaintext, 0, plaintext_len); + intkeyring_buff_free(iv); + purple_cipher_context_destroy(context); + + if (encrypted_size < 0) + return NULL; + + return purple_base64_encode(encrypted_raw, encrypted_size); + +} + +static gchar * +intkeyring_decrypt(intkeyring_buff_t *key, const gchar *str) +{ + PurpleCipherContext *context; + guchar *encrypted_raw; + gsize encrypted_size; + size_t iv_len, verify_len, text_len; + guchar plaintext[INTKEYRING_ENCRYPT_BUFF_LEN]; + const gchar *verify_str = NULL; + ssize_t plaintext_len; + gchar *ret; + + g_return_val_if_fail(key != NULL, NULL); + g_return_val_if_fail(str != NULL, NULL); + + context = purple_cipher_context_new_by_name("aes", NULL); + g_return_val_if_fail(context != NULL, NULL); + + encrypted_raw = purple_base64_decode(str, &encrypted_size); + g_return_val_if_fail(encrypted_raw != NULL, NULL); + + iv_len = purple_cipher_context_get_block_size(context); + if (encrypted_size < iv_len) { + g_free(encrypted_raw); + return NULL; + } + + purple_cipher_context_set_iv(context, encrypted_raw, iv_len); + purple_cipher_context_set_key(context, key->data, key->len); + purple_cipher_context_set_batch_mode(context, + PURPLE_CIPHER_BATCH_MODE_CBC); + + plaintext_len = purple_cipher_context_decrypt(context, + encrypted_raw + iv_len, encrypted_size - iv_len, + plaintext, sizeof(plaintext)); + + g_free(encrypted_raw); + purple_cipher_context_destroy(context); + + verify_len = strlen(INTKEYRING_VERIFY_STR); + /* Don't remove the len > 0 check! */ + if (plaintext_len > 0 && plaintext_len > verify_len && + plaintext[plaintext_len] == '\0') + { + verify_str = (gchar*)plaintext + plaintext_len - verify_len; + } + + if (g_strcmp0(verify_str, INTKEYRING_VERIFY_STR) != 0) { + purple_debug_warning("keyring-internal", + "Verification failed on decryption\n"); + memset(plaintext, 0, sizeof(plaintext)); + return NULL; + } + + text_len = plaintext_len - verify_len; + ret = g_new(gchar, text_len + 1); + memcpy(ret, plaintext, text_len); + memset(plaintext, 0, plaintext_len); + ret[text_len] = '\0'; + + return ret; +} + +/************************************************************************/ +/* Password encryption */ +/************************************************************************/ + +static gboolean +intkeyring_change_masterpw(const gchar *new_password) +{ + intkeyring_buff_t *salt, *key; + gchar *verifier = NULL, *salt_b64 = NULL; + int old_iter; + gboolean succ = TRUE;; + + g_return_val_if_fail(intkeyring_unlocked, FALSE); + + old_iter = purple_prefs_get_int(INTKEYRING_PREFS "pbkdf2_iterations"); + purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_iterations", + purple_prefs_get_int(INTKEYRING_PREFS + "pbkdf2_desired_iterations")); + + salt = intkeyring_gen_salt(32); + key = intkeyring_derive_key(new_password, salt); + + if (salt && key && key->len == INTKEYRING_KEY_LEN) { + /* In fact, verify str will be concatenated twice before + * encryption (it's used as a suffix in encryption routine), + * but it's not a problem. + */ + verifier = intkeyring_encrypt(key, INTKEYRING_VERIFY_STR); + salt_b64 = purple_base64_encode(salt->data, salt->len); + } + + if (!verifier || !salt_b64) { + purple_debug_error("keyring-internal", "Failed to change " + "master password\n"); + succ = FALSE; + purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_iterations", + old_iter); + } else { + purple_prefs_set_string(INTKEYRING_PREFS "pbkdf2_salt", + salt_b64); + purple_prefs_set_string(INTKEYRING_PREFS "key_verifier", + verifier); + + intkeyring_buff_free(intkeyring_key); + intkeyring_key = key; + key = NULL; + + intkeyring_reencrypt_passwords(); + + purple_signal_emit(purple_keyring_get_handle(), + "password-migration", NULL); + } + + g_free(salt_b64); + g_free(verifier); + intkeyring_buff_free(salt); + intkeyring_buff_free(key); + + return succ; +} + +static void +intkeyring_process_queue(void) +{ + GList *requests, *it; + gboolean open = intkeyring_unlocked; + + requests = g_list_first(intkeyring_pending_requests); + intkeyring_pending_requests = NULL; + + for (it = requests; it != NULL; it = g_list_next(it)) + { + intkeyring_request *req = it->data; + + if (open && req->type == INTKEYRING_REQUEST_READ) { + intkeyring_read(req->account, req->cb.read, + req->cb_data); + } else if (open && req->type == INTKEYRING_REQUEST_SAVE) { + intkeyring_save(req->account, req->password, + req->cb.save, req->cb_data); + } else if (open) + g_assert_not_reached(); + else if (req->cb.read != NULL /* || req->cb.write != NULL */ ) { + GError *error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_CANCELLED, + _("Operation cancelled.")); + if (req->type == INTKEYRING_REQUEST_READ) { + req->cb.read(req->account, NULL, error, + req->cb_data); + } else if (req->type == INTKEYRING_REQUEST_SAVE) + req->cb.save(req->account, error, req->cb_data); + else + g_assert_not_reached(); + g_error_free(error); + } + + intkeyring_request_free(req); + } + g_list_free(requests); +} + +static void +intkeyring_decrypt_password(PurpleAccount *account, const gchar *ciphertext) +{ + gchar *plaintext; + + plaintext = intkeyring_decrypt(intkeyring_key, ciphertext); + if (plaintext == NULL) { + purple_debug_warning("keyring-internal", + "Failed to decrypt a password\n"); + return; + } + + g_hash_table_replace(intkeyring_passwords, account, plaintext); +} + +static void +intkeyring_encrypt_password_if_needed(PurpleAccount *account) +{ + const gchar *plaintext; + gchar *ciphertext; + + ciphertext = g_hash_table_lookup(intkeyring_ciphertexts, account); + if (ciphertext != NULL) + return; + + plaintext = g_hash_table_lookup(intkeyring_passwords, account); + if (plaintext == NULL) + return; + + ciphertext = intkeyring_encrypt(intkeyring_key, plaintext); + g_return_if_fail(ciphertext != NULL); + + g_hash_table_replace(intkeyring_ciphertexts, account, ciphertext); +} + +static void +intkeyring_encrypt_passwords_if_needed_it(gpointer account, gpointer plaintext, + gpointer _unused) +{ + intkeyring_encrypt_password_if_needed(account); +} + +static void +intkeyring_reencrypt_passwords(void) +{ + g_hash_table_remove_all(intkeyring_ciphertexts); + g_hash_table_foreach(intkeyring_passwords, + intkeyring_encrypt_passwords_if_needed_it, NULL); +} + +static void +intkeyring_unlock_decrypt(gpointer account, gpointer ciphertext, + gpointer _unused) +{ + intkeyring_decrypt_password(account, ciphertext); +} + +/************************************************************************/ +/* Opening and unlocking keyring */ +/************************************************************************/ + +static void +intkeyring_unlock_ok(gpointer _unused, + PurpleRequestFields *fields) +{ + const gchar *masterpw; + gchar *verifier; + intkeyring_buff_t *salt, *key; + + intkeyring_masterpw_uirequest = NULL; + + if (g_strcmp0(purple_prefs_get_string(INTKEYRING_PREFS + "encryption_method"), INTKEYRING_ENCRYPTION_METHOD) != 0) { + purple_notify_error(NULL, + _("Unlocking internal keyring"), + _("Selected encryption method is not supported."), + _("Most probably, your passwords were encrypted with " + "newer Pidgin/libpurple version, please update.")); + return; + } + + masterpw = purple_request_fields_get_string(fields, "password"); + + if (masterpw == NULL || masterpw[0] == '\0') { + intkeyring_unlock(_("No password entered.")); + return; + } + + salt = intkeyring_buff_from_base64(purple_prefs_get_string( + INTKEYRING_PREFS "pbkdf2_salt")); + key = intkeyring_derive_key(masterpw, salt); + intkeyring_buff_free(salt); + + verifier = intkeyring_decrypt(key, purple_prefs_get_string( + INTKEYRING_PREFS "key_verifier")); + + if (g_strcmp0(verifier, INTKEYRING_VERIFY_STR) != 0) { + g_free(verifier); + intkeyring_buff_free(key); + intkeyring_unlock(_("Invalid master password entered, " + "try again.")); + return; + } + + g_free(verifier); + intkeyring_key = key; + intkeyring_unlocked = TRUE; + + g_hash_table_foreach(intkeyring_ciphertexts, + intkeyring_unlock_decrypt, NULL); + + intkeyring_process_queue(); +} + +static void +intkeyring_unlock_cancel(gpointer _unused, + PurpleRequestFields *fields) +{ + intkeyring_masterpw_uirequest = NULL; + intkeyring_process_queue(); +} + +static void +intkeyring_unlock(const gchar *message) +{ + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; + const gchar *primary_msg, *secondary_msg = NULL; + + if (intkeyring_unlocked || intkeyring_masterpw_uirequest != NULL) + return; + + if (!purple_prefs_get_bool(INTKEYRING_PREFS "encrypt_passwords")) { + intkeyring_unlocked = TRUE; + intkeyring_process_queue(); + return; + } + + fields = purple_request_fields_new(); + group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, group); + + field = purple_request_field_string_new("password", + _("Master password"), "", FALSE); + purple_request_field_string_set_masked(field, TRUE); + purple_request_field_group_add_field(group, field); + + primary_msg = _("Please, enter master password"); + if (message) { + secondary_msg = primary_msg; + primary_msg = message; + } + + intkeyring_masterpw_uirequest = purple_request_fields(NULL, + _("Unlocking internal keyring"), + primary_msg, secondary_msg, fields, + _("OK"), G_CALLBACK(intkeyring_unlock_ok), + _("Cancel"), G_CALLBACK(intkeyring_unlock_cancel), + NULL, NULL, NULL, NULL); +} + +static void +intkeyring_open(void) +{ + if (intkeyring_opened) + return; + intkeyring_opened = TRUE; + + intkeyring_passwords = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify)purple_str_wipe); + intkeyring_ciphertexts = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, g_free); +} + +/************************************************************************/ +/* Keyring interface implementation */ +/************************************************************************/ + +static void +intkeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb, + gpointer data) +{ + const char *password; + GError *error; + + intkeyring_open(); + + if (!intkeyring_unlocked && g_hash_table_lookup(intkeyring_ciphertexts, + account) != NULL) + { + intkeyring_request *req = g_new0(intkeyring_request, 1); + + req->type = INTKEYRING_REQUEST_READ; + req->account = account; + req->cb.read = cb; + req->cb_data = data; + intkeyring_pending_requests = + g_list_append(intkeyring_pending_requests, req); + + intkeyring_unlock(NULL); + return; + } + + password = g_hash_table_lookup(intkeyring_passwords, account); + + if (password != NULL) { + purple_debug_misc("keyring-internal", + "Got password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + if (cb != NULL) + cb(account, password, NULL, data); + } else { + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring-internal", + "No password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOPASSWORD, + _("Password not found.")); + if (cb != NULL) + cb(account, NULL, error, data); + g_error_free(error); + } +} + +static void +intkeyring_save(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data) +{ + void *old_password; + + intkeyring_open(); + + if (!intkeyring_unlocked) { + intkeyring_request *req; + + if (password == NULL) { + g_hash_table_remove(intkeyring_ciphertexts, account); + g_hash_table_remove(intkeyring_passwords, account); + if (cb) + cb(account, NULL, data); + return; + } + + req = g_new0(intkeyring_request, 1); + req->type = INTKEYRING_REQUEST_SAVE; + req->account = account; + req->password = g_strdup(password); + req->cb.save = cb; + req->cb_data = data; + intkeyring_pending_requests = + g_list_append(intkeyring_pending_requests, req); + + intkeyring_unlock(NULL); + return; + } + + g_hash_table_remove(intkeyring_ciphertexts, account); + + old_password = g_hash_table_lookup(intkeyring_passwords, account); + + if (password == NULL) + g_hash_table_remove(intkeyring_passwords, account); + else { + g_hash_table_replace(intkeyring_passwords, account, + g_strdup(password)); + } + + intkeyring_encrypt_password_if_needed(account); + + if (!(password == NULL && old_password == NULL)) { + purple_debug_misc("keyring-internal", + "Password %s for account %s (%s).\n", + (password == NULL ? "removed" : (old_password == NULL ? + "saved" : "updated")), + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } else if (purple_debug_is_verbose()) { + purple_debug_misc("keyring-internal", + "Password for account %s (%s) was already removed.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } + + if (cb != NULL) + cb(account, NULL, data); +} + +static void +intkeyring_close(void) +{ + if (!intkeyring_opened) + return; + intkeyring_opened = FALSE; + intkeyring_unlocked = FALSE; + + if (intkeyring_masterpw_uirequest) { + purple_request_close(PURPLE_REQUEST_FIELDS, + intkeyring_masterpw_uirequest); + } + g_warn_if_fail(intkeyring_masterpw_uirequest == NULL); + g_warn_if_fail(intkeyring_pending_requests == NULL); + + intkeyring_buff_free(intkeyring_key); + intkeyring_key = NULL; + g_hash_table_destroy(intkeyring_passwords); + intkeyring_passwords = NULL; + g_hash_table_destroy(intkeyring_ciphertexts); + intkeyring_ciphertexts = NULL; +} + +static gboolean +intkeyring_import_password(PurpleAccount *account, const char *mode, + const char *data, GError **error) +{ + g_return_val_if_fail(account != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); + + intkeyring_open(); + + if (mode == NULL) + mode = "cleartext"; + + if (g_strcmp0(mode, "cleartext") == 0) { + g_hash_table_replace(intkeyring_passwords, account, + g_strdup(data)); + return TRUE; + } else if (g_strcmp0(mode, "ciphertext") == 0) { + if (intkeyring_unlocked) + intkeyring_decrypt_password(account, data); + else { + g_hash_table_replace(intkeyring_ciphertexts, account, + g_strdup(data)); + } + return TRUE; + } else { + if (error != NULL) { + *error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Invalid password storage mode.")); + } + return FALSE; + } +} + +static gboolean +intkeyring_export_password(PurpleAccount *account, const char **mode, + char **data, GError **error, GDestroyNotify *destroy) +{ + gchar *ciphertext = NULL; + intkeyring_open(); + + if (!purple_prefs_get_bool(INTKEYRING_PREFS "encrypt_passwords")) { + gchar *cleartext = g_hash_table_lookup(intkeyring_passwords, + account); + + if (cleartext == NULL) + return FALSE; + + *mode = "cleartext"; + *data = g_strdup(cleartext); + *destroy = (GDestroyNotify)purple_str_wipe; + return TRUE; + } + + ciphertext = g_strdup(g_hash_table_lookup(intkeyring_ciphertexts, + account)); + + if (ciphertext == NULL && intkeyring_unlocked) { + gchar *plaintext = g_hash_table_lookup(intkeyring_passwords, + account); + + if (plaintext == NULL) + return FALSE; + + purple_debug_warning("keyring-internal", "Encrypted password " + "is missing at export (it shouldn't happen)\n"); + ciphertext = intkeyring_encrypt(intkeyring_key, plaintext); + } + + if (ciphertext == NULL) + return FALSE; + + *mode = "ciphertext"; + *data = ciphertext; + *destroy = (GDestroyNotify)g_free; + return TRUE; +} + +static PurpleRequestFields * +intkeyring_read_settings(void) +{ + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; + + fields = purple_request_fields_new(); + group = purple_request_field_group_new(NULL); + purple_request_fields_add_group(fields, group); + + field = purple_request_field_bool_new("encrypt_passwords", + _("Encrypt passwords"), purple_prefs_get_bool( + INTKEYRING_PREFS "encrypt_passwords")); + purple_request_field_group_add_field(group, field); + + group = purple_request_field_group_new(_("Master password")); + purple_request_fields_add_group(fields, group); + + field = purple_request_field_string_new("passphrase1", + _("New passphrase:"), "", FALSE); + purple_request_field_string_set_masked(field, TRUE); + purple_request_field_group_add_field(group, field); + + field = purple_request_field_string_new("passphrase2", + _("New passphrase (again):"), "", FALSE); + purple_request_field_string_set_masked(field, TRUE); + purple_request_field_group_add_field(group, field); + + group = purple_request_field_group_new(_("Advanced settings")); + purple_request_fields_add_group(fields, group); + + field = purple_request_field_int_new("pbkdf2_desired_iterations", + _("Number of PBKDF2 iterations:"), purple_prefs_get_int( + INTKEYRING_PREFS "pbkdf2_desired_iterations"), + INTKEYRING_PBKDF2_ITERATIONS_MIN, + INTKEYRING_PBKDF2_ITERATIONS_MAX); + purple_request_field_group_add_field(group, field); + + return fields; +} + +static gboolean +intkeyring_apply_settings(void *notify_handle, + PurpleRequestFields *fields) +{ + const gchar *passphrase, *passphrase2; + + intkeyring_unlock(_("You have to unlock the keyring first.")); + if (!intkeyring_unlocked) + return FALSE; + + passphrase = purple_request_fields_get_string(fields, "passphrase1"); + if (g_strcmp0(passphrase, "") == 0) + passphrase = NULL; + passphrase2 = purple_request_fields_get_string(fields, "passphrase2"); + if (g_strcmp0(passphrase2, "") == 0) + passphrase2 = NULL; + + if (g_strcmp0(passphrase, passphrase2) != 0) { + purple_notify_error(notify_handle, + _("Internal keyring settings"), + _("Passphrases do not match"), NULL); + return FALSE; + } + + if (purple_request_fields_get_bool(fields, "encrypt_passwords") && + !passphrase && !intkeyring_key) + { + purple_notify_error(notify_handle, + _("Internal keyring settings"), + _("You have to set up a Master password, if you want " + "to enable encryption"), NULL); + return FALSE; + } + + if (!purple_request_fields_get_bool(fields, "encrypt_passwords") && + passphrase) + { + purple_notify_error(notify_handle, + _("Internal keyring settings"), + _("You don't need any master password, if you won't " + "enable passwords encryption"), NULL); + return FALSE; + } + + purple_prefs_set_string(INTKEYRING_PREFS "encryption_method", + INTKEYRING_ENCRYPTION_METHOD); + + purple_prefs_set_int(INTKEYRING_PREFS "pbkdf2_desired_iterations", + purple_request_fields_get_integer(fields, + "pbkdf2_desired_iterations")); + + if (passphrase != NULL) { + if (!intkeyring_change_masterpw(passphrase)) + return FALSE; + } + + purple_prefs_set_bool(INTKEYRING_PREFS "encrypt_passwords", + purple_request_fields_get_bool(fields, "encrypt_passwords")); + + purple_signal_emit(purple_keyring_get_handle(), "password-migration", + NULL); + + + return TRUE; +} + +static gboolean +intkeyring_load(PurplePlugin *plugin) +{ + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, INTKEYRING_NAME); + purple_keyring_set_id(keyring_handler, INTKEYRING_ID); + purple_keyring_set_read_password(keyring_handler, + intkeyring_read); + purple_keyring_set_save_password(keyring_handler, + intkeyring_save); + purple_keyring_set_close_keyring(keyring_handler, + intkeyring_close); + purple_keyring_set_import_password(keyring_handler, + intkeyring_import_password); + purple_keyring_set_export_password(keyring_handler, + intkeyring_export_password); + purple_keyring_set_read_settings(keyring_handler, + intkeyring_read_settings); + purple_keyring_set_apply_settings(keyring_handler, + intkeyring_apply_settings); + + purple_keyring_register(keyring_handler); + + return TRUE; +} + +static gboolean +intkeyring_unload(PurplePlugin *plugin) +{ + if (purple_keyring_get_inuse() == keyring_handler) { + purple_debug_warning("keyring-internal", + "keyring in use, cannot unload\n"); + return FALSE; + } + + intkeyring_close(); + + purple_keyring_unregister(keyring_handler); + purple_keyring_free(keyring_handler); + keyring_handler = NULL; + + if (intkeyring_key != NULL) { + purple_debug_warning("keyring-internal", "Master key should be " + "cleaned up at this point\n"); + intkeyring_buff_free(intkeyring_key); + intkeyring_key = NULL; + } + + return TRUE; +} + +PurplePluginInfo plugininfo = +{ + PURPLE_PLUGIN_MAGIC, /* magic */ + PURPLE_MAJOR_VERSION, /* major_version */ + PURPLE_MINOR_VERSION, /* minor_version */ + PURPLE_PLUGIN_STANDARD, /* type */ + NULL, /* ui_requirement */ + PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + INTKEYRING_ID, /* id */ + INTKEYRING_NAME, /* name */ + DISPLAY_VERSION, /* version */ + "Internal Keyring Plugin", /* summary */ + INTKEYRING_DESCRIPTION, /* description */ + INTKEYRING_AUTHOR, /* author */ + PURPLE_WEBSITE, /* homepage */ + intkeyring_load, /* load */ + intkeyring_unload, /* unload */ + NULL, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, NULL, NULL, NULL /* padding */ +}; + +static void +init_plugin(PurplePlugin *plugin) +{ + purple_prefs_add_none("/plugins/keyrings"); + purple_prefs_add_none("/plugins/keyrings/internal"); + purple_prefs_add_bool(INTKEYRING_PREFS "encrypt_passwords", FALSE); + purple_prefs_add_string(INTKEYRING_PREFS "encryption_method", + INTKEYRING_ENCRYPTION_METHOD); + purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_desired_iterations", + INTKEYRING_PBKDF2_ITERATIONS); + purple_prefs_add_int(INTKEYRING_PREFS "pbkdf2_iterations", + INTKEYRING_PBKDF2_ITERATIONS); + purple_prefs_add_string(INTKEYRING_PREFS "pbkdf2_salt", ""); + purple_prefs_add_string(INTKEYRING_PREFS "key_verifier", ""); +} + +PURPLE_INIT_PLUGIN(internal_keyring, init_plugin, plugininfo)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/kwallet.cpp Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,582 @@ +/** + * @file kwallet.cpp KWallet password storage + * @ingroup plugins + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" +#include "account.h" +#include "core.h" +#include "debug.h" +#include "keyring.h" +#include "plugin.h" +#include "version.h" + +#include <QQueue> +#include <QCoreApplication> +#include <kwallet.h> + +#define KWALLET_NAME N_("KWallet") +#define KWALLET_DESCRIPTION N_("This plugin will store passwords in KWallet.") +#define KWALLET_AUTHOR "QuLogic (qulogic[at]pidgin.im)" +#define KWALLET_ID "keyring-kwallet" + +#define KWALLET_WALLET_NAME KWallet::Wallet::NetworkWallet() +#define KWALLET_APP_NAME "Libpurple" +#define KWALLET_FOLDER_NAME "libpurple" + +PurpleKeyring *keyring_handler = NULL; +QCoreApplication *qCoreApp = NULL; + +namespace KWalletPlugin { + +class request +{ + public: + virtual ~request(); + virtual void detailedAbort(enum PurpleKeyringError error) = 0; + void abort(); + virtual void execute(KWallet::Wallet *wallet) = 0; + + protected: + gpointer data; + PurpleAccount *account; + QString password; + bool noPassword; +}; + +class engine : private QObject, private QQueue<request*> +{ + Q_OBJECT + + public: + engine(); + ~engine(); + void queue(request *req); + void abortAll(); + static engine *instance(bool create); + static void closeInstance(void); + + private slots: + void walletOpened(bool opened); + void walletClosed(); + + private: + static engine *pinstance; + + bool connected; + bool failed; + bool closing; + bool externallyClosed; + bool busy; + bool closeAfterBusy; + + KWallet::Wallet *wallet; + + void reopenWallet(); + void executeRequests(); +}; + +class save_request : public request +{ + public: + save_request(PurpleAccount *account, const char *password, + PurpleKeyringSaveCallback cb, void *data); + void detailedAbort(enum PurpleKeyringError error); + void execute(KWallet::Wallet *wallet); + + private: + PurpleKeyringSaveCallback callback; +}; + +class read_request : public request +{ + public: + read_request(PurpleAccount *account, + PurpleKeyringReadCallback cb, void *data); + void detailedAbort(enum PurpleKeyringError error); + void execute(KWallet::Wallet *wallet); + + private: + PurpleKeyringReadCallback callback; +}; + +} + +static gboolean +kwallet_is_enabled(void) +{ + return KWallet::Wallet::isEnabled() ? TRUE : FALSE; +} + +KWalletPlugin::engine *KWalletPlugin::engine::pinstance = NULL; + +KWalletPlugin::request::~request() +{ +} + +void +KWalletPlugin::request::abort() +{ + detailedAbort(PURPLE_KEYRING_ERROR_CANCELLED); +} + +KWalletPlugin::engine::engine() +{ + connected = false; + failed = false; + closing = false; + externallyClosed = false; + wallet = NULL; + busy = false; + closeAfterBusy = false; + + reopenWallet(); +} + +void +KWalletPlugin::engine::reopenWallet() +{ + if (closing) { + purple_debug_error("keyring-kwallet", + "wallet is closing right now\n"); + failed = true; + return; + } + + connected = false; + failed = false; + externallyClosed = false; + + wallet = KWallet::Wallet::openWallet(KWALLET_WALLET_NAME, 0, + KWallet::Wallet::Asynchronous); + if (wallet == NULL) { + failed = true; + purple_debug_error("keyring-kwallet", + "failed opening a wallet\n"); + return; + } + + failed |= !connect(wallet, SIGNAL(walletClosed()), + SLOT(walletClosed())); + failed |= !connect(wallet, SIGNAL(walletOpened(bool)), + SLOT(walletOpened(bool))); + if (failed) { + purple_debug_error("keyring-kwallet", + "failed connecting to wallet signal\n"); + } +} + +KWalletPlugin::engine::~engine() +{ + closing = true; + + abortAll(); + + delete wallet; + + if (pinstance == this) + pinstance = NULL; +} + +void +KWalletPlugin::engine::abortAll() +{ + int abortedCount = 0; + + while (!isEmpty()) { + request *req = dequeue(); + req->abort(); + delete req; + abortedCount++; + } + + if (abortedCount > 0) { + purple_debug_info("keyring-kwallet", "aborted requests: %d\n", + abortedCount); + } +} + +KWalletPlugin::engine * +KWalletPlugin::engine::instance(bool create) +{ + if (pinstance == NULL && create) + pinstance = new engine; + return pinstance; +} + +void +KWalletPlugin::engine::closeInstance(void) +{ + if (pinstance == NULL) + return; + if (pinstance->closing) + return; + if (pinstance->busy) { + purple_debug_misc("keyring-kwallet", + "current instance is busy, will be freed later\n"); + pinstance->closeAfterBusy = true; + } else + delete pinstance; + pinstance = NULL; +} + +void +KWalletPlugin::engine::walletOpened(bool opened) +{ + connected = opened; + + if (!opened) { + purple_debug_warning("keyring-kwallet", + "failed to open a wallet\n"); + delete this; + return; + } + + if (!wallet->hasFolder(KWALLET_FOLDER_NAME)) { + if (!wallet->createFolder(KWALLET_FOLDER_NAME)) { + purple_debug_error("keyring-kwallet", + "couldn't create \"" KWALLET_FOLDER_NAME + "\" folder in wallet\n"); + failed = true; + } + } + if (!failed) + wallet->setFolder(KWALLET_FOLDER_NAME); + + executeRequests(); +} + +void +KWalletPlugin::engine::walletClosed() +{ + if (!closing) { + purple_debug_info("keyring-kwallet", + "wallet was externally closed\n"); + externallyClosed = true; + delete wallet; + wallet = NULL; + } +} + +void +KWalletPlugin::engine::queue(request *req) +{ + enqueue(req); + executeRequests(); +} + +void +KWalletPlugin::engine::executeRequests() +{ + if (closing || busy) + return; + busy = true; + if (externallyClosed) { + reopenWallet(); + } else if (connected || failed) { + while (!isEmpty()) { + request *req = dequeue(); + if (connected) + req->execute(wallet); + else + req->abort(); + delete req; + } + } else if (purple_debug_is_verbose()) { + purple_debug_misc("keyring-kwallet", "not yet connected\n"); + } + busy = false; + if (closeAfterBusy) { + purple_debug_misc("keyring-kwallet", + "instance freed after being busy\n"); + delete this; + } +} + +KWalletPlugin::save_request::save_request(PurpleAccount *acc, const char *pw, + PurpleKeyringSaveCallback cb, void *userdata) +{ + account = acc; + data = userdata; + callback = cb; + password = QString(pw); + noPassword = (pw == NULL); +} + +KWalletPlugin::read_request::read_request(PurpleAccount *acc, + PurpleKeyringReadCallback cb, void *userdata) +{ + account = acc; + data = userdata; + callback = cb; + password = QString(); +} + +void +KWalletPlugin::save_request::detailedAbort(enum PurpleKeyringError error) +{ + GError *gerror; + if (callback == NULL) + return; + + gerror = g_error_new(PURPLE_KEYRING_ERROR, error, + _("Failed to save password.")); + callback(account, gerror, data); + g_error_free(gerror); +} + +void +KWalletPlugin::read_request::detailedAbort(enum PurpleKeyringError error) +{ + GError *gerror; + if (callback == NULL) + return; + + gerror = g_error_new(PURPLE_KEYRING_ERROR, error, + _("Failed to read password.")); + callback(account, NULL, gerror, data); + g_error_free(gerror); +} + +static QString +kwallet_account_key(PurpleAccount *account) +{ + return QString(purple_account_get_protocol_id(account)) + ":" + + purple_account_get_username(account); +} + +void +KWalletPlugin::read_request::execute(KWallet::Wallet *wallet) +{ + int result; + + g_return_if_fail(wallet != NULL); + + result = wallet->readPassword(kwallet_account_key(account), password); + + if (result != 0) { + purple_debug_warning("keyring-kwallet", + "failed to read password, result was %d\n", result); + abort(); + return; + } + + purple_debug_misc("keyring-kwallet", + "Got password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + if (callback != NULL) + callback(account, password.toUtf8().constData(), NULL, data); +} + +void +KWalletPlugin::save_request::execute(KWallet::Wallet *wallet) +{ + int result; + + g_return_if_fail(wallet != NULL); + + if (noPassword) + result = wallet->removeEntry(kwallet_account_key(account)); + else { + result = wallet->writePassword(kwallet_account_key(account), + password); + } + + if (result != 0) { + purple_debug_warning("keyring-kwallet", + "failed to write password, result was %d\n", result); + abort(); + return; + } + + purple_debug_misc("keyring-kwallet", + "Password %s for account %s (%s).\n", + (noPassword ? "removed" : "saved"), + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + if (callback != NULL) + callback(account, NULL, data); +} + +extern "C" +{ + +static void +kwallet_read(PurpleAccount *account, PurpleKeyringReadCallback cb, + gpointer data) +{ + KWalletPlugin::read_request *req = + new KWalletPlugin::read_request(account, cb, data); + + if (KWallet::Wallet::keyDoesNotExist(KWALLET_WALLET_NAME, + KWALLET_FOLDER_NAME, kwallet_account_key(account))) { + req->detailedAbort(PURPLE_KEYRING_ERROR_NOPASSWORD); + delete req; + } + else + KWalletPlugin::engine::instance(true)->queue(req); +} + +static void +kwallet_save(PurpleAccount *account, const char *password, + PurpleKeyringSaveCallback cb, gpointer data) +{ + if (password == NULL && KWallet::Wallet::keyDoesNotExist( + KWALLET_WALLET_NAME, KWALLET_FOLDER_NAME, + kwallet_account_key(account))) { + if (cb != NULL) + cb(account, NULL, data); + } + else + KWalletPlugin::engine::instance(true)->queue( + new KWalletPlugin::save_request(account, password, cb, + data)); +} + +static void +kwallet_cancel(void) +{ + KWalletPlugin::engine *instance = + KWalletPlugin::engine::instance(false); + if (instance) + instance->abortAll(); +} + +static void * +kwallet_get_handle(void) +{ + static int handle; + + return &handle; +} + +static const char *kwallet_get_ui_name(void) +{ + GHashTable *ui_info; + const char *ui_name = NULL; + + ui_info = purple_core_get_ui_info(); + if (ui_info != NULL) + ui_name = (const char*)g_hash_table_lookup(ui_info, "name"); + if (ui_name == NULL) + ui_name = KWALLET_APP_NAME; + + return ui_name; +} + +static gboolean +kwallet_load(PurplePlugin *plugin) +{ + if (!qCoreApp) { + int argc = 0; + qCoreApp = new QCoreApplication(argc, NULL); + qCoreApp->setApplicationName(kwallet_get_ui_name()); + } + + if (!kwallet_is_enabled()) { + purple_debug_info("keyring-kwallet", + "KWallet service is disabled\n"); + return FALSE; + } + + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, KWALLET_NAME); + purple_keyring_set_id(keyring_handler, KWALLET_ID); + purple_keyring_set_read_password(keyring_handler, kwallet_read); + purple_keyring_set_save_password(keyring_handler, kwallet_save); + purple_keyring_set_cancel_requests(keyring_handler, kwallet_cancel); + purple_keyring_set_close_keyring(keyring_handler, + KWalletPlugin::engine::closeInstance); + + purple_keyring_register(keyring_handler); + + return TRUE; +} + +static gboolean +kwallet_unload(PurplePlugin *plugin) +{ + if (purple_keyring_get_inuse() == keyring_handler) { + purple_debug_warning("keyring-kwallet", + "keyring in use, cannot unload\n"); + return FALSE; + } + + purple_signals_disconnect_by_handle(kwallet_get_handle()); + + KWalletPlugin::engine::closeInstance(); + + purple_keyring_unregister(keyring_handler); + purple_keyring_free(keyring_handler); + keyring_handler = NULL; + + if (qCoreApp) { + delete qCoreApp; + qCoreApp = NULL; + } + + return TRUE; +} + +PurplePluginInfo plugininfo = +{ + PURPLE_PLUGIN_MAGIC, /* magic */ + PURPLE_MAJOR_VERSION, /* major_version */ + PURPLE_MINOR_VERSION, /* minor_version */ + PURPLE_PLUGIN_STANDARD, /* type */ + NULL, /* ui_requirement */ + PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + KWALLET_ID, /* id */ + KWALLET_NAME, /* name */ + DISPLAY_VERSION, /* version */ + "KWallet Keyring Plugin", /* summary */ + KWALLET_DESCRIPTION, /* description */ + KWALLET_AUTHOR, /* author */ + PURPLE_WEBSITE, /* homepage */ + kwallet_load, /* load */ + kwallet_unload, /* unload */ + NULL, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, NULL, NULL, NULL /* padding */ +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(kwallet_keyring, init_plugin, plugininfo) + +} /* extern "C" */ + +#include "kwallet.moc"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/secretservice.c Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,337 @@ +/* purple + * @file secretservice.c Secret Service password storage + * @ingroup plugins + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#error "This keyring needs some more work (see TODO)" + +/* TODO + * + * This keyring needs some more work, so it will be disabled until its quality + * was raised. Some of the pain points: + * - throws a lot of g_warnings + * - it doesn't notify about some backend faults (like access denied), some of + * them are not handled at all + * - it could use libsecret's Complete API + * - code formatting could be better + */ + +#include "internal.h" +#include "account.h" +#include "debug.h" +#include "keyring.h" +#include "plugin.h" +#include "version.h" + +#include <libsecret/secret.h> + +#define SECRETSERVICE_NAME N_("Secret Service") +#define SECRETSERVICE_ID "keyring-libsecret" + +static PurpleKeyring *keyring_handler = NULL; + +static const SecretSchema purple_schema = { + "im.pidgin.Purple", SECRET_SCHEMA_NONE, + { + {"user", SECRET_SCHEMA_ATTRIBUTE_STRING}, + {"protocol", SECRET_SCHEMA_ATTRIBUTE_STRING}, + {"NULL", 0} + }, + /* Reserved fields */ + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +typedef struct _InfoStorage InfoStorage; + +struct _InfoStorage +{ + PurpleAccount *account; + gpointer cb; + gpointer user_data; +}; + +/***********************************************/ +/* Keyring interface */ +/***********************************************/ +static void +ss_read_continue(GObject *object, GAsyncResult *result, gpointer data) +{ + InfoStorage *storage = data; + PurpleAccount *account = storage->account; + PurpleKeyringReadCallback cb = storage->cb; + char *password; + GError *error = NULL; + + password = secret_password_lookup_finish(result, &error); + + if (error != NULL) { + int code = error->code; + + switch (code) { + case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND: + case G_DBUS_ERROR_IO_ERROR: + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + "Failed to communicate with Secret Service (account : %s).", + purple_account_get_username(account)); + if (cb != NULL) + cb(account, NULL, error, storage->user_data); + g_error_free(error); + break; + + default: + purple_debug_error("keyring-libsecret", + "Unknown error (account: %s (%s), domain: %s, code: %d): %s.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account), + g_quark_to_string(error->domain), code, error->message); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + "Unknown error (account : %s).", + purple_account_get_username(account)); + if (cb != NULL) + cb(account, NULL, error, storage->user_data); + g_error_free(error); + break; + } + + } else if (password == NULL) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOPASSWORD, + "No password found for account: %s", + purple_account_get_username(account)); + if (cb != NULL) + cb(account, NULL, error, storage->user_data); + g_error_free(error); + + } else { + if (cb != NULL) + cb(account, password, NULL, storage->user_data); + } + + g_free(storage); +} + +static void +ss_read(PurpleAccount *account, PurpleKeyringReadCallback cb, gpointer data) +{ + InfoStorage *storage = g_new0(InfoStorage, 1); + + storage->account = account; + storage->cb = cb; + storage->user_data = data; + + secret_password_lookup(&purple_schema, + NULL, ss_read_continue, storage, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); +} + +static void +ss_save_continue(GObject *object, GAsyncResult *result, gpointer data) +{ + InfoStorage *storage = data; + PurpleKeyringSaveCallback cb; + GError *error = NULL; + PurpleAccount *account; + + account = storage->account; + cb = storage->cb; + + secret_password_store_finish(result, &error); + + if (error != NULL) { + int code = error->code; + + switch (code) { + case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND: + case G_DBUS_ERROR_IO_ERROR: + purple_debug_info("keyring-libsecret", + "Failed to communicate with Secret Service (account : %s (%s)).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + "Failed to communicate with Secret Service (account : %s).", + purple_account_get_username(account)); + if (cb != NULL) + cb(account, error, storage->user_data); + g_error_free(error); + break; + + default: + purple_debug_error("keyring-libsecret", + "Unknown error (account: %s (%s), domain: %s, code: %d): %s.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account), + g_quark_to_string(error->domain), code, error->message); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + "Unknown error (account : %s).", + purple_account_get_username(account)); + if (cb != NULL) + cb(account, error, storage->user_data); + g_error_free(error); + break; + } + + } else { + purple_debug_info("keyring-libsecret", "Password for %s updated.\n", + purple_account_get_username(account)); + + if (cb != NULL) + cb(account, NULL, storage->user_data); + } + + g_free(storage); +} + +static void +ss_save(PurpleAccount *account, + const gchar *password, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + InfoStorage *storage = g_new0(InfoStorage, 1); + + storage->account = account; + storage->cb = cb; + storage->user_data = data; + + if (password != NULL && *password != '\0') { + const char *username = purple_account_get_username(account); + char *label; + + purple_debug_info("keyring-libsecret", + "Updating password for account %s (%s).\n", + username, purple_account_get_protocol_id(account)); + + label = g_strdup_printf(_("Pidgin IM password for account %s"), username); + secret_password_store(&purple_schema, SECRET_COLLECTION_DEFAULT, + label, password, + NULL, ss_save_continue, storage, + "user", username, + "protocol", purple_account_get_protocol_id(account), + NULL); + g_free(label); + + } else { /* password == NULL, delete password. */ + purple_debug_info("keyring-libsecret", + "Forgetting password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + secret_password_clear(&purple_schema, NULL, ss_save_continue, storage, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + } +} + +static void +ss_close(void) +{ +} + +static gboolean +ss_init(void) +{ + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, SECRETSERVICE_NAME); + purple_keyring_set_id(keyring_handler, SECRETSERVICE_ID); + purple_keyring_set_read_password(keyring_handler, ss_read); + purple_keyring_set_save_password(keyring_handler, ss_save); + purple_keyring_set_close_keyring(keyring_handler, ss_close); + + purple_keyring_register(keyring_handler); + + return TRUE; +} + +static void +ss_uninit(void) +{ + ss_close(); + purple_keyring_unregister(keyring_handler); + purple_keyring_free(keyring_handler); + keyring_handler = NULL; +} + +/***********************************************/ +/* Plugin interface */ +/***********************************************/ + +static gboolean +ss_load(PurplePlugin *plugin) +{ + return ss_init(); +} + +static gboolean +ss_unload(PurplePlugin *plugin) +{ + if (purple_keyring_get_inuse() == keyring_handler) + return FALSE; + + ss_uninit(); + + return TRUE; +} + +PurplePluginInfo plugininfo = +{ + PURPLE_PLUGIN_MAGIC, /* magic */ + PURPLE_MAJOR_VERSION, /* major_version */ + PURPLE_MINOR_VERSION, /* minor_version */ + PURPLE_PLUGIN_STANDARD, /* type */ + NULL, /* ui_requirement */ + PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + SECRETSERVICE_ID, /* id */ + SECRETSERVICE_NAME, /* name */ + DISPLAY_VERSION, /* version */ + "Secret Service Plugin", /* summary */ + N_("This plugin will store passwords in Secret Service."), /* description */ + "Elliott Sales de Andrade (qulogic[at]pidgin.im)", /* author */ + PURPLE_WEBSITE, /* homepage */ + ss_load, /* load */ + ss_unload, /* unload */ + NULL, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, /* padding... */ + NULL, + NULL, + NULL, +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(secret_service, init_plugin, plugininfo) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/wincred.c Mon Jun 10 01:22:52 2013 +0530 @@ -0,0 +1,321 @@ +/** + * @file wincred.c Passwords storage using Windows credentials + * @ingroup plugins + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "debug.h" +#include "internal.h" +#include "keyring.h" +#include "plugin.h" +#include "version.h" + +#include <wincred.h> + +#define WINCRED_NAME N_("Windows credentials") +#define WINCRED_SUMMARY N_("Store passwords using Windows credentials") +#define WINCRED_DESCRIPTION N_("This plugin stores passwords using Windows " \ + "credentials.") +#define WINCRED_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)" +#define WINCRED_ID "keyring-wincred" + +#define WINCRED_MAX_TARGET_NAME 256 + +static PurpleKeyring *keyring_handler = NULL; + +static gunichar2 * +wincred_get_target_name(PurpleAccount *account) +{ + gchar target_name_utf8[WINCRED_MAX_TARGET_NAME]; + gunichar2 *target_name_utf16; + + g_return_val_if_fail(account != NULL, NULL); + + g_snprintf(target_name_utf8, WINCRED_MAX_TARGET_NAME, "libpurple_%s_%s", + purple_account_get_protocol_id(account), + purple_account_get_username(account)); + + target_name_utf16 = g_utf8_to_utf16(target_name_utf8, -1, + NULL, NULL, NULL); + + if (target_name_utf16 == NULL) { + purple_debug_fatal("keyring-wincred", "Couldn't convert target " + "name\n"); + } + + return target_name_utf16; +} + +static void +wincred_read(PurpleAccount *account, PurpleKeyringReadCallback cb, + gpointer data) +{ + GError *error = NULL; + gunichar2 *target_name = NULL; + gchar *password; + PCREDENTIALW credential; + + g_return_if_fail(account != NULL); + + target_name = wincred_get_target_name(account); + g_return_if_fail(target_name != NULL); + + if (!CredReadW(target_name, CRED_TYPE_GENERIC, 0, &credential)) { + DWORD error_code = GetLastError(); + + if (error_code == ERROR_NOT_FOUND) { + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring-wincred", + "No password found for account %s\n", + purple_account_get_username(account)); + } + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOPASSWORD, + _("Password not found.")); + } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) { + purple_debug_error("keyring-wincred", + "Cannot read password, no valid logon " + "session\n"); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_ACCESSDENIED, + _("Cannot read password, no valid logon " + "session.")); + } else { + purple_debug_error("keyring-wincred", + "Cannot read password, error %lx\n", + error_code); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Cannot read password (error %lx)."), error_code); + } + + if (cb != NULL) + cb(account, NULL, error, data); + g_error_free(error); + return; + } + + password = g_utf16_to_utf8((gunichar2*)credential->CredentialBlob, + credential->CredentialBlobSize / sizeof(gunichar2), + NULL, NULL, NULL); + + memset(credential->CredentialBlob, 0, credential->CredentialBlobSize); + CredFree(credential); + + if (password == NULL) { + purple_debug_error("keyring-wincred", + "Cannot convert password\n"); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Cannot read password (unicode error).")); + } else { + purple_debug_misc("keyring-wincred", + _("Got password for account %s.\n"), + purple_account_get_username(account)); + } + + if (cb != NULL) + cb(account, password, error, data); + if (error != NULL) + g_error_free(error); + + purple_str_wipe(password); +} + +static void +wincred_save(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data) +{ + GError *error = NULL; + gunichar2 *target_name = NULL; + gunichar2 *username_utf16 = NULL; + gunichar2 *password_utf16 = NULL; + CREDENTIALW credential; + + g_return_if_fail(account != NULL); + + target_name = wincred_get_target_name(account); + g_return_if_fail(target_name != NULL); + + if (password == NULL) + { + if (CredDeleteW(target_name, CRED_TYPE_GENERIC, 0)) { + purple_debug_misc("keyring-wincred", "Password for " + "account %s removed\n", + purple_account_get_username(account)); + } else { + DWORD error_code = GetLastError(); + + if (error_code == ERROR_NOT_FOUND) { + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring-wincred", + "Password for account %s was already " + "removed.\n", + purple_account_get_username(account)); + } + } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) { + purple_debug_error("keyring-wincred", + "Cannot remove password, no valid " + "logon session\n"); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_ACCESSDENIED, + _("Cannot remove password, no valid " + "logon session.")); + } else { + purple_debug_error("keyring-wincred", + "Cannot remove password, error %lx\n", + error_code); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Cannot remove password (error %lx)."), + error_code); + } + } + + if (cb != NULL) + cb(account, error, data); + if (error != NULL) + g_error_free(error); + return; + } + + username_utf16 = g_utf8_to_utf16(purple_account_get_username(account), + -1, NULL, NULL, NULL); + password_utf16 = g_utf8_to_utf16(password, -1, NULL, NULL, NULL); + + if (username_utf16 == NULL || password_utf16 == NULL) { + g_free(username_utf16); + purple_utf16_wipe(password_utf16); + + purple_debug_fatal("keyring-wincred", "Couldn't convert " + "username or password\n"); + g_return_if_reached(); + } + + memset(&credential, 0, sizeof(CREDENTIALW)); + credential.Type = CRED_TYPE_GENERIC; + credential.TargetName = target_name; + credential.CredentialBlobSize = purple_utf16_size(password_utf16) - 2; + credential.CredentialBlob = (LPBYTE)password_utf16; + credential.Persist = CRED_PERSIST_LOCAL_MACHINE; + credential.UserName = username_utf16; + + if (!CredWriteW(&credential, 0)) { + DWORD error_code = GetLastError(); + + if (error_code == ERROR_NO_SUCH_LOGON_SESSION) { + purple_debug_error("keyring-wincred", + "Cannot store password, no valid logon " + "session\n"); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_ACCESSDENIED, + _("Cannot remove password, no valid logon " + "session.")); + } else { + purple_debug_error("keyring-wincred", + "Cannot store password, error %lx\n", + error_code); + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_BACKENDFAIL, + _("Cannot store password (error %lx)."), error_code); + } + } else { + purple_debug_misc("keyring-wincred", + "Password updated for account %s.\n", + purple_account_get_username(account)); + } + + g_free(target_name); + g_free(username_utf16); + purple_utf16_wipe(password_utf16); + + if (cb != NULL) + cb(account, error, data); + if (error != NULL) + g_error_free(error); +} + +static gboolean +wincred_load(PurplePlugin *plugin) +{ + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, WINCRED_NAME); + purple_keyring_set_id(keyring_handler, WINCRED_ID); + purple_keyring_set_read_password(keyring_handler, wincred_read); + purple_keyring_set_save_password(keyring_handler, wincred_save); + + purple_keyring_register(keyring_handler); + + return TRUE; +} + +static gboolean +wincred_unload(PurplePlugin *plugin) +{ + if (purple_keyring_get_inuse() == keyring_handler) { + purple_debug_warning("keyring-wincred", + "keyring in use, cannot unload\n"); + return FALSE; + } + + purple_keyring_unregister(keyring_handler); + purple_keyring_free(keyring_handler); + keyring_handler = NULL; + + return TRUE; +} + +PurplePluginInfo plugininfo = +{ + PURPLE_PLUGIN_MAGIC, /* magic */ + PURPLE_MAJOR_VERSION, /* major_version */ + PURPLE_MINOR_VERSION, /* minor_version */ + PURPLE_PLUGIN_STANDARD, /* type */ + NULL, /* ui_requirement */ + PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + WINCRED_ID, /* id */ + WINCRED_NAME, /* name */ + DISPLAY_VERSION, /* version */ + WINCRED_SUMMARY, /* summary */ + WINCRED_DESCRIPTION, /* description */ + WINCRED_AUTHOR, /* author */ + PURPLE_WEBSITE, /* homepage */ + wincred_load, /* load */ + wincred_unload, /* unload */ + NULL, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, NULL, NULL, NULL /* padding */ +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(wincred_keyring, init_plugin, plugininfo)
--- a/libpurple/plugins/one_time_password.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/plugins/one_time_password.c Mon Jun 10 01:22:52 2013 +0530 @@ -46,7 +46,7 @@ purple_account_get_username(account), purple_account_get_protocol_name(account)); - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); /* TODO: Do we need to somehow clear conn->password ? */ } }
--- a/libpurple/plugins/perl/common/Account.xs Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/plugins/perl/common/Account.xs Mon Jun 10 01:22:52 2013 +0530 @@ -1,4 +1,5 @@ #include "module.h" +#include "../perl-handlers.h" MODULE = Purple::Account PACKAGE = Purple::Account PREFIX = purple_account_ PROTOTYPES: ENABLE @@ -44,9 +45,13 @@ const char * username void -purple_account_set_password(account, password) +purple_account_set_password(account, password, func, data = 0) Purple::Account account const char * password + SV *func + SV *data +CODE: + purple_perl_account_set_password(account, password, func, data); void purple_account_set_alias(account, alias) @@ -130,9 +135,13 @@ purple_account_get_username(account) Purple::Account account -const char * -purple_account_get_password(account) +void +purple_account_get_password(account, func, data = 0) Purple::Account account + SV *func + SV *data +CODE: + purple_perl_account_get_password(account, func, data); const char * purple_account_get_alias(account)
--- a/libpurple/plugins/perl/perl-handlers.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/plugins/perl/perl-handlers.c Mon Jun 10 01:22:52 2013 +0530 @@ -4,6 +4,12 @@ #include "debug.h" #include "signals.h" +typedef struct +{ + SV *callback; + SV *data; +} PurplePerlAccountPasswordHandler; + extern PerlInterpreter *my_perl; static GSList *cmd_handlers = NULL; static GSList *signal_handlers = NULL; @@ -845,3 +851,115 @@ destroy_prefs_handler(handler); } } + +static void +perl_account_save_cb(PurpleAccount *account, GError *error, gpointer _handler) +{ + PurplePerlAccountPasswordHandler *handler = _handler; + SV *accountSV, *errorSV; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + + accountSV = sv_2mortal(purple_perl_bless_object(account, + "Purple::Account")); + XPUSHs(accountSV); + + errorSV = sv_2mortal(purple_perl_bless_object(error, "GLib::Error")); + XPUSHs(errorSV); + + XPUSHs((SV *)handler->data); + + PUTBACK; + call_sv(handler->callback, G_EVAL | G_SCALAR); + SPAGAIN; + + if (SvTRUE(ERRSV)) { + purple_debug_error("perl", "Perl plugin command function " + "exited abnormally: %s\n", SvPVutf8_nolen(ERRSV)); + } + + PUTBACK; + FREETMPS; + LEAVE; + + g_free(handler); +} + +static void +perl_account_read_cb(PurpleAccount *account, const gchar *password, + GError *error, gpointer _handler) +{ + PurplePerlAccountPasswordHandler *handler = _handler; + SV *accountSV, *passwordSV, *errorSV; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + + accountSV = sv_2mortal(purple_perl_bless_object(account, + "Purple::Account")); + XPUSHs(accountSV); + + passwordSV = sv_2mortal(newSVpv(password, 0)); + XPUSHs(passwordSV); + + errorSV = sv_2mortal(purple_perl_bless_object(error, "GLib::Error")); + XPUSHs(errorSV); + + XPUSHs((SV *)handler->data); + + PUTBACK; + call_sv(handler->callback, G_EVAL | G_SCALAR); + SPAGAIN; + + if (SvTRUE(ERRSV)) { + purple_debug_error("perl", "Perl plugin command function " + "exited abnormally: %s\n", SvPVutf8_nolen(ERRSV)); + } + + PUTBACK; + FREETMPS; + LEAVE; + + g_free(handler); +} + +void +purple_perl_account_get_password(PurpleAccount *account, SV *func, SV *data) +{ + PurplePerlAccountPasswordHandler *handler; + + if (func == &PL_sv_undef) + func = NULL; + if (data == &PL_sv_undef) + data = NULL; + + handler = g_new0(PurplePerlAccountPasswordHandler, 1); + handler->callback = (func != NULL ? newSVsv(func) : NULL); + handler->data = (data != NULL ? newSVsv(data) : NULL); + + purple_account_get_password(account, perl_account_read_cb, handler); +} + +void +purple_perl_account_set_password(PurpleAccount *account, const gchar *password, + SV *func, SV *data) +{ + PurplePerlAccountPasswordHandler *handler; + + if (func == &PL_sv_undef) + func = NULL; + if (data == &PL_sv_undef) + data = NULL; + + handler = g_new0(PurplePerlAccountPasswordHandler, 1); + handler->callback = (func != NULL ? newSVsv(func) : NULL); + handler->data = (data != NULL ? newSVsv(data) : NULL); + + purple_account_set_password(account, password, perl_account_save_cb, + handler); +}
--- a/libpurple/plugins/perl/perl-handlers.h Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/plugins/perl/perl-handlers.h Mon Jun 10 01:22:52 2013 +0530 @@ -82,4 +82,10 @@ void purple_perl_prefs_disconnect_callback(guint callback_id); void purple_perl_pref_cb_clear_for_plugin(PurplePlugin *plugin); +void +purple_perl_account_get_password(PurpleAccount *account, SV *func, SV *data); +void +purple_perl_account_set_password(PurpleAccount *account, const char *password, + SV *func, SV *data); + #endif /* _PURPLE_PERL_HANDLERS_H_ */
--- a/libpurple/plugins/perl/scripts/account.pl Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/plugins/perl/scripts/account.pl Mon Jun 10 01:22:52 2013 +0530 @@ -34,6 +34,37 @@ return %PLUGIN_INFO; } +sub set_password_cb +{ + my $account = shift; + my $error = shift; + my $data = shift; + + if ($error) { + Purple::Debug::warning($MODULE_NAME, "Failed to set password " . + "for $account\n"); + return; + } + + Purple::Debug::misc($MODULE_NAME, "Password for $account was set\n"); +} + +sub get_password_cb +{ + my $account = shift; + my $password = shift; + my $error = shift; + my $data = shift; + + if ($error) { + Purple::Debug::warning($MODULE_NAME, "Failed to get password for $account\n"); + return; + } + + Purple::Debug::misc($MODULE_NAME, "Got password for $account\n"); + + $account->set_password($password, \&set_password_cb); +} # This is the sub defined in %PLUGIN_INFO to be called when the plugin is loaded # Note: The plugin has a reference to itself on top of the argument stack. @@ -101,6 +132,8 @@ $account->set_status("available", TRUE); $account->connect(); + $account->get_password(\&get_password_cb); + print "\n\n"; Purple::Debug::info($MODULE_NAME, "plugin_load() - Testing $MODULE_NAME Completed.\n"); print "\n\n" . "#" x 80 . "\n\n";
--- a/libpurple/protocols/gg/account.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/gg/account.c Mon Jun 10 01:22:52 2013 +0530 @@ -359,7 +359,8 @@ purple_account_set_username(account, ggp_uin_to_str(uin)); purple_account_set_remember_password(account, register_data->password_remember); - purple_account_set_password(account, register_data->password); + purple_account_set_password(account, register_data->password, + NULL, NULL); tmp = g_strdup_printf(_("Your new GG number: %u."), uin); purple_notify_info(account, GGP_ACCOUNT_REGISTER_TITLE, @@ -554,7 +555,7 @@ g_assert(chpass_data->token_value != NULL); if (g_utf8_collate(chpass_data->password_current, - purple_account_get_password(account)) != 0) + purple_connection_get_password(chpass_data->gc)) != 0) { g_free(chpass_data->password_current); chpass_data->password_current = NULL; @@ -636,7 +637,8 @@ purple_debug_info("gg", "ggp_account_chpass_response: " "password changed\n"); - purple_account_set_password(account, chpass_data->password_new); + purple_account_set_password(account, chpass_data->password_new, + NULL, NULL); purple_notify_info(account, GGP_ACCOUNT_CHPASS_TITLE, _("Your password has been changed."), NULL);
--- a/libpurple/protocols/gg/gg.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/gg/gg.c Mon Jun 10 01:22:52 2013 +0530 @@ -896,7 +896,8 @@ ggp_status_setup(gc); glp->uin = ggp_str_to_uin(purple_account_get_username(account)); - glp->password = ggp_convert_to_cp1250(purple_account_get_password(account)); + glp->password = + ggp_convert_to_cp1250(purple_connection_get_password(gc)); if (glp->uin == 0) { purple_connection_error(gc,
--- a/libpurple/protocols/gg/oauth/oauth-purple.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/gg/oauth/oauth-purple.c Mon Jun 10 01:22:52 2013 +0530 @@ -88,7 +88,7 @@ auth = gg_oauth_generate_header(method, url, purple_account_get_username(account), - purple_account_get_password(account), NULL, NULL); + purple_connection_get_password(gc), NULL, NULL); request = g_strdup_printf( "POST /request_token HTTP/1.1\r\n" "Host: api.gadu-gadu.pl\r\n" @@ -165,7 +165,7 @@ "callback_url=http://www.mojageneracja.pl&request_token=%s&" "uin=%s&password=%s", data->token, purple_account_get_username(account), - purple_account_get_password(account)); + purple_connection_get_password(data->gc)); request = g_strdup_printf( "POST /authorize HTTP/1.1\r\n" "Host: login.gadu-gadu.pl\r\n" @@ -206,7 +206,7 @@ auth = gg_oauth_generate_header("POST", url, purple_account_get_username(account), - purple_account_get_password(account), + purple_connection_get_password(data->gc), data->token, data->token_secret); request = g_strdup_printf( @@ -267,7 +267,7 @@ auth = gg_oauth_generate_header( data->sign_method, data->sign_url, purple_account_get_username(account), - purple_account_get_password(account), + purple_connection_get_password(data->gc), token, token_secret); data->callback(data->gc, auth, data->user_data); }
--- a/libpurple/protocols/irc/msgs.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/irc/msgs.c Mon Jun 10 01:22:52 2013 +0530 @@ -1471,7 +1471,8 @@ const char *pw; size_t len; - pw = purple_account_get_password(irc->account); + pw = purple_connection_get_password(purple_account_get_connection( + irc->account)); if (!conn || !secret || id != SASL_CB_PASS) return SASL_BADPARAM;
--- a/libpurple/protocols/jabber/auth.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/jabber/auth.c Mon Jun 10 01:22:52 2013 +0530 @@ -110,7 +110,7 @@ if (remember) purple_account_set_remember_password(account, TRUE); - purple_account_set_password(account, entry); + purple_account_set_password(account, entry, NULL, NULL); /* Restart our connection */ jabber_auth_start_old(js); @@ -228,7 +228,7 @@ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; /* Clear the pasword if it isn't being saved */ if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); } purple_connection_error(js->gc, reason, msg); @@ -372,7 +372,7 @@ * password prompting here */ - if (!purple_account_get_password(account)) { + if (!purple_connection_get_password(js->gc)) { purple_account_request_password(account, G_CALLBACK(auth_old_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc); return; }
--- a/libpurple/protocols/jabber/auth_cyrus.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/jabber/auth_cyrus.c Mon Jun 10 01:22:52 2013 +0530 @@ -91,12 +91,10 @@ static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret) { JabberStream *js = ctx; - PurpleAccount *account; const char *pw; size_t len; - account = purple_connection_get_account(js->gc); - pw = purple_account_get_password(account); + pw = purple_connection_get_password(js->gc); if (!conn || !secret || id != SASL_CB_PASS) return SASL_BADPARAM; @@ -154,7 +152,7 @@ if (remember) purple_account_set_remember_password(account, TRUE); - purple_account_set_password(account, entry); + purple_account_set_password(account, entry, NULL, NULL); /* Rebuild our callbacks as we now have a password to offer */ jabber_sasl_build_callbacks(js); @@ -249,7 +247,7 @@ * to get one */ - if (!purple_account_get_password(account)) { + if (!purple_connection_get_password(js->gc)) { purple_account_request_password(account, G_CALLBACK(auth_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc); return JABBER_SASL_STATE_CONTINUE; @@ -364,7 +362,6 @@ static void jabber_sasl_build_callbacks(JabberStream *js) { - PurpleAccount *account; int id; /* Set up our callbacks structure */ @@ -387,8 +384,7 @@ js->sasl_cb[id].context = (void *)js; id++; - account = purple_connection_get_account(js->gc); - if (purple_account_get_password(account) != NULL ) { + if (purple_connection_get_password(js->gc) != NULL) { js->sasl_cb[id].id = SASL_CB_PASS; js->sasl_cb[id].proc = (void *)jabber_sasl_cb_secret; js->sasl_cb[id].context = (void *)js;
--- a/libpurple/protocols/jabber/jabber.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/jabber/jabber.c Mon Jun 10 01:22:52 2013 +0530 @@ -1263,7 +1263,7 @@ cbdata->js->user->node = g_strdup(value); } if(cbdata->js->registration && !strcmp(id, "password")) - purple_account_set_password(purple_connection_get_account(cbdata->js->gc), value); + purple_account_set_password(purple_connection_get_account(cbdata->js->gc), value, NULL, NULL); } } } @@ -2485,7 +2485,7 @@ purple_notify_info(js->gc, _("Password Changed"), _("Password Changed"), _("Your password has been changed.")); - purple_account_set_password(purple_connection_get_account(js->gc), (char *)data); + purple_account_set_password(purple_connection_get_account(js->gc), (const char *)data, NULL, NULL); } else { char *msg = jabber_parse_error(js, packet, NULL); @@ -2741,7 +2741,7 @@ SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED); /* Clear the pasword if it isn't being saved */ if (!purple_account_get_remember_password(purple_connection_get_account(js->gc))) - purple_account_set_password(purple_connection_get_account(js->gc), NULL); + purple_account_set_password(purple_connection_get_account(js->gc), NULL, NULL, NULL); text = _("Not Authorized"); } else if(xmlnode_get_child(packet, "temporary-auth-failure")) { text = _("Temporary Authentication Failure");
--- a/libpurple/protocols/jabber/jingle/jingle.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/jabber/jingle/jingle.c Mon Jun 10 01:22:52 2013 +0530 @@ -460,7 +460,9 @@ memset(&value, 0, sizeof(GValue)); g_value_init(&value, GST_TYPE_STRUCTURE); gst_value_set_structure(&value, turn_setup); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS relay_info = g_value_array_append(relay_info, &value); +G_GNUC_END_IGNORE_DEPRECATIONS gst_structure_free(turn_setup); } return relay_info; @@ -500,7 +502,9 @@ } if (relay_ip) { +G_GNUC_BEGIN_IGNORE_DEPRECATIONS GValueArray *relay_info = g_value_array_new(0); +G_GNUC_END_IGNORE_DEPRECATIONS if (relay_udp) { relay_info = @@ -518,9 +522,11 @@ relay_password, "tls", relay_info); } params[next_index].name = "relay-info"; +G_GNUC_BEGIN_IGNORE_DEPRECATIONS g_value_init(¶ms[next_index].value, G_TYPE_VALUE_ARRAY); g_value_set_boxed(¶ms[next_index].value, relay_info); g_value_array_free(relay_info); +G_GNUC_END_IGNORE_DEPRECATIONS } }
--- a/libpurple/protocols/msn/session.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/msn/session.c Mon Jun 10 01:22:52 2013 +0530 @@ -385,7 +385,7 @@ reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; msg = g_strdup(_("You have signed on from another location")); if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); + purple_account_set_password(session->account, NULL, NULL, NULL); break; case MSN_ERROR_SERV_UNAVAILABLE: reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; @@ -405,7 +405,7 @@ _("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); + purple_account_set_password(session->account, NULL, NULL, NULL); break; case MSN_ERROR_BAD_BLIST: reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
--- a/libpurple/protocols/mxit/actions.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/mxit/actions.c Mon Jun 10 01:22:52 2013 +0530 @@ -361,7 +361,7 @@ out: if ( !err ) { /* update PIN in account */ - purple_account_set_password( session->acc, pin ); + purple_account_set_password( session->acc, pin, NULL, NULL ); /* update session object */ g_free( session->encpwd ); @@ -385,7 +385,6 @@ static void mxit_change_pin_action( PurplePluginAction* action ) { PurpleConnection* gc = (PurpleConnection*) action->context; - struct MXitSession* session = purple_connection_get_protocol_data( gc ); PurpleRequestFields* fields = NULL; PurpleRequestFieldGroup* group = NULL; @@ -398,12 +397,12 @@ purple_request_fields_add_group( fields, group ); /* pin */ - field = purple_request_field_string_new( "pin", _( "PIN" ), purple_account_get_password( session->acc ), FALSE ); + field = purple_request_field_string_new( "pin", _( "PIN" ), purple_connection_get_password( gc ), FALSE ); purple_request_field_string_set_masked( field, TRUE ); purple_request_field_group_add_field( group, field ); /* verify pin */ - field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), purple_account_get_password( session->acc ), FALSE ); + field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), purple_connection_get_password( gc ), FALSE ); purple_request_field_string_set_masked( field, TRUE ); purple_request_field_group_add_field( group, field );
--- a/libpurple/protocols/mxit/cipher.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/mxit/cipher.c Mon Jun 10 01:22:52 2013 +0530 @@ -79,7 +79,7 @@ static char* transport_layer_key( struct MXitSession* session ) { static char key[16 + 1]; - const char* password = purple_account_get_password( session->acc ); + const char* password = purple_connection_get_password( session->con ); int passlen = strlen( password ); /* initialize with initial key */ @@ -123,7 +123,7 @@ /* build the secret data to be encrypted: SECRET_HEADER + password */ pass = g_string_new( SECRET_HEADER ); - g_string_append( pass, purple_account_get_password( session->acc) ); + g_string_append( pass, purple_connection_get_password( session->con ) ); padding_add( pass ); /* add ISO10126 padding */ /* now encrypt the secret. we encrypt each block separately (ECB mode) */
--- a/libpurple/protocols/mxit/login.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/mxit/login.c Mon Jun 10 01:22:52 2013 +0530 @@ -287,7 +287,7 @@ out: if ( !err ) { - purple_account_set_password( session->acc, session->profile->pin ); + purple_account_set_password( session->acc, session->profile->pin, NULL, NULL ); mxit_login_connect( session ); } else {
--- a/libpurple/protocols/myspace/myspace.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/myspace/myspace.c Mon Jun 10 01:22:52 2013 +0530 @@ -704,7 +704,7 @@ purple_connection_update_progress(session->gc, _("Logging in"), 2, 4); response_len = 0; - response = msim_compute_login_response(nc, purple_account_get_username(account), purple_account_get_password(account), &response_len); + response = msim_compute_login_response(nc, purple_account_get_username(account), purple_connection_get_password(session->gc), &response_len); g_free(nc); @@ -1835,9 +1835,9 @@ case MSIM_ERROR_INCORRECT_PASSWORD: /* Incorrect password */ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); + purple_account_set_password(session->account, NULL, NULL, NULL); #ifdef MSIM_MAX_PASSWORD_LENGTH - if (purple_account_get_password(session->account) && (strlen(purple_account_get_password(session->account)) > MSIM_MAX_PASSWORD_LENGTH)) { + if (purple_connection_get_password(session->gc) && (strlen(purple_connection_get_password(session->gc)) > MSIM_MAX_PASSWORD_LENGTH)) { gchar *suggestion; suggestion = g_strdup_printf(_("%s Your password is " @@ -1845,7 +1845,7 @@ "maximum length of %d. Please shorten your " "password at http://profileedit.myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try again."), full_errmsg, - (gsize)strlen(purple_account_get_password(session->account)), + (gsize)strlen(purple_connection_get_password(session->gc)), MSIM_MAX_PASSWORD_LENGTH); /* Replace full_errmsg. */ @@ -1860,7 +1860,7 @@ case MSIM_ERROR_LOGGED_IN_ELSEWHERE: /* Logged in elsewhere */ reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); + purple_account_set_password(session->account, NULL, NULL, NULL); break; } purple_connection_error(session->gc, reason, full_errmsg);
--- a/libpurple/protocols/novell/novell.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/novell/novell.c Mon Jun 10 01:22:52 2013 +0530 @@ -132,7 +132,7 @@ * password was invalid. */ if (!purple_account_get_remember_password(purple_connection_get_account(gc))) - purple_account_set_password(purple_connection_get_account(gc), NULL); + purple_account_set_password(purple_connection_get_account(gc), NULL, NULL, NULL); reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; break; default: @@ -2031,7 +2031,7 @@ if (gc) { if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _("You have signed on from another location"));
--- a/libpurple/protocols/oscar/clientlogin.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/oscar/clientlogin.c Mon Jun 10 01:22:52 2013 +0530 @@ -481,7 +481,7 @@ if (status_code == 330 && status_detail_code == 3011) { PurpleAccount *account = purple_connection_get_account(gc); if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
--- a/libpurple/protocols/oscar/flap_connection.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/oscar/flap_connection.c Mon Jun 10 01:22:52 2013 +0530 @@ -467,7 +467,7 @@ reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; tmp = g_strdup(_("You have signed on from another location")); if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); } else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) tmp = g_strdup(_("Server closed the connection")); else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
--- a/libpurple/protocols/oscar/oscar.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/oscar/oscar.c Mon Jun 10 01:22:52 2013 +0530 @@ -1077,7 +1077,7 @@ case 0x05: /* Incorrect password */ if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password")); break; case 0x11:
--- a/libpurple/protocols/sametime/sametime.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/sametime/sametime.c Mon Jun 10 01:22:52 2013 +0530 @@ -3694,7 +3694,7 @@ return; } - pass = g_strdup(purple_account_get_password(account)); + pass = g_strdup(purple_connection_get_password(gc)); port = purple_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT); DEBUG_INFO("user: '%s'\n", user);
--- a/libpurple/protocols/silc/silc.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/silc/silc.c Mon Jun 10 01:22:52 2013 +0530 @@ -481,7 +481,7 @@ if (remember) purple_account_set_remember_password(account, TRUE); - purple_account_set_password(account, password); + purple_account_set_password(account, password, NULL, NULL); /* Load SILC key pair */ g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); @@ -530,7 +530,7 @@ (char *)purple_account_get_string(account, "private-key", prd), (purple_connection_get_password(gc) == NULL) ? "" : purple_connection_get_password(gc), &sg->public_key, &sg->private_key)) { - if (!purple_account_get_password(account)) { + if (!purple_connection_get_password(gc)) { purple_account_request_password(account, G_CALLBACK(silcpurple_got_password_cb), G_CALLBACK(silcpurple_no_password_cb), gc); return;
--- a/libpurple/protocols/simple/simple.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/simple/simple.c Mon Jun 10 01:22:52 2013 +0530 @@ -1122,7 +1122,7 @@ purple_debug_info("simple", "REGISTER retries %d\n", sip->registrar.retries); if(sip->registrar.retries > SIMPLE_REGISTER_RETRY_MAX) { if (!purple_account_get_remember_password(purple_connection_get_account(sip->gc))) - purple_account_set_password(purple_connection_get_account(sip->gc), NULL); + purple_account_set_password(purple_connection_get_account(sip->gc), NULL, NULL, NULL); purple_connection_error(sip->gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
--- a/libpurple/protocols/yahoo/libymsg.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/protocols/yahoo/libymsg.c Mon Jun 10 01:22:52 2013 +0530 @@ -159,7 +159,7 @@ if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _("You have signed on from another location")); return; @@ -1940,7 +1940,7 @@ /* Password incorrect */ /* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */ if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); error_reason = g_strdup(_("Incorrect password")); error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; break; @@ -2232,7 +2232,7 @@ } #endif /* TRY_WEBMESSENGER_LOGIN */ if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); msg = g_strdup(_("Invalid username or password")); reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
--- a/libpurple/prpl.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/prpl.c Mon Jun 10 01:22:52 2013 +0530 @@ -358,9 +358,10 @@ { if (!purple_account_is_disconnected(account)) purple_account_disconnect(account); - /* Clear out the unsaved password if we're already disconnected and we switch to offline status */ - else if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + /* Clear out the unsaved password if we switch to offline status */ + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL, NULL, NULL); + return; }
--- a/libpurple/request.h Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/request.h Mon Jun 10 01:22:52 2013 +0530 @@ -30,6 +30,8 @@ #include <glib-object.h> #include <glib.h> +#include "certificate.h" + /** * A request field. */
--- a/libpurple/util.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/util.c Mon Jun 10 01:22:52 2013 +0530 @@ -3762,6 +3762,41 @@ return g_string_free(ret, FALSE); } +size_t +purple_utf16_size(const gunichar2 *str) +{ + /* UTF16 cannot contain two consequent NUL bytes starting at even + * position - see Unicode standards Chapter 3.9 D91 or RFC2781 + * Chapter 2. + */ + + size_t i = 0; + + g_return_val_if_fail(str != NULL, 0); + + while (str[i++]); + + return i * sizeof(gunichar2); +} + +void +purple_str_wipe(gchar *str) +{ + if (str == NULL) + return; + memset(str, 0, strlen(str)); + g_free(str); +} + +void +purple_utf16_wipe(gunichar2 *str) +{ + if (str == NULL) + return; + memset(str, 0, purple_utf16_size(str)); + g_free(str); +} + /************************************************************************** * URI/URL Functions **************************************************************************/
--- a/libpurple/util.h Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/util.h Mon Jun 10 01:22:52 2013 +0530 @@ -1132,6 +1132,33 @@ * @return A newly allocated ASCIIZ string. */ char *purple_str_binary_to_ascii(const unsigned char *binary, guint len); + +/** + * Calculates UTF-16 string size (in bytes). + * + * @param str String to check. + * @return Number of bytes (including NUL character) that string occupies. + */ +size_t purple_utf16_size(const gunichar2 *str); + +/** + * Fills a NUL-terminated string with zeros and frees it. + * + * It should be used to free sensitive data, like passwords. + * + * @param str A NUL-terminated string to free, or a NULL-pointer. + */ +void purple_str_wipe(gchar *str); + +/** + * Fills a NUL-terminated UTF-16 string with zeros and frees it. + * + * It should be used to free sensitive data, like passwords. + * + * @param str A NUL-terminated string to free, or a NULL-pointer. + */ +void purple_utf16_wipe(gunichar2 *str); + /*@}*/
--- a/libpurple/win32/global.mak Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/win32/global.mak Mon Jun 10 01:22:52 2013 +0530 @@ -128,7 +128,7 @@ MINGW_MAKEFILE := Makefile.mingw MAKE_at := @ -USE_VV ?= 0 +USE_VV ?= 1 ifeq "$(USE_VV)" "1" VV_LIBS := \
--- a/libpurple/win32/win32dep.c Mon Jun 03 15:26:52 2013 +0530 +++ b/libpurple/win32/win32dep.c Mon Jun 10 01:22:52 2013 +0530 @@ -450,10 +450,11 @@ g_thread_init(NULL); #endif - purple_debug_info("wpurple", "wpurple_init start\n"); + if (purple_debug_is_verbose()) + purple_debug_misc("wpurple", "wpurple_init start\n"); + purple_debug_info("wpurple", "libpurple version: " DISPLAY_VERSION "\n"); - - purple_debug_info("wpurple", "Glib:%u.%u.%u\n", + purple_debug_info("wpurple", "Glib: %u.%u.%u\n", glib_major_version, glib_minor_version, glib_micro_version); /* Winsock init */ @@ -469,7 +470,8 @@ WSACleanup(); } - purple_debug_info("wpurple", "wpurple_init end\n"); + if (purple_debug_is_verbose()) + purple_debug_misc("wpurple", "wpurple_init end\n"); } /* Windows Cleanup */
--- a/pidgin/gtkaccount.c Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/gtkaccount.c Mon Jun 10 01:22:52 2013 +0530 @@ -118,6 +118,7 @@ GtkWidget *login_frame; GtkWidget *protocol_menu; GtkWidget *password_box; + gchar *password; GtkWidget *username_entry; #if GTK_CHECK_VERSION(3,0,0) GdkRGBA username_entry_hint_color; @@ -732,10 +733,11 @@ /* Set the fields. */ if (dialog->account != NULL) { - if (purple_account_get_password(dialog->account) && - purple_account_get_remember_password(dialog->account)) + if (dialog->password && purple_account_get_remember_password( + dialog->account)) { gtk_entry_set_text(GTK_ENTRY(dialog->password_entry), - purple_account_get_password(dialog->account)); + dialog->password); + } gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(dialog->remember_pass_check), @@ -1381,6 +1383,8 @@ purple_signals_disconnect_by_handle(dialog); + purple_str_wipe(dialog->password); + g_free(dialog); return FALSE; } @@ -1418,6 +1422,7 @@ char *tmp; gboolean new_acct = FALSE, icon_change = FALSE; PurpleAccount *account; + gboolean remember; /* Build the username string. */ username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->username_entry))); @@ -1523,9 +1528,12 @@ /* Remember Password */ - purple_account_set_remember_password(account, - gtk_toggle_button_get_active( - GTK_TOGGLE_BUTTON(dialog->remember_pass_check))); + remember = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(dialog->remember_pass_check)); + if(!remember) + purple_keyring_set_password(account, NULL, NULL, NULL); + + purple_account_set_remember_password(account, remember); /* Check Mail */ if (dialog->prpl_info && dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK) @@ -1543,9 +1551,9 @@ * don't want to prompt them. */ if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0')) - purple_account_set_password(account, value); + purple_account_set_password(account, value, NULL, NULL); else - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_account_set_username(account, username); g_free(username); @@ -1687,10 +1695,11 @@ {"STRING", 0, 2} }; -void -pidgin_account_dialog_show(PidginAccountDialogType type, - PurpleAccount *account) +static void +pidgin_account_dialog_show_continue(PurpleAccount *account, + const gchar *password, GError *error, gpointer _type) { + PidginAccountDialogType type = GPOINTER_TO_INT(_type); AccountPrefsDialog *dialog; GtkWidget *win; GtkWidget *main_vbox; @@ -1714,8 +1723,9 @@ } dialog->account = account; - dialog->type = type; - dialog->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + dialog->password = g_strdup(password); + dialog->type = type; + dialog->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); if (dialog->account == NULL) { /* Select the first prpl in the list*/ @@ -1810,6 +1820,14 @@ gtk_widget_grab_focus(dialog->protocol_menu); } +void +pidgin_account_dialog_show(PidginAccountDialogType type, PurpleAccount *account) +{ + /* this is to make sure the password will be cached */ + purple_account_get_password(account, + pidgin_account_dialog_show_continue, GINT_TO_POINTER(type)); +} + /************************************************************************** * Accounts Dialog **************************************************************************/
--- a/pidgin/gtkconn.c Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/gtkconn.c Mon Jun 10 01:22:52 2013 +0530 @@ -196,10 +196,7 @@ while (l) { PurpleAccount *a = (PurpleAccount*)l->data; if (!purple_account_is_disconnected(a)) { - char *password = g_strdup(purple_account_get_password(a)); purple_account_disconnect(a); - purple_account_set_password(a, password); - g_free(password); } l = l->next; }
--- a/pidgin/gtkmedia.c Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/gtkmedia.c Mon Jun 10 01:22:52 2013 +0530 @@ -1057,46 +1057,93 @@ } static GstElement * +create_vv_element(const gchar *plugin, const gchar *device) +{ + GstElement *element, *source; + + g_return_val_if_fail(plugin != NULL, NULL); + g_return_val_if_fail(plugin[0] != '\0', NULL); + + if (device != NULL && device[0] == '\0') + device = NULL; + + if (g_strcmp0(plugin, "videodisabledsrc") == 0) { + element = gst_element_factory_make("videotestsrc", NULL); + g_object_set(G_OBJECT(element), "is-live", TRUE, NULL); + if (g_strcmp0(device, "snow") == 0) { + /* GST_VIDEO_TEST_SRC_SNOW */ + g_object_set(G_OBJECT(element), "pattern", 1, NULL); + } else { + /* GST_VIDEO_TEST_SRC_BLACK */ + g_object_set(G_OBJECT(element), "pattern", 2, NULL); + } + return element; + } + + if (g_strcmp0(plugin, "ksvideosrc") == 0) { + GstElement *ksv_bin, *ksv_src, *ksv_filter; + GstPad *ksv_pad, *ksv_ghost; + + ksv_bin = gst_bin_new("ksvideofilteredsrc"); + ksv_src = gst_element_factory_make("ksvideosrc", NULL); + ksv_filter = gst_element_factory_make("ffmpegcolorspace", NULL); + + gst_bin_add_many(GST_BIN(ksv_bin), ksv_src, ksv_filter, NULL); + + gst_element_link(ksv_src, ksv_filter); + + ksv_pad = gst_element_get_static_pad(ksv_filter, "src"); + ksv_ghost = gst_ghost_pad_new("src", ksv_pad); + gst_object_unref(ksv_pad); + gst_element_add_pad(ksv_bin, ksv_ghost); + + element = ksv_bin; + source = ksv_src; + } + else + element = source = gst_element_factory_make(plugin, NULL); + + if (element == NULL) + return NULL; + + if (device != NULL) { + GObjectClass *klass = G_OBJECT_GET_CLASS(source); + if (g_object_class_find_property(klass, "device")) + g_object_set(G_OBJECT(source), "device", device, NULL); + else if (g_object_class_find_property(klass, "device-index")) + g_object_set(G_OBJECT(source), "device-index", + g_ascii_strtoull(device, NULL, 10), NULL); + else + purple_debug_warning("gtkmedia", "No possibility to " + "set device\n"); + } + + if (g_strcmp0(plugin, "videotestsrc") == 0) + g_object_set(G_OBJECT(element), "is-live", TRUE, NULL); + + return element; +} + +static GstElement * create_configured_vv_element(const gchar *type, const gchar *dir) { gchar *tmp; const gchar *plugin, *device; - GstElement *ret; - tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/plugin", type, dir); + tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/plugin", + type, dir); plugin = purple_prefs_get_string(tmp); g_free(tmp); - tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/device", type, dir); + tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/device", + type, dir); device = purple_prefs_get_string(tmp); g_free(tmp); if (plugin == NULL || plugin[0] == '\0') return NULL; - if (g_strcmp0(type, "video") == 0 && g_strcmp0(dir, "src") == 0 && - g_strcmp0(plugin, "disabled") == 0) - { - ret = gst_element_factory_make("videotestsrc", NULL); - g_object_set(G_OBJECT(ret), "is-live", 1, NULL); - if (g_strcmp0(device, "snow") == 0) { - /* GST_VIDEO_TEST_SRC_SNOW */ - g_object_set(G_OBJECT(ret), "pattern", 1, NULL); - } else { - /* GST_VIDEO_TEST_SRC_BLACK */ - g_object_set(G_OBJECT(ret), "pattern", 2, NULL); - } - return ret; - } - - ret = gst_element_factory_make(plugin, NULL); - if (device != NULL && device[0] != '\0') - g_object_set(G_OBJECT(ret), "device", device, NULL); - - if (g_strcmp0(plugin, "videotestsrc") == 0) - g_object_set(G_OBJECT(ret), "is-live", 1, NULL); - - return ret; + return create_vv_element(plugin, device); } static GstElement * @@ -1110,33 +1157,29 @@ src = create_configured_vv_element("video", "src"); #ifdef _WIN32 - /* autovideosrc doesn't pick ksvideosrc for some reason */ if (src == NULL) - src = gst_element_factory_make("ksvideosrc", NULL); + src = create_vv_element("ksvideosrc", NULL); if (src == NULL) - src = gst_element_factory_make("dshowvideosrc", NULL); + src = create_vv_element("dshowvideosrc", NULL); if (src == NULL) - src = gst_element_factory_make("autovideosrc", NULL); + src = create_vv_element("autovideosrc", NULL); #elif defined(__APPLE__) if (src == NULL) - src = gst_element_factory_make("osxvideosrc", NULL); + src = create_vv_element("osxvideosrc", NULL); if (src == NULL) - src = gst_element_factory_make("autovideosrc", NULL); + src = create_vv_element("autovideosrc", NULL); #else if (src == NULL) - src = gst_element_factory_make("gconfvideosrc", NULL); + src = create_vv_element("gconfvideosrc", NULL); if (src == NULL) - src = gst_element_factory_make("autovideosrc", NULL); - if (src == NULL) - src = gst_element_factory_make("v4l2src", NULL); + src = create_vv_element("autovideosrc", NULL); if (src == NULL) - src = gst_element_factory_make("v4lsrc", NULL); + src = create_vv_element("v4l2src", NULL); + if (src == NULL) + src = create_vv_element("v4lsrc", NULL); #endif - if (src == NULL) { - src = gst_element_factory_make("videotestsrc", NULL); - if (src != NULL) - g_object_set(G_OBJECT(src), "is-live", TRUE, NULL); - } + if (src == NULL) + src = create_vv_element("videotestsrc", NULL); if (src == NULL) { purple_debug_error("gtkmedia", "Unable to find a suitable " "element for the default video source.\n"); @@ -1164,11 +1207,11 @@ sink = create_configured_vv_element("video", "sink"); if (sink == NULL) - sink = gst_element_factory_make("directdrawsink", NULL); + sink = create_vv_element("directdrawsink", NULL); if (sink == NULL) - sink = gst_element_factory_make("gconfvideosink", NULL); + sink = create_vv_element("gconfvideosink", NULL); if (sink == NULL) - sink = gst_element_factory_make("autovideosink", NULL); + sink = create_vv_element("autovideosink", NULL); if (sink == NULL) purple_debug_error("gtkmedia", "Unable to find a suitable " "element for the default video sink.\n"); @@ -1184,19 +1227,19 @@ src = create_configured_vv_element("audio", "src"); if (src == NULL) - src = gst_element_factory_make("directsoundsrc", NULL); + src = create_vv_element("directsoundsrc", NULL); if (src == NULL) - src = gst_element_factory_make("gconfaudiosrc", NULL); + src = create_vv_element("gconfaudiosrc", NULL); if (src == NULL) - src = gst_element_factory_make("autoaudiosrc", NULL); + src = create_vv_element("autoaudiosrc", NULL); if (src == NULL) - src = gst_element_factory_make("alsasrc", NULL); + src = create_vv_element("alsasrc", NULL); if (src == NULL) - src = gst_element_factory_make("osssrc", NULL); + src = create_vv_element("osssrc", NULL); if (src == NULL) - src = gst_element_factory_make("dshowaudiosrc", NULL); + src = create_vv_element("dshowaudiosrc", NULL); if (src == NULL) - src = gst_element_factory_make("osxaudiosrc", NULL); + src = create_vv_element("osxaudiosrc", NULL); if (src == NULL) { purple_debug_error("gtkmedia", "Unable to find a suitable " "element for the default audio source.\n"); @@ -1215,11 +1258,11 @@ sink = create_configured_vv_element("audio", "sink"); if (sink == NULL) - sink = gst_element_factory_make("directsoundsink", NULL); + sink = create_vv_element("directsoundsink", NULL); if (sink == NULL) - sink = gst_element_factory_make("gconfaudiosink", NULL); + sink = create_vv_element("gconfaudiosink", NULL); if (sink == NULL) - sink = gst_element_factory_make("autoaudiosink",NULL); + sink = create_vv_element("autoaudiosink",NULL); if (sink == NULL) { purple_debug_error("gtkmedia", "Unable to find a suitable " "element for the default audio sink.\n");
--- a/pidgin/gtkprefs.c Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/gtkprefs.c Mon Jun 10 01:22:52 2013 +0530 @@ -43,6 +43,7 @@ #include "upnp.h" #include "util.h" #include "network.h" +#include "keyring.h" #include "gtkblist.h" #include "gtkconv.h" @@ -110,6 +111,14 @@ static GtkWidget *prefs_status_themes_combo_box; static GtkWidget *prefs_smiley_themes_combo_box; +/* Keyrings page */ +static GtkWidget *keyring_page_instance = NULL; +static GtkComboBox *keyring_combo = NULL; +static GtkBox *keyring_vbox = NULL; +static PurpleRequestFields *keyring_settings = NULL; +static GList *keyring_settings_fields = NULL; +static GtkWidget *keyring_apply = NULL; + /* Sound theme specific */ static GtkWidget *sound_entry = NULL; static int sound_row_sel = 0; @@ -150,7 +159,7 @@ }; static const gchar *VIDEO_SRC_PLUGINS[] = { - "disabled", N_("Disabled"), + "videodisabledsrc", N_("Disabled"), "videotestsrc", "Test Input", "dshowvideosrc","DirectDraw", "ksvideosrc", "KS Video", @@ -274,83 +283,105 @@ PREF_DROPDOWN_COUNT }; -static void -dropdown_set(GObject *w, const char *key) +typedef struct { - const char *str_value; - int int_value; - gboolean bool_value; PurplePrefType type; + union { + const char *string; + int integer; + gboolean boolean; + } value; +} PidginPrefValue; + +typedef void (*PidginPrefsDropdownCallback)(GtkComboBox *combo_box, + PidginPrefValue value); + +static void +dropdown_set(GtkComboBox *combo_box, gpointer _cb) +{ + PidginPrefsDropdownCallback cb = _cb; GtkTreeIter iter; GtkTreeModel *tree_model; - - tree_model = gtk_combo_box_get_model(GTK_COMBO_BOX(w)); - if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(w), &iter)) + PidginPrefValue active; + + tree_model = gtk_combo_box_get_model(combo_box); + if (!gtk_combo_box_get_active_iter(combo_box, &iter)) return; - - type = GPOINTER_TO_INT(g_object_get_data(w, "type")); - - if (type == PURPLE_PREF_INT) { - gtk_tree_model_get(tree_model, &iter, - PREF_DROPDOWN_VALUE, &int_value, - -1); - - purple_prefs_set_int(key, int_value); + active.type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo_box), + "type")); + + g_object_set_data(G_OBJECT(combo_box), "previously_active", + g_object_get_data(G_OBJECT(combo_box), "current_active")); + g_object_set_data(G_OBJECT(combo_box), "current_active", + GINT_TO_POINTER(gtk_combo_box_get_active(combo_box))); + + if (active.type == PURPLE_PREF_INT) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &active.value.integer, -1); } - else if (type == PURPLE_PREF_STRING) { - gtk_tree_model_get(tree_model, &iter, - PREF_DROPDOWN_VALUE, &str_value, - -1); - - purple_prefs_set_string(key, str_value); + else if (active.type == PURPLE_PREF_STRING) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &active.value.string, -1); + } + else if (active.type == PURPLE_PREF_BOOLEAN) { + gtk_tree_model_get(tree_model, &iter, PREF_DROPDOWN_VALUE, + &active.value.boolean, -1); } - else if (type == PURPLE_PREF_BOOLEAN) { - gtk_tree_model_get(tree_model, &iter, - PREF_DROPDOWN_VALUE, &bool_value, - -1); - - purple_prefs_set_bool(key, bool_value); - } + + cb(combo_box, active); } -GtkWidget * -pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title, - PurplePrefType type, const char *key, GList *menuitems) +static void pidgin_prefs_dropdown_revert_active(GtkComboBox *combo_box) { - GtkWidget *dropdown; - GtkWidget *label = NULL; - gchar *text; - const char *stored_str = NULL; - int stored_int = 0; - gboolean stored_bool = FALSE; - int int_value = 0; - const char *str_value = NULL; - gboolean bool_value = FALSE; + gint previously_active; + + g_return_if_fail(combo_box != NULL); + + previously_active = GPOINTER_TO_INT(g_object_get_data( + G_OBJECT(combo_box), "previously_active")); + g_object_set_data(G_OBJECT(combo_box), "current_active", + GINT_TO_POINTER(previously_active)); + + gtk_combo_box_set_active(combo_box, previously_active); +} + +static GtkWidget * +pidgin_prefs_dropdown_from_list_with_cb(GtkWidget *box, const gchar *title, + GtkComboBox **dropdown_out, GList *menuitems, + PidginPrefValue initial, PidginPrefsDropdownCallback cb) +{ + GtkWidget *dropdown; + GtkWidget *label = NULL; + gchar *text; GtkListStore *store = NULL; GtkTreeIter iter; GtkTreeIter active; GtkCellRenderer *renderer; + gpointer current_active; g_return_val_if_fail(menuitems != NULL, NULL); - if (type == PURPLE_PREF_INT) { + if (initial.type == PURPLE_PREF_INT) { store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_INT); - stored_int = purple_prefs_get_int(key); - } else if (type == PURPLE_PREF_STRING) { + } else if (initial.type == PURPLE_PREF_STRING) { store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_STRING); - stored_str = purple_prefs_get_string(key); - } else if (type == PURPLE_PREF_BOOLEAN) { + } else if (initial.type == PURPLE_PREF_BOOLEAN) { store = gtk_list_store_new(PREF_DROPDOWN_COUNT, G_TYPE_STRING, G_TYPE_BOOLEAN); - stored_bool = purple_prefs_get_bool(key); } else { g_warn_if_reached(); return NULL; } dropdown = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); - g_object_set_data(G_OBJECT(dropdown), "type", GINT_TO_POINTER(type)); + if (dropdown_out != NULL) + *dropdown_out = GTK_COMBO_BOX(dropdown); + g_object_set_data(G_OBJECT(dropdown), "type", GINT_TO_POINTER(initial.type)); while (menuitems != NULL && (text = (char *)menuitems->data) != NULL) { + int int_value = 0; + const char *str_value = NULL; + gboolean bool_value = FALSE; + menuitems = g_list_next(menuitems); g_return_val_if_fail(menuitems != NULL, NULL); @@ -359,30 +390,31 @@ PREF_DROPDOWN_TEXT, text, -1); - if (type == PURPLE_PREF_INT) { + if (initial.type == PURPLE_PREF_INT) { int_value = GPOINTER_TO_INT(menuitems->data); gtk_list_store_set(store, &iter, PREF_DROPDOWN_VALUE, int_value, -1); } - else if (type == PURPLE_PREF_STRING) { + else if (initial.type == PURPLE_PREF_STRING) { str_value = (const char *)menuitems->data; gtk_list_store_set(store, &iter, PREF_DROPDOWN_VALUE, str_value, -1); } - else if (type == PURPLE_PREF_BOOLEAN) { + else if (initial.type == PURPLE_PREF_BOOLEAN) { bool_value = (gboolean)GPOINTER_TO_INT(menuitems->data); gtk_list_store_set(store, &iter, PREF_DROPDOWN_VALUE, bool_value, -1); } - if ((type == PURPLE_PREF_INT && stored_int == int_value) || - (type == PURPLE_PREF_STRING && stored_str != NULL && - !strcmp(stored_str, str_value)) || - (type == PURPLE_PREF_BOOLEAN && - (stored_bool == bool_value))) { + if ((initial.type == PURPLE_PREF_INT && + initial.value.integer == int_value) || + (initial.type == PURPLE_PREF_STRING && + !g_strcmp0(initial.value.string, str_value)) || + (initial.type == PURPLE_PREF_BOOLEAN && + (initial.value.boolean == bool_value))) { active = iter; } @@ -397,15 +429,65 @@ NULL); gtk_combo_box_set_active_iter(GTK_COMBO_BOX(dropdown), &active); + current_active = GINT_TO_POINTER(gtk_combo_box_get_active(GTK_COMBO_BOX( + dropdown))); + g_object_set_data(G_OBJECT(dropdown), "current_active", current_active); + g_object_set_data(G_OBJECT(dropdown), "previously_active", current_active); g_signal_connect(G_OBJECT(dropdown), "changed", - G_CALLBACK(dropdown_set), (char *)key); + G_CALLBACK(dropdown_set), cb); pidgin_add_widget_to_vbox(GTK_BOX(box), title, NULL, dropdown, FALSE, &label); return label; } +static void +pidgin_prefs_dropdown_from_list_cb(GtkComboBox *combo_box, + PidginPrefValue value) +{ + const char *key; + + key = g_object_get_data(G_OBJECT(combo_box), "key"); + + if (value.type == PURPLE_PREF_INT) { + purple_prefs_set_int(key, value.value.integer); + } else if (value.type == PURPLE_PREF_STRING) { + purple_prefs_set_string(key, value.value.string); + } else if (value.type == PURPLE_PREF_BOOLEAN) { + purple_prefs_set_bool(key, value.value.boolean); + } else { + g_return_if_reached(); + } +} + +GtkWidget * +pidgin_prefs_dropdown_from_list(GtkWidget *box, const gchar *title, + PurplePrefType type, const char *key, GList *menuitems) +{ + PidginPrefValue initial; + GtkComboBox *dropdown = NULL; + GtkWidget *label; + + initial.type = type; + if (type == PURPLE_PREF_INT) { + initial.value.integer = purple_prefs_get_int(key); + } else if (type == PURPLE_PREF_STRING) { + initial.value.string = purple_prefs_get_string(key); + } else if (type == PURPLE_PREF_BOOLEAN) { + initial.value.boolean = purple_prefs_get_bool(key); + } else { + g_return_val_if_reached(NULL); + } + + label = pidgin_prefs_dropdown_from_list_with_cb(box, title, &dropdown, + menuitems, initial, pidgin_prefs_dropdown_from_list_cb); + + g_object_set_data(G_OBJECT(dropdown), "key", (gpointer)key); + + return label; +} + GtkWidget * pidgin_prefs_dropdown(GtkWidget *box, const gchar *title, PurplePrefType type, const char *key, ...) @@ -448,12 +530,16 @@ return dropdown; } +static void keyring_page_cleanup(void); + static void delete_prefs(GtkWidget *asdf, void *gdsa) { /* Close any "select sound" request dialogs */ purple_request_close_with_handle(prefs); + purple_notify_close_with_handle(prefs); + /* Unregister callbacks. */ purple_prefs_disconnect_by_handle(prefs); @@ -469,6 +555,8 @@ prefs_status_themes_combo_box = NULL; prefs_smiley_themes_combo_box = NULL; + keyring_page_cleanup(); + sample_webview = NULL; notebook_page = 0; @@ -2559,6 +2647,262 @@ return ret; } +/*** keyring page *******************************************************/ + +static void +keyring_page_settings_changed(GtkWidget *widget, gpointer _setting) +{ + PurpleRequestField *setting = _setting; + PurpleRequestFieldType field_type; + + gtk_widget_set_sensitive(keyring_apply, TRUE); + + field_type = purple_request_field_get_type(setting); + + if (field_type == PURPLE_REQUEST_FIELD_BOOLEAN) { + purple_request_field_bool_set_value(setting, + gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(widget))); + } else if (field_type == PURPLE_REQUEST_FIELD_STRING) { + purple_request_field_string_set_value(setting, + gtk_entry_get_text(GTK_ENTRY(widget))); + } else if (field_type == PURPLE_REQUEST_FIELD_INTEGER) { + purple_request_field_int_set_value(setting, + gtk_spin_button_get_value_as_int( + GTK_SPIN_BUTTON(widget))); + } else + g_return_if_reached(); +} + +static GtkWidget * +keyring_page_add_settings_field(GtkBox *vbox, PurpleRequestField *setting, + GtkSizeGroup *sg) +{ + GtkWidget *widget, *hbox; + PurpleRequestFieldType field_type; + const gchar *label; + + label = purple_request_field_get_label(setting); + + field_type = purple_request_field_get_type(setting); + if (field_type == PURPLE_REQUEST_FIELD_BOOLEAN) { + widget = gtk_check_button_new_with_label(label); + label = NULL; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), + purple_request_field_bool_get_value(setting)); + g_signal_connect(G_OBJECT(widget), "toggled", + G_CALLBACK(keyring_page_settings_changed), setting); + } else if (field_type == PURPLE_REQUEST_FIELD_STRING) { + widget = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(widget), + purple_request_field_string_get_value(setting)); + if (purple_request_field_string_is_masked(setting)) + gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE); + g_signal_connect(G_OBJECT(widget), "changed", + G_CALLBACK(keyring_page_settings_changed), setting); + } else if (field_type == PURPLE_REQUEST_FIELD_INTEGER) { + widget = gtk_spin_button_new_with_range( + purple_request_field_int_get_lower_bound(setting), + purple_request_field_int_get_upper_bound(setting), 1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), + purple_request_field_int_get_value(setting)); + g_signal_connect(G_OBJECT(widget), "value-changed", + G_CALLBACK(keyring_page_settings_changed), setting); + } else { + purple_debug_error("gtkprefs", "Unsupported field type\n"); + return NULL; + } + + hbox = pidgin_add_widget_to_vbox(vbox, label, sg, widget, + FALSE, NULL); + return ((void*)hbox == (void*)vbox) ? widget : hbox; +} + +/* XXX: it could be available for all plugins, not keyrings only */ +static GList * +keyring_page_add_settings(PurpleRequestFields *settings) +{ + GList *it, *groups, *added_fields; + GtkSizeGroup *sg; + + sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + added_fields = NULL; + groups = purple_request_fields_get_groups(settings); + for (it = g_list_first(groups); it != NULL; it = g_list_next(it)) { + GList *it2, *fields; + GtkBox *vbox; + PurpleRequestFieldGroup *group; + const gchar *group_title; + + group = it->data; + group_title = purple_request_field_group_get_title(group); + if (group_title) { + vbox = GTK_BOX(pidgin_make_frame( + GTK_WIDGET(keyring_vbox), group_title)); + added_fields = g_list_prepend(added_fields, + g_object_get_data(G_OBJECT(vbox), "main-vbox")); + } else + vbox = keyring_vbox; + + fields = purple_request_field_group_get_fields(group); + for (it2 = g_list_first(fields); it2 != NULL; + it2 = g_list_next(it2)) { + GtkWidget *added = keyring_page_add_settings_field(vbox, + it2->data, sg); + if (added == NULL || vbox != keyring_vbox) + continue; + added_fields = g_list_prepend(added_fields, added); + } + } + + g_object_unref(sg); + + return added_fields; +} + +static void +keyring_page_settings_apply(GtkButton *button, gpointer _unused) +{ + if (!purple_keyring_apply_settings(prefs, keyring_settings)) + return; + + gtk_widget_set_sensitive(keyring_apply, FALSE); +} + +static void +keyring_page_update_settings() +{ + if (keyring_settings != NULL) + purple_request_fields_destroy(keyring_settings); + keyring_settings = purple_keyring_read_settings(); + if (!keyring_settings) + return; + + keyring_settings_fields = keyring_page_add_settings(keyring_settings); + + keyring_apply = gtk_button_new_with_mnemonic(_("_Apply")); + gtk_box_pack_start(keyring_vbox, keyring_apply, FALSE, FALSE, 1); + gtk_widget_set_sensitive(keyring_apply, FALSE); + keyring_settings_fields = g_list_prepend(keyring_settings_fields, + keyring_apply); + g_signal_connect(G_OBJECT(keyring_apply), "clicked", + G_CALLBACK(keyring_page_settings_apply), NULL); + + gtk_widget_show_all(keyring_page_instance); +} + +static void +keyring_page_pref_set_inuse(GError *error, gpointer _keyring_page_instance) +{ + PurpleKeyring *in_use = purple_keyring_get_inuse(); + + if (_keyring_page_instance != keyring_page_instance) { + purple_debug_info("gtkprefs", "pref window already closed\n"); + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(keyring_combo), TRUE); + + if (error != NULL) { + pidgin_prefs_dropdown_revert_active(keyring_combo); + purple_notify_error(NULL, _("Keyring"), + _("Failed to set new keyring"), error->message); + return; + } + + g_return_if_fail(in_use != NULL); + purple_prefs_set_string("/purple/keyring/active", + purple_keyring_get_id(in_use)); + + keyring_page_update_settings(); +} + +static void +keyring_page_pref_changed(GtkComboBox *combo_box, PidginPrefValue value) +{ + const char *keyring_id; + PurpleKeyring *keyring; + GList *it; + + g_return_if_fail(combo_box != NULL); + g_return_if_fail(value.type == PURPLE_PREF_STRING); + + keyring_id = value.value.string; + keyring = purple_keyring_find_keyring_by_id(keyring_id); + if (keyring == NULL) { + pidgin_prefs_dropdown_revert_active(keyring_combo); + purple_notify_error(NULL, _("Keyring"), + _("Selected keyring is disabled"), NULL); + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(combo_box), FALSE); + + for (it = keyring_settings_fields; it != NULL; it = g_list_next(it)) + { + GtkWidget *widget = it->data; + gtk_container_remove( + GTK_CONTAINER(gtk_widget_get_parent(widget)), widget); + } + gtk_widget_show_all(keyring_page_instance); + g_list_free(keyring_settings_fields); + keyring_settings_fields = NULL; + if (keyring_settings) + purple_request_fields_destroy(keyring_settings); + keyring_settings = NULL; + + purple_keyring_set_inuse(keyring, FALSE, keyring_page_pref_set_inuse, + keyring_page_instance); +} + +static void +keyring_page_cleanup(void) +{ + keyring_page_instance = NULL; + keyring_combo = NULL; + keyring_vbox = NULL; + g_list_free(keyring_settings_fields); + keyring_settings_fields = NULL; + if (keyring_settings) + purple_request_fields_destroy(keyring_settings); + keyring_settings = NULL; + keyring_apply = NULL; +} + +static GtkWidget * +keyring_page(void) +{ + GList *names; + PidginPrefValue initial; + + g_return_val_if_fail(keyring_page_instance == NULL, + keyring_page_instance); + + keyring_page_instance = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + gtk_container_set_border_width(GTK_CONTAINER(keyring_page_instance), + PIDGIN_HIG_BORDER); + + /* Keyring selection */ + keyring_vbox = GTK_BOX(pidgin_make_frame(keyring_page_instance, + _("Keyring"))); + names = purple_keyring_get_options(); + initial.type = PURPLE_PREF_STRING; + initial.value.string = purple_prefs_get_string("/purple/keyring/active"); + pidgin_prefs_dropdown_from_list_with_cb(GTK_WIDGET(keyring_vbox), + _("Keyring:"), &keyring_combo, names, initial, + keyring_page_pref_changed); + g_list_free(names); + + keyring_page_update_settings(); + + gtk_widget_show_all(keyring_page_instance); + + return keyring_page_instance; +} + +/*** keyring page - end *************************************************/ + static gint sound_cmd_yeah(GtkEntry *entry, gpointer d) { @@ -3105,18 +3449,25 @@ #if !GST_CHECK_VERSION(1,0,0) GstPropertyProbe *probe; const GParamSpec *pspec; + gint i; + GValueArray *array; + enum { + PROBE_NONE, + PROBE_DEVICE, + PROBE_NAME + } probe_attr; #endif - ret = g_list_prepend(ret, (gpointer)_("Default")); - ret = g_list_prepend(ret, ""); + ret = g_list_prepend(ret, g_strdup(_("Default"))); + ret = g_list_prepend(ret, g_strdup("")); if (!strcmp(element_name, "<custom>") || (*element_name == '\0')) { return g_list_reverse(ret); } - if (g_strcmp0(element_name, "disabled") == 0) { - ret = g_list_prepend(ret, (gpointer)_("Random noise")); - ret = g_list_prepend(ret, "snow"); + if (g_strcmp0(element_name, "videodisabledsrc") == 0) { + ret = g_list_prepend(ret, g_strdup(_("Random noise"))); + ret = g_list_prepend(ret, g_strdup("snow")); return g_list_reverse(ret); } @@ -3124,70 +3475,112 @@ element = gst_element_factory_make(element_name, "test"); if (!element) { purple_debug_info("vvconfig", "'%s' - unable to find element\n", - element_name); + element_name); return g_list_reverse(ret); } klass = G_OBJECT_GET_CLASS (element); if (!klass) { - purple_debug_info("vvconfig", "'%s' - unable to find GObject Class\n", - element_name); + purple_debug_info("vvconfig", "'%s' - unable to find GObject " + "Class\n", element_name); return g_list_reverse(ret); } #if GST_CHECK_VERSION(1,0,0) - purple_debug_info("vvconfig", "'%s' - no device\n", element_name); + purple_debug_info("vvconfig", "'%s' - gstreamer-1.0 doesn't suport " + "property probing\n", element_name); #else - if (!g_object_class_find_property(klass, "device") || - !GST_IS_PROPERTY_PROBE(element) || - !(probe = GST_PROPERTY_PROBE(element)) || - !(pspec = gst_property_probe_get_property(probe, "device"))) { - purple_debug_info("vvconfig", "'%s' - no device\n", element_name); - } else { - gint n; - GValueArray *array; - - /* Set autoprobe[-fps] to FALSE to avoid delays when probing. */ - if (g_object_class_find_property(klass, "autoprobe")) { - g_object_set(G_OBJECT(element), "autoprobe", FALSE, NULL); - if (g_object_class_find_property(klass, "autoprobe-fps")) { - g_object_set(G_OBJECT(element), "autoprobe-fps", FALSE, NULL); - } - } - - array = gst_property_probe_probe_and_get_values(probe, pspec); - if (array == NULL) { - purple_debug_info("vvconfig", "'%s' has no devices\n", element_name); - return g_list_reverse(ret); - } - - for (n = 0; n < array->n_values; ++n) { - GValue *device; - const gchar *name; - const gchar *device_name; - - device = g_value_array_get_nth(array, n); - g_object_set_property(G_OBJECT(element), "device", device); - if (gst_element_set_state(element, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { - purple_debug_warning("vvconfig", "Error changing state of %s\n", - element_name); + if (g_object_class_find_property(klass, "device")) + probe_attr = PROBE_DEVICE; + else if (g_object_class_find_property(klass, "device-index") && + g_object_class_find_property(klass, "device-name")) + probe_attr = PROBE_NAME; + else + probe_attr = PROBE_NONE; + + if (!GST_IS_PROPERTY_PROBE(element)) + probe_attr = PROBE_NONE; + + if (probe_attr == PROBE_NONE) + { + purple_debug_info("vvconfig", "'%s' - no possibility to probe " + "for devices\n", element_name); + gst_object_unref(element); + return g_list_reverse(ret); + } + + probe = GST_PROPERTY_PROBE(element); + + if (probe_attr == PROBE_DEVICE) + pspec = gst_property_probe_get_property(probe, "device"); + else /* probe_attr == PROBE_NAME */ + pspec = gst_property_probe_get_property(probe, "device-name"); + + if (!pspec) { + purple_debug_info("vvconfig", "'%s' - creating probe failed\n", + element_name); + gst_object_unref(element); + return g_list_reverse(ret); + } + + /* Set autoprobe[-fps] to FALSE to avoid delays when probing. */ + if (g_object_class_find_property(klass, "autoprobe")) + g_object_set(G_OBJECT(element), "autoprobe", FALSE, NULL); + if (g_object_class_find_property(klass, "autoprobe-fps")) + g_object_set(G_OBJECT(element), "autoprobe-fps", FALSE, NULL); + + array = gst_property_probe_probe_and_get_values(probe, pspec); + if (array == NULL) { + purple_debug_info("vvconfig", "'%s' has no devices\n", + element_name); + gst_object_unref(element); + return g_list_reverse(ret); + } + + for (i = 0; i < array->n_values; i++) { + GValue *device; + const gchar *name; + const gchar *device_name; + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + /* GValueArray is in gstreamer-0.10 API */ + device = g_value_array_get_nth(array, i); +G_GNUC_END_IGNORE_DEPRECATIONS + + if (probe_attr == PROBE_DEVICE) { + g_object_set_property(G_OBJECT(element), "device", + device); + if (gst_element_set_state(element, GST_STATE_READY) + != GST_STATE_CHANGE_SUCCESS) + { + purple_debug_warning("vvconfig", "Error " + "changing state of %s\n", element_name); continue; } - g_object_get(G_OBJECT(element), "device-name", &name, NULL); - device_name = g_value_get_string(device); - if (name == NULL) - name = _("Unknown"); - purple_debug_info("vvconfig", "Found device %s : %s for %s\n", - device_name, name, element_name); - ret = g_list_prepend(ret, (gpointer)name); - ret = g_list_prepend(ret, (gpointer)device_name); - gst_element_set_state(element, GST_STATE_NULL); + g_object_get(G_OBJECT(element), "device-name", &name, + NULL); + device_name = g_strdup(g_value_get_string(device)); + } else /* probe_attr == PROBE_NAME */ { + name = g_strdup(g_value_get_string(device)); + device_name = g_strdup_printf("%d", i); } + + if (name == NULL) + name = _("Unknown"); + purple_debug_info("vvconfig", "Found device %s: %s for %s\n", + device_name, name, element_name); + ret = g_list_prepend(ret, (gpointer)name); + ret = g_list_prepend(ret, (gpointer)device_name); + gst_element_set_state(element, GST_STATE_NULL); } +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + /* GValueArray is in gstreamer-0.10 API */ + g_value_array_free(array); +G_GNUC_END_IGNORE_DEPRECATIONS #endif + gst_object_unref(element); - return g_list_reverse(ret); } @@ -3205,7 +3598,7 @@ #else if (gst_default_registry_check_feature_version(plugins[0], 0, 0, 0) #endif - || g_strcmp0(plugins[0], "disabled") == 0) + || g_strcmp0(plugins[0], "videodisabledsrc") == 0) { ret = g_list_prepend(ret, (gpointer)plugins[1]); ret = g_list_prepend(ret, (gpointer)plugins[0]); @@ -3237,7 +3630,7 @@ purple_prefs_set_string(pref, g_list_next(devices)->data); widget = pidgin_prefs_dropdown_from_list(vbox, _("_Device"), PURPLE_PREF_STRING, pref, devices); - g_list_free(devices); + g_list_free_full(devices, g_free); gtk_size_group_add_widget(sg, widget); gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5); @@ -3273,7 +3666,7 @@ widget = pidgin_prefs_dropdown_from_list(vbox, _("_Device"), PURPLE_PREF_STRING, device_pref, devices); - g_list_free(devices); + g_list_free_full(devices, g_free); gtk_size_group_add_widget(sg, widget); gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5); @@ -3715,6 +4108,7 @@ prefs_notebook_add_page(_("Logging"), logging_page(), notebook_page++); prefs_notebook_add_page(_("Network"), network_page(), notebook_page++); prefs_notebook_add_page(_("Proxy"), proxy_page(), notebook_page++); + prefs_notebook_add_page(_("Password Storage"), keyring_page(), notebook_page++); prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++); prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++);
--- a/pidgin/gtkutils.c Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/gtkutils.c Mon Jun 10 01:22:52 2013 +0530 @@ -483,7 +483,7 @@ GtkWidget * pidgin_make_frame(GtkWidget *parent, const char *title) { - GtkWidget *vbox, *label, *hbox; + GtkWidget *vbox, *vbox2, *label, *hbox; char *labeltitle; vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); @@ -509,11 +509,13 @@ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); - vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); - gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); - gtk_widget_show(vbox); - - return vbox; + vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 0); + gtk_widget_show(vbox2); + + g_object_set_data(G_OBJECT(vbox2), "main-vbox", vbox); + + return vbox2; } static gpointer
--- a/pidgin/win32/gtkwin32dep.c Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/win32/gtkwin32dep.c Mon Jun 10 01:22:52 2013 +0530 @@ -390,7 +390,8 @@ LPFNSETLOGFILE MySetLogFile; gchar *exchndl_dll_path; - purple_debug_info("winpidgin", "winpidgin_init start\n"); + if (purple_debug_is_verbose()) + purple_debug_misc("winpidgin", "winpidgin_init start\n"); exe_hInstance = hint; @@ -415,12 +416,13 @@ #ifdef USE_GTKSPELL winpidgin_spell_init(); #endif - purple_debug_info("winpidgin", "GTK+ :%u.%u.%u\n", + purple_debug_info("winpidgin", "GTK+: %u.%u.%u\n", gtk_major_version, gtk_minor_version, gtk_micro_version); messagewin_hwnd = winpidgin_message_window_init(); - purple_debug_info("winpidgin", "winpidgin_init end\n"); + if (purple_debug_is_verbose()) + purple_debug_misc("winpidgin", "winpidgin_init end\n"); } void winpidgin_post_init(void) {
--- a/pidgin/win32/nsis/generate_gtk_zip.sh Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/win32/nsis/generate_gtk_zip.sh Mon Jun 10 01:22:52 2013 +0530 @@ -18,7 +18,7 @@ #This needs to be changed every time there is any sort of change. BUNDLE_VERSION=2.24.14.0 -BUNDLE_SHA1SUM="dfbefd657bd9040dab4e02b93e1c656e791da8b4" +BUNDLE_SHA1SUM="df28047f00934e6a00a5962387a1005114ec772e" ZIP_FILE="$PIDGIN_BASE/pidgin/win32/nsis/gtk-runtime-$BUNDLE_VERSION.zip" #BUNDLE_URL="https://pidgin.im/win32/download_redir.php?version=$PIDGIN_VERSION>k_version=$BUNDLE_VERSION&dl_pkg=gtk" BUNDLE_URL="https://dl.dropbox.com/u/5448886/pidgin-win32/gtk-runtime-2.24.14.0.zip" @@ -77,55 +77,169 @@ #TODO: this is just a temporary mirror - Tomek Wasilczyk's <tomkiewicz@cpw.pidgin.im> Dropbox DOWNLOAD_HOST="https://dl.dropbox.com/u/5448886/pidgin-win32/runtime-deps/" -ATK="${DOWNLOAD_HOST}mingw32-atk-2.8.0-1.5.noarch.rpm ATK 2.8.0-1.5 sha1sum:0c682eadc299963aaa5d7998d655e46ead7d7515" -CAIRO2="${DOWNLOAD_HOST}mingw32-libcairo2-1.10.2-8.12.noarch.rpm Cairo 1.10.2-8.12 sha1sum:e33c58603678a8ba113bffe282bec810bd70172e" -DBUS="${DOWNLOAD_HOST}mingw32-dbus-1-1.7.0-2.1.noarch.rpm D-Bus 1.7.0-2.1 sha1sum:6961e4df15172b057e91b266dbb7cbd01a736e56" -DBUS_GLIB="${DOWNLOAD_HOST}mingw32-dbus-1-glib-0.100.2-1.2.noarch.rpm dbus-glib 0.100.2-1.2 sha1sum:72d27c0c01ce3e94e4dddc653c6ac0544c28362f" -ENCHANT="${DOWNLOAD_HOST}mingw32-enchant-1.6.0-3.9.noarch.rpm Enchant 1.6.0-3.9 sha1sum:a8569c8dcc77f69d065320388fca822ef5bf1fe5" -FONTCONFIG="${DOWNLOAD_HOST}mingw32-fontconfig-2.10.92-1.2.noarch.rpm fontconfig 2.10.92-1.2 sha1sum:d8da351f5d4d9816f02ac5287dd7887c711902ed" -FREETYPE="${DOWNLOAD_HOST}mingw32-freetype-2.4.11-2.1.noarch.rpm freetype 2.4.11-2.1 sha1sum:0eee29696e87d47e7487ba5e90530bf20159f31e" -GDK_PIXBUF="${DOWNLOAD_HOST}mingw32-gdk-pixbuf-2.28.0-1.2.noarch.rpm gdk-pixbuf 2.28.0-1.2 sha1sum:8673e06c3a838e47a093043bf86bb62ea3627fe0" -GEOCLUE="${DOWNLOAD_HOST}mingw32-libgeoclue-0.12.99-1.10.noarch.rpm Geoclue 0.12.99-1.10 sha1sum:84410ca9a6d2fac46217c51e22ebbc5ac3cae040" -GLIB="${DOWNLOAD_HOST}mingw32-glib2-2.36.1-1.1.noarch.rpm Glib 2.36.1-1.1 sha1sum:ed468f064f61c5a12b716c83cba8ccbe05d22992" -GNUTLS="${DOWNLOAD_HOST}mingw32-libgnutls-2.12.22-2.2.noarch.rpm GnuTLS 2.12.22-2.2 sha1sum:ee65a8971582f55aa469dbce82eb180fb1b35705" -GNUTLS_GCRYPT="${DOWNLOAD_HOST}mingw32-libgcrypt-1.5.2-1.1.noarch.rpm libgcrypt 1.5.2-1.1 sha1sum:861335a6edaa8419bc8f2d4ba6104c8da197e8e2" -GNUTLS_GPGERR="${DOWNLOAD_HOST}mingw32-libgpg-error-1.10-1.6.noarch.rpm gpg-error 1.10-1.6 sha1sum:51a649ee41167ed3fafd243f8ded5d30b53f213d" -GST="${DOWNLOAD_HOST}mingw32-libgstreamer-0.10.35-1.10.noarch.rpm GStreamer 0.10.35-1.10 sha1sum:7097499f3a34b42c25a200fa18f5df6f3f3ba527" -GST_INT="${DOWNLOAD_HOST}mingw32-libgstinterfaces-0.10.32-5.10.noarch.rpm GStreamer-interfaces 0.10.32-5.10 sha1sum:29cfb0668a2e54287ea969eab1a4f3672a09c04a" -GTK2="${DOWNLOAD_HOST}mingw32-gtk2-2.24.14-2.7.noarch.rpm GTK+ 2.24.14-2.7 sha1sum:a6f98a0c974c0273127c6423d89fca5f48c7d30c" -GTKSPELL="${DOWNLOAD_HOST}mingw32-gtkspell-2.0.16-2.10.noarch.rpm GtkSpell 2.0.16-2.10 sha1sum:623afdc7cc2c43c1f5d39be797c3ec8ee1ab5570" -LIBFFI="${DOWNLOAD_HOST}mingw32-libffi-3.0.10-2.7.noarch.rpm libffi 3.0.10-2.7 sha1sum:628b014349dc132d3aa46362b30fc1cdd61f6b97" -LIBGCC="${DOWNLOAD_HOST}mingw32-libgcc-4.8.0-6.1.noarch.rpm libgcc 4.8.0-6.1 sha1sum:ab599bf07bf2d56367c57b442440598358c943af" -LIBGNURX="${DOWNLOAD_HOST}mingw32-libgnurx-2.5-4.6.noarch.rpm libgnurx 2.5-4.6 sha1sum:51571e6b1e5e9fb865c110cae04c582ff3c44cb7" -LIBHB="${DOWNLOAD_HOST}mingw32-libharfbuzz0-0.9.16-3.1.noarch.rpm libharfbuzz 0.9.16-3.1 sha1sum:5c377190429f45e566b07439c99937798c4c13f0" -LIBJASPER="${DOWNLOAD_HOST}mingw32-libjasper-1.900.1-6.6.noarch.rpm JasPer 1.900.1-6.6 sha1sum:1a0f0072e0b0f73bd8d4e26aed93baa10d77e504" -LIBICU="${DOWNLOAD_HOST}mingw32-libicu-51.1-2.3.noarch.rpm ICU 51.1-2.3 sha1sum:c259c9d7f9f58934ebb49ecc80b15b7492e5a245" -LIBINTL="${DOWNLOAD_HOST}mingw32-libintl-0.18.1.1-13.6.noarch.rpm libintl 0.18.1.1-13.6 sha1sum:0e6fde8e86788874366f308e25634f95613e906a" -LIBJPEG="${DOWNLOAD_HOST}mingw32-libjpeg-8d-3.6.noarch.rpm libjpeg 8d-3.6 sha1sum:db85723377243045388a5d3c873262cd83ffa7e2" -LIBJSON="${DOWNLOAD_HOST}mingw32-libjson-glib-0.14.2-2.1.noarch.rpm json-glib 0.14.2-2.1 sha1sum:366bf545855ced7fdfefc57b75ef7bbb5ebc249b" -LIBLZMA="${DOWNLOAD_HOST}mingw32-liblzma-5.0.4-1.6.noarch.rpm liblzma 5.0.4-1.6 sha1sum:67bad5204ae09d163f799adec3286fff297e3bc8" -LIBPNG="${DOWNLOAD_HOST}mingw32-libpng-1.5.11-1.6.noarch.rpm libpng 1.5.11-1.6 sha1sum:bb28549351c1f0d7a8afd129ac656be18a616149" -LIBSILC="${DOWNLOAD_HOST}mingw32-libsilc-1.1.10-2.1.noarch.rpm libsilc 1.1.10-2.1 sha1sum:b7690eac1a91caf2b02b058483a3768705a6f3df" -LIBSILCCL="${DOWNLOAD_HOST}mingw32-libsilcclient-1.1.10-2.1.noarch.rpm libsilcclient 1.1.10-2.1 sha1sum:88b84ff4c43643ce4b8ec1eb345e73c139cc164a" -LIBSOUP="${DOWNLOAD_HOST}mingw32-libsoup-2.42.2-1.1.noarch.rpm libsoup 2.42.2-1.1 sha1sum:f0af29ceb420daaa549dd5dc470fbd62bc732252" -LIBSSP="${DOWNLOAD_HOST}mingw32-libssp-4.8.0-6.1.noarch.rpm LibSSP 4.8.0-6.1 sha1sum:c05b2e0470f41d26f8ebfff93dfd51263842a4ea" -LIBSTDCPP="${DOWNLOAD_HOST}mingw32-libstdc++-4.8.0-6.1.noarch.rpm libstdc++ 4.8.0-6.1 sha1sum:627860950e951764fe1aa229d3a63bb01618ba90" -LIBTIFF="${DOWNLOAD_HOST}mingw32-libtiff-4.0.2-1.6.noarch.rpm libtiff 4.0.2-1.6 sha1sum:3a082540386748ead608d388ce55a0c1dd28715d" -LIBXML="${DOWNLOAD_HOST}mingw32-libxml2-2.9.0-2.1.noarch.rpm libxml 2.9.0-2.1 sha1sum:de73090544effcd167f94fcfe8e2d1f005adbea7" -LIBXSLT="${DOWNLOAD_HOST}mingw32-libxslt-1.1.28-1.2.noarch.rpm libxslt 1.1.28-1.2 sha1sum:6ee150c6271edded95f92285f59d02c2896e459e" -MEANW="${DOWNLOAD_HOST}mingw32-meanwhile-1.0.2-3.2.noarch.rpm Meanwhile 1.0.2-3.2 sha1sum:6b0fd8d94205d80eba37ea3e3f19ded7a1297473" -MOZNSS="${DOWNLOAD_HOST}mingw32-mozilla-nss-3.14.3-2.2.noarch.rpm NSS 3.14.3-2.2 sha1sum:b0353cda8b5670796f0246f1da27754c3205a009" -MOZNSPR="${DOWNLOAD_HOST}mingw32-mozilla-nspr-4.9.6-4.1.noarch.rpm NSPR 4.9.6-4.1 sha1sum:42b1a803c54639f6cf0aa1f6287324d0658c705c" -PANGO="${DOWNLOAD_HOST}mingw32-pango-1.34.0-2.3.noarch.rpm Pango 1.34.0-2.3 sha1sum:65b55b73c4f5c8107fdf48ef2e4f5c351189cd4f" -PIXMAN="${DOWNLOAD_HOST}mingw32-pixman-0.26.0-1.6.noarch.rpm pixman 0.26.0-1.6 sha1sum:b0a440a3761e77d890a2e7de52405e2ce364c9b2" -PTHREADS="${DOWNLOAD_HOST}mingw32-pthreads-2.8.0-14.6.noarch.rpm pthreads 2.8.0-14.6 sha1sum:e948ae221f82bbcb4fbfd991638e4170c150fe9f" -SQLITE="${DOWNLOAD_HOST}mingw32-libsqlite-3.7.6.2-1.6.noarch.rpm SQLite 3.7.6.2-1.6 sha1sum:f61529bc0c996d9af28a94648ce6102d579ed928" -TCL="${DOWNLOAD_HOST}mingw32-tcl-8.5.9-14.1.noarch.rpm Tcl 8.5.9-14.1 sha1sum:1178fc95ab97d274504f3d76c3ebe9bdbb39847a" -TK="${DOWNLOAD_HOST}mingw32-tk-8.5.9-8.7.noarch.rpm Tk 8.5.9-8.7 sha1sum:4ec1595fdf06eaa9f4d38b433edc430217697995" -WEBKITGTK="${DOWNLOAD_HOST}mingw32-libwebkitgtk-1.10.2-9.2.noarch.rpm WebKitGTK+ 1.10.2-9.2 sha1sum:010dbad413f824696cd1e32fe70046c9a1cb425f" -ZLIB="${DOWNLOAD_HOST}mingw32-zlib-1.2.7-1.4.noarch.rpm zlib 1.2.7-1.4 sha1sum:83e91f3b4d14e47131ca33fc69e12b82aabdd589" +ALL="" + +ARC_ATK="${DOWNLOAD_HOST}mingw32-atk-2.8.0-1.5.noarch.rpm ATK 2.8.0-1.5 sha1sum:0c682eadc299963aaa5d7998d655e46ead7d7515" +ALL+="ARC_ATK " + +ARC_CAIRO2="${DOWNLOAD_HOST}mingw32-libcairo2-1.10.2-8.12.noarch.rpm Cairo 1.10.2-8.12 sha1sum:e33c58603678a8ba113bffe282bec810bd70172e" +ALL+="ARC_CAIRO2 " + +ARC_DBUS="${DOWNLOAD_HOST}mingw32-dbus-1-1.7.0-2.1.noarch.rpm D-Bus 1.7.0-2.1 sha1sum:6961e4df15172b057e91b266dbb7cbd01a736e56" +ALL+="ARC_DBUS " + +ARC_DBUS_GLIB="${DOWNLOAD_HOST}mingw32-dbus-1-glib-0.100.2-1.2.noarch.rpm dbus-glib 0.100.2-1.2 sha1sum:72d27c0c01ce3e94e4dddc653c6ac0544c28362f" +ALL+="ARC_DBUS_GLIB " + +ARC_ENCHANT="${DOWNLOAD_HOST}mingw32-enchant-1.6.0-3.9.noarch.rpm Enchant 1.6.0-3.9 sha1sum:a8569c8dcc77f69d065320388fca822ef5bf1fe5" +ALL+="ARC_ENCHANT " + +ARC_FONTCONFIG="${DOWNLOAD_HOST}mingw32-fontconfig-2.10.92-1.2.noarch.rpm fontconfig 2.10.92-1.2 sha1sum:d8da351f5d4d9816f02ac5287dd7887c711902ed" +ALL+="ARC_FONTCONFIG " + +ARC_FREETYPE="${DOWNLOAD_HOST}mingw32-freetype-2.4.11-2.1.noarch.rpm freetype 2.4.11-2.1 sha1sum:0eee29696e87d47e7487ba5e90530bf20159f31e" +ALL+="ARC_FREETYPE " + +ARC_GDK_PIXBUF="${DOWNLOAD_HOST}mingw32-gdk-pixbuf-2.28.0-1.2.noarch.rpm gdk-pixbuf 2.28.0-1.2 sha1sum:8673e06c3a838e47a093043bf86bb62ea3627fe0" +ALL+="ARC_GDK_PIXBUF " + +ARC_GEOCLUE="${DOWNLOAD_HOST}mingw32-libgeoclue-0.12.99-1.10.noarch.rpm Geoclue 0.12.99-1.10 sha1sum:84410ca9a6d2fac46217c51e22ebbc5ac3cae040" +ALL+="ARC_GEOCLUE " + +ARC_GLIB="${DOWNLOAD_HOST}mingw32-glib2-2.36.1-1.1.noarch.rpm Glib 2.36.1-1.1 sha1sum:ed468f064f61c5a12b716c83cba8ccbe05d22992" +ALL+="ARC_GLIB " + +ARC_GNUTLS="${DOWNLOAD_HOST}mingw32-libgnutls-2.12.22-2.2.noarch.rpm GnuTLS 2.12.22-2.2 sha1sum:ee65a8971582f55aa469dbce82eb180fb1b35705" +ALL+="ARC_GNUTLS " + +ARC_GNUTLS_GCRYPT="${DOWNLOAD_HOST}mingw32-libgcrypt-1.5.2-1.1.noarch.rpm libgcrypt 1.5.2-1.1 sha1sum:861335a6edaa8419bc8f2d4ba6104c8da197e8e2" +ALL+="ARC_GNUTLS_GCRYPT " + +ARC_GNUTLS_GPGERR="${DOWNLOAD_HOST}mingw32-libgpg-error-1.10-1.6.noarch.rpm gpg-error 1.10-1.6 sha1sum:51a649ee41167ed3fafd243f8ded5d30b53f213d" +ALL+="ARC_GNUTLS_GPGERR " + +ARC_GTK2="${DOWNLOAD_HOST}mingw32-gtk2-2.24.14-2.7.noarch.rpm GTK+ 2.24.14-2.7 sha1sum:a6f98a0c974c0273127c6423d89fca5f48c7d30c" +ALL+="ARC_GTK2 " + +ARC_GTKSPELL="${DOWNLOAD_HOST}mingw32-gtkspell-2.0.16-2.10.noarch.rpm GtkSpell 2.0.16-2.10 sha1sum:623afdc7cc2c43c1f5d39be797c3ec8ee1ab5570" +ALL+="ARC_GTKSPELL " + +ARC_LIBFFI="${DOWNLOAD_HOST}mingw32-libffi-3.0.10-2.7.noarch.rpm libffi 3.0.10-2.7 sha1sum:628b014349dc132d3aa46362b30fc1cdd61f6b97" +ALL+="ARC_LIBFFI " + +ARC_LIBGCC="${DOWNLOAD_HOST}mingw32-libgcc-4.8.0-6.1.noarch.rpm libgcc 4.8.0-6.1 sha1sum:ab599bf07bf2d56367c57b442440598358c943af" +ALL+="ARC_LIBGCC " + +ARC_LIBGNURX="${DOWNLOAD_HOST}mingw32-libgnurx-2.5-4.6.noarch.rpm libgnurx 2.5-4.6 sha1sum:51571e6b1e5e9fb865c110cae04c582ff3c44cb7" +ALL+="ARC_LIBGNURX " + +ARC_LIBHB="${DOWNLOAD_HOST}mingw32-libharfbuzz0-0.9.16-3.1.noarch.rpm libharfbuzz 0.9.16-3.1 sha1sum:5c377190429f45e566b07439c99937798c4c13f0" +ALL+="ARC_LIBHB " + +ARC_LIBJASPER="${DOWNLOAD_HOST}mingw32-libjasper-1.900.1-6.6.noarch.rpm JasPer 1.900.1-6.6 sha1sum:1a0f0072e0b0f73bd8d4e26aed93baa10d77e504" +ALL+="ARC_LIBJASPER " + +ARC_LIBICU="${DOWNLOAD_HOST}mingw32-libicu-51.1-2.3.noarch.rpm ICU 51.1-2.3 sha1sum:c259c9d7f9f58934ebb49ecc80b15b7492e5a245" +ALL+="ARC_LIBICU " + +ARC_LIBINTL="${DOWNLOAD_HOST}mingw32-libintl-0.18.1.1-13.6.noarch.rpm libintl 0.18.1.1-13.6 sha1sum:0e6fde8e86788874366f308e25634f95613e906a" +ALL+="ARC_LIBINTL " + +ARC_LIBJPEG="${DOWNLOAD_HOST}mingw32-libjpeg-8d-3.6.noarch.rpm libjpeg 8d-3.6 sha1sum:db85723377243045388a5d3c873262cd83ffa7e2" +ALL+="ARC_LIBJPEG " + +ARC_LIBJSON="${DOWNLOAD_HOST}mingw32-libjson-glib-0.14.2-2.1.noarch.rpm json-glib 0.14.2-2.1 sha1sum:366bf545855ced7fdfefc57b75ef7bbb5ebc249b" +ALL+="ARC_LIBJSON " + +ARC_LIBLZMA="${DOWNLOAD_HOST}mingw32-liblzma-5.0.4-1.6.noarch.rpm liblzma 5.0.4-1.6 sha1sum:67bad5204ae09d163f799adec3286fff297e3bc8" +ALL+="ARC_LIBLZMA " + +ARC_LIBPNG="${DOWNLOAD_HOST}mingw32-libpng-1.5.11-1.6.noarch.rpm libpng 1.5.11-1.6 sha1sum:bb28549351c1f0d7a8afd129ac656be18a616149" +ALL+="ARC_LIBPNG " -ALL="ATK CAIRO2 DBUS DBUS_GLIB ENCHANT FONTCONFIG FREETYPE GDK_PIXBUF GEOCLUE GLIB GNUTLS GNUTLS_GCRYPT GNUTLS_GPGERR GST GST_INT GTK2 GTKSPELL LIBFFI LIBGCC LIBGNURX LIBHB LIBJASPER LIBICU LIBINTL LIBJPEG LIBJSON LIBLZMA LIBPNG LIBSILC LIBSILCCL LIBSOUP LIBSSP LIBSTDCPP LIBTIFF LIBXML LIBXSLT MEANW MOZNSS MOZNSPR PANGO PIXMAN PTHREADS SQLITE TCL TK WEBKITGTK ZLIB" +ARC_LIBSILC="${DOWNLOAD_HOST}mingw32-libsilc-1.1.10-2.1.noarch.rpm libsilc 1.1.10-2.1 sha1sum:b7690eac1a91caf2b02b058483a3768705a6f3df" +ALL+="ARC_LIBSILC " + +ARC_LIBSILCCL="${DOWNLOAD_HOST}mingw32-libsilcclient-1.1.10-2.1.noarch.rpm libsilcclient 1.1.10-2.1 sha1sum:88b84ff4c43643ce4b8ec1eb345e73c139cc164a" +ALL+="ARC_LIBSILCCL " + +ARC_LIBSOUP="${DOWNLOAD_HOST}mingw32-libsoup-2.42.2-1.1.noarch.rpm libsoup 2.42.2-1.1 sha1sum:f0af29ceb420daaa549dd5dc470fbd62bc732252" +ALL+="ARC_LIBSOUP " + +ARC_LIBSSP="${DOWNLOAD_HOST}mingw32-libssp-4.8.0-6.1.noarch.rpm LibSSP 4.8.0-6.1 sha1sum:c05b2e0470f41d26f8ebfff93dfd51263842a4ea" +ALL+="ARC_LIBSSP " + +ARC_LIBSTDCPP="${DOWNLOAD_HOST}mingw32-libstdc++-4.8.0-6.1.noarch.rpm libstdc++ 4.8.0-6.1 sha1sum:627860950e951764fe1aa229d3a63bb01618ba90" +ALL+="ARC_LIBSTDCPP " + +ARC_LIBTIFF="${DOWNLOAD_HOST}mingw32-libtiff-4.0.2-1.6.noarch.rpm libtiff 4.0.2-1.6 sha1sum:3a082540386748ead608d388ce55a0c1dd28715d" +ALL+="ARC_LIBTIFF " + +ARC_LIBXML="${DOWNLOAD_HOST}mingw32-libxml2-2.9.0-2.1.noarch.rpm libxml 2.9.0-2.1 sha1sum:de73090544effcd167f94fcfe8e2d1f005adbea7" +ALL+="ARC_LIBXML " + +ARC_LIBXSLT="${DOWNLOAD_HOST}mingw32-libxslt-1.1.28-1.2.noarch.rpm libxslt 1.1.28-1.2 sha1sum:6ee150c6271edded95f92285f59d02c2896e459e" +ALL+="ARC_LIBXSLT " + +ARC_MEANW="${DOWNLOAD_HOST}mingw32-meanwhile-1.0.2-3.2.noarch.rpm Meanwhile 1.0.2-3.2 sha1sum:6b0fd8d94205d80eba37ea3e3f19ded7a1297473" +ALL+="ARC_MEANW " + +ARC_MOZNSS="${DOWNLOAD_HOST}mingw32-mozilla-nss-3.14.3-2.2.noarch.rpm NSS 3.14.3-2.2 sha1sum:b0353cda8b5670796f0246f1da27754c3205a009" +ALL+="ARC_MOZNSS " + +ARC_MOZNSPR="${DOWNLOAD_HOST}mingw32-mozilla-nspr-4.9.6-4.1.noarch.rpm NSPR 4.9.6-4.1 sha1sum:42b1a803c54639f6cf0aa1f6287324d0658c705c" +ALL+="ARC_MOZNSPR " + +ARC_PANGO="${DOWNLOAD_HOST}mingw32-pango-1.34.0-2.3.noarch.rpm Pango 1.34.0-2.3 sha1sum:65b55b73c4f5c8107fdf48ef2e4f5c351189cd4f" +ALL+="ARC_PANGO " + +ARC_PIXMAN="${DOWNLOAD_HOST}mingw32-pixman-0.26.0-1.6.noarch.rpm pixman 0.26.0-1.6 sha1sum:b0a440a3761e77d890a2e7de52405e2ce364c9b2" +ALL+="ARC_PIXMAN " + +ARC_PTHREADS="${DOWNLOAD_HOST}mingw32-pthreads-2.8.0-14.6.noarch.rpm pthreads 2.8.0-14.6 sha1sum:e948ae221f82bbcb4fbfd991638e4170c150fe9f" +ALL+="ARC_PTHREADS " + +ARC_SQLITE="${DOWNLOAD_HOST}mingw32-libsqlite-3.7.6.2-1.6.noarch.rpm SQLite 3.7.6.2-1.6 sha1sum:f61529bc0c996d9af28a94648ce6102d579ed928" +ALL+="ARC_SQLITE " + +ARC_VV_FARST="${DOWNLOAD_HOST}mingw32-farstream-0.1.2-19.1.noarch.rpm farstream 0.1.2-19.1 sha1sum:1c46b2c2f6b669824a70ca05d3958b2dc2197dc3" +ALL+="ARC_VV_FARST " + +ARC_VV_GST="${DOWNLOAD_HOST}mingw32-gstreamer-0.10.36-10.1.noarch.rpm GStreamer 0.10.36-10.1 sha1sum:79ec8a1c0613211345efd0e88e7f3e5668e01150" +ALL+="ARC_VV_GST " + +ARC_VV_GST_LIB="${DOWNLOAD_HOST}mingw32-libgstreamer-0.10.36-10.1.noarch.rpm GStreamer-libgstreamer 0.10.36-10.1 sha1sum:3066c1047fba2181e688671fa9e12ed818d7df4a" +ALL+="ARC_VV_GST_LIB " + +ARC_VV_GST_INT="${DOWNLOAD_HOST}mingw32-libgstinterfaces-0.10.36-15.1.noarch.rpm GStreamer-interfaces 0.10.36-15.1 sha1sum:77123565b8254bd2a741fce3b769ccaa2d5297f9" +ALL+="ARC_VV_GST_INT " + +ARC_VV_GST_PLBAD="${DOWNLOAD_HOST}mingw32-gst-plugins-bad-0.10.23-28.1.noarch.rpm GStreamer-plugins-bad 0.10.23-28.1 sha1sum:994ff1e2c7f839a5ef2dedfb01eeed0d9fcd4be3" +ALL+="ARC_VV_GST_PLBAD " + +ARC_VV_GST_PLBASE="${DOWNLOAD_HOST}mingw32-gst-plugins-base-0.10.36-15.1.noarch.rpm GStreamer-plugins-base 0.10.36-15.1 sha1sum:e9026a63c3c7d3f3f8d8ccbf255ed8e865b34c39" +ALL+="ARC_VV_GST_PLBASE " + +ARC_VV_GST_PLGOOD="${DOWNLOAD_HOST}mingw32-gst-plugins-good-0.10.31-5.1.noarch.rpm GStreamer-plugins-good 0.10.31-5.1 sha1sum:96f24a739f2f243ab55bc3cc159d43256b464861" +ALL+="ARC_VV_GST_PLGOOD " + +ARC_VV_LIBNICE="${DOWNLOAD_HOST}mingw32-libnice-0.1.4-19.1.noarch.rpm libnice 0.1.4-19.1 sha1sum:c1916d36f8083f76ef19437ddd255b12077ad465" +ALL+="ARC_VV_LIBNICE " + +ARC_VV_LIBOGG="${DOWNLOAD_HOST}mingw32-libogg-1.3.0-1.8.noarch.rpm libogg 1.3.0-1.8 sha1sum:1978cbd5148630fc95d4a6b1c5024f76f519fcd4" +ALL+="ARC_VV_LIBOGG " + +ARC_VV_LIBTHEORA="${DOWNLOAD_HOST}mingw32-libtheora-1.1.1-5.8.noarch.rpm libtheora 1.1.1-5.8 sha1sum:9809978e4e7c0a620dd735218bb1bd317fe32149" +ALL+="ARC_VV_LIBTHEORA " + +ARC_VV_LIBVORBIS="${DOWNLOAD_HOST}mingw32-libvorbis-1.3.3-1.8.noarch.rpm libvorbis 1.3.3-1.8 sha1sum:c9efd698ed62c26cf62442dafc2d9d2dcbcd651c" +ALL+="ARC_VV_LIBVORBIS " + +ARC_WEBKITGTK="${DOWNLOAD_HOST}mingw32-libwebkitgtk-1.10.2-9.2.noarch.rpm WebKitGTK+ 1.10.2-9.2 sha1sum:010dbad413f824696cd1e32fe70046c9a1cb425f" +ALL+="ARC_WEBKITGTK " + +ARC_ZLIB="${DOWNLOAD_HOST}mingw32-zlib-1.2.7-1.4.noarch.rpm zlib 1.2.7-1.4 sha1sum:83e91f3b4d14e47131ca33fc69e12b82aabdd589" +ALL+="ARC_ZLIB " mkdir -p $STAGE_DIR cd $STAGE_DIR @@ -156,6 +270,28 @@ mkdir "$CPIO_DIR" tar xf "$FILE" --strip-components=1 --directory="$CPIO_DIR" || exit 1 +function rpm_install { + PKG_NAME=${NAME%%\ *} + if [ "$PKG_NAME" = "GStreamer-plugins-bad" ]; then + cp $MINGW_DIR/lib/gstreamer-0.10/libgstdirectdrawsink.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstdirectsoundsrc.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstliveadder.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstrtpmux.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstwinks.dll $INSTALL_DIR/lib/gstreamer-0.10/ + elif [ "$PKG_NAME" = "GStreamer-plugins-good" ]; then + cp $MINGW_DIR/lib/gstreamer-0.10/libgstalaw.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstdirectsoundsink.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstlevel.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstmulaw.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstrtp.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstrtpmanager.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstrtsp.dll $INSTALL_DIR/lib/gstreamer-0.10/ + cp $MINGW_DIR/lib/gstreamer-0.10/libgstwavparse.dll $INSTALL_DIR/lib/gstreamer-0.10/ + else + cp -rf $MINGW_DIR/* $INSTALL_DIR + fi +} + function download_and_extract { URL=${1%%\ *} VALIDATION=${1##*\ } @@ -167,6 +303,8 @@ if [ ! -e $FILE ]; then echo "Downloading $NAME" download "$URL" "$FILE" || exit 1 + else + echo "Extracting $NAME" fi VALIDATION_TYPE=${VALIDATION%%:*} VALIDATION_VALUE=${VALIDATION##*:} @@ -200,7 +338,7 @@ if [ $EXTENSION == 'rpm' ]; then rm -rf $MINGW_DIR_TOP bsdcpio/bsdcpio.exe --quiet -f etc/fonts/conf.d -di < $FILE || exit 1 - cp -rf $MINGW_DIR/* $INSTALL_DIR + rpm_install rm -rf $MINGW_DIR_TOP else unzip -q $FILE -d $INSTALL_DIR || exit 1 @@ -214,10 +352,13 @@ VAR=${!VAL} download_and_extract "$VAR" done -mv "${STAGE_DIR}/${INSTALL_DIR}/share/tcl8.5" "${STAGE_DIR}/${INSTALL_DIR}/lib/" rm -rf $CPIO_DIR rm "${STAGE_DIR}/../cacert.pem" +#mv "${STAGE_DIR}/${INSTALL_DIR}/share/tcl8.5" "${STAGE_DIR}/${INSTALL_DIR}/lib/" +rm "${STAGE_DIR}/${INSTALL_DIR}/lib/gstreamer-0.10/libfsmsnconference.dll" +rm "${STAGE_DIR}/${INSTALL_DIR}/lib/gstreamer-0.10/libgstgnomevfs.dll" + echo "All components ready" #Default GTK+ Theme to MS-Windows (already set)
--- a/pidgin/win32/prepare-workspace.sh Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/win32/prepare-workspace.sh Mon Jun 10 01:22:52 2013 +0530 @@ -123,6 +123,13 @@ ARC_GTL="${DOWNLOAD_HOST}mingw32-libintl-0.18.1.1-13.6.noarch.rpm;gettext: libintl;0.18.1.1-13.6;0e6fde8e86788874366f308e25634f95613e906a;${OBS_SKIP};gettext-0.18" ARCHIVES+="ARC_GTL " +ARC_VV1="${DOWNLOAD_HOST}mingw32-gstreamer-devel-0.10.36-10.1.noarch.rpm;gstreamer;0.10.36-10.1;a54b53b31a47dd3d4243b8e772553e0b05430aaf;${OBS_SKIP};gstreamer-0.10" +ARCHIVES+="ARC_VV1 " +ARC_VV2="${DOWNLOAD_HOST}mingw32-gst-plugins-base-devel-0.10.36-15.1.noarch.rpm;gst-plugins-base;0.10.36-15.1;5bc0d94abdce4f2f2bafceda8046f01a5b29bd71;${OBS_SKIP};gstreamer-0.10" +ARCHIVES+="ARC_VV2 " +ARC_VV3="${DOWNLOAD_HOST}mingw32-farstream-devel-0.1.2-19.1.noarch.rpm;farstream;0.1.2-19.1;6c9f29de289b661d192c88998ed5bdf17de7bcec;${OBS_SKIP};gstreamer-0.10" +ARCHIVES+="ARC_VV3 " + # implementation if [ `uname -o` != "Cygwin" ]; then
--- a/pidgin/win32/winpidgin.c Mon Jun 03 15:26:52 2013 +0530 +++ b/pidgin/win32/winpidgin.c Mon Jun 10 01:22:52 2013 +0530 @@ -60,6 +60,44 @@ return err_msg; } +static BOOL reg_value_exists(HKEY key, wchar_t *sub_key, wchar_t *val_name) { + HKEY hkey; + LONG retv; + DWORD index; + wchar_t name_buffer[100]; + BOOL exists = FALSE; + + if (sub_key == NULL || val_name == NULL) + return FALSE; + + retv = RegOpenKeyExW(key, sub_key, 0, KEY_ENUMERATE_SUB_KEYS, &hkey); + if (retv != ERROR_SUCCESS) + return FALSE; + + if (val_name[0] == L'\0') { + RegCloseKey(hkey); + return TRUE; + } + + index = 0; + while (TRUE) + { + DWORD name_size = sizeof(name_buffer); + retv = RegEnumValueW(hkey, index++, name_buffer, &name_size, + NULL, NULL, NULL, NULL); + if (retv != ERROR_SUCCESS) + break; + name_size /= sizeof(wchar_t); + if (wcsncmp(name_buffer, val_name, name_size) == 0) { + exists = TRUE; + break; + } + } + + RegCloseKey(hkey); + return exists; +} + static BOOL read_reg_string(HKEY key, wchar_t *sub_key, wchar_t *val_name, LPBYTE data, LPDWORD data_len) { HKEY hkey; BOOL ret = FALSE; @@ -92,9 +130,7 @@ return ret; } -static BOOL common_dll_prep(const wchar_t *path) { - HMODULE hmod; - HKEY hkey; +static BOOL check_for_gtk(const wchar_t *path) { struct _stat stat_buf; wchar_t test_path[MAX_PATH + 1]; @@ -102,14 +138,58 @@ L"%s\\libgtk-win32-2.0-0.dll", path); test_path[sizeof(test_path) / sizeof(wchar_t) - 1] = L'\0'; - if (_wstat(test_path, &stat_buf) != 0) { - printf("Unable to determine GTK+ path. \n" - "Assuming GTK+ is in the PATH.\n"); - return FALSE; + return (_wstat(test_path, &stat_buf) == 0); +} + +static void common_dll_prep(const wchar_t *path) { + HMODULE hmod; + HKEY hkey; + wchar_t alt_path_buff[MAX_PATH + 1]; + wchar_t tmp_path[MAX_PATH + 1]; + /* Hold strlen("FS_PLUGIN_PATH=") + MAX_PATH + 1 */ + wchar_t farstream_path[MAX_PATH + 16]; + + if (!check_for_gtk(path)) { + const wchar_t *winpath = _wgetenv(L"PATH"); + wchar_t *delim; + + if (winpath == NULL) { + printf("Unable to determine GTK+ path (and PATH is not set).\n"); + exit(-1); + } + + path = NULL; + do + { + wcsncpy(alt_path_buff, winpath, MAX_PATH); + alt_path_buff[MAX_PATH] = L'\0'; + delim = wcschr(alt_path_buff, L';'); + if (delim != NULL) { + delim[0] = L'\0'; + winpath = wcschr(winpath, L';') + 1; + } + if (check_for_gtk(alt_path_buff)) { + path = alt_path_buff; + break; + } + } + while (delim != NULL); + + if (path == NULL) { + printf("Unable to determine GTK+ path.\n"); + exit(-1); + } } + wprintf(L"GTK+ path found: %s\n", path); - wprintf(L"GTK+ path found: %s\n", path); + wcsncpy(tmp_path, path, MAX_PATH); + tmp_path[MAX_PATH] = L'\0'; + wcsrchr(tmp_path, L'\\')[0] = L'\0'; + _snwprintf(farstream_path, sizeof(farstream_path) / sizeof(wchar_t), + L"FS_PLUGIN_PATH=%s\\lib\\farstream-0.1", tmp_path); + farstream_path[sizeof(farstream_path) / sizeof(wchar_t) - 1] = L'\0'; + _wputenv(farstream_path); if ((hmod = GetModuleHandleW(L"kernel32.dll"))) { MySetDllDirectory = (LPFNSETDLLDIRECTORY) GetProcAddress( @@ -121,7 +201,6 @@ /* For Windows XP SP1+ / Server 2003 we use SetDllDirectory to avoid dll hell */ if (MySetDllDirectory) { - printf("Using SetDllDirectory\n"); MySetDllDirectory(path); } @@ -179,11 +258,9 @@ printf("SafeDllSearchMode is set to 0\n"); }/*end else*/ } - - return TRUE; } -static BOOL dll_prep(const wchar_t *pidgin_dir) { +static void dll_prep(const wchar_t *pidgin_dir) { wchar_t path[MAX_PATH + 1]; path[0] = L'\0'; @@ -192,7 +269,7 @@ path[sizeof(path) / sizeof(wchar_t) - 1] = L'\0'; } - return common_dll_prep(path); + common_dll_prep(path); } static void portable_mode_dll_prep(const wchar_t *pidgin_dir) { @@ -214,8 +291,8 @@ path[cnt] = L'\0'; } else { printf("Unable to determine current executable path. \n" - "This will prevent the settings dir from being set.\n" - "Assuming GTK+ is in the PATH.\n"); + "This will prevent the settings dir from being set.\n"); + common_dll_prep(L'\0'); return; } @@ -229,7 +306,12 @@ wprintf(L"Setting settings dir: %s\n", path2); _wputenv(path2); - if (!dll_prep(pidgin_dir)) { + _snwprintf(path2, sizeof(path2) / sizeof(wchar_t), L"%s\\Gtk\\bin", + pidgin_dir); + path2[sizeof(path2) / sizeof(wchar_t) - 1] = L'\0'; + if (check_for_gtk(path2)) + common_dll_prep(path2); + else { /* set the GTK+ path to be \\path\to\GTK\bin */ wcscat(path, L"\\GTK\\bin"); common_dll_prep(path); @@ -428,7 +510,8 @@ printf("%s", "Looking for Perl... "); plen = sizeof(perl_path) / sizeof(wchar_t); - if (read_reg_string(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", L"", + if (reg_value_exists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", L"") && + read_reg_string(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", L"", (LPBYTE) &perl_path, &plen)) { /* We *could* check for perl510.dll, but it seems unnecessary. */ wprintf(L"found in '%s'.\n", perl_path); @@ -444,7 +527,8 @@ printf("%s", "Looking for MIT Kerberos... "); plen = sizeof(mit_kerberos_path) / sizeof(wchar_t); - if (read_reg_string(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MIT\\Kerberos", L"InstallDir", + if (reg_value_exists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MIT\\Kerberos", L"InstallDir") && + read_reg_string(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MIT\\Kerberos", L"InstallDir", (LPBYTE) &mit_kerberos_path, &plen)) { /* We *could* check for gssapi32.dll */ wprintf(L"found in '%s'.\n", mit_kerberos_path);
--- a/po/POTFILES.in Mon Jun 03 15:26:52 2013 +0530 +++ b/po/POTFILES.in Mon Jun 10 01:22:52 2013 +0530 @@ -49,6 +49,7 @@ libpurple/dnsquery.c libpurple/ft.c libpurple/gconf/purple.schemas.in +libpurple/keyring.c libpurple/log.c libpurple/media/backend-fs2.c libpurple/plugin.c @@ -61,6 +62,11 @@ libpurple/plugins/ipc-test-client.c libpurple/plugins/ipc-test-server.c libpurple/plugins/joinpart.c +libpurple/plugins/keyrings/gnomekeyring.c +libpurple/plugins/keyrings/internalkeyring.c +libpurple/plugins/keyrings/kwallet.cpp +libpurple/plugins/keyrings/secretservice.c +libpurple/plugins/keyrings/wincred.c libpurple/plugins/log_reader.c libpurple/plugins/mono/loader/mono.c libpurple/plugins/newline.c