From 626182bef55006c632db43e73e53105d451f9930 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 8 Mar 2002 23:43:43 +0000 Subject: Don't call camel_remote_store_get_authtypes since we no longer subclass 2002-03-08 Jeffrey Stedfast * 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). svn path=/trunk/; revision=15998 --- camel/providers/pop3/camel-pop3-engine.c | 51 ++++-- camel/providers/pop3/camel-pop3-engine.h | 4 + camel/providers/pop3/camel-pop3-provider.c | 2 +- camel/providers/pop3/camel-pop3-store.c | 257 ++++++++++++++++++++++++---- camel/providers/pop3/camel-pop3-store.h | 6 +- camel/providers/smtp/camel-smtp-transport.c | 2 +- 6 files changed, 272 insertions(+), 50 deletions(-) (limited to 'camel/providers') 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 +#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 -#include +#include #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); -- cgit