diff --git a/TODO b/TODO index 721b96a585e8ddf2ae505764fadd141e3fb4de28..59b845084b84698df1a2f553073bf53d3a3c8ed9 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,7 @@ Current activities: -* Build a sieve tool to filter an entire existing mailbox through a Sieve - script. +* Update include extension to latest draft (v13 currently): + - Implement :optional tag. Parallel plugin-based efforts: @@ -15,9 +15,6 @@ Parallel plugin-based efforts: Next (mostly in order of descending priority/precedence): * Implement index extension -* Update include extension to latest draft (v10 currently): - - Implement :optional tag. - - Implement required ManageSieve behavior * Add normalize() method to comparators to normalize the string before matching (for efficiency). * Improve error handling. diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c index 1ad1d16213598df24d1e65de52ad4959b16e8f6b..8c6a8ae6141755a81f94ed965ebb266b858d8424 100644 --- a/src/lib-sieve/plugins/include/cmd-include.c +++ b/src/lib-sieve/plugins/include/cmd-include.c @@ -203,7 +203,8 @@ static bool cmd_include_validate (struct cmd_include_context_data *) cmd->data; struct sieve_script *script; const char *script_path, *script_name; - enum sieve_error error = TRUE; + enum sieve_error error = SIEVE_ERROR_NONE; + bool include = TRUE; /* Check argument */ if ( !sieve_validate_positional_argument @@ -251,18 +252,33 @@ static bool cmd_include_validate sieve_validator_error_handler(valdtr), &error); if ( script == NULL ) { - if ( error == SIEVE_ERROR_NOT_FOUND ) { - sieve_argument_validate_error(valdtr, arg, - "included %s script '%s' does not exist", - ext_include_script_location_name(ctx_data->location), - str_sanitize(script_name, 80)); + if ( error != SIEVE_ERROR_NOT_FOUND ) { + return FALSE; + } else { + enum sieve_compile_flags cpflags = + sieve_validator_compile_flags(valdtr); + + if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) { + sieve_argument_validate_warning(valdtr, arg, + "included %s script '%s' does not exist (ignored during upload)", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80)); + include = FALSE; + } else { + sieve_argument_validate_error(valdtr, arg, + "included %s script '%s' does not exist", + ext_include_script_location_name(ctx_data->location), + str_sanitize(script_name, 80)); + return FALSE; + } } - return FALSE; } - ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script); - ctx_data->script = script; - + if ( include ) { + ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script); + ctx_data->script = script; + } + arg = sieve_ast_arguments_detach(arg, 1); return TRUE; @@ -279,18 +295,26 @@ static bool cmd_include_generate (struct cmd_include_context_data *) cmd->data; const struct ext_include_script_info *included; unsigned int flags = ctx_data->include_once; + int ret; - /* Compile (if necessary) and include the script into the binary. - * This yields the id of the binary block containing the compiled byte code. + /* Upon upload ctx_data->script may be NULL if the script was not found. We + * don't emit any code for this include command in that case. */ - if ( !ext_include_generate_include - (cgenv, cmd, ctx_data->location, ctx_data->script, &included, - ctx_data->include_once) ) - return FALSE; - - (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation); - (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id); - (void)sieve_binary_emit_byte(cgenv->sblock, flags); + if ( ctx_data->script != NULL ) { + /* Compile (if necessary) and include the script into the binary. + * This yields the id of the binary block containing the compiled byte code. + */ + if ( (ret=ext_include_generate_include + (cgenv, cmd, ctx_data->location, ctx_data->script, &included, + ctx_data->include_once)) < 0 ) + return FALSE; + + if ( ret > 0 ) { + (void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation); + (void)sieve_binary_emit_unsigned(cgenv->sblock, included->id); + (void)sieve_binary_emit_byte(cgenv->sblock, flags); + } + } return TRUE; } diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c index c47dcc21b210f550cf3162fe3464502fffe6388c..a5f4c983e2b41408790535c020b2ee9902248e43 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.c +++ b/src/lib-sieve/plugins/include/ext-include-common.c @@ -462,7 +462,7 @@ struct sieve_variable_storage *ext_include_interpreter_get_global_variables * Including a script during code generation */ -bool ext_include_generate_include +int ext_include_generate_include (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, enum ext_include_script_location location, struct sieve_script *script, const struct ext_include_script_info **included_r, bool once) @@ -470,7 +470,7 @@ bool ext_include_generate_include const struct sieve_extension *this_ext = cmd->ext; struct ext_include_context *ext_ctx = (struct ext_include_context *)this_ext->context; - bool result = TRUE; + int result = 1; struct sieve_ast *ast; struct sieve_binary *sbin = cgenv->sbin; struct sieve_generator *gentr = cgenv->gentr; @@ -488,14 +488,14 @@ bool ext_include_generate_include * already. */ if ( sieve_get_errors(ehandler) > 0 ) - return FALSE; + return -1; /* Limit nesting level */ if ( ctx->nesting_depth >= ext_ctx->max_nesting_depth ) { sieve_command_generate_error (gentr, cmd, "cannot nest includes deeper than %d levels", ext_ctx->max_nesting_depth); - return FALSE; + return -1; } /* Check for circular include */ @@ -503,9 +503,18 @@ bool ext_include_generate_include pctx = ctx; while ( pctx != NULL ) { if ( sieve_script_equals(pctx->script, script) ) { + /* Just drop circular include when uploading inactive script; + * not an error + */ + if ( (cgenv->flags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 && + (cgenv->flags & SIEVE_COMPILE_FLAG_ACTIVATED) == 0 ) { + sieve_command_generate_warning + (gentr, cmd, "circular include (ignored during upload)"); + return 0; + } + sieve_command_generate_error(gentr, cmd, "circular include"); - - return FALSE; + return -1; } pctx = pctx->parent; @@ -520,7 +529,7 @@ bool ext_include_generate_include { struct sieve_binary_block *inc_block; const char *script_name = sieve_script_name(script); - enum sieve_compile_flags cpflags = 0; + enum sieve_compile_flags cpflags = cgenv->flags; /* Check whether include limit is exceeded */ if ( ext_include_binary_script_get_count(binctx) >= @@ -528,7 +537,7 @@ bool ext_include_generate_include sieve_command_generate_error(gentr, cmd, "failed to include script '%s': no more than %u includes allowed", str_sanitize(script_name, 80), ext_ctx->max_includes); - return FALSE; + return -1; } /* No, allocate a new block in the binary and mark the script as included. @@ -541,13 +550,15 @@ bool ext_include_generate_include if ( (ast = sieve_parse(script, ehandler, NULL)) == NULL ) { sieve_command_generate_error(gentr, cmd, "failed to parse included script '%s'", str_sanitize(script_name, 80)); - return FALSE; + return -1; } /* Included scripts inherit global variable scope */ (void)ext_include_create_ast_context(this_ext, ast, cmd->ast_node->ast); - if ( location != EXT_INCLUDE_LOCATION_GLOBAL ) + if ( location == EXT_INCLUDE_LOCATION_GLOBAL ) + cpflags &= ~SIEVE_RUNTIME_FLAG_NOGLOBAL; + else cpflags |= SIEVE_RUNTIME_FLAG_NOGLOBAL; /* Validate */ @@ -556,7 +567,7 @@ bool ext_include_generate_include "failed to validate included script '%s'", str_sanitize(script_name, 80)); sieve_ast_unref(&ast); - return FALSE; + return -1; } /* Generate @@ -564,14 +575,14 @@ bool ext_include_generate_include * FIXME: It might not be a good idea to recurse code generation for * included scripts. */ - subgentr = sieve_generator_create(ast, ehandler); + subgentr = sieve_generator_create(ast, ehandler, cpflags); ext_include_initialize_generator_context(cmd->ext, subgentr, ctx, script); if ( sieve_generator_run(subgentr, &inc_block) == NULL ) { sieve_command_generate_error(gentr, cmd, "failed to generate code for included script '%s'", str_sanitize(script_name, 80)); - result = FALSE; + result = -1; } sieve_generator_free(&subgentr); @@ -580,7 +591,7 @@ bool ext_include_generate_include sieve_ast_unref(&ast); } - if ( result ) *included_r = included; + if ( result > 0 ) *included_r = included; return result; } diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h index 4d27e80f0731b1b5b09a292ba5b3e5f229d681ef..c55617ecf6c3ed0ae29447753cc61d4b7a888cd3 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.h +++ b/src/lib-sieve/plugins/include/ext-include-common.h @@ -142,7 +142,7 @@ void ext_include_register_generator_context (const struct sieve_extension *this_ext, const struct sieve_codegen_env *cgenv); -bool ext_include_generate_include +int ext_include_generate_include (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, enum ext_include_script_location location, struct sieve_script *script, const struct ext_include_script_info **included_r, bool once); diff --git a/src/lib-sieve/sieve-commands.h b/src/lib-sieve/sieve-commands.h index feaf9899c2d1492e543d93d5bbb3cad90d3439f0..49d872f3c6fd25e127f8c215bc9d837e6cff1509 100644 --- a/src/lib-sieve/sieve-commands.h +++ b/src/lib-sieve/sieve-commands.h @@ -200,6 +200,8 @@ bool sieve_command_block_exits_unconditionally #define sieve_command_generate_error(gentr, context, ...) \ sieve_generator_error(gentr, (context)->ast_node->source_line, __VA_ARGS__) +#define sieve_command_generate_warning(gentr, context, ...) \ + sieve_generator_warning(gentr, (context)->ast_node->source_line, __VA_ARGS__) /* Utility macros */ diff --git a/src/lib-sieve/sieve-generator.c b/src/lib-sieve/sieve-generator.c index 637a75c843564a0c42def5a817b779a05f4e7eba..d3224507378f44ce89be348660691caa396ada0c 100644 --- a/src/lib-sieve/sieve-generator.c +++ b/src/lib-sieve/sieve-generator.c @@ -76,7 +76,8 @@ struct sieve_generator { }; struct sieve_generator *sieve_generator_create -(struct sieve_ast *ast, struct sieve_error_handler *ehandler) +(struct sieve_ast *ast, struct sieve_error_handler *ehandler, + enum sieve_compile_flags flags) { pool_t pool; struct sieve_generator *gentr; @@ -91,6 +92,7 @@ struct sieve_generator *sieve_generator_create sieve_error_handler_ref(ehandler); gentr->genenv.gentr = gentr; + gentr->genenv.flags = flags; gentr->genenv.ast = ast; sieve_ast_ref(ast); diff --git a/src/lib-sieve/sieve-generator.h b/src/lib-sieve/sieve-generator.h index 34a2c2aa3b9c3dce1c33f4701a16a5616801bace..654fdd43c1ce069d1fce010fb6d4fc5326d012e3 100644 --- a/src/lib-sieve/sieve-generator.h +++ b/src/lib-sieve/sieve-generator.h @@ -16,6 +16,8 @@ struct sieve_codegen_env { struct sieve_generator *gentr; struct sieve_instance *svinst; + enum sieve_compile_flags flags; + struct sieve_script *script; struct sieve_ast *ast; @@ -24,7 +26,8 @@ struct sieve_codegen_env { }; struct sieve_generator *sieve_generator_create - (struct sieve_ast *ast, struct sieve_error_handler *ehandler); + (struct sieve_ast *ast, struct sieve_error_handler *ehandler,\ + enum sieve_compile_flags flags); void sieve_generator_free(struct sieve_generator **generator); /* diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h index 5a3223bd0a9a125b2a655284ee6ccef3dbd038e2..3da2192ce8e01d032dc9c59a0c86804f53c2bd41 100644 --- a/src/lib-sieve/sieve-types.h +++ b/src/lib-sieve/sieve-types.h @@ -65,7 +65,14 @@ enum sieve_error { */ enum sieve_compile_flags { - SIEVE_COMPILE_FLAG_NOGLOBAL = (1<<0) + /* No global extensions are allowed + * (as marked by sieve_global_extensions setting) + */ + SIEVE_COMPILE_FLAG_NOGLOBAL = (1<<0), + /* Script is being uploaded (usually through ManageSieve) */ + SIEVE_COMPILE_FLAG_UPLOADED = (1<<1), + /* Script is being activated (usually through ManageSieve) */ + SIEVE_COMPILE_FLAG_ACTIVATED = (1<<2), }; /* @@ -88,6 +95,9 @@ struct sieve_message_data { */ enum sieve_runtime_flags { + /* No global extensions are allowed + * (as marked by sieve_global_extensions setting) + */ SIEVE_RUNTIME_FLAG_NOGLOBAL = (1<<0) }; diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c index 5e2a8526338ef510e4d4fe8fa440b05a1f2c5da7..bdd1dba0424633bd193e36acb974041ca81cfa11 100644 --- a/src/lib-sieve/sieve.c +++ b/src/lib-sieve/sieve.c @@ -177,9 +177,10 @@ bool sieve_validate static struct sieve_binary *sieve_generate (struct sieve_ast *ast, struct sieve_error_handler *ehandler, - enum sieve_error *error_r) + enum sieve_compile_flags flags, enum sieve_error *error_r) { - struct sieve_generator *generator = sieve_generator_create(ast, ehandler); + struct sieve_generator *generator = + sieve_generator_create(ast, ehandler, flags); struct sieve_binary *sbin = NULL; sbin = sieve_generator_run(generator, NULL); @@ -222,7 +223,7 @@ struct sieve_binary *sieve_compile_script } /* Generate */ - if ( (sbin=sieve_generate(ast, ehandler, error_r)) == NULL ) { + if ( (sbin=sieve_generate(ast, ehandler, flags, error_r)) == NULL ) { sieve_error(ehandler, sieve_script_name(script), "code generation failed"); sieve_ast_unref(&ast); diff --git a/src/lib-sievestorage/sieve-storage-save.c b/src/lib-sievestorage/sieve-storage-save.c index 60ed394fb4ed5aace44f384ba9c3e9bba669c642..9fa2ee5070faefe86c1033045d80582e7445902a 100644 --- a/src/lib-sievestorage/sieve-storage-save.c +++ b/src/lib-sievestorage/sieve-storage-save.c @@ -316,6 +316,24 @@ struct sieve_script *sieve_storage_save_get_tempscript return ctx->scriptobject; } +bool sieve_storage_save_will_activate +(struct sieve_save_context *ctx) +{ + const char *scriptname; + int ret = 0; + + T_BEGIN { + ret = sieve_storage_get_active_scriptfile(ctx->storage, &scriptname); + + if ( ret > 0 ) { + /* Is the requested script active? */ + ret = ( strcmp(ctx->scriptname, scriptname) == 0 ? 1 : 0 ); + } + } T_END; + + return ret; +} + int sieve_storage_save_commit(struct sieve_save_context **ctx) { const char *dest_path; diff --git a/src/lib-sievestorage/sieve-storage-save.h b/src/lib-sievestorage/sieve-storage-save.h index 819b4cb1670d496fb00a6ba023f3747416ddfdfd..3e861d580ca447a11c07b78fc3a32d15d306c21b 100644 --- a/src/lib-sievestorage/sieve-storage-save.h +++ b/src/lib-sievestorage/sieve-storage-save.h @@ -19,6 +19,9 @@ int sieve_storage_save_finish(struct sieve_save_context *ctx); struct sieve_script *sieve_storage_save_get_tempscript (struct sieve_save_context *ctx); +bool sieve_storage_save_will_activate + (struct sieve_save_context *ctx); + void sieve_storage_save_cancel(struct sieve_save_context **ctx); int sieve_storage_save_commit(struct sieve_save_context **ctx); diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c index 58567d6b30c0f40747eceea1b130570a8a9b48e3..a57784c93870983e47767e18a31d46cd918417b0 100644 --- a/src/lib-sievestorage/sieve-storage-script.c +++ b/src/lib-sievestorage/sieve-storage-script.c @@ -164,7 +164,7 @@ static int sieve_storage_read_active_link } static const char *sieve_storage_parse_link -(struct sieve_storage *storage, const char *link) +(struct sieve_storage *storage, const char *link, const char **scriptname_r) { const char *fname, *scriptname, *scriptpath; @@ -200,6 +200,9 @@ static const char *sieve_storage_parse_link return NULL; } + if ( scriptname_r != NULL ) + *scriptname_r = scriptname; + return fname; } @@ -216,7 +219,7 @@ int sieve_storage_get_active_scriptfile return ret; /* Parse the link */ - scriptfile = sieve_storage_parse_link(storage, link); + scriptfile = sieve_storage_parse_link(storage, link, NULL); if (scriptfile == NULL) { /* Obviously someone has been playing with our symlink, @@ -230,6 +233,29 @@ int sieve_storage_get_active_scriptfile return 1; } +int sieve_storage_get_active_scriptname +(struct sieve_storage *storage, const char **name_r) +{ + const char *link; + int ret; + + *name_r = NULL; + + /* Read the active link */ + if ( (ret=sieve_storage_read_active_link(storage, &link)) <= 0 ) + return ret; + + if ( sieve_storage_parse_link(storage, link, name_r) == NULL ) { + /* Obviously someone has been playing with our symlink, + * ignore this situation and report 'no active script'. + * Activation should fix this situation. + */ + return 0; + } + + return 1; +} + struct sieve_script *sieve_storage_get_active_script (struct sieve_storage *storage) { @@ -251,7 +277,7 @@ struct sieve_script *sieve_storage_get_active_script } /* Parse the link */ - scriptfile = sieve_storage_parse_link(storage, link); + scriptfile = sieve_storage_parse_link(storage, link, NULL); if (scriptfile == NULL) { /* Obviously someone has been playing with our symlink, diff --git a/src/lib-sievestorage/sieve-storage-script.h b/src/lib-sievestorage/sieve-storage-script.h index c440086ace6775f9ceab8090bcce4606251979e8..d446b0ecf47248e90ff0152977ee64920fc5d2d5 100644 --- a/src/lib-sievestorage/sieve-storage-script.h +++ b/src/lib-sievestorage/sieve-storage-script.h @@ -16,6 +16,8 @@ const char *sieve_storage_file_get_scriptname int sieve_storage_get_active_scriptfile (struct sieve_storage *storage, const char **file_r); +int sieve_storage_get_active_scriptname + (struct sieve_storage *storage, const char **name_r); struct sieve_script *sieve_storage_get_active_script (struct sieve_storage *storage); diff --git a/src/managesieve/cmd-putscript.c b/src/managesieve/cmd-putscript.c index 1165e6fa5bc2616c3f3b3ca60a98be02bedac901..1fe8c07922863e48553e24f844a6f63d6a17f1f9 100644 --- a/src/managesieve/cmd-putscript.c +++ b/src/managesieve/cmd-putscript.c @@ -174,8 +174,6 @@ static bool cmd_putscript_finish_parsing(struct client_command_context *cmd) if (args[0].type == MANAGESIEVE_ARG_EOL) { struct sieve_script *script; - - /* Last (and only) script */ bool success = TRUE; /* Eat away the trailing CRLF */ @@ -194,9 +192,16 @@ static bool cmd_putscript_finish_parsing(struct client_command_context *cmd) /* Try to compile script */ T_BEGIN { struct sieve_error_handler *ehandler; + enum sieve_compile_flags cpflags = + SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_UPLOADED; struct sieve_binary *sbin; string_t *errors; + /* Mark this as an activation when we are replacing the active script */ + if ( sieve_storage_save_will_activate(ctx->save_ctx) ) { + cpflags |= SIEVE_COMPILE_FLAG_ACTIVATED; + } + /* Prepare error handler */ errors = str_new(default_pool, 1024); ehandler = sieve_strbuf_ehandler_create(client->svinst, errors, TRUE, @@ -204,7 +209,7 @@ static bool cmd_putscript_finish_parsing(struct client_command_context *cmd) /* Compile */ if ( (sbin=sieve_compile_script - (script, ehandler, SIEVE_COMPILE_FLAG_NOGLOBAL, NULL)) == NULL ) { + (script, ehandler, cpflags, NULL)) == NULL ) { client_send_no(client, str_c(errors)); success = FALSE; } else { diff --git a/src/managesieve/cmd-setactive.c b/src/managesieve/cmd-setactive.c index 3e566cd79c23b8e04e5d525a4b1451bfd1d719dd..0cf518f6004f0a365a456b370b7e366007ee8435 100644 --- a/src/managesieve/cmd-setactive.c +++ b/src/managesieve/cmd-setactive.c @@ -2,7 +2,9 @@ */ #include "lib.h" +#include "str.h" +#include "sieve.h" #include "sieve-storage.h" #include "sieve-storage-script.h" @@ -14,39 +16,88 @@ bool cmd_setactive(struct client_command_context *cmd) struct client *client = cmd->client; struct sieve_storage *storage = client->storage; const char *scriptname; - struct sieve_script *script;; + struct sieve_script *script; int ret; /* <scriptname> */ if ( !client_read_string_args(cmd, 1, TRUE, &scriptname) ) return FALSE; + /* Activate, or .. */ if ( *scriptname != '\0' ) { - script = sieve_storage_script_init(storage, scriptname); + string_t *errors = NULL; + bool warnings = FALSE; + bool success = TRUE; + script = sieve_storage_script_init(storage, scriptname); if ( script == NULL ) { client_send_storage_error(client, storage); return TRUE; } + + if ( sieve_storage_script_is_active(script) <= 0 ) { + /* Script is first being activated; compile it again without the UPLOAD + * flag. + */ + T_BEGIN { + struct sieve_error_handler *ehandler; + enum sieve_compile_flags cpflags = + SIEVE_COMPILE_FLAG_NOGLOBAL | SIEVE_COMPILE_FLAG_ACTIVATED; + struct sieve_binary *sbin; + + /* Prepare error handler */ + errors = str_new(default_pool, 1024); + ehandler = sieve_strbuf_ehandler_create(client->svinst, errors, TRUE, + client->set->managesieve_max_compile_errors); + + /* Compile */ + if ( (sbin=sieve_compile_script + (script, ehandler, cpflags, NULL)) == NULL ) { + success = FALSE; + } else { + sieve_close(&sbin); + } + + warnings = ( sieve_get_warnings(ehandler) > 0 ); + sieve_error_handler_unref(&ehandler); + } T_END; + } - ret = sieve_storage_script_activate(script); - if ( ret < 0 ) - client_send_storage_error(client, storage); - else - client_send_ok(client, ret ? - "Setactive completed." : - "Script is already active."); + /* Activate only when script is valid (or already active) */ + if ( success ) { + /* Refresh activation no matter what; this can also resolve some erroneous + * situations. + */ + ret = sieve_storage_script_activate(script); + if ( ret < 0 ) { + client_send_storage_error(client, storage); + } else { + if ( warnings ) { + client_send_okresp(client, "WARNINGS", str_c(errors)); + } else { + client_send_ok(client, ( ret > 0 ? + "Setactive completed." : + "Script is already active." )); + } + } + } else { + client_send_no(client, str_c(errors)); + } + if ( errors != NULL ) + str_free(&errors); sieve_script_unref(&script); + + /* ... deactivate */ } else { ret = sieve_storage_deactivate(storage); if ( ret < 0 ) client_send_storage_error(client, storage); else - client_send_ok(client, ret ? + client_send_ok(client, ( ret > 0 ? "Active script is now deactivated." : - "No scripts currently active."); + "No scripts currently active." )); } return TRUE;