aboutsummaryrefslogtreecommitdiffstats
path: root/mail/e-mail-autoconfig.c
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2011-04-13 22:30:40 +0800
committerMatthew Barnes <mbarnes@redhat.com>2012-06-03 11:00:40 +0800
commit3449e5fcc7f9c797fcde7f2a444b1eb7a934cd81 (patch)
treeff59febf4ac0c6316ef344ea25cee002088bd314 /mail/e-mail-autoconfig.c
parentf78795f4dff8b225d78385c5e23e1cd44ee946ad (diff)
downloadgsoc2013-evolution-3449e5fcc7f9c797fcde7f2a444b1eb7a934cd81.tar.gz
gsoc2013-evolution-3449e5fcc7f9c797fcde7f2a444b1eb7a934cd81.tar.zst
gsoc2013-evolution-3449e5fcc7f9c797fcde7f2a444b1eb7a934cd81.zip
Adapt mail to the new ESource API.
Diffstat (limited to 'mail/e-mail-autoconfig.c')
-rw-r--r--mail/e-mail-autoconfig.c883
1 files changed, 883 insertions, 0 deletions
diff --git a/mail/e-mail-autoconfig.c b/mail/e-mail-autoconfig.c
new file mode 100644
index 0000000000..e3588a8964
--- /dev/null
+++ b/mail/e-mail-autoconfig.c
@@ -0,0 +1,883 @@
+/*
+ * e-mail-autoconfig.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-autoconfig.h"
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+/* Stuff for DNS querying and message parsing. */
+#include <netdb.h>
+#include <netinet/in.h>
+#include <resolv.h>
+#include <arpa/nameser.h>
+#if defined(HAVE_ARPA_NAMESER_COMPAT_H) && !defined(GETSHORT)
+#include <arpa/nameser_compat.h>
+#endif
+
+/* For error codes. */
+#include <libsoup/soup.h>
+
+#include <libedataserver/e-source-camel.h>
+#include <libedataserver/e-source-mail-account.h>
+#include <libedataserver/e-source-mail-transport.h>
+
+#define E_MAIL_AUTOCONFIG_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_MAIL_AUTOCONFIG, EMailAutoconfigPrivate))
+
+#define AUTOCONFIG_BASE_URI \
+ "http://api.gnome.org/evolution/autoconfig/1.1/"
+
+/* XXX g_file_load_contents() on an "http://" URI returns error codes
+ * in the SOUP_HTTP_ERROR domain instead of the G_IO_ERROR domain.
+ * That is both undocumented and unexpected. */
+#define ERROR_IS_NOT_FOUND(error) \
+ (g_error_matches ((error), SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND))
+
+typedef struct _ParserClosure ParserClosure;
+typedef struct _ResolverClosure ResolverClosure;
+
+struct _EMailAutoconfigPrivate {
+ gchar *email_address;
+ gchar *email_local_part;
+ gchar *email_domain_part;
+ gchar *markup_content;
+};
+
+struct _ParserClosure {
+ CamelNetworkSettings *network_settings;
+ const gchar *expected_type;
+ const gchar *email_address;
+ const gchar *email_local_part;
+ const gchar *email_domain_part;
+ gboolean in_server_element;
+ gboolean settings_modified;
+};
+
+struct _ResolverClosure {
+ volatile gint ref_count;
+ GMainContext *main_context;
+ GMainLoop *main_loop;
+ gchar *domain_name;
+ gchar *name_server;
+ GError *error;
+};
+
+enum {
+ PROP_0,
+ PROP_EMAIL_ADDRESS
+};
+
+/* Forward Declarations */
+static void e_mail_autoconfig_initable_init (GInitableIface *interface);
+
+/* By default, the GAsyncInitable interface calls GInitable.init()
+ * from a separate thread, so we only have to override GInitable. */
+G_DEFINE_TYPE_WITH_CODE (
+ EMailAutoconfig,
+ e_mail_autoconfig,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (
+ G_TYPE_INITABLE, e_mail_autoconfig_initable_init)
+ G_IMPLEMENT_INTERFACE (
+ G_TYPE_ASYNC_INITABLE, NULL))
+
+static ResolverClosure *
+resolver_closure_new (const gchar *domain_name)
+{
+ ResolverClosure *closure;
+
+ closure = g_slice_new0 (ResolverClosure);
+ closure->domain_name = g_strdup (domain_name);
+ closure->main_context = g_main_context_new ();
+ closure->main_loop = g_main_loop_new (closure->main_context, FALSE);
+ closure->ref_count = 1;
+
+ return closure;
+}
+
+static ResolverClosure *
+resolver_closure_ref (ResolverClosure *closure)
+{
+ g_return_val_if_fail (closure != NULL, NULL);
+ g_return_val_if_fail (closure->ref_count > 0, NULL);
+
+ g_atomic_int_inc (&closure->ref_count);
+
+ return closure;
+}
+
+static void
+resolver_closure_unref (ResolverClosure *closure)
+{
+ g_return_if_fail (closure != NULL);
+ g_return_if_fail (closure->ref_count > 0);
+
+ if (g_atomic_int_dec_and_test (&closure->ref_count)) {
+ g_main_context_unref (closure->main_context);
+ g_main_loop_unref (closure->main_loop);
+ g_free (closure->domain_name);
+ g_free (closure->name_server);
+ g_clear_error (&closure->error);
+ g_slice_free (ResolverClosure, closure);
+ }
+}
+
+static gboolean
+mail_autoconfig_resolver_idle_quit (gpointer user_data)
+{
+ GMainLoop *main_loop = user_data;
+
+ g_main_loop_quit (main_loop);
+
+ return FALSE;
+}
+
+static void
+mail_autoconfig_resolver_cancelled (GCancellable *cancellable,
+ ResolverClosure *closure)
+{
+ GSource *source;
+
+ source = g_idle_source_new ();
+ g_source_set_callback (
+ source,
+ mail_autoconfig_resolver_idle_quit,
+ g_main_loop_ref (closure->main_loop),
+ (GDestroyNotify) g_main_loop_unref);
+ g_source_attach (source, closure->main_context);
+ g_source_unref (source);
+}
+
+static gpointer
+mail_autoconfig_resolver_thread (gpointer user_data)
+{
+ ResolverClosure *closure = user_data;
+ HEADER *header;
+ guchar answer[1024];
+ gchar namebuf[1024];
+ guchar *end, *cp;
+ gint count;
+ gint length;
+ gint herr;
+
+ /* Query DNS for the MX record for the domain name given in the
+ * email address. We need an authoritative name server for it. */
+
+ length = res_query (
+ closure->domain_name, C_IN, T_MX,
+ answer, sizeof (answer));
+ herr = h_errno; /* h_errno is defined in <netdb.h> */
+
+ /* Based heavily on _g_resolver_targets_from_res_query().
+ * The binary DNS message format is described in RFC 1035. */
+
+ if (length <= 0) {
+ if (length == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
+ g_set_error (
+ &closure->error,
+ G_RESOLVER_ERROR,
+ G_RESOLVER_ERROR_NOT_FOUND,
+ _("No mail exchanger record for '%s'"),
+ closure->domain_name);
+ else if (herr == TRY_AGAIN)
+ g_set_error (
+ &closure->error,
+ G_RESOLVER_ERROR,
+ G_RESOLVER_ERROR_TEMPORARY_FAILURE,
+ _("Temporarily unable to resolve '%s'"),
+ closure->domain_name);
+ else
+ g_set_error (
+ &closure->error,
+ G_RESOLVER_ERROR,
+ G_RESOLVER_ERROR_INTERNAL,
+ _("Error resolving '%s'"),
+ closure->domain_name);
+ goto exit;
+ }
+
+ header = (HEADER *) answer;
+ cp = answer + sizeof (HEADER);
+ end = answer + length;
+
+ /* Skip the 'question' section. */
+ count = ntohs (header->qdcount);
+ while (count-- && cp < end) {
+ cp += dn_expand (answer, end, cp, namebuf, sizeof (namebuf));
+ cp += 2; /* skip QTYPE */
+ cp += 2; /* skip QCLASS */
+ }
+
+ /* Skip the 'answers' section. */
+ count = ntohs (header->ancount);
+ while (count-- && cp < end) {
+ guint16 rdlength;
+ cp += dn_expand (answer, end, cp, namebuf, sizeof (namebuf));
+ cp += 2; /* skip TYPE */
+ cp += 2; /* skip CLASS */
+ cp += 4; /* skip TTL */
+ GETSHORT (rdlength, cp); /* read RDLENGTH */
+ cp += rdlength; /* skip RDATA */
+ }
+
+ /* Read the 'authority' section. */
+ count = ntohs (header->nscount);
+ while (count-- && cp < end) {
+ guint16 type, qclass, rdlength;
+ cp += dn_expand (answer, end, cp, namebuf, sizeof (namebuf));
+ GETSHORT (type, cp);
+ GETSHORT (qclass, cp);
+ cp += 4; /* skip TTL */
+ GETSHORT (rdlength, cp);
+
+ if (type != T_NS || qclass != C_IN) {
+ cp += rdlength;
+ continue;
+ }
+
+ cp += dn_expand (answer, end, cp, namebuf, sizeof (namebuf));
+
+ /* Pick the first T_NS record we find. */
+ closure->name_server = g_strdup (namebuf);
+ break;
+ }
+
+ if (closure->name_server == NULL)
+ g_set_error (
+ &closure->error,
+ G_RESOLVER_ERROR,
+ G_RESOLVER_ERROR_NOT_FOUND,
+ _("No authoritative name server for '%s'"),
+ closure->domain_name);
+
+exit:
+ g_main_loop_quit (closure->main_loop);
+ resolver_closure_unref (closure);
+
+ return NULL; /* return value is not used */
+}
+
+static gchar *
+mail_autoconfig_resolve_authority (const gchar *domain,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ResolverClosure *closure;
+ GThread *resolver_thread;
+ gchar *name_server = NULL;
+ gulong cancel_id = 0;
+
+ closure = resolver_closure_new (domain);
+
+ /* DNS record lookup is not cancellable, so we run it in a
+ * separate thread. We don't join with the thread, however,
+ * because if we get cancelled we want to return immediately.
+ * So use a reference count on the thread closure and always
+ * let the thread run to completion even if we're not around
+ * any longer to pick up the result. */
+ resolver_thread = g_thread_create (
+ mail_autoconfig_resolver_thread,
+ resolver_closure_ref (closure),
+ FALSE /* not joinable */, error);
+
+ if (resolver_thread == NULL)
+ return FALSE;
+
+ if (G_IS_CANCELLABLE (cancellable))
+ cancel_id = g_cancellable_connect (
+ cancellable,
+ G_CALLBACK (mail_autoconfig_resolver_cancelled),
+ resolver_closure_ref (closure),
+ (GDestroyNotify) resolver_closure_unref);
+
+ g_main_loop_run (closure->main_loop);
+
+ if (cancel_id > 0)
+ g_cancellable_disconnect (cancellable, cancel_id);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+ /* do nothing */
+
+ } else if (closure->error != NULL) {
+ g_warn_if_fail (closure->name_server == NULL);
+ g_propagate_error (error, closure->error);
+ closure->error = NULL;
+
+ } else {
+ g_warn_if_fail (closure->name_server != NULL);
+ name_server = closure->name_server;
+ closure->name_server = NULL;
+ }
+
+ resolver_closure_unref (closure);
+
+ return name_server;
+}
+
+static gboolean
+mail_autoconfig_lookup (EMailAutoconfig *autoconfig,
+ const gchar *domain,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFile *file;
+ gchar *uri;
+ gboolean success;
+
+ uri = g_strconcat (AUTOCONFIG_BASE_URI, domain, NULL);
+ file = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ /* Just to make sure we don't leak. */
+ g_free (autoconfig->priv->markup_content);
+ autoconfig->priv->markup_content = NULL;
+
+ success = g_file_load_contents (
+ file, cancellable,
+ &autoconfig->priv->markup_content,
+ NULL, NULL, error);
+
+ g_object_unref (file);
+
+ return success;
+}
+
+static void
+mail_autoconfig_parse_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParserClosure *closure = user_data;
+ gboolean is_incoming_server;
+ gboolean is_outgoing_server;
+
+ is_incoming_server = g_str_equal (element_name, "incomingServer");
+ is_outgoing_server = g_str_equal (element_name, "outgoingServer");
+
+ if (is_incoming_server || is_outgoing_server) {
+ const gchar *type = NULL;
+
+ g_markup_collect_attributes (
+ element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRING,
+ "type", &type,
+ G_MARKUP_COLLECT_INVALID);
+
+ closure->in_server_element =
+ (g_strcmp0 (type, closure->expected_type) == 0);
+ }
+}
+
+static void
+mail_autoconfig_parse_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParserClosure *closure = user_data;
+ gboolean is_incoming_server;
+ gboolean is_outgoing_server;
+
+ is_incoming_server = g_str_equal (element_name, "incomingServer");
+ is_outgoing_server = g_str_equal (element_name, "outgoingServer");
+
+ if (is_incoming_server || is_outgoing_server)
+ closure->in_server_element = FALSE;
+}
+
+static void
+mail_autoconfig_parse_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_length,
+ gpointer user_data,
+ GError **error)
+{
+ ParserClosure *closure = user_data;
+ const gchar *element_name;
+ GString *string;
+
+ if (!closure->in_server_element)
+ return;
+
+ /* Perform the following text substitutions:
+ *
+ * %EMAILADDRESS% : closure->email_address
+ * %EMAILLOCALPART% : closure->email_local_part
+ * %EMAILDOMAIN% : closure->email_domain_part
+ */
+ if (strchr (text, '%') == NULL)
+ string = g_string_new (text);
+ else {
+ const gchar *cp = text;
+
+ string = g_string_sized_new (256);
+ while (*cp != '\0') {
+ const gchar *variable;
+ const gchar *substitute;
+
+ if (*cp != '%') {
+ g_string_append_c (string, *cp++);
+ continue;
+ }
+
+ variable = "%EMAILADDRESS%";
+ substitute = closure->email_address;
+
+ if (strncmp (cp, variable, strlen (variable)) == 0) {
+ g_string_append (string, substitute);
+ cp += strlen (variable);
+ continue;
+ }
+
+ variable = "%EMAILLOCALPART%";
+ substitute = closure->email_local_part;
+
+ if (strncmp (cp, variable, strlen (variable)) == 0) {
+ g_string_append (string, substitute);
+ cp += strlen (variable);
+ continue;
+ }
+
+ variable = "%EMAILDOMAIN%";
+ substitute = closure->email_domain_part;
+
+ if (strncmp (cp, variable, strlen (variable)) == 0) {
+ g_string_append (string, substitute);
+ cp += strlen (variable);
+ continue;
+ }
+
+ g_string_append_c (string, *cp++);
+ }
+ }
+
+ element_name = g_markup_parse_context_get_element (context);
+
+ if (g_str_equal (element_name, "hostname")) {
+ camel_network_settings_set_host (
+ closure->network_settings, string->str);
+ closure->settings_modified = TRUE;
+
+ } else if (g_str_equal (element_name, "username")) {
+ camel_network_settings_set_user (
+ closure->network_settings, string->str);
+ closure->settings_modified = TRUE;
+
+ } else if (g_str_equal (element_name, "port")) {
+ glong port = strtol (string->str, NULL, 10);
+ if (port == CLAMP (port, 1, G_MAXUINT16)) {
+ camel_network_settings_set_port (
+ closure->network_settings, (guint16) port);
+ closure->settings_modified = TRUE;
+ }
+
+ } else if (g_str_equal (element_name, "socketType")) {
+ if (g_str_equal (string->str, "plain")) {
+ camel_network_settings_set_security_method (
+ closure->network_settings,
+ CAMEL_NETWORK_SECURITY_METHOD_NONE);
+ closure->settings_modified = TRUE;
+ } else if (g_str_equal (string->str, "SSL")) {
+ camel_network_settings_set_security_method (
+ closure->network_settings,
+ CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT);
+ closure->settings_modified = TRUE;
+ } else if (g_str_equal (string->str, "STARTTLS")) {
+ camel_network_settings_set_security_method (
+ closure->network_settings,
+ CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT);
+ closure->settings_modified = TRUE;
+ }
+ }
+
+ /* FIXME Not handling <authentication> elements.
+ * Unclear how some map to SASL mechanisms. */
+
+ g_string_free (string, TRUE);
+}
+
+static GMarkupParser mail_autoconfig_parser = {
+ mail_autoconfig_parse_start_element,
+ mail_autoconfig_parse_end_element,
+ mail_autoconfig_parse_text
+};
+
+static gboolean
+mail_autoconfig_set_details (EMailAutoconfig *autoconfig,
+ const gchar *expected_type,
+ ESource *source,
+ const gchar *extension_name)
+{
+ GMarkupParseContext *context;
+ ESourceCamel *camel_ext;
+ ESourceBackend *backend_ext;
+ CamelSettings *settings;
+ ParserClosure closure;
+ const gchar *backend_name;
+ const gchar *markup_content;
+ gboolean success;
+
+ if (!e_source_has_extension (source, extension_name))
+ return FALSE;
+
+ backend_ext = e_source_get_extension (source, extension_name);
+ backend_name = e_source_backend_get_backend_name (backend_ext);
+ extension_name = e_source_camel_get_extension_name (backend_name);
+ camel_ext = e_source_get_extension (source, extension_name);
+
+ settings = e_source_camel_get_settings (camel_ext);
+ g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), FALSE);
+
+ markup_content = e_mail_autoconfig_get_markup_content (autoconfig);
+ g_return_val_if_fail (markup_content != NULL, FALSE);
+
+ closure.network_settings = CAMEL_NETWORK_SETTINGS (settings);
+ closure.expected_type = expected_type;
+ closure.in_server_element = FALSE;
+ closure.settings_modified = FALSE;
+
+ /* These are used for text substitutions. */
+ closure.email_address = autoconfig->priv->email_address;
+ closure.email_local_part = autoconfig->priv->email_local_part;
+ closure.email_domain_part = autoconfig->priv->email_domain_part;
+
+ context = g_markup_parse_context_new (
+ &mail_autoconfig_parser, 0, &closure, (GDestroyNotify) NULL);
+
+ success = g_markup_parse_context_parse (
+ context, markup_content, strlen (markup_content), NULL);
+
+ success &= g_markup_parse_context_end_parse (context, NULL);
+
+ /* Did we actually configure anything? */
+ success &= closure.settings_modified;
+
+ g_markup_parse_context_free (context);
+
+ return success;
+}
+
+static void
+mail_autoconfig_set_email_address (EMailAutoconfig *autoconfig,
+ const gchar *email_address)
+{
+ g_return_if_fail (email_address != NULL);
+ g_return_if_fail (autoconfig->priv->email_address == NULL);
+
+ autoconfig->priv->email_address = g_strdup (email_address);
+}
+
+static void
+mail_autoconfig_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EMAIL_ADDRESS:
+ mail_autoconfig_set_email_address (
+ E_MAIL_AUTOCONFIG (object),
+ g_value_get_string (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_autoconfig_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_EMAIL_ADDRESS:
+ g_value_set_string (
+ value,
+ e_mail_autoconfig_get_email_address (
+ E_MAIL_AUTOCONFIG (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+mail_autoconfig_finalize (GObject *object)
+{
+ EMailAutoconfigPrivate *priv;
+
+ priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (object);
+
+ g_free (priv->email_address);
+ g_free (priv->email_local_part);
+ g_free (priv->email_domain_part);
+ g_free (priv->markup_content);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_mail_autoconfig_parent_class)->finalize (object);
+}
+
+static gboolean
+mail_autoconfig_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EMailAutoconfig *autoconfig;
+ const gchar *email_address;
+ const gchar *domain;
+ const gchar *cp;
+ gchar *name_server;
+ gboolean success = FALSE;
+ GError *local_error = NULL;
+
+ autoconfig = E_MAIL_AUTOCONFIG (initable);
+ email_address = e_mail_autoconfig_get_email_address (autoconfig);
+
+ if (email_address == NULL) {
+ g_set_error_literal (
+ error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("No email address provided"));
+ return FALSE;
+ }
+
+ cp = strchr (email_address, '@');
+ if (cp == NULL) {
+ g_set_error_literal (
+ error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Missing domain in email address"));
+ return FALSE;
+ }
+
+ domain = cp + 1;
+
+ autoconfig->priv->email_local_part =
+ g_strndup (email_address, cp - email_address);
+ autoconfig->priv->email_domain_part = g_strdup (domain);
+
+ /* First try the email address domain verbatim. */
+ success = mail_autoconfig_lookup (
+ autoconfig, domain, cancellable, &local_error);
+
+ g_warn_if_fail (
+ (success && local_error == NULL) ||
+ (!success && local_error != NULL));
+
+ if (success)
+ return TRUE;
+
+ /* "404 Not Found" errors are non-fatal this time around. */
+ if (ERROR_IS_NOT_FOUND (local_error)) {
+ g_clear_error (&local_error);
+ } else {
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ /* Look up an authoritative name server for the email address
+ * domain according to its "mail exchanger" (MX) DNS record. */
+ name_server = mail_autoconfig_resolve_authority (
+ domain, cancellable, error);
+
+ if (name_server == NULL)
+ return FALSE;
+
+ /* Widdle away segments of the name server domain until
+ * we find a match, or until we widdle down to nothing. */
+
+ cp = name_server;
+ while (cp != NULL && strchr (cp, '.') != NULL) {
+ g_clear_error (&local_error);
+
+ success = mail_autoconfig_lookup (
+ autoconfig, cp, cancellable, &local_error);
+
+ g_warn_if_fail (
+ (success && local_error == NULL) ||
+ (!success && local_error != NULL));
+
+ if (success || !ERROR_IS_NOT_FOUND (local_error))
+ break;
+
+ cp = strchr (cp, '.');
+ if (cp != NULL)
+ cp++;
+ }
+
+ if (local_error != NULL)
+ g_propagate_error (error, local_error);
+
+ g_free (name_server);
+
+ return success;
+}
+
+static void
+e_mail_autoconfig_class_init (EMailAutoconfigClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EMailAutoconfigPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = mail_autoconfig_set_property;
+ object_class->get_property = mail_autoconfig_get_property;
+ object_class->finalize = mail_autoconfig_finalize;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_EMAIL_ADDRESS,
+ g_param_spec_string (
+ "email-address",
+ "Email Address",
+ "The address from which to query config data",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_mail_autoconfig_initable_init (GInitableIface *interface)
+{
+ interface->init = mail_autoconfig_initable_init;
+}
+
+static void
+e_mail_autoconfig_init (EMailAutoconfig *autoconfig)
+{
+ autoconfig->priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (autoconfig);
+}
+
+EMailAutoconfig *
+e_mail_autoconfig_new_sync (const gchar *email_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (email_address != NULL, NULL);
+
+ return g_initable_new (
+ E_TYPE_MAIL_AUTOCONFIG,
+ cancellable, error,
+ "email-address", email_address,
+ NULL);
+}
+
+void
+e_mail_autoconfig_new (const gchar *email_address,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (email_address != NULL);
+
+ g_async_initable_new_async (
+ E_TYPE_MAIL_AUTOCONFIG,
+ io_priority, cancellable,
+ callback, user_data,
+ "email-address", email_address,
+ NULL);
+}
+
+EMailAutoconfig *
+e_mail_autoconfig_finish (GAsyncResult *result,
+ GError **error)
+{
+ GObject *source_object;
+ GObject *autoconfig;
+
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+ source_object = g_async_result_get_source_object (result);
+ g_return_val_if_fail (source_object != NULL, NULL);
+
+ autoconfig = g_async_initable_new_finish (
+ G_ASYNC_INITABLE (source_object), result, error);
+
+ g_object_unref (source_object);
+
+ if (autoconfig == NULL)
+ return NULL;
+
+ return E_MAIL_AUTOCONFIG (autoconfig);
+}
+
+const gchar *
+e_mail_autoconfig_get_email_address (EMailAutoconfig *autoconfig)
+{
+ g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
+
+ return autoconfig->priv->email_address;
+}
+
+const gchar *
+e_mail_autoconfig_get_markup_content (EMailAutoconfig *autoconfig)
+{
+ g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
+
+ return autoconfig->priv->markup_content;
+}
+
+gboolean
+e_mail_autoconfig_set_imap_details (EMailAutoconfig *autoconfig,
+ ESource *imap_source)
+{
+ g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (imap_source), FALSE);
+
+ return mail_autoconfig_set_details (
+ autoconfig, "imap", imap_source,
+ E_SOURCE_EXTENSION_MAIL_ACCOUNT);
+}
+
+gboolean
+e_mail_autoconfig_set_pop3_details (EMailAutoconfig *autoconfig,
+ ESource *pop3_source)
+{
+ g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (pop3_source), FALSE);
+
+ return mail_autoconfig_set_details (
+ autoconfig, "pop3", pop3_source,
+ E_SOURCE_EXTENSION_MAIL_ACCOUNT);
+}
+
+gboolean
+e_mail_autoconfig_set_smtp_details (EMailAutoconfig *autoconfig,
+ ESource *smtp_source)
+{
+ g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (smtp_source), FALSE);
+
+ return mail_autoconfig_set_details (
+ autoconfig, "smtp", smtp_source,
+ E_SOURCE_EXTENSION_MAIL_TRANSPORT);
+}
+