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;
}
static bool _sieve_binary_save
(struct sieve_binary *sbin, struct ostream *stream)
{
	struct sieve_binary_header header;
	struct sieve_binary_extension_reg *const *regs;
	struct sieve_binary_block *ext_block;
	unsigned int ext_count, blk_count, i;
	uoff_t block_index;

	blk_count = sieve_binary_block_count(sbin);

	/* Signal all extensions to finish generating their blocks */

	regs = array_get(&sbin->extensions, &ext_count);
	for ( i = 0; i < ext_count; i++ ) {
		const struct sieve_binary_extension *binext = regs[i]->binext;

		if ( binext != NULL && binext->binary_save != NULL )
			binext->binary_save(regs[i]->extension, sbin, regs[i]->context);
	}

	/* Create header */

	header.magic = SIEVE_BINARY_MAGIC;
	header.version_major = SIEVE_BINARY_VERSION_MAJOR;
	header.version_minor = SIEVE_BINARY_VERSION_MINOR;
	header.blocks = blk_count;

	if ( !_save_aligned(sbin, stream, &header, sizeof(header), NULL) ) {
		sieve_sys_error(sbin->svinst, "binary save: failed to save header");
		return FALSE;
	}

	/* Skip block index for now */

	if ( !_save_skip_aligned(sbin, stream,
		sizeof(struct sieve_binary_block_index) * blk_count, &block_index) )
		return FALSE;

	/* Create block containing all used extensions */

	ext_block = sieve_binary_block_get(sbin, SBIN_SYSBLOCK_EXTENSIONS);
	i_assert( ext_block != NULL );
	sieve_binary_block_clear(ext_block);

	ext_count = array_count(&sbin->linked_extensions);
	sieve_binary_emit_unsigned(ext_block, ext_count);

	for ( i = 0; i < ext_count; i++ ) {
		struct sieve_binary_extension_reg * const *ext
			= array_idx(&sbin->linked_extensions, i);

		sieve_binary_emit_cstring
			(ext_block, sieve_extension_name((*ext)->extension));
		sieve_binary_emit_unsigned
			(ext_block, sieve_extension_version((*ext)->extension));
		sieve_binary_emit_unsigned(ext_block, (*ext)->block_id);
	}

	/* Save all blocks into the binary */

	for ( i = 0; i < blk_count; i++ ) {
		if ( !_save_block(sbin, stream, i) )
			return FALSE;
	}

	/* Create the block index */
	o_stream_seek(stream, block_index);
	for ( i = 0; i < blk_count; i++ ) {
		if ( !_save_block_index_record(sbin, stream, i) )
			return FALSE;
	}

	return TRUE;
}