Rename camel_service_get_settings().
[platform/upstream/evolution-data-server.git] / camel / providers / local / camel-mbox-store.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Authors: Michael Zucchi <notzed@ximian.com>
4  *
5  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19  * USA
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32
33 #include <glib/gi18n-lib.h>
34 #include <glib/gstdio.h>
35
36 #include "camel-mbox-folder.h"
37 #include "camel-mbox-store.h"
38
39 #define d(x)
40
41 G_DEFINE_TYPE (CamelMboxStore, camel_mbox_store, CAMEL_TYPE_LOCAL_STORE)
42
43 static const gchar *extensions[] = {
44         ".msf", ".ev-summary", ".ev-summary-meta", ".ibex.index", ".ibex.index.data", ".cmeta", ".lock", ".db", ".journal"
45 };
46
47 /* used to find out where we've visited already */
48 struct _inode {
49         dev_t dnode;
50         ino_t inode;
51 };
52
53 static guint
54 inode_hash (gconstpointer d)
55 {
56         const struct _inode *v = d;
57
58         return v->inode ^ v->dnode;
59 }
60
61 static gboolean
62 inode_equal (gconstpointer a,
63              gconstpointer b)
64 {
65         const struct _inode *v1 = a, *v2 = b;
66
67         return v1->inode == v2->inode && v1->dnode == v2->dnode;
68 }
69
70 static void
71 inode_free (gpointer k,
72             gpointer v,
73             gpointer d)
74 {
75         g_free (k);
76 }
77
78 static gboolean
79 ignore_file (const gchar *filename,
80              gboolean sbd)
81 {
82         gint flen, len, i;
83
84         /* TODO: Should probably just be 1 regex */
85         flen = strlen (filename);
86         if (flen > 0 && filename[flen - 1] == '~')
87                 return TRUE;
88
89         for (i = 0; i < G_N_ELEMENTS (extensions); i++) {
90                 len = strlen (extensions[i]);
91                 if (len < flen && !strcmp (filename + flen - len, extensions[i]))
92                         return TRUE;
93         }
94
95         if (sbd && flen > 4 && !strcmp(filename + flen - 4, ".sbd"))
96                 return TRUE;
97
98         return FALSE;
99 }
100
101 /* NB: duplicated in maildir store */
102 static void
103 fill_fi (CamelStore *store,
104          CamelFolderInfo *fi,
105          guint32 flags)
106 {
107         CamelFolder *folder;
108
109         fi->unread = -1;
110         fi->total = -1;
111         folder = camel_object_bag_peek (store->folders, fi->full_name);
112         if (folder) {
113                 if ((flags & CAMEL_STORE_FOLDER_INFO_FAST) == 0)
114                         camel_folder_refresh_info_sync (folder, NULL, NULL);
115                 fi->unread = camel_folder_get_unread_message_count (folder);
116                 fi->total = camel_folder_get_message_count (folder);
117                 g_object_unref (folder);
118         } else {
119                 CamelLocalStore *local_store;
120                 gchar *folderpath;
121                 CamelMboxSummary *mbs;
122
123                 local_store = CAMEL_LOCAL_STORE (store);
124
125                 /* This should be fast enough not to have to test for INFO_FAST */
126                 folderpath = camel_local_store_get_full_path (
127                         local_store, fi->full_name);
128
129                 mbs = (CamelMboxSummary *) camel_mbox_summary_new (NULL, folderpath, NULL);
130                 /* FIXME[disk-summary] track exception */
131                 if (camel_folder_summary_header_load_from_db ((CamelFolderSummary *) mbs, store, fi->full_name, NULL)) {
132                         fi->unread = camel_folder_summary_get_unread_count ((CamelFolderSummary *) mbs);
133                         fi->total = camel_folder_summary_get_saved_count ((CamelFolderSummary *) mbs);
134                 }
135
136                 g_object_unref (mbs);
137                 g_free (folderpath);
138         }
139
140         if (camel_local_store_is_main_store (CAMEL_LOCAL_STORE (store)) && fi->full_name
141             && (fi->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_NORMAL)
142                 fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK)
143                             | camel_local_store_get_folder_type_by_full_name (CAMEL_LOCAL_STORE (store), fi->full_name);
144 }
145
146 static CamelFolderInfo *
147 scan_dir (CamelStore *store,
148           GHashTable *visited,
149           CamelFolderInfo *parent,
150           const gchar *root,
151           const gchar *name,
152           guint32 flags,
153           GError **error)
154 {
155         CamelFolderInfo *folders, *tail, *fi;
156         GHashTable *folder_hash;
157         const gchar *dent;
158         GDir *dir;
159
160         tail = folders = NULL;
161
162         if (!(dir = g_dir_open (root, 0, NULL)))
163                 return NULL;
164
165         folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
166
167         /* FIXME: it would be better if we queue'd up the recursive
168          * scans till the end so that we can limit the number of
169          * directory descriptors open at any given time... */
170
171         while ((dent = g_dir_read_name (dir))) {
172                 gchar *short_name, *full_name, *path, *ext;
173                 struct stat st;
174
175                 if (dent[0] == '.')
176                         continue;
177
178                 if (ignore_file (dent, FALSE))
179                         continue;
180
181                 path = g_strdup_printf("%s/%s", root, dent);
182                 if (g_stat (path, &st) == -1) {
183                         g_free (path);
184                         continue;
185                 }
186 #ifndef G_OS_WIN32
187                 if (S_ISDIR (st.st_mode)) {
188                         struct _inode in = { st.st_dev, st.st_ino };
189
190                         if (g_hash_table_lookup (visited, &in)) {
191                                 g_free (path);
192                                 continue;
193                         }
194                 }
195 #endif
196                 short_name = g_strdup (dent);
197                 if ((ext = strrchr(short_name, '.')) && !strcmp(ext, ".sbd"))
198                         *ext = '\0';
199
200                 if (name != NULL)
201                         full_name = g_strdup_printf("%s/%s", name, short_name);
202                 else
203                         full_name = g_strdup (short_name);
204
205                 if ((fi = g_hash_table_lookup (folder_hash, short_name)) != NULL) {
206                         g_free (short_name);
207                         g_free (full_name);
208
209                         if (S_ISDIR (st.st_mode)) {
210                                 fi->flags =(fi->flags & ~CAMEL_FOLDER_NOCHILDREN) | CAMEL_FOLDER_CHILDREN;
211                         } else {
212                                 fi->flags &= ~CAMEL_FOLDER_NOSELECT;
213                         }
214                 } else {
215                         fi = camel_folder_info_new ();
216                         fi->parent = parent;
217
218                         fi->full_name = full_name;
219                         fi->display_name = short_name;
220                         fi->unread = -1;
221                         fi->total = -1;
222
223                         if (S_ISDIR (st.st_mode))
224                                 fi->flags = CAMEL_FOLDER_NOSELECT;
225                         else
226                                 fi->flags = CAMEL_FOLDER_NOCHILDREN;
227
228                         if (tail == NULL)
229                                 folders = fi;
230                         else
231                                 tail->next = fi;
232
233                         tail = fi;
234
235                         g_hash_table_insert (folder_hash, fi->display_name, fi);
236                 }
237
238                 if (!S_ISDIR (st.st_mode)) {
239                         fill_fi (store, fi, flags);
240                 } else if ((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE)) {
241                         struct _inode in = { st.st_dev, st.st_ino };
242
243                         if (g_hash_table_lookup (visited, &in) == NULL) {
244 #ifndef G_OS_WIN32
245                                 struct _inode *inew = g_new (struct _inode, 1);
246
247                                 *inew = in;
248                                 g_hash_table_insert (visited, inew, inew);
249 #endif
250                                 if ((fi->child = scan_dir (store, visited, fi, path, fi->full_name, flags, error)))
251                                         fi->flags |= CAMEL_FOLDER_CHILDREN;
252                                 else
253                                         fi->flags =(fi->flags & ~CAMEL_FOLDER_CHILDREN) | CAMEL_FOLDER_NOCHILDREN;
254                         }
255                 }
256
257                 g_free (path);
258         }
259
260         g_dir_close (dir);
261
262         g_hash_table_destroy (folder_hash);
263
264         return folders;
265 }
266
267 static gint
268 xrename (CamelStore *store,
269          const gchar *old_name,
270          const gchar *new_name,
271          const gchar *ext,
272          gboolean missingok)
273 {
274         CamelLocalStore *ls = (CamelLocalStore *) store;
275         gchar *oldpath, *newpath;
276         struct stat st;
277         gint ret = -1;
278
279         if (ext != NULL) {
280                 oldpath = camel_local_store_get_meta_path (ls, old_name, ext);
281                 newpath = camel_local_store_get_meta_path (ls, new_name, ext);
282         } else {
283                 oldpath = camel_local_store_get_full_path (ls, old_name);
284                 newpath = camel_local_store_get_full_path (ls, new_name);
285         }
286
287         if (g_stat (oldpath, &st) == -1) {
288                 if (missingok && errno == ENOENT) {
289                         ret = 0;
290                 } else {
291                         ret = -1;
292                 }
293 #ifndef G_OS_WIN32
294         } else if (S_ISDIR (st.st_mode)) {
295                 /* use rename for dirs */
296                 if (g_rename (oldpath, newpath) == 0 || g_stat (newpath, &st) == 0) {
297                         ret = 0;
298                 } else {
299                         ret = -1;
300                 }
301         } else if (link (oldpath, newpath) == 0 /* and link for files */
302                    ||(g_stat (newpath, &st) == 0 && st.st_nlink == 2)) {
303                 if (unlink (oldpath) == 0) {
304                         ret = 0;
305                 } else {
306                         unlink (newpath);
307                         ret = -1;
308                 }
309         } else {
310                 ret = -1;
311 #else
312         } else if ((!g_file_test (newpath, G_FILE_TEST_EXISTS) || g_remove (newpath) == 0) &&
313                    g_rename (oldpath, newpath) == 0) {
314                 ret = 0;
315         } else {
316                 ret = -1;
317 #endif
318         }
319
320         g_free (oldpath);
321         g_free (newpath);
322
323         return ret;
324 }
325
326 static CamelFolder *
327 mbox_store_get_folder_sync (CamelStore *store,
328                             const gchar *folder_name,
329                             CamelStoreGetFolderFlags flags,
330                             GCancellable *cancellable,
331                             GError **error)
332 {
333         CamelStoreClass *store_class;
334         CamelLocalStore *local_store;
335         struct stat st;
336         gchar *name;
337
338         /* Chain up to parent's get_folder_sync() method. */
339         store_class = CAMEL_STORE_CLASS (camel_mbox_store_parent_class);
340         if (!store_class->get_folder_sync (store, folder_name, flags, cancellable, error))
341                 return NULL;
342
343         local_store = CAMEL_LOCAL_STORE (store);
344         name = camel_local_store_get_full_path (local_store, folder_name);
345
346         if (g_stat (name, &st) == -1) {
347                 gchar *basename;
348                 gchar *dirname;
349                 gint fd;
350
351                 if (errno != ENOENT) {
352                         g_set_error (
353                                 error, G_IO_ERROR,
354                                 g_io_error_from_errno (errno),
355                                 _("Cannot get folder '%s': %s"),
356                                 folder_name, g_strerror (errno));
357                         g_free (name);
358                         return NULL;
359                 }
360
361                 if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) {
362                         g_set_error (
363                                 error, CAMEL_STORE_ERROR,
364                                 CAMEL_STORE_ERROR_NO_FOLDER,
365                                 _("Cannot get folder '%s': folder does not exist."),
366                                 folder_name);
367                         g_free (name);
368                         return NULL;
369                 }
370
371                 /* sanity check the folder name */
372                 basename = g_path_get_basename (folder_name);
373
374                 if (basename[0] == '.' || ignore_file (basename, TRUE)) {
375                         g_set_error (
376                                 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
377                                 _("Cannot create a folder by this name."));
378                         g_free (name);
379                         g_free (basename);
380                         return NULL;
381                 }
382                 g_free (basename);
383
384                 dirname = g_path_get_dirname (name);
385                 if (g_mkdir_with_parents (dirname, 0700) == -1 && errno != EEXIST) {
386                         g_set_error (
387                                 error, G_IO_ERROR,
388                                 g_io_error_from_errno (errno),
389                                 _("Cannot create folder '%s': %s"),
390                                 folder_name, g_strerror (errno));
391                         g_free (dirname);
392                         g_free (name);
393                         return NULL;
394                 }
395
396                 g_free (dirname);
397
398                 fd = g_open (name, O_LARGEFILE | O_WRONLY | O_CREAT | O_APPEND | O_BINARY, 0666);
399                 if (fd == -1) {
400                         g_set_error (
401                                 error, G_IO_ERROR,
402                                 g_io_error_from_errno (errno),
403                                 _("Cannot create folder '%s': %s"),
404                                 folder_name, g_strerror (errno));
405                         g_free (name);
406                         return NULL;
407                 }
408
409                 g_free (name);
410                 close (fd);
411         } else if (!S_ISREG (st.st_mode)) {
412                 g_set_error (
413                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
414                         _("Cannot get folder '%s': not a regular file."),
415                         folder_name);
416                 g_free (name);
417                 return NULL;
418         } else if (flags & CAMEL_STORE_FOLDER_EXCL) {
419                 g_set_error (
420                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
421                         _("Cannot create folder '%s': folder exists."),
422                         folder_name);
423                 g_free (name);
424                 return NULL;
425         } else
426                 g_free (name);
427
428         return camel_mbox_folder_new (store, folder_name, flags, cancellable, error);
429 }
430
431 static CamelFolderInfo *
432 mbox_store_get_folder_info_sync (CamelStore *store,
433                                  const gchar *top,
434                                  CamelStoreGetFolderInfoFlags flags,
435                                  GCancellable *cancellable,
436                                  GError **error)
437 {
438         CamelLocalStore *local_store;
439         GHashTable *visited;
440 #ifndef G_OS_WIN32
441         struct _inode *inode;
442 #endif
443         gchar *path, *subdir;
444         CamelFolderInfo *fi;
445         gchar *basename;
446         struct stat st;
447
448         if (top == NULL)
449                 top = "";
450
451         local_store = CAMEL_LOCAL_STORE (store);
452         path = camel_local_store_get_full_path (local_store, top);
453
454         if (*top == '\0') {
455                 /* requesting root dir scan */
456                 if (g_stat (path, &st) == -1 || !S_ISDIR (st.st_mode)) {
457                         g_free (path);
458                         return NULL;
459                 }
460
461                 visited = g_hash_table_new (inode_hash, inode_equal);
462 #ifndef G_OS_WIN32
463                 inode = g_malloc0 (sizeof (*inode));
464                 inode->dnode = st.st_dev;
465                 inode->inode = st.st_ino;
466
467                 g_hash_table_insert (visited, inode, inode);
468 #endif
469                 fi = scan_dir (store, visited, NULL, path, NULL, flags, error);
470                 g_hash_table_foreach (visited, inode_free, NULL);
471                 g_hash_table_destroy (visited);
472                 g_free (path);
473
474                 return fi;
475         }
476
477         /* requesting scan of specific folder */
478         if (g_stat (path, &st) == -1 || !S_ISREG (st.st_mode)) {
479                 char *test_if_subdir = g_strdup_printf("%s.sbd", path);
480
481                 if (g_stat (test_if_subdir, &st) == -1) {
482                         g_free (path);
483                         g_free (test_if_subdir);
484                         return NULL;
485                 }
486                 g_free (test_if_subdir);
487         }
488
489         visited = g_hash_table_new (inode_hash, inode_equal);
490
491         basename = g_path_get_basename (top);
492
493         fi = camel_folder_info_new ();
494         fi->parent = NULL;
495         fi->full_name = g_strdup (top);
496         fi->display_name = basename;
497         fi->unread = -1;
498         fi->total = -1;
499
500         fill_fi (store, fi, flags);
501
502         subdir = g_strdup_printf("%s.sbd", path);
503         if (g_stat (subdir, &st) == 0) {
504                 if  (S_ISDIR (st.st_mode))
505                         fi->child = scan_dir (store, visited, fi, subdir, top, flags, error);
506         }
507
508         if (fi->child)
509                 fi->flags |= CAMEL_FOLDER_CHILDREN;
510         else
511                 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
512
513         g_free (subdir);
514
515         g_hash_table_foreach (visited, inode_free, NULL);
516         g_hash_table_destroy (visited);
517         g_free (path);
518
519         return fi;
520 }
521
522 static CamelFolderInfo *
523 mbox_store_create_folder_sync (CamelStore *store,
524                                const gchar *parent_name,
525                                const gchar *folder_name,
526                                GCancellable *cancellable,
527                                GError **error)
528 {
529         /* FIXME: this is almost an exact copy of CamelLocalStore::create_folder() except that we use
530          * different path schemes... need to find a way to share parent's code? */
531         CamelLocalSettings *local_settings;
532         CamelLocalStore *local_store;
533         CamelFolderInfo *info = NULL;
534         CamelSettings *settings;
535         CamelService *service;
536         gchar *root_path = NULL;
537         gchar *name = NULL;
538         gchar *path = NULL;
539         gchar *dir;
540         CamelFolder *folder;
541         struct stat st;
542
543         service = CAMEL_SERVICE (store);
544
545         settings = camel_service_ref_settings (service);
546
547         local_settings = CAMEL_LOCAL_SETTINGS (settings);
548         root_path = camel_local_settings_dup_path (local_settings);
549
550         g_object_unref (settings);
551
552         local_store = CAMEL_LOCAL_STORE (store);
553
554         if (!g_path_is_absolute (root_path)) {
555                 g_set_error (
556                         error, CAMEL_STORE_ERROR,
557                         CAMEL_STORE_ERROR_NO_FOLDER,
558                         _("Store root %s is not an absolute path"),
559                         root_path);
560                 goto exit;
561         }
562
563         if (folder_name[0] == '.' || ignore_file (folder_name, TRUE)) {
564                 g_set_error (
565                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
566                         _("Cannot create a folder by this name."));
567                 goto exit;
568         }
569
570         if (parent_name && *parent_name)
571                 name = g_strdup_printf ("%s/%s", parent_name, folder_name);
572         else
573                 name = g_strdup (folder_name);
574
575         path = camel_local_store_get_full_path (local_store, name);
576
577         dir = g_path_get_dirname (path);
578         if (g_mkdir_with_parents (dir, 0777) == -1 && errno != EEXIST) {
579                 g_set_error (
580                         error, G_IO_ERROR,
581                         g_io_error_from_errno (errno),
582                         _("Cannot create directory '%s': %s."),
583                         dir, g_strerror (errno));
584                 g_free (dir);
585                 goto exit;
586         }
587
588         g_free (dir);
589
590         if (g_stat (path, &st) == 0 || errno != ENOENT) {
591                 g_set_error (
592                         error, G_IO_ERROR,
593                         g_io_error_from_errno (errno),
594                         _("Cannot create folder: %s: %s"),
595                         path, errno ? g_strerror (errno) :
596                         _("Folder already exists"));
597                 goto exit;
598         }
599
600         folder = CAMEL_STORE_GET_CLASS (store)->get_folder_sync (
601                 store, name, CAMEL_STORE_FOLDER_CREATE, cancellable, error);
602         if (folder) {
603                 g_object_unref (folder);
604                 info = CAMEL_STORE_GET_CLASS (store)->get_folder_info_sync (
605                         store, name, 0, cancellable, error);
606         }
607
608 exit:
609         g_free (root_path);
610         g_free (name);
611         g_free (path);
612
613         return info;
614 }
615
616 static gboolean
617 mbox_store_delete_folder_sync (CamelStore *store,
618                                const gchar *folder_name,
619                                GCancellable *cancellable,
620                                GError **error)
621 {
622         CamelLocalStore *local_store;
623         CamelFolderInfo *fi;
624         CamelFolder *lf;
625         gchar *name, *path;
626         struct stat st;
627
628         local_store = CAMEL_LOCAL_STORE (store);
629         name = camel_local_store_get_full_path (local_store, folder_name);
630         path = g_strdup_printf("%s.sbd", name);
631
632         if (g_rmdir (path) == -1 && errno != ENOENT) {
633                 g_set_error (
634                         error, G_IO_ERROR,
635                         g_io_error_from_errno (errno),
636                         _("Could not delete folder '%s':\n%s"),
637                         folder_name, g_strerror (errno));
638                 g_free (path);
639                 g_free (name);
640                 return FALSE;
641         }
642
643         g_free (path);
644
645         if (g_stat (name, &st) == -1) {
646                 g_set_error (
647                         error, G_IO_ERROR,
648                         g_io_error_from_errno (errno),
649                         _("Could not delete folder '%s':\n%s"),
650                         folder_name, g_strerror (errno));
651                 g_free (name);
652                 return FALSE;
653         }
654
655         if (!S_ISREG (st.st_mode)) {
656                 g_set_error (
657                         error, CAMEL_STORE_ERROR,
658                         CAMEL_STORE_ERROR_NO_FOLDER,
659                         _("'%s' is not a regular file."), name);
660                 g_free (name);
661                 return FALSE;
662         }
663
664         if (st.st_size != 0) {
665                 g_set_error (
666                         error, CAMEL_FOLDER_ERROR,
667                         CAMEL_FOLDER_ERROR_NON_EMPTY,
668                         _("Folder '%s' is not empty. Not deleted."),
669                         folder_name);
670                 g_free (name);
671                 return FALSE;
672         }
673
674         if (g_unlink (name) == -1 && errno != ENOENT) {
675                 g_set_error (
676                         error, G_IO_ERROR,
677                         g_io_error_from_errno (errno),
678                         _("Could not delete folder '%s':\n%s"),
679                         name, g_strerror (errno));
680                 g_free (name);
681                 return FALSE;
682         }
683
684         /* FIXME: we have to do our own meta cleanup here rather than
685          * calling our parent class' delete_folder() method since our
686          * naming convention is different. Need to find a way for
687          * CamelLocalStore to be able to construct the folder & meta
688          * paths itself */
689         path = camel_local_store_get_meta_path (
690                 local_store, folder_name, ".ev-summary");
691         if (g_unlink (path) == -1 && errno != ENOENT) {
692                 g_set_error (
693                         error, G_IO_ERROR,
694                         g_io_error_from_errno (errno),
695                         _("Could not delete folder summary file '%s': %s"),
696                         path, g_strerror (errno));
697                 g_free (path);
698                 g_free (name);
699                 return FALSE;
700         }
701
702         g_free (path);
703
704         path = camel_local_store_get_meta_path (
705                 local_store, folder_name, ".ev-summary-meta");
706         if (g_unlink (path) == -1 && errno != ENOENT) {
707                 g_set_error (
708                         error, G_IO_ERROR,
709                         g_io_error_from_errno (errno),
710                         _("Could not delete folder summary file '%s': %s"),
711                         path, g_strerror (errno));
712                 g_free (path);
713                 g_free (name);
714                 return FALSE;
715         }
716
717         g_free (path);
718
719         path = camel_local_store_get_meta_path (
720                 local_store, folder_name, ".ibex");
721         if (camel_text_index_remove (path) == -1 && errno != ENOENT) {
722                 g_set_error (
723                         error, G_IO_ERROR,
724                         g_io_error_from_errno (errno),
725                         _("Could not delete folder index file '%s': %s"),
726                         path, g_strerror (errno));
727                 g_free (path);
728                 g_free (name);
729                 return FALSE;
730         }
731
732         g_free (path);
733
734         path = NULL;
735         if ((lf = camel_store_get_folder_sync (store, folder_name, 0, cancellable, NULL))) {
736                 CamelObject *object = CAMEL_OBJECT (lf);
737                 const gchar *state_filename;
738
739                 state_filename = camel_object_get_state_filename (object);
740                 path = g_strdup (state_filename);
741
742                 camel_object_set_state_filename (object, NULL);
743
744                 g_object_unref (lf);
745         }
746
747         if (path == NULL)
748                 path = camel_local_store_get_meta_path (
749                         local_store, folder_name, ".cmeta");
750
751         if (g_unlink (path) == -1 && errno != ENOENT) {
752                 g_set_error (
753                         error, G_IO_ERROR,
754                         g_io_error_from_errno (errno),
755                         _("Could not delete folder meta file '%s': %s"),
756                         path, g_strerror (errno));
757
758                 g_free (path);
759                 g_free (name);
760                 return FALSE;
761         }
762
763         g_free (path);
764         g_free (name);
765
766         fi = camel_folder_info_new ();
767         fi->full_name = g_strdup (folder_name);
768         fi->display_name = g_path_get_basename (folder_name);
769         fi->unread = -1;
770
771         camel_store_folder_deleted (store, fi);
772         camel_folder_info_free (fi);
773
774         return TRUE;
775 }
776
777 static gboolean
778 mbox_store_rename_folder_sync (CamelStore *store,
779                                const gchar *old,
780                                const gchar *new,
781                                GCancellable *cancellable,
782                                GError **error)
783 {
784         CamelLocalStore *local_store;
785         CamelLocalFolder *folder = NULL;
786         gchar *oldibex, *newibex, *newdir;
787         gint errnosav;
788
789         if (new[0] == '.' || ignore_file (new, TRUE)) {
790                 g_set_error (
791                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
792                         _("The new folder name is illegal."));
793                 return FALSE;
794         }
795
796         /* try to rollback failures, has obvious races */
797
798         local_store = CAMEL_LOCAL_STORE (store);
799         oldibex = camel_local_store_get_meta_path (local_store, old, ".ibex");
800         newibex = camel_local_store_get_meta_path (local_store, new, ".ibex");
801
802         newdir = g_path_get_dirname (newibex);
803         if (g_mkdir_with_parents (newdir, 0700) == -1) {
804                 if (errno != EEXIST) {
805                         g_set_error (
806                                 error, G_IO_ERROR,
807                                 g_io_error_from_errno (errno),
808                                 _("Could not rename '%s': '%s': %s"),
809                                 old, new, g_strerror (errno));
810                         g_free (oldibex);
811                         g_free (newibex);
812                         g_free (newdir);
813
814                         return FALSE;
815                 }
816
817                 g_free (newdir);
818                 newdir = NULL;
819         }
820
821         folder = camel_object_bag_get (store->folders, old);
822         if (folder && folder->index) {
823                 if (camel_index_rename (folder->index, newibex) == -1 && errno != ENOENT) {
824                         errnosav = errno;
825                         goto ibex_failed;
826                 }
827         } else {
828                 /* TODO: camel_text_index_rename should find out if we have an active index itself? */
829                 if (camel_text_index_rename (oldibex, newibex) == -1 && errno != ENOENT) {
830                         errnosav = errno;
831                         goto ibex_failed;
832                 }
833         }
834
835         if (xrename(store, old, new, ".ev-summary", TRUE) == -1) {
836                 errnosav = errno;
837                 goto summary_failed;
838         }
839
840         if (xrename(store, old, new, ".ev-summary-meta", TRUE) == -1) {
841                 errnosav = errno;
842                 goto summary_failed;
843         }
844
845         if (xrename(store, old, new, ".cmeta", TRUE) == -1) {
846                 errnosav = errno;
847                 goto cmeta_failed;
848         }
849
850         if (xrename(store, old, new, ".sbd", TRUE) == -1) {
851                 errnosav = errno;
852                 goto subdir_failed;
853         }
854
855         if (xrename (store, old, new, NULL, FALSE) == -1) {
856                 errnosav = errno;
857                 goto base_failed;
858         }
859
860         g_free (oldibex);
861         g_free (newibex);
862
863         if (folder)
864                 g_object_unref (folder);
865
866         return TRUE;
867
868 base_failed:
869         xrename(store, new, old, ".sbd", TRUE);
870 subdir_failed:
871         xrename(store, new, old, ".cmeta", TRUE);
872 cmeta_failed:
873         xrename(store, new, old, ".ev-summary", TRUE);
874         xrename(store, new, old, ".ev-summary-meta", TRUE);
875 summary_failed:
876         if (folder) {
877                 if (folder->index)
878                         camel_index_rename (folder->index, oldibex);
879         } else
880                 camel_text_index_rename (newibex, oldibex);
881 ibex_failed:
882         if (newdir) {
883                 /* newdir is only non-NULL if we needed to mkdir */
884                 g_rmdir (newdir);
885                 g_free (newdir);
886         }
887
888         g_set_error (
889                 error, G_IO_ERROR,
890                 g_io_error_from_errno (errnosav),
891                 _("Could not rename '%s' to %s: %s"),
892                 old, new, g_strerror (errnosav));
893
894         g_free (newibex);
895         g_free (oldibex);
896
897         if (folder)
898                 g_object_unref (folder);
899
900         return FALSE;
901 }
902
903 static gchar *
904 mbox_store_get_full_path (CamelLocalStore *ls,
905                           const gchar *full_name)
906 {
907         CamelLocalSettings *local_settings;
908         CamelSettings *settings;
909         CamelService *service;
910         GString *full_path;
911         gchar *root_path;
912         const gchar *cp;
913
914         service = CAMEL_SERVICE (ls);
915
916         settings = camel_service_ref_settings (service);
917
918         local_settings = CAMEL_LOCAL_SETTINGS (settings);
919         root_path = camel_local_settings_dup_path (local_settings);
920
921         g_object_unref (settings);
922
923         g_return_val_if_fail (root_path != NULL, NULL);
924
925         full_path = g_string_new (root_path);
926
927         /* Root path may or may not have a trailing separator. */
928         if (!g_str_has_suffix (root_path, G_DIR_SEPARATOR_S))
929                 g_string_append_c (full_path, G_DIR_SEPARATOR);
930
931         cp = full_name;
932         while (*cp != '\0') {
933                 if (G_IS_DIR_SEPARATOR (*cp)) {
934                         g_string_append (full_path, ".sbd");
935                         g_string_append_c (full_path, *cp++);
936
937                         /* Skip extranaeous separators. */
938                         while (G_IS_DIR_SEPARATOR (*cp))
939                                 cp++;
940                 } else {
941                         g_string_append_c (full_path, *cp++);
942                 }
943         }
944
945         g_free (root_path);
946
947         return g_string_free (full_path, FALSE);
948 }
949
950 static gchar *
951 mbox_store_get_meta_path (CamelLocalStore *ls,
952                           const gchar *full_name,
953                           const gchar *ext)
954 {
955 /*#define USE_HIDDEN_META_FILES*/
956 #ifdef USE_HIDDEN_META_FILES
957         gchar *name, *slash;
958
959         name = g_alloca (strlen (full_name) + strlen (ext) + 2);
960         if ((slash = strrchr (full_name, '/')))
961                 sprintf (name, "%.*s.%s%s", slash - full_name + 1, full_name, slash + 1, ext);
962         else
963                 sprintf (name, ".%s%s", full_name, ext);
964
965         return mbox_store_get_full_path (ls, name);
966 #else
967         gchar *full_path, *path;
968
969         full_path = mbox_store_get_full_path (ls, full_name);
970         path = g_strdup_printf ("%s%s", full_path, ext);
971         g_free (full_path);
972
973         return path;
974 #endif
975 }
976
977 static void
978 camel_mbox_store_class_init (CamelMboxStoreClass *class)
979 {
980         CamelStoreClass *store_class;
981         CamelLocalStoreClass *local_store_class;
982
983         store_class = CAMEL_STORE_CLASS (class);
984         store_class->free_folder_info = camel_store_free_folder_info_full;
985         store_class->get_folder_sync = mbox_store_get_folder_sync;
986         store_class->get_folder_info_sync = mbox_store_get_folder_info_sync;
987         store_class->create_folder_sync = mbox_store_create_folder_sync;
988         store_class->delete_folder_sync = mbox_store_delete_folder_sync;
989         store_class->rename_folder_sync = mbox_store_rename_folder_sync;
990
991         local_store_class = CAMEL_LOCAL_STORE_CLASS (class);
992         local_store_class->get_full_path = mbox_store_get_full_path;
993         local_store_class->get_meta_path = mbox_store_get_meta_path;
994 }
995
996 static void
997 camel_mbox_store_init (CamelMboxStore *mbox_store)
998 {
999 }
1000