[dovecot-cvs] dovecot/src/imap cmd-append.c,1.41,1.42

cras at dovecot.org cras at dovecot.org
Sun Aug 22 12:17:10 EEST 2004


Update of /home/cvs/dovecot/src/imap
In directory talvi:/tmp/cvs-serv2400/imap

Modified Files:
	cmd-append.c 
Log Message:
Changed mail saving API to be nonblocking.



Index: cmd-append.c
===================================================================
RCS file: /home/cvs/dovecot/src/imap/cmd-append.c,v
retrieving revision 1.41
retrieving revision 1.42
diff -u -d -r1.41 -r1.42
--- cmd-append.c	18 Aug 2004 23:53:39 -0000	1.41
+++ cmd-append.c	22 Aug 2004 09:17:08 -0000	1.42
@@ -12,6 +12,56 @@
 
 #include <sys/time.h>
 
+struct cmd_append_context {
+	struct client *client;
+	struct mail_storage *storage;
+	struct mailbox *box;
+        struct mailbox_transaction_context *t;
+
+	struct istream *input;
+	uoff_t msg_size;
+
+	struct imap_parser *save_parser;
+	struct mail_save_context *save_ctx;
+};
+
+static int cmd_append_continue_message(struct client *client);
+
+static void client_input(void *context)
+{
+	struct client *client = context;
+
+	client->last_input = ioloop_time;
+
+	switch (i_stream_read(client->input)) {
+	case -1:
+		/* disconnected */
+		client_destroy(client);
+		return;
+	case -2:
+		if (client->command_pending) {
+			/* message data, this is handled internally by
+			   mailbox_save_continue() */
+			break;
+		}
+
+		/* parameter word is longer than max. input buffer size.
+		   this is most likely an error, so skip the new data
+		   until newline is found. */
+		client->input_skip_line = TRUE;
+
+		client_send_command_error(client, "Too long argument.");
+		_client_reset_command(client);
+		break;
+	}
+
+	if (client->cmd_func(client)) {
+		/* command execution was finished */
+		client->bad_counter = 0;
+		_client_reset_command(client);
+	}
+}
+
 /* Returns -1 = error, 0 = need more data, 1 = successful. flags and
    internal_date may be NULL as a result, but mailbox and msg_size are always
    set when successful. */
@@ -44,175 +94,244 @@
 	return TRUE;
 }
 
-int cmd_append(struct client *client)
+static void cmd_append_finish(struct cmd_append_context *ctx)
 {
-	struct mail_storage *storage;
-	struct mailbox *box;
-	struct mailbox_status status;
-        struct mailbox_transaction_context *t;
-	struct imap_parser *save_parser;
+	size_t size;
+
+	io_remove(ctx->client->io);
+	ctx->client->io = NULL;
+
+        imap_parser_destroy(ctx->save_parser);
+
+	if (ctx->input != NULL)
+		i_stream_unref(ctx->input);
+
+	if (ctx->save_ctx != NULL)
+		mailbox_save_cancel(ctx->save_ctx);
+
+	if (ctx->t != NULL)
+		mailbox_transaction_rollback(ctx->t);
+
+	if (ctx->box != ctx->client->mailbox)
+		mailbox_close(ctx->box);
+
+	(void)i_stream_get_data(ctx->client->input, &size);
+	ctx->client->input_pending = size != 0;
+}
+
+static int cmd_append_continue_parsing(struct client *client)
+{
+	struct cmd_append_context *ctx = client->cmd_context;
 	struct imap_arg *args;
 	struct imap_arg_list *flags_list;
-        struct mailbox_keywords old_flags;
 	struct mail_full_flags flags;
-	struct istream *input;
+	const char *internal_date_str;
 	time_t internal_date;
-	const char *mailbox, *internal_date_str;
-        enum mailbox_sync_flags sync_flags = 0;
-	uoff_t msg_size;
-	unsigned int count;
-	int ret, failed, timezone_offset, nonsync;
+	int ret, timezone_offset, nonsync;
 
-	/* <mailbox> */
-	if (!client_read_string_args(client, 1, &mailbox))
-		return FALSE;
+	/* if error occurs, the CRLF is already read. */
+	client->input_skip_line = FALSE;
 
-	if (!client_verify_mailbox_name(client, mailbox, TRUE, FALSE))
+	/* [<flags>] [<internal date>] <message literal> */
+	ret = imap_parser_read_args(ctx->save_parser, 0,
+				    IMAP_PARSE_FLAG_LITERAL_SIZE, &args);
+	if (ret == -1) {
+		client_send_command_error(client, NULL);
+		cmd_append_finish(ctx);
 		return TRUE;
+	}
+	if (ret < 0) {
+		/* need more data */
+		return FALSE;
+	}
 
-	storage = client_find_storage(client, &mailbox);
-	if (storage == NULL)
+	if (args->type == IMAP_ARG_EOL) {
+		/* last message */
+		enum mailbox_sync_flags sync_flags;
+
+		ret = mailbox_transaction_commit(ctx->t);
+		ctx->t = NULL;
+		if (ret < 0) {
+			client_send_storage_error(client, ctx->storage);
+			cmd_append_finish(ctx);
+			return TRUE;
+		}
+
+		sync_flags = ctx->box == client->mailbox ?
+			0 : MAILBOX_SYNC_FLAG_FAST;
+
+		client->input_skip_line = TRUE;
+		cmd_append_finish(ctx);
+		return cmd_sync(client, sync_flags, "OK Append completed.");
+	}
+
+	if (!validate_args(args, &flags_list, &internal_date_str,
+			   &ctx->msg_size, &nonsync)) {
+		client_send_command_error(client, "Invalid arguments.");
+		cmd_append_finish(ctx);
 		return TRUE;
+	}
 
-	if (client->mailbox != NULL &&
-	    mailbox_name_equals(mailbox_get_name(client->mailbox), mailbox))
-		box = client->mailbox;
-	else {
-		box = mailbox_open(storage, mailbox, MAILBOX_OPEN_FAST |
-				   MAILBOX_OPEN_KEEP_RECENT);
-		if (box == NULL) {
-			client_send_storage_error(client, storage);
+	if (flags_list != NULL) {
+		if (!client_parse_mail_flags(client, flags_list->args,
+					     &client->keywords, &flags)) {
+			cmd_append_finish(ctx);
 			return TRUE;
 		}
+	} else {
+		memset(&flags, 0, sizeof(flags));
 	}
 
-	if (mailbox_get_status(box, STATUS_KEYWORDS, &status) < 0) {
-		client_send_storage_error(client, storage);
-		mailbox_close(box);
+	if (internal_date_str == NULL) {
+		/* no time given, default to now. */
+		internal_date = (time_t)-1;
+		timezone_offset = 0;
+	} else if (!imap_parse_datetime(internal_date_str,
+					&internal_date, &timezone_offset)) {
+		client_send_tagline(client, "BAD Invalid internal date.");
+		cmd_append_finish(ctx);
 		return TRUE;
 	}
-	memset(&old_flags, 0, sizeof(old_flags));
-        old_flags.pool = pool_datastack_create();
-	client_save_keywords(&old_flags, status.keywords,
-			     status.keywords_count);
 
-	t = mailbox_transaction_begin(box, FALSE);
+	if (ctx->msg_size == 0) {
+		/* no message data, abort */
+		client_send_tagline(client, "NO Append aborted.");
+		cmd_append_finish(ctx);
+		return TRUE;
+	}
 
-	/* if error occurs, the CRLF is already read. */
-	client->input_skip_line = FALSE;
+	if (!nonsync) {
+		o_stream_send(client->output, "+ OK\r\n", 6);
+		o_stream_flush(client->output);
+	}
 
-	count = 0;
-	failed = TRUE;
-	save_parser = imap_parser_create(client->input, client->output,
-					 imap_max_line_length);
+	/* after literal comes CRLF, if we fail make sure we eat it away */
+	client->input_skip_line = TRUE;
 
-	for (;;) {
-		/* [<flags>] [<internal date>] <message literal> */
-		imap_parser_reset(save_parser);
-		for (;;) {
-			ret = imap_parser_read_args(save_parser, 0,
-						   IMAP_PARSE_FLAG_LITERAL_SIZE,
-						   &args);
-			if (ret >= 0)
-				break;
-			if (ret == -1) {
-				client_send_command_error(client, NULL);
-				break;
-			}
+	/* save the mail */
+	ctx->input = i_stream_create_limit(default_pool, client->input,
+					   client->input->v_offset,
+					   ctx->msg_size);
+	ctx->save_ctx = mailbox_save_init(ctx->t, &flags, internal_date,
+					  timezone_offset, NULL,
+					  ctx->input, TRUE);
 
-			/* need more data */
-			ret = i_stream_read(client->input);
-			if (ret < 0) {
-				if (ret == -2) {
-					client_send_command_error(client,
-						"Too long argument.");
-				}
-				break;
-			}
-		}
+	client->command_pending = TRUE;
+	client->cmd_func = cmd_append_continue_message;
+	return cmd_append_continue_message(client);
+}
 
-		if (ret < 0)
-			break;
+static int cmd_append_continue_message(struct client *client)
+{
+	struct cmd_append_context *ctx = client->cmd_context;
+	size_t size;
+	int failed;
 
-		if (args->type == IMAP_ARG_EOL) {
-			/* last one */
-			if (count > 0)
-				failed = FALSE;
-			client->input_skip_line = TRUE;
-			break;
+	if (ctx->save_ctx != NULL) {
+		if (mailbox_save_continue(ctx->save_ctx) < 0) {
+			/* we still have to finish reading the message
+			   from client */
+			mailbox_save_cancel(ctx->save_ctx);
+			ctx->save_ctx = NULL;
 		}
+	}
 
-		if (!validate_args(args, &flags_list, &internal_date_str,
-				   &msg_size, &nonsync)) {
-			/* error */
-			client_send_command_error(client, "Invalid arguments.");
-			break;
-		}
+	if (ctx->save_ctx == NULL) {
+		(void)i_stream_read(ctx->input);
+		(void)i_stream_get_data(ctx->input, &size);
+		i_stream_skip(ctx->input, size);
+	}
 
-		if (flags_list != NULL) {
-			if (!client_parse_mail_flags(client, flags_list->args,
-						     &old_flags, &flags))
-				break;
+	if (ctx->input->v_offset == ctx->msg_size) {
+		/* finished */
+		i_stream_unref(ctx->input);
+		ctx->input = NULL;
+
+		if (ctx->save_ctx == NULL) {
+			/* failed above */
+			client_send_storage_error(client, ctx->storage);
+			failed = TRUE;
+		} else if (client->input->eof) {
+			/* client disconnected */
+			failed = TRUE;
+			mailbox_save_cancel(ctx->save_ctx);
+		} else if (mailbox_save_finish(ctx->save_ctx, NULL) < 0) {
+			failed = TRUE;
+			client_send_storage_error(client, ctx->storage);
 		} else {
-			memset(&flags, 0, sizeof(flags));
+			failed = FALSE;
 		}
+		ctx->save_ctx = NULL;
 
-		if (internal_date_str == NULL) {
-			/* no time given, default to now. */
-			internal_date = (time_t)-1;
-			timezone_offset = 0;
-		} else if (!imap_parse_datetime(internal_date_str,
-						&internal_date,
-						&timezone_offset)) {
-			client_send_tagline(client,
-					    "BAD Invalid internal date.");
-			break;
+		if (failed) {
+			cmd_append_finish(ctx);
+			return TRUE;
 		}
 
-		if (msg_size == 0) {
-			/* no message data, abort */
-			client_send_tagline(client, "NO Append aborted.");
-			break;
-		}
+		/* prepare for next message */
+		client->command_pending = FALSE;
+		imap_parser_reset(ctx->save_parser);
+		client->cmd_func = cmd_append_continue_parsing;
+		return cmd_append_continue_parsing(client);
+	}
 
-		if (!nonsync) {
-			o_stream_send(client->output, "+ OK\r\n", 6);
-			o_stream_flush(client->output);
-		}
+	return FALSE;
+}
 
-		/* save the mail */
-		input = i_stream_create_limit(default_pool, client->input,
-					      client->input->v_offset,
-					      msg_size);
-		if (mailbox_save(t, &flags, internal_date, timezone_offset,
-				 NULL, input, NULL) < 0) {
-			i_stream_unref(input);
-			client_send_storage_error(client, storage);
-			break;
-		}
-		i_stream_unref(input);
+int cmd_append(struct client *client)
+{
+        struct cmd_append_context *ctx;
+	struct mail_storage *storage;
+	struct mailbox *box;
+	struct mailbox_status status;
+	const char *mailbox;
 
-		if (client->input->closed)
-			break;
+	/* <mailbox> */
+	if (!client_read_string_args(client, 1, &mailbox))
+		return FALSE;
 
-		count++;
-	}
-        imap_parser_destroy(save_parser);
+	if (!client_verify_mailbox_name(client, mailbox, TRUE, FALSE))
+		return TRUE;
 
-	if (failed)
-		mailbox_transaction_rollback(t);
+	storage = client_find_storage(client, &mailbox);
+	if (storage == NULL)
+		return TRUE;
+
+	if (client->mailbox != NULL &&
+	    mailbox_name_equals(mailbox_get_name(client->mailbox), mailbox))
+		box = client->mailbox;
 	else {
-		if (mailbox_transaction_commit(t) < 0) {
-			failed = TRUE;
+		box = mailbox_open(storage, mailbox, MAILBOX_OPEN_FAST |
+				   MAILBOX_OPEN_KEEP_RECENT);
+		if (box == NULL) {
 			client_send_storage_error(client, storage);
+			return TRUE;
 		}
 	}
 
-	if (box != client->mailbox) {
-		sync_flags |= MAILBOX_SYNC_FLAG_FAST;
+	if (mailbox_get_status(box, STATUS_KEYWORDS, &status) < 0) {
+		client_send_storage_error(client, storage);
 		mailbox_close(box);
+		return TRUE;
 	}
 
-	return failed ? TRUE :
-		cmd_sync(client, sync_flags, "OK Append completed.");
+	ctx = p_new(client->cmd_pool, struct cmd_append_context, 1);
+	ctx->client = client;
+	ctx->storage = storage;
+	ctx->box = box;
+	ctx->t = mailbox_transaction_begin(box, FALSE);
+
+	client_save_keywords(&client->keywords, status.keywords,
+			     status.keywords_count);
+
+	io_remove(client->io);
+	client->io = io_add(i_stream_get_fd(client->input), IO_READ,
+			    client_input, client);
+
+	ctx->save_parser = imap_parser_create(client->input, client->output,
+					      imap_max_line_length);
+
+	client->cmd_func = cmd_append_continue_parsing;
+	client->cmd_context = ctx;
+	return cmd_append_continue_parsing(client);
 }



More information about the dovecot-cvs mailing list