1 /*
2  * e-mail-autoconfig.c
3  *
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.
7  *
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.
12  *
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/>.
15  *
16  */
17 
18 /* XXX Thoughts on RFC 6186: Use of SRV Records for Locating Email
19  *                           Submission/Access Services
20  *
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.
25  *
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.
32  *
33  *     I'm uncomfortable with this for a number of reasons:
34  *
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.
38  *
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).
45  *
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.
52  *
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.
60  *
61  * Recording my thoughts here for posterity. -- mbarnes
62  */
63 
64 #include "evolution-config.h"
65 
66 #include <string.h>
67 #include <glib/gi18n-lib.h>
68 
69 /* For error codes. */
70 #include <libsoup/soup.h>
71 
72 #include "e-mail-autoconfig.h"
73 
74 #define E_MAIL_AUTOCONFIG_GET_PRIVATE(obj) \
75 	(G_TYPE_INSTANCE_GET_PRIVATE \
76 	((obj), E_TYPE_MAIL_AUTOCONFIG, EMailAutoconfigPrivate))
77 
78 #define AUTOCONFIG_BASE_URI "https://autoconfig.thunderbird.net/v1.1/"
79 
80 #define FAKE_EVOLUTION_USER_STRING "EVOLUTIONUSER"
81 
82 #define ERROR_IS_NOT_FOUND(error) \
83 	(g_error_matches ((error), SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND))
84 
85 typedef struct _EMailAutoconfigResult EMailAutoconfigResult;
86 typedef struct _ParserClosure ParserClosure;
87 
88 struct _EMailAutoconfigResult {
89 	gboolean set;
90 	gchar *user;
91 	gchar *host;
92 	guint16 port;
93 	gchar *auth_mechanism;
94 	CamelNetworkSecurityMethod security_method;
95 };
96 
97 struct _EMailAutoconfigPrivate {
98 	ESourceRegistry *registry;
99 	gchar *email_address;
100 	gchar *email_local_part;
101 	gchar *email_domain_part;
102 	gchar *use_domain;
103 	EMailAutoconfigResult imap_result;
104 	EMailAutoconfigResult pop3_result;
105 	EMailAutoconfigResult smtp_result;
106 };
107 
108 struct _ParserClosure {
109 	EMailAutoconfig *autoconfig;
110 	EMailAutoconfigResult *result;
111 };
112 
113 enum {
114 	PROP_0,
115 	PROP_EMAIL_ADDRESS,
116 	PROP_REGISTRY,
117 	PROP_USE_DOMAIN
118 };
119 
120 /* Forward Declarations */
121 static void	e_mail_autoconfig_initable_init	(GInitableIface *iface);
122 
123 /* By default, the GAsyncInitable interface calls GInitable.init()
124  * 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))125 G_DEFINE_TYPE_WITH_CODE (
126 	EMailAutoconfig,
127 	e_mail_autoconfig,
128 	G_TYPE_OBJECT,
129 	G_IMPLEMENT_INTERFACE (
130 		G_TYPE_INITABLE, e_mail_autoconfig_initable_init)
131 	G_IMPLEMENT_INTERFACE (
132 		G_TYPE_ASYNC_INITABLE, NULL))
133 
134 static void
135 mail_autoconfig_parse_start_element (GMarkupParseContext *context,
136                                      const gchar *element_name,
137                                      const gchar **attribute_names,
138                                      const gchar **attribute_values,
139                                      gpointer user_data,
140                                      GError **error)
141 {
142 	ParserClosure *closure = user_data;
143 	EMailAutoconfigPrivate *priv;
144 	gboolean is_incoming_server;
145 	gboolean is_outgoing_server;
146 
147 	priv = closure->autoconfig->priv;
148 
149 	is_incoming_server = g_str_equal (element_name, "incomingServer");
150 	is_outgoing_server = g_str_equal (element_name, "outgoingServer");
151 
152 	if (is_incoming_server || is_outgoing_server) {
153 		const gchar *type = NULL;
154 
155 		g_markup_collect_attributes (
156 			element_name,
157 			attribute_names,
158 			attribute_values,
159 			error,
160 			G_MARKUP_COLLECT_STRING,
161 			"type", &type,
162 			G_MARKUP_COLLECT_INVALID);
163 
164 		if (g_strcmp0 (type, "imap") == 0)
165 			closure->result = &priv->imap_result;
166 		if (g_strcmp0 (type, "pop3") == 0)
167 			closure->result = &priv->pop3_result;
168 		if (g_strcmp0 (type, "smtp") == 0)
169 			closure->result = &priv->smtp_result;
170 	}
171 }
172 
173 static void
mail_autoconfig_parse_end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)174 mail_autoconfig_parse_end_element (GMarkupParseContext *context,
175                                    const gchar *element_name,
176                                    gpointer user_data,
177                                    GError **error)
178 {
179 	ParserClosure *closure = user_data;
180 	gboolean is_incoming_server;
181 	gboolean is_outgoing_server;
182 
183 	is_incoming_server = g_str_equal (element_name, "incomingServer");
184 	is_outgoing_server = g_str_equal (element_name, "outgoingServer");
185 
186 	if (is_incoming_server || is_outgoing_server)
187 		closure->result = NULL;
188 }
189 
190 /* Returns NULL when not being there */
191 static gchar *
mail_autoconfig_replace_case_insensitive(const gchar * text,const gchar * before,const gchar * after)192 mail_autoconfig_replace_case_insensitive (const gchar *text,
193 					  const gchar *before,
194 					  const gchar *after)
195 {
196 	const gchar *p, *next;
197 	GString *str;
198 	gint find_len;
199 
200 	if (!text)
201 		return NULL;
202 
203 	find_len = strlen (before);
204 	str = g_string_new ("");
205 
206 	p = text;
207 	while (next = camel_strstrcase (p, before), next) {
208 		if (p < next)
209 			g_string_append_len (str, p, next - p);
210 
211 		if (after && *after)
212 			g_string_append (str, after);
213 
214 		p = next + find_len;
215 	}
216 
217 	if (p == text) {
218 		g_string_free (str, TRUE);
219 		return NULL;
220 	}
221 
222 	g_string_append (str, p);
223 
224 	return g_string_free (str, FALSE);
225 }
226 
227 static void
mail_autoconfig_parse_text(GMarkupParseContext * context,const gchar * in_text,gsize in_text_length,gpointer user_data,GError ** error)228 mail_autoconfig_parse_text (GMarkupParseContext *context,
229                             const gchar *in_text,
230                             gsize in_text_length,
231                             gpointer user_data,
232                             GError **error)
233 {
234 	ParserClosure *closure = user_data;
235 	EMailAutoconfigPrivate *priv;
236 	const gchar *element_name, *text;
237 	gchar *to_free;
238 	GString *string;
239 
240 	priv = closure->autoconfig->priv;
241 
242 	if (closure->result == NULL)
243 		return;
244 
245 	to_free = mail_autoconfig_replace_case_insensitive (in_text, FAKE_EVOLUTION_USER_STRING, priv->email_local_part);
246 	text = to_free ? to_free : in_text;
247 
248 	/* Perform the following text substitutions:
249 	 *
250 	 * %EMAILADDRESS%    :  closure->email_address
251 	 * %EMAILLOCALPART%  :  closure->email_local_part
252 	 * %EMAILDOMAIN%     :  closure->email_domain_part
253 	 */
254 	if (strchr (text, '%') == NULL)
255 		string = g_string_new (text);
256 	else {
257 		const gchar *cp = text;
258 
259 		string = g_string_sized_new (256);
260 		while (*cp != '\0') {
261 			const gchar *variable;
262 			const gchar *substitute;
263 
264 			if (*cp != '%') {
265 				g_string_append_c (string, *cp++);
266 				continue;
267 			}
268 
269 			variable = "%EMAILADDRESS%";
270 			substitute = priv->email_address;
271 
272 			if (strncmp (cp, variable, strlen (variable)) == 0) {
273 				g_string_append (string, substitute);
274 				cp += strlen (variable);
275 				continue;
276 			}
277 
278 			variable = "%EMAILLOCALPART%";
279 			substitute = priv->email_local_part;
280 
281 			if (strncmp (cp, variable, strlen (variable)) == 0) {
282 				g_string_append (string, substitute);
283 				cp += strlen (variable);
284 				continue;
285 			}
286 
287 			variable = "%EMAILDOMAIN%";
288 			substitute = priv->use_domain;
289 
290 			if (strncmp (cp, variable, strlen (variable)) == 0) {
291 				g_string_append (string, substitute);
292 				cp += strlen (variable);
293 				continue;
294 			}
295 
296 			g_string_append_c (string, *cp++);
297 		}
298 	}
299 
300 	element_name = g_markup_parse_context_get_element (context);
301 
302 	if (g_str_equal (element_name, "hostname")) {
303 		closure->result->host = g_strdup (string->str);
304 		closure->result->set = TRUE;
305 
306 	} else if (g_str_equal (element_name, "username")) {
307 		closure->result->user = g_strdup (string->str);
308 		closure->result->set = TRUE;
309 
310 	} else if (g_str_equal (element_name, "port")) {
311 		glong port = strtol (string->str, NULL, 10);
312 		if (port == CLAMP (port, 1, G_MAXUINT16)) {
313 			closure->result->port = (guint16) port;
314 			closure->result->set = TRUE;
315 		}
316 
317 	} else if (g_str_equal (element_name, "socketType")) {
318 		if (g_str_equal (string->str, "plain")) {
319 			closure->result->security_method =
320 				CAMEL_NETWORK_SECURITY_METHOD_NONE;
321 			closure->result->set = TRUE;
322 		} else if (g_str_equal (string->str, "SSL")) {
323 			closure->result->security_method =
324 				CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT;
325 			closure->result->set = TRUE;
326 		} else if (g_str_equal (string->str, "STARTTLS")) {
327 			closure->result->security_method =
328 				CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT;
329 			closure->result->set = TRUE;
330 		}
331 
332 	} else if (g_str_equal (element_name, "authentication")) {
333 		gboolean use_plain_auth = FALSE;
334 
335 		/* "password-cleartext" and "plain" are synonymous. */
336 
337 		if (g_str_equal (string->str, "password-cleartext"))
338 			use_plain_auth = TRUE;
339 
340 		if (g_str_equal (string->str, "plain"))
341 			use_plain_auth = TRUE;
342 
343 		if (use_plain_auth) {
344 			gchar *auth_mechanism = NULL;
345 
346 			/* The exact auth name depends on the protocol. */
347 
348 			/* Leave this NULL for IMAP so Camel
349 			 * will issue an IMAP LOGIN command. */
350 			if (closure->result == &priv->imap_result)
351 				auth_mechanism = NULL;
352 
353 			/* Leave this NULL for POP3 so Camel
354 			 * will issue POP3 USER/PASS commands. */
355 			if (closure->result == &priv->pop3_result)
356 				auth_mechanism = NULL;
357 
358 			if (closure->result == &priv->smtp_result)
359 				auth_mechanism = g_strdup ("LOGIN");
360 
361 			closure->result->auth_mechanism = auth_mechanism;
362 			closure->result->set = TRUE;
363 		}
364 
365 		/* "password-encrypted" apparently maps to CRAM-MD5,
366 		 * or at least that's how Thunderbird interprets it. */
367 
368 		if (g_str_equal (string->str, "password-encrypted")) {
369 			closure->result->auth_mechanism = g_strdup ("CRAM-MD5");
370 			closure->result->set = TRUE;
371 		}
372 
373 		/* XXX Other <authentication> values not handled,
374 		 *     but they are corner cases for the most part. */
375 	}
376 
377 	g_string_free (string, TRUE);
378 	g_free (to_free);
379 }
380 
381 static GMarkupParser mail_autoconfig_parser = {
382 	mail_autoconfig_parse_start_element,
383 	mail_autoconfig_parse_end_element,
384 	mail_autoconfig_parse_text
385 };
386 
387 static gchar *
mail_autoconfig_resolve_name_server(const gchar * domain,GCancellable * cancellable,GError ** error)388 mail_autoconfig_resolve_name_server (const gchar *domain,
389                                      GCancellable *cancellable,
390                                      GError **error)
391 {
392 	GResolver *resolver;
393 	GList *records;
394 	gchar *name_server = NULL;
395 
396 	resolver = g_resolver_get_default ();
397 
398 	records = g_resolver_lookup_records (
399 		resolver, domain, G_RESOLVER_RECORD_NS, cancellable, error);
400 
401 	/* This list is sorted per RFC 2782, so use the first item. */
402 	if (records != NULL) {
403 		GVariant *variant = records->data;
404 		g_variant_get_child (variant, 0, "s", &name_server);
405 	}
406 
407 	g_list_free_full (records, (GDestroyNotify) g_variant_unref);
408 
409 	g_object_unref (resolver);
410 
411 	return name_server;
412 }
413 
414 static void
mail_autoconfig_abort_soup_session_cb(GCancellable * cancellable,SoupSession * soup_session)415 mail_autoconfig_abort_soup_session_cb (GCancellable *cancellable,
416                                        SoupSession *soup_session)
417 {
418 	soup_session_abort (soup_session);
419 }
420 
421 static gboolean
mail_autoconfig_lookup_uri_sync(EMailAutoconfig * autoconfig,const gchar * uri,SoupSession * soup_session,GCancellable * cancellable,GError ** error)422 mail_autoconfig_lookup_uri_sync (EMailAutoconfig *autoconfig,
423 				 const gchar *uri,
424 				 SoupSession *soup_session,
425 				 GCancellable *cancellable,
426 				 GError **error)
427 {
428 	SoupMessage *soup_message;
429 	gboolean success;
430 	guint status;
431 
432 	soup_message = soup_message_new (SOUP_METHOD_GET, uri);
433 
434 	if (!soup_message) {
435 		g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
436 			_("Invalid URI: “%s”"), uri);
437 		return FALSE;
438 	}
439 
440 	soup_message_headers_append (
441 		soup_message->request_headers,
442 		"User-Agent", "Evolution/" VERSION VERSION_SUBSTRING " " VERSION_COMMENT);
443 
444 	status = soup_session_send_message (soup_session, soup_message);
445 
446 	success = SOUP_STATUS_IS_SUCCESSFUL (status);
447 
448 	if (success) {
449 		GMarkupParseContext *context;
450 		ParserClosure closure;
451 
452 		closure.autoconfig = autoconfig;
453 		closure.result = NULL;
454 
455 		context = g_markup_parse_context_new (
456 			&mail_autoconfig_parser, 0,
457 			&closure, (GDestroyNotify) NULL);
458 
459 		success = g_markup_parse_context_parse (
460 			context,
461 			soup_message->response_body->data,
462 			soup_message->response_body->length,
463 			error);
464 
465 		if (success)
466 			success = g_markup_parse_context_end_parse (context, error);
467 
468 		g_markup_parse_context_free (context);
469 	} else {
470 		g_set_error_literal (
471 			error, SOUP_HTTP_ERROR,
472 			soup_message->status_code,
473 			soup_message->reason_phrase);
474 	}
475 
476 	g_object_unref (soup_message);
477 
478 	return success;
479 }
480 
481 static gboolean
mail_autoconfig_lookup(EMailAutoconfig * autoconfig,const gchar * domain,const gchar * emailmd5,GCancellable * cancellable,GError ** error)482 mail_autoconfig_lookup (EMailAutoconfig *autoconfig,
483                         const gchar *domain,
484 			const gchar *emailmd5,
485                         GCancellable *cancellable,
486                         GError **error)
487 {
488 	ESourceRegistry *registry;
489 	ESource *proxy_source;
490 	SoupSession *soup_session;
491 	gulong cancel_id = 0;
492 	gchar *uri;
493 	gboolean success = FALSE;
494 
495 	registry = e_mail_autoconfig_get_registry (autoconfig);
496 	proxy_source = e_source_registry_ref_builtin_proxy (registry);
497 
498 	soup_session = soup_session_new_with_options (
499 		SOUP_SESSION_PROXY_RESOLVER, G_PROXY_RESOLVER (proxy_source),
500 		SOUP_SESSION_TIMEOUT, 15,
501 		NULL);
502 
503 	g_object_unref (proxy_source);
504 
505 	if (G_IS_CANCELLABLE (cancellable))
506 		cancel_id = g_cancellable_connect (
507 			cancellable,
508 			G_CALLBACK (mail_autoconfig_abort_soup_session_cb),
509 			g_object_ref (soup_session),
510 			(GDestroyNotify) g_object_unref);
511 
512 	/* First try user configuration in autoconfig.$DOMAIN URL and ignore error */
513 	if (!success && ((error && !*error && !g_cancellable_set_error_if_cancelled (cancellable, error)) || !g_cancellable_is_cancelled (cancellable))) {
514 		uri = g_strconcat ("https://autoconfig.", domain, "/mail/config-v1.1.xml?emailaddress=" FAKE_EVOLUTION_USER_STRING "%40", domain, "&emailmd5=", emailmd5, NULL);
515 		success = mail_autoconfig_lookup_uri_sync (autoconfig, uri, soup_session, cancellable, NULL);
516 		g_free (uri);
517 	}
518 
519 	if (!success && ((error && !*error && !g_cancellable_set_error_if_cancelled (cancellable, error)) || !g_cancellable_is_cancelled (cancellable))) {
520 		uri = g_strconcat ("http://autoconfig.", domain, "/mail/config-v1.1.xml?emailaddress=" FAKE_EVOLUTION_USER_STRING "%40", domain, "&emailmd5=", emailmd5, NULL);
521 		success = mail_autoconfig_lookup_uri_sync (autoconfig, uri, soup_session, cancellable, NULL);
522 		g_free (uri);
523 	}
524 
525 	/* Then with $DOMAIN/.well-known/autoconfig/ and ignore error */
526 	if (!success && ((error && !*error && !g_cancellable_set_error_if_cancelled (cancellable, error)) || !g_cancellable_is_cancelled (cancellable))) {
527 		uri = g_strconcat ("https://", domain, "/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=" FAKE_EVOLUTION_USER_STRING "%40", domain, "&emailmd5=", emailmd5, NULL);
528 		success = mail_autoconfig_lookup_uri_sync (autoconfig, uri, soup_session, cancellable, NULL);
529 		g_free (uri);
530 	}
531 
532 	if (!success && ((error && !*error && !g_cancellable_set_error_if_cancelled (cancellable, error)) || !g_cancellable_is_cancelled (cancellable))) {
533 		uri = g_strconcat ("http://", domain, "/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=" FAKE_EVOLUTION_USER_STRING "%40", domain, "&emailmd5=", emailmd5, NULL);
534 		success = mail_autoconfig_lookup_uri_sync (autoconfig, uri, soup_session, cancellable, NULL);
535 		g_free (uri);
536 	}
537 
538 	/* Final, try the upstream ISPDB and propagate error */
539 	if (!success && ((error && !*error && !g_cancellable_set_error_if_cancelled (cancellable, error)) || !g_cancellable_is_cancelled (cancellable))) {
540 		uri = g_strconcat (AUTOCONFIG_BASE_URI, domain, NULL);
541 		success = mail_autoconfig_lookup_uri_sync (autoconfig, uri, soup_session, cancellable, error);
542 		g_free (uri);
543 	}
544 
545 	if (cancel_id > 0)
546 		g_cancellable_disconnect (cancellable, cancel_id);
547 
548 	g_object_unref (soup_session);
549 
550 	return success;
551 }
552 
553 static gboolean
mail_autoconfig_set_details(ESourceRegistry * registry,EMailAutoconfigResult * result,ESource * source,const gchar * extension_name,const gchar * default_backend_name)554 mail_autoconfig_set_details (ESourceRegistry *registry,
555 			     EMailAutoconfigResult *result,
556 			     ESource *source,
557 			     const gchar *extension_name,
558 			     const gchar *default_backend_name)
559 {
560 	ESourceCamel *camel_ext;
561 	ESourceBackend *backend_ext;
562 	CamelSettings *settings;
563 	const gchar *backend_name;
564 
565 	g_return_val_if_fail (result != NULL, FALSE);
566 
567 	if (!result->set)
568 		return FALSE;
569 
570 	if (!e_source_has_extension (source, extension_name))
571 		return FALSE;
572 
573 	backend_ext = e_source_get_extension (source, extension_name);
574 	backend_name = e_source_backend_get_backend_name (backend_ext);
575 	if (!backend_name || !*backend_name) {
576 		e_source_backend_set_backend_name (backend_ext, default_backend_name);
577 		backend_name = default_backend_name;
578 	}
579 
580 	if (!backend_name)
581 		return FALSE;
582 
583 	extension_name = e_source_camel_get_extension_name (backend_name);
584 	camel_ext = e_source_get_extension (source, extension_name);
585 
586 	settings = e_source_camel_get_settings (camel_ext);
587 	g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), FALSE);
588 
589 	/* Set the security method before the port, to not have it overwritten
590 	   in New Mail Account wizard (binding callback). */
591 	g_object_set (settings,
592 		"auth-mechanism", result->auth_mechanism,
593 		"security-method", result->security_method,
594 		"user", result->user,
595 		"host", result->host,
596 		"port", result->port,
597 		NULL);
598 
599 	if (result->host && registry) {
600 		EOAuth2Service *oauth2_service;
601 
602 		/* Prefer OAuth2, if available */
603 		oauth2_service = e_oauth2_services_find (e_source_registry_get_oauth2_services (registry), source);
604 		if (!oauth2_service) {
605 			oauth2_service = e_oauth2_services_guess (e_source_registry_get_oauth2_services (registry),
606 				backend_name, result->host);
607 		}
608 
609 		if (oauth2_service) {
610 			g_object_set (settings,
611 				"auth-mechanism", e_oauth2_service_get_name (oauth2_service),
612 				NULL);
613 		}
614 
615 		g_clear_object (&oauth2_service);
616 	}
617 
618 	return TRUE;
619 }
620 
621 #define E_TYPE_MAIL_CONFIG_LOOKUP_RESULT \
622 	(e_mail_config_lookup_result_get_type ())
623 #define E_MAIL_CONFIG_LOOKUP_RESULT(obj) \
624 	(G_TYPE_CHECK_INSTANCE_CAST \
625 	((obj), E_TYPE_MAIL_CONFIG_LOOKUP_RESULT, EMailConfigLookupResult))
626 #define E_IS_MAIL_CONFIG_LOOKUP_RESULT(obj) \
627 	(G_TYPE_CHECK_INSTANCE_TYPE \
628 	((obj), E_TYPE_MAIL_CONFIG_LOOKUP_RESULT))
629 
630 typedef struct _EMailConfigLookupResult EMailConfigLookupResult;
631 typedef struct _EMailConfigLookupResultClass EMailConfigLookupResultClass;
632 
633 struct _EMailConfigLookupResult {
634 	/*< private >*/
635 	EConfigLookupResultSimple parent;
636 
637 	EMailAutoconfigResult result;
638 	gchar *extension_name;
639 };
640 
641 struct _EMailConfigLookupResultClass {
642 	/*< private >*/
643 	EConfigLookupResultSimpleClass parent_class;
644 };
645 
646 GType e_mail_config_lookup_result_get_type (void) G_GNUC_CONST;
647 
G_DEFINE_TYPE(EMailConfigLookupResult,e_mail_config_lookup_result,E_TYPE_CONFIG_LOOKUP_RESULT_SIMPLE)648 G_DEFINE_TYPE (EMailConfigLookupResult, e_mail_config_lookup_result, E_TYPE_CONFIG_LOOKUP_RESULT_SIMPLE)
649 
650 static gboolean
651 mail_config_lookup_result_configure_source (EConfigLookupResult *lookup_result,
652 					    EConfigLookup *config_lookup,
653 					    ESource *source)
654 {
655 	EMailConfigLookupResult *mail_result;
656 
657 	g_return_val_if_fail (E_IS_MAIL_CONFIG_LOOKUP_RESULT (lookup_result), FALSE);
658 
659 	mail_result = E_MAIL_CONFIG_LOOKUP_RESULT (lookup_result);
660 
661 	/* No chain up to parent method, not needed here, because not used */
662 	return mail_autoconfig_set_details (
663 		e_config_lookup_get_registry (config_lookup),
664 		&mail_result->result, source,
665 		mail_result->extension_name,
666 		e_config_lookup_result_get_protocol (lookup_result));
667 }
668 
669 static void
mail_config_lookup_result_finalize(GObject * object)670 mail_config_lookup_result_finalize (GObject *object)
671 {
672 	EMailConfigLookupResult *mail_result = E_MAIL_CONFIG_LOOKUP_RESULT (object);
673 
674 	g_free (mail_result->result.user);
675 	g_free (mail_result->result.host);
676 	g_free (mail_result->result.auth_mechanism);
677 	g_free (mail_result->extension_name);
678 
679 	/* Chain up to parent's method. */
680 	G_OBJECT_CLASS (e_mail_config_lookup_result_parent_class)->finalize (object);
681 }
682 
683 static void
e_mail_config_lookup_result_class_init(EMailConfigLookupResultClass * klass)684 e_mail_config_lookup_result_class_init (EMailConfigLookupResultClass *klass)
685 {
686 	EConfigLookupResultSimpleClass *simple_result_class;
687 	GObjectClass *object_class;
688 
689 	object_class = G_OBJECT_CLASS (klass);
690 	object_class->finalize = mail_config_lookup_result_finalize;
691 
692 	simple_result_class = E_CONFIG_LOOKUP_RESULT_SIMPLE_CLASS (klass);
693 	simple_result_class->configure_source = mail_config_lookup_result_configure_source;
694 }
695 
696 static void
e_mail_config_lookup_result_init(EMailConfigLookupResult * mail_result)697 e_mail_config_lookup_result_init (EMailConfigLookupResult *mail_result)
698 {
699 }
700 
701 static EConfigLookupResult *
e_mail_config_lookup_result_new(EConfigLookupResultKind kind,gint priority,const gchar * protocol,const gchar * display_name,const gchar * description,const EMailAutoconfigResult * result,const gchar * extension_name)702 e_mail_config_lookup_result_new (EConfigLookupResultKind kind,
703 				 gint priority,
704 				 const gchar *protocol,
705 				 const gchar *display_name,
706 				 const gchar *description,
707 				 const EMailAutoconfigResult *result,
708 				 const gchar *extension_name)
709 {
710 	EMailConfigLookupResult *mail_result;
711 
712 	g_return_val_if_fail (protocol != NULL, NULL);
713 	g_return_val_if_fail (display_name != NULL, NULL);
714 	g_return_val_if_fail (description != NULL, NULL);
715 	g_return_val_if_fail (result != NULL, NULL);
716 	g_return_val_if_fail (extension_name != NULL, NULL);
717 
718 	mail_result = g_object_new (E_TYPE_MAIL_CONFIG_LOOKUP_RESULT,
719 		"kind", kind,
720 		"priority", priority,
721 		"is-complete", TRUE,
722 		"protocol", protocol,
723 		"display-name", display_name,
724 		"description", description,
725 		"password", NULL,
726 		NULL);
727 
728 	mail_result->result.set = result->set;
729 	mail_result->result.user = g_strdup (result->user);
730 	mail_result->result.host = g_strdup (result->host);
731 	mail_result->result.port = result->port;
732 	mail_result->result.auth_mechanism = g_strdup (result->auth_mechanism);
733 	mail_result->result.security_method = result->security_method;
734 	mail_result->extension_name = g_strdup (extension_name);
735 
736 	return E_CONFIG_LOOKUP_RESULT (mail_result);
737 }
738 
739 static void
mail_autoconfig_result_to_config_lookup(EMailAutoconfig * mail_autoconfig,EConfigLookup * config_lookup,EMailAutoconfigResult * result,gint priority,const gchar * protocol,const gchar * display_name,const gchar * extension_name)740 mail_autoconfig_result_to_config_lookup (EMailAutoconfig *mail_autoconfig,
741 					 EConfigLookup *config_lookup,
742 					 EMailAutoconfigResult *result,
743 					 gint priority,
744 					 const gchar *protocol,
745 					 const gchar *display_name,
746 					 const gchar *extension_name)
747 {
748 	EConfigLookupResult *lookup_result;
749 	EConfigLookupResultKind kind;
750 	GString *description;
751 
752 	g_return_if_fail (E_IS_MAIL_AUTOCONFIG (mail_autoconfig));
753 	g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
754 	g_return_if_fail (result != NULL);
755 	g_return_if_fail (protocol != NULL);
756 	g_return_if_fail (display_name != NULL);
757 	g_return_if_fail (extension_name != NULL);
758 
759 	if (!result->set)
760 		return;
761 
762 	kind = E_CONFIG_LOOKUP_RESULT_MAIL_RECEIVE;
763 	if (g_strcmp0 (extension_name, E_SOURCE_EXTENSION_MAIL_TRANSPORT) == 0)
764 		kind = E_CONFIG_LOOKUP_RESULT_MAIL_SEND;
765 
766 	description = g_string_new ("");
767 
768 	g_string_append_printf (description, _("Host: %s:%d"), result->host, result->port);
769 
770 	if (result->user && *result->user) {
771 		g_string_append_c (description, '\n');
772 		g_string_append_printf (description, _("User: %s"), result->user);
773 	}
774 
775 	g_string_append_c (description, '\n');
776 	g_string_append_printf (description, _("Security method: %s"),
777 		result->security_method == CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT ?  _("TLS") :
778 		result->security_method == CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT ? _("STARTTLS") : _("None"));
779 
780 	if (result->auth_mechanism && *result->auth_mechanism) {
781 		g_string_append_c (description, '\n');
782 		g_string_append_printf (description, _("Authentication mechanism: %s"), result->auth_mechanism);
783 	}
784 
785 	lookup_result = e_mail_config_lookup_result_new (kind, priority, protocol, display_name, description->str, result, extension_name);
786 	e_config_lookup_add_result (config_lookup, lookup_result);
787 
788 	g_string_free (description, TRUE);
789 }
790 
791 static gchar *
mail_autoconfig_calc_emailmd5(const gchar * email_address)792 mail_autoconfig_calc_emailmd5 (const gchar *email_address)
793 {
794 	gchar *lowercase, *emailmd5;
795 
796 	if (!email_address)
797 		return NULL;
798 
799 	lowercase = g_ascii_strdown (email_address, -1);
800 	if (!lowercase || !*lowercase) {
801 		g_free (lowercase);
802 		return NULL;
803 	}
804 
805 	emailmd5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, lowercase, -1);
806 
807 	g_free (lowercase);
808 
809 	return emailmd5;
810 }
811 
812 static void
mail_autoconfig_set_email_address(EMailAutoconfig * autoconfig,const gchar * email_address)813 mail_autoconfig_set_email_address (EMailAutoconfig *autoconfig,
814                                    const gchar *email_address)
815 {
816 	g_return_if_fail (email_address != NULL);
817 	g_return_if_fail (autoconfig->priv->email_address == NULL);
818 
819 	autoconfig->priv->email_address = g_strdup (email_address);
820 }
821 
822 static void
mail_autoconfig_set_use_domain(EMailAutoconfig * autoconfig,const gchar * use_domain)823 mail_autoconfig_set_use_domain (EMailAutoconfig *autoconfig,
824 				const gchar *use_domain)
825 {
826 	if (g_strcmp0 (autoconfig->priv->use_domain, use_domain) != 0) {
827 		g_free (autoconfig->priv->use_domain);
828 		autoconfig->priv->use_domain = g_strdup (use_domain);
829 	}
830 }
831 
832 static void
mail_autoconfig_set_registry(EMailAutoconfig * autoconfig,ESourceRegistry * registry)833 mail_autoconfig_set_registry (EMailAutoconfig *autoconfig,
834                               ESourceRegistry *registry)
835 {
836 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
837 	g_return_if_fail (autoconfig->priv->registry == NULL);
838 
839 	autoconfig->priv->registry = g_object_ref (registry);
840 }
841 
842 static void
mail_autoconfig_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)843 mail_autoconfig_set_property (GObject *object,
844                               guint property_id,
845                               const GValue *value,
846                               GParamSpec *pspec)
847 {
848 	switch (property_id) {
849 		case PROP_EMAIL_ADDRESS:
850 			mail_autoconfig_set_email_address (
851 				E_MAIL_AUTOCONFIG (object),
852 				g_value_get_string (value));
853 			return;
854 
855 		case PROP_REGISTRY:
856 			mail_autoconfig_set_registry (
857 				E_MAIL_AUTOCONFIG (object),
858 				g_value_get_object (value));
859 			return;
860 
861 		case PROP_USE_DOMAIN:
862 			mail_autoconfig_set_use_domain (
863 				E_MAIL_AUTOCONFIG (object),
864 				g_value_get_string (value));
865 			return;
866 	}
867 
868 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
869 }
870 
871 static void
mail_autoconfig_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)872 mail_autoconfig_get_property (GObject *object,
873                               guint property_id,
874                               GValue *value,
875                               GParamSpec *pspec)
876 {
877 	switch (property_id) {
878 		case PROP_EMAIL_ADDRESS:
879 			g_value_set_string (
880 				value,
881 				e_mail_autoconfig_get_email_address (
882 				E_MAIL_AUTOCONFIG (object)));
883 			return;
884 
885 		case PROP_REGISTRY:
886 			g_value_set_object (
887 				value,
888 				e_mail_autoconfig_get_registry (
889 				E_MAIL_AUTOCONFIG (object)));
890 			return;
891 
892 		case PROP_USE_DOMAIN:
893 			g_value_set_string (
894 				value,
895 				e_mail_autoconfig_get_use_domain (
896 				E_MAIL_AUTOCONFIG (object)));
897 			return;
898 	}
899 
900 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
901 }
902 
903 static void
mail_autoconfig_dispose(GObject * object)904 mail_autoconfig_dispose (GObject *object)
905 {
906 	EMailAutoconfigPrivate *priv;
907 
908 	priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (object);
909 
910 	g_clear_object (&priv->registry);
911 
912 	/* Chain up to parent's dispose() method. */
913 	G_OBJECT_CLASS (e_mail_autoconfig_parent_class)->dispose (object);
914 }
915 
916 static void
mail_autoconfig_finalize(GObject * object)917 mail_autoconfig_finalize (GObject *object)
918 {
919 	EMailAutoconfigPrivate *priv;
920 
921 	priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (object);
922 
923 	g_free (priv->email_address);
924 	g_free (priv->email_local_part);
925 	g_free (priv->email_domain_part);
926 	g_free (priv->use_domain);
927 
928 	g_free (priv->imap_result.user);
929 	g_free (priv->imap_result.host);
930 	g_free (priv->imap_result.auth_mechanism);
931 	g_free (priv->pop3_result.user);
932 	g_free (priv->pop3_result.host);
933 	g_free (priv->pop3_result.auth_mechanism);
934 	g_free (priv->smtp_result.user);
935 	g_free (priv->smtp_result.host);
936 	g_free (priv->smtp_result.auth_mechanism);
937 
938 	/* Chain up to parent's finalize() method. */
939 	G_OBJECT_CLASS (e_mail_autoconfig_parent_class)->finalize (object);
940 }
941 
942 static gboolean
mail_autoconfig_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)943 mail_autoconfig_initable_init (GInitable *initable,
944                                GCancellable *cancellable,
945                                GError **error)
946 {
947 	EMailAutoconfig *autoconfig;
948 	const gchar *email_address;
949 	const gchar *domain;
950 	const gchar *cp;
951 	gchar *name_server, *emailmd5;
952 	gboolean success = FALSE;
953 	GError *local_error = NULL;
954 
955 	autoconfig = E_MAIL_AUTOCONFIG (initable);
956 	email_address = e_mail_autoconfig_get_email_address (autoconfig);
957 
958 	if (email_address == NULL) {
959 		g_set_error_literal (
960 			error, G_IO_ERROR,
961 			G_IO_ERROR_INVALID_ARGUMENT,
962 			_("No email address provided"));
963 		return FALSE;
964 	}
965 
966 	cp = strchr (email_address, '@');
967 	if (cp == NULL) {
968 		g_set_error_literal (
969 			error, G_IO_ERROR,
970 			G_IO_ERROR_INVALID_ARGUMENT,
971 			_("Missing domain in email address"));
972 		return FALSE;
973 	}
974 
975 	domain = cp + 1;
976 
977 	autoconfig->priv->email_local_part =
978 		g_strndup (email_address, cp - email_address);
979 	autoconfig->priv->email_domain_part = g_strdup (domain);
980 
981 	if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain)
982 		domain = autoconfig->priv->use_domain;
983 
984 	emailmd5 = mail_autoconfig_calc_emailmd5 (email_address);
985 
986 	/* First try the email address domain verbatim. */
987 	success = mail_autoconfig_lookup (
988 		autoconfig, domain, emailmd5, cancellable, &local_error);
989 
990 	g_warn_if_fail (
991 		(success && local_error == NULL) ||
992 		(!success && local_error != NULL));
993 
994 	if (success) {
995 		g_free (emailmd5);
996 		return TRUE;
997 	}
998 
999 	/* "404 Not Found" errors are non-fatal this time around. */
1000 	if (ERROR_IS_NOT_FOUND (local_error)) {
1001 		g_clear_error (&local_error);
1002 	} else {
1003 		g_propagate_error (error, local_error);
1004 		g_free (emailmd5);
1005 		return FALSE;
1006 	}
1007 
1008 	/* Look up an authoritative name server for the email address
1009 	 * domain according to its "name server" (NS) DNS record. */
1010 	name_server = mail_autoconfig_resolve_name_server (
1011 		domain, cancellable, error);
1012 
1013 	if (!name_server) {
1014 		g_free (emailmd5);
1015 		return FALSE;
1016 	}
1017 
1018 	/* Widdle away segments of the name server domain until
1019 	 * we find a match, or until we widdle down to nothing. */
1020 
1021 	cp = name_server;
1022 	while (cp != NULL && strchr (cp, '.') != NULL) {
1023 		g_clear_error (&local_error);
1024 
1025 		success = mail_autoconfig_lookup (
1026 			autoconfig, cp, emailmd5, cancellable, &local_error);
1027 
1028 		g_warn_if_fail (
1029 			(success && local_error == NULL) ||
1030 			(!success && local_error != NULL));
1031 
1032 		if (success || !ERROR_IS_NOT_FOUND (local_error))
1033 			break;
1034 
1035 		cp = strchr (cp, '.');
1036 		if (cp != NULL)
1037 			cp++;
1038 	}
1039 
1040 	if (!success && !local_error)
1041 		g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Unknown error"));
1042 	else if (local_error)
1043 		g_propagate_error (error, local_error);
1044 
1045 	g_free (name_server);
1046 	g_free (emailmd5);
1047 
1048 	return success;
1049 }
1050 
1051 static void
e_mail_autoconfig_class_init(EMailAutoconfigClass * class)1052 e_mail_autoconfig_class_init (EMailAutoconfigClass *class)
1053 {
1054 	GObjectClass *object_class;
1055 
1056 	g_type_class_add_private (class, sizeof (EMailAutoconfigPrivate));
1057 
1058 	object_class = G_OBJECT_CLASS (class);
1059 	object_class->set_property = mail_autoconfig_set_property;
1060 	object_class->get_property = mail_autoconfig_get_property;
1061 	object_class->dispose = mail_autoconfig_dispose;
1062 	object_class->finalize = mail_autoconfig_finalize;
1063 
1064 	g_object_class_install_property (
1065 		object_class,
1066 		PROP_EMAIL_ADDRESS,
1067 		g_param_spec_string (
1068 			"email-address",
1069 			"Email Address",
1070 			"The address from which to query config data",
1071 			NULL,
1072 			G_PARAM_READWRITE |
1073 			G_PARAM_CONSTRUCT_ONLY |
1074 			G_PARAM_STATIC_STRINGS));
1075 
1076 	g_object_class_install_property (
1077 		object_class,
1078 		PROP_REGISTRY,
1079 		g_param_spec_object (
1080 			"registry",
1081 			"Registry",
1082 			"Data source registry",
1083 			E_TYPE_SOURCE_REGISTRY,
1084 			G_PARAM_READWRITE |
1085 			G_PARAM_CONSTRUCT_ONLY |
1086 			G_PARAM_STATIC_STRINGS));
1087 
1088 	g_object_class_install_property (
1089 		object_class,
1090 		PROP_USE_DOMAIN,
1091 		g_param_spec_string (
1092 			"use-domain",
1093 			"Use Domain",
1094 			"A domain to use, instead of the one from email-address",
1095 			NULL,
1096 			G_PARAM_READWRITE |
1097 			G_PARAM_CONSTRUCT_ONLY |
1098 			G_PARAM_STATIC_STRINGS));
1099 }
1100 
1101 static void
e_mail_autoconfig_initable_init(GInitableIface * iface)1102 e_mail_autoconfig_initable_init (GInitableIface *iface)
1103 {
1104 	iface->init = mail_autoconfig_initable_init;
1105 }
1106 
1107 static void
e_mail_autoconfig_init(EMailAutoconfig * autoconfig)1108 e_mail_autoconfig_init (EMailAutoconfig *autoconfig)
1109 {
1110 	autoconfig->priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (autoconfig);
1111 }
1112 
1113 EMailAutoconfig *
e_mail_autoconfig_new_sync(ESourceRegistry * registry,const gchar * email_address,const gchar * use_domain,GCancellable * cancellable,GError ** error)1114 e_mail_autoconfig_new_sync (ESourceRegistry *registry,
1115                             const gchar *email_address,
1116 			    const gchar *use_domain,
1117                             GCancellable *cancellable,
1118                             GError **error)
1119 {
1120 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1121 	g_return_val_if_fail (email_address != NULL, NULL);
1122 
1123 	return g_initable_new (
1124 		E_TYPE_MAIL_AUTOCONFIG,
1125 		cancellable, error,
1126 		"registry", registry,
1127 		"email-address", email_address,
1128 		"use-domain", use_domain,
1129 		NULL);
1130 }
1131 
1132 void
e_mail_autoconfig_new(ESourceRegistry * registry,const gchar * email_address,const gchar * use_domain,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1133 e_mail_autoconfig_new (ESourceRegistry *registry,
1134                        const gchar *email_address,
1135 		       const gchar *use_domain,
1136                        gint io_priority,
1137                        GCancellable *cancellable,
1138                        GAsyncReadyCallback callback,
1139                        gpointer user_data)
1140 {
1141 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
1142 	g_return_if_fail (email_address != NULL);
1143 
1144 	g_async_initable_new_async (
1145 		E_TYPE_MAIL_AUTOCONFIG,
1146 		io_priority, cancellable,
1147 		callback, user_data,
1148 		"registry", registry,
1149 		"email-address", email_address,
1150 		"use-domain", use_domain,
1151 		NULL);
1152 }
1153 
1154 EMailAutoconfig *
e_mail_autoconfig_finish(GAsyncResult * result,GError ** error)1155 e_mail_autoconfig_finish (GAsyncResult *result,
1156                           GError **error)
1157 {
1158 	GObject *source_object;
1159 	GObject *autoconfig;
1160 
1161 	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
1162 
1163 	source_object = g_async_result_get_source_object (result);
1164 	g_return_val_if_fail (source_object != NULL, NULL);
1165 
1166 	autoconfig = g_async_initable_new_finish (
1167 		G_ASYNC_INITABLE (source_object), result, error);
1168 
1169 	g_object_unref (source_object);
1170 
1171 	if (autoconfig == NULL)
1172 		return NULL;
1173 
1174 	return E_MAIL_AUTOCONFIG (autoconfig);
1175 }
1176 
1177 /**
1178  * e_mail_autoconfig_get_registry:
1179  * @autoconfig: an #EMailAutoconfig
1180  *
1181  * Returns the #ESourceRegistry passed to e_mail_autoconfig_new() or
1182  * e_mail_autoconfig_new_sync().
1183  *
1184  * Returns: an #ESourceRegistry
1185  **/
1186 ESourceRegistry *
e_mail_autoconfig_get_registry(EMailAutoconfig * autoconfig)1187 e_mail_autoconfig_get_registry (EMailAutoconfig *autoconfig)
1188 {
1189 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
1190 
1191 	return autoconfig->priv->registry;
1192 }
1193 
1194 const gchar *
e_mail_autoconfig_get_email_address(EMailAutoconfig * autoconfig)1195 e_mail_autoconfig_get_email_address (EMailAutoconfig *autoconfig)
1196 {
1197 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
1198 
1199 	return autoconfig->priv->email_address;
1200 }
1201 
1202 const gchar *
e_mail_autoconfig_get_use_domain(EMailAutoconfig * autoconfig)1203 e_mail_autoconfig_get_use_domain (EMailAutoconfig *autoconfig)
1204 {
1205 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
1206 
1207 	return autoconfig->priv->use_domain;
1208 }
1209 
1210 gboolean
e_mail_autoconfig_set_imap_details(EMailAutoconfig * autoconfig,ESource * imap_source)1211 e_mail_autoconfig_set_imap_details (EMailAutoconfig *autoconfig,
1212                                     ESource *imap_source)
1213 {
1214 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
1215 	g_return_val_if_fail (E_IS_SOURCE (imap_source), FALSE);
1216 
1217 	return mail_autoconfig_set_details (
1218 		autoconfig->priv->registry,
1219 		&autoconfig->priv->imap_result,
1220 		imap_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT, "imapx");
1221 }
1222 
1223 gboolean
e_mail_autoconfig_set_pop3_details(EMailAutoconfig * autoconfig,ESource * pop3_source)1224 e_mail_autoconfig_set_pop3_details (EMailAutoconfig *autoconfig,
1225                                     ESource *pop3_source)
1226 {
1227 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
1228 	g_return_val_if_fail (E_IS_SOURCE (pop3_source), FALSE);
1229 
1230 	return mail_autoconfig_set_details (
1231 		autoconfig->priv->registry,
1232 		&autoconfig->priv->pop3_result,
1233 		pop3_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT, "pop3");
1234 }
1235 
1236 gboolean
e_mail_autoconfig_set_smtp_details(EMailAutoconfig * autoconfig,ESource * smtp_source)1237 e_mail_autoconfig_set_smtp_details (EMailAutoconfig *autoconfig,
1238                                     ESource *smtp_source)
1239 {
1240 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
1241 	g_return_val_if_fail (E_IS_SOURCE (smtp_source), FALSE);
1242 
1243 	return mail_autoconfig_set_details (
1244 		autoconfig->priv->registry,
1245 		&autoconfig->priv->smtp_result,
1246 		smtp_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT, "smtp");
1247 }
1248 
1249 void
e_mail_autoconfig_dump_results(EMailAutoconfig * autoconfig)1250 e_mail_autoconfig_dump_results (EMailAutoconfig *autoconfig)
1251 {
1252 	const gchar *email_address;
1253 	gboolean have_results;
1254 
1255 	g_return_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig));
1256 
1257 	email_address = autoconfig->priv->email_address;
1258 
1259 	have_results =
1260 		autoconfig->priv->imap_result.set ||
1261 		autoconfig->priv->pop3_result.set ||
1262 		autoconfig->priv->smtp_result.set;
1263 
1264 	if (have_results) {
1265 		if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain)
1266 			g_print ("Results for <%s> and domain '%s'\n", email_address, autoconfig->priv->use_domain);
1267 		else
1268 			g_print ("Results for <%s>\n", email_address);
1269 
1270 		if (autoconfig->priv->imap_result.set) {
1271 			g_print (
1272 				"IMAP: %s@%s:%u\n",
1273 				autoconfig->priv->imap_result.user,
1274 				autoconfig->priv->imap_result.host,
1275 				autoconfig->priv->imap_result.port);
1276 		}
1277 
1278 		if (autoconfig->priv->pop3_result.set) {
1279 			g_print (
1280 				"POP3: %s@%s:%u\n",
1281 				autoconfig->priv->pop3_result.user,
1282 				autoconfig->priv->pop3_result.host,
1283 				autoconfig->priv->pop3_result.port);
1284 		}
1285 
1286 		if (autoconfig->priv->smtp_result.set) {
1287 			g_print (
1288 				"SMTP: %s@%s:%u\n",
1289 				autoconfig->priv->smtp_result.user,
1290 				autoconfig->priv->smtp_result.host,
1291 				autoconfig->priv->smtp_result.port);
1292 		}
1293 
1294 	} else if (autoconfig->priv->use_domain && *autoconfig->priv->use_domain) {
1295 		g_print ("No results for <%s> and domain '%s'\n", email_address, autoconfig->priv->use_domain);
1296 	} else {
1297 		g_print ("No results for <%s>\n", email_address);
1298 	}
1299 }
1300 
1301 /**
1302  * e_mail_autoconfig_copy_results_to_config_lookup:
1303  * @mail_autoconfig: an #EMailAutoconfig
1304  * @config_lookup: an #EConfigLookup
1305  *
1306  * Copies any valid result from @mail_autoconfig to @config_lookup.
1307  *
1308  * Since: 3.26
1309  **/
1310 void
e_mail_autoconfig_copy_results_to_config_lookup(EMailAutoconfig * mail_autoconfig,EConfigLookup * config_lookup)1311 e_mail_autoconfig_copy_results_to_config_lookup (EMailAutoconfig *mail_autoconfig,
1312 						 EConfigLookup *config_lookup)
1313 {
1314 	g_return_if_fail (E_IS_MAIL_AUTOCONFIG (mail_autoconfig));
1315 	g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup));
1316 
1317 	mail_autoconfig_result_to_config_lookup (mail_autoconfig, config_lookup,
1318 		&mail_autoconfig->priv->imap_result,
1319 		E_CONFIG_LOOKUP_RESULT_PRIORITY_IMAP,
1320 		"imapx",
1321 		_("IMAP server"),
1322 		E_SOURCE_EXTENSION_MAIL_ACCOUNT);
1323 
1324 	mail_autoconfig_result_to_config_lookup (mail_autoconfig, config_lookup,
1325 		&mail_autoconfig->priv->pop3_result,
1326 		E_CONFIG_LOOKUP_RESULT_PRIORITY_POP3,
1327 		"pop",
1328 		_("POP3 server"),
1329 		E_SOURCE_EXTENSION_MAIL_ACCOUNT);
1330 
1331 	mail_autoconfig_result_to_config_lookup (mail_autoconfig, config_lookup,
1332 		&mail_autoconfig->priv->smtp_result,
1333 		E_CONFIG_LOOKUP_RESULT_PRIORITY_SMTP,
1334 		"smtp",
1335 		_("SMTP server"),
1336 		E_SOURCE_EXTENSION_MAIL_TRANSPORT);
1337 }
1338