summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/istream-mail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-storage/index/istream-mail.c')
-rw-r--r--src/lib-storage/index/istream-mail.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/lib-storage/index/istream-mail.c b/src/lib-storage/index/istream-mail.c
new file mode 100644
index 0000000..1477823
--- /dev/null
+++ b/src/lib-storage/index/istream-mail.c
@@ -0,0 +1,173 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mail-storage-private.h"
+#include "istream-private.h"
+#include "index-mail.h"
+#include "istream-mail.h"
+
+struct mail_istream {
+ struct istream_private istream;
+
+ struct mail *mail;
+ uoff_t expected_size;
+ bool files_read_increased:1;
+ bool input_has_body:1;
+};
+
+static bool i_stream_mail_try_get_cached_size(struct mail_istream *mstream)
+{
+ struct mail *mail = mstream->mail;
+ enum mail_lookup_abort orig_lookup_abort;
+
+ if (mstream->expected_size != UOFF_T_MAX)
+ return TRUE;
+
+ /* make sure this call doesn't change any existing error message,
+ just in case there's already something important in it. */
+ mail_storage_last_error_push(mail->box->storage);
+ orig_lookup_abort = mail->lookup_abort;
+ mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE;
+ if (mail_get_physical_size(mail, &mstream->expected_size) < 0)
+ mstream->expected_size = UOFF_T_MAX;
+ mail->lookup_abort = orig_lookup_abort;
+ mail_storage_last_error_pop(mail->box->storage);
+ return mstream->expected_size != UOFF_T_MAX;
+}
+
+static const char *
+i_stream_mail_get_cached_mail_id(struct mail_istream *mstream ATTR_UNUSED)
+{
+#if 0
+ /* FIXME: This function may get called in the middle of header parsing,
+ which then goes into parsing cached headers and causes crashes.
+ So disable this for now. Eventually it would be nice if recursion
+ was possible by each parser using its own private struct. */
+ static const char *headers[] = {
+ "Message-Id",
+ "Date",
+ "Subject"
+ };
+ struct mail *mail = mstream->mail;
+ enum mail_lookup_abort orig_lookup_abort;
+ const char *value, *ret = "";
+ unsigned int i;
+
+ orig_lookup_abort = mail->lookup_abort;
+ mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE;
+ for (i = 0; i < N_ELEMENTS(headers); i++) {
+ if (mail_get_first_header(mail, headers[i], &value) > 0) {
+ ret = t_strdup_printf("%s=%s", headers[i], value);
+ break;
+ }
+ }
+ mail->lookup_abort = orig_lookup_abort;
+ return ret;
+#else
+ return "";
+#endif
+}
+
+static void
+i_stream_mail_set_size_corrupted(struct mail_istream *mstream, size_t size)
+{
+ uoff_t cur_size = mstream->istream.istream.v_offset + size;
+ const char *str, *mail_id;
+ char chr;
+
+ if (mstream->expected_size < cur_size) {
+ /* input stream is larger than cached message size */
+ str = "smaller";
+ chr = '<';
+ mstream->istream.istream.stream_errno = EINVAL;
+ } else {
+ /* input stream is smaller than cached message size */
+ str = "larger";
+ chr = '>';
+ mstream->istream.istream.stream_errno = EPIPE;
+ }
+
+ mail_id = i_stream_mail_get_cached_mail_id(mstream);
+ if (mail_id[0] != '\0')
+ mail_id = t_strconcat(", cached ", mail_id, NULL);
+ io_stream_set_error(&mstream->istream.iostream,
+ "Cached message size %s than expected "
+ "(%"PRIuUOFF_T" %c %"PRIuUOFF_T", box=%s, UID=%u%s)", str,
+ mstream->expected_size, chr, cur_size,
+ mailbox_get_vname(mstream->mail->box),
+ mstream->mail->uid, mail_id);
+ mail_set_cache_corrupted(mstream->mail, MAIL_FETCH_PHYSICAL_SIZE,
+ t_strdup_printf("read(%s) failed: %s",
+ i_stream_get_name(&mstream->istream.istream),
+ mstream->istream.iostream.error));
+}
+
+static ssize_t
+i_stream_mail_read(struct istream_private *stream)
+{
+ struct mail_istream *mstream = (struct mail_istream *)stream;
+ size_t size;
+ ssize_t ret;
+
+ i_stream_seek(stream->parent, stream->parent_start_offset +
+ stream->istream.v_offset);
+
+ ret = i_stream_read_copy_from_parent(&stream->istream);
+ size = i_stream_get_data_size(&stream->istream);
+ if (ret > 0) {
+ mstream->mail->transaction->stats.files_read_bytes += ret;
+ if (!mstream->files_read_increased) {
+ mstream->files_read_increased = TRUE;
+ mstream->mail->transaction->stats.files_read_count++;
+ }
+ if (mstream->expected_size < stream->istream.v_offset + size) {
+ i_stream_mail_set_size_corrupted(mstream, size);
+ /* istream code expects that the position has not changed
+ when read error occurs, so move pos back. */
+ i_assert(stream->pos >= (size_t)ret);
+ stream->pos -= ret;
+ return -1;
+ }
+ } else if (ret == -1 && stream->istream.eof) {
+ if (!mstream->input_has_body) {
+ /* trying to read past the header, but this stream
+ doesn't have the body */
+ return -1;
+ }
+ if (stream->istream.stream_errno != 0) {
+ if (stream->istream.stream_errno == ENOENT) {
+ /* update mail's expunged-flag if needed */
+ index_mail_refresh_expunged(mstream->mail);
+ }
+ return -1;
+ }
+ if (i_stream_mail_try_get_cached_size(mstream) &&
+ mstream->expected_size > stream->istream.v_offset + size) {
+ i_stream_mail_set_size_corrupted(mstream, size);
+ return -1;
+ }
+ }
+ return ret;
+}
+
+struct istream *i_stream_create_mail(struct mail *mail, struct istream *input,
+ bool input_has_body)
+{
+ struct mail_istream *mstream;
+
+ mstream = i_new(struct mail_istream, 1);
+ mstream->mail = mail;
+ mstream->input_has_body = input_has_body;
+ mstream->expected_size = UOFF_T_MAX;
+ (void)i_stream_mail_try_get_cached_size(mstream);
+ mstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
+ mstream->istream.stream_size_passthrough = TRUE;
+
+ mstream->istream.read = i_stream_mail_read;
+
+ mstream->istream.istream.readable_fd = input->readable_fd;
+ mstream->istream.istream.blocking = input->blocking;
+ mstream->istream.istream.seekable = input->seekable;
+ return i_stream_create(&mstream->istream, input,
+ i_stream_get_fd(input), 0);
+}