aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camel/ChangeLog29
-rw-r--r--camel/camel-tcp-stream-openssl.c154
-rw-r--r--camel/camel-tcp-stream-openssl.h4
-rw-r--r--camel/camel-tcp-stream-ssl.c136
-rw-r--r--camel/camel-tcp-stream-ssl.h4
-rw-r--r--camel/providers/smtp/camel-smtp-provider.c36
-rw-r--r--camel/providers/smtp/camel-smtp-transport.c141
-rw-r--r--camel/providers/smtp/camel-smtp-transport.h13
8 files changed, 433 insertions, 84 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog
index 3820ce8426..23d5f21e78 100644
--- a/camel/ChangeLog
+++ b/camel/ChangeLog
@@ -1,3 +1,32 @@
+2002-03-05 Jeffrey Stedfast <fejj@ximian.com>
+
+ * camel-tcp-stream-openssl.c (camel_tcp_stream_openssl_new_raw):
+ Start the ssl stream off in non-ssl mode (useful for STARTTLS).
+ (camel_tcp_stream_openssl_enable_ssl): New function to toggle an
+ ssl stream into ssl mode.
+ (open_ssl_connection): Close the sockfd on fail so our caller
+ doesn't have to - this also allows us to save the original errno.
+ (stream_connect): If we want ssl mode, do our ssl stuff.
+ (camel_tcp_stream_openssl_class_init): Init some SSL stuff here
+ instead of in open_ssl_connection since these only ever need to be
+ called once.
+ (stream_read): Only use SSL_read if we are in ssl mode.
+ (stream_write): Only use SSL_write if we are in ssl mode.
+
+ * providers/smtp/camel-smtp-transport.c (smtp_helo): Check for the
+ STARTTLS extension.
+ (connect_to_server): Try to use STARTTLS whenever possible rather
+ than the old way of doing things.
+ (connect_to_server_wrapper): Wrapper around connect_to_server() to
+ first try STARTTLS and then attempt normal SSL mode if we can't
+ connect via STARTTLS.
+
+ * camel-tcp-stream-ssl.c (camel_tcp_stream_ssl_enable_ssl): New
+ function to toggle an ssl stream into ssl mode.
+ (camel_tcp_stream_ssl_new_raw): Start the ssl stream off in
+ non-ssl mode (useful for STARTTLS).
+ (stream_connect): Only connect in SSL mode if required.
+
2002-03-01 Jeffrey Stedfast <fejj@ximian.com>
* camel-vtrash-folder.h:
diff --git a/camel/camel-tcp-stream-openssl.c b/camel/camel-tcp-stream-openssl.c
index cceb9975b7..a8256bf0ae 100644
--- a/camel/camel-tcp-stream-openssl.c
+++ b/camel/camel-tcp-stream-openssl.c
@@ -60,12 +60,15 @@ static int stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data);
static int stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data);
static gpointer stream_get_socket (CamelTcpStream *stream);
+static SSL *open_ssl_connection (CamelService *service, int sockfd, CamelTcpStreamOpenSSL *openssl);
+
struct _CamelTcpStreamOpenSSLPrivate {
int sockfd;
SSL *ssl;
CamelService *service;
char *expected_host;
+ gboolean ssl_mode;
};
static void
@@ -88,6 +91,10 @@ camel_tcp_stream_openssl_class_init (CamelTcpStreamOpenSSLClass *camel_tcp_strea
camel_tcp_stream_class->getsockopt = stream_getsockopt;
camel_tcp_stream_class->setsockopt = stream_setsockopt;
camel_tcp_stream_class->get_socket = stream_get_socket;
+
+ /* init OpenSSL stuff */
+ SSLeay_add_ssl_algorithms ();
+ SSL_load_error_strings ();
}
static void
@@ -152,7 +159,7 @@ camel_tcp_stream_openssl_get_type (void)
* user, a CamelService is needed. @expected_host is needed as a
* protection against an MITM attack.
*
- * Return value: a tcp stream
+ * Return value: a ssl stream (in ssl mode)
**/
CamelStream *
camel_tcp_stream_openssl_new (CamelService *service, const char *expected_host)
@@ -163,10 +170,38 @@ camel_tcp_stream_openssl_new (CamelService *service, const char *expected_host)
stream->priv->service = service;
stream->priv->expected_host = g_strdup (expected_host);
+ stream->priv->ssl_mode = TRUE;
+
+ return CAMEL_STREAM (stream);
+}
+
+
+/**
+ * camel_tcp_stream_openssl_new_raw:
+ * @service: camel service
+ * @expected_host: host that the stream is expecting to connect with.
+ *
+ * Since the SSL certificate authenticator may need to prompt the
+ * user, a CamelService is needed. @expected_host is needed as a
+ * protection against an MITM attack.
+ *
+ * Return value: a ssl-capable stream (in non ssl mode)
+ **/
+CamelStream *
+camel_tcp_stream_openssl_new_raw (CamelService *service, const char *expected_host)
+{
+ CamelTcpStreamOpenSSL *stream;
+
+ stream = CAMEL_TCP_STREAM_OPENSSL (camel_object_new (camel_tcp_stream_openssl_get_type ()));
+
+ stream->priv->service = service;
+ stream->priv->expected_host = g_strdup (expected_host);
+ stream->priv->ssl_mode = FALSE;
return CAMEL_STREAM (stream);
}
+
static int
ssl_errno (SSL *ssl, int ret)
{
@@ -193,11 +228,41 @@ ssl_errno (SSL *ssl, int ret)
}
}
+
+/**
+ * camel_tcp_stream_openssl_enable_ssl:
+ * @stream: ssl stream
+ *
+ * Toggles an ssl-capable stream into ssl mode (if it isn't already).
+ *
+ * Returns 0 on success or -1 on fail.
+ **/
+int
+camel_tcp_stream_openssl_enable_ssl (CamelTcpStreamOpenSSL *stream)
+{
+ SSL *ssl;
+
+ g_return_val_if_fail (CAMEL_IS_TCP_STREAM_OPENSSL (ssl), -1);
+
+ if (stream->priv->sockfd && !stream->priv->ssl_mode) {
+ ssl = open_ssl_connection (stream->priv->service, stream->priv->sockfd, stream);
+ if (ssl == NULL)
+ return -1;
+
+ ssl->priv->ssl = ssl;
+ }
+
+ ssl->priv->ssl_mode = TRUE;
+
+ return 0;
+}
+
+
static ssize_t
stream_read (CamelStream *stream, char *buffer, size_t n)
{
- CamelTcpStreamOpenSSL *tcp_stream_openssl = CAMEL_TCP_STREAM_OPENSSL (stream);
- SSL *ssl = tcp_stream_openssl->priv->ssl;
+ CamelTcpStreamOpenSSL *openssl = CAMEL_TCP_STREAM_OPENSSL (stream);
+ SSL *ssl = openssl->priv->ssl;
ssize_t nread;
int cancel_fd;
@@ -209,40 +274,48 @@ stream_read (CamelStream *stream, char *buffer, size_t n)
cancel_fd = camel_operation_cancel_fd (NULL);
if (cancel_fd == -1) {
do {
- nread = SSL_read (ssl, buffer, n);
- if (nread < 0)
- errno = ssl_errno (ssl, nread);
- } while (nread < 0 && (errno == EINTR || errno == EAGAIN));
+ if (ssl) {
+ nread = SSL_read (ssl, buffer, n);
+ if (nread < 0)
+ errno = ssl_errno (ssl, nread);
+ } else {
+ nread = read (openssl->priv->sockfd, buffer, n);
+ }
+ } while (nread < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
} else {
int error, flags, fdmax;
fd_set rdset;
- flags = fcntl (tcp_stream_openssl->priv->sockfd, F_GETFL);
- fcntl (tcp_stream_openssl->priv->sockfd, F_SETFL, flags | O_NONBLOCK);
+ flags = fcntl (openssl->priv->sockfd, F_GETFL);
+ fcntl (openssl->priv->sockfd, F_SETFL, flags | O_NONBLOCK);
- fdmax = MAX (tcp_stream_openssl->priv->sockfd, cancel_fd) + 1;
+ fdmax = MAX (openssl->priv->sockfd, cancel_fd) + 1;
do {
FD_ZERO (&rdset);
- FD_SET (tcp_stream_openssl->priv->sockfd, &rdset);
+ FD_SET (openssl->priv->sockfd, &rdset);
FD_SET (cancel_fd, &rdset);
select (fdmax, &rdset, 0, 0, NULL);
if (FD_ISSET (cancel_fd, &rdset)) {
- fcntl (tcp_stream_openssl->priv->sockfd, F_SETFL, flags);
+ fcntl (openssl->priv->sockfd, F_SETFL, flags);
errno = EINTR;
return -1;
}
do {
- nread = SSL_read (ssl, buffer, n);
- if (nread < 0)
- errno = ssl_errno (ssl, nread);
+ if (ssl) {
+ nread = SSL_read (ssl, buffer, n);
+ if (nread < 0)
+ errno = ssl_errno (ssl, nread);
+ } else {
+ nread = read (openssl->priv->sockfd, buffer, n);
+ }
} while (nread < 0 && errno == EINTR);
- } while (nread < 0 && errno == EAGAIN);
+ } while (nread < 0 && (errno == EAGAIN || errno == EWOULDBLOCK));
error = errno;
- fcntl (tcp_stream_openssl->priv->sockfd, F_SETFL, flags);
+ fcntl (openssl->priv->sockfd, F_SETFL, flags);
errno = error;
}
@@ -252,8 +325,8 @@ stream_read (CamelStream *stream, char *buffer, size_t n)
static ssize_t
stream_write (CamelStream *stream, const char *buffer, size_t n)
{
- CamelTcpStreamOpenSSL *tcp_stream_openssl = CAMEL_TCP_STREAM_OPENSSL (stream);
- SSL *ssl = tcp_stream_openssl->priv->ssl;
+ CamelTcpStreamOpenSSL *openssl = CAMEL_TCP_STREAM_OPENSSL (stream);
+ SSL *ssl = openssl->priv->ssl;
ssize_t w, written = 0;
int cancel_fd;
@@ -266,10 +339,14 @@ stream_write (CamelStream *stream, const char *buffer, size_t n)
if (cancel_fd == -1) {
do {
do {
- w = SSL_write (ssl, buffer + written, n - written);
- if (w < 0)
- errno = ssl_errno (ssl, w);
- } while (w < 0 && (errno == EINTR || errno == EAGAIN));
+ if (ssl) {
+ w = SSL_write (ssl, buffer + written, n - written);
+ if (w < 0)
+ errno = ssl_errno (ssl, w);
+ } else {
+ w = write (openssl->priv->sockfd, buffer + written, n - written);
+ }
+ } while (w < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
if (w > 0)
written += w;
@@ -296,13 +373,17 @@ stream_write (CamelStream *stream, const char *buffer, size_t n)
}
do {
- w = SSL_write (ssl, buffer + written, n - written);
- if (w < 0)
- errno = ssl_errno (ssl, w);
+ if (ssl) {
+ w = SSL_write (ssl, buffer + written, n - written);
+ if (w < 0)
+ errno = ssl_errno (ssl, w);
+ } else {
+ w = write (openssl->priv->sockfd, buffer + written, n - written);
+ }
} while (w < 0 && errno == EINTR);
if (w < 0) {
- if (errno == EAGAIN) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
w = 0;
} else {
error = errno;
@@ -542,9 +623,6 @@ open_ssl_connection (CamelService *service, int sockfd, CamelTcpStreamOpenSSL *o
SSL *ssl = NULL;
int n;
- SSLeay_add_ssl_algorithms();
- SSL_load_error_strings();
-
/* SSLv23_client_method will negotiate with SSL v2, v3, or TLS v1 */
ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
g_return_val_if_fail (ssl_ctx != NULL, NULL);
@@ -557,7 +635,7 @@ open_ssl_connection (CamelService *service, int sockfd, CamelTcpStreamOpenSSL *o
n = SSL_connect (ssl);
if (n != 1) {
- errno = ssl_errno (ssl, n);
+ int errnosave = ssl_errno (ssl, n);
SSL_shutdown (ssl);
@@ -566,6 +644,10 @@ open_ssl_connection (CamelService *service, int sockfd, CamelTcpStreamOpenSSL *o
SSL_free (ssl);
ssl = NULL;
+
+ close (sockfd);
+
+ errno = errnosave;
}
return ssl;
@@ -575,7 +657,7 @@ static int
stream_connect (CamelTcpStream *stream, struct hostent *host, int port)
{
CamelTcpStreamOpenSSL *openssl = CAMEL_TCP_STREAM_OPENSSL (stream);
- SSL *ssl;
+ SSL *ssl = NULL;
int fd;
g_return_val_if_fail (host != NULL, -1);
@@ -584,9 +666,11 @@ stream_connect (CamelTcpStream *stream, struct hostent *host, int port)
if (fd == -1)
return -1;
- ssl = open_ssl_connection (openssl->priv->service, fd, openssl);
- if (!ssl)
- return -1;
+ if (openssl->priv->ssl_mode) {
+ ssl = open_ssl_connection (openssl->priv->service, fd, openssl);
+ if (!ssl)
+ return -1;
+ }
openssl->priv->sockfd = fd;
openssl->priv->ssl = ssl;
diff --git a/camel/camel-tcp-stream-openssl.h b/camel/camel-tcp-stream-openssl.h
index 2d428c628d..9509eaf343 100644
--- a/camel/camel-tcp-stream-openssl.h
+++ b/camel/camel-tcp-stream-openssl.h
@@ -57,6 +57,10 @@ CamelType camel_tcp_stream_openssl_get_type (void);
/* public methods */
CamelStream *camel_tcp_stream_openssl_new (CamelService *service, const char *expected_host);
+CamelStream *camel_tcp_stream_openssl_new_raw (CamelService *service, const char *expected_host);
+
+int camel_tcp_stream_openssl_enable_ssl (CamelTcpStreamOpenSSL *ssl);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/camel/camel-tcp-stream-ssl.c b/camel/camel-tcp-stream-ssl.c
index 50c4684980..9b467b3463 100644
--- a/camel/camel-tcp-stream-ssl.c
+++ b/camel/camel-tcp-stream-ssl.c
@@ -60,6 +60,8 @@ static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
static int stream_flush (CamelStream *stream);
static int stream_close (CamelStream *stream);
+static PRFileDesc *enable_ssl (CamelTcpStreamSSL *ssl, PRFileDesc *fd);
+
static int stream_connect (CamelTcpStream *stream, struct hostent *host, int port);
static int stream_getsockopt (CamelTcpStream *stream, CamelSockOptData *data);
static int stream_setsockopt (CamelTcpStream *stream, const CamelSockOptData *data);
@@ -70,6 +72,7 @@ struct _CamelTcpStreamSSLPrivate {
CamelService *service;
char *expected_host;
+ gboolean ssl_mode;
};
static void
@@ -135,6 +138,7 @@ camel_tcp_stream_ssl_get_type (void)
return type;
}
+
/**
* camel_tcp_stream_ssl_new:
* @service: camel service
@@ -144,7 +148,7 @@ camel_tcp_stream_ssl_get_type (void)
* user, a CamelService is needed. @expected_host is needed as a
* protection against an MITM attack.
*
- * Return value: a tcp stream
+ * Return value: a ssl stream (in ssl mode)
**/
CamelStream *
camel_tcp_stream_ssl_new (CamelService *service, const char *expected_host)
@@ -155,10 +159,38 @@ camel_tcp_stream_ssl_new (CamelService *service, const char *expected_host)
stream->priv->service = service;
stream->priv->expected_host = g_strdup (expected_host);
+ stream->priv->ssl_mode = TRUE;
return CAMEL_STREAM (stream);
}
+
+/**
+ * camel_tcp_stream_ssl_new_raw:
+ * @service: camel service
+ * @expected_host: host that the stream is expected to connect with.
+ *
+ * Since the SSL certificate authenticator may need to prompt the
+ * user, a CamelService is needed. @expected_host is needed as a
+ * protection against an MITM attack.
+ *
+ * Return value: a ssl-capable stream (in non ssl mode)
+ **/
+CamelStream *
+camel_tcp_stream_ssl_new_raw (CamelService *service, const char *expected_host)
+{
+ CamelTcpStreamSSL *stream;
+
+ stream = CAMEL_TCP_STREAM_SSL (camel_object_new (camel_tcp_stream_ssl_get_type ()));
+
+ stream->priv->service = service;
+ stream->priv->expected_host = g_strdup (expected_host);
+ stream->priv->ssl_mode = FALSE;
+
+ return CAMEL_STREAM (stream);
+}
+
+
static void
set_errno (int code)
{
@@ -181,6 +213,45 @@ set_errno (int code)
}
}
+
+/**
+ * camel_tcp_stream_ssl_enable_ssl:
+ * @ssl: ssl stream
+ *
+ * Toggles an ssl-capable stream into ssl mode (if it isn't already).
+ *
+ * Returns 0 on success or -1 on fail.
+ **/
+int
+camel_tcp_stream_ssl_enable_ssl (CamelTcpStreamSSL *ssl)
+{
+ PRFileDesc *fd;
+
+ g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (ssl), -1);
+
+ if (ssl->priv->sockfd && !ssl->priv->ssl_mode) {
+ fd = enable_ssl (ssl, NULL);
+ if (fd == NULL) {
+ int errnosave;
+
+ set_errno (PR_GetError ());
+ errnosave = errno;
+ errno = errnosave;
+
+ return -1;
+ }
+
+ SSL_ResetHandshake (fd, FALSE);
+
+ ssl->priv->sockfd = fd;
+ }
+
+ ssl->priv->ssl_mode = TRUE;
+
+ return 0;
+}
+
+
static ssize_t
stream_read (CamelStream *stream, char *buffer, size_t n)
{
@@ -486,13 +557,34 @@ ssl_bad_cert (void *data, PRFileDesc *sockfd)
return SECFailure;
}
+static PRFileDesc *
+enable_ssl (CamelTcpStreamSSL *ssl, PRFileDesc *fd)
+{
+ PRFileDesc *ssl_fd;
+
+ ssl_fd = SSL_ImportFD (NULL, fd ? fd : ssl->priv->sockfd);
+ if (!ssl_fd)
+ return NULL;
+
+ SSL_OptionSet (ssl_fd, SSL_SECURITY, PR_TRUE);
+ SSL_SetURL (ssl_fd, ssl->priv->expected_host);
+
+ /*SSL_GetClientAuthDataHook (sslSocket, ssl_get_client_auth, (void *) certNickname);*/
+ /*SSL_AuthCertificateHook (ssl_fd, ssl_auth_cert, (void *) CERT_GetDefaultCertDB ());*/
+ SSL_BadCertHook (ssl_fd, ssl_bad_cert, ssl);
+
+ ssl->priv->ssl_mode = TRUE;
+
+ return ssl_fd;
+}
+
static int
stream_connect (CamelTcpStream *stream, struct hostent *host, int port)
{
CamelTcpStreamSSL *ssl = CAMEL_TCP_STREAM_SSL (stream);
PRIntervalTime timeout = PR_INTERVAL_MIN;
PRNetAddr netaddr;
- PRFileDesc *fd, *ssl_fd;
+ PRFileDesc *fd;
g_return_val_if_fail (host != NULL, -1);
@@ -505,30 +597,42 @@ stream_connect (CamelTcpStream *stream, struct hostent *host, int port)
}
fd = PR_OpenTCPSocket (host->h_addrtype);
- ssl_fd = SSL_ImportFD (NULL, fd);
-
- SSL_OptionSet (ssl_fd, SSL_SECURITY, PR_TRUE);
- SSL_SetURL (ssl_fd, ssl->priv->expected_host);
+ if (fd == NULL) {
+ set_errno (PR_GetError ());
+ return -1;
+ }
- if (ssl_fd == NULL || PR_Connect (ssl_fd, &netaddr, timeout) == PR_FAILURE) {
- if (ssl_fd != NULL) {
+ if (ssl->priv->ssl_mode) {
+ PRFileDesc *ssl_fd;
+
+ ssl_fd = enable_ssl (ssl, fd);
+ if (ssl_fd == NULL) {
int errnosave;
set_errno (PR_GetError ());
errnosave = errno;
- PR_Close (ssl_fd);
+ PR_Close (fd);
errno = errnosave;
- } else
- errno = EINVAL;
+
+ return -1;
+ }
- return -1;
+ fd = ssl_fd;
}
- /*SSL_GetClientAuthDataHook (sslSocket, ssl_get_client_auth, (void *) certNickname);*/
- /*SSL_AuthCertificateHook (ssl_fd, ssl_auth_cert, (void *) CERT_GetDefaultCertDB ());*/
- SSL_BadCertHook (ssl_fd, ssl_bad_cert, ssl);
+ if (PR_Connect (fd, &netaddr, timeout) == PR_FAILURE) {
+ int errnosave;
+
+ set_errno (PR_GetError ());
+ errnosave = errno;
+ PR_Close (fd);
+ ssl->priv->sockfd = NULL;
+ errno = errnosave;
+
+ return -1;
+ }
- ssl->priv->sockfd = ssl_fd;
+ ssl->priv->sockfd = fd;
return 0;
}
diff --git a/camel/camel-tcp-stream-ssl.h b/camel/camel-tcp-stream-ssl.h
index a9becb9816..5db4a0eb70 100644
--- a/camel/camel-tcp-stream-ssl.h
+++ b/camel/camel-tcp-stream-ssl.h
@@ -57,6 +57,10 @@ CamelType camel_tcp_stream_ssl_get_type (void);
/* public methods */
CamelStream *camel_tcp_stream_ssl_new (CamelService *service, const char *expected_host);
+CamelStream *camel_tcp_stream_ssl_new_raw (CamelService *service, const char *expected_host);
+
+int camel_tcp_stream_ssl_enable_ssl (CamelTcpStreamSSL *ssl);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/camel/providers/smtp/camel-smtp-provider.c b/camel/providers/smtp/camel-smtp-provider.c
index 13f0cb6369..48e9bcc6d1 100644
--- a/camel/providers/smtp/camel-smtp-provider.c
+++ b/camel/providers/smtp/camel-smtp-provider.c
@@ -1,27 +1,27 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* camel-smtp-provider.c: smtp provider registration code */
-
-/*
- * Authors :
- * Jeffrey Stedfast <fejj@stampede.org>
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2002 Ximian, Inc. (www.ximian.com)
*
- * Copyright (C) 2000 Ximian, Inc. (www.ximian.com)
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
*/
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -53,8 +53,8 @@ camel_provider_module_init (CamelSession *session)
{
smtp_provider.object_types[CAMEL_PROVIDER_TRANSPORT] =
camel_smtp_transport_get_type ();
- smtp_provider.authtypes = g_list_append(camel_sasl_authtype_list(TRUE), camel_sasl_authtype ("LOGIN"));
- smtp_provider.authtypes = g_list_append(smtp_provider.authtypes, camel_sasl_authtype ("POPB4SMTP"));
+ smtp_provider.authtypes = g_list_append (camel_sasl_authtype_list (TRUE), camel_sasl_authtype ("LOGIN"));
+ smtp_provider.authtypes = g_list_append (smtp_provider.authtypes, camel_sasl_authtype ("POPB4SMTP"));
smtp_provider.service_cache = g_hash_table_new (camel_url_hash, camel_url_equal);
smtp_provider.url_hash = camel_url_hash;
smtp_provider.url_equal = camel_url_equal;
diff --git a/camel/providers/smtp/camel-smtp-transport.c b/camel/providers/smtp/camel-smtp-transport.c
index b06b3cb329..c5bf11971e 100644
--- a/camel/providers/smtp/camel-smtp-transport.c
+++ b/camel/providers/smtp/camel-smtp-transport.c
@@ -92,6 +92,9 @@ static gboolean smtp_data (CamelSmtpTransport *transport, CamelMedium *message,
static gboolean smtp_rset (CamelSmtpTransport *transport, CamelException *ex);
static gboolean smtp_quit (CamelSmtpTransport *transport, CamelException *ex);
+static void smtp_set_exception (CamelSmtpTransport *transport, const char *respbuf,
+ const char *message, CamelException *ex);
+
/* private data members */
static CamelTransportClass *parent_class = NULL;
@@ -151,11 +154,16 @@ smtp_construct (CamelService *service, CamelSession *session,
CamelException *ex)
{
CamelSmtpTransport *smtp_transport = CAMEL_SMTP_TRANSPORT (service);
+ const char *use_ssl;
CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
- if (camel_url_get_param (url, "use_ssl"))
- smtp_transport->flags |= CAMEL_SMTP_TRANSPORT_USE_SSL;
+ if ((use_ssl = camel_url_get_param (url, "use_ssl"))) {
+ if (!strcmp (use_ssl, "always"))
+ smtp_transport->flags |= CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS;
+ else if (!strcmp (use_ssl, "when-possible"))
+ smtp_transport->flags |= CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE;
+ }
}
static const char *
@@ -228,11 +236,11 @@ smtp_error_string (int error)
}
static gboolean
-connect_to_server (CamelService *service, CamelException *ex)
+connect_to_server (CamelService *service, int try_starttls, CamelException *ex)
{
CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
CamelStream *tcp_stream;
- gchar *respbuf = NULL;
+ char *respbuf = NULL;
struct hostent *h;
guint32 addrlen;
int port, ret;
@@ -247,20 +255,28 @@ connect_to_server (CamelService *service, CamelException *ex)
/* set some smtp transport defaults */
transport->flags &= ~(CAMEL_SMTP_TRANSPORT_IS_ESMTP |
CAMEL_SMTP_TRANSPORT_8BITMIME |
+ CAMEL_SMTP_TRANSPORT_STARTTLS |
CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES);
transport->authtypes = NULL;
port = service->url->port ? service->url->port : SMTP_PORT;
-#if defined(HAVE_NSS) || defined(HAVE_OPENSSL)
+#if defined (HAVE_NSS) || defined (HAVE_OPENSSL)
if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL) {
- port = service->url->port ? service->url->port : 465;
+ if (!try_starttls)
+ port = service->url->port ? service->url->port : 465;
#ifdef HAVE_NSS
/* use the preferred implementation - NSS */
- tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host);
+ if (try_starttls)
+ tcp_stream = camel_tcp_stream_ssl_new_raw (service, service->url->host);
+ else
+ tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host);
#else
- tcp_stream = camel_tcp_stream_openssl_new (service, service->url->host);
+ if (try_starttls)
+ tcp_stream = camel_tcp_stream_openssl_new_raw (service, service->url->host);
+ else
+ tcp_stream = camel_tcp_stream_openssl_new (service, service->url->host);
#endif /* HAVE_NSS */
} else {
tcp_stream = camel_tcp_stream_raw_new ();
@@ -340,7 +356,107 @@ connect_to_server (CamelService *service, CamelException *ex)
smtp_helo (transport, ex);
}
+#if defined (HAVE_NSS) || defined (HAVE_OPENSSL)
+ if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE) {
+ /* try_starttls is always TRUE here */
+ if (transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS)
+ goto starttls;
+ } else if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS) {
+ if (try_starttls) {
+ if (transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS) {
+ goto starttls;
+ } else {
+ /* server doesn't support STARTTLS, abort */
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Failed to connect to SMTP server %s in secure mode: %s"),
+ service->url->host, _("server does not appear to support SSL"));
+ goto exception_cleanup;
+ }
+ }
+ }
+#endif /* HAVE_NSS || HAVE_OPENSSL */
+
return TRUE;
+
+#if defined (HAVE_NSS) || defined (HAVE_OPENSSL)
+ starttls:
+ d(fprintf (stderr, "sending : STARTTLS\r\n"));
+ if (camel_stream_write (tcp_stream, "STARTTLS\r\n", 10) == -1) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("STARTTLS request timed out: %s"),
+ g_strerror (errno));
+ goto exception_cleanup;
+ }
+
+ respbuf = NULL;
+
+ do {
+ /* Check for "220 Ready for TLS" */
+ g_free (respbuf);
+ respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
+
+ d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
+
+ if (!respbuf || strncmp (respbuf, "220", 3)) {
+ smtp_set_exception (transport, respbuf, _("STARTTLS response error"), ex);
+ g_free (respbuf);
+ goto exception_cleanup;
+ }
+ } while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */
+
+ /* Okay, now toggle SSL/TLS mode */
+#ifdef HAVE_NSS
+ ret = camel_tcp_stream_ssl_enable_ssl (CAMEL_TCP_STREAM_SSL (tcp_stream));
+#else /* HAVE_OPENSSL */
+ ret = camel_tcp_stream_openssl_enable_ssl (CAMEL_TCP_STREAM_OPENSSL (tcp_stream));
+#endif
+ if (ret != -1)
+ return TRUE;
+
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Failed to connect to SMTP server %s in secure mode: %s"),
+ service->url->host, g_strerror (errno));
+
+ exception_cleanup:
+ camel_object_unref (CAMEL_OBJECT (transport->istream));
+ transport->istream = NULL;
+ camel_object_unref (CAMEL_OBJECT (transport->ostream));
+ transport->ostream = NULL;
+
+ return FALSE;
+#endif /* HAVE_NSS || HAVE_OPENSSL */
+}
+
+#define EXCEPTION_RETRY(ex) (camel_exception_get_id (ex) != CAMEL_EXCEPTION_USER_CANCEL && \
+ camel_exception_get_id (ex) != CAMEL_EXCEPTION_SERVICE_UNAVAILABLE)
+
+static gboolean
+connect_to_server_wrapper (CamelService *service, CamelException *ex)
+{
+#if defined (HAVE_NSS) || defined (HAVE_OPENSSL)
+ CamelSmtpTransport *transport = (CamelSmtpTransport *) service;
+
+ if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS) {
+ /* First try STARTTLS */
+ if (!connect_to_server (service, TRUE, ex) &&
+ !transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS &&
+ EXCEPTION_RETRY (ex)) {
+ /* STARTTLS is unavailable - okay, now try port 465 */
+ camel_exception_clear (ex);
+ return connect_to_server (service, FALSE, ex);
+ }
+
+ return TRUE;
+ } else if (transport->flags & CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE) {
+ /* If the server supports STARTTLS, use it */
+ return connect_to_server (service, TRUE, ex);
+ } else {
+ /* User doesn't care about SSL */
+ return connect_to_server (service, FALSE, ex);
+ }
+#else
+ return connect_to_server (service, FALSE, ex);
+#endif
}
static gboolean
@@ -364,10 +480,10 @@ smtp_connect (CamelService *service, CamelException *ex)
if (!truth)
return FALSE;
- return connect_to_server (service, ex);
+ return connect_to_server_wrapper (service, ex);
}
- if (!connect_to_server (service, ex))
+ if (!connect_to_server_wrapper (service, ex))
return FALSE;
/* check to see if AUTH is required, if so...then AUTH ourselves */
@@ -539,7 +655,7 @@ query_auth_types (CamelService *service, CamelException *ex)
CamelServiceAuthType *authtype;
GList *types, *t, *next;
- if (!connect_to_server (service, ex))
+ if (!connect_to_server_wrapper (service, ex))
return NULL;
types = g_list_copy (service->provider->authtypes);
@@ -852,6 +968,9 @@ smtp_helo (CamelSmtpTransport *transport, CamelException *ex)
} else if (!strncmp (token, "ENHANCEDSTATUSCODES", 19)) {
d(fprintf (stderr, "This server supports enhanced status codes\n"));
transport->flags |= CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES;
+ } else if (!strncmp (token, "STARTTLS", 8)) {
+ d(fprintf (stderr, "This server supports STARTTLS\n"));
+ transport->flags |= CAMEL_SMTP_TRANSPORT_STARTTLS;
} else if (!transport->authtypes && !strncmp (token, "AUTH", 4)) {
/* Don't bother parsing any authtypes if we already have a list.
* Some servers will list AUTH twice, once the standard way and
diff --git a/camel/providers/smtp/camel-smtp-transport.h b/camel/providers/smtp/camel-smtp-transport.h
index 8bba00e987..2f6ac261f4 100644
--- a/camel/providers/smtp/camel-smtp-transport.h
+++ b/camel/providers/smtp/camel-smtp-transport.h
@@ -47,11 +47,16 @@ extern "C" {
#define CAMEL_IS_SMTP_TRANSPORT(o) (CAMEL_CHECK_TYPE((o), CAMEL_SMTP_TRANSPORT_TYPE))
-#define CAMEL_SMTP_TRANSPORT_IS_ESMTP (1 << 0)
-#define CAMEL_SMTP_TRANSPORT_8BITMIME (1 << 1)
-#define CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES (1 << 2)
+#define CAMEL_SMTP_TRANSPORT_IS_ESMTP (1 << 0)
+#define CAMEL_SMTP_TRANSPORT_8BITMIME (1 << 1)
+#define CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES (1 << 2)
+#define CAMEL_SMTP_TRANSPORT_STARTTLS (1 << 3)
-#define CAMEL_SMTP_TRANSPORT_USE_SSL (1 << 3)
+#define CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS (1 << 4)
+#define CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE (1 << 5)
+
+#define CAMEL_SMTP_TRANSPORT_USE_SSL (CAMEL_SMTP_TRANSPORT_USE_SSL_ALWAYS | \
+ CAMEL_SMTP_TRANSPORT_USE_SSL_WHEN_POSSIBLE)
typedef struct {
CamelTransport parent_object;