diff options
Diffstat (limited to 'src/lib-storage/index/pop3c/pop3c-mail.c')
-rw-r--r-- | src/lib-storage/index/pop3c/pop3c-mail.c | 304 |
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, +}; |