changeset 14591:07e6ca397a72

Created lib-imap-storage for IMAP-specific functionality that uses lib-storage. This is done to prevent introducing dependencies on lib-storage in lib-imap. It contains IMAP message part access functionality, including URL-based access.
author Stephan Bosch <stephan@rename-it.nl>
date Sat, 02 Jun 2012 17:56:27 +0300
parents 9eef4f7b0187
children 5344ff4215b4
files configure.in src/Makefile.am src/imap/Makefile.am src/imap/imap-fetch-body.c src/lib-imap-storage/Makefile.am src/lib-imap-storage/imap-msgpart-url.c src/lib-imap-storage/imap-msgpart-url.h src/lib-imap-storage/imap-msgpart.c src/lib-imap-storage/imap-msgpart.h src/lib-storage/Makefile.am
diffstat 10 files changed, 683 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Sat Jun 02 17:53:31 2012 +0300
+++ b/configure.in	Sat Jun 02 17:56:27 2012 +0300
@@ -2512,13 +2512,13 @@
 if test "$want_shared_libs" = "yes"; then
   LIBDOVECOT_DEPS='$(top_builddir)/src/lib-dovecot/libdovecot.la'
   LIBDOVECOT="$LIBDOVECOT_DEPS"
-  LIBDOVECOT_STORAGE_DEPS='$(top_builddir)/src/lib-storage/libdovecot-storage.la'
+  LIBDOVECOT_STORAGE_DEPS='$(top_builddir)/src/lib-storage/libdovecot-storage.la $(top_builddir)/src/lib-imap-storage/libimap-storage.la'
   LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/libdovecot-login.la'
   LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/libdovecot-lda.la'
 else
   LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib/liblib.la'
   LIBDOVECOT="$LIBDOVECOT_DEPS \$(LIBICONV)"
-  LIBDOVECOT_STORAGE_LAST='$(top_builddir)/src/lib-storage/list/libstorage_list.la $(top_builddir)/src/lib-storage/index/libstorage_index.la $(top_builddir)/src/lib-storage/libstorage.la $(top_builddir)/src/lib-index/libindex.la'
+  LIBDOVECOT_STORAGE_LAST='$(top_builddir)/src/lib-storage/list/libstorage_list.la $(top_builddir)/src/lib-storage/index/libstorage_index.la $(top_builddir)/src/lib-storage/libstorage.la $(top_builddir)/src/lib-index/libindex.la $(top_builddir)/src/lib-imap-storage/libimap-storage.la'
   LIBDOVECOT_STORAGE_FIRST='$(top_builddir)/src/lib-storage/libstorage_service.la $(top_builddir)/src/lib-storage/register/libstorage_register.la'
   LIBDOVECOT_STORAGE_DEPS="$LIBDOVECOT_STORAGE_FIRST $LINKED_STORAGE_LIBS $LIBDOVECOT_STORAGE_LAST"
   LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/liblogin.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la'
@@ -2748,6 +2748,7 @@
 src/lib-dns/Makefile
 src/lib-fs/Makefile
 src/lib-imap/Makefile
+src/lib-imap-storage/Makefile
 src/lib-imap-client/Makefile
 src/lib-index/Makefile
 src/lib-lda/Makefile
--- a/src/Makefile.am	Sat Jun 02 17:53:31 2012 +0300
+++ b/src/Makefile.am	Sat Jun 02 17:56:27 2012 +0300
@@ -6,6 +6,7 @@
 	lib-fs \
 	lib-mail \
 	lib-imap \
+	lib-imap-storage \
 	lib-master \
 	lib-dict \
 	lib-settings
--- a/src/imap/Makefile.am	Sat Jun 02 17:53:31 2012 +0300
+++ b/src/imap/Makefile.am	Sat Jun 02 17:56:27 2012 +0300
@@ -9,6 +9,7 @@
 	-I$(top_srcdir)/src/lib-master \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
+	-I$(top_srcdir)/src/lib-imap-storage \
 	-I$(top_srcdir)/src/lib-index \
 	-I$(top_srcdir)/src/lib-storage
 
@@ -78,7 +79,6 @@
 	mail-storage-callbacks.c \
 	main.c
 
-
 headers = \
 	imap-client.h \
 	imap-commands.h \
--- a/src/imap/imap-fetch-body.c	Sat Jun 02 17:53:31 2012 +0300
+++ b/src/imap/imap-fetch-body.c	Sat Jun 02 17:56:27 2012 +0300
@@ -11,6 +11,7 @@
 #include "message-send.h"
 #include "mail-storage-private.h"
 #include "imap-parser.h"
+#include "imap-msgpart.h"
 #include "imap-fetch.h"
 
 #include <stdlib.h>
@@ -464,63 +465,13 @@
 	return fetch_data(ctx, body, &size);
 }
 
-/* Find message_part for section (eg. 1.3.4) */
-static int part_find(struct mail *mail, const struct imap_fetch_body_data *body,
-		     const struct message_part **part_r, const char **section_r)
-{
-	struct message_part *part;
-	const char *path;
-	unsigned int num;
-
-	if (mail_get_parts(mail, &part) < 0)
-		return -1;
-
-	path = body->section;
-	while (*path >= '0' && *path <= '9' && part != NULL) {
-		/* get part number, we have already verified its validity */
-		num = 0;
-		while (*path != '\0' && *path != '.') {
-			i_assert(*path >= '0' && *path <= '9');
-
-			num = num*10 + (*path - '0');
-			path++;
-		}
-
-		if (*path == '.')
-			path++;
-
-		if (part->flags & MESSAGE_PART_FLAG_MULTIPART) {
-			/* find the part */
-			part = part->children;
-			for (; num > 1 && part != NULL; num--)
-				part = part->next;
-		} else {
-			/* only 1 allowed with non-multipart messages */
-			if (num != 1)
-				part = NULL;
-		}
-
-		if (part != NULL &&
-		    (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) &&
-		    (*path >= '0' && *path <= '9')) {
-			/* if we continue inside the message/rfc822, skip this
-			   body part */
-			part = part->children;
-		}
-	}
-
-	*part_r = part;
-	*section_r = path;
-	return 0;
-}
-
 static int fetch_body_mime(struct imap_fetch_context *ctx, struct mail *mail,
 			   const struct imap_fetch_body_data *body)
 {
 	const struct message_part *part;
 	const char *section;
 
-	if (part_find(mail, body, &part, &section) < 0)
+	if (imap_msgpart_find(mail, body->section, &part, &section) < 0)
 		return -1;
 
 	if (part == NULL) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-storage/Makefile.am	Sat Jun 02 17:56:27 2012 +0300
@@ -0,0 +1,21 @@
+noinst_LTLIBRARIES = libimap-storage.la
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-test \
+	-I$(top_srcdir)/src/lib-charset \
+	-I$(top_srcdir)/src/lib-mail \
+	-I$(top_srcdir)/src/lib-storage \
+	-I$(top_srcdir)/src/lib-imap
+
+libimap_storage_la_SOURCES = \
+	imap-msgpart.c \
+	imap-msgpart-url.c
+
+headers = \
+	imap-msgpart.h \
+	imap-msgpart-url.h
+
+pkginc_libdir=$(pkgincludedir)
+pkginc_lib_HEADERS = $(headers)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-storage/imap-msgpart-url.c	Sat Jun 02 17:56:27 2012 +0300
@@ -0,0 +1,230 @@
+#include "lib.h"
+#include "network.h"
+#include "istream.h"
+#include "message-parser.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+#include "imap-url.h"
+#include "imap-msgpart.h"
+#include "imap-msgpart-url.h"
+
+struct imap_msgpart_url {
+	char *mailbox;
+	uint32_t uidvalidity;
+	uint32_t uid;
+	char *section;
+	uoff_t partial_offset, partial_size;
+
+	struct mail_user *user;
+	struct mailbox *box;
+	struct mailbox_transaction_context *trans;
+	struct mail *mail;
+	
+	struct istream *input;
+	uoff_t part_size;
+};
+
+struct imap_msgpart_url *
+imap_msgpart_url_create(struct mail_user *user, const struct imap_url *url)
+{
+	struct imap_msgpart_url *mpurl;
+
+	i_assert(url->mailbox != NULL && url->uid != 0 &&
+		 url->search_program == NULL);
+
+	mpurl = i_new(struct imap_msgpart_url, 1);
+	mpurl->user = user;
+	mpurl->mailbox = i_strdup(url->mailbox);
+	mpurl->uidvalidity = url->uidvalidity;
+	mpurl->uid = url->uid;
+	if (url->section != NULL)
+		mpurl->section = i_strdup(url->section);
+	mpurl->partial_offset = url->partial_offset;
+	mpurl->partial_size = url->partial_size;
+	return mpurl;
+}
+
+struct imap_msgpart_url *
+imap_msgpart_url_parse(struct mail_user *user, struct mailbox *selected_box,
+		       const char *urlstr, const char **error_r)
+{
+	struct mailbox_status box_status;
+	struct imap_url base_url, *url;
+	const char  *error;
+
+	/* build base url */
+	memset(&base_url, 0, sizeof(base_url));
+	if (selected_box != NULL) {
+		mailbox_get_open_status(selected_box, STATUS_UIDVALIDITY,
+					&box_status);
+		base_url.mailbox = mailbox_get_vname(selected_box);
+		base_url.uidvalidity = box_status.uidvalidity;
+	}
+
+	/* parse url */
+	url = imap_url_parse(urlstr, NULL, &base_url,
+			     IMAP_URL_PARSE_REQUIRE_RELATIVE, &error);
+	if (url == NULL) {
+		*error_r = t_strconcat("Invalid IMAP URL: ", error, NULL);
+		return NULL;
+	}
+	if (url->mailbox == NULL) {
+		*error_r = "Mailbox-relative IMAP URL, but no mailbox selected";
+		return NULL;
+	}
+	if (url->uid == 0 || url->search_program != NULL) {
+		*error_r = "Invalid messagepart IMAP URL";
+		return NULL;
+	}
+	return imap_msgpart_url_create(user, url);
+}
+
+struct mailbox *imap_msgpart_url_get_mailbox(struct imap_msgpart_url *mpurl)
+{
+	return mpurl->box;
+}
+
+struct mailbox *
+imap_msgpart_url_open_mailbox(struct imap_msgpart_url *mpurl,
+			      const char **error_r)
+{
+	struct mailbox_status box_status;
+	enum mail_error error_code;
+	enum mailbox_flags flags = MAILBOX_FLAG_READONLY;
+	struct mail_namespace *ns;
+	struct mailbox *box;
+
+	if (mpurl->box != NULL)
+		return mpurl->box;
+
+	/* find mailbox namespace */
+	ns = mail_namespace_find(mpurl->user->namespaces, mpurl->mailbox);
+	if (ns == NULL) {
+		*error_r = "Nonexistent mailbox namespace";
+		return NULL;
+	}
+
+	/* open mailbox */
+	box = mailbox_alloc(ns->list, mpurl->mailbox, flags);
+	if (mailbox_open(box) < 0) {
+		*error_r = mail_storage_get_last_error(mailbox_get_storage(box),
+						       &error_code);
+		mailbox_free(&box);
+		return NULL;
+	}
+
+	/* verify UIDVALIDITY */
+	mailbox_get_open_status(box, STATUS_UIDVALIDITY, &box_status);
+	if (mpurl->uidvalidity > 0 &&
+	    box_status.uidvalidity != mpurl->uidvalidity) {
+		*error_r = "Invalid UIDVALIDITY";
+		mailbox_free(&box);
+		return NULL;
+	}
+	mpurl->box = box;
+	return box;
+}
+
+struct mail *
+imap_msgpart_url_open_mail(struct imap_msgpart_url *mpurl, const char **error_r)
+{
+	struct mailbox_transaction_context *t;
+	struct mail *mail;
+
+	if (mpurl->mail != NULL)
+		return mpurl->mail;
+
+	/* open mailbox if it is not yet open */
+	if (mpurl->box == NULL) {
+		if (imap_msgpart_url_open_mailbox(mpurl, error_r) == NULL)
+			return NULL;
+	}
+
+	/* start transaction */
+	t = mailbox_transaction_begin(mpurl->box, 0);
+	mail = mail_alloc(t, 0, NULL);
+
+	/* find the message */
+	if (!mail_set_uid(mail, mpurl->uid)) {
+		*error_r = "Message not found";
+		mail_free(&mail);
+		mailbox_transaction_rollback(&t);	
+		return NULL;
+	}
+
+	mpurl->trans = t;
+	mpurl->mail = mail;
+	return mail;
+}
+
+bool imap_msgpart_url_read_part(struct imap_msgpart_url *mpurl,
+				struct istream **stream_r, uoff_t *size_r,
+				const char **error_r)
+{
+	struct istream *input;
+	uoff_t part_size;
+
+	if (mpurl->input != NULL) {
+		i_stream_seek(mpurl->input, 0);
+		*stream_r = mpurl->input;
+		*size_r = mpurl->part_size;
+		return TRUE;
+	}
+
+	/* open mailbox if it is not yet open */
+	if (mpurl->mail == NULL) {
+		if (imap_msgpart_url_open_mail(mpurl, error_r) == NULL)
+			return FALSE;
+	}
+
+	/* open the referenced part as a stream */
+	if (!imap_msgpart_open(mpurl->mail, mpurl->section,
+			       mpurl->partial_offset, mpurl->partial_size,
+			       &input, &part_size, error_r))
+		return FALSE;
+
+	mpurl->input = input;
+	mpurl->part_size = part_size;
+
+	*stream_r = input;
+	*size_r = part_size;
+	return TRUE;
+}
+
+bool imap_msgpart_url_verify(struct imap_msgpart_url *mpurl,
+			     const char **error_r)
+{
+	if (mpurl->input != NULL)
+		return TRUE;
+
+	/* open mailbox if it is not yet open */
+	if (mpurl->mail == NULL) {
+		if (imap_msgpart_url_open_mail(mpurl, error_r) == NULL)
+			return FALSE;
+	}
+
+	/* open the referenced part as a stream */
+	if (!imap_msgpart_verify(mpurl->mail, mpurl->section, error_r))
+		return FALSE;
+	return TRUE;
+}
+
+void imap_msgpart_url_free(struct imap_msgpart_url **_mpurl)
+{
+	struct imap_msgpart_url *mpurl = *_mpurl;
+
+	*_mpurl = NULL;
+
+	if (mpurl->input != NULL)
+		i_stream_unref(&mpurl->input);
+	if (mpurl->mail != NULL)
+		mail_free(&mpurl->mail);
+	if (mpurl->trans != NULL)
+		mailbox_transaction_rollback(&mpurl->trans);
+	if (mpurl->box != NULL)
+		mailbox_free(&mpurl->box);	
+	if (mpurl->section != NULL)
+		i_free(mpurl->section);
+	i_free(mpurl->mailbox);
+	i_free(mpurl);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-storage/imap-msgpart-url.h	Sat Jun 02 17:56:27 2012 +0300
@@ -0,0 +1,29 @@
+#ifndef IMAP_MSGPART_URL_H
+#define IMAP_MSGPART_URL_H
+
+struct imap_url;
+struct imap_msgpart_url;
+
+struct imap_msgpart_url *
+imap_msgpart_url_create(struct mail_user *user, const struct imap_url *url);
+struct imap_msgpart_url *
+imap_msgpart_url_parse(struct mail_user *user, struct mailbox *selected_box,
+		       const char *urlstr, const char **error_r);
+
+struct mailbox *
+imap_msgpart_url_open_mailbox(struct imap_msgpart_url *mpurl,
+			      const char **error_r);
+struct mailbox *imap_msgpart_url_get_mailbox(struct imap_msgpart_url *mpurl);
+struct mail *
+imap_msgpart_url_open_mail(struct imap_msgpart_url *mpurl, const char **error_r);
+
+/* Returns NULL stream when part has zero length, e.g. when partial offset is
+   larger than the size of the referenced part */
+bool imap_msgpart_url_read_part(struct imap_msgpart_url *mpurl,
+				struct istream **stream_r, uoff_t *size_r,
+				const char **error_r);
+bool imap_msgpart_url_verify(struct imap_msgpart_url *mpurl,
+			     const char **error_r);
+void imap_msgpart_url_free(struct imap_msgpart_url **mpurl);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-storage/imap-msgpart.c	Sat Jun 02 17:56:27 2012 +0300
@@ -0,0 +1,371 @@
+#include "lib.h"
+#include "array.h"
+#include "istream.h"
+#include "istream-crlf.h"
+#include "istream-header-filter.h"
+#include "message-parser.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+#include "imap-parser.h"
+#include "imap-msgpart.h"
+
+int imap_msgpart_find(struct mail *mail, const char *section,
+		      const struct message_part **part_r,
+		      const char **subsection_r)
+{
+	struct message_part *part;
+	const char *path;
+	unsigned int num;
+
+	if (mail_get_parts(mail, &part) < 0)
+		return -1;
+
+	path = section;
+	while (*path >= '0' && *path <= '9' && part != NULL) {
+		/* get part number, we have already verified its validity */
+		num = 0;
+		while (*path != '\0' && *path != '.') {
+			i_assert(*path >= '0' && *path <= '9');
+
+			num = num*10 + (*path - '0');
+			path++;
+		}
+
+		if (*path == '.')
+			path++;
+
+		if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
+			/* find the part */
+			part = part->children;
+			for (; num > 1 && part != NULL; num--)
+				part = part->next;
+		} else {
+			/* only 1 allowed with non-multipart messages */
+			if (num != 1)
+				part = NULL;
+		}
+
+		if (part != NULL &&
+		    (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0 &&
+		    (*path >= '0' && *path <= '9')) {
+			/* if we continue inside the message/rfc822, skip this
+			   body part */
+			part = part->children;
+		}
+	}
+
+	*part_r = part;
+	*subsection_r = path;
+	return 0;
+}
+
+static bool
+imap_msgpart_get_header_fields(const char *header_list,
+			       const char *const **fields_r, size_t *count_r)
+{
+	struct istream *input;
+	struct imap_parser *parser;
+	const struct imap_arg *args, *hdr_list;
+	unsigned int list_count;
+	ARRAY_TYPE(const_string) fields = ARRAY_INIT;
+	unsigned int i;
+	bool result = TRUE;
+
+	input = i_stream_create_from_data(header_list, strlen(header_list));
+	parser = imap_parser_create(input, NULL, (size_t)-1);
+
+	if (imap_parser_finish_line(parser, 0, 0, &args) > 0 &&
+	    imap_arg_get_list_full(args, &hdr_list, &list_count) &&
+	    list_count > 0) {
+		const char *value;
+		
+		if (fields_r != NULL)
+			t_array_init(&fields, list_count);
+
+		for (i = 0; i < list_count; i++) {
+			if (!imap_arg_get_astring(&hdr_list[i], &value)) {
+				result = FALSE;
+				break;
+			}
+
+			if (fields_r != NULL) {
+				value = t_str_ucase(value);
+				array_append(&fields, &value, 1);
+			}
+		}
+
+		if (fields_r != NULL) {
+			*fields_r = array_get(&fields, &list_count);
+			*count_r = list_count;
+		}
+	} else {
+		result = FALSE;
+	}
+
+	imap_parser_unref(&parser);
+	i_stream_unref(&input);
+
+	return result;
+}
+
+static bool
+imap_msgpart_verify_header_fields(const char *header_list, const char **error_r)
+{
+	/* HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */
+	if (!imap_msgpart_get_header_fields(header_list, NULL, NULL)) {
+		*error_r = "Invalid header fields";
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static struct istream *
+imap_msgpart_get_partial_header(struct istream *mail_input, bool exclude,
+				const char *header_list,
+				struct message_size *hdr_size_r,
+				const char **error_r)
+{
+	const char *const *hdr_fields;
+	size_t hdr_count;
+	struct istream *input;
+	uoff_t old_offset;
+
+	/* HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */
+	if (!imap_msgpart_get_header_fields(header_list, &hdr_fields, &hdr_count)) {
+		*error_r = "Invalid header fields";
+		return NULL;
+	}
+
+	if (!exclude) {
+		input = i_stream_create_header_filter(mail_input,
+						      HEADER_FILTER_INCLUDE,
+						      hdr_fields, hdr_count,
+						      null_header_filter_callback, NULL);
+	} else {
+		input = i_stream_create_header_filter(mail_input,
+						      HEADER_FILTER_EXCLUDE,
+						      hdr_fields, hdr_count,
+						      null_header_filter_callback, NULL);
+	}
+
+	old_offset = input->v_offset;
+	if (message_get_header_size(input, hdr_size_r, NULL) < 0) {
+		*error_r = "Failed to determine header size";
+		return NULL;
+	}
+	i_stream_seek(input, old_offset);
+
+	return input;
+}
+
+static bool
+imap_msgpart_get_partial(struct istream *input, struct message_size part_size,
+			 uoff_t partial_offset, uoff_t partial_size,
+			 struct istream **stream_r, uoff_t *size_r)
+{
+	struct istream *result;
+	uoff_t size = part_size.virtual_size;
+
+	if (partial_offset >= size) {
+		i_stream_unref(&input);
+		*size_r = 0;
+		*stream_r = NULL;
+		return TRUE;
+	}
+
+	if (size != part_size.physical_size) {
+		result = i_stream_create_crlf(input);
+		i_stream_unref(&input);
+		input = result;
+	}
+
+	if (partial_offset > 0)
+		i_stream_seek(input, input->v_offset + partial_offset);
+
+	size = partial_size > 0 && (size - partial_offset) > partial_size ?
+		partial_size : (size - partial_offset);
+	result = i_stream_create_limit(input, size);
+	i_stream_unref(&input);
+	
+	*size_r = size;
+	*stream_r = result;
+	return TRUE;
+}
+
+bool imap_msgpart_open(struct mail *mail, const char *section,
+		       uoff_t partial_offset, uoff_t partial_size,
+		       struct istream **stream_r,
+		       uoff_t *size_r, const char **error_r)
+{
+	struct message_size hdr_size, body_size, part_size;
+	struct istream *input = NULL;
+
+	/* only get stream when we intend to read actual data */
+	if (stream_r != NULL) {
+		if (mail_get_stream(mail, &hdr_size, &body_size, &input) < 0) {
+			*error_r = "Failed to read message";
+			return FALSE;
+		}
+	}
+
+	if (section == NULL || *section == '\0') {
+		/* full message */
+		if (stream_r == NULL)
+			return TRUE;
+
+		part_size.physical_size =
+			hdr_size.physical_size + body_size.physical_size;
+		part_size.virtual_size =
+			hdr_size.virtual_size + body_size.virtual_size;
+
+		i_stream_seek(input, 0);
+		i_stream_ref(input);
+		return imap_msgpart_get_partial(input, part_size,
+						partial_offset, partial_size,
+						stream_r, size_r);
+	}
+
+	section = t_str_ucase(section);
+
+	if (strcmp(section, "TEXT") == 0) {
+		/* message body */
+		if (stream_r == NULL)
+			return TRUE;
+		
+		i_stream_seek(input, hdr_size.physical_size);
+		i_stream_ref(input);
+		return imap_msgpart_get_partial(input, body_size,
+						partial_offset, partial_size,
+						stream_r, size_r);
+	}
+
+	if (strncmp(section, "HEADER", 6) == 0) {
+		/* header */
+		if (stream_r == NULL) {
+			if (section[6] == '\0') {
+				return TRUE;
+			} else if (strncmp(section, "HEADER.FIELDS ", 14) == 0) {
+				return imap_msgpart_verify_header_fields(section+14, error_r);
+			} else if (strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0) {
+				return imap_msgpart_verify_header_fields(section+18, error_r);
+			}
+		} else {
+			i_stream_seek(input, 0);
+
+			if (section[6] == '\0') {
+				i_stream_ref(input);
+			} else if (strncmp(section, "HEADER.FIELDS ", 14) == 0) {
+				input = imap_msgpart_get_partial_header(input,
+					FALSE, section+14, &hdr_size, error_r);
+			} else if (strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0) {
+				input = imap_msgpart_get_partial_header(input,
+					TRUE, section+18, &hdr_size, error_r);
+			} else {
+				input = NULL;
+			}
+
+			if (input != NULL) {
+				return imap_msgpart_get_partial(input,
+					hdr_size, partial_offset, partial_size,
+					stream_r, size_r);
+			}
+		}
+
+	} else if (*section >= '0' && *section <= '9') {
+		const struct message_part *part;
+		const char *subsection;
+
+		if (imap_msgpart_find(mail, section, &part, &subsection) < 0) {
+			*error_r = "Cannot read message part";
+			return FALSE;
+		}
+
+		if (part == NULL) {
+			*error_r = "Unknown message part";
+			return FALSE;
+		}
+
+		if (*subsection == '\0') {
+			if (stream_r == NULL)
+				return TRUE;
+		
+			/* fetch the whole section */
+			i_stream_seek(input, part->physical_pos +
+				      part->header_size.physical_size);
+			i_stream_ref(input);
+			return imap_msgpart_get_partial(input, part->body_size, partial_offset,
+					partial_size, stream_r, size_r);
+		}
+
+		if (strcmp(subsection, "MIME") == 0) {
+			if (stream_r == NULL)
+				return TRUE;
+
+			/* fetch section's MIME header */
+			i_stream_seek(input, part->physical_pos);
+			i_stream_ref(input);
+			return imap_msgpart_get_partial(input, part->header_size,
+				partial_offset, partial_size, stream_r, size_r);
+		}
+
+		/* TEXT and HEADER are only for message/rfc822 parts */
+		if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0) {
+			*error_r = "Invalid section";
+			return FALSE;
+		}
+
+		i_assert(part->children != NULL && part->children->next == NULL);
+		part = part->children;
+
+		if (strcmp(subsection, "TEXT") == 0) {
+			if (stream_r == NULL)
+				return TRUE;
+
+			/* sub-message body */
+			i_stream_seek(input, part->physical_pos +
+				      part->header_size.physical_size);
+			i_stream_ref(input);
+			return imap_msgpart_get_partial(input, part->body_size,
+				partial_offset, partial_size, stream_r, size_r);
+		}	
+
+		if (strncmp(subsection, "HEADER", 6) == 0) {
+			if (stream_r == NULL) {
+				if (section[6] == '\0') {
+					return TRUE;
+				} else if (strncmp(subsection, "HEADER.FIELDS ", 14) == 0) {
+					return imap_msgpart_verify_header_fields(subsection+14, error_r);
+				} else if (strncmp(subsection, "HEADER.FIELDS.NOT ", 18) == 0) {
+					return imap_msgpart_verify_header_fields(subsection+18, error_r);
+				}
+			} else {
+				i_stream_seek(input, part->physical_pos);
+
+				if (subsection[6] == '\0') {
+					/* full */
+					hdr_size = part->header_size;
+					i_stream_ref(input);
+				} else if (strncmp(subsection, "HEADER.FIELDS ", 14) == 0) {
+					input = imap_msgpart_get_partial_header(
+							input, FALSE, section+14,
+							&hdr_size, error_r);
+				} else if (strncmp(subsection, "HEADER.FIELDS.NOT ", 18) == 0) {
+					input = imap_msgpart_get_partial_header(
+							input, TRUE, section+18,
+							&hdr_size, error_r);
+				} else {
+					input = NULL;
+				}
+
+				if (input != NULL) {
+					return imap_msgpart_get_partial(input,
+						hdr_size, partial_offset,
+						partial_size, stream_r, size_r);
+				}
+			}
+		}
+	}
+
+	*error_r = "Invalid section";
+	return FALSE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-imap-storage/imap-msgpart.h	Sat Jun 02 17:56:27 2012 +0300
@@ -0,0 +1,24 @@
+#ifndef IMAP_MSGPART_H
+#define IMAP_MSGPART_H
+
+/* Find message_part for section (eg. 1.3.4) */
+int imap_msgpart_find(struct mail *mail, const char *section,
+		      const struct message_part **part_r,
+		      const char **subsection_r);
+
+/* Open message part refenced by IMAP section as istream. Returns TRUE on
+   success and FALSE otherwise. Returned stream_r stream may be NULL when there
+   is no data to return. */
+bool imap_msgpart_open(struct mail *mail, const char *section,
+		       uoff_t partial_offset, uoff_t partial_size,
+		       struct istream **stream_r,
+		       uoff_t *size_r, const char **error_r);
+
+static inline bool
+imap_msgpart_verify(struct mail *mail, const char *section,
+		    const char **error_r)
+{
+	return imap_msgpart_open(mail, section, 0, 0, NULL, NULL, error_r);
+}
+
+#endif
--- a/src/lib-storage/Makefile.am	Sat Jun 02 17:53:31 2012 +0300
+++ b/src/lib-storage/Makefile.am	Sat Jun 02 17:56:27 2012 +0300
@@ -82,6 +82,7 @@
 	index/libstorage_index.la \
 	register/libstorage_register.la \
 	../lib-index/libindex.la \
+	../lib-imap-storage/libimap-storage.la \
 	../lib-dovecot/libdovecot.la
 
 pkglib_LTLIBRARIES = libdovecot-storage.la