dovecot-2.2: imap: Implemented CATENATE extension.

dovecot at dovecot.org dovecot at dovecot.org
Sat Jun 2 19:02:27 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.2/rev/5344ff4215b4
changeset: 14592:5344ff4215b4
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Sat Jun 02 18:14:16 2012 +0300
description:
imap: Implemented CATENATE extension.

diffstat:

 README                |    1 +
 configure.in          |    2 +-
 src/imap/cmd-append.c |  633 +++++++++++++++++++++++++++++++++++--------------
 3 files changed, 453 insertions(+), 183 deletions(-)

diffs (truncated from 793 to 300 lines):

diff -r 07e6ca397a72 -r 5344ff4215b4 README
--- a/README	Sat Jun 02 17:56:27 2012 +0300
+++ b/README	Sat Jun 02 18:14:16 2012 +0300
@@ -40,6 +40,7 @@
  3691       - IMAP4 UNSELECT command
  4314       - IMAP4 Access Control List (ACL) Extension
  4315       - IMAP UIDPLUS extension
+ 4469       - IMAP CATENATE Extension
  4551       - IMAP Extension for Conditional STORE Operation
               or Quick Flag Changes Resynchronization
  4731       - IMAP4 Extension to SEARCH Command for Controlling
diff -r 07e6ca397a72 -r 5344ff4215b4 configure.in
--- a/configure.in	Sat Jun 02 17:56:27 2012 +0300
+++ b/configure.in	Sat Jun 02 18:14:16 2012 +0300
@@ -2704,7 +2704,7 @@
 dnl IDLE doesn't really belong to banner. It's there just to make Blackberries
 dnl happy, because otherwise BIS server disables push email.
 capability_banner="IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE"
-capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS SPECIAL-USE"
+capability="$capability_banner SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS SPECIAL-USE"
 AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
 AC_DEFINE_UNQUOTED(CAPABILITY_BANNER_STRING, "$capability_banner", IMAP capabilities advertised in banner) 
 
diff -r 07e6ca397a72 -r 5344ff4215b4 src/imap/cmd-append.c
--- a/src/imap/cmd-append.c	Sat Jun 02 17:56:27 2012 +0300
+++ b/src/imap/cmd-append.c	Sat Jun 02 18:14:16 2012 +0300
@@ -3,12 +3,14 @@
 #include "imap-common.h"
 #include "ioloop.h"
 #include "istream.h"
+#include "istream-chain.h"
 #include "ostream.h"
 #include "str.h"
 #include "imap-parser.h"
 #include "imap-date.h"
 #include "imap-util.h"
 #include "imap-commands.h"
+#include "imap-msgpart-url.h"
 
 #include <sys/time.h>
 
@@ -26,14 +28,19 @@
         struct mailbox_transaction_context *t;
 	time_t started;
 
+	struct istream_chain *catchain;
+	uoff_t cat_msg_size;
+
 	struct istream *input;
-	uoff_t msg_size;
+	struct istream *litinput;
+	uoff_t literal_size;
 
 	struct imap_parser *save_parser;
 	struct mail_save_context *save_ctx;
 	unsigned int count;
 
 	unsigned int message_input:1;
+	unsigned int catenate:1;
 	unsigned int failed:1;
 };
 
@@ -50,7 +57,7 @@
 		    ctx->count, secs);
 	if (ctx->input != NULL) {
 		str_printfa(str, ", %"PRIuUOFF_T"/%"PRIuUOFF_T" bytes",
-			    ctx->input->v_offset, ctx->msg_size);
+			    ctx->input->v_offset, ctx->literal_size);
 	}
 	str_append_c(str, ')');
 	return str_c(str);
@@ -112,37 +119,6 @@
 		client_continue_pending_input(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. */
-static int validate_args(const struct imap_arg *args,
-			 const struct imap_arg **flags_r,
-			 const char **internal_date_r, uoff_t *msg_size_r,
-			 bool *nonsync_r)
-{
-	/* [<flags>] */
-	if (!imap_arg_get_list(args, flags_r))
-		*flags_r = NULL;
-	else
-		args++;
-
-	/* [<internal date>] */
-	if (args->type != IMAP_ARG_STRING)
-		*internal_date_r = NULL;
-	else {
-		*internal_date_r = imap_arg_as_astring(args);
-		args++;
-	}
-
-	if (!imap_arg_get_literal_size(args, msg_size_r)) {
-		*nonsync_r = FALSE;
-		return FALSE;
-	}
-
-	*nonsync_r = args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC;
-	return TRUE;
-}
-
 static void cmd_append_finish(struct cmd_append_context *ctx)
 {
 	imap_parser_unref(&ctx->save_parser);
@@ -155,6 +131,8 @@
 	o_stream_set_flush_callback(ctx->client->output,
 				    client_output, ctx->client);
 
+	if (ctx->litinput != NULL)
+		i_stream_unref(&ctx->litinput);
 	if (ctx->input != NULL)
 		i_stream_unref(&ctx->input);
 	if (ctx->save_ctx != NULL)
@@ -184,11 +162,10 @@
 		return TRUE;
 	}
 
-	if (ctx->input->v_offset == ctx->msg_size) {
+	if (ctx->litinput->v_offset == ctx->literal_size) {
 		/* finished, but with MULTIAPPEND and LITERAL+ we may get
 		   more messages. */
-		i_stream_unref(&ctx->input);
-		ctx->input = NULL;
+		i_stream_unref(&ctx->litinput);
 
 		ctx->message_input = FALSE;
 		imap_parser_reset(ctx->save_parser);
@@ -210,7 +187,7 @@
 
 	/* we have to read the nonsynced literal so we don't treat the message
 	   data as commands. */
-	ctx->input = i_stream_create_limit(ctx->client->input, ctx->msg_size);
+	ctx->input = i_stream_create_limit(ctx->client->input, ctx->literal_size);
 
 	ctx->message_input = TRUE;
 	ctx->cmd->func = cmd_append_continue_cancel;
@@ -218,20 +195,405 @@
 	return cmd_append_continue_cancel(ctx->cmd);
 }
 
+static int
+cmd_append_catenate(struct client_command_context *cmd,
+		    const struct imap_arg *args, bool *nonsync_r)
+{
+	struct client *client = cmd->client;
+	struct cmd_append_context *ctx = cmd->context;
+	struct imap_msgpart_url *mpurl;
+	const char *catpart, *error;
+	uoff_t newsize;
+	int ret;
+
+	*nonsync_r = FALSE;
+
+	/* Handle URLs until a TEXT literal is encountered */
+	while (imap_arg_get_atom(args, &catpart)) {
+		const char *caturl;
+
+		if (strcasecmp(catpart, "URL") == 0 ) {
+			/* URL <url> */ 
+			args++;
+			if (!imap_arg_get_astring(args, &caturl))
+				break;
+			if (ctx->failed)
+				return -1;
+
+			mpurl = imap_msgpart_url_parse(client->user, client->mailbox, caturl, &error);
+			if (mpurl == NULL) {
+				/* invalid url, abort */
+				client_send_tagline(cmd,
+					t_strdup_printf("NO [BADURL %s] %s.", caturl, error));
+				return -1;
+			}
+
+			if (cmd->cancel) {
+				imap_msgpart_url_free(&mpurl);
+				cmd_append_finish(ctx);
+				return 1;
+			}
+
+			/* catenate URL */
+			if (ctx->save_ctx != NULL) {
+				struct istream *input = NULL;
+				uoff_t size;
+
+				if (!imap_msgpart_url_read_part(mpurl, &input, &size, &error)) {
+					/* invalid url, abort */
+					client_send_tagline(cmd,
+						t_strdup_printf("NO [BADURL %s] %s.", caturl, error));
+					return -1;
+				}
+
+				newsize = ctx->cat_msg_size + size;
+				if (newsize < ctx->cat_msg_size) {
+					client_send_tagline(cmd,
+						"NO [TOOBIG] Composed message grows too big.");
+					imap_msgpart_url_free(&mpurl);
+					return -1;
+				}
+
+				if (input != NULL) {
+					ctx->cat_msg_size = newsize;
+					i_stream_chain_append(ctx->catchain, input);
+
+					while (!input->eof) {
+						ret = i_stream_read(ctx->input);
+						if (mailbox_save_continue(ctx->save_ctx) < 0) {
+							/* we still have to finish reading the message
+							   from client */
+							mailbox_save_cancel(&ctx->save_ctx);
+							break;
+						}
+						if (ret == -1 || ret == 0)
+							break;
+					}
+
+					if (!input->eof) {
+						client_send_tagline(cmd, t_strdup_printf(
+							"NO [BADURL %s] Failed to read all data.", caturl));
+						imap_msgpart_url_free(&mpurl);
+						return -1;
+					}
+				}
+			}
+			imap_msgpart_url_free(&mpurl);
+		} else if (strcasecmp(catpart, "TEXT") == 0) {
+			/* TEXT <literal> */
+			args++;
+			if (!imap_arg_get_literal_size(args, &ctx->literal_size))
+				break;
+
+			*nonsync_r = args->type == IMAP_ARG_LITERAL_SIZE_NONSYNC;
+			if (ctx->failed) {
+				/* we failed earlier, make sure we just eat
+				   nonsync-literal if it's given. */
+				return -1;
+			}
+
+			newsize = ctx->cat_msg_size + ctx->literal_size;
+			if (newsize < ctx->cat_msg_size) {
+				client_send_tagline(cmd,
+					"NO [TOOBIG] Composed message grows too big.");
+				return -1;
+			}
+
+			/* save the mail */
+			ctx->cat_msg_size = newsize;
+			ctx->litinput = i_stream_create_limit(client->input, ctx->literal_size);
+			i_stream_chain_append(ctx->catchain, ctx->litinput);
+			return 1;
+		} else {
+			break;
+		}
+		args++;
+	}
+
+	if (IMAP_ARG_IS_EOL(args)) {
+		/* ")" */
+		return 0;
+	}
+	client_send_command_error(cmd, "Invalid arguments.");
+	return -1;
+}
+
+static void cmd_append_finish_catenate(struct client_command_context *cmd)
+{
+	struct cmd_append_context *ctx = cmd->context;
+
+	i_stream_chain_append(ctx->catchain, NULL);
+	i_stream_unref(&ctx->input);
+	ctx->input = NULL;
+	ctx->catenate = FALSE;
+
+	if (mailbox_save_finish(&ctx->save_ctx) < 0) {
+		ctx->failed = TRUE;
+		client_send_storage_error(cmd, ctx->storage);
+	}
+}
+
+static bool cmd_append_continue_catenate(struct client_command_context *cmd)
+{
+	struct client *client = cmd->client;
+	struct cmd_append_context *ctx = cmd->context;
+	const struct imap_arg *args;
+	const char *msg;
+	bool fatal, nonsync = FALSE;
+	int ret;
+
+	if (cmd->cancel) {
+		cmd_append_finish(ctx);
+		return TRUE;
+	}
+
+	ret = imap_parser_read_args(ctx->save_parser, 0,
+				    IMAP_PARSE_FLAG_LITERAL_SIZE |
+				    IMAP_PARSE_FLAG_INSIDE_LIST, &args);


More information about the dovecot-cvs mailing list