diff options
Diffstat (limited to 'mail/e-mail-autoconfig.c')
-rw-r--r-- | mail/e-mail-autoconfig.c | 883 |
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); +} + |