summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2012-09-20 15:09:20 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2012-09-20 15:09:29 +0100
commitb6f897048723dab0a1aefaacb969e8026a3d6e4d (patch)
tree8aa50109dc04474884e40a68be9908741bd3a1ed
parent5b5ee4862e11ab2b12d8f652d06b45600dc358af (diff)
parent7095375b51547bea365ed321f06ba6a7c7cd2101 (diff)
downloadtelepathy-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.ac4
-rw-r--r--src/mcd-account-manager-default.c158
-rw-r--r--src/mcd-account.c328
-rw-r--r--src/mcd-misc.c17
-rw-r--r--src/mcd-misc.h2
-rw-r--r--tests/account-store-default.c22
-rw-r--r--tests/twisted/Makefile.am12
-rw-r--r--tests/twisted/account-manager/auto-connect.py5
-rw-r--r--tests/twisted/account-manager/avatar-persist.py53
-rw-r--r--tests/twisted/account-manager/avatar-refresh.py17
-rw-r--r--tests/twisted/account-manager/avatar.py23
-rw-r--r--tests/twisted/account-manager/make-valid.py5
-rw-r--r--tests/twisted/account-storage/default-keyring-storage.py72
-rw-r--r--tests/twisted/account-storage/diverted-storage.py9
-rw-r--r--tests/twisted/crash-recovery/crash-recovery.py5
-rw-r--r--tests/twisted/dispatcher/create-at-startup.py5
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]