aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camel/ChangeLog18
-rw-r--r--camel/providers/pop3/camel-pop3-engine.c51
-rw-r--r--camel/providers/pop3/camel-pop3-engine.h4
-rw-r--r--camel/providers/pop3/camel-pop3-provider.c2
-rw-r--r--camel/providers/pop3/camel-pop3-store.c257
-rw-r--r--camel/providers/pop3/camel-pop3-store.h6
-rw-r--r--camel/providers/smtp/camel-smtp-transport.c2
7 files changed, 290 insertions, 50 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog
index d1d41fd8fb..2f4d8f4b71 100644
--- a/camel/ChangeLog
+++ b/camel/ChangeLog
@@ -1,3 +1,21 @@
+2002-03-08 Jeffrey Stedfast <fejj@ximian.com>
+
+ * providers/pop3/camel-pop3-provider.c
+ (camel_provider_module_init): Don't call
+ camel_remote_store_get_authtypes since we no longer subclass
+ camel-remote-store.
+
+ * providers/pop3/camel-pop3-engine.c: Added STARTTLS to the
+ capabilities to look for.
+ (camel_pop3_engine_reget_capabilities): New function to re-get
+ capabilities.
+
+ * providers/pop3/camel-pop3-store.c: Updated to not subclass
+ CamelRemoteStore.
+ (connect_to_server): Rewritten to not depend on CamelRemoteStore's
+ connect implementation. Also added support for STLS (aka
+ STARTTLS).
+
2002-03-07 Jeffrey Stedfast <fejj@ximian.com>
* camel-pgp-mime.c (camel_pgp_mime_part_sign): Add support for
diff --git a/camel/providers/pop3/camel-pop3-engine.c b/camel/providers/pop3/camel-pop3-engine.c
index 682141f975..1783520b7c 100644
--- a/camel/providers/pop3/camel-pop3-engine.c
+++ b/camel/providers/pop3/camel-pop3-engine.c
@@ -44,7 +44,7 @@
extern int camel_verbose_debug;
#define dd(x) (camel_verbose_debug?(x):0)
-static void get_capabilities(CamelPOP3Engine *pe);
+static void get_capabilities(CamelPOP3Engine *pe, int read_greeting);
static CamelObjectClass *parent_class = NULL;
@@ -96,6 +96,7 @@ camel_pop3_engine_get_type (void)
/**
* camel_pop3_engine_new:
+ * @source: source stream
*
* Returns a NULL stream. A null stream is always at eof, and
* always returns success for all reads and writes.
@@ -112,13 +113,28 @@ camel_pop3_engine_new(CamelStream *source)
pe->stream = (CamelPOP3Stream *)camel_pop3_stream_new(source);
pe->state = CAMEL_POP3_ENGINE_AUTH;
- get_capabilities(pe);
+ get_capabilities(pe, TRUE);
return pe;
}
+
+/**
+ * camel_pop3_engine_reget_capabilities:
+ * @engine: pop3 engine
+ *
+ * Regets server capabilities (needed after a STLS command is issued for example).
+ **/
+void
+camel_pop3_engine_reget_capabilities (CamelPOP3Engine *engine)
+{
+ g_return_if_fail (CAMEL_IS_POP3_ENGINE (engine));
+
+ get_capabilities (engine, FALSE);
+}
+
+
/* TODO: read implementation too?
- STARTLS?
etc? */
struct {
char *cap;
@@ -128,6 +144,7 @@ struct {
{ "TOP" , CAMEL_POP3_CAP_TOP },
{ "UIDL", CAMEL_POP3_CAP_UIDL },
{ "PIPELINING", CAMEL_POP3_CAP_PIPE },
+ { "STLS", CAMEL_POP3_CAP_STLS }, /* STARTTLS */
};
static void
@@ -171,25 +188,27 @@ cmd_capa(CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data)
}
static void
-get_capabilities(CamelPOP3Engine *pe)
+get_capabilities(CamelPOP3Engine *pe, int read_greeting)
{
CamelPOP3Command *pc;
unsigned char *line, *apop, *apopend;
unsigned int len;
extern CamelServiceAuthType camel_pop3_password_authtype;
extern CamelServiceAuthType camel_pop3_apop_authtype;
-
- /* first, read the greeting */
- if (camel_pop3_stream_line(pe->stream, &line, &len) == -1
- || strncmp(line, "+OK", 3) != 0)
- return;
-
- if ((apop = strchr(line+3, '<'))
- && (apopend = strchr(apop, '>'))) {
- apopend[1] = 0;
- pe->apop = g_strdup(apop);
- pe->capa = CAMEL_POP3_CAP_APOP;
- pe->auth = g_list_append(pe->auth, &camel_pop3_apop_authtype);
+
+ if (read_greeting) {
+ /* first, read the greeting */
+ if (camel_pop3_stream_line(pe->stream, &line, &len) == -1
+ || strncmp(line, "+OK", 3) != 0)
+ return;
+
+ if ((apop = strchr(line+3, '<'))
+ && (apopend = strchr(apop, '>'))) {
+ apopend[1] = 0;
+ pe->apop = g_strdup(apop);
+ pe->capa = CAMEL_POP3_CAP_APOP;
+ pe->auth = g_list_append(pe->auth, &camel_pop3_apop_authtype);
+ }
}
pe->auth = g_list_prepend(pe->auth, &camel_pop3_password_authtype);
diff --git a/camel/providers/pop3/camel-pop3-engine.h b/camel/providers/pop3/camel-pop3-engine.h
index 418bc5efb9..6b1f0cee1e 100644
--- a/camel/providers/pop3/camel-pop3-engine.h
+++ b/camel/providers/pop3/camel-pop3-engine.h
@@ -65,6 +65,7 @@ enum {
CAMEL_POP3_CAP_SASL = 1<<2,
CAMEL_POP3_CAP_TOP = 1<<3,
CAMEL_POP3_CAP_PIPE = 1<<4,
+ CAMEL_POP3_CAP_STLS = 1<<5
};
typedef void (*CamelPOP3CommandFunc)(CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data);
@@ -114,6 +115,9 @@ struct _CamelPOP3EngineClass {
guint camel_pop3_engine_get_type (void);
CamelPOP3Engine *camel_pop3_engine_new (CamelStream *source);
+
+void camel_pop3_engine_reget_capabilities (CamelPOP3Engine *engine);
+
void camel_pop3_engine_command_free(CamelPOP3Engine *pe, CamelPOP3Command *pc);
int camel_pop3_engine_iterate (CamelPOP3Engine *pe, CamelPOP3Command *pc);
diff --git a/camel/providers/pop3/camel-pop3-provider.c b/camel/providers/pop3/camel-pop3-provider.c
index 3da9ac060d..f68784fbd1 100644
--- a/camel/providers/pop3/camel-pop3-provider.c
+++ b/camel/providers/pop3/camel-pop3-provider.c
@@ -96,7 +96,7 @@ camel_provider_module_init (CamelSession *session)
pop3_provider.url_hash = camel_url_hash;
pop3_provider.url_equal = camel_url_equal;
- pop3_provider.authtypes = g_list_concat(camel_remote_store_authtype_list(), camel_sasl_authtype_list(FALSE));
+ pop3_provider.authtypes = camel_sasl_authtype_list (FALSE);
auth = camel_sasl_authtype("LOGIN");
if (auth)
pop3_provider.authtypes = g_list_prepend(pop3_provider.authtypes, auth);
diff --git a/camel/providers/pop3/camel-pop3-store.c b/camel/providers/pop3/camel-pop3-store.c
index 7f716ece68..515e982b95 100644
--- a/camel/providers/pop3/camel-pop3-store.c
+++ b/camel/providers/pop3/camel-pop3-store.c
@@ -42,7 +42,6 @@
#include "camel-pop3-store.h"
#include "camel-pop3-folder.h"
#include "camel-stream-buffer.h"
-#include "camel-tcp-stream.h"
#include "camel-session.h"
#include "camel-exception.h"
#include "camel-url.h"
@@ -50,11 +49,20 @@
#include "camel-pop3-engine.h"
#include "camel-sasl.h"
#include "camel-data-cache.h"
+#include "camel-tcp-stream.h"
+#include "camel-tcp-stream-raw.h"
+#ifdef HAVE_NSS
+#include "camel-tcp-stream-ssl.h"
+#include <prnetdb.h>
+#endif
+#ifdef HAVE_OPENSSL
+#include "camel-tcp-stream-openssl.h"
+#endif
/* Specified in RFC 1939 */
#define POP3_PORT 110
-static CamelRemoteStoreClass *parent_class = NULL;
+static CamelStoreClass *parent_class = NULL;
static void finalize (CamelObject *object);
@@ -76,9 +84,8 @@ camel_pop3_store_class_init (CamelPOP3StoreClass *camel_pop3_store_class)
CamelStoreClass *camel_store_class =
CAMEL_STORE_CLASS (camel_pop3_store_class);
- parent_class = CAMEL_REMOTE_STORE_CLASS(camel_type_get_global_classfuncs
- (camel_remote_store_get_type ()));
-
+ parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_store_get_type ()));
+
/* virtual method overload */
camel_service_class->query_auth_types = query_auth_types;
camel_service_class->connect = pop3_connect;
@@ -94,11 +101,7 @@ camel_pop3_store_class_init (CamelPOP3StoreClass *camel_pop3_store_class)
static void
camel_pop3_store_init (gpointer object, gpointer klass)
{
- CamelRemoteStore *remote_store = CAMEL_REMOTE_STORE (object);
-
- remote_store->default_port = POP3_PORT;
- /* FIXME: what should this port be?? */
- remote_store->default_ssl_port = 995;
+ ;
}
CamelType
@@ -107,7 +110,8 @@ camel_pop3_store_get_type (void)
static CamelType camel_pop3_store_type = CAMEL_INVALID_TYPE;
if (!camel_pop3_store_type) {
- camel_pop3_store_type = camel_type_register (CAMEL_REMOTE_STORE_TYPE, "CamelPOP3Store",
+ camel_pop3_store_type = camel_type_register (CAMEL_STORE_TYPE,
+ "CamelPOP3Store",
sizeof (CamelPOP3Store),
sizeof (CamelPOP3StoreClass),
(CamelObjectClassInitFunc) camel_pop3_store_class_init,
@@ -135,20 +139,215 @@ finalize (CamelObject *object)
camel_object_unref((CamelObject *)pop3_store->cache);
}
+enum {
+ USE_SSL_NEVER,
+ USE_SSL_ALWAYS,
+ USE_SSL_WHEN_POSSIBLE
+};
+
static gboolean
-connect_to_server (CamelService *service, CamelException *ex)
+connect_to_server (CamelService *service, int ssl_mode, int try_starttls, int *stls_support, CamelException *ex)
{
CamelPOP3Store *store = CAMEL_POP3_STORE (service);
- gboolean result;
-
- result = CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex);
-
- if (result == FALSE)
+ CamelStream *tcp_stream;
+ CamelPOP3Command *pc;
+ struct hostent *h;
+ int clean_quit;
+ int ret, port;
+
+ h = camel_service_gethost (service, ex);
+ if (!h)
+ return FALSE;
+
+ port = service->url->port ? service->url->port : 110;
+
+#if defined (HAVE_NSS) || defined (HAVE_OPENSSL)
+ /* FIXME: check for "always" and "when-possible" to support STARTTLS */
+ if (camel_url_get_param (service->url, "use_ssl")) {
+ if (!try_starttls)
+ port = service->url->port ? service->url->port : 995;
+#ifdef HAVE_NSS
+ /* this is the preferred SSL implementation */
+ 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
+ /* use openssl... */
+ 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 ();
+ }
+#else
+ tcp_stream = camel_tcp_stream_raw_new ();
+#endif /* HAVE_NSS || HAVE_OPENSSL */
+
+ ret = camel_tcp_stream_connect (CAMEL_TCP_STREAM (tcp_stream), h, port);
+ camel_free_host (h);
+ if (ret == -1) {
+ if (errno == EINTR)
+ camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
+ _("Connection cancelled"));
+ else
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
+ _("Could not connect to %s (port %d): %s"),
+ service->url->host ? service->url->host : _("(unknown host)"),
+ port, g_strerror (errno));
return FALSE;
+ }
+
+ /* parent class connect initialization */
+ if (CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex) == FALSE) {
+ camel_object_unref (CAMEL_OBJECT (tcp_stream));
+ return FALSE;
+ }
+
+ store->engine = camel_pop3_engine_new (tcp_stream);
+
+ if (stls_support)
+ *stls_support = store->engine->capa & CAMEL_POP3_CAP_STLS;
+
+#if defined (HAVE_NSS) || defined (HAVE_OPENSSL)
+ if (store->engine) {
+ if (ssl_mode == USE_SSL_WHEN_POSSIBLE) {
+ if (store->engine->capa & CAMEL_POP3_CAP_STLS)
+ goto starttls;
+ } else if (ssl_mode == USE_SSL_ALWAYS) {
+ if (try_starttls) {
+ if (store->engine->capa & CAMEL_POP3_CAP_STLS) {
+ /* attempt to toggle STARTTLS mode */
+ goto starttls;
+ } else {
+ /* server doesn't support STARTTLS, abort */
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Failed to connect to POP server %s in secure mode: %s"),
+ service->url->host, _("SSL/TLS extension not supported."));
+ /* we have the possibility of quitting cleanly here */
+ clean_quit = TRUE;
+ goto stls_exception;
+ }
+ }
+ }
+ }
+#endif /* HAVE_NSS || HAVE_OPENSSL */
+
+ camel_object_unref (CAMEL_OBJECT (tcp_stream));
+
+ return store->engine != NULL;
+
+#if defined (HAVE_NSS) || defined (HAVE_OPENSSL)
+ starttls:
+ /* as soon as we send a STLS command, all hope is lost of a clean QUIT if problems arise */
+ clean_quit = FALSE;
+
+ pc = camel_pop3_engine_command_new (store->engine, 0, NULL, NULL, "STLS\r\n");
+ while (camel_pop3_engine_iterate (store->engine, NULL) > 0)
+ ;
+
+ ret = pc->state == CAMEL_POP3_COMMAND_OK;
+ camel_pop3_engine_command_free (store->engine, pc);
+
+ if (ret == FALSE) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Failed to connect to POP server %s in secure mode: %s"),
+ service->url->host, store->engine->line);
+ goto stls_exception;
+ }
+
+ /* 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
+
+ camel_object_unref (CAMEL_OBJECT (tcp_stream));
+
+ if (ret == -1) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Failed to connect to POP server %s in secure mode: %s"),
+ service->url->host, _("SSL negotiations failed"));
+ goto stls_exception;
+ }
+
+ /* rfc2595, section 4 states that after a successful STLS
+ command, the client MUST discard prior CAPA responses */
+ camel_pop3_engine_reget_capabilities (store->engine);
+
+ return TRUE;
+
+ stls_exception:
+ if (clean_quit) {
+ /* try to disconnect cleanly */
+ pc = camel_pop3_engine_command_new (store->engine, 0, NULL, NULL, "QUIT\r\n");
+ while (camel_pop3_engine_iterate (store->engine, NULL) > 0)
+ ;
+ camel_pop3_engine_command_free (store->engine, pc);
+ }
+
+ camel_object_unref (CAMEL_OBJECT (store->engine));
+ camel_object_unref (CAMEL_OBJECT (tcp_stream));
+ store->engine = NULL;
+
+ return FALSE;
+#endif /* HAVE_NSS || HAVE_OPENSSL */
+}
- store->engine = camel_pop3_engine_new(CAMEL_REMOTE_STORE(store)->ostream);
+static struct {
+ char *value;
+ int mode;
+} ssl_options[] = {
+ { "", USE_SSL_ALWAYS },
+ { "always", USE_SSL_ALWAYS },
+ { "when-possible", USE_SSL_WHEN_POSSIBLE },
+ { "never", USE_SSL_NEVER },
+ { NULL, USE_SSL_NEVER },
+};
- return store->engine != NULL;
+#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)
+ const char *use_ssl;
+ int stls_supported;
+ int i, ssl_mode;
+
+ use_ssl = camel_url_get_param (service->url, "use_ssl");
+ if (use_ssl) {
+ for (i = 0; ssl_options[i].value; i++)
+ if (!strcmp (ssl_options[i].value, use_ssl))
+ break;
+ ssl_mode = ssl_options[i].mode;
+ } else
+ ssl_mode = USE_SSL_NEVER;
+
+ if (ssl_mode == USE_SSL_ALWAYS) {
+ /* First try STARTTLS */
+ if (!connect_to_server (service, ssl_mode, TRUE, &stls_supported, ex) &&
+ !stls_supported && EXCEPTION_RETRY (ex)) {
+ /* STARTTLS is unavailable - okay, now try old-style SSL */
+ camel_exception_clear (ex);
+ return connect_to_server (service, ssl_mode, FALSE, NULL, ex);
+ }
+
+ return TRUE;
+ } else if (ssl_mode == USE_SSL_WHEN_POSSIBLE) {
+ /* If the server supports STARTTLS, use it */
+ return connect_to_server (service, ssl_mode, TRUE, NULL, ex);
+ } else {
+ /* User doesn't care about SSL */
+ return connect_to_server (service, ssl_mode, FALSE, NULL, ex);
+ }
+#else
+ return connect_to_server (service, USE_SSL_NEVER, FALSE, NULL, ex);
+#endif
}
extern CamelServiceAuthType camel_pop3_password_authtype;
@@ -162,15 +361,15 @@ query_auth_types (CamelService *service, CamelException *ex)
types = CAMEL_SERVICE_CLASS (parent_class)->query_auth_types (service, ex);
if (camel_exception_is_set (ex))
- return types;
+ return NULL;
- if (connect_to_server (service, NULL)) {
+ if (connect_to_server_wrapper (service, NULL)) {
types = g_list_concat(types, g_list_copy(store->engine->auth));
pop3_disconnect (service, TRUE, NULL);
} else {
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
- _("Could not connect to POP server on "
- "%s."), service->url->host);
+ _("Could not connect to POP server on %s"),
+ service->url->host);
}
return types;
@@ -341,7 +540,7 @@ pop3_connect (CamelService *service, CamelException *ex)
char *errbuf = NULL;
gboolean tryagain;
CamelPOP3Store *store = (CamelPOP3Store *)service;
-
+
if (store->cache == NULL) {
char *root;
@@ -357,7 +556,7 @@ pop3_connect (CamelService *service, CamelException *ex)
}
}
- if (!connect_to_server (service, ex))
+ if (!connect_to_server_wrapper (service, ex))
return FALSE;
camel_exception_clear (ex);
@@ -393,19 +592,19 @@ pop3_disconnect (CamelService *service, gboolean clean, CamelException *ex)
if (clean) {
CamelPOP3Command *pc;
-
+
pc = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "QUIT\r\n");
while (camel_pop3_engine_iterate(store->engine, NULL) > 0)
;
camel_pop3_engine_command_free(store->engine, pc);
}
-
- camel_object_unref((CamelObject *)store->engine);
- store->engine = NULL;
if (!CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex))
return FALSE;
+ camel_object_unref((CamelObject *)store->engine);
+ store->engine = NULL;
+
return TRUE;
}
diff --git a/camel/providers/pop3/camel-pop3-store.h b/camel/providers/pop3/camel-pop3-store.h
index 8d560f8247..3b9b1f7ae1 100644
--- a/camel/providers/pop3/camel-pop3-store.h
+++ b/camel/providers/pop3/camel-pop3-store.h
@@ -34,7 +34,7 @@ extern "C" {
#endif /* __cplusplus }*/
#include <camel/camel-types.h>
-#include <camel/camel-remote-store.h>
+#include <camel/camel-store.h>
#include "camel-pop3-engine.h"
#define CAMEL_POP3_STORE_TYPE (camel_pop3_store_get_type ())
@@ -44,7 +44,7 @@ extern "C" {
typedef struct {
- CamelRemoteStore parent_object;
+ CamelStore parent_object;
CamelPOP3Engine *engine; /* pop processing engine */
@@ -54,7 +54,7 @@ typedef struct {
typedef struct {
- CamelRemoteStoreClass parent_class;
+ CamelStoreClass parent_class;
} CamelPOP3StoreClass;
diff --git a/camel/providers/smtp/camel-smtp-transport.c b/camel/providers/smtp/camel-smtp-transport.c
index 3e80ae6e7e..d07a4305aa 100644
--- a/camel/providers/smtp/camel-smtp-transport.c
+++ b/camel/providers/smtp/camel-smtp-transport.c
@@ -442,7 +442,7 @@ connect_to_server_wrapper (CamelService *service, CamelException *ex)
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 &&
+ !(transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS) &&
EXCEPTION_RETRY (ex)) {
/* STARTTLS is unavailable - okay, now try port 465 */
camel_exception_clear (ex);