1
/* Copyright (c) 2011-2012 Dovecot authors, see the included COPYING file */
5
#include "hex-binary.h"
8
#include "imap-envelope.h"
9
#include "imapc-msgmap.h"
10
#include "imapc-mail.h"
11
#include "imapc-client.h"
12
#include "imapc-storage.h"
15
imapc_mail_alloc(struct mailbox_transaction_context *t,
16
enum mail_fetch_field wanted_fields,
17
struct mailbox_header_lookup_ctx *wanted_headers)
19
struct imapc_mail *mail;
22
pool = pool_alloconly_create("mail", 2048);
23
mail = p_new(pool, struct imapc_mail, 1);
24
mail->imail.mail.pool = pool;
27
index_mail_init(&mail->imail, t, wanted_fields, wanted_headers);
28
return &mail->imail.mail.mail;
31
static bool imapc_mail_is_expunged(struct mail *_mail)
33
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
34
struct imapc_msgmap *msgmap;
37
if (mbox->sync_view != NULL) {
38
/* check if another session has already expunged it */
39
if (!mail_index_lookup_seq(mbox->sync_view, _mail->uid, &lseq))
43
/* check if we've received EXPUNGE for it */
44
msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box);
45
if (!imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq))
48
/* we may be running against a server that hasn't bothered sending
49
us an EXPUNGE. see if NOOP sends it. */
50
imapc_mailbox_noop(mbox);
52
return !imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq);
55
static int imapc_mail_failed(struct mail *mail, const char *field)
57
struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->box;
59
if (mail->expunged || imapc_mail_is_expunged(mail)) {
60
mail_set_expunged(mail);
62
} else if (!imapc_client_mailbox_is_opened(mbox->client_box)) {
63
/* we've already logged a disconnection error */
64
mail_storage_set_internal_error(mail->box->storage);
67
mail_storage_set_critical(mail->box->storage,
68
"imapc: Remote server didn't send %s for UID %u in %s",
69
field, mail->uid, mail->box->vname);
74
static int imapc_mail_get_received_date(struct mail *_mail, time_t *date_r)
76
struct index_mail *mail = (struct index_mail *)_mail;
77
struct index_mail_data *data = &mail->data;
79
if (index_mail_get_received_date(_mail, date_r) == 0)
82
if (data->received_date == (time_t)-1) {
83
if (imapc_mail_fetch(_mail, MAIL_FETCH_RECEIVED_DATE) < 0)
85
if (data->received_date == (time_t)-1) {
86
if (imapc_mail_failed(_mail, "INTERNALDATE") < 0)
88
/* assume that the server never returns INTERNALDATE
89
for this mail (see BODY[] failure handling) */
90
data->received_date = 0;
93
*date_r = data->received_date;
97
static int imapc_mail_get_save_date(struct mail *_mail, time_t *date_r)
99
struct index_mail *mail = (struct index_mail *)_mail;
100
struct index_mail_data *data = &mail->data;
102
if (data->save_date == (time_t)-1) {
106
*date_r = data->save_date;
110
static int imapc_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
112
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
113
struct index_mail *mail = (struct index_mail *)_mail;
114
struct index_mail_data *data = &mail->data;
115
struct istream *input;
119
if (data->physical_size == (uoff_t)-1) {
120
(void)index_mail_get_physical_size(_mail, size_r);
121
if (data->physical_size != (uoff_t)-1) {
122
*size_r = data->physical_size;
127
if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE) &&
128
data->stream == NULL) {
129
/* trust RFC822.SIZE to be correct */
130
if (imapc_mail_fetch(_mail, MAIL_FETCH_PHYSICAL_SIZE) < 0)
132
if (data->physical_size == (uoff_t)-1) {
133
if (imapc_mail_failed(_mail, "RFC822.SIZE") < 0)
135
/* assume that the server never returns RFC822.SIZE
136
for this mail (see BODY[] failure handling) */
137
data->physical_size = 0;
139
*size_r = data->physical_size;
143
old_offset = data->stream == NULL ? 0 : data->stream->v_offset;
144
if (mail_get_stream(_mail, NULL, NULL, &input) < 0)
146
i_stream_seek(data->stream, old_offset);
148
ret = i_stream_get_size(data->stream, TRUE,
149
&data->physical_size);
152
mail_storage_set_critical(_mail->box->storage,
153
"imapc: stat(%s) failed: %m",
154
i_stream_get_name(data->stream));
157
*size_r = data->physical_size;
162
imapc_mail_get_stream(struct mail *_mail, bool get_body,
163
struct message_size *hdr_size,
164
struct message_size *body_size, struct istream **stream_r)
166
struct imapc_mail *mail = (struct imapc_mail *)_mail;
167
struct index_mail_data *data = &mail->imail.data;
168
enum mail_fetch_field fetch_field;
170
if (get_body && !mail->body_fetched &&
171
mail->imail.data.stream != NULL) {
172
/* we've fetched the header, but we need the body now too */
173
index_mail_close_streams(&mail->imail);
176
if (data->stream == NULL) {
177
if (!data->initialized) {
178
/* coming here from mail_set_seq() */
179
return mail_set_aborted(_mail);
181
fetch_field = get_body ||
182
(data->access_part & READ_BODY) != 0 ?
183
MAIL_FETCH_STREAM_BODY : MAIL_FETCH_STREAM_HEADER;
184
if (imapc_mail_fetch(_mail, fetch_field) < 0)
187
if (data->stream == NULL) {
188
if (imapc_mail_failed(_mail, "BODY[]") < 0)
190
i_assert(data->stream == NULL);
192
/* this could be either a temporary server bug, or the
193
server may permanently just not return anything for
194
this mail. the latter happens at least with Exchange
195
when trying to fetch calendar "mails", so we'll just
196
return them as empty mails instead of disconnecting
198
mail->body_fetched = TRUE;
199
data->stream = i_stream_create_from_data(NULL, 0);
200
imapc_mail_init_stream(mail, TRUE);
204
return index_mail_init_stream(&mail->imail, hdr_size, body_size,
209
imapc_mail_has_headers_in_cache(struct index_mail *mail,
210
struct mailbox_header_lookup_ctx *headers)
212
struct mail *_mail = &mail->mail.mail;
215
for (i = 0; i < headers->count; i++) {
216
if (mail_cache_field_exists(_mail->transaction->cache_view,
217
_mail->seq, headers->idx[i]) <= 0)
223
static void index_mail_update_access_parts(struct index_mail *mail)
225
struct mail *_mail = &mail->mail.mail;
226
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
227
struct index_mail_data *data = &mail->data;
228
struct mailbox_header_lookup_ctx *header_ctx;
232
if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0)
233
(void)index_mail_get_received_date(_mail, &date);
234
if ((data->wanted_fields & MAIL_FETCH_PHYSICAL_SIZE) != 0) {
235
if (index_mail_get_physical_size(_mail, &size) < 0 &&
236
!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
237
data->access_part |= READ_HDR | READ_BODY;
240
if (data->access_part == 0 && data->wanted_headers != NULL) {
241
/* see if all wanted headers exist in cache */
242
if (!imapc_mail_has_headers_in_cache(mail, data->wanted_headers))
243
data->access_part |= PARSE_HDR;
245
if (data->access_part == 0 &&
246
(data->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0) {
247
/* the common code already checked this partially,
248
but we need a guaranteed correct answer */
249
header_ctx = mailbox_header_lookup_init(_mail->box,
250
imap_envelope_headers);
251
if (!imapc_mail_has_headers_in_cache(mail, header_ctx))
252
data->access_part |= PARSE_HDR;
253
mailbox_header_lookup_unref(&header_ctx);
257
static void imapc_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving)
259
struct imapc_mail *imail = (struct imapc_mail *)_mail;
260
struct index_mail *mail = &imail->imail;
262
index_mail_set_seq(_mail, seq, saving);
264
/* searching code handles prefetching internally,
265
elsewhere we want to do it immediately */
266
if (!mail->search_mail && !_mail->saving)
267
(void)imapc_mail_prefetch(_mail);
271
imapc_mail_add_temp_wanted_fields(struct mail *_mail,
272
enum mail_fetch_field fields,
273
struct mailbox_header_lookup_ctx *headers)
275
struct index_mail *mail = (struct index_mail *)_mail;
277
index_mail_add_temp_wanted_fields(_mail, fields, headers);
278
index_mail_update_access_parts(mail);
281
static void imapc_mail_close(struct mail *_mail)
283
struct imapc_mail *mail = (struct imapc_mail *)_mail;
284
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
285
struct imapc_mail_cache *cache = &mbox->prev_mail_cache;
287
while (mail->fetch_count > 0)
288
imapc_storage_run(mbox->storage);
290
index_mail_close(_mail);
292
if (mail->body_fetched) {
293
imapc_mail_cache_free(cache);
294
cache->uid = _mail->uid;
295
if (cache->fd != -1) {
296
cache->fd = mail->fd;
299
cache->buf = mail->body;
303
if (mail->fd != -1) {
304
if (close(mail->fd) < 0)
305
i_error("close(imapc mail) failed: %m");
308
if (mail->body != NULL)
309
buffer_free(&mail->body);
312
static int imapc_mail_get_hdr_hash(struct index_mail *imail)
314
struct istream *input;
315
const unsigned char *data;
318
struct sha1_ctxt sha1_ctx;
319
unsigned char sha1_output[SHA1_RESULTLEN];
320
const char *sha1_str;
322
sha1_init(&sha1_ctx);
323
old_offset = imail->data.stream == NULL ? 0 :
324
imail->data.stream->v_offset;
325
if (mail_get_hdr_stream(&imail->mail.mail, NULL, &input) < 0)
327
while (i_stream_read_data(input, &data, &size, 0) > 0) {
328
sha1_loop(&sha1_ctx, data, size);
329
i_stream_skip(input, size);
331
i_stream_seek(imail->data.stream, old_offset);
332
sha1_result(&sha1_ctx, sha1_output);
334
sha1_str = binary_to_hex(sha1_output, sizeof(sha1_output));
335
imail->data.guid = p_strdup(imail->data_pool, sha1_str);
339
static int imapc_mail_get_guid(struct mail *_mail, const char **value_r)
341
struct index_mail *imail = (struct index_mail *)_mail;
342
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
343
const enum index_cache_field cache_idx =
344
imail->ibox->cache_fields[MAIL_CACHE_GUID].idx;
347
if (imail->data.guid != NULL) {
348
*value_r = imail->data.guid;
352
str = str_new(imail->data_pool, 64);
353
if (mail_cache_lookup_field(_mail->transaction->cache_view,
354
str, imail->mail.mail.seq, cache_idx) > 0) {
355
*value_r = str_c(str);
359
/* GUID not in cache, fetch it */
360
if (mbox->guid_fetch_field_name != NULL) {
361
if (imapc_mail_fetch(_mail, MAIL_FETCH_GUID) < 0)
363
if (imail->data.guid == NULL) {
364
(void)imapc_mail_failed(_mail, mbox->guid_fetch_field_name);
368
/* use hash of message headers as the GUID */
369
if (imapc_mail_get_hdr_hash(imail) < 0)
373
index_mail_cache_add_idx(imail, cache_idx,
374
imail->data.guid, strlen(imail->data.guid)+1);
375
*value_r = imail->data.guid;
380
imapc_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
381
const char **value_r)
383
struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
386
case MAIL_FETCH_GUID:
387
if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GUID_FORCED) &&
388
mbox->guid_fetch_field_name == NULL) {
389
/* GUIDs not supported by server */
393
return imapc_mail_get_guid(_mail, value_r);
398
return index_mail_get_special(_mail, field, value_r);
401
struct mail_vfuncs imapc_mail_vfuncs = {
406
index_mail_set_uid_cache_updates,
409
imapc_mail_add_temp_wanted_fields,
411
index_mail_get_flags,
412
index_mail_get_keywords,
413
index_mail_get_keyword_indexes,
414
index_mail_get_modseq,
415
index_mail_get_parts,
417
imapc_mail_get_received_date,
418
imapc_mail_get_save_date,
419
index_mail_get_virtual_size,
420
imapc_mail_get_physical_size,
421
index_mail_get_first_header,
422
index_mail_get_headers,
423
index_mail_get_header_stream,
424
imapc_mail_get_stream,
425
imapc_mail_get_special,
426
index_mail_get_real_mail,
427
index_mail_update_flags,
428
index_mail_update_keywords,
429
index_mail_update_modseq,
432
index_mail_set_cache_corrupted,