From 9514b62ed68588af4371569c9f09bad7ac8f1c7c Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Fri, 16 Jan 2015 18:25:51 +0100 Subject: [PATCH] lib-sieve: file storage: Restructured storage initialization to address backwards compatibility issues. --- .../storage/file/sieve-file-script.c | 105 ++-- .../storage/file/sieve-file-storage-active.c | 9 +- .../storage/file/sieve-file-storage-list.c | 1 + .../storage/file/sieve-file-storage-quota.c | 1 + .../storage/file/sieve-file-storage-save.c | 1 + .../storage/file/sieve-file-storage.c | 542 ++++++++++++------ .../storage/file/sieve-file-storage.h | 3 + 7 files changed, 433 insertions(+), 229 deletions(-) diff --git a/src/lib-sieve/storage/file/sieve-file-script.c b/src/lib-sieve/storage/file/sieve-file-script.c index 2acb1ccff..007da4e3b 100644 --- a/src/lib-sieve/storage/file/sieve-file-script.c +++ b/src/lib-sieve/storage/file/sieve-file-script.c @@ -52,7 +52,7 @@ const char *sieve_script_file_from_name(const char *name) */ static void sieve_file_script_handle_error -(struct sieve_file_script *fscript, const char *path, +(struct sieve_file_script *fscript, const char *op, const char *path, const char *name, enum sieve_error *error_r) { struct sieve_script *script = &fscript->script; @@ -67,12 +67,14 @@ static void sieve_file_script_handle_error break; case EACCES: sieve_script_set_critical(script, - "Failed to stat sieve script: %s", eacces_error_get("stat", path)); + "Failed to %s sieve script: %s", + op, eacces_error_get(op, path)); *error_r = SIEVE_ERROR_NO_PERMISSION; break; default: sieve_script_set_critical(script, - "Failed to stat sieve script: stat(%s) failed: %m", path); + "Failed to %s sieve script: %s(%s) failed: %m", + op, op, path); *error_r = SIEVE_ERROR_TEMP_FAILURE; break; } @@ -150,14 +152,15 @@ struct sieve_file_script *sieve_file_script_init_from_name struct sieve_storage *storage = &fstorage->storage; struct sieve_file_script *fscript; - if (name != NULL) { + if (name != NULL && S_ISDIR(fstorage->st.st_mode)) { return sieve_file_script_init_from_filename (fstorage, sieve_script_file_from_name(name), name); } fscript = sieve_file_script_alloc(); sieve_script_init - (&fscript->script, storage, &sieve_file_script, fstorage->path, NULL); + (&fscript->script, storage, &sieve_file_script, + fstorage->active_path, name); return fscript; } @@ -259,7 +262,7 @@ static int sieve_file_script_stat if ( S_ISLNK(st->st_mode) && stat(path, st) < 0 ) return -1; - return 1; + return 0; } static const char * @@ -289,67 +292,67 @@ static int sieve_file_script_open pool_t pool = script->pool; const char *filename, *name, *path; const char *dirpath, *basename, *binpath, *binprefix; - struct stat st; - struct stat lnk_st; + struct stat st, lnk_st; bool success = TRUE; - int ret; + int ret = 0; filename = fscript->filename; basename = NULL; name = script->name; - path = fstorage->path; + st = fstorage->st; + lnk_st = fstorage->lnk_st; if (name == NULL) name = storage->script_name; T_BEGIN { - if ( (ret=sieve_file_script_stat(path, &st, &lnk_st)) > 0 ) { - if ( S_ISDIR(st.st_mode) ) { - /* Path is directory */ - if ( (filename == NULL || *filename == '\0') && - name != NULL && *name != '\0' ) { - /* Name is used to find actual filename */ - filename = sieve_script_file_from_name(name); + if ( S_ISDIR(st.st_mode) ) { + /* Storage is a directory */ + path = fstorage->path; + + if ( (filename == NULL || *filename == '\0') && + name != NULL && *name != '\0' ) { + /* Name is used to find actual filename */ + filename = sieve_script_file_from_name(name); + basename = name; + } + if ( filename == NULL || *filename == '\0' ) { + sieve_script_set_critical(script, + "Sieve script file path '%s' is a directory.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + success = FALSE; + } else { + /* Extend storage path with filename */ + if (name == NULL) { + if ( basename == NULL && + (basename=sieve_script_file_get_scriptname(filename)) == NULL ) + basename = filename; + name = basename; + } else if (basename == NULL) { basename = name; } - if ( filename == NULL || *filename == '\0' ) { - sieve_script_set_critical(script, - "Sieve script file path '%s' is a directory.", path); - *error_r = SIEVE_ERROR_TEMP_FAILURE; - success = FALSE; - } else { - /* Extend storage path with filename */ - if (name == NULL) { - if ( basename == NULL && - (basename=sieve_script_file_get_scriptname(filename)) == NULL ) - basename = filename; - name = basename; - } else if (basename == NULL) { - basename = name; - } - dirpath = path; + dirpath = path; - path = sieve_file_storage_path_extend(fstorage, filename); - ret = sieve_file_script_stat(path, &st, &lnk_st); - } + path = sieve_file_storage_path_extend(fstorage, filename); + ret = sieve_file_script_stat(path, &st, &lnk_st); + } - } else { + } else { + /* Storage is a single file */ + path = fstorage->active_path; - /* Extract filename from path */ - filename = path_split_filename(path, &dirpath); + /* Extract filename from path */ + filename = path_split_filename(path, &dirpath); - if ( (basename=sieve_script_file_get_scriptname(filename)) == NULL ) - basename = filename; + if ( (basename=sieve_script_file_get_scriptname(filename)) == NULL ) + basename = filename; - if ( name == NULL ) - name = basename; - } - } else { - basename = name; + if ( name == NULL ) + name = basename; } if ( success ) { - if ( ret <= 0 ) { + if ( ret < 0 ) { /* Make sure we have a script name for the error */ if ( name == NULL ) { if ( basename == NULL ) { @@ -361,7 +364,8 @@ static int sieve_file_script_open } name = basename; } - sieve_file_script_handle_error(fscript, path, name, error_r); + sieve_file_script_handle_error + (fscript, "stat", path, name, error_r); success = FALSE; } else if ( !S_ISREG(st.st_mode) ) { @@ -431,7 +435,7 @@ static int sieve_file_script_get_stream if ( (fd=open(fscript->path, O_RDONLY)) < 0 ) { sieve_file_script_handle_error - (fscript, fscript->path, fscript->script.name, error_r); + (fscript, "open", fscript->path, fscript->script.name, error_r); return -1; } @@ -599,6 +603,8 @@ static int _sieve_file_storage_script_activate if ( ret <= 0 || strcmp(fscript->filename, afile) != 0 ) activated = 1; + i_assert( fstorage->link_path != NULL ); + /* Check the scriptfile we are trying to activate */ if ( lstat(fscript->path, &st) != 0 ) { sieve_script_set_critical(script, @@ -684,6 +690,7 @@ static int sieve_file_storage_script_rename /* Is the requested script active? */ if ( sieve_script_is_active(script) ) { /* Active; make active link point to the new copy */ + i_assert( fstorage->link_path != NULL ); link_path = t_strconcat ( fstorage->link_path, newfile, NULL ); diff --git a/src/lib-sieve/storage/file/sieve-file-storage-active.c b/src/lib-sieve/storage/file/sieve-file-storage-active.c index e0a8ce99d..01a772163 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage-active.c +++ b/src/lib-sieve/storage/file/sieve-file-storage-active.c @@ -133,6 +133,7 @@ static const char *sieve_file_storage_active_parse_link } /* Check whether the path is any good */ + i_assert( fstorage->link_path != NULL ); if ( _file_path_cmp(scriptpath, fstorage->link_path) != 0 && _file_path_cmp(scriptpath, fstorage->path) != 0 ) { sieve_storage_sys_warning(storage, @@ -284,8 +285,12 @@ struct sieve_script *sieve_file_storage_active_script_open return NULL; /* Try to open the active_path as a regular file */ - fscript = sieve_file_script_open_from_path(fstorage, - fstorage->active_path, NULL, NULL); + if ( S_ISDIR(fstorage->st.st_mode) ) { + fscript = sieve_file_script_open_from_path(fstorage, + fstorage->active_path, NULL, NULL); + } else { + fscript = sieve_file_script_open_from_name(fstorage, NULL); + } if ( fscript == NULL ) { if ( storage->error_code != SIEVE_ERROR_NOT_FOUND ) { sieve_storage_set_critical(storage, diff --git a/src/lib-sieve/storage/file/sieve-file-storage-list.c b/src/lib-sieve/storage/file/sieve-file-storage-list.c index 330903290..6bbe2382a 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage-list.c +++ b/src/lib-sieve/storage/file/sieve-file-storage-list.c @@ -84,6 +84,7 @@ const char *sieve_file_storage_list_next /* Don't list our active sieve script link if the link * resides in the script dir (generally a bad idea). */ + i_assert( fstorage->link_path != NULL ); if ( *(fstorage->link_path) == '\0' && strcmp(fstorage->active_fname, dp->d_name) == 0 ) continue; diff --git a/src/lib-sieve/storage/file/sieve-file-storage-quota.c b/src/lib-sieve/storage/file/sieve-file-storage-quota.c index d6f14134a..089300565 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage-quota.c +++ b/src/lib-sieve/storage/file/sieve-file-storage-quota.c @@ -60,6 +60,7 @@ int sieve_file_storage_quota_havespace /* Don't list our active sieve script link if the link * resides in the script dir (generally a bad idea). */ + i_assert( fstorage->link_path != NULL ); if ( *(fstorage->link_path) == '\0' && strcmp(fstorage->active_fname, dp->d_name) == 0 ) continue; diff --git a/src/lib-sieve/storage/file/sieve-file-storage-save.c b/src/lib-sieve/storage/file/sieve-file-storage-save.c index ea89c1e9e..02b004fba 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage-save.c +++ b/src/lib-sieve/storage/file/sieve-file-storage-save.c @@ -184,6 +184,7 @@ sieve_file_storage_save_init(struct sieve_storage *storage, /* Prevent overwriting the active script link when it resides in the * sieve storage directory. */ + i_assert( fstorage->link_path != NULL ); if ( *(fstorage->link_path) == '\0' ) { const char *svext; size_t namelen; diff --git a/src/lib-sieve/storage/file/sieve-file-storage.c b/src/lib-sieve/storage/file/sieve-file-storage.c index 7e0767853..981ad24b8 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage.c +++ b/src/lib-sieve/storage/file/sieve-file-storage.c @@ -2,6 +2,7 @@ */ #include "lib.h" +#include "abspath.h" #include "home-expand.h" #include "ioloop.h" #include "mkdir-parents.h" @@ -46,6 +47,46 @@ const char *sieve_file_storage_path_extend * */ +static int sieve_file_storage_stat +(struct sieve_file_storage *fstorage, const char *path, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct stat st; + + if ( lstat(path, &st) == 0 ) { + fstorage->lnk_st = st; + + if ( !S_ISLNK(st.st_mode) || stat(path, &st) == 0 ) { + fstorage->st = st; + return 0; + } + } + + switch ( errno ) { + case ENOENT: + sieve_storage_sys_debug(storage, + "Storage path `%s' not found", t_abspath(path)); + sieve_storage_set_internal_error(storage); // should be overriden + *error_r = SIEVE_ERROR_NOT_FOUND; + break; + case EACCES: + sieve_storage_set_critical(storage, + "Failed to stat sieve storage path: %s", + eacces_error_get("stat", path)); + *error_r = SIEVE_ERROR_NO_PERMISSION; + break; + default: + sieve_storage_set_critical(storage, + "Failed to stat sieve storage path: " + "stat(%s) failed: %m", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + break; + } + + return -1; +} + static const char *sieve_storage_get_relative_link_path (const char *active_path, const char *storage_dir) { @@ -94,58 +135,6 @@ static mode_t get_dir_mode(mode_t mode) return mode; } -static void sieve_file_storage_get_permissions -(struct sieve_storage *storage, const char *path, -mode_t *file_mode_r, mode_t *dir_mode_r, gid_t *gid_r, - const char **gid_origin_r) -{ - struct stat st; - - /* Use safe defaults */ - *file_mode_r = 0600; - *dir_mode_r = 0700; - *gid_r = (gid_t)-1; - *gid_origin_r = "defaults"; - - if ( stat(path, &st) < 0 ) { - if ( !ENOTFOUND(errno) ) { - sieve_storage_sys_error(storage, - "stat(%s) failed: %m", path); - } else { - sieve_storage_sys_debug(storage, - "Permission lookup failed from %s", path); - } - return; - - } else { - *file_mode_r = (st.st_mode & 0666) | 0600; - *dir_mode_r = (st.st_mode & 0777) | 0700; - *gid_origin_r = path; - - if ( !S_ISDIR(st.st_mode) ) { - /* We're getting permissions from a file. Apply +x modes as necessary. */ - *dir_mode_r = get_dir_mode(*dir_mode_r); - } - - if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) { - /* Directory's GID is used automatically for new files */ - *gid_r = (gid_t)-1; - } else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) { - /* Group has same permissions as world, so don't bother changing it */ - *gid_r = (gid_t)-1; - } else if (getegid() == st.st_gid) { - /* Using our own gid, no need to change it */ - *gid_r = (gid_t)-1; - } else { - *gid_r = st.st_gid; - } - } - - sieve_storage_sys_debug(storage, - "Using permissions from %s: mode=0%o gid=%ld", - path, (int)*dir_mode_r, *gid_r == (gid_t)-1 ? -1L : (long)*gid_r); -} - static int mkdir_verify (struct sieve_storage *storage, const char *dir, mode_t mode, gid_t gid, const char *gid_origin) @@ -233,34 +222,47 @@ static struct sieve_storage *sieve_file_storage_alloc(void) return &fstorage->storage; } -static int sieve_file_storage_init_paths -(struct sieve_file_storage *fstorage, const char *active_path, - const char *storage_path, enum sieve_error *error_r) - ATTR_NULL(2, 3) +static int sieve_file_storage_get_full_path +(struct sieve_file_storage *fstorage, const char **storage_path, + enum sieve_error *error_r) { struct sieve_storage *storage = &fstorage->storage; struct sieve_instance *svinst = storage->svinst; - const char *tmp_dir, *link_path, *path, *active_fname; - mode_t dir_create_mode, file_create_mode; - gid_t file_create_gid; - const char *file_create_gid_origin; - int ret; + const char *path = *storage_path; - fstorage->prev_mtime = (time_t)-1; + /* Get full storage path */ - /* Active script path */ + if ( path != NULL && + ((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) || + (((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/')) ) { + /* home-relative path. change to absolute. */ + const char *home = sieve_environment_get_homedir(svinst); - if ( storage->main_storage || - (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { - if ( active_path == NULL || *active_path == '\0' ) { - sieve_storage_sys_debug(storage, - "Active script path is unconfigured; " - "using default (path=%s)", SIEVE_FILE_DEFAULT_PATH); - active_path = SIEVE_FILE_DEFAULT_PATH; + if ( home != NULL ) { + if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') ) + path = home_expand_tilde(path, home); + else + path = t_strconcat(home, "/", path, NULL); + } else { + sieve_storage_set_critical(storage, + "Sieve storage path `%s' is relative to home directory, " + "but home directory is not available.", path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; } } + *storage_path = path; + return 0; +} + +static int sieve_file_storage_get_full_active_path +(struct sieve_file_storage *fstorage, const char **active_path, + enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + struct sieve_instance *svinst = storage->svinst; + const char *path = *active_path; - path = active_path; if ( path != NULL && *path != '\0' && ((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) || (((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/')) @@ -275,97 +277,139 @@ static int sieve_file_storage_init_paths path = t_strconcat(home, "/", path, NULL); } else { sieve_storage_set_critical(storage, - "Sieve storage path `%s' is relative to home directory, " + "Sieve storage active script path `%s' is relative to home directory, " "but home directory is not available.", path); *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } } - active_path = path; + *active_path = path; + return 0; +} + +static int sieve_file_storage_init_common +(struct sieve_file_storage *fstorage, const char *active_path, + const char *storage_path, bool exists, enum sieve_error *error_r) + ATTR_NULL(2, 3) +{ + struct sieve_storage *storage = &fstorage->storage; + const char *tmp_dir, *link_path, *active_fname; + int ret; + + fstorage->prev_mtime = (time_t)-1; + + /* Get active script path */ + + if ( sieve_file_storage_get_full_active_path + (fstorage, &active_path, error_r) < 0 ) + return -1; /* Get the filename for the active script link */ active_fname = NULL; if ( active_path != NULL && *active_path != '\0' ) { active_fname = strrchr(active_path, '/'); - if ( active_fname == NULL ) + if ( active_fname == NULL ) { active_fname = active_path; - else + } else { active_fname++; + } if ( *active_fname == '\0' ) { /* Link cannot be just a path ending in '/' */ sieve_storage_set_critical(storage, - "Path to active symlink must include the link's filename " - "(path=%s)", active_path); + "Path to %sscript must include the filename (path=%s)", + ( storage_path != NULL ? "active link/" : "" ), + active_path); *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } + + sieve_storage_sys_debug(storage, + "Using %sSieve script path: %s", + ( storage_path != NULL ? "active " : "" ), + active_path); + + fstorage->active_path = p_strdup(storage->pool, active_path); + fstorage->active_fname = p_strdup(storage->pool, active_fname); } - /* Storage path */ + /* Determine storage path */ - path = storage_path; - if ( path != NULL && - ((path[0] == '~' && (path[1] == '/' || path[1] == '\0')) || - (((svinst->flags & SIEVE_FLAG_HOME_RELATIVE) != 0 ) && path[0] != '/')) ) { - /* home-relative path. change to absolute. */ - const char *home = sieve_environment_get_homedir(svinst); + if (storage_path != NULL && *storage_path != '\0') { + sieve_storage_sys_debug(storage, + "Using script storage path: %s", storage_path); - if ( home != NULL ) { - if ( path[0] == '~' && (path[1] == '/' || path[1] == '\0') ) - path = home_expand_tilde(path, home); - else - path = t_strconcat(home, "/", path, NULL); - } else { - sieve_storage_set_critical(storage, - "Sieve storage path `%s' is relative to home directory, " - "but home directory is not available.", path); - *error_r = SIEVE_ERROR_TEMP_FAILURE; - return -1; + if ( active_path != NULL && *active_path != '\0' ) { + /* Get the path to be prefixed to the script name in the symlink pointing + * to the active script. + */ + link_path = sieve_storage_get_relative_link_path + (fstorage->active_path, storage_path); + + sieve_storage_sys_debug(storage, + "Relative path to sieve storage in active link: %s", + link_path); + + fstorage->link_path = p_strdup(storage->pool, link_path); } - } - storage_path = path; - if (storage_path == NULL || *storage_path == '\0') { + } else if ((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { sieve_storage_set_critical(storage, - "Storage path cannot be empty"); + "Storage path cannot be empty for write access"); *error_r = SIEVE_ERROR_TEMP_FAILURE; return -1; } - - sieve_storage_sys_debug(storage, - "Using script storage path: %s", storage_path); - fstorage->path = p_strdup(storage->pool, storage_path); + fstorage->path = ( storage_path != NULL ? + p_strdup(storage->pool, storage_path) : + p_strdup(storage->pool, active_path) ); - if ( active_path != NULL && *active_path != '\0' ) { - sieve_storage_sys_debug(storage, - "Using active Sieve script path: %s", active_path); + /* Prepare for write access */ - fstorage->active_path = p_strdup(storage->pool, active_path); - fstorage->active_fname = p_strdup(storage->pool, active_fname); + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { + mode_t dir_create_mode, file_create_mode; + gid_t file_create_gid; + const char *file_create_gid_origin; + + /* Use safe permission defaults */ + file_create_mode = 0600; + dir_create_mode = 0700; + file_create_gid = (gid_t)-1; + file_create_gid_origin = "defaults"; + + /* Get actual permissions */ + if ( exists ) { + file_create_mode = (fstorage->st.st_mode & 0666) | 0600; + dir_create_mode = (fstorage->st.st_mode & 0777) | 0700; + file_create_gid_origin = storage_path; + + if ( !S_ISDIR(fstorage->st.st_mode) ) { + /* We're getting permissions from a file. + Apply +x modes as necessary. */ + dir_create_mode = get_dir_mode(dir_create_mode); + } - /* Get the path to be prefixed to the script name in the symlink pointing - * to the active script. - */ - link_path = sieve_storage_get_relative_link_path - (fstorage->active_path, fstorage->path); + if (S_ISDIR(fstorage->st.st_mode) && + (fstorage->st.st_mode & S_ISGID) != 0) { + /* Directory's GID is used automatically for new files */ + file_create_gid = (gid_t)-1; + } else if ((fstorage->st.st_mode & 0070) >> 3 == + (fstorage->st.st_mode & 0007)) { + /* Group has same permissions as world, so don't bother changing it */ + file_create_gid = (gid_t)-1; + } else if (getegid() == fstorage->st.st_gid) { + /* Using our own gid, no need to change it */ + file_create_gid = (gid_t)-1; + } else { + file_create_gid = fstorage->st.st_gid; + } + } sieve_storage_sys_debug(storage, - "Relative path to sieve storage in active link: %s", - link_path); - - fstorage->link_path = p_strdup(storage->pool, link_path); - } - - /* Prepare for write access */ - - if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { - /* Get permissions */ - sieve_file_storage_get_permissions(storage, - fstorage->path, &file_create_mode, &dir_create_mode, &file_create_gid, - &file_create_gid_origin); + "Using permissions from %s: mode=0%o gid=%ld", + storage_path, (int)dir_create_mode, + file_create_gid == (gid_t)-1 ? -1L : (long)file_create_gid); /* * Ensure sieve local directory structure exists (full autocreate): @@ -392,6 +436,13 @@ static int sieve_file_storage_init_paths fstorage->file_create_gid = file_create_gid; } + if ( !exists && sieve_file_storage_stat + (fstorage, fstorage->path, error_r) < 0 ) + return -1; + + if (storage->location == NULL) + storage->location = p_strdup(storage->pool, storage_path); + return 0; } @@ -401,7 +452,9 @@ static int sieve_file_storage_init { struct sieve_file_storage *fstorage = (struct sieve_file_storage *)storage; + const char *storage_path = storage->location; const char *active_path = ""; + bool exists = FALSE; if ( options != NULL ) { while ( *options != NULL ) { @@ -420,68 +473,198 @@ static int sieve_file_storage_init } } - return sieve_file_storage_init_paths - (fstorage, active_path, storage->location, error_r); + /* Get full storage path */ + + if ( sieve_file_storage_get_full_path + (fstorage, &storage_path, error_r) < 0 ) + return -1; + + /* Stat storage directory */ + + if ( storage_path != NULL && *storage_path != '\0' ) { + if ( sieve_file_storage_stat (fstorage, storage_path, error_r) < 0 ) { + if ( (*error_r != SIEVE_ERROR_NOT_FOUND) || + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0 ) + return -1; + } else { + exists = TRUE; + + if ( !S_ISDIR(fstorage->st.st_mode) ) { + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { + sieve_storage_set_critical(storage, + "Sieve storage path `%s' is not a directory, " + "but it is to be opened for write access", storage_path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + if ( active_path != NULL && *active_path != '\0' ) { + sieve_storage_sys_warning(storage, + "Explicitly specified active script path `%s' is ignored; " + "storage path `%s' is not a directory", + active_path, storage_path); + } + active_path = storage_path; + storage_path = NULL; + + } + } + } + + if ( active_path == NULL || *active_path == '\0' ) { + if ( storage->main_storage || + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0) { + sieve_storage_sys_debug(storage, + "Active script path is unconfigured; " + "using default (path=%s)", SIEVE_FILE_DEFAULT_PATH); + active_path = SIEVE_FILE_DEFAULT_PATH; + } + } + + return sieve_file_storage_init_common + (fstorage, active_path, storage_path, exists, error_r); } -struct sieve_storage *sieve_file_storage_init_legacy -(struct sieve_instance *svinst, const char *active_path, - const char *storage_path, enum sieve_storage_flags flags, - enum sieve_error *error_r) +static void sieve_file_storage_autodetect +(struct sieve_file_storage *fstorage, const char **storage_path_r) { - struct sieve_storage *storage; - struct sieve_file_storage *fstorage; + struct sieve_storage *storage = &fstorage->storage; + struct sieve_instance *svinst = storage->svinst; + const char *home = sieve_environment_get_homedir(svinst); + int mode = ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ? + R_OK|W_OK|X_OK : R_OK|X_OK ); - storage = sieve_storage_alloc - (svinst, &sieve_file_storage, "", flags, TRUE); - storage->location = p_strdup(storage->pool, storage_path); - fstorage = (struct sieve_file_storage *)storage; - - T_BEGIN { - if ( storage_path == NULL || *storage_path == '\0' ) { - const char *home = sieve_environment_get_homedir(svinst); + sieve_storage_sys_debug(storage, + "Performing auto-detection"); + /* We'll need to figure out the storage location ourself. + * + * It's $HOME/sieve or /sieve when (presumed to be) chrooted. + */ + if ( home != NULL && *home != '\0' ) { + if (access(home, mode) == 0) { + /* Use default ~/sieve */ sieve_storage_sys_debug(storage, - "Performing auto-detection"); + "Root exists (%s)", home); - /* We'll need to figure out the storage location ourself. - * - * It's $HOME/sieve or /sieve when (presumed to be) chrooted. - */ - if ( home != NULL && *home != '\0' ) { - if (access(home, R_OK|W_OK|X_OK) == 0) { - /* Use default ~/sieve */ - sieve_storage_sys_debug(storage, - "Root exists (%s)", home); - - storage_path = t_strconcat(home, "/sieve", NULL); - } else { - /* Don't have required access on the home directory */ - - sieve_storage_sys_debug(storage, - "access(%s, rwx) failed: %m", home); - } - } else { - sieve_storage_sys_debug(storage, - "HOME is not set"); + *storage_path_r = t_strconcat(home, "/sieve", NULL); + } else { + /* Don't have required access on the home directory */ + + sieve_storage_sys_debug(storage, + "access(%s, rwx) failed: %m", home); + } + } else { + sieve_storage_sys_debug(storage, + "HOME is not set"); + + if (access("/sieve", mode) == 0) { + *storage_path_r = "/sieve"; + sieve_storage_sys_debug(storage, + "Directory `/sieve' exists, assuming chroot"); + } + } +} - if (access("/sieve", R_OK|W_OK|X_OK) == 0) { - storage_path = "/sieve"; - sieve_storage_sys_debug(storage, - "Directory `/sieve' exists, assuming chroot"); +static int sieve_file_storage_do_init_legacy +(struct sieve_file_storage *fstorage, const char *active_path, + const char *storage_path, enum sieve_error *error_r) +{ + struct sieve_storage *storage = &fstorage->storage; + bool explicit = FALSE, exists = FALSE; + + if ( storage_path == NULL || *storage_path == '\0' ) { + /* Try autodectection */ + sieve_file_storage_autodetect(fstorage, &storage_path); + + if ( storage_path != NULL && *storage_path != '\0') { + /* Got something: stat it */ + if (sieve_file_storage_stat + (fstorage, storage_path, error_r) < 0 ) { + if (*error_r != SIEVE_ERROR_NOT_FOUND) { + /* Error */ + return -1; } + } else if ( S_ISDIR(fstorage->st.st_mode) ) { + /* Success */ + exists = TRUE; } } - if (storage_path == NULL || *storage_path == '\0') { + if ( (storage_path == NULL || *storage_path == '\0') && + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0 ) { sieve_storage_set_critical(storage, - "Could not find storage root directory; " + "Could not find storage root directory for write access; " "path was left unconfigured and autodetection failed"); *error_r = SIEVE_ERROR_TEMP_FAILURE; - sieve_storage_unref(&storage); - storage = NULL; - } else if (sieve_file_storage_init_paths - (fstorage, active_path, storage_path, error_r) < 0) { + return -1; + } + + } else { + /* Get full storage path */ + if ( sieve_file_storage_get_full_path + (fstorage, &storage_path, error_r) < 0 ) + return -1; + + /* Stat storage directory */ + if ( sieve_file_storage_stat(fstorage, storage_path, error_r) < 0 ) { + if ( (*error_r != SIEVE_ERROR_NOT_FOUND) ) + return -1; + if ( (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0 ) + storage_path = NULL; + } else { + exists = TRUE; + } + + /* Storage path must be a directory */ + if ( exists && !S_ISDIR(fstorage->st.st_mode) ) { + sieve_storage_set_critical(storage, + "Sieve storage path `%s' configured using sieve_dir " + "is not a directory", storage_path); + *error_r = SIEVE_ERROR_TEMP_FAILURE; + return -1; + } + + explicit = TRUE; + } + + if ( (active_path == NULL || *active_path == '\0') ) { + if ( storage->main_storage || + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0) { + sieve_storage_sys_debug(storage, + "Active script path is unconfigured; " + "using default (path=%s)", SIEVE_FILE_DEFAULT_PATH); + active_path = SIEVE_FILE_DEFAULT_PATH; + } else { + return -1; + } + } + + if ( !explicit && !exists && + active_path != NULL && *active_path != '\0' && + (storage->flags & SIEVE_STORAGE_FLAG_READWRITE) == 0 ) + storage_path = NULL; + + if ( sieve_file_storage_init_common + (fstorage, active_path, storage_path, exists, error_r) < 0 ) + return -1; + return 0; +} + +struct sieve_storage *sieve_file_storage_init_legacy +(struct sieve_instance *svinst, const char *active_path, + const char *storage_path, enum sieve_storage_flags flags, + enum sieve_error *error_r) +{ + struct sieve_storage *storage; + struct sieve_file_storage *fstorage; + + storage = sieve_storage_alloc + (svinst, &sieve_file_storage, "", flags, TRUE); + fstorage = (struct sieve_file_storage *)storage; + + T_BEGIN { + if ( sieve_file_storage_do_init_legacy + (fstorage, active_path, storage_path, error_r) < 0 ) { sieve_storage_unref(&storage); storage = NULL; } @@ -497,16 +680,17 @@ struct sieve_file_storage *sieve_file_storage_init_from_path struct sieve_storage *storage; struct sieve_file_storage *fstorage; + i_assert( path != NULL ); + storage = sieve_storage_alloc (svinst, &sieve_file_storage, "", flags, FALSE); - storage->location = p_strdup(storage->pool, path); fstorage = (struct sieve_file_storage *)storage; T_BEGIN { - if ( sieve_file_storage_init_paths - (fstorage, NULL, path, error_r) < 0 ) { + if ( sieve_file_storage_init_common + (fstorage, path, NULL, FALSE, error_r) < 0 ) { sieve_storage_unref(&storage); - storage = NULL; + fstorage = NULL; } } T_END; @@ -520,6 +704,9 @@ static int sieve_file_storage_is_singular (struct sieve_file_storage *)storage; struct stat st; + if ( fstorage->active_path == NULL ) + return 1; + /* Stat the file */ if ( lstat(fstorage->active_path, &st) != 0 ) { if ( errno != ENOENT ) { @@ -542,7 +729,6 @@ static int sieve_file_storage_is_singular return 1; } - /* * */ diff --git a/src/lib-sieve/storage/file/sieve-file-storage.h b/src/lib-sieve/storage/file/sieve-file-storage.h index 1614e30d6..b1501a84c 100644 --- a/src/lib-sieve/storage/file/sieve-file-storage.h +++ b/src/lib-sieve/storage/file/sieve-file-storage.h @@ -35,6 +35,9 @@ struct sieve_file_storage { const char *active_fname; const char *link_path; + struct stat st; + struct stat lnk_st; + mode_t dir_create_mode; mode_t file_create_mode; gid_t file_create_gid; -- GitLab