From 1ed7208ad8a498d2c8a5014ec60c25c01a660e55 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 12 Sep 2003 18:43:04 +0000 Subject: Implements CamelLocalFolder::get_full_path() (publicly namespaced so that 2003-09-12 Jeffrey Stedfast * providers/local/camel-mbox-folder.c (camel_mbox_folder_get_full_path): Implements CamelLocalFolder::get_full_path() (publicly namespaced so that CamelMboxStore can re-use them). (camel_mbox_folder_get_meta_path): Same. * providers/local/camel-mbox-store.c (get_folder): Changed the way the path is constructed, since we now handle subdirectories and stuff. (delete_folder): Try deleting the Folder.sbd directory. We also need to manage our own meta files since CamelLocalStore's impl constructs paths differently than what we need. (create_folder): Implemented. (rename_folder): Implemented. (scan_dir): Scan an mbox tree (get_folder_info): Implemented using scan_dir(). * providers/local/camel-local-store.c (delete_folder): Set fi->url to the correct value, meaning we need to prefix it with the protocol and the folder_name is not actually part of the path, it is a separate component to the url. * providers/local/camel-local-folder.c (camel_local_folder_construct): Use the new class virtual method to construct the full folder path and all the meta files. (local_get_full_path): Implemented default get_full_path method. (local_get_meta_path): Implemented default get_meta_path method. svn path=/trunk/; revision=22557 --- camel/ChangeLog | 30 ++ camel/providers/local/camel-local-folder.c | 33 +- camel/providers/local/camel-local-folder.h | 6 +- camel/providers/local/camel-local-store.c | 11 +- camel/providers/local/camel-mbox-folder.c | 69 +++- camel/providers/local/camel-mbox-store.c | 598 +++++++++++++++++++++++++++-- 6 files changed, 696 insertions(+), 51 deletions(-) (limited to 'camel') diff --git a/camel/ChangeLog b/camel/ChangeLog index b1a79215f6..66a7e8f731 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,33 @@ +2003-09-12 Jeffrey Stedfast + + * providers/local/camel-mbox-folder.c + (camel_mbox_folder_get_full_path): Implements + CamelLocalFolder::get_full_path() (publicly namespaced so that + CamelMboxStore can re-use them). + (camel_mbox_folder_get_meta_path): Same. + + * providers/local/camel-mbox-store.c (get_folder): Changed the way + the path is constructed, since we now handle subdirectories and + stuff. + (delete_folder): Try deleting the Folder.sbd directory. We also + need to manage our own meta files since CamelLocalStore's impl + constructs paths differently than what we need. + (create_folder): Implemented. + (rename_folder): Implemented. + (scan_dir): Scan an mbox tree + (get_folder_info): Implemented using scan_dir(). + + * providers/local/camel-local-store.c (delete_folder): Set fi->url + to the correct value, meaning we need to prefix it with the + protocol and the folder_name is not actually part of the path, it + is a separate component to the url. + + * providers/local/camel-local-folder.c + (camel_local_folder_construct): Use the new class virtual method + to construct the full folder path and all the meta files. + (local_get_full_path): Implemented default get_full_path method. + (local_get_meta_path): Implemented default get_meta_path method. + 2003-09-11 Dan Winship * Makefile.am (noinst_LTLIBRARIES): Remove libcamel-static.la diff --git a/camel/providers/local/camel-local-folder.c b/camel/providers/local/camel-local-folder.c index e0b3c7b55a..608954af94 100644 --- a/camel/providers/local/camel-local-folder.c +++ b/camel/providers/local/camel-local-folder.c @@ -73,6 +73,9 @@ static int local_setv(CamelObject *object, CamelException *ex, CamelArgV *args); static int local_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); static void local_unlock(CamelLocalFolder *lf); +static char *local_get_full_path (const char *toplevel_dir, const char *full_name); +static char *local_get_meta_path (const char *toplevel_dir, const char *full_name, const char *ext); + static void local_refresh_info(CamelFolder *folder, CamelException *ex); static void local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex); @@ -110,6 +113,9 @@ camel_local_folder_class_init(CamelLocalFolderClass * camel_local_folder_class) camel_folder_class->delete = local_delete; camel_folder_class->rename = local_rename; + camel_local_folder_class->get_full_path = local_get_full_path; + camel_local_folder_class->get_meta_path = local_get_meta_path; + camel_local_folder_class->lock = local_lock; camel_local_folder_class->unlock = local_unlock; } @@ -236,16 +242,16 @@ camel_local_folder_construct(CamelLocalFolder *lf, CamelStore *parent_store, con /* not really sure to do with these for now? */ lf->summary_path = g_strdup_printf("%s.ev-summary", tmp); lf->index_path = g_strdup_printf("%s.ibex", tmp); - statepath = alloca(strlen(tmp)+7); - sprintf(statepath, "%s.cmeta", tmp); + statepath = g_strdup_printf ("%s.cmeta", tmp); } else { - lf->folder_path = g_strdup_printf("%s/%s", root_dir_path, full_name); - lf->summary_path = g_strdup_printf("%s/%s.ev-summary", root_dir_path, full_name); - lf->index_path = g_strdup_printf("%s/%s.ibex", root_dir_path, full_name); - statepath = alloca(strlen(full_name)+strlen(root_dir_path)+8); - sprintf(statepath, "%s/%s.cmeta", root_dir_path, full_name); + lf->folder_path = CLOCALF_CLASS (lf)->get_full_path (root_dir_path, full_name); + lf->summary_path = CLOCALF_CLASS (lf)->get_meta_path (root_dir_path, full_name, ".ev-summary"); + lf->index_path = CLOCALF_CLASS (lf)->get_meta_path (root_dir_path, full_name, ".ibex"); + statepath = CLOCALF_CLASS (lf)->get_meta_path (root_dir_path, full_name, ".cmeta"); } camel_object_set(lf, NULL, CAMEL_OBJECT_STATE_FILE, statepath, NULL); + g_free (statepath); + if (camel_object_state_read(lf) == -1) { /* FIXME: load defaults? */ } @@ -416,7 +422,6 @@ local_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args) static int local_setv(CamelObject *object, CamelException *ex, CamelArgV *args) { - CamelFolder *folder = (CamelFolder *)object; int i; guint32 tag; @@ -440,6 +445,18 @@ local_setv(CamelObject *object, CamelException *ex, CamelArgV *args) return ((CamelObjectClass *)parent_class)->setv(object, ex, args); } +static char * +local_get_full_path (const char *toplevel_dir, const char *full_name) +{ + return g_strdup_printf ("%s/%s", toplevel_dir, full_name); +} + +static char * +local_get_meta_path (const char *toplevel_dir, const char *full_name, const char *ext) +{ + return g_strdup_printf ("%s/%s%s", toplevel_dir, full_name, ext); +} + static int local_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex) { diff --git a/camel/providers/local/camel-local-folder.h b/camel/providers/local/camel-local-folder.h index 63a6870793..4fb5980a07 100644 --- a/camel/providers/local/camel-local-folder.h +++ b/camel/providers/local/camel-local-folder.h @@ -73,7 +73,11 @@ typedef struct { CamelFolderClass parent_class; /* Virtual methods */ - + + /* path construction, only used at init */ + char * (* get_full_path) (const char *toplevel_dir, const char *full_name); + char * (* get_meta_path) (const char *toplevel_dir, const char *full_name, const char *ext); + /* summary factory, only used at init */ CamelLocalSummary *(*create_summary)(const char *path, const char *folder, CamelIndex *index); diff --git a/camel/providers/local/camel-local-store.c b/camel/providers/local/camel-local-store.c index 18226d968c..78a3bedcb0 100644 --- a/camel/providers/local/camel-local-store.c +++ b/camel/providers/local/camel-local-store.c @@ -50,7 +50,6 @@ static void construct (CamelService *service, CamelSession *session, CamelProvid static CamelFolder *get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex); static char *get_name(CamelService *service, gboolean brief); static CamelFolder *get_inbox (CamelStore *store, CamelException *ex); -static void rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex); static CamelFolderInfo *get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex); static void delete_folder(CamelStore *store, const char *folder_name, CamelException *ex); static void rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex); @@ -207,7 +206,7 @@ get_folder_info (CamelStore *store, const char *top, * there before. */ - d(printf("-- LOCAL STRE -- get folder info: %s\n", top)); + d(printf("-- LOCAL STORE -- get folder info: %s\n", top)); return NULL; } @@ -407,12 +406,12 @@ delete_folder(CamelStore *store, const char *folder_name, CamelException *ex) fi = g_new0 (CamelFolderInfo, 1); fi->full_name = g_strdup (folder_name); fi->name = g_path_get_basename (folder_name); - fi->url = g_strdup_printf ("%s%s", CAMEL_LOCAL_STORE(store)->toplevel_dir, folder_name); + fi->url = g_strdup_printf ("%s:%s#%s", ((CamelService *) store)->url->protocol, + CAMEL_LOCAL_STORE(store)->toplevel_dir, folder_name); fi->unread_message_count = -1; camel_folder_info_build_path(fi, '/'); - - camel_object_trigger_event (CAMEL_OBJECT (store), - "folder_deleted", fi); + + camel_object_trigger_event (store, "folder_deleted", fi); camel_folder_info_free (fi); } diff --git a/camel/providers/local/camel-mbox-folder.c b/camel/providers/local/camel-mbox-folder.c index 942610d5a9..159ae353d7 100644 --- a/camel/providers/local/camel-mbox-folder.c +++ b/camel/providers/local/camel-mbox-folder.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- * * Authors: Michael Zucchi + * Jeffrey Stedfast * * Copyright (C) 1999, 2003 Ximian Inc. * @@ -51,6 +52,9 @@ static CamelLocalFolderClass *parent_class = NULL; #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) #define CMBOXS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so)) +char *camel_mbox_folder_get_full_path (const char *toplevel_dir, const char *full_name); +char *camel_mbox_folder_get_meta_path (const char *toplevel_dir, const char *full_name, const char *ext); + static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex); static void mbox_unlock(CamelLocalFolder *lf); @@ -86,7 +90,9 @@ camel_mbox_folder_class_init(CamelMboxFolderClass * camel_mbox_folder_class) #endif camel_folder_class->set_message_user_flag = mbox_set_message_user_flag; camel_folder_class->set_message_user_tag = mbox_set_message_user_tag; - + + lclass->get_full_path = camel_mbox_folder_get_full_path; + lclass->get_meta_path = camel_mbox_folder_get_meta_path; lclass->create_summary = mbox_create_summary; lclass->lock = mbox_lock; lclass->unlock = mbox_unlock; @@ -140,6 +146,67 @@ camel_mbox_folder_new(CamelStore *parent_store, const char *full_name, guint32 f return folder; } +char * +camel_mbox_folder_get_full_path (const char *toplevel_dir, const char *full_name) +{ + const char *inptr = full_name; + int subdirs = 0; + char *path, *p; + + while (*inptr != '\0') { + if (*inptr == '/') + subdirs++; + inptr++; + } + + path = g_malloc (strlen (toplevel_dir) + (inptr - full_name) + (4 * subdirs) + 1); + p = g_stpcpy (path, toplevel_dir); + + inptr = full_name; + while (*inptr != '\0') { + while (*inptr != '/' && *inptr != '\0') + *p++ = *inptr++; + + if (*inptr == '/') { + p = g_stpcpy (p, ".sbd/"); + inptr++; + + /* strip extranaeous '/'s */ + while (*inptr == '/') + inptr++; + } + } + + *p = '\0'; + + return path; +} + +char * +camel_mbox_folder_get_meta_path (const char *toplevel_dir, const char *full_name, const char *ext) +{ +/*#define USE_HIDDEN_META_FILES*/ +#ifdef USE_HIDDEN_META_FILES + char *name, *slash; + + name = g_alloca (strlen (full_name) + strlen (ext) + 2); + if ((slash = strrchr (full_name, '/'))) + sprintf (name, "%.*s.%s%s", slash - full_name + 1, full_name, slash + 1, ext); + else + sprintf (name, ".%s%s", full_name, ext); + + return camel_mbox_folder_get_full_path (toplevel_dir, name); +#else + char *full_path, *path; + + full_path = camel_mbox_folder_get_full_path (toplevel_dir, full_name); + path = g_strdup_printf ("%s%s", full_path, ext); + g_free (full_path); + + return path; +#endif +} + static CamelLocalSummary *mbox_create_summary(const char *path, const char *folder, CamelIndex *index) { return (CamelLocalSummary *)camel_mbox_summary_new(path, folder, index); diff --git a/camel/providers/local/camel-mbox-store.c b/camel/providers/local/camel-mbox-store.c index 9bf6737372..0363047c6f 100644 --- a/camel/providers/local/camel-mbox-store.c +++ b/camel/providers/local/camel-mbox-store.c @@ -23,18 +23,23 @@ #include #endif +#include #include #include +#include +#include #include #include -#include -#include #include "camel-mbox-store.h" #include "camel-mbox-folder.h" +#include "camel-file-utils.h" +#include "camel-text-index.h" #include "camel-exception.h" #include "camel-url.h" +#define d(x) + static CamelLocalStoreClass *parent_class = NULL; /* Returns the class for a CamelMboxStore */ @@ -42,8 +47,14 @@ static CamelLocalStoreClass *parent_class = NULL; #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) #define CMBOXF_CLASS(so) CAMEL_MBOX_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) +extern char *camel_mbox_folder_get_full_path (const char *toplevel_dir, const char *full_name); +extern char *camel_mbox_folder_get_meta_path (const char *toplevel_dir, const char *full_name, const char *ext); + static CamelFolder *get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex); static void delete_folder(CamelStore *store, const char *folder_name, CamelException *ex); +static void rename_folder (CamelStore *store, const char *old, const char *new, CamelException *ex); +static CamelFolderInfo *create_folder (CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex); +static CamelFolderInfo *get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex); static void camel_mbox_store_class_init (CamelMboxStoreClass *camel_mbox_store_class) @@ -55,6 +66,11 @@ camel_mbox_store_class_init (CamelMboxStoreClass *camel_mbox_store_class) /* virtual method overload */ camel_store_class->get_folder = get_folder; camel_store_class->delete_folder = delete_folder; + camel_store_class->rename_folder = rename_folder; + camel_store_class->create_folder = create_folder; + + camel_store_class->get_folder_info = get_folder_info; + camel_store_class->free_folder_info = camel_store_free_folder_info_full; } CamelType @@ -75,64 +91,118 @@ camel_mbox_store_get_type (void) return camel_mbox_store_type; } -static CamelFolder * -get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) +static char * +mbox_folder_name_to_path (CamelStore *store, const char *folder_name) { - char *name; - struct stat st; + const char *toplevel_dir = CAMEL_LOCAL_STORE (store)->toplevel_dir; + + return camel_mbox_folder_get_full_path (toplevel_dir, folder_name); +} - if (!((CamelStoreClass *)parent_class)->get_folder(store, folder_name, flags, ex)) - return NULL; +static char * +mbox_folder_name_to_meta_path (CamelStore *store, const char *folder_name, const char *ext) +{ + const char *toplevel_dir = CAMEL_LOCAL_STORE (store)->toplevel_dir; + + return camel_mbox_folder_get_meta_path (toplevel_dir, folder_name, ext); +} - name = g_strdup_printf("%s%s", CAMEL_LOCAL_STORE(store)->toplevel_dir, folder_name); +static char *extensions[] = { + ".msf", ".ev-summary", ".ibex.index", ".ibex.index.data", ".cmeta" +}; - if (stat(name, &st) == -1) { - int fd; +static gboolean +ignore_file (const char *filename, gboolean sbd) +{ + int flen, len, i; + + flen = strlen (filename); + for (i = 0; i < (sizeof (extensions) / sizeof (extensions[0])); i++) { + len = strlen (extensions[i]); + if (len < flen && !strcmp (filename + flen - len, extensions[i])) + return TRUE; + } + + if (sbd && flen > 4 && !strcmp (filename + flen - 4, ".sbd")) + return TRUE; + + return FALSE; +} +static CamelFolder * +get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) +{ + struct stat st; + char *name; + + if (!((CamelStoreClass *) parent_class)->get_folder (store, folder_name, flags, ex)) + return NULL; + + name = mbox_folder_name_to_path (store, folder_name); + + if (stat (name, &st) == -1) { + int fd; + if (errno != ENOENT) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Could not open file `%s':\n%s"), name, g_strerror (errno)); - g_free(name); + g_free (name); return NULL; } + if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) { camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, _("Folder `%s' does not exist."), folder_name); - g_free(name); + g_free (name); return NULL; } - - fd = open(name, O_WRONLY | O_CREAT | O_APPEND, 0600); + + fd = open (name, O_WRONLY | O_CREAT | O_APPEND, 0666); if (fd == -1) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Could not create file `%s':\n%s"), name, g_strerror (errno)); - g_free(name); + g_free (name); return NULL; } - g_free(name); - close(fd); - } else if (!S_ISREG(st.st_mode)) { - camel_exception_setv(ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, - _("`%s' is not a regular file."), - name); - g_free(name); + + g_free (name); + close (fd); + } else if (!S_ISREG (st.st_mode)) { + camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, + _("`%s' is not a regular file."), + name); + g_free (name); return NULL; } else - g_free(name); - - return camel_mbox_folder_new(store, folder_name, flags, ex); + g_free (name); + + return camel_mbox_folder_new (store, folder_name, flags, ex); } static void delete_folder (CamelStore *store, const char *folder_name, CamelException *ex) { - char *name; + CamelFolderInfo *fi; + char *name, *path; struct stat st; - - name = g_strdup_printf ("%s%s", CAMEL_LOCAL_STORE (store)->toplevel_dir, folder_name); + + name = mbox_folder_name_to_path (store, folder_name); + path = g_strdup_printf ("%s.sbd", name); + + if (rmdir (path) == -1 && errno != ENOENT) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not delete folder `%s':\n%s"), + folder_name, g_strerror (errno)); + g_free (path); + g_free (name); + return; + } + + g_free (path); + if (stat (name, &st) == -1) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Could not delete folder `%s':\n%s"), @@ -155,17 +225,475 @@ delete_folder (CamelStore *store, const char *folder_name, CamelException *ex) g_free (name); return; } - - if (unlink(name) == -1 && errno != ENOENT) { + + if (unlink (name) == -1 && errno != ENOENT) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Could not delete folder `%s':\n%s"), name, g_strerror (errno)); - g_free(name); + g_free (name); + return; + } + + /* FIXME: we have to do our own meta cleanup here rather than + * calling our parent class' delete_folder() method since our + * naming convention is different. Need to find a way for + * CamelLocalStore to be able to construct the folder & meta + * paths itself */ + path = mbox_folder_name_to_meta_path (store, folder_name, ".ev-summary"); + if (unlink (path) == -1 && errno != ENOENT) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not delete folder summary file `%s': %s"), + path, g_strerror (errno)); + g_free (path); + g_free (name); + return; + } + + g_free (path); + + path = mbox_folder_name_to_meta_path (store, folder_name, ".ibex"); + if (camel_text_index_remove (path) == -1 && errno != ENOENT) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not delete folder index file `%s': %s"), + path, g_strerror (errno)); + g_free (path); + g_free (name); + return; + } + + g_free (path); + g_free (name); + + fi = g_new0 (CamelFolderInfo, 1); + fi->full_name = g_strdup (folder_name); + fi->name = g_path_get_basename (folder_name); + fi->url = g_strdup_printf ("mbox:%s#%s", ((CamelService *) store)->url->path, folder_name); + fi->unread_message_count = -1; + camel_folder_info_build_path (fi, '/'); + + camel_object_trigger_event (store, "folder_deleted", fi); + + camel_folder_info_free (fi); +} + +static CamelFolderInfo * +create_folder (CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex) +{ + /* FIXME: this is almost an exact copy of CamelLocalStore::create_folder() except that we use + * different path schemes... need to find a way to share parent's code? */ + const char *toplevel_dir = ((CamelLocalStore *) store)->toplevel_dir; + CamelFolderInfo *info = NULL; + char *path, *name, *dir; + CamelFolder *folder; + struct stat st; + + if (toplevel_dir[0] != '/') { + camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, + _("Store root %s is not an absolute path"), toplevel_dir); + return NULL; + } + + if (folder_name[0] == '.' || ignore_file (folder_name, TRUE)) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot create a folder by this name.")); + return NULL; + } + + if (parent_name) + name = g_strdup_printf ("%s/%s", parent_name, folder_name); + else + name = g_strdup (folder_name); + + path = mbox_folder_name_to_path (store, name); + + dir = g_path_get_dirname (path); + if (camel_mkdir (dir, 0777) == -1 && errno != EEXIST) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create directory `%s': %s."), + dir, g_strerror (errno)); + + g_free (path); + g_free (name); + g_free (dir); + + return NULL; + } + + g_free (dir); + + if (stat (path, &st) == 0 || errno != ENOENT) { + camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER, + _("Cannot create folder: %s: %s"), + path, g_strerror (errno)); + + g_free (path); + g_free (name); + + return NULL; + } + + g_free (path); + + folder = ((CamelStoreClass *) ((CamelObject *) store)->klass)->get_folder (store, name, CAMEL_STORE_FOLDER_CREATE, ex); + if (folder) { + camel_object_unref (folder); + info = ((CamelStoreClass *) ((CamelObject *) store)->klass)->get_folder_info (store, name, 0, ex); + } + + g_free (name); + + return info; +} + +static int +xrename (CamelStore *store, const char *old_name, const char *new_name, const char *ext, gboolean missingok, CamelException *ex) +{ + const char *toplevel_dir = ((CamelLocalStore *) store)->toplevel_dir; + char *oldpath, *newpath; + struct stat st; + int ret = -1; + int err = 0; + + if (ext != NULL) { + oldpath = camel_mbox_folder_get_meta_path (toplevel_dir, old_name, ext); + newpath = camel_mbox_folder_get_meta_path (toplevel_dir, new_name, ext); + } else { + oldpath = camel_mbox_folder_get_full_path (toplevel_dir, old_name); + newpath = camel_mbox_folder_get_full_path (toplevel_dir, new_name); + } + + if (stat (oldpath, &st) == -1) { + if (missingok && errno == ENOENT) { + ret = 0; + } else { + err = errno; + ret = -1; + } + } else if (S_ISDIR (st.st_mode)) { + /* use rename for dirs */ + if (rename (oldpath, newpath) == 0 || stat (newpath, &st) == 0) { + ret = 0; + } else { + err = errno; + ret = -1; + } + } else if (link (oldpath, newpath) == 0 /* and link for files */ + || (stat (newpath, &st) == 0 && st.st_nlink == 2)) { + if (unlink (oldpath) == 0) { + ret = 0; + } else { + err = errno; + unlink (newpath); + ret = -1; + } + } else { + err = errno; + ret = -1; + } + + if (ret == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not rename %s to %s: %s"), + oldpath, newpath, g_strerror (err)); + } + + g_free (oldpath); + g_free (newpath); + + return ret; +} + +static void +rename_folder (CamelStore *store, const char *old, const char *new, CamelException *ex) +{ + CamelLocalFolder *folder = NULL; + char *oldibex, *newibex; + + if (new[0] == '.' || ignore_file (new, TRUE)) { + printf ("exception: The new folder name is illegal.\n"); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("The new folder name is illegal.")); return; } + + /* try to rollback failures, has obvious races */ + + oldibex = mbox_folder_name_to_meta_path (store, old, ".ibex"); + newibex = mbox_folder_name_to_meta_path (store, new, ".ibex"); + + folder = camel_object_bag_get (store->folders, old); + if (folder && folder->index) { + if (camel_index_rename (folder->index, newibex) == -1) + goto ibex_failed; + } else { + /* TODO: camel_text_index_rename should find out if we have an active index itself? */ + if (camel_text_index_rename (oldibex, newibex) == -1) + goto ibex_failed; + } + + if (xrename (store, old, new, ".ev-summary", TRUE, ex)) + goto summary_failed; + + if (xrename (store, old, new, NULL, FALSE, ex)) + goto base_failed; + + g_free (oldibex); + g_free (newibex); + + if (folder) + camel_object_unref (folder); + + return; + + base_failed: + + xrename (store, new, old, ".ev-summary", TRUE, ex); + + summary_failed: + + if (folder) { + if (folder->index) + camel_index_rename (folder->index, oldibex); + } else + camel_text_index_rename (newibex, oldibex); + ibex_failed: + + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not rename '%s': %s"), + old, g_strerror (errno)); + + g_free (newibex); + g_free (oldibex); + + if (folder) + camel_object_unref (folder); +} - g_free(name); - /* and remove metadata */ - ((CamelStoreClass *)parent_class)->delete_folder(store, folder_name, ex); +/* used to find out where we've visited already */ +struct _inode { + dev_t dnode; + ino_t inode; +}; + +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 * +scan_dir (CamelStore *store, GHashTable *visited, CamelFolderInfo *parent, const char *root, + const char *name, guint32 flags, CamelException *ex) +{ + CamelFolderInfo *folders, *tail, *fi; + GHashTable *folder_hash; + struct dirent *dent; + DIR *dir; + + tail = folders = NULL; + + if (!(dir = opendir (root))) + return NULL; + + folder_hash = g_hash_table_new (g_str_hash, g_str_equal); + + /* FIXME: it would be better if we queue'd up the recursive + * scans till the end so that we can limit the number of + * directory descriptors open at any given time... */ + + while ((dent = readdir (dir))) { + char *short_name, *full_name, *path, *ext; + CamelFolder *folder; + struct stat st; + int unread = -1; + + if (dent->d_name[0] == '.') + continue; + + if (ignore_file (dent->d_name, FALSE)) + continue; + + path = g_strdup_printf ("%s/%s", root, dent->d_name); + if (stat (path, &st) == -1) { + g_free (path); + continue; + } + + if (S_ISDIR (st.st_mode)) { + struct _inode in = { st.st_dev, st.st_ino }; + + if (g_hash_table_lookup (visited, &in)) { + g_free (path); + continue; + } + } + + short_name = g_strdup (dent->d_name); + if ((ext = strrchr (short_name, '.')) && !strcmp (ext, ".sbd")) + *ext = '\0'; + + if (name != NULL) + full_name = g_strdup_printf ("%s/%s", name, short_name); + else + full_name = g_strdup (short_name); + + if (!S_ISDIR (st.st_mode)) { + folder = camel_object_bag_get (store->folders, full_name); + if (folder) { + if ((flags & CAMEL_STORE_FOLDER_INFO_FAST) == 0) + camel_folder_refresh_info (folder, NULL); + unread = camel_folder_get_unread_message_count (folder); + camel_object_unref (folder); + } + } + + if ((fi = g_hash_table_lookup (folder_hash, short_name)) != NULL) { + g_free (short_name); + g_free (full_name); + + if (!S_ISDIR (st.st_mode)) { + fi->unread_message_count = unread; + if ((ext = strchr (fi->url, ';')) && !strncmp (ext, ";noselect=yes", 13)) + memmove (ext, ext + 13, strlen (ext + 13) + 1); + } + } else { + fi = g_new0 (CamelFolderInfo, 1); + fi->parent = parent; + + /* add ";noselect=yes" if we haven't found the mbox file yet. when we find it, remove the noselect */ + fi->url = g_strdup_printf ("mbox:%s%s#%s", ((CamelService *) store)->url->path, + S_ISDIR (st.st_mode) ? ";noselect=yes" : "", full_name); + fi->name = short_name; + fi->full_name = full_name; + fi->path = g_strdup_printf ("/%s", full_name); + fi->unread_message_count = unread; + + if (tail == NULL) + folders = fi; + else + tail->sibling = fi; + + tail = fi; + + g_hash_table_insert (folder_hash, fi->name, fi); + } + + if ((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) && S_ISDIR (st.st_mode)) { + struct _inode in = { st.st_dev, st.st_ino }; + + if (g_hash_table_lookup (visited, &in) == NULL) { + struct _inode *inew = g_new (struct _inode, 1); + + *inew = in; + + g_hash_table_insert (visited, inew, inew); + + fi->child = scan_dir (store, visited, fi, path, fi->full_name, flags, ex); + } + } + + g_free (path); + } + + closedir (dir); + + g_hash_table_destroy (folder_hash); + + return folders; +} + +static CamelFolderInfo * +get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex) +{ + GHashTable *visited; + struct _inode *inode; + char *path, *subdir; + CamelFolderInfo *fi; + CamelFolder *folder; + const char *base; + struct stat st; + int unread = -1; + + path = mbox_folder_name_to_path (store, top ? top : ""); + + if (top == NULL) { + /* requesting root dir scan */ + if (stat (path, &st) == -1 || !S_ISDIR (st.st_mode)) { + g_free (path); + return NULL; + } + + visited = g_hash_table_new (inode_hash, inode_equal); + + inode = g_new (struct _inode, 1); + inode->dnode = st.st_dev; + inode->inode = st.st_ino; + + g_hash_table_insert (visited, inode, inode); + + fi = scan_dir (store, visited, NULL, path, NULL, flags, ex); + g_hash_table_foreach (visited, inode_free, NULL); + g_hash_table_destroy (visited); + g_free (path); + + return fi; + } + + /* requesting scan of specific folder */ + if (stat (path, &st) == -1 || !S_ISREG (st.st_mode)) { + g_free (path); + return NULL; + } + + visited = g_hash_table_new (inode_hash, inode_equal); + + if (!(base = strrchr (top, '/'))) + base = top; + else + base++; + + folder = camel_object_bag_get (store->folders, top); + if (folder) { + if ((flags & CAMEL_STORE_FOLDER_INFO_FAST) == 0) + camel_folder_refresh_info (folder, NULL); + unread = camel_folder_get_unread_message_count (folder); + camel_object_unref (folder); + } + + fi = g_new0 (CamelFolderInfo, 1); + fi->parent = NULL; + fi->url = g_strdup_printf ("mbox:%s#%s", ((CamelService *) store)->url->path, top); + fi->name = g_strdup (base); + fi->full_name = g_strdup (top); + fi->unread_message_count = unread; + fi->path = g_strdup_printf ("/%s", top); + + if ((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) != 0) { + subdir = g_strdup_printf ("%s.sbd", path); + if (stat (subdir, &st) == 0 && S_ISDIR (st.st_mode)) + fi->child = scan_dir (store, visited, fi, subdir, top, flags, ex); + g_free (subdir); + } + + g_hash_table_foreach (visited, inode_free, NULL); + g_hash_table_destroy (visited); + g_free (path); + + return fi; } -- cgit v1.2.3