diff options
Diffstat (limited to 'camel/providers/local/camel-spool-store.c')
-rw-r--r-- | camel/providers/local/camel-spool-store.c | 299 |
1 files changed, 251 insertions, 48 deletions
diff --git a/camel/providers/local/camel-spool-store.c b/camel/providers/local/camel-spool-store.c index b0a1bda602..6c26f780a0 100644 --- a/camel/providers/local/camel-spool-store.c +++ b/camel/providers/local/camel-spool-store.c @@ -28,6 +28,7 @@ #include <string.h> #include <unistd.h> #include <stdio.h> +#include <dirent.h> #include "camel-spool-store.h" #include "camel-spool-folder.h" @@ -60,7 +61,7 @@ camel_spool_store_class_init (CamelSpoolStoreClass *camel_spool_store_class) CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS (camel_spool_store_class); CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS (camel_spool_store_class); - parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_store_get_type ())); + parent_class = CAMEL_STORE_CLASS(camel_mbox_store_get_type()); /* virtual method overload */ camel_service_class->construct = construct; @@ -80,7 +81,7 @@ camel_spool_store_get_type (void) static CamelType camel_spool_store_type = CAMEL_INVALID_TYPE; if (camel_spool_store_type == CAMEL_INVALID_TYPE) { - camel_spool_store_type = camel_type_register (CAMEL_STORE_TYPE, "CamelSpoolStore", + camel_spool_store_type = camel_type_register (camel_mbox_store_get_type(), "CamelSpoolStore", sizeof (CamelSpoolStore), sizeof (CamelSpoolStoreClass), (CamelObjectClassInitFunc) camel_spool_store_class_init, @@ -110,40 +111,43 @@ construct (CamelService *service, CamelSession *session, CamelProvider *provider return; } - if (stat(service->url->path, &st) == -1 || !S_ISREG(st.st_mode)) { + if (stat(service->url->path, &st) == -1) { camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, - _("Spool `%s' does not exist or is not a regular file"), - service->url->path); + _("Spool `%s' cannot be opened: %s"), + service->url->path, strerror(errno)); return; } -} - -const char * -camel_spool_store_get_toplevel_dir (CamelSpoolStore *store) -{ - CamelURL *url = CAMEL_SERVICE (store)->url; - g_assert (url != NULL); - return url->path; + if (S_ISREG(st.st_mode)) + ((CamelSpoolStore *)service)->type = CAMEL_SPOOL_STORE_MBOX; + else if (S_ISDIR(st.st_mode)) + /* we could check here for slight variations */ + ((CamelSpoolStore *)service)->type = CAMEL_SPOOL_STORE_ELM; + else { + camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, + _("Spool `%s' is not a regular file or directory"), + service->url->path); + return; + } } static CamelFolder * get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex) { - char *path = ((CamelService *)store)->url->path; CamelFolder *folder; d(printf("opening folder %s on path %s\n", folder_name, path)); - /* we only support an 'INBOX' */ - if (strcmp(folder_name, "INBOX") != 0) { + /* we only support an 'INBOX' in mbox mode */ + if (((CamelSpoolStore *)store)->type == CAMEL_SPOOL_STORE_MBOX + && strcmp(folder_name, "INBOX") != 0) { camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, _("Folder `%s/%s' does not exist."), - path, folder_name); + ((CamelService *)store)->url->path, folder_name); return NULL; } - folder = camel_spool_folder_new(store, folder_name, path, flags, ex); + folder = camel_spool_folder_new(store, folder_name, flags, ex); return folder; } @@ -151,21 +155,236 @@ get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelExce static CamelFolder * get_inbox(CamelStore *store, CamelException *ex) { - return get_folder (store, "INBOX", CAMEL_STORE_FOLDER_CREATE, ex); + if (((CamelSpoolStore *)store)->type == CAMEL_SPOOL_STORE_MBOX) + return get_folder (store, "INBOX", CAMEL_STORE_FOLDER_CREATE, ex); + else { + camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, + _("Store does not support an INBOX")); + return NULL; + } } static char * get_name (CamelService *service, gboolean brief) { if (brief) - return g_strdup (service->url->path); + return g_strdup(service->url->path); else - return g_strdup_printf (_("Spool mail file %s"), service->url->path); + return g_strdup_printf(((CamelSpoolStore *)service)->type == CAMEL_SPOOL_STORE_MBOX? + _("Spool mail file %s"):_("Spool folder tree %s"), service->url->path); +} + +/* default implementation, rename all */ +static void +rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex) +{ + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Spool folders cannot be renamed")); +} + +/* default implementation, only delete metadata */ +static void +delete_folder(CamelStore *store, const char *folder_name, CamelException *ex) +{ + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Spool folders cannot be deleted")); +} + +static void free_folder_info (CamelStore *store, CamelFolderInfo *fi) +{ + if (fi) { + g_free(fi->url); + g_free(fi->name); + g_free(fi->full_name); + g_free(fi->path); + g_free(fi); + } +} + +static CamelFolderInfo * +camel_folder_info_new(const char *url, const char *full, const char *name, int unread) +{ + CamelFolderInfo *fi; + + fi = g_malloc0(sizeof(*fi)); + fi->url = g_strdup(url); + fi->full_name = g_strdup(full); + fi->name = g_strdup(name); + fi->unread_message_count = unread; + camel_folder_info_build_path(fi, '/'); + + d(printf("Adding spoold info: '%s' '%s' '%s' '%s'\n", fi->path, fi->name, fi->full_name, fi->url)); + + return fi; +} + +/* used to find out where we've visited already */ +struct _inode { + dev_t dnode; + ino_t inode; +}; + +/* returns number of records found at or below this level */ +static int scan_dir(CamelStore *store, GHashTable *visited, char *root, const char *path, guint32 flags, CamelFolderInfo *parent, CamelFolderInfo **fip, CamelException *ex) +{ + DIR *dir; + struct dirent *d; + char *name, *uri, *tmp, *fname; + CamelFolderInfo *fi = NULL; + struct stat st; + CamelFolder *folder; + int unread; + char from[80]; + FILE *fp; + + d(printf("checking dir '%s' part '%s' for mbox content\n", root, path)); + + /* look for folders matching the right structure, recursively */ + if (path) { + name = alloca(strlen(root)+strlen(path)+2); + sprintf(name, "%s/%s", root, path); + } else + name = root; + + dir = opendir(name); + if (dir == NULL) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not scan folder `%s': %s"), + root, strerror(errno)); + g_free(name); + return -1; + } + + if (path != NULL) { + uri = g_strdup_printf("%s:%s;noselect=yes#%s", ((CamelService *)store)->url->protocol, root, path); + tmp = strrchr(path, '/'); + if (tmp == NULL) + tmp = (char *)path; + else + tmp++; + fi = camel_folder_info_new(uri, path, tmp, -1); + fi->parent = parent; + fi->sibling = *fip; + *fip = fi; + g_free(uri); + + fip = &fi->child; + parent = fi; + } + + while ( (d = readdir(dir)) ) { + if (strcmp(d->d_name, ".") == 0 + || strcmp(d->d_name, "..") == 0) + continue; + + tmp = g_strdup_printf("%s/%s", name, d->d_name); + if (stat(tmp, &st) == 0) { + if (path) + fname = g_strdup_printf("%s/%s", path, d->d_name); + else + fname = g_strdup(d->d_name); + + if (S_ISREG(st.st_mode)) { + /* first, see if we already have it open */ + CAMEL_STORE_LOCK(store, cache_lock); + folder = g_hash_table_lookup(store->folders, fname); + if (folder) + unread = camel_folder_get_unread_message_count(folder); + else + unread = -1; + CAMEL_STORE_UNLOCK(store, cache_lock); + + /* no? check its content to see if its a folder or not */ + if (folder == NULL) { + fp = fopen(tmp, "r"); + if (fp != NULL) { + if (fgets(from, sizeof(from), fp) != NULL + && strncmp(from, "From ", 5) == 0) { + folder = (CamelFolder *)1; + /* TODO: if slow mode selected, we could look up unread counts here - + but its pretty expensive */ + } + fclose(fp); + } + } + + if (folder != NULL) { + uri = g_strdup_printf("%s:%s#%s", ((CamelService *)store)->url->protocol, root, fname); + fi = camel_folder_info_new(uri, fname, d->d_name, unread); + fi->parent = parent; + fi->sibling = *fip; + *fip = fi; + g_free(uri); + } + + } else if (S_ISDIR(st.st_mode)) { + struct _inode in = { st.st_dev, st.st_ino }; + + /* see if we've visited already */ + if (g_hash_table_lookup(visited, &in) == NULL) { + struct _inode *inew = g_malloc(sizeof(*inew)); + + *inew = in; + g_hash_table_insert(visited, inew, inew); + + if (scan_dir(store, visited, root, fname, flags, parent, fip, ex) == -1) { + g_free(tmp); + g_free(fname); + closedir(dir); + return -1; + } + } + } + g_free(fname); + + } + g_free(tmp); + } + closedir(dir); + + return 0; +} + +static guint inode_hash(const void *d) +{ + const struct _inode *v = d; + + return v->inode ^ v->dnode; +} + +static gboolean inode_equal(const void *a, const void *b) +{ + const struct _inode *v1 = a, *v2 = b; + + return v1->inode == v2->inode && v1->dnode == v2->dnode; +} + +static void inode_free(void *k, void *v, void *d) +{ + g_free(k); +} + +static CamelFolderInfo * +get_folder_info_elm(CamelStore *store, const char *top, guint32 flags, CamelException *ex) +{ + CamelFolderInfo *fi = NULL; + GHashTable *visited; + + visited = g_hash_table_new(inode_hash, inode_equal); + + if (scan_dir(store, visited, ((CamelService *)store)->url->path, top, flags, NULL, &fi, ex) == -1 && fi != NULL) { + camel_store_free_folder_info_full(store, fi); + fi = NULL; + } + + g_hash_table_foreach(visited, inode_free, NULL); + g_hash_table_destroy(visited); + + return fi; } static CamelFolderInfo * -get_folder_info (CamelStore *store, const char *top, - guint32 flags, CamelException *ex) +get_folder_info_mbox(CamelStore *store, const char *top, guint32 flags, CamelException *ex) { CamelFolderInfo *fi = NULL; CamelService *service = (CamelService *)store; @@ -173,9 +392,9 @@ get_folder_info (CamelStore *store, const char *top, if (top == NULL || strcmp(top, "INBOX") == 0) { fi = g_malloc0(sizeof(*fi)); - fi->full_name = "INBOX"; - fi->name = "INBOX"; - fi->url = g_strdup_printf("spool:%s#%s", service->url->path, fi->name); + fi->full_name = g_strdup("INBOX"); + fi->name = g_strdup("INBOX"); + fi->url = g_strdup_printf("%s:%s#%s", service->url->protocol, service->url->path, fi->name); CAMEL_STORE_LOCK(store, cache_lock); folder = g_hash_table_lookup(store->folders, fi->full_name); @@ -191,27 +410,11 @@ get_folder_info (CamelStore *store, const char *top, return fi; } -static void free_folder_info (CamelStore *store, CamelFolderInfo *fi) -{ - if (fi) { - g_free(fi->url); - g_free(fi); - } -} - - -/* default implementation, rename all */ -static void -rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex) -{ - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Spool folders cannot be renamed")); -} - -/* default implementation, only delete metadata */ -static void -delete_folder(CamelStore *store, const char *folder_name, CamelException *ex) +static CamelFolderInfo * +get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Spool folders cannot be deleted")); + if (((CamelSpoolStore *)store)->type == CAMEL_SPOOL_STORE_MBOX) + return get_folder_info_mbox(store, top, flags, ex); + else + return get_folder_info_elm(store, top, flags, ex); } |