diff options
| -rw-r--r-- | src/mcd-account-manager-default.c | 513 | ||||
| -rw-r--r-- | src/mcd-account-manager-default.h | 2 | ||||
| -rw-r--r-- | tests/twisted/account-storage/default-keyring-storage.py | 108 | ||||
| -rw-r--r-- | tests/twisted/account-storage/diverted-storage.py | 4 | 
4 files changed, 481 insertions, 146 deletions
diff --git a/src/mcd-account-manager-default.c b/src/mcd-account-manager-default.c index 6bbea0ed..4dc0e8b0 100644 --- a/src/mcd-account-manager-default.c +++ b/src/mcd-account-manager-default.c @@ -1,8 +1,8 @@  /* - * The default account manager keyfile storage pseudo-plugin + * The default account manager storage pseudo-plugin   *   * Copyright © 2010 Nokia Corporation - * Copyright © 2010 Collabora Ltd. + * Copyright © 2010-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 @@ -32,20 +32,22 @@  #include "mcd-debug.h"  #include "mcd-misc.h" -#define PLUGIN_NAME "default-gkeyfile" +#define PLUGIN_NAME "default"  #define PLUGIN_PRIORITY MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT -#define PLUGIN_DESCRIPTION "GKeyFile (default) account storage backend" -#define INITIAL_CONFIG "# Telepathy accounts\n" +#define PLUGIN_DESCRIPTION "Default account storage backend"  typedef struct {      /* owned string, attribute => owned string, value -     * attributes to be stored in the keyfile */ +     * attributes to be stored in the variant-file */      GHashTable *attributes;      /* owned string, parameter (without "param-") => owned string, value -     * parameters to be stored in the keyfile */ +     * parameters to be stored in the variant-file */      GHashTable *untyped_parameters;      /* TRUE if the entire account is pending deletion */      gboolean pending_deletion; +    /* TRUE if the account doesn't really exist, but is here to stop us +     * loading it from a lower-priority file */ +    gboolean absent;  } McdDefaultStoredAccount;  static McdDefaultStoredAccount * @@ -72,6 +74,7 @@ ensure_stored_account (McdAccountManagerDefault *self,      }    sa->pending_deletion = FALSE; +  sa->absent = FALSE;    return sa;  } @@ -113,17 +116,37 @@ get_old_filename (void)  }  static gchar * -account_filename_in (const gchar *dir) +accounts_cfg_in (const gchar *dir)  {    return g_build_filename (dir, "telepathy", "mission-control", "accounts.cfg",        NULL);  } +static gchar * +account_directory_in (const gchar *dir) +{ +  return g_build_filename (dir, "telepathy", "mission-control", NULL); +} + +static gchar * +account_file_in (const gchar *dir, +    const gchar *account) +{ +  gchar *basename = g_strdup_printf ("%s.account", account); +  gchar *ret; + +  g_strdelimit (basename, "/", '-'); +  ret = g_build_filename (dir, "telepathy", "mission-control", +      basename, NULL); +  g_free (basename); +  return ret; +} +  static void  mcd_account_manager_default_init (McdAccountManagerDefault *self)  {    DEBUG ("mcd_account_manager_default_init"); -  self->filename = account_filename_in (g_get_user_data_dir ()); +  self->directory = account_directory_in (g_get_user_data_dir ());    self->accounts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,        stored_account_free);    self->save = FALSE; @@ -136,8 +159,7 @@ mcd_account_manager_default_class_init (McdAccountManagerDefaultClass *cls)    DEBUG ("mcd_account_manager_default_class_init");  } -/* We happen to know that the string MC gave us is "sufficiently escaped" to - * put it in the keyfile as-is. */ +/* The value is escaped as if for a keyfile */  static gboolean  set_parameter (const McpAccountStorage *self,      const McpAccountManager *am, @@ -213,7 +235,7 @@ get_parameter (const McpAccountStorage *self,      {        gchar *v = NULL; -      if (sa == NULL) +      if (sa == NULL || sa->absent)          return FALSE;        v = g_hash_table_lookup (sa->untyped_parameters, parameter); @@ -222,7 +244,6 @@ get_parameter (const McpAccountStorage *self,          return FALSE;        mcp_account_manager_set_value (am, account, prefixed, v); -      g_free (v);      }    else      { @@ -241,7 +262,7 @@ _get (const McpAccountStorage *self,    McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self);    McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); -  if (sa == NULL) +  if (sa == NULL || sa->absent)      return FALSE;    if (key != NULL) @@ -259,7 +280,6 @@ _get (const McpAccountStorage *self,          return FALSE;        mcp_account_manager_set_value (am, account, key, v); -      g_free (v);      }    else      { @@ -321,7 +341,7 @@ _delete (const McpAccountStorage *self,    McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self);    McdDefaultStoredAccount *sa = lookup_stored_account (amd, account); -  if (sa == NULL) +  if (sa == NULL || sa->absent)      {        /* Apparently we never had this account anyway. The plugin API         * considers this to be "success". */ @@ -362,84 +382,162 @@ _delete (const McpAccountStorage *self,    return TRUE;  } +static gboolean +am_default_commit_one (McdAccountManagerDefault *self, +    const gchar *account_name, +    McdDefaultStoredAccount *sa) +{ +  gchar *filename; +  GHashTableIter inner; +  gpointer k, v; +  GVariantBuilder params_builder; +  GVariantBuilder attrs_builder; +  GVariant *content; +  gchar *content_text; +  gboolean ret; +  GError *error = NULL; + +  filename = account_file_in (g_get_user_data_dir (), account_name); + +  if (sa->pending_deletion) +    { +      const gchar * const *iter; + +      DEBUG ("Deleting account %s from %s", account_name, filename); + +      if (g_unlink (filename) != 0) +        { +          int e = errno; + +          /* ENOENT is OK, anything else is more upsetting */ +          if (e != ENOENT) +            { +              WARNING ("Unable to delete %s: %s", filename, +                  g_strerror (e)); +              g_free (filename); +              return FALSE; +            } +        } + +      for (iter = g_get_system_data_dirs (); +          iter != NULL && *iter != NULL; +          iter++) +        { +          gchar *other = account_file_in (*iter, account_name); +          gboolean other_exists = g_file_test (other, G_FILE_TEST_EXISTS); + +          g_free (other); + +          if (other_exists) +            { +              /* There is a lower-priority file that would provide this +               * account. We can't delete a file from XDG_DATA_DIRS which +               * are conceptually read-only, but we can mask it with an +               * empty file (prior art: systemd) */ +              if (!g_file_set_contents (filename, "", 0, &error)) +                { +                  WARNING ("Unable to save empty account file to %s: %s", +                      filename, error->message); +                  g_clear_error (&error); +                  g_free (filename); +                  return FALSE; +                } + +              break; +            } +        } + +      g_free (filename); +      return TRUE; +    } + +  DEBUG ("Saving account %s to %s", account_name, filename); + +  g_variant_builder_init (&attrs_builder, G_VARIANT_TYPE_VARDICT); + +  g_hash_table_iter_init (&inner, sa->attributes); + +  while (g_hash_table_iter_next (&inner, &k, &v)) +    { +      /* FIXME: for now, we put the keyfile-escaped value in a string, +       * and store that. This needs fixing to use typed attributes +       * before this code gets released. */ +      g_variant_builder_add (&attrs_builder, "{sv}", +          k, g_variant_new_string (v)); +    } + +  g_variant_builder_init (¶ms_builder, G_VARIANT_TYPE ("a{ss}")); +  g_hash_table_iter_init (&inner, sa->untyped_parameters); + +  while (g_hash_table_iter_next (&inner, &k, &v)) +    { +      g_variant_builder_add (¶ms_builder, "{ss}", k, v); +    } + +  g_variant_builder_add (&attrs_builder, "{sv}", +      "KeyFileParameters", g_variant_builder_end (¶ms_builder)); + +  content = g_variant_ref_sink (g_variant_builder_end (&attrs_builder)); +  content_text = g_variant_print (content, TRUE); +  DEBUG ("%s", content_text); +  g_variant_unref (content); + +  if (g_file_set_contents (filename, content_text, -1, &error)) +    { +      ret = TRUE; +    } +  else +    { +      WARNING ("Unable to save account to %s: %s", filename, +          error->message); +      g_clear_error (&error); +      ret = FALSE; +    } + +  g_free (filename); +  g_free (content_text); +  return ret; +}  static gboolean  _commit (const McpAccountStorage *self,      const McpAccountManager *am,      const gchar *account)  { -  gsize n; -  gchar *data;    McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self); -  gboolean rval = FALSE; -  gchar *dir; +  gboolean all_succeeded = TRUE;    GError *error = NULL;    GHashTableIter outer;    gpointer account_p, sa_p; -  GKeyFile *keyfile;    if (!amd->save)      return TRUE; -  dir = g_path_get_dirname (amd->filename); - -  DEBUG ("Saving accounts to %s", amd->filename); +  DEBUG ("Saving accounts to %s", amd->directory); -  if (!mcd_ensure_directory (dir, &error)) +  if (!mcd_ensure_directory (amd->directory, &error))      {        g_warning ("%s", error->message);        g_error_free (error); -      /* fall through anyway: writing to the file will fail, but it does +      /* fall through anyway: writing to the files will fail, but it does         * give us a chance to commit to the keyring too */      } -  g_free (dir); - -  keyfile = g_key_file_new (); -    g_hash_table_iter_init (&outer, amd->accounts);    while (g_hash_table_iter_next (&outer, &account_p, &sa_p))      { -      McdDefaultStoredAccount *sa = sa_p; -      GHashTableIter inner; -      gpointer k, v; - -      /* don't save accounts that are being deleted */ -      if (sa->pending_deletion) -        continue; - -      g_hash_table_iter_init (&inner, sa->attributes); - -      while (g_hash_table_iter_next (&inner, &k, &v)) -        g_key_file_set_value (keyfile, account_p, k, v); - -      g_hash_table_iter_init (&inner, sa->untyped_parameters); - -      while (g_hash_table_iter_next (&inner, &k, &v)) +      if (account == NULL || !tp_strdiff (account, account_p))          { -          gchar *prefixed = g_strdup_printf ("param-%s", (const gchar *) k); - -          g_key_file_set_value (keyfile, account_p, prefixed, v); -          g_free (prefixed); +          if (!am_default_commit_one (amd, account_p, sa_p)) +            all_succeeded = FALSE;          }      } -  data = g_key_file_to_data (keyfile, &n, NULL); -  rval = g_file_set_contents (amd->filename, data, n, &error); - -  if (rval) +  if (all_succeeded)      {        amd->save = FALSE;      } -  else -    { -      g_warning ("%s", error->message); -      g_error_free (error); -    } - -  g_free (data); -  g_key_file_unref (keyfile);    g_hash_table_iter_init (&outer, amd->accounts); @@ -452,10 +550,10 @@ _commit (const McpAccountStorage *self,          g_hash_table_iter_remove (&outer);      } -  return rval; +  return all_succeeded;  } -static void +static gboolean  am_default_load_keyfile (McdAccountManagerDefault *self,      const gchar *filename)  { @@ -464,6 +562,13 @@ am_default_load_keyfile (McdAccountManagerDefault *self,    gsize i;    gsize n = 0;    GStrv account_tails; +  gboolean all_ok = TRUE; + +  /* We shouldn't call this function without modification if we think we've +   * migrated to a newer storage format, because it doesn't check whether +   * each account has already been loaded. */ +  g_assert (!self->loaded); +  g_assert (g_hash_table_size (self->accounts) == 0);    if (g_key_file_load_from_file (keyfile, filename,          G_KEY_FILE_KEEP_COMMENTS, &error)) @@ -475,12 +580,10 @@ am_default_load_keyfile (McdAccountManagerDefault *self,        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 (keyfile, INITIAL_CONFIG, -1, -          G_KEY_FILE_KEEP_COMMENTS, NULL); +      /* Start with a blank configuration. */ +      g_key_file_load_from_data (keyfile, "", -1, 0, NULL); +      /* Don't delete the old file, which might be recoverable. */ +      all_ok = FALSE;      }    account_tails = g_key_file_get_groups (keyfile, &n); @@ -493,6 +596,9 @@ am_default_load_keyfile (McdAccountManagerDefault *self,        gsize m = 0;        GStrv keys = g_key_file_get_keys (keyfile, account, &m, NULL); +      /* We're going to need to migrate this account. */ +      self->save = TRUE; +        for (j = 0; j < m; j++)          {            gchar *key = keys[j]; @@ -516,6 +622,179 @@ am_default_load_keyfile (McdAccountManagerDefault *self,    g_strfreev (account_tails);    g_key_file_unref (keyfile); +  return all_ok; +} + +static void +am_default_load_variant_file (McdAccountManagerDefault *self, +    const gchar *account_tail, +    const gchar *full_name) +{ +  McdDefaultStoredAccount *sa; +  gchar *text = NULL; +  gsize len; +  GVariant *contents = NULL; +  GVariantIter iter; +  const gchar *k; +  GVariant *v; +  GError *error = NULL; + +  DEBUG ("%s from %s", account_tail, full_name); + +  sa = lookup_stored_account (self, account_tail); + +  if (sa != NULL) +    { +      DEBUG ("Ignoring %s: account %s already %s", +          full_name, account_tail, sa->absent ? "masked" : "loaded"); +      goto finally; +    } + +  if (!g_file_get_contents (full_name, &text, &len, &error)) +    { +      WARNING ("Unable to read account %s from %s: %s", +          account_tail, full_name, error->message); +      g_error_free (error); +      goto finally; +    } + +  if (len == 0) +    { +      DEBUG ("Empty file %s masks account %s", full_name, account_tail); +      ensure_stored_account (self, account_tail)->absent = TRUE; +      goto finally; +    } + +  contents = g_variant_parse (G_VARIANT_TYPE_VARDICT, +      text, text + len, NULL, &error); + +  if (contents == NULL) +    { +      WARNING ("Unable to parse account %s from %s: %s", +          account_tail, full_name, error->message); +      g_error_free (error); +      goto finally; +    } + +  sa = ensure_stored_account (self, account_tail); + +  g_variant_iter_init (&iter, contents); + +  while (g_variant_iter_loop (&iter, "{sv}", &k, &v)) +    { +      if (!tp_strdiff (k, "KeyFileParameters")) +        { +          GVariantIter param_iter; +          gchar *parameter; +          gchar *param_value; + +          if (!g_variant_is_of_type (v, G_VARIANT_TYPE ("a{ss}"))) +            { +              gchar *repr = g_variant_print (v, TRUE); + +              WARNING ("invalid KeyFileParameters found in %s, " +                  "ignoring: %s", full_name, repr); +              g_free (repr); +              continue; +            } + +          g_variant_iter_init (¶m_iter, v); + +          while (g_variant_iter_next (¶m_iter, "{ss}", ¶meter, +                ¶m_value)) +            { +              /* steals parameter, param_value */ +              g_hash_table_insert (sa->untyped_parameters, parameter, +                  param_value); +            } +        } +      else +        { +          /* an ordinary attribute */ +          /* FIXME: this is temporary and should not be released: +           * it assumes that all attributes are keyfile-escaped +           * strings */ +          if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) +            { +              g_hash_table_insert (sa->attributes, +                  g_strdup (k), g_variant_dup_string (v, NULL)); +            } +          else +            { +              gchar *repr = g_variant_print (v, TRUE); + +              WARNING ("Not a string: %s", repr); +              g_free (repr); +            } +        } +    } + +finally: +  tp_clear_pointer (&contents, g_variant_unref); +  g_free (text); +} + +static void +am_default_load_directory (McdAccountManagerDefault *self, +    const gchar *directory) +{ +  GDir *dir_handle; +  const gchar *basename; +  GRegex *regex; +  GError *error = NULL; + +  dir_handle = g_dir_open (directory, 0, &error); + +  if (dir_handle == NULL) +    { +      /* We expect ENOENT. Anything else is a cause for (minor) concern. */ +      if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) +        DEBUG ("%s", error->message); +      else +        WARNING ("%s", error->message); + +      g_error_free (error); +      return; +    } + +  DEBUG ("Looking for accounts in %s", directory); + +  regex = g_regex_new ("^[A-Za-z][A-Za-z0-9_]*-"  /* CM name */ +      "[A-Za-z][A-Za-z0-9_]*-"                    /* protocol with s/-/_/ */ +      "[A-Za-z_][A-Za-z0-9_]*\\.account$",        /* account-specific part */ +      G_REGEX_DOLLAR_ENDONLY, 0, &error); +  g_assert_no_error (error); + +  while ((basename = g_dir_read_name (dir_handle)) != NULL) +    { +      gchar *full_name; +      gchar *account_tail; + +      /* skip it silently if it's obviously not an account */ +      if (!g_str_has_suffix (basename, ".account")) +        continue; + +      /* We consider ourselves to have migrated to the new storage format +       * as soon as we find something that looks as though it ought to be an +       * account. */ +      self->loaded = TRUE; + +      if (!g_regex_match (regex, basename, 0, NULL)) +        { +          WARNING ("Ignoring %s/%s: not a syntactically valid account", +              directory, basename); +        } + +      full_name = g_build_filename (directory, basename, NULL); +      account_tail = g_strdup (basename); +      g_strdelimit (account_tail, "-", '/'); +      g_strdelimit (account_tail, ".", '\0'); + +      am_default_load_variant_file (self, account_tail, full_name); + +      g_free (account_tail); +      g_free (full_name); +    }  }  static GList * @@ -525,16 +804,47 @@ _list (const McpAccountStorage *self,    GList *rval = NULL;    McdAccountManagerDefault *amd = MCD_ACCOUNT_MANAGER_DEFAULT (self);    GHashTableIter hash_iter; +  gchar *migrate_from = NULL;    gpointer k, v; -  if (!amd->loaded && g_file_test (amd->filename, G_FILE_TEST_EXISTS)) +  if (!amd->loaded)      { -      /* 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; +      const gchar * const *iter; + +      am_default_load_directory (amd, amd->directory); + +      /* We do this even if am_default_load_directory() succeeded, and +       * do not stop when amd->loaded becomes true. If XDG_DATA_HOME +       * contains gabble-jabber-example_2eexample_40com.account, that doesn't +       * mean a directory in XDG_DATA_DIRS doesn't also contain +       * haze-msn-example_2ehotmail_40com.account or something, which +       * should also be loaded. */ +      for (iter = g_get_system_data_dirs (); +          iter != NULL && *iter != NULL; +          iter++) +        { +          gchar *dir = account_directory_in (*iter); + +          am_default_load_directory (amd, dir); +          g_free (dir); +        } +    } + +  if (!amd->loaded) +    { +      migrate_from = accounts_cfg_in (g_get_user_data_dir ()); + +      if (g_file_test (migrate_from, G_FILE_TEST_EXISTS)) +        { +          if (!am_default_load_keyfile (amd, migrate_from)) +            tp_clear_pointer (&migrate_from, g_free); + +          amd->loaded = TRUE; +        } +      else +        { +          tp_clear_pointer (&migrate_from, g_free); +        }      }    if (!amd->loaded) @@ -545,14 +855,14 @@ _list (const McpAccountStorage *self,            iter != NULL && *iter != NULL;            iter++)          { -          gchar *filename = account_filename_in (*iter); +          /* not setting migrate_from here - XDG_DATA_DIRS are conceptually +           * read-only, so we don't want to delete these files */ +          gchar *filename = accounts_cfg_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); @@ -564,25 +874,19 @@ _list (const McpAccountStorage *self,    if (!amd->loaded)      { -      gchar *old_filename = get_old_filename (); +      migrate_from = get_old_filename (); -      if (g_file_test (old_filename, G_FILE_TEST_EXISTS)) +      if (g_file_test (migrate_from, G_FILE_TEST_EXISTS))          { -          am_default_load_keyfile (amd, old_filename); +          if (!am_default_load_keyfile (amd, migrate_from)) +            tp_clear_pointer (&migrate_from, g_free);            amd->loaded = TRUE;            amd->save = TRUE; - -          if (_commit (self, am, NULL)) -            { -              DEBUG ("Migrated %s to new location: deleting old copy", -                  old_filename); -              if (g_unlink (old_filename) != 0) -                g_warning ("Unable to delete %s: %s", old_filename, -                    g_strerror (errno)); -            }          } - -      g_free (old_filename); +      else +        { +          tp_clear_pointer (&migrate_from, g_free); +        }      }    if (!amd->loaded) @@ -590,9 +894,28 @@ _list (const McpAccountStorage *self,        DEBUG ("Creating initial account data");        amd->loaded = TRUE;        amd->save = TRUE; -      _commit (self, am, NULL);      } +  if (amd->save) +    { +      DEBUG ("Saving initial or migrated account data"); + +      if (_commit (self, am, NULL)) +        { +          if (migrate_from != NULL) +            { +              DEBUG ("Migrated %s to new location: deleting old copy", +                  migrate_from); + +              if (g_unlink (migrate_from) != 0) +                WARNING ("Unable to delete %s: %s", migrate_from, +                    g_strerror (errno)); +            } +        } +    } + +  tp_clear_pointer (&migrate_from, g_free); +    g_hash_table_iter_init (&hash_iter, amd->accounts);    while (g_hash_table_iter_next (&hash_iter, &k, &v)) diff --git a/src/mcd-account-manager-default.h b/src/mcd-account-manager-default.h index e48a767d..e16e062f 100644 --- a/src/mcd-account-manager-default.h +++ b/src/mcd-account-manager-default.h @@ -50,7 +50,7 @@ G_BEGIN_DECLS  typedef struct {    GObject parent;    GHashTable *accounts; -  gchar *filename; +  gchar *directory;    gboolean save;    gboolean loaded;  } _McdAccountManagerDefault; diff --git a/tests/twisted/account-storage/default-keyring-storage.py b/tests/twisted/account-storage/default-keyring-storage.py index 74f3bd3b..c67399ed 100644 --- a/tests/twisted/account-storage/default-keyring-storage.py +++ b/tests/twisted/account-storage/default-keyring-storage.py @@ -61,8 +61,11 @@ def account_store(op, backend, key=None, value=None,  def test(q, bus, mc):      ctl_dir = os.environ['MC_ACCOUNT_DIR']      old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') -    new_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], +    newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'],              'telepathy', 'mission-control', 'accounts.cfg') +    new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], +            'telepathy', 'mission-control', +            'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account')      group = 'fakecm/fakeprotocol/dontdivert_40example_2ecom0'      account_manager, properties, interfaces = connect_to_mc(q, bus, mc) @@ -95,25 +98,35 @@ def test(q, bus, mc):      account_props.Set(cs.ACCOUNT, 'DisplayName', 'Work account')      account_props.Set(cs.ACCOUNT, 'Icon', 'im-jabber')      account_props.Set(cs.ACCOUNT, 'Nickname', 'Joe Bloggs') +    account_props.Set(cs.ACCOUNT, 'ConnectAutomatically', True) +    account_props.Set(cs.ACCOUNT, 'AutomaticPresence', +            (dbus.UInt32(cs.PRESENCE_EXTENDED_AWAY), 'xa', +                'never online'))      tell_mc_to_die(q, bus)      # .. let's check the keyfile      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' -    assert kf[group]['param-account'] == params['account'], kf -    assert kf[group]['DisplayName'] == 'Work account', kf -    assert kf[group]['Icon'] == 'im-jabber', kf -    assert kf[group]['Nickname'] == 'Joe Bloggs', kf - -    pwd = account_store('get', 'keyfile', 'param-password') -    assert pwd == params['password'], pwd - -    # We no longer use gnome-keyring, so the password is stored as clear-text. -    assert kf[group]['param-password'] == params['password'], kf +    assert not os.path.exists(newer_key_file_name) +    assert 'Joe Bloggs' in open(new_variant_file_name).read() +    assertEquals("'fakecm'", account_store('get', 'variant-file', 'manager')) +    assertEquals("'fakeprotocol'", account_store('get', 'variant-file', +        'protocol')) +    assertEquals("'Work account'", account_store('get', 'variant-file', +        'DisplayName')) +    assertEquals("'im-jabber'", account_store('get', 'variant-file', +        'Icon')) +    assertEquals("'Joe Bloggs'", account_store('get', 'variant-file', +        'Nickname')) +    # For now, everything is a keyfile-escaped string +    assertEquals("'true'", account_store('get', 'variant-file', +        'ConnectAutomatically')) +    assertEquals("'4;xa;never online;'", account_store('get', 'variant-file', +        'AutomaticPresence')) +    assertEquals("keyfile-escaped 'dontdivert@example.com'", +            account_store('get', 'variant-file', 'param-account')) +    assertEquals("keyfile-escaped 'secrecy'", +            account_store('get', 'variant-file', 'param-password'))      # Reactivate MC      account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) @@ -139,29 +152,33 @@ def test(q, bus, mc):      # Check the account is correctly deleted      assert not os.path.exists(old_key_file_name) -    kf = keyfile_read(new_key_file_name) -    assert group not in kf, kf +    assert not os.path.exists(newer_key_file_name) +    assert not os.path.exists(new_variant_file_name)      # Tell MC to die, again      tell_mc_to_die(q, bus) -    low_prio_key_file_name = os.path.join( +    low_prio_variant_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) +            'telepathy', 'mission-control', +            'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account') +    os.makedirs(os.path.dirname(low_prio_variant_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 -protocol=fakeprotocol -param-account=dontdivert@example.com -param-password=password_in_keyfile -DisplayName=New and improved account -AutomaticPresence=2;available;; -""" % group) +    open(low_prio_variant_file_name, 'w').write( +    # For now, everything is a keyfile-escaped string, so AutomaticPresence +    # is weird +"""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'New and improved account'>, +'AutomaticPresence': <'2;available;;'>, +'KeyFileParameters': <{ +    'account': 'dontdivert@example.com', +    'password': 'password_in_variant_file' +    }> +} +""")      account_manager, properties, interfaces = resuscitate_mc(q, bus, mc)      account = get_fakecm_account(bus, mc, account_path) @@ -169,8 +186,8 @@ AutomaticPresence=2;available;;      # 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) +    assert not os.path.exists(new_variant_file_name) +    assert os.path.exists(low_prio_variant_file_name)      # Delete the password (only), like Empathy 3.0-3.4 do when migrating      account_iface.UpdateParameters({}, ['password']) @@ -188,16 +205,16 @@ AutomaticPresence=2;available;;      # 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', 'keyfile', 'param-password') +    assert not os.path.exists(newer_key_file_name) +    assert os.path.exists(low_prio_variant_file_name) +    assert os.path.exists(new_variant_file_name) +    pwd = account_store('get', 'variant-file', 'param-password')      assertEquals(None, pwd)      # 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) +    # migration from there +    os.remove(new_variant_file_name) +    os.remove(low_prio_variant_file_name)      open(old_key_file_name, 'w').write(  r"""# Telepathy accounts  [%s] @@ -212,13 +229,12 @@ AutomaticPresence=2;available;;      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 +    # This time it *does* get deleted automatically during MC startup, +    # after copying its contents to the new name/format      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']) +    assert not os.path.exists(low_prio_variant_file_name) +    assertEquals("'Ye olde account'", +            account_store('get', 'variant-file', 'DisplayName'))  if __name__ == '__main__':      ctl_dir = os.environ['MC_ACCOUNT_DIR'] diff --git a/tests/twisted/account-storage/diverted-storage.py b/tests/twisted/account-storage/diverted-storage.py index 2346b853..7aa011e8 100644 --- a/tests/twisted/account-storage/diverted-storage.py +++ b/tests/twisted/account-storage/diverted-storage.py @@ -88,10 +88,6 @@ def test(q, bus, mc):      assert kf[group]['Icon'] == 'im-jabber', kf      assert kf[group]['Nickname'] == 'Joe Bloggs', kf -    # default keyfile should be empty -    ekf = keyfile_read(empty_key_file_name) -    assert ekf == { None: {} }, ekf -      # Reactivate MC      account_manager, properties, interfaces = resuscitate_mc(q, bus, mc)      account = get_fakecm_account(bus, mc, account_path)  | 
