-
Stephan Bosch authoredStephan Bosch authored
sieve-script.c 26.13 KiB
/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
*/
#include "lib.h"
#include "compat.h"
#include "unichar.h"
#include "str.h"
#include "str-sanitize.h"
#include "hash.h"
#include "array.h"
#include "eacces-error.h"
#include "mkdir-parents.h"
#include "istream.h"
#include "sieve-common.h"
#include "sieve-limits.h"
#include "sieve-settings.old.h"
#include "sieve-error.h"
#include "sieve-dump.h"
#include "sieve-binary.h"
#include "sieve-storage-private.h"
#include "sieve-script-private.h"
/*
* Script name
*/
bool sieve_script_name_is_valid(const char *scriptname)
{
ARRAY_TYPE(unichars) uni_name;
unsigned int count, i;
const unichar_t *name_chars;
size_t namelen = strlen(scriptname);
/* Check minimum length */
if (namelen == 0)
return FALSE;
/* Check worst-case maximum length */
if (namelen > SIEVE_MAX_SCRIPT_NAME_LEN * 4)
return FALSE;
/* Intialize array for unicode characters */
t_array_init(&uni_name, namelen * 4);
/* Convert UTF-8 to UCS4/UTF-32 */
if (uni_utf8_to_ucs4(scriptname, &uni_name) < 0)
return FALSE;
name_chars = array_get(&uni_name, &count);
/* Check true maximum length */
if (count > SIEVE_MAX_SCRIPT_NAME_LEN)
return FALSE;
/* Scan name for invalid characters
* FIXME: compliance with Net-Unicode Definition (Section 2 of
* RFC 5198) is not checked fully and no normalization
* is performed.
*/
for (i = 0; i < count; i++) {
/* 0000-001F; [CONTROL CHARACTERS] */
if (name_chars[i] <= 0x001f)
return FALSE;
/* 002F; SLASH (not RFC-prohibited, but '/' is dangerous) */
if (name_chars[i] == 0x002f)
return FALSE;
/* 007F; DELETE */
if (name_chars[i] == 0x007f)
return FALSE;
/* 0080-009F; [CONTROL CHARACTERS] */
if (name_chars[i] >= 0x0080 && name_chars[i] <= 0x009f)
return FALSE;
/* 00FF */
if (name_chars[i] == 0x00ff)
return FALSE;
/* 2028; LINE SEPARATOR */
/* 2029; PARAGRAPH SEPARATOR */
if (name_chars[i] == 0x2028 || name_chars[i] == 0x2029)
return FALSE;
}
return TRUE;
}
/*
* Script instance
*/
static void sieve_script_update_event(struct sieve_script *script)
{
if (script->name == NULL)
event_set_append_log_prefix(script->event, "script: ");
else {
event_add_str(script->event, "script_name", script->name);
event_set_append_log_prefix(
script->event, t_strdup_printf("script '%s': ",
script->name));
}
}
void sieve_script_init(struct sieve_script *script,
struct sieve_storage *storage,
const struct sieve_script *script_class,
const char *location, const char *name)
{
i_assert(storage != NULL);
script->script_class = script_class;
script->refcount = 1;
script->storage = storage;
script->location = p_strdup_empty(script->pool, location);
script->name = p_strdup_empty(script->pool, name);
script->event = event_create(storage->event);
event_add_str(script->event, "script_location", location);
sieve_script_update_event(script);
sieve_storage_ref(storage);
}
int sieve_script_create(struct sieve_instance *svinst,
const char *location, const char *name,
struct sieve_script **script_r,
enum sieve_error *error_code_r)
{
struct sieve_storage *storage;
int ret;
*script_r = NULL;
sieve_error_args_init(&error_code_r, NULL);
if (sieve_storage_create(svinst, svinst->event, location, 0,
&storage, error_code_r) < 0)
return -1;
ret = sieve_storage_get_script(storage, name, script_r, error_code_r);
sieve_storage_unref(&storage);
return ret;
}
void sieve_script_ref(struct sieve_script *script)
{
script->refcount++;
}
void sieve_script_unref(struct sieve_script **_script)
{
struct sieve_script *script = *_script;
if (script == NULL)
return;
*_script = NULL;
i_assert(script->refcount > 0);
if (--script->refcount != 0)
return;
if (script->stream != NULL) {
struct event_passthrough *e =
event_create_passthrough(script->event)->
set_name("sieve_script_closed");
e_debug(e->event(), "Closed script");
}
i_stream_unref(&script->stream);
if (script->v.destroy != NULL)
script->v.destroy(script);
sieve_storage_unref(&script->storage);
event_unref(&script->event);
pool_unref(&script->pool);
}
int sieve_script_open(struct sieve_script *script,
enum sieve_error *error_code_r)
{
struct sieve_storage *storage = script->storage;
int ret;
sieve_error_args_init(&error_code_r, NULL);
sieve_storage_clear_error(storage);
if (script->open)
return 0;
ret = script->v.open(script);
i_assert(ret <= 0);
if (ret < 0) {
i_assert(storage->error_code != SIEVE_ERROR_NONE);
i_assert(storage->error != NULL);
*error_code_r = storage->error_code;
return -1;
}
i_assert(script->location != NULL);
i_assert(script->name != NULL);
script->open = TRUE;
sieve_script_update_event(script);
e_debug(script->event, "Opened from '%s'", script->location);
return 0;
}
int sieve_script_open_as(struct sieve_script *script, const char *name,
enum sieve_error *error_code_r)
{
if (sieve_script_open(script, error_code_r) < 0)
return -1;
/* override name */
i_assert(name != NULL && *name != '\0');
script->name = p_strdup(script->pool, name);
sieve_script_update_event(script);
return 0;
}
int sieve_script_create_open(struct sieve_instance *svinst,
const char *location, const char *name,
struct sieve_script **script_r,
enum sieve_error *error_code_r)
{
struct sieve_script *script;
if (sieve_script_create(svinst, location, name,
&script, error_code_r) < 0)
return -1;
if (sieve_script_open(script, error_code_r) < 0) {
sieve_script_unref(&script);
return -1;
}
*script_r = script;
return 0;
}
int sieve_script_check(struct sieve_instance *svinst, const char *location,
const char *name, enum sieve_error *error_code_r)
{
struct sieve_script *script;
if (sieve_script_create_open(svinst, location, name,
&script, error_code_r) < 0)
return (*error_code_r == SIEVE_ERROR_NOT_FOUND ? 0 : -1);
sieve_script_unref(&script);
return 1;
}
/*
* Properties
*/
const char *sieve_script_name(const struct sieve_script *script)
{
return script->name;
}
const char *sieve_script_location(const struct sieve_script *script)
{
return script->location;
}
struct sieve_instance *sieve_script_svinst(const struct sieve_script *script)
{
return script->storage->svinst;
}
int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r)
{
struct istream *stream;
int ret;
if (script->v.get_size != NULL) {
if ((ret = script->v.get_size(script, size_r)) != 0)
return ret;
}
/* Try getting size from the stream */
if (script->stream == NULL &&
sieve_script_get_stream(script, &stream, NULL) < 0)
return -1;
if (i_stream_get_size(script->stream, TRUE, size_r) < 0) {
sieve_storage_set_critical(script->storage,
"i_stream_get_size(%s) failed: %s",
i_stream_get_name(script->stream),
i_stream_get_error(script->stream));
return -1;
}
return 0;
}
bool sieve_script_is_open(const struct sieve_script *script)
{
return script->open;
}
bool sieve_script_is_default(const struct sieve_script *script)
{
return script->storage->is_default;
}
/*
* Stream management
*/
int sieve_script_get_stream(struct sieve_script *script,
struct istream **stream_r,
enum sieve_error *error_code_r)
{
struct sieve_storage *storage = script->storage;
int ret;
sieve_error_args_init(&error_code_r, NULL);
sieve_storage_clear_error(storage);
if (script->stream != NULL) {
*stream_r = script->stream;
return 0;
}
// FIXME: necessary?
i_assert(script->open);
T_BEGIN {
ret = script->v.get_stream(script, &script->stream);
} T_END;
if (ret < 0) {
*error_code_r = storage->error_code;
struct event_passthrough *e =
event_create_passthrough(script->event)->
add_str("error", storage->error)->
set_name("sieve_script_opened");
e_debug(e->event(), "Failed to open script for reading: %s",
storage->error);
return -1;
}
struct event_passthrough *e =
event_create_passthrough(script->event)->
set_name("sieve_script_opened");
e_debug(e->event(), "Opened script for reading");
*stream_r = script->stream;
return 0;
}
/*
* Comparison
*/
int sieve_script_cmp(const struct sieve_script *script,
const struct sieve_script *other)
{
if (script == other)
return 0;
if (script == NULL || other == NULL)
return (script == NULL ? -1 : 1);
if (script->script_class != other->script_class)
return (script->script_class > other->script_class ? 1 : -1);
if (script->v.cmp == NULL) {
i_assert (script->location != NULL && other->location != NULL);
return strcmp(script->location, other->location);
}
return script->v.cmp(script, other);
}
unsigned int sieve_script_hash(const struct sieve_script *script)
{
i_assert(script->name != NULL);
return str_hash(script->name);
}
/*
* Binary
*/
int sieve_script_binary_read_metadata(struct sieve_script *script,
struct sieve_binary_block *sblock,
sieve_size_t *offset)
{
struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
string_t *storage_class, *location;
unsigned int version;
if ((sieve_binary_block_get_size(sblock) - *offset) == 0)
return 0;
/* storage class */
if (!sieve_binary_read_string(sblock, offset, &storage_class)) {
e_error(script->event,
"Binary '%s' has invalid metadata for script '%s': "
"Invalid storage class",
sieve_binary_path(sbin), script->location);
return -1;
}
if (strcmp(str_c(storage_class), script->driver_name) != 0) {
e_debug(script->event,
"Binary '%s' reports unexpected driver name for script '%s' "
"('%s' rather than '%s')",
sieve_binary_path(sbin), script->location,
str_c(storage_class), script->driver_name);
return 0;
}
/* version */
if (!sieve_binary_read_unsigned(sblock, offset, &version)) {
e_error(script->event,
"Binary '%s' has invalid metadata for script '%s': "
"Invalid version",
sieve_binary_path(sbin), script->location);
return -1;
}
if (script->storage->version != version) {
e_debug(script->event,
"Binary '%s' was compiled with "
"a different version of the '%s' script storage class "
"(compiled v%d, expected v%d; "
"automatically fixed when re-compiled)",
sieve_binary_path(sbin), script->driver_name,
version, script->storage->version);
return 0;
}
/* location */
if (!sieve_binary_read_string(sblock, offset, &location)) {
e_error(script->event,
"Binary '%s' has invalid metadata for script '%s': "
"Invalid location",
sieve_binary_path(sbin), script->location);
return -1;
}
i_assert(script->location != NULL);
if (strcmp(str_c(location), script->location) != 0) {
e_debug(script->event,
"Binary '%s' reports different location "
"for script '%s' (binary points to '%s')",
sieve_binary_path(sbin), script->location,
str_c(location));
return 0;
}
if (script->v.binary_read_metadata == NULL)
return 1;
return script->v.binary_read_metadata(script, sblock, offset);
}
void sieve_script_binary_write_metadata(struct sieve_script *script,
struct sieve_binary_block *sblock)
{
sieve_binary_emit_cstring(sblock, script->driver_name);
sieve_binary_emit_unsigned(sblock, script->storage->version);
sieve_binary_emit_cstring(sblock, (script->location == NULL ?
"" : script->location));
if (script->v.binary_write_metadata == NULL)
return;
script->v.binary_write_metadata(script, sblock);
}
bool sieve_script_binary_dump_metadata(struct sieve_script *script,
struct sieve_dumptime_env *denv,
struct sieve_binary_block *sblock,
sieve_size_t *offset)
{
struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
struct sieve_instance *svinst = sieve_binary_svinst(sbin);
string_t *storage_class, *location;
struct sieve_script *adhoc_script = NULL;
unsigned int version;
bool result = TRUE;
/* storage class */
if (!sieve_binary_read_string(sblock, offset, &storage_class))
return FALSE;
sieve_binary_dumpf(denv, "class = %s\n", str_c(storage_class));
/* version */
if (!sieve_binary_read_unsigned(sblock, offset, &version))
return FALSE;
sieve_binary_dumpf(denv, "class.version = %d\n", version);
/* location */
if (!sieve_binary_read_string(sblock, offset, &location))
return FALSE;
sieve_binary_dumpf(denv, "location = %s\n", str_c(location));
if (script == NULL) {
adhoc_script = NULL;
if (sieve_script_create(svinst, str_c(location),
NULL, &script, NULL) == 0)
adhoc_script = script;
}
if (script != NULL && script->v.binary_dump_metadata != NULL) {
result = script->v.binary_dump_metadata(
script, denv, sblock, offset);
}
sieve_script_unref(&adhoc_script);
return result;
}
int sieve_script_binary_load_default(struct sieve_script *script,
const char *path,
struct sieve_binary **sbin_r)
{
struct sieve_instance *svinst = script->storage->svinst;
enum sieve_error error_code;
if (path == NULL) {
sieve_script_set_error(
script, SIEVE_ERROR_NOT_POSSIBLE,
"Cannot load script binary for this storage");
return -1;
}
if (sieve_binary_open(svinst, path, script, sbin_r, &error_code) < 0) {
sieve_script_set_error(script, error_code,
"Failed to load script binary");
return -1;
}
return 0;
}
int sieve_script_binary_load(struct sieve_script *script,
struct sieve_binary **sbin_r,
enum sieve_error *error_code_r)
{
struct sieve_storage *storage = script->storage;
int ret;
*sbin_r = NULL;
sieve_error_args_init(&error_code_r, NULL);
sieve_storage_clear_error(storage);
if (script->v.binary_load == NULL) {
sieve_script_set_error(
script, SIEVE_ERROR_NOT_POSSIBLE,
"Cannot load script binary for this storage type");
ret = -1;
} else {
ret = script->v.binary_load(script, sbin_r);
i_assert(ret <= 0);
i_assert(ret < 0 || *sbin_r != NULL);
}
if (ret < 0) {
*error_code_r = script->storage->error_code;
return -1;
}
return 0;
}
int sieve_script_binary_save_default(struct sieve_script *script ATTR_UNUSED,
struct sieve_binary *sbin,
const char *path, bool update,
mode_t save_mode)
{
struct sieve_storage *storage = script->storage;
enum sieve_error error_code;
int ret;
if (path == NULL) {
sieve_script_set_error(
script, SIEVE_ERROR_NOT_POSSIBLE,
"Cannot save script binary for this storage");
return -1;
}
if (storage->bin_path != NULL &&
str_begins_with(path, storage->bin_path) &&
sieve_storage_setup_bin_path(
script->storage, mkdir_get_executable_mode(save_mode)) < 0)
return -1;
ret = sieve_binary_save(sbin, path, update, save_mode, &error_code);
if (ret < 0) {
sieve_script_set_error(script, error_code,
"Failed to save script binary");
return -1;
}
return 0;
}
int sieve_script_binary_save(struct sieve_script *script,
struct sieve_binary *sbin, bool update,
enum sieve_error *error_code_r)
{
struct sieve_storage *storage = script->storage;
struct sieve_script *bin_script = sieve_binary_script(sbin);
int ret;
sieve_error_args_init(&error_code_r, NULL);
sieve_storage_clear_error(storage);
i_assert(bin_script == NULL || sieve_script_equals(bin_script, script));
if (script->v.binary_save == NULL) {
sieve_script_set_error(
script, SIEVE_ERROR_NOT_POSSIBLE,
"Cannot save script binary for this storage type");
ret = -1;
} else {
ret = script->v.binary_save(script, sbin, update);
}
if (ret < 0) {
*error_code_r = script->storage->error_code;
return -1;
}
return 0;
}
const char *sieve_script_binary_get_prefix(struct sieve_script *script)
{
struct sieve_storage *storage = script->storage;
if (storage->bin_path != NULL &&
sieve_storage_setup_bin_path(storage, 0700) >= 0)
return t_strconcat(storage->bin_path, "/", script->name, NULL);
if (script->v.binary_get_prefix == NULL)
return NULL;
return script->v.binary_get_prefix(script);
}
/*
* Management
*/
static int
sieve_script_copy_from_default(struct sieve_script *script, const char *newname)
{
struct sieve_storage *storage = script->storage;
struct istream *input;
int ret;
/* copy from default */
if ((ret = sieve_script_open(script, NULL)) < 0 ||
(ret = sieve_script_get_stream(script, &input, NULL)) < 0) {
sieve_storage_copy_error(storage->default_storage_for, storage);
return ret;
}
ret = sieve_storage_save_as(storage->default_storage_for,
input, newname);
if (ret < 0) {
sieve_storage_copy_error(storage, storage->default_storage_for);
} else if (sieve_script_is_active(script) > 0) {
struct sieve_script *newscript;
enum sieve_error error_code;
if (sieve_storage_open_script(storage->default_storage_for,
newname, &newscript,
&error_code) < 0) {
/* Somehow not actually saved */
ret = (error_code == SIEVE_ERROR_NOT_FOUND ? 0 : -1);
} else if (sieve_script_activate(newscript, (time_t)-1) < 0) {
/* Failed to activate; roll back */
ret = -1;
(void)sieve_script_delete(newscript, TRUE);
}
sieve_script_unref(&newscript);
if (ret < 0) {
e_error(storage->event,
"Failed to implicitly activate script '%s' "
"after rename", newname);
sieve_storage_copy_error(storage->default_storage_for,
storage);
}
}
return ret;
}
int sieve_script_rename(struct sieve_script *script, const char *newname)
{
struct sieve_storage *storage = script->storage;
const char *oldname = script->name;
struct event_passthrough *event;
int ret;
i_assert(newname != NULL);
sieve_storage_clear_error(storage);
/* Check script name */
if (!sieve_script_name_is_valid(newname)) {
sieve_script_set_error(script,
SIEVE_ERROR_BAD_PARAMS,
"Invalid new Sieve script name '%s'.",
str_sanitize(newname, 80));
return -1;
}
i_assert(script->open); // FIXME: auto-open?
if (storage->default_storage_for == NULL) {
i_assert((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0);
/* rename script */
i_assert(script->v.rename != NULL);
ret = script->v.rename(script, newname);
/* rename INBOX mailbox attribute */
if (ret >= 0 && oldname != NULL) {
(void)sieve_storage_sync_script_rename(storage, oldname,
newname);
}
} else if (sieve_storage_check_script(storage->default_storage_for,
newname, NULL) > 0) {
sieve_script_set_error(script, SIEVE_ERROR_EXISTS,
"A sieve script with that name already exists.");
sieve_storage_copy_error(storage->default_storage_for, storage);
ret = -1;
} else {
ret = sieve_script_copy_from_default(script, newname);
}
event = event_create_passthrough(script->event)->
clear_field("script_name")->
add_str("old_script_name", script->name)->
add_str("new_script_name", newname)->
set_name("sieve_script_renamed");
if (ret >= 0) {
e_debug(event->event(), "Script renamed to '%s'", newname);
sieve_script_update_event(script);
} else {
event = event->add_str("error", storage->error);
e_debug(event->event(), "Failed to rename script: %s",
storage->error);
}
return ret;
}
int sieve_script_delete(struct sieve_script *script, bool ignore_active)
{
struct sieve_storage *storage = script->storage;
bool is_active = FALSE;
int ret = 0;
i_assert(script->open); // FIXME: auto-open?
sieve_storage_clear_error(storage);
/* Is the requested script active? */
if (sieve_script_is_active(script) > 0) {
is_active = TRUE;
if (!ignore_active) {
sieve_script_set_error(script, SIEVE_ERROR_ACTIVE,
"Cannot delete the active Sieve script.");
if (storage->default_storage_for != NULL) {
sieve_storage_copy_error(
storage->default_storage_for, storage);
}
return -1;
}
}
/* Trying to delete the default script? */
if (storage->is_default) {
/* ignore */
return 0;
}
i_assert((script->storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0);
/* Deactivate it explicity */
if (ignore_active && is_active)
(void)sieve_storage_deactivate(storage, (time_t)-1);
i_assert(script->v.delete != NULL);
ret = script->v.delete(script);
if (ret >= 0) {
struct event_passthrough *e =
event_create_passthrough(script->event)->
set_name("sieve_script_deleted");
e_debug(e->event(), "Script deleted");
/* unset INBOX mailbox attribute */
(void)sieve_storage_sync_script_delete(storage, script->name);
} else {
struct event_passthrough *e =
event_create_passthrough(script->event)->
add_str("error", storage->error)->
set_name("sieve_script_deleted");
e_debug(e->event(), "Failed to delete script: %s",
storage->error);
}
return ret;
}
int sieve_script_is_active(struct sieve_script *script)
{
struct sieve_storage *storage = script->storage;
sieve_storage_clear_error(storage);
/* Special handling if this is a default script */
if (storage->default_storage_for != NULL) {
int ret = sieve_storage_active_script_is_default(
storage->default_storage_for);
if (ret < 0) {
sieve_storage_copy_error(storage,
storage->default_storage_for);
}
return ret;
}
if (script->v.is_active == NULL)
return 0;
return script->v.is_active(script);
}
int sieve_script_activate(struct sieve_script *script, time_t mtime)
{
struct sieve_storage *storage = script->storage;
int ret = 0;
i_assert(script->open); // FIXME: auto-open?
sieve_storage_clear_error(storage);
if (storage->default_storage_for == NULL) {
i_assert((storage->flags & SIEVE_STORAGE_FLAG_READWRITE) != 0);
i_assert(script->v.activate != NULL);
ret = script->v.activate(script);
if (ret >= 0) {
struct event_passthrough *e =
event_create_passthrough(script->event)->
set_name("sieve_script_activated");
e_debug(e->event(), "Script activated");
sieve_storage_set_modified(storage, mtime);
(void)sieve_storage_sync_script_activate(storage);
} else {
struct event_passthrough *e =
event_create_passthrough(script->event)->
add_str("error", storage->error)->
set_name("sieve_script_activated");
e_debug(e->event(), "Failed to activate script: %s",
storage->error);
}
} else {
/* Activating the default script is equal to deactivating
the storage */
ret = sieve_storage_deactivate(storage->default_storage_for,
(time_t)-1);
if (ret < 0) {
sieve_storage_copy_error(storage,
storage->default_storage_for);
}
}
return ret;
}
/*
* Error handling
*/
void sieve_script_set_error(struct sieve_script *script,
enum sieve_error error_code, const char *fmt, ...)
{
struct sieve_storage *storage = script->storage;
va_list va;
sieve_storage_clear_error(storage);
if (fmt != NULL) {
va_start(va, fmt);
storage->error = i_strdup_vprintf(fmt, va);
va_end(va);
}
storage->error_code = error_code;
}
void sieve_script_set_internal_error(struct sieve_script *script)
{
sieve_storage_set_internal_error(script->storage);
}
void sieve_script_set_critical(struct sieve_script *script,
const char *fmt, ...)
{
struct sieve_storage *storage = script->storage;
va_list va;
if (fmt != NULL) {
if ((storage->flags & SIEVE_STORAGE_FLAG_SYNCHRONIZING) == 0) {
va_start(va, fmt);
e_error(script->event, "%s", t_strdup_vprintf(fmt, va));
va_end(va);
sieve_storage_set_internal_error(storage);
} else {
sieve_storage_clear_error(storage);
/* no user is involved while synchronizing, so do it the
normal way */
va_start(va, fmt);
storage->error = i_strdup_vprintf(fmt, va);
va_end(va);
storage->error_code = SIEVE_ERROR_TEMP_FAILURE;
}
}
}
void sieve_script_set_not_found_error(struct sieve_script *script,
const char *name)
{
name = (name == NULL || *name == '\0' ? script->name : name);
sieve_storage_set_not_found_error(script->storage, name);
}
const char *
sieve_script_get_last_error(struct sieve_script *script,
enum sieve_error *error_code_r)
{
return sieve_storage_get_last_error(script->storage, error_code_r);
}
const char *sieve_script_get_last_error_lcase(struct sieve_script *script)
{
return sieve_error_from_external(script->storage->error);
}
/*
* Script sequence
*/
int sieve_script_sequence_create(struct sieve_instance *svinst,
const char *location,
struct sieve_script_sequence **sseq_r,
enum sieve_error *error_code_r)
{
struct sieve_storage_sequence *storage_seq;
struct sieve_script_sequence *sseq;
*sseq_r = NULL;
sieve_error_args_init(&error_code_r, NULL);
if (sieve_storage_sequence_create(svinst, svinst->event, location,
&storage_seq, error_code_r) < 0)
return -1;
sseq = i_new(struct sieve_script_sequence, 1);
sseq->storage_seq = storage_seq;
*sseq_r = sseq;
return 0;
}
static int
sieve_script_sequence_init_storage(struct sieve_script_sequence *sseq,
enum sieve_error *error_code_r)
{
int ret;
while (sseq->storage == NULL) {
ret = sieve_storage_sequence_next(sseq->storage_seq,
&sseq->storage, error_code_r);
if (ret == 0)
return 0;
if (ret < 0) {
if (*error_code_r == SIEVE_ERROR_NOT_FOUND)
continue;
return -1;
}
struct sieve_storage *storage = sseq->storage;
i_assert(storage->v.script_sequence_init != NULL);
*error_code_r = SIEVE_ERROR_NONE;
ret = storage->v.script_sequence_init(sseq);
*error_code_r = storage->error_code;
sieve_storage_unref(&sseq->storage);
if (ret < 0 && *error_code_r != SIEVE_ERROR_NOT_FOUND)
return -1;
}
return 1;
}
static void
sieve_script_sequence_deinit_storage(struct sieve_script_sequence *sseq)
{
struct sieve_storage *storage = sseq->storage;
if (storage != NULL && storage->v.script_sequence_destroy != NULL)
storage->v.script_sequence_destroy(sseq);
sseq->storage_data = NULL;
sieve_storage_unref(&sseq->storage);
}
int sieve_script_sequence_next(struct sieve_script_sequence *sseq,
struct sieve_script **script_r,
enum sieve_error *error_code_r)
{
int ret;
*script_r = NULL;
sieve_error_args_init(&error_code_r, NULL);
while ((ret = sieve_script_sequence_init_storage(sseq, error_code_r)) > 0) {
struct sieve_storage *storage = sseq->storage;
i_assert(storage->v.script_sequence_next != NULL);
sieve_storage_clear_error(storage);
ret = storage->v.script_sequence_next(sseq, script_r);
*error_code_r = storage->error_code;
if (ret > 0)
break;
if (ret < 0 && *error_code_r == SIEVE_ERROR_NOT_FOUND)
ret = 0;
sieve_script_sequence_deinit_storage(sseq);
if (ret < 0)
break;
}
return ret;
}
void sieve_script_sequence_free(struct sieve_script_sequence **_sseq)
{
struct sieve_script_sequence *sseq = *_sseq;
if (sseq == NULL)
return;
*_sseq = NULL;
sieve_script_sequence_deinit_storage(sseq);
sieve_storage_sequence_free(&sseq->storage_seq);
i_free(sseq);
}