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;
}
struct sieve_storage_save_context *
sieve_file_storage_save_init(struct sieve_storage *storage,
	const char *scriptname, struct istream *input)
{
	struct sieve_file_storage *fstorage =
		(struct sieve_file_storage *)storage;
	struct sieve_file_save_context *fsctx;
	pool_t pool;
	const char *path;
	int fd;

	if ( sieve_file_storage_pre_modify(storage) < 0 )
		return NULL;

	if ( scriptname != NULL ) {
		/* Prevent overwriting the active script link when it resides in the
		 * sieve storage directory.
		 */
		i_assert( fstorage->link_path != NULL );
		if ( *(fstorage->link_path) == '\0' ) {
			const char *svext;
			size_t namelen;

			svext = strrchr(fstorage->active_fname, '.');
			namelen = svext - fstorage->active_fname;
			if ( svext != NULL && strncmp(svext+1, "sieve", 5) == 0 &&
				strlen(scriptname) == namelen &&
				strncmp(scriptname, fstorage->active_fname, namelen) == 0 )
			{
				sieve_storage_set_error(storage,
					SIEVE_ERROR_BAD_PARAMS,
					"Script name `%s' is reserved for internal use.",
					scriptname);
				return NULL;
			}
		}
	}

	T_BEGIN {
		fd = sieve_file_storage_create_tmp(fstorage, scriptname, &path);
		if (fd == -1) {
			fsctx = NULL;
		} else {
			pool = pool_alloconly_create("sieve_file_save_context", 1024);
			fsctx = p_new(pool, struct sieve_file_save_context, 1);
			fsctx->context.scriptname = p_strdup(pool, scriptname);
			fsctx->context.input = input;
			fsctx->context.pool = pool;
			fsctx->fd = fd;
			fsctx->output = o_stream_create_fd(fsctx->fd, 0);
			fsctx->tmp_path = p_strdup(pool, path);
		}
	} T_END;

	return &fsctx->context;
}
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 );
}
示例#4
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);
}
示例#5
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, 1, TRUE, &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_script_init(client->storage, scriptname);

	if (ctx->script == NULL) {
		ctx->failed = TRUE;
		return cmd_getscript_finish(ctx);
	}
			
	ctx->script_stream = sieve_script_open(ctx->script, &error);

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

	ctx->script_size = sieve_script_get_size(ctx->script);
	ctx->script_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);
}
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;
}
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;
}