update for CamelFolder changes
[platform/upstream/evolution-data-server.git] / camel / providers / mbox / camel-mbox-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-mbox-folder.c : Abstract class for an email folder */
3
4 /* 
5  * Author : Bertrand Guiheneuf <bertrand@helixcode.com> 
6  *
7  * Copyright (C) 1999 Helix Code .
8  *
9  * This program is free software; you can redistribute it and/or 
10  * modify it under the terms of the GNU General Public License as 
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22  * USA
23  */
24
25
26 #include <config.h> 
27
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <dirent.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <fcntl.h>
36
37 #include "camel-mbox-folder.h"
38 #include "camel-mbox-store.h"
39 #include "string-utils.h"
40 #include "camel-log.h"
41 #include "camel-stream-buffered-fs.h"
42 #include "camel-folder-summary.h"
43 #include "camel-mbox-summary.h"
44 #include "camel-mbox-parser.h"
45 #include "camel-mbox-utils.h"
46 #include "md5-utils.h"
47 #include "gmime-utils.h"
48 #include "camel-mbox-search.h"
49 #include "camel-data-wrapper.h"
50 #include "camel-mime-message.h"
51
52 #include "camel-exception.h"
53
54 static CamelFolderClass *parent_class=NULL;
55
56 /* Returns the class for a CamelMboxFolder */
57 #define CMBOXF_CLASS(so) CAMEL_MBOX_FOLDER_CLASS (GTK_OBJECT(so)->klass)
58 #define CF_CLASS(so) CAMEL_FOLDER_CLASS (GTK_OBJECT(so)->klass)
59 #define CMBOXS_CLASS(so) CAMEL_STORE_CLASS (GTK_OBJECT(so)->klass)
60
61
62 static void _init (CamelFolder *folder, CamelStore *parent_store,
63                    CamelFolder *parent_folder, const gchar *name,
64                    gchar separator, CamelException *ex);
65 static void _set_name(CamelFolder *folder, const gchar *name, CamelException *ex);
66
67
68 static void _open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex);
69 static void _close (CamelFolder *folder, gboolean expunge, CamelException *ex);
70 static gboolean _exists (CamelFolder *folder, CamelException *ex);
71 static gboolean _create(CamelFolder *folder, CamelException *ex);
72 static gboolean _delete (CamelFolder *folder, gboolean recurse, CamelException *ex);
73 static gboolean _delete_messages (CamelFolder *folder, CamelException *ex);
74 static GList *_list_subfolders (CamelFolder *folder, CamelException *ex);
75 /* static CamelMimeMessage *_get_message_by_number (CamelFolder *folder, gint number, CamelException *ex);*/
76 static gint _get_message_count (CamelFolder *folder, CamelException *ex);
77 static void _append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex);
78 static GList *_get_uid_list  (CamelFolder *folder, CamelException *ex);
79 static CamelMimeMessage *_get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex);
80 #if 0
81 static void _expunge (CamelFolder *folder, CamelException *ex);
82 static void _copy_message_to (CamelFolder *folder, CamelMimeMessage *message, CamelFolder *dest_folder, CamelException *ex);
83 static const gchar *_get_message_uid (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex);
84 #endif
85
86 static GList *search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex);
87
88 static void _finalize (GtkObject *object);
89
90 static void
91 camel_mbox_folder_class_init (CamelMboxFolderClass *camel_mbox_folder_class)
92 {
93         CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_mbox_folder_class);
94         GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (camel_folder_class);
95
96         parent_class = gtk_type_class (camel_folder_get_type ());
97                 
98         /* virtual method definition */
99
100         /* virtual method overload */
101         camel_folder_class->init = _init;
102         camel_folder_class->set_name = _set_name;
103         camel_folder_class->open = _open;
104         camel_folder_class->close = _close;
105         camel_folder_class->exists = _exists;
106         camel_folder_class->create = _create;
107         camel_folder_class->delete = _delete;
108         camel_folder_class->delete_messages = _delete_messages;
109         camel_folder_class->list_subfolders = _list_subfolders;
110         /* camel_folder_class->get_message_by_number = _get_message_by_number; */
111         camel_folder_class->get_message_count = _get_message_count;
112         camel_folder_class->append_message = _append_message;
113         camel_folder_class->get_uid_list = _get_uid_list;
114 #if 0
115         camel_folder_class->expunge = _expunge;
116         camel_folder_class->copy_message_to = _copy_message_to;
117         camel_folder_class->get_message_uid = _get_message_uid;
118 #endif
119         camel_folder_class->get_message_by_uid = _get_message_by_uid;
120
121         camel_folder_class->search_by_expression = search_by_expression;
122
123         gtk_object_class->finalize = _finalize;
124         
125 }
126
127
128
129 static void           
130 _finalize (GtkObject *object)
131 {
132         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (object);
133
134         CAMEL_LOG_FULL_DEBUG ("Entering CamelFolder::finalize\n");
135
136         
137         g_free (mbox_folder->folder_file_path);
138         g_free (mbox_folder->folder_dir_path);
139
140         GTK_OBJECT_CLASS (parent_class)->finalize (object);
141         CAMEL_LOG_FULL_DEBUG ("Leaving CamelFolder::finalize\n");
142 }
143
144
145
146
147
148 GtkType
149 camel_mbox_folder_get_type (void)
150 {
151         static GtkType camel_mbox_folder_type = 0;
152         
153         if (!camel_mbox_folder_type)    {
154                 GtkTypeInfo camel_mbox_folder_info =    
155                 {
156                         "CamelMboxFolder",
157                         sizeof (CamelMboxFolder),
158                         sizeof (CamelMboxFolderClass),
159                         (GtkClassInitFunc) camel_mbox_folder_class_init,
160                         (GtkObjectInitFunc) NULL,
161                                 /* reserved_1 */ NULL,
162                                 /* reserved_2 */ NULL,
163                         (GtkClassInitFunc) NULL,
164                 };
165                 
166                 camel_mbox_folder_type = gtk_type_unique (CAMEL_FOLDER_TYPE, &camel_mbox_folder_info);
167         }
168         
169         return camel_mbox_folder_type;
170 }
171
172
173
174  
175
176
177 static void 
178 _init (CamelFolder *folder, CamelStore *parent_store,
179        CamelFolder *parent_folder, const gchar *name, gchar separator,
180        CamelException *ex)
181 {
182         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
183
184
185         CAMEL_LOG_FULL_DEBUG ("Entering CamelMboxFolder::init_with_store\n");
186
187         /* call parent method */
188         parent_class->init (folder, parent_store, parent_folder,
189                             name, separator, ex);
190         if (camel_exception_get_id (ex)) return;
191
192         /* we assume that the parent init
193            method checks for the existance of @folder */
194            
195         folder->can_hold_messages = TRUE;
196         folder->can_hold_folders = TRUE;
197         folder->has_summary_capability = TRUE;
198         folder->has_uid_capability = TRUE;
199         folder->summary = camel_folder_summary_new ();
200
201         mbox_folder->folder_file_path = NULL;
202         mbox_folder->summary_file_path = NULL;
203         mbox_folder->folder_dir_path = NULL;
204         mbox_folder->index_file_path = NULL;
205         mbox_folder->internal_summary = NULL;
206         mbox_folder->uid_array = NULL;
207         
208         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMboxFolder::init_with_store\n");
209 }
210
211
212
213 /* internal method used to : 
214    - test for the existence of a summary file 
215    - test the sync between the summary and the mbox file
216    - load the summary or create it if necessary 
217 */ 
218 static void
219 _check_get_or_maybe_generate_summary_file (CamelMboxFolder *mbox_folder, CamelException *ex)
220 {
221         CamelFolder *folder = CAMEL_FOLDER (mbox_folder);
222         GArray *message_info_array;
223         gboolean summary_file_exists;
224         gboolean summary_file_is_sync;
225         GArray *mbox_summary_info;
226         gint mbox_file_fd;
227         guint32 next_uid;
228         guint32 file_size;
229
230         /* test for the existence of the summary file */
231         summary_file_exists = (access (mbox_folder->summary_file_path, F_OK) == 0);
232
233         /* if the summary file exists, test if the 
234            md5 of the mbox file is still in sync
235            with the one we had computed the last time
236            we saved the summary file */
237         if (summary_file_exists) {
238                 
239                 summary_file_is_sync = 
240                         camel_mbox_check_summary_sync (mbox_folder->summary_file_path,
241                                                        mbox_folder->folder_file_path,
242                                                        ex);
243                 if (camel_exception_get_id (ex)) return;
244         }
245
246
247         /* in the case where the summary does not exist 
248            or is not in sync with the mbox file
249            regenerate it */
250         if ( !(summary_file_exists && summary_file_is_sync)) {
251                 
252                 /* parse the mbox folder and get some
253                    information about the messages */
254
255                 mbox_file_fd = open (mbox_folder->folder_file_path, O_RDONLY);
256                 message_info_array = camel_mbox_parse_file (mbox_file_fd, 
257                                                             "From ", 
258                                                             0,
259                                                             &file_size,
260                                                             &next_uid,
261                                                             TRUE,
262                                                             NULL,
263                                                             0,
264                                                             ex); 
265                 
266                 close (mbox_file_fd);
267                 if (camel_exception_get_id (ex)) { 
268                         return;
269                 }
270
271                 
272                 next_uid = camel_mbox_write_xev (mbox_folder->folder_file_path, 
273                                                  message_info_array, &file_size, next_uid, ex);
274
275                 if (camel_exception_get_id (ex)) { 
276                         /* ** FIXME : free the preparsed information */
277                         return;
278                 }
279                                                 
280                 mbox_summary_info =
281                         parsed_information_to_mbox_summary (message_info_array);
282                 
283                 /* **FIXME : Free the parsed information structure */
284
285                 /* allocate an internal summary object */
286                 mbox_folder->internal_summary = g_new (CamelMboxSummary, 1);
287                 
288                 /* generate the folder md5 signature */
289                 md5_get_digest_from_file (mbox_folder->folder_file_path, mbox_folder->internal_summary->md5_digest);
290
291                 /* store the number of messages as well as the summary array */
292                 mbox_folder->internal_summary->nb_message = mbox_summary_info->len;             
293                 mbox_folder->internal_summary->next_uid = next_uid;             
294                 mbox_folder->internal_summary->mbox_file_size = file_size;              
295                 mbox_folder->internal_summary->message_info = mbox_summary_info;
296                 
297         } else {
298                 /* every thing seems ok, just read the summary file from disk */
299                 mbox_folder->internal_summary = camel_mbox_load_summary (mbox_folder->summary_file_path, ex);
300         }
301         
302         /* copy the internal summary information to the external 
303            folder summary used by the display engines */
304         camel_mbox_summary_append_internal_to_external (mbox_folder->internal_summary, folder->summary, 0);
305 }
306
307
308
309 static void
310 _open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex)
311 {
312         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
313         //struct dirent *dir_entry;
314         //struct stat stat_buf;
315
316
317         if (folder->open_state == FOLDER_OPEN) {
318                 camel_exception_set (ex, 
319                                      CAMEL_EXCEPTION_FOLDER_INVALID_STATE,
320                                      "folder is already open");
321                 return;
322         }
323         
324         
325         
326         /* get (or create) uid list */
327         //if (!(mbox_load_uid_list (mbox_folder) > 0))
328         //      mbox_generate_uid_list (mbox_folder);
329         
330         _check_get_or_maybe_generate_summary_file (mbox_folder, ex);
331 }
332
333
334 static void
335 _close (CamelFolder *folder, gboolean expunge, CamelException *ex)
336 {
337         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
338
339
340         /* call parent implementation */
341         parent_class->close (folder, expunge, ex);
342         
343         /* save the folder summary on disc */
344         camel_mbox_save_summary (mbox_folder->internal_summary, mbox_folder->summary_file_path, ex);
345 }
346
347
348
349
350 static void
351 _set_name (CamelFolder *folder, const gchar *name, CamelException *ex)
352 {
353         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER (folder);
354         const gchar *root_dir_path;
355         //gchar *full_name;
356         //const gchar *parent_full_name;
357         
358         CAMEL_LOG_FULL_DEBUG ("Entering CamelMboxFolder::set_name\n");
359
360         /* call default implementation */
361         parent_class->set_name (folder, name, ex);
362         if (camel_exception_get_id (ex)) return;
363
364         g_free (mbox_folder->folder_file_path);
365         g_free (mbox_folder->folder_dir_path);
366         g_free (mbox_folder->index_file_path);
367
368         root_dir_path = camel_mbox_store_get_toplevel_dir (CAMEL_MBOX_STORE(folder->parent_store));
369
370         CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::set_name full_name is %s\n", folder->full_name);
371         CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::set_name root_dir_path is %s\n", root_dir_path);
372
373         mbox_folder->folder_file_path = g_strdup_printf ("%s/%s", root_dir_path, folder->full_name);
374         mbox_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary", root_dir_path, folder->full_name);
375         mbox_folder->folder_dir_path = g_strdup_printf ("%s/%s.sdb", root_dir_path, folder->full_name);
376         mbox_folder->index_file_path = g_strdup_printf ("%s/%s.ibex", root_dir_path, folder->full_name);
377
378         CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::set_name mbox_folder->folder_file_path is %s\n", 
379                               mbox_folder->folder_file_path);
380         CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::set_name mbox_folder->folder_dir_path is %s\n", 
381                               mbox_folder->folder_dir_path);
382         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMboxFolder::set_name\n");
383 }
384
385
386
387
388
389
390 static gboolean
391 _exists (CamelFolder *folder, CamelException *ex)
392 {
393         CamelMboxFolder *mbox_folder;
394         struct stat stat_buf;
395         gint stat_error;
396         gboolean exists;
397
398         CAMEL_LOG_FULL_DEBUG ("Entering CamelMboxFolder::exists\n");
399         
400  
401         /* check if the folder object exists */
402         if (!folder) {
403                 camel_exception_set (ex, 
404                                      CAMEL_EXCEPTION_FOLDER_NULL,
405                                      "folder object is NULL");
406                 return FALSE;
407         }
408
409         mbox_folder = CAMEL_MBOX_FOLDER(folder);
410
411         
412         /* check if the mbox file path is determined */
413         if (!mbox_folder->folder_file_path) {
414                 camel_exception_set (ex, 
415                                      CAMEL_EXCEPTION_FOLDER_INVALID,
416                                      "undetermined folder file path. Maybe use set_name ?");
417                 return FALSE;
418         }
419
420         /* check if the mbox dir path is determined */
421         if (!mbox_folder->folder_dir_path) {
422                 camel_exception_set (ex, 
423                                      CAMEL_EXCEPTION_FOLDER_INVALID,
424                                      "undetermined folder directory path. Maybe use set_name ?");
425                 return FALSE;
426         }
427
428
429         /* we should not check for that here */
430 #if 0
431         /* check if the mbox directory exists */
432         access_result = access (mbox_folder->folder_dir_path, F_OK);
433         if (access_result < 0) {
434                 CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::exists errot when executing access on %s\n", 
435                                       mbox_folder->folder_dir_path);
436                 CAMEL_LOG_FULL_DEBUG ("  Full error text is : %s\n", strerror(errno));
437                 camel_exception_set (ex, 
438                                      CAMEL_EXCEPTION_SYSTEM,
439                                      strerror(errno));
440                 return FALSE;
441         }
442         stat_error = stat (mbox_folder->folder_dir_path, &stat_buf);
443         if (stat_error == -1)  {
444                 CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::exists when executing stat on %s, stat_error = %d\n", 
445                                       mbox_folder->folder_dir_path, stat_error);
446                 CAMEL_LOG_FULL_DEBUG ("  Full error text is : %s\n", strerror(errno));
447                 camel_exception_set (ex, 
448                                      CAMEL_EXCEPTION_SYSTEM,
449                                      strerror(errno));
450                 return FALSE;
451         }
452         exists = S_ISDIR (stat_buf.st_mode);
453         if (!exists) return FALSE;
454 #endif 
455
456
457         /* check if the mbox file exists */
458         stat_error = stat (mbox_folder->folder_file_path, &stat_buf);
459         if (stat_error == -1)
460                 return FALSE;
461         
462         exists = S_ISREG (stat_buf.st_mode);
463         /* we should  check the rights here  */
464         
465         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMboxFolder::exists\n");
466         return exists;
467 }
468
469
470
471
472
473
474
475
476 static gboolean
477 _create (CamelFolder *folder, CamelException *ex)
478 {
479         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(folder);
480         const gchar *folder_file_path, *folder_dir_path;
481         mode_t dir_mode = S_IRWXU;
482         gint mkdir_error;
483         gboolean folder_already_exists;
484         int creat_fd;
485         mode_t old_umask;
486
487         /* check if the folder object exists */
488         if (!folder) {
489                 camel_exception_set (ex, 
490                                      CAMEL_EXCEPTION_FOLDER_NULL,
491                                      "folder object is NULL");
492                 return FALSE;
493         }
494
495
496         /* call default implementation */
497         parent_class->create (folder, ex);
498
499         /* get the paths of what we need to create */
500         folder_file_path = mbox_folder->folder_file_path;
501         folder_dir_path = mbox_folder->folder_dir_path;
502         
503         if (!(folder_file_path || folder_dir_path)) {
504                 camel_exception_set (ex, 
505                                      CAMEL_EXCEPTION_FOLDER_INVALID,
506                                      "invalid folder path. Use set_name ?");
507                 return FALSE;
508         }
509
510         
511         /* if the folder already exists, simply return */
512         folder_already_exists = camel_folder_exists (folder,ex);
513         if (camel_exception_get_id (ex)) return FALSE;
514
515         if (folder_already_exists) return TRUE;
516
517
518         /* create the directory for the subfolders */
519         mkdir_error = mkdir (folder_dir_path, dir_mode);
520         if (mkdir_error == -1) goto io_error;
521         
522
523         /* create the mbox file */ 
524         /* it must be rw for the user and none for the others */
525         old_umask = umask (0700);
526         creat_fd = open (folder_file_path, 
527                          O_WRONLY | O_CREAT | O_APPEND,
528                          S_IRUSR  | S_IWUSR); 
529         umask (old_umask);
530         if (creat_fd == -1) goto io_error;
531         close (creat_fd);
532
533         /* create the summary object */ 
534         mbox_folder->internal_summary = g_new (CamelMboxSummary, 1);
535         mbox_folder->internal_summary->nb_message = 0;
536         mbox_folder->internal_summary->next_uid = 1;
537         mbox_folder->internal_summary->mbox_file_size = 0;
538         mbox_folder->internal_summary->message_info = g_array_new (FALSE, FALSE, sizeof (CamelMboxSummaryInformation));
539
540         return TRUE;
541
542         /* exception handling for io errors */
543         io_error :
544
545                 CAMEL_LOG_WARNING ("CamelMboxFolder::create, error when creating %s and %s\n", 
546                                    folder_dir_path, folder_file_path);
547                 CAMEL_LOG_FULL_DEBUG ( "  Full error text is : %s\n", strerror(errno));
548
549                 if (errno == EACCES) {
550                         camel_exception_set (ex, 
551                                              CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
552                                              "You don't have the permission to create the mbox file.");
553                         return FALSE;
554                 } else {
555                         camel_exception_set (ex, 
556                                              CAMEL_EXCEPTION_SYSTEM,
557                                              "Unable to create the mbox file.");
558                         return FALSE;
559                 }
560 }
561
562
563
564
565
566
567
568
569 static gboolean
570 _delete (CamelFolder *folder, gboolean recurse, CamelException *ex)
571 {
572         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(folder);
573         const gchar *folder_file_path, *folder_dir_path;
574         gint rmdir_error = 0;
575         gint unlink_error = 0;
576         gboolean folder_already_exists;
577
578         /* check if the folder object exists */
579         if (!folder) {
580                 camel_exception_set (ex, 
581                                      CAMEL_EXCEPTION_FOLDER_NULL,
582                                      "folder object is NULL");
583                 return FALSE;
584         }
585
586
587         /* in the case where the folder does not exist, 
588            return immediatly */
589         folder_already_exists = camel_folder_exists (folder, ex);
590         if (camel_exception_get_id (ex)) return FALSE;
591
592         if (!folder_already_exists) return TRUE;
593
594
595         /* call default implementation.
596            It should delete the messages in the folder
597            and recurse the operation to subfolders */
598         parent_class->delete (folder, recurse, ex);
599         
600
601         /* get the paths of what we need to be deleted */
602         folder_file_path = mbox_folder->folder_file_path;
603         folder_dir_path = mbox_folder->folder_file_path;
604         
605         if (!(folder_file_path || folder_dir_path)) {
606                 camel_exception_set (ex, 
607                                      CAMEL_EXCEPTION_FOLDER_INVALID,
608                                      "invalid folder path. Use set_name ?");
609                 return FALSE;
610         }
611
612         
613         /* physically delete the directory */
614         CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::delete removing directory %s\n", folder_dir_path);
615         rmdir_error = rmdir (folder_dir_path);
616         if (rmdir_error == -1) 
617                 switch errno { 
618                 case EACCES :
619                         camel_exception_set (ex, 
620                                              CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
621                                              "Not enough permission to delete the mbox folder");
622                         return FALSE;                   
623                         break;
624                         
625                 case ENOTEMPTY :
626                                 camel_exception_set (ex, 
627                                              CAMEL_EXCEPTION_FOLDER_NON_EMPTY,
628                                                      "mbox folder not empty. Cannot delete it. Maybe use recurse flag ?");
629                                 return FALSE;           
630                                 break;
631                 default :
632                         camel_exception_set (ex, 
633                                              CAMEL_EXCEPTION_SYSTEM,
634                                              "Unable to delete the mbox folder.");
635                         return FALSE;
636         }
637         
638         /* physically delete the file */
639         unlink_error = unlink (folder_dir_path);
640         if (unlink_error == -1) 
641                 switch errno { 
642                 case EACCES :
643                 case EPERM :
644                 case EROFS :
645                         camel_exception_set (ex, 
646                                              CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
647                                              "Not enough permission to delete the mbox file");
648                         return FALSE;                   
649                         break;
650                         
651                 case EFAULT :
652                 case ENOENT :
653                 case ENOTDIR :
654                 case EISDIR :
655                         camel_exception_set (ex, 
656                                              CAMEL_EXCEPTION_FOLDER_INVALID_PATH,
657                                              "Invalid mbox file");
658                         return FALSE;                   
659                         break;
660
661                 default :
662                         camel_exception_set (ex, 
663                                              CAMEL_EXCEPTION_SYSTEM,
664                                              "Unable to delete the mbox folder.");
665                         return FALSE;
666         }
667
668
669         return TRUE;
670 }
671
672
673
674
675 gboolean
676 _delete_messages (CamelFolder *folder, CamelException *ex)
677 {
678         
679         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(folder);
680         const gchar *folder_file_path;
681         gboolean folder_already_exists;
682         int creat_fd;
683         mode_t old_umask;
684         
685
686         /* check if the folder object exists */
687         if (!folder) {
688                 camel_exception_set (ex, 
689                                      CAMEL_EXCEPTION_FOLDER_NULL,
690                                      "folder object is NULL");
691                 return FALSE;
692         }
693
694         /* in the case where the folder does not exist, 
695            return immediatly */
696         folder_already_exists = camel_folder_exists (folder, ex);
697         if (camel_exception_get_id (ex)) return FALSE;
698
699         if (!folder_already_exists) return TRUE;
700
701
702
703         /* get the paths of the mbox file we need to delete */
704         folder_file_path = mbox_folder->folder_file_path;
705         
706         if (!folder_file_path) {
707                 camel_exception_set (ex, 
708                                      CAMEL_EXCEPTION_FOLDER_INVALID,
709                                      "invalid folder path. Use set_name ?");
710                 return FALSE;
711         }
712
713                 
714         /* create the mbox file */ 
715         /* it must be rw for the user and none for the others */
716         old_umask = umask (0700);
717         creat_fd = open (folder_file_path, 
718                          O_WRONLY | O_TRUNC,
719                          S_IRUSR  | S_IWUSR); 
720         umask (old_umask);
721         if (creat_fd == -1) goto io_error;
722         close (creat_fd);
723         
724         return TRUE;
725
726         /* exception handling for io errors */
727         io_error :
728
729                 CAMEL_LOG_WARNING ("CamelMboxFolder::create, error when deleting files  %s\n", 
730                                    folder_file_path);
731                 CAMEL_LOG_FULL_DEBUG ( "  Full error text is : %s\n", strerror(errno));
732
733                 if (errno == EACCES) {
734                         camel_exception_set (ex, 
735                                              CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
736                                              "You don't have the permission to write in the mbox file.");
737                         return FALSE;
738                 } else {
739                         camel_exception_set (ex, 
740                                              CAMEL_EXCEPTION_SYSTEM,
741                                              "Unable to write in the mbox file.");
742                         return FALSE;
743                 }
744         
745
746 }
747
748
749
750
751
752
753
754
755
756 static GList *
757 _list_subfolders (CamelFolder *folder, CamelException *ex)
758 {
759         GList *subfolder_name_list = NULL;
760
761         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(folder);
762         const gchar *folder_dir_path;
763         gboolean folder_exists;
764
765         struct stat stat_buf;
766         gint stat_error = 0;
767         //GList *file_list;
768         gchar *entry_name;
769         gchar *full_entry_name;
770         gchar *real_folder_name;
771         struct dirent *dir_entry;
772         DIR *dir_handle;
773         gboolean folder_suffix_found;
774         
775         //gchar *io_error_text;
776         
777
778
779         /* check if the folder object exists */
780         if (!folder) {
781                 camel_exception_set (ex, 
782                                      CAMEL_EXCEPTION_FOLDER_NULL,
783                                      "folder object is NULL");
784                 return FALSE;
785         }
786
787
788         /* in the case the folder does not exist, 
789            raise an exception */
790         folder_exists = camel_folder_exists (folder, ex);
791         if (camel_exception_get_id (ex)) return FALSE;
792
793         if (!folder_exists) {
794                 camel_exception_set (ex, 
795                                      CAMEL_EXCEPTION_FOLDER_INVALID,
796                                      "Inexistant folder.");
797                 return FALSE;
798         }
799
800
801         /* get the mbox subfolders directories */
802         folder_dir_path = mbox_folder->folder_file_path;
803         if (!folder_dir_path) {
804                 camel_exception_set (ex, 
805                                      CAMEL_EXCEPTION_FOLDER_INVALID,
806                                      "Invalid folder path. Use set_name ?");
807                 return FALSE;
808         }
809
810                 
811         dir_handle = opendir (folder_dir_path);
812         
813         /* read the first entry in the directory */
814         dir_entry = readdir (dir_handle);
815         while ((stat_error != -1) && (dir_entry != NULL)) {
816
817                 /* get the name of the next entry in the dir */
818                 entry_name = dir_entry->d_name;
819                 full_entry_name = g_strdup_printf ("%s/%s", folder_dir_path, entry_name);
820                 stat_error = stat (full_entry_name, &stat_buf);
821                 g_free (full_entry_name);
822
823                 /* is it a directory ? */
824                 if ((stat_error != -1) && S_ISDIR (stat_buf.st_mode)) {
825                         /* yes, add it to the list */
826                         if (entry_name[0] != '.') {
827                                 CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::list_subfolders adding "
828                                                       "%s\n", entry_name);
829
830                                 /* if the folder is a netscape folder, remove the  
831                                    ".sdb" from the name */
832                                 real_folder_name = string_prefix (entry_name, ".sdb", &folder_suffix_found);
833                                 /* stick here the tests for other folder suffixes if any */
834                                 
835                                 if (!folder_suffix_found) real_folder_name = g_strdup (entry_name);
836                                 
837                                 /* add the folder name to the list */
838                                 subfolder_name_list = g_list_append (subfolder_name_list, 
839                                                                      real_folder_name);
840                         }
841                 }
842                 /* read next entry */
843                 dir_entry = readdir (dir_handle);
844         }
845
846         closedir (dir_handle);
847
848         return subfolder_name_list;
849
850         
851
852         /* io exception handling */
853                 switch errno { 
854                 case EACCES :
855                         
856                         camel_exception_setv (ex, 
857                                               CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
858                                               "Unable to list the directory. Full Error text is : %s ", 
859                                               strerror (errno));
860                         break;
861                         
862                 case ENOENT :
863                 case ENOTDIR :
864                         camel_exception_setv (ex, 
865                                               CAMEL_EXCEPTION_FOLDER_INVALID_PATH,
866                                               "Invalid mbox folder path. Full Error text is : %s ", 
867                                               strerror (errno));
868                         break;
869                         
870                 default :
871                         camel_exception_set (ex, 
872                                              CAMEL_EXCEPTION_SYSTEM,
873                                              "Unable to delete the mbox folder.");
874                         
875                 }
876         
877         g_list_free (subfolder_name_list);
878         return NULL;
879 }
880
881
882
883
884 static gint
885 _get_message_count (CamelFolder *folder, CamelException *ex)
886 {
887         
888         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(folder);
889         gint message_count;
890
891         g_assert (folder);
892         g_assert (mbox_folder->internal_summary);
893         
894         message_count = mbox_folder->internal_summary->nb_message;
895
896         CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::get_message_count found %d messages\n", message_count);
897         return message_count;
898 }
899
900
901 static void
902 _append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex)
903 {
904         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(folder);
905         //guint new_msg_number;
906         CamelStream *output_stream;
907         guint32 tmp_file_size;
908         guint32 next_uid;
909         gint tmp_file_fd;
910         GArray *message_info_array;
911         GArray *mbox_summary_info;
912         gchar *tmp_message_filename;
913         gint fd1, fd2;
914
915
916         CAMEL_LOG_FULL_DEBUG ("Entering CamelMboxFolder::append_message\n");
917
918         tmp_message_filename = g_strdup_printf ("%s.tmp", mbox_folder->folder_file_path);
919         /* write the message itself */
920         output_stream = camel_stream_fs_new_with_name (tmp_message_filename, CAMEL_STREAM_FS_WRITE);
921         if (output_stream != NULL) {
922                 camel_stream_write_string (output_stream, "From - \n");
923                 camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), output_stream);
924         }
925         camel_stream_close (output_stream);
926
927         /* at this point we have saved the message to a
928            temporary file, now, we have to add the x-evolution 
929            field and also update the main summary summary */
930
931         /* 
932            First : parse the mbox file, but only from the 
933            position where the message has been added, 
934            wich happens to be the last postion in the 
935            mbox file before we added the message.
936            This position is still stored in the summary 
937            for the moment 
938         */
939         next_uid = mbox_folder->internal_summary->next_uid;
940         tmp_file_fd = open (tmp_message_filename, O_RDONLY);
941         message_info_array = camel_mbox_parse_file (tmp_file_fd, 
942                                                     "From - ", 
943                                                     0,
944                                                     &tmp_file_size,
945                                                     &next_uid,
946                                                     TRUE,
947                                                     NULL,
948                                                     0,
949                                                     ex); 
950         
951         close (tmp_file_fd);
952
953         /* get the value of the last available UID
954            as saved in the summary file */
955         next_uid = mbox_folder->internal_summary->next_uid;
956
957         /* 
958            OK, this is not very efficient, we should not use the same
959            method as for parsing an entire mail file, 
960            but I have no time to write a simpler parser 
961         */
962         next_uid = camel_mbox_write_xev (tmp_message_filename, 
963                                          message_info_array, &tmp_file_size, next_uid, ex);
964         
965         if (camel_exception_get_id (ex)) { 
966                 /* ** FIXME : free the preparsed information */
967                 return;
968                 }
969
970         mbox_summary_info =
971                 parsed_information_to_mbox_summary (message_info_array);
972
973
974
975
976         /* store the number of messages as well as the summary array */
977         mbox_folder->internal_summary->nb_message += 1;         
978         mbox_folder->internal_summary->next_uid = next_uid;     
979
980         ((CamelMboxSummaryInformation *)(mbox_summary_info->data))->position += mbox_folder->internal_summary->mbox_file_size;
981         mbox_folder->internal_summary->mbox_file_size += tmp_file_size;         
982         
983         camel_mbox_summary_append_entries (mbox_folder->internal_summary, mbox_summary_info);
984
985         /* append the new entry of the internal summary to 
986            the external summary */
987         camel_mbox_summary_append_internal_to_external (mbox_folder->internal_summary, 
988                                                         folder->summary, 
989                                                         mbox_folder->internal_summary->nb_message-1);
990
991         g_array_free (mbox_summary_info, TRUE); 
992         
993
994         /* append the temporary file message to the mbox file */
995         fd1 = open (tmp_message_filename, O_RDONLY);
996         fd2 = open (mbox_folder->folder_file_path, 
997                     O_WRONLY | O_CREAT | O_APPEND,
998                     S_IRUSR  | S_IWUSR);
999
1000         if (fd2 == -1) {
1001                         camel_exception_setv (ex, 
1002                                              CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
1003                                              "could not open the mbox folder file for appending the message\n"
1004                                               "\t%s\n"
1005                                               "Full error is : %s\n",
1006                                               mbox_folder->folder_file_path,
1007                                               strerror (errno));
1008                         return;
1009                 }
1010
1011         camel_mbox_copy_file_chunk (fd1,
1012                                     fd2, 
1013                                     tmp_file_size, 
1014                                     ex);
1015         close (fd1);
1016         close (fd2);
1017
1018         /* remove the temporary file */
1019         unlink (tmp_message_filename);
1020
1021         /* generate the folder md5 signature */
1022         md5_get_digest_from_file (mbox_folder->folder_file_path, mbox_folder->internal_summary->md5_digest);
1023
1024
1025         g_free (tmp_message_filename);
1026         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMboxFolder::append_message\n");
1027 }
1028
1029
1030
1031
1032 static GList *
1033 _get_uid_list (CamelFolder *folder, CamelException *ex) 
1034 {
1035         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(folder);
1036         GArray *message_info_array;
1037         CamelMboxSummaryInformation *message_info;
1038         GList *uid_list = NULL;
1039         int i;
1040
1041         CAMEL_LOG_FULL_DEBUG ("Entering CamelMboxFolder::get_uid_list\n");
1042         
1043         message_info_array = mbox_folder->internal_summary->message_info;
1044         
1045         for (i=0; i<message_info_array->len; i++) {
1046                 
1047                 message_info = (CamelMboxSummaryInformation *)(message_info_array->data) + i;
1048                 uid_list = g_list_prepend (uid_list, g_strdup_printf ("%u", message_info->uid));
1049         }
1050         
1051         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMboxFolder::get_uid_list\n");
1052         
1053         return uid_list;
1054 }
1055
1056
1057
1058
1059
1060
1061
1062
1063 static CamelMimeMessage *
1064 _get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex)
1065 {
1066         
1067         CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(folder);
1068         GArray *message_info_array;
1069         CamelMboxSummaryInformation *message_info;
1070         guint32 searched_uid;
1071         int i;
1072         gboolean uid_found;
1073         CamelStream *message_stream;
1074         CamelMimeMessage *message = NULL;
1075         CamelStore *parent_store;
1076
1077         CAMEL_LOG_FULL_DEBUG ("Entering CamelMboxFolder::get_uid_list\n");
1078         
1079         searched_uid = strtoul(uid, (char **)NULL, 10);
1080
1081         message_info_array = mbox_folder->internal_summary->message_info;
1082         i=0;
1083         uid_found = FALSE;
1084         
1085         /* first, look for the message that has the searched uid */
1086         while ((i<message_info_array->len) && (!uid_found)) {
1087                 message_info = (CamelMboxSummaryInformation *)(message_info_array->data) + i;
1088                 uid_found = (message_info->uid == searched_uid);
1089                 i++;
1090         }
1091         
1092         /* if the uid was not found, raise an exception and return */
1093         if (!uid_found) {
1094                 camel_exception_setv (ex, 
1095                                      CAMEL_EXCEPTION_FOLDER_INVALID_UID,
1096                                      "uid %s not found in the folder",
1097                                       uid);
1098                 CAMEL_LOG_FULL_DEBUG ("Leaving CamelMboxFolder::get_uid_list\n");
1099                 return NULL;
1100         }
1101         
1102         /* at this point, the message_info structure 
1103            contains the informations concerning the 
1104            message that was searched for */
1105         
1106         /* create a stream bound to the message */
1107         message_stream = camel_stream_fs_new_with_name_and_bounds (mbox_folder->folder_file_path, 
1108                                                                    CAMEL_STREAM_FS_READ,
1109                                                                    message_info->position, 
1110                                                                    message_info->position + message_info->size);
1111
1112
1113         /* get the parent store */
1114         parent_store = camel_folder_get_parent_store (folder, ex);
1115         if (camel_exception_get_id (ex)) {
1116                 gtk_object_unref (GTK_OBJECT (message_stream));
1117                 return NULL;
1118         }
1119
1120         
1121         message = camel_mime_message_new_with_session (camel_service_get_session (CAMEL_SERVICE (parent_store)));
1122         camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (message), message_stream);
1123         
1124         
1125
1126         
1127         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMboxFolder::get_uid_list\n");       
1128         return message;
1129 }
1130
1131 static GList *
1132 search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex)
1133 {
1134         return camel_mbox_folder_search_by_expression(folder, expression, ex);
1135 }