Bug 720387 - Always set error on failure of mail_autoconfig_initable_init()
[evolution.git] / src / mail / e-mail-autoconfig.c
blob1b321c82838cf592276cec14cb6c3a48354cad7a
1 /*
2 * e-mail-autoconfig.c
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 /* XXX Thoughts on RFC 6186: Use of SRV Records for Locating Email
19 * Submission/Access Services
21 * RFC 6186 specifies using SRV DNS lookups to aid in automatic
22 * configuration of mail accounts. While it may be tempting to
23 * implement the RFC here (I was tempted at least), upon closer
24 * examination I find the RFC to be insufficient.
26 * An SRV DNS lookup only provides a host name and port number.
27 * The RFC assumes the account's user name can be derived from
28 * the email address, and suggests probing the mail server for
29 * a valid user name by actually attempting authentication,
30 * first with the user's full email address and then falling
31 * back to only the local part.
33 * I'm uncomfortable with this for a number of reasons:
35 * 1) I would prefer the user have a chance to manually review
36 * the settings before transmitting credentials of any kind,
37 * since DNS responses can be spoofed.
39 * 2) Authentication at this phase would require asking for
40 * a password either before or during auto-configuration.
41 * Asking before assumes a password-based authentication
42 * mechanism is to be used, which is not always the case,
43 * and asking during may raise the user's suspicion about
44 * what's going on behind the scenes (it would mine).
46 * 3) For better or worse, our architecture isn't really built
47 * to handle authentication at this stage. EMailSession is
48 * wired into too many other areas to be reused here without
49 * risking unwanted side-effects, therefore it would require
50 * a custom CamelSession subclass with an authenticate_sync()
51 * implementation similar to EMailSession.
53 * While the technical limitations of (3) could be overcome, my concerns
54 * in (1) and (2) still stand. I think for the time being a better solution
55 * is to have an administrator script on api.gnome.org that compares the host
56 * and port settings in each clientConfig file to the _imap._tcp, _pop3._tcp,
57 * and _submission._tcp SRV records for that service provider (if available)
58 * to help ensure the static XML content remains accurate. It would also be
59 * instructive to track how many service providers even implement RFC 6186.
61 * Recording my thoughts here for posterity. -- mbarnes
64 #include "evolution-config.h"
66 #include <string.h>
67 #include <glib/gi18n-lib.h>
69 /* For error codes. */
70 #include <libsoup/soup.h>
72 #include "e-mail-autoconfig.h"
74 #define E_MAIL_AUTOCONFIG_GET_PRIVATE(obj) \
75 (G_TYPE_INSTANCE_GET_PRIVATE \
76 ((obj), E_TYPE_MAIL_AUTOCONFIG, EMailAutoconfigPrivate))
78 #define AUTOCONFIG_BASE_URI \
79 "https://api.gnome.org/evolution/autoconfig/1.1/"
81 #define ERROR_IS_NOT_FOUND(error) \
82 (g_error_matches ((error), SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND))
84 typedef struct _EMailAutoconfigResult EMailAutoconfigResult;
85 typedef struct _ParserClosure ParserClosure;
87 struct _EMailAutoconfigResult {
88 gboolean set;
89 gchar *user;
90 gchar *host;
91 guint16 port;
92 gchar *auth_mechanism;
93 CamelNetworkSecurityMethod security_method;
96 struct _EMailAutoconfigPrivate {
97 ESourceRegistry *registry;
98 gchar *email_address;
99 gchar *email_local_part;
100 gchar *email_domain_part;
101 gchar *use_domain;
102 EMailAutoconfigResult imap_result;
103 EMailAutoconfigResult pop3_result;
104 EMailAutoconfigResult smtp_result;
107 struct _ParserClosure {
108 EMailAutoconfig *autoconfig;
109 EMailAutoconfigResult *result;
112 enum {
113 PROP_0,
114 PROP_EMAIL_ADDRESS,
115 PROP_REGISTRY,
116 PROP_USE_DOMAIN
119 /* Forward Declarations */
120 static void e_mail_autoconfig_initable_init (GInitableIface *iface);
122 /* By default, the GAsyncInitable interface calls GInitable.init()
123 * from a separate thread, so we only have to override GInitable. */
124 G_DEFINE_TYPE_WITH_CODE (
125 EMailAutoconfig,
126 e_mail_autoconfig,
127 G_TYPE_OBJECT,
128 G_IMPLEMENT_INTERFACE (
129 G_TYPE_INITABLE, e_mail_autoconfig_initable_init)
130 G_IMPLEMENT_INTERFACE (
131 G_TYPE_ASYNC_INITABLE, NULL))
133 static void
134 mail_autoconfig_parse_start_element (GMarkupParseContext *context,
135 const gchar *element_name,
136 const gchar **attribute_names,
137 const gchar **attribute_values,
138 gpointer user_data,
139 GError **error)
141 ParserClosure *closure = user_data;
142 EMailAutoconfigPrivate *priv;
143 gboolean is_incoming_server;
144 gboolean is_outgoing_server;
146 priv = closure->autoconfig->priv;
148 is_incoming_server = g_str_equal (element_name, "incomingServer");
149 is_outgoing_server = g_str_equal (element_name, "outgoingServer");
151 if (is_incoming_server || is_outgoing_server) {
152 const gchar *type = NULL;
154 g_markup_collect_attributes (
155 element_name,
156 attribute_names,
157 attribute_values,
158 error,
159 G_MARKUP_COLLECT_STRING,
160 "type", &type,
161 G_MARKUP_COLLECT_INVALID);
163 if (g_strcmp0 (type, "imap") == 0)
164 closure->result = &priv->imap_result;
165 if (g_strcmp0 (type, "pop3") == 0)
166 closure->result = &priv->pop3_result;
167 if (g_strcmp0 (type, "smtp") == 0)
168 closure->result = &priv->smtp_result;
172 static void
173 mail_autoconfig_parse_end_element (GMarkupParseContext *context,
174 const gchar *element_name,
175 gpointer user_data,
176 GError **error)
178 ParserClosure *closure = user_data;
179 gboolean is_incoming_server;
180 gboolean is_outgoing_server;
182 is_incoming_server = g_str_equal (element_name, "incomingServer");
183 is_outgoing_server = g_str_equal (element_name, "outgoingServer");
185 if (is_incoming_server || is_outgoing_server)
186 closure->result = NULL;
189 static void
190 mail_autoconfig_parse_text (GMarkupParseContext *context,
191 const gchar *text,
192 gsize text_length,
193 gpointer user_data,
194 GError **error)
196 ParserClosure *closure = user_data;
197 EMailAutoconfigPrivate *priv;
198 const gchar *element_name;
199 GString *string;
201 priv = closure->autoconfig->priv;
203 if (closure->result == NULL)
204 return;
206 /* Perform the following text substitutions:
208 * %EMAILADDRESS% : closure->email_address
209 * %EMAILLOCALPART% : closure->email_local_part
210 * %EMAILDOMAIN% : closure->email_domain_part
212 if (strchr (text, '%') == NULL)
213 string = g_string_new (text);
214 else {
215 const gchar *cp = text;
217 string = g_string_sized_new (256);
218 while (*cp != '\0') {
219 const gchar *variable;
220 const gchar *substitute;
222 if (*cp != '%') {
223 g_string_append_c (string, *cp++);
224 continue;
227 variable = "%EMAILADDRESS%";
228 substitute = priv->email_address;
230 if (strncmp (cp, variable, strlen (variable)) == 0) {
231 g_string_append (string, substitute);
232 cp += strlen (variable);
233 continue;
236 variable = "%EMAILLOCALPART%";
237 substitute = priv->email_local_part;
239 if (strncmp (cp, variable, strlen (variable)) == 0) {
240 g_string_append (string, substitute);
241 cp += strlen (variable);
242 continue;
245 variable = "%EMAILDOMAIN%";
246 substitute = priv->use_domain;
248 if (strncmp (cp, variable, strlen (variable)) == 0) {
249 g_string_append (string, substitute);
250 cp += strlen (variable);
251 continue;
254 g_string_append_c (string, *cp++);
258 element_name = g_markup_parse_context_get_element (context);
260 if (g_str_equal (element_name, "hostname")) {
261 closure->result->host = g_strdup (string->str);
262 closure->result->set = TRUE;
264 } else if (g_str_equal (element_name, "username")) {
265 closure->result->user = g_strdup (string->str);
266 closure->result->set = TRUE;
268 } else if (g_str_equal (element_name, "port")) {
269 glong port = strtol (string->str, NULL, 10);
270 if (port == CLAMP (port, 1, G_MAXUINT16)) {
271 closure->result->port = (guint16) port;
272 closure->result->set = TRUE;
275 } else if (g_str_equal (element_name, "socketType")) {
276 if (g_str_equal (string->str, "plain")) {
277 closure->result->security_method =
278 CAMEL_NETWORK_SECURITY_METHOD_NONE;
279 closure->result->set = TRUE;
280 } else if (g_str_equal (string->str, "SSL")) {
281 closure->result->security_method =
282 CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT;
283 closure->result->set = TRUE;
284 } else if (g_str_equal (string->str, "STARTTLS")) {
285 closure->result->security_method =
286 CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT;
287 closure->result->set = TRUE;
290 } else if (g_str_equal (element_name, "authentication")) {
291 gboolean use_plain_auth = FALSE;
293 /* "password-cleartext" and "plain" are synonymous. */
295 if (g_str_equal (string->str, "password-cleartext"))
296 use_plain_auth = TRUE;
298 if (g_str_equal (string->str, "plain"))
299 use_plain_auth = TRUE;
301 if (use_plain_auth) {
302 gchar *auth_mechanism = NULL;
304 /* The exact auth name depends on the protocol. */
306 /* Leave this NULL for IMAP so Camel
307 * will issue an IMAP LOGIN command. */
308 if (closure->result == &priv->imap_result)
309 auth_mechanism = NULL;
311 /* Leave this NULL for POP3 so Camel
312 * will issue POP3 USER/PASS commands. */
313 if (closure->result == &priv->pop3_result)
314 auth_mechanism = NULL;
316 if (closure->result == &priv->smtp_result)
317 auth_mechanism = g_strdup ("LOGIN");
319 closure->result->auth_mechanism = auth_mechanism;
320 closure->result->set = TRUE;
323 /* "password-encrypted" apparently maps to CRAM-MD5,
324 * or at least that's how Thunderbird interprets it. */
326 if (g_str_equal (string->str, "password-encrypted")) {
327 closure->result->auth_mechanism = g_strdup ("CRAM-MD5");
328 closure->result->set = TRUE;
331 /* XXX Other <authentication> values not handled,
332 * but they are corner cases for the most part. */
335 g_string_free (string, TRUE);
338 static GMarkupParser mail_autoconfig_parser = {
339 mail_autoconfig_parse_start_element,
340 mail_autoconfig_parse_end_element,
341 mail_autoconfig_parse_text
344 static gchar *
345 mail_autoconfig_resolve_name_server (const gchar *domain,
346 GCancellable *cancellable,
347 GError **error)
349 GResolver *resolver;
350 GList *records;
351 gchar *name_server = NULL;
353 resolver = g_resolver_get_default ();
355 records = g_resolver_lookup_records (
356 resolver, domain, G_RESOLVER_RECORD_NS, cancellable, error);
358 /* This list is sorted per RFC 2782, so use the first item. */
359 if (records != NULL) {
360 GVariant *variant = records->data;
361 g_variant_get_child (variant, 0, "s", &name_server);
364 g_list_free_full (records, (GDestroyNotify) g_variant_unref);
366 g_object_unref (resolver);
368 return name_server;
371 static void
372 mail_autoconfig_abort_soup_session_cb (GCancellable *cancellable,
373 SoupSession *soup_session)
375 soup_session_abort (soup_session);
378 static gboolean
379 mail_autoconfig_lookup (EMailAutoconfig *autoconfig,
380 const gchar *domain,
381 GCancellable *cancellable,
382 GError **error)
384 GMarkupParseContext *context;
385 ESourceRegistry *registry;
386 ESource *proxy_source;
387 SoupMessage *soup_message;
388 SoupSession *soup_session;
389 ParserClosure closure;
390 gulong cancel_id = 0;
391 gboolean success;
392 guint status;
393 gchar *uri;
395 registry = e_mail_autoconfig_get_registry (autoconfig);
396 proxy_source = e_source_registry_ref_builtin_proxy (registry);
398 soup_session = soup_session_new_with_options (
399 SOUP_SESSION_PROXY_RESOLVER,
400 G_PROXY_RESOLVER (proxy_source),
401 NULL);
403 g_object_unref (proxy_source);
405 uri = g_strconcat (AUTOCONFIG_BASE_URI, domain, NULL);
407 soup_message = soup_message_new (SOUP_METHOD_GET, uri);
408 g_free (uri);
410 if (G_IS_CANCELLABLE (cancellable))
411 cancel_id = g_cancellable_connect (
412 cancellable,
413 G_CALLBACK (mail_autoconfig_abort_soup_session_cb),
414 g_object_ref (soup_session),
415 (GDestroyNotify) g_object_unref);
417 status = soup_session_send_message (soup_session, soup_message);
419 if (cancel_id > 0)
420 g_cancellable_disconnect (cancellable, cancel_id);
422 success = SOUP_STATUS_IS_SUCCESSFUL (status);
424 if (!success) {
425 g_set_error_literal (
426 error, SOUP_HTTP_ERROR,
427 soup_message->status_code,
428 soup_message->reason_phrase);
429 goto exit;
432 closure.autoconfig = autoconfig;
433 closure.result = NULL;
435 context = g_markup_parse_context_new (
436 &mail_autoconfig_parser, 0,
437 &closure, (GDestroyNotify) NULL);
439 success = g_markup_parse_context_parse (
440 context,
441 soup_message->response_body->data,
442 soup_message->response_body->length,
443 error);
445 if (success)
446 success = g_markup_parse_context_end_parse (context, error);
448 g_markup_parse_context_free (context);
450 exit:
451 g_object_unref (soup_message);
452 g_object_unref (soup_session);
454 return success;
457 static gboolean
458 mail_autoconfig_set_details (EMailAutoconfigResult *result,
459 ESource *source,
460 const gchar *extension_name,
461 const gchar *default_backend_name)
463 ESourceCamel *camel_ext;
464 ESourceBackend *backend_ext;
465 CamelSettings *settings;
466 const gchar *backend_name;
468 g_return_val_if_fail (result != NULL, FALSE);
470 if (!result->set)
471 return FALSE;
473 if (!e_source_has_extension (source, extension_name))
474 return FALSE;
476 backend_ext = e_source_get_extension (source, extension_name);
477 backend_name = e_source_backend_get_backend_name (backend_ext);
478 if (!backend_name || !*backend_name) {
479 e_source_backend_set_backend_name (backend_ext, default_backend_name);
480 backend_name = default_backend_name;
483 if (!backend_name)
484 return FALSE;
486 extension_name = e_source_camel_get_extension_name (backend_name);
487 camel_ext = e_source_get_extension (source, extension_name);
489 settings = e_source_camel_get_settings (camel_ext);
490 g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), FALSE);
492 g_object_set (
493 settings,
494 "user", result->user,
495 "host", result->host,
496 "port", result->port,
497 "auth-mechanism", result->auth_mechanism,
498 "security-method", result->security_method,
499 NULL);
501 return TRUE;
504 #define E_TYPE_MAIL_CONFIG_LOOKUP_RESULT \
505 (e_mail_config_lookup_result_get_type ())
506 #define E_MAIL_CONFIG_LOOKUP_RESULT(obj) \
507 (G_TYPE_CHECK_INSTANCE_CAST \
508 ((obj), E_TYPE_MAIL_CONFIG_LOOKUP_RESULT, EMailConfigLookupResult))
509 #define E_IS_MAIL_CONFIG_LOOKUP_RESULT(obj) \
510 (G_TYPE_CHECK_INSTANCE_TYPE \
511 ((obj), E_TYPE_MAIL_CONFIG_LOOKUP_RESULT))
513 typedef struct _EMailConfigLookupResult EMailConfigLookupResult;
514 typedef struct _EMailConfigLookupResultClass EMailConfigLookupResultClass;
516 struct _EMailConfigLookupResult {
517 /*< private >*/
518 EConfigLookupResultSimple parent;
520 EMailAutoconfigResult result;
521 gchar *extension_name;
524 struct _EMailConfigLookupResultClass {
525 /*< private >*/
526 EConfigLookupResultSimpleClass parent_class;
529 GType e_mail_config_lookup_result_get_type (void) G_GNUC_CONST;
531 G_DEFINE_TYPE (EMailConfigLookupResult, e_mail_config_lookup_result, E_TYPE_CONFIG_LOOKUP_RESULT_SIMPLE)
533 static gboolean
534 mail_config_lookup_result_configure_source (EConfigLookupResult *lookup_result,
535 EConfigLookup *config_lookup,
536 ESource *source)
538 EMailConfigLookupResult *mail_result;
540 g_return_val_if_fail (E_IS_MAIL_CONFIG_LOOKUP_RESULT (lookup_result), FALSE);
542 mail_result = E_MAIL_CONFIG_LOOKUP_RESULT (lookup_result);
544 /* No chain up to parent method, not needed here, because not used */
545 return mail_autoconfig_set_details (&mail_result->result, source, mail_result->extension_name,
546 e_config_lookup_result_get_protocol (lookup_result));
549 static void
550 mail_config_lookup_result_finalize (GObject *object)
552 EMailConfigLookupResult *mail_result = E_MAIL_CONFIG_LOOKUP_RESULT (object);
554 g_free (mail_result->result.user);
555 g_free (mail_result->result.host);
556 g_free (mail_result->result.auth_mechanism);
557 g_free (mail_result->extension_name);
559 /* Chain up to parent's method. */
560 G_OBJECT_CLASS (e_mail_config_lookup_result_parent_class)->finalize (object);
563 static void
564 e_mail_config_lookup_result_class_init (EMailConfigLookupResultClass *klass)
566 EConfigLookupResultSimpleClass *simple_result_class;
567 GObjectClass *object_class;
569 object_class = G_OBJECT_CLASS (klass);
570 object_class->finalize = mail_config_lookup_result_finalize;
572 simple_result_class = E_CONFIG_LOOKUP_RESULT_SIMPLE_CLASS (klass);
573 simple_result_class->configure_source = mail_config_lookup_result_configure_source;
576 static void
577 e_mail_config_lookup_result_init (EMailConfigLookupResult *mail_result)
581 static EConfigLookupResult *
582 e_mail_config_lookup_result_new (EConfigLookupResultKind kind,
583 gint priority,
584 const gchar *protocol,
585 const gchar *display_name,
586 const gchar *description,
587 const EMailAutoconfigResult *result,
588 const gchar *extension_name)
590 EMailConfigLookupResult *mail_result;
592 g_return_val_if_fail (protocol != NULL, NULL);
593 g_return_val_if_fail (display_name != NULL, NULL);
594 g_return_val_if_fail (description != NULL, NULL);
595 g_return_val_if_fail (result != NULL, NULL);
596 g_return_val_if_fail (extension_name != NULL, NULL);
598 mail_result = g_object_new (E_TYPE_MAIL_CONFIG_LOOKUP_RESULT,
599 "kind", kind,
600 "priority", priority,
601 "is-complete", TRUE,
602 "protocol", protocol,
603 "display-name", display_name,
604 "description", description,
605 "password", NULL,
606 NULL);
608 mail_result->result.set = result->set;
609 mail_result->result.user = g_strdup (result->user);
610 mail_result->result.host = g_strdup (result->host);
611 mail_result->result.port = result->port;
612 mail_result->result.auth_mechanism = g_strdup (result->auth_mechanism);
613 mail_result->result.security_method = result->security_method;
614 mail_result->extension_name = g_strdup (extension_name);
616 return E_CONFIG_LOOKUP_RESULT (mail_result);
619 static void
620 mail_autoconfig_result_to_config_lookup (EMailAutoconfig *mail_autoconfig,
621 EConfigLookup *config_lookup,
622 EMailAutoconfigResult *result,
623 gint priority,
624 const gchar *protocol,
625 const gchar *display_name,
626 const gchar *extension_name)
628 EConfigLookupResult *lookup_result;
629 EConfigLookupResultKind kind;
630 GString *description;
632 g_return_if_fail (E_IS_MAIL_AUTOCONFIG (mail_autoconfig));
633 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
634 g_return_if_fail (result != NULL);
635 g_return_if_fail (protocol != NULL);
636 g_return_if_fail (display_name != NULL);
637 g_return_if_fail (extension_name != NULL);
639 if (!result->set)
640 return;
642 kind = E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE;
643 if (g_strcmp0 (extension_name, E_SOURCE_EXTENSION_MAIL_TRANSPORT) == 0)
644 kind = E_CONFIG_LOOKUP_RESULT_MAIL_SEND;
646 description = g_string_new ("");
648 g_string_append_printf (description, _("Host: %s:%d"), result->host, result->port);
650 if (result->user && *result->user) {
651 g_string_append_c (description, '\n');
652 g_string_append_printf (description, _("User: %s"), result->user);
655 g_string_append_c (description, '\n');
656 g_string_append_printf (description, _("Security method: %s"),
657 result->security_method == CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT ? _("TLS") :
658 result->security_method == CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT ? _("STARTTLS") : _("None"));
660 if (result->auth_mechanism && *result->auth_mechanism) {
661 g_string_append_c (description, '\n');
662 g_string_append_printf (description, _("Authentication mechanism: %s"), result->auth_mechanism);
665 lookup_result = e_mail_config_lookup_result_new (kind, priority, protocol, display_name, description->str, result, extension_name);
666 e_config_lookup_add_result (config_lookup, lookup_result);
668 g_string_free (description, TRUE);
671 static void
672 mail_autoconfig_set_email_address (EMailAutoconfig *autoconfig,
673 const gchar *email_address)
675 g_return_if_fail (email_address != NULL);
676 g_return_if_fail (autoconfig->priv->email_address == NULL);
678 autoconfig->priv->email_address = g_strdup (email_address);
681 static void
682 mail_autoconfig_set_use_domain (EMailAutoconfig *autoconfig,
683 const gchar *use_domain)
685 if (g_strcmp0 (autoconfig->priv->use_domain, use_domain) != 0) {
686 g_free (autoconfig->priv->use_domain);
687 autoconfig->priv->use_domain = g_strdup (use_domain);
691 static void
692 mail_autoconfig_set_registry (EMailAutoconfig *autoconfig,
693 ESourceRegistry *registry)
695 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
696 g_return_if_fail (autoconfig->priv->registry == NULL);
698 autoconfig->priv->registry = g_object_ref (registry);
701 static void
702 mail_autoconfig_set_property (GObject *object,
703 guint property_id,
704 const GValue *value,
705 GParamSpec *pspec)
707 switch (property_id) {
708 case PROP_EMAIL_ADDRESS:
709 mail_autoconfig_set_email_address (
710 E_MAIL_AUTOCONFIG (object),
711 g_value_get_string (value));
712 return;
714 case PROP_REGISTRY:
715 mail_autoconfig_set_registry (
716 E_MAIL_AUTOCONFIG (object),
717 g_value_get_object (value));
718 return;
720 case PROP_USE_DOMAIN:
721 mail_autoconfig_set_use_domain (
722 E_MAIL_AUTOCONFIG (object),
723 g_value_get_string (value));
724 return;
727 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
730 static void
731 mail_autoconfig_get_property (GObject *object,
732 guint property_id,
733 GValue *value,
734 GParamSpec *pspec)
736 switch (property_id) {
737 case PROP_EMAIL_ADDRESS:
738 g_value_set_string (
739 value,
740 e_mail_autoconfig_get_email_address (
741 E_MAIL_AUTOCONFIG (object)));
742 return;
744 case PROP_REGISTRY:
745 g_value_set_object (
746 value,
747 e_mail_autoconfig_get_registry (
748 E_MAIL_AUTOCONFIG (object)));
749 return;
751 case PROP_USE_DOMAIN:
752 g_value_set_string (
753 value,
754 e_mail_autoconfig_get_use_domain (
755 E_MAIL_AUTOCONFIG (object)));
756 return;
759 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
762 static void
763 mail_autoconfig_dispose (GObject *object)
765 EMailAutoconfigPrivate *priv;
767 priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (object);
769 g_clear_object (&priv->registry);
771 /* Chain up to parent's dispose() method. */
772 G_OBJECT_CLASS (e_mail_autoconfig_parent_class)->dispose (object);
775 static void
776 mail_autoconfig_finalize (GObject *object)
778 EMailAutoconfigPrivate *priv;
780 priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (object);
782 g_free (priv->email_address);
783 g_free (priv->email_local_part);
784 g_free (priv->email_domain_part);
785 g_free (priv->use_domain);
787 g_free (priv->imap_result.user);
788 g_free (priv->imap_result.host);
789 g_free (priv->imap_result.auth_mechanism);
790 g_free (priv->pop3_result.user);
791 g_free (priv->pop3_result.host);
792 g_free (priv->pop3_result.auth_mechanism);
793 g_free (priv->smtp_result.user);
794 g_free (priv->smtp_result.host);
795 g_free (priv->smtp_result.auth_mechanism);
797 /* Chain up to parent's finalize() method. */
798 G_OBJECT_CLASS (e_mail_autoconfig_parent_class)->finalize (object);
801 static gboolean
802 mail_autoconfig_initable_init (GInitable *initable,
803 GCancellable *cancellable,
804 GError **error)
806 EMailAutoconfig *autoconfig;
807 const gchar *email_address;
808 const gchar *domain;
809 const gchar *cp;
810 gchar *name_server;
811 gboolean success = FALSE;
812 GError *local_error = NULL;
814 autoconfig = E_MAIL_AUTOCONFIG (initable);
815 email_address = e_mail_autoconfig_get_email_address (autoconfig);
817 if (email_address == NULL) {
818 g_set_error_literal (
819 error, G_IO_ERROR,
820 G_IO_ERROR_INVALID_ARGUMENT,
821 _("No email address provided"));
822 return FALSE;
825 cp = strchr (email_address, '@');
826 if (cp == NULL) {
827 g_set_error_literal (
828 error, G_IO_ERROR,
829 G_IO_ERROR_INVALID_ARGUMENT,
830 _("Missing domain in email address"));
831 return FALSE;
834 domain = cp + 1;
836 autoconfig->priv->email_local_part =
837 g_strndup (email_address, cp - email_address);
838 autoconfig->priv->email_domain_part = g_strdup (domain);
840 if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain)
841 domain = autoconfig->priv->use_domain;
843 /* First try the email address domain verbatim. */
844 success = mail_autoconfig_lookup (
845 autoconfig, domain, cancellable, &local_error);
847 g_warn_if_fail (
848 (success && local_error == NULL) ||
849 (!success && local_error != NULL));
851 if (success)
852 return TRUE;
854 /* "404 Not Found" errors are non-fatal this time around. */
855 if (ERROR_IS_NOT_FOUND (local_error)) {
856 g_clear_error (&local_error);
857 } else {
858 g_propagate_error (error, local_error);
859 return FALSE;
862 /* Look up an authoritative name server for the email address
863 * domain according to its "name server" (NS) DNS record. */
864 name_server = mail_autoconfig_resolve_name_server (
865 domain, cancellable, error);
867 if (name_server == NULL)
868 return FALSE;
870 /* Widdle away segments of the name server domain until
871 * we find a match, or until we widdle down to nothing. */
873 cp = name_server;
874 while (cp != NULL && strchr (cp, '.') != NULL) {
875 g_clear_error (&local_error);
877 success = mail_autoconfig_lookup (
878 autoconfig, cp, cancellable, &local_error);
880 g_warn_if_fail (
881 (success && local_error == NULL) ||
882 (!success && local_error != NULL));
884 if (success || !ERROR_IS_NOT_FOUND (local_error))
885 break;
887 cp = strchr (cp, '.');
888 if (cp != NULL)
889 cp++;
892 if (!success && !local_error)
893 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Unknown error"));
894 else if (local_error)
895 g_propagate_error (error, local_error);
897 g_free (name_server);
899 return success;
902 static void
903 e_mail_autoconfig_class_init (EMailAutoconfigClass *class)
905 GObjectClass *object_class;
907 g_type_class_add_private (class, sizeof (EMailAutoconfigPrivate));
909 object_class = G_OBJECT_CLASS (class);
910 object_class->set_property = mail_autoconfig_set_property;
911 object_class->get_property = mail_autoconfig_get_property;
912 object_class->dispose = mail_autoconfig_dispose;
913 object_class->finalize = mail_autoconfig_finalize;
915 g_object_class_install_property (
916 object_class,
917 PROP_EMAIL_ADDRESS,
918 g_param_spec_string (
919 "email-address",
920 "Email Address",
921 "The address from which to query config data",
922 NULL,
923 G_PARAM_READWRITE |
924 G_PARAM_CONSTRUCT_ONLY |
925 G_PARAM_STATIC_STRINGS));
927 g_object_class_install_property (
928 object_class,
929 PROP_REGISTRY,
930 g_param_spec_object (
931 "registry",
932 "Registry",
933 "Data source registry",
934 E_TYPE_SOURCE_REGISTRY,
935 G_PARAM_READWRITE |
936 G_PARAM_CONSTRUCT_ONLY |
937 G_PARAM_STATIC_STRINGS));
939 g_object_class_install_property (
940 object_class,
941 PROP_USE_DOMAIN,
942 g_param_spec_string (
943 "use-domain",
944 "Use Domain",
945 "A domain to use, instead of the one from email-address",
946 NULL,
947 G_PARAM_READWRITE |
948 G_PARAM_CONSTRUCT_ONLY |
949 G_PARAM_STATIC_STRINGS));
952 static void
953 e_mail_autoconfig_initable_init (GInitableIface *iface)
955 iface->init = mail_autoconfig_initable_init;
958 static void
959 e_mail_autoconfig_init (EMailAutoconfig *autoconfig)
961 autoconfig->priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (autoconfig);
964 EMailAutoconfig *
965 e_mail_autoconfig_new_sync (ESourceRegistry *registry,
966 const gchar *email_address,
967 const gchar *use_domain,
968 GCancellable *cancellable,
969 GError **error)
971 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
972 g_return_val_if_fail (email_address != NULL, NULL);
974 return g_initable_new (
975 E_TYPE_MAIL_AUTOCONFIG,
976 cancellable, error,
977 "registry", registry,
978 "email-address", email_address,
979 "use-domain", use_domain,
980 NULL);
983 void
984 e_mail_autoconfig_new (ESourceRegistry *registry,
985 const gchar *email_address,
986 const gchar *use_domain,
987 gint io_priority,
988 GCancellable *cancellable,
989 GAsyncReadyCallback callback,
990 gpointer user_data)
992 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
993 g_return_if_fail (email_address != NULL);
995 g_async_initable_new_async (
996 E_TYPE_MAIL_AUTOCONFIG,
997 io_priority, cancellable,
998 callback, user_data,
999 "registry", registry,
1000 "email-address", email_address,
1001 "use-domain", use_domain,
1002 NULL);
1005 EMailAutoconfig *
1006 e_mail_autoconfig_finish (GAsyncResult *result,
1007 GError **error)
1009 GObject *source_object;
1010 GObject *autoconfig;
1012 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
1014 source_object = g_async_result_get_source_object (result);
1015 g_return_val_if_fail (source_object != NULL, NULL);
1017 autoconfig = g_async_initable_new_finish (
1018 G_ASYNC_INITABLE (source_object), result, error);
1020 g_object_unref (source_object);
1022 if (autoconfig == NULL)
1023 return NULL;
1025 return E_MAIL_AUTOCONFIG (autoconfig);
1029 * e_mail_autoconfig_get_registry:
1030 * @autoconfig: an #EMailAutoconfig
1032 * Returns the #ESourceRegistry passed to e_mail_autoconfig_new() or
1033 * e_mail_autoconfig_new_sync().
1035 * Returns: an #ESourceRegistry
1037 ESourceRegistry *
1038 e_mail_autoconfig_get_registry (EMailAutoconfig *autoconfig)
1040 g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
1042 return autoconfig->priv->registry;
1045 const gchar *
1046 e_mail_autoconfig_get_email_address (EMailAutoconfig *autoconfig)
1048 g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
1050 return autoconfig->priv->email_address;
1053 const gchar *
1054 e_mail_autoconfig_get_use_domain (EMailAutoconfig *autoconfig)
1056 g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
1058 return autoconfig->priv->use_domain;
1061 gboolean
1062 e_mail_autoconfig_set_imap_details (EMailAutoconfig *autoconfig,
1063 ESource *imap_source)
1065 g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
1066 g_return_val_if_fail (E_IS_SOURCE (imap_source), FALSE);
1068 return mail_autoconfig_set_details (
1069 &autoconfig->priv->imap_result,
1070 imap_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT, "imapx");
1073 gboolean
1074 e_mail_autoconfig_set_pop3_details (EMailAutoconfig *autoconfig,
1075 ESource *pop3_source)
1077 g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
1078 g_return_val_if_fail (E_IS_SOURCE (pop3_source), FALSE);
1080 return mail_autoconfig_set_details (
1081 &autoconfig->priv->pop3_result,
1082 pop3_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT, "pop3");
1085 gboolean
1086 e_mail_autoconfig_set_smtp_details (EMailAutoconfig *autoconfig,
1087 ESource *smtp_source)
1089 g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
1090 g_return_val_if_fail (E_IS_SOURCE (smtp_source), FALSE);
1092 return mail_autoconfig_set_details (
1093 &autoconfig->priv->smtp_result,
1094 smtp_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT, "smtp");
1097 void
1098 e_mail_autoconfig_dump_results (EMailAutoconfig *autoconfig)
1100 const gchar *email_address;
1101 gboolean have_results;
1103 g_return_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig));
1105 email_address = autoconfig->priv->email_address;
1107 have_results =
1108 autoconfig->priv->imap_result.set ||
1109 autoconfig->priv->pop3_result.set ||
1110 autoconfig->priv->smtp_result.set;
1112 if (have_results) {
1113 if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain)
1114 g_print ("Results for <%s> and domain '%s'\n", email_address, autoconfig->priv->use_domain);
1115 else
1116 g_print ("Results for <%s>\n", email_address);
1118 if (autoconfig->priv->imap_result.set) {
1119 g_print (
1120 "IMAP: %s@%s:%u\n",
1121 autoconfig->priv->imap_result.user,
1122 autoconfig->priv->imap_result.host,
1123 autoconfig->priv->imap_result.port);
1126 if (autoconfig->priv->pop3_result.set) {
1127 g_print (
1128 "POP3: %s@%s:%u\n",
1129 autoconfig->priv->pop3_result.user,
1130 autoconfig->priv->pop3_result.host,
1131 autoconfig->priv->pop3_result.port);
1134 if (autoconfig->priv->smtp_result.set) {
1135 g_print (
1136 "SMTP: %s@%s:%u\n",
1137 autoconfig->priv->smtp_result.user,
1138 autoconfig->priv->smtp_result.host,
1139 autoconfig->priv->smtp_result.port);
1142 } else if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain) {
1143 g_print ("No results for <%s> and domain '%s'\n", email_address, autoconfig->priv->use_domain);
1144 } else {
1145 g_print ("No results for <%s>\n", email_address);
1150 * e_mail_autoconfig_copy_results_to_config_lookup:
1151 * @mail_autoconfig: an #EMailAutoconfig
1152 * @config_lookup: an #EConfigLookup
1154 * Copies any valid result from @mail_autoconfig to @config_lookup.
1156 * Since: 3.26
1158 void
1159 e_mail_autoconfig_copy_results_to_config_lookup (EMailAutoconfig *mail_autoconfig,
1160 EConfigLookup *config_lookup)
1162 g_return_if_fail (E_IS_MAIL_AUTOCONFIG (mail_autoconfig));
1163 g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
1165 mail_autoconfig_result_to_config_lookup (mail_autoconfig, config_lookup,
1166 &mail_autoconfig->priv->imap_result,
1167 E_CONFIG_LOOKUP_RESULT_PRIORITY_IMAP,
1168 "imapx",
1169 _("IMAP server"),
1170 E_SOURCE_EXTENSION_MAIL_ACCOUNT);
1172 mail_autoconfig_result_to_config_lookup (mail_autoconfig, config_lookup,
1173 &mail_autoconfig->priv->pop3_result,
1174 E_CONFIG_LOOKUP_RESULT_PRIORITY_POP3,
1175 "pop",
1176 _("POP3 server"),
1177 E_SOURCE_EXTENSION_MAIL_ACCOUNT);
1179 mail_autoconfig_result_to_config_lookup (mail_autoconfig, config_lookup,
1180 &mail_autoconfig->priv->smtp_result,
1181 E_CONFIG_LOOKUP_RESULT_PRIORITY_SMTP,
1182 "smtp",
1183 _("SMTP server"),
1184 E_SOURCE_EXTENSION_MAIL_TRANSPORT);