/* * e-source-webdav.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-webdav * @include: libedataserver/libedataserver.h * @short_description: #ESource extension for WebDAV settings * * The #ESourceWebdav extension tracks settings for accessing resources * on a remote WebDAV server. * * This class exists in libedataserver because we have several * WebDAV-based backends. Each of these backends is free to use * this class directly or subclass it with additional settings. * Subclasses should override the extension name. * * The #SoupURI is parsed into components and distributed across * several other built-in extensions such as #ESourceAuthentication * and #ESourceSecurity. * * Access the extension as follows: * * |[ * #include * * ESourceWebdav *extension; * * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); * ]| **/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "e-source-webdav.h" #define E_SOURCE_WEBDAV_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_SOURCE_WEBDAV, ESourceWebdavPrivate)) struct _ESourceWebdavPrivate { gchar *display_name; gchar *email_address; gchar *resource_path; gchar *resource_query; gchar *ssl_trust; gboolean avoid_ifmatch; gboolean calendar_auto_schedule; SoupURI *soup_uri; }; enum { PROP_0, PROP_AVOID_IFMATCH, PROP_CALENDAR_AUTO_SCHEDULE, PROP_DISPLAY_NAME, PROP_EMAIL_ADDRESS, PROP_RESOURCE_PATH, PROP_RESOURCE_QUERY, PROP_SOUP_URI, PROP_SSL_TRUST }; G_DEFINE_TYPE ( ESourceWebdav, e_source_webdav, E_TYPE_SOURCE_EXTENSION) static void source_webdav_notify_cb (GObject *object, GParamSpec *pspec, ESourceWebdav *extension) { g_object_notify (G_OBJECT (extension), "soup-uri"); } static gboolean source_webdav_user_to_method (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { GObject *target_object; ESourceAuthentication *extension; const gchar *user; gchar *method; gboolean success = TRUE; target_object = g_binding_get_target (binding); extension = E_SOURCE_AUTHENTICATION (target_object); method = e_source_authentication_dup_method (extension); g_return_val_if_fail (method != NULL, FALSE); /* Be careful not to stomp on a custom method name. * Only change it under the following conditions: * * 1) If "user" is empty, set "method" to "none". * 2) If "user" is not empty and "method" is "none", * set "method" to "plain/password" (corresponds * to HTTP Basic authentication). * 3) Otherwise preserve the current "method" value. */ user = g_value_get_string (source_value); if (user == NULL || *user == '\0') { g_value_set_string (target_value, "none"); } else if (g_str_equal (method, "none")) { g_value_set_string (target_value, "plain/password"); } else { success = FALSE; } g_free (method); return success; } static void source_webdav_update_properties_from_soup_uri (ESourceWebdav *webdav_extension) { ESource *source; ESourceExtension *extension; SoupURI *soup_uri; const gchar *extension_name; /* Do not use e_source_webdav_dup_soup_uri() here. That * builds the URI from properties we haven't yet updated. */ e_source_extension_property_lock (E_SOURCE_EXTENSION (webdav_extension)); soup_uri = soup_uri_copy (webdav_extension->priv->soup_uri); e_source_extension_property_unlock (E_SOURCE_EXTENSION (webdav_extension)); extension = E_SOURCE_EXTENSION (webdav_extension); source = e_source_extension_ref_source (extension); g_object_set ( extension, "resource-path", soup_uri->path, "resource-query", soup_uri->query, NULL); extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; extension = e_source_get_extension (source, extension_name); g_object_set ( extension, "host", soup_uri->host, "port", soup_uri->port, NULL); if (soup_uri->user && *soup_uri->user) g_object_set ( extension, "user", soup_uri->user, NULL); extension_name = E_SOURCE_EXTENSION_SECURITY; extension = e_source_get_extension (source, extension_name); g_object_set ( extension, "secure", (soup_uri->scheme == SOUP_URI_SCHEME_HTTPS), NULL); g_object_unref (source); soup_uri_free (soup_uri); } static void source_webdav_update_soup_uri_from_properties (ESourceWebdav *webdav_extension) { ESource *source; ESourceExtension *extension; SoupURI *soup_uri; const gchar *extension_name; gchar *user; gchar *host; gchar *path; gchar *query; guint port; gboolean secure; extension = E_SOURCE_EXTENSION (webdav_extension); source = e_source_extension_ref_source (extension); g_object_get ( extension, "resource-path", &path, "resource-query", &query, NULL); extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; extension = e_source_get_extension (source, extension_name); g_object_get ( extension, "user", &user, "host", &host, "port", &port, NULL); extension_name = E_SOURCE_EXTENSION_SECURITY; extension = e_source_get_extension (source, extension_name); g_object_get ( extension, "secure", &secure, NULL); g_object_unref (source); e_source_extension_property_lock (E_SOURCE_EXTENSION (webdav_extension)); soup_uri = webdav_extension->priv->soup_uri; /* Try not to disturb the scheme, in case it's "webcal" or some * other non-standard value. But if we have to change it, do it. */ if (secure && soup_uri->scheme != SOUP_URI_SCHEME_HTTPS) soup_uri_set_scheme (soup_uri, SOUP_URI_SCHEME_HTTPS); if (!secure && soup_uri->scheme == SOUP_URI_SCHEME_HTTPS) soup_uri_set_scheme (soup_uri, SOUP_URI_SCHEME_HTTP); soup_uri_set_user (soup_uri, user); soup_uri_set_host (soup_uri, host); if (port > 0) soup_uri_set_port (soup_uri, port); /* SoupURI doesn't like NULL paths. */ soup_uri_set_path (soup_uri, (path != NULL) ? path : ""); soup_uri_set_query (soup_uri, query); e_source_extension_property_unlock (E_SOURCE_EXTENSION (webdav_extension)); g_free (user); g_free (host); g_free (path); g_free (query); } static void source_webdav_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_AVOID_IFMATCH: e_source_webdav_set_avoid_ifmatch ( E_SOURCE_WEBDAV (object), g_value_get_boolean (value)); return; case PROP_CALENDAR_AUTO_SCHEDULE: e_source_webdav_set_calendar_auto_schedule ( E_SOURCE_WEBDAV (object), g_value_get_boolean (value)); return; case PROP_DISPLAY_NAME: e_source_webdav_set_display_name ( E_SOURCE_WEBDAV (object), g_value_get_string (value)); return; case PROP_EMAIL_ADDRESS: e_source_webdav_set_email_address ( E_SOURCE_WEBDAV (object), g_value_get_string (value)); return; case PROP_RESOURCE_PATH: e_source_webdav_set_resource_path ( E_SOURCE_WEBDAV (object), g_value_get_string (value)); return; case PROP_RESOURCE_QUERY: e_source_webdav_set_resource_query ( E_SOURCE_WEBDAV (object), g_value_get_string (value)); return; case PROP_SOUP_URI: e_source_webdav_set_soup_uri ( E_SOURCE_WEBDAV (object), g_value_get_boxed (value)); return; case PROP_SSL_TRUST: e_source_webdav_set_ssl_trust ( E_SOURCE_WEBDAV (object), g_value_get_string (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void source_webdav_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_AVOID_IFMATCH: g_value_set_boolean ( value, e_source_webdav_get_avoid_ifmatch ( E_SOURCE_WEBDAV (object))); return; case PROP_CALENDAR_AUTO_SCHEDULE: g_value_set_boolean ( value, e_source_webdav_get_calendar_auto_schedule ( E_SOURCE_WEBDAV (object))); return; case PROP_DISPLAY_NAME: g_value_take_string ( value, e_source_webdav_dup_display_name ( E_SOURCE_WEBDAV (object))); return; case PROP_EMAIL_ADDRESS: g_value_take_string ( value, e_source_webdav_dup_email_address ( E_SOURCE_WEBDAV (object))); return; case PROP_RESOURCE_PATH: g_value_take_string ( value, e_source_webdav_dup_resource_path ( E_SOURCE_WEBDAV (object))); return; case PROP_RESOURCE_QUERY: g_value_take_string ( value, e_source_webdav_dup_resource_query ( E_SOURCE_WEBDAV (object))); return; case PROP_SOUP_URI: g_value_take_boxed ( value, e_source_webdav_dup_soup_uri ( E_SOURCE_WEBDAV (object))); return; case PROP_SSL_TRUST: g_value_take_string ( value, e_source_webdav_dup_ssl_trust ( E_SOURCE_WEBDAV (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void source_webdav_finalize (GObject *object) { ESourceWebdavPrivate *priv; priv = E_SOURCE_WEBDAV_GET_PRIVATE (object); g_free (priv->display_name); g_free (priv->email_address); g_free (priv->resource_path); g_free (priv->resource_query); g_free (priv->ssl_trust); soup_uri_free (priv->soup_uri); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_source_webdav_parent_class)->finalize (object); } static void source_webdav_constructed (GObject *object) { ESource *source; ESourceExtension *extension; const gchar *extension_name; /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_source_webdav_parent_class)->constructed (object); /* XXX I *think* we don't need to worry about disconnecting the * signals. ESourceExtensions are only added, never removed, * and they all finalize with their ESource. At least that's * how it's supposed to work if everyone follows the rules. */ extension = E_SOURCE_EXTENSION (object); source = e_source_extension_ref_source (extension); g_signal_connect ( extension, "notify::resource-path", G_CALLBACK (source_webdav_notify_cb), object); g_signal_connect ( extension, "notify::resource-query", G_CALLBACK (source_webdav_notify_cb), object); extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; extension = e_source_get_extension (source, extension_name); g_signal_connect ( extension, "notify::host", G_CALLBACK (source_webdav_notify_cb), object); g_signal_connect ( extension, "notify::port", G_CALLBACK (source_webdav_notify_cb), object); g_signal_connect ( extension, "notify::user", G_CALLBACK (source_webdav_notify_cb), object); /* This updates the authentication method * based on whether a user name was given. */ e_binding_bind_property_full ( extension, "user", extension, "method", G_BINDING_SYNC_CREATE, source_webdav_user_to_method, NULL, NULL, (GDestroyNotify) NULL); extension_name = E_SOURCE_EXTENSION_SECURITY; extension = e_source_get_extension (source, extension_name); g_signal_connect ( extension, "notify::secure", G_CALLBACK (source_webdav_notify_cb), object); g_object_unref (source); } static void e_source_webdav_class_init (ESourceWebdavClass *class) { GObjectClass *object_class; ESourceExtensionClass *extension_class; g_type_class_add_private (class, sizeof (ESourceWebdavPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = source_webdav_set_property; object_class->get_property = source_webdav_get_property; object_class->finalize = source_webdav_finalize; object_class->constructed = source_webdav_constructed; extension_class = E_SOURCE_EXTENSION_CLASS (class); extension_class->name = E_SOURCE_EXTENSION_WEBDAV_BACKEND; g_object_class_install_property ( object_class, PROP_AVOID_IFMATCH, g_param_spec_boolean ( "avoid-ifmatch", "Avoid If-Match", "Work around a bug in old Apache servers", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_CALENDAR_AUTO_SCHEDULE, g_param_spec_boolean ( "calendar-auto-schedule", "Calendar Auto-Schedule", "Whether the server handles meeting " "invitations (CalDAV-only)", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_DISPLAY_NAME, g_param_spec_string ( "display-name", "Display Name", "Display name of the WebDAV resource", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_EMAIL_ADDRESS, g_param_spec_string ( "email-address", "Email Address", "The user's email address", "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_RESOURCE_PATH, g_param_spec_string ( "resource-path", "Resource Path", "Absolute path to a WebDAV resource", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_RESOURCE_QUERY, g_param_spec_string ( "resource-query", "Resource Query", "Query to access a WebDAV resource", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | E_SOURCE_PARAM_SETTING)); g_object_class_install_property ( object_class, PROP_SOUP_URI, g_param_spec_boxed ( "soup-uri", "SoupURI", "WebDAV service as a SoupURI", SOUP_TYPE_URI, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_SSL_TRUST, g_param_spec_string ( "ssl-trust", "SSL Trust", "SSL certificate trust setting, " "for invalid server certificates", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | E_SOURCE_PARAM_SETTING)); } static void e_source_webdav_init (ESourceWebdav *extension) { extension->priv = E_SOURCE_WEBDAV_GET_PRIVATE (extension); /* Initialize this enough for SOUP_URI_IS_VALID() to pass. */ extension->priv->soup_uri = soup_uri_new (NULL); extension->priv->soup_uri->scheme = SOUP_URI_SCHEME_HTTP; extension->priv->soup_uri->path = g_strdup (""); } /** * e_source_webdav_get_avoid_ifmatch: * @extension: an #ESourceWebdav * * This setting works around a * * bug in older Apache mod_dav versions. * * * * We may deprecate this once Apache 2.2.8 or newer becomes * sufficiently ubiquitous, or we figure out a way to detect * and work around the bug automatically. * * * * Returns: whether the WebDAV server is known to exhibit the bug * * Since: 3.6 **/ gboolean e_source_webdav_get_avoid_ifmatch (ESourceWebdav *extension) { g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE); return extension->priv->avoid_ifmatch; } /** * e_source_webdav_set_avoid_ifmatch: * @extension: an #ESourceWebdav * @avoid_ifmatch: whether the WebDAV server is known to exhibit the bug * * This setting works around a * * bug in older Apache mod_dav versions. * * * * We may deprecate this once Apache 2.2.8 or newer becomes * sufficiently ubiquitous, or we figure out a way to detect * and work around the bug automatically. * * * * Since: 3.6 **/ void e_source_webdav_set_avoid_ifmatch (ESourceWebdav *extension, gboolean avoid_ifmatch) { g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); if (extension->priv->avoid_ifmatch == avoid_ifmatch) return; extension->priv->avoid_ifmatch = avoid_ifmatch; g_object_notify (G_OBJECT (extension), "avoid-ifmatch"); } /** * e_source_webdav_get_calendar_auto_schedule: * @extension: an #ESourceWebdav * * FIXME Document me! * * Since: 3.6 **/ gboolean e_source_webdav_get_calendar_auto_schedule (ESourceWebdav *extension) { g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE); return extension->priv->calendar_auto_schedule; } /** * e_source_webdav_set_calendar_auto_schedule: * @extension: an #ESourceWebdav * @calendar_auto_schedule: whether the server supports the * "calendar-auto-schedule" feature of CalDAV * * FIXME Document me! * * Since: 3.6 **/ void e_source_webdav_set_calendar_auto_schedule (ESourceWebdav *extension, gboolean calendar_auto_schedule) { g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); if (extension->priv->calendar_auto_schedule == calendar_auto_schedule) return; extension->priv->calendar_auto_schedule = calendar_auto_schedule; g_object_notify (G_OBJECT (extension), "calendar-auto-schedule"); } /** * e_source_webdav_get_display_name: * @extension: an #ESourceWebdav * * Returns the last known display name of a WebDAV resource, which may * differ from the #ESource:display-name property of the #ESource to which * @extension belongs. * * Returns: the display name of the WebDAV resource * * Since: 3.6 **/ const gchar * e_source_webdav_get_display_name (ESourceWebdav *extension) { g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); return extension->priv->display_name; } /** * e_source_webdav_dup_display_name: * @extension: an #ESourceWebdav * * Thread-safe variation of e_source_webdav_get_display_name(). * Use this function when accessing @extension from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: a newly-allocated copy of #ESourceWebdav:display-name * * Since: 3.6 **/ gchar * e_source_webdav_dup_display_name (ESourceWebdav *extension) { const gchar *protected; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); protected = e_source_webdav_get_display_name (extension); duplicate = g_strdup (protected); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return duplicate; } /** * e_source_webdav_set_display_name: * @extension: an #ESourceWebdav * @display_name: (allow-none): the display name of the WebDAV resource, * or %NULL * * Updates the last known display name of a WebDAV resource, which may * differ from the #ESource:display-name property of the #ESource to which * @extension belongs. * * The internal copy of @display_name is automatically stripped of leading * and trailing whitespace. If the resulting string is empty, %NULL is set * instead. * * Since: 3.6 **/ void e_source_webdav_set_display_name (ESourceWebdav *extension, const gchar *display_name) { g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); if (g_strcmp0 (extension->priv->display_name, display_name) == 0) { e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return; } g_free (extension->priv->display_name); extension->priv->display_name = e_util_strdup_strip (display_name); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); g_object_notify (G_OBJECT (extension), "display-name"); } /** * e_source_webdav_get_email_address: * @extension: an #ESourceWebdav * * Returns the user's email address which can be passed to a CalDAV server * if the user wishes to receive scheduling messages. * * Returns: the user's email address * * Since: 3.6 **/ const gchar * e_source_webdav_get_email_address (ESourceWebdav *extension) { g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); return extension->priv->email_address; } /** * e_source_webdav_dup_email_address: * @extension: an #ESourceWebdav * * Thread-safe variation of e_source_webdav_get_email_address(). * Use this function when accessing @extension from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: the newly-allocated copy of #ESourceWebdav:email-address * * Since: 3.6 **/ gchar * e_source_webdav_dup_email_address (ESourceWebdav *extension) { const gchar *protected; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); protected = e_source_webdav_get_email_address (extension); duplicate = g_strdup (protected); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return duplicate; } /** * e_source_webdav_set_email_address: * @extension: an #ESourceWebdav * @email_address: (allow-none): the user's email address, or %NULL * * Sets the user's email address which can be passed to a CalDAV server if * the user wishes to receive scheduling messages. * * The internal copy of @email_address is automatically stripped of leading * and trailing whitespace. If the resulting string is empty, %NULL is set * instead. * * Since: 3.6 **/ void e_source_webdav_set_email_address (ESourceWebdav *extension, const gchar *email_address) { g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); if (g_strcmp0 (extension->priv->email_address, email_address) == 0) { e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return; } g_free (extension->priv->email_address); extension->priv->email_address = e_util_strdup_strip (email_address); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); g_object_notify (G_OBJECT (extension), "email-address"); } /** * e_source_webdav_get_resource_path: * @extension: an #ESourceWebdav * * Returns the absolute path to a resource on a WebDAV server. * * Returns: the absolute path to a WebDAV resource * * Since: 3.6 **/ const gchar * e_source_webdav_get_resource_path (ESourceWebdav *extension) { g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); return extension->priv->resource_path; } /** * e_source_webdav_dup_resource_path: * @extension: an #ESourceWebdav * * Thread-safe variation of e_source_webdav_get_resource_path(). * Use this function when accessing @extension from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: the newly-allocated copy of #ESourceWebdav:resource-path * * Since: 3.6 **/ gchar * e_source_webdav_dup_resource_path (ESourceWebdav *extension) { const gchar *protected; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); protected = e_source_webdav_get_resource_path (extension); duplicate = g_strdup (protected); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return duplicate; } /** * e_source_webdav_set_resource_path: * @extension: an #ESourceWebdav * @resource_path: (allow-none): the absolute path to a WebDAV resource, * or %NULL * * Sets the absolute path to a resource on a WebDAV server. * * The internal copy of @resource_path is automatically stripped of leading * and trailing whitespace. If the resulting string is empty, %NULL is set * instead. * * Since: 3.6 **/ void e_source_webdav_set_resource_path (ESourceWebdav *extension, const gchar *resource_path) { g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); if (g_strcmp0 (extension->priv->resource_path, resource_path) == 0) { e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return; } g_free (extension->priv->resource_path); extension->priv->resource_path = e_util_strdup_strip (resource_path); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); g_object_notify (G_OBJECT (extension), "resource-path"); } /** * e_source_webdav_get_resource_query: * @extension: an #ESourceWebdav * * Returns the URI query required to access a resource on a WebDAV server. * * This is typically used when the #ESourceWebdav:resource-path points not * to the resource itself but to a web program that generates the resource * content on-the-fly. The #ESourceWebdav:resource-query holds the input * values for the program. * * Returns: the query to access a WebDAV resource * * Since: 3.6 **/ const gchar * e_source_webdav_get_resource_query (ESourceWebdav *extension) { g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); return extension->priv->resource_query; } /** * e_source_webdav_dup_resource_query: * @extension: an #ESourceWebdav * * Thread-safe variation of e_source_webdav_get_resource_query(). * Use this function when accessing @extension from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: the newly-allocated copy of #ESourceWebdav:resource-query * * Since: 3.6 **/ gchar * e_source_webdav_dup_resource_query (ESourceWebdav *extension) { const gchar *protected; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); protected = e_source_webdav_get_resource_query (extension); duplicate = g_strdup (protected); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return duplicate; } /** * e_source_webdav_set_resource_query: * @extension: an #ESourceWebdav * @resource_query: (allow-none): the query to access a WebDAV resource, * or %NULL * * Sets the URI query required to access a resource on a WebDAV server. * * This is typically used when the #ESourceWebdav:resource-path points not * to the resource itself but to a web program that generates the resource * content on-the-fly. The #ESourceWebdav:resource-query holds the input * values for the program. * * The internal copy of @resource_query is automatically stripped of leading * and trailing whitespace. If the resulting string is empty, %NULL is set * instead. * * Since: 3.6 **/ void e_source_webdav_set_resource_query (ESourceWebdav *extension, const gchar *resource_query) { g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); if (g_strcmp0 (extension->priv->resource_query, resource_query) == 0) { e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return; } g_free (extension->priv->resource_query); extension->priv->resource_query = e_util_strdup_strip (resource_query); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); g_object_notify (G_OBJECT (extension), "resource-query"); } /** * e_source_webdav_get_ssl_trust: * @extension: an #ESourceWebdav * * Returns an SSL certificate trust for the @extension. * The value encodes three parameters, divided by a pipe '|', * the first is users preference, can be one of "reject", "accept", * "temporary-reject" and "temporary-accept". The second is a host * name for which the trust was set. Finally the last is a SHA1 * hash of the certificate. This is not meant to be changed by a caller, * it is supposed to be manipulated with e_source_webdav_update_ssl_trust() * and e_source_webdav_verify_ssl_trust(). * * Returns: an SSL certificate trust for the @extension * * Since: 3.8 **/ const gchar * e_source_webdav_get_ssl_trust (ESourceWebdav *extension) { g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); return extension->priv->ssl_trust; } /** * e_source_webdav_dup_ssl_trust: * @extension: an #ESourceWebdav * * Thread-safe variation of e_source_webdav_get_ssl_trust(). * Use this function when accessing @extension from multiple threads. * * The returned string should be freed with g_free() when no longer needed. * * Returns: the newly-allocated copy of #ESourceWebdav:ssl-trust * * Since: 3.8 **/ gchar * e_source_webdav_dup_ssl_trust (ESourceWebdav *extension) { const gchar *protected; gchar *duplicate; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); protected = e_source_webdav_get_ssl_trust (extension); duplicate = g_strdup (protected); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return duplicate; } /** * e_source_webdav_set_ssl_trust: * @extension: an #ESourceWebdav * @ssl_trust: (allow-none): the ssl_trust to store, or %NULL to unset * * Sets the SSL certificate trust. See e_source_webdav_get_ssl_trust() * for more infomation about its content and how to use it. * * Since: 3.8 **/ void e_source_webdav_set_ssl_trust (ESourceWebdav *extension, const gchar *ssl_trust) { g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); if (g_strcmp0 (extension->priv->ssl_trust, ssl_trust) == 0) { e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return; } g_free (extension->priv->ssl_trust); extension->priv->ssl_trust = g_strdup (ssl_trust); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); g_object_notify (G_OBJECT (extension), "ssl-trust"); } /** * e_source_webdav_dup_soup_uri: * @extension: an #ESourceWebdav * * This is a convenience function which returns a newly-allocated * #SoupURI, its contents assembled from the #ESourceAuthentication * extension, the #ESourceSecurity extension, and @extension itself. * Free the returned #SoupURI with soup_uri_free(). * * Returns: (transfer full): a newly-allocated #SoupURI * * Since: 3.6 **/ SoupURI * e_source_webdav_dup_soup_uri (ESourceWebdav *extension) { SoupURI *duplicate; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); /* Keep this outside of the property lock. */ source_webdav_update_soup_uri_from_properties (extension); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); duplicate = soup_uri_copy (extension->priv->soup_uri); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); return duplicate; } /** * e_source_webdav_set_soup_uri: * @extension: an #ESourceWebdav * @soup_uri: a #SoupURI * * This is a convenience function which propagates the components of * @uri to the #ESourceAuthentication extension, the #ESourceSecurity * extension, and @extension itself. (The "fragment" component of * @uri is ignored.) * * Since: 3.6 **/ void e_source_webdav_set_soup_uri (ESourceWebdav *extension, SoupURI *soup_uri) { g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); g_return_if_fail (SOUP_URI_IS_VALID (soup_uri)); e_source_extension_property_lock (E_SOURCE_EXTENSION (extension)); /* Do not test for URI equality because our * internal SoupURI might not be up-to-date. */ soup_uri_free (extension->priv->soup_uri); extension->priv->soup_uri = soup_uri_copy (soup_uri); e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension)); g_object_freeze_notify (G_OBJECT (extension)); source_webdav_update_properties_from_soup_uri (extension); g_object_notify (G_OBJECT (extension), "soup-uri"); g_object_thaw_notify (G_OBJECT (extension)); } static gboolean decode_ssl_trust (ESourceWebdav *extension, ETrustPromptResponse *response, gchar **host, gchar **hash) { gchar *ssl_trust, **strv; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE); ssl_trust = e_source_webdav_dup_ssl_trust (extension); if (!ssl_trust || !*ssl_trust) { g_free (ssl_trust); return FALSE; } strv = g_strsplit (ssl_trust, "|", 3); if (!strv || !strv[0] || !strv[1] || !strv[2] || strv[3]) { g_free (ssl_trust); g_strfreev (strv); return FALSE; } if (response) { const gchar *resp = strv[0]; if (g_strcmp0 (resp, "reject") == 0) *response = E_TRUST_PROMPT_RESPONSE_REJECT; else if (g_strcmp0 (resp, "accept") == 0) *response = E_TRUST_PROMPT_RESPONSE_ACCEPT; else if (g_strcmp0 (resp, "temporary-reject") == 0) *response = E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY; else if (g_strcmp0 (resp, "temporary-accept") == 0) *response = E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY; else *response = E_TRUST_PROMPT_RESPONSE_UNKNOWN; } if (host) *host = g_strdup (strv[1]); if (hash) *hash = g_strdup (strv[2]); g_free (ssl_trust); g_strfreev (strv); return TRUE; } static gboolean encode_ssl_trust (ESourceWebdav *extension, ETrustPromptResponse response, const gchar *host, const gchar *hash) { gchar *ssl_trust; const gchar *resp; gboolean changed; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE); if (response == E_TRUST_PROMPT_RESPONSE_REJECT) resp = "reject"; else if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT) resp = "accept"; else if (response == E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY) resp = "temporary-reject"; else if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) resp = "temporary-accept"; else resp = "temporary-reject"; if (host && *host && hash && *hash) { ssl_trust = g_strconcat (resp, "|", host, "|", hash, NULL); } else { ssl_trust = NULL; } changed = g_strcmp0 (extension->priv->ssl_trust, ssl_trust) != 0; if (changed) e_source_webdav_set_ssl_trust (extension, ssl_trust); g_free (ssl_trust); return changed; } /** * e_source_webdav_update_ssl_trust: * @extension: an #ESourceWebdav * @host: a host name to store the certificate for * @cert: the invalid certificate of the connection over which @host is about * to be sent * @response: user's response from a trust prompt for @cert * * Updates user's response from a trust prompt, thus it is re-used the next * time it'll be needed. An #E_TRUST_PROMPT_RESPONSE_UNKNOWN is treated as * a temporary reject, which means the user will be asked again. * * Since: 3.16 **/ void e_source_webdav_update_ssl_trust (ESourceWebdav *extension, const gchar *host, GTlsCertificate *cert, ETrustPromptResponse response) { GByteArray *bytes = NULL; gchar *hash; g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); g_return_if_fail (host != NULL); g_return_if_fail (cert != NULL); g_object_get (cert, "certificate", &bytes, NULL); if (!bytes) return; hash = g_compute_checksum_for_data (G_CHECKSUM_SHA1, bytes->data, bytes->len); encode_ssl_trust (extension, response, host, hash); g_byte_array_unref (bytes); g_free (hash); } /** * e_source_webdav_verify_ssl_trust: * @extension: an #ESourceWebdav * @host: a host name to store the certificate for * @cert: the invalid certificate of the connection over which @host is about * to be sent * * Verifies SSL trust for the given @host and @cert, as previously stored in the @extension * with e_source_webdav_update_ssl_trust(). **/ ETrustPromptResponse e_source_webdav_verify_ssl_trust (ESourceWebdav *extension, const gchar *host, GTlsCertificate *cert, GTlsCertificateFlags cert_errors) { ETrustPromptResponse response; GByteArray *bytes = NULL; gchar *old_host = NULL; gchar *old_hash = NULL; g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), E_TRUST_PROMPT_RESPONSE_UNKNOWN); g_return_val_if_fail (host != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN); g_return_val_if_fail (cert != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN); /* Always reject revoked certificates */ if ((cert_errors & G_TLS_CERTIFICATE_REVOKED) != 0) return E_TRUST_PROMPT_RESPONSE_REJECT; g_object_get (cert, "certificate", &bytes, NULL); if (bytes == NULL) return E_TRUST_PROMPT_RESPONSE_REJECT; if (decode_ssl_trust (extension, &response, &old_host, &old_hash)) { gchar *hash; hash = g_compute_checksum_for_data (G_CHECKSUM_SHA1, bytes->data, bytes->len); if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN && g_strcmp0 (old_host, host) == 0 && g_strcmp0 (old_hash, hash) == 0) { g_byte_array_unref (bytes); g_free (old_host); g_free (old_hash); g_free (hash); return response; } g_free (old_host); g_free (old_hash); g_free (hash); } g_byte_array_unref (bytes); return E_TRUST_PROMPT_RESPONSE_UNKNOWN; } /** * e_source_webdav_unset_temporary_ssl_trust: * @extension: an #ESourceWebdav * * Unsets temporary trust set on this @extension, but keeps * it as is for other values. * * Since: 3.8 **/ void e_source_webdav_unset_temporary_ssl_trust (ESourceWebdav *extension) { ETrustPromptResponse response = E_TRUST_PROMPT_RESPONSE_UNKNOWN; g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); if (!decode_ssl_trust (extension, &response, NULL, NULL) || response == E_TRUST_PROMPT_RESPONSE_UNKNOWN || response == E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY || response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) e_source_webdav_set_ssl_trust (extension, NULL); }