Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / pop3 / camel-pop3-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-pop3-folder.c : class for a pop3 folder */
3
4 /* 
5  * Authors:
6  *   Dan Winship <danw@ximian.com>
7  *   Michael Zucchi <notzed@ximian.com>
8  *
9  * Copyright (C) 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 <stdlib.h>
32 #include <string.h>
33
34 #include <glib/gi18n-lib.h>
35
36 #include <libedataserver/md5-utils.h>
37
38 #include "camel-data-cache.h"
39 #include "camel-exception.h"
40 #include "camel-mime-message.h"
41 #include "camel-operation.h"
42 #include "camel-pop3-folder.h"
43 #include "camel-pop3-store.h"
44 #include "camel-stream-filter.h"
45 #include "camel-stream-mem.h"
46
47 #define d(x) (x)
48
49 #define CF_CLASS(o) (CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(o)))
50 static CamelFolderClass *parent_class;
51
52 static void pop3_finalize (CamelObject *object);
53 static void pop3_refresh_info (CamelFolder *folder, CamelException *ex);
54 static void pop3_sync (CamelFolder *folder, gboolean expunge, CamelException *ex);
55 static gint pop3_get_message_count (CamelFolder *folder);
56 static GPtrArray *pop3_get_uids (CamelFolder *folder);
57 static CamelMimeMessage *pop3_get_message (CamelFolder *folder, const char *uid, CamelException *ex);
58 static gboolean pop3_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set);
59
60 static void
61 camel_pop3_folder_class_init (CamelPOP3FolderClass *camel_pop3_folder_class)
62 {
63         CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS(camel_pop3_folder_class);
64         
65         parent_class = CAMEL_FOLDER_CLASS(camel_folder_get_type());
66         
67         /* virtual method overload */
68         camel_folder_class->refresh_info = pop3_refresh_info;
69         camel_folder_class->sync = pop3_sync;
70         
71         camel_folder_class->get_message_count = pop3_get_message_count;
72         camel_folder_class->get_uids = pop3_get_uids;
73         camel_folder_class->free_uids = camel_folder_free_shallow;
74         
75         camel_folder_class->get_message = pop3_get_message;
76         camel_folder_class->set_message_flags = pop3_set_message_flags;
77 }
78
79 CamelType
80 camel_pop3_folder_get_type (void)
81 {
82         static CamelType camel_pop3_folder_type = CAMEL_INVALID_TYPE;
83         
84         if (!camel_pop3_folder_type) {
85                 camel_pop3_folder_type = camel_type_register (CAMEL_FOLDER_TYPE, "CamelPOP3Folder",
86                                                               sizeof (CamelPOP3Folder),
87                                                               sizeof (CamelPOP3FolderClass),
88                                                               (CamelObjectClassInitFunc) camel_pop3_folder_class_init,
89                                                               NULL,
90                                                               NULL,
91                                                               (CamelObjectFinalizeFunc) pop3_finalize);
92         }
93         
94         return camel_pop3_folder_type;
95 }
96
97 static void
98 pop3_finalize (CamelObject *object)
99 {
100         CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (object);
101         CamelPOP3FolderInfo **fi = (CamelPOP3FolderInfo **)pop3_folder->uids->pdata;
102         CamelPOP3Store *pop3_store = (CamelPOP3Store *)((CamelFolder *)pop3_folder)->parent_store;
103         int i;
104
105         if (pop3_folder->uids) {
106                 for (i=0;i<pop3_folder->uids->len;i++,fi++) {
107                         if (fi[0]->cmd) {
108                                 while (camel_pop3_engine_iterate(pop3_store->engine, fi[0]->cmd) > 0)
109                                         ;
110                                 camel_pop3_engine_command_free(pop3_store->engine, fi[0]->cmd);
111                         }
112                         
113                         g_free(fi[0]->uid);
114                         g_free(fi[0]);
115                 }
116                 
117                 g_ptr_array_free(pop3_folder->uids, TRUE);
118                 g_hash_table_destroy(pop3_folder->uids_uid);
119         }
120 }
121
122 CamelFolder *
123 camel_pop3_folder_new (CamelStore *parent, CamelException *ex)
124 {
125         CamelFolder *folder;
126
127         d(printf("opening pop3 INBOX folder\n"));
128         
129         folder = CAMEL_FOLDER (camel_object_new (CAMEL_POP3_FOLDER_TYPE));
130         camel_folder_construct (folder, parent, "inbox", "inbox");
131         
132         /* mt-ok, since we dont have the folder-lock for new() */
133         camel_folder_refresh_info (folder, ex);/* mt-ok */
134         if (camel_exception_is_set (ex)) {
135                 camel_object_unref (CAMEL_OBJECT (folder));
136                 folder = NULL;
137         }
138         
139         return folder;
140 }
141
142 /* create a uid from md5 of 'top' output */
143 static void
144 cmd_builduid(CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data)
145 {
146         CamelPOP3FolderInfo *fi = data;
147         MD5Context md5;
148         unsigned char digest[16];
149         struct _camel_header_raw *h;
150         CamelMimeParser *mp;
151
152         /* TODO; somehow work out the limit and use that for proper progress reporting
153            We need a pointer to the folder perhaps? */
154         camel_operation_progress_count(NULL, fi->id);
155
156         md5_init(&md5);
157         mp = camel_mime_parser_new();
158         camel_mime_parser_init_with_stream(mp, (CamelStream *)stream);
159         switch (camel_mime_parser_step(mp, NULL, NULL)) {
160         case CAMEL_MIME_PARSER_STATE_HEADER:
161         case CAMEL_MIME_PARSER_STATE_MESSAGE:
162         case CAMEL_MIME_PARSER_STATE_MULTIPART:
163                 h = camel_mime_parser_headers_raw(mp);
164                 while (h) {
165                         if (g_ascii_strcasecmp(h->name, "status") != 0
166                             && g_ascii_strcasecmp(h->name, "x-status") != 0) {
167                                 md5_update(&md5, (const guchar *) h->name, strlen(h->name));
168                                 md5_update(&md5, (const guchar *) h->value, strlen(h->value));
169                         }
170                         h = h->next;
171                 }
172         default:
173                 break;
174         }
175         camel_object_unref(mp);
176         md5_final(&md5, digest);
177         fi->uid = camel_base64_encode_simple((const char *) digest, 16);
178
179         d(printf("building uid for id '%d' = '%s'\n", fi->id, fi->uid));
180 }
181
182 static void
183 cmd_list(CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data)
184 {
185         int ret;
186         unsigned int len, id, size;
187         unsigned char *line;
188         CamelFolder *folder = data;
189         CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (folder->parent_store);
190         CamelPOP3FolderInfo *fi;
191
192         do {
193                 ret = camel_pop3_stream_line(stream, &line, &len);
194                 if (ret>=0) {
195                         if (sscanf((char *) line, "%u %u", &id, &size) == 2) {
196                                 fi = g_malloc0(sizeof(*fi));
197                                 fi->size = size;
198                                 fi->id = id;
199                                 fi->index = ((CamelPOP3Folder *)folder)->uids->len;
200                                 if ((pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) == 0)
201                                         fi->cmd = camel_pop3_engine_command_new(pe, CAMEL_POP3_COMMAND_MULTI, cmd_builduid, fi, "TOP %u 0\r\n", id);
202                                 g_ptr_array_add(((CamelPOP3Folder *)folder)->uids, fi);
203                                 g_hash_table_insert(((CamelPOP3Folder *)folder)->uids_id, GINT_TO_POINTER(id), fi);
204                         }
205                 }
206         } while (ret>0);
207 }
208
209 static void
210 cmd_uidl(CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data)
211 {
212         int ret;
213         unsigned int len;
214         unsigned char *line;
215         char uid[1025];
216         unsigned int id;
217         CamelPOP3FolderInfo *fi;
218         CamelPOP3Folder *folder = data;
219         
220         do {
221                 ret = camel_pop3_stream_line(stream, &line, &len);
222                 if (ret>=0) {
223                         if (strlen((char *) line) > 1024)
224                                 line[1024] = 0;
225                         if (sscanf((char *) line, "%u %s", &id, uid) == 2) {
226                                 fi = g_hash_table_lookup(folder->uids_id, GINT_TO_POINTER(id));
227                                 if (fi) {
228                                         camel_operation_progress(NULL, (fi->index+1) * 100 / folder->uids->len);
229                                         fi->uid = g_strdup(uid);
230                                         g_hash_table_insert(folder->uids_uid, fi->uid, fi);
231                                 } else {
232                                         g_warning("ID %u (uid: %s) not in previous LIST output", id, uid);
233                                 }
234                         }
235                 }
236         } while (ret>0);
237 }
238
239 static void 
240 pop3_refresh_info (CamelFolder *folder, CamelException *ex)
241 {
242         CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (folder->parent_store);
243         CamelPOP3Folder *pop3_folder = (CamelPOP3Folder *) folder;
244         CamelPOP3Command *pcl, *pcu = NULL;
245         int i;
246
247         camel_operation_start (NULL, _("Retrieving POP summary"));
248
249         pop3_folder->uids = g_ptr_array_new ();
250         pop3_folder->uids_uid = g_hash_table_new(g_str_hash, g_str_equal);
251         /* only used during setup */
252         pop3_folder->uids_id = g_hash_table_new(NULL, NULL);
253
254         pcl = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_list, folder, "LIST\r\n");
255         if (pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) {
256                 pcu = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_uidl, folder, "UIDL\r\n");
257         }
258         while ((i = camel_pop3_engine_iterate(pop3_store->engine, NULL)) > 0)
259                 ;
260
261         if (i == -1) {
262                 if (errno == EINTR)
263                         camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
264                 else
265                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
266                                               _("Cannot get POP summary: %s"),
267                                               g_strerror (errno));
268         }
269
270         /* TODO: check every id has a uid & commands returned OK too? */
271         
272         camel_pop3_engine_command_free(pop3_store->engine, pcl);
273         
274         if (pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) {
275                 camel_pop3_engine_command_free(pop3_store->engine, pcu);
276         } else {
277                 for (i=0;i<pop3_folder->uids->len;i++) {
278                         CamelPOP3FolderInfo *fi = pop3_folder->uids->pdata[i];
279                         if (fi->cmd) {
280                                 camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
281                                 fi->cmd = NULL;
282                         }
283                         if (fi->uid)
284                                 g_hash_table_insert(pop3_folder->uids_uid, fi->uid, fi);
285                 }
286         }
287
288         /* dont need this anymore */
289         g_hash_table_destroy(pop3_folder->uids_id);
290         
291         camel_operation_end (NULL);
292         return;
293 }
294
295 static void
296 pop3_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
297 {
298         CamelPOP3Folder *pop3_folder;
299         CamelPOP3Store *pop3_store;
300         int i;
301         CamelPOP3FolderInfo *fi;
302
303         pop3_folder = CAMEL_POP3_FOLDER (folder);
304         pop3_store = CAMEL_POP3_STORE (folder->parent_store);
305
306         if(pop3_store->delete_after && !expunge)
307         {       
308                 d(printf("%s(%d): pop3_store->delete_after = [%d], expunge=[%d]\n",
309                          __FILE__, __LINE__, pop3_store->delete_after, expunge));
310                 camel_operation_start(NULL, _("Expunging old messages"));
311                 camel_pop3_delete_old(folder, pop3_store->delete_after,ex);     
312         }       
313
314         if (!expunge) {
315                 return;
316         }       
317         
318         camel_operation_start(NULL, _("Expunging deleted messages"));
319         
320         for (i = 0; i < pop3_folder->uids->len; i++) {
321                 fi = pop3_folder->uids->pdata[i];
322                 /* busy already?  wait for that to finish first */
323                 if (fi->cmd) {
324                         while (camel_pop3_engine_iterate(pop3_store->engine, fi->cmd) > 0)
325                                 ;
326                         camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
327                         fi->cmd = NULL;
328                 }
329
330                 if (fi->flags & CAMEL_MESSAGE_DELETED) {
331                         fi->cmd = camel_pop3_engine_command_new(pop3_store->engine,
332                                                                 0,
333                                                                 NULL,
334                                                                 NULL,
335                                                                 "DELE %u\r\n",
336                                                                 fi->id);
337
338                         /* also remove from cache */
339                         if (pop3_store->cache && fi->uid)
340                                 camel_data_cache_remove(pop3_store->cache, "cache", fi->uid, NULL);
341                 }
342         }
343
344         for (i = 0; i < pop3_folder->uids->len; i++) {
345                 fi = pop3_folder->uids->pdata[i];
346                 /* wait for delete commands to finish */
347                 if (fi->cmd) {
348                         while (camel_pop3_engine_iterate(pop3_store->engine, fi->cmd) > 0)
349                                 ;
350                         camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
351                         fi->cmd = NULL;
352                 }
353                 camel_operation_progress(NULL, (i+1) * 100 / pop3_folder->uids->len);
354         }
355
356         camel_operation_end(NULL);
357
358         camel_pop3_store_expunge (pop3_store, ex);
359 }
360
361 int
362 camel_pop3_delete_old(CamelFolder *folder, int days_to_delete,  CamelException *ex)
363 {
364         CamelPOP3Folder *pop3_folder;
365         CamelPOP3FolderInfo *fi;
366         int i;
367         CamelPOP3Store *pop3_store;
368         time_t temp;
369         CamelMessageInfo *minfo;
370
371         pop3_folder = CAMEL_POP3_FOLDER (folder);
372         pop3_store = CAMEL_POP3_STORE (CAMEL_FOLDER(pop3_folder)->parent_store);        
373         temp = time(&temp);
374
375         d(printf("%s(%d): pop3_folder->uids->len=[%d]\n", __FILE__, __LINE__, pop3_folder->uids->len));
376         for (i = 0; i < pop3_folder->uids->len; i++) {
377                 fi = pop3_folder->uids->pdata[i];
378
379                 minfo = camel_folder_get_message_info (folder, fi->uid);
380                 d(printf("%s(%d): fi->uid=[%s]\n", __FILE__, __LINE__, fi->uid));
381                 if(minfo) {
382                         time_t message_time = ((CamelMessageInfoBase *)minfo)->date_received;
383                         double time_diff = difftime(temp,message_time);
384                         int day_lag = time_diff/(60*60*24);
385
386                         d(printf("%s(%d): message_time= [%ld]\n", __FILE__, __LINE__, message_time));
387                         d(printf("%s(%d): day_lag=[%d] \t days_to_delete=[%d]\n", 
388                                  __FILE__, __LINE__, day_lag, days_to_delete));
389
390                         if( day_lag > days_to_delete)
391                         {
392                                 if (fi->cmd) {
393                                         while (camel_pop3_engine_iterate(pop3_store->engine, fi->cmd) > 0) {
394                                                 ; /* do nothing - iterating until end */
395                                         }
396                                         
397                                         camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
398                                         fi->cmd = NULL;
399                                 }
400                                 d(printf("%s(%d): Deleting old messages\n", __FILE__, __LINE__));
401                                 fi->cmd = camel_pop3_engine_command_new(pop3_store->engine, 
402                                                                         0,
403                                                                         NULL,
404                                                                         NULL,
405                                                                         "DELE %u\r\n",
406                                                                         fi->id);
407                                 /* also remove from cache */
408                                 if (pop3_store->cache && fi->uid) {
409                                         camel_data_cache_remove(pop3_store->cache, "cache", fi->uid, NULL);
410                                 }
411                         }
412                         /* free message - not used anymore */
413                         camel_folder_free_message_info (folder, minfo);
414                 }
415         }
416
417         for (i = 0; i < pop3_folder->uids->len; i++) {
418                 fi = pop3_folder->uids->pdata[i];
419                 /* wait for delete commands to finish */
420                 if (fi->cmd) {
421                         while (camel_pop3_engine_iterate(pop3_store->engine, fi->cmd) > 0)
422                                 ;
423                         camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
424                         fi->cmd = NULL;
425                 }
426                 camel_operation_progress(NULL, (i+1) * 100 / pop3_folder->uids->len);
427         }
428
429         camel_operation_end(NULL);
430
431         camel_pop3_store_expunge (pop3_store, ex);
432
433         return 0;
434         
435 }
436
437 static void
438 cmd_tocache(CamelPOP3Engine *pe, CamelPOP3Stream *stream, void *data)
439 {
440         CamelPOP3FolderInfo *fi = data;
441         char buffer[2048];
442         int w = 0, n;
443
444         /* What if it fails? */
445
446         /* We write an '*' to the start of the stream to say its not complete yet */
447         /* This should probably be part of the cache code */
448         if ((n = camel_stream_write(fi->stream, "*", 1)) == -1)
449                 goto done;
450
451         while ((n = camel_stream_read((CamelStream *)stream, buffer, sizeof(buffer))) > 0) {
452                 n = camel_stream_write(fi->stream, buffer, n);
453                 if (n == -1)
454                         break;
455
456                 w += n;
457                 if (w > fi->size)
458                         w = fi->size;
459                 if (fi->size != 0)
460                         camel_operation_progress(NULL, (w * 100) / fi->size);
461         }
462
463         /* it all worked, output a '#' to say we're a-ok */
464         if (n != -1) {
465                 camel_stream_reset(fi->stream);
466                 n = camel_stream_write(fi->stream, "#", 1);
467         }
468 done:
469         if (n == -1) {
470                 fi->err = errno;
471                 g_warning("POP3 retrieval failed: %s", strerror(errno));
472         } else {
473                 fi->err = 0;
474         }
475         
476         camel_object_unref((CamelObject *)fi->stream);
477         fi->stream = NULL;
478 }
479
480 static CamelMimeMessage *
481 pop3_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
482 {
483         CamelMimeMessage *message = NULL;
484         CamelPOP3Store *pop3_store = CAMEL_POP3_STORE (folder->parent_store);
485         CamelPOP3Folder *pop3_folder = (CamelPOP3Folder *)folder;
486         CamelPOP3Command *pcr;
487         CamelPOP3FolderInfo *fi;
488         char buffer[1];
489         int i, last;
490         CamelStream *stream = NULL;
491
492         fi = g_hash_table_lookup(pop3_folder->uids_uid, uid);
493         if (fi == NULL) {
494                 camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
495                                       _("No message with UID %s"), uid);
496                 return NULL;
497         }
498
499         /* Sigh, most of the crap in this function is so that the cancel button
500            returns the proper exception code.  Sigh. */
501
502         camel_operation_start_transient(NULL, _("Retrieving POP message %d"), fi->id);
503
504         /* If we have an oustanding retrieve message running, wait for that to complete
505            & then retrieve from cache, otherwise, start a new one, and similar */
506
507         if (fi->cmd != NULL) {
508                 while ((i = camel_pop3_engine_iterate(pop3_store->engine, fi->cmd)) > 0)
509                         ;
510
511                 if (i == -1)
512                         fi->err = errno;
513
514                 /* getting error code? */
515                 /*g_assert (fi->cmd->state == CAMEL_POP3_COMMAND_DATA);*/
516                 camel_pop3_engine_command_free(pop3_store->engine, fi->cmd);
517                 fi->cmd = NULL;
518
519                 if (fi->err != 0) {
520                         if (fi->err == EINTR)
521                                 camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
522                         else
523                                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
524                                                       _("Cannot get message %s: %s"),
525                                                       uid, g_strerror (fi->err));
526                         goto fail;
527                 }
528         }
529         
530         /* check to see if we have safely written flag set */
531         if (pop3_store->cache == NULL
532             || (stream = camel_data_cache_get(pop3_store->cache, "cache", fi->uid, NULL)) == NULL
533             || camel_stream_read(stream, buffer, 1) != 1
534             || buffer[0] != '#') {
535
536                 /* Initiate retrieval, if disk backing fails, use a memory backing */
537                 if (pop3_store->cache == NULL
538                     || (stream = camel_data_cache_add(pop3_store->cache, "cache", fi->uid, NULL)) == NULL)
539                         stream = camel_stream_mem_new();
540
541                 /* ref it, the cache storage routine unref's when done */
542                 camel_object_ref((CamelObject *)stream);
543                 fi->stream = stream;
544                 fi->err = EIO;
545                 pcr = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_tocache, fi, "RETR %u\r\n", fi->id);
546
547                 /* Also initiate retrieval of some of the following messages, assume we'll be receiving them */
548                 if (pop3_store->cache != NULL) {
549                         /* This should keep track of the last one retrieved, also how many are still
550                            oustanding incase of random access on large folders */
551                         i = fi->index+1;
552                         last = MIN(i+10, pop3_folder->uids->len);
553                         for (;i<last;i++) {
554                                 CamelPOP3FolderInfo *pfi = pop3_folder->uids->pdata[i];
555                                 
556                                 if (pfi->uid && pfi->cmd == NULL) {
557                                         pfi->stream = camel_data_cache_add(pop3_store->cache, "cache", pfi->uid, NULL);
558                                         if (pfi->stream) {
559                                                 pfi->err = EIO;
560                                                 pfi->cmd = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI,
561                                                                                          cmd_tocache, pfi, "RETR %u\r\n", pfi->id);
562                                         }
563                                 }
564                         }
565                 }
566
567                 /* now wait for the first one to finish */
568                 while ((i = camel_pop3_engine_iterate(pop3_store->engine, pcr)) > 0)
569                         ;
570
571                 if (i == -1)
572                         fi->err = errno;
573
574                 /* getting error code? */
575                 /*g_assert (pcr->state == CAMEL_POP3_COMMAND_DATA);*/
576                 camel_pop3_engine_command_free(pop3_store->engine, pcr);
577                 camel_stream_reset(stream);
578
579                 /* Check to see we have safely written flag set */
580                 if (fi->err != 0) {
581                         if (fi->err == EINTR)
582                                 camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
583                         else
584                                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
585                                                       _("Cannot get message %s: %s"),
586                                                       uid, g_strerror (fi->err));
587                         goto done;
588                 }
589
590                 if (camel_stream_read(stream, buffer, 1) != 1 || buffer[0] != '#') {
591                         camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
592                                              _("Cannot get message %s: %s"), uid, _("Unknown reason"));
593                         goto done;
594                 }
595         }
596
597         message = camel_mime_message_new ();
598         if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)message, stream) == -1) {
599                 if (errno == EINTR)
600                         camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
601                 else
602                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
603                                               _("Cannot get message %s: %s"),
604                                               uid, g_strerror (errno));
605                 camel_object_unref((CamelObject *)message);
606                 message = NULL;
607         }
608 done:
609         camel_object_unref((CamelObject *)stream);
610 fail:
611         camel_operation_end(NULL);
612
613         return message;
614 }
615
616 static gboolean
617 pop3_set_message_flags (CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
618 {
619         CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder);
620         CamelPOP3FolderInfo *fi;
621         gboolean res = FALSE;
622
623         fi = g_hash_table_lookup(pop3_folder->uids_uid, uid);
624         if (fi) {
625                 guint32 new = (fi->flags & ~flags) | (set & flags);
626
627                 if (fi->flags != new) {
628                         fi->flags = new;
629                         res = TRUE;
630                 }
631         }
632
633         return res;
634 }
635
636 static gint
637 pop3_get_message_count (CamelFolder *folder)
638 {
639         CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder);
640         
641         return pop3_folder->uids->len;
642 }
643
644 static GPtrArray *
645 pop3_get_uids (CamelFolder *folder)
646 {
647         CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder);
648         GPtrArray *uids = g_ptr_array_new();
649         CamelPOP3FolderInfo **fi = (CamelPOP3FolderInfo **)pop3_folder->uids->pdata;
650         int i;
651
652         for (i=0;i<pop3_folder->uids->len;i++,fi++) {
653                 if (fi[0]->uid)
654                         g_ptr_array_add(uids, fi[0]->uid);
655         }
656         
657         return uids;
658 }