imapc-mail-fetch.c revision 0f641fb49bf0a934556717a2c209566ac387d7bb
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen/* Copyright (c) 2011-2013 Dovecot authors, see the included COPYING file */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "lib.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "str.h"
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen#include "istream.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "istream-header-filter.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "message-header-parser.h"
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen#include "imap-arg.h"
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#include "imap-date.h"
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#include "imap-quote.h"
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#include "imapc-client.h"
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#include "imapc-mail.h"
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#include "imapc-storage.h"
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic void
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenimapc_mail_prefetch_callback(const struct imapc_command_reply *reply,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen void *context)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen struct imapc_mail *mail = context;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct imapc_mailbox *mbox =
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen (struct imapc_mailbox *)mail->imail.mail.mail.box;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_assert(mail->fetch_count > 0);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (--mail->fetch_count == 0) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct imapc_mail *const *fetch_mails;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen unsigned int i, count;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fetch_mails = array_get(&mbox->fetch_mails, &count);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen for (i = 0; i < count; i++) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (fetch_mails[i] == mail) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen array_delete(&mbox->fetch_mails, i, 1);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen break;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_assert(i != count);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen mail->fetching_fields = 0;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (reply->state == IMAPC_COMMAND_STATE_OK)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen ;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen else if (reply->state == IMAPC_COMMAND_STATE_NO) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen imapc_copy_error_from_reply(mbox->storage, MAIL_ERROR_PARAMS,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen reply);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* The disconnection message was already logged */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen mail_storage_set_internal_error(&mbox->storage->storage);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen } else {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen mail_storage_set_critical(&mbox->storage->storage,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen "imapc: Mail prefetch failed: %s", reply->text_full);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen pool_unref(&mail->imail.mail.pool);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen imapc_client_stop(mbox->storage->client->client);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenstatic bool
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenheaders_have_subset(const char *const *superset, const char *const *subset)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen{
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen unsigned int i;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (superset == NULL)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return FALSE;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (subset != NULL) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen for (i = 0; subset[i] != NULL; i++) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (!str_array_icase_find(superset, subset[i]))
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen return FALSE;
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen }
d4c3d55021bcbf2b062f4782b1cde9115d35aefcTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return TRUE;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen}
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic const char *const *
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenheaders_merge(pool_t pool, const char *const *h1, const char *const *h2)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen{
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen ARRAY_TYPE(const_string) headers;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen const char *value;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen unsigned int i;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen p_array_init(&headers, pool, 16);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (h1 != NULL) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen for (i = 0; h1[i] != NULL; i++) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen value = p_strdup(pool, h1[i]);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen array_append(&headers, &value, 1);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (h2 != NULL) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen for (i = 0; h2[i] != NULL; i++) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (h1 == NULL || !str_array_icase_find(h1, h2[i])) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen value = p_strdup(pool, h2[i]);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen array_append(&headers, &value, 1);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen array_append_zero(&headers);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return array_idx(&headers, 0);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen}
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenstatic int
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainenimapc_mail_send_fetch(struct mail *_mail, enum mail_fetch_field fields,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen const char *const *headers)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen{
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct imapc_mail *mail = (struct imapc_mail *)_mail;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct imapc_command *cmd;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen struct mail_index_view *view;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen string_t *str;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen uint32_t seq;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen unsigned int i;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen return -1;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* drop any fields that we may already be fetching currently */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen fields &= ~mail->fetching_fields;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if (headers_have_subset(mail->fetching_headers, headers))
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen headers = NULL;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (fields == 0 && headers == NULL)
ba886767d3817d210b20e1d42983078d189fb13cTimo Sirainen return 0;
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen
ba886767d3817d210b20e1d42983078d189fb13cTimo Sirainen if (!_mail->saving) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen /* if we already know that the mail is expunged,
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen don't try to FETCH it */
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen view = mbox->delayed_sync_view != NULL ?
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen mbox->delayed_sync_view : mbox->box.view;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (!mail_index_lookup_seq(view, _mail->uid, &seq) ||
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen mail_index_is_expunged(view, seq)) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen mail_set_expunged(_mail);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return -1;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
2766f1de8141c09767a959d2d2c3065c5a300bf0Timo Sirainen } else if (mbox->client_box == NULL) {
2766f1de8141c09767a959d2d2c3065c5a300bf0Timo Sirainen /* opened as save-only. we'll need to fetch the mail,
2766f1de8141c09767a959d2d2c3065c5a300bf0Timo Sirainen so actually SELECT/EXAMINE the mailbox */
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_assert(mbox->box.opened);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (imapc_mailbox_select(mbox) < 0)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen return -1;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if ((fields & MAIL_FETCH_STREAM_BODY) != 0)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen fields |= MAIL_FETCH_STREAM_HEADER;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen str = t_str_new(64);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen str_printfa(str, "UID FETCH %u (", _mail->uid);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if ((fields & MAIL_FETCH_RECEIVED_DATE) != 0)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen str_append(str, "INTERNALDATE ");
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if ((fields & MAIL_FETCH_PHYSICAL_SIZE) != 0)
ba886767d3817d210b20e1d42983078d189fb13cTimo Sirainen str_append(str, "RFC822.SIZE ");
ba886767d3817d210b20e1d42983078d189fb13cTimo Sirainen if ((fields & MAIL_FETCH_GUID) != 0) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen str_append(str, mbox->guid_fetch_field_name);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen str_append_c(str, ' ');
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen }
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen if ((fields & MAIL_FETCH_STREAM_BODY) != 0)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen str_append(str, "BODY.PEEK[] ");
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen else if ((fields & MAIL_FETCH_STREAM_HEADER) != 0)
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen str_append(str, "BODY.PEEK[HEADER] ");
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen else if (headers != NULL) {
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen mail->fetching_headers =
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen headers_merge(mail->imail.mail.pool, headers,
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen mail->fetching_headers);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen str_append(str, "BODY.PEEK[HEADER.FIELDS (");
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen for (i = 0; mail->fetching_headers[i] != NULL; i++) {
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (i > 0)
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen str_append_c(str, ' ');
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen imap_append_astring(str, mail->fetching_headers[i]);
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen }
c33d3f93abf8392fdc60e12bea41ffd12cc85a8dTimo Sirainen str_append(str, ")] ");
mail->header_list_fetched = FALSE;
}
str_truncate(str, str_len(str)-1);
str_append_c(str, ')');
pool_ref(mail->imail.mail.pool);
mail->fetching_fields |= fields;
if (mail->fetch_count++ == 0)
array_append(&mbox->fetch_mails, &mail, 1);
cmd = imapc_client_mailbox_cmd(mbox->client_box,
imapc_mail_prefetch_callback, mail);
imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE);
imapc_command_send(cmd, str_c(str));
mail->imail.data.prefetch_sent = TRUE;
return 0;
}
static void imapc_mail_cache_get(struct imapc_mail *mail,
struct imapc_mail_cache *cache)
{
if (mail->body_fetched)
return;
if (cache->fd != -1) {
mail->fd = cache->fd;
mail->imail.data.stream =
i_stream_create_fd(mail->fd, 0, FALSE);
cache->fd = -1;
} else if (cache->buf != NULL) {
mail->body = cache->buf;
mail->imail.data.stream =
i_stream_create_from_data(mail->body->data,
mail->body->used);
cache->buf = NULL;
} else {
return;
}
mail->body_fetched = TRUE;
imapc_mail_init_stream(mail, TRUE);
}
bool imapc_mail_prefetch(struct mail *_mail)
{
struct imapc_mail *mail = (struct imapc_mail *)_mail;
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
struct index_mail_data *data = &mail->imail.data;
enum mail_fetch_field fields = 0;
if (mbox->prev_mail_cache.uid == _mail->uid)
imapc_mail_cache_get(mail, &mbox->prev_mail_cache);
if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0 &&
data->received_date == (time_t)-1)
fields |= MAIL_FETCH_RECEIVED_DATE;
if ((data->wanted_fields & MAIL_FETCH_PHYSICAL_SIZE) != 0 &&
data->physical_size == (uoff_t)-1 &&
IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
fields |= MAIL_FETCH_PHYSICAL_SIZE;
if ((data->wanted_fields & MAIL_FETCH_GUID) != 0 &&
data->guid == NULL && mbox->guid_fetch_field_name != NULL)
fields |= MAIL_FETCH_GUID;
if (data->stream == NULL && data->access_part != 0) {
if ((data->access_part & (READ_BODY | PARSE_BODY)) != 0)
fields |= MAIL_FETCH_STREAM_BODY;
else
fields |= MAIL_FETCH_STREAM_HEADER;
}
if (fields != 0) T_BEGIN {
(void)imapc_mail_send_fetch(_mail, fields,
data->wanted_headers == NULL ? NULL :
data->wanted_headers->name);
} T_END;
return !mail->imail.data.prefetch_sent;
}
static bool
imapc_mail_have_fields(struct imapc_mail *imail, enum mail_fetch_field fields)
{
if ((fields & MAIL_FETCH_RECEIVED_DATE) != 0) {
if (imail->imail.data.received_date == (time_t)-1)
return FALSE;
fields &= ~MAIL_FETCH_RECEIVED_DATE;
}
if ((fields & MAIL_FETCH_PHYSICAL_SIZE) != 0) {
if (imail->imail.data.physical_size == (uoff_t)-1)
return FALSE;
fields &= ~MAIL_FETCH_PHYSICAL_SIZE;
}
if ((fields & MAIL_FETCH_GUID) != 0) {
if (imail->imail.data.guid == NULL)
return FALSE;
fields &= ~MAIL_FETCH_GUID;
}
if ((fields & (MAIL_FETCH_STREAM_HEADER |
MAIL_FETCH_STREAM_BODY)) != 0) {
if (imail->imail.data.stream == NULL)
return FALSE;
fields &= ~(MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY);
}
i_assert(fields == 0);
return TRUE;
}
int imapc_mail_fetch(struct mail *_mail, enum mail_fetch_field fields,
const char *const *headers)
{
struct imapc_mail *imail = (struct imapc_mail *)_mail;
struct imapc_mailbox *mbox =
(struct imapc_mailbox *)_mail->box;
int ret;
if ((fields & MAIL_FETCH_GUID) != 0 &&
mbox->guid_fetch_field_name == NULL) {
mail_storage_set_error(_mail->box->storage,
MAIL_ERROR_NOTPOSSIBLE,
"Message GUID not available in this server");
return -1;
}
T_BEGIN {
ret = imapc_mail_send_fetch(_mail, fields, headers);
} T_END;
if (ret < 0)
return -1;
/* we'll continue waiting until we've got all the fields we wanted,
or until all FETCH replies have been received (i.e. some FETCHes
failed) */
while (imail->fetch_count > 0 &&
(!imapc_mail_have_fields(imail, fields) ||
!imail->header_list_fetched))
imapc_storage_run(mbox->storage);
return 0;
}
static bool imapc_find_lfile_arg(const struct imapc_untagged_reply *reply,
const struct imap_arg *arg, int *fd_r)
{
const struct imap_arg *list;
unsigned int i, count;
for (i = 0; i < reply->file_args_count; i++) {
const struct imapc_arg_file *farg = &reply->file_args[i];
if (farg->parent_arg == arg->parent &&
imap_arg_get_list_full(arg->parent, &list, &count) &&
farg->list_idx < count && &list[farg->list_idx] == arg) {
*fd_r = farg->fd;
return TRUE;
}
}
return FALSE;
}
static void imapc_stream_filter(struct istream **input)
{
static const char *imapc_hide_headers[] = {
/* Added by MS Exchange 2010 when \Flagged flag is set.
This violates IMAP guarantee of messages being immutable. */
"X-Message-Flag"
};
struct istream *filter_input;
filter_input = i_stream_create_header_filter(*input,
HEADER_FILTER_EXCLUDE,
imapc_hide_headers, N_ELEMENTS(imapc_hide_headers),
*null_header_filter_callback, (void *)NULL);
i_stream_unref(input);
*input = filter_input;
}
void imapc_mail_init_stream(struct imapc_mail *mail, bool have_body)
{
struct index_mail *imail = &mail->imail;
struct mail *_mail = &imail->mail.mail;
struct istream *input;
uoff_t size;
int ret;
i_stream_set_name(imail->data.stream,
t_strdup_printf("imapc mail uid=%u", _mail->uid));
index_mail_set_read_buffer_size(_mail, imail->data.stream);
imapc_stream_filter(&imail->data.stream);
if (imail->mail.v.istream_opened != NULL) {
if (imail->mail.v.istream_opened(_mail,
&imail->data.stream) < 0) {
index_mail_close_streams(imail);
return;
}
} else if (have_body) {
ret = i_stream_get_size(imail->data.stream, TRUE, &size);
if (ret < 0) {
index_mail_close_streams(imail);
return;
}
i_assert(ret != 0);
imail->data.physical_size = size;
/* we'll assume that the remote server is working properly and
sending CRLF linefeeds */
imail->data.virtual_size = size;
}
imail->data.stream_has_only_header = !have_body;
if (index_mail_init_stream(imail, NULL, NULL, &input) < 0)
index_mail_close_streams(imail);
}
static void
imapc_fetch_stream(struct imapc_mail *mail,
const struct imapc_untagged_reply *reply,
const struct imap_arg *arg, bool body)
{
struct index_mail *imail = &mail->imail;
const char *value;
int fd;
if (imail->data.stream != NULL) {
if (!body)
return;
/* maybe the existing stream has no body. replace it. */
index_mail_close_streams(imail);
if (mail->fd != -1) {
if (close(mail->fd) < 0)
i_error("close(imapc mail) failed: %m");
mail->fd = -1;
}
}
if (arg->type == IMAP_ARG_LITERAL_SIZE) {
if (!imapc_find_lfile_arg(reply, arg, &fd))
return;
if ((fd = dup(fd)) == -1) {
i_error("dup() failed: %m");
return;
}
mail->fd = fd;
imail->data.stream = i_stream_create_fd(fd, 0, FALSE);
} else {
if (!imap_arg_get_nstring(arg, &value))
return;
if (value == NULL) {
mail_set_expunged(&imail->mail.mail);
return;
}
if (mail->body == NULL) {
mail->body = buffer_create_dynamic(default_pool,
arg->str_len + 1);
}
buffer_set_used_size(mail->body, 0);
buffer_append(mail->body, value, arg->str_len);
imail->data.stream = i_stream_create_from_data(mail->body->data,
mail->body->used);
}
mail->body_fetched = body;
imapc_mail_init_stream(mail, body);
}
static void
imapc_fetch_header_stream(struct imapc_mail *mail,
const struct imapc_untagged_reply *reply,
const struct imap_arg *args)
{
const enum message_header_parser_flags hdr_parser_flags =
MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
MESSAGE_HEADER_PARSER_FLAG_DROP_CR;
const struct imap_arg *hdr_list;
struct mailbox_header_lookup_ctx *headers_ctx;
struct message_header_parser_ctx *parser;
struct message_header_line *hdr;
struct istream *input;
ARRAY_TYPE(const_string) hdr_arr;
const char *value;
int ret, fd;
if (!imap_arg_get_list(args, &hdr_list))
return;
if (!imap_arg_atom_equals(args+1, "]"))
return;
args += 2;
/* see if this is reply to the latest headers list request
(parse it even if it's not) */
t_array_init(&hdr_arr, 16);
while (imap_arg_get_astring(hdr_list, &value)) {
array_append(&hdr_arr, &value, 1);
hdr_list++;
}
if (hdr_list->type != IMAP_ARG_EOL)
return;
array_append_zero(&hdr_arr);
if (headers_have_subset(array_idx(&hdr_arr, 0), mail->fetching_headers))
mail->header_list_fetched = TRUE;
if (args->type == IMAP_ARG_LITERAL_SIZE) {
if (!imapc_find_lfile_arg(reply, args, &fd))
return;
input = i_stream_create_fd(fd, 0, FALSE);
} else {
if (!imap_arg_get_nstring(args, &value))
return;
if (value == NULL) {
mail_set_expunged(&mail->imail.mail.mail);
return;
}
input = i_stream_create_from_data(value, args->str_len);
}
headers_ctx = mailbox_header_lookup_init(mail->imail.mail.mail.box,
array_idx(&hdr_arr, 0));
index_mail_parse_header_init(&mail->imail, headers_ctx);
parser = message_parse_header_init(input, NULL, hdr_parser_flags);
while ((ret = message_parse_header_next(parser, &hdr)) > 0)
index_mail_parse_header(NULL, hdr, &mail->imail);
i_assert(ret != 0);
index_mail_parse_header(NULL, NULL, &mail->imail);
message_parse_header_deinit(&parser);
mailbox_header_lookup_unref(&headers_ctx);
i_stream_destroy(&input);
}
void imapc_mail_fetch_update(struct imapc_mail *mail,
const struct imapc_untagged_reply *reply,
const struct imap_arg *args)
{
struct imapc_mailbox *mbox =
(struct imapc_mailbox *)mail->imail.mail.mail.box;
const char *key, *value;
unsigned int i;
uoff_t size;
time_t t;
int tz;
bool match = FALSE;
for (i = 0; args[i].type != IMAP_ARG_EOL; i += 2) {
if (!imap_arg_get_atom(&args[i], &key) ||
args[i+1].type == IMAP_ARG_EOL)
break;
if (strcasecmp(key, "BODY[]") == 0) {
imapc_fetch_stream(mail, reply, &args[i+1], TRUE);
match = TRUE;
} else if (strcasecmp(key, "BODY[HEADER]") == 0) {
imapc_fetch_stream(mail, reply, &args[i+1], FALSE);
match = TRUE;
} else if (strcasecmp(key, "BODY[HEADER.FIELDS") == 0) {
imapc_fetch_header_stream(mail, reply, &args[i+1]);
match = TRUE;
} else if (strcasecmp(key, "INTERNALDATE") == 0) {
if (imap_arg_get_astring(&args[i+1], &value) &&
imap_parse_datetime(value, &t, &tz))
mail->imail.data.received_date = t;
match = TRUE;
} else if (strcasecmp(key, "RFC822.SIZE") == 0) {
if (imap_arg_get_atom(&args[i+1], &value) &&
str_to_uoff(value, &size) == 0 &&
IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
mail->imail.data.physical_size = size;
match = TRUE;
} else if (strcasecmp(key, "X-GM-MSGID") == 0 ||
strcasecmp(key, "X-GUID") == 0) {
if (imap_arg_get_astring(&args[i+1], &value)) {
mail->imail.data.guid =
p_strdup(mail->imail.mail.pool, value);
}
match = TRUE;
}
}
if (!match) {
/* this is only a FETCH FLAGS update for the wanted mail */
} else {
imapc_client_stop(mbox->storage->client->client);
}
}