Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / pop3 / camel-pop3-store.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-pop3-store.c : class for a pop3 store */
3
4 /* 
5  * Authors:
6  *   Dan Winship <danw@ximian.com>
7  *   Michael Zucchi <notzed@ximian.com>
8  *
9  * Copyright (C) 2000-2002 Ximian, Inc. (www.ximian.com)
10  *
11  * This program is free software; you can redistribute it and/or 
12  * modify it under the terms of version 2 of the GNU Lesser General Public 
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
23  * USA
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <ctype.h>
37
38 #include <glib/gi18n-lib.h>
39
40 #include <libedataserver/md5-utils.h>
41
42 #include "camel-data-cache.h"
43 #include "camel-exception.h"
44 #include "camel-net-utils.h"
45 #include "camel-operation.h"
46 #include "camel-pop3-engine.h"
47 #include "camel-pop3-folder.h"
48 #include "camel-pop3-store.h"
49 #include "camel-sasl.h"
50 #include "camel-session.h"
51 #include "camel-stream-buffer.h"
52 #include "camel-tcp-stream-raw.h"
53 #include "camel-tcp-stream.h"
54 #include "camel-url.h"
55
56 #ifdef HAVE_SSL
57 #include "camel-tcp-stream-ssl.h"
58 #endif
59
60 /* Specified in RFC 1939 */
61 #define POP3_PORT "110"
62 #define POP3S_PORT "995"
63
64 static CamelStoreClass *parent_class = NULL;
65
66 static void finalize (CamelObject *object);
67
68 static gboolean pop3_connect (CamelService *service, CamelException *ex);
69 static gboolean pop3_disconnect (CamelService *service, gboolean clean, CamelException *ex);
70 static GList *query_auth_types (CamelService *service, CamelException *ex);
71
72 static CamelFolder *get_folder (CamelStore *store, const char *folder_name, 
73                                 guint32 flags, CamelException *ex);
74
75 static CamelFolder *get_trash  (CamelStore *store, CamelException *ex);
76
77 static void
78 camel_pop3_store_class_init (CamelPOP3StoreClass *camel_pop3_store_class)
79 {
80         CamelServiceClass *camel_service_class =
81                 CAMEL_SERVICE_CLASS (camel_pop3_store_class);
82         CamelStoreClass *camel_store_class =
83                 CAMEL_STORE_CLASS (camel_pop3_store_class);
84
85         parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_store_get_type ()));
86         
87         /* virtual method overload */
88         camel_service_class->query_auth_types = query_auth_types;
89         camel_service_class->connect = pop3_connect;
90         camel_service_class->disconnect = pop3_disconnect;
91
92         camel_store_class->get_folder = get_folder;
93         camel_store_class->get_trash = get_trash;
94 }
95
96
97
98 static void
99 camel_pop3_store_init (gpointer object, gpointer klass)
100 {
101         ;
102 }
103
104 CamelType
105 camel_pop3_store_get_type (void)
106 {
107         static CamelType camel_pop3_store_type = CAMEL_INVALID_TYPE;
108
109         if (!camel_pop3_store_type) {
110                 camel_pop3_store_type = camel_type_register (CAMEL_STORE_TYPE,
111                                                              "CamelPOP3Store",
112                                                              sizeof (CamelPOP3Store),
113                                                              sizeof (CamelPOP3StoreClass),
114                                                              (CamelObjectClassInitFunc) camel_pop3_store_class_init,
115                                                              NULL,
116                                                              (CamelObjectInitFunc) camel_pop3_store_init,
117                                                              finalize);
118         }
119
120         return camel_pop3_store_type;
121 }
122
123 static void
124 finalize (CamelObject *object)
125 {
126         CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (object);
127
128         /* force disconnect so we dont have it run later, after we've cleaned up some stuff */
129         /* SIGH */
130
131         camel_service_disconnect((CamelService *)pop3_store, TRUE, NULL);
132
133         if (pop3_store->engine)
134                 camel_object_unref((CamelObject *)pop3_store->engine);
135         if (pop3_store->cache)
136                 camel_object_unref((CamelObject *)pop3_store->cache);
137 }
138
139 enum {
140         MODE_CLEAR,
141         MODE_SSL,
142         MODE_TLS,
143 };
144
145 #ifdef HAVE_SSL
146 #define SSL_PORT_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 | CAMEL_TCP_STREAM_SSL_ENABLE_SSL3)
147 #define STARTTLS_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_TLS)
148 #endif
149
150 static gboolean
151 connect_to_server (CamelService *service, struct addrinfo *ai, int ssl_mode, CamelException *ex)
152 {
153         CamelPOP3Store *store = CAMEL_POP3_STORE (service);
154         CamelStream *tcp_stream;
155         CamelPOP3Command *pc;
156         guint32 flags = 0;
157         int clean_quit = TRUE;
158         int ret;
159         const gchar *delete_days;
160         
161         if (ssl_mode != MODE_CLEAR) {
162 #ifdef HAVE_SSL
163                 if (ssl_mode == MODE_TLS) {
164                         tcp_stream = camel_tcp_stream_ssl_new_raw (service->session, service->url->host, STARTTLS_FLAGS);
165                 } else {
166                         tcp_stream = camel_tcp_stream_ssl_new (service->session, service->url->host, SSL_PORT_FLAGS);
167                 }
168 #else
169                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
170                                       _("Could not connect to %s: %s"),
171                                       service->url->host, _("SSL unavailable"));
172                 
173                 return FALSE;
174 #endif /* HAVE_SSL */
175         } else {
176                 tcp_stream = camel_tcp_stream_raw_new ();
177         }
178         
179         if ((ret = camel_tcp_stream_connect ((CamelTcpStream *) tcp_stream, ai)) == -1) {
180                 if (errno == EINTR)
181                         camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
182                                              _("Connection canceled"));
183                 else
184                         camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
185                                               _("Could not connect to %s: %s"),
186                                               service->url->host,
187                                               g_strerror (errno));
188                 
189                 camel_object_unref (tcp_stream);
190                 
191                 return FALSE;
192         }
193         
194         /* parent class connect initialization */
195         if (CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex) == FALSE) {
196                 camel_object_unref (tcp_stream);
197                 return FALSE;
198         }
199         
200         if (camel_url_get_param (service->url, "disable_extensions"))
201                 flags |= CAMEL_POP3_ENGINE_DISABLE_EXTENSIONS;
202         
203         if ((delete_days = (gchar *) camel_url_get_param(service->url,"delete_after"))) 
204                 store->delete_after =  atoi(delete_days);
205         
206         if (!(store->engine = camel_pop3_engine_new (tcp_stream, flags))) {
207                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
208                                       _("Failed to read a valid greeting from POP server %s"),
209                                       service->url->host);
210                 camel_object_unref (tcp_stream);
211                 return FALSE;
212         }
213         
214         if (ssl_mode != MODE_TLS) {
215                 camel_object_unref (tcp_stream);
216                 return TRUE;
217         }
218         
219 #ifdef HAVE_SSL
220         if (!(store->engine->capa & CAMEL_POP3_CAP_STLS)) {
221                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
222                                       _("Failed to connect to POP server %s in secure mode: %s"),
223                                       service->url->host, _("STLS not supported by server"));
224                 goto stls_exception;
225         }
226         
227         /* as soon as we send a STLS command, all hope is lost of a clean QUIT if problems arise */
228         clean_quit = FALSE;
229         
230         pc = camel_pop3_engine_command_new (store->engine, 0, NULL, NULL, "STLS\r\n");
231         while (camel_pop3_engine_iterate (store->engine, NULL) > 0)
232                 ;
233         
234         ret = pc->state == CAMEL_POP3_COMMAND_OK;
235         camel_pop3_engine_command_free (store->engine, pc);
236         
237         if (ret == FALSE) {
238                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
239                                       _("Failed to connect to POP server %s in secure mode: %s"),
240                                       service->url->host, store->engine->line);
241                 goto stls_exception;
242         }
243         
244         /* Okay, now toggle SSL/TLS mode */
245         ret = camel_tcp_stream_ssl_enable_ssl (CAMEL_TCP_STREAM_SSL (tcp_stream));
246         
247         if (ret == -1) {
248                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
249                                       _("Failed to connect to POP server %s in secure mode: %s"),
250                                       service->url->host, _("TLS negotiations failed"));
251                 goto stls_exception;
252         }
253 #else
254         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
255                               _("Failed to connect to POP server %s in secure mode: %s"),
256                               service->url->host, _("TLS is not available in this build"));
257         goto stls_exception;
258 #endif /* HAVE_SSL */
259         
260         camel_object_unref (tcp_stream);
261         
262         /* rfc2595, section 4 states that after a successful STLS
263            command, the client MUST discard prior CAPA responses */
264         camel_pop3_engine_reget_capabilities (store->engine);
265         
266         return TRUE;
267         
268  stls_exception:
269         if (clean_quit) {
270                 /* try to disconnect cleanly */
271                 pc = camel_pop3_engine_command_new (store->engine, 0, NULL, NULL, "QUIT\r\n");
272                 while (camel_pop3_engine_iterate (store->engine, NULL) > 0)
273                         ;
274                 camel_pop3_engine_command_free (store->engine, pc);
275         }
276         
277         camel_object_unref (CAMEL_OBJECT (store->engine));
278         camel_object_unref (CAMEL_OBJECT (tcp_stream));
279         store->engine = NULL;
280         
281         return FALSE;
282 }
283
284 static struct {
285         char *value;
286         char *serv;
287         char *port;
288         int mode;
289 } ssl_options[] = {
290         { "",              "pop3s", POP3S_PORT, MODE_SSL   },  /* really old (1.x) */
291         { "always",        "pop3s", POP3S_PORT, MODE_SSL   },
292         { "when-possible", "pop3",  POP3_PORT,  MODE_TLS   },
293         { "never",         "pop3",  POP3_PORT,  MODE_CLEAR },
294         { NULL,            "pop3",  POP3_PORT,  MODE_CLEAR },
295 };
296
297 static gboolean
298 connect_to_server_wrapper (CamelService *service, CamelException *ex)
299 {
300         struct addrinfo hints, *ai;
301         const char *ssl_mode;
302         int mode, ret, i;
303         char *serv;
304         const char *port;
305
306         if ((ssl_mode = camel_url_get_param (service->url, "use_ssl"))) {
307                 for (i = 0; ssl_options[i].value; i++)
308                         if (!strcmp (ssl_options[i].value, ssl_mode))
309                                 break;
310                 mode = ssl_options[i].mode;
311                 serv = ssl_options[i].serv;
312                 port = ssl_options[i].port;
313         } else {
314                 mode = MODE_CLEAR;
315                 serv = "pop3";
316                 port = POP3S_PORT;
317         }
318         
319         if (service->url->port) {
320                 serv = g_alloca (16);
321                 sprintf (serv, "%d", service->url->port);
322                 port = NULL;
323         }
324         
325         memset (&hints, 0, sizeof (hints));
326         hints.ai_socktype = SOCK_STREAM;
327         hints.ai_family = PF_UNSPEC;
328         ai = camel_getaddrinfo(service->url->host, serv, &hints, ex);
329         if (ai == NULL && port != NULL && camel_exception_get_id(ex) != CAMEL_EXCEPTION_USER_CANCEL) {
330                 camel_exception_clear (ex);
331                 ai = camel_getaddrinfo(service->url->host, port, &hints, ex);
332         }
333         
334         if (ai == NULL)
335                 return FALSE;
336         
337         ret = connect_to_server (service, ai, mode, ex);
338         
339         camel_freeaddrinfo (ai);
340         
341         return ret;
342 }
343
344 extern CamelServiceAuthType camel_pop3_password_authtype;
345 extern CamelServiceAuthType camel_pop3_apop_authtype;
346
347 static GList *
348 query_auth_types (CamelService *service, CamelException *ex)
349 {
350         CamelPOP3Store *store = CAMEL_POP3_STORE (service);
351         GList *types = NULL;
352
353         types = CAMEL_SERVICE_CLASS (parent_class)->query_auth_types (service, ex);
354         if (camel_exception_is_set (ex))
355                 return NULL;
356
357         if (connect_to_server_wrapper (service, NULL)) {
358                 types = g_list_concat(types, g_list_copy(store->engine->auth));
359                 pop3_disconnect (service, TRUE, NULL);
360         } else {
361                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
362                                       _("Could not connect to POP server %s"),
363                                       service->url->host);
364         }
365
366         return types;
367 }
368
369 /**
370  * camel_pop3_store_expunge:
371  * @store: the store
372  * @ex: a CamelException
373  *
374  * Expunge messages from the store. This will result in the connection
375  * being closed, which may cause later commands to fail if they can't
376  * reconnect.
377  **/
378 void
379 camel_pop3_store_expunge (CamelPOP3Store *store, CamelException *ex)
380 {
381         CamelPOP3Command *pc;
382
383         pc = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "QUIT\r\n");
384         while (camel_pop3_engine_iterate(store->engine, NULL) > 0)
385                 ;
386         camel_pop3_engine_command_free(store->engine, pc);
387
388         camel_service_disconnect (CAMEL_SERVICE (store), FALSE, ex);
389 }
390
391 static int
392 try_sasl(CamelPOP3Store *store, const char *mech, CamelException *ex)
393 {
394         CamelPOP3Stream *stream = store->engine->stream;
395         unsigned char *line, *resp;
396         CamelSasl *sasl;
397         unsigned int len;
398         int ret;
399
400         sasl = camel_sasl_new("pop3", mech, (CamelService *)store);
401         if (sasl == NULL) {
402                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
403                                       _("Unable to connect to POP server %s: "
404                                         "No support for requested authentication mechanism."),
405                                       CAMEL_SERVICE (store)->url->host);
406                 return -1;
407         }
408
409         if (camel_stream_printf((CamelStream *)stream, "AUTH %s\r\n", mech) == -1)
410                 goto ioerror;
411
412         while (1) {
413                 if (camel_pop3_stream_line(stream, &line, &len) == -1)
414                         goto ioerror;
415                 if (strncmp((char *) line, (char *) "+OK", 3) == 0)
416                         break;
417                 if (strncmp((char *) line, (char *) "-ERR", 4) == 0) {
418                         camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
419                                               _("SASL `%s' Login failed for POP server %s: %s"),
420                                               mech, CAMEL_SERVICE (store)->url->host, line);
421                         goto done;
422                 }
423                 /* If we dont get continuation, or the sasl object's run out of work, or we dont get a challenge,
424                    its a protocol error, so fail, and try reset the server */
425                 if (strncmp((char *) line, (char *) "+ ", 2) != 0
426                     || camel_sasl_authenticated(sasl)
427                     || (resp = (unsigned char *) camel_sasl_challenge_base64(sasl, (const char *) line+2, ex)) == NULL) {
428                         camel_stream_printf((CamelStream *)stream, "*\r\n");
429                         camel_pop3_stream_line(stream, &line, &len);
430                         camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
431                                               _("Cannot login to POP server %s: SASL Protocol error"),
432                                               CAMEL_SERVICE (store)->url->host);
433                         goto done;
434                 }
435
436                 ret = camel_stream_printf((CamelStream *)stream, "%s\r\n", resp);
437                 g_free(resp);
438                 if (ret == -1)
439                         goto ioerror;
440
441         }
442         camel_object_unref((CamelObject *)sasl);
443         return 0;
444         
445  ioerror:
446         if (errno == EINTR) {
447                 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled"));
448         } else {
449                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
450                                       _("Failed to authenticate on POP server %s: %s"),
451                                       CAMEL_SERVICE (store)->url->host, g_strerror (errno));
452         }
453  done:
454         camel_object_unref((CamelObject *)sasl);
455         return -1;
456 }
457
458 static int
459 pop3_try_authenticate (CamelService *service, gboolean reprompt, const char *errmsg, CamelException *ex)
460 {
461         CamelPOP3Store *store = (CamelPOP3Store *)service;
462         CamelPOP3Command *pcu = NULL, *pcp = NULL;
463         int status;
464         
465         /* override, testing only */
466         /*printf("Forcing authmech to 'login'\n");
467         service->url->authmech = g_strdup("LOGIN");*/
468         
469         if (!service->url->passwd) {
470                 char *prompt;
471                 guint32 flags = CAMEL_SESSION_PASSWORD_SECRET;
472                 
473                 if (reprompt)
474                         flags |= CAMEL_SESSION_PASSWORD_REPROMPT;
475                 
476                 prompt = g_strdup_printf (_("%sPlease enter the POP password for %s on host %s"),
477                                           errmsg ? errmsg : "",
478                                           service->url->user,
479                                           service->url->host);
480                 service->url->passwd = camel_session_get_password (camel_service_get_session (service), service, NULL,
481                                                                    prompt, "password", flags, ex);
482                 g_free (prompt);
483                 if (!service->url->passwd)
484                         return FALSE;
485         }
486
487         if (!service->url->authmech) {
488                 /* pop engine will take care of pipelining ability */
489                 pcu = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "USER %s\r\n", service->url->user);
490                 pcp = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "PASS %s\r\n", service->url->passwd);
491         } else if (strcmp(service->url->authmech, "+APOP") == 0 && store->engine->apop) {
492                 char *secret, md5asc[33], *d;
493                 unsigned char md5sum[16], *s;
494
495                 d = store->engine->apop;
496
497                 while (*d != '\0') {
498                         if (!isascii((int)*d)) {
499
500                                 /* README for Translators: The string APOP should not be translated */
501                                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
502                                                 _("Unable to connect to POP server %s:  Invalid APOP ID received. Impersonation attack suspected. Please contact your admin."),
503                                                 CAMEL_SERVICE (store)->url->host);
504
505                                 return FALSE;
506                         }
507                         d++;
508                 }
509                 
510                 secret = g_alloca(strlen(store->engine->apop)+strlen(service->url->passwd)+1);
511                 sprintf(secret, "%s%s",  store->engine->apop, service->url->passwd);
512                 md5_get_digest(secret, strlen (secret), md5sum);
513
514                 for (s = md5sum, d = md5asc; d < md5asc + 32; s++, d += 2)
515                         sprintf (d, "%.2x", *s);
516                 
517                 pcp = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "APOP %s %s\r\n",
518                                                     service->url->user, md5asc);
519         } else {
520                 CamelServiceAuthType *auth;
521                 GList *l;
522
523                 l = store->engine->auth;
524                 while (l) {
525                         auth = l->data;
526                         if (strcmp(auth->authproto, service->url->authmech) == 0)
527                                 return try_sasl(store, service->url->authmech, ex) == -1;
528                         l = l->next;
529                 }
530                 
531                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
532                                       _("Unable to connect to POP server %s: "
533                                         "No support for requested authentication mechanism."),
534                                       CAMEL_SERVICE (store)->url->host);
535                 return FALSE;
536         }
537         
538         while ((status = camel_pop3_engine_iterate(store->engine, pcp)) > 0)
539                 ;
540         
541         if (status == -1) {
542                 if (errno == EINTR) {
543                         camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Canceled"));
544                 } else {
545                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
546                                               _("Unable to connect to POP server %s.\n"
547                                                 "Error sending password: %s"),
548                                               CAMEL_SERVICE (store)->url->host,
549                                               errno ? g_strerror (errno) : _("Unknown error"));
550                 }
551         } else if (pcu && pcu->state != CAMEL_POP3_COMMAND_OK) {
552                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
553                                       _("Unable to connect to POP server %s.\n"
554                                         "Error sending username: %s"),
555                                       CAMEL_SERVICE (store)->url->host,
556                                       store->engine->line ? (char *)store->engine->line : _("Unknown error"));
557         } else if (pcp->state != CAMEL_POP3_COMMAND_OK)
558                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
559                                       _("Unable to connect to POP server %s.\n"
560                                         "Error sending password: %s"),
561                                       CAMEL_SERVICE (store)->url->host,
562                                       store->engine->line ? (char *)store->engine->line : _("Unknown error"));
563         
564         camel_pop3_engine_command_free(store->engine, pcp);
565         
566         if (pcu)
567                 camel_pop3_engine_command_free(store->engine, pcu);
568         
569         return status;
570 }
571
572 static gboolean
573 pop3_connect (CamelService *service, CamelException *ex)
574 {
575         CamelPOP3Store *store = (CamelPOP3Store *)service;
576         gboolean reprompt = FALSE;
577         CamelSession *session;
578         char *errbuf = NULL;
579         int status;
580         
581         session = camel_service_get_session (service);
582         
583         if (store->cache == NULL) {
584                 char *root;
585
586                 root = camel_session_get_storage_path (session, service, ex);
587                 if (root) {
588                         store->cache = camel_data_cache_new(root, 0, ex);
589                         g_free(root);
590                         if (store->cache) {
591                                 /* Default cache expiry - 1 week or not visited in a day */
592                                 camel_data_cache_set_expire_age(store->cache, 60*60*24*7);
593                                 camel_data_cache_set_expire_access(store->cache, 60*60*24);
594                         }
595                 }
596         }
597         
598         if (!connect_to_server_wrapper (service, ex))
599                 return FALSE;
600         
601         while (1) {
602                 status = pop3_try_authenticate (service, reprompt, errbuf, ex);
603                 g_free (errbuf);
604                 errbuf = NULL;
605                 
606                 /* we only re-prompt if we failed to authenticate, any other error and we just abort */
607                 if (status == 0 && camel_exception_get_id (ex) == CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE) {
608                         errbuf = g_strdup_printf ("%s\n\n", camel_exception_get_description (ex));
609                         g_free (service->url->passwd);
610                         service->url->passwd = NULL;
611                         reprompt = TRUE;
612                         camel_exception_clear (ex);
613                 } else
614                         break;
615         }
616         
617         g_free (errbuf);
618         
619         if (status == -1 || camel_exception_is_set(ex)) {
620                 camel_service_disconnect(service, TRUE, ex);
621                 return FALSE;
622         }
623         
624         /* Now that we are in the TRANSACTION state, try regetting the capabilities */
625         store->engine->state = CAMEL_POP3_ENGINE_TRANSACTION;
626         camel_pop3_engine_reget_capabilities (store->engine);
627         
628         return TRUE;
629 }
630
631 static gboolean
632 pop3_disconnect (CamelService *service, gboolean clean, CamelException *ex)
633 {
634         CamelPOP3Store *store = CAMEL_POP3_STORE (service);
635         
636         if (clean) {
637                 CamelPOP3Command *pc;
638                 
639                 pc = camel_pop3_engine_command_new(store->engine, 0, NULL, NULL, "QUIT\r\n");
640                 while (camel_pop3_engine_iterate(store->engine, NULL) > 0)
641                         ;
642                 camel_pop3_engine_command_free(store->engine, pc);
643         }
644         
645         if (!CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex))
646                 return FALSE;
647         
648         camel_object_unref((CamelObject *)store->engine);
649         store->engine = NULL;
650         
651         return TRUE;
652 }
653
654 static CamelFolder *
655 get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
656 {
657         if (g_ascii_strcasecmp (folder_name, "inbox") != 0) {
658                 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
659                                       _("No such folder `%s'."), folder_name);
660                 return NULL;
661         }
662         return camel_pop3_folder_new (store, ex);
663 }
664
665 static CamelFolder *
666 get_trash (CamelStore *store, CamelException *ex)
667 {
668         /* no-op */
669         return NULL;
670 }