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