summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/index/pop3c/pop3c-mail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-storage/index/pop3c/pop3c-mail.c')
-rw-r--r--src/lib-storage/index/pop3c/pop3c-mail.c304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/lib-storage/index/pop3c/pop3c-mail.c b/src/lib-storage/index/pop3c/pop3c-mail.c
new file mode 100644
index 0000000..27a8427
--- /dev/null
+++ b/src/lib-storage/index/pop3c/pop3c-mail.c
@@ -0,0 +1,304 @@
+/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "index-mail.h"
+#include "pop3c-client.h"
+#include "pop3c-sync.h"
+#include "pop3c-storage.h"
+
+struct mail *
+pop3c_mail_alloc(struct mailbox_transaction_context *t,
+ enum mail_fetch_field wanted_fields,
+ struct mailbox_header_lookup_ctx *wanted_headers)
+{
+ struct pop3c_mail *mail;
+ pool_t pool;
+
+ pool = pool_alloconly_create("mail", 2048);
+ mail = p_new(pool, struct pop3c_mail, 1);
+
+ index_mail_init(&mail->imail, t, wanted_fields, wanted_headers, pool, NULL);
+ return &mail->imail.mail.mail;
+}
+
+static void pop3c_mail_close(struct mail *_mail)
+{
+ struct pop3c_mail *pmail = POP3C_MAIL(_mail);
+ struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
+
+ /* wait for any prefetch to finish before closing the mail */
+ while (pmail->prefetching)
+ pop3c_client_wait_one(mbox->client);
+ i_stream_unref(&pmail->prefetch_stream);
+ index_mail_close(_mail);
+}
+
+static int pop3c_mail_get_received_date(struct mail *_mail, time_t *date_r)
+{
+ struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
+ int tz;
+
+ if (mbox->storage->set->pop3c_quick_received_date) {
+ /* we don't care about the date, just return the current date */
+ *date_r = ioloop_time;
+ return 0;
+ }
+
+ /* FIXME: we could also parse the first Received: header and get
+ the date from there, but since this code is unlikely to be called
+ except during migration, I don't think it really matters. */
+ return index_mail_get_date(_mail, date_r, &tz);
+}
+
+static int pop3c_mail_get_save_date(struct mail *_mail, time_t *date_r)
+{
+ struct index_mail *mail = INDEX_MAIL(_mail);
+ struct index_mail_data *data = &mail->data;
+
+ if (data->save_date == (time_t)-1) {
+ /* FIXME: we could use a value stored in cache */
+ if (pop3c_mail_get_received_date(_mail, date_r) < 0)
+ return -1;
+ return 0;
+ }
+ *date_r = data->save_date;
+ return 0;
+}
+
+static int pop3c_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
+{
+ struct index_mail *mail = INDEX_MAIL(_mail);
+ struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
+ struct message_size hdr_size, body_size;
+ struct istream *input;
+
+ if (mail->data.virtual_size != UOFF_T_MAX) {
+ /* virtual size is already known. it's the same as our
+ (correct) physical size */
+ *size_r = mail->data.virtual_size;
+ return 0;
+ }
+ if (index_mail_get_physical_size(_mail, size_r) == 0) {
+ *size_r = mail->data.physical_size;
+ return 0;
+ }
+
+ if (_mail->lookup_abort == MAIL_LOOKUP_ABORT_READ_MAIL &&
+ (_mail->box->flags & MAILBOX_FLAG_POP3_SESSION) != 0) {
+ /* kludge: we want output for POP3 LIST with
+ pop3_fast_size_lookups=yes. use the remote's LIST values
+ regardless of their correctness */
+ if (mbox->msg_sizes == NULL) {
+ if (pop3c_sync_get_sizes(mbox) < 0)
+ return -1;
+ }
+ i_assert(_mail->seq <= mbox->msg_count);
+ *size_r = mbox->msg_sizes[_mail->seq-1];
+ return 0;
+ }
+
+ /* slow way: get the whole message body */
+ if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0)
+ return -1;
+
+ i_assert(mail->data.physical_size != UOFF_T_MAX);
+ *size_r = mail->data.physical_size;
+ return 0;
+}
+
+static void pop3c_mail_cache_size(struct index_mail *mail)
+{
+ uoff_t size;
+
+ if (i_stream_get_size(mail->data.stream, TRUE, &size) <= 0)
+ return;
+ mail->data.virtual_size = size;
+ /* it'll be actually added to index when closing the mail in
+ index_mail_cache_sizes() */
+}
+
+static void
+pop3c_mail_prefetch_done(enum pop3c_command_state state,
+ const char *reply ATTR_UNUSED, void *context)
+{
+ struct pop3c_mail *pmail = context;
+
+ switch (state) {
+ case POP3C_COMMAND_STATE_OK:
+ break;
+ case POP3C_COMMAND_STATE_ERR:
+ case POP3C_COMMAND_STATE_DISCONNECTED:
+ i_stream_unref(&pmail->prefetch_stream);
+ /* let pop3c_mail_get_stream() figure out the error handling.
+ in case of a -ERR a retry might even work. */
+ break;
+ }
+ pmail->prefetching = FALSE;
+}
+
+static bool pop3c_mail_prefetch(struct mail *_mail)
+{
+ struct pop3c_mail *pmail = POP3C_MAIL(_mail);
+ struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
+ enum pop3c_capability capa;
+ const char *cmd;
+
+ if (pmail->imail.data.access_part != 0 &&
+ pmail->imail.data.stream == NULL &&
+ mail_stream_access_start(_mail)) {
+ capa = pop3c_client_get_capabilities(mbox->client);
+ pmail->prefetching_body = (capa & POP3C_CAPABILITY_TOP) == 0 ||
+ (pmail->imail.data.access_part & (READ_BODY | PARSE_BODY)) != 0;
+ if (pmail->prefetching_body)
+ cmd = t_strdup_printf("RETR %u\r\n", _mail->seq);
+ else
+ cmd = t_strdup_printf("TOP %u 0\r\n", _mail->seq);
+
+ pmail->prefetching = TRUE;
+ pmail->prefetch_stream =
+ pop3c_client_cmd_stream_async(mbox->client, cmd,
+ pop3c_mail_prefetch_done, pmail);
+ i_stream_set_name(pmail->prefetch_stream, t_strcut(cmd, '\r'));
+ return !pmail->prefetching;
+ }
+ return index_mail_prefetch(_mail);
+}
+
+static int
+pop3c_mail_get_stream(struct mail *_mail, bool get_body,
+ struct message_size *hdr_size,
+ struct message_size *body_size, struct istream **stream_r)
+{
+ struct pop3c_mail *pmail = POP3C_MAIL(_mail);
+ struct index_mail *mail = &pmail->imail;
+ struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
+ enum pop3c_capability capa;
+ const char *name, *cmd, *error;
+ struct istream *input;
+ bool new_stream = FALSE;
+
+ if ((mail->data.access_part & (READ_BODY | PARSE_BODY)) != 0)
+ get_body = TRUE;
+
+ while (pmail->prefetching) {
+ /* wait for prefetch to finish */
+ pop3c_client_wait_one(mbox->client);
+ }
+
+ if (pmail->prefetch_stream != NULL && mail->data.stream == NULL) {
+ mail->data.stream = pmail->prefetch_stream;
+ pmail->prefetch_stream = NULL;
+ new_stream = TRUE;
+ }
+
+ if (get_body && mail->data.stream != NULL) {
+ name = i_stream_get_name(mail->data.stream);
+ if (str_begins(name, "RETR")) {
+ /* we've fetched the body */
+ } else if (str_begins(name, "TOP")) {
+ /* we've fetched the header, but we need the body
+ now too */
+ index_mail_close_streams(mail);
+ } else {
+ i_panic("Unexpected POP3 stream name: %s", name);
+ }
+ }
+
+ if (mail->data.stream == NULL) {
+ if (!mail_stream_access_start(_mail))
+ return -1;
+ capa = pop3c_client_get_capabilities(mbox->client);
+ if (get_body || (capa & POP3C_CAPABILITY_TOP) == 0) {
+ cmd = t_strdup_printf("RETR %u\r\n", _mail->seq);
+ get_body = TRUE;
+ } else {
+ cmd = t_strdup_printf("TOP %u 0\r\n", _mail->seq);
+ }
+ if (pop3c_client_cmd_stream(mbox->client, cmd,
+ &input, &error) < 0) {
+ mail_storage_set_error(mbox->box.storage,
+ !pop3c_client_is_connected(mbox->client) ?
+ MAIL_ERROR_TEMP : MAIL_ERROR_EXPUNGED, error);
+ return -1;
+ }
+ mail->data.stream = input;
+ i_stream_set_name(mail->data.stream, t_strcut(cmd, '\r'));
+ new_stream = TRUE;
+ }
+ if (new_stream) {
+ if (mail->mail.v.istream_opened != NULL) {
+ if (mail->mail.v.istream_opened(_mail,
+ &mail->data.stream) < 0) {
+ index_mail_close_streams(mail);
+ return -1;
+ }
+ }
+ if (get_body)
+ pop3c_mail_cache_size(mail);
+ }
+ /* if this stream is used by some filter stream, make the
+ filter stream blocking */
+ mail->data.stream->blocking = TRUE;
+ return index_mail_init_stream(mail, hdr_size, body_size, stream_r);
+}
+
+static int
+pop3c_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
+ const char **value_r)
+{
+ struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
+
+ switch (field) {
+ case MAIL_FETCH_UIDL_BACKEND:
+ case MAIL_FETCH_GUID:
+ if (mbox->msg_uidls == NULL) {
+ if (pop3c_sync_get_uidls(mbox) < 0)
+ return -1;
+ }
+ i_assert(_mail->seq <= mbox->msg_count);
+ *value_r = mbox->msg_uidls[_mail->seq-1];
+ return 0;
+ default:
+ return index_mail_get_special(_mail, field, value_r);
+ }
+}
+
+struct mail_vfuncs pop3c_mail_vfuncs = {
+ pop3c_mail_close,
+ index_mail_free,
+ index_mail_set_seq,
+ index_mail_set_uid,
+ index_mail_set_uid_cache_updates,
+ pop3c_mail_prefetch,
+ index_mail_precache,
+ index_mail_add_temp_wanted_fields,
+
+ index_mail_get_flags,
+ index_mail_get_keywords,
+ index_mail_get_keyword_indexes,
+ index_mail_get_modseq,
+ index_mail_get_pvt_modseq,
+ index_mail_get_parts,
+ index_mail_get_date,
+ pop3c_mail_get_received_date,
+ pop3c_mail_get_save_date,
+ index_mail_get_virtual_size,
+ pop3c_mail_get_physical_size,
+ index_mail_get_first_header,
+ index_mail_get_headers,
+ index_mail_get_header_stream,
+ pop3c_mail_get_stream,
+ index_mail_get_binary_stream,
+ pop3c_mail_get_special,
+ index_mail_get_backend_mail,
+ index_mail_update_flags,
+ index_mail_update_keywords,
+ index_mail_update_modseq,
+ index_mail_update_pvt_modseq,
+ NULL,
+ index_mail_expunge,
+ index_mail_set_cache_corrupted,
+ index_mail_opened,
+};