From 00b4d08e6a8d7a4c4a9eb23232a7369bf538a419 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Tue, 17 Jul 2012 16:51:41 +0200 Subject: [PATCH] CreateAccount: make possible to select a desired storage https://bugs.freedesktop.org/show_bug.cgi?id=52231 --- mission-control-plugins/account-storage.c | 54 +++++++++++++++++ mission-control-plugins/account-storage.h | 18 ++++++ src/mcd-account-manager-default.c | 20 +++++++ src/mcd-account-manager.c | 26 ++++++-- src/mcd-account.c | 23 +++++++- src/mcd-storage.c | 41 +++++++++++++ src/mcd-storage.h | 14 +++++ src/plugin-account.c | 92 +++++++++++++++++++++++++++++ 8 files changed, 281 insertions(+), 7 deletions(-) diff --git a/mission-control-plugins/account-storage.c b/mission-control-plugins/account-storage.c index e70354f..5743a73 100644 --- a/mission-control-plugins/account-storage.c +++ b/mission-control-plugins/account-storage.c @@ -332,6 +332,14 @@ mcp_account_storage_iface_implement_get_restrictions ( iface->get_restrictions = method; } +void +mcp_account_storage_iface_implement_create ( + McpAccountStorageIface *iface, + McpAccountStorageCreate method) +{ + iface->create = method; +} + /** * mcp_account_storage_priority: * @storage: an #McpAccountStorage instance @@ -442,6 +450,52 @@ mcp_account_storage_set (const McpAccountStorage *storage, } /** + * mcp_account_storage_create: + * @storage: an #McpAccountStorage instance + * @manager: the name of the manager + * @protocol: the name of the protocol + * @params: A gchar * / GValue * hash table of account parameters + * @error: a GError to fill + * + * Inform the plugin that a new account is being created. @manager, @protocol + * and @params are given to help determining the account's unique name, but does + * not need to be stored on the account yet, mcp_account_storage_set() and + * mcp_account_storage_commit() will be called later. + * + * It is recommended to use mcp_account_manager_get_unique_name() to create the + * unique name, but it's not mandatory. One could base the unique name on an + * internal storage identifier, prefixed with the provider's name + * (e.g. goa__1234). + * + * #McpAccountStorage::created signal should not be emitted for this account, + * not even when mcp_account_storage_commit() will be called. + * + * Returns: the newly allocated account name, which should be freed + * once the caller is done with it, or %NULL if that couldn't be done. + */ +gchar * +mcp_account_storage_create (const McpAccountStorage *storage, + const McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + GHashTable *params, + GError **error) +{ + McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); + + g_return_val_if_fail (iface != NULL, NULL); + + if (iface->create == NULL) + { + g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "This storage does not implement create function"); + return NULL; + } + + return iface->create (storage, am, manager, protocol, params, error); +} + +/** * mcp_account_storage_delete: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance diff --git a/mission-control-plugins/account-storage.h b/mission-control-plugins/account-storage.h index 056674b..460a71f 100644 --- a/mission-control-plugins/account-storage.h +++ b/mission-control-plugins/account-storage.h @@ -63,6 +63,13 @@ typedef gboolean (*McpAccountStorageSetFunc) ( const gchar *account, const gchar *key, const gchar *val); +typedef gchar * (*McpAccountStorageCreate) ( + const McpAccountStorage *storage, + const McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + GHashTable *params, + GError **error); typedef gboolean (*McpAccountStorageDeleteFunc) ( const McpAccountStorage *storage, const McpAccountManager *am, @@ -112,6 +119,7 @@ struct _McpAccountStorageIface McpAccountStorageGetIdentifierFunc get_identifier; McpAccountStorageGetAdditionalInfoFunc get_additional_info; McpAccountStorageGetRestrictionsFunc get_restrictions; + McpAccountStorageCreate create; }; /* functions with which to fill in the vtable */ @@ -131,6 +139,9 @@ void mcp_account_storage_iface_implement_get (McpAccountStorageIface *iface, McpAccountStorageGetFunc method); void mcp_account_storage_iface_implement_set (McpAccountStorageIface *iface, McpAccountStorageSetFunc method); +void mcp_account_storage_iface_implement_create ( + McpAccountStorageIface *iface, + McpAccountStorageCreate method); void mcp_account_storage_iface_implement_delete (McpAccountStorageIface *iface, McpAccountStorageDeleteFunc method); void mcp_account_storage_iface_implement_list (McpAccountStorageIface *iface, @@ -166,6 +177,13 @@ gboolean mcp_account_storage_set (const McpAccountStorage *storage, const gchar *key, const gchar *value); +gchar * mcp_account_storage_create (const McpAccountStorage *storage, + const McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + GHashTable *params, + GError **error); + gboolean mcp_account_storage_delete (const McpAccountStorage *storage, const McpAccountManager *am, const gchar *account, diff --git a/src/mcd-account-manager-default.c b/src/mcd-account-manager-default.c index e156de1..5fa0a3d 100644 --- a/src/mcd-account-manager-default.c +++ b/src/mcd-account-manager-default.c @@ -519,6 +519,25 @@ _get (const McpAccountStorage *self, return TRUE; } +static gchar * +_create (const McpAccountStorage *self, + const McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + GHashTable *params, + GError **error) +{ + gchar *unique_name; + + /* See comment in plugin-account.c::_storage_create_account() for an + * explaination of how this works */ + unique_name = mcp_account_manager_get_unique_name (MCP_ACCOUNT_MANAGER (am), + manager, protocol, params); + g_return_val_if_fail (unique_name != NULL, NULL); + + return unique_name; +} + static gboolean _delete (const McpAccountStorage *self, const McpAccountManager *am, @@ -639,6 +658,7 @@ account_storage_iface_init (McpAccountStorageIface *iface, mcp_account_storage_iface_implement_get (iface, _get); mcp_account_storage_iface_implement_set (iface, _set); + mcp_account_storage_iface_implement_create (iface, _create); 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); diff --git a/src/mcd-account-manager.c b/src/mcd-account-manager.c index c71d808..45a50e6 100644 --- a/src/mcd-account-manager.c +++ b/src/mcd-account-manager.c @@ -845,15 +845,16 @@ _mcd_account_manager_create_account (McdAccountManager *account_manager, GDestroyNotify destroy) { McdAccountManagerPrivate *priv = account_manager->priv; - McpAccountManager *ma = MCP_ACCOUNT_MANAGER (priv->plugin_manager); McdStorage *storage = MCD_STORAGE (priv->plugin_manager); McdCreateAccountData *cad; McdAccount *account; - gchar *unique_name; + gchar *unique_name = NULL; + const gchar *provider; + GError *e = NULL; DEBUG ("called"); if (G_UNLIKELY (manager == NULL || manager[0] == 0 || - protocol == NULL || protocol[0] == 0)) + protocol == NULL || protocol[0] == 0)) { GError error = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Invalid parameters"}; @@ -863,9 +864,21 @@ _mcd_account_manager_create_account (McdAccountManager *account_manager, return; } - unique_name = - mcp_account_manager_get_unique_name (ma, manager, protocol, params); - g_return_if_fail (unique_name != NULL); + provider = tp_asv_get_string (properties, + TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER); + + unique_name = mcd_storage_create_account (storage, provider, + manager, protocol, params, + &e); + + if (unique_name == NULL) + { + callback (account_manager, NULL, e, user_data); + g_clear_error (&e); + if (destroy) + destroy (user_data); + return; + } /* create the basic account keys */ mcd_storage_set_string (storage, unique_name, @@ -1022,6 +1035,7 @@ get_supported_account_properties (TpSvcDBusProperties *svc, TP_IFACE_ACCOUNT ".Supersedes", TP_IFACE_ACCOUNT_INTERFACE_AVATAR ".Avatar", MC_IFACE_ACCOUNT_INTERFACE_CONDITIONS ".Condition", + TP_PROP_ACCOUNT_INTERFACE_STORAGE_STORAGE_PROVIDER, NULL }; diff --git a/src/mcd-account.c b/src/mcd-account.c index eaa5040..644ad15 100644 --- a/src/mcd-account.c +++ b/src/mcd-account.c @@ -1858,6 +1858,27 @@ get_storage_provider (TpSvcDBusProperties *self, g_value_set_static_string (value, ""); } +static gboolean +set_storage_provider (TpSvcDBusProperties *self, + const gchar *name, + const GValue *value, + GError **error) +{ + McdAccount *account = MCD_ACCOUNT (self); + McpAccountStorage *storage_plugin = get_storage_plugin (account); + const gchar *current_provider = mcp_account_storage_provider (storage_plugin); + + if (!G_VALUE_HOLDS_STRING (value) || + tp_strdiff (g_value_get_string (value), current_provider)) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Cannot change provider, it is defined at account creation only"); + return FALSE; + } + + return TRUE; +} + static void get_storage_identifier (TpSvcDBusProperties *self, const gchar *name, GValue *value) @@ -1954,7 +1975,7 @@ static const McdDBusProp account_avatar_properties[] = { }; static const McdDBusProp account_storage_properties[] = { - { "StorageProvider", NULL, get_storage_provider }, + { "StorageProvider", set_storage_provider, get_storage_provider }, { "StorageIdentifier", NULL, get_storage_identifier }, { "StorageSpecificInformation", NULL, get_storage_specific_info }, { "StorageRestrictions", NULL, get_storage_restrictions }, diff --git a/src/mcd-storage.c b/src/mcd-storage.c index e9658ad..e87d393 100644 --- a/src/mcd-storage.c +++ b/src/mcd-storage.c @@ -395,6 +395,47 @@ mcd_storage_get_plugin (McdStorage *storage, const gchar *account) } /** + * mcd_storage_create_account: + * @storage: An object implementing the #McdStorage interface + * @provider: the desired storage provider, or %NULL + * @manager: the name of the manager + * @protocol: the name of the protocol + * @params: A gchar * / GValue * hash table of account parameters + * @error: a #GError to fill when returning %NULL + * + * Create a new account in storage. This should not store any + * information on the long term storage until mcd_storage_commit() is called. + * + * See mcp_account_storage_create(). + * + * Returns: the unique name to use for the new account, or %NULL on error. + */ +gchar * +mcd_storage_create_account (McdStorage *storage, + const gchar *provider, + const gchar *manager, + const gchar *protocol, + GHashTable *params, + GError **error) +{ + McdStorageIface *iface = MCD_STORAGE_GET_IFACE (storage); + + g_assert (iface != NULL); + g_assert (!tp_str_empty (manager)); + g_assert (!tp_str_empty (protocol)); + + if (iface->create_account == NULL) + { + g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "McdStorage::create_account not implemented"); + return NULL; + } + + return iface->create_account (storage, provider, manager, protocol, params, + error); +} + +/** * mcd_storage_delete_account: * @storage: An object implementing the #McdStorage interface * @account: unique name of the account diff --git a/src/mcd-storage.h b/src/mcd-storage.h index be26ccf..a988118 100644 --- a/src/mcd-storage.h +++ b/src/mcd-storage.h @@ -51,6 +51,13 @@ struct _McdStorageIface { const GValue *value, gboolean secret); + gchar * (*create_account) (McdStorage *storage, + const gchar *provider, + const gchar *manager, + const gchar *protocol, + GHashTable *params, + GError **error); + void (*delete_account) (McdStorage *storage, const gchar *account); @@ -121,6 +128,13 @@ gboolean mcd_storage_set_value (McdStorage *storage, const GValue *value, gboolean secret); +gchar *mcd_storage_create_account (McdStorage *storage, + const gchar *provider, + const gchar *manager, + const gchar *protocol, + GHashTable *params, + GError **error); + void mcd_storage_delete_account (McdStorage *storage, const gchar *account); void mcd_storage_commit (McdStorage *storage, const gchar *account); diff --git a/src/plugin-account.c b/src/plugin-account.c index e0fa5bb..4f26b06 100644 --- a/src/plugin-account.c +++ b/src/plugin-account.c @@ -819,6 +819,97 @@ _storage_set_value (McdStorage *storage, } } +static gchar * +_storage_create_account (McdStorage *storage, + const gchar *provider, + const gchar *manager, + const gchar *protocol, + GHashTable *params, + GError **error) +{ + GList *store; + McdPluginAccountManager *self = MCD_PLUGIN_ACCOUNT_MANAGER (storage); + McpAccountManager *ma = MCP_ACCOUNT_MANAGER (self); + + /* If a storage provider is specified, use only it or fail */ + if (provider != NULL) + { + for (store = stores; store != NULL; store = g_list_next (store)) + { + McpAccountStorage *plugin = store->data; + + if (!tp_strdiff (mcp_account_storage_provider (plugin), provider)) + { + return mcp_account_storage_create (plugin, ma, manager, + protocol, params, error); + } + } + + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Storage provider '%s' does not exist", provider); + + return NULL; + } + + /* No provider specified, let's pick the first plugin able to create this + * account in priority order. + * + * FIXME: This is rather subtle, and relies on the fact that accounts + * aren't always strongly tied to a single plugin. + * + * For plugins that only store their accounts set up specifically + * through them (like the libaccounts/SSO pseudo-plugin, + * McdAccountManagerSSO), create() will fail as unimplemented, + * and we'll fall through to the next plugin. Eventually we'll + * reach the default keyfile+gnome-keyring plugin, or another + * plugin that accepts arbitrary accounts. When set() is called, + * the libaccounts/SSO plugin will reject that too, and again, + * we'll fall through to a plugin that accepts arbitrary + * accounts. + * + * Plugins that will accept arbitrary accounts being created + * via D-Bus (like the default keyfile+gnome-keyring plugin, + * and the account-diversion plugin in tests/twisted) + * should, in principle, implement create() to be successful. + * If they do, their create() will succeed, and later, so will + * their set(). + * + * We can't necessarily rely on all such plugins implementing + * create(), because it isn't a mandatory part of the plugin + * API (it was added later). However, as it happens, the + * default plugin returns successfully from create() without + * really doing anything. When we iterate through the accounts again + * to call set(), higher-priority plugins are given a second + * chance to intercept that; so we end up with create() in + * the default plugin being followed by set() from the + * higher-priority plugin. In theory that's bad because it + * splits the account across two plugins, but in practice + * it isn't a problem because the default plugin's create() + * doesn't really do anything anyway. + */ + for (store = stores; store != NULL; store = g_list_next (store)) + { + McpAccountStorage *plugin = store->data; + gchar *ret; + + ret = mcp_account_storage_create (plugin, ma, manager, protocol, params, + error); + + if (ret != NULL) + return ret; + + g_clear_error (error); + } + + /* This should never happen since the default storage is always able to create + * an account */ + g_warn_if_reached (); + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "None of the storage provider are able to create the account"); + + return NULL; +} + static void _storage_delete_account (McdStorage *storage, const gchar *account) { @@ -884,6 +975,7 @@ storage_iface_init (McdStorageIface *iface, iface->dup_accounts = _storage_dup_accounts; iface->dup_settings = _storage_dup_settings; + iface->create_account = _storage_create_account; iface->delete_account = _storage_delete_account; iface->set_string = _storage_set_string; iface->set_value = _storage_set_value; -- 1.7.10.4