/* Mission Control plugin API - Account Storage plugins. * * Copyright © 2010 Nokia Corporation * Copyright © 2010 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 */ /** * SECTION:account-storage * @title: McpAccountStorage * @short_description: Account Storage object, implemented by plugins * @see_also: * @include: mission-control-plugins/mission-control-plugins.h * * Plugins may implement #McpAccountStorage in order to provide account * parameter storage backends to the AccountManager object. * * To do so, the plugin must implement a #GObject subclass that implements * #McpAccountStorage, then return an instance of that subclass from * mcp_plugin_ref_nth_object(). * * The contents of the #McpAccountStorage struct are not public, * so to provide an implementation of the virtual methods, * plugins should call mcp_account_operation_iface_implement_*() * from the interface initialization function, like this: * * * G_DEFINE_TYPE_WITH_CODE (APlugin, a_plugin, * G_TYPE_OBJECT, * G_IMPLEMENT_INTERFACE (...); * G_IMPLEMENT_INTERFACE (MCP_TYPE_ACCOUNT_STORAGE, * account_storage_iface_init)); * /* ... */ * static void * account_storage_iface_init (McpAccountStorageIface *iface, * gpointer unused G_GNUC_UNUSED) * { * mcp_account_storage_iface_set_priority (iface, 0); * mcp_account_storage_iface_set_name (iface, "foo") * mcp_account_storage_iface_set_desc (iface, "The FOO storage backend"); * mcp_account_storage_iface_set_provider (iface, * "org.freedesktop.Telepathy.MissionControl5.FooStorage"); * mcp_account_storage_iface_implement_get (iface, _plugin_getval); * mcp_account_storage_iface_implement_set (iface, _plugin_setval); * mcp_account_storage_iface_implement_delete (iface, _plugin_delete); * mcp_account_storage_iface_implement_commit (iface, _plugin_commit); * mcp_account_storage_iface_implement_commit_one (iface, _plugin_commit_one); * mcp_account_storage_iface_implement_list (iface, _plugin_list); * mcp_account_storage_iface_implement_ready (iface, _plugin_ready); * mcp_account_storage_iface_implement_get_identifier (iface, * _plugin_get_identifier); * mcp_account_storage_iface_implement_get_additional_info (iface, * _plugin_get_additional_info); * mcp_account_storage_iface_implement_get_restrictions (iface, * _plugin_get_restrictions); * /* ... */ * } * * * A single object can implement more than one interface; It is currently * unlikely that you would find it useful to implement anything other than * an account storage plugin in an account storage object, though. */ #include "config.h" #include #include #include #include #include #define MCP_DEBUG_TYPE MCP_DEBUG_ACCOUNT_STORAGE #ifdef ENABLE_DEBUG #define SDEBUG(_p, _format, ...) \ DEBUG("%s: " _format, \ (_p != NULL) ? mcp_account_storage_name (_p) : "NULL", ##__VA_ARGS__) #else /* ENABLE_DEBUG */ #define SDEBUG(_p, _format, ...) do {} while (0); #endif /* ENABLE_DEBUG */ enum { CREATED, ALTERED, TOGGLED, DELETED, ALTERED_ONE, NO_SIGNAL }; static guint signals[NO_SIGNAL] = { 0 }; static void class_init (gpointer klass, gpointer data) { GType type = G_TYPE_FROM_CLASS (klass); /** * McpAccountStorage::created * @account: the unique name of the created account * * emitted if an external entity creates an account * in the backend the emitting plugin handles * * Should not be fired until mcp_account_storage_ready() has been called * **/ signals[CREATED] = g_signal_new ("created", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * McpAccountStorage::altered * @account: the unique name of the created account * * emitted if an external entity alters an account * in the backend the emitting plugin handles * should not be emitted if a single known property has been * altered, see McpAccountStorage::altered-one instead * * Should not be fired until mcp_account_storage_ready() has been called * **/ signals[ALTERED] = g_signal_new ("altered", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * McpAccountStorage::altered-one * @account: the unique name of the created account * @name: the name of the altered property (its key) * * emitted if an external entity alters an account * in the backend that the emitting plugin handles. * * If many properties have changed, the plugin may choose to emit * McpAccountStorage::altered _instead_, but should not emit both. * * Should not be fired until mcp_account_storage_ready() has been called **/ signals[ALTERED_ONE] = g_signal_new ("altered-one", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _mcp_marshal_VOID__STRING_STRING, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); /** * McpAccountStorage::deleted * @account: the unique name of the created account * * emitted if an external entity deletes an account * in the backend the emitting plugin handles * * Should not be fired until mcp_account_storage_ready() has been called * **/ signals[DELETED] = g_signal_new ("deleted", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); /** * McpAccountStorage::toggled * @account: the unique name of the created account * @enabled: #gboolean indicating whether the account is enabled * * emitted if an external entity enables/disables an account * in the backend the emitting plugin handles * * Should not be fired until mcp_account_storage_ready() has been called * **/ signals[TOGGLED] = g_signal_new ("toggled", type, G_SIGNAL_RUN_LAST, 0, NULL, NULL, _mcp_marshal_VOID__STRING_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN); } GType mcp_account_storage_get_type (void) { static gsize once = 0; static GType type = 0; if (g_once_init_enter (&once)) { static const GTypeInfo info = { sizeof (McpAccountStorageIface), NULL, /* base_init */ NULL, /* base_finalize */ class_init, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ 0, /* instance_size */ 0, /* n_preallocs */ NULL, /* instance_init */ NULL /* value_table */ }; type = g_type_register_static (G_TYPE_INTERFACE, "McpAccountStorage", &info, 0); g_type_interface_add_prerequisite (type, G_TYPE_OBJECT); g_once_init_leave (&once, 1); } return type; } void mcp_account_storage_iface_set_priority (McpAccountStorageIface *iface, guint prio) { iface->priority = prio; } void mcp_account_storage_iface_set_name (McpAccountStorageIface *iface, const gchar *name) { iface->name = name; } void mcp_account_storage_iface_set_desc (McpAccountStorageIface *iface, const gchar *desc) { iface->desc = desc; } void mcp_account_storage_iface_set_provider (McpAccountStorageIface *iface, const gchar *provider) { iface->provider = provider; } void mcp_account_storage_iface_implement_get (McpAccountStorageIface *iface, McpAccountStorageGetFunc method) { iface->get = method; } void mcp_account_storage_iface_implement_set (McpAccountStorageIface *iface, McpAccountStorageSetFunc method) { iface->set = method; } void mcp_account_storage_iface_implement_delete (McpAccountStorageIface *iface, McpAccountStorageDeleteFunc method) { iface->delete = method; } void mcp_account_storage_iface_implement_commit (McpAccountStorageIface *iface, McpAccountStorageCommitFunc method) { iface->commit = method; } void mcp_account_storage_iface_implement_commit_one (McpAccountStorageIface *iface, McpAccountStorageCommitOneFunc method) { iface->commit_one = method; } void mcp_account_storage_iface_implement_list (McpAccountStorageIface *iface, McpAccountStorageListFunc method) { iface->list = method; } void mcp_account_storage_iface_implement_ready (McpAccountStorageIface *iface, McpAccountStorageReadyFunc method) { iface->ready = method; } void mcp_account_storage_iface_implement_get_identifier ( McpAccountStorageIface *iface, McpAccountStorageGetIdentifierFunc method) { iface->get_identifier = method; } void mcp_account_storage_iface_implement_get_additional_info ( McpAccountStorageIface *iface, McpAccountStorageGetAdditionalInfoFunc method) { iface->get_additional_info = method; } void mcp_account_storage_iface_implement_get_restrictions ( McpAccountStorageIface *iface, McpAccountStorageGetRestrictionsFunc method) { 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 * * Gets the priority for this plugin. * * Priorities currently run from MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_DEFAULT * (the default storage plugin priority) upwards. * * Plugins at a higher priority then MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING * will have the opportunity to "steal" passwords from the gnome keyring: * Plugins at a lower priority than this will not receive secret parameters * from MC as the keyring plugin will already have claimed them. * * Plugins at a lower priority than the default plugin will never be asked to * store any details, although they may still be asked to list them at startup * time, and may asynchronously notify MC of accounts via the signals above. * * When loading accounts at startup, plugins are consulted in order from * lowest to highest, so that higher priority plugins may overrule settings * from lower priority plugins. * * Loading all the accounts is only done at startup, before the dbus name * is claimed, and is therefore the only time plugins are allowed to indulge * in blocking calls (indeed, they are expected to carry out this operation, * and ONLY this operation, synchronously). * * When values are being set, the plugins are invoked from highest priority * to lowest, with the first plugin that claims a setting being assigned * ownership, and all lower priority plugins being asked to delete the * setting in question. * * Returns: the priority of this plugin **/ gint mcp_account_storage_priority (const McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); g_return_val_if_fail (iface != NULL, -1); return iface->priority; } /** * mcp_account_storage_get: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account * @key: the setting whose value we wish to fetch * * The plugin is expected to quickly and synchronously update * the value associated with @key using calls to @am. * * The plugin is not required to consult whatever long term storage * it uses, and may fetch said value from its internal cache, if any. * * If @key is %NULL the plugin should write all its settings for @account * into the account manager via @am. The return value in this case should * be %TRUE if any settings were found. * * Returns: %TRUE if a value was found and %FALSE otherwise */ gboolean mcp_account_storage_get (const McpAccountStorage *storage, McpAccountManager *am, const gchar *account, const gchar *key) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, FALSE); return iface->get (storage, am, account, key); } /** * mcp_account_storage_set: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique name of the account * @key: the setting whose value we wish to fetch * @value: a value to associate with @key * * The plugin is expected to either quickly and synchronously * update its internal cache of values with @value, or to * decline to store the setting. * * The plugin is not expected to write to its long term storage * at this point. * * Returns: %TRUE if the setting was claimed, %FALSE otherwise */ gboolean mcp_account_storage_set (const McpAccountStorage *storage, const McpAccountManager *am, const gchar *account, const gchar *key, const gchar *value) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, FALSE); return iface->set (storage, am, account, key, value); } /** * 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 * @account: the unique name of the account * @key: the setting whose value we wish to fetch * * The plugin is expected to remove the setting for @key from its * internal cache and to remember that its state has changed, so * that it can delete said setting from its long term storage if * its long term storage method makes this necessary. * * If @key is %NULL, the plugin should forget all its settings for * @account (and remember to delete @account from its storage later) * * The plugin is not expected to update its long term storage at * this point. * * Returns: %TRUE if the setting or settings are not * the plugin's cache after this operation, %FALSE otherwise. * This is very unlikely to ever be %FALSE, as a plugin is always * expected to be able to manipulate its own cache. */ gboolean mcp_account_storage_delete (const McpAccountStorage *storage, const McpAccountManager *am, const gchar *account, const gchar *key) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, FALSE); return iface->delete (storage, am, account, key); } /** * mcp_account_storage_commit: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * * The plugin is expected to write its cache to long term storage, * deleting, adding or updating entries in said storage as needed. * * This call is expected to return promptly, but the plugin is * not required to have finished its commit operation when it returns, * merely to have started the operation. * * If the @commit_one method is implemented, it will be called preferentially * if only one account is to be committed. If the @commit_one method is * implemented but @commit is not, @commit_one will be called with * @account_name = %NULL to commit all accounts. * * Returns: %TRUE if the commit process was started (but not necessarily * completed) successfully; %FALSE if there was a problem that was immediately * obvious. */ gboolean mcp_account_storage_commit (const McpAccountStorage *storage, const McpAccountManager *am) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, "committing all accounts"); g_return_val_if_fail (iface != NULL, FALSE); if (iface->commit != NULL) { return iface->commit (storage, am); } else if (iface->commit_one != NULL) { return iface->commit_one (storage, am, NULL); } else { SDEBUG (storage, "neither commit nor commit_one is implemented; cannot save accounts"); return FALSE; } } /** * mcp_account_storage_commit_one: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * @account: the unique suffix of an account's object path, or %NULL if * all accounts are to be committed * * The same as mcp_account_storage_commit(), but only commit the given * account. This is optional to implement; the default implementation * is to call @commit. * * Returns: %TRUE if the commit process was started (but not necessarily * completed) successfully; %FALSE if there was a problem that was immediately * obvious. */ gboolean mcp_account_storage_commit_one (const McpAccountStorage *storage, const McpAccountManager *am, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, "called for %s", account ? account : ""); g_return_val_if_fail (iface != NULL, FALSE); if (iface->commit_one != NULL) return iface->commit_one (storage, am, account); else /* Fall back to plain ->commit() */ return mcp_account_storage_commit (storage, am); } /** * mcp_account_storage_list: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * * This method is called only at initialisation time, before the dbus name * has been claimed, and is the only one permitted to block. * * Returns: (element-type utf8) (transfer full): a list of account names that * the plugin has settings for. The account names should be freed with * g_free(), and the list with g_list_free(), when the caller is done with * them. **/ GList * mcp_account_storage_list (const McpAccountStorage *storage, const McpAccountManager *am) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, NULL); return iface->list (storage, am); } /** * mcp_account_storage_ready: * @storage: an #McpAccountStorage instance * @am: an #McpAccountManager instance * * Informs the plugin that it is now permitted to create new accounts, * ie it can now fire its "created", "altered", "toggled" and "deleted" * signals. */ void mcp_account_storage_ready (const McpAccountStorage *storage, const McpAccountManager *am) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); g_return_if_fail (iface != NULL); /* plugins that can't create accounts from external sources don't * * need to implement this method, as they can never fire the async * * account change signals: */ if (iface->ready != NULL) iface->ready (storage, am); } /** * mcp_account_storage_get_identifier: * @storage: an #McpAccountStorage instance * @account: the unique name of the account * @identifier: a zero-filled #GValue whose type can be sent over D-Bus by * dbus-glib to hold the identifier. * * Get the storage-specific identifier for this account. The type is variant, * hence the GValue. */ void mcp_account_storage_get_identifier (const McpAccountStorage *storage, const gchar *account, GValue *identifier) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); SDEBUG (storage, ""); g_return_if_fail (iface != NULL); g_return_if_fail (identifier != NULL); g_return_if_fail (!G_IS_VALUE (identifier)); if (iface->get_identifier == NULL) { g_value_init (identifier, G_TYPE_STRING); g_value_set_string (identifier, account); } else { iface->get_identifier (storage, account, identifier); } } /** * mcp_account_storage_get_additional_info: * @storage: an #McpAccountStorage instance * @account: the unique name of the account * * Return additional storage-specific information about this account, which is * made available on D-Bus but not otherwise interpreted by Mission Control. * * Returns: a mapping from strings to #GValues, which must be freed by * the caller. */ GHashTable * mcp_account_storage_get_additional_info (const McpAccountStorage *storage, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); GHashTable *ret = NULL; SDEBUG (storage, ""); g_return_val_if_fail (iface != NULL, FALSE); if (iface->get_additional_info != NULL) ret = iface->get_additional_info (storage, account); if (ret == NULL) ret = g_hash_table_new (g_str_hash, g_str_equal); return ret; } /** * mcp_account_storage_get_restrictions: * @storage: an #McpAccountStorage instance * @account: the unique name of the account * * Returns: a bitmask of %TpStorageRestrictionFlags with the restrictions to * account storage. */ /* FIXME: when breaking API, make this return TpStorageRestrictionFlags */ guint mcp_account_storage_get_restrictions (const McpAccountStorage *storage, const gchar *account) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); g_return_val_if_fail (iface != NULL, 0); if (iface->get_restrictions == NULL) return 0; else return iface->get_restrictions (storage, account); } /** * mcp_account_storage_name: * @storage: an #McpAccountStorage instance * * Returns: the plugin's name (for logging etc) */ const gchar * mcp_account_storage_name (const McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); g_return_val_if_fail (iface != NULL, NULL); return iface->name; } /** * mcp_account_storage_description: * @storage: an #McpAccountStorage instance * * Returns: the plugin's description (for logging etc) */ const gchar * mcp_account_storage_description (const McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); g_return_val_if_fail (iface != NULL, NULL); return iface->desc; } /** * mcp_account_storage_provider: * @storage: an #McpAccountStorage instance * * Returns: a DBus namespaced name for this plugin. */ const gchar * mcp_account_storage_provider (const McpAccountStorage *storage) { McpAccountStorageIface *iface = MCP_ACCOUNT_STORAGE_GET_IFACE (storage); g_return_val_if_fail (iface != NULL, NULL); return iface->provider != NULL ? iface->provider : ""; }