diff options
Diffstat (limited to 'camel/providers/pop3/camel-pop3-store.c')
-rw-r--r-- | camel/providers/pop3/camel-pop3-store.c | 770 |
1 files changed, 399 insertions, 371 deletions
diff --git a/camel/providers/pop3/camel-pop3-store.c b/camel/providers/pop3/camel-pop3-store.c index 64f2712685..6ff88d61c2 100644 --- a/camel/providers/pop3/camel-pop3-store.c +++ b/camel/providers/pop3/camel-pop3-store.c @@ -4,9 +4,8 @@ /* * Authors: * Dan Winship <danw@ximian.com> - * Michael Zucchi <notzed@ximian.com> * - * Copyright (C) 2000-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 version 2 of the GNU General Public @@ -39,26 +38,36 @@ #include "camel-operation.h" +#ifdef HAVE_KRB4 +/* Specified nowhere */ +#define KPOP_PORT 1109 + +#include <krb.h> +/* MIT krb4 des.h #defines _. Sigh. We don't need it. */ +#undef _ + +#ifdef NEED_KRB_SENDAUTH_PROTO +extern int krb_sendauth(long options, int fd, KTEXT ticket, char *service, + char *inst, char *realm, unsigned KRB4_32 checksum, + MSG_DAT *msg_data, CREDENTIALS *cred, + Key_schedule schedule, struct sockaddr_in *laddr, + struct sockaddr_in *faddr, char *version); +#endif +#endif + #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" #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_SSL -#include "camel-tcp-stream-ssl.h" -#endif /* Specified in RFC 1939 */ #define POP3_PORT 110 -static CamelStoreClass *parent_class = NULL; +static CamelRemoteStoreClass *parent_class = NULL; static void finalize (CamelObject *object); @@ -72,16 +81,20 @@ static CamelFolder *get_folder (CamelStore *store, const char *folder_name, static void init_trash (CamelStore *store); static CamelFolder *get_trash (CamelStore *store, CamelException *ex); +static int pop3_get_response (CamelPop3Store *store, char **ret, CamelException *ex); + + static void -camel_pop3_store_class_init (CamelPOP3StoreClass *camel_pop3_store_class) +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 ())); - + parent_class = CAMEL_REMOTE_STORE_CLASS(camel_type_get_global_classfuncs + (camel_remote_store_get_type ())); + /* virtual method overload */ camel_service_class->query_auth_types = query_auth_types; camel_service_class->connect = pop3_connect; @@ -97,7 +110,11 @@ 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 = 110; + /* FIXME: what should this port be?? */ + remote_store->default_ssl_port = 995; } CamelType @@ -106,10 +123,9 @@ 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), + camel_pop3_store_type = camel_type_register (CAMEL_REMOTE_STORE_TYPE, "CamelPop3Store", + sizeof (CamelPop3Store), + sizeof (CamelPop3StoreClass), (CamelObjectClassInitFunc) camel_pop3_store_class_init, NULL, (CamelObjectInitFunc) camel_pop3_store_init, @@ -122,236 +138,189 @@ camel_pop3_store_get_type (void) 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 */ + CamelPop3Store *pop3_store = CAMEL_POP3_STORE (object); - 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); + if (pop3_store->apop_timestamp) + g_free (pop3_store->apop_timestamp); + if (pop3_store->implementation) + g_free (pop3_store->implementation); } -enum { - USE_SSL_NEVER, - USE_SSL_ALWAYS, - USE_SSL_WHEN_POSSIBLE -}; - static gboolean -connect_to_server (CamelService *service, int ssl_mode, int try_starttls, CamelException *ex) +connect_to_server (CamelService *service, 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; - -#ifdef HAVE_SSL - if (camel_url_get_param (service->url, "use_ssl")) { - if (try_starttls) - tcp_stream = camel_tcp_stream_ssl_new_raw (service, service->url->host); - else { - port = service->url->port ? service->url->port : 995; - tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host); - } - } else { - tcp_stream = camel_tcp_stream_raw_new (); + CamelPop3Store *store = CAMEL_POP3_STORE (service); + char *buf, *apoptime, *apopend; + int status; + gboolean result; + +#ifdef HAVE_KRB4 + gboolean set_port = FALSE, kpop; + + kpop = (service->url->authmech && + !strcmp (service->url->authmech, "+KPOP")); + + if (kpop && service->url->port == 0) { + set_port = TRUE; + service->url->port = KPOP_PORT; } -#else - tcp_stream = camel_tcp_stream_raw_new (); -#endif /* HAVE_SSL */ - - 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, port, g_strerror (errno)); - - camel_object_unref (CAMEL_OBJECT (tcp_stream)); - +#endif + + result = CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex); + +#ifdef HAVE_KRB4 + if (set_port) + service->url->port = 0; +#endif + + if (result == FALSE) return FALSE; + +#ifdef HAVE_KRB4 + if (kpop) { + KTEXT_ST ticket_st; + MSG_DAT msg_data; + CREDENTIALS cred; + Key_schedule schedule; + struct hostent *h; + int fd; + + h = camel_service_gethost (service, ex); + + fd = GPOINTER_TO_INT (camel_tcp_stream_get_socket (CAMEL_TCP_STREAM (CAMEL_REMOTE_STORE (service)->ostream))); + status = krb_sendauth (0, fd, &ticket_st, "pop", h->h_name, + krb_realmofhost (h->h_name), 0, + &msg_data, &cred, schedule, + NULL, NULL, "KPOPV0.1"); + camel_free_host (h); + if (status != KSUCCESS) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Could not authenticate to " + "KPOP server: %s"), + krb_err_txt[status]); + return FALSE; + } + + if (!service->url->passwd) + service->url->passwd = g_strdup (service->url->user); } - - /* parent class connect initialization */ - if (CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex) == FALSE) { - camel_object_unref (CAMEL_OBJECT (tcp_stream)); +#endif /* HAVE_KRB4 */ + + /* Read the greeting, check status */ + status = pop3_get_response (store, &buf, ex); + switch (status) { + case CAMEL_POP3_ERR: + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Could not connect to server: %s"), + buf); + g_free (buf); + /* fall through */ + case CAMEL_POP3_FAIL: return FALSE; } - - store->engine = camel_pop3_engine_new (tcp_stream); - -#ifdef HAVE_SSL - 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; - } - } + + if (buf) { + apoptime = strchr (buf, '<'); + apopend = apoptime ? strchr (apoptime, '>') : NULL; + if (apopend) { + store->apop_timestamp = + g_strndup (apoptime, apopend - apoptime + 1); + memmove (apoptime, apopend + 1, strlen (apopend + 1)); } + store->implementation = buf; } -#endif /* HAVE_SSL */ - - camel_object_unref (CAMEL_OBJECT (tcp_stream)); - - return store->engine != NULL; - -#ifdef HAVE_SSL - 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 */ - ret = camel_tcp_stream_ssl_enable_ssl (CAMEL_TCP_STREAM_SSL (tcp_stream)); - - 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_SSL */ -} -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 }, -}; + /* Check extensions */ + store->login_delay = -1; + store->supports_top = -1; + store->supports_uidl = -1; + store->expires = -1; -static gboolean -connect_to_server_wrapper (CamelService *service, CamelException *ex) -{ -#ifdef HAVE_SSL - const char *use_ssl; - 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 the ssl port */ - if (!connect_to_server (service, ssl_mode, FALSE, ex)) { - if (camel_exception_get_id (ex) == CAMEL_EXCEPTION_SERVICE_UNAVAILABLE) { - /* The ssl port seems to be unavailable, lets try STARTTLS */ - camel_exception_clear (ex); - return connect_to_server (service, ssl_mode, TRUE, ex); - } else { - return FALSE; - } + status = camel_pop3_command (store, NULL, ex, "CAPA"); + if (status == CAMEL_POP3_OK) { + char *p; + int len; + + buf = camel_pop3_command_get_additional_data (store, 0, ex); + if (camel_exception_is_set (ex)) + return FALSE; + + p = buf; + while (*p) { + len = strcspn (p, "\n"); + if (!strncmp (p, "IMPLEMENTATION ", 15)) { + g_free (store->implementation); + store->implementation = + g_strndup (p + 15, len - 15); + } else if (len == 3 && !strncmp (p, "TOP", 3)) + store->supports_top = TRUE; + else if (len == 4 && !strncmp (p, "UIDL", 4)) + store->supports_uidl = TRUE; + else if (!strncmp (p, "LOGIN-DELAY ", 12)) + store->login_delay = atoi (p + 12); + else if (!strncmp (p, "EXPIRE NEVER", 12)) + store->expires = FALSE; + else if (!strncmp (p, "EXPIRE ", 7)) + store->expires = TRUE; + + p += len; + if (*p) + p++; } - - 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, ex); - } else { - /* User doesn't care about SSL */ - return connect_to_server (service, ssl_mode, FALSE, ex); + + g_free (buf); } -#else - return connect_to_server (service, USE_SSL_NEVER, FALSE, ex); -#endif + + return TRUE; } extern CamelServiceAuthType camel_pop3_password_authtype; extern CamelServiceAuthType camel_pop3_apop_authtype; +#ifdef HAVE_KRB4 +extern CamelServiceAuthType camel_pop3_kpop_authtype; +#endif static GList * query_auth_types (CamelService *service, CamelException *ex) { - CamelPOP3Store *store = CAMEL_POP3_STORE (service); + CamelPop3Store *store = CAMEL_POP3_STORE (service); GList *types = NULL; + gboolean passwd = TRUE, apop = TRUE; +#ifdef HAVE_KRB4 + gboolean kpop; +#endif types = CAMEL_SERVICE_CLASS (parent_class)->query_auth_types (service, ex); if (camel_exception_is_set (ex)) - return NULL; + return types; - if (connect_to_server_wrapper (service, NULL)) { - types = g_list_concat(types, g_list_copy(store->engine->auth)); + passwd = connect_to_server (service, NULL); + apop = store->apop_timestamp != NULL; + if (passwd) pop3_disconnect (service, TRUE, NULL); - } else { + +#ifdef HAVE_KRB4 + service->url->authmech = "+KPOP"; + kpop = connect_to_server (service, NULL); + service->url->authmech = NULL; + if (kpop) + pop3_disconnect (service, TRUE, NULL); +#endif + + if (passwd) + types = g_list_append (types, &camel_pop3_password_authtype); + if (apop) + types = g_list_append (types, &camel_pop3_apop_authtype); +#ifdef HAVE_KRB4 + if (kpop) + types = g_list_append (types, &camel_pop3_kpop_authtype); +#endif + + if (!types) { 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; } @@ -365,95 +334,31 @@ query_auth_types (CamelService *service, CamelException *ex) * reconnect. **/ void -camel_pop3_store_expunge (CamelPOP3Store *store, CamelException *ex) +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_pop3_command (store, NULL, ex, "QUIT"); 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; - CamelPOP3Command *pcu = NULL, *pcp = NULL; + CamelPop3Store *store = (CamelPop3Store *)service; int status; + char *msg; - /* override, testing only */ - /*printf("Forcing authmech to 'login'\n"); - service->url->authmech = g_strdup("LOGIN");*/ - + /* The KPOP code will have set the password to be the username + * in connect_to_server. Password and APOP are the only other + * cases, and they both need a password. So if there's no + * password stored, query for it. + */ if (!service->url->passwd) { char *prompt; - prompt = g_strdup_printf (_("%sPlease enter the POP password for %s@%s"), - errmsg ? errmsg : "", + prompt = g_strdup_printf (_("%sPlease enter the POP3 password " + "for %s@%s"), errmsg ? errmsg : "", service->url->user, service->url->host); service->url->passwd = camel_session_get_password (camel_service_get_session (service), @@ -462,35 +367,42 @@ pop3_try_authenticate (CamelService *service, const char *errmsg, 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) { + + if (!service->url->authmech || !strcmp (service->url->authmech, "+KPOP")) { + status = camel_pop3_command (store, &msg, ex, "USER %s", + service->url->user); + switch (status) { + case CAMEL_POP3_ERR: + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Unable to connect to POP " + "server.\nError sending " + "username: %s"), + msg ? msg : _("(Unknown)")); + g_free (msg); + /*fallll*/ + case CAMEL_POP3_FAIL: + return FALSE; + } + g_free (msg); + + status = camel_pop3_command (store, &msg, ex, "PASS %s", + service->url->passwd); + } else if (!strcmp (service->url->authmech, "+APOP") + && store->apop_timestamp) { 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); - + secret = g_strdup_printf ("%s%s", store->apop_timestamp, + service->url->passwd); + md5_get_digest (secret, strlen (secret), md5sum); + g_free (secret); + 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); + status = camel_pop3_command (store, &msg, ex, "APOP %s %s", + 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 " @@ -498,25 +410,16 @@ pop3_try_authenticate (CamelService *service, const char *errmsg, return FALSE; } - while ((status = camel_pop3_engine_iterate (store->engine, pcp)) > 0) - ; - - if (pcp->state != CAMEL_POP3_COMMAND_OK) { - if (status == -1) - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Unable to connect to POP server.\nError sending password: %s"), - errno ? g_strerror (errno) : _("Unknown error")); - else - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Unable to connect to POP server.\nError sending password: %s"), - store->engine->line); + if (status == CAMEL_POP3_ERR) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Unable to connect to POP server.\n" + "Error sending password: %s"), + msg ? msg : _("(Unknown)")); } - camel_pop3_engine_command_free(store->engine, pcp); - - if (pcu) - camel_pop3_engine_command_free(store->engine, pcu); - - return status; + + g_free (msg); + + return status == CAMEL_POP3_ERR; } static gboolean @@ -524,24 +427,8 @@ 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)) + if (!connect_to_server (service, ex)) return FALSE; camel_exception_clear (ex); @@ -550,14 +437,11 @@ pop3_connect (CamelService *service, CamelException *ex) errbuf = g_strdup_printf ("%s\n\n", camel_exception_get_description (ex)); camel_exception_clear (ex); - /* don't forget the password if we encountered an unknown error */ - if (camel_exception_get_id (ex) == CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE) { - /* 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; - } + /* 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); @@ -576,30 +460,22 @@ pop3_connect (CamelService *service, CamelException *ex) static gboolean pop3_disconnect (CamelService *service, gboolean clean, CamelException *ex) { - CamelPOP3Store *store = CAMEL_POP3_STORE (service); + 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 (clean) + camel_pop3_command (store, NULL, ex, "QUIT"); 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) +get_folder (CamelStore *store, const char *folder_name, + guint32 flags, CamelException *ex) { - if (strcasecmp (folder_name, "inbox") != 0) { + if (g_strcasecmp (folder_name, "inbox") != 0) { camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID, _("No such folder `%s'."), folder_name); return NULL; @@ -620,3 +496,155 @@ get_trash (CamelStore *store, CamelException *ex) /* no-op */ return NULL; } + + +/** + * camel_pop3_command: Send a command to a POP3 server. + * @store: the POP3 store + * @ret: a pointer to return the full server response in + * @fmt: a printf-style format string, followed by arguments + * + * This command sends the command specified by @fmt and the following + * arguments to the connected POP3 store specified by @store. It then + * reads the server's response and parses out the status code. If + * the caller passed a non-NULL pointer for @ret, camel_pop3_command + * will set it to point to an buffer containing the rest of the + * response from the POP3 server. (If @ret was passed but there was + * no extended response, @ret will be set to NULL.) The caller must + * free this buffer when it is done with it. + * + * Return value: one of CAMEL_POP3_OK (command executed successfully), + * CAMEL_POP3_ERR (command encounted an error), or CAMEL_POP3_FAIL + * (a protocol-level error occurred, and Camel is uncertain of the + * result of the command.) @ex will be set if the return value is + * CAMEL_POP3_FAIL, but *NOT* if it is CAMEL_POP3_ERR. + **/ +int +camel_pop3_command (CamelPop3Store *store, char **ret, CamelException *ex, char *fmt, ...) +{ + char *cmdbuf; + va_list ap; + + va_start (ap, fmt); + cmdbuf = g_strdup_vprintf (fmt, ap); + va_end (ap); + + /* Send the command */ + if (camel_remote_store_send_string (CAMEL_REMOTE_STORE (store), ex, "%s\r\n", cmdbuf) < 0) { + g_free (cmdbuf); + if (ret) + *ret = NULL; + return CAMEL_POP3_FAIL; + } + g_free (cmdbuf); + + return pop3_get_response (store, ret, ex); +} + +static int +pop3_get_response (CamelPop3Store *store, char **ret, CamelException *ex) +{ + char *respbuf; + int status; + + if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &respbuf, ex) < 0) { + if (ret) + *ret = NULL; + return CAMEL_POP3_FAIL; + } + + if (!strncmp (respbuf, "+OK", 3)) + status = CAMEL_POP3_OK; + else if (!strncmp (respbuf, "-ERR", 4)) + status = CAMEL_POP3_ERR; + else { + status = CAMEL_POP3_FAIL; + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + _("Unexpected response from POP server: %s"), + respbuf); + } + + if (ret) { + if (status != CAMEL_POP3_FAIL) { + *ret = strchr (respbuf, ' '); + if (*ret) + *ret = g_strdup (*ret + 1); + } else + *ret = NULL; + } + g_free (respbuf); + + return status; +} + +/** + * camel_pop3_command_get_additional_data: get "additional data" from + * a POP3 command. + * @store: the POP3 store + * @total: Total bytes expected (for progress reporting), use 0 for 'unknown'. + * + * This command gets the additional data returned by "multi-line" POP + * commands, such as LIST, RETR, TOP, and UIDL. This command _must_ + * be called after a successful (CAMEL_POP3_OK) call to + * camel_pop3_command for a command that has a multi-line response. + * The returned data is un-byte-stuffed, and has lines termined by + * newlines rather than CR/LF pairs. + * + * Return value: the data, which the caller must free. + **/ +char * +camel_pop3_command_get_additional_data (CamelPop3Store *store, int total, CamelException *ex) +{ + GPtrArray *data; + char *buf, *p; + int i, len = 0, status = CAMEL_POP3_OK; + int pc = 0; + + data = g_ptr_array_new (); + while (1) { + if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (store), &buf, ex) < 0) { + status = CAMEL_POP3_FAIL; + break; + } + + if (!strcmp (buf, ".")) + break; + + g_ptr_array_add (data, buf); + len += strlen (buf) + 1; + + if (total) { + pc = (len+1) * 100 / total; + camel_operation_progress(NULL, pc); + } else { + camel_operation_progress_count(NULL, len); + } + } + + if (buf) + g_free (buf); + + if (status == CAMEL_POP3_OK) { + buf = g_malloc0 (len + 1); + + for (i = 0, p = buf; i < data->len; i++) { + char *ptr, *datap; + + datap = (char *) data->pdata[i]; + ptr = (*datap == '.') ? datap + 1 : datap; + len = strlen (ptr); + memcpy (p, ptr, len); + p += len; + *p++ = '\n'; + } + *p = '\0'; + } else + buf = NULL; + + for (i = 0; i < data->len; i++) + g_free (data->pdata[i]); + g_ptr_array_free (data, TRUE); + + return buf; +} + |