struct sieve_binary *sieve_open_script (struct sieve_script *script, struct sieve_error_handler *ehandler, enum sieve_compile_flags flags, enum sieve_error *error_r) { struct sieve_instance *svinst = sieve_script_svinst(script); struct sieve_binary *sbin; T_BEGIN { /* Then try to open the matching binary */ sbin = sieve_script_binary_load(script, error_r); if (sbin != NULL) { /* Ok, it exists; now let's see if it is up to date */ if ( !sieve_binary_up_to_date(sbin, flags) ) { /* Not up to date */ if ( svinst->debug ) { sieve_sys_debug(svinst, "Script binary %s is not up-to-date", sieve_binary_path(sbin)); } sieve_binary_unref(&sbin); sbin = NULL; } } /* If the binary does not exist or is not up-to-date, we need * to (re-)compile. */ if ( sbin != NULL ) { if ( svinst->debug ) { sieve_sys_debug(svinst, "Script binary %s successfully loaded", sieve_binary_path(sbin)); } } else { sbin = sieve_compile_script(script, ehandler, flags, error_r); if ( sbin != NULL ) { if ( svinst->debug ) { sieve_sys_debug(svinst, "Script `%s' from %s successfully compiled", sieve_script_name(script), sieve_script_location(script)); } } } } T_END; return sbin; }
struct sieve_binary *sieve_compile (struct sieve_instance *svinst, const char *script_location, const char *script_name, struct sieve_error_handler *ehandler, enum sieve_compile_flags flags, enum sieve_error *error_r) { struct sieve_script *script; struct sieve_binary *sbin; enum sieve_error error; if ( (script = sieve_script_create_open (svinst, script_location, script_name, &error)) == NULL ) { if (error_r != NULL) *error_r = error; switch ( error ) { case SIEVE_ERROR_NOT_FOUND: sieve_error(ehandler, script_name, "script not found"); break; default: sieve_internal_error(ehandler, script_name, "failed to open script"); } return NULL; } sbin = sieve_compile_script(script, ehandler, flags, error_r); if ( svinst->debug && sbin != NULL ) { sieve_sys_debug(svinst, "Script `%s' from %s successfully compiled", sieve_script_name(script), sieve_script_location(script)); } sieve_script_unref(&script); return sbin; }
struct sieve_extprograms_config *sieve_extprograms_config_init (const struct sieve_extension *ext) { struct sieve_instance *svinst = ext->svinst; struct sieve_extprograms_config *ext_config; const char *extname = sieve_extension_name(ext); const char *bin_dir, *socket_dir, *input_eol; sieve_number_t execute_timeout; extname = strrchr(extname, '.'); i_assert(extname != NULL); extname++; bin_dir = sieve_setting_get (svinst, t_strdup_printf("sieve_%s_bin_dir", extname)); socket_dir = sieve_setting_get (svinst, t_strdup_printf("sieve_%s_socket_dir", extname)); input_eol = sieve_setting_get (svinst, t_strdup_printf("sieve_%s_input_eol", extname)); ext_config = i_new(struct sieve_extprograms_config, 1); ext_config->execute_timeout = SIEVE_EXTPROGRAMS_DEFAULT_EXEC_TIMEOUT_SECS; if ( bin_dir == NULL && socket_dir == NULL ) { if ( svinst->debug ) { sieve_sys_debug(svinst, "%s extension: " "no bin or socket directory specified; extension is unconfigured " "(both sieve_%s_bin_dir and sieve_%s_socket_dir are not set)", sieve_extension_name(ext), extname, extname); } } else { ext_config->bin_dir = i_strdup(bin_dir); ext_config->socket_dir = i_strdup(socket_dir); if (sieve_setting_get_duration_value (svinst, t_strdup_printf("sieve_%s_exec_timeout", extname), &execute_timeout)) { ext_config->execute_timeout = execute_timeout; } ext_config->default_input_eol = SIEVE_EXTPROGRAMS_EOL_CRLF; if (input_eol != NULL && strcasecmp(input_eol, "lf") == 0) ext_config->default_input_eol = SIEVE_EXTPROGRAMS_EOL_LF; } if ( sieve_extension_is(ext, vnd_pipe_extension) ) ext_config->copy_ext = sieve_ext_copy_get_extension(ext->svinst); if ( sieve_extension_is(ext, vnd_execute_extension) ) ext_config->var_ext = sieve_ext_variables_get_extension(ext->svinst); return ext_config; }
static int _read_extensions(struct sieve_binary_block *sblock) { struct sieve_binary *sbin = sblock->sbin; sieve_size_t offset = 0; unsigned int i, count; bool result = 1; if ( !sieve_binary_read_unsigned(sblock, &offset, &count) ) return -1; for ( i = 0; result > 0 && i < count; i++ ) { T_BEGIN { string_t *extension; const struct sieve_extension *ext; unsigned int version; if ( sieve_binary_read_string(sblock, &offset, &extension) ) { ext = sieve_extension_get_by_name(sbin->svinst, str_c(extension)); if ( ext == NULL ) { sieve_sys_error(sbin->svinst, "binary open: binary %s requires unknown extension `%s'", sbin->path, str_sanitize(str_c(extension), 128)); result = 0; } else { struct sieve_binary_extension_reg *ereg = NULL; (void) sieve_binary_extension_register(sbin, ext, &ereg); if ( !sieve_binary_read_unsigned(sblock, &offset, &version) || !sieve_binary_read_unsigned(sblock, &offset, &ereg->block_id) ) { result = -1; } else if ( !sieve_extension_version_is(ext, version) ) { sieve_sys_debug(sbin->svinst, "binary open: binary %s was compiled with different version " "of the `%s' extension (compiled v%d, expected v%d;" "automatically fixed when re-compiled)", sbin->path, sieve_extension_name(ext), version, sieve_extension_version(ext)); result = 0; } } } else result = -1; } T_END; } return result; }
struct sieve_instance *sieve_init (const struct sieve_environment *env, const struct sieve_callbacks *callbacks, void *context, bool debug) { struct sieve_instance *svinst; const char *domain; pool_t pool; /* Create Sieve engine instance */ pool = pool_alloconly_create("sieve", 8192); svinst = p_new(pool, struct sieve_instance, 1); svinst->pool = pool; svinst->callbacks = callbacks; svinst->context = context; svinst->debug = debug; svinst->base_dir = p_strdup_empty(pool, env->base_dir); svinst->username = p_strdup_empty(pool, env->username); svinst->home_dir = p_strdup_empty(pool, env->home_dir); svinst->temp_dir = p_strdup_empty(pool, env->temp_dir); svinst->flags = env->flags; svinst->env_location = env->location; svinst->delivery_phase = env->delivery_phase; /* Determine domain */ if ( env->domainname != NULL && *(env->domainname) != '\0' ) { domain = env->domainname; } else { /* Fall back to parsing username localpart@domain */ domain = svinst->username == NULL ? NULL : strchr(svinst->username, '@'); if ( domain == NULL || *(domain+1) == '\0' ) { /* Fall back to parsing hostname host.domain */ domain = ( env->hostname != NULL ? strchr(env->hostname, '.') : NULL ); if ( domain == NULL || *(domain+1) == '\0' || strchr(domain+1, '.') == NULL ) { /* Fall back to bare hostname */ domain = env->hostname; } else { domain++; } } else { domain++; } } svinst->hostname = p_strdup_empty(pool, env->hostname); svinst->domainname = p_strdup(pool, domain); sieve_errors_init(svinst); if ( debug ) { sieve_sys_debug(svinst, "%s version %s initializing", PIGEONHOLE_NAME, PIGEONHOLE_VERSION_FULL); } /* Read configuration */ sieve_settings_load(svinst); /* Initialize extensions */ if ( !sieve_extensions_init(svinst) ) { sieve_deinit(&svinst); return NULL; } /* Initialize storage classes */ sieve_storages_init(svinst); /* Initialize plugins */ sieve_plugins_load(svinst, NULL, NULL); /* Configure extensions */ sieve_extensions_configure(svinst); return svinst; }
static bool _sieve_binary_open(struct sieve_binary *sbin) { bool result = TRUE; off_t offset = 0; const struct sieve_binary_header *header; struct sieve_binary_block *ext_block; unsigned int i, blk_count; int ret; /* Verify header */ T_BEGIN { header = LOAD_HEADER(sbin, &offset, const struct sieve_binary_header); /* Check header presence */ if ( header == NULL ) { sieve_sys_error(sbin->svinst, "binary_open: file %s is not large enough to contain the header.", sbin->path); result = FALSE; /* Check header validity */ } else if ( header->magic != SIEVE_BINARY_MAGIC ) { if ( header->magic != SIEVE_BINARY_MAGIC_OTHER_ENDIAN ) sieve_sys_error(sbin->svinst, "binary_open: binary %s has corrupted header " "(0x%08x) or it is not a Sieve binary", sbin->path, header->magic); else if ( sbin->svinst->debug ) sieve_sys_debug(sbin->svinst, "binary open: binary %s stored with in different endian format " "(automatically fixed when re-compiled)", sbin->path); result = FALSE; /* Check binary version */ } else if ( result && ( header->version_major != SIEVE_BINARY_VERSION_MAJOR || header->version_minor != SIEVE_BINARY_VERSION_MINOR ) ) { /* Binary is of different version. Caller will have to recompile */ if ( sbin->svinst->debug ) { sieve_sys_debug(sbin->svinst, "binary open: binary %s stored with different binary version %d.%d " "(!= %d.%d; automatically fixed when re-compiled)", sbin->path, (int) header->version_major, header->version_minor, SIEVE_BINARY_VERSION_MAJOR, SIEVE_BINARY_VERSION_MINOR); } result = FALSE; /* Check block content */ } else if ( result && header->blocks == 0 ) { sieve_sys_error(sbin->svinst, "binary open: binary %s is corrupt: it contains no blocks", sbin->path); result = FALSE; /* Valid */ } else { blk_count = header->blocks; } } T_END; if ( !result ) return FALSE; /* Load block index */ for ( i = 0; i < blk_count && result; i++ ) { T_BEGIN { if ( !_read_block_index_record(sbin, &offset, i) ) { result = FALSE; } } T_END; } if ( !result ) return FALSE; /* Load extensions used by this binary */ T_BEGIN { ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS); if ( ext_block == NULL ) { result = FALSE; } else if ( (ret=_read_extensions(ext_block)) <= 0 ) { if ( ret < 0 ) { sieve_sys_error(sbin->svinst, "binary open: binary %s is corrupt: failed to load extension block", sbin->path); } result = FALSE; } } T_END; return result; }
int sieve_binary_save (struct sieve_binary *sbin, const char *path, bool update, mode_t save_mode, enum sieve_error *error_r) { int result, fd; string_t *temp_path; struct ostream *stream; if ( error_r != NULL ) *error_r = SIEVE_ERROR_NONE; /* Check whether saving is necessary */ if ( !update && sbin->path != NULL && strcmp(sbin->path, path) == 0 ) { if ( sbin->svinst->debug ) { sieve_sys_debug(sbin->svinst, "binary save: not saving binary %s, " "because it is already stored", path); } return 0; } /* Open it as temp file first, as not to overwrite an existing just yet */ temp_path = t_str_new(256); str_append(temp_path, path); str_append_c(temp_path, '.'); fd = safe_mkstemp_hostpid(temp_path, save_mode, (uid_t)-1, (gid_t)-1); if ( fd < 0 ) { if ( errno == EACCES ) { sieve_sys_error(sbin->svinst, "binary save: failed to create temporary file: %s", eacces_error_get_creating("open", str_c(temp_path))); if ( error_r != NULL ) *error_r = SIEVE_ERROR_NO_PERMISSION; } else { sieve_sys_error(sbin->svinst, "binary save: failed to create temporary file: open(%s) failed: %m", str_c(temp_path)); if ( error_r != NULL ) *error_r = SIEVE_ERROR_TEMP_FAILURE; } return -1; } /* Save binary */ result = 1; stream = o_stream_create_fd(fd, 0, FALSE); if ( !_sieve_binary_save(sbin, stream) ) { result = -1; if ( error_r != NULL ) *error_r = SIEVE_ERROR_TEMP_FAILURE; } o_stream_destroy(&stream); /* Close saved binary */ if ( close(fd) < 0 ) { sieve_sys_error(sbin->svinst, "binary save: failed to close temporary file: " "close(fd=%s) failed: %m", str_c(temp_path)); } /* Replace any original binary atomically */ if ( result && (rename(str_c(temp_path), path) < 0) ) { if ( errno == EACCES ) { sieve_sys_error(sbin->svinst, "binary save: failed to save binary: %s", eacces_error_get_creating("rename", path)); if ( error_r != NULL ) *error_r = SIEVE_ERROR_NO_PERMISSION; } else { sieve_sys_error(sbin->svinst, "binary save: failed to save binary: " "rename(%s, %s) failed: %m", str_c(temp_path), path); if ( error_r != NULL ) *error_r = SIEVE_ERROR_TEMP_FAILURE; } result = -1; } if ( result < 0 ) { /* Get rid of temp output (if any) */ if ( unlink(str_c(temp_path)) < 0 && errno != ENOENT ) { sieve_sys_error(sbin->svinst, "binary save: failed to clean up after error: unlink(%s) failed: %m", str_c(temp_path)); } } else { if ( sbin->path == NULL ) { sbin->path = p_strdup(sbin->pool, path); } } return result; }