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