4ad5409658b019c69dfb95c2c66fba928195c82b
[platform/upstream/evolution-data-server.git] / camel / providers / maildir / camel-maildir-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-maildir-folder.c : camel-folder subclass for maildir folders */
3
4 /* 
5  *
6  * Copyright (C) 1999 Bertrand Guiheneuf <Bertrand.Guiheneuf@inria.fr> .
7  *
8  * This program is free software; you can redistribute it and/or 
9  * modify it under the terms of the GNU General Public License as 
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21  * USA
22  */
23
24 /*
25  * AUTHORS : Jukka Zitting 
26  *  
27  */
28
29
30 #include <config.h> 
31 #include <sys/stat.h> 
32 #include <sys/param.h> 
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <fcntl.h>
36 #include <dirent.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <time.h>
40 #include <string.h>
41 #include "camel-maildir-folder.h"
42 #include "camel-maildir-store.h"
43 #include "camel-stream-fs.h"
44 #include "camel-log.h"
45
46 static CamelFolderClass *parent_class=NULL;
47
48 /* Returns the class for a CamelMaildirFolder */
49 #define CMAILDIRF_CLASS(so) CAMEL_MAILDIR_FOLDER_CLASS (GTK_OBJECT(so)->klass)
50 #define CF_CLASS(so) CAMEL_FOLDER_CLASS (GTK_OBJECT(so)->klass)
51 #define CMAILDIRS_CLASS(so) CAMEL_STORE_CLASS (GTK_OBJECT(so)->klass)
52
53 static void _init_with_store (CamelFolder *folder, CamelStore *parent_store, CamelException *ex);
54 static void _set_name (CamelFolder *folder, const gchar *name, CamelException *ex);
55 static gboolean _exists (CamelFolder *folder, CamelException *ex);
56 static gboolean _create (CamelFolder *folder, CamelException *ex);
57 static gboolean _delete (CamelFolder *folder, gboolean recurse, CamelException *ex);
58 static gboolean _delete_messages (CamelFolder *folder, CamelException *ex);
59 static CamelMimeMessage *_get_message (CamelFolder *folder, gint number, CamelException *ex);
60 static gint _get_message_count (CamelFolder *folder, CamelException *ex);
61 static void _expunge (CamelFolder *folder, CamelException *ex);
62 static GList *_list_subfolders (CamelFolder *folder, CamelException *ex);
63
64 /* fs utility functions */
65 static DIR * _xopendir (const gchar *path);
66 static gboolean _xstat (const gchar *path, struct stat *buf);
67 static gboolean _xmkdir (const gchar *path);
68 static gboolean _xrename (const gchar *from, const gchar *to);
69 static gboolean _xunlink (const gchar *path);
70 static gboolean _xrmdir (const gchar *path);
71 /* ** */
72
73 static void
74 camel_maildir_folder_class_init (CamelMaildirFolderClass *camel_maildir_folder_class)
75 {
76         CamelFolderClass *camel_folder_class =
77                 CAMEL_FOLDER_CLASS (camel_maildir_folder_class);
78
79         parent_class = gtk_type_class (camel_folder_get_type ());
80
81         /* virtual method definition */
82         /* virtual method overload */
83         camel_folder_class->init_with_store   = _init_with_store;
84         camel_folder_class->set_name          = _set_name;
85         camel_folder_class->exists            = _exists;
86         camel_folder_class->create            = _create;
87         camel_folder_class->delete            = _delete;
88         camel_folder_class->delete_messages   = _delete_messages;
89         camel_folder_class->expunge           = _expunge;
90         camel_folder_class->get_message       = _get_message;
91         camel_folder_class->get_message_count = _get_message_count;
92         camel_folder_class->list_subfolders   = _list_subfolders;
93 }
94
95 GtkType
96 camel_maildir_folder_get_type (void)
97 {
98         static GtkType camel_maildir_folder_type = 0;
99         
100         if (!camel_maildir_folder_type) {
101                 GtkTypeInfo camel_maildir_folder_info = 
102                 {
103                         "CamelMaildirFolder",
104                         sizeof (CamelMaildirFolder),
105                         sizeof (CamelMaildirFolderClass),
106                         (GtkClassInitFunc) camel_maildir_folder_class_init,
107                         (GtkObjectInitFunc) NULL,
108                                 /* reserved_1 */ NULL,
109                                 /* reserved_2 */ NULL,
110                         (GtkClassInitFunc) NULL,
111                 };
112                 
113                 camel_maildir_folder_type =
114                         gtk_type_unique (CAMEL_FOLDER_TYPE, &camel_maildir_folder_info);
115         }
116         
117         return camel_maildir_folder_type;
118 }
119
120
121
122
123
124
125 /**
126  * CamelMaildirFolder::init_with_store: initializes the folder object
127  * @folder:       folder object to initialize
128  * @parent_store: parent store object of the folder
129  *
130  * Simply tells that the folder can contain messages but not subfolders.
131  * Perhaps we'll later implement subfolders too...
132  */
133 static void 
134 _init_with_store (CamelFolder *folder, CamelStore *parent_store, CamelException *ex)
135 {
136         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::init_with_store\n");
137         g_assert (folder);
138         g_assert (parent_store);
139         
140         /* call parent method */
141         parent_class->init_with_store (folder, parent_store, ex);
142         
143         folder->can_hold_messages = TRUE;
144         folder->can_hold_folders = TRUE;
145         folder->has_summary_capability = FALSE;
146
147         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::init_with_store\n");
148 }
149
150 /**
151  * CamelMaildirFolder::set_name: sets the name of the folder
152  * @folder: folder object
153  * @name:   name of the folder
154  *
155  * Sets the name of the folder object. The existence of a folder with
156  * the given name is not checked in this function.
157  */
158 static void
159 _set_name (CamelFolder *folder, const gchar *name, CamelException *ex)
160 {
161         CamelMaildirFolder *maildir_folder;
162         CamelMaildirStore *maildir_store;
163         
164         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::set_name\n");
165         g_assert (folder);
166         g_assert (name);
167         g_assert (folder->parent_store);
168
169         maildir_folder = CAMEL_MAILDIR_FOLDER (folder);
170         maildir_store = CAMEL_MAILDIR_STORE (folder->parent_store);
171
172         /* call default implementation */
173         parent_class->set_name (folder, name, ex);
174         
175         if (maildir_folder->directory_path)
176                 g_free (maildir_folder->directory_path);
177
178         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::set_name full_name is %s\n", folder->full_name);
179         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::set_name toplevel_dir is %s\n", maildir_store->toplevel_dir);
180         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::set_name separator is %c\n", camel_store_get_separator (folder->parent_store));
181
182         if (folder->full_name && folder->full_name[0])
183                 maildir_folder->directory_path =
184                         g_strconcat (maildir_store->toplevel_dir, G_DIR_SEPARATOR_S,
185                                      folder->full_name, NULL);
186         else
187                 maildir_folder->directory_path = g_strdup (maildir_store->toplevel_dir);
188
189         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::set_name: name set to %s\n", name);
190         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::set_name\n");
191 }
192
193 /**
194  * CamelMaildirFolder::exists: tests whether the named maildir exists
195  * @folder: folder object
196  *
197  * A created maildir folder object doesn't necessarily exist yet in the
198  * filesystem. This function checks whether the maildir exists.
199  * The structure of the maildir is stated in the maildir.5 manpage.
200  *
201  * maildir.5:
202  *     A directory in maildir format  has  three  subdirectories,
203  *     all on the same filesystem: tmp, new, and cur.
204  *
205  * Return value: TRUE if the maildir exists, FALSE otherwise
206  */
207 static gboolean
208 _exists (CamelFolder *folder, CamelException *ex)
209 {
210         CamelMaildirFolder *maildir_folder = CAMEL_MAILDIR_FOLDER (folder);
211         static const gchar *dir[3] = { "new", "cur", "tmp" };
212         gint i;
213         struct stat statbuf;
214         const gchar *maildir;
215         gchar *path;
216         gboolean rv = TRUE;
217
218         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::exists\n");
219         g_assert (folder);
220         g_return_val_if_fail (maildir_folder->directory_path, FALSE);
221
222         maildir = maildir_folder->directory_path;
223
224         CAMEL_LOG_FULL_DEBUG ("CamelMailFolder::exists: checking maildir %s\n",
225                               maildir);
226
227         /* check whether the toplevel directory exists */
228         rv = _xstat (maildir, &statbuf) && S_ISDIR (statbuf.st_mode);
229
230         /* check whether the maildir subdirectories exist */
231         for (i = 0; rv && i < 3; i++) {
232                 path = g_strconcat (maildir, G_DIR_SEPARATOR_S, dir[i], NULL);
233
234                 rv = _xstat (path, &statbuf) && S_ISDIR (statbuf.st_mode);
235
236                 g_free (path);
237         }
238
239         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::exists: %s\n",
240                               (rv) ? "maildir found" : "maildir not found");
241         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::exists\n");
242         return rv;
243 }
244
245 /**
246  * CamelMaildirFolder::create: creates the named maildir
247  * @folder: folder object
248  *
249  * A created maildir folder object doesn't necessarily exist yet in the
250  * filesystem. This function creates the maildir if it doesn't yet exist.
251  * The structure of the maildir is stated in the maildir.5 manpage.
252  *
253  * maildir.5:
254  *     A directory in maildir format  has  three  subdirectories,
255  *     all on the same filesystem: tmp, new, and cur.
256  *
257  * Return value: TRUE if the maildir existed already or was created,
258  *               FALSE otherwise
259  */
260 static gboolean
261 _create (CamelFolder *folder, CamelException *ex)
262 {
263         CamelMaildirFolder *maildir_folder = CAMEL_MAILDIR_FOLDER (folder);
264         static const gchar *dir[3] = { "new", "cur", "tmp" };
265         gint i;
266         const gchar *maildir;
267         gchar *path;
268         gboolean rv = TRUE;
269
270         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::create\n");
271         g_assert (folder);
272
273         /* check whether the maildir already exists */
274         if (camel_folder_exists (folder, ex)) return TRUE;
275
276         maildir = maildir_folder->directory_path;
277
278         CAMEL_LOG_FULL_DEBUG ("CamelMailFolder::create: creating maildir %s\n",
279                               maildir);
280
281         /* create the toplevel directory */
282         rv = _xmkdir (maildir);
283
284         /* create the maildir subdirectories */
285         for (i = 0; rv && i < 3; i++) {
286                 path = g_strconcat (maildir, G_DIR_SEPARATOR_S, dir[i], NULL);
287
288                 rv = _xmkdir (path);
289
290                 g_free (path);
291         }
292
293         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::create: %s\n",
294                               rv ? "maildir created" : "an error occurred");
295         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::create\n");
296         return rv;
297 }
298
299 /**
300  * CamelMaildirFolder::delete: delete the maildir folder
301  * @folder: the folder object
302  * @recurse:
303  *
304  * This function empties and deletes the maildir folder. The subdirectories
305  * "tmp", "cur", and "new" are removed first and then the toplevel maildir
306  * directory is deleted. All files from the directories are deleted as well, 
307  * so you should be careful when using this function. If a subdirectory cannot
308  * be deleted, then the operation it is stopped. Thus if an error occurs, the
309  * maildir directory won't be removed, but it might no longer be a valid maildir.
310  */
311 static gboolean
312 _delete (CamelFolder *folder, gboolean recurse, CamelException *ex)
313 {
314         CamelMaildirFolder *maildir_folder = CAMEL_MAILDIR_FOLDER (folder);
315         static const gchar *dir[3] = { "new", "cur", "tmp" };
316         gint i;
317         const gchar *maildir;
318         gchar *path;
319         gboolean rv = TRUE;
320
321         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::create\n");
322         g_assert (folder);
323
324         /* check whether the maildir already exists */
325         if (!camel_folder_exists (folder, ex)) return TRUE;
326
327         maildir = maildir_folder->directory_path;
328
329         CAMEL_LOG_FULL_DEBUG ("CamelMailFolder::delete: deleting maildir %s\n",
330                               maildir);
331
332         /* delete the maildir subdirectories */
333         for (i = 0; rv && i < 3; i++) {
334                 path = g_strconcat (maildir, G_DIR_SEPARATOR_S, dir[i], NULL);
335
336                 rv = _xrmdir (path);
337
338                 g_free (path);
339         }
340
341         /* create the toplevel directory */
342         if (rv)
343                 rv = _xrmdir (maildir);
344
345         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::delete: %s\n",
346                               rv ? "maildir deleted" : "an error occurred");
347         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::delete\n");
348         return rv;
349 }
350
351 /**
352  * CamelMaildirFolder::delete_messages: empty the maildir folder
353  * @folder:  the folder object
354  *
355  * This function empties the maildir folder. All messages from the
356  * "cur" subdirectory are deleted. If a message cannot be deleted, then
357  * it is just skipped and the rest of the messages are still deleted.
358  * Files with names starting with a dot are skipped as described in the
359  * maildir.5 manpage.
360  *
361  * maildir.5:
362  *     It is a good idea for readers to skip all filenames in new
363  *     and cur starting with a dot. Other than this, readers
364  *     should not attempt to parse filenames.
365  *
366  * Return value: FALSE on error and if some messages could not be deleted.
367  *               TRUE otherwise.
368  */
369 static gboolean 
370 _delete_messages (CamelFolder *folder, CamelException *ex)
371 {
372         CamelMaildirFolder *maildir_folder = CAMEL_MAILDIR_FOLDER (folder);
373         const gchar *maildir;
374         gchar *curdir, *file;
375         DIR *dir_handle;
376         struct dirent *dir_entry;
377         gboolean rv = TRUE;
378
379         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::delete_messages\n");
380         g_assert (folder);
381
382         /* call default implementation */
383         parent_class->delete_messages (folder, ex);
384
385         /* Check if the folder didn't exist */
386         if (!camel_folder_exists (folder, ex)) return TRUE;
387
388         maildir = maildir_folder->directory_path;
389
390         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::delete_messages: "
391                               "deleting messages from %s\n", maildir);
392
393         /* delete messages from the maildir subdirectory "cur" */
394         curdir = g_strconcat (maildir, G_DIR_SEPARATOR_S, "cur", NULL);
395
396         dir_handle = _xopendir (curdir);
397         if (dir_handle) {
398                 while ((dir_entry = readdir (dir_handle))) {
399                         if (dir_entry->d_name[0] == '.') continue;
400                         file = g_strconcat (curdir, G_DIR_SEPARATOR_S,
401                                             dir_entry->d_name, NULL);
402
403                         if (!_xunlink (file)) rv = FALSE;
404
405                         g_free (file);
406                 }
407                 closedir (dir_handle);
408         } else
409                 rv = FALSE;
410
411         g_free (curdir);
412         
413         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::delete_messages: %s\n",
414                               rv ? "messages deleted" : "an error occurred");
415         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::delete_messages\n");
416         return rv;
417 }
418
419 /**
420  * CamelMaildirFolder::get_message: get a message from maildir
421  * @folder: the folder object
422  * @number: number of the message within the folder
423  *
424  * Return value: the message, NULL on error
425  */
426 static CamelMimeMessage *
427 _get_message (CamelFolder *folder, gint number, CamelException *ex)
428 {
429         CamelMaildirFolder *maildir_folder = CAMEL_MAILDIR_FOLDER(folder);
430         DIR *dir_handle;
431         struct dirent *dir_entry;
432         CamelStream *stream;
433         CamelMimeMessage *message = NULL;
434         const gchar *maildir;
435         gchar *curdir, *file = NULL;
436         gint count = -1;
437
438         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::get_message\n");
439         g_assert(folder);
440
441         /* Check if the folder exists */
442         if (!camel_folder_exists (folder, ex)) return NULL;
443
444         maildir = maildir_folder->directory_path;
445
446         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::get_message: "
447                               "getting message #%d from %s\n", number, maildir);
448
449         /* Count until the desired message is reached */
450         curdir = g_strconcat (maildir, G_DIR_SEPARATOR_S, "cur", NULL);
451         if ((dir_handle = _xopendir (curdir))) {
452                 while ((count < number) && (dir_entry = readdir (dir_handle)))
453                         if (dir_entry->d_name[0] != '.') count++;
454
455                 if (count == number)
456                         file = g_strconcat (curdir, G_DIR_SEPARATOR_S,
457                                             dir_entry->d_name, NULL);
458
459                 closedir (dir_handle);
460         }
461         g_free (curdir);
462         if (!file) return NULL;
463
464         /* Create the message object */
465 #warning use session field here
466         message = camel_mime_message_new_with_session ((CamelSession *) NULL);
467         stream = camel_stream_fs_new_with_name (file, CAMEL_STREAM_FS_READ);
468
469         if (!message || !stream) {
470                 g_free (file);
471                 if (stream) gtk_object_unref (GTK_OBJECT (stream));
472                 if (message) gtk_object_unref (GTK_OBJECT (message));
473                 return NULL;
474         }
475
476         camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (message),
477                                                   stream);
478         gtk_object_unref (GTK_OBJECT (stream));
479         gtk_object_set_data_full (GTK_OBJECT (message),
480                                   "fullpath", file, g_free);
481
482         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::get_message: "
483                               "message %p created from %s\n", message, file);
484         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::get_message\n");
485         return message;
486 }
487
488 /**
489  * CamelMaildirFolder::get_message_count: count messages in maildir
490  * @folder:  the folder object
491  *
492  * Returns the number of messages in the maildir folder. New messages
493  * are included in this count. 
494  *
495  * Return value: number of messages in the maildir, -1 on error
496  */
497 static gint
498 _get_message_count (CamelFolder *folder, CamelException *ex)
499 {
500         CamelMaildirFolder *maildir_folder = CAMEL_MAILDIR_FOLDER(folder);
501         const gchar *maildir;
502         gchar *newdir, *curdir, *newfile, *curfile;
503         DIR *dir_handle;
504         struct dirent *dir_entry;
505         guint count = 0;
506
507         CAMEL_LOG_FULL_DEBUG ("Entering "
508                               "CamelMaildirFolder::get_message_count\n");
509         g_assert(folder);
510
511         /* check if the maildir exists */
512         if (!camel_folder_exists (folder, ex)) return -1;
513
514         maildir = maildir_folder->directory_path;
515
516         newdir = g_strconcat (maildir, G_DIR_SEPARATOR_S, "new", NULL);
517         curdir = g_strconcat (maildir, G_DIR_SEPARATOR_S, "cur", NULL);
518
519         /* Check new messages */
520         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::get_message_count: "
521                               "getting new messages from %s\n", newdir);
522         if ((dir_handle = _xopendir (newdir))) {
523                 while ((dir_entry = readdir (dir_handle))) {
524                         if (dir_entry->d_name[0] == '.') continue;
525                         newfile = g_strconcat (newdir, G_DIR_SEPARATOR_S,
526                                                dir_entry->d_name, NULL);
527                         curfile = g_strconcat (curdir, G_DIR_SEPARATOR_S,
528                                                dir_entry->d_name, ":2,", NULL);
529                         
530                         _xrename (newfile, curfile);
531                         
532                         g_free (curfile);
533                         g_free (newfile);
534                 }
535                 closedir (dir_handle);
536         }
537
538         /* Count messages */
539         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::get_message_count: "
540                               "counting messages in %s\n", curdir);
541         if ((dir_handle = _xopendir (curdir))) {
542                 while ((dir_entry = readdir (dir_handle)))
543                         if (dir_entry->d_name[0] != '.') count++;
544                 closedir (dir_handle);
545         }
546
547         g_free (curdir);
548         g_free (newdir);
549
550         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::get_message_count: "
551                               " found %d messages\n", count);
552         CAMEL_LOG_FULL_DEBUG ("Leaving "
553                               "CamelMaildirFolder::get_message_count\n");
554         return count;
555 }
556
557
558
559
560 /**
561  * CamelMaildirFolder::expunge: expunge messages marked as deleted
562  * @folder:  the folder object
563  *
564  * Physically deletes the messages marked as deleted in the folder.
565  */
566 static void
567 _expunge (CamelFolder *folder, CamelException *ex)
568 {
569         CamelMimeMessage *message;
570         GList *node;
571         gchar *fullpath;
572
573         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::expunge\n");
574         g_assert(folder);
575
576         /* expunge messages marked for deletion */
577         for (node = folder->message_list; node; node = g_list_next(node)) {
578                 message = CAMEL_MIME_MESSAGE (node->data);
579                 if (!message) {
580                         CAMEL_LOG_WARNING ("CamelMaildirFolder::expunge: "
581                                            "null message in node %p\n", node);
582                         continue;
583                 }
584                                 
585                 if (camel_mime_message_get_flag (message, "DELETED")) {
586                         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::expunge: "
587                                               "expunging message #%d\n",
588                                               message->message_number);
589
590                         /* expunge the message */
591                         fullpath = gtk_object_get_data (GTK_OBJECT (message),
592                                                         "fullpath");
593                         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::expunge: "
594                                               "message fullpath is %s\n",
595                                               fullpath);
596
597                         if (_xunlink (fullpath))
598                                 message->expunged = TRUE;
599                 } else {
600                         CAMEL_LOG_FULL_DEBUG ("CamelMaildirFolder::expunge: "
601                                               "skipping message #%d\n",
602                                               message->message_number);
603                 }
604         }
605         
606         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::expunge\n");
607 }
608
609
610
611
612 /**
613  * CamelMaildirFolder::list_subfolders: return a list of subfolders
614  * @folder:  the folder object
615  *
616  * Returns the names of the maildir subfolders in a list.
617  *
618  * Return value: list of subfolder names
619  */
620 static GList *
621 _list_subfolders (CamelFolder *folder, CamelException *ex)
622 {
623         CamelMaildirFolder *maildir_folder = CAMEL_MAILDIR_FOLDER (folder);
624         const gchar *maildir;
625         gchar *subdir;
626         struct stat statbuf;
627         struct dirent *dir_entry;
628         DIR *dir_handle;
629         GList *subfolders = NULL;
630
631         CAMEL_LOG_FULL_DEBUG ("Entering CamelMaildirFolder::list_subfolders\n");
632         g_assert (folder);
633
634         /* check if the maildir exists */
635         if (!camel_folder_exists (folder, ex)) return NULL;
636
637         /* scan through the maildir toplevel directory */
638         maildir = maildir_folder->directory_path;
639         if ((dir_handle = _xopendir (maildir))) {
640                 while ((dir_entry = readdir (dir_handle))) {
641                         if (dir_entry->d_name[0] == '.') continue;
642                         if (strcmp (dir_entry->d_name, "new") == 0) continue;
643                         if (strcmp (dir_entry->d_name, "cur") == 0) continue;
644                         if (strcmp (dir_entry->d_name, "tmp") == 0) continue;
645
646                         subdir = g_strconcat (maildir, G_DIR_SEPARATOR_S,
647                                               dir_entry->d_name, NULL);
648                         
649                         if (_xstat (subdir, &statbuf)
650                             && S_ISDIR (statbuf.st_mode))
651                                 subfolders =
652                                         g_list_append (
653                                                 subfolders,
654                                                 g_strdup (dir_entry->d_name));
655                         
656                         g_free (subdir);
657                 }
658                 closedir (dir_handle);
659         }
660
661         CAMEL_LOG_FULL_DEBUG ("Leaving CamelMaildirFolder::list_subfolders\n");
662         return subfolders;
663 }
664
665
666
667
668
669
670
671 /*
672  * fs utility function 
673  *
674  */
675
676 static DIR *
677 _xopendir (const gchar *path)
678 {
679         DIR *handle;
680         g_assert (path);
681
682         handle = opendir (path);
683         if (!handle) {
684                 CAMEL_LOG_WARNING ("ERROR: opendir (%s);\n", path);
685                 CAMEL_LOG_FULL_DEBUG ("  Full error text is: (%d) %s\n",
686                                       errno, strerror(errno));
687         }
688
689         return handle;
690 }
691
692 static gboolean
693 _xstat (const gchar *path, struct stat *buf)
694 {
695         gint stat_error;
696         g_assert (path);
697         g_assert (buf);
698
699         stat_error = stat (path, buf);
700         if (stat_error == 0) {
701                 return TRUE;
702         } else if (errno == ENOENT) {
703                 buf->st_mode = 0;
704                 return TRUE;
705         } else {
706                 CAMEL_LOG_WARNING ("ERROR: stat (%s, %p);\n", path, buf);
707                 CAMEL_LOG_FULL_DEBUG ("  Full error text is: (%d) %s\n",
708                                       errno, strerror(errno));
709                 return FALSE;
710         }
711 }
712
713 static gboolean
714 _xmkdir (const gchar *path)
715 {
716         g_assert (path);
717
718         if (mkdir (path, S_IRWXU) == -1) {
719                 CAMEL_LOG_WARNING ("ERROR: mkdir (%s, S_IRWXU);\n", path);
720                 CAMEL_LOG_FULL_DEBUG ("  Full error text is: (%d) %s\n",
721                                       errno, strerror(errno));
722                 return FALSE;
723         } 
724
725         return TRUE;
726 }
727
728 static gboolean
729 _xrename (const gchar *from, const gchar *to)
730 {
731         g_assert (from);
732         g_assert (to);
733
734         if (rename (from, to) == 0) {
735                 return TRUE;
736         } else {
737                 CAMEL_LOG_WARNING ("ERROR: rename (%s, %s);\n", from, to);
738                 CAMEL_LOG_FULL_DEBUG ("  Full error text is: (%d) %s\n",
739                                       errno, strerror(errno));
740                 return FALSE;
741         }
742 }
743
744 static gboolean
745 _xunlink (const gchar *path)
746 {
747         g_assert (path);
748
749         if (unlink (path) == 0) {
750                 return TRUE;
751         } else if (errno == ENOENT) {
752                 return TRUE;
753         } else {
754                 CAMEL_LOG_WARNING ("ERROR: unlink (%s);\n", path);
755                 CAMEL_LOG_FULL_DEBUG ("  Full error text is: (%d) %s\n",
756                                       errno, strerror(errno));
757                 return FALSE;
758         }
759 }
760
761 static gboolean
762 _xrmdir (const gchar *path)
763 {
764         DIR *dir_handle;
765         struct dirent *dir_entry;
766         gchar *file;
767         struct stat statbuf;
768         g_assert (path);
769
770         dir_handle = opendir (path);
771         if (!dir_handle && errno == ENOENT) {
772                 return TRUE;
773         } else if (!dir_handle) {
774                 CAMEL_LOG_WARNING ("ERROR: opendir (%s);\n", path);
775                 CAMEL_LOG_FULL_DEBUG ("  Full error text is: (%d) %s\n",
776                                       errno, strerror(errno));
777                 return FALSE;
778         }
779
780         while ((dir_entry = readdir (dir_handle))) {
781                 file = g_strconcat (path, G_DIR_SEPARATOR_S, dir_entry->d_name,
782                                     NULL);
783                 if (_xstat (file, &statbuf) && S_ISREG (statbuf.st_mode))
784                         _xunlink (file);
785                 g_free (file);
786         }
787
788         closedir (dir_handle);
789
790         if (rmdir (path) == 0) {
791                 return TRUE;
792         } else if (errno == ENOENT) {
793                 return TRUE;
794         } else {
795                 CAMEL_LOG_WARNING ("ERROR: rmdir (%s);\n", path);
796                 CAMEL_LOG_FULL_DEBUG ("  Full error text is: (%d) %s\n",
797                                       errno, strerror(errno));
798                 return FALSE;
799         } 
800 }
801
802 /** *** **/
803