diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2012-09-20 15:09:20 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2012-09-20 15:09:29 +0100 |
commit | b6f897048723dab0a1aefaacb969e8026a3d6e4d (patch) | |
tree | 8aa50109dc04474884e40a68be9908741bd3a1ed | |
parent | 5b5ee4862e11ab2b12d8f652d06b45600dc358af (diff) | |
parent | 7095375b51547bea365ed321f06ba6a7c7cd2101 (diff) | |
download | telepathy-mission-control-b6f897048723dab0a1aefaacb969e8026a3d6e4d.tar.gz |
Merge branch 'xdg-keyfile-35896'
Reviewed-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=35896
Conflicts:
src/mcd-account-manager-default.c
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | src/mcd-account-manager-default.c | 158 | ||||
-rw-r--r-- | src/mcd-account.c | 328 | ||||
-rw-r--r-- | src/mcd-misc.c | 17 | ||||
-rw-r--r-- | src/mcd-misc.h | 2 | ||||
-rw-r--r-- | tests/account-store-default.c | 22 | ||||
-rw-r--r-- | tests/twisted/Makefile.am | 12 | ||||
-rw-r--r-- | tests/twisted/account-manager/auto-connect.py | 5 | ||||
-rw-r--r-- | tests/twisted/account-manager/avatar-persist.py | 53 | ||||
-rw-r--r-- | tests/twisted/account-manager/avatar-refresh.py | 17 | ||||
-rw-r--r-- | tests/twisted/account-manager/avatar.py | 23 | ||||
-rw-r--r-- | tests/twisted/account-manager/make-valid.py | 5 | ||||
-rw-r--r-- | tests/twisted/account-storage/default-keyring-storage.py | 72 | ||||
-rw-r--r-- | tests/twisted/account-storage/diverted-storage.py | 9 | ||||
-rw-r--r-- | tests/twisted/crash-recovery/crash-recovery.py | 5 | ||||
-rw-r--r-- | tests/twisted/dispatcher/create-at-startup.py | 5 |
16 files changed, 618 insertions, 119 deletions
diff --git a/configure.ac b/configure.ac index 32779a7e..fb0cda6b 100644 --- a/configure.ac +++ b/configure.ac @@ -131,14 +131,14 @@ AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], [test "x$installed_tests" = xyes]) mctestsdir=${libdir}/telepathy-mission-control-tests AC_SUBST(mctestsdir) -AC_ARG_WITH(accounts_dir, AS_HELP_STRING([--with-accounts-dir=<path>],[Directory for storing accounts])) +AC_ARG_WITH([accounts_dir], [AS_HELP_STRING([--with-accounts-dir=<path>],[Directory in which accounts were previously stored])]) if test -z "$with_accounts_dir" ; then ACCOUNTS_DIR="~/.mission-control/accounts" else ACCOUNTS_DIR=$with_accounts_dir fi AC_SUBST(ACCOUNTS_DIR) -AC_DEFINE_UNQUOTED(ACCOUNTS_DIR,"$ACCOUNTS_DIR", [Directory for storing accounts]) +AC_DEFINE_UNQUOTED([ACCOUNTS_DIR], ["$ACCOUNTS_DIR"], [Directory in which accounts were previously stored]) AC_ARG_WITH(accounts_cache_dir, AS_HELP_STRING([--with-accounts-cache-dir=<path>],[Directory for account/connection mapping for crash recovery])) diff --git a/src/mcd-account-manager-default.c b/src/mcd-account-manager-default.c index ffd522fb..8d05fb8c 100644 --- a/src/mcd-account-manager-default.c +++ b/src/mcd-account-manager-default.c @@ -21,12 +21,16 @@ #include "config.h" +#include <errno.h> #include <string.h> +#include <glib/gstdio.h> + #include <telepathy-glib/telepathy-glib.h> #include "mcd-account-manager-default.h" #include "mcd-debug.h" +#include "mcd-misc.h" #define PLUGIN_NAME "default-gkeyfile" #define PLUGIN_PRIORITY MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT @@ -397,7 +401,7 @@ G_DEFINE_TYPE_WITH_CODE (McdAccountManagerDefault, mcd_account_manager_default, account_storage_iface_init)); static gchar * -get_account_conf_filename (void) +get_old_filename (void) { const gchar *base; @@ -415,11 +419,18 @@ get_account_conf_filename (void) return g_build_filename (base, "accounts.cfg", NULL); } +static gchar * +account_filename_in (const gchar *dir) +{ + return g_build_filename (dir, "telepathy", "mission-control", "accounts.cfg", + NULL); +} + static void mcd_account_manager_default_init (McdAccountManagerDefault *self) { DEBUG ("mcd_account_manager_default_init"); - self->filename = get_account_conf_filename (); + self->filename = account_filename_in (g_get_user_data_dir ()); self->keyfile = g_key_file_new (); self->secrets = g_key_file_new (); self->removed = g_key_file_new (); @@ -435,25 +446,6 @@ mcd_account_manager_default_class_init (McdAccountManagerDefaultClass *cls) DEBUG ("mcd_account_manager_default_class_init"); } -static gboolean -_have_config (McdAccountManagerDefault *self) -{ - DEBUG ("checking for %s", self->filename); - return g_file_test (self->filename, G_FILE_TEST_EXISTS); -} - -static void -_create_config (McdAccountManagerDefault *self) -{ - gchar *dir = g_path_get_dirname (self->filename); - - DEBUG (""); - g_mkdir_with_parents (dir, 0700); - g_free (dir); - g_file_set_contents (self->filename, INITIAL_CONFIG, -1, NULL); - DEBUG ("created %s", self->filename); -} - /* We happen to know that the string MC gave us is "sufficiently escaped" to * put it in the keyfile as-is. */ static gboolean @@ -663,16 +655,39 @@ _commit (const McpAccountStorage *self, gchar *data; McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); gboolean rval = FALSE; + gchar *dir; + GError *error = NULL; if (!amd->save) return TRUE; - if (!_have_config (amd)) - _create_config (amd); + dir = g_path_get_dirname (amd->filename); + + DEBUG ("Saving accounts to %s", amd->filename); + + if (!mcd_ensure_directory (dir, &error)) + { + g_warning ("%s", error->message); + g_error_free (error); + /* fall through anyway: writing to the file will fail, but it does + * give us a chance to commit to the keyring too */ + } + + g_free (dir); data = g_key_file_to_data (amd->keyfile, &n, NULL); - rval = g_file_set_contents (amd->filename, data, n, NULL); - amd->save = !rval; + rval = g_file_set_contents (amd->filename, data, n, &error); + + if (rval) + { + amd->save = FALSE; + } + else + { + g_warning ("%s", error->message); + g_error_free (error); + } + g_free (data); _keyring_commit (self, am, account); @@ -680,6 +695,31 @@ _commit (const McpAccountStorage *self, return rval; } +static void +am_default_load_keyfile (McdAccountManagerDefault *self, + const gchar *filename) +{ + GError *error = NULL; + + if (g_key_file_load_from_file (self->keyfile, filename, + G_KEY_FILE_KEEP_COMMENTS, &error)) + { + DEBUG ("Loaded accounts from %s", filename); + } + else + { + DEBUG ("Failed to load accounts from %s: %s", filename, error->message); + g_error_free (error); + + /* Start with a blank configuration, but do not save straight away; + * we don't want to overwrite a corrupt-but-maybe-recoverable + * configuration file with an empty one until given a reason to + * do so. */ + g_key_file_load_from_data (self->keyfile, INITIAL_CONFIG, -1, + G_KEY_FILE_KEEP_COMMENTS, NULL); + } +} + static GList * _list (const McpAccountStorage *self, const McpAccountManager *am) @@ -690,12 +730,72 @@ _list (const McpAccountStorage *self, GList *rval = NULL; McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); - if (!_have_config (amd)) - _create_config (amd); + if (!amd->loaded && g_file_test (amd->filename, G_FILE_TEST_EXISTS)) + { + /* If the file exists, but loading it fails, we deliberately + * do not fall through to the "initial configuration" case, + * because we don't want to overwrite a corrupted file + * with an empty one until an actual write takes place. */ + am_default_load_keyfile (amd, amd->filename); + amd->loaded = TRUE; + } + + if (!amd->loaded) + { + const gchar * const *iter; + + for (iter = g_get_system_data_dirs (); + iter != NULL && *iter != NULL; + iter++) + { + gchar *filename = account_filename_in (*iter); + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) + { + am_default_load_keyfile (amd, filename); + amd->loaded = TRUE; + /* Do not set amd->save: we don't need to write it to a + * higher-priority directory until it actually changes. */ + } + + g_free (filename); + + if (amd->loaded) + break; + } + } + + if (!amd->loaded) + { + gchar *old_filename = get_old_filename (); + + if (g_file_test (old_filename, G_FILE_TEST_EXISTS)) + { + am_default_load_keyfile (amd, old_filename); + amd->loaded = TRUE; + amd->save = TRUE; + + if (_commit (self, am, NULL)) + { + DEBUG ("Migrated %s to new location: deleting old copy"); + if (g_unlink (old_filename) != 0) + g_warning ("Unable to delete %s: %s", old_filename, + g_strerror (errno)); + } + } + + g_free (old_filename); + } if (!amd->loaded) - amd->loaded = g_key_file_load_from_file (amd->keyfile, amd->filename, - G_KEY_FILE_KEEP_COMMENTS, NULL); + { + DEBUG ("Creating initial account data"); + g_key_file_load_from_data (amd->keyfile, INITIAL_CONFIG, -1, + G_KEY_FILE_KEEP_COMMENTS, NULL); + amd->loaded = TRUE; + amd->save = TRUE; + _commit (self, am, NULL); + } accounts = g_key_file_get_groups (amd->keyfile, &n); diff --git a/src/mcd-account.c b/src/mcd-account.c index ecc10358..256575b1 100644 --- a/src/mcd-account.c +++ b/src/mcd-account.c @@ -24,6 +24,7 @@ #include "config.h" #include "mcd-account.h" +#include <errno.h> #include <stdio.h> #include <string.h> @@ -51,7 +52,7 @@ #include "_gen/cli-Connection_Manager_Interface_Account_Storage-body.h" #define MAX_KEY_LENGTH (DBUS_MAXIMUM_NAME_LENGTH + 6) -#define MC_AVATAR_FILENAME "avatar.bin" +#define MC_OLD_AVATAR_FILENAME "avatar.bin" #define MCD_ACCOUNT_PRIV(account) (MCD_ACCOUNT (account)->priv) @@ -673,7 +674,7 @@ load_manager (McdAccount *account) /* Returns the data dir for the given account name. * Returned string must be freed by caller. */ static gchar * -get_account_data_path (McdAccountPrivate *priv) +get_old_account_data_path (McdAccountPrivate *priv) { const gchar *base; @@ -759,7 +760,7 @@ mcd_account_delete (McdAccount *account, mcd_storage_delete_account (priv->storage, name); - data_dir_str = get_account_data_path (priv); + data_dir_str = get_old_account_data_path (priv); if (data_dir_str != NULL) { @@ -2811,6 +2812,213 @@ register_dbus_service (McdAccount *self, (GObject *) self); } +/* + * @account: (allow-none): + * @dir_out: (out): e.g. ~/.local/share/telepathy/mission-control + * @basename_out: (out): e.g. gabble-jabber-fred_40example_2ecom.avatar + * @file_out: (out): @dir_out + "/" + @basename_out + */ +static void +get_avatar_paths (McdAccount *account, + gchar **dir_out, + gchar **basename_out, + gchar **file_out) +{ + gchar *dir = NULL; + + dir = g_build_filename (g_get_user_data_dir (), + "telepathy", "mission-control", NULL); + + if (account == NULL) + { + if (file_out != NULL) + *file_out = NULL; + + if (basename_out != NULL) + *basename_out = NULL; + } + else if (basename_out != NULL || file_out != NULL) + { + gchar *basename = NULL; + + basename = g_strdup_printf ("%s.avatar", account->priv->unique_name); + g_strdelimit (basename, "/", '-'); + + if (file_out != NULL) + *file_out = g_build_filename (dir, basename, NULL); + + if (basename_out != NULL) + *basename_out = basename; + else + g_free (basename); + } + + if (dir_out != NULL) + *dir_out = dir; + else + g_free (dir); +} + +static gboolean +save_avatar (McdAccount *self, + gpointer data, + gssize len, + GError **error) +{ + gchar *dir = NULL; + gchar *file = NULL; + gboolean ret = FALSE; + + get_avatar_paths (self, &dir, NULL, &file); + + if (mcd_ensure_directory (dir, error) && + g_file_set_contents (file, data, len, error)) + { + DEBUG ("Saved avatar to %s", file); + ret = TRUE; + } + else if (len == 0) + { + GArray *avatar = NULL; + + /* It failed, but maybe that's OK, since we didn't really want + * an avatar anyway. */ + _mcd_account_get_avatar (self, &avatar, NULL); + + if (avatar == NULL) + { + /* Creating the empty file failed, but it's fine, since what's + * on disk correctly indicates that we have no avatar. */ + DEBUG ("Ignoring failure to write empty avatar"); + + if (error != NULL) + g_clear_error (error); + } + else + { + /* Continue to raise the error: we failed to write a 0-byte + * file into the highest-priority avatar directory, and we do + * need it, since there is a non-empty avatar in either that + * directory or a lower-priority directory */ + g_array_free (avatar, TRUE); + } + } + + g_free (dir); + g_free (file); + return ret; +} + +static gchar *_mcd_account_get_old_avatar_filename (McdAccount *account, + gchar **old_dir); + +static void +mcd_account_migrate_avatar (McdAccount *account) +{ + GError *error = NULL; + gchar *old_file; + gchar *old_dir = NULL; + gchar *new_dir = NULL; + gchar *basename = NULL; + gchar *new_file = NULL; + gchar *contents = NULL; + guint i; + + /* Try to migrate the avatar to a better location */ + old_file = _mcd_account_get_old_avatar_filename (account, &old_dir); + + if (!g_file_test (old_file, G_FILE_TEST_EXISTS)) + { + /* nothing to do */ + goto finally; + } + + DEBUG ("Migrating avatar from %s", old_file); + + get_avatar_paths (account, &new_dir, &basename, &new_file); + + if (g_file_test (new_file, G_FILE_TEST_IS_REGULAR)) + { + DEBUG ("... already migrated to %s", new_file); + + if (g_unlink (old_file) != 0) + { + DEBUG ("Failed to unlink %s: %s", old_file, + g_strerror (errno)); + } + + goto finally; + } + + if (!mcd_ensure_directory (new_dir, &error)) + { + DEBUG ("%s", error->message); + goto finally; + } + + if (g_rename (old_file, new_file) == 0) + { + DEBUG ("Renamed %s to %s", old_file, new_file); + } + else + { + gsize len; + + DEBUG ("Unable to rename %s to %s, will try copy+delete: %s", + old_file, new_file, g_strerror (errno)); + + if (!g_file_get_contents (old_file, &contents, &len, &error)) + { + DEBUG ("Unable to load old avatar %s: %s", old_file, + error->message); + goto finally; + } + + if (g_file_set_contents (new_file, contents, len, &error)) + { + DEBUG ("Copied old avatar from %s to %s", old_file, new_file); + } + else + { + DEBUG ("Unable to save new avatar %s: %s", new_file, + error->message); + goto finally; + } + + if (g_unlink (old_file) != 0) + { + DEBUG ("Failed to unlink %s: %s", old_file, g_strerror (errno)); + goto finally; + } + } + + /* old_dir is typically ~/.mission-control/accounts/gabble/jabber/badger0. + * We want to delete badger0, jabber, gabble, accounts if they are empty. + * If they are not, we'll just get ENOTEMPTY and stop. */ + for (i = 0; i < 4; i++) + { + gchar *tmp; + + if (g_rmdir (old_dir) != 0) + { + DEBUG ("Failed to rmdir %s: %s", old_dir, g_strerror (errno)); + goto finally; + } + + tmp = g_path_get_dirname (old_dir); + g_free (old_dir); + old_dir = tmp; + } + +finally: + g_clear_error (&error); + g_free (basename); + g_free (new_file); + g_free (new_dir); + g_free (contents); + g_free (old_file); +} + static gboolean mcd_account_setup (McdAccount *account) { @@ -3100,6 +3308,7 @@ _mcd_account_constructed (GObject *object) DEBUG ("%p (%s)", object, account->priv->unique_name); + mcd_account_migrate_avatar (account); mcd_account_setup (account); } @@ -3633,32 +3842,27 @@ _mcd_account_set_avatar (McdAccount *account, const GArray *avatar, { McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account); const gchar *account_name = mcd_account_get_unique_name (account); - gchar *data_dir, *filename; DEBUG ("called"); - data_dir = get_account_data_path (priv); - filename = g_build_filename (data_dir, MC_AVATAR_FILENAME, NULL); - if (!g_file_test (data_dir, G_FILE_TEST_EXISTS)) - g_mkdir_with_parents (data_dir, 0700); - _mcd_chmod_private (data_dir); - g_free (data_dir); if (G_LIKELY(avatar) && avatar->len > 0) { - if (!g_file_set_contents (filename, avatar->data, - (gssize)avatar->len, error)) + if (!save_avatar (account, avatar->data, avatar->len, error)) { - g_warning ("%s: writing to file %s failed", G_STRLOC, - filename); - g_free (filename); + g_warning ("%s: writing avatar failed", G_STRLOC); return FALSE; } } else { - g_remove (filename); + /* We implement "deleting" an avatar by writing out a zero-length + * file, so that it will override lower-priority directories. */ + if (!save_avatar (account, "", 0, error)) + { + g_warning ("%s: writing empty avatar failed", G_STRLOC); + return FALSE; + } } - g_free (filename); if (mime_type != NULL) mcd_storage_set_string (priv->storage, @@ -3701,7 +3905,37 @@ _mcd_account_set_avatar (McdAccount *account, const GArray *avatar, return TRUE; } -static gchar *_mcd_account_get_avatar_filename (McdAccount *account); +static GArray * +load_avatar_or_warn (const gchar *filename) +{ + GError *error = NULL; + gchar *data = NULL; + gsize length; + + if (g_file_get_contents (filename, &data, &length, &error)) + { + if (length > 0 && length < G_MAXUINT) + { + GArray *ret; + + ret = g_array_new (FALSE, FALSE, 1); + g_array_append_vals (ret, data, (guint) length); + return ret; + } + else + { + DEBUG ("avatar %s was empty or ridiculously large (%" + G_GSIZE_FORMAT " bytes)", filename, length); + return NULL; + } + } + else + { + DEBUG ("error reading %s: %s", filename, error->message); + g_error_free (error); + return NULL; + } +} void _mcd_account_get_avatar (McdAccount *account, GArray **avatar, @@ -3709,6 +3943,7 @@ _mcd_account_get_avatar (McdAccount *account, GArray **avatar, { McdAccountPrivate *priv = MCD_ACCOUNT_PRIV (account); const gchar *account_name = mcd_account_get_unique_name (account); + gchar *basename; gchar *filename; if (mime_type != NULL) @@ -3720,29 +3955,37 @@ _mcd_account_get_avatar (McdAccount *account, GArray **avatar, *avatar = NULL; - filename = _mcd_account_get_avatar_filename (account); + get_avatar_paths (account, NULL, &basename, &filename); - if (filename && g_file_test (filename, G_FILE_TEST_EXISTS)) + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { - GError *error = NULL; - gchar *data = NULL; - gsize length; - if (g_file_get_contents (filename, &data, &length, &error)) - { - if (length > 0 && length < G_MAXUINT) - { - *avatar = g_array_new (FALSE, FALSE, 1); - (*avatar)->data = data; - (*avatar)->len = (guint)length; - } - } - else - { - DEBUG ("error reading %s: %s", filename, error->message); - g_error_free (error); - } + *avatar = load_avatar_or_warn (filename); } + else + { + const gchar * const *iter; + + for (iter = g_get_system_data_dirs (); + iter != NULL && *iter != NULL; + iter++) + { + gchar *candidate = g_build_filename (*iter, "telepathy", + "mission-control", + basename, NULL); + + if (g_file_test (candidate, G_FILE_TEST_EXISTS)) + { + *avatar = load_avatar_or_warn (candidate); + g_free (candidate); + break; + } + + g_free (candidate); + } + } + g_free (filename); + g_free (basename); } GPtrArray * @@ -4203,15 +4446,14 @@ _mcd_account_get_keyfile (McdAccount *account) } static gchar * -_mcd_account_get_avatar_filename (McdAccount *account) +_mcd_account_get_old_avatar_filename (McdAccount *account, + gchar **old_dir) { McdAccountPrivate *priv = account->priv; - gchar *data_dir, *filename; + gchar *filename; - data_dir = get_account_data_path (priv); - DEBUG("data dir: %s", data_dir); - filename = g_build_filename (data_dir, MC_AVATAR_FILENAME, NULL); - g_free (data_dir); + *old_dir = get_old_account_data_path (priv); + filename = g_build_filename (*old_dir, MC_OLD_AVATAR_FILENAME, NULL); return filename; } diff --git a/src/mcd-misc.c b/src/mcd-misc.c index 294d2a78..c0cc4e13 100644 --- a/src/mcd-misc.c +++ b/src/mcd-misc.c @@ -176,6 +176,23 @@ _mcd_object_ready (gpointer object, GQuark quark, const GError *error) g_object_unref (object); } +gboolean +mcd_ensure_directory (const gchar *dir, + GError **error) +{ + DEBUG ("%s", dir); + + if (g_mkdir_with_parents (dir, 0700) != 0) + { + g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "Unable to create directory '%s': %s", + dir, g_strerror (errno)); + return FALSE; + } + + return TRUE; +} + int _mcd_chmod_private (const gchar *filename) { diff --git a/src/mcd-misc.h b/src/mcd-misc.h index e69ee728..741a1092 100644 --- a/src/mcd-misc.h +++ b/src/mcd-misc.h @@ -51,6 +51,8 @@ void _mcd_object_ready (gpointer object, GQuark quark, const GError *error); G_GNUC_INTERNAL void _mcd_ext_register_dbus_glib_marshallers (void); +gboolean mcd_ensure_directory (const gchar *dir, GError **error); + G_GNUC_INTERNAL int _mcd_chmod_private (const gchar *filename); G_END_DECLS diff --git a/tests/account-store-default.c b/tests/account-store-default.c index 944bf2f7..513799a5 100644 --- a/tests/account-store-default.c +++ b/tests/account-store-default.c @@ -146,26 +146,8 @@ _keyring_remove_account (const gchar *acct) static const gchar *default_config (void) { - const gchar *base; - static const gchar *path = NULL; - - if (path != NULL) - return path; - - base = g_getenv ("MC_ACCOUNT_DIR"); - - if (!base) - base = ACCOUNTS_DIR; - - if (!base) - return NULL; - - if (base[0] == '~') - path = g_build_filename (g_get_home_dir(), base + 1, "accounts.cfg", NULL); - else - path = g_build_filename (base, "accounts.cfg", NULL); - - return path; + return g_build_filename (g_get_user_data_dir (), "telepathy", + "mission-control", "accounts.cfg", NULL); } static GKeyFile * default_keyfile (void) diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 8cb78774..ac931465 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -222,13 +222,13 @@ BASIC_TESTS_ENVIRONMENT = \ PYTHONPATH=@abs_top_srcdir@/tests/twisted:@abs_top_builddir@/tests/twisted \ MC_DEBUG=all \ MC_FILTER_PLUGIN_DIR=@abs_top_builddir@/tests/twisted/.libs \ - MC_ACCOUNT_DIR=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX) \ - XDG_CONFIG_HOME=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX) \ + MC_ACCOUNT_DIR=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX)/mc-account-dir \ + XDG_CONFIG_HOME=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX)/config \ XDG_CONFIG_DIRS=@abs_top_srcdir@/tests/twisted \ - XDG_DATA_HOME=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX) \ - XDG_DATA_DIRS=@abs_top_srcdir@/tests/twisted \ - XDG_CACHE_DIR=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX) \ - XDG_CACHE_HOME=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX) \ + XDG_DATA_HOME=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX)/localshare \ + XDG_DATA_DIRS=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX)/share:@abs_top_srcdir@/tests/twisted \ + XDG_CACHE_DIR=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX)/cache \ + XDG_CACHE_HOME=@abs_top_builddir@/tests/twisted/tmp-$(TMPSUFFIX)/cache \ MC_CLIENTS_DIR=@abs_top_srcdir@/tests/twisted/telepathy/clients \ MC_MANAGER_DIR=@abs_top_srcdir@/tests/twisted/telepathy/managers \ G_DEBUG=fatal_criticals diff --git a/tests/twisted/account-manager/auto-connect.py b/tests/twisted/account-manager/auto-connect.py index 38227f5f..581a1b66 100644 --- a/tests/twisted/account-manager/auto-connect.py +++ b/tests/twisted/account-manager/auto-connect.py @@ -39,6 +39,11 @@ def preseed(): accounts_dir = os.environ['MC_ACCOUNT_DIR'] + try: + os.mkdir(accounts_dir, 0700) + except OSError: + pass + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') # As a regression test for part of fd.o #28557, the password starts and diff --git a/tests/twisted/account-manager/avatar-persist.py b/tests/twisted/account-manager/avatar-persist.py index 06ad65d8..6dfae62a 100644 --- a/tests/twisted/account-manager/avatar-persist.py +++ b/tests/twisted/account-manager/avatar-persist.py @@ -27,7 +27,7 @@ import dbus import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ - call_async + call_async, assertEquals from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC import constants as cs @@ -40,6 +40,11 @@ def preseed(): accounts_dir = os.environ['MC_ACCOUNT_DIR'] + try: + os.mkdir(accounts_dir, 0700) + except OSError: + pass + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') accounts_cfg.write("""# Telepathy accounts [%s] @@ -60,8 +65,12 @@ avatar_token=Deus Ex """ % account_id) accounts_cfg.close() - os.makedirs(accounts_dir + '/' + account_id) - avatar_bin = open(accounts_dir + '/' + account_id + '/avatar.bin', 'w') + datadirs = os.environ['XDG_DATA_DIRS'].split(':') + + os.makedirs(datadirs[0] + '/telepathy/mission-control') + avatar_filename = (datadirs[0] + '/telepathy/mission-control/' + + account_id.replace('/', '-') + '.avatar') + avatar_bin = open(avatar_filename, 'w') avatar_bin.write('Deus Ex') avatar_bin.close() @@ -133,6 +142,44 @@ def test(q, bus, unused): assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', byte_arrays=True) == conn.avatar + # The avatar wasn't deleted from $XDG_DATA_DIRS, but it was overridden. + assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/' + + account_id + '/avatar.bin') + assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/fakecm') + + avatar_filename = account_id + avatar_filename = avatar_filename.replace('/', '-') + '.avatar' + avatar_filename = (os.environ['XDG_DATA_HOME'] + + '/telepathy/mission-control/' + avatar_filename) + assertEquals('MJ12', ''.join(open(avatar_filename, 'r').readlines())) + + datadirs = os.environ['XDG_DATA_DIRS'].split(':') + low_prio_filename = account_id + low_prio_filename = low_prio_filename.replace('/', '-') + '.avatar' + low_prio_filename = (datadirs[0] + + '/telepathy/mission-control/' + low_prio_filename) + assertEquals('Deus Ex', ''.join(open(low_prio_filename, 'r').readlines())) + + # If we set the avatar to be empty, that's written out as a file, + # so it'll override the one in XDG_DATA_DIRS + call_async(q, account_props, 'Set', cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + (dbus.ByteArray(''), '')) + + q.expect_many( + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='ClearAvatar', + args=[]), + EventPattern('dbus-signal', path=account.object_path, + interface=cs.ACCOUNT_IFACE_AVATAR, signal='AvatarChanged'), + EventPattern('dbus-return', method='Set') + ) + + assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=True) == ('', '') + + assertEquals('', ''.join(open(avatar_filename, 'r').readlines())) + assertEquals('Deus Ex', ''.join(open(low_prio_filename, 'r').readlines())) + if __name__ == '__main__': preseed() exec_test(test, {}, preload_mc=False) diff --git a/tests/twisted/account-manager/avatar-refresh.py b/tests/twisted/account-manager/avatar-refresh.py index a93912eb..f3e535a9 100644 --- a/tests/twisted/account-manager/avatar-refresh.py +++ b/tests/twisted/account-manager/avatar-refresh.py @@ -27,7 +27,7 @@ import dbus import dbus.service from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ - call_async + call_async, assertEquals from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC import constants as cs @@ -40,6 +40,11 @@ def preseed(): accounts_dir = os.environ['MC_ACCOUNT_DIR'] + try: + os.mkdir(accounts_dir, 0700) + except OSError: + pass + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') accounts_cfg.write("""# Telepathy accounts [%s] @@ -114,6 +119,16 @@ def test(q, bus, unused): ), ) + # The avatar got migrated, too. + assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/' + + account_id + '/avatar.bin') + assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/fakecm') + avatar_filename = account_id + avatar_filename = avatar_filename.replace('/', '-') + '.avatar' + avatar_filename = (os.environ['XDG_DATA_HOME'] + + '/telepathy/mission-control/' + avatar_filename) + assertEquals('Deus Ex', ''.join(open(avatar_filename, 'r').readlines())) + if __name__ == '__main__': preseed() exec_test(test, {}, preload_mc=False) diff --git a/tests/twisted/account-manager/avatar.py b/tests/twisted/account-manager/avatar.py index c616e146..af89c6fc 100644 --- a/tests/twisted/account-manager/avatar.py +++ b/tests/twisted/account-manager/avatar.py @@ -16,7 +16,8 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA -import dbus +import os + import dbus import dbus.service @@ -33,6 +34,13 @@ def test(q, bus, mc): account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + assertEquals(cs.ACCOUNT_PATH_PREFIX, + account.object_path[:len(cs.ACCOUNT_PATH_PREFIX)]) + avatar_filename = account.object_path[len(cs.ACCOUNT_PATH_PREFIX):] + avatar_filename = avatar_filename.replace('/', '-') + '.avatar' + avatar_filename = (os.environ['XDG_DATA_HOME'] + + '/telepathy/mission-control/' + avatar_filename) + call_async(q, account_props, 'Set', cs.ACCOUNT_IFACE_AVATAR, 'Avatar', dbus.Struct((dbus.ByteArray('AAAA'), 'image/jpeg'))) q.expect_many( @@ -46,6 +54,10 @@ def test(q, bus, mc): assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', byte_arrays=True) == ('AAAA', 'image/jpeg') + assertEquals('AAAA', ''.join(open(avatar_filename, 'r').readlines())) + # We aren't storing in the old location + assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/fakecm') + # OK, let's go online. The avatar is set regardless of the CM conn, e = enable_fakecm_account(q, bus, mc, account, params, has_avatars=True, avatars_persist=True, @@ -72,6 +84,9 @@ def test(q, bus, mc): assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', byte_arrays=True) == ('BBBB', 'image/png') + assertEquals('BBBB', ''.join(open(avatar_filename, 'r').readlines())) + assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/fakecm') + someone_else = conn.ensure_handle(cs.HT_CONTACT, 'alberto@example.com') # Another contact changes their avatar: ignored @@ -97,6 +112,8 @@ def test(q, bus, mc): assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', byte_arrays=True) == ('CCCC', 'image/svg') + assertEquals('CCCC', ''.join(open(avatar_filename, 'r').readlines())) + # empty avatar tests conn.forget_avatar() q.dbus_emit(conn.object_path, cs.CONN_IFACE_AVATARS, 'AvatarUpdated', @@ -108,5 +125,9 @@ def test(q, bus, mc): assertEquals(account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', byte_arrays=False), ([], '')) + # empty avatars are represented by an empty file, not no file, + # to get the right precedence over XDG_DATA_DIRS + assertEquals('', ''.join(open(avatar_filename, 'r').readlines())) + if __name__ == '__main__': exec_test(test, {}) diff --git a/tests/twisted/account-manager/make-valid.py b/tests/twisted/account-manager/make-valid.py index ee3ee98f..457229db 100644 --- a/tests/twisted/account-manager/make-valid.py +++ b/tests/twisted/account-manager/make-valid.py @@ -40,6 +40,11 @@ def preseed(): accounts_dir = os.environ['MC_ACCOUNT_DIR'] + try: + os.mkdir(accounts_dir, 0700) + except OSError: + pass + # The passwords are missing, so the accounts can't connect yet. accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') accounts_cfg.write("""# Telepathy accounts diff --git a/tests/twisted/account-storage/default-keyring-storage.py b/tests/twisted/account-storage/default-keyring-storage.py index bab0b726..93feca4f 100644 --- a/tests/twisted/account-storage/default-keyring-storage.py +++ b/tests/twisted/account-storage/default-keyring-storage.py @@ -124,7 +124,9 @@ def stop_gnome_keyring_daemon(): def test(q, bus, mc): ctl_dir = os.environ['MC_ACCOUNT_DIR'] - key_file_name = os.path.join(ctl_dir, 'accounts.cfg') + old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') + new_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') group = 'fakecm/fakeprotocol/dontdivert_40example_2ecom0' account_manager, properties, interfaces = connect_to_mc(q, bus, mc) @@ -161,7 +163,8 @@ def test(q, bus, mc): tell_mc_to_die(q, bus) # .. let's check the keyfile - kf = keyfile_read(key_file_name) + assert not os.path.exists(old_key_file_name) + kf = keyfile_read(new_key_file_name) assert group in kf, kf assert kf[group]['manager'] == 'fakecm' assert kf[group]['protocol'] == 'fakeprotocol' @@ -204,7 +207,8 @@ def test(q, bus, mc): ) # Check the account is correctly deleted - kf = keyfile_read(key_file_name) + assert not os.path.exists(old_key_file_name) + kf = keyfile_read(new_key_file_name) assert group not in kf, kf if use_keyring: @@ -215,11 +219,22 @@ def test(q, bus, mc): # Tell MC to die, again tell_mc_to_die(q, bus) - # Write out account configurations in which the password is in + # Write out an account configuration in which the password is in # both the keyfile and the keyring - account_store('set', 'default', 'param-password', 'password_in_keyring') - open(ctl_dir + '/accounts.cfg', 'w').write( + + if use_keyring: + account_store('set', 'default', 'param-password', + 'password_in_keyring') + + low_prio_key_file_name = os.path.join( + os.environ['XDG_DATA_DIRS'].split(':')[0], + 'telepathy', 'mission-control', 'accounts.cfg') + os.makedirs(os.path.dirname(low_prio_key_file_name), 0700) + + # This is deliberately a lower-priority location + os.remove(new_key_file_name) + open(low_prio_key_file_name, 'w').write( r"""# Telepathy accounts [%s] manager=fakecm @@ -233,12 +248,14 @@ DisplayName=New and improved account account = get_fakecm_account(bus, mc, account_path) account_iface = dbus.Interface(account, cs.ACCOUNT) - pwd = account_store('get', 'default', 'param-password') + # Files in lower-priority XDG locations aren't copied until something + # actually changes, and they aren't deleted. + assert not os.path.exists(new_key_file_name) + assert os.path.exists(low_prio_key_file_name) + if use_keyring: + pwd = account_store('get', 'default', 'param-password') assertEquals('password_in_keyring', pwd) - else: - # it was overwritten when we edited the keyfile - assertEquals('password_in_keyfile', pwd) # Delete the password (only), like Empathy 3.0-3.4 do when migrating account_iface.UpdateParameters({}, ['password']) @@ -253,19 +270,48 @@ DisplayName=New and improved account # Tell MC to die yet again tell_mc_to_die(q, bus) - # Check the password is correctly deleted - kf = keyfile_read(key_file_name) + # Check the account has copied (not moved! XDG_DATA_DIRS are, + # conceptually, read-only) from the old to the new name + assert not os.path.exists(old_key_file_name) + assert os.path.exists(low_prio_key_file_name) + kf = keyfile_read(new_key_file_name) assert 'param-password' not in kf[group] pwd = account_store('get', 'default', 'param-password') assertEquals(None, pwd) pwd = account_store('count-passwords', 'default') assertEquals('0', pwd) - # Put it back, just so deleting all accounts won't raise errors + # Write out an account configuration in the old keyfile, to test + # migration + os.remove(new_key_file_name) + os.remove(low_prio_key_file_name) + open(old_key_file_name, 'w').write( +r"""# Telepathy accounts +[%s] +manager=fakecm +protocol=fakeprotocol +param-account=dontdivert@example.com +DisplayName=Ye olde account +""" % group) + account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) + account = get_fakecm_account(bus, mc, account_path) + account_iface = dbus.Interface(account, cs.ACCOUNT) + + # This time it *does* get moved (really copied+deleted) automatically + # during MC startup + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(low_prio_key_file_name) + kf = keyfile_read(new_key_file_name) + assert 'param-password' not in kf[group] + assertEquals('Ye olde account', kf[group]['DisplayName']) if __name__ == '__main__': ctl_dir = os.environ['MC_ACCOUNT_DIR'] + try: + os.mkdir(ctl_dir, 0700) + except OSError: + pass start_gnome_keyring_daemon(ctl_dir) exec_test(test, {}, timeout=10) stop_gnome_keyring_daemon() diff --git a/tests/twisted/account-storage/diverted-storage.py b/tests/twisted/account-storage/diverted-storage.py index d645656e..258b0e2b 100644 --- a/tests/twisted/account-storage/diverted-storage.py +++ b/tests/twisted/account-storage/diverted-storage.py @@ -33,7 +33,14 @@ from mctest import ( import constants as cs def test(q, bus, mc): - empty_key_file_name = os.path.join(os.environ['MC_ACCOUNT_DIR'], 'accounts.cfg') + accounts_dir = os.environ['MC_ACCOUNT_DIR'] + try: + os.mkdir(accounts_dir, 0700) + except OSError: + pass + + empty_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') group = 'fakecm/fakeprotocol/someguy_40example_2ecom0' diff --git a/tests/twisted/crash-recovery/crash-recovery.py b/tests/twisted/crash-recovery/crash-recovery.py index e189b850..0446550c 100644 --- a/tests/twisted/crash-recovery/crash-recovery.py +++ b/tests/twisted/crash-recovery/crash-recovery.py @@ -34,6 +34,11 @@ import constants as cs def preseed(): accounts_dir = os.environ['MC_ACCOUNT_DIR'] + try: + os.mkdir(accounts_dir, 0700) + except OSError: + pass + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') accounts_cfg.write("""# Telepathy accounts diff --git a/tests/twisted/dispatcher/create-at-startup.py b/tests/twisted/dispatcher/create-at-startup.py index c4e24765..992145fb 100644 --- a/tests/twisted/dispatcher/create-at-startup.py +++ b/tests/twisted/dispatcher/create-at-startup.py @@ -35,6 +35,11 @@ import constants as cs def preseed(): accounts_dir = os.environ['MC_ACCOUNT_DIR'] + try: + os.mkdir(accounts_dir, 0700) + except OSError: + pass + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') accounts_cfg.write("""# Telepathy accounts [fakecm/fakeprotocol/jc_2edenton_40unatco_2eint] |