示例#1
0
gchar *
camel_ews_utils_get_host_name (CamelSettings *settings)
{
	CamelURL *url;
	gchar *host = NULL, *hosturl;

	g_return_val_if_fail (settings != NULL, NULL);

	hosturl = camel_ews_settings_dup_hosturl (CAMEL_EWS_SETTINGS (settings));

	url = camel_url_new (hosturl, NULL);
	if (url) {
		host = g_strdup (url->host);
		camel_url_free (url);
	}

	if (!host || !*host) {
		g_free (host);
		host = camel_network_settings_dup_host (CAMEL_NETWORK_SETTINGS (settings));
	}

	g_free (hosturl);

	return host;
}
示例#2
0
static gchar *
pop3_store_get_name (CamelService *service,
                     gboolean brief)
{
	CamelNetworkSettings *network_settings;
	CamelSettings *settings;
	gchar *host;
	gchar *user;
	gchar *name;

	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	user = camel_network_settings_dup_user (network_settings);

	g_object_unref (settings);

	if (brief)
		name = g_strdup_printf (
			_("POP3 server %s"), host);
	else
		name = g_strdup_printf (
			_("POP3 server for %s on %s"), user, host);

	g_free (host);
	g_free (user);

	return name;
}
static gchar *
smtp_transport_get_name (CamelService *service,
                         gboolean brief)
{
	CamelNetworkSettings *network_settings;
	CamelSettings *settings;
	gchar *host;
	gchar *name;

	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);

	g_object_unref (settings);

	if (brief)
		name = g_strdup_printf (
			_("SMTP server %s"), host);
	else
		name = g_strdup_printf (
			_("SMTP mail delivery via %s"), host);

	g_free (host);

	return name;
}
示例#4
0
static void
smtp_settings_get_property (GObject *object,
                            guint property_id,
                            GValue *value,
                            GParamSpec *pspec)
{
	switch (property_id) {
		case PROP_AUTH_MECHANISM:
			g_value_take_string (
				value,
				camel_network_settings_dup_auth_mechanism (
				CAMEL_NETWORK_SETTINGS (object)));
			return;

		case PROP_HOST:
			g_value_take_string (
				value,
				camel_network_settings_dup_host (
				CAMEL_NETWORK_SETTINGS (object)));
			return;

		case PROP_PORT:
			g_value_set_uint (
				value,
				camel_network_settings_get_port (
				CAMEL_NETWORK_SETTINGS (object)));
			return;

		case PROP_SECURITY_METHOD:
			g_value_set_enum (
				value,
				camel_network_settings_get_security_method (
				CAMEL_NETWORK_SETTINGS (object)));
			return;

		case PROP_USER:
			g_value_take_string (
				value,
				camel_network_settings_dup_user (
				CAMEL_NETWORK_SETTINGS (object)));
			return;
	}

	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
smtp_debug_print_server_name (CamelService *service,
			      const gchar *what)
{
	CamelNetworkSettings *network_settings;
	CamelSettings *settings;
	gchar *host;
	gint port;

	if (d(1) + 0 == 0)
		return;

	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	port = camel_network_settings_get_port (network_settings);

	g_object_unref (settings);

	fprintf (stderr, "[SMTP] %s server %s:%d from account %s\r\n", what, host, port, camel_service_get_uid (service));

	g_free (host);
}
示例#6
0
static CamelAuthenticationResult
pop3_store_authenticate_sync (CamelService *service,
                              const gchar *mechanism,
                              GCancellable *cancellable,
                              GError **error)
{
	CamelPOP3Store *store = CAMEL_POP3_STORE (service);
	CamelNetworkSettings *network_settings;
	CamelAuthenticationResult result;
	CamelSettings *settings;
	CamelPOP3Command *pcu = NULL;
	CamelPOP3Command *pcp = NULL;
	CamelPOP3Engine *pop3_engine;
	const gchar *password;
	gchar *host;
	gchar *user;
	gint status;

	password = camel_service_get_password (service);

	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	user = camel_network_settings_dup_user (network_settings);

	g_object_unref (settings);

	pop3_engine = camel_pop3_store_ref_engine (store);
	if (!pop3_engine) {
		g_set_error_literal (
			error, CAMEL_SERVICE_ERROR,
			CAMEL_SERVICE_ERROR_UNAVAILABLE,
			_("You must be working online to complete this operation"));
		result = CAMEL_AUTHENTICATION_ERROR;
		goto exit;
	}

	if (mechanism == NULL) {
		if (password == NULL) {
			g_set_error_literal (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("Authentication password not available"));
			result = CAMEL_AUTHENTICATION_ERROR;
			goto exit;
		}

		/* pop engine will take care of pipelining ability */
		pcu = camel_pop3_engine_command_new (
			pop3_engine, 0, NULL, NULL, cancellable, error,
			"USER %s\r\n", user);
		pcp = camel_pop3_engine_command_new (
			pop3_engine, 0, NULL, NULL, cancellable, error,
			"PASS %s\r\n", password);

	} else if (strcmp (mechanism, "+APOP") == 0 && pop3_engine->apop) {
		gchar *secret, *md5asc, *d;

		if (password == NULL) {
			g_set_error_literal (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("Authentication password not available"));
			result = CAMEL_AUTHENTICATION_ERROR;
			goto exit;
		}

		d = pop3_engine->apop;

		while (*d != '\0') {
			if (!isascii ((gint) * d)) {

				/* Translators: Do not translate APOP. */
				g_set_error (
					error, CAMEL_SERVICE_ERROR,
					CAMEL_SERVICE_ERROR_URL_INVALID,
					_("Unable to connect to POP server %s:	"
					"Invalid APOP ID received. Impersonation "
					"attack suspected. Please contact your admin."),
					host);

				result = CAMEL_AUTHENTICATION_ERROR;
				goto exit;
			}
			d++;
		}

		secret = g_alloca (
			strlen (pop3_engine->apop) +
			strlen (password) + 1);
		sprintf (secret, "%s%s", pop3_engine->apop, password);
		md5asc = g_compute_checksum_for_string (
			G_CHECKSUM_MD5, secret, -1);
		pcp = camel_pop3_engine_command_new (
			pop3_engine, 0, NULL, NULL, cancellable, error,
			"APOP %s %s\r\n", user, md5asc);
		g_free (md5asc);

	} else {
		GList *link;

		link = pop3_engine->auth;
		while (link != NULL) {
			CamelServiceAuthType *auth = link->data;

			if (g_strcmp0 (auth->authproto, mechanism) == 0) {
				result = try_sasl (
					store, mechanism,
					cancellable, error);
				goto exit;
			}
			link = g_list_next (link);
		}

		g_set_error (
			error, CAMEL_SERVICE_ERROR,
			CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
			_("No support for %s authentication"), mechanism);
		result = CAMEL_AUTHENTICATION_ERROR;
		goto exit;
	}

	while ((status = camel_pop3_engine_iterate (pop3_engine, pcp, cancellable, error)) > 0)
		;

	if (status == -1) {
		g_prefix_error (
			error,
			_("Unable to connect to POP server %s.\n"
			"Error sending password: "******": " separator. */
			_("Unable to connect to POP server %s.\n"
			"Error sending username%s"),
			host, (tmp != NULL) ? tmp : "");
		g_free (tmp);
		result = CAMEL_AUTHENTICATION_ERROR;

	} else if (pcp->state != CAMEL_POP3_COMMAND_OK) {
		result = CAMEL_AUTHENTICATION_REJECTED;
	} else {
		result = CAMEL_AUTHENTICATION_ACCEPTED;
	}

	camel_pop3_engine_command_free (pop3_engine, pcp);

	if (pcu != NULL)
		camel_pop3_engine_command_free (pop3_engine, pcu);

exit:
	g_free (host);
	g_free (user);

	g_clear_object (&pop3_engine);

	return result;
}
示例#7
0
static CamelAuthenticationResult
try_sasl (CamelPOP3Store *store,
          const gchar *mechanism,
          GCancellable *cancellable,
          GError **error)
{
	CamelPOP3Engine *pop3_engine;
	CamelPOP3Stream *pop3_stream;
	CamelNetworkSettings *network_settings;
	CamelAuthenticationResult result;
	CamelSettings *settings;
	CamelService *service;
	guchar *line, *resp;
	CamelSasl *sasl = NULL;
	gchar *string;
	gchar *host;
	guint len;
	gint ret;

	service = CAMEL_SERVICE (store);

	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);

	g_object_unref (settings);

	pop3_engine = camel_pop3_store_ref_engine (store);
	if (!pop3_engine) {
		g_set_error_literal (
			error, CAMEL_SERVICE_ERROR,
			CAMEL_SERVICE_ERROR_UNAVAILABLE,
			_("You must be working online to complete this operation"));
		result = CAMEL_AUTHENTICATION_ERROR;
		goto exit;
	}

	pop3_stream = pop3_engine->stream;

	sasl = camel_sasl_new ("pop", mechanism, service);
	if (sasl == NULL) {
		g_set_error (
			error, CAMEL_SERVICE_ERROR,
			CAMEL_SERVICE_ERROR_URL_INVALID,
			_("No support for %s authentication"), mechanism);
		result = CAMEL_AUTHENTICATION_ERROR;
		goto exit;
	}

	string = g_strdup_printf ("AUTH %s\r\n", mechanism);
	ret = camel_stream_write_string (
		CAMEL_STREAM (pop3_stream), string, cancellable, error);
	g_free (string);

	if (ret == -1)
		goto ioerror;

	while (1) {
		GError *local_error = NULL;

		if (camel_pop3_stream_line (pop3_stream, &line, &len, cancellable, error) == -1)
			goto ioerror;

		if (strncmp ((gchar *) line, "+OK", 3) == 0) {
			result = CAMEL_AUTHENTICATION_ACCEPTED;
			break;
		}

		if (strncmp ((gchar *) line, "-ERR", 4) == 0) {
			result = CAMEL_AUTHENTICATION_REJECTED;
			break;
		}

		/* If we dont get continuation, or the sasl object's run out
		 * of work, or we dont get a challenge, its a protocol error,
		 * so fail, and try reset the server. */
		if (strncmp ((gchar *) line, "+ ", 2) != 0
		    || camel_sasl_get_authenticated (sasl)
		    || (resp = (guchar *) camel_sasl_challenge_base64_sync (sasl, (const gchar *) line + 2, cancellable, &local_error)) == NULL) {
			camel_stream_write_string (
				CAMEL_STREAM (pop3_stream), "*\r\n", cancellable, NULL);
			camel_pop3_stream_line (pop3_stream, &line, &len, cancellable, NULL);

			if (local_error) {
				g_propagate_error (error, local_error);
				local_error = NULL;
				goto ioerror;
			}

			g_set_error (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("Cannot login to POP server %s: "
				"SASL Protocol error"), host);
			result = CAMEL_AUTHENTICATION_ERROR;
			goto exit;
		}

		string = g_strdup_printf ("%s\r\n", resp);
		ret = camel_stream_write_string (
			CAMEL_STREAM (pop3_stream), string, cancellable, error);
		g_free (string);

		g_free (resp);

		if (ret == -1)
			goto ioerror;

	}

	goto exit;

ioerror:
	g_prefix_error (
		error, _("Failed to authenticate on POP server %s: "), host);
	result = CAMEL_AUTHENTICATION_ERROR;

exit:
	if (sasl != NULL)
		g_object_unref (sasl);

	g_free (host);

	g_clear_object (&pop3_engine);

	return result;
}
示例#8
0
static gboolean
connect_to_server (CamelService *service,
                   GCancellable *cancellable,
                   GError **error)
{
	CamelPOP3Store *store = CAMEL_POP3_STORE (service);
	CamelNetworkSettings *network_settings;
	CamelNetworkSecurityMethod method;
	CamelSettings *settings;
	CamelStream *tcp_stream;
	CamelPOP3Engine *pop3_engine = NULL;
	CamelPOP3Command *pc;
	gboolean disable_extensions;
	gboolean success = TRUE;
	gchar *host;
	guint32 flags = 0;
	gint ret;
	GError *local_error = NULL;

	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	method = camel_network_settings_get_security_method (network_settings);

	disable_extensions = camel_pop3_settings_get_disable_extensions (
		CAMEL_POP3_SETTINGS (settings));

	g_object_unref (settings);

	tcp_stream = camel_network_service_connect_sync (
		CAMEL_NETWORK_SERVICE (service), cancellable, error);

	if (tcp_stream == NULL) {
		success = FALSE;
		goto exit;
	}

	/* parent class connect initialization */
	if (CAMEL_SERVICE_CLASS (camel_pop3_store_parent_class)->
		connect_sync (service, cancellable, error) == FALSE) {
		g_object_unref (tcp_stream);
		success = FALSE;
		goto exit;
	}

	if (disable_extensions)
		flags |= CAMEL_POP3_ENGINE_DISABLE_EXTENSIONS;

	if (!(pop3_engine = camel_pop3_engine_new (tcp_stream, flags, cancellable, &local_error)) ||
	    local_error != NULL) {
		if (local_error)
			g_propagate_error (error, local_error);
		else
			g_set_error (
				error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
				_("Failed to read a valid greeting from POP server %s"),
				host);
		g_object_unref (tcp_stream);
		success = FALSE;
		goto exit;
	}

	if (method != CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT) {
		g_object_unref (tcp_stream);
		goto exit;
	}

	if (!(pop3_engine->capa & CAMEL_POP3_CAP_STLS)) {
		g_set_error (
			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
			_("Failed to connect to POP server %s in secure mode: %s"),
			host, _("STLS not supported by server"));
		goto stls_exception;
	}

	pc = camel_pop3_engine_command_new (
		pop3_engine, 0, NULL, NULL,
		cancellable, error, "STLS\r\n");
	while (camel_pop3_engine_iterate (pop3_engine, NULL, cancellable, NULL) > 0)
		;

	ret = pc->state == CAMEL_POP3_COMMAND_OK;
	camel_pop3_engine_command_free (pop3_engine, pc);

	if (ret == FALSE) {
		gchar *tmp;

		tmp = get_valid_utf8_error ((gchar *) pop3_engine->line);
		g_set_error (
			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
			/* Translators: Last %s is an optional
			 * explanation beginning with ": " separator. */
			_("Failed to connect to POP server %s in secure mode%s"),
			host, (tmp != NULL) ? tmp : "");
		g_free (tmp);
		goto stls_exception;
	}

	/* Okay, now toggle SSL/TLS mode */
	ret = camel_tcp_stream_ssl_enable_ssl (
		CAMEL_TCP_STREAM_SSL (tcp_stream), cancellable, error);

	if (ret == -1) {
		g_prefix_error (
			error,
			_("Failed to connect to POP server %s in secure mode: "),
			host);
		goto stls_exception;
	}

	g_object_unref (tcp_stream);

	/* rfc2595, section 4 states that after a successful STLS
	 * command, the client MUST discard prior CAPA responses */
	if (!camel_pop3_engine_reget_capabilities (pop3_engine, cancellable, error))
		goto exception;

	goto exit;

stls_exception:
	/* As soon as we send a STLS command, all hope
	 * is lost of a clean QUIT if problems arise. */
	/* if (clean_quit) {
		/ * try to disconnect cleanly * /
		pc = camel_pop3_engine_command_new (
			pop3_engine, 0, NULL, NULL,
			cancellable, NULL, "QUIT\r\n");
		while (camel_pop3_engine_iterate (pop3_engine, NULL, cancellable, NULL) > 0)
			;
		camel_pop3_engine_command_free (pop3_engine, pc);
	}*/

exception:
	g_object_unref (tcp_stream);

	g_clear_object (&pop3_engine);

	success = FALSE;

exit:
	g_free (host);

	g_mutex_lock (&store->priv->property_lock);
	if (pop3_engine != NULL)
		store->priv->engine = g_object_ref (pop3_engine);
	g_mutex_unlock (&store->priv->property_lock);

	g_clear_object (&pop3_engine);

	return success;
}
示例#9
0
static gchar *
format_service_name (CamelService *service)
{
	CamelProvider *provider;
	CamelSettings *settings;
	gchar *service_name = NULL;
	const gchar *display_name;
	gchar *pretty_url = NULL;
	gchar *host = NULL;
	gchar *path = NULL;
	gchar *user = NULL;
	gchar *cp;
	gboolean have_host = FALSE;
	gboolean have_path = FALSE;
	gboolean have_user = FALSE;

	provider = camel_service_get_provider (service);
	display_name = camel_service_get_display_name (service);

	settings = camel_service_ref_settings (service);

	if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
		host = camel_network_settings_dup_host (
			CAMEL_NETWORK_SETTINGS (settings));
		have_host = (host != NULL) && (*host != '\0');

		user = camel_network_settings_dup_user (
			CAMEL_NETWORK_SETTINGS (settings));
		have_user = (user != NULL) && (*user != '\0');
	}

	if (CAMEL_IS_LOCAL_SETTINGS (settings)) {
		path = camel_local_settings_dup_path (
			CAMEL_LOCAL_SETTINGS (settings));
		have_path = (path != NULL) && (*path != '\0');
	}

	g_object_unref (settings);

	/* Shorten user names with '@', since multiple '@' in a
	 * 'user@host' label look weird.  This is just supposed
	 * to be a hint anyway so it doesn't matter if it's not
	 * strictly correct. */
	if (have_user && (cp = strchr (user, '@')) != NULL)
		*cp = '\0';

	g_return_val_if_fail (provider != NULL, NULL);

	/* This should never happen, but if the service has no
	 * display name, fall back to the generic service name. */
	if (display_name == NULL || *display_name == '\0') {
		service_name = camel_service_get_name (service, TRUE);
		display_name = service_name;
	}

	if (have_host && have_user) {
		pretty_url = g_markup_printf_escaped (
			"<b>%s</b> <small>(%s@%s)</small>",
			display_name, user, host);
	} else if (have_host) {
		pretty_url = g_markup_printf_escaped (
			"<b>%s</b> <small>(%s)</small>",
			display_name, host);
	} else if (have_path) {
		pretty_url = g_markup_printf_escaped (
			"<b>%s</b> <small>(%s)</small>",
			display_name, path);
	} else {
		pretty_url = g_markup_printf_escaped (
			"<b>%s</b>", display_name);
	}

	g_free (service_name);
	g_free (host);
	g_free (path);
	g_free (user);

	return pretty_url;
}
static gboolean
smtp_transport_connect_sync (CamelService *service,
                             GCancellable *cancellable,
                             GError **error)
{
	CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
	CamelNetworkSettings *network_settings;
	CamelSettings *settings;
	gchar *host;
	gchar *mechanism;
	gboolean auth_required;
	gboolean success = TRUE;

	/* Chain up to parent's method. */
	if (!CAMEL_SERVICE_CLASS (camel_smtp_transport_parent_class)->connect_sync (service, cancellable, error))
		return FALSE;

	smtp_debug_print_server_name (service, "Connecting to");

	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	mechanism = camel_network_settings_dup_auth_mechanism (network_settings);

	g_object_unref (settings);

	/* We (probably) need to check popb4smtp before we connect ... */
	if (g_strcmp0 (mechanism, "POPB4SMTP") == 0) {
		GByteArray *chal;
		CamelSasl *sasl;

		sasl = camel_sasl_new ("smtp", "POPB4SMTP", service);
		chal = camel_sasl_challenge_sync (sasl, NULL, cancellable, error);
		if (chal != NULL)
			g_byte_array_free (chal, TRUE);

		if (camel_sasl_get_authenticated (sasl))
			success = connect_to_server (
				service, cancellable, error);
		else
			success = FALSE;

		g_object_unref (sasl);

		goto exit;
	}

	success = connect_to_server (service, cancellable, error);

	if (!success)
		return FALSE;

	/* check to see if AUTH is required, if so...then AUTH ourselves */
	auth_required =
		(mechanism != NULL) &&
		(transport->authtypes != NULL) &&
		(g_hash_table_size (transport->authtypes) > 0) &&
		(transport->flags & CAMEL_SMTP_TRANSPORT_IS_ESMTP);

	if (auth_required) {
		CamelSession *session;

		session = camel_service_ref_session (service);

		if (g_hash_table_lookup (transport->authtypes, mechanism)) {
			success = camel_session_authenticate_sync (
				session, service, mechanism,
				cancellable, error);
		} else {
			g_set_error (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("SMTP server %s does not support %s "
				"authentication"), host, mechanism);
			success = FALSE;
		}

		g_object_unref (session);

		if (!success)
			transport->connected = FALSE;
	}

exit:
	g_free (host);
	g_free (mechanism);

	return success;
}
static gboolean
connect_to_server (CamelService *service,
                   GCancellable *cancellable,
                   GError **error)
{
	CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
	CamelNetworkSettings *network_settings;
	CamelNetworkSecurityMethod method;
	CamelSettings *settings;
	CamelStream *stream;
	GIOStream *base_stream;
	GIOStream *tls_stream;
	gchar *respbuf = NULL;
	gboolean success = TRUE;
	gchar *host;

	if (!CAMEL_SERVICE_CLASS (camel_smtp_transport_parent_class)->
		connect_sync (service, cancellable, error))
		return FALSE;

	/* set some smtp transport defaults */
	transport->flags = 0;
	transport->authtypes = NULL;

	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	method = camel_network_settings_get_security_method (network_settings);

	g_object_unref (settings);

	base_stream = camel_network_service_connect_sync (
		CAMEL_NETWORK_SERVICE (service), cancellable, error);

	if (base_stream != NULL) {
		/* get the localaddr - needed later by smtp_helo */
		transport->local_address =
			g_socket_connection_get_local_address (
			G_SOCKET_CONNECTION (base_stream), NULL);

		stream = camel_stream_new (base_stream);
		g_object_unref (base_stream);
	} else {
		success = FALSE;
		goto exit;
	}

	transport->connected = TRUE;

	transport->ostream = stream;
	transport->istream = camel_stream_buffer_new (
		stream, CAMEL_STREAM_BUFFER_READ);

	/* Read the greeting, note whether the server is ESMTP or not. */
	do {
		/* Check for "220" */
		g_free (respbuf);
		respbuf = camel_stream_buffer_read_line (
			CAMEL_STREAM_BUFFER (transport->istream),
			cancellable, error);
		d (fprintf (stderr, "[SMTP] received: %s\n", respbuf ? respbuf : "(null)"));
		if (respbuf == NULL) {
			g_prefix_error (error, _("Welcome response error: "));
			transport->connected = FALSE;
			success = FALSE;
			goto exit;
		}
		if (strncmp (respbuf, "220", 3)) {
			smtp_set_error (
				transport, respbuf, cancellable, error);
			g_prefix_error (error, _("Welcome response error: "));
			g_free (respbuf);
			success = FALSE;
			goto exit;
		}
	} while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */
	g_free (respbuf);

	/* Try sending EHLO */
	transport->flags |= CAMEL_SMTP_TRANSPORT_IS_ESMTP;
	if (!smtp_helo (transport, cancellable, error)) {
		if (!transport->connected) {
			success = FALSE;
			goto exit;
		}

		/* Fall back to HELO */
		g_clear_error (error);
		transport->flags &= ~CAMEL_SMTP_TRANSPORT_IS_ESMTP;

		if (!smtp_helo (transport, cancellable, error)) {
			success = FALSE;
			goto exit;
		}
	}

	/* Clear any EHLO/HELO exception and assume that
	 * any SMTP errors encountered were non-fatal. */
	g_clear_error (error);

	if (method != CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT)
		goto exit;  /* we're done */

	if (!(transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS)) {
		g_set_error (
			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
			_("Failed to connect to SMTP server %s in secure mode: %s"),
			host, _("STARTTLS not supported"));

		success = FALSE;
		goto exit;
	}

	d (fprintf (stderr, "[SMTP] sending: STARTTLS\r\n"));
	if (camel_stream_write (
		stream, "STARTTLS\r\n", 10, cancellable, error) == -1) {
		g_prefix_error (error, _("STARTTLS command failed: "));
		success = FALSE;
		goto exit;
	}

	respbuf = NULL;

	do {
		/* Check for "220 Ready for TLS" */
		g_free (respbuf);
		respbuf = camel_stream_buffer_read_line (
			CAMEL_STREAM_BUFFER (transport->istream),
			cancellable, error);
		d (fprintf (stderr, "[SMTP] received: %s\n", respbuf ? respbuf : "(null)"));
		if (respbuf == NULL) {
			g_prefix_error (error, _("STARTTLS command failed: "));
			transport->connected = FALSE;
			success = FALSE;
			goto exit;
		}
		if (strncmp (respbuf, "220", 3) != 0) {
			smtp_set_error (
				transport, respbuf, cancellable, error);
			g_prefix_error (error, _("STARTTLS command failed: "));
			g_free (respbuf);
			success = FALSE;
			goto exit;
		}
	} while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */

	/* Okay, now toggle SSL/TLS mode */
	base_stream = camel_stream_ref_base_stream (stream);
	tls_stream = camel_network_service_starttls (
		CAMEL_NETWORK_SERVICE (service), base_stream, error);
	g_object_unref (base_stream);

	if (tls_stream != NULL) {
		camel_stream_set_base_stream (stream, tls_stream);
		g_object_unref (tls_stream);
	} else {
		g_prefix_error (
			error,
			_("Failed to connect to SMTP server %s in secure mode: "),
			host);
		success = FALSE;
		goto exit;
	}

	/* We are supposed to re-EHLO after a successful STARTTLS to
	 * re-fetch any supported extensions. */
	if (!smtp_helo (transport, cancellable, error)) {
		success = FALSE;
	}

exit:
	g_free (host);

	if (!success) {
		transport->connected = FALSE;
		g_clear_object (&transport->istream);
		g_clear_object (&transport->ostream);
	}

	return success;
}