summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonny Lamb <jonny.lamb@collabora.co.uk>2012-06-15 18:18:48 +0100
committerJonny Lamb <jonny.lamb@collabora.co.uk>2012-08-03 10:43:40 +0100
commit0df88cd7a55e7a3a1e9bfce86adf1d6665e8c770 (patch)
tree752e176ea52057cbb1268e895b8f02c9e718386c
parentef972de9612dc06a72a94bdb05299c80956b3cb1 (diff)
add initial version of new gvariant account backend
The big problem with this right now is that the mcp API depends on all account properties being strings. Once the API is fixed we can update this plugin easily to support it, and actually save types to disk! Bug: https://bugs.freedesktop.org/show_bug.cgi?id=35896 Signed-off-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
-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)