summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c')
-rw-r--r--pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c832
1 files changed, 832 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c b/pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c
new file mode 100644
index 0000000..3c4ec60
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/storage/file/sieve-file-script.c
@@ -0,0 +1,832 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "mempool.h"
+#include "path-util.h"
+#include "istream.h"
+#include "time-util.h"
+#include "eacces-error.h"
+
+#include "sieve-binary.h"
+#include "sieve-script-private.h"
+
+#include "sieve-file-storage.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <fcntl.h>
+
+/*
+ * Filename to name/name to filename
+ */
+
+const char *sieve_script_file_get_scriptname(const char *filename)
+{
+ const char *ext;
+
+ /* Extract the script name */
+ ext = strrchr(filename, '.');
+ if ( ext == NULL || ext == filename ||
+ strcmp(ext, "."SIEVE_SCRIPT_FILEEXT) != 0 )
+ return NULL;
+
+ return t_strdup_until(filename, ext);
+}
+
+bool sieve_script_file_has_extension(const char *filename)
+{
+ return ( sieve_script_file_get_scriptname(filename) != NULL );
+}
+
+const char *sieve_script_file_from_name(const char *name)
+{
+ return t_strconcat(name, "."SIEVE_SCRIPT_FILEEXT, NULL);
+}
+
+/*
+ * Common error handling
+ */
+
+static void sieve_file_script_handle_error
+(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;
+ const char *abspath, *error;
+
+ switch ( errno ) {
+ case ENOENT:
+ if (t_abspath(path, &abspath, &error) < 0) {
+ sieve_script_set_error(script,
+ SIEVE_ERROR_TEMP_FAILURE,
+ "t_abspath(%s) failed: %s",
+ path, error);
+ *error_r = SIEVE_ERROR_TEMP_FAILURE;
+ break;
+ }
+ e_debug(script->event, "File `%s' not found", abspath);
+ sieve_script_set_error(script,
+ SIEVE_ERROR_NOT_FOUND,
+ "Sieve script `%s' not found", name);
+ *error_r = SIEVE_ERROR_NOT_FOUND;
+ break;
+ case EACCES:
+ sieve_script_set_critical(script,
+ "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 %s sieve script: %s(%s) failed: %m",
+ op, op, path);
+ *error_r = SIEVE_ERROR_TEMP_FAILURE;
+ break;
+ }
+}
+
+/*
+ *
+ */
+
+static struct sieve_file_script *sieve_file_script_alloc(void)
+{
+ struct sieve_file_script *fscript;
+ pool_t pool;
+
+ pool = pool_alloconly_create("sieve_file_script", 2048);
+ fscript = p_new(pool, struct sieve_file_script, 1);
+ fscript->script = sieve_file_script;
+ fscript->script.pool = pool;
+
+ return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_init_from_filename
+(struct sieve_file_storage *fstorage, const char *filename,
+ const char *scriptname)
+{
+ struct sieve_storage *storage = &fstorage->storage;
+ struct sieve_file_script *fscript = NULL;
+
+ /* Prevent initializing the active script link as a script when it
+ * resides in the sieve storage directory.
+ */
+ if ( scriptname != NULL && fstorage->link_path != NULL &&
+ *(fstorage->link_path) == '\0' ) {
+ if ( strcmp(filename, fstorage->active_fname) == 0 ) {
+ sieve_storage_set_error(storage,
+ SIEVE_ERROR_NOT_FOUND,
+ "Script `%s' does not exist.", scriptname);
+ return NULL;
+ }
+ }
+
+ fscript = sieve_file_script_alloc();
+ sieve_script_init
+ (&fscript->script, storage, &sieve_file_script,
+ sieve_file_storage_path_extend(fstorage, filename), scriptname);
+ fscript->filename = p_strdup(fscript->script.pool, filename);
+ return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_open_from_filename
+(struct sieve_file_storage *fstorage, const char *filename,
+ const char *scriptname)
+{
+ struct sieve_file_script *fscript;
+ enum sieve_error error;
+
+ fscript = sieve_file_script_init_from_filename
+ (fstorage, filename, scriptname);
+ if ( fscript == NULL )
+ return NULL;
+
+ if ( sieve_script_open(&fscript->script, &error) < 0 ) {
+ struct sieve_script *script = &fscript->script;
+ sieve_script_unref(&script);
+ return NULL;
+ }
+
+ return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_init_from_name
+(struct sieve_file_storage *fstorage, const char *name)
+{
+ struct sieve_storage *storage = &fstorage->storage;
+ struct sieve_file_script *fscript;
+
+ 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->active_path, name);
+ return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_open_from_name
+(struct sieve_file_storage *fstorage, const char *name)
+{
+ struct sieve_file_script *fscript;
+ enum sieve_error error;
+
+ fscript = sieve_file_script_init_from_name(fstorage, name);
+ if ( fscript == NULL )
+ return NULL;
+
+ if ( sieve_script_open(&fscript->script, &error) < 0 ) {
+ struct sieve_script *script = &fscript->script;
+ sieve_script_unref(&script);
+ return NULL;
+ }
+
+ return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_init_from_path
+(struct sieve_file_storage *fstorage, const char *path,
+ const char *scriptname, enum sieve_error *error_r)
+{
+ struct sieve_instance *svinst = fstorage->storage.svinst;
+ struct sieve_file_storage *fsubstorage;
+ struct sieve_file_script *fscript;
+ struct sieve_storage *substorage;
+ enum sieve_error error;
+
+ if ( error_r != NULL )
+ *error_r = SIEVE_ERROR_NONE;
+ else
+ error_r = &error;
+
+ fsubstorage = sieve_file_storage_init_from_path
+ (svinst, path, 0, error_r);
+ if (fsubstorage == NULL)
+ return NULL;
+ substorage = &fsubstorage->storage;
+
+ fscript = sieve_file_script_alloc();
+ sieve_script_init(&fscript->script,
+ substorage, &sieve_file_script, path, scriptname);
+ sieve_storage_unref(&substorage);
+
+ return fscript;
+}
+
+struct sieve_file_script *sieve_file_script_open_from_path
+(struct sieve_file_storage *fstorage, const char *path,
+ const char *scriptname, enum sieve_error *error_r)
+{
+ struct sieve_storage *storage = &fstorage->storage;
+ struct sieve_file_script *fscript;
+ enum sieve_error error;
+
+ if ( error_r != NULL )
+ *error_r = SIEVE_ERROR_NONE;
+ else
+ error_r = &error;
+
+ fscript = sieve_file_script_init_from_path
+ (fstorage, path, scriptname, error_r);
+ if (fscript == NULL) {
+ sieve_storage_set_error(storage,
+ *error_r, "Failed to open script");
+ return NULL;
+ }
+
+ if ( sieve_script_open(&fscript->script, error_r) < 0 ) {
+ struct sieve_script *script = &fscript->script;
+ const char *errormsg;
+
+ errormsg = sieve_script_get_last_error(&fscript->script, error_r);
+ sieve_storage_set_error(storage,
+ *error_r, "%s", errormsg);
+ sieve_script_unref(&script);
+ return NULL;
+ }
+
+ return fscript;
+}
+
+/*
+ * Open
+ */
+
+static int sieve_file_script_stat
+(const char *path, struct stat *st, struct stat *lnk_st)
+{
+ if ( lstat(path, st) < 0 )
+ return -1;
+
+ *lnk_st = *st;
+
+ if ( S_ISLNK(st->st_mode) && stat(path, st) < 0 )
+ return -1;
+
+ return 0;
+}
+
+static const char *
+path_split_filename(const char *path, const char **dirpath_r)
+{
+ const char *filename;
+
+ filename = strrchr(path, '/');
+ if ( filename == NULL ) {
+ *dirpath_r = "";
+ filename = path;
+ } else {
+ *dirpath_r = t_strdup_until(path, filename);
+ filename++;
+ }
+ return filename;
+}
+
+static int sieve_file_script_open
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+ struct sieve_file_script *fscript =
+ (struct sieve_file_script *)script;
+ struct sieve_storage *storage = script->storage;
+ struct sieve_file_storage *fstorage =
+ (struct sieve_file_storage *)storage;
+ pool_t pool = script->pool;
+ const char *filename, *name, *path;
+ const char *dirpath, *basename, *binpath, *binprefix;
+ struct stat st, lnk_st;
+ bool success = TRUE;
+ int ret = 0;
+
+ filename = fscript->filename;
+ basename = NULL;
+ name = script->name;
+ st = fstorage->st;
+ lnk_st = fstorage->lnk_st;
+
+ if (name == NULL)
+ name = storage->script_name;
+
+ T_BEGIN {
+ 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;
+ }
+ dirpath = path;
+
+ path = sieve_file_storage_path_extend(fstorage, filename);
+ ret = sieve_file_script_stat(path, &st, &lnk_st);
+ }
+
+ } else {
+ /* Storage is a single file */
+ path = fstorage->active_path;
+
+ /* Extract filename from path */
+ filename = path_split_filename(path, &dirpath);
+
+ if ( (basename=sieve_script_file_get_scriptname(filename)) == NULL )
+ basename = filename;
+
+ if ( name == NULL )
+ name = basename;
+ }
+
+ if ( success ) {
+ if ( ret < 0 ) {
+ /* Make sure we have a script name for the error */
+ if ( name == NULL ) {
+ i_assert( basename != NULL );
+ name = basename;
+ }
+ sieve_file_script_handle_error
+ (fscript, "stat", path, name, error_r);
+ success = FALSE;
+
+ } else if ( !S_ISREG(st.st_mode) ) {
+ sieve_script_set_critical(script,
+ "Sieve script file '%s' is not a regular file.", path);
+ *error_r = SIEVE_ERROR_TEMP_FAILURE;
+ success = FALSE;
+ }
+ }
+
+ if ( success ) {
+ const char *bpath, *bfile, *bprefix;
+
+ if ( storage->bin_dir != NULL ) {
+ bpath = storage->bin_dir;
+ bfile = sieve_binfile_from_name(name);
+ bprefix = name;
+
+ } else {
+ bpath = dirpath;
+ bfile = sieve_binfile_from_name(basename);
+ bprefix = basename;
+ }
+
+ if ( *bpath == '\0' ) {
+ binpath = bfile;
+ binprefix = bprefix;
+ } else if ( bpath[strlen(bpath)-1] == '/' ) {
+ binpath = t_strconcat(bpath, bfile, NULL);
+ binprefix = t_strconcat(bpath, bprefix, NULL);
+ } else {
+ binpath = t_strconcat(bpath, "/", bfile, NULL);
+ binprefix = t_strconcat(bpath, "/", bprefix, NULL);
+ }
+
+ fscript->st = st;
+ fscript->lnk_st = lnk_st;
+ fscript->path = p_strdup(pool, path);
+ fscript->filename = p_strdup(pool, filename);
+ fscript->dirpath = p_strdup(pool, dirpath);
+ fscript->binpath = p_strdup(pool, binpath);
+ fscript->binprefix = p_strdup(pool, binprefix);
+
+ fscript->script.location = fscript->path;
+
+ if ( fscript->script.name == NULL )
+ fscript->script.name = p_strdup(pool, basename);
+ }
+ } T_END;
+
+ return ( success ? 0 : -1 );
+}
+
+static int sieve_file_script_get_stream
+(struct sieve_script *script, struct istream **stream_r,
+ enum sieve_error *error_r)
+{
+ struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+ struct stat st;
+ struct istream *result;
+ int fd;
+
+ if ( (fd=open(fscript->path, O_RDONLY)) < 0 ) {
+ sieve_file_script_handle_error
+ (fscript, "open", fscript->path, fscript->script.name, error_r);
+ return -1;
+ }
+
+ if ( fstat(fd, &st) != 0 ) {
+ sieve_script_set_critical(script,
+ "Failed to open sieve script: fstat(fd=%s) failed: %m",
+ fscript->path);
+ *error_r = SIEVE_ERROR_TEMP_FAILURE;
+ result = NULL;
+ } else {
+ /* Re-check the file type just to be sure */
+ if ( !S_ISREG(st.st_mode) ) {
+ sieve_script_set_critical(script,
+ "Sieve script file `%s' is not a regular file", fscript->path);
+ *error_r = SIEVE_ERROR_TEMP_FAILURE;
+ result = NULL;
+ } else {
+ result = i_stream_create_fd_autoclose(&fd, SIEVE_FILE_READ_BLOCK_SIZE);
+ fscript->st = fscript->lnk_st = st;
+ }
+ }
+
+ if ( result == NULL ) {
+ /* Something went wrong, close the fd */
+ if ( fd >= 0 && close(fd) != 0 ) {
+ e_error(script->event,
+ "Failed to close sieve script: "
+ "close(fd=%s) failed: %m", fscript->path);
+ }
+ return -1;
+ }
+
+ *stream_r = result;
+ return 0;
+}
+
+/*
+ * Binary
+ */
+
+static int sieve_file_script_binary_read_metadata
+(struct sieve_script *script, struct sieve_binary_block *sblock,
+ sieve_size_t *offset ATTR_UNUSED)
+{
+ struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+ struct sieve_instance *svinst = script->storage->svinst;
+ struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
+ const struct stat *sstat, *bstat;
+
+ bstat = sieve_binary_stat(sbin);
+ if ( fscript->st.st_mtime > fscript->lnk_st.st_mtime ||
+ (fscript->st.st_mtime == fscript->lnk_st.st_mtime &&
+ ST_MTIME_NSEC(fscript->st) >= ST_MTIME_NSEC(fscript->lnk_st)) ) {
+ sstat = &fscript->st;
+ } else {
+ sstat = &fscript->lnk_st;
+ }
+
+ if ( bstat->st_mtime < sstat->st_mtime ||
+ (bstat->st_mtime == sstat->st_mtime &&
+ ST_MTIME_NSEC(*bstat) <= ST_MTIME_NSEC(*sstat)) ) {
+ if ( svinst->debug ) {
+ e_debug(script->event,
+ "Sieve binary `%s' is not newer "
+ "than the Sieve script `%s' (%s.%lu <= %s.%lu)",
+ sieve_binary_path(sbin), sieve_script_location(script),
+ t_strflocaltime("%Y-%m-%d %H:%M:%S", bstat->st_mtime),
+ ST_MTIME_NSEC(*bstat),
+ t_strflocaltime("%Y-%m-%d %H:%M:%S", sstat->st_mtime),
+ ST_MTIME_NSEC(*sstat));
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct sieve_binary *sieve_file_script_binary_load
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+ struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+ struct sieve_instance *svinst = script->storage->svinst;
+
+ return sieve_binary_open(svinst, fscript->binpath, script, error_r);
+}
+
+static int sieve_file_script_binary_save
+(struct sieve_script *script, struct sieve_binary *sbin, bool update,
+ enum sieve_error *error_r)
+{
+ struct sieve_storage *storage = script->storage;
+ struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+ if ( storage->bin_dir != NULL &&
+ sieve_storage_setup_bindir(storage, 0700) < 0 )
+ return -1;
+
+ return sieve_binary_save(sbin, fscript->binpath, update,
+ fscript->st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), error_r);
+}
+
+static const char *sieve_file_script_binary_get_prefix
+(struct sieve_script *script)
+{
+ struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+ return fscript->binprefix;
+}
+
+/*
+ * Management
+ */
+
+static int sieve_file_storage_script_is_active(struct sieve_script *script)
+{
+ struct sieve_file_script *fscript =
+ (struct sieve_file_script *) script;
+ struct sieve_file_storage *fstorage =
+ (struct sieve_file_storage *)script->storage;
+ const char *afile;
+ int ret = 0;
+
+ T_BEGIN {
+ ret = sieve_file_storage_active_script_get_file(fstorage, &afile);
+
+ if ( ret > 0 ) {
+ /* Is the requested script active? */
+ ret = ( strcmp(fscript->filename, afile) == 0 ? 1 : 0 );
+ }
+ } T_END;
+
+ return ret;
+}
+
+static int sieve_file_storage_script_delete(struct sieve_script *script)
+{
+ struct sieve_file_script *fscript =
+ (struct sieve_file_script *)script;
+ int ret = 0;
+
+ if ( sieve_file_storage_pre_modify(script->storage) < 0 )
+ return -1;
+
+ ret = unlink(fscript->path);
+ if ( ret < 0 ) {
+ if ( errno == ENOENT ) {
+ sieve_script_set_error(script,
+ SIEVE_ERROR_NOT_FOUND,
+ "Sieve script does not exist.");
+ } else {
+ sieve_script_set_critical(script,
+ "Performing unlink() failed on sieve file `%s': %m",
+ fscript->path);
+ }
+ }
+ return ret;
+}
+
+static int _sieve_file_storage_script_activate
+(struct sieve_file_script *fscript)
+{
+ struct sieve_script *script = &fscript->script;
+ struct sieve_storage *storage = script->storage;
+ struct sieve_file_storage *fstorage =
+ (struct sieve_file_storage *)storage;
+ struct stat st;
+ const char *link_path, *afile;
+ int activated = 0;
+ int ret;
+
+ /* Find out whether there is an active script, but recreate
+ * the symlink either way. This way, any possible error in the symlink
+ * resolves automatically. This step is only necessary to provide a
+ * proper return value indicating whether the script was already active.
+ */
+ ret = sieve_file_storage_active_script_get_file(fstorage, &afile);
+
+ /* Is the requested script already active? */
+ 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,
+ "Failed to activate Sieve script: lstat(%s) failed: %m.",
+ fscript->path);
+ return -1;
+ }
+
+ /* Rescue a possible .dovecot.sieve regular file remaining from old
+ * installations.
+ */
+ if ( !sieve_file_storage_active_rescue_regular(fstorage) ) {
+ /* Rescue failed, manual intervention is necessary */
+ return -1;
+ }
+
+ /* Just try to create the symlink first */
+ link_path = t_strconcat
+ ( fstorage->link_path, fscript->filename, NULL );
+
+ ret = symlink(link_path, fstorage->active_path);
+ if ( ret < 0 ) {
+ if ( errno == EEXIST ) {
+ ret = sieve_file_storage_active_replace_link(fstorage, link_path);
+ if ( ret < 0 ) {
+ return ret;
+ }
+ } else {
+ /* Other error, critical */
+ sieve_script_set_critical(script,
+ "Failed to activate Sieve script: "
+ "symlink(%s, %s) failed: %m",
+ link_path, fstorage->active_path);
+ return -1;
+ }
+ }
+ return activated;
+}
+
+static int sieve_file_storage_script_activate
+(struct sieve_script *script)
+{
+ struct sieve_file_script *fscript =
+ (struct sieve_file_script *)script;
+ int ret;
+
+ if ( sieve_file_storage_pre_modify(script->storage) < 0 )
+ return -1;
+
+ T_BEGIN {
+ ret = _sieve_file_storage_script_activate(fscript);
+ } T_END;
+
+ return ret;
+}
+
+static int sieve_file_storage_script_rename
+(struct sieve_script *script, const char *newname)
+{
+ struct sieve_file_script *fscript =
+ (struct sieve_file_script *)script;
+ struct sieve_storage *storage = script->storage;
+ struct sieve_file_storage *fstorage =
+ (struct sieve_file_storage *)storage;
+ const char *newpath, *newfile, *link_path;
+ int ret = 0;
+
+ if ( sieve_file_storage_pre_modify(storage) < 0 )
+ return -1;
+
+ T_BEGIN {
+ newfile = sieve_script_file_from_name(newname);
+ newpath = t_strconcat( fstorage->path, "/", newfile, NULL );
+
+ /* The normal rename() system call overwrites the existing file without
+ * notice. Also, active scripts must not be disrupted by renaming a script.
+ * That is why we use a link(newpath) [activate newpath] unlink(oldpath)
+ */
+
+ /* Link to the new path */
+ ret = link(fscript->path, newpath);
+ if ( ret >= 0 ) {
+ /* Is the requested script active? */
+ if ( sieve_script_is_active(script) > 0 ) {
+ /* Active; make active link point to the new copy */
+ i_assert( fstorage->link_path != NULL );
+ link_path = t_strconcat
+ ( fstorage->link_path, newfile, NULL );
+
+ ret = sieve_file_storage_active_replace_link(fstorage, link_path);
+ }
+
+ if ( ret >= 0 ) {
+ /* If all is good, remove the old link */
+ if ( unlink(fscript->path) < 0 ) {
+ e_error(script->event,
+ "Failed to clean up after rename: "
+ "unlink(%s) failed: %m", fscript->path);
+ }
+
+ if ( script->name != NULL && *script->name != '\0' )
+ script->name = p_strdup(script->pool, newname);
+ fscript->path = p_strdup(script->pool, newpath);
+ fscript->filename = p_strdup(script->pool, newfile);
+ } else {
+ /* If something went wrong, remove the new link to restore previous
+ * state
+ */
+ if ( unlink(newpath) < 0 ) {
+ e_error(script->event,
+ "Failed to clean up after failed rename: "
+ "unlink(%s) failed: %m", newpath);
+ }
+ }
+ } else {
+ /* Our efforts failed right away */
+ switch ( errno ) {
+ case ENOENT:
+ sieve_script_set_error(script, SIEVE_ERROR_NOT_FOUND,
+ "Sieve script does not exist.");
+ break;
+ case EEXIST:
+ sieve_script_set_error(script, SIEVE_ERROR_EXISTS,
+ "A sieve script with that name already exists.");
+ break;
+ default:
+ sieve_script_set_critical(script,
+ "Failed to rename Sieve script: "
+ "link(%s, %s) failed: %m", fscript->path, newpath);
+ }
+ }
+ } T_END;
+
+ return ret;
+}
+
+/*
+ * Properties
+ */
+
+static int sieve_file_script_get_size
+(const struct sieve_script *script, uoff_t *size_r)
+{
+ struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+ *size_r = fscript->st.st_size;
+ return 1;
+}
+
+const char *sieve_file_script_get_dirpath
+(const struct sieve_script *script)
+{
+ struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+ if ( script->driver_name != sieve_file_script.driver_name )
+ return NULL;
+
+ return fscript->dirpath;
+}
+
+const char *sieve_file_script_get_path
+(const struct sieve_script *script)
+{
+ struct sieve_file_script *fscript = (struct sieve_file_script *)script;
+
+ if ( script->driver_name != sieve_file_script.driver_name )
+ return NULL;
+
+ return fscript->path;
+}
+
+/*
+ * Matching
+ */
+
+static bool sieve_file_script_equals
+(const struct sieve_script *script, const struct sieve_script *other)
+{
+ struct sieve_file_script *fscript =
+ (struct sieve_file_script *)script;
+ struct sieve_file_script *fother =
+ (struct sieve_file_script *)other;
+
+ return ( CMP_DEV_T(fscript->st.st_dev, fother->st.st_dev) &&
+ fscript->st.st_ino == fother->st.st_ino );
+}
+
+/*
+ * Driver definition
+ */
+
+const struct sieve_script sieve_file_script = {
+ .driver_name = SIEVE_FILE_STORAGE_DRIVER_NAME,
+ .v = {
+ .open = sieve_file_script_open,
+
+ .get_stream = sieve_file_script_get_stream,
+
+ .binary_read_metadata = sieve_file_script_binary_read_metadata,
+ .binary_load = sieve_file_script_binary_load,
+ .binary_save = sieve_file_script_binary_save,
+ .binary_get_prefix = sieve_file_script_binary_get_prefix,
+
+ .rename = sieve_file_storage_script_rename,
+ .delete = sieve_file_storage_script_delete,
+ .is_active = sieve_file_storage_script_is_active,
+ .activate = sieve_file_storage_script_activate,
+
+ .get_size = sieve_file_script_get_size,
+
+ .equals = sieve_file_script_equals
+ }
+};
+