/* * e-source.c * * 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. * * 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, see . * */ /** * SECTION: e-source * @include: libedataserver/libedataserver.h * @short_description: Hierarchical data sources * * An #ESource (or "data source") is a description of a file or network * location where data can be obtained (such as a mail account), or a * description of a resource at that location (such as a mail folder). * * In more concrete terms, it's an interface for a key file. All such * key files have a main group named [Data Source]. The keys in a * [Data Source] group map to #GObject properties in an #ESource. * * Additional groups in the key file are referred to as "extensions". * #ESourceExtension serves as the base class for writing interfaces * for these additional key file groups. The keys in one of these * key file groups map to #GObject properties in some custom subclass * of #ESourceExtension which was written specifically for that key * file group. For example, a key file might include a group named * [Calendar], whose keys map to #GObject properties in an extension * class named #ESourceCalendar. * * Each #ESource contains an internal dictionary of extension objects, * accessible by their key file group name. e_source_get_extension() * can look up extension objects by name. * * An #ESource is identified by a unique identifier string, or "UID", * which is also the basename of the corresponding key file. Additional * files related to the #ESource, such as cache files, are usually kept * in a directory named after the UID of the #ESource. Similarly, the * password for an account described by an #ESource is kept in GNOME * Keyring under the UID of the #ESource. This makes finding these * additional resources simple. * * Several extensions for common information such as authentication * details are built into libedataserver (#ESourceAuthentication, for * example). Backend modules may also define their own extensions for * information and settings unique to the backend. #ESourceExtension * subclasses written for specific backends are generally not available * to applications and shared libraries. This is by design, to try and * keep backend-specific knowledge from creeping into places it doesn't * belong. * * As of 3.12, an #ESource with an #ESourceProxy extension can serve as a * #GProxyResolver. Calling g_proxy_resolver_is_supported() on an #ESource * will reflect this constraint. Attempting a proxy lookup operation on an * #ESource for which g_proxy_resolver_is_supported() returns %FALSE will * fail with %G_IO_ERROR_NOT_SUPPORTED. **/ #include "e-source.h" #include #include #include /* Private D-Bus classes. */ #include #include "e-data-server-util.h" #include "e-secret-store.h" #include "e-source-enumtypes.h" #include "e-source-extension.h" #include "e-uid.h" /* built-in extension types */ #include "e-source-address-book.h" #include "e-source-alarms.h" #include "e-source-authentication.h" #include "e-source-autocomplete.h" #include "e-source-calendar.h" #include "e-source-camel.h" #include "e-source-collection.h" #include "e-source-contacts.h" #include "e-source-goa.h" #include "e-source-ldap.h" #include "e-source-local.h" #include "e-source-mail-account.h" #include "e-source-mail-composition.h" #include "e-source-mail-identity.h" #include "e-source-mail-signature.h" #include "e-source-mail-submission.h" #include "e-source-mail-transport.h" #include "e-source-mdn.h" #include "e-source-offline.h" #include "e-source-openpgp.h" #include "e-source-proxy.h" #include "e-source-refresh.h" #include "e-source-resource.h" #include "e-source-revision-guards.h" #include "e-source-security.h" #include "e-source-selectable.h" #include "e-source-smime.h" #include "e-source-uoa.h" #include "e-source-weather.h" #include "e-source-webdav.h" #define E_SOURCE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_SOURCE, ESourcePrivate)) #define PRIMARY_GROUP_NAME "Data Source" typedef struct _AsyncContext AsyncContext; typedef struct _RemoveContext RemoveContext; struct _ESourcePrivate { GDBusObject *dbus_object; GMainContext *main_context; GSource *changed; GMutex changed_lock; guint ignore_changed_signal; GSource *connection_status_change; GMutex connection_status_change_lock; ESourceConnectionStatus connection_status; GSource *credentials_required_call; GMutex credentials_required_call_lock; GMutex property_lock; gchar *display_name; gchar *collate_key; gchar *parent; gchar *uid; /* The lock guards the key file and hash table. */ GKeyFile *key_file; GRecMutex lock; GHashTable *extensions; gboolean enabled; gboolean initialized; }; struct _AsyncContext { ESource *scratch_source; gchar *access_token; gint expires_in; gchar *password; gboolean permanently; }; /* Used in e_source_remove_sync() */ struct _RemoveContext { GMainContext *main_context; GMainLoop *main_loop; }; enum { PROP_0, PROP_DBUS_OBJECT, PROP_DISPLAY_NAME, PROP_ENABLED, PROP_MAIN_CONTEXT, PROP_PARENT, PROP_REMOTE_CREATABLE, PROP_REMOTE_DELETABLE, PROP_REMOVABLE, PROP_UID, PROP_WRITABLE, PROP_CONNECTION_STATUS }; enum { CHANGED, CREDENTIALS_REQUIRED, AUTHENTICATE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; /* Forward Declarations */ static void e_source_initable_init (GInitableIface *iface); static void e_source_proxy_resolver_init (GProxyResolverInterface *iface); /* Private function shared only with ESourceRegistry. */ void __e_source_private_replace_dbus_object (ESource *source, GDBusObject *dbus_object); G_DEFINE_TYPE_WITH_CODE ( ESource, e_source, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE ( G_TYPE_INITABLE, e_source_initable_init) G_IMPLEMENT_INTERFACE ( G_TYPE_PROXY_RESOLVER, e_source_proxy_resolver_init)) static void async_context_free (AsyncContext *async_context) { if (async_context->scratch_source != NULL) g_object_unref (async_context->scratch_source); g_free (async_context->access_token); g_free (async_context->password); g_slice_free (AsyncContext, async_context); } static RemoveContext * remove_context_new (void) { RemoveContext *remove_context; remove_context = g_slice_new0 (RemoveContext); remove_context->main_context = g_main_context_new (); remove_context->main_loop = g_main_loop_new ( remove_context->main_context, FALSE); return remove_context; } static void remove_context_free (RemoveContext *remove_context) { g_main_loop_unref (remove_context->main_loop); g_main_context_unref (remove_context->main_context); g_slice_free (RemoveContext, remove_context); } static void source_find_extension_classes_rec (GType parent_type, GHashTable *hash_table) { GType *children; guint n_children, ii; children = g_type_children (parent_type, &n_children); for (ii = 0; ii < n_children; ii++) { GType type = children[ii]; ESourceExtensionClass *class; gpointer key; /* Recurse over the child's children. */ source_find_extension_classes_rec (type, hash_table); /* Skip abstract types. */ if (G_TYPE_IS_ABSTRACT (type)) continue; class = g_type_class_ref (type); key = (gpointer) class->name; if (key != NULL) g_hash_table_insert (hash_table, key, class); else g_type_class_unref (class); } g_free (children); } static GHashTable * source_find_extension_classes (void) { GHashTable *hash_table; hash_table = g_hash_table_new_full ( (GHashFunc) g_str_hash, (GEqualFunc) g_str_equal, (GDestroyNotify) NULL, (GDestroyNotify) g_type_class_unref); source_find_extension_classes_rec ( E_TYPE_SOURCE_EXTENSION, hash_table); return hash_table; } static void source_localized_hack (GKeyFile *key_file, const gchar *group_name, const gchar *key, const gchar *new_value) { const gchar * const *language_names; gint ii; /* XXX If we're changing a string key that has translations, * set "key[$CURRENT_LOCALE]" (if available) to the new * value so g_key_file_get_locale_string() will pick it * up. This is not a perfect solution however. When a * different locale is used the value may revert to its * original localized string. Good enough for now. */ language_names = g_get_language_names (); for (ii = 0; language_names[ii] != NULL; ii++) { gboolean has_localized_key; gchar *localized_key; localized_key = g_strdup_printf ( "%s[%s]", key, language_names[ii]); has_localized_key = g_key_file_has_key ( key_file, group_name, localized_key, NULL); if (has_localized_key) g_key_file_set_string ( key_file, group_name, localized_key, new_value); g_free (localized_key); if (has_localized_key) return; } g_key_file_set_string (key_file, group_name, key, new_value); } static gboolean source_check_values_differ (GType g_type, const GValue *value, const GValue *value2) { gboolean differ = TRUE; GValue *value1; g_return_val_if_fail (value != NULL, TRUE); g_return_val_if_fail (value2 != NULL, TRUE); value1 = g_slice_new0 (GValue); g_value_init (value1, g_type); g_value_copy (value2, value1); if (g_value_transform (value, value1)) { #define check_type(name,get) G_STMT_START { \ if (G_VALUE_HOLDS_ ## name (value1)) { \ differ = g_value_get_ ## get (value1) != g_value_get_ ## get (value2); \ break; \ } } G_STMT_END do { check_type (BOOLEAN, boolean); check_type (CHAR, schar); check_type (DOUBLE, double); check_type (ENUM, enum); check_type (FLAGS, flags); check_type (FLOAT, float); check_type (GTYPE, gtype); check_type (INT, int); check_type (INT64, int64); check_type (LONG, long); check_type (POINTER, pointer); check_type (UCHAR, uchar); check_type (UINT, uint); check_type (UINT64, uint64); check_type (ULONG, ulong); if (G_VALUE_HOLDS_STRING (value1)) { differ = g_strcmp0 (g_value_get_string (value1), g_value_get_string (value2)) != 0; break; } if (G_VALUE_HOLDS_VARIANT (value1)) { GVariant *variant1, *variant2; variant1 = g_value_get_variant (value1); variant2 = g_value_get_variant (value2); differ = g_variant_compare (variant1, variant2) != 0; break; } } while (FALSE); #undef check_type } g_value_unset (value1); g_slice_free (GValue, value1); return differ; } static void source_set_key_file_from_property (GObject *object, GParamSpec *pspec, GKeyFile *key_file, const gchar *group_name) { GValue *pvalue; GValue *svalue; gchar *key; pvalue = g_slice_new0 (GValue); g_value_init (pvalue, pspec->value_type); g_object_get_property (object, pspec->name, pvalue); svalue = g_slice_new0 (GValue); g_value_init (svalue, G_TYPE_STRING); key = e_source_parameter_to_key (pspec->name); /* For the most part we can just transform any supported * property type to a string, with a couple exceptions. */ /* Transforming a boolean GValue to a string results in * "TRUE" or "FALSE" (all uppercase), but GKeyFile only * recognizes "true" or "false" (all lowercase). So we * have to use g_key_file_set_boolean(). */ if (G_VALUE_HOLDS_BOOLEAN (pvalue)) { gboolean v_boolean = g_value_get_boolean (pvalue); g_key_file_set_boolean (key_file, group_name, key, v_boolean); /* Store UIN64 in hexa */ } else if (G_VALUE_HOLDS_UINT64 (pvalue)) { gchar *v_str; v_str = g_strdup_printf ( "%016" G_GINT64_MODIFIER "X", g_value_get_uint64 (pvalue)); g_key_file_set_string (key_file, group_name, key, v_str); g_free (v_str); /* String GValues may contain characters that need escaping. */ } else if (G_VALUE_HOLDS_STRING (pvalue)) { const gchar *v_string = g_value_get_string (pvalue); if (v_string == NULL) v_string = ""; /* Special case for localized "DisplayName" keys. */ source_localized_hack (key_file, group_name, key, v_string); /* Transforming an enum GValue to a string results in * the GEnumValue name. We want the shorter nickname. */ } else if (G_VALUE_HOLDS_ENUM (pvalue)) { GParamSpecEnum *enum_pspec; GEnumClass *enum_class; GEnumValue *enum_value; gint value; enum_pspec = G_PARAM_SPEC_ENUM (pspec); enum_class = enum_pspec->enum_class; value = g_value_get_enum (pvalue); enum_value = g_enum_get_value (enum_class, value); if (enum_value == NULL) { value = enum_pspec->default_value; enum_value = g_enum_get_value (enum_class, value); } if (enum_value != NULL) g_key_file_set_string ( key_file, group_name, key, enum_value->value_nick); } else if (G_VALUE_HOLDS (pvalue, G_TYPE_STRV)) { const gchar **strv = g_value_get_boxed (pvalue); guint length = 0; if (strv != NULL) length = g_strv_length ((gchar **) strv); g_key_file_set_string_list ( key_file, group_name, key, strv, length); /* For GValues holding a GFile object we save the URI. */ } else if (G_VALUE_HOLDS (pvalue, G_TYPE_FILE)) { GFile *file = g_value_get_object (pvalue); gchar *uri = NULL; if (file != NULL) uri = g_file_get_uri (file); g_key_file_set_string ( key_file, group_name, key, (uri != NULL) ? uri : ""); g_free (uri); } else if (g_value_transform (pvalue, svalue)) { const gchar *value = g_value_get_string (svalue); g_key_file_set_value (key_file, group_name, key, value); } g_free (key); g_value_unset (pvalue); g_value_unset (svalue); g_slice_free (GValue, pvalue); g_slice_free (GValue, svalue); } static void source_set_property_from_key_file (GObject *object, GParamSpec *pspec, GKeyFile *key_file, const gchar *group_name) { gchar *key; GValue *value; GError *local_error = NULL; value = g_slice_new0 (GValue); key = e_source_parameter_to_key (pspec->name); if (G_IS_PARAM_SPEC_CHAR (pspec) || G_IS_PARAM_SPEC_UCHAR (pspec) || G_IS_PARAM_SPEC_INT (pspec) || G_IS_PARAM_SPEC_UINT (pspec) || G_IS_PARAM_SPEC_LONG (pspec) || G_IS_PARAM_SPEC_ULONG (pspec)) { gint v_int; v_int = g_key_file_get_integer ( key_file, group_name, key, &local_error); if (local_error == NULL) { g_value_init (value, G_TYPE_INT); g_value_set_int (value, v_int); } } else if (G_IS_PARAM_SPEC_INT64 (pspec)) { gint64 v_int64; v_int64 = g_key_file_get_int64 ( key_file, group_name, key, &local_error); if (local_error == NULL) { g_value_init (value, G_TYPE_INT64); g_value_set_int64 (value, v_int64); } } else if (G_IS_PARAM_SPEC_UINT64 (pspec)) { guint64 v_uint64; gchar *v_str; v_str = g_key_file_get_string ( key_file, group_name, key, &local_error); if (local_error == NULL) { v_uint64 = g_ascii_strtoull (v_str, NULL, 16); g_value_init (value, G_TYPE_UINT64); g_value_set_uint64 (value, v_uint64); } g_free (v_str); } else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) { gboolean v_boolean; v_boolean = g_key_file_get_boolean ( key_file, group_name, key, &local_error); if (local_error == NULL) { g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, v_boolean); } } else if (G_IS_PARAM_SPEC_ENUM (pspec)) { gchar *nick; nick = g_key_file_get_string ( key_file, group_name, key, &local_error); if (local_error == NULL) { GParamSpecEnum *enum_pspec; GEnumValue *enum_value; enum_pspec = G_PARAM_SPEC_ENUM (pspec); enum_value = g_enum_get_value_by_nick ( enum_pspec->enum_class, nick); if (enum_value != NULL) { g_value_init (value, pspec->value_type); g_value_set_enum (value, enum_value->value); } g_free (nick); } } else if (G_IS_PARAM_SPEC_FLOAT (pspec) || G_IS_PARAM_SPEC_DOUBLE (pspec)) { gdouble v_double; v_double = g_key_file_get_double ( key_file, group_name, key, &local_error); if (local_error == NULL) { g_value_init (value, G_TYPE_DOUBLE); g_value_set_double (value, v_double); } } else if (G_IS_PARAM_SPEC_STRING (pspec)) { gchar *v_string; /* Get the localized string if present. */ v_string = g_key_file_get_locale_string ( key_file, group_name, key, NULL, &local_error); if (local_error == NULL) { g_value_init (value, G_TYPE_STRING); g_value_take_string (value, v_string); } } else if (g_type_is_a (pspec->value_type, G_TYPE_STRV)) { gchar **strv; strv = g_key_file_get_string_list ( key_file, group_name, key, NULL, &local_error); if (local_error == NULL) { g_value_init (value, G_TYPE_STRV); g_value_take_boxed (value, strv); } } else if (g_type_is_a (pspec->value_type, G_TYPE_FILE)) { gchar *uri; /* Create the GFile from the URI string. */ uri = g_key_file_get_locale_string ( key_file, group_name, key, NULL, &local_error); if (local_error == NULL) { GFile *file = NULL; if (uri != NULL && *uri != '\0') file = g_file_new_for_uri (uri); g_value_init (value, pspec->value_type); g_value_take_object (value, file); g_free (uri); } } else { g_warning ( "No GKeyFile-to-GValue converter defined " "for type '%s'", G_PARAM_SPEC_TYPE_NAME (pspec)); } /* If a value could not be retrieved from the key * file, restore the property to its default value. */ if (local_error != NULL) { g_value_init (value, pspec->value_type); g_param_value_set_default (pspec, value); g_error_free (local_error); } if (G_IS_VALUE (value)) { GValue *cvalue; cvalue = g_slice_new0 (GValue); g_value_init (cvalue, pspec->value_type); g_object_get_property (object, pspec->name, cvalue); /* This is because the g_object_set_property() invokes "notify" signal * on the set property, even if the value did not change, which creates * false notifications, which can cause UI or background activities * without any real reason (especially with the ''enabled' property load). */ if (!G_IS_VALUE (cvalue) || source_check_values_differ (pspec->value_type, value, cvalue)) g_object_set_property (object, pspec->name, value); if (G_IS_VALUE (cvalue)) g_value_unset (cvalue); g_slice_free (GValue, cvalue); g_value_unset (value); } g_slice_free (GValue, value); g_free (key); } static void source_load_from_key_file (GObject *object, GKeyFile *key_file, const gchar *group_name) { GObjectClass *class; GParamSpec **properties; guint n_properties, ii; class = G_OBJECT_GET_CLASS (object); properties = g_object_class_list_properties (class, &n_properties); g_object_freeze_notify (object); for (ii = 0; ii < n_properties; ii++) { if (properties[ii]->flags & E_SOURCE_PARAM_SETTING) { source_set_property_from_key_file ( object, properties[ii], key_file, group_name); } } g_object_thaw_notify (object); g_free (properties); } static void source_save_to_key_file (GObject *object, GKeyFile *key_file, const gchar *group_name) { GObjectClass *class; GParamSpec **properties; guint n_properties, ii; class = G_OBJECT_GET_CLASS (object); properties = g_object_class_list_properties (class, &n_properties); for (ii = 0; ii < n_properties; ii++) { if (properties[ii]->flags & E_SOURCE_PARAM_SETTING) { source_set_key_file_from_property ( object, properties[ii], key_file, group_name); } } g_free (properties); } static gboolean source_parse_dbus_data (ESource *source, GError **error) { GHashTableIter iter; EDBusObject *dbus_object; EDBusSource *dbus_source; GKeyFile *key_file; gpointer group_name; gpointer extension; gchar *data; gboolean success; if (!source->priv->dbus_object) return FALSE; dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); dbus_source = e_dbus_object_get_source (dbus_object); data = e_dbus_source_dup_data (dbus_source); g_object_unref (dbus_source); g_return_val_if_fail (data != NULL, FALSE); key_file = source->priv->key_file; success = g_key_file_load_from_data ( key_file, data, strlen (data), G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, error); g_free (data); data = NULL; if (!success) return FALSE; /* Make sure the key file has a [Data Source] group. */ if (!g_key_file_has_group (key_file, PRIMARY_GROUP_NAME)) { g_set_error ( error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND, _("Source file is missing a [%s] group"), PRIMARY_GROUP_NAME); return FALSE; } /* Load key file values from the [Data Source] group and from * any other groups for which an extension object has already * been created. Note that not all the extension classes may * be registered at this point, so avoid attempting to create * new extension objects here. Extension objects are created * on-demand in e_source_get_extension(). */ source_load_from_key_file ( G_OBJECT (source), key_file, PRIMARY_GROUP_NAME); g_hash_table_iter_init (&iter, source->priv->extensions); while (g_hash_table_iter_next (&iter, &group_name, &extension)) source_load_from_key_file (extension, key_file, group_name); return TRUE; } static void source_notify_dbus_data_cb (EDBusSource *dbus_source, GParamSpec *pspec, ESource *source) { GError *local_error = NULL; g_rec_mutex_lock (&source->priv->lock); /* Since the source data came from a GKeyFile structure on the * server-side, this should never fail. But we'll print error * messages to the terminal just in case. */ source_parse_dbus_data (source, &local_error); if (local_error != NULL) { g_warning ("%s", local_error->message); g_error_free (local_error); } g_rec_mutex_unlock (&source->priv->lock); } static gboolean source_update_connection_status_internal (ESource *source, EDBusSource *dbus_source) { ESourceConnectionStatus connection_status_value; gchar *connection_status; gboolean changed = FALSE; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (dbus_source != NULL, FALSE); connection_status_value = E_SOURCE_CONNECTION_STATUS_DISCONNECTED; connection_status = e_dbus_source_dup_connection_status (dbus_source); if (connection_status) { GEnumClass *enum_class; GEnumValue *enum_value; enum_class = g_type_class_ref (E_TYPE_SOURCE_CONNECTION_STATUS); enum_value = g_enum_get_value_by_nick (enum_class, connection_status); if (enum_value) { connection_status_value = enum_value->value; } else if (!*connection_status) { connection_status_value = E_SOURCE_CONNECTION_STATUS_DISCONNECTED; } else { g_warning ("%s: Unknown connection status: '%s'", G_STRFUNC, connection_status); } g_type_class_unref (enum_class); g_free (connection_status); } if (source->priv->connection_status != connection_status_value) { source->priv->connection_status = connection_status_value; changed = TRUE; } return changed; } static gboolean source_idle_connection_status_change_cb (gpointer user_data) { ESource *source = E_SOURCE (user_data); EDBusObject *dbus_object; EDBusSource *dbus_source; gboolean changed = FALSE; if (g_source_is_destroyed (g_main_current_source ())) return FALSE; /* If the ESource is still initializing itself in a different * thread, skip the signal emission and try again on the next * main loop iteration. This is a busy wait but it should be * a very short wait. */ if (!source->priv->initialized) return TRUE; g_mutex_lock (&source->priv->connection_status_change_lock); if (source->priv->connection_status_change != NULL) { g_source_unref (source->priv->connection_status_change); source->priv->connection_status_change = NULL; } g_mutex_unlock (&source->priv->connection_status_change_lock); g_object_freeze_notify (G_OBJECT (source)); g_mutex_lock (&source->priv->property_lock); if (source->priv->dbus_object) { dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); dbus_source = e_dbus_object_get_source (dbus_object); changed = source_update_connection_status_internal (source, dbus_source); g_object_unref (dbus_source); } if (changed) g_object_notify (G_OBJECT (source), "connection-status"); g_mutex_unlock (&source->priv->property_lock); g_object_thaw_notify (G_OBJECT (source)); return FALSE; } static void source_notify_dbus_connection_status_cb (EDBusSource *dbus_source, GParamSpec *pspec, ESource *source) { g_mutex_lock (&source->priv->connection_status_change_lock); if (source->priv->connection_status_change == NULL) { source->priv->connection_status_change = g_idle_source_new (); g_source_set_callback ( source->priv->connection_status_change, source_idle_connection_status_change_cb, source, NULL); g_source_attach ( source->priv->connection_status_change, source->priv->main_context); } g_mutex_unlock (&source->priv->connection_status_change_lock); } static ESourceCredentialsReason source_credentials_reason_from_text (const gchar *arg_reason) { ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN; if (arg_reason && *arg_reason) { GEnumClass *enum_class; GEnumValue *enum_value; enum_class = g_type_class_ref (E_TYPE_SOURCE_CREDENTIALS_REASON); enum_value = g_enum_get_value_by_nick (enum_class, arg_reason); if (enum_value) { reason = enum_value->value; } else { g_warning ("%s: Unknown reason enum: '%s'", G_STRFUNC, arg_reason); } g_type_class_unref (enum_class); } return reason; } static GTlsCertificateFlags source_certificate_errors_from_text (const gchar *arg_certificate_errors) { GTlsCertificateFlags certificate_errors = 0; if (arg_certificate_errors && *arg_certificate_errors) { GFlagsClass *flags_class; gchar **flags_strv; gsize ii; flags_class = g_type_class_ref (G_TYPE_TLS_CERTIFICATE_FLAGS); flags_strv = g_strsplit (arg_certificate_errors, ":", -1); for (ii = 0; flags_strv[ii] != NULL; ii++) { GFlagsValue *flags_value; flags_value = g_flags_get_value_by_nick (flags_class, flags_strv[ii]); if (flags_value != NULL) { certificate_errors |= flags_value->value; } else { g_warning ("%s: Unknown flag: '%s'", G_STRFUNC, flags_strv[ii]); } } g_strfreev (flags_strv); g_type_class_unref (flags_class); } return certificate_errors; } static void source_dbus_credentials_required_cb (EDBusSource *dbus_source, const gchar *arg_reason, const gchar *arg_certificate_pem, const gchar *arg_certificate_errors, const gchar *arg_dbus_error_name, const gchar *arg_dbus_error_message, ESource *source) { ESourceCredentialsReason reason; GTlsCertificateFlags certificate_errors; GError *op_error = NULL; g_return_if_fail (E_IS_SOURCE (source)); g_return_if_fail (arg_reason != NULL); g_return_if_fail (arg_certificate_pem != NULL); g_return_if_fail (arg_certificate_errors != NULL); g_return_if_fail (arg_dbus_error_name != NULL); g_return_if_fail (arg_dbus_error_message != NULL); reason = source_credentials_reason_from_text (arg_reason); certificate_errors = source_certificate_errors_from_text (arg_certificate_errors); if (*arg_dbus_error_name) { op_error = g_dbus_error_new_for_dbus_error (arg_dbus_error_name, arg_dbus_error_message); g_dbus_error_strip_remote_error (op_error); } /* This is delivered in the GDBus thread */ e_source_emit_credentials_required (source, reason, arg_certificate_pem, certificate_errors, op_error); g_clear_error (&op_error); } static gboolean source_dbus_authenticate_cb (EDBusSource *dbus_interface, const gchar *const *arg_credentials, ESource *source) { ENamedParameters *credentials; credentials = e_named_parameters_new_strv (arg_credentials); /* This is delivered in the GDBus thread */ g_signal_emit (source, signals[AUTHENTICATE], 0, credentials); e_named_parameters_free (credentials); return TRUE; } static gboolean source_idle_changed_cb (gpointer user_data) { ESource *source = E_SOURCE (user_data); if (g_source_is_destroyed (g_main_current_source ())) return FALSE; /* If the ESource is still initializing itself in a different * thread, skip the signal emission and try again on the next * main loop iteration. This is a busy wait but it should be * a very short wait. */ if (!source->priv->initialized) return TRUE; g_mutex_lock (&source->priv->changed_lock); if (source->priv->changed != NULL) { g_source_unref (source->priv->changed); source->priv->changed = NULL; } g_mutex_unlock (&source->priv->changed_lock); g_signal_emit (source, signals[CHANGED], 0); return FALSE; } static void source_set_dbus_object (ESource *source, EDBusObject *dbus_object) { /* D-Bus object will be NULL when configuring a new source. */ if (dbus_object == NULL) return; g_return_if_fail (E_DBUS_IS_OBJECT (dbus_object)); g_return_if_fail (source->priv->dbus_object == NULL); source->priv->dbus_object = g_object_ref (dbus_object); } static void source_set_main_context (ESource *source, GMainContext *main_context) { g_return_if_fail (source->priv->main_context == NULL); source->priv->main_context = (main_context != NULL) ? g_main_context_ref (main_context) : g_main_context_ref_thread_default (); } static void source_set_uid (ESource *source, const gchar *uid) { /* The "uid" argument will usually be NULL unless called * from e_source_new_with_uid(). If NULL, we'll pick up * a UID in source_initable_init(). */ g_return_if_fail (source->priv->uid == NULL); source->priv->uid = g_strdup (uid); } static void source_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_DBUS_OBJECT: source_set_dbus_object ( E_SOURCE (object), g_value_get_object (value)); return; case PROP_DISPLAY_NAME: e_source_set_display_name ( E_SOURCE (object), g_value_get_string (value)); return; case PROP_ENABLED: e_source_set_enabled ( E_SOURCE (object), g_value_get_boolean (value)); return; case PROP_MAIN_CONTEXT: source_set_main_context ( E_SOURCE (object), g_value_get_boxed (value)); return; case PROP_PARENT: e_source_set_parent ( E_SOURCE (object), g_value_get_string (value)); return; case PROP_UID: source_set_uid ( E_SOURCE (object), g_value_get_string (value)); return; case PROP_CONNECTION_STATUS: e_source_set_connection_status (E_SOURCE (object), g_value_get_enum (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void source_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_DBUS_OBJECT: g_value_take_object ( value, e_source_ref_dbus_object ( E_SOURCE (object))); return; case PROP_DISPLAY_NAME: g_value_take_string ( value, e_source_dup_display_name ( E_SOURCE (object))); return; case PROP_ENABLED: g_value_set_boolean ( value, e_source_get_enabled ( E_SOURCE (object))); return; case PROP_MAIN_CONTEXT: g_value_take_boxed ( value, e_source_ref_main_context ( E_SOURCE (object))); return; case PROP_PARENT: g_value_take_string ( value, e_source_dup_parent ( E_SOURCE (object))); return; case PROP_REMOTE_CREATABLE: g_value_set_boolean ( value, e_source_get_remote_creatable ( E_SOURCE (object))); return; case PROP_REMOTE_DELETABLE: g_value_set_boolean ( value, e_source_get_remote_deletable ( E_SOURCE (object))); return; case PROP_REMOVABLE: g_value_set_boolean ( value, e_source_get_removable ( E_SOURCE (object))); return; case PROP_UID: g_value_take_string ( value, e_source_dup_uid ( E_SOURCE (object))); return; case PROP_WRITABLE: g_value_set_boolean ( value, e_source_get_writable ( E_SOURCE (object))); return; case PROP_CONNECTION_STATUS: g_value_set_enum (value, e_source_get_connection_status (E_SOURCE (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void source_dispose (GObject *object) { ESourcePrivate *priv; priv = E_SOURCE_GET_PRIVATE (object); if (priv->dbus_object != NULL) { EDBusObject *dbus_object; EDBusSource *dbus_source; dbus_object = E_DBUS_OBJECT (priv->dbus_object); dbus_source = e_dbus_object_get_source (dbus_object); if (dbus_source != NULL) { g_signal_handlers_disconnect_matched ( dbus_source, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object); g_object_unref (dbus_source); } g_object_unref (priv->dbus_object); priv->dbus_object = NULL; } if (priv->main_context != NULL) { g_main_context_unref (priv->main_context); priv->main_context = NULL; } /* XXX Maybe not necessary to acquire the lock? */ g_mutex_lock (&priv->changed_lock); if (priv->changed != NULL) { g_source_destroy (priv->changed); g_source_unref (priv->changed); priv->changed = NULL; } g_mutex_unlock (&priv->changed_lock); g_mutex_lock (&priv->connection_status_change_lock); if (priv->connection_status_change != NULL) { g_source_destroy (priv->connection_status_change); g_source_unref (priv->connection_status_change); priv->connection_status_change = NULL; } g_mutex_unlock (&priv->connection_status_change_lock); g_mutex_lock (&priv->credentials_required_call_lock); if (priv->credentials_required_call != NULL) { g_source_destroy (priv->credentials_required_call); g_source_unref (priv->credentials_required_call); priv->credentials_required_call = NULL; } g_mutex_unlock (&priv->credentials_required_call_lock); g_hash_table_remove_all (priv->extensions); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_source_parent_class)->dispose (object); } static void source_finalize (GObject *object) { ESourcePrivate *priv; priv = E_SOURCE_GET_PRIVATE (object); g_mutex_clear (&priv->changed_lock); g_mutex_clear (&priv->connection_status_change_lock); g_mutex_clear (&priv->credentials_required_call_lock); g_mutex_clear (&priv->property_lock); g_free (priv->display_name); g_free (priv->collate_key); g_free (priv->parent); g_free (priv->uid); g_key_file_free (priv->key_file); g_rec_mutex_clear (&priv->lock); g_hash_table_destroy (priv->extensions); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_source_parent_class)->finalize (object); } static void source_notify (GObject *object, GParamSpec *pspec) { if ((pspec->flags & E_SOURCE_PARAM_SETTING) != 0) e_source_changed (E_SOURCE (object)); } /* Helper for source_remove_sync() */ static gboolean source_remove_main_loop_quit_cb (gpointer user_data) { GMainLoop *main_loop = user_data; g_main_loop_quit (main_loop); return FALSE; } /* Helper for e_source_remove_sync() */ static void source_remove_notify_dbus_object_cb (ESource *source, GParamSpec *pspec, RemoveContext *remove_context) { GDBusObject *dbus_object; dbus_object = e_source_ref_dbus_object (source); /* The GDBusObject will be NULL once the ESourceRegistry * receives an "object-removed" signal for this ESource. */ if (dbus_object == NULL) { GSource *idle_source; idle_source = g_idle_source_new (); g_source_set_callback ( idle_source, source_remove_main_loop_quit_cb, g_main_loop_ref (remove_context->main_loop), (GDestroyNotify) g_main_loop_unref); g_source_attach (idle_source, remove_context->main_context); g_source_unref (idle_source); } g_clear_object (&dbus_object); } static gboolean source_remove_sync (ESource *source, GCancellable *cancellable, GError **error) { EDBusSourceRemovable *dbus_interface = NULL; GDBusObject *dbus_object; RemoveContext *remove_context; gulong notify_dbus_object_id; GError *local_error = NULL; dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_interface = e_dbus_object_get_source_removable ( E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (dbus_interface == NULL) { g_set_error ( error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, _("Data source '%s' is not removable"), e_source_get_display_name (source)); return FALSE; } remove_context = remove_context_new (); g_main_context_push_thread_default (remove_context->main_context); notify_dbus_object_id = g_signal_connect ( source, "notify::dbus-object", G_CALLBACK (source_remove_notify_dbus_object_cb), remove_context); e_dbus_source_removable_call_remove_sync ( dbus_interface, cancellable, &local_error); /* Wait for the ESourceRegistry to remove our GDBusObject while * handling an "object-removed" signal from the registry service. * But also set a short timeout to avoid getting deadlocked here. */ if (local_error == NULL) { GSource *timeout_source; timeout_source = g_timeout_source_new_seconds (2); g_source_set_callback ( timeout_source, source_remove_main_loop_quit_cb, g_main_loop_ref (remove_context->main_loop), (GDestroyNotify) g_main_loop_unref); g_source_attach (timeout_source, remove_context->main_context); g_source_unref (timeout_source); g_main_loop_run (remove_context->main_loop); } g_signal_handler_disconnect (source, notify_dbus_object_id); g_main_context_pop_thread_default (remove_context->main_context); remove_context_free (remove_context); g_object_unref (dbus_interface); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return TRUE; } /* Helper for source_remove() */ static void source_remove_thread (GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable) { GError *local_error = NULL; e_source_remove_sync (E_SOURCE (object), cancellable, &local_error); if (local_error != NULL) g_simple_async_result_take_error (simple, local_error); } static void source_remove (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new ( G_OBJECT (source), callback, user_data, source_remove); g_simple_async_result_set_check_cancellable (simple, cancellable); g_simple_async_result_run_in_thread ( simple, source_remove_thread, G_PRIORITY_DEFAULT, cancellable); g_object_unref (simple); } static gboolean source_remove_finish (ESource *source, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (source), source_remove), FALSE); simple = G_SIMPLE_ASYNC_RESULT (result); /* Assume success unless a GError is set. */ return !g_simple_async_result_propagate_error (simple, error); } static gboolean source_write_sync (ESource *source, GCancellable *cancellable, GError **error) { EDBusSourceWritable *dbus_interface = NULL; GDBusObject *dbus_object; gchar *data; GError *local_error = NULL; dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_interface = e_dbus_object_get_source_writable ( E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (dbus_interface == NULL) { g_set_error ( error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, _("Data source '%s' is not writable"), e_source_get_display_name (source)); return FALSE; } data = e_source_to_string (source, NULL); e_dbus_source_writable_call_write_sync ( dbus_interface, data, cancellable, &local_error); g_free (data); g_object_unref (dbus_interface); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return TRUE; } /* Helper for source_write() */ static void source_write_thread (GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable) { GError *local_error = NULL; e_source_write_sync (E_SOURCE (object), cancellable, &local_error); if (local_error != NULL) g_simple_async_result_take_error (simple, local_error); } static void source_write (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new ( G_OBJECT (source), callback, user_data, source_write); g_simple_async_result_set_check_cancellable (simple, cancellable); g_simple_async_result_run_in_thread ( simple, source_write_thread, G_PRIORITY_DEFAULT, cancellable); g_object_unref (simple); } static gboolean source_write_finish (ESource *source, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (source), source_write), FALSE); simple = G_SIMPLE_ASYNC_RESULT (result); /* Assume success unless a GError is set. */ return !g_simple_async_result_propagate_error (simple, error); } static gboolean source_remote_create_sync (ESource *source, ESource *scratch_source, GCancellable *cancellable, GError **error) { EDBusSourceRemoteCreatable *dbus_interface = NULL; GDBusObject *dbus_object; gchar *uid, *data; GError *local_error = NULL; dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_interface = e_dbus_object_get_source_remote_creatable ( E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (dbus_interface == NULL) { g_set_error ( error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Data source '%s' does not " "support creating remote resources"), e_source_get_display_name (source)); return FALSE; } uid = e_source_dup_uid (scratch_source); data = e_source_to_string (scratch_source, NULL); e_dbus_source_remote_creatable_call_create_sync ( dbus_interface, uid, data, cancellable, &local_error); g_free (data); g_free (uid); g_object_unref (dbus_interface); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return TRUE; } /* Helper for source_remote_create() */ static void source_remote_create_thread (GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable) { AsyncContext *async_context; GError *local_error = NULL; async_context = g_simple_async_result_get_op_res_gpointer (simple); e_source_remote_create_sync ( E_SOURCE (object), async_context->scratch_source, cancellable, &local_error); if (local_error != NULL) g_simple_async_result_take_error (simple, local_error); } static void source_remote_create (ESource *source, ESource *scratch_source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; AsyncContext *async_context; async_context = g_slice_new0 (AsyncContext); async_context->scratch_source = g_object_ref (scratch_source); simple = g_simple_async_result_new ( G_OBJECT (source), callback, user_data, source_remote_create); g_simple_async_result_set_check_cancellable (simple, cancellable); g_simple_async_result_set_op_res_gpointer ( simple, async_context, (GDestroyNotify) async_context_free); g_simple_async_result_run_in_thread ( simple, source_remote_create_thread, G_PRIORITY_DEFAULT, cancellable); g_object_unref (simple); } static gboolean source_remote_create_finish (ESource *source, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (source), source_remote_create), FALSE); simple = G_SIMPLE_ASYNC_RESULT (result); /* Assume success unless a GError is set. */ return !g_simple_async_result_propagate_error (simple, error); } static gboolean source_remote_delete_sync (ESource *source, GCancellable *cancellable, GError **error) { EDBusSourceRemoteDeletable *dbus_interface = NULL; GDBusObject *dbus_object; GError *local_error = NULL; dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_interface = e_dbus_object_get_source_remote_deletable ( E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (dbus_interface == NULL) { g_set_error ( error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Data source '%s' does not " "support deleting remote resources"), e_source_get_display_name (source)); return FALSE; } e_dbus_source_remote_deletable_call_delete_sync ( dbus_interface, cancellable, &local_error); g_object_unref (dbus_interface); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return TRUE; } /* Helper for source_remote_delete() */ static void source_remote_delete_thread (GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable) { GError *local_error = NULL; e_source_remote_delete_sync ( E_SOURCE (object), cancellable, &local_error); if (local_error != NULL) g_simple_async_result_take_error (simple, local_error); } static void source_remote_delete (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new ( G_OBJECT (source), callback, user_data, source_remote_delete); g_simple_async_result_set_check_cancellable (simple, cancellable); g_simple_async_result_run_in_thread ( simple, source_remote_delete_thread, G_PRIORITY_DEFAULT, cancellable); g_object_unref (simple); } static gboolean source_remote_delete_finish (ESource *source, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (source), source_remote_delete), FALSE); simple = G_SIMPLE_ASYNC_RESULT (result); /* Assume success unless a GError is set. */ return !g_simple_async_result_propagate_error (simple, error); } static gboolean source_get_oauth2_access_token_sync (ESource *source, GCancellable *cancellable, gchar **out_access_token, gint *out_expires_in, GError **error) { EDBusSourceOAuth2Support *dbus_interface = NULL; GDBusObject *dbus_object; GError *local_error = NULL; dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_interface = e_dbus_object_get_source_oauth2_support ( E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (dbus_interface == NULL) { g_set_error ( error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Data source '%s' does not " "support OAuth 2.0 authentication"), e_source_get_display_name (source)); return FALSE; } e_dbus_source_oauth2_support_call_get_access_token_sync ( dbus_interface, out_access_token, out_expires_in, cancellable, &local_error); g_object_unref (dbus_interface); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return TRUE; } /* Helper for source_get_oauth2_access_token() */ static void source_get_oauth2_access_token_thread (GSimpleAsyncResult *simple, GObject *object, GCancellable *cancellable) { AsyncContext *async_context; GError *local_error = NULL; async_context = g_simple_async_result_get_op_res_gpointer (simple); e_source_get_oauth2_access_token_sync ( E_SOURCE (object), cancellable, &async_context->access_token, &async_context->expires_in, &local_error); if (local_error != NULL) g_simple_async_result_take_error (simple, local_error); } static void source_get_oauth2_access_token (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; AsyncContext *async_context; async_context = g_slice_new0 (AsyncContext); simple = g_simple_async_result_new ( G_OBJECT (source), callback, user_data, source_get_oauth2_access_token); g_simple_async_result_set_check_cancellable (simple, cancellable); g_simple_async_result_set_op_res_gpointer ( simple, async_context, (GDestroyNotify) async_context_free); g_simple_async_result_run_in_thread ( simple, source_get_oauth2_access_token_thread, G_PRIORITY_DEFAULT, cancellable); g_object_unref (simple); } static gboolean source_get_oauth2_access_token_finish (ESource *source, GAsyncResult *result, gchar **out_access_token, gint *out_expires_in, GError **error) { GSimpleAsyncResult *simple; AsyncContext *async_context; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (source), source_get_oauth2_access_token), FALSE); simple = G_SIMPLE_ASYNC_RESULT (result); async_context = g_simple_async_result_get_op_res_gpointer (simple); if (g_simple_async_result_propagate_error (simple, error)) return FALSE; g_return_val_if_fail (async_context->access_token != NULL, FALSE); if (out_access_token != NULL) { *out_access_token = async_context->access_token; async_context->access_token = NULL; } if (out_expires_in != NULL) *out_expires_in = async_context->expires_in; return TRUE; } static gboolean source_invoke_credentials_required_impl (ESource *source, gpointer dbus_source, /* EDBusSource * */ const gchar *arg_reason, const gchar *arg_certificate_pem, const gchar *arg_certificate_errors, const gchar *arg_dbus_error_name, const gchar *arg_dbus_error_message, GCancellable *cancellable, GError **error) { g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE); return e_dbus_source_call_invoke_credentials_required_sync (dbus_source, arg_reason ? arg_reason : "", arg_certificate_pem ? arg_certificate_pem : "", arg_certificate_errors ? arg_certificate_errors : "", arg_dbus_error_name ? arg_dbus_error_name : "", arg_dbus_error_message ? arg_dbus_error_message : "", cancellable, error); } static gboolean source_invoke_authenticate_impl (ESource *source, gpointer dbus_source, /* EDBusSource * */ const gchar * const *arg_credentials, GCancellable *cancellable, GError **error) { g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE); return e_dbus_source_call_invoke_authenticate_sync (dbus_source, arg_credentials, cancellable, error); } static gboolean source_unset_last_credentials_required_arguments_impl (ESource *source, GCancellable *cancellable, GError **error) { GDBusObject *dbus_object; EDBusSource *dbus_source = NULL; gboolean success; GError *local_error = NULL; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (!dbus_source) return FALSE; success = e_dbus_source_call_unset_last_credentials_required_arguments_sync (dbus_source, cancellable, &local_error); g_object_unref (dbus_source); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return success; } static gboolean source_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { ESource *source; gboolean success = TRUE; source = E_SOURCE (initable); /* The D-Bus object has the unique identifier (UID). */ if (source->priv->dbus_object != NULL) { EDBusObject *dbus_object; EDBusSource *dbus_source; dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); /* An EDBusObject lacking an EDBusSource * interface indicates a programmer error. */ dbus_source = e_dbus_object_get_source (dbus_object); g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE); /* The UID never changes, so we can cache a copy. * * XXX Note, EServerSideSource may have already set this * by way of the "uid" construct-only property, hence * the g_free() call. Not a problem, we'll just free * our UID string and set it to the same value again. */ g_free (source->priv->uid); source->priv->uid = e_dbus_source_dup_uid (dbus_source); source_update_connection_status_internal (source, dbus_source); g_signal_connect ( dbus_source, "notify::data", G_CALLBACK (source_notify_dbus_data_cb), source); g_signal_connect ( dbus_source, "notify::connection-status", G_CALLBACK (source_notify_dbus_connection_status_cb), source); g_signal_connect ( dbus_source, "credentials-required", G_CALLBACK (source_dbus_credentials_required_cb), source); g_signal_connect ( dbus_source, "authenticate", G_CALLBACK (source_dbus_authenticate_cb), source); success = source_parse_dbus_data (source, error); g_object_unref (dbus_source); /* No D-Bus object implies we're configuring a new source, * so generate a new unique identifier (UID) unless one was * explicitly provided through e_source_new_with_uid(). */ } else if (source->priv->uid == NULL) { source->priv->uid = e_uid_new (); } /* Try to avoid a spurious "changed" emission. */ g_mutex_lock (&source->priv->changed_lock); if (source->priv->changed != NULL) { g_source_destroy (source->priv->changed); g_source_unref (source->priv->changed); source->priv->changed = NULL; } g_mutex_unlock (&source->priv->changed_lock); source->priv->initialized = TRUE; return success; } static gboolean source_proxy_resolver_is_supported (GProxyResolver *resolver) { return e_source_has_extension ( E_SOURCE (resolver), E_SOURCE_EXTENSION_PROXY); } static gchar ** source_proxy_resolver_lookup (GProxyResolver *resolver, const gchar *uri, GCancellable *cancellable, GError **error) { return e_source_proxy_lookup_sync ( E_SOURCE (resolver), uri, cancellable, error); } /* Helper for source_proxy_resolver_lookup_async() */ static void source_proxy_resolver_lookup_ready_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *simple; gchar **proxies; GError *local_error = NULL; simple = G_SIMPLE_ASYNC_RESULT (user_data); proxies = e_source_proxy_lookup_finish ( E_SOURCE (object), result, &local_error); /* Sanity check. */ g_return_if_fail ( ((proxies != NULL) && (local_error == NULL)) || ((proxies == NULL) && (local_error != NULL))); if (proxies != NULL) { g_simple_async_result_set_op_res_gpointer ( simple, proxies, (GDestroyNotify) g_strfreev); } else { g_simple_async_result_take_error (simple, local_error); } g_simple_async_result_complete (simple); g_object_unref (simple); } static void source_proxy_resolver_lookup_async (GProxyResolver *resolver, const gchar *uri, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new ( G_OBJECT (resolver), callback, user_data, source_proxy_resolver_lookup_async); g_simple_async_result_set_check_cancellable (simple, cancellable); e_source_proxy_lookup ( E_SOURCE (resolver), uri, cancellable, source_proxy_resolver_lookup_ready_cb, g_object_ref (simple)); g_object_unref (simple); } static gchar ** source_proxy_resolver_lookup_finish (GProxyResolver *resolver, GAsyncResult *result, GError **error) { GSimpleAsyncResult *simple; gchar **proxies; g_return_val_if_fail ( g_simple_async_result_is_valid ( result, G_OBJECT (resolver), source_proxy_resolver_lookup_async), NULL); simple = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_propagate_error (simple, error)) return NULL; proxies = g_simple_async_result_get_op_res_gpointer (simple); return g_strdupv (proxies); } static void e_source_class_init (ESourceClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (ESourcePrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = source_set_property; object_class->get_property = source_get_property; object_class->dispose = source_dispose; object_class->finalize = source_finalize; object_class->notify = source_notify; class->remove_sync = source_remove_sync; class->remove = source_remove; class->remove_finish = source_remove_finish; class->write_sync = source_write_sync; class->write = source_write; class->write_finish = source_write_finish; class->remote_create_sync = source_remote_create_sync; class->remote_create = source_remote_create; class->remote_create_finish = source_remote_create_finish; class->remote_delete_sync = source_remote_delete_sync; class->remote_delete = source_remote_delete; class->remote_delete_finish = source_remote_delete_finish; class->get_oauth2_access_token_sync = source_get_oauth2_access_token_sync; class->get_oauth2_access_token = source_get_oauth2_access_token; class->get_oauth2_access_token_finish = source_get_oauth2_access_token_finish; class->invoke_credentials_required_impl = source_invoke_credentials_required_impl; class->invoke_authenticate_impl = source_invoke_authenticate_impl; class->unset_last_credentials_required_arguments_impl = source_unset_last_credentials_required_arguments_impl; g_object_class_install_property ( object_class, PROP_DBUS_OBJECT, g_param_spec_object ( "dbus-object", "D-Bus Object", "The D-Bus object for the data source", E_DBUS_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_DISPLAY_NAME, g_param_spec_string ( "display-name", "Display Name", "The human-readable name of the data source", _("Unnamed"), G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_ENABLED, g_param_spec_boolean ( "enabled", "Enabled", "Whether the data source is enabled", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_MAIN_CONTEXT, g_param_spec_boxed ( "main-context", "Main Context", "The main loop context on " "which to attach event sources", G_TYPE_MAIN_CONTEXT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_PARENT, g_param_spec_string ( "parent", "Parent", "The unique identity of the parent data source", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_REMOTE_CREATABLE, g_param_spec_boolean ( "remote-creatable", "Remote Creatable", "Whether the data source " "can create remote resources", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_REMOTE_DELETABLE, g_param_spec_boolean ( "remote-deletable", "Remote Deletable", "Whether the data source " "can delete remote resources", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_REMOVABLE, g_param_spec_boolean ( "removable", "Removable", "Whether the data source is removable", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_UID, g_param_spec_string ( "uid", "UID", "The unique identity of the data source", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_WRITABLE, g_param_spec_boolean ( "writable", "Writable", "Whether the data source is writable", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_CONNECTION_STATUS, g_param_spec_enum ( "connection-status", "Connection Status", "Connection status of the source", E_TYPE_SOURCE_CONNECTION_STATUS, E_SOURCE_CONNECTION_STATUS_DISCONNECTED, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * ESource::changed: * @source: the #ESource that received the signal * * The ::changed signal is emitted when a property in @source or * one of its extension objects changes. A common use for this * signal is to notify a #GtkTreeModel containing data collected * from #ESources that it needs to update a row. **/ signals[CHANGED] = g_signal_new ( "changed", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (ESourceClass, changed), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ESource::credentials-required: * @source: the #ESource that received the signal * @reason: an #ESourceCredentialsReason indicating why the credentials are requested * @certificate_pem: PEM-encoded secure connection certificate for failed SSL checks * @certificate_errors: what failed with the SSL certificate * @error: a text description of the error, if any * * The ::credentials-required signal is emitted when the @source * requires credentials to connect to (possibly remote) * data store. The credentials can be passed to the backend using * e_source_authenticate() function. **/ signals[CREDENTIALS_REQUIRED] = g_signal_new ( "credentials-required", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (ESourceClass, credentials_required), NULL, NULL, NULL, G_TYPE_NONE, 4, E_TYPE_SOURCE_CREDENTIALS_REASON, G_TYPE_STRING, G_TYPE_TLS_CERTIFICATE_FLAGS, G_TYPE_ERROR); /** * ESource::authenticate * @source: the #ESource that received the signal * @credentials: an #ENamedParameters with provided credentials * * Let's the backend know provided credentials to use to login * to (possibly remote) data store. **/ signals[AUTHENTICATE] = g_signal_new ( "authenticate", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (ESourceClass, authenticate), NULL, NULL, NULL, G_TYPE_NONE, 1, E_TYPE_NAMED_PARAMETERS); /* Register built-in ESourceExtension types. */ g_type_ensure (E_TYPE_SOURCE_ADDRESS_BOOK); g_type_ensure (E_TYPE_SOURCE_ALARMS); g_type_ensure (E_TYPE_SOURCE_AUTHENTICATION); g_type_ensure (E_TYPE_SOURCE_AUTOCOMPLETE); g_type_ensure (E_TYPE_SOURCE_CALENDAR); g_type_ensure (E_TYPE_SOURCE_COLLECTION); g_type_ensure (E_TYPE_SOURCE_CONTACTS); g_type_ensure (E_TYPE_SOURCE_GOA); g_type_ensure (E_TYPE_SOURCE_LDAP); g_type_ensure (E_TYPE_SOURCE_LOCAL); g_type_ensure (E_TYPE_SOURCE_MAIL_ACCOUNT); g_type_ensure (E_TYPE_SOURCE_MAIL_COMPOSITION); g_type_ensure (E_TYPE_SOURCE_MAIL_IDENTITY); g_type_ensure (E_TYPE_SOURCE_MAIL_SIGNATURE); g_type_ensure (E_TYPE_SOURCE_MAIL_SUBMISSION); g_type_ensure (E_TYPE_SOURCE_MAIL_TRANSPORT); g_type_ensure (E_TYPE_SOURCE_MDN); g_type_ensure (E_TYPE_SOURCE_MEMO_LIST); g_type_ensure (E_TYPE_SOURCE_OFFLINE); g_type_ensure (E_TYPE_SOURCE_OPENPGP); g_type_ensure (E_TYPE_SOURCE_PROXY); g_type_ensure (E_TYPE_SOURCE_REFRESH); g_type_ensure (E_TYPE_SOURCE_RESOURCE); g_type_ensure (E_TYPE_SOURCE_REVISION_GUARDS); g_type_ensure (E_TYPE_SOURCE_SECURITY); g_type_ensure (E_TYPE_SOURCE_SELECTABLE); g_type_ensure (E_TYPE_SOURCE_SMIME); g_type_ensure (E_TYPE_SOURCE_TASK_LIST); g_type_ensure (E_TYPE_SOURCE_UOA); g_type_ensure (E_TYPE_SOURCE_WEATHER); g_type_ensure (E_TYPE_SOURCE_WEBDAV); } static void e_source_initable_init (GInitableIface *iface) { iface->init = source_initable_init; } static void e_source_proxy_resolver_init (GProxyResolverInterface *iface) { iface->is_supported = source_proxy_resolver_is_supported; iface->lookup = source_proxy_resolver_lookup; iface->lookup_async = source_proxy_resolver_lookup_async; iface->lookup_finish = source_proxy_resolver_lookup_finish; } static void e_source_init (ESource *source) { GHashTable *extensions; /* Don't do this as part of class initialization because it * loads Camel modules and can screw up introspection, which * occurs at compile-time before Camel modules are installed. */ e_source_camel_register_types (); extensions = g_hash_table_new_full ( (GHashFunc) g_str_hash, (GEqualFunc) g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); source->priv = E_SOURCE_GET_PRIVATE (source); g_mutex_init (&source->priv->changed_lock); g_mutex_init (&source->priv->connection_status_change_lock); g_mutex_init (&source->priv->credentials_required_call_lock); g_mutex_init (&source->priv->property_lock); source->priv->key_file = g_key_file_new (); source->priv->extensions = extensions; source->priv->connection_status = E_SOURCE_CONNECTION_STATUS_DISCONNECTED; g_rec_mutex_init (&source->priv->lock); } void __e_source_private_replace_dbus_object (ESource *source, GDBusObject *dbus_object) { /* XXX This function is only ever called by ESourceRegistry * either when the registry service reported an ESource * removal, or while recovering from a registry service * restart. In the first case the GDBusObject is NULL, * in the second case the GDBusObject is an equivalent * proxy for the newly-started registry service. */ g_return_if_fail (E_IS_SOURCE (source)); if (dbus_object != NULL) { g_return_if_fail (E_DBUS_IS_OBJECT (dbus_object)); dbus_object = g_object_ref (dbus_object); } g_mutex_lock (&source->priv->property_lock); g_clear_object (&source->priv->dbus_object); source->priv->dbus_object = dbus_object; g_mutex_unlock (&source->priv->property_lock); g_object_notify (G_OBJECT (source), "dbus-object"); } /** * e_source_new: * @dbus_object: (allow-none): a #GDBusObject or %NULL * @main_context: (allow-none): a #GMainContext or %NULL * @error: return location for a #GError, or %NULL * * Creates a new #ESource instance. * * The #ESource::changed signal will be emitted from @main_context if given, * or else from the thread-default #GMainContext at the time this function is * called. * * The only time the function should be called outside of #ESourceRegistry * is to create a so-called "scratch" #ESource for editing in a Properties * window or an account setup assistant. * * FIXME: Elaborate on scratch sources. * * Returns: a new #ESource, or %NULL on error * * Since: 3.6 **/ ESource * e_source_new (GDBusObject *dbus_object, GMainContext *main_context, GError **error) { if (dbus_object != NULL) g_return_val_if_fail (E_DBUS_IS_OBJECT (dbus_object), NULL); return g_initable_new ( E_TYPE_SOURCE, NULL, error, "dbus-object", dbus_object, "main-context", main_context, NULL); } /** * e_source_new_with_uid: * @uid: a new unique identifier string * @main_context: (allow-none): a #GMainContext or %NULL * @error: return location for a #GError, or %NULL * * Creates a new "scratch" #ESource with a predetermined unique identifier. * * The #ESource::changed signal will be emitted from @main_context if given, * or else from the thread-default #GMainContext at the time this function is * called. * * Returns: a new scratch #ESource, or %NULL on error * * Since: 3.6 **/ ESource * e_source_new_with_uid (const gchar *uid, GMainContext *main_context, GError **error) { g_return_val_if_fail (uid != NULL, NULL); return g_initable_new ( E_TYPE_SOURCE, NULL, error, "main-context", main_context, "uid", uid, NULL); } /** * e_source_hash: * @source: an #ESource * * Generates a hash value for @source. This function is intended for * easily hashing an #ESource to add to a #GHashTable or similar data * structure. * * Returns: a hash value for @source. * * Since: 3.6 **/ guint e_source_hash (ESource *source) { const gchar *uid; g_return_val_if_fail (E_IS_SOURCE (source), 0); uid = e_source_get_uid (source); return g_str_hash (uid); } /** * e_source_equal: * @source1: the first #ESource * @source2: the second #ESource * * Checks two #ESource instances for equality. #ESource instances are * equal if their unique identifier strings are equal. * * Returns: %TRUE if @source1 and @source2 are equal * * Since: 3.6 **/ gboolean e_source_equal (ESource *source1, ESource *source2) { const gchar *uid1, *uid2; g_return_val_if_fail (E_IS_SOURCE (source1), FALSE); g_return_val_if_fail (E_IS_SOURCE (source2), FALSE); if (source1 == source2) return TRUE; uid1 = e_source_get_uid (source1); uid2 = e_source_get_uid (source2); return g_str_equal (uid1, uid2); } /** * e_source_changed: * @source: an #ESource * * Emits the #ESource::changed signal from an idle callback in * @source's #ESource:main-context. * * This function is primarily intended for use by #ESourceExtension * when emitting a #GObject::notify signal on one of its properties. * * Since: 3.6 **/ void e_source_changed (ESource *source) { g_return_if_fail (E_IS_SOURCE (source)); g_mutex_lock (&source->priv->changed_lock); if (!source->priv->ignore_changed_signal && source->priv->changed == NULL) { source->priv->changed = g_idle_source_new (); g_source_set_callback ( source->priv->changed, source_idle_changed_cb, source, (GDestroyNotify) NULL); g_source_attach ( source->priv->changed, source->priv->main_context); } g_mutex_unlock (&source->priv->changed_lock); } /** * e_source_get_uid: * @source: an #ESource * * Returns the unique identifier string for @source. * * Returns: the UID for @source * * Since: 3.6 **/ const gchar * e_source_get_uid (ESource *source) { g_return_val_if_fail (E_IS_SOURCE (source), NULL); return source->priv->uid; } /** * e_source_dup_uid: * @source: an #ESource * * Thread-safe variation of e_source_get_uid(). * Use this function when accessing @source from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: a newly-allocated copy of #ESource:uid * * Since: 3.6 **/ gchar * e_source_dup_uid (ESource *source) { const gchar *protected; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE (source), NULL); /* Perhaps we don't need to lock the mutex since * this is a read-only property but it can't hurt. */ g_mutex_lock (&source->priv->property_lock); protected = e_source_get_uid (source); duplicate = g_strdup (protected); g_mutex_unlock (&source->priv->property_lock); return duplicate; } /** * e_source_get_parent: * @source: an #ESource * * Returns the unique identifier string of the parent #ESource. * * Returns: the UID of the parent #ESource * * Since: 3.6 **/ const gchar * e_source_get_parent (ESource *source) { g_return_val_if_fail (E_IS_SOURCE (source), NULL); return source->priv->parent; } /** * e_source_dup_parent: * @source: an #ESource * * Thread-safe variation of e_source_get_parent(). * Use this function when accessing @source from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: a newly-allocated copy of #ESource:parent * * Since: 3.6 **/ gchar * e_source_dup_parent (ESource *source) { const gchar *protected; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE (source), NULL); g_mutex_lock (&source->priv->property_lock); protected = e_source_get_parent (source); duplicate = g_strdup (protected); g_mutex_unlock (&source->priv->property_lock); return duplicate; } /** * e_source_set_parent: * @source: an #ESource * @parent: (allow-none): the UID of the parent #ESource, or %NULL * * Identifies the parent of @source by its unique identifier string. * This can only be set prior to adding @source to an #ESourceRegistry. * * The internal copy of #ESource:parent is automatically stripped of leading * and trailing whitespace. If the resulting string is empty, %NULL is set * instead. * * Since: 3.6 **/ void e_source_set_parent (ESource *source, const gchar *parent) { g_return_if_fail (E_IS_SOURCE (source)); g_mutex_lock (&source->priv->property_lock); if (g_strcmp0 (source->priv->parent, parent) == 0) { g_mutex_unlock (&source->priv->property_lock); return; } g_free (source->priv->parent); source->priv->parent = e_util_strdup_strip (parent); g_mutex_unlock (&source->priv->property_lock); g_object_notify (G_OBJECT (source), "parent"); } /** * e_source_get_enabled: * @source: an #ESource * * Returns %TRUE if @source is enabled. * * An application should try to honor this setting if at all possible, * even if it does not provide a way to change the setting through its * user interface. Disabled data sources should generally be hidden. * * * This function does not take into account @source's ancestors in the * #ESource hierarchy, each of which have their own enabled state. If * any of @source's ancestors are disabled, then @source itself should * be treated as disabled. Use e_source_registry_check_enabled() to * easily check for this. * * * Returns: whether @source is enabled * * Since: 3.6 **/ gboolean e_source_get_enabled (ESource *source) { g_return_val_if_fail (E_IS_SOURCE (source), FALSE); return source->priv->enabled; } /** * e_source_set_enabled: * @source: an #ESource * @enabled: whether to enable @source * * Enables or disables @source. * * An application should try to honor this setting if at all possible, * even if it does not provide a way to change the setting through its * user interface. Disabled data sources should generally be hidden. * * Since: 3.6 **/ void e_source_set_enabled (ESource *source, gboolean enabled) { g_return_if_fail (E_IS_SOURCE (source)); if (source->priv->enabled == enabled) return; source->priv->enabled = enabled; g_object_notify (G_OBJECT (source), "enabled"); } /** * e_source_get_writable: * @source: an #ESource * * Returns whether the D-Bus service will accept changes to @source. * If @source is not writable, calls to e_source_write() will fail. * * Returns: whether @source is writable * * Since: 3.6 **/ gboolean e_source_get_writable (ESource *source) { GDBusObject *dbus_object; gboolean writable = FALSE; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { EDBusSourceWritable *dbus_interface; dbus_interface = e_dbus_object_peek_source_writable ( E_DBUS_OBJECT (dbus_object)); writable = (dbus_interface != NULL); g_object_unref (dbus_object); } return writable; } /** * e_source_get_removable: * @source: an #ESource * * Returns whether the D-Bus service will allow @source to be removed. * If @source is not writable, calls to e_source_remove() will fail. * * Returns: whether @source is removable * * Since: 3.6 **/ gboolean e_source_get_removable (ESource *source) { GDBusObject *dbus_object; gboolean removable = FALSE; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { EDBusSourceRemovable *dbus_interface; dbus_interface = e_dbus_object_peek_source_removable ( E_DBUS_OBJECT (dbus_object)); removable = (dbus_interface != NULL); g_object_unref (dbus_object); } return removable; } /** * e_source_get_remote_creatable: * @source: an #ESource * * Returns whether new resources can be created on a remote server by * calling e_source_remote_create() on @source. * * Generally this is only %TRUE if @source has an #ESourceCollection * extension, which means there is an #ECollectionBackend in the D-Bus * service that can handle create requests. If @source does not have * this capability, calls to e_source_remote_create() will fail. * * Returns: whether @source can create remote resources * * Since: 3.6 **/ gboolean e_source_get_remote_creatable (ESource *source) { GDBusObject *dbus_object; gboolean remote_creatable = FALSE; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { EDBusSourceRemoteCreatable *dbus_interface; dbus_interface = e_dbus_object_peek_source_remote_creatable ( E_DBUS_OBJECT (dbus_object)); remote_creatable = (dbus_interface != NULL); g_object_unref (dbus_object); } return remote_creatable; } /** * e_source_get_remote_deletable: * @source: an #ESource * * Returns whether the resource represented by @source can be deleted * from a remote server by calling e_source_remote_delete(). * * Generally this is only %TRUE if @source is a child of an #ESource * which has an #ESourceCollection extension, which means there is an * #ECollectionBackend in the D-Bus service that can handle delete * requests. If @source does not have this capability, calls to * e_source_remote_delete() will fail. * * Returns: whether @source can delete remote resources * * Since: 3.6 **/ gboolean e_source_get_remote_deletable (ESource *source) { GDBusObject *dbus_object; gboolean remote_deletable = FALSE; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { EDBusSourceRemoteDeletable *dbus_interface; dbus_interface = e_dbus_object_peek_source_remote_deletable ( E_DBUS_OBJECT (dbus_object)); remote_deletable = (dbus_interface != NULL); g_object_unref (dbus_object); } return remote_deletable; } /** * e_source_get_extension: * @source: an #ESource * @extension_name: an extension name * * Returns an instance of some #ESourceExtension subclass which registered * itself under @extension_name. If no such instance exists within @source, * one will be created. It is the caller's responsibility to know which * subclass is being returned. * * If you just want to test for the existence of an extension within @source * without creating it, use e_source_has_extension(). * * Extension instances are owned by their #ESource and should not be * referenced directly. Instead, reference the #ESource instance and * use this function to fetch the extension instance as needed. * * Returns: (type ESourceExtension) (transfer none): an instance of some * #ESourceExtension subclass * * Since: 3.6 **/ gpointer e_source_get_extension (ESource *source, const gchar *extension_name) { ESourceExtension *extension; GHashTable *hash_table; GTypeClass *class; g_return_val_if_fail (E_IS_SOURCE (source), NULL); g_return_val_if_fail (extension_name != NULL, NULL); g_rec_mutex_lock (&source->priv->lock); /* Check if we already have the extension. */ extension = g_hash_table_lookup ( source->priv->extensions, extension_name); if (extension != NULL) goto exit; /* Find all subclasses of ESourceExtensionClass. */ hash_table = source_find_extension_classes (); class = g_hash_table_lookup (hash_table, extension_name); /* Create a new instance of the appropriate GType. */ if (class != NULL) { g_mutex_lock (&source->priv->changed_lock); source->priv->ignore_changed_signal++; g_mutex_unlock (&source->priv->changed_lock); extension = g_object_new ( G_TYPE_FROM_CLASS (class), "source", source, NULL); source_load_from_key_file ( G_OBJECT (extension), source->priv->key_file, extension_name); g_hash_table_insert ( source->priv->extensions, g_strdup (extension_name), extension); g_mutex_lock (&source->priv->changed_lock); source->priv->ignore_changed_signal--; g_mutex_unlock (&source->priv->changed_lock); } else { /* XXX Tie this into a debug setting for ESources. */ #ifdef DEBUG g_critical ( "No registered GType for ESource " "extension '%s'", extension_name); #endif } g_hash_table_destroy (hash_table); exit: g_rec_mutex_unlock (&source->priv->lock); return extension; } /** * e_source_has_extension: * @source: an #ESource * @extension_name: an extension name * * Checks whether @source has an #ESourceExtension with the given name. * * Returns: %TRUE if @source has such an extension, %FALSE if not * * Since: 3.6 **/ gboolean e_source_has_extension (ESource *source, const gchar *extension_name) { ESourceExtension *extension; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (extension_name != NULL, FALSE); g_rec_mutex_lock (&source->priv->lock); /* Two cases to check for, either one is good enough: * 1) Our internal GKeyFile has a group named 'extension_name'. * 2) Our 'extensions' table has an entry for 'extension_name'. * * We have to check both data structures in case a new extension * not present in the GKeyFile was instantiated, but we have not * yet updated our internal GKeyFile. A common occurrence when * editing a brand new data source. * * When checking the GKeyFile we want to actually fetch the * extension with e_source_get_extension() to make sure it's * a registered extension name and not just an arbitrary key * file group name. */ if (g_key_file_has_group (source->priv->key_file, extension_name)) { extension = e_source_get_extension (source, extension_name); } else { GHashTable *hash_table = source->priv->extensions; extension = g_hash_table_lookup (hash_table, extension_name); } g_rec_mutex_unlock (&source->priv->lock); return (extension != NULL); } /** * e_source_ref_dbus_object: * @source: an #ESource * * Returns the #GDBusObject that was passed to e_source_new(). * * The returned #GDBusObject is referenced for thread-safety and must be * unreferenced with g_object_unref() when finished with it. * * Returns: (transfer full): the #GDBusObject for @source, or %NULL * * Since: 3.6 **/ GDBusObject * e_source_ref_dbus_object (ESource *source) { GDBusObject *dbus_object = NULL; g_return_val_if_fail (E_IS_SOURCE (source), NULL); g_mutex_lock (&source->priv->property_lock); if (source->priv->dbus_object != NULL) dbus_object = g_object_ref (source->priv->dbus_object); g_mutex_unlock (&source->priv->property_lock); return dbus_object; } /** * e_source_ref_main_context: * @source: an #ESource * * Returns the #GMainContext on which event sources for @source are to * be attached. * * The returned #GMainContext is referenced for thread-safety and must be * unreferenced with g_main_context_unref() when finished with it. * * Returns: (transfer full): a #GMainContext * * Since: 3.6 **/ GMainContext * e_source_ref_main_context (ESource *source) { g_return_val_if_fail (E_IS_SOURCE (source), NULL); return g_main_context_ref (source->priv->main_context); } /** * e_source_get_display_name: * @source: an #ESource * * Returns the display name for @source. Use the display name to * represent the #ESource in a user interface. * * Returns: the display name for @source * * Since: 3.6 **/ const gchar * e_source_get_display_name (ESource *source) { g_return_val_if_fail (E_IS_SOURCE (source), NULL); return source->priv->display_name; } /** * e_source_dup_display_name: * @source: an #ESource * * Thread-safe variation of e_source_get_display_name(). * Use this function when accessing @source from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: a newly-allocated copy of #ESource:display-name * * Since: 3.6 **/ gchar * e_source_dup_display_name (ESource *source) { const gchar *protected; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE (source), NULL); g_mutex_lock (&source->priv->property_lock); protected = e_source_get_display_name (source); duplicate = g_strdup (protected); g_mutex_unlock (&source->priv->property_lock); return duplicate; } /** * e_source_set_display_name: * @source: an #ESource * @display_name: a display name * * Sets the display name for @source. The @display_name argument must be a * valid UTF-8 string. Use the display name to represent the #ESource in a * user interface. * * The internal copy of @display_name is automatically stripped of leading * and trailing whitespace. * * Since: 3.6 **/ void e_source_set_display_name (ESource *source, const gchar *display_name) { g_return_if_fail (E_IS_SOURCE (source)); g_return_if_fail (display_name != NULL); g_return_if_fail (g_utf8_validate (display_name, -1, NULL)); g_mutex_lock (&source->priv->property_lock); if (g_strcmp0 (source->priv->display_name, display_name) == 0) { g_mutex_unlock (&source->priv->property_lock); return; } g_free (source->priv->display_name); source->priv->display_name = g_strdup (display_name); /* Strip leading and trailing whitespace. */ g_strstrip (source->priv->display_name); /* This is used in e_source_compare_by_display_name(). */ g_free (source->priv->collate_key); source->priv->collate_key = g_utf8_collate_key (display_name, -1); g_mutex_unlock (&source->priv->property_lock); g_object_notify (G_OBJECT (source), "display-name"); } /** * e_source_dup_secret_label: * @source: an #ESource * * Creates a label string based on @source's #ESource:display-name for use * with #SecretItem. * * Returns: a newly-allocated secret label * * Since: 3.12 **/ gchar * e_source_dup_secret_label (ESource *source) { gchar *display_name; gchar *backend_name = NULL; const gchar *type = NULL; const gchar *parent; GString *secret_label; g_return_val_if_fail (E_IS_SOURCE (source), NULL); display_name = e_source_dup_display_name (source); if (display_name == NULL || *display_name == '\0') { g_free (display_name); display_name = e_source_dup_uid (source); } #define update_backend_name(ext) G_STMT_START { \ ESourceBackend *backend_extension; \ backend_extension = e_source_get_extension (source, ext); \ g_free (backend_name); \ backend_name = e_source_backend_dup_backend_name (backend_extension); \ } G_STMT_END if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) { type = "Addressbook"; update_backend_name (E_SOURCE_EXTENSION_ADDRESS_BOOK); } if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) { if (!type) { type = "Calendar"; update_backend_name (E_SOURCE_EXTENSION_CALENDAR); } else type = ""; } if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) { if (!type) { type = "Mail Account"; update_backend_name (E_SOURCE_EXTENSION_MAIL_ACCOUNT); } else type = ""; } if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) { if (!type) { type = "Mail Transport"; update_backend_name (E_SOURCE_EXTENSION_MAIL_TRANSPORT); } else type = ""; } if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) { if (!type) { type = "Memo List"; update_backend_name (E_SOURCE_EXTENSION_MEMO_LIST); } else type = ""; } if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) { if (!type) { type = "Task List"; update_backend_name (E_SOURCE_EXTENSION_TASK_LIST); } else type = ""; } if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) { if (!type) { type = "Collection"; update_backend_name (E_SOURCE_EXTENSION_COLLECTION); } else type = ""; } if (!type || !*type) { g_free (backend_name); backend_name = NULL; type = NULL; } if (backend_name && !*backend_name) { g_free (backend_name); backend_name = NULL; } secret_label = g_string_new (NULL); if (type && backend_name) g_string_append_printf (secret_label, "Evolution Data Source \"%s\" (%s - %s) ", display_name, type, backend_name); else if (type) g_string_append_printf (secret_label, "Evolution Data Source \"%s\" (%s)", display_name, type); else g_string_append_printf (secret_label, "Evolution Data Source \"%s\"", display_name); g_free (backend_name); g_free (display_name); parent = e_source_get_parent (source); if (parent && *parent) g_string_append_printf (secret_label, " of %s", parent); return g_string_free (secret_label, FALSE); } /** * e_source_compare_by_display_name: * @source1: the first #ESource * @source2: the second #ESource * * Compares two #ESource instances by their display names. Useful for * ordering sources in a user interface. * * Returns: a negative value if @source1 compares before @source2, zero if * they compare equal, or a positive value if @source1 compares * after @source2 * * Since: 3.6 **/ gint e_source_compare_by_display_name (ESource *source1, ESource *source2) { gint res; res = g_strcmp0 ( source1->priv->collate_key, source2->priv->collate_key); if (res == 0) res = g_strcmp0 (source1->priv->uid, source2->priv->uid); return res; } /** * e_source_to_string: * @source: an #ESource * @length: (allow-none): return location for the length of the returned * string, or %NULL * * Outputs the current contents of @source as a key file string. * Free the returned string with g_free(). * * Returns: a newly-allocated string * * Since: 3.6 **/ gchar * e_source_to_string (ESource *source, gsize *length) { GHashTableIter iter; GKeyFile *key_file; gpointer group_name; gpointer extension; gchar *data; g_return_val_if_fail (E_IS_SOURCE (source), NULL); g_rec_mutex_lock (&source->priv->lock); key_file = source->priv->key_file; source_save_to_key_file ( G_OBJECT (source), key_file, PRIMARY_GROUP_NAME); g_hash_table_iter_init (&iter, source->priv->extensions); while (g_hash_table_iter_next (&iter, &group_name, &extension)) source_save_to_key_file (extension, key_file, group_name); data = g_key_file_to_data (key_file, length, NULL); g_rec_mutex_unlock (&source->priv->lock); return data; } /** * e_source_parameter_to_key: * @param_name: a #GParamSpec name * * Converts a #GParamSpec name (e.g. "foo-bar" or "foo_bar") * to "CamelCase" for use as a #GKeyFile key (e.g. "FooBar"). * * This function is made public only to aid in account migration. * Applications should not need to use this. * * Since: 3.6 **/ gchar * e_source_parameter_to_key (const gchar *param_name) { gboolean uppercase = TRUE; gchar *key, *cp; gint ii; g_return_val_if_fail (param_name != NULL, NULL); key = cp = g_malloc0 (strlen (param_name) + 1); for (ii = 0; param_name[ii] != '\0'; ii++) { if (g_ascii_isalnum (param_name[ii]) && uppercase) { *cp++ = g_ascii_toupper (param_name[ii]); uppercase = FALSE; } else if (param_name[ii] == '-' || param_name[ii] == '_') uppercase = TRUE; else *cp++ = param_name[ii]; } return key; } /** * e_source_get_connection_status: * @source: an #ESource * * Obtain current connection status of the @source. * * Returns: Current connection status of the @source. * * Since: 3.16 **/ ESourceConnectionStatus e_source_get_connection_status (ESource *source) { g_return_val_if_fail (E_IS_SOURCE (source), E_SOURCE_CONNECTION_STATUS_DISCONNECTED); return source->priv->connection_status; } /** * e_source_set_connection_status: * @source: an #ESource * @connection_status: one of the #ESourceConnectionStatus * * Set's current connection status of the @source. * * Since: 3.16 **/ void e_source_set_connection_status (ESource *source, ESourceConnectionStatus connection_status) { GEnumClass *enum_class; GEnumValue *enum_value; g_return_if_fail (E_IS_SOURCE (source)); if (source->priv->connection_status == connection_status) return; source->priv->connection_status = connection_status; enum_class = g_type_class_ref (E_TYPE_SOURCE_CONNECTION_STATUS); enum_value = g_enum_get_value (enum_class, connection_status); if (enum_value) { GDBusObject *dbus_object; EDBusSource *dbus_source; dbus_object = e_source_ref_dbus_object (E_SOURCE (source)); if (dbus_object) { dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object)); if (dbus_source) { e_dbus_source_set_connection_status (dbus_source, enum_value->value_nick); g_object_unref (dbus_source); } g_object_unref (dbus_object); } } else { g_warning ("%s: Unknown connection status: %x", G_STRFUNC, connection_status); } g_type_class_unref (enum_class); g_object_notify (G_OBJECT (source), "connection-status"); } /** * e_source_remove_sync: * @source: the #ESource to be removed * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @error: return location for a #GError, or %NULL * * Requests the D-Bus service to delete the key files for @source and all of * its descendants and broadcast their removal to all clients. The @source * must be #ESource:removable. * * If an error occurs, the functon will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.6 **/ gboolean e_source_remove_sync (ESource *source, GCancellable *cancellable, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (class->remove_sync != NULL, FALSE); return class->remove_sync (source, cancellable, error); } /** * e_source_remove: * @source: the #ESource to be removed * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: (scope async): a #GAsyncReadyCallback to call when the request * is satisfied * @user_data: (closure): data to pass to the callback function * * Asynchronously requests the D-Bus service to delete the key files for * @source and all of its descendants and broadcast their removal to all * clients. The @source must be #ESource:removable. * * When the operation is finished, @callback will be called. You can then * call e_source_remove_finish() to get the result of the operation. * * Since: 3.6 **/ void e_source_remove (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ESourceClass *class; g_return_if_fail (E_IS_SOURCE (source)); class = E_SOURCE_GET_CLASS (source); g_return_if_fail (class->remove != NULL); class->remove (source, cancellable, callback, user_data); } /** * e_source_remove_finish: * @source: the #ESource to be removed * @result: a #GAsyncResult * @error: return location for a #GError, or %NULL * * Finishes the operation started with e_source_remove(). If an * error occurred, the function will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE of failure * * Since: 3.6 **/ gboolean e_source_remove_finish (ESource *source, GAsyncResult *result, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (class->remove_finish != NULL, FALSE); return class->remove_finish (source, result, error); } /** * e_source_write_sync: * @source: a writable #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @error: return location for a #GError, or %NULL * * Submits the current contents of @source to the D-Bus service to be * written to disk and broadcast to other clients. The @source must * be #ESource:writable. * * If an error occurs, the functon will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.6 **/ gboolean e_source_write_sync (ESource *source, GCancellable *cancellable, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (class->write_sync != NULL, FALSE); return class->write_sync (source, cancellable, error); } /** * e_source_write: * @source: a writable #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: (scope async): a #GAsyncReadyCallback to call when the request * is satisfied * @user_data: (closure): data to pass to the callback function * * Asynchronously submits the current contents of @source to the D-Bus * service to be written to disk and broadcast to other clients. The * @source must be #ESource:writable. * * When the operation is finished, @callback will be called. You can then * call e_source_write_finish() to get the result of the operation. * * Since: 3.6 **/ void e_source_write (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ESourceClass *class; g_return_if_fail (E_IS_SOURCE (source)); class = E_SOURCE_GET_CLASS (source); g_return_if_fail (class->write != NULL); class->write (source, cancellable, callback, user_data); } /** * e_source_write_finish: * @source: a writable #ESource * @result: a #GAsyncResult * @error: return location for a #GError, or %NULL * * Finishes the operation started with e_source_write(). If an * error occurred, the function will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.6 **/ gboolean e_source_write_finish (ESource *source, GAsyncResult *result, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (class->write_finish != NULL, FALSE); return class->write_finish (source, result, error); } /** * e_source_remote_create_sync: * @source: an #ESource * @scratch_source: an #ESource describing the resource to create * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @error: return location for a #GError, or %NULL * * Creates a new remote resource by picking out relevant details from * @scratch_source. The @scratch_source must be an #ESource with no * #GDBusObject. The @source must be #ESource:remote-creatable. * * The details required to create the resource vary by #ECollectionBackend, * but in most cases the @scratch_source need only define the resource type * (address book, calendar, etc.), a display name for the resource, and * possibly a server-side path or ID for the resource. * * If an error occurs, the function will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.6 **/ gboolean e_source_remote_create_sync (ESource *source, ESource *scratch_source, GCancellable *cancellable, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (E_IS_SOURCE (scratch_source), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (class->remote_create_sync != NULL, FALSE); return class->remote_create_sync ( source, scratch_source, cancellable, error); } /** * e_source_remote_create: * @source: an #ESource * @scratch_source: an #ESource describing the resource to create * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: (scope async): a #GAsyncReadyCallback to call when the request * is satisfied * @user_data: (closure): data to pass to the callback function * * Asynchronously creates a new remote resource by picking out relevant * details from @scratch_source. The @scratch_source must be an #ESource * with no #GDBusObject. The @source must be #ESource:remote-creatable. * * The details required to create the resource vary by #ECollectionBackend, * but in most cases the @scratch_source need only define the resource type * (address book, calendar, etc.), a display name for the resource, and * possibly a server-side path or ID for the resource. * * When the operation is finished, @callback will be called. You can then * call e_source_remote_create_finish() to get the result of the operation. * * Since: 3.6 **/ void e_source_remote_create (ESource *source, ESource *scratch_source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ESourceClass *class; g_return_if_fail (E_IS_SOURCE (source)); g_return_if_fail (E_IS_SOURCE (scratch_source)); class = E_SOURCE_GET_CLASS (source); g_return_if_fail (class->remote_create != NULL); class->remote_create ( source, scratch_source, cancellable, callback, user_data); } /** * e_source_remote_create_finish: * @source: an #ESource * @result: a #GAsyncResult * @error: return location for a #GError, or %NULL * * Finishes the operation started with e_source_remote_create(). If * an error occurred, the function will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.6 **/ gboolean e_source_remote_create_finish (ESource *source, GAsyncResult *result, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (class->remote_create_finish != NULL, FALSE); return class->remote_create_finish (source, result, error); } /** * e_source_remote_delete_sync: * @source: an #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @error: return location for a #GError, or %NULL * * Deletes the resource represented by @source from a remote server. * The @source must be #ESource:remote-deletable. This will also delete * the key file for @source and broadcast its removal to all clients, * similar to e_source_remove_sync(). * * If an error occurs, the function will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.6 **/ gboolean e_source_remote_delete_sync (ESource *source, GCancellable *cancellable, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (class->remote_delete_sync != NULL, FALSE); return class->remote_delete_sync (source, cancellable, error); } /** * e_source_remote_delete: * @source: an #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: (scope async): a #GAsyncReadyCallback to call when the request * is satisfied * @user_data: (closure): data to pass to the callback function * * Asynchronously deletes the resource represented by @source from a remote * server. The @source must be #ESource:remote-deletable. This will also * delete the key file for @source and broadcast its removal to all clients, * similar to e_source_remove(). * * When the operation is finished, @callback will be called. You can then * call e_source_remote_delete_finish() to get the result of the operation. * * Since: 3.6 **/ void e_source_remote_delete (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ESourceClass *class; g_return_if_fail (E_IS_SOURCE (source)); class = E_SOURCE_GET_CLASS (source); g_return_if_fail (class->remote_delete != NULL); class->remote_delete (source, cancellable, callback, user_data); } /** * e_source_remote_delete_finish: * @source: an #ESource * @result: a #GAsyncResult * @error: return location for a #GError, or %NULL * * Finishes the operation started with e_source_remote_delete(). If * an error occurred, the function will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.6 **/ gboolean e_source_remote_delete_finish (ESource *source, GAsyncResult *result, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (class->remote_delete_finish != NULL, FALSE); return class->remote_delete_finish (source, result, error); } /** * e_source_get_oauth2_access_token_sync: * @source: an #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @out_access_token: (allow-none) (out): return location for the access token, * or %NULL * @out_expires_in: (allow-none) (out): return location for the token expiry, * or %NULL * @error: return location for a #GError, or %NULL * * Obtains the OAuth 2.0 access token for @source along with its expiry * in seconds from the current time (or 0 if unknown). * * Free the returned access token with g_free() when finished with it. * If an error occurs, the function will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.8 **/ gboolean e_source_get_oauth2_access_token_sync (ESource *source, GCancellable *cancellable, gchar **out_access_token, gint *out_expires_in, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail ( class->get_oauth2_access_token_sync != NULL, FALSE); return class->get_oauth2_access_token_sync ( source, cancellable, out_access_token, out_expires_in, error); } /** * e_source_get_oauth2_access_token: * @source: an #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: (scope async): a #GAsyncReadyCallback to call when the request * is satisfied * @user_data: (closure): data to pass to the callback function * * Asynchronously obtains the OAuth 2.0 access token for @source along * with its expiry in seconds from the current time (or 0 if unknown). * * When the operation is finished, @callback will be called. You can then * call e_source_get_oauth2_access_token_finish() to get the result of the * operation. * * Since: 3.8 **/ void e_source_get_oauth2_access_token (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ESourceClass *class; g_return_if_fail (E_IS_SOURCE (source)); class = E_SOURCE_GET_CLASS (source); g_return_if_fail (class->get_oauth2_access_token != NULL); return class->get_oauth2_access_token ( source, cancellable, callback, user_data); } /** * e_source_get_oauth2_access_token_finish: * @source: an #ESource * @result: a #GAsyncResult * @out_access_token: (allow-none) (out): return location for the access token, * or %NULL * @out_expires_in: (allow-none) (out): return location for the token expiry, * or %NULL * @error: return location for a #GError, or %NULL * * Finishes the operation started with e_source_get_oauth2_access_token(). * * Free the returned access token with g_free() when finished with it. * If an error occurred, the function will set @error and return %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.8 **/ gboolean e_source_get_oauth2_access_token_finish (ESource *source, GAsyncResult *result, gchar **out_access_token, gint *out_expires_in, GError **error) { ESourceClass *class; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); class = E_SOURCE_GET_CLASS (source); g_return_val_if_fail ( class->get_oauth2_access_token_finish != NULL, FALSE); return class->get_oauth2_access_token_finish ( source, result, out_access_token, out_expires_in, error); } /** * e_source_store_password_sync: * @source: an #ESource * @password: the password to store * @permanently: store permanently or just for the session * @cancellable: optional #GCancellable object, or %NULL * @error: return location for a #GError, or %NULL * * Stores a password for @source. This operation does not rely on the * registry service and therefore works for any #ESource -- registered * or "scratch". * * If @permanently is %TRUE, the password is stored in the default keyring. * Otherwise the password is stored in the memory-only session keyring. If * an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.12 **/ gboolean e_source_store_password_sync (ESource *source, const gchar *password, gboolean permanently, GCancellable *cancellable, GError **error) { gboolean success; const gchar *uid; gchar *label; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (password != NULL, FALSE); uid = e_source_get_uid (source); label = e_source_dup_secret_label (source); success = e_secret_store_store_sync (uid, password, label, permanently, cancellable, error); g_free (label); return success; } /* Helper for e_source_store_password() */ static void source_store_password_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { gboolean success; AsyncContext *async_context; GError *local_error = NULL; async_context = (AsyncContext *) task_data; success = e_source_store_password_sync ( E_SOURCE (source_object), async_context->password, async_context->permanently, cancellable, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_boolean (task, success); } } /** * e_source_store_password: * @source: an #ESource * @password: the password to store * @permanently: store permanently or just for the session * @cancellable: optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously stores a password for @source. This operation does * not rely on the registry service and therefore works for any #ESource * -- registered or "scratch". * * If @permanently is %TRUE, the password is stored in the default keyring. * Otherwise the password is stored in the memory-only session keyring. If * an error occurs, the function sets @error and returns %FALSE. * * When the operation is finished, @callback will be called. You can then * call e_source_store_password_finish() to get the result of the operation. * * Since: 3.12 **/ void e_source_store_password (ESource *source, const gchar *password, gboolean permanently, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; AsyncContext *async_context; g_return_if_fail (E_IS_SOURCE (source)); g_return_if_fail (password != NULL); async_context = g_slice_new0 (AsyncContext); async_context->password = g_strdup (password); async_context->permanently = permanently; task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, e_source_store_password); g_task_set_task_data ( task, async_context, (GDestroyNotify) async_context_free); g_task_run_in_thread (task, source_store_password_thread); g_object_unref (task); } /** * e_source_store_password_finish: * @source: an #ESource * @result: a #GAsyncResult * @error: return location for a #GError, or %NULL * * Finishes the operation started with e_source_store_password(). * * Returns: %TRUE on success, %FALSE on error * * Since: 3.12 **/ gboolean e_source_store_password_finish (ESource *source, GAsyncResult *result, GError **error) { g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (g_task_is_valid (result, source), FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, e_source_store_password), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } /** * e_source_lookup_password_sync: * @source: an #ESource * @cancellable: optional #GCancellable object, or %NULL * @out_password: (out): return location for the password, or %NULL * @error: return location for a #GError, or %NULL * * Looks up a password for @source. Both the default and session keyrings * are queried. This operation does not rely on the registry service and * therefore works for any #ESource -- registered or "scratch". * * Note the boolean return value indicates whether the lookup operation * itself completed successfully, not whether a password was found. If * no password was found, the function will set @out_password to %NULL * but still return %TRUE. If an error occurs, the function sets @error * and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.12 **/ gboolean e_source_lookup_password_sync (ESource *source, GCancellable *cancellable, gchar **out_password, GError **error) { const gchar *uid; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); uid = e_source_get_uid (source); return e_secret_store_lookup_sync (uid, out_password, cancellable, error); } /* Helper for e_source_lookup_password() */ static void source_lookup_password_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { gboolean success; AsyncContext *async_context; GError *local_error = NULL; async_context = (AsyncContext *) task_data; success = e_source_lookup_password_sync ( E_SOURCE (source_object), cancellable, &async_context->password, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_boolean (task, success); } } /** * e_source_lookup_password: * @source: an #ESource * @cancellable: optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously looks up a password for @source. Both the default and * session keyrings are queried. This operation does not rely on the * registry service and therefore works for any #ESource -- registered * or "scratch". * * When the operation is finished, @callback will be called. You can then * call e_source_lookup_password_finish() to get the result of the operation. * * Since: 3.12 **/ void e_source_lookup_password (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; AsyncContext *async_context; g_return_if_fail (E_IS_SOURCE (source)); async_context = g_slice_new0 (AsyncContext); task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, e_source_lookup_password); g_task_set_task_data ( task, async_context, (GDestroyNotify) async_context_free); g_task_run_in_thread (task, source_lookup_password_thread); g_object_unref (task); } /** * e_source_lookup_password_finish: * @source: an #ESource * @result: a #GAsyncResult * @out_password: (out): return location for the password, or %NULL * @error: return location for a #GError, or %NULL * * Finishes the operation started with e_source_lookup_password(). * * Note the boolean return value indicates whether the lookup operation * itself completed successfully, not whether a password was found. If * no password was found, the function will set @out_password to %NULL * but still return %TRUE. If an error occurs, the function sets @error * and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.12 **/ gboolean e_source_lookup_password_finish (ESource *source, GAsyncResult *result, gchar **out_password, GError **error) { AsyncContext *async_context; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (g_task_is_valid (result, source), FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, e_source_lookup_password), FALSE); async_context = g_task_get_task_data (G_TASK (result)); if (!g_task_had_error (G_TASK (result))) { if (out_password != NULL) { *out_password = async_context->password; async_context->password = NULL; } } return g_task_propagate_boolean (G_TASK (result), error); } /** * e_source_delete_password_sync: * @source: an #ESource * @cancellable: optional #GCancellable object, or %NULL * @error: return location for a #GError, or %NULL * * Deletes the password for @source from either the default keyring or * session keyring. This operation does not rely on the registry service * and therefore works for any #ESource -- registered or "scratch". * * Note the boolean return value indicates whether the delete operation * itself completed successfully, not whether a password was found and * deleted. If no password was found, the function will still return * %TRUE. If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.12 **/ gboolean e_source_delete_password_sync (ESource *source, GCancellable *cancellable, GError **error) { const gchar *uid; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); uid = e_source_get_uid (source); return e_secret_store_delete_sync (uid, cancellable, error); } /* Helper for e_source_delete_password() */ static void source_delete_password_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { gboolean success; GError *local_error = NULL; success = e_source_delete_password_sync ( E_SOURCE (source_object), cancellable, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_boolean (task, success); } } /** * e_source_delete_password: * @source: an #ESource * @cancellable: optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously deletes the password for @source from either the default * keyring or session keyring. This operation does not rely on the registry * service and therefore works for any #ESource -- registered or "scratch". * * When the operation is finished, @callback will be called. You can then * call e_source_delete_password_finish() to get the result of the operation. * * Since: 3.12 **/ void e_source_delete_password (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail (E_IS_SOURCE (source)); task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, e_source_delete_password); g_task_run_in_thread (task, source_delete_password_thread); g_object_unref (task); } /** * e_source_delete_password_finish: * @source: an #ESource * @result: a #GAsyncResult * @error: return location for a #GError, or %NULL * * Finishes the operation started with e_source_delete_password(). * * Note the boolean return value indicates whether the delete operation * itself completed successfully, not whether a password was found and * deleted. If no password was found, the function will still return * %TRUE. If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.12 **/ gboolean e_source_delete_password_finish (ESource *source, GAsyncResult *result, GError **error) { g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (g_task_is_valid (result, source), FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, e_source_delete_password), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } /** * e_source_invoke_credentials_required_sync: * @source: an #ESource * @reason: an #ESourceCredentialsReason, why the credentials are required * @certificate_pem: PEM-encoded secure connection certificate, or an empty string * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate * @op_error: (allow-none): a #GError with a description of the previous credentials error, or %NULL * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @error: (allow-none): return location for a #GError, or %NULL * * Let's the client-side know that credentials are required. The @reason defines which * parameters are used. The client passed the credentials with an e_source_authenitcate() * call. * * The %E_SOURCE_CREDENTIALS_REASON_REQUIRED is used for the first credentials prompt, * when the client can return credentials as stored from the previous success login. * * The %E_SOURCE_CREDENTIALS_REASON_REJECTED is used when the previously used credentials * had been rejected by the server. That usually means that the user should be asked * to provide/correct the credentials. * * The %E_SOURCE_CREDENTIALS_REASON_SSL_FAILED is used when a secured connection failed * due to some server-side certificate issues. * * The %E_SOURCE_CREDENTIALS_REASON_ERROR is used when the server returned an error. * It is not possible to connect to it at the moment usually. * * If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.16 **/ gboolean e_source_invoke_credentials_required_sync (ESource *source, ESourceCredentialsReason reason, const gchar *certificate_pem, GTlsCertificateFlags certificate_errors, const GError *op_error, GCancellable *cancellable, GError **error) { GDBusObject *dbus_object; EDBusSource *dbus_source = NULL; ESourceClass *klass; gchar *arg_reason, *arg_certificate_errors; GEnumClass *enum_class; GEnumValue *enum_value; GFlagsClass *flags_class; GFlagsValue *flags_value; GString *certificate_errors_str; gchar *dbus_error_name = NULL; GError *local_error = NULL; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); klass = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (klass->invoke_credentials_required_impl != NULL, FALSE); dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (!dbus_source) { g_warn_if_fail (dbus_source != NULL); return FALSE; } enum_class = g_type_class_ref (E_TYPE_SOURCE_CREDENTIALS_REASON); enum_value = g_enum_get_value (enum_class, reason); g_return_val_if_fail (enum_value != NULL, FALSE); arg_reason = g_strdup (enum_value->value_nick); g_type_class_unref (enum_class); certificate_errors_str = g_string_new (""); flags_class = g_type_class_ref (G_TYPE_TLS_CERTIFICATE_FLAGS); for (flags_value = g_flags_get_first_value (flags_class, certificate_errors); flags_value; flags_value = g_flags_get_first_value (flags_class, certificate_errors)) { if (certificate_errors_str->len) g_string_append_c (certificate_errors_str, ':'); g_string_append (certificate_errors_str, flags_value->value_nick); certificate_errors &= ~flags_value->value; } g_type_class_unref (flags_class); arg_certificate_errors = g_string_free (certificate_errors_str, FALSE); if (reason == E_SOURCE_CREDENTIALS_REASON_SSL_FAILED) e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_SSL_FAILED); else if (reason != E_SOURCE_CREDENTIALS_REASON_ERROR) e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS); if (op_error) dbus_error_name = g_dbus_error_encode_gerror (op_error); klass->invoke_credentials_required_impl (source, dbus_source, arg_reason ? arg_reason : "", certificate_pem ? certificate_pem : "", arg_certificate_errors ? arg_certificate_errors : "", dbus_error_name ? dbus_error_name : "", op_error ? op_error->message : "", cancellable, &local_error); g_free (arg_reason); g_free (arg_certificate_errors); g_free (dbus_error_name); g_object_unref (dbus_source); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return TRUE; } typedef struct _InvokeCredentialsRequiredData { ESourceCredentialsReason reason; gchar *certificate_pem; GTlsCertificateFlags certificate_errors; GError *op_error; } InvokeCredentialsRequiredData; static void invoke_credentials_required_data_free (gpointer ptr) { InvokeCredentialsRequiredData *data = ptr; if (data) { g_free (data->certificate_pem); g_clear_error (&data->op_error); g_free (data); } } static void source_invoke_credentials_required_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { InvokeCredentialsRequiredData *data = task_data; gboolean success; GError *local_error = NULL; success = e_source_invoke_credentials_required_sync ( E_SOURCE (source_object), data->reason, data->certificate_pem, data->certificate_errors, data->op_error, cancellable, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_boolean (task, success); } } /** * e_source_invoke_credentials_required: * @source: an #ESource * @reason: an #ESourceCredentialsReason, why the credentials are required * @certificate_pem: PEM-encoded secure connection certificate, or an empty string * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate * @op_error: (allow-none): a #GError with a description of the previous credentials error, or %NULL * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously calls the InvokeCredentialsRequired method on the server side, * to inform clients that credentials are required. * * When the operation is finished, @callback will be called. You can then * call e_source_invoke_credentials_required_finish() to get the result of the operation. * * Since: 3.16 **/ void e_source_invoke_credentials_required (ESource *source, ESourceCredentialsReason reason, const gchar *certificate_pem, GTlsCertificateFlags certificate_errors, const GError *op_error, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { InvokeCredentialsRequiredData *data; GTask *task; g_return_if_fail (E_IS_SOURCE (source)); data = g_new0 (InvokeCredentialsRequiredData, 1); data->reason = reason; data->certificate_pem = g_strdup (certificate_pem); data->certificate_errors = certificate_errors; data->op_error = op_error ? g_error_copy (op_error) : NULL; task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, e_source_invoke_credentials_required); g_task_set_task_data (task, data, invoke_credentials_required_data_free); g_task_run_in_thread (task, source_invoke_credentials_required_thread); g_object_unref (task); } /** * e_source_invoke_credentials_required_finish: * @source: an #ESource * @result: a #GAsyncResult * @error: (allow-none): return location for a #GError, or %NULL * * Finishes the operation started with e_source_invoke_credentials_required(). * * If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.16 **/ gboolean e_source_invoke_credentials_required_finish (ESource *source, GAsyncResult *result, GError **error) { g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (g_task_is_valid (result, source), FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, e_source_invoke_credentials_required), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } /** * e_source_invoke_authenticate_sync: * @source: an #ESource * @credentials: (allow-none): an #ENamedParameters structure with credentials to use; can be %NULL * to use those from the last call * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @error: (allow-none): return location for a #GError, or %NULL * * Calls the InvokeAuthenticate method on the server side, thus the backend * knows what credentials to use to connect to its (possibly remote) data store. * * If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.16 **/ gboolean e_source_invoke_authenticate_sync (ESource *source, const ENamedParameters *credentials, GCancellable *cancellable, GError **error) { GDBusObject *dbus_object; EDBusSource *dbus_source = NULL; ESourceClass *klass; gchar **credentials_strv; gboolean success; GError *local_error = NULL; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); klass = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (klass->invoke_authenticate_impl != NULL, FALSE); dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (!dbus_source) { g_warn_if_fail (dbus_source != NULL); return FALSE; } if (credentials) { if (e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND) && !e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_SSL_TRUST)) { ENamedParameters *clone; ESourceWebdav *webdav_extension; clone = e_named_parameters_new_clone (credentials); webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); e_named_parameters_set (clone, E_SOURCE_CREDENTIAL_SSL_TRUST, e_source_webdav_get_ssl_trust (webdav_extension)); credentials_strv = e_named_parameters_to_strv (clone); e_named_parameters_free (clone); } else { credentials_strv = e_named_parameters_to_strv (credentials); } } else { ENamedParameters *empty_credentials; empty_credentials = e_named_parameters_new (); credentials_strv = e_named_parameters_to_strv (empty_credentials); e_named_parameters_free (empty_credentials); } success = klass->invoke_authenticate_impl (source, dbus_source, (const gchar * const *) credentials_strv, cancellable, &local_error); g_strfreev (credentials_strv); g_object_unref (dbus_source); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return success; } static void source_invoke_authenticate_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { gboolean success; GError *local_error = NULL; success = e_source_invoke_authenticate_sync ( E_SOURCE (source_object), task_data, cancellable, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_boolean (task, success); } } /** * e_source_invoke_authenticate: * @source: an #ESource * @credentials: (allow-none): an #ENamedParameters structure with credentials to use; can be %NULL * to use those from the last call * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously calls the InvokeAuthenticate method on the server side, * thus the backend knows what credentials to use to connect to its (possibly * remote) data store. * * When the operation is finished, @callback will be called. You can then * call e_source_invoke_authenticate_finish() to get the result of the operation. * * Since: 3.16 **/ void e_source_invoke_authenticate (ESource *source, const ENamedParameters *credentials, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { ENamedParameters *credentials_copy; GTask *task; g_return_if_fail (E_IS_SOURCE (source)); credentials_copy = e_named_parameters_new_clone (credentials); task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, e_source_invoke_authenticate); g_task_set_task_data (task, credentials_copy, (GDestroyNotify) e_named_parameters_free); g_task_run_in_thread (task, source_invoke_authenticate_thread); g_object_unref (task); } /** * e_source_invoke_authenticate_finish: * @source: an #ESource * @result: a #GAsyncResult * @error: (allow-none): return location for a #GError, or %NULL * * Finishes the operation started with e_source_invoke_authenticate(). * * If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.16 **/ gboolean e_source_invoke_authenticate_finish (ESource *source, GAsyncResult *result, GError **error) { g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (g_task_is_valid (result, source), FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, e_source_invoke_authenticate), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } /** * e_source_emit_credentials_required: * @reason: an #ESourceCredentialsReason, why the credentials are required * @certificate_pem: PEM-encoded secure connection certificate, or an empty string * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate * @op_error: (allow-none): a #GError with a description of the previous credentials error, or %NULL * * Emits localy (in this process only) the ESource::credentials-required * signal with given parameters. That's the difference with e_source_invoke_credentials_required(), * which calls the signal globally, within each client. * * Since: 3.16 **/ void e_source_emit_credentials_required (ESource *source, ESourceCredentialsReason reason, const gchar *certificate_pem, GTlsCertificateFlags certificate_errors, const GError *op_error) { g_return_if_fail (E_IS_SOURCE (source)); g_signal_emit (source, signals[CREDENTIALS_REQUIRED], 0, reason, certificate_pem, certificate_errors, op_error); } /** * e_source_get_last_credentials_required_arguments_sync: * @source: an #ESource * @out_reason: (out): an #ESourceCredentialsReason, why the credentials are required * @out_certificate_pem: (out): PEM-encoded secure connection certificate, or an empty string * @out_certificate_errors: (out): a bit-or of #GTlsCertificateFlags for secure connection certificate * @out_op_error: (out): a #GError with a description of the previous credentials error * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @error: (allow-none): return location for a #GError, or %NULL * * Retrieves the last used arguments of the 'credentials-required' signal emission. * If there was none emitted yet, or a corresponding 'authenitcate' had been emitted * already, then the @out_reason is set to #E_SOURCE_CREDENTIALS_REASON_UNKNOWN * and the value of other 'out' arguments is set to no values. * * If an error occurs, the function sets @error and returns %FALSE. The result gchar * values should be freed with g_free() when no longer needed. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.16 **/ gboolean e_source_get_last_credentials_required_arguments_sync (ESource *source, ESourceCredentialsReason *out_reason, gchar **out_certificate_pem, GTlsCertificateFlags *out_certificate_errors, GError **out_op_error, GCancellable *cancellable, GError **error) { GDBusObject *dbus_object; EDBusSource *dbus_source = NULL; gboolean success; gchar *arg_reason = NULL, *arg_certificate_errors = NULL; gchar *arg_dbus_error_name = NULL, *arg_dbus_error_message = NULL; GError *local_error = NULL; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (out_reason != NULL, FALSE); g_return_val_if_fail (out_certificate_pem != NULL, FALSE); g_return_val_if_fail (out_certificate_errors != NULL, FALSE); g_return_val_if_fail (out_op_error != NULL, FALSE); *out_reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN; *out_certificate_pem = NULL; *out_certificate_errors = 0; *out_op_error = NULL; dbus_object = e_source_ref_dbus_object (source); if (dbus_object != NULL) { dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object)); g_object_unref (dbus_object); } if (!dbus_source) return FALSE; success = e_dbus_source_call_get_last_credentials_required_arguments_sync (dbus_source, &arg_reason, out_certificate_pem, &arg_certificate_errors, &arg_dbus_error_name, &arg_dbus_error_message, cancellable, &local_error); g_object_unref (dbus_source); *out_reason = source_credentials_reason_from_text (arg_reason); *out_certificate_errors = source_certificate_errors_from_text (arg_certificate_errors); if (arg_dbus_error_name && *arg_dbus_error_name && arg_dbus_error_message) { *out_op_error = g_dbus_error_new_for_dbus_error (arg_dbus_error_name, arg_dbus_error_message); g_dbus_error_strip_remote_error (*out_op_error); } if (*out_certificate_pem && !**out_certificate_pem) { g_free (*out_certificate_pem); *out_certificate_pem = NULL; } g_free (arg_reason); g_free (arg_certificate_errors); g_free (arg_dbus_error_name); g_free (arg_dbus_error_message); if (local_error != NULL) { g_dbus_error_strip_remote_error (local_error); g_propagate_error (error, local_error); return FALSE; } return success; } static void source_get_last_credentials_required_arguments_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { InvokeCredentialsRequiredData *data; GError *local_error = NULL; data = g_new0 (InvokeCredentialsRequiredData, 1); data->reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN; data->certificate_pem = NULL; data->certificate_errors = 0; data->op_error = NULL; e_source_get_last_credentials_required_arguments_sync ( E_SOURCE (source_object), &data->reason, &data->certificate_pem, &data->certificate_errors, &data->op_error, cancellable, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_pointer (task, data, invoke_credentials_required_data_free); } } /** * e_source_get_last_credentials_required_arguments: * @source: an #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously calls the GetLastCredentialsRequiredArguments method * on the server side, to get the last values used for the 'credentials-required' * signal. See e_source_get_last_credentials_required_arguments_sync() for * more information. * * When the operation is finished, @callback will be called. You can then * call e_source_get_last_credentials_required_arguments_finish() to get * the result of the operation. * * Since: 3.16 **/ void e_source_get_last_credentials_required_arguments (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail (E_IS_SOURCE (source)); task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, e_source_get_last_credentials_required_arguments); g_task_run_in_thread (task, source_get_last_credentials_required_arguments_thread); g_object_unref (task); } /** * e_source_get_last_credentials_required_arguments_finish: * @source: an #ESource * @result: a #GAsyncResult * @out_reason: (out): an #ESourceCredentialsReason, why the credentials are required * @out_certificate_pem: (out): PEM-encoded secure connection certificate, or an empty string * @out_certificate_errors: (out): a bit-or of #GTlsCertificateFlags for secure connection certificate * @out_op_error: (out): a #GError with a description of the previous credentials error * @error: (allow-none): return location for a #GError, or %NULL * * Finishes the operation started with e_source_get_last_credentials_required_arguments(). * See e_source_get_last_credentials_required_arguments_sync() for more information * about the output arguments. * * If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.16 **/ gboolean e_source_get_last_credentials_required_arguments_finish (ESource *source, GAsyncResult *result, ESourceCredentialsReason *out_reason, gchar **out_certificate_pem, GTlsCertificateFlags *out_certificate_errors, GError **out_op_error, GError **error) { InvokeCredentialsRequiredData *data; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (g_task_is_valid (result, source), FALSE); g_return_val_if_fail (out_reason != NULL, FALSE); g_return_val_if_fail (out_certificate_pem != NULL, FALSE); g_return_val_if_fail (out_certificate_errors != NULL, FALSE); g_return_val_if_fail (out_op_error != NULL, FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, e_source_get_last_credentials_required_arguments), FALSE); data = g_task_propagate_pointer (G_TASK (result), error); if (!data) return FALSE; *out_reason = data->reason; *out_certificate_pem = g_strdup (data->certificate_pem); *out_certificate_errors = data->certificate_errors; *out_op_error = data->op_error ? g_error_copy (data->op_error) : NULL; invoke_credentials_required_data_free (data); return TRUE; } /** * e_source_unset_last_credentials_required_arguments_sync: * @source: an #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @error: (allow-none): return location for a #GError, or %NULL * * Unsets the last used arguments of the 'credentials-required' signal emission. * * If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.18 **/ gboolean e_source_unset_last_credentials_required_arguments_sync (ESource *source, GCancellable *cancellable, GError **error) { ESourceClass *klass; g_return_val_if_fail (E_IS_SOURCE (source), FALSE); klass = E_SOURCE_GET_CLASS (source); g_return_val_if_fail (klass->unset_last_credentials_required_arguments_impl != NULL, FALSE); return klass->unset_last_credentials_required_arguments_impl (source, cancellable, error); } static void source_unset_last_credentials_required_arguments_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { GError *local_error = NULL; e_source_unset_last_credentials_required_arguments_sync ( E_SOURCE (source_object), cancellable, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_boolean (task, TRUE); } } /** * e_source_unset_last_credentials_required_arguments: * @source: an #ESource * @cancellable: (allow-none): optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously calls the UnsetLastCredentialsRequiredArguments method * on the server side, to unset the last values used for the 'credentials-required' * signal. * * When the operation is finished, @callback will be called. You can then * call e_source_unset_last_credentials_required_arguments_finish() to get * the result of the operation. * * Since: 3.18 **/ void e_source_unset_last_credentials_required_arguments (ESource *source, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail (E_IS_SOURCE (source)); task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, e_source_unset_last_credentials_required_arguments); g_task_run_in_thread (task, source_unset_last_credentials_required_arguments_thread); g_object_unref (task); } /** * e_source_unset_last_credentials_required_arguments_finish: * @source: an #ESource * @result: a #GAsyncResult * @error: (allow-none): return location for a #GError, or %NULL * * Finishes the operation started with e_source_unset_last_credentials_required_arguments(). * * If an error occurs, the function sets @error and returns %FALSE. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.18 **/ gboolean e_source_unset_last_credentials_required_arguments_finish (ESource *source, GAsyncResult *result, GError **error) { g_return_val_if_fail (E_IS_SOURCE (source), FALSE); g_return_val_if_fail (g_task_is_valid (result, source), FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, e_source_unset_last_credentials_required_arguments), FALSE); return g_task_propagate_boolean (G_TASK (result), error); }