int sieve_file_storage_save_continue
(struct sieve_storage_save_context *sctx)
{
	struct sieve_file_save_context *fsctx =
		(struct sieve_file_save_context *)sctx;

	switch (o_stream_send_istream(fsctx->output, sctx->input)) {
	case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
		return 0;
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
		i_unreached();
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
		sieve_storage_set_critical(sctx->storage,
			"save: read(%s) failed: %s",
			i_stream_get_name(sctx->input),
			i_stream_get_error(sctx->input));
		return -1;
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
		sieve_storage_set_critical(sctx->storage,
			"save: write(%s) failed: %s", fsctx->tmp_path,
			o_stream_get_error(fsctx->output));
		return -1;
	}
	return 0;
}
static int sieve_file_storage_create_tmp
(struct sieve_file_storage *fstorage, const char *scriptname,
	const char **fpath_r)
{
	struct sieve_storage *storage = &fstorage->storage;
	struct stat st;
	unsigned int prefix_len;
	const char *tmp_fname = NULL;
	string_t *path;
	int fd;

	path = t_str_new(256);
	str_append(path, fstorage->path);
	str_append(path, "/tmp/");
	prefix_len = str_len(path);

	for (;;) {
		tmp_fname = sieve_generate_tmp_filename(scriptname);
		str_truncate(path, prefix_len);
		str_append(path, tmp_fname);

		/* stat() first to see if it exists. pretty much the only
		   possibility of that happening is if time had moved
		   backwards, but even then it's highly unlikely. */
		if (stat(str_c(path), &st) == 0) {
			/* try another file name */
		} else if (errno != ENOENT) {
			sieve_storage_set_critical(storage, "save: "
				"stat(%s) failed: %m", str_c(path));
			return -1;
		} else {
			/* doesn't exist */
			mode_t old_mask = umask(0777 & ~(fstorage->file_create_mode));
			fd = open(str_c(path),
				O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777);
			umask(old_mask);

			if (fd != -1 || errno != EEXIST)
				break;
			/* race condition between stat() and open().
				highly unlikely. */
		}
	}

	*fpath_r = str_c(path);
	if (fd == -1) {
		if (ENOQUOTA(errno)) {
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NO_QUOTA,
				"Not enough disk quota");
		} else {
			sieve_storage_set_critical(storage, "save: "
				"open(%s) failed: %m", str_c(path));
		}
	}

	return fd;
}
示例#3
0
static int sieve_dict_storage_init
(struct sieve_storage *storage, const char *const *options,
	enum sieve_error *error_r)
{
	struct sieve_dict_storage *dstorage =
		(struct sieve_dict_storage *)storage;
	struct sieve_instance *svinst = storage->svinst;
	const char *uri = storage->location, *username = NULL;

	if ( options != NULL ) {
		while ( *options != NULL ) {
			const char *option = *options;

			if ( strncasecmp(option, "user="******"Invalid option `%s'", option);
				*error_r = SIEVE_ERROR_TEMP_FAILURE;
				return -1;
			}

			options++;
		}
	}

	if ( username == NULL ) {
		if ( svinst->username == NULL ) {
			sieve_storage_set_critical(storage,
				"No username specified");
			*error_r = SIEVE_ERROR_TEMP_FAILURE;
			return -1;
		}
		username = svinst->username;
	}

	if ( svinst->base_dir == NULL ) {
		sieve_storage_set_critical(storage,
			"BUG: Sieve interpreter is initialized without a base_dir");
		*error_r = SIEVE_ERROR_TEMP_FAILURE;
		return -1;
	}

	sieve_storage_sys_debug(storage,
		"user=%s, uri=%s", username, uri);

	dstorage->uri = p_strdup(storage->pool, uri);
	dstorage->username = p_strdup(storage->pool, username);

	storage->location = p_strconcat(storage->pool,
		SIEVE_DICT_STORAGE_DRIVER_NAME, ":", storage->location,
		";user=", username, NULL);

	return 0;
}
int sieve_file_storage_save_finish
(struct sieve_storage_save_context *sctx)
{
	struct sieve_file_save_context *fsctx =
		(struct sieve_file_save_context *)sctx;
	struct sieve_storage *storage = sctx->storage;
	int output_errno;

	if ( sctx->failed && fsctx->fd == -1 ) {
		/* tmp file creation failed */
		return -1;
	}

	T_BEGIN {
		output_errno = fsctx->output->stream_errno;
		o_stream_destroy(&fsctx->output);

		if ( fsync(fsctx->fd) < 0 ) {
			sieve_storage_set_critical(storage, "save: "
				"fsync(%s) failed: %m", fsctx->tmp_path);
			sctx->failed = TRUE;
		}
		if ( close(fsctx->fd) < 0 ) {
			sieve_storage_set_critical(storage, "save: "
				"close(%s) failed: %m", fsctx->tmp_path);
			sctx->failed = TRUE;
		}
		fsctx->fd = -1;

		if ( sctx->failed ) {
			/* delete the tmp file */
			if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) {
				sieve_storage_sys_warning(storage, "save: "
					"unlink(%s) failed: %m", fsctx->tmp_path);
			}

			fsctx->tmp_path = NULL;
			
			errno = output_errno;
			if ( ENOQUOTA(errno) ) {
				sieve_storage_set_error(storage,
					SIEVE_ERROR_NO_QUOTA,
					"Not enough disk quota");
			} else if ( errno != 0 ) {
				sieve_storage_set_critical(storage, "save: "
					"write(%s) failed: %m", fsctx->tmp_path);
			}
		}
	} T_END;

	return ( sctx->failed ? -1 : 0 );
}
示例#5
0
int sieve_dict_storage_get_dict
(struct sieve_dict_storage *dstorage, struct dict **dict_r,
	enum sieve_error *error_r)
{
	struct sieve_storage *storage = &dstorage->storage;
	struct sieve_instance *svinst = storage->svinst;
	struct dict_settings dict_set;
	const char *error;
	int ret;

	if ( dstorage->dict == NULL ) {
		memset(&dict_set, 0, sizeof(dict_set));
		dict_set.username = dstorage->username;
		dict_set.base_dir = svinst->base_dir;
		ret = dict_init(dstorage->uri, &dict_set, &dstorage->dict, &error);
		if ( ret < 0 ) {
			sieve_storage_set_critical(storage,
				"Failed to initialize dict with data `%s' for user `%s': %s",
				dstorage->uri, dstorage->username, error);
			*error_r = SIEVE_ERROR_TEMP_FAILURE;
			return -1;
		}
	}

	*dict_r = dstorage->dict;
	return 0;
}
static int sieve_ldap_script_open
(struct sieve_script *script, enum sieve_error *error_r)
{
	struct sieve_ldap_script *lscript =
		(struct sieve_ldap_script *)script;
	struct sieve_storage *storage = script->storage;
	struct sieve_ldap_storage *lstorage =
		(struct sieve_ldap_storage *)storage;
	int ret;

	if ( sieve_ldap_db_connect(lstorage->conn) < 0 ) {
		sieve_storage_set_critical(storage,
			"Failed to connect to LDAP database");
		*error_r = storage->error_code;
		return -1;
	}

	if ( (ret=sieve_ldap_db_lookup_script(
		lstorage->conn, script->name, &lscript->dn, &lscript->modattr)) <= 0 ) {
		if ( ret == 0 ) {
			sieve_script_sys_debug(script, "Script not found");
			sieve_script_set_error(script,
				SIEVE_ERROR_NOT_FOUND,
				"Sieve script not found");
		} else {
			sieve_script_set_internal_error(script);
		}
		*error_r = script->storage->error_code;
		return -1;
	}

	return 0;
}
static int sieve_file_storage_script_move
(struct sieve_file_save_context *fsctx, const char *dst)
{
	struct sieve_storage_save_context *sctx = &fsctx->context;
	struct sieve_storage *storage = sctx->storage;
	int result = 0;

	T_BEGIN {

		/* Using rename() to ensure existing files are replaced
		 * without conflicts with other processes using the same
		 * file. The kernel wont fully delete the original until
		 * all processes have closed the file.
		 */
		if (rename(fsctx->tmp_path, dst) == 0)
			result = 0;
		else {
			result = -1;
			if ( ENOQUOTA(errno) ) {
				sieve_storage_set_error(storage,
					SIEVE_ERROR_NO_QUOTA,
					"Not enough disk quota");
			} else if ( errno == EACCES ) {
				sieve_storage_set_critical(storage, "save: "
					"Failed to save Sieve script: "
					"%s", eacces_error_get("rename", dst));
			} else {
				sieve_storage_set_critical(storage, "save: "
					"rename(%s, %s) failed: %m", fsctx->tmp_path, dst);
			}
		}

		/* Always destroy temp file */
		if (unlink(fsctx->tmp_path) < 0 && errno != ENOENT) {
			sieve_storage_sys_warning(storage, "save: "
				"unlink(%s) failed: %m", fsctx->tmp_path);
		}
	} T_END;

	return result;
}
示例#8
0
bool cmd_getscript(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	struct cmd_getscript_context *ctx;
	const char *scriptname;
	enum sieve_error error;

	/* <scriptname> */
	if ( !client_read_string_args(cmd, TRUE, 1, &scriptname) )
		return FALSE;

	ctx = p_new(cmd->pool, struct cmd_getscript_context, 1);
	ctx->cmd = cmd;
	ctx->client = client;
	ctx->storage = client->storage;
	ctx->failed = FALSE;

	ctx->script = sieve_storage_open_script
		(client->storage, scriptname, NULL);
	if (ctx->script == NULL) {
		ctx->failed = TRUE;
		return cmd_getscript_finish(ctx);
	}

	if ( sieve_script_get_stream
		(ctx->script, &ctx->script_stream, &error) < 0 ) {
		if ( error == SIEVE_ERROR_NOT_FOUND )
			sieve_storage_set_error(client->storage, error, "Script does not exist.");
		ctx->failed = TRUE;
		return cmd_getscript_finish(ctx);
	}

	if ( sieve_script_get_size(ctx->script, &ctx->script_size) <= 0 ) {
		sieve_storage_set_critical(ctx->storage,
			"failed to obtain script size for script `%s' from %s",
			sieve_script_name(ctx->script), sieve_script_location(ctx->script));
		ctx->failed = TRUE;
		return cmd_getscript_finish(ctx);
	}

	i_assert(ctx->script_stream->v_offset == 0);

	client_send_line
		(client, t_strdup_printf("{%"PRIuUOFF_T"}", ctx->script_size));

	client->command_pending = TRUE;
	cmd->func = cmd_getscript_continue;
	cmd->context = ctx;

	return cmd_getscript_continue(cmd);
}
示例#9
0
static bool cmd_getscript_continue(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	struct cmd_getscript_context *ctx = cmd->context;

	switch (o_stream_send_istream(client->output, ctx->script_stream)) {
	case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
		if ( ctx->script_stream->v_offset != ctx->script_size && !ctx->failed ) {
			/* Input stream gave less data than expected */
			sieve_storage_set_critical(ctx->storage,
				"GETSCRIPT for script `%s' from %s got too little data: "
				"%"PRIuUOFF_T" vs %"PRIuUOFF_T, sieve_script_name(ctx->script),
				sieve_script_location(ctx->script), ctx->script_stream->v_offset, ctx->script_size);

			client_disconnect(ctx->client, "GETSCRIPT failed");
			ctx->failed = TRUE;
		}
		break;
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
		i_unreached();
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
		return FALSE;
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
		sieve_storage_set_critical(ctx->storage,
			"o_stream_send_istream() failed for script `%s' from %s: %s",
			sieve_script_name(ctx->script),
			sieve_script_location(ctx->script),
			i_stream_get_error(ctx->script_stream));
		ctx->failed = TRUE;
		break;
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
		client_disconnect(ctx->client,
			io_stream_get_disconnect_reason(client->input, client->output));
		ctx->failed = TRUE;
		break;
	}
	return cmd_getscript_finish(ctx);
}
struct sieve_script *sieve_file_storage_save_get_tempscript
(struct sieve_storage_save_context *sctx)
{
	struct sieve_file_save_context *fsctx =
		(struct sieve_file_save_context *)sctx;
	struct sieve_file_storage *fstorage = 
		(struct sieve_file_storage *)sctx->storage;
	struct sieve_file_script *tmpscript;
	enum sieve_error error;
	const char *scriptname;

	if (sctx->failed)
		return NULL;

	if ( sctx->scriptobject != NULL )
		return sctx->scriptobject;

	scriptname =
		( sctx->scriptname == NULL ? "" : sctx->scriptname );
	tmpscript = sieve_file_script_open_from_path
		(fstorage, fsctx->tmp_path, scriptname, &error);

	if ( tmpscript == NULL ) {
		if ( error == SIEVE_ERROR_NOT_FOUND ) {
			sieve_storage_set_critical(sctx->storage, "save: "
				"Temporary script file `%s' got lost, "
				"which should not happen (possibly deleted externally).",
				fsctx->tmp_path);
		} else {
			sieve_storage_set_critical(sctx->storage, "save: "
				"Failed to open temporary script file `%s'",
				fsctx->tmp_path);
		}
		return NULL;
	}

	return &tmpscript->script;
}
示例#11
0
static bool cmd_getscript_continue(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	struct cmd_getscript_context *ctx = cmd->context;
	off_t ret;

	ret = o_stream_send_istream(client->output, ctx->script_stream);

	if ( ret < 0 ) {
		sieve_storage_set_critical(ctx->storage,
			"o_stream_send_istream(%s) failed: %m", 
			sieve_script_filename(ctx->script));
		ctx->failed = TRUE;
		return cmd_getscript_finish(ctx);
	}

	ctx->script_offset += ret;

	if ( ctx->script_offset != ctx->script_size && !ctx->failed ) {
		/* unfinished */
		if ( !i_stream_have_bytes_left(ctx->script_stream) ) {
			/* Input stream gave less data than expected */
			sieve_storage_set_critical(ctx->storage,
				"GETSCRIPT for SCRIPT %s got too little data: "
				"%"PRIuUOFF_T" vs %"PRIuUOFF_T, sieve_script_name(ctx->script),
				ctx->script_offset, ctx->script_size);

			client_disconnect(ctx->client, "GETSCRIPT failed");
			ctx->failed = TRUE;
			return cmd_getscript_finish(ctx);
		}

		return FALSE;
	}

	return cmd_getscript_finish(ctx);
}
示例#12
0
int sieve_storage_quota_havespace
(struct sieve_storage *storage, const char *scriptname, size_t size,
	enum sieve_storage_quota *quota_r, uint64_t *limit_r)
{	
	struct dirent *dp;
	DIR *dirp;
	uint64_t script_count = 1;
	uint64_t script_storage = size;
	int result = 1;

	*limit_r = 0;
	*quota_r = SIEVE_STORAGE_QUOTA_NONE;

	/* Check the script size */
	if ( !sieve_storage_quota_validsize(storage, size, limit_r) ) {
		*quota_r = SIEVE_STORAGE_QUOTA_MAXSIZE;
        return 0;
    }

	/* Do we need to scan the storage (quota enabled) ? */
	if ( storage->max_scripts == 0 && storage->max_storage == 0 ) {
		return 1;
	}

	/* Open the directory */
	if ( (dirp = opendir(storage->dir)) == NULL ) {
		sieve_storage_set_critical
			(storage, "quota: opendir(%s) failed: %m", storage->dir);
		return -1;
	}

	/* Scan all files */
	for (;;) {
		const char *name;
		bool replaced = FALSE;

		/* Read next entry */
		errno = 0;
		if ( (dp = readdir(dirp)) == NULL ) {
			if ( errno != 0 ) {
				sieve_storage_set_critical
		            (storage, "quota: readdir(%s) failed: %m", storage->dir);
				result = -1;
			}
			break;
		}

		/* Parse filename */
		name = sieve_storage_file_get_scriptname(storage, dp->d_name);	

		/* Ignore non-script files */
		if ( name == NULL )
			continue;
			
		/* Don't list our active sieve script link if the link 
		 * resides in the script dir (generally a bad idea).
		 */
		if ( *(storage->link_path) == '\0' && 
			strcmp(storage->active_fname, dp->d_name) == 0 )
			continue;

		if ( strcmp(name, scriptname) == 0 )
			replaced = TRUE;

		/* Check cont quota if necessary */
		if ( storage->max_scripts > 0 ) {
			if ( !replaced ) {
				script_count++;
			
				if ( script_count > storage->max_scripts ) {				
					*quota_r = SIEVE_STORAGE_QUOTA_MAXSCRIPTS;
					*limit_r = storage->max_scripts;
					result = 0;
					break;
				}
			}
		}

		/* Check storage quota if necessary */
		if ( storage->max_storage > 0 ) { 
			const char *path;
			struct stat st;
			int ret;

			path = t_strconcat(storage->dir, "/", dp->d_name, NULL);
		
			if ( (ret=stat(path, &st)) < 0 ) {
				i_warning
					("sieve-storage: quota: stat(%s) failed: %m", path);
				continue;
			}

			if ( !replaced ) {
				script_storage += st.st_size;

				if ( script_storage > storage->max_storage ) {
					*quota_r = SIEVE_STORAGE_QUOTA_MAXSTORAGE;
					*limit_r = storage->max_storage;
					result = 0;
					break;	
				}
			}
		}
	}

	/* Close directory */
	if ( closedir(dirp) < 0 ) {
		sieve_storage_set_critical
			(storage, "quota: closedir(%s) failed: %m", storage->dir);
	}

	return result;
}
int sieve_ldap_storage_read_settings
(struct sieve_ldap_storage *lstorage, const char *config_path)
{
    struct sieve_storage *storage = &lstorage->storage;
    const char *str, *error;
    struct stat st;

    if ( stat(config_path, &st) < 0 ) {
        sieve_storage_sys_error(storage,
                                "Failed to read LDAP storage config: "
                                "stat(%s) failed: %m", config_path);
        return -1;
    }

    lstorage->set = default_settings;
    lstorage->set_mtime = st.st_mtime;

    if (!settings_read_nosection
            (config_path, parse_setting, lstorage, &error)) {
        sieve_storage_set_critical(storage,
                                   "Failed to read LDAP storage config `%s': %s",
                                   config_path, error);
        return -1;
    }

    if (lstorage->set.base == NULL) {
        sieve_storage_set_critical(storage,
                                   "Invalid LDAP storage config `%s': "
                                   "No search base given", config_path);
        return -1;
    }

    if (lstorage->set.uris == NULL && lstorage->set.hosts == NULL) {
        sieve_storage_set_critical(storage,
                                   "Invalid LDAP storage config `%s': "
                                   "No uris or hosts set", config_path);
        return -1;
    }

    if (*lstorage->set.ldaprc_path != '\0') {
        str = getenv("LDAPRC");
        if (str != NULL && strcmp(str, lstorage->set.ldaprc_path) != 0) {
            sieve_storage_set_critical(storage,
                                       "Invalid LDAP storage config `%s': "
                                       "Multiple different ldaprc_path settings not allowed "
                                       "(%s and %s)", config_path, str, lstorage->set.ldaprc_path);
            return -1;
        }
        env_put(t_strconcat("LDAPRC=", lstorage->set.ldaprc_path, NULL));
    }

    if ( ldap_deref_from_str
            (lstorage->set.deref, &lstorage->set.ldap_deref) < 0 ) {
        sieve_storage_set_critical(storage,
                                   "Invalid LDAP storage config `%s': "
                                   "Invalid deref option `%s'",
                                   config_path, lstorage->set.deref);;
    }

    if ( ldap_scope_from_str
            (lstorage->set.scope, &lstorage->set.ldap_scope) < 0 ) {
        sieve_storage_set_critical(storage,
                                   "Invalid LDAP storage config `%s': "
                                   "Invalid scope option `%s'",
                                   config_path, lstorage->set.scope);;
    }

#ifdef OPENLDAP_TLS_OPTIONS
    if ( lstorage->set.tls_require_cert != NULL &&
            ldap_tls_require_cert_from_str(lstorage->set.tls_require_cert,
                                           &lstorage->set.ldap_tls_require_cert) < 0) {
        sieve_storage_set_critical(storage,
                                   "Invalid LDAP storage config `%s': "
                                   "Invalid tls_require_cert option `%s'",
                                   config_path, lstorage->set.tls_require_cert);
    }
#endif
    return 0;
}
static int sieve_file_script_sequence_read_dir
(struct sieve_file_script_sequence *fseq, const char *path)
{
	struct sieve_storage *storage = fseq->seq.storage;
	DIR *dirp;
	int ret = 0;

	/* Open the directory */
	if ( (dirp = opendir(path)) == NULL ) {
		switch ( errno ) {
		case ENOENT:
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NOT_FOUND,
				"Script sequence location not found");
			break;
		case EACCES:
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NO_PERMISSION,
				"Script sequence location not accessible");
			sieve_storage_sys_error(storage,
				"Failed to open sieve sequence: "
				"%s",	eacces_error_get("stat", path));
			break;
		default:
			sieve_storage_set_critical(storage,
				"Failed to open sieve sequence: "
				"opendir(%s) failed: %m", path);
			break;
		}
		return -1;
	}

	/* Read and sort script files */
	for (;;) {
		const char *const *files;
		unsigned int count, i;
		const char *file;
		struct dirent *dp;
		struct stat st;

		errno = 0;
		if ( (dp=readdir(dirp)) == NULL )
			break;

		if ( !sieve_script_file_has_extension(dp->d_name) )
			continue;

		file = NULL;
		T_BEGIN {
			if ( path[strlen(path)-1] == '/' )
				file = t_strconcat(path, dp->d_name, NULL);
			else
				file = t_strconcat(path, "/", dp->d_name, NULL);

			if ( stat(file, &st) == 0 && S_ISREG(st.st_mode) )
				file = p_strdup(fseq->pool, dp->d_name);
			else
				file = NULL;
		} T_END;

		if (file == NULL)
			continue;
		
		/* Insert into sorted array */
		files = array_get(&fseq->script_files, &count);
		for ( i = 0; i < count; i++ ) {
			if ( strcmp(file, files[i]) < 0 )
				break;
		}

		if ( i == count )
			array_append(&fseq->script_files, &file, 1);
		else
			array_insert(&fseq->script_files, i, &file, 1);
	} 

	if ( errno != 0 ) {
		sieve_storage_set_critical(storage,
			"Failed to read sequence directory: "
			"readdir(%s) failed: %m", path);
		ret = -1;
	}

	/* Close the directory */
	if ( dirp != NULL && closedir(dirp) < 0 ) {
		sieve_storage_sys_error(storage,
			"Failed to close sequence directory: "
			"closedir(%s) failed: %m", path);
	}
	return ret;
}
struct sieve_script_sequence *sieve_file_storage_get_script_sequence
(struct sieve_storage *storage, enum sieve_error *error_r)
{
	struct sieve_file_storage *fstorage =
		(struct sieve_file_storage *)storage;
	struct sieve_file_script_sequence *fseq = NULL;
	const char *name = storage->script_name;
	const char *file;
	pool_t pool;
	struct stat st;

	/* Specified path can either be a regular file or a directory */
	if ( stat(fstorage->path, &st) != 0 ) {
		switch ( errno ) {
		case ENOENT:
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NOT_FOUND,
				"Script sequence location not found");
			break;
		case EACCES:
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NO_PERMISSION,
				"Script sequence location not accessible");
			sieve_storage_sys_error(storage,
				"Failed to open sieve sequence: "
				"%s",	eacces_error_get("stat", fstorage->path));
			break;
		default:
			sieve_storage_set_critical(storage,
				"Failed to open sieve sequence: "
				"stat(%s) failed: %m", fstorage->path);
			break;
		}
		*error_r = storage->error_code;
		return NULL;
	}

	/* Create sequence object */
	pool = pool_alloconly_create("sieve_file_script_sequence", 1024);
	fseq = p_new(pool, struct sieve_file_script_sequence, 1);
	fseq->pool = pool;
	sieve_script_sequence_init(&fseq->seq, storage);

	if ( S_ISDIR(st.st_mode) ) {
		i_array_init(&fseq->script_files, 16);

		/* Path is directory */
		if (name == 0 || *name == '\0') {
			/* Read all '.sieve' files in directory */
			if (sieve_file_script_sequence_read_dir
				(fseq, fstorage->path) < 0) {
				*error_r = storage->error_code;
				sieve_file_script_sequence_destroy(&fseq->seq);
				return NULL;
			}

		}	else {
			/* Read specific script file */
			file = sieve_script_file_from_name(name);
			file = p_strdup(pool, file);
			array_append(&fseq->script_files, &file, 1);
		}

	} else {
		/* Path is a file
		   (apparently; we'll see about that once it is opened) */
		fseq->storage_is_file = TRUE;
	}
		
	return &fseq->seq;
}
static int
sieve_file_storage_save_to(struct sieve_file_storage *fstorage,
	string_t *temp_path, struct istream *input,
	const char *target)
{
	struct sieve_storage *storage = &fstorage->storage;
	struct ostream *output;
	int fd;

	// FIXME: move this to base class
	// FIXME: use io_stream_temp

	fd = safe_mkstemp_hostpid
		(temp_path, fstorage->file_create_mode, (uid_t)-1, (gid_t)-1);
	if ( fd < 0 ) {
		if ( errno == EACCES ) {
			sieve_storage_set_critical(storage,
				"Failed to create temporary file: %s",
				eacces_error_get_creating("open", str_c(temp_path)));
		} else {
			sieve_storage_set_critical(storage,
				"Failed to create temporary file: open(%s) failed: %m",
				str_c(temp_path));
		}
		return -1;
	}

	output = o_stream_create_fd(fd, 0);
	switch ( o_stream_send_istream(output, input) ) {
	case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
		break;
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
		i_unreached();
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
		sieve_storage_set_critical(storage,
			"read(%s) failed: %s", i_stream_get_name(input),
			i_stream_get_error(input));
		o_stream_destroy(&output);
		i_unlink(str_c(temp_path));
		return -1;
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
		sieve_storage_set_critical(storage,
			"write(%s) failed: %s", str_c(temp_path),
			o_stream_get_error(output));
		o_stream_destroy(&output);
		i_unlink(str_c(temp_path));
		return -1;
	}
	o_stream_destroy(&output);

	if ( rename(str_c(temp_path), target) < 0 ) {
		if ( ENOQUOTA(errno) ) {
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NO_QUOTA,
				"Not enough disk quota");
		} else if ( errno == EACCES ) {
			sieve_storage_set_critical(storage,
				"%s", eacces_error_get("rename", target));
		} else {
			sieve_storage_set_critical(storage,
				"rename(%s, %s) failed: %m",
				str_c(temp_path), target);
		}
		i_unlink(str_c(temp_path));
	}
	return 0;
}