1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Authors: Michael Zucchi <notzed@ximian.com>
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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.
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.
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
33 #include <sys/types.h>
35 #include <glib/gstdio.h>
36 #include <glib/gi18n-lib.h>
38 #include "camel-spool-folder.h"
39 #include "camel-spool-settings.h"
40 #include "camel-spool-store.h"
42 #define CAMEL_SPOOL_STORE_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE \
44 ((obj), CAMEL_TYPE_SPOOL_STORE, CamelSpoolStorePrivate))
48 typedef enum _camel_spool_store_t {
49 CAMEL_SPOOL_STORE_INVALID,
50 CAMEL_SPOOL_STORE_MBOX, /* a single mbox */
51 CAMEL_SPOOL_STORE_ELM /* elm/pine/etc tree of mbox files in folders */
52 } camel_spool_store_t;
54 struct _CamelSpoolStorePrivate {
55 gint placeholder; /* for future expansion */
61 CAMEL_TYPE_MBOX_STORE)
63 static camel_spool_store_t
64 spool_store_get_type (CamelSpoolStore *spool_store,
67 CamelLocalSettings *local_settings;
68 CamelSettings *settings;
69 CamelService *service;
70 camel_spool_store_t type;
74 service = CAMEL_SERVICE (spool_store);
76 settings = camel_service_ref_settings (service);
78 local_settings = CAMEL_LOCAL_SETTINGS (settings);
79 path = camel_local_settings_dup_path (local_settings);
81 g_object_unref (settings);
83 /* Check the path for validity while we have the opportunity. */
85 if (path == NULL || *path != '/') {
87 error, CAMEL_STORE_ERROR,
88 CAMEL_STORE_ERROR_NO_FOLDER,
89 _("Store root %s is not an absolute path"),
90 (path != NULL) ? path : "(null)");
91 type = CAMEL_SPOOL_STORE_INVALID;
93 } else if (g_stat (path, &st) == -1) {
96 g_io_error_from_errno (errno),
97 _("Spool '%s' cannot be opened: %s"),
98 path, g_strerror (errno));
99 type = CAMEL_SPOOL_STORE_INVALID;
101 } else if (S_ISREG (st.st_mode)) {
102 type = CAMEL_SPOOL_STORE_MBOX;
104 } else if (S_ISDIR (st.st_mode)) {
105 type = CAMEL_SPOOL_STORE_ELM;
109 error, CAMEL_STORE_ERROR,
110 CAMEL_STORE_ERROR_NO_FOLDER,
111 _("Spool '%s' is not a regular file or directory"),
113 type = CAMEL_SPOOL_STORE_INVALID;
121 /* partially copied from mbox */
123 spool_fill_fi (CamelStore *store,
126 GCancellable *cancellable)
132 folder = camel_object_bag_peek (store->folders, fi->full_name);
134 if ((flags & CAMEL_STORE_FOLDER_INFO_FAST) == 0)
135 camel_folder_refresh_info_sync (folder, cancellable, NULL);
136 fi->unread = camel_folder_get_unread_message_count (folder);
137 fi->total = camel_folder_get_message_count (folder);
138 g_object_unref (folder);
142 static CamelFolderInfo *
143 spool_new_fi (CamelStore *store,
144 CamelFolderInfo *parent,
145 CamelFolderInfo **fip,
152 name = strrchr (full, '/');
158 fi = camel_folder_info_new ();
159 fi->full_name = g_strdup (full);
160 fi->display_name = g_strdup (name);
172 /* used to find out where we've visited already */
178 /* returns number of records found at or below this level */
180 scan_dir (CamelStore *store,
185 CamelFolderInfo *parent,
186 CamelFolderInfo **fip,
187 GCancellable *cancellable,
192 gchar *name, *tmp, *fname;
194 CamelFolderInfo *fi = NULL;
200 d (printf ("checking dir '%s' part '%s' for mbox content\n", root, path));
202 /* look for folders matching the right structure, recursively */
204 name_len = strlen (root) + strlen (path) + 2;
205 name = alloca (name_len);
206 g_snprintf (name, name_len, "%s/%s", root, path);
208 name = (gchar *) root; /* XXX casting away const */
210 if (g_stat (name, &st) == -1) {
213 g_io_error_from_errno (errno),
214 _("Could not scan folder '%s': %s"),
215 name, g_strerror (errno));
216 } else if (S_ISREG (st.st_mode)) {
217 /* incase we start scanning from a file. messy duplication :-/ */
220 store, parent, fip, path,
221 CAMEL_FOLDER_NOINFERIORS |
222 CAMEL_FOLDER_NOCHILDREN);
223 spool_fill_fi (store, fi, flags, cancellable);
228 dir = opendir (name);
232 g_io_error_from_errno (errno),
233 _("Could not scan folder '%s': %s"),
234 name, g_strerror (errno));
240 store, parent, fip, path,
241 CAMEL_FOLDER_NOSELECT);
246 while ((d = readdir (dir))) {
247 if (strcmp (d->d_name, ".") == 0
248 || strcmp (d->d_name, "..") == 0)
251 tmp = g_strdup_printf ("%s/%s", name, d->d_name);
252 if (g_stat (tmp, &st) == 0) {
254 fname = g_strdup_printf (
255 "%s/%s", path, d->d_name);
257 fname = g_strdup (d->d_name);
259 if (S_ISREG (st.st_mode)) {
260 gint isfolder = FALSE;
262 /* first, see if we already have it open */
263 folder = camel_object_bag_peek (
264 store->folders, fname);
265 if (folder == NULL) {
266 fp = fopen (tmp, "r");
268 isfolder = (st.st_size == 0
269 || (fgets (from, sizeof (from), fp) != NULL
270 && strncmp (from, "From ", 5) == 0));
275 if (folder != NULL || isfolder) {
277 store, parent, fip, fname,
278 CAMEL_FOLDER_NOINFERIORS |
279 CAMEL_FOLDER_NOCHILDREN);
281 store, fi, flags, cancellable);
284 g_object_unref (folder);
286 } else if (S_ISDIR (st.st_mode)) {
287 struct _inode in = { st.st_dev, st.st_ino };
289 /* see if we've visited already */
290 if (g_hash_table_lookup (visited, &in) == NULL) {
291 struct _inode *inew = g_malloc (sizeof (*inew));
294 g_hash_table_insert (visited, inew, inew);
296 if (scan_dir (store, visited, root, fname, flags, parent, fip, cancellable, error) == -1) {
315 inode_hash (gconstpointer d)
317 const struct _inode *v = d;
319 return v->inode ^ v->dnode;
323 inode_equal (gconstpointer a,
326 const struct _inode *v1 = a, *v2 = b;
328 return v1->inode == v2->inode && v1->dnode == v2->dnode;
332 inode_free (gpointer k,
339 static CamelFolderInfo *
340 get_folder_info_elm (CamelStore *store,
343 GCancellable *cancellable,
346 CamelLocalSettings *local_settings;
347 CamelSettings *settings;
348 CamelService *service;
349 CamelFolderInfo *fi = NULL;
353 service = CAMEL_SERVICE (store);
355 settings = camel_service_ref_settings (service);
357 local_settings = CAMEL_LOCAL_SETTINGS (settings);
358 path = camel_local_settings_dup_path (local_settings);
360 g_object_unref (settings);
362 visited = g_hash_table_new (inode_hash, inode_equal);
365 store, visited, path, top, flags,
366 NULL, &fi, cancellable, error) == -1 && fi != NULL) {
367 camel_folder_info_free (fi);
371 g_hash_table_foreach (visited, inode_free, NULL);
372 g_hash_table_destroy (visited);
379 static CamelFolderInfo *
380 get_folder_info_mbox (CamelStore *store,
383 GCancellable *cancellable,
386 CamelFolderInfo *fi = NULL, *fip = NULL;
388 if (top == NULL || strcmp (top, "INBOX") == 0) {
390 store, NULL, &fip, "INBOX",
391 CAMEL_FOLDER_NOINFERIORS |
392 CAMEL_FOLDER_NOCHILDREN |
393 CAMEL_FOLDER_SYSTEM);
394 g_free (fi->display_name);
395 fi->display_name = g_strdup (_("Inbox"));
396 spool_fill_fi (store, fi, flags, cancellable);
403 spool_store_get_name (CamelService *service,
406 CamelLocalSettings *local_settings;
407 CamelSpoolStore *spool_store;
408 CamelSettings *settings;
412 spool_store = CAMEL_SPOOL_STORE (service);
414 settings = camel_service_ref_settings (service);
416 local_settings = CAMEL_LOCAL_SETTINGS (settings);
417 path = camel_local_settings_dup_path (local_settings);
419 g_object_unref (settings);
424 switch (spool_store_get_type (spool_store, NULL)) {
425 case CAMEL_SPOOL_STORE_MBOX:
426 name = g_strdup_printf (
427 _("Spool mail file %s"), path);
429 case CAMEL_SPOOL_STORE_ELM:
430 name = g_strdup_printf (
431 _("Spool folder tree %s"), path);
434 name = g_strdup (_("Invalid spool"));
444 spool_store_get_folder_sync (CamelStore *store,
445 const gchar *folder_name,
446 CamelStoreGetFolderFlags flags,
447 GCancellable *cancellable,
450 CamelLocalSettings *local_settings;
451 CamelSpoolStore *spool_store;
452 CamelSettings *settings;
453 CamelService *service;
454 CamelFolder *folder = NULL;
455 camel_spool_store_t type;
460 d (printf ("opening folder %s on path %s\n", folder_name, path));
462 spool_store = CAMEL_SPOOL_STORE (store);
463 type = spool_store_get_type (spool_store, error);
465 if (type == CAMEL_SPOOL_STORE_INVALID)
468 service = CAMEL_SERVICE (store);
470 settings = camel_service_ref_settings (service);
472 local_settings = CAMEL_LOCAL_SETTINGS (settings);
473 path = camel_local_settings_dup_path (local_settings);
475 g_object_unref (settings);
477 /* we only support an 'INBOX' in mbox mode */
478 if (type == CAMEL_SPOOL_STORE_MBOX) {
479 if (strcmp (folder_name, "INBOX") != 0) {
481 error, CAMEL_STORE_ERROR,
482 CAMEL_STORE_ERROR_NO_FOLDER,
483 _("Folder '%s/%s' does not exist."),
486 folder = camel_spool_folder_new (
487 store, folder_name, flags, cancellable, error);
490 name = g_build_filename (path, folder_name, NULL);
491 if (g_stat (name, &st) == -1) {
492 if (errno != ENOENT) {
495 g_io_error_from_errno (errno),
496 _("Could not open folder '%s':\n%s"),
497 folder_name, g_strerror (errno));
498 } else if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) {
500 error, CAMEL_STORE_ERROR,
501 CAMEL_STORE_ERROR_NO_FOLDER,
502 _("Folder '%s' does not exist."),
505 gint fd = creat (name, 0600);
509 g_io_error_from_errno (errno),
510 _("Could not create folder '%s':\n%s"),
511 folder_name, g_strerror (errno));
514 folder = camel_spool_folder_new (
515 store, folder_name, flags,
519 } else if (!S_ISREG (st.st_mode)) {
521 error, CAMEL_STORE_ERROR,
522 CAMEL_STORE_ERROR_NO_FOLDER,
523 _("'%s' is not a mailbox file."), name);
525 folder = camel_spool_folder_new (
526 store, folder_name, flags, cancellable, error);
536 static CamelFolderInfo *
537 spool_store_get_folder_info_sync (CamelStore *store,
539 CamelStoreGetFolderInfoFlags flags,
540 GCancellable *cancellable,
543 CamelSpoolStore *spool_store;
544 CamelFolderInfo *folder_info = NULL;
546 spool_store = CAMEL_SPOOL_STORE (store);
548 switch (spool_store_get_type (spool_store, error)) {
549 case CAMEL_SPOOL_STORE_MBOX:
550 folder_info = get_folder_info_mbox (
551 store, top, flags, cancellable, error);
554 case CAMEL_SPOOL_STORE_ELM:
555 folder_info = get_folder_info_elm (
556 store, top, flags, cancellable, error);
567 spool_store_get_inbox_folder_sync (CamelStore *store,
568 GCancellable *cancellable,
571 CamelSpoolStore *spool_store;
572 CamelFolder *folder = NULL;
574 spool_store = CAMEL_SPOOL_STORE (store);
576 switch (spool_store_get_type (spool_store, error)) {
577 case CAMEL_SPOOL_STORE_MBOX:
578 folder = spool_store_get_folder_sync (
579 store, "INBOX", CAMEL_STORE_FOLDER_CREATE,
583 case CAMEL_SPOOL_STORE_ELM:
585 error, CAMEL_STORE_ERROR,
586 CAMEL_STORE_ERROR_NO_FOLDER,
587 _("Store does not support an INBOX"));
597 /* default implementation, only delete metadata */
599 spool_store_delete_folder_sync (CamelStore *store,
600 const gchar *folder_name,
601 GCancellable *cancellable,
605 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
606 _("Spool folders cannot be deleted"));
611 /* default implementation, rename all */
613 spool_store_rename_folder_sync (CamelStore *store,
616 GCancellable *cancellable,
620 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
621 _("Spool folders cannot be renamed"));
627 spool_store_get_full_path (CamelLocalStore *local_store,
628 const gchar *full_name)
630 CamelLocalSettings *local_settings;
631 CamelSpoolStore *spool_store;
632 CamelSettings *settings;
633 CamelService *service;
637 service = CAMEL_SERVICE (local_store);
639 settings = camel_service_ref_settings (service);
641 local_settings = CAMEL_LOCAL_SETTINGS (settings);
642 path = camel_local_settings_dup_path (local_settings);
644 g_object_unref (settings);
646 spool_store = CAMEL_SPOOL_STORE (local_store);
648 switch (spool_store_get_type (spool_store, NULL)) {
649 case CAMEL_SPOOL_STORE_MBOX:
650 full_path = g_strdup (path);
653 case CAMEL_SPOOL_STORE_ELM:
654 full_path = g_build_filename (path, full_name, NULL);
668 spool_store_get_meta_path (CamelLocalStore *ls,
669 const gchar *full_name,
672 CamelService *service;
673 const gchar *user_data_dir;
676 service = CAMEL_SERVICE (ls);
677 user_data_dir = camel_service_get_user_data_dir (service);
679 key = camel_file_util_safe_filename (full_name);
680 path = g_strdup_printf ("%s/%s%s", user_data_dir, key, ext);
687 camel_spool_store_class_init (CamelSpoolStoreClass *class)
689 CamelServiceClass *service_class;
690 CamelStoreClass *store_class;
691 CamelLocalStoreClass *local_store_class;
693 g_type_class_add_private (class, sizeof (CamelSpoolStorePrivate));
695 service_class = CAMEL_SERVICE_CLASS (class);
696 service_class->settings_type = CAMEL_TYPE_SPOOL_SETTINGS;
697 service_class->get_name = spool_store_get_name;
699 store_class = CAMEL_STORE_CLASS (class);
700 store_class->get_folder_sync = spool_store_get_folder_sync;
701 store_class->get_folder_info_sync = spool_store_get_folder_info_sync;
702 store_class->get_inbox_folder_sync = spool_store_get_inbox_folder_sync;
703 store_class->delete_folder_sync = spool_store_delete_folder_sync;
704 store_class->rename_folder_sync = spool_store_rename_folder_sync;
706 local_store_class = CAMEL_LOCAL_STORE_CLASS (class);
707 local_store_class->get_full_path = spool_store_get_full_path;
708 local_store_class->get_meta_path = spool_store_get_meta_path;
712 camel_spool_store_init (CamelSpoolStore *spool_store)
714 spool_store->priv = CAMEL_SPOOL_STORE_GET_PRIVATE (spool_store);