diff options
26 files changed, 957 insertions, 200 deletions
diff --git a/mission-control-plugins/account-storage.c b/mission-control-plugins/account-storage.c index 92af6de3..7f7187f9 100644 --- a/mission-control-plugins/account-storage.c +++ b/mission-control-plugins/account-storage.c @@ -217,9 +217,15 @@ class_init (gpointer klass, * in the backend that the emitting plugin handles. * * Before emitting this signal, the plugin must call + * either mcp_account_manager_set_attribute(), + * either mcp_account_manager_set_parameter() or * mcp_account_manager_set_value() to push the new value * into the account manager. * + * Note that mcp_account_manager_set_parameter() does not use the + * "param-" prefix, but this signal and mcp_account_manager_set_value() + * both do. + * * Should not be fired until mcp_account_storage_ready() has been called */ signals[ALTERED_ONE] = g_signal_new ("altered-one", @@ -252,7 +258,7 @@ class_init (gpointer klass, * in the backend the emitting plugin handles. This is similar to * emitting #McpAccountStorage::altered-one for the attribute * "Enabled", except that the plugin is not required to call - * mcp_account_manager_set_value() first. + * a function like mcp_account_manager_set_value() first. * * Should not be fired until mcp_account_storage_ready() has been called * @@ -514,10 +520,16 @@ mcp_account_storage_priority (const McpAccountStorage *storage) * like "DisplayName", or "param-" plus a parameter like "account" * * Get a value from the plugin's in-memory cache. - * The plugin is expected to call mcp_account_manager_set_value(), - * and if appropriate, mcp_account_manager_parameter_make_secret(), + * Before emitting this signal, the plugin must call + * either mcp_account_manager_set_attribute(), + * mcp_account_manager_set_parameter(), + * or mcp_account_manager_set_value() and (if appropriate) + * mcp_account_manager_parameter_make_secret() * before returning from this method call. * + * Note that mcp_account_manager_set_parameter() does not use the + * "param-" prefix, even if called from this function. + * * If @key is %NULL the plugin should iterate through all attributes and * parameters, and push each of them into @am, as if this method had * been called once for each attribute or parameter. It must then return diff --git a/mission-control-plugins/account-storage.h b/mission-control-plugins/account-storage.h index a0054112..5b6c21c4 100644 --- a/mission-control-plugins/account-storage.h +++ b/mission-control-plugins/account-storage.h @@ -32,15 +32,6 @@ G_BEGIN_DECLS #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_NORMAL 100 #define MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_KEYRING 10000 -typedef enum { - MCP_PARAMETER_FLAG_NONE = 0, - MCP_PARAMETER_FLAG_SECRET = TP_CONN_MGR_PARAM_FLAG_SECRET -} McpParameterFlags; - -typedef enum { - MCP_ATTRIBUTE_FLAG_NONE = 0 -} McpAttributeFlags; - /* API for plugins to implement */ typedef struct _McpAccountStorage McpAccountStorage; typedef struct _McpAccountStorageIface McpAccountStorageIface; diff --git a/mission-control-plugins/account.c b/mission-control-plugins/account.c index 32f60332..e962857c 100644 --- a/mission-control-plugins/account.c +++ b/mission-control-plugins/account.c @@ -86,6 +86,9 @@ mcp_account_manager_get_type (void) * * This function may either be called from mcp_account_storage_get(), * or just before emitting #McpAccountStorage::altered-one. + * + * New plugins should call mcp_account_manager_set_attribute() or + * mcp_account_manager_set_parameter() instead. */ void mcp_account_manager_set_value (const McpAccountManager *mcpa, @@ -102,6 +105,69 @@ mcp_account_manager_set_value (const McpAccountManager *mcpa, } /** + * mcp_account_manager_set_attribute: + * @mcpa: an #McpAccountManager instance + * @account: the unique name of an account + * @attribute: the name of an attribute, such as "DisplayName" + * @value: (allow-none): the new value, or %NULL to delete the attribute + * @flags: flags for the new value (only used if @value is non-%NULL) + * + * Inform Mission Control that @attribute has changed its value to @value. + * + * If @value is a floating reference, Mission Control will take ownership + * of it, much like g_variant_builder_add_value(). + * + * This function may either be called from mcp_account_storage_get(), + * or just before emitting #McpAccountStorage::altered-one. + */ +void +mcp_account_manager_set_attribute (const McpAccountManager *mcpa, + const gchar *account, + const gchar *attribute, + GVariant *value, + McpAttributeFlags flags) +{ + McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); + + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->set_attribute != NULL); + + iface->set_attribute (mcpa, account, attribute, value, flags); +} + +/** + * mcp_account_manager_set_parameter: + * @mcpa: an #McpAccountManager instance + * @account: the unique name of an account + * @parameter: the name of a parameter, such as "account", without + * the "param-" prefix + * @value: (allow-none): the new value, or %NULL to delete the parameter + * @flags: flags for the new value (only used if @value is non-%NULL) + * + * Inform Mission Control that @parameter has changed its value to @value. + * + * If @value is a floating reference, Mission Control will take ownership + * of it, much like g_variant_builder_add_value(). + * + * This function may either be called from mcp_account_storage_get(), + * or just before emitting #McpAccountStorage::altered-one. + */ +void +mcp_account_manager_set_parameter (const McpAccountManager *mcpa, + const gchar *account, + const gchar *parameter, + GVariant *value, + McpParameterFlags flags) +{ + McpAccountManagerIface *iface = MCP_ACCOUNT_MANAGER_GET_IFACE (mcpa); + + g_return_if_fail (iface != NULL); + g_return_if_fail (iface->set_parameter != NULL); + + iface->set_parameter (mcpa, account, parameter, value, flags); +} + +/** * mcp_account_manage_list_keys: * @mcpa: a #McpAccountManager instance * @account: the unique name of an account diff --git a/mission-control-plugins/account.h b/mission-control-plugins/account.h index 4536ab18..69a33d56 100644 --- a/mission-control-plugins/account.h +++ b/mission-control-plugins/account.h @@ -50,6 +50,18 @@ void mcp_account_manager_set_value (const McpAccountManager *mcpa, const gchar *key, const gchar *value); +void mcp_account_manager_set_attribute (const McpAccountManager *mcpa, + const gchar *account, + const gchar *attribute, + GVariant *value, + McpAttributeFlags flags); + +void mcp_account_manager_set_parameter (const McpAccountManager *mcpa, + const gchar *account, + const gchar *parameter, + GVariant *value, + McpParameterFlags flags); + gchar * mcp_account_manager_get_value (const McpAccountManager *mcpa, const gchar *account, const gchar *key); diff --git a/mission-control-plugins/implementation.h b/mission-control-plugins/implementation.h index eb857205..20c5fdf4 100644 --- a/mission-control-plugins/implementation.h +++ b/mission-control-plugins/implementation.h @@ -119,6 +119,18 @@ struct _McpAccountManagerIface { gchar * (* escape_variant_for_keyfile) (const McpAccountManager *mcpa, GVariant *variant); + + void (* set_attribute) (const McpAccountManager *mcpa, + const gchar *account, + const gchar *attribute, + GVariant *value, + McpAttributeFlags flags); + + void (* set_parameter) (const McpAccountManager *mcpa, + const gchar *account, + const gchar *parameter, + GVariant *value, + McpParameterFlags flags); }; G_END_DECLS diff --git a/mission-control-plugins/mission-control-plugins.h b/mission-control-plugins/mission-control-plugins.h index fe0d8afb..d875199c 100644 --- a/mission-control-plugins/mission-control-plugins.h +++ b/mission-control-plugins/mission-control-plugins.h @@ -25,6 +25,15 @@ #include <glib-object.h> #include <telepathy-glib/telepathy-glib.h> +typedef enum { + MCP_PARAMETER_FLAG_NONE = 0, + MCP_PARAMETER_FLAG_SECRET = TP_CONN_MGR_PARAM_FLAG_SECRET +} McpParameterFlags; + +typedef enum { + MCP_ATTRIBUTE_FLAG_NONE = 0 +} McpAttributeFlags; + #define _MCP_IN_MISSION_CONTROL_PLUGINS_H #include <mission-control-plugins/account.h> #include <mission-control-plugins/account-storage.h> diff --git a/src/mcd-account-conditions.c b/src/mcd-account-conditions.c index b36d6b1f..e40dccf9 100644 --- a/src/mcd-account-conditions.c +++ b/src/mcd-account-conditions.c @@ -53,8 +53,11 @@ store_condition (gpointer key, gpointer value, gpointer userdata) } static gboolean -set_condition (TpSvcDBusProperties *self, const gchar *name, - const GValue *value, GError **error) +set_condition (TpSvcDBusProperties *self, + const gchar *name, + const GValue *value, + McdDBusPropSetFlags flags, + GError **error) { McdAccount *account = MCD_ACCOUNT (self); McdStorage *storage = _mcd_account_get_storage (account); @@ -95,9 +98,12 @@ set_condition (TpSvcDBusProperties *self, const gchar *name, g_strfreev (keys); - g_hash_table_foreach (conditions, store_condition, account); + if (!(flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE)) + { + g_hash_table_foreach (conditions, store_condition, account); - mcd_storage_commit (storage, account_name); + mcd_storage_commit (storage, account_name); + } return TRUE; } diff --git a/src/mcd-account-config.h b/src/mcd-account-config.h index 3124692e..fc7a9eae 100644 --- a/src/mcd-account-config.h +++ b/src/mcd-account-config.h @@ -39,6 +39,8 @@ #define MC_ACCOUNTS_KEY_NORMALIZED_NAME "NormalizedName" #define MC_ACCOUNTS_KEY_AVATAR_TOKEN "avatar_token" #define MC_ACCOUNTS_KEY_AVATAR_MIME "AvatarMime" +#define MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE "AutomaticPresence" +/* next two are obsoleted by MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE */ #define MC_ACCOUNTS_KEY_AUTO_PRESENCE_STATUS "AutomaticPresenceStatus" #define MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE "AutomaticPresenceMessage" #define MC_ACCOUNTS_KEY_ICON "Icon" @@ -47,6 +49,7 @@ /* ... also "condition-*" reserved by mcd-account-conditions.c */ /* unsigned 32-bit integer, 'u' */ +/* obsoleted by MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE */ #define MC_ACCOUNTS_KEY_AUTO_PRESENCE_TYPE "AutomaticPresenceType" /* boolean, 'b' */ diff --git a/src/mcd-account-manager.c b/src/mcd-account-manager.c index e597eb1d..29482255 100644 --- a/src/mcd-account-manager.c +++ b/src/mcd-account-manager.c @@ -399,7 +399,8 @@ toggled_cb (GObject *plugin, const gchar *name, gboolean on, gpointer data) return; } - _mcd_account_set_enabled (account, on, FALSE, &error); + _mcd_account_set_enabled (account, on, FALSE, + MCD_DBUS_PROP_SET_FLAG_NONE, &error); if (error != NULL) { @@ -1225,7 +1226,8 @@ migrate_create_account_cb (McdAccountManager *account_manager, if (error != NULL) { DEBUG ("Failed to create account: %s", error->message); - _mcd_account_set_enabled (ctx->account, FALSE, TRUE, NULL); + _mcd_account_set_enabled (ctx->account, FALSE, TRUE, + MCD_DBUS_PROP_SET_FLAG_NONE, NULL); migrate_ctx_free (ctx); return; } @@ -1253,7 +1255,8 @@ migrate_butterfly_haze_ready (McdManager *manager, if (error != NULL) { DEBUG ("Can't find Haze: %s", error->message); - _mcd_account_set_enabled (ctx->account, FALSE, TRUE, NULL); + _mcd_account_set_enabled (ctx->account, FALSE, TRUE, + MCD_DBUS_PROP_SET_FLAG_NONE, NULL); goto error; } @@ -1262,7 +1265,8 @@ migrate_butterfly_haze_ready (McdManager *manager, "account", G_TYPE_STRING, &v, NULL)) { - _mcd_account_set_enabled (ctx->account, FALSE, TRUE, NULL); + _mcd_account_set_enabled (ctx->account, FALSE, TRUE, + MCD_DBUS_PROP_SET_FLAG_NONE, NULL); goto error; } @@ -1349,7 +1353,8 @@ butterfly_account_loaded (McdAccount *account, if (manager == NULL) { DEBUG ("Can't find Haze"); - _mcd_account_set_enabled (account, FALSE, TRUE, NULL); + _mcd_account_set_enabled (account, FALSE, TRUE, + MCD_DBUS_PROP_SET_FLAG_NONE, NULL); goto error; } diff --git a/src/mcd-account-priv.h b/src/mcd-account-priv.h index a00b398e..e80eb995 100644 --- a/src/mcd-account-priv.h +++ b/src/mcd-account-priv.h @@ -169,6 +169,7 @@ G_GNUC_INTERNAL void _mcd_account_set_changing_presence (McdAccount *self, G_GNUC_INTERNAL gboolean _mcd_account_set_enabled (McdAccount *account, gboolean enabled, gboolean write_out, + McdDBusPropSetFlags flags, GError **error); G_GNUC_INTERNAL gboolean _mcd_account_presence_type_is_settable ( diff --git a/src/mcd-account.c b/src/mcd-account.c index c7b941db..5faf104e 100644 --- a/src/mcd-account.c +++ b/src/mcd-account.c @@ -708,7 +708,8 @@ mcd_account_delete (McdAccount *account, /* got to turn the account off before removing it, otherwise we can * * end up with an orphaned CM holding the account online */ - if (!_mcd_account_set_enabled (account, FALSE, FALSE, &error)) + if (!_mcd_account_set_enabled (account, FALSE, FALSE, + MCD_DBUS_PROP_SET_FLAG_NONE, &error)) { g_warning ("could not disable account %s (%s)", name, error->message); callback (account, error, user_data); @@ -1012,8 +1013,11 @@ typedef enum { * %SET_RESULT_ERROR on error */ static SetResult -mcd_account_set_string_val (McdAccount *account, const gchar *key, - const GValue *value, GError **error) +mcd_account_set_string_val (McdAccount *account, + const gchar *key, + const GValue *value, + McdDBusPropSetFlags flags, + GError **error) { McdAccountPrivate *priv = account->priv; McdStorage *storage = priv->storage; @@ -1034,11 +1038,19 @@ mcd_account_set_string_val (McdAccount *account, const gchar *key, new_string = NULL; } - if (mcd_storage_set_string (storage, name, key, new_string)) { + if (flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) + { + mcd_account_changed_property (account, key, value); + return SET_RESULT_CHANGED; + } + else if (mcd_storage_set_string (storage, name, key, new_string)) + { mcd_storage_commit (storage, name); mcd_account_changed_property (account, key, value); return SET_RESULT_CHANGED; - } else { + } + else + { return SET_RESULT_UNCHANGED; } } @@ -1059,15 +1071,19 @@ mcd_account_get_string_val (McdAccount *account, const gchar *key, } static gboolean -set_display_name (TpSvcDBusProperties *self, const gchar *name, - const GValue *value, GError **error) +set_display_name (TpSvcDBusProperties *self, + const gchar *name, + const GValue *value, + McdDBusPropSetFlags flags, + GError **error) { McdAccount *account = MCD_ACCOUNT (self); McdAccountPrivate *priv = account->priv; DEBUG ("called for %s", priv->unique_name); return (mcd_account_set_string_val (account, - MC_ACCOUNTS_KEY_DISPLAY_NAME, value, error) != SET_RESULT_ERROR); + MC_ACCOUNTS_KEY_DISPLAY_NAME, value, flags, + error) != SET_RESULT_ERROR); } static void @@ -1079,7 +1095,10 @@ get_display_name (TpSvcDBusProperties *self, const gchar *name, GValue *value) } static gboolean -set_icon (TpSvcDBusProperties *self, const gchar *name, const GValue *value, +set_icon (TpSvcDBusProperties *self, + const gchar *name, + const GValue *value, + McdDBusPropSetFlags flags, GError **error) { McdAccount *account = MCD_ACCOUNT (self); @@ -1087,7 +1106,7 @@ set_icon (TpSvcDBusProperties *self, const gchar *name, const GValue *value, DEBUG ("called for %s", priv->unique_name); return (mcd_account_set_string_val (account, - MC_ACCOUNTS_KEY_ICON, value, error) != SET_RESULT_ERROR); + MC_ACCOUNTS_KEY_ICON, value, flags, error) != SET_RESULT_ERROR); } static void @@ -1131,6 +1150,7 @@ gboolean _mcd_account_set_enabled (McdAccount *account, gboolean enabled, gboolean write_out, + McdDBusPropSetFlags flags, GError **error) { McdAccountPrivate *priv = account->priv; @@ -1159,11 +1179,14 @@ _mcd_account_set_enabled (McdAccount *account, g_value_init (&value, G_TYPE_BOOLEAN); g_value_set_boolean (&value, enabled); - mcd_storage_set_attribute (priv->storage, name, - MC_ACCOUNTS_KEY_ENABLED, &value); + if (!(flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE)) + { + mcd_storage_set_attribute (priv->storage, name, + MC_ACCOUNTS_KEY_ENABLED, &value); - if (write_out) - mcd_storage_commit (priv->storage, name); + if (write_out) + mcd_storage_commit (priv->storage, name); + } mcd_account_changed_property (account, "Enabled", &value); @@ -1180,7 +1203,10 @@ _mcd_account_set_enabled (McdAccount *account, } static gboolean -set_enabled (TpSvcDBusProperties *self, const gchar *name, const GValue *value, +set_enabled (TpSvcDBusProperties *self, + const gchar *name, + const GValue *value, + McdDBusPropSetFlags flags, GError **error) { McdAccount *account = MCD_ACCOUNT (self); @@ -1199,7 +1225,7 @@ set_enabled (TpSvcDBusProperties *self, const gchar *name, const GValue *value, enabled = g_value_get_boolean (value); - return _mcd_account_set_enabled (account, enabled, TRUE, error); + return _mcd_account_set_enabled (account, enabled, TRUE, flags, error); } static void @@ -1214,7 +1240,9 @@ get_enabled (TpSvcDBusProperties *self, const gchar *name, GValue *value) static gboolean set_service (TpSvcDBusProperties *self, const gchar *name, - const GValue *value, GError **error) + const GValue *value, + McdDBusPropSetFlags flags, + GError **error) { McdAccount *account = MCD_ACCOUNT (self); SetResult ret = SET_RESULT_ERROR; @@ -1240,7 +1268,7 @@ set_service (TpSvcDBusProperties *self, const gchar *name, if (proceed) { ret = mcd_account_set_string_val (account, MC_ACCOUNTS_KEY_SERVICE, - value, error); + value, flags, error); } else { @@ -1302,7 +1330,9 @@ mcd_account_send_nickname_to_connection (McdAccount *self, static gboolean set_nickname (TpSvcDBusProperties *self, const gchar *name, - const GValue *value, GError **error) + const GValue *value, + McdDBusPropSetFlags flags, + GError **error) { McdAccount *account = MCD_ACCOUNT (self); McdAccountPrivate *priv = account->priv; @@ -1330,7 +1360,7 @@ set_nickname (TpSvcDBusProperties *self, const gchar *name, } ret = mcd_account_set_string_val (account, MC_ACCOUNTS_KEY_NICKNAME, - value, error); + value, flags, error); if (ret != SET_RESULT_ERROR) { @@ -1517,6 +1547,7 @@ mcd_account_send_avatar_to_connection (McdAccount *self, static gboolean set_avatar (TpSvcDBusProperties *self, const gchar *name, const GValue *value, + McdDBusPropSetFlags flags, GError **error) { McdAccount *account = MCD_ACCOUNT (self); @@ -1621,7 +1652,10 @@ _presence_type_is_online (TpConnectionPresenceType type) static gboolean set_automatic_presence (TpSvcDBusProperties *self, - const gchar *name, const GValue *value, GError **error) + const gchar *name, + const GValue *value, + McdDBusPropSetFlags flags, + GError **error) { McdAccount *account = MCD_ACCOUNT (self); McdAccountPrivate *priv = account->priv; @@ -1658,29 +1692,12 @@ set_automatic_presence (TpSvcDBusProperties *self, if (priv->auto_presence_type != type) { - GValue presence = G_VALUE_INIT; - - g_value_init (&presence, G_TYPE_INT); - g_value_set_int (&presence, type); - - mcd_storage_set_attribute (priv->storage, account_name, - MC_ACCOUNTS_KEY_AUTO_PRESENCE_TYPE, - &presence); priv->auto_presence_type = type; changed = TRUE; } if (tp_strdiff (priv->auto_presence_status, status)) { - const gchar *new_status = NULL; - - if (status != NULL && status[0] != 0) - new_status = status; - - mcd_storage_set_string (priv->storage, account_name, - MC_ACCOUNTS_KEY_AUTO_PRESENCE_STATUS, - new_status); - g_free (priv->auto_presence_status); priv->auto_presence_status = g_strdup (status); changed = TRUE; @@ -1688,23 +1705,16 @@ set_automatic_presence (TpSvcDBusProperties *self, if (tp_strdiff (priv->auto_presence_message, message)) { - const gchar *new_message = NULL; - - if (!tp_str_empty (message)) - new_message = message; - - mcd_storage_set_string (priv->storage, account_name, - MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE, - new_message); - g_free (priv->auto_presence_message); priv->auto_presence_message = g_strdup (message); changed = TRUE; } - if (changed) { + mcd_storage_set_attribute (priv->storage, account_name, + MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE, + value); mcd_storage_commit (priv->storage, account_name); mcd_account_changed_property (account, name, value); } @@ -1738,7 +1748,9 @@ get_automatic_presence (TpSvcDBusProperties *self, static gboolean set_connect_automatically (TpSvcDBusProperties *self, - const gchar *name, const GValue *value, + const gchar *name, + const GValue *value, + McdDBusPropSetFlags flags, GError **error) { McdAccount *account = MCD_ACCOUNT (self); @@ -1768,12 +1780,16 @@ set_connect_automatically (TpSvcDBusProperties *self, if (priv->connect_automatically != connect_automatically) { const gchar *account_name = mcd_account_get_unique_name (account); - mcd_storage_set_attribute (priv->storage, account_name, - MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY, - value); + + if (!(flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE)) + { + mcd_storage_set_attribute (priv->storage, account_name, + MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY, + value); + mcd_storage_commit (priv->storage, account_name); + } priv->connect_automatically = connect_automatically; - mcd_storage_commit (priv->storage, account_name); mcd_account_changed_property (account, name, value); if (connect_automatically) @@ -1880,7 +1896,9 @@ get_current_presence (TpSvcDBusProperties *self, const gchar *name, static gboolean set_requested_presence (TpSvcDBusProperties *self, - const gchar *name, const GValue *value, + const gchar *name, + const GValue *value, + McdDBusPropSetFlags flags, GError **error) { McdAccount *account = MCD_ACCOUNT (self); @@ -1973,6 +1991,7 @@ static gboolean set_supersedes (TpSvcDBusProperties *svc, const gchar *name, const GValue *value, + McdDBusPropSetFlags flags, GError **error) { McdAccount *self = MCD_ACCOUNT (svc); @@ -2048,6 +2067,7 @@ static gboolean set_storage_provider (TpSvcDBusProperties *self, const gchar *name, const GValue *value, + McdDBusPropSetFlags flags, GError **error) { McdAccount *account = MCD_ACCOUNT (self); @@ -2192,6 +2212,7 @@ static gboolean set_hidden (TpSvcDBusProperties *self, const gchar *name, const GValue *value, + McdDBusPropSetFlags flags, GError **error) { McdAccount *account = MCD_ACCOUNT (self); @@ -2478,6 +2499,31 @@ mcd_account_altered_by_plugin (McdAccount *account, { guint i = 0; const McdDBusProp *prop = NULL; + GValue value = G_VALUE_INIT; + GError *error = NULL; + + DEBUG ("%s", name); + + if (tp_strdiff (name, "Parameters") && + !mcd_storage_init_value_for_attribute (&value, name)) + { + WARNING ("plugin wants to alter %s but I don't know what " + "type that ought to be", name); + return; + } + + if (!tp_strdiff (name, "Parameters")) + { + get_parameters (TP_SVC_DBUS_PROPERTIES (account), name, &value); + } + else if (!mcd_storage_get_attribute (account->priv->storage, + account->priv->unique_name, + name, &value, &error)) + { + WARNING ("cannot get new value of %s: %s", name, error->message); + g_error_free (error); + return; + } /* find the property update handler */ for (; prop == NULL && account_properties[i].name != NULL; i++) @@ -2490,32 +2536,34 @@ mcd_account_altered_by_plugin (McdAccount *account, * then issue the change notification (DBus signals etc) for it */ if (prop != NULL) { - TpSvcDBusProperties *self = TP_SVC_DBUS_PROPERTIES (account); - - if (prop->getprop != NULL) + /* poke the value back into itself with the setter: this * + * extra round-trip may trigger extra actions like notifying * + * the connection manager of the change, even though our own * + * internal storage already has this value and needn't change */ + if (prop->setprop != NULL) { - GValue value = G_VALUE_INIT; - - prop->getprop (self, name, &value); - - /* poke the value back into itself with the setter: this * - * extra round-trip may trigger extra actions like notifying * - * the connection manager of the change, even though our own * - * internal storage already has this value and needn't change */ - if (prop->setprop != NULL) - prop->setprop (self, prop->name, &value, NULL); - else - mcd_account_changed_property (account, prop->name, &value); - - g_value_unset (&value); + DEBUG ("Calling property setter for %s", name); + if (!prop->setprop (TP_SVC_DBUS_PROPERTIES (account), + prop->name, &value, + MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE, + &error)) + { + WARNING ("Unable to set %s: %s", name, error->message); + g_error_free (error); + } } else { - DEBUG ("Valid DBus property %s with no get method was changed" - " - cannot notify change since we cannot get its value", - name); + DEBUG ("Emitting signal directly for %s", name); + mcd_account_changed_property (account, prop->name, &value); } } + else + { + DEBUG ("%s does not appear to be an Account property", name); + } + + g_value_unset (&value); } } @@ -3261,32 +3309,78 @@ mcd_account_setup (McdAccount *account) priv->always_dispatch = mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_ALWAYS_DISPATCH); - /* load the automatic presence */ - priv->auto_presence_type = - mcd_storage_get_integer (storage, name, - MC_ACCOUNTS_KEY_AUTO_PRESENCE_TYPE); + g_value_init (&value, TP_STRUCT_TYPE_SIMPLE_PRESENCE); + + g_free (priv->auto_presence_status); + g_free (priv->auto_presence_message); - /* If invalid or something, force it to AVAILABLE - we want the auto - * presence type to be an online status */ - if (!_presence_type_is_online (priv->auto_presence_type)) + if (mcd_storage_get_attribute (storage, name, + MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE, &value, + NULL)) { - priv->auto_presence_type = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE; - g_free (priv->auto_presence_status); - priv->auto_presence_status = g_strdup ("available"); + GValueArray *va = g_value_get_boxed (&value); + + priv->auto_presence_type = g_value_get_uint (va->values + 0); + priv->auto_presence_status = g_value_dup_string (va->values + 1); + priv->auto_presence_message = g_value_dup_string (va->values + 2); + + if (priv->auto_presence_status == NULL) + priv->auto_presence_status = g_strdup (""); + if (priv->auto_presence_message == NULL) + priv->auto_presence_message = g_strdup (""); } else { - g_free (priv->auto_presence_status); + /* try the old versions */ + priv->auto_presence_type = + mcd_storage_get_integer (storage, name, + MC_ACCOUNTS_KEY_AUTO_PRESENCE_TYPE); priv->auto_presence_status = mcd_storage_dup_string (storage, name, MC_ACCOUNTS_KEY_AUTO_PRESENCE_STATUS); + priv->auto_presence_message = + mcd_storage_dup_string (storage, name, + MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE); + + if (priv->auto_presence_status == NULL) + priv->auto_presence_status = g_strdup (""); + if (priv->auto_presence_message == NULL) + priv->auto_presence_message = g_strdup (""); + + /* migrate to a more sensible storage format */ + g_value_take_boxed (&value, tp_value_array_build (3, + G_TYPE_UINT, (guint) priv->auto_presence_type, + G_TYPE_STRING, priv->auto_presence_status, + G_TYPE_STRING, priv->auto_presence_message, + G_TYPE_INVALID)); + + if (mcd_storage_set_attribute (storage, name, + MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE, + &value)) + { + mcd_storage_set_attribute (storage, name, + MC_ACCOUNTS_KEY_AUTO_PRESENCE_TYPE, + NULL); + mcd_storage_set_attribute (storage, name, + MC_ACCOUNTS_KEY_AUTO_PRESENCE_STATUS, + NULL); + mcd_storage_set_attribute (storage, name, + MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE, + NULL); + mcd_storage_commit (storage, name); + } } - g_free (priv->auto_presence_message); - priv->auto_presence_message = - mcd_storage_dup_string (storage, name, - MC_ACCOUNTS_KEY_AUTO_PRESENCE_MESSAGE); + /* If invalid or something, force it to AVAILABLE - we want the auto + * presence type to be an online status */ + if (!_presence_type_is_online (priv->auto_presence_type)) + { + priv->auto_presence_type = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE; + g_free (priv->auto_presence_status); + priv->auto_presence_status = g_strdup ("available"); + } + g_value_unset (&value); g_value_init (&value, TP_ARRAY_TYPE_OBJECT_PATH_LIST); if (priv->supersedes != NULL) @@ -4193,7 +4287,8 @@ mcd_account_self_contact_notify_alias_cb (McdAccount *self, g_value_init (&value, G_TYPE_STRING); g_object_get_property (G_OBJECT (self_contact), "alias", &value); - mcd_account_set_string_val (self, MC_ACCOUNTS_KEY_NICKNAME, &value, NULL); + mcd_account_set_string_val (self, MC_ACCOUNTS_KEY_NICKNAME, &value, + MCD_DBUS_PROP_SET_FLAG_NONE, NULL); g_value_unset (&value); } diff --git a/src/mcd-dbusprop.c b/src/mcd-dbusprop.c index f15bd85b..f0d64e58 100644 --- a/src/mcd-dbusprop.c +++ b/src/mcd-dbusprop.c @@ -181,7 +181,8 @@ mcd_dbusprop_set_property (TpSvcDBusProperties *self, /* we pass property->name, because we know it's a static value and there * will be no need to care about its lifetime */ - return property->setprop (self, property->name, value, error); + return property->setprop (self, property->name, value, + MCD_DBUS_PROP_SET_FLAG_NONE, error); } void diff --git a/src/mcd-dbusprop.h b/src/mcd-dbusprop.h index fa5091c2..509541a6 100644 --- a/src/mcd-dbusprop.h +++ b/src/mcd-dbusprop.h @@ -32,8 +32,16 @@ G_BEGIN_DECLS -typedef gboolean (*mcd_setprop) (TpSvcDBusProperties *self, const gchar *name, - const GValue *value, GError **error); +typedef enum { + MCD_DBUS_PROP_SET_FLAG_NONE = 0, + MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE = 1 +} McdDBusPropSetFlags; + +typedef gboolean (*mcd_setprop) (TpSvcDBusProperties *self, + const gchar *name, + const GValue *value, + McdDBusPropSetFlags flags, + GError **error); typedef void (*mcd_getprop) (TpSvcDBusProperties *self, const gchar *name, GValue *value); diff --git a/src/mcd-debug.h b/src/mcd-debug.h index f9bd0227..f91e69d1 100644 --- a/src/mcd-debug.h +++ b/src/mcd-debug.h @@ -39,6 +39,8 @@ G_BEGIN_DECLS #define DEBUGGING (_mcd_debug_get_level () > 0) #define DEBUG(format, ...) \ mcd_debug ("%s: " format, G_STRFUNC, ##__VA_ARGS__) +#define WARNING(format, ...) \ + g_warning ("%s: " format, G_STRFUNC, ##__VA_ARGS__) #else /* !defined ENABLE_DEBUG */ diff --git a/src/mcd-storage.c b/src/mcd-storage.c index 5ca194c7..967228a2 100644 --- a/src/mcd-storage.c +++ b/src/mcd-storage.c @@ -29,6 +29,7 @@ #include "mcd-misc.h" #include "plugin-loader.h" +#include <errno.h> #include <string.h> #include <telepathy-glib/telepathy-glib.h> @@ -299,6 +300,9 @@ static struct { } known_attributes[] = { /* Please keep this sorted by type, then by name. */ + /* Structs */ + { "(uss)", MC_ACCOUNTS_KEY_AUTOMATIC_PRESENCE }, + /* Array of object path */ { "ao", MC_ACCOUNTS_KEY_SUPERSEDES }, @@ -331,7 +335,7 @@ static struct { { NULL, NULL } }; -static const gchar * +const gchar * mcd_storage_get_attribute_type (const gchar *attribute) { guint i; @@ -349,7 +353,7 @@ mcd_storage_get_attribute_type (const gchar *attribute) return NULL; } -static gboolean +gboolean mcd_storage_init_value_for_attribute (GValue *value, const gchar *attribute) { @@ -387,6 +391,16 @@ mcd_storage_init_value_for_attribute (GValue *value, } } break; + + case '(': + { + if (!tp_strdiff (s, "(uss)")) + { + g_value_init (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE); + return TRUE; + } + } + break; } return FALSE; @@ -401,6 +415,51 @@ mcpa_init_value_for_attribute (const McpAccountManager *mcpa, } static void +mcpa_set_attribute (const McpAccountManager *ma, + const gchar *account, + const gchar *attribute, + GVariant *value, + McpAttributeFlags flags) +{ + McdStorage *self = MCD_STORAGE (ma); + McdStorageAccount *sa = ensure_account (self, account); + + if (value != NULL) + { + g_hash_table_insert (sa->attributes, g_strdup (attribute), + g_variant_ref_sink (value)); + } + else + { + g_hash_table_remove (sa->attributes, attribute); + } +} + +static void +mcpa_set_parameter (const McpAccountManager *ma, + const gchar *account, + const gchar *parameter, + GVariant *value, + McpParameterFlags flags) +{ + McdStorage *self = MCD_STORAGE (ma); + McdStorageAccount *sa = ensure_account (self, account); + + g_hash_table_remove (sa->parameters, parameter); + g_hash_table_remove (sa->escaped_parameters, parameter); + + if (value != NULL) + g_hash_table_insert (sa->parameters, g_strdup (parameter), + g_variant_ref_sink (value)); + + if (flags & MCP_PARAMETER_FLAG_SECRET) + { + DEBUG ("flagging %s parameter %s as secret", account, parameter); + g_hash_table_add (sa->secrets, g_strdup (parameter)); + } +} + +static void set_value (const McpAccountManager *ma, const gchar *account, const gchar *key, @@ -1239,6 +1298,48 @@ mcd_keyfile_get_value (GKeyFile *keyfile, ret = TRUE; } } + else if (type == TP_STRUCT_TYPE_SIMPLE_PRESENCE) + { + gchar **v = g_key_file_get_string_list (keyfile, group, + key, NULL, error); + + if (v == NULL) + { + /* error is already set, do nothing */ + } + else if (g_strv_length (v) != 3) + { + g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "Invalid simple-presence structure stored in keyfile"); + } + else + { + guint64 u; + gchar *endptr; + + errno = 0; + u = g_ascii_strtoull (v[0], &endptr, 10); + + if (errno != 0 || *endptr != '\0' || u > G_MAXUINT32) + { + g_set_error (error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "Invalid presence type stored in keyfile: %s", v[0]); + } + else + { + /* a syntactically valid simple presence */ + g_value_take_boxed (value, + tp_value_array_build (3, + G_TYPE_UINT, (guint) u, + G_TYPE_STRING, v[1], + G_TYPE_STRING, v[2], + G_TYPE_INVALID)); + ret = TRUE; + } + } + + g_strfreev (v); + } else { gchar *message = @@ -1695,6 +1796,22 @@ mcd_keyfile_set_value (GKeyFile *keyfile, g_key_file_set_string_list (keyfile, name, key, (const gchar * const *) arr->pdata, arr->len); } + else if (G_VALUE_HOLDS (value, TP_STRUCT_TYPE_SIMPLE_PRESENCE)) + { + guint type; + /* enough for "4294967296" + \0 */ + gchar printf_buf[11]; + const gchar * strv[4] = { NULL, NULL, NULL, NULL }; + + tp_value_array_unpack (g_value_get_boxed (value), 3, + &type, + &(strv[1]), + &(strv[2])); + g_snprintf (printf_buf, sizeof (printf_buf), "%u", type); + strv[0] = printf_buf; + + g_key_file_set_string_list (keyfile, name, key, strv, 3); + } else { g_warning ("Unexpected param type %s", @@ -1956,6 +2073,8 @@ plugin_iface_init (McpAccountManagerIface *iface, iface->get_value = get_value; iface->set_value = set_value; + iface->set_attribute = mcpa_set_attribute; + iface->set_parameter = mcpa_set_parameter; iface->is_secret = is_secret; iface->make_secret = make_secret; iface->unique_name = unique_name; diff --git a/src/mcd-storage.h b/src/mcd-storage.h index a856b290..893a0af3 100644 --- a/src/mcd-storage.h +++ b/src/mcd-storage.h @@ -150,6 +150,10 @@ gboolean mcd_keyfile_unescape_value (const gchar *escaped, GValue *value, GError **error); +const gchar *mcd_storage_get_attribute_type (const gchar *attribute); +gboolean mcd_storage_init_value_for_attribute (GValue *value, + const gchar *attribute); + G_END_DECLS #endif /* MCD_STORAGE_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 65e5ebc4..c02550f8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,7 +16,11 @@ endif SUBDIRS = . twisted -TEST_EXECUTABLES = test-value-is-same +TEST_EXECUTABLES = \ + test-keyfile \ + test-value-is-same \ + $(NULL) + NON_TEST_EXECUTABLES = account-store tease-the-minotaur if ENABLE_GNOME_KEYRING @@ -32,6 +36,9 @@ TESTS = $(TEST_EXECUTABLES) test_value_is_same_SOURCES = value-is-same.c test_value_is_same_LDADD = $(top_builddir)/src/libmcd-convenience.la +test_keyfile_SOURCES = keyfile.c +test_keyfile_LDADD = $(top_builddir)/src/libmcd-convenience.la + tease_the_minotaur_SOURCES = tease-the-minotaur.c tease_the_minotaur_LDADD = $(top_builddir)/src/libmcd-convenience.la diff --git a/tests/keyfile.c b/tests/keyfile.c new file mode 100644 index 00000000..df891ce5 --- /dev/null +++ b/tests/keyfile.c @@ -0,0 +1,351 @@ +/* + * Regression test for keyfile (un)escaping + * + * 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 <telepathy-glib/telepathy-glib.h> +#include <telepathy-glib/telepathy-glib-dbus.h> + +#include "mcd-storage.h" + +typedef enum { + FAILS = 1, + NOT_NORMALIZED = 2 +} Flags; + +#define SIMPLE_TEST(NAME, GTYPE, CMP, GET) \ +static void \ +test_ ## NAME (void) \ +{ \ + guint i; \ +\ + for (i = 0; NAME ## _tests[i].escaped != NULL; i++) \ + { \ + gboolean success; \ + gchar *escaped; \ + GValue unescaped = G_VALUE_INIT; \ + GError *error = NULL; \ +\ + g_value_init (&unescaped, GTYPE); \ +\ + success = mcd_keyfile_unescape_value (NAME ## _tests[i].escaped, \ + &unescaped, &error); \ +\ + if (NAME ## _tests[i].flags & FAILS) \ + { \ + if (success || error == NULL) \ + g_error ("Interpreting '%s' as %s was meant to fail", \ + NAME ## _tests[i].escaped, g_type_name (GTYPE)); \ +\ + g_error_free (error); \ + } \ + else \ + { \ + if (error != NULL) \ + g_error ("Interpreting '%s' as %s was meant to succeed: %s", \ + NAME ## _tests[i].escaped, g_type_name (GTYPE), error->message); \ +\ + if (!success) \ + g_error ("Interpreting '%s' as %s was meant to succeed", \ + NAME ## _tests[i].escaped, g_type_name (GTYPE)); \ +\ + g_assert (success); \ + CMP (GET (&unescaped), ==, \ + NAME ## _tests[i].unescaped); \ +\ + escaped = mcd_keyfile_escape_value (&unescaped); \ +\ + if (NAME ## _tests[i].flags & NOT_NORMALIZED) \ + g_assert (escaped != NULL); \ + else \ + g_assert_cmpstr (escaped, ==, NAME ## _tests[i].escaped); \ +\ + g_free (escaped); \ + } \ +\ + g_value_unset (&unescaped); \ + } \ +} + +struct { + const gchar *escaped; + gint32 unescaped; + Flags flags; +} int32_tests[] = { + { "-2147483649", 0, FAILS }, + { "-2147483648", G_MININT32, 0 }, + { "-2147483647", -2147483647, 0 }, + { "-1", -1, 0 }, + { "x", 0, FAILS }, + { "0", 0, 0 }, + { "000", 0, NOT_NORMALIZED }, + { "1", 1, 0 }, + { "001", 1, NOT_NORMALIZED }, + { "042", 42, NOT_NORMALIZED }, + { "2147483647", 2147483647, 0 }, + { "2147483648", 2147483648, FAILS }, + { NULL, 0, 0 } +}; + +struct { + const gchar *escaped; + guint32 unescaped; + Flags flags; +} uint32_tests[] = { + { "-1", 0, FAILS }, + { "x", 0, FAILS }, + { "0", 0, 0 }, + { "000", 0, NOT_NORMALIZED }, + { "1", 1, 0 }, + { "001", 1, NOT_NORMALIZED }, + { "042", 42, NOT_NORMALIZED }, + { "2147483647", 2147483647, 0 }, + { "2147483648", 2147483648u, 0 }, + { "4294967295", 4294967295u, 0 }, + { "4294967296", 0, FAILS }, + { NULL, 0, 0 } +}; + +struct { + const gchar *escaped; + gint64 unescaped; + Flags flags; +} int64_tests[] = { +#if 0 + /* This actually "succeeds". tp_g_key_file_get_int64() and _uint64(), + * and the copy of them that was merged in GLib, don't detect overflow */ + { "-9223372036854775809", 0, FAILS }, +#endif + { "-9223372036854775808", G_MININT64, 0 }, + { "-1", -1, 0 }, + { "0", 0, 0 }, + { "1", 1, 0 }, + { "9223372036854775807", G_GINT64_CONSTANT (9223372036854775807), 0 }, +#if 0 + { "9223372036854775808", 0, FAILS }, +#endif + { "x", 0, FAILS }, + { NULL, 0, 0 } +}; + +struct { + const gchar *escaped; + guint64 unescaped; + Flags flags; +} uint64_tests[] = { +#if 0 + { "-1", 0, FAILS }, +#endif + { "0", 0, 0 }, + { "1", 1, 0 }, + { "9223372036854775807", G_GUINT64_CONSTANT (9223372036854775807), 0 }, + { "9223372036854775808", G_GUINT64_CONSTANT (9223372036854775808), 0 }, + { "18446744073709551615", G_GUINT64_CONSTANT (18446744073709551615), 0 }, +#if 0 + { "18446744073709551616", 0, FAILS }, +#endif + { "x", 0, FAILS }, + { NULL, 0, 0 } +}; + +struct { + const gchar *escaped; + guchar unescaped; + Flags flags; +} byte_tests[] = { + { "-1", 0, FAILS }, + { "x", 0, FAILS }, + { "0", 0, 0 }, + { "1", 1, 0 }, + { "255", 255, 0 }, + { "256", 0, FAILS }, + { NULL, 0, 0 } +}; + +struct { + const gchar *escaped; + gboolean unescaped; + Flags flags; +} boolean_tests[] = { + { "true", TRUE, 0 }, + { "false", FALSE, 0 }, + { "0", FALSE, NOT_NORMALIZED }, + { "1", TRUE, NOT_NORMALIZED }, + { "2", 0, FAILS }, + { "", 0, FAILS }, + { NULL, 0, 0 } +}; + +struct { + const gchar *escaped; + const gchar *unescaped; + Flags flags; +} string_tests[] = { + { "lol", "lol", 0 }, + { "\\s", " ", 0 }, + { "\\s ", " ", NOT_NORMALIZED }, + { "\\t", "\t", 0 }, + { NULL, NULL, 0 } +}; + +struct { + const gchar *escaped; + const gchar *unescaped; + Flags flags; +} path_tests[] = { + { "/", "/", 0 }, + { "/foo", "/foo", 0 }, + { "x", NULL, FAILS }, + { NULL, NULL, 0 } +}; + +struct { + const gchar *escaped; + double unescaped; + Flags flags; +} double_tests[] = { + { "0", 0.0, 0 }, + { "0.5", 0.5, 0 }, + { "x", 0.0, FAILS }, + { NULL, 0.0, 0 } +}; + +SIMPLE_TEST (int32, G_TYPE_INT, g_assert_cmpint, g_value_get_int) +SIMPLE_TEST (uint32, G_TYPE_UINT, g_assert_cmpuint, g_value_get_uint) +SIMPLE_TEST (int64, G_TYPE_INT64, g_assert_cmpint, g_value_get_int64) +SIMPLE_TEST (uint64, G_TYPE_UINT64, g_assert_cmpuint, g_value_get_uint64) +SIMPLE_TEST (byte, G_TYPE_UCHAR, g_assert_cmpuint, g_value_get_uchar) +SIMPLE_TEST (boolean, G_TYPE_BOOLEAN, g_assert_cmpuint, g_value_get_boolean) +SIMPLE_TEST (string, G_TYPE_STRING, g_assert_cmpstr, g_value_get_string) +SIMPLE_TEST (path, DBUS_TYPE_G_OBJECT_PATH, g_assert_cmpstr, g_value_get_boxed) +SIMPLE_TEST (double, G_TYPE_DOUBLE, g_assert_cmpfloat, g_value_get_double) + +static void +test_strv (void) +{ + gboolean success; + gchar *escaped; + GValue unescaped = G_VALUE_INIT; + GError *error = NULL; + gchar **unescaped_strv; + + g_value_init (&unescaped, G_TYPE_STRV); + + success = mcd_keyfile_unescape_value ("x;\\t;z;", &unescaped, &error); + + g_assert_no_error (error); + g_assert (success); + unescaped_strv = g_value_get_boxed (&unescaped); + g_assert_cmpstr (unescaped_strv[0], ==, "x"); + g_assert_cmpstr (unescaped_strv[1], ==, "\t"); + g_assert_cmpstr (unescaped_strv[2], ==, "z"); + g_assert_cmpstr (unescaped_strv[3], ==, NULL); + + escaped = mcd_keyfile_escape_value (&unescaped); + g_assert_cmpstr (escaped, ==, "x;\\t;z;"); + g_free (escaped); + + g_value_unset (&unescaped); +} + +static void +test_ao (void) +{ + gboolean success; + gchar *escaped; + GValue unescaped = G_VALUE_INIT; + GError *error = NULL; + GPtrArray *unescaped_pa; + + g_value_init (&unescaped, TP_ARRAY_TYPE_OBJECT_PATH_LIST); + + success = mcd_keyfile_unescape_value ("/x;/;", &unescaped, &error); + + g_assert_no_error (error); + g_assert (success); + unescaped_pa = g_value_get_boxed (&unescaped); + g_assert_cmpuint (unescaped_pa->len, ==, 2); + g_assert_cmpstr (g_ptr_array_index (unescaped_pa, 0), ==, "/x"); + g_assert_cmpstr (g_ptr_array_index (unescaped_pa, 1), ==, "/"); + + escaped = mcd_keyfile_escape_value (&unescaped); + g_assert_cmpstr (escaped, ==, "/x;/;"); + g_free (escaped); + + g_value_unset (&unescaped); +} + +static void +test_uss (void) +{ + gboolean success; + gchar *escaped; + GValue unescaped = G_VALUE_INIT; + GError *error = NULL; + GValueArray *unescaped_va; + + g_value_init (&unescaped, TP_STRUCT_TYPE_SIMPLE_PRESENCE); + + success = mcd_keyfile_unescape_value ("2;available;\\;;", + &unescaped, &error); + + g_assert_no_error (error); + g_assert (success); + unescaped_va = g_value_get_boxed (&unescaped); + g_assert_cmpuint (g_value_get_uint (unescaped_va->values + 0), ==, 2); + g_assert_cmpstr (g_value_get_string (unescaped_va->values + 1), ==, + "available"); + g_assert_cmpstr (g_value_get_string (unescaped_va->values + 2), ==, + ";"); + + escaped = mcd_keyfile_escape_value (&unescaped); + g_assert_cmpstr (escaped, ==, "2;available;\\;;"); + g_free (escaped); + + g_value_unset (&unescaped); +} + +int +main (int argc, + char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugs.freedesktop.org/show_bug.cgi?id="); + + g_type_init (); + + g_test_add_func ("/keyfile/int32", test_int32); + g_test_add_func ("/keyfile/uint32", test_uint32); + g_test_add_func ("/keyfile/int64", test_int64); + g_test_add_func ("/keyfile/uint64", test_uint64); + g_test_add_func ("/keyfile/string", test_string); + g_test_add_func ("/keyfile/byte", test_byte); + g_test_add_func ("/keyfile/boolean", test_boolean); + g_test_add_func ("/keyfile/double", test_double); + g_test_add_func ("/keyfile/path", test_path); + + g_test_add_func ("/keyfile/strv", test_strv); + g_test_add_func ("/keyfile/ao", test_ao); + g_test_add_func ("/keyfile/uss", test_uss); + + return g_test_run (); +} diff --git a/tests/twisted/account-manager/auto-connect.py b/tests/twisted/account-manager/auto-connect.py index d6269835..a623902c 100644 --- a/tests/twisted/account-manager/auto-connect.py +++ b/tests/twisted/account-manager/auto-connect.py @@ -51,6 +51,7 @@ def preseed(q, bus, fake_accounts_service): 'NormalizedName': 'jc.denton@unatco.int', 'Enabled': True, 'ConnectAutomatically': True, + # These are in the old format. They'll be combined shortly. 'AutomaticPresenceType': dbus.UInt32(2), 'AutomaticPresenceStatus': 'available', 'AutomaticPresenceMessage': 'My vision is augmented', @@ -85,7 +86,24 @@ def test(q, bus, unused, **kwargs): 'password': r'\\ionstorm\\', } - mc = MC(q, bus) + mc = MC(q, bus, wait_for_names=False) + mc.wait_for_names( + # Migration step: the three separate attributes get combined + # (before the names are taken, so we need to expect it here) + EventPattern('dbus-method-call', + interface=cs.TEST_DBUS_ACCOUNT_SERVICE_IFACE, + method='UpdateAttributes', + predicate=(lambda e: + e.args[0] == account_id and + e.args[1] == {'AutomaticPresence': + (2, 'available', 'My vision is augmented')} and + e.args[2] == {'AutomaticPresence': 0} and # flags + set(e.args[3]) == set([ # no particular order + 'AutomaticPresenceType', + 'AutomaticPresenceStatus', + 'AutomaticPresenceMessage', + ]))) + ) request_conn, prop_changed = q.expect_many( EventPattern('dbus-method-call', method='RequestConnection', diff --git a/tests/twisted/account-manager/avatar-persist.py b/tests/twisted/account-manager/avatar-persist.py index f9c28c76..7dedb188 100644 --- a/tests/twisted/account-manager/avatar-persist.py +++ b/tests/twisted/account-manager/avatar-persist.py @@ -52,9 +52,8 @@ def preseed(q, bus, fake_accounts_service): 'NormalizedName': 'jc.denton@unatco.int', 'Enabled': True, 'ConnectAutomatically': True, - 'AutomaticPresenceType': dbus.UInt32(2), - 'AutomaticPresenceStatus': 'available', - 'AutomaticPresenceMessage': 'My vision is augmented', + 'AutomaticPresence': (dbus.UInt32(2), 'available', + 'My vision is augmented'), 'Nickname': 'JC', 'AvatarMime': 'image/jpeg', 'avatar_token': 'Deus Ex', diff --git a/tests/twisted/account-manager/avatar-refresh.py b/tests/twisted/account-manager/avatar-refresh.py index a6cb9e18..817a623e 100644 --- a/tests/twisted/account-manager/avatar-refresh.py +++ b/tests/twisted/account-manager/avatar-refresh.py @@ -52,9 +52,8 @@ def preseed(q, bus, fake_accounts_service): 'NormalizedName': 'jc.denton@unatco.int', 'Enabled': True, 'ConnectAutomatically': True, - 'AutomaticPresenceType': dbus.UInt32(2), - 'AutomaticPresenceStatus': 'available', - 'AutomaticPresenceMessage': 'My vision is augmented', + 'AutomaticPresence': (dbus.UInt32(2), 'available', + 'My vision is augmented'), 'Nickname': 'JC', 'AvatarMime': 'image/jpeg', 'avatar_token': 'Deus Ex', diff --git a/tests/twisted/account-manager/backend-makes-changes.py b/tests/twisted/account-manager/backend-makes-changes.py index d895dfb6..89eb4c1d 100644 --- a/tests/twisted/account-manager/backend-makes-changes.py +++ b/tests/twisted/account-manager/backend-makes-changes.py @@ -132,9 +132,8 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): args=[account_path, cs.ACCOUNT_IFACE_ADDRESSING + '.URISchemes']), ) - # FIXME: doesn't work - #assertEquals(['xmpp'], - # account.Properties.Get(cs.ACCOUNT_IFACE_ADDRESSING, 'URISchemes')) + assertEquals(['xmpp'], + account.Properties.Get(cs.ACCOUNT_IFACE_ADDRESSING, 'URISchemes')) fake_accounts_service.update_attributes(account_tail, {'ConnectAutomatically': True}) @@ -145,20 +144,19 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): args=[account_tail, {'ConnectAutomatically': True}, {'ConnectAutomatically': 0}, []]), - # FIXME: signal not actually emitted - #EventPattern('dbus-signal', - # path=account_path, - # signal='AccountPropertyChanged', - # interface=cs.ACCOUNT, - # args=[{'ConnectAutomatically': True}]), + EventPattern('dbus-signal', + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + predicate=(lambda e: + e.args[0].get('ConnectAutomatically') == True)), EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, signal='AttributeChanged', args=[account_path, 'ConnectAutomatically']), ) - # FIXME: doesn't work - #assertEquals(True, - # account.Properties.Get(cs.ACCOUNT, 'ConnectAutomatically')) + assertEquals(True, + account.Properties.Get(cs.ACCOUNT, 'ConnectAutomatically')) fake_accounts_service.update_attributes(account_tail, {'Supersedes': [cs.ACCOUNT_PATH_PREFIX + 'ac1/game/altair']}) @@ -174,63 +172,45 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): path=account_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, - # FIXME: signal is emitted, but doesn't have the new value - #args=[{'Supersedes': - # [cs.ACCOUNT_PATH_PREFIX + 'ac1/game/altair']}], - predicate=(lambda e: 'Supersedes' in e.args[0]), + args=[{'Supersedes': + [cs.ACCOUNT_PATH_PREFIX + 'ac1/game/altair']}], ), EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, signal='AttributeChanged', args=[account_path, 'Supersedes']), ) - # FIXME: doesn't work - #assertEquals([cs.ACCOUNT_PATH_PREFIX + 'ac1/game/altair'], - # account.Properties.Get(cs.ACCOUNT, 'Supersedes')) + assertEquals([cs.ACCOUNT_PATH_PREFIX + 'ac1/game/altair'], + account.Properties.Get(cs.ACCOUNT, 'Supersedes')) fake_accounts_service.update_attributes(account_tail, - {'AutomaticPresenceType': cs.PRESENCE_TYPE_HIDDEN, - 'AutomaticPresenceStatus': 'hidden', - 'AutomaticPresenceMessage': 'in a haystack or something'}) + {'AutomaticPresence': (dbus.UInt32(cs.PRESENCE_TYPE_HIDDEN), 'hidden', + 'in a haystack or something')}) q.expect_many( EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, signal='AttributesChanged', args=[account_tail, - {'AutomaticPresenceType': cs.PRESENCE_TYPE_HIDDEN, - 'AutomaticPresenceStatus': 'hidden', - 'AutomaticPresenceMessage': - 'in a haystack or something'}, - {'AutomaticPresenceType': 0, - 'AutomaticPresenceStatus': 0, - 'AutomaticPresenceMessage': 0}, + {'AutomaticPresence': (cs.PRESENCE_TYPE_HIDDEN, + 'hidden', + 'in a haystack or something')}, + {'AutomaticPresence': 0}, []]), - # FIXME: signal not actually emitted: the three parts don't get - # combined correctly - #EventPattern('dbus-signal', - # path=account_path, - # signal='AccountPropertyChanged', - # interface=cs.ACCOUNT, - # args=[{'AutomaticPresence': - # (cs.PRESENCE_TYPE_HIDDEN, 'hidden', - # 'in a haystack or something')}]), EventPattern('dbus-signal', - path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, - signal='AttributeChanged', - args=[account_path, 'AutomaticPresenceType']), - EventPattern('dbus-signal', - path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, - signal='AttributeChanged', - args=[account_path, 'AutomaticPresenceStatus']), + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'AutomaticPresence': + (cs.PRESENCE_TYPE_HIDDEN, 'hidden', + 'in a haystack or something')}]), EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, signal='AttributeChanged', - args=[account_path, 'AutomaticPresenceMessage']), + args=[account_path, 'AutomaticPresence']), ) - # FIXME: doesn't work - #assertEquals((cs.PRESENCE_TYPE_HIDDEN, 'hidden', - # 'in a haystack or something'), - # account.Properties.Get(cs.ACCOUNT, 'AutomaticPresence')) + assertEquals((cs.PRESENCE_TYPE_HIDDEN, 'hidden', + 'in a haystack or something'), + account.Properties.Get(cs.ACCOUNT, 'AutomaticPresence')) fake_accounts_service.update_attributes(account_tail, { 'DisplayName': 'Ezio\'s IM account'}) @@ -240,13 +220,11 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): signal='AttributesChanged', args=[account_tail, {'DisplayName': 'Ezio\'s IM account'}, {'DisplayName': 0}, []]), - # FIXME: signal not actually emitted. Service, Icon, Nickname - # probably have the same bug. - #EventPattern('dbus-signal', - # path=account_path, - # signal='AccountPropertyChanged', - # interface=cs.ACCOUNT, - # args=[{'DisplayName': 'Ezio\'s IM account'}]), + EventPattern('dbus-signal', + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'DisplayName': 'Ezio\'s IM account'}]), EventPattern('dbus-signal', path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, signal='AttributeChanged', @@ -255,6 +233,48 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): assertEquals("Ezio's IM account", account.Properties.Get(cs.ACCOUNT, 'DisplayName')) + fake_accounts_service.update_attributes(account_tail, { + 'Icon': 'im-machiavelli'}) + q.expect_many( + EventPattern('dbus-signal', + path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, + signal='AttributesChanged', + args=[account_tail, {'Icon': 'im-machiavelli'}, + {'Icon': 0}, []]), + EventPattern('dbus-signal', + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'Icon': 'im-machiavelli'}]), + EventPattern('dbus-signal', + path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, + signal='AttributeChanged', + args=[account_path, 'Icon']), + ) + assertEquals('im-machiavelli', + account.Properties.Get(cs.ACCOUNT, 'Icon')) + + fake_accounts_service.update_attributes(account_tail, { + 'Service': 'machiavelli-talk'}) + q.expect_many( + EventPattern('dbus-signal', + path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, + signal='AttributesChanged', + args=[account_tail, {'Service': 'machiavelli-talk'}, + {'Service': 0}, []]), + EventPattern('dbus-signal', + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'Service': 'machiavelli-talk'}]), + EventPattern('dbus-signal', + path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, + signal='AttributeChanged', + args=[account_path, 'Service']), + ) + assertEquals('machiavelli-talk', + account.Properties.Get(cs.ACCOUNT, 'Service')) + fake_accounts_service.update_parameters(account_tail, { 'password': 'high profile'}, flags={'password': cs.PARAM_FLAG_SECRET}) q.expect_many( diff --git a/tests/twisted/account-storage/default-keyring-storage.py b/tests/twisted/account-storage/default-keyring-storage.py index 903df862..f41bc170 100644 --- a/tests/twisted/account-storage/default-keyring-storage.py +++ b/tests/twisted/account-storage/default-keyring-storage.py @@ -242,6 +242,7 @@ protocol=fakeprotocol param-account=dontdivert@example.com param-password=password_in_keyfile DisplayName=New and improved account +AutomaticPresence=2;available;; """ % group) account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) @@ -292,6 +293,7 @@ manager=fakecm protocol=fakeprotocol param-account=dontdivert@example.com DisplayName=Ye olde account +AutomaticPresence=2;available;; """ % group) account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) diff --git a/tests/twisted/dbus-account-plugin.c b/tests/twisted/dbus-account-plugin.c index c7690fbc..16b1c9c0 100644 --- a/tests/twisted/dbus-account-plugin.c +++ b/tests/twisted/dbus-account-plugin.c @@ -428,19 +428,21 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, (stored == NULL || !g_variant_equal (value, stored))) { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile ( - self->feedback, value); + gchar *repr = g_variant_print (value, TRUE); + guint32 flags; - DEBUG ("%s changed to (escaped) %s, signalling MC", - attr, escaped); + DEBUG ("%s changed to %s, signalling MC", attr, repr); + g_free (repr); + + if (!g_variant_lookup (attr_flags, attr, "u", &flags)) + flags = 0; g_hash_table_insert (account->attributes, g_strdup (attr), g_variant_ref (value)); - mcp_account_manager_set_value (self->feedback, - account_name, attr, escaped); + mcp_account_manager_set_attribute (self->feedback, + account_name, attr, value, flags); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, attr); - g_free (escaped); g_dbus_connection_emit_signal (self->bus, NULL, TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, @@ -462,8 +464,8 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, DEBUG ("%s deleted", attr); g_hash_table_remove (account->attributes, attr); - mcp_account_manager_set_value (self->feedback, - account_name, attr, NULL); + mcp_account_manager_set_attribute (self->feedback, + account_name, attr, NULL, 0); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, attr); @@ -538,17 +540,19 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, (stored == NULL || !g_variant_equal (value, stored))) { + guint32 flags; + + if (!g_variant_lookup (param_flags, param, "u", &flags)) + flags = 0; + g_hash_table_insert (account->parameters, g_strdup (param), g_variant_ref (value)); - escaped = mcp_account_manager_escape_variant_for_keyfile ( - self->feedback, value); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_value (self->feedback, - account_name, key, escaped); + mcp_account_manager_set_parameter (self->feedback, + account_name, param, value, flags); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); - g_free (escaped); g_dbus_connection_emit_signal (self->bus, NULL, TEST_DBUS_ACCOUNT_PLUGIN_PATH, @@ -607,8 +611,8 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, g_hash_table_remove (account->untyped_parameters, param); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_value (self->feedback, - account_name, key, NULL); + mcp_account_manager_set_parameter (self->feedback, + account_name, param, NULL, 0); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); diff --git a/tests/twisted/dispatcher/create-at-startup.py b/tests/twisted/dispatcher/create-at-startup.py index c3c2a173..e0d73722 100644 --- a/tests/twisted/dispatcher/create-at-startup.py +++ b/tests/twisted/dispatcher/create-at-startup.py @@ -49,9 +49,7 @@ def preseed(q, bus, fake_accounts_service): 'NormalizedName': 'jc.denton@unatco.int', 'Enabled': True, 'ConnectAutomatically': False, - 'AutomaticPresenceType': dbus.UInt32(2), - 'AutomaticPresenceStatus': 'available', - 'AutomaticPresenceMessage': '', + 'AutomaticPresence': (dbus.UInt32(2), 'available', ''), }) fake_accounts_service.update_parameters(account_id, untyped={ 'account': 'jc.denton@unatco.int', diff --git a/util/mc-tool.c b/util/mc-tool.c index 1bd4c067..59a1a919 100644 --- a/util/mc-tool.c +++ b/util/mc-tool.c @@ -425,6 +425,7 @@ typedef enum { GET_PARAM, GET_STRING, GET_BOOLEAN, + GET_PRESENCE, GET_PRESENCE_TYPE, GET_PRESENCE_STATUS, GET_PRESENCE_MESSAGE @@ -474,6 +475,8 @@ getter_list_init(void) tp_account_get_connect_automatically); getter_list_add("NormalizedName", GET_STRING, tp_account_get_normalized_name); + getter_list_add("AutomaticPresence", + GET_PRESENCE, tp_account_get_automatic_presence); getter_list_add("AutomaticPresenceType", GET_PRESENCE_TYPE, tp_account_get_automatic_presence); getter_list_add("AutomaticPresenceStatus", @@ -894,6 +897,16 @@ command_get (TpAccount *account) else if (getter->type == GET_BOOLEAN) { puts(getboolean(account) ? "true" : "false"); } + else if (getter->type == GET_PRESENCE) + { + struct presence presence; + + presence.type = getpresence(account, &presence.status, + &presence.message); + printf ("(%u, \"%s\", \"%s\")\n", presence.type, + presence.status, presence.message); + free_presence (&presence); + } else if (getter->type == GET_PRESENCE_TYPE || getter->type == GET_PRESENCE_STATUS || getter->type == GET_PRESENCE_MESSAGE) { |