#include "lib.h"
#include "str.h"
#include "istream.h"
#include "buffer.h"
#include "eacces-error.h"
#include "sieve-limits.h"
#include "sieve-settings.h"
#include "sieve-extensions.h"
#include "sieve-plugins.h"
#include "sieve-script.h"
#include "sieve-ast.h"
#include "sieve-binary.h"
#include "sieve-actions.h"
#include "sieve-result.h"
#include "sieve-parser.h"
#include "sieve-validator.h"
#include "sieve-generator.h"
#include "sieve-interpreter.h"
#include "sieve-binary-dumper.h"
#include "sieve.h"
#include "sieve-common.h"
#include "sieve-error-private.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
struct sieve_instance *sieve_init
(const struct sieve_environment *env, void *context, bool debug)
{
struct sieve_instance *svinst;
unsigned long long int uint_setting;
size_t size_setting;
pool_t pool;
pool = pool_alloconly_create("sieve", 8192);
svinst = p_new(pool, struct sieve_instance, 1);
svinst->pool = pool;
svinst->env = env;
svinst->context = context;
svinst->debug = debug;
sieve_errors_init(svinst);
svinst->max_script_size = SIEVE_DEFAULT_MAX_SCRIPT_SIZE;
svinst->max_actions = SIEVE_DEFAULT_MAX_ACTIONS;
svinst->max_redirects = SIEVE_DEFAULT_MAX_REDIRECTS;
if ( sieve_setting_get_size_value
(svinst, "sieve_max_script_size", &size_setting) ) {
svinst->max_script_size = size_setting;
}
if ( sieve_setting_get_uint_value
(svinst, "sieve_max_actions", &uint_setting) ) {
svinst->max_actions = (unsigned int) uint_setting;
}
if ( sieve_setting_get_uint_value
(svinst, "sieve_max_redirects", &uint_setting) ) {
svinst->max_redirects = (unsigned int) uint_setting;
}
if ( !sieve_extensions_init(svinst) ) {
sieve_deinit(&svinst);
return NULL;
}
sieve_plugins_load(svinst, NULL, NULL);
return svinst;
}
void sieve_deinit(struct sieve_instance **svinst)
{
sieve_plugins_unload(*svinst);
sieve_extensions_deinit(*svinst);
sieve_errors_deinit(*svinst);
pool_unref(&(*svinst)->pool);
*svinst = NULL;
}
void sieve_set_extensions
(struct sieve_instance *svinst, const char *extensions)
{
sieve_extensions_set_string(svinst, extensions);
}
const char *sieve_get_capabilities
(struct sieve_instance *svinst, const char *name)
{
if ( name == NULL || *name == '\0' )
return sieve_extensions_get_string(svinst);
return sieve_extension_capabilities_get_string(svinst, name);
}
struct sieve_ast *sieve_parse
(struct sieve_script *script, struct sieve_error_handler *ehandler,
enum sieve_error *error_r)
{
struct sieve_parser *parser;
struct sieve_ast *ast = NULL;
if ( (parser = sieve_parser_create(script, ehandler, error_r)) == NULL )
return NULL;
if ( !sieve_parser_run(parser, &ast) ) {
ast = NULL;
} else
sieve_ast_ref(ast);
sieve_parser_free(&parser);
if ( error_r != NULL ) {
if ( ast == NULL )
*error_r = SIEVE_ERROR_NOT_VALID;
else
*error_r = SIEVE_ERROR_NONE;
}
return ast;
}
bool sieve_validate
(struct sieve_ast *ast, struct sieve_error_handler *ehandler,
enum sieve_error *error_r)
{
bool result = TRUE;
struct sieve_validator *validator = sieve_validator_create(ast, ehandler);
if ( !sieve_validator_run(validator) )
result = FALSE;
sieve_validator_free(&validator);
if ( error_r != NULL ) {
if ( !result )
*error_r = SIEVE_ERROR_NOT_VALID;
else
*error_r = SIEVE_ERROR_NONE;
}
return result;
}
static struct sieve_binary *sieve_generate
(struct sieve_ast *ast, struct sieve_error_handler *ehandler,
enum sieve_error *error_r)
{
struct sieve_generator *generator = sieve_generator_create(ast, ehandler);
struct sieve_binary *sbin = NULL;
sbin = sieve_generator_run(generator, NULL);
sieve_generator_free(&generator);
if ( error_r != NULL ) {
if ( sbin == NULL )
*error_r = SIEVE_ERROR_NOT_VALID;
else
*error_r = SIEVE_ERROR_NONE;
}
return sbin;
}
struct sieve_binary *sieve_compile_script
(struct sieve_script *script, struct sieve_error_handler *ehandler,
enum sieve_error *error_r)
{
struct sieve_ast *ast;
struct sieve_binary *sbin;
if ( (ast = sieve_parse(script, ehandler, error_r)) == NULL ) {
sieve_error(ehandler, sieve_script_name(script), "parse failed");
return NULL;
}
if ( !sieve_validate(ast, ehandler, error_r) ) {
sieve_error(ehandler, sieve_script_name(script), "validation failed");
sieve_ast_unref(&ast);
return NULL;
}
if ( (sbin=sieve_generate(ast, ehandler, error_r)) == NULL ) {
sieve_error(ehandler, sieve_script_name(script), "code generation failed");
sieve_ast_unref(&ast);
return NULL;
}
sieve_ast_unref(&ast);
if ( error_r != NULL )
error_r = SIEVE_ERROR_NONE;
return sbin;
}
struct sieve_binary *sieve_compile
(struct sieve_instance *svinst, const char *script_path,
const char *script_name, struct sieve_error_handler *ehandler,
enum sieve_error *error_r)
{
struct sieve_script *script;
struct sieve_binary *sbin;
if ( (script = sieve_script_create
(svinst, script_path, script_name, ehandler, error_r)) == NULL )
return NULL;
sbin = sieve_compile_script(script, ehandler, error_r);
sieve_script_unref(&script);
if ( svinst->debug && sbin != NULL ) {
sieve_sys_debug(svinst, "script file %s successfully compiled",
script_path);
}
return sbin;
}
static int sieve_run
(struct sieve_binary *sbin, struct sieve_result **result,
const struct sieve_message_data *msgdata, const struct sieve_script_env *senv,
struct sieve_error_handler *ehandler)
{
struct sieve_interpreter *interp;
int ret = 0;
if ( (interp=sieve_interpreter_create(sbin, msgdata, senv, ehandler))
== NULL )
return SIEVE_EXEC_BIN_CORRUPT;
if ( senv->exec_status != NULL )
memset(senv->exec_status, 0, sizeof(*senv->exec_status));
if ( *result == NULL )
*result = sieve_result_create
(sieve_binary_svinst(sbin), msgdata, senv, ehandler);
else {
sieve_result_set_error_handler(*result, ehandler);
}
ret = sieve_interpreter_run(interp, *result);
sieve_interpreter_free(&interp);
return ret;
}
struct sieve_binary *sieve_load
(struct sieve_instance *svinst, const char *bin_path, enum sieve_error *error_r)
{
return sieve_binary_open(svinst, bin_path, NULL, error_r);
}
struct sieve_binary *sieve_open
(struct sieve_instance *svinst, const char *script_path,
const char *script_name, struct sieve_error_handler *ehandler,
enum sieve_error *error_r)
{
struct sieve_script *script;
struct sieve_binary *sbin;
const char *bin_path;
script = sieve_script_create
(svinst, script_path, script_name, ehandler, error_r);
if ( script == NULL ) {
return NULL;
}
T_BEGIN {
bin_path = sieve_script_binpath(script);
sbin = sieve_binary_open(svinst, bin_path, script, error_r);
if (sbin != NULL) {
if ( !sieve_binary_up_to_date(sbin) ) {
if ( svinst->debug )
sieve_sys_debug(svinst, "script binary %s is not up-to-date",
bin_path);
sieve_binary_unref(&sbin);
sbin = NULL;
}
}
if ( sbin != NULL ) {
if ( svinst->debug )
sieve_sys_debug(svinst, "script binary %s successfully loaded",
bin_path);
} else {
sbin = sieve_compile_script(script, ehandler, error_r);
if ( sbin != NULL ) {
if ( svinst->debug )
sieve_sys_debug(svinst, "script %s successfully compiled",
script_path);
}
}
} T_END;
sieve_script_unref(&script);
return sbin;
}
const char *sieve_get_source(struct sieve_binary *sbin)
{
return sieve_binary_source(sbin);
}
bool sieve_is_loaded(struct sieve_binary *sbin)
{
return sieve_binary_loaded(sbin);
}
int sieve_save
(struct sieve_binary *sbin, const char *bin_path, bool update,
enum sieve_error *error_r)
{
return sieve_binary_save(sbin, bin_path, update, error_r);
}
void sieve_close(struct sieve_binary **sbin)
{
sieve_binary_unref(sbin);
}
void sieve_dump
(struct sieve_binary *sbin, struct ostream *stream, bool verbose)
{
struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin);
sieve_binary_dumper_run(dumpr, stream, verbose);
sieve_binary_dumper_free(&dumpr);
}
void sieve_hexdump
(struct sieve_binary *sbin, struct ostream *stream)
{
struct sieve_binary_dumper *dumpr = sieve_binary_dumper_create(sbin);
sieve_binary_dumper_hexdump(dumpr, stream);
sieve_binary_dumper_free(&dumpr);
}
int sieve_test
(struct sieve_binary *sbin, const struct sieve_message_data *msgdata,
const struct sieve_script_env *senv, struct sieve_error_handler *ehandler,
struct ostream *stream, bool *keep)
{
struct sieve_result *result = NULL;
int ret;
if ( keep != NULL ) *keep = FALSE;
ret = sieve_run(sbin, &result, msgdata, senv, ehandler);
if ( ret > 0 ) {
ret = sieve_result_print(result, senv, stream, keep);
} else if ( ret == 0 ) {
if ( keep != NULL ) *keep = TRUE;
}
sieve_result_unref(&result);
return ret;
}
int sieve_execute
(struct sieve_binary *sbin, const struct sieve_message_data *msgdata,
const struct sieve_script_env *senv, struct sieve_error_handler *ehandler,
bool *keep)
{
struct sieve_result *result = NULL;
int ret;
if ( keep != NULL ) *keep = FALSE;
ret = sieve_run(sbin, &result, msgdata, senv, ehandler);
if ( ret > 0 ) {
ret = sieve_result_execute(result, keep);
} else if ( ret == 0 ) {
if ( !sieve_result_implicit_keep(result) ) {
ret = SIEVE_EXEC_KEEP_FAILED;
} else {
if ( keep != NULL ) *keep = TRUE;
}
}
sieve_result_unref(&result);
return ret;
}
struct sieve_multiscript {
struct sieve_instance *svinst;
struct sieve_result *result;
const struct sieve_message_data *msgdata;
const struct sieve_script_env *scriptenv;
int status;
bool active;
bool keep;
struct ostream *teststream;
};
struct sieve_multiscript *sieve_multiscript_start_execute
(struct sieve_instance *svinst, const struct sieve_message_data *msgdata,
const struct sieve_script_env *senv)
{
pool_t pool;
struct sieve_result *result;
struct sieve_multiscript *mscript;
result = sieve_result_create(svinst, msgdata, senv, NULL);
pool = sieve_result_pool(result);
sieve_result_set_keep_action(result, NULL, NULL);
mscript = p_new(pool, struct sieve_multiscript, 1);
mscript->svinst = svinst;
mscript->result = result;
mscript->msgdata = msgdata;
mscript->scriptenv = senv;
mscript->status = SIEVE_EXEC_OK;
mscript->active = TRUE;
mscript->keep = TRUE;
return mscript;
}
struct sieve_multiscript *sieve_multiscript_start_test
(struct sieve_instance *svinst, const struct sieve_message_data *msgdata,
const struct sieve_script_env *senv, struct ostream *stream)
{
struct sieve_multiscript *mscript =
sieve_multiscript_start_execute(svinst, msgdata, senv);
mscript->teststream = stream;
return mscript;
}
static void sieve_multiscript_test
(struct sieve_multiscript *mscript, struct sieve_error_handler *ehandler,
bool *keep)
{
sieve_result_set_error_handler(mscript->result, ehandler);
if ( mscript->status > 0 ) {
mscript->status = sieve_result_print
(mscript->result, mscript->scriptenv, mscript->teststream, keep);
} else {
if ( keep != NULL ) *keep = TRUE;
}
sieve_result_mark_executed(mscript->result);
}
static void sieve_multiscript_execute
(struct sieve_multiscript *mscript, struct sieve_error_handler *ehandler,
bool *keep)
{
sieve_result_set_error_handler(mscript->result, ehandler);
if ( mscript->status > 0 ) {
mscript->status = sieve_result_execute(mscript->result, keep);
} else {
if ( !sieve_result_implicit_keep(mscript->result) )
mscript->status = SIEVE_EXEC_KEEP_FAILED;
else
if ( keep != NULL ) *keep = TRUE;
}
}
bool sieve_multiscript_run
(struct sieve_multiscript *mscript, struct sieve_binary *sbin,
struct sieve_error_handler *ehandler, bool final)
{
if ( !mscript->active ) return FALSE;
if ( final )
sieve_result_set_keep_action(mscript->result, NULL, &act_store);
mscript->status = sieve_run(sbin, &mscript->result, mscript->msgdata,
mscript->scriptenv, ehandler);
if ( mscript->status >= 0 ) {
mscript->keep = FALSE;
if ( mscript->teststream != NULL )
sieve_multiscript_test(mscript, ehandler, &mscript->keep);
else
sieve_multiscript_execute(mscript, ehandler, &mscript->keep);
mscript->active =
( mscript->active && mscript->keep && !final && mscript->status > 0 );
}
if ( mscript->status <= 0 )
return FALSE;
return mscript->active;
}
int sieve_multiscript_status(struct sieve_multiscript *mscript)
{
return mscript->status;
}
int sieve_multiscript_finish(struct sieve_multiscript **mscript,
struct sieve_error_handler *ehandler, bool *keep)
{
struct sieve_result *result = (*mscript)->result;
int ret = (*mscript)->status;
if ( ehandler != NULL )
sieve_result_set_error_handler((*mscript)->result, ehandler);
if ( (*mscript)->active ) {
ret = SIEVE_EXEC_FAILURE;
if ( (*mscript)->teststream ) {
(*mscript)->keep = TRUE;
} else {
if ( !sieve_result_implicit_keep((*mscript)->result) )
ret = SIEVE_EXEC_KEEP_FAILED;
else
(*mscript)->keep = TRUE;
}
}
if ( keep != NULL ) *keep = (*mscript)->keep;
sieve_result_unref(&result);
*mscript = NULL;
return ret;
}
unsigned int sieve_max_redirects(struct sieve_instance *svinst)
{
return svinst->max_redirects;
}
unsigned int sieve_max_actions(struct sieve_instance *svinst)
{
return svinst->max_actions;
}
size_t sieve_max_script_size(struct sieve_instance *svinst)
{
return svinst->max_script_size;
}
struct sieve_directory {
struct sieve_instance *svinst;
DIR *dirp;
const char *path;
};
struct sieve_directory *sieve_directory_open
(struct sieve_instance *svinst, const char *path, enum sieve_error *error_r)
{
struct sieve_directory *sdir = NULL;
DIR *dirp;
struct stat st;
if ( error_r != NULL )
*error_r = SIEVE_ERROR_NONE;
if ( stat(path, &st) != 0 ) {
switch ( errno ) {
case ENOENT:
if ( error_r != NULL )
*error_r = SIEVE_ERROR_NOT_FOUND;
break;
case EACCES:
sieve_sys_error(svinst, "failed to open sieve dir: %s",
eacces_error_get("stat", path));
if ( error_r != NULL )
*error_r = SIEVE_ERROR_NO_PERM;
break;
default:
sieve_sys_error(svinst, "failed to open sieve dir: stat(%s) failed: %m",
path);
if ( error_r != NULL )
*error_r = SIEVE_ERROR_TEMP_FAIL;
break;
}
return NULL;
}
if ( S_ISDIR(st.st_mode) ) {
if ( (dirp = opendir(path)) == NULL ) {
switch ( errno ) {
case ENOENT:
if ( error_r != NULL )
*error_r = SIEVE_ERROR_NOT_FOUND;
break;
case EACCES:
sieve_sys_error(svinst, "failed to open sieve dir: %s",
eacces_error_get("opendir", path));
if ( error_r != NULL )
*error_r = SIEVE_ERROR_NO_PERM;
break;
default:
sieve_sys_error(svinst, "failed to open sieve dir: opendir(%s) failed: "
"%m", path);
if ( error_r != NULL )
*error_r = SIEVE_ERROR_TEMP_FAIL;
break;
}
return NULL;
}
sdir = t_new(struct sieve_directory, 1);
sdir->path = path;
sdir->dirp = dirp;
} else {
sdir = t_new(struct sieve_directory, 1);
sdir->path = path;
sdir->dirp = NULL;
}
sdir->svinst = svinst;
return sdir;
}
const char *sieve_directory_get_scriptfile(struct sieve_directory *sdir)
{
const char *script = NULL;
struct dirent *dp;
if ( sdir->dirp != NULL ) {
while ( script == NULL ) {
const char *file;
struct stat st;
errno = 0;
if ( (dp = readdir(sdir->dirp)) == NULL ) {
if ( errno != 0 ) {
sieve_sys_error(sdir->svinst, "failed to read sieve dir: "
"readdir(%s) failed: %m", sdir->path);
}
return NULL;
}
if ( !sieve_script_file_has_extension(dp->d_name) )
continue;
if ( sdir->path[strlen(sdir->path)-1] == '/' )
file = t_strconcat(sdir->path, dp->d_name, NULL);
else
file = t_strconcat(sdir->path, "/", dp->d_name, NULL);
if ( stat(file, &st) != 0 || !S_ISREG(st.st_mode) )
continue;
script = file;
}
} else {
script = sdir->path;
sdir->path = NULL;
}
return script;
}
void sieve_directory_close(struct sieve_directory **sdir)
{
if ( (*sdir)->dirp != NULL && closedir((*sdir)->dirp) < 0 )
sieve_sys_error((*sdir)->svinst, "failed to close sieve dir: "
"closedir(%s) failed: %m", (*sdir)->path);
*sdir = NULL;
}