diff options
Diffstat (limited to 'camel/providers/pop3/camel-pop3-store.c')
-rw-r--r-- | camel/providers/pop3/camel-pop3-store.c | 634 |
1 files changed, 0 insertions, 634 deletions
diff --git a/camel/providers/pop3/camel-pop3-store.c b/camel/providers/pop3/camel-pop3-store.c deleted file mode 100644 index 515e982b95..0000000000 --- a/camel/providers/pop3/camel-pop3-store.c +++ /dev/null @@ -1,634 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-pop3-store.c : class for a pop3 store */ - -/* - * Authors: - * Dan Winship <danw@ximian.com> - * Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 2000-2002 Ximian, Inc. (www.ximian.com) - * - * 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. - * - * 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 - -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -#include "camel-operation.h" - -#include "camel-pop3-store.h" -#include "camel-pop3-folder.h" -#include "camel-stream-buffer.h" -#include "camel-session.h" -#include "camel-exception.h" -#include "camel-url.h" -#include "e-util/md5-utils.h" -#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 CamelStoreClass *parent_class = NULL; - -static void finalize (CamelObject *object); - -static gboolean pop3_connect (CamelService *service, CamelException *ex); -static gboolean pop3_disconnect (CamelService *service, gboolean clean, CamelException *ex); -static GList *query_auth_types (CamelService *service, CamelException *ex); - -static CamelFolder *get_folder (CamelStore *store, const char *folder_name, - guint32 flags, CamelException *ex); - -static void init_trash (CamelStore *store); -static CamelFolder *get_trash (CamelStore *store, CamelException *ex); - -static void -camel_pop3_store_class_init (CamelPOP3StoreClass *camel_pop3_store_class) -{ - CamelServiceClass *camel_service_class = - CAMEL_SERVICE_CLASS (camel_pop3_store_class); - CamelStoreClass *camel_store_class = - CAMEL_STORE_CLASS (camel_pop3_store_class); - - 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; - camel_service_class->disconnect = pop3_disconnect; - - camel_store_class->get_folder = get_folder; - camel_store_class->init_trash = init_trash; - camel_store_class->get_trash = get_trash; -} - - - -static void -camel_pop3_store_init (gpointer object, gpointer klass) -{ - ; -} - -CamelType -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_STORE_TYPE, - "CamelPOP3Store", - sizeof (CamelPOP3Store), - sizeof (CamelPOP3StoreClass), - (CamelObjectClassInitFunc) camel_pop3_store_class_init, - NULL, - (CamelObjectInitFunc) camel_pop3_store_init, - finalize); - } - - return camel_pop3_store_type; -} - -static void -finalize (CamelObject *object) -{ - CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (object); - - /* force disconnect so we dont have it run later, after we've cleaned up some stuff */ - /* SIGH */ - - camel_service_disconnect((CamelService *)pop3_store, TRUE, NULL); - - if (pop3_store->engine) - camel_object_unref((CamelObject *)pop3_store->engine); - if (pop3_store->cache) - 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, int ssl_mode, int try_starttls, int *stls_support, CamelException *ex) -{ - CamelPOP3Store *store = CAMEL_POP3_STORE (service); - 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 */ -} - -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 }, -}; - -#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; -extern CamelServiceAuthType camel_pop3_apop_authtype; - -static GList * -query_auth_types (CamelService *service, CamelException *ex) -{ - CamelPOP3Store *store = CAMEL_POP3_STORE (service); - GList *types = NULL; - - types = CAMEL_SERVICE_CLASS (parent_class)->query_auth_types (service, ex); - if (camel_exception_is_set (ex)) - return 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); - } - - return types; -} - -/** - * camel_pop3_store_expunge: - * @store: the store - * @ex: a CamelException - * - * Expunge messages from the store. This will result in the connection - * being closed, which may cause later commands to fail if they can't - * reconnect. - **/ -void -camel_pop3_store_expunge (CamelPOP3Store *store, CamelException *ex) -{ - 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_service_disconnect (CAMEL_SERVICE (store), FALSE, ex); -} - -static int -try_sasl(CamelPOP3Store *store, const char *mech, CamelException *ex) -{ - CamelPOP3Stream *stream = store->engine->stream; - unsigned char *line, *resp; - CamelSasl *sasl; - unsigned int len; - int ret; - - sasl = camel_sasl_new("pop3", mech, (CamelService *)store); - if (sasl == NULL) { - camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Unable to connect to POP server.\n" - "No support for requested " - "authentication mechanism.")); - return -1; - } - - if (camel_stream_printf((CamelStream *)stream, "AUTH %s\r\n", mech) == -1) - goto ioerror; - - while (1) { - if (camel_pop3_stream_line(stream, &line, &len) == -1) - goto ioerror; - if (strncmp(line, "+OK", 3) == 0) - break; - if (strncmp(line, "-ERR", 4) == 0) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("SASL `%s' Login failed: %s"), mech, line); - goto done; - } - /* 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(line, "+ ", 2) != 0 - || camel_sasl_authenticated(sasl) - || (resp = camel_sasl_challenge_base64(sasl, line+2, ex)) == NULL) { - camel_stream_printf((CamelStream *)stream, "*\r\n"); - camel_pop3_stream_line(stream, &line, &len); - camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("SASL Protocol error")); - goto done; - } - - ret = camel_stream_printf((CamelStream *)stream, "%s\r\n", resp); - g_free(resp); - if (ret == -1) - goto ioerror; - - } - camel_object_unref((CamelObject *)sasl); - return 0; - -ioerror: - camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("I/O Error: %s"), strerror(errno)); -done: - camel_object_unref((CamelObject *)sasl); - return -1; -} - -static gboolean -pop3_try_authenticate (CamelService *service, const char *errmsg, - CamelException *ex) -{ - CamelPOP3Store *store = (CamelPOP3Store *)service; - int status; - CamelPOP3Command *pcu = NULL, *pcp = NULL; - - /* override, testing only */ - /*printf("Forcing authmech to 'login'\n"); - service->url->authmech = g_strdup("LOGIN");*/ - - if (!service->url->passwd) { - char *prompt; - - prompt = g_strdup_printf (_("%sPlease enter the POP password for %s@%s"), - errmsg ? errmsg : "", - service->url->user, - service->url->host); - service->url->passwd = camel_session_get_password (camel_service_get_session (service), - prompt, TRUE, service, "password", ex); - g_free (prompt); - if (!service->url->passwd) - return FALSE; - } - - if (!service->url->authmech) { - /* pop engine will take care of pipelining ability */ - pcu = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "USER %s\r\n", service->url->user); - pcp = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "PASS %s\r\n", service->url->passwd); - } else if (strcmp(service->url->authmech, "+APOP") == 0 && store->engine->apop) { - char *secret, md5asc[33], *d; - unsigned char md5sum[16], *s; - - secret = alloca(strlen(store->engine->apop)+strlen(service->url->passwd)+1); - sprintf(secret, "%s%s", store->engine->apop, service->url->passwd); - md5_get_digest(secret, strlen (secret), md5sum); - - for (s = md5sum, d = md5asc; d < md5asc + 32; s++, d += 2) - sprintf (d, "%.2x", *s); - - pcp = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "APOP %s %s\r\n", service->url->user, md5asc); - } else { - CamelServiceAuthType *auth; - GList *l; - - l = store->engine->auth; - while (l) { - auth = l->data; - if (strcmp(auth->authproto, service->url->authmech) == 0) - return try_sasl(store, service->url->authmech, ex) == -1; - l = l->next; - } - - camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Unable to connect to POP server.\n" - "No support for requested " - "authentication mechanism.")); - return FALSE; - } - - while (camel_pop3_engine_iterate(store->engine, pcp) > 0) - ; - status = pcp->state != CAMEL_POP3_COMMAND_OK; - if (status) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Unable to connect to POP server.\nError sending password: %s"), - store->engine->line); - } - camel_pop3_engine_command_free(store->engine, pcp); - - if (pcu) - camel_pop3_engine_command_free(store->engine, pcu); - - return status; -} - -static gboolean -pop3_connect (CamelService *service, CamelException *ex) -{ - char *errbuf = NULL; - gboolean tryagain; - CamelPOP3Store *store = (CamelPOP3Store *)service; - - if (store->cache == NULL) { - char *root; - - root = camel_session_get_storage_path(service->session, service, ex); - if (root) { - store->cache = camel_data_cache_new(root, 0, ex); - g_free(root); - if (store->cache) { - /* Default cache expiry - 1 week or not visited in a day */ - camel_data_cache_set_expire_age(store->cache, 60*60*24*7); - camel_data_cache_set_expire_access(store->cache, 60*60*24); - } - } - } - - if (!connect_to_server_wrapper (service, ex)) - return FALSE; - - camel_exception_clear (ex); - do { - if (camel_exception_is_set (ex)) { - errbuf = g_strdup_printf ("%s\n\n", camel_exception_get_description (ex)); - camel_exception_clear (ex); - - /* Uncache the password before prompting again. */ - camel_session_forget_password (camel_service_get_session (service), - service, "password", ex); - g_free (service->url->passwd); - service->url->passwd = NULL; - } - - tryagain = pop3_try_authenticate (service, errbuf, ex); - g_free (errbuf); - errbuf = NULL; - } while (tryagain); - - if (camel_exception_is_set (ex)) { - camel_service_disconnect (service, TRUE, ex); - return FALSE; - } - - return TRUE; -} - -static gboolean -pop3_disconnect (CamelService *service, gboolean clean, CamelException *ex) -{ - CamelPOP3Store *store = CAMEL_POP3_STORE (service); - - 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); - } - - if (!CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex)) - return FALSE; - - camel_object_unref((CamelObject *)store->engine); - store->engine = NULL; - - return TRUE; -} - -static CamelFolder * -get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) -{ - if (strcasecmp (folder_name, "inbox") != 0) { - camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, - _("No such folder `%s'."), folder_name); - return NULL; - } - return camel_pop3_folder_new (store, ex); -} - -static void -init_trash (CamelStore *store) -{ - /* no-op */ - ; -} - -static CamelFolder * -get_trash (CamelStore *store, CamelException *ex) -{ - /* no-op */ - return NULL; -} |