1
/* Copyright (c) 2011-2012 Dovecot authors, see the included COPYING file */
6
#include "istream-header-filter.h"
9
#include "imapc-client.h"
10
#include "imapc-mail.h"
11
#include "imapc-storage.h"
14
imapc_mail_prefetch_callback(const struct imapc_command_reply *reply,
17
struct imapc_mail *mail = context;
18
struct imapc_mailbox *mbox =
19
(struct imapc_mailbox *)mail->imail.mail.mail.box;
21
i_assert(mail->fetch_count > 0);
23
if (--mail->fetch_count == 0) {
24
struct imapc_mail *const *fetch_mails;
25
unsigned int i, count;
27
fetch_mails = array_get(&mbox->fetch_mails, &count);
28
for (i = 0; i < count; i++) {
29
if (fetch_mails[i] == mail) {
30
array_delete(&mbox->fetch_mails, i, 1);
35
mail->fetching_fields = 0;
38
if (reply->state == IMAPC_COMMAND_STATE_OK)
40
else if (reply->state == IMAPC_COMMAND_STATE_NO) {
41
imapc_copy_error_from_reply(mbox->storage, MAIL_ERROR_PARAMS,
43
} else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) {
44
/* The disconnection message was already logged */
45
mail_storage_set_internal_error(&mbox->storage->storage);
47
mail_storage_set_critical(&mbox->storage->storage,
48
"imapc: Mail prefetch failed: %s", reply->text_full);
50
pool_unref(&mail->imail.mail.pool);
51
imapc_client_stop(mbox->storage->client);
55
imapc_mail_send_fetch(struct mail *_mail, enum mail_fetch_field fields)
57
struct imapc_mail *mail = (struct imapc_mail *)_mail;
58
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
59
struct imapc_command *cmd;
60
struct mail_index_view *view;
64
if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
67
/* drop any fields that we may already be fetching currently */
68
fields &= ~mail->fetching_fields;
73
/* if we already know that the mail is expunged,
74
don't try to FETCH it */
75
view = mbox->delayed_sync_view != NULL ?
76
mbox->delayed_sync_view : mbox->box.view;
77
if (!mail_index_lookup_seq(view, _mail->uid, &seq) ||
78
mail_index_is_expunged(view, seq)) {
79
mail_set_expunged(_mail);
82
} else if (mbox->client_box == NULL) {
83
/* opened as save-only. we'll need to fetch the mail,
84
so actually SELECT/EXAMINE the mailbox */
85
i_assert(mbox->box.opened);
87
if (imapc_mailbox_select(mbox) < 0)
91
if ((fields & MAIL_FETCH_STREAM_BODY) != 0)
92
fields |= MAIL_FETCH_STREAM_HEADER;
95
str_printfa(str, "UID FETCH %u (", _mail->uid);
96
if ((fields & MAIL_FETCH_RECEIVED_DATE) != 0)
97
str_append(str, "INTERNALDATE ");
98
if ((fields & MAIL_FETCH_PHYSICAL_SIZE) != 0)
99
str_append(str, "RFC822.SIZE ");
100
if ((fields & MAIL_FETCH_GUID) != 0) {
101
str_append(str, mbox->guid_fetch_field_name);
102
str_append_c(str, ' ');
105
if ((fields & MAIL_FETCH_STREAM_BODY) != 0)
106
str_append(str, "BODY.PEEK[] ");
107
else if ((fields & MAIL_FETCH_STREAM_HEADER) != 0)
108
str_append(str, "BODY.PEEK[HEADER] ");
109
str_truncate(str, str_len(str)-1);
110
str_append_c(str, ')');
112
pool_ref(mail->imail.mail.pool);
113
mail->fetching_fields |= fields;
114
if (mail->fetch_count++ == 0)
115
array_append(&mbox->fetch_mails, &mail, 1);
117
cmd = imapc_client_mailbox_cmd(mbox->client_box,
118
imapc_mail_prefetch_callback, mail);
119
imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE);
120
imapc_command_send(cmd, str_c(str));
121
mail->imail.data.prefetch_sent = TRUE;
125
static void imapc_mail_cache_get(struct imapc_mail *mail,
126
struct imapc_mail_cache *cache)
128
if (mail->body_fetched)
131
if (cache->fd != -1) {
132
mail->fd = cache->fd;
133
mail->imail.data.stream =
134
i_stream_create_fd(mail->fd, 0, FALSE);
136
} else if (cache->buf != NULL) {
137
mail->body = cache->buf;
138
mail->imail.data.stream =
139
i_stream_create_from_data(mail->body->data,
145
mail->body_fetched = TRUE;
146
imapc_mail_init_stream(mail, TRUE);
149
bool imapc_mail_prefetch(struct mail *_mail)
151
struct imapc_mail *mail = (struct imapc_mail *)_mail;
152
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
153
struct index_mail_data *data = &mail->imail.data;
154
enum mail_fetch_field fields = 0;
156
if (mbox->prev_mail_cache.uid == _mail->uid)
157
imapc_mail_cache_get(mail, &mbox->prev_mail_cache);
159
if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0 &&
160
data->received_date == (time_t)-1)
161
fields |= MAIL_FETCH_RECEIVED_DATE;
162
if ((data->wanted_fields & MAIL_FETCH_PHYSICAL_SIZE) != 0 &&
163
data->physical_size == (uoff_t)-1 &&
164
IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
165
fields |= MAIL_FETCH_PHYSICAL_SIZE;
166
if ((data->wanted_fields & MAIL_FETCH_GUID) != 0 &&
167
data->guid == NULL && mbox->guid_fetch_field_name != NULL)
168
fields |= MAIL_FETCH_GUID;
170
if (data->stream == NULL && data->access_part != 0) {
171
if ((data->access_part & (READ_BODY | PARSE_BODY)) != 0)
172
fields |= MAIL_FETCH_STREAM_BODY;
174
fields |= MAIL_FETCH_STREAM_HEADER;
176
if (fields != 0) T_BEGIN {
177
(void)imapc_mail_send_fetch(_mail, fields);
179
return !mail->imail.data.prefetch_sent;
183
imapc_mail_have_fields(struct imapc_mail *imail, enum mail_fetch_field fields)
185
if ((fields & MAIL_FETCH_RECEIVED_DATE) != 0) {
186
if (imail->imail.data.received_date == (time_t)-1)
188
fields &= ~MAIL_FETCH_RECEIVED_DATE;
190
if ((fields & MAIL_FETCH_PHYSICAL_SIZE) != 0) {
191
if (imail->imail.data.physical_size == (uoff_t)-1)
193
fields &= ~MAIL_FETCH_PHYSICAL_SIZE;
195
if ((fields & MAIL_FETCH_GUID) != 0) {
196
if (imail->imail.data.guid == NULL)
198
fields &= ~MAIL_FETCH_GUID;
200
if ((fields & (MAIL_FETCH_STREAM_HEADER |
201
MAIL_FETCH_STREAM_BODY)) != 0) {
202
if (imail->imail.data.stream == NULL)
204
fields &= ~(MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY);
206
i_assert(fields == 0);
210
int imapc_mail_fetch(struct mail *_mail, enum mail_fetch_field fields)
212
struct imapc_mail *imail = (struct imapc_mail *)_mail;
213
struct imapc_mailbox *mbox =
214
(struct imapc_mailbox *)_mail->box;
217
if ((fields & MAIL_FETCH_GUID) != 0 &&
218
mbox->guid_fetch_field_name == NULL) {
219
mail_storage_set_error(_mail->box->storage,
220
MAIL_ERROR_NOTPOSSIBLE,
221
"Message GUID not available in this server");
226
ret = imapc_mail_send_fetch(_mail, fields);
231
/* we'll continue waiting until we've got all the fields we wanted,
232
or until all FETCH replies have been received (i.e. some FETCHes
234
while (!imapc_mail_have_fields(imail, fields) && imail->fetch_count > 0)
235
imapc_storage_run(mbox->storage);
239
static bool imapc_find_lfile_arg(const struct imapc_untagged_reply *reply,
240
const struct imap_arg *arg, int *fd_r)
242
const struct imap_arg *list;
243
unsigned int i, count;
245
for (i = 0; i < reply->file_args_count; i++) {
246
const struct imapc_arg_file *farg = &reply->file_args[i];
248
if (farg->parent_arg == arg->parent &&
249
imap_arg_get_list_full(arg->parent, &list, &count) &&
250
farg->list_idx < count && &list[farg->list_idx] == arg) {
258
static void imapc_stream_filter(struct istream **input)
260
static const char *imapc_hide_headers[] = {
261
/* Added by MS Exchange 2010 when \Flagged flag is set.
262
This violates IMAP guarantee of messages being immutable. */
265
struct istream *filter_input;
267
filter_input = i_stream_create_header_filter(*input,
268
HEADER_FILTER_EXCLUDE,
269
imapc_hide_headers, N_ELEMENTS(imapc_hide_headers),
270
null_header_filter_callback, NULL);
271
i_stream_unref(input);
272
*input = filter_input;
275
void imapc_mail_init_stream(struct imapc_mail *mail, bool have_body)
277
struct index_mail *imail = &mail->imail;
278
struct mail *_mail = &imail->mail.mail;
279
struct istream *input;
283
i_stream_set_name(imail->data.stream,
284
t_strdup_printf("imapc mail uid=%u", _mail->uid));
285
index_mail_set_read_buffer_size(_mail, imail->data.stream);
287
imapc_stream_filter(&imail->data.stream);
288
if (imail->mail.v.istream_opened != NULL) {
289
if (imail->mail.v.istream_opened(_mail,
290
&imail->data.stream) < 0) {
291
index_mail_close_streams(imail);
294
} else if (have_body) {
295
ret = i_stream_get_size(imail->data.stream, TRUE, &size);
297
index_mail_close_streams(imail);
301
imail->data.physical_size = size;
302
/* we'll assume that the remote server is working properly and
303
sending CRLF linefeeds */
304
imail->data.virtual_size = size;
307
imail->data.stream_has_only_header = !have_body;
308
if (index_mail_init_stream(imail, NULL, NULL, &input) < 0)
309
index_mail_close_streams(imail);
313
imapc_fetch_stream(struct imapc_mail *mail,
314
const struct imapc_untagged_reply *reply,
315
const struct imap_arg *arg, bool body)
317
struct index_mail *imail = &mail->imail;
321
if (imail->data.stream != NULL) {
324
/* maybe the existing stream has no body. replace it. */
325
index_mail_close_streams(imail);
326
if (mail->fd != -1) {
327
if (close(mail->fd) < 0)
328
i_error("close(imapc mail) failed: %m");
333
if (arg->type == IMAP_ARG_LITERAL_SIZE) {
334
if (!imapc_find_lfile_arg(reply, arg, &fd))
336
if ((fd = dup(fd)) == -1) {
337
i_error("dup() failed: %m");
341
imail->data.stream = i_stream_create_fd(fd, 0, FALSE);
343
if (!imap_arg_get_nstring(arg, &value))
346
mail_set_expunged(&imail->mail.mail);
349
if (mail->body == NULL) {
350
mail->body = buffer_create_dynamic(default_pool,
353
buffer_set_used_size(mail->body, 0);
354
buffer_append(mail->body, value, arg->str_len);
355
imail->data.stream = i_stream_create_from_data(mail->body->data,
358
mail->body_fetched = body;
360
imapc_mail_init_stream(mail, body);
363
void imapc_mail_fetch_update(struct imapc_mail *mail,
364
const struct imapc_untagged_reply *reply,
365
const struct imap_arg *args)
367
struct imapc_mailbox *mbox =
368
(struct imapc_mailbox *)mail->imail.mail.mail.box;
369
const char *key, *value;
376
for (i = 0; args[i].type != IMAP_ARG_EOL; i += 2) {
377
if (!imap_arg_get_atom(&args[i], &key) ||
378
args[i+1].type == IMAP_ARG_EOL)
381
if (strcasecmp(key, "BODY[]") == 0) {
382
imapc_fetch_stream(mail, reply, &args[i+1], TRUE);
384
} else if (strcasecmp(key, "BODY[HEADER]") == 0) {
385
imapc_fetch_stream(mail, reply, &args[i+1], FALSE);
387
} else if (strcasecmp(key, "INTERNALDATE") == 0) {
388
if (imap_arg_get_astring(&args[i+1], &value) &&
389
imap_parse_datetime(value, &t, &tz))
390
mail->imail.data.received_date = t;
392
} else if (strcasecmp(key, "RFC822.SIZE") == 0) {
393
if (imap_arg_get_atom(&args[i+1], &value) &&
394
str_to_uoff(value, &size) == 0 &&
395
IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
396
mail->imail.data.physical_size = size;
398
} else if (strcasecmp(key, "X-GM-MSGID") == 0 ||
399
strcasecmp(key, "X-GUID") == 0) {
400
if (imap_arg_get_astring(&args[i+1], &value)) {
401
mail->imail.data.guid =
402
p_strdup(mail->imail.mail.pool, value);
408
/* this is only a FETCH FLAGS update for the wanted mail */
410
imapc_client_stop(mbox->storage->client);