summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am2
-rw-r--r--src/mcd-account-manager-gvariant.c439
-rw-r--r--src/mcd-account-manager-gvariant.h83
-rw-r--r--src/plugin-account.c2
-rw-r--r--tests/twisted/Makefile.am1
-rw-r--r--tests/twisted/account-manager/gvariant-accounts.py130
6 files changed, 657 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0c78fc48..114a1077 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,6 +20,7 @@ mc_headers = \
mcd-account-conditions.h \
mcd-account-manager.h \
mcd-account-manager-default.h \
+ mcd-account-manager-gvariant.h \
mcd-debug.h \
mcd-mission.h \
mcd-operation.h \
@@ -120,6 +121,7 @@ libmcd_convenience_la_SOURCES = \
mcd-account-manager.c \
mcd-account-manager-priv.h \
mcd-account-manager-default.c \
+ mcd-account-manager-gvariant.c \
mcd-account-priv.h \
mcd-client.c \
mcd-client-priv.h \
diff --git a/src/mcd-account-manager-gvariant.c b/src/mcd-account-manager-gvariant.c
new file mode 100644
index 00000000..2aa9b5c5
--- /dev/null
+++ b/src/mcd-account-manager-gvariant.c
@@ -0,0 +1,439 @@
+/*
+ * The gvariant account manager keyfile storage pseudo-plugin
+ *
+ * Copyright © 2012 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include "mcd-account-manager-gvariant.h"
+#include "mcd-debug.h"
+
+#define PLUGIN_NAME "gvariant"
+#define PLUGIN_PRIORITY MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT
+#define PLUGIN_DESCRIPTION "GVariant account storage backend"
+
+static void account_storage_iface_init (McpAccountStorageIface *,
+ gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (McdAccountManagerGVariant, mcd_account_manager_gvariant,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE,
+ account_storage_iface_init));
+
+static void
+mcd_account_manager_gvariant_init (McdAccountManagerGVariant *self)
+{
+ DEBUG ("");
+ self->loaded = FALSE;
+
+ /* TODO: use this instead of MC_ACCOUNT_DIR
+ self->directory = g_build_filename (
+ g_get_user_config_dir (), "mission-control", "accounts", NULL);
+ */
+
+ /* TODO: this will only work in the test environment for now. */
+ self->directory = g_build_filename (g_getenv ("MC_ACCOUNT_DIR"), NULL);
+
+ self->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, (GDestroyNotify) g_hash_table_unref);
+ self->accounts_to_paths = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, (GDestroyNotify) g_free);
+ self->accounts_modified = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, NULL);
+}
+
+static void
+_dispose (GObject *object)
+{
+ McdAccountManagerGVariant *self = (McdAccountManagerGVariant *) object;
+
+ tp_clear_pointer (&self->directory, g_free);
+
+ tp_clear_pointer (&self->accounts, g_hash_table_unref);
+ tp_clear_pointer (&self->accounts_to_paths, g_hash_table_unref);
+ tp_clear_pointer (&self->accounts_modified, g_hash_table_unref);
+
+ if (G_OBJECT_CLASS (mcd_account_manager_gvariant_parent_class)->dispose)
+ G_OBJECT_CLASS (mcd_account_manager_gvariant_parent_class)->dispose (object);
+}
+
+static void
+mcd_account_manager_gvariant_class_init (McdAccountManagerGVariantClass *cls)
+{
+ GObjectClass *oclass = (GObjectClass *) cls;
+
+ DEBUG ("");
+
+ oclass->dispose = _dispose;
+}
+
+/* simply replaces '/' with '_' */
+static gchar *
+escape_path (const gchar *str)
+{
+ guint len = strlen (str);
+ gchar *s = g_new0 (gchar, len + 1);
+ guint i;
+
+ for (i = 0; str[i] != '\0'; i++)
+ {
+ if (str[i] == '/')
+ s[i] = '_';
+ else
+ s[i] = str[i];
+ }
+
+ s[i] = '\0';
+
+ return s;
+}
+
+/* We happen to know that the string MC gave us is "sufficiently escaped" to
+ * put it in the keyfile as-is. */
+static gboolean
+_set (const McpAccountStorage *storage,
+ const McpAccountManager *am,
+ const gchar *account,
+ const gchar *key,
+ const gchar *val)
+{
+ McdAccountManagerGVariant *self = MCD_ACCOUNT_MANAGER_GVARIANT (storage);
+ GHashTable *props;
+
+ props = g_hash_table_lookup (self->accounts, account);
+ if (props == NULL)
+ {
+ gchar *tmp, *path;
+
+ props = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, (GDestroyNotify) tp_g_value_slice_free);
+
+ g_hash_table_insert (self->accounts,
+ g_strdup (account), props);
+
+ /* now the file path */
+ tmp = escape_path (account);
+ path = g_build_filename (self->directory, tmp, NULL);
+ g_hash_table_insert (self->accounts_to_paths,
+ g_strdup (account), path);
+ g_free (tmp);
+ }
+
+ if (val != NULL)
+ {
+ g_hash_table_insert (props, g_strdup (key),
+ tp_g_value_slice_new_string (val));
+ }
+ else
+ {
+ g_hash_table_remove (props, key);
+ }
+
+ g_hash_table_insert (self->accounts_modified,
+ g_strdup (account), GINT_TO_POINTER (TRUE));
+
+ return TRUE;
+}
+
+static gboolean
+_get (const McpAccountStorage *storage,
+ const McpAccountManager *am,
+ const gchar *account,
+ const gchar *key)
+{
+ McdAccountManagerGVariant *self = MCD_ACCOUNT_MANAGER_GVARIANT (storage);
+ GHashTable *asv;
+
+ asv = g_hash_table_lookup (self->accounts, account);
+ if (asv == NULL)
+ return FALSE;
+
+ if (key != NULL)
+ {
+ const gchar *val = tp_asv_get_string (asv, key);
+
+ if (val == NULL)
+ return FALSE;
+
+ mcp_account_manager_set_value (am, account,
+ key, val);
+ }
+ else
+ {
+ GHashTableIter iter;
+ gpointer k, v;
+
+ g_hash_table_iter_init (&iter, asv);
+ while (g_hash_table_iter_next (&iter, &k, &v))
+ {
+ mcp_account_manager_set_value (am, account,
+ (const gchar *) k,
+ g_value_get_string ((GValue *) v));
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_delete (const McpAccountStorage *storage,
+ const McpAccountManager *am,
+ const gchar *account,
+ const gchar *key)
+{
+ McdAccountManagerGVariant *self = MCD_ACCOUNT_MANAGER_GVARIANT (storage);
+ const gchar *filename;
+ gint ret = 0;
+
+ /* first get rid of its details (we don't care if this fails) */
+ g_hash_table_remove (self->accounts, account);
+ g_hash_table_remove (self->accounts_modified, account);
+
+ /* and now its file */
+ filename = g_hash_table_lookup (self->accounts_to_paths, account);
+ if (filename != NULL)
+ {
+ ret = g_unlink (filename);
+ g_hash_table_remove (self->accounts_to_paths, account);
+ }
+
+ return (ret == 0);
+}
+
+static gboolean
+_commit_one (McdAccountManagerGVariant *self,
+ const gchar *account)
+{
+ gboolean rval = TRUE;
+ gpointer p;
+ GHashTable *props;
+
+ GValue v = G_VALUE_INIT;
+ GVariant *variant;
+ const gchar *filename;
+ gchar *str;
+
+ /* don't bother doing anything if it hasn't been modified */
+ p = g_hash_table_lookup (self->accounts_modified, account);
+ if (!GINT_TO_POINTER (p))
+ return TRUE;
+
+ props = g_hash_table_lookup (self->accounts, account);
+ filename = g_hash_table_lookup (self->accounts_to_paths, account);
+ if (props == NULL || filename == NULL)
+ return FALSE;
+
+ /* re-add this value for serialization */
+ g_hash_table_insert (props, g_strdup ("id"),
+ tp_g_value_slice_new_string (account));
+
+ /* make into a GVariant */
+ g_value_init (&v, TP_HASH_TYPE_STRING_VARIANT_MAP);
+ g_value_set_boxed (&v, props);
+ variant = dbus_g_value_build_g_variant (&v);
+
+ /* now save to disk */
+ str = g_variant_print (variant, TRUE);
+ rval = g_file_set_contents (filename, str, -1, NULL);
+
+ /* clean up */
+ g_value_unset (&v);
+ g_free (str);
+ g_hash_table_remove (self->accounts_modified, account);
+
+ /* and finally remove this again */
+ g_hash_table_remove (props, "id");
+
+ return rval;
+}
+
+static gboolean
+_commit (const McpAccountStorage *storage,
+ const McpAccountManager *am,
+ const gchar *account)
+{
+ McdAccountManagerGVariant *self = MCD_ACCOUNT_MANAGER_GVARIANT (storage);
+ gboolean rval = TRUE;
+
+ if (account != NULL)
+ {
+ rval = _commit_one (self, account);
+ }
+ else
+ {
+ GHashTableIter iter;
+ gpointer key;
+
+ g_hash_table_iter_init (&iter, self->accounts);
+ while (rval && g_hash_table_iter_next (&iter, &key, NULL))
+ {
+ rval = _commit_one (self, (const gchar *) key);
+ }
+ }
+
+ return rval;
+}
+
+static void
+add_account (McdAccountManagerGVariant *self,
+ GVariant *variant,
+ const gchar *path)
+{
+ const gchar *account_id;
+ gboolean found;
+ GValue value = G_VALUE_INIT;
+ GHashTable *result;
+
+ found = g_variant_lookup (variant, "id", "&s", &account_id);
+ if (!found)
+ return;
+
+ dbus_g_value_parse_g_variant (variant, &value);
+ result = g_value_dup_boxed (&value);
+ g_value_unset (&value);
+
+ /* remove this object path value now; we'll add it back just before
+ * we serialize back to disk. */
+ g_hash_table_remove (result, "id");
+
+ g_hash_table_insert (self->accounts,
+ g_strdup (account_id), result);
+ g_hash_table_insert (self->accounts_to_paths,
+ g_strdup (account_id), g_strdup (path));
+}
+
+static gboolean
+load_accounts (McdAccountManagerGVariant *self,
+ GError **error)
+{
+ gboolean ret = TRUE;
+ GFile *dir;
+ GFileEnumerator *enumerator;
+ GFileInfo *info;
+
+ if (self->loaded)
+ return TRUE;
+ self->loaded = TRUE;
+
+ dir = g_file_new_for_path (self->directory);
+
+ enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE, NULL, error);
+
+ if (enumerator == NULL)
+ {
+ ret = FALSE;
+ goto out;
+ }
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, error)) != NULL)
+ {
+ const gchar *filename = g_file_info_get_name (info);
+ gchar *path;
+ gchar *contents;
+
+ path = g_build_filename (self->directory, filename, NULL);
+
+ /* ignore errors tbh */
+ if (g_file_get_contents (path, &contents, NULL, NULL))
+ {
+ GVariant *variant;
+ GError *variant_error = NULL;
+
+ variant = g_variant_parse (G_VARIANT_TYPE_VARDICT,
+ contents, NULL, NULL, &variant_error);
+
+ if (variant == NULL)
+ {
+ DEBUG ("failed to parse account <%s>: %s", filename,
+ variant_error->message);
+ g_clear_error (&variant_error);
+ }
+ else
+ {
+ add_account (self, variant, path);
+ g_variant_unref (variant);
+ }
+ }
+ else
+ {
+ DEBUG ("failed to read file: %s; ignoring", path);
+ }
+
+ g_free (path);
+ g_object_unref (info);
+ }
+
+out:
+ g_clear_object (&dir);
+ g_clear_object (&enumerator);
+
+ return ret;
+}
+
+static GList *
+_list (const McpAccountStorage *storage,
+ const McpAccountManager *am)
+{
+ McdAccountManagerGVariant *self = (McdAccountManagerGVariant *) storage;
+ GList *accounts = NULL;
+ GError *error = NULL;
+
+ if (!load_accounts (self, &error))
+ {
+ DEBUG ("failed to load accounts: %s", error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ GHashTableIter iter;
+ gpointer key;
+
+ g_hash_table_iter_init (&iter, self->accounts);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ accounts = g_list_append (accounts, g_strdup (key));
+ }
+
+ return accounts;
+}
+
+static void
+account_storage_iface_init (McpAccountStorageIface *iface,
+ gpointer unused G_GNUC_UNUSED)
+{
+ mcp_account_storage_iface_set_name (iface, PLUGIN_NAME);
+ mcp_account_storage_iface_set_desc (iface, PLUGIN_DESCRIPTION);
+ mcp_account_storage_iface_set_priority (iface, PLUGIN_PRIORITY);
+
+ mcp_account_storage_iface_implement_get (iface, _get);
+ mcp_account_storage_iface_implement_set (iface, _set);
+ mcp_account_storage_iface_implement_delete (iface, _delete);
+ mcp_account_storage_iface_implement_commit_one (iface, _commit);
+ mcp_account_storage_iface_implement_list (iface, _list);
+
+}
+
+McdAccountManagerGVariant *
+mcd_account_manager_gvariant_new (void)
+{
+ return g_object_new (MCD_TYPE_ACCOUNT_MANAGER_GVARIANT, NULL);
+}
diff --git a/src/mcd-account-manager-gvariant.h b/src/mcd-account-manager-gvariant.h
new file mode 100644
index 00000000..20537c02
--- /dev/null
+++ b/src/mcd-account-manager-gvariant.h
@@ -0,0 +1,83 @@
+/*
+ * The gvariant account manager keyfile storage pseudo-plugin
+ *
+ * Copyright © 2012 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <mission-control-plugins/mission-control-plugins.h>
+
+#ifndef __MCD_ACCOUNT_MANAGER_GVARIANT_H__
+#define __MCD_ACCOUNT_MANAGER_GVARIANT_H__
+
+G_BEGIN_DECLS
+
+#define MCD_TYPE_ACCOUNT_MANAGER_GVARIANT \
+ (mcd_account_manager_gvariant_get_type ())
+
+#define MCD_ACCOUNT_MANAGER_GVARIANT(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), MCD_TYPE_ACCOUNT_MANAGER_GVARIANT, \
+ McdAccountManagerGVariant))
+
+#define MCD_ACCOUNT_MANAGER_GVARIANT_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST((k), MCD_TYPE_ACCOUNT_MANAGER_GVARIANT, \
+ McdAccountManagerClass))
+
+#define MCD_IS_ACCOUNT_MANAGER_GVARIANT(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), MCD_TYPE_ACCOUNT_MANAGER_GVARIANT))
+
+#define MCD_IS_ACCOUNT_MANAGER_GVARIANT_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), MCD_TYPE_ACCOUNT_MANAGER_GVARIANT))
+
+#define MCD_ACCOUNT_MANAGER_GVARIANT_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_ACCOUNT_MANAGER_GVARIANT, \
+ McdAccountManagerGVariantClass))
+
+typedef struct {
+ GObject parent;
+
+ gboolean loaded;
+ gchar *directory;
+
+ /* owned (gchar *) of account => owned GHashTable<owned gchar*,
+ * slice allocated GValue> of properties */
+ GHashTable *accounts;
+
+ /* owned (gchar *) of account => owned (gchar *) of full path to
+ * account file */
+ GHashTable *accounts_to_paths;
+
+ /* owned (gchar *) of account => (gpointer) boolean whether account
+ * has changed and so needs to be written to disk. in reality the
+ * value is always GUINT_TO_POINTER(TRUE) or the account is simply
+ * absent. */
+ GHashTable *accounts_modified;
+} _McdAccountManagerGVariant;
+
+typedef struct {
+ GObjectClass parent_class;
+} _McdAccountManagerGVariantClass;
+
+typedef _McdAccountManagerGVariant McdAccountManagerGVariant;
+typedef _McdAccountManagerGVariantClass McdAccountManagerGVariantClass;
+
+GType mcd_account_manager_gvariant_get_type (void) G_GNUC_CONST;
+
+McdAccountManagerGVariant *mcd_account_manager_gvariant_new (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/plugin-account.c b/src/plugin-account.c
index 4f26b068..db7c538c 100644
--- a/src/plugin-account.c
+++ b/src/plugin-account.c
@@ -33,6 +33,7 @@
/* these pseudo-plugins take care of the actual account storage/retrieval */
#include "mcd-account-manager-default.h"
+#include "mcd-account-manager-gvariant.h"
#if ENABLE_LIBACCOUNTS_SSO
#include "mcd-account-manager-sso.h"
@@ -311,6 +312,7 @@ sort_and_cache_plugins ()
/* Add compiled-in plugins */
add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_account_manager_default_new ()));
+ add_storage_plugin (MCP_ACCOUNT_STORAGE (mcd_account_manager_gvariant_new ()));
add_libaccounts_plugins_if_enabled ();
for (p = mcp_list_objects(); p != NULL; p = g_list_next (p))
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 8cb78774..0cf469b1 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -84,6 +84,7 @@ TWISTED_SEPARATE_TESTS = \
account-manager/avatar-persist.py \
account-manager/avatar-refresh.py \
account-manager/device-idle.py \
+ account-manager/gvariant-accounts.py \
account-manager/make-valid.py \
crash-recovery/crash-recovery.py \
dispatcher/create-at-startup.py
diff --git a/tests/twisted/account-manager/gvariant-accounts.py b/tests/twisted/account-manager/gvariant-accounts.py
new file mode 100644
index 00000000..3f271be6
--- /dev/null
+++ b/tests/twisted/account-manager/gvariant-accounts.py
@@ -0,0 +1,130 @@
+# Copyright (C) 2012 Collabora Ltd.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+import dbus
+import dbus.service
+import os
+import sys
+
+from servicetest import EventPattern, call_async, assertEquals
+from mctest import exec_test, AccountManager, Account, MC
+import constants as cs
+
+account_prefix = cs.tp_path_prefix + '/Account/'
+account1_id = 'fakecm/fakeprotocol/jc_2edenton_40unatco_2eint'
+filename = None
+
+def preseed():
+ global filename
+
+ accounts_dir = os.environ['MC_ACCOUNT_DIR']
+
+ escaped = account1_id.replace('/', '_')
+ filename = accounts_dir + '/' + escaped
+ account = open(filename, 'w')
+ account.write("""{
+'id': <'%s'>,
+'manager': <'fakecm'>,
+'protocol': <'fakeprotocol'>,
+'DisplayName': <'Work account'>,
+'NormalizedName': <'jc.denton@unatco.int'>,
+'param-account': <'jc.denton@unatco.int'>,
+'param-password': <'ionstorm'>,
+'Enabled': <'true'>,
+'ConnectAutomatically': <'true'>,
+'AutomaticPresenceType': <'2'>,
+'AutomaticPresenceMessage': <'My vision is augmented'>,
+'Nickname': <'JC'>,
+'AvatarMime': <'image/jpeg'>,
+'avatar_token': <'Deus Ex'>
+}""" % account1_id)
+ account.close()
+
+def test(q, bus, unused):
+ global filename
+
+ expected_params = {
+ 'account': 'jc.denton@unatco.int',
+ 'password': 'ionstorm',
+ }
+
+ mc = MC(q, bus)
+
+ am = AccountManager(bus)
+
+ # make sure we have the right accounts
+ props = am.GetAll(cs.AM, dbus_interface=dbus.PROPERTIES_IFACE)
+ accounts = props['ValidAccounts']
+
+ assertEquals(1, len(accounts))
+ path = accounts[0]
+ assertEquals(account_prefix + account1_id, path)
+
+ account = Account(bus, path)
+
+ # make sure the account has got the right properties
+ props = account.GetAll(cs.ACCOUNT, dbus_interface=dbus.PROPERTIES_IFACE)
+
+ assertEquals('Work account', props['DisplayName'])
+ assertEquals('jc.denton@unatco.int', props['NormalizedName'])
+ assertEquals(expected_params, props['Parameters'])
+ assertEquals(True, props['Enabled'])
+ assertEquals(True, props['ConnectAutomatically'])
+ assertEquals((cs.PRESENCE_TYPE_AVAILABLE, '', 'My vision is augmented'),
+ props['AutomaticPresence'])
+ assertEquals('JC', props['Nickname'])
+
+ props = account.GetAll(cs.ACCOUNT_IFACE_AVATAR, dbus_interface=dbus.PROPERTIES_IFACE)
+ avatar = props['Avatar']
+ assertEquals('image/jpeg', avatar[1])
+
+ # now delete the account and make sure the file is removed
+ assert os.path.exists(filename)
+
+ account.Remove()
+
+ q.expect_many(EventPattern('dbus-signal', signal='Removed'),
+ EventPattern('dbus-signal', signal='AccountRemoved'))
+
+ assert not os.path.exists(filename)
+
+ accounts = am.Get(cs.AM, 'ValidAccounts', dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(0, len(accounts))
+
+ # create an account and assert the file is present
+ parameters = {
+ 'account': 'dontdivert@bar.com',
+ 'password': 'le password'
+ }
+
+ accounts_dir = os.environ['MC_ACCOUNT_DIR']
+ name = 'fakecm/fakeprotocol/dontdivert_40bar_2ecom0'
+ second_filename = os.path.join(accounts_dir, name.replace('/', '_'))
+ assert not os.path.exists(second_filename)
+
+ path = am.CreateAccount('fakecm', 'fakeprotocol', 'Display name', parameters, {})
+
+ e = q.expect('dbus-signal', signal='AccountValidityChanged')
+ object_path, valid = e.args
+ assertEquals(path, object_path)
+ assert valid
+
+ assert os.path.exists(second_filename)
+
+if __name__ == '__main__':
+ preseed()
+ exec_test(test, {}, preload_mc=False)