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