changeset 21983:33dbc4cdd0c5

imapc: Add imapc_features=fetch-bodystructure This allows using the remote IMAP server's BODY and BODYSTRUCTURE replies.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 24 Apr 2017 13:29:13 +0300
parents 9282e5b090f1
children 855908f469d8
files src/lib-storage/index/imapc/imapc-mail-fetch.c src/lib-storage/index/imapc/imapc-mail.c src/lib-storage/index/imapc/imapc-settings.c src/lib-storage/index/imapc/imapc-settings.h src/lib-storage/index/imapc/imapc-storage.c
diffstat 5 files changed, 103 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/imapc/imapc-mail-fetch.c	Mon Apr 24 13:27:43 2017 +0300
+++ b/src/lib-storage/index/imapc/imapc-mail-fetch.c	Mon Apr 24 13:29:13 2017 +0300
@@ -10,6 +10,7 @@
 #include "imap-arg.h"
 #include "imap-date.h"
 #include "imap-quote.h"
+#include "imap-bodystructure.h"
 #include "imap-resp-code.h"
 #include "imapc-client.h"
 #include "imapc-mail.h"
@@ -259,6 +260,10 @@
 		str_append(str, mbox->guid_fetch_field_name);
 		str_append_c(str, ' ');
 	}
+	if ((fields & MAIL_FETCH_IMAP_BODY) != 0)
+		str_append(str, "BODY ");
+	if ((fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0)
+		str_append(str, "BODYSTRUCTURE ");
 
 	if ((fields & MAIL_FETCH_STREAM_BODY) != 0) {
 		if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_ZIMBRA_WORKAROUNDS))
@@ -343,6 +348,14 @@
 	    data->physical_size == (uoff_t)-1 &&
 	    IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
 		fields |= MAIL_FETCH_PHYSICAL_SIZE | MAIL_FETCH_VIRTUAL_SIZE;
+	if ((data->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0 &&
+	    data->body == NULL &&
+	    IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE))
+		fields |= MAIL_FETCH_IMAP_BODY;
+	if ((data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0 &&
+	    data->bodystructure == NULL &&
+	    IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE))
+		fields |= MAIL_FETCH_IMAP_BODYSTRUCTURE;
 	if ((data->wanted_fields & MAIL_FETCH_GUID) != 0 &&
 	    data->guid == NULL && mbox->guid_fetch_field_name != NULL)
 		fields |= MAIL_FETCH_GUID;
@@ -398,6 +411,16 @@
 			return FALSE;
 		fields &= ~MAIL_FETCH_GUID;
 	}
+	if ((fields & MAIL_FETCH_IMAP_BODY) != 0) {
+		if (imail->imail.data.body == NULL)
+			return FALSE;
+		fields &= ~MAIL_FETCH_IMAP_BODY;
+	}
+	if ((fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0) {
+		if (imail->imail.data.bodystructure == NULL)
+			return FALSE;
+		fields &= ~MAIL_FETCH_IMAP_BODYSTRUCTURE;
+	}
 	if ((fields & (MAIL_FETCH_STREAM_HEADER |
 		       MAIL_FETCH_STREAM_BODY)) != 0) {
 		if (imail->imail.data.stream == NULL)
@@ -724,6 +747,35 @@
 	i_stream_destroy(&input);
 }
 
+static const char *
+imapc_args_to_bodystructure(struct imapc_mail *mail,
+			    const struct imap_arg *list_arg, bool extended)
+{
+	const struct imap_arg *args;
+	struct message_part *parts = NULL;
+	const char *ret, *error;
+	pool_t pool;
+
+	if (!imap_arg_get_list(list_arg, &args)) {
+		mail_storage_set_critical(mail->imail.mail.mail.box->storage,
+			"imapc: Server sent invalid BODYSTRUCTURE parameters");
+		return NULL;
+	}
+
+	pool = pool_alloconly_create("imap bodystructure", 1024);
+	if (imap_bodystructure_parse_args(args, pool, &parts, &error) < 0) {
+		mail_storage_set_critical(mail->imail.mail.mail.box->storage,
+			"imapc: Server sent invalid BODYSTRUCTURE: %s", error);
+		ret = NULL;
+	} else {
+		string_t *str = t_str_new(128);
+		imap_bodystructure_write(parts, str, extended);
+		ret = p_strdup(mail->imail.mail.data_pool, str_c(str));
+	}
+	pool_unref(&pool);
+	return ret;
+}
+
 void imapc_mail_fetch_update(struct imapc_mail *mail,
 			     const struct imapc_untagged_reply *reply,
 			     const struct imap_arg *args)
@@ -759,6 +811,18 @@
 			    imap_parse_datetime(value, &t, &tz))
 				mail->imail.data.received_date = t;
 			match = TRUE;
+		} else if (strcasecmp(key, "BODY") == 0) {
+			if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) {
+				mail->imail.data.body =
+					imapc_args_to_bodystructure(mail, &args[i+1], FALSE);
+			}
+			match = TRUE;
+		} else if (strcasecmp(key, "BODYSTRUCTURE") == 0) {
+			if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) {
+				mail->imail.data.bodystructure =
+					imapc_args_to_bodystructure(mail, &args[i+1], TRUE);
+			}
+			match = TRUE;
 		} else if (strcasecmp(key, "RFC822.SIZE") == 0) {
 			if (imap_arg_get_atom(&args[i+1], &value) &&
 			    str_to_uoff(value, &size) == 0 &&
--- a/src/lib-storage/index/imapc/imapc-mail.c	Mon Apr 24 13:27:43 2017 +0300
+++ b/src/lib-storage/index/imapc/imapc-mail.c	Mon Apr 24 13:29:13 2017 +0300
@@ -351,6 +351,7 @@
 	struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 	struct index_mail_data *data = &mail->data;
 	struct mailbox_header_lookup_ctx *header_ctx;
+	const char *str;
 	time_t date;
 	uoff_t size;
 
@@ -370,6 +371,10 @@
 	}
 	if ((data->wanted_fields & MAIL_FETCH_GUID) != 0)
 		(void)imapc_mail_get_cached_guid(_mail);
+	if ((data->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0)
+		(void)index_mail_get_cached_body(mail, &str);
+	if ((data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0)
+		(void)index_mail_get_cached_bodystructure(mail, &str);
 
 	if (data->access_part == 0 && data->wanted_headers != NULL &&
 	    !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_HEADERS)) {
@@ -569,6 +574,34 @@
 		*value_r = p_strdup_printf(imail->mail.data_pool, "GmailId%llx",
 					   (unsigned long long)num);
 		return 0;
+	case MAIL_FETCH_IMAP_BODY:
+		if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE))
+			break;
+
+		if (index_mail_get_cached_body(imail, value_r))
+			return 0;
+		if (imapc_mail_fetch(_mail, field, NULL) < 0)
+			return -1;
+		if (imail->data.body == NULL) {
+			(void)imapc_mail_failed(_mail, "BODY");
+			return -1;
+		}
+		*value_r = imail->data.body;
+		return 0;
+	case MAIL_FETCH_IMAP_BODYSTRUCTURE:
+		if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_FETCH_BODYSTRUCTURE))
+			break;
+
+		if (index_mail_get_cached_bodystructure(imail, value_r))
+			return 0;
+		if (imapc_mail_fetch(_mail, field, NULL) < 0)
+			return -1;
+		if (imail->data.bodystructure == NULL) {
+			(void)imapc_mail_failed(_mail, "BODYSTRUCTURE");
+			return -1;
+		}
+		*value_r = imail->data.bodystructure;
+		return 0;
 	default:
 		break;
 	}
--- a/src/lib-storage/index/imapc/imapc-settings.c	Mon Apr 24 13:27:43 2017 +0300
+++ b/src/lib-storage/index/imapc/imapc-settings.c	Mon Apr 24 13:29:13 2017 +0300
@@ -101,6 +101,7 @@
 	{ "fetch-fix-broken-mails", IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS },
 	{ "modseq", IMAPC_FEATURE_MODSEQ },
 	{ "delay-login", IMAPC_FEATURE_DELAY_LOGIN },
+	{ "fetch-bodystructure", IMAPC_FEATURE_FETCH_BODYSTRUCTURE },
 	{ NULL, 0 }
 };
 
--- a/src/lib-storage/index/imapc/imapc-settings.h	Mon Apr 24 13:27:43 2017 +0300
+++ b/src/lib-storage/index/imapc/imapc-settings.h	Mon Apr 24 13:29:13 2017 +0300
@@ -17,6 +17,7 @@
 	IMAPC_FEATURE_FETCH_FIX_BROKEN_MAILS	= 0x200,
 	IMAPC_FEATURE_MODSEQ			= 0x400,
 	IMAPC_FEATURE_DELAY_LOGIN		= 0x800,
+	IMAPC_FEATURE_FETCH_BODYSTRUCTURE	= 0x1000,
 };
 /* </settings checks> */
 
--- a/src/lib-storage/index/imapc/imapc-storage.c	Mon Apr 24 13:27:43 2017 +0300
+++ b/src/lib-storage/index/imapc/imapc-storage.c	Mon Apr 24 13:29:13 2017 +0300
@@ -411,6 +411,10 @@
 	}
 	storage->client->_storage = storage;
 	p_array_init(&storage->remote_namespaces, _storage->pool, 4);
+	if (IMAPC_HAS_FEATURE(storage, IMAPC_FEATURE_FETCH_BODYSTRUCTURE)) {
+		_storage->nonbody_access_fields |=
+			MAIL_FETCH_IMAP_BODY | MAIL_FETCH_IMAP_BODYSTRUCTURE;
+	}
 
 	imapc_storage_client_register_untagged(storage->client, "STATUS",
 					       imapc_untagged_status);