[dovecot-cvs] dovecot/src/lib-storage/index/mbox mbox-list.c,1.13,1.14 mbox-storage.c,1.39,1.40 mbox-storage.h,1.14,1.15
cras at procontrol.fi
cras at procontrol.fi
Wed Feb 19 21:55:30 EET 2003
Update of /home/cvs/dovecot/src/lib-storage/index/mbox
In directory danu:/tmp/cvs-serv21976/src/lib-storage/index/mbox
Modified Files:
mbox-list.c mbox-storage.c mbox-storage.h
Log Message:
Rewrote LIST, LSUB and subscription file handling. LIST replies aren't
sorted anymore by default, it can be enabled with client_workarounds =
list-sort.
Index: mbox-list.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/mbox/mbox-list.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- mbox-list.c 11 Feb 2003 19:37:16 -0000 1.13
+++ mbox-list.c 19 Feb 2003 19:55:27 -0000 1.14
@@ -11,255 +11,380 @@
#include <dirent.h>
#include <sys/stat.h>
-struct find_subscribed_context {
- mailbox_list_callback_t *callback;
- void *context;
+#define STAT_GET_MARKED(st) \
+ ((st).st_size != 0 && (st).st_atime < (st).st_ctime ? \
+ MAILBOX_MARKED : MAILBOX_UNMARKED)
+
+struct list_dir_context {
+ struct list_dir_context *prev;
+
+ DIR *dirp;
+ char *real_path, *virtual_path;
};
-struct list_context {
+struct mailbox_list_context {
struct mail_storage *storage;
+ enum mailbox_list_flags flags;
+
struct imap_match_glob *glob;
- mailbox_list_callback_t *callback;
- void *context;
+ struct subsfile_list_context *subsfile_ctx;
- const char *rootdir;
+ int failed;
+
+ struct mailbox_list *(*next)(struct mailbox_list_context *ctx);
+
+ pool_t list_pool;
+ struct mailbox_list list;
+ struct list_dir_context *dir;
};
-static int mbox_find_path(struct list_context *ctx, const char *relative_dir)
+static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx);
+static struct mailbox_list *mbox_list_inbox(struct mailbox_list_context *ctx);
+static struct mailbox_list *mbox_list_path(struct mailbox_list_context *ctx);
+static struct mailbox_list *mbox_list_next(struct mailbox_list_context *ctx);
+
+static const char *mask_get_dir(const char *mask)
{
- DIR *dirp;
- struct dirent *d;
- struct stat st;
- const char *dir, *listpath;
- char fulldir[PATH_MAX], path[PATH_MAX], fullpath[PATH_MAX];
- int failed, match;
- size_t len;
+ const char *p, *last_dir;
- t_push();
+ last_dir = NULL;
+ for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) {
+ if (*p == '/')
+ last_dir = p;
+ }
- if (relative_dir == NULL)
- dir = ctx->rootdir;
- else if (*ctx->rootdir == '\0' && *relative_dir != '\0')
- dir = relative_dir;
- else {
- if (str_path(fulldir, sizeof(fulldir),
- ctx->rootdir, relative_dir) < 0) {
- mail_storage_set_critical(ctx->storage,
- "Path too long: %s",
- relative_dir);
- return FALSE;
- }
+ return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
+}
- dir = fulldir;
- }
+static const char *mbox_get_path(struct mail_storage *storage, const char *name)
+{
+ if (!full_filesystem_access || name == NULL ||
+ (*name != '/' && *name != '~' && *name != '\0'))
+ return t_strconcat(storage->dir, "/", name, NULL);
+ else
+ return home_expand(name);
+}
- dir = home_expand(dir);
- dirp = opendir(dir);
- if (dirp == NULL) {
- t_pop();
+static int list_opendir(struct mail_storage *storage,
+ const char *path, int root, DIR **dirp)
+{
+ *dirp = opendir(*path == '\0' ? "/" : path);
+ if (*dirp != NULL)
+ return 1;
- if (relative_dir != NULL &&
- (errno == ENOENT || errno == ENOTDIR)) {
- /* probably just race condition with other client
- deleting the mailbox. */
- return TRUE;
- }
+ if (!root && (errno == ENOENT || errno == ENOTDIR)) {
+ /* probably just race condition with other client
+ deleting the mailbox. */
+ return 0;
+ }
- if (errno == EACCES) {
- if (relative_dir != NULL) {
- /* subfolder, ignore */
- return TRUE;
- }
- mail_storage_set_error(ctx->storage, "Access denied");
- return FALSE;
+ if (errno == EACCES) {
+ if (!root) {
+ /* subfolder, ignore */
+ return 0;
}
-
- mail_storage_set_critical(ctx->storage,
- "opendir(%s) failed: %m", dir);
- return FALSE;
+ mail_storage_set_error(storage, "Access denied");
+ return -1;
}
- failed = FALSE;
- while ((d = readdir(dirp)) != NULL) {
- const char *fname = d->d_name;
+ mail_storage_set_critical(storage, "opendir(%s) failed: %m", path);
+ return -1;
+}
- /* skip all hidden files */
- if (fname[0] == '.')
- continue;
+struct mailbox_list_context *
+mbox_list_mailbox_init(struct mail_storage *storage, const char *mask,
+ enum mailbox_list_flags flags, int *sorted)
+{
+ struct mailbox_list_context *ctx;
+ const char *path, *virtual_path;
+ DIR *dirp;
- /* skip all .lock files */
- len = strlen(fname);
- if (len > 5 && strcmp(fname+len-5, ".lock") == 0)
- continue;
+ *sorted = (flags & MAILBOX_LIST_SUBSCRIBED) == 0;
- /* check the mask */
- if (relative_dir == NULL)
- listpath = fname;
- else {
- if (str_path(path, sizeof(path),
- relative_dir, fname) < 0) {
- mail_storage_set_critical(ctx->storage,
- "Path too long: %s/%s",
- relative_dir, fname);
- failed = TRUE;
- break;
- }
- listpath = path;
- }
+ /* check that we're not trying to do any "../../" lists */
+ if (!mbox_is_valid_mask(mask)) {
+ mail_storage_set_error(storage, "Invalid mask");
+ return NULL;
+ }
- if ((match = imap_match(ctx->glob, listpath)) < 0)
- continue;
+ mail_storage_clear_error(storage);
- /* see if it's a directory */
- if (str_path(fullpath, sizeof(fullpath), dir, fname) < 0) {
- mail_storage_set_critical(ctx->storage,
- "Path too long: %s/%s",
- dir, fname);
- failed = TRUE;
- break;
+ if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
+ ctx = i_new(struct mailbox_list_context, 1);
+ ctx->storage = storage;
+ ctx->flags = flags;
+ ctx->next = mbox_list_subs;
+ ctx->subsfile_ctx = subsfile_list_init(storage);
+ if (ctx->subsfile_ctx == NULL) {
+ i_free(ctx);
+ return NULL;
}
+ ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
+ return ctx;
+ }
- if (stat(fullpath, &st) < 0) {
- if (errno == ENOENT)
- continue; /* just deleted, ignore */
+ /* if we're matching only subdirectories, don't bother scanning the
+ parent directories */
+ virtual_path = mask_get_dir(mask);
- mail_storage_set_critical(ctx->storage,
- "stat(%s) failed: %m",
- fullpath);
+ path = mbox_get_path(storage, virtual_path);
+ if (list_opendir(storage, path, TRUE, &dirp) <= 0)
+ return NULL;
+
+ ctx = i_new(struct mailbox_list_context, 1);
+ ctx->storage = storage;
+ ctx->flags = flags;
+ ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
+ ctx->list_pool = pool_alloconly_create("mbox_list", 1024);
+
+ if (virtual_path == NULL && imap_match(ctx->glob, "INBOX") > 0)
+ ctx->next = mbox_list_inbox;
+ else if (virtual_path != NULL)
+ ctx->next = mbox_list_path;
+ else
+ ctx->next = mbox_list_next;
+
+ ctx->dir = i_new(struct list_dir_context, 1);
+ ctx->dir->dirp = dirp;
+ ctx->dir->real_path = i_strdup(path);
+ ctx->dir->virtual_path = i_strdup(virtual_path);
+ return ctx;
+}
+
+static void list_dir_context_free(struct list_dir_context *dir)
+{
+ (void)closedir(dir->dirp);
+ i_free(dir->real_path);
+ i_free(dir->virtual_path);
+ i_free(dir);
+}
+
+int mbox_list_mailbox_deinit(struct mailbox_list_context *ctx)
+{
+ int failed = ctx->failed;
+
+ if (ctx->subsfile_ctx != NULL) {
+ if (!subsfile_list_deinit(ctx->subsfile_ctx))
failed = TRUE;
- break;
- }
+ }
- if (S_ISDIR(st.st_mode)) {
- /* subdirectory, scan it too */
- t_push();
- ctx->callback(ctx->storage, listpath, MAILBOX_NOSELECT,
- ctx->context);
- t_pop();
+ while (ctx->dir != NULL) {
+ struct list_dir_context *dir = ctx->dir;
- if (!mbox_find_path(ctx, listpath)) {
- failed = TRUE;
- break;
- }
- } else if (match > 0 &&
- strcmp(fullpath, ctx->storage->inbox_file) != 0 &&
- strcasecmp(listpath, "INBOX") != 0) {
- /* don't match any INBOX here, it's added later.
- we might also have ~/mail/inbox, ~/mail/Inbox etc.
- Just ignore them for now. */
- t_push();
- ctx->callback(ctx->storage, listpath,
- MAILBOX_NOINFERIORS, ctx->context);
- t_pop();
- }
+ ctx->dir = dir->prev;
+ list_dir_context_free(dir);
}
- t_pop();
+ if (ctx->list_pool != NULL)
+ pool_unref(ctx->list_pool);
+ imap_match_deinit(ctx->glob);
+ i_free(ctx);
- (void)closedir(dirp);
return !failed;
}
-static const char *mask_get_dir(const char *mask)
+struct mailbox_list *mbox_list_mailbox_next(struct mailbox_list_context *ctx)
{
- const char *p, *last_dir;
+ return ctx->next(ctx);
+}
- last_dir = NULL;
- for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) {
- if (*p == '/')
- last_dir = p;
+static int list_file(struct mailbox_list_context *ctx, const char *fname)
+{
+ struct list_dir_context *dir;
+ const char *list_path, *real_path, *path;
+ struct stat st;
+ DIR *dirp;
+ size_t len;
+ enum imap_match_result match, match2;
+ int ret;
+
+ /* skip all hidden files */
+ if (fname[0] == '.')
+ return 0;
+
+ /* skip all .lock files */
+ len = strlen(fname);
+ if (len > 5 && strcmp(fname+len-5, ".lock") == 0)
+ return 0;
+
+ /* check the mask */
+ if (ctx->dir->virtual_path == NULL)
+ list_path = fname;
+ else {
+ list_path = t_strconcat(ctx->dir->virtual_path,
+ "/", fname, NULL);
}
- return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
-}
+ if ((match = imap_match(ctx->glob, list_path)) < 0)
+ return 0;
-int mbox_find_mailboxes(struct mail_storage *storage, const char *mask,
- mailbox_list_callback_t callback, void *context)
-{
- struct list_context ctx;
- struct imap_match_glob *glob;
- const char *relative_dir;
+ /* see if it's a directory */
+ real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL);
+ if (stat(real_path, &st) < 0) {
+ if (errno == ENOENT)
+ return 0; /* just deleted, ignore */
+ mail_storage_set_critical(ctx->storage, "stat(%s) failed: %m",
+ real_path);
+ return -1;
+ }
- /* check that we're not trying to do any "../../" lists */
- if (!mbox_is_valid_mask(mask)) {
- mail_storage_set_error(storage, "Invalid mask");
- return FALSE;
+ if (S_ISDIR(st.st_mode)) {
+ /* subdirectory. scan inside it. */
+ path = t_strconcat(list_path, "/", NULL);
+ match2 = imap_match(ctx->glob, path);
+
+ if (match > 0) {
+ ctx->list.flags = MAILBOX_NOSELECT;
+ ctx->list.name = p_strdup(ctx->list_pool, list_path);
+ } else if (match2 > 0) {
+ ctx->list.flags = MAILBOX_NOSELECT;
+ ctx->list.name = p_strdup(ctx->list_pool, path);
+ }
+
+ ret = match2 < 0 ? 0 :
+ list_opendir(ctx->storage, real_path, FALSE, &dirp);
+ if (ret > 0) {
+ dir = i_new(struct list_dir_context, 1);
+ dir->dirp = dirp;
+ dir->real_path = i_strdup(real_path);
+ dir->virtual_path = i_strdup(list_path);
+
+ dir->prev = ctx->dir;
+ ctx->dir = dir;
+ } else if (ret < 0)
+ return -1;
+ return match > 0 || match2 > 0;
+ } else if (match > 0 &&
+ strcmp(real_path, ctx->storage->inbox_file) != 0 &&
+ strcasecmp(list_path, "INBOX") != 0) {
+ /* don't match any INBOX here, it's added separately.
+ we might also have ~/mail/inbox, ~/mail/Inbox etc.
+ Just ignore them for now. */
+ ctx->list.flags = MAILBOX_NOINFERIORS | STAT_GET_MARKED(st);
+ ctx->list.name = p_strdup(ctx->list_pool, list_path);
+ return 1;
}
- mail_storage_clear_error(storage);
+ return 0;
+}
- /* if we're matching only subdirectories, don't bother scanning the
- parent directories */
- relative_dir = mask_get_dir(mask);
+static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx)
+{
+ struct stat st;
+ const char *name, *path, *p;
+ enum imap_match_result match = IMAP_MATCH_NO;
- glob = imap_match_init(mask, TRUE, '/');
- if (relative_dir == NULL && imap_match(glob, "INBOX") > 0) {
- /* INBOX exists always, even if the file doesn't. */
- callback(storage, "INBOX", MAILBOX_NOINFERIORS, context);
+ while ((name = subsfile_list_next(ctx->subsfile_ctx)) != NULL) {
+ match = imap_match(ctx->glob, name);
+ if (match == IMAP_MATCH_YES || match == IMAP_MATCH_PARENT)
+ break;
}
- memset(&ctx, 0, sizeof(ctx));
- ctx.storage = storage;
- ctx.glob = glob;
- ctx.callback = callback;
- ctx.context = context;
+ if (name == NULL)
+ return NULL;
- if (!full_filesystem_access || relative_dir == NULL ||
- (*relative_dir != '/' && *relative_dir != '~' &&
- *relative_dir != '\0'))
- ctx.rootdir = storage->dir;
- else
- ctx.rootdir = "";
+ ctx->list.flags = 0;
+ ctx->list.name = name;
- if (relative_dir != NULL) {
- const char *matchdir = t_strconcat(relative_dir, "/", NULL);
+ if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) != 0)
+ return &ctx->list;
- if (imap_match(ctx.glob, matchdir) > 0) {
- t_push();
- ctx.callback(ctx.storage, matchdir, MAILBOX_NOSELECT,
- ctx.context);
- t_pop();
+ if (match == IMAP_MATCH_PARENT) {
+ /* placeholder */
+ ctx->list.flags = MAILBOX_NOSELECT;
+ while ((p = strrchr(name, '/')) != NULL) {
+ name = t_strdup_until(name, p);
+ if (imap_match(ctx->glob, name) > 0) {
+ ctx->list.name = name;
+ return &ctx->list;
+ }
}
+ i_unreached();
}
- if (!mbox_find_path(&ctx, relative_dir))
- return FALSE;
-
- return TRUE;
+ t_push();
+ path = mbox_get_path(ctx->storage, ctx->list.name);
+ if (stat(path, &st) == 0) {
+ if (S_ISDIR(st.st_mode))
+ ctx->list.flags = MAILBOX_NOSELECT;
+ else {
+ ctx->list.flags = MAILBOX_NOINFERIORS |
+ STAT_GET_MARKED(st);
+ }
+ } else {
+ if (strcasecmp(ctx->list.name, "INBOX") == 0)
+ ctx->list.flags = MAILBOX_UNMARKED;
+ else
+ ctx->list.flags = MAILBOX_NOSELECT;
+ }
+ t_pop();
+ return &ctx->list;
}
-static int mbox_subs_cb(struct mail_storage *storage, const char *name,
- void *context)
+static struct mailbox_list *mbox_list_inbox(struct mailbox_list_context *ctx)
{
- struct find_subscribed_context *ctx = context;
- enum mailbox_flags flags;
struct stat st;
- char path[PATH_MAX];
- /* see if the mailbox exists, don't bother with the marked flags */
- if (strcasecmp(name, "INBOX") == 0) {
- /* inbox always exists */
- flags = 0;
- } else {
- flags = str_path(path, sizeof(path), storage->dir, name) == 0 &&
- stat(path, &st) == 0 && !S_ISDIR(st.st_mode) ?
- 0 : MAILBOX_NOSELECT;
+ if (ctx->dir->virtual_path != NULL)
+ ctx->next = mbox_list_path;
+ else
+ ctx->next = mbox_list_next;
+
+ /* INBOX exists always, even if the file doesn't. */
+ ctx->list.flags = MAILBOX_NOINFERIORS;
+ if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) == 0) {
+ if (stat(ctx->storage->inbox_file, &st) < 0)
+ ctx->list.flags |= MAILBOX_UNMARKED;
+ else
+ ctx->list.flags |= STAT_GET_MARKED(st);
}
- ctx->callback(storage, name, flags, ctx->context);
- return TRUE;
+ ctx->list.name = "INBOX";
+ return &ctx->list;
}
-int mbox_find_subscribed(struct mail_storage *storage, const char *mask,
- mailbox_list_callback_t callback, void *context)
+static struct mailbox_list *mbox_list_path(struct mailbox_list_context *ctx)
{
- struct find_subscribed_context ctx;
+ ctx->next = mbox_list_next;
- ctx.callback = callback;
- ctx.context = context;
+ ctx->list.flags = MAILBOX_NOSELECT;
+ ctx->list.name = p_strconcat(ctx->list_pool,
+ ctx->dir->virtual_path, "/", NULL);
- if (subsfile_foreach(storage, mask, mbox_subs_cb, &ctx) <= 0)
- return FALSE;
+ if (imap_match(ctx->glob, ctx->list.name) > 0)
+ return &ctx->list;
+ else
+ return ctx->next(ctx);
+}
- return TRUE;
+static struct mailbox_list *mbox_list_next(struct mailbox_list_context *ctx)
+{
+ struct list_dir_context *dir;
+ struct dirent *d;
+ int ret;
+
+ p_clear(ctx->list_pool);
+
+ while (ctx->dir != NULL) {
+ /* NOTE: list_file() may change ctx->dir */
+ while ((d = readdir(ctx->dir->dirp)) != NULL) {
+ t_push();
+ ret = list_file(ctx, d->d_name);
+ t_pop();
+
+ if (ret > 0)
+ return &ctx->list;
+ if (ret < 0) {
+ ctx->failed = TRUE;
+ return NULL;
+ }
+ }
+
+ dir = ctx->dir;
+ ctx->dir = dir->prev;
+ list_dir_context_free(dir);
+ }
+
+ /* finished */
+ return NULL;
}
Index: mbox-storage.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/mbox/mbox-storage.c,v
retrieving revision 1.39
retrieving revision 1.40
diff -u -d -r1.39 -r1.40
--- mbox-storage.c 14 Feb 2003 10:46:44 -0000 1.39
+++ mbox-storage.c 19 Feb 2003 19:55:27 -0000 1.40
@@ -645,9 +645,10 @@
mbox_create_mailbox,
mbox_delete_mailbox,
mbox_rename_mailbox,
- mbox_find_mailboxes,
+ mbox_list_mailbox_init,
+ mbox_list_mailbox_deinit,
+ mbox_list_mailbox_next,
subsfile_set_subscribed,
- mbox_find_subscribed,
mbox_get_mailbox_name_status,
mail_storage_get_last_error,
Index: mbox-storage.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/mbox/mbox-storage.h,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- mbox-storage.h 22 Jan 2003 19:23:28 -0000 1.14
+++ mbox-storage.h 19 Feb 2003 19:55:27 -0000 1.15
@@ -14,10 +14,11 @@
time_t received_date, int timezone_offset,
struct istream *data);
-int mbox_find_mailboxes(struct mail_storage *storage, const char *mask,
- mailbox_list_callback_t callback, void *context);
-int mbox_find_subscribed(struct mail_storage *storage, const char *mask,
- mailbox_list_callback_t callback, void *context);
+struct mailbox_list_context *
+mbox_list_mailbox_init(struct mail_storage *storage, const char *mask,
+ enum mailbox_list_flags flags, int *sorted);
+int mbox_list_mailbox_deinit(struct mailbox_list_context *ctx);
+struct mailbox_list *mbox_list_mailbox_next(struct mailbox_list_context *ctx);
int mbox_expunge_locked(struct index_mailbox *ibox, int notify);
More information about the dovecot-cvs
mailing list