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