evolution-3.6.4/mail/e-mail-autoconfig.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-autoconfig.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-autoconfig.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-autoconfig.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  * e-mail-autoconfig.c
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  */
 18 
 19 #include "e-mail-autoconfig.h"
 20 
 21 #include <config.h>
 22 #include <string.h>
 23 #include <glib/gi18n-lib.h>
 24 
 25 /* Stuff for DNS querying and message parsing. */
 26 #include <netdb.h>
 27 #include <netinet/in.h>
 28 #include <resolv.h>
 29 #include <arpa/nameser.h>
 30 #if defined(HAVE_ARPA_NAMESER_COMPAT_H) && !defined(GETSHORT)
 31 #include <arpa/nameser_compat.h>
 32 #endif
 33 
 34 /* For error codes. */
 35 #include <libsoup/soup.h>
 36 
 37 #define E_MAIL_AUTOCONFIG_GET_PRIVATE(obj) \
 38 	(G_TYPE_INSTANCE_GET_PRIVATE \
 39 	((obj), E_TYPE_MAIL_AUTOCONFIG, EMailAutoconfigPrivate))
 40 
 41 #define AUTOCONFIG_BASE_URI \
 42 	"http://api.gnome.org/evolution/autoconfig/1.1/"
 43 
 44 /* XXX g_file_load_contents() on an "http://" URI returns error codes
 45  *     in the SOUP_HTTP_ERROR domain instead of the G_IO_ERROR domain.
 46  *     That is both undocumented and unexpected. */
 47 #define ERROR_IS_NOT_FOUND(error) \
 48 	(g_error_matches ((error), SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND))
 49 
 50 typedef struct _ParserClosure ParserClosure;
 51 typedef struct _ResolverClosure ResolverClosure;
 52 
 53 struct _EMailAutoconfigPrivate {
 54 	gchar *email_address;
 55 	gchar *email_local_part;
 56 	gchar *email_domain_part;
 57 	gchar *markup_content;
 58 };
 59 
 60 struct _ParserClosure {
 61 	CamelNetworkSettings *network_settings;
 62 	const gchar *expected_type;
 63 	const gchar *email_address;
 64 	const gchar *email_local_part;
 65 	const gchar *email_domain_part;
 66 	gboolean in_server_element;
 67 	gboolean settings_modified;
 68 };
 69 
 70 struct _ResolverClosure {
 71 	volatile gint ref_count;
 72 	GMainContext *main_context;
 73 	GMainLoop *main_loop;
 74 	gchar *domain_name;
 75 	gchar *name_server;
 76 	GError *error;
 77 };
 78 
 79 enum {
 80 	PROP_0,
 81 	PROP_EMAIL_ADDRESS
 82 };
 83 
 84 /* Forward Declarations */
 85 static void	e_mail_autoconfig_initable_init	(GInitableIface *interface);
 86 
 87 /* By default, the GAsyncInitable interface calls GInitable.init()
 88  * from a separate thread, so we only have to override GInitable. */
 89 G_DEFINE_TYPE_WITH_CODE (
 90 	EMailAutoconfig,
 91 	e_mail_autoconfig,
 92 	G_TYPE_OBJECT,
 93 	G_IMPLEMENT_INTERFACE (
 94 		G_TYPE_INITABLE, e_mail_autoconfig_initable_init)
 95 	G_IMPLEMENT_INTERFACE (
 96 		G_TYPE_ASYNC_INITABLE, NULL))
 97 
 98 static ResolverClosure *
 99 resolver_closure_new (const gchar *domain_name)
100 {
101 	ResolverClosure *closure;
102 
103 	closure = g_slice_new0 (ResolverClosure);
104 	closure->domain_name = g_strdup (domain_name);
105 	closure->main_context = g_main_context_new ();
106 	closure->main_loop = g_main_loop_new (closure->main_context, FALSE);
107 	closure->ref_count = 1;
108 
109 	return closure;
110 }
111 
112 static ResolverClosure *
113 resolver_closure_ref (ResolverClosure *closure)
114 {
115 	g_return_val_if_fail (closure != NULL, NULL);
116 	g_return_val_if_fail (closure->ref_count > 0, NULL);
117 
118 	g_atomic_int_inc (&closure->ref_count);
119 
120 	return closure;
121 }
122 
123 static void
124 resolver_closure_unref (ResolverClosure *closure)
125 {
126 	g_return_if_fail (closure != NULL);
127 	g_return_if_fail (closure->ref_count > 0);
128 
129 	if (g_atomic_int_dec_and_test (&closure->ref_count)) {
130 		g_main_context_unref (closure->main_context);
131 		g_main_loop_unref (closure->main_loop);
132 		g_free (closure->domain_name);
133 		g_free (closure->name_server);
134 		g_clear_error (&closure->error);
135 		g_slice_free (ResolverClosure, closure);
136 	}
137 }
138 
139 static gboolean
140 mail_autoconfig_resolver_idle_quit (gpointer user_data)
141 {
142 	GMainLoop *main_loop = user_data;
143 
144 	g_main_loop_quit (main_loop);
145 
146 	return FALSE;
147 }
148 
149 static void
150 mail_autoconfig_resolver_cancelled (GCancellable *cancellable,
151                                     ResolverClosure *closure)
152 {
153 	GSource *source;
154 
155 	source = g_idle_source_new ();
156 	g_source_set_callback (
157 		source,
158 		mail_autoconfig_resolver_idle_quit,
159 		g_main_loop_ref (closure->main_loop),
160 		(GDestroyNotify) g_main_loop_unref);
161 	g_source_attach (source, closure->main_context);
162 	g_source_unref (source);
163 }
164 
165 static gpointer
166 mail_autoconfig_resolver_thread (gpointer user_data)
167 {
168 	ResolverClosure *closure = user_data;
169 	HEADER *header;
170 	guchar answer[1024];
171 	gchar namebuf[1024];
172 	guchar *end, *cp;
173 	gint count;
174 	gint length;
175 	gint herr;
176 
177 	/* Query DNS for the MX record for the domain name given in the
178 	 * email address.  We need an authoritative name server for it. */
179 
180 	length = res_query (
181 		closure->domain_name, C_IN, T_MX,
182 		answer, sizeof (answer));
183 	herr = h_errno;  /* h_errno is defined in <netdb.h> */
184 
185 	/* Based heavily on _g_resolver_targets_from_res_query().
186 	 * The binary DNS message format is described in RFC 1035. */
187 
188 	if (length <= 0) {
189 		if (length == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
190 			g_set_error (
191 				&closure->error,
192 				G_RESOLVER_ERROR,
193 				G_RESOLVER_ERROR_NOT_FOUND,
194 				_("No mail exchanger record for '%s'"),
195 				closure->domain_name);
196 		else if (herr == TRY_AGAIN)
197 			g_set_error (
198 				&closure->error,
199 				G_RESOLVER_ERROR,
200 				G_RESOLVER_ERROR_TEMPORARY_FAILURE,
201 				_("Temporarily unable to resolve '%s'"),
202 				closure->domain_name);
203 		else
204 			g_set_error (
205 				&closure->error,
206 				G_RESOLVER_ERROR,
207 				G_RESOLVER_ERROR_INTERNAL,
208 				_("Error resolving '%s'"),
209 				closure->domain_name);
210 		goto exit;
211 	}
212 
213 	header = (HEADER *) answer;
214 	cp = answer + sizeof (HEADER);
215 	end = answer + length;
216 
217 	/* Skip the 'question' section. */
218 	count = ntohs (header->qdcount);
219 	while (count-- && cp < end) {
220 		cp += dn_expand (answer, end, cp, namebuf, sizeof (namebuf));
221 		cp += 2;			/* skip QTYPE */
222 		cp += 2;			/* skip QCLASS */
223 	}
224 
225 	/* Skip the 'answers' section. */
226 	count = ntohs (header->ancount);
227 	while (count-- && cp < end) {
228 		guint16 rdlength;
229 		cp += dn_expand (answer, end, cp, namebuf, sizeof (namebuf));
230 		cp += 2;			/* skip TYPE */
231 		cp += 2;			/* skip CLASS */
232 		cp += 4;			/* skip TTL */
233 		GETSHORT (rdlength, cp);	/* read RDLENGTH */
234 		cp += rdlength;			/* skip RDATA */
235 	}
236 
237 	/* Read the 'authority' section. */
238 	count = ntohs (header->nscount);
239 	while (count-- && cp < end) {
240 		guint16 type, qclass, rdlength;
241 		cp += dn_expand (answer, end, cp, namebuf, sizeof (namebuf));
242 		GETSHORT (type, cp);
243 		GETSHORT (qclass, cp);
244 		cp += 4;			/* skip TTL */
245 		GETSHORT (rdlength, cp);
246 
247 		if (type != T_NS || qclass != C_IN) {
248 			cp += rdlength;
249 			continue;
250 		}
251 
252 		cp += dn_expand (answer, end, cp, namebuf, sizeof (namebuf));
253 
254 		/* Pick the first T_NS record we find. */
255 		closure->name_server = g_strdup (namebuf);
256 		break;
257 	}
258 
259 	if (closure->name_server == NULL)
260 		g_set_error (
261 			&closure->error,
262 			G_RESOLVER_ERROR,
263 			G_RESOLVER_ERROR_NOT_FOUND,
264 			_("No authoritative name server for '%s'"),
265 			closure->domain_name);
266 
267 exit:
268 	g_main_loop_quit (closure->main_loop);
269 	resolver_closure_unref (closure);
270 
271 	return NULL;  /* return value is not used */
272 }
273 
274 static gchar *
275 mail_autoconfig_resolve_authority (const gchar *domain,
276                                    GCancellable *cancellable,
277                                    GError **error)
278 {
279 	ResolverClosure *closure;
280 	GThread *resolver_thread;
281 	gchar *name_server = NULL;
282 	gulong cancel_id = 0;
283 
284 	closure = resolver_closure_new (domain);
285 
286 	/* DNS record lookup is not cancellable, so we run it in a
287 	 * separate thread.  We don't join with the thread, however,
288 	 * because if we get cancelled we want to return immediately.
289 	 * So use a reference count on the thread closure and always
290 	 * let the thread run to completion even if we're not around
291 	 * any longer to pick up the result. */
292 	resolver_thread = g_thread_create (
293 		mail_autoconfig_resolver_thread,
294 		resolver_closure_ref (closure),
295 		FALSE /* not joinable */, error);
296 
297 	if (resolver_thread == NULL)
298 		return FALSE;
299 
300 	if (G_IS_CANCELLABLE (cancellable))
301 		cancel_id = g_cancellable_connect (
302 			cancellable,
303 			G_CALLBACK (mail_autoconfig_resolver_cancelled),
304 			resolver_closure_ref (closure),
305 			(GDestroyNotify) resolver_closure_unref);
306 
307 	g_main_loop_run (closure->main_loop);
308 
309 	if (cancel_id > 0)
310 		g_cancellable_disconnect (cancellable, cancel_id);
311 
312 	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
313 		/* do nothing */
314 
315 	} else if (closure->error != NULL) {
316 		g_warn_if_fail (closure->name_server == NULL);
317 		g_propagate_error (error, closure->error);
318 		closure->error = NULL;
319 
320 	} else {
321 		g_warn_if_fail (closure->name_server != NULL);
322 		name_server = closure->name_server;
323 		closure->name_server = NULL;
324 	}
325 
326 	resolver_closure_unref (closure);
327 
328 	return name_server;
329 }
330 
331 static gboolean
332 mail_autoconfig_lookup (EMailAutoconfig *autoconfig,
333                         const gchar *domain,
334                         GCancellable *cancellable,
335                         GError **error)
336 {
337 	GFile *file;
338 	gchar *uri;
339 	gboolean success;
340 
341 	uri = g_strconcat (AUTOCONFIG_BASE_URI, domain, NULL);
342 	file = g_file_new_for_uri (uri);
343 	g_free (uri);
344 
345 	/* Just to make sure we don't leak. */
346 	g_free (autoconfig->priv->markup_content);
347 	autoconfig->priv->markup_content = NULL;
348 
349 	success = g_file_load_contents (
350 		file, cancellable,
351 		&autoconfig->priv->markup_content,
352 		NULL, NULL, error);
353 
354 	g_object_unref (file);
355 
356 	return success;
357 }
358 
359 static void
360 mail_autoconfig_parse_start_element (GMarkupParseContext *context,
361                                      const gchar *element_name,
362                                      const gchar **attribute_names,
363                                      const gchar **attribute_values,
364                                      gpointer user_data,
365                                      GError **error)
366 {
367 	ParserClosure *closure = user_data;
368 	gboolean is_incoming_server;
369 	gboolean is_outgoing_server;
370 
371 	is_incoming_server = g_str_equal (element_name, "incomingServer");
372 	is_outgoing_server = g_str_equal (element_name, "outgoingServer");
373 
374 	if (is_incoming_server || is_outgoing_server) {
375 		const gchar *type = NULL;
376 
377 		g_markup_collect_attributes (
378 			element_name,
379 			attribute_names,
380 			attribute_values,
381 			error,
382 			G_MARKUP_COLLECT_STRING,
383 			"type", &type,
384 			G_MARKUP_COLLECT_INVALID);
385 
386 		closure->in_server_element =
387 			(g_strcmp0 (type, closure->expected_type) == 0);
388 	}
389 }
390 
391 static void
392 mail_autoconfig_parse_end_element (GMarkupParseContext *context,
393                                    const gchar *element_name,
394                                    gpointer user_data,
395                                    GError **error)
396 {
397 	ParserClosure *closure = user_data;
398 	gboolean is_incoming_server;
399 	gboolean is_outgoing_server;
400 
401 	is_incoming_server = g_str_equal (element_name, "incomingServer");
402 	is_outgoing_server = g_str_equal (element_name, "outgoingServer");
403 
404 	if (is_incoming_server || is_outgoing_server)
405 		closure->in_server_element = FALSE;
406 }
407 
408 static void
409 mail_autoconfig_parse_text (GMarkupParseContext *context,
410                             const gchar *text,
411                             gsize text_length,
412                             gpointer user_data,
413                             GError **error)
414 {
415 	ParserClosure *closure = user_data;
416 	const gchar *element_name;
417 	GString *string;
418 
419 	if (!closure->in_server_element)
420 		return;
421 
422 	/* Perform the following text substitutions:
423 	 *
424 	 * %EMAILADDRESS%    :  closure->email_address
425 	 * %EMAILLOCALPART%  :  closure->email_local_part
426 	 * %EMAILDOMAIN%     :  closure->email_domain_part
427 	 */
428 	if (strchr (text, '%') == NULL)
429 		string = g_string_new (text);
430 	else {
431 		const gchar *cp = text;
432 
433 		string = g_string_sized_new (256);
434 		while (*cp != '\0') {
435 			const gchar *variable;
436 			const gchar *substitute;
437 
438 			if (*cp != '%') {
439 				g_string_append_c (string, *cp++);
440 				continue;
441 			}
442 
443 			variable = "%EMAILADDRESS%";
444 			substitute = closure->email_address;
445 
446 			if (strncmp (cp, variable, strlen (variable)) == 0) {
447 				g_string_append (string, substitute);
448 				cp += strlen (variable);
449 				continue;
450 			}
451 
452 			variable = "%EMAILLOCALPART%";
453 			substitute = closure->email_local_part;
454 
455 			if (strncmp (cp, variable, strlen (variable)) == 0) {
456 				g_string_append (string, substitute);
457 				cp += strlen (variable);
458 				continue;
459 			}
460 
461 			variable = "%EMAILDOMAIN%";
462 			substitute = closure->email_domain_part;
463 
464 			if (strncmp (cp, variable, strlen (variable)) == 0) {
465 				g_string_append (string, substitute);
466 				cp += strlen (variable);
467 				continue;
468 			}
469 
470 			g_string_append_c (string, *cp++);
471 		}
472 	}
473 
474 	element_name = g_markup_parse_context_get_element (context);
475 
476 	if (g_str_equal (element_name, "hostname")) {
477 		camel_network_settings_set_host (
478 			closure->network_settings, string->str);
479 		closure->settings_modified = TRUE;
480 
481 	} else if (g_str_equal (element_name, "username")) {
482 		camel_network_settings_set_user (
483 			closure->network_settings, string->str);
484 		closure->settings_modified = TRUE;
485 
486 	} else if (g_str_equal (element_name, "port")) {
487 		glong port = strtol (string->str, NULL, 10);
488 		if (port == CLAMP (port, 1, G_MAXUINT16)) {
489 			camel_network_settings_set_port (
490 				closure->network_settings, (guint16) port);
491 			closure->settings_modified = TRUE;
492 		}
493 
494 	} else if (g_str_equal (element_name, "socketType")) {
495 		if (g_str_equal (string->str, "plain")) {
496 			camel_network_settings_set_security_method (
497 				closure->network_settings,
498 				CAMEL_NETWORK_SECURITY_METHOD_NONE);
499 			closure->settings_modified = TRUE;
500 		} else if (g_str_equal (string->str, "SSL")) {
501 			camel_network_settings_set_security_method (
502 				closure->network_settings,
503 				CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT);
504 			closure->settings_modified = TRUE;
505 		} else if (g_str_equal (string->str, "STARTTLS")) {
506 			camel_network_settings_set_security_method (
507 				closure->network_settings,
508 				CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT);
509 			closure->settings_modified = TRUE;
510 		}
511 	}
512 
513 	/* FIXME Not handling <authentication> elements.
514 	 *       Unclear how some map to SASL mechanisms. */
515 
516 	g_string_free (string, TRUE);
517 }
518 
519 static GMarkupParser mail_autoconfig_parser = {
520 	mail_autoconfig_parse_start_element,
521 	mail_autoconfig_parse_end_element,
522 	mail_autoconfig_parse_text
523 };
524 
525 static gboolean
526 mail_autoconfig_set_details (EMailAutoconfig *autoconfig,
527                              const gchar *expected_type,
528                              ESource *source,
529                              const gchar *extension_name)
530 {
531 	GMarkupParseContext *context;
532 	ESourceCamel *camel_ext;
533 	ESourceBackend *backend_ext;
534 	CamelSettings *settings;
535 	ParserClosure closure;
536 	const gchar *backend_name;
537 	const gchar *markup_content;
538 	gboolean success;
539 
540 	if (!e_source_has_extension (source, extension_name))
541 		return FALSE;
542 
543 	backend_ext = e_source_get_extension (source, extension_name);
544 	backend_name = e_source_backend_get_backend_name (backend_ext);
545 	extension_name = e_source_camel_get_extension_name (backend_name);
546 	camel_ext = e_source_get_extension (source, extension_name);
547 
548 	settings = e_source_camel_get_settings (camel_ext);
549 	g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), FALSE);
550 
551 	markup_content = e_mail_autoconfig_get_markup_content (autoconfig);
552 	g_return_val_if_fail (markup_content != NULL, FALSE);
553 
554 	closure.network_settings = CAMEL_NETWORK_SETTINGS (settings);
555 	closure.expected_type = expected_type;
556 	closure.in_server_element = FALSE;
557 	closure.settings_modified = FALSE;
558 
559 	/* These are used for text substitutions. */
560 	closure.email_address = autoconfig->priv->email_address;
561 	closure.email_local_part = autoconfig->priv->email_local_part;
562 	closure.email_domain_part = autoconfig->priv->email_domain_part;
563 
564 	context = g_markup_parse_context_new (
565 		&mail_autoconfig_parser, 0, &closure, (GDestroyNotify) NULL);
566 
567 	success = g_markup_parse_context_parse (
568 		context, markup_content, strlen (markup_content), NULL);
569 
570 	success &= g_markup_parse_context_end_parse (context, NULL);
571 
572 	/* Did we actually configure anything? */
573 	success &= closure.settings_modified;
574 
575 	g_markup_parse_context_free (context);
576 
577 	return success;
578 }
579 
580 static void
581 mail_autoconfig_set_email_address (EMailAutoconfig *autoconfig,
582                                    const gchar *email_address)
583 {
584 	g_return_if_fail (email_address != NULL);
585 	g_return_if_fail (autoconfig->priv->email_address == NULL);
586 
587 	autoconfig->priv->email_address = g_strdup (email_address);
588 }
589 
590 static void
591 mail_autoconfig_set_property (GObject *object,
592                               guint property_id,
593                               const GValue *value,
594                               GParamSpec *pspec)
595 {
596 	switch (property_id) {
597 		case PROP_EMAIL_ADDRESS:
598 			mail_autoconfig_set_email_address (
599 				E_MAIL_AUTOCONFIG (object),
600 				g_value_get_string (value));
601 			return;
602 	}
603 
604 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
605 }
606 
607 static void
608 mail_autoconfig_get_property (GObject *object,
609                               guint property_id,
610                               GValue *value,
611                               GParamSpec *pspec)
612 {
613 	switch (property_id) {
614 		case PROP_EMAIL_ADDRESS:
615 			g_value_set_string (
616 				value,
617 				e_mail_autoconfig_get_email_address (
618 				E_MAIL_AUTOCONFIG (object)));
619 			return;
620 	}
621 
622 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
623 }
624 
625 static void
626 mail_autoconfig_finalize (GObject *object)
627 {
628 	EMailAutoconfigPrivate *priv;
629 
630 	priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (object);
631 
632 	g_free (priv->email_address);
633 	g_free (priv->email_local_part);
634 	g_free (priv->email_domain_part);
635 	g_free (priv->markup_content);
636 
637 	/* Chain up to parent's finalize() method. */
638 	G_OBJECT_CLASS (e_mail_autoconfig_parent_class)->finalize (object);
639 }
640 
641 static gboolean
642 mail_autoconfig_initable_init (GInitable *initable,
643                                GCancellable *cancellable,
644                                GError **error)
645 {
646 	EMailAutoconfig *autoconfig;
647 	const gchar *email_address;
648 	const gchar *domain;
649 	const gchar *cp;
650 	gchar *name_server;
651 	gboolean success = FALSE;
652 	GError *local_error = NULL;
653 
654 	autoconfig = E_MAIL_AUTOCONFIG (initable);
655 	email_address = e_mail_autoconfig_get_email_address (autoconfig);
656 
657 	if (email_address == NULL) {
658 		g_set_error_literal (
659 			error, G_IO_ERROR,
660 			G_IO_ERROR_INVALID_ARGUMENT,
661 			_("No email address provided"));
662 		return FALSE;
663 	}
664 
665 	cp = strchr (email_address, '@');
666 	if (cp == NULL) {
667 		g_set_error_literal (
668 			error, G_IO_ERROR,
669 			G_IO_ERROR_INVALID_ARGUMENT,
670 			_("Missing domain in email address"));
671 		return FALSE;
672 	}
673 
674 	domain = cp + 1;
675 
676 	autoconfig->priv->email_local_part =
677 		g_strndup (email_address, cp - email_address);
678 	autoconfig->priv->email_domain_part = g_strdup (domain);
679 
680 	/* First try the email address domain verbatim. */
681 	success = mail_autoconfig_lookup (
682 		autoconfig, domain, cancellable, &local_error);
683 
684 	g_warn_if_fail (
685 		(success && local_error == NULL) ||
686 		(!success && local_error != NULL));
687 
688 	if (success)
689 		return TRUE;
690 
691 	/* "404 Not Found" errors are non-fatal this time around. */
692 	if (ERROR_IS_NOT_FOUND (local_error)) {
693 		g_clear_error (&local_error);
694 	} else {
695 		g_propagate_error (error, local_error);
696 		return FALSE;
697 	}
698 
699 	/* Look up an authoritative name server for the email address
700 	 * domain according to its "mail exchanger" (MX) DNS record. */
701 	name_server = mail_autoconfig_resolve_authority (
702 		domain, cancellable, error);
703 
704 	if (name_server == NULL)
705 		return FALSE;
706 
707 	/* Widdle away segments of the name server domain until
708 	 * we find a match, or until we widdle down to nothing. */
709 
710 	cp = name_server;
711 	while (cp != NULL && strchr (cp, '.') != NULL) {
712 		g_clear_error (&local_error);
713 
714 		success = mail_autoconfig_lookup (
715 			autoconfig, cp, cancellable, &local_error);
716 
717 		g_warn_if_fail (
718 			(success && local_error == NULL) ||
719 			(!success && local_error != NULL));
720 
721 		if (success || !ERROR_IS_NOT_FOUND (local_error))
722 			break;
723 
724 		cp = strchr (cp, '.');
725 		if (cp != NULL)
726 			cp++;
727 	}
728 
729 	if (local_error != NULL)
730 		g_propagate_error (error, local_error);
731 
732 	g_free (name_server);
733 
734 	return success;
735 }
736 
737 static void
738 e_mail_autoconfig_class_init (EMailAutoconfigClass *class)
739 {
740 	GObjectClass *object_class;
741 
742 	g_type_class_add_private (class, sizeof (EMailAutoconfigPrivate));
743 
744 	object_class = G_OBJECT_CLASS (class);
745 	object_class->set_property = mail_autoconfig_set_property;
746 	object_class->get_property = mail_autoconfig_get_property;
747 	object_class->finalize = mail_autoconfig_finalize;
748 
749 	g_object_class_install_property (
750 		object_class,
751 		PROP_EMAIL_ADDRESS,
752 		g_param_spec_string (
753 			"email-address",
754 			"Email Address",
755 			"The address from which to query config data",
756 			NULL,
757 			G_PARAM_READWRITE |
758 			G_PARAM_CONSTRUCT_ONLY |
759 			G_PARAM_STATIC_STRINGS));
760 }
761 
762 static void
763 e_mail_autoconfig_initable_init (GInitableIface *interface)
764 {
765 	interface->init = mail_autoconfig_initable_init;
766 }
767 
768 static void
769 e_mail_autoconfig_init (EMailAutoconfig *autoconfig)
770 {
771 	autoconfig->priv = E_MAIL_AUTOCONFIG_GET_PRIVATE (autoconfig);
772 }
773 
774 EMailAutoconfig *
775 e_mail_autoconfig_new_sync (const gchar *email_address,
776                             GCancellable *cancellable,
777                             GError **error)
778 {
779 	g_return_val_if_fail (email_address != NULL, NULL);
780 
781 	return g_initable_new (
782 		E_TYPE_MAIL_AUTOCONFIG,
783 		cancellable, error,
784 		"email-address", email_address,
785 		NULL);
786 }
787 
788 void
789 e_mail_autoconfig_new (const gchar *email_address,
790                        gint io_priority,
791                        GCancellable *cancellable,
792                        GAsyncReadyCallback callback,
793                        gpointer user_data)
794 {
795 	g_return_if_fail (email_address != NULL);
796 
797 	g_async_initable_new_async (
798 		E_TYPE_MAIL_AUTOCONFIG,
799 		io_priority, cancellable,
800 		callback, user_data,
801 		"email-address", email_address,
802 		NULL);
803 }
804 
805 EMailAutoconfig *
806 e_mail_autoconfig_finish (GAsyncResult *result,
807                           GError **error)
808 {
809 	GObject *source_object;
810 	GObject *autoconfig;
811 
812 	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
813 
814 	source_object = g_async_result_get_source_object (result);
815 	g_return_val_if_fail (source_object != NULL, NULL);
816 
817 	autoconfig = g_async_initable_new_finish (
818 		G_ASYNC_INITABLE (source_object), result, error);
819 
820 	g_object_unref (source_object);
821 
822 	if (autoconfig == NULL)
823 		return NULL;
824 
825 	return E_MAIL_AUTOCONFIG (autoconfig);
826 }
827 
828 const gchar *
829 e_mail_autoconfig_get_email_address (EMailAutoconfig *autoconfig)
830 {
831 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
832 
833 	return autoconfig->priv->email_address;
834 }
835 
836 const gchar *
837 e_mail_autoconfig_get_markup_content (EMailAutoconfig *autoconfig)
838 {
839 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), NULL);
840 
841 	return autoconfig->priv->markup_content;
842 }
843 
844 gboolean
845 e_mail_autoconfig_set_imap_details (EMailAutoconfig *autoconfig,
846                                     ESource *imap_source)
847 {
848 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
849 	g_return_val_if_fail (E_IS_SOURCE (imap_source), FALSE);
850 
851 	return mail_autoconfig_set_details (
852 		autoconfig, "imap", imap_source,
853 		E_SOURCE_EXTENSION_MAIL_ACCOUNT);
854 }
855 
856 gboolean
857 e_mail_autoconfig_set_pop3_details (EMailAutoconfig *autoconfig,
858                                     ESource *pop3_source)
859 {
860 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
861 	g_return_val_if_fail (E_IS_SOURCE (pop3_source), FALSE);
862 
863 	return mail_autoconfig_set_details (
864 		autoconfig, "pop3", pop3_source,
865 		E_SOURCE_EXTENSION_MAIL_ACCOUNT);
866 }
867 
868 gboolean
869 e_mail_autoconfig_set_smtp_details (EMailAutoconfig *autoconfig,
870                                     ESource *smtp_source)
871 {
872 	g_return_val_if_fail (E_IS_MAIL_AUTOCONFIG (autoconfig), FALSE);
873 	g_return_val_if_fail (E_IS_SOURCE (smtp_source), FALSE);
874 
875 	return mail_autoconfig_set_details (
876 		autoconfig, "smtp", smtp_source,
877 		E_SOURCE_EXTENSION_MAIL_TRANSPORT);
878 }