pop3c-mail.c revision 120e61ccce21fd33d6b55b6f27346e1690b64bd8
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen#include "lib.h"
d6499957ea59e6d9729d3350d9ac5eae992635f6Timo Sirainen#include "ioloop.h"
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen#include "istream.h"
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen#include "index-mail.h"
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen#include "pop3c-client.h"
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen#include "pop3c-sync.h"
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen#include "pop3c-storage.h"
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainenstruct mail *
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainenpop3c_mail_alloc(struct mailbox_transaction_context *t,
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen enum mail_fetch_field wanted_fields,
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen struct mailbox_header_lookup_ctx *wanted_headers)
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen{
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen struct pop3c_mail *mail;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen pool_t pool;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen pool = pool_alloconly_create("mail", 2048);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen mail = p_new(pool, struct pop3c_mail, 1);
4c0aff96fc7e6d779be43458f96cbf015849a3deTimo Sirainen mail->imail.mail.pool = pool;
4c0aff96fc7e6d779be43458f96cbf015849a3deTimo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen index_mail_init(&mail->imail, t, wanted_fields, wanted_headers);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen return &mail->imail.mail.mail;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen}
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen
4c0aff96fc7e6d779be43458f96cbf015849a3deTimo Sirainenstatic void pop3c_mail_close(struct mail *_mail)
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen{
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen struct pop3c_mail *pmail = POP3C_MAIL(_mail);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen /* wait for any prefetch to finish before closing the mail */
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen while (pmail->prefetching)
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen pop3c_client_wait_one(mbox->client);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen if (pmail->prefetch_stream != NULL)
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen i_stream_unref(&pmail->prefetch_stream);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen index_mail_close(_mail);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen}
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainenstatic int pop3c_mail_get_received_date(struct mail *_mail, time_t *date_r)
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen{
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen int tz;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen if (mbox->storage->set->pop3c_quick_received_date) {
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen /* we don't care about the date, just return the current date */
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen *date_r = ioloop_time;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen return 0;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen }
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen /* FIXME: we could also parse the first Received: header and get
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen the date from there, but since this code is unlikely to be called
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen except during migration, I don't think it really matters. */
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen return index_mail_get_date(_mail, date_r, &tz);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen}
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainenstatic int pop3c_mail_get_save_date(struct mail *_mail, time_t *date_r)
d6499957ea59e6d9729d3350d9ac5eae992635f6Timo Sirainen{
4c0aff96fc7e6d779be43458f96cbf015849a3deTimo Sirainen struct index_mail *mail = INDEX_MAIL(_mail);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen struct index_mail_data *data = &mail->data;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen if (data->save_date == (time_t)-1) {
77b5fd56e5a06d624f3ab92198272287333114f4Timo Sirainen /* FIXME: we could use a value stored in cache */
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen return pop3c_mail_get_received_date(_mail, date_r);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen }
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen *date_r = data->save_date;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen return 0;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen}
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainenstatic int pop3c_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen{
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen struct index_mail *mail = INDEX_MAIL(_mail);
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen struct pop3c_mailbox *mbox = POP3C_MAILBOX(_mail->box);
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi struct message_size hdr_size, body_size;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen struct istream *input;
2a6c02a5e7e5cdf4980befbfaf372603ce338a25Timo Sirainen
2a6c02a5e7e5cdf4980befbfaf372603ce338a25Timo Sirainen if (mail->data.virtual_size != (uoff_t)-1) {
2a6c02a5e7e5cdf4980befbfaf372603ce338a25Timo Sirainen /* virtual size is already known. it's the same as our
2a6c02a5e7e5cdf4980befbfaf372603ce338a25Timo Sirainen (correct) physical size */
2a6c02a5e7e5cdf4980befbfaf372603ce338a25Timo Sirainen *size_r = mail->data.virtual_size;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen return 0;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen }
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen if (index_mail_get_physical_size(_mail, size_r) == 0) {
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen *size_r = mail->data.physical_size;
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainen return 0;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen }
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen if (_mail->lookup_abort == MAIL_LOOKUP_ABORT_READ_MAIL &&
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen (_mail->box->flags & MAILBOX_FLAG_POP3_SESSION) != 0) {
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen /* kludge: we want output for POP3 LIST with
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen pop3_fast_size_lookups=yes. use the remote's LIST values
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen regardless of their correctness */
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen 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)-1);
*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) {
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 (strncmp(name, "RETR", 4) == 0) {
/* we've fetched the body */
} else if (strncmp(name, "TOP", 3) == 0) {
/* 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) {
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,
};