Mercurial > dovecot > core-2.2
view src/lib-storage/index/dbox/dbox-list.c @ 3863:55df57c028d4 HEAD
Added "bool" type and changed all ints that were used as booleans to bool.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 13 Jan 2006 22:25:57 +0200 |
parents | fd0986477809 |
children | 928229f8b3e6 |
line wrap: on
line source
/* Copyright (C) 2002-2005 Timo Sirainen */ #include "lib.h" #include "unlink-directory.h" #include "imap-match.h" #include "subscription-file/subscription-file.h" #include "dbox-storage.h" #include "home-expand.h" #include <dirent.h> #include <sys/stat.h> struct list_dir_context { struct list_dir_context *prev; DIR *dirp; char *real_path, *virtual_path; }; struct dbox_list_context { struct mailbox_list_context mailbox_ctx; struct index_storage *istorage; enum mailbox_list_flags flags; struct imap_match_glob *glob; struct subsfile_list_context *subsfile_ctx; bool failed, inbox_found; struct mailbox_list *(*next)(struct dbox_list_context *ctx); pool_t list_pool; struct mailbox_list list; struct list_dir_context *dir; }; static struct mailbox_list *dbox_list_subs(struct dbox_list_context *ctx); static struct mailbox_list *dbox_list_path(struct dbox_list_context *ctx); static struct mailbox_list *dbox_list_next(struct dbox_list_context *ctx); static const char *mask_get_dir(const char *mask) { const char *p, *last_dir; last_dir = NULL; for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) { if (*p == '/') last_dir = p; } return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir); } static const char * dbox_get_path(struct index_storage *storage, const char *name) { if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) == 0 || name == NULL || (*name != '/' && *name != '~' && *name != '\0')) return t_strconcat(storage->dir, "/", name, NULL); else return home_expand(name); } static int list_opendir(struct mail_storage *storage, const char *path, bool root, DIR **dirp) { *dirp = opendir(*path == '\0' ? "/" : path); if (*dirp != NULL) return 1; if (ENOTFOUND(errno)) { /* root) user gave invalid hiearchy, ignore sub) probably just race condition with other client deleting the mailbox. */ return 0; } if (errno == EACCES) { if (!root) { /* subfolder, ignore */ return 0; } mail_storage_set_error(storage, "Access denied"); return -1; } mail_storage_set_critical(storage, "opendir(%s) failed: %m", path); return -1; } struct mailbox_list_context * dbox_mailbox_list_init(struct mail_storage *storage, const char *ref, const char *mask, enum mailbox_list_flags flags) { struct index_storage *istorage = (struct index_storage *)storage; struct dbox_list_context *ctx; const char *path, *virtual_path; DIR *dirp; ctx = i_new(struct dbox_list_context, 1); ctx->mailbox_ctx.storage = storage; ctx->istorage = istorage; ctx->list_pool = pool_alloconly_create("dbox_list", 1024); ctx->next = dbox_list_next; mail_storage_clear_error(storage); /* check that we're not trying to do any "../../" lists */ if (!dbox_is_valid_mask(storage, ref) || !dbox_is_valid_mask(storage, mask)) { mail_storage_set_error(storage, "Invalid mask"); return &ctx->mailbox_ctx; } if (*mask == '/' || *mask == '~') { /* mask overrides reference */ } else if (*ref != '\0') { /* merge reference and mask */ if (ref[strlen(ref)-1] == '/') mask = t_strconcat(ref, mask, NULL); else mask = t_strconcat(ref, "/", mask, NULL); } if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) { ctx->mailbox_ctx.storage = storage; ctx->istorage = istorage; ctx->flags = flags; ctx->next = dbox_list_subs; path = t_strconcat(istorage->dir, "/" SUBSCRIPTION_FILE_NAME, NULL); ctx->subsfile_ctx = subsfile_list_init(storage, path); if (ctx->subsfile_ctx == NULL) { ctx->next = dbox_list_next; ctx->failed = TRUE; return &ctx->mailbox_ctx; } ctx->glob = imap_match_init(default_pool, mask, TRUE, '/'); return &ctx->mailbox_ctx; } /* if we're matching only subdirectories, don't bother scanning the parent directories */ virtual_path = mask_get_dir(mask); path = dbox_get_path(istorage, virtual_path); if (list_opendir(storage, path, TRUE, &dirp) < 0) return &ctx->mailbox_ctx; /* if user gave invalid directory, we just don't show any results. */ ctx->flags = flags; ctx->glob = imap_match_init(default_pool, mask, TRUE, '/'); if (virtual_path != NULL && dirp != NULL) ctx->next = dbox_list_path; if (dirp != NULL) { 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->mailbox_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 dbox_mailbox_list_deinit(struct mailbox_list_context *_ctx) { struct dbox_list_context *ctx = (struct dbox_list_context *)_ctx; int ret = ctx->failed ? -1 : 0; if (ctx->subsfile_ctx != NULL) { if (subsfile_list_deinit(ctx->subsfile_ctx) < 0) ret = -1; } while (ctx->dir != NULL) { struct list_dir_context *dir = ctx->dir; ctx->dir = dir->prev; list_dir_context_free(dir); } if (ctx->list_pool != NULL) pool_unref(ctx->list_pool); if (ctx->glob != NULL) imap_match_deinit(ctx->glob); i_free(ctx); return ret; } struct mailbox_list * dbox_mailbox_list_next(struct mailbox_list_context *_ctx) { struct dbox_list_context *ctx = (struct dbox_list_context *)_ctx; return ctx->next(ctx); } static int list_file(struct dbox_list_context *ctx, const char *fname) { struct list_dir_context *dir; const char *list_path, *real_path, *path, *mail_path; struct stat st; DIR *dirp; size_t len; enum imap_match_result match, match2; int ret, noselect; /* 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; /* skip Mails/ dir */ if (strcmp(fname, DBOX_MAILDIR_NAME) == 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); } if ((match = imap_match(ctx->glob, list_path)) < 0) return 0; /* first as an optimization check if it contains Mails/ directory. that means it's a directory and it contains mails. */ real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL); mail_path = t_strconcat(real_path, "/"DBOX_MAILDIR_NAME, NULL); if (stat(mail_path, &st) == 0) noselect = FALSE; else { /* non-selectable, but may contain subdirs */ noselect = TRUE; if (stat(real_path, &st) < 0) { if (ENOTFOUND(errno)) return 0; /* just lost it */ if (errno != EACCES && errno != ELOOP) { mail_storage_set_critical( ctx->mailbox_ctx.storage, "stat(%s) failed: %m", real_path); return -1; } } } if (!S_ISDIR(st.st_mode)) { /* not a directory - we don't care about it */ return 0; } /* make sure we give only one correct INBOX */ if (strcasecmp(list_path, "INBOX") == 0 && (ctx->flags & MAILBOX_LIST_INBOX) != 0) { if (ctx->inbox_found) return 0; ctx->inbox_found = TRUE; } /* scan inside the directory */ path = t_strconcat(list_path, "/", NULL); match2 = imap_match(ctx->glob, path); ctx->list.flags = noselect ? MAILBOX_NOSELECT : 0; if (match > 0) ctx->list.name = p_strdup(ctx->list_pool, list_path); else if (match2 > 0) ctx->list.name = p_strdup(ctx->list_pool, path); else ctx->list.name = NULL; ret = match2 < 0 ? 0 : list_opendir(ctx->mailbox_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; } static struct mailbox_list *dbox_list_subs(struct dbox_list_context *ctx) { struct stat st; const char *name, *path, *p; enum imap_match_result match = IMAP_MATCH_NO; 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; } if (name == NULL) return NULL; ctx->list.flags = 0; ctx->list.name = name; if (match == IMAP_MATCH_PARENT) { /* placeholder */ ctx->list.flags = MAILBOX_PLACEHOLDER; while ((p = strrchr(name, '/')) != NULL) { name = t_strdup_until(name, p); if (imap_match(ctx->glob, name) > 0) { p_clear(ctx->list_pool); ctx->list.name = p_strdup(ctx->list_pool, name); return &ctx->list; } } i_unreached(); } if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) != 0) return &ctx->list; t_push(); path = dbox_get_path(ctx->istorage, ctx->list.name); if (stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN; else { ctx->list.flags = MAILBOX_NOINFERIORS; } } else { ctx->list.flags = MAILBOX_NONEXISTENT; } t_pop(); return &ctx->list; } static struct mailbox_list *dbox_list_path(struct dbox_list_context *ctx) { ctx->next = dbox_list_next; ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN; ctx->list.name = p_strconcat(ctx->list_pool, ctx->dir->virtual_path, "/", NULL); if (imap_match(ctx->glob, ctx->list.name) > 0) return &ctx->list; else return ctx->next(ctx); } static struct mailbox_list *dbox_list_inbox(struct dbox_list_context *ctx) { ctx->list.flags = MAILBOX_UNMARKED | MAILBOX_NOCHILDREN; ctx->list.name = "INBOX"; return &ctx->list; } static struct mailbox_list *dbox_list_next(struct dbox_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); } if (!ctx->inbox_found && (ctx->flags & MAILBOX_LIST_INBOX) != 0 && ctx->glob != NULL && imap_match(ctx->glob, "INBOX") > 0) { /* show inbox */ ctx->inbox_found = TRUE; return dbox_list_inbox(ctx); } /* finished */ return NULL; }