-
Notifications
You must be signed in to change notification settings - Fork 271
/
mbox-mail.c
451 lines (392 loc) · 12.6 KB
/
mbox-mail.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/* Copyright (c) 2003-2016 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "istream.h"
#include "hex-binary.h"
#include "index-mail.h"
#include "mbox-storage.h"
#include "mbox-file.h"
#include "mbox-lock.h"
#include "mbox-sync-private.h"
#include "istream-raw-mbox.h"
#include "istream-header-filter.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
static void mbox_prepare_resync(struct mail *mail)
{
struct mbox_transaction_context *t =
(struct mbox_transaction_context *)mail->transaction;
struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->box;
if (mbox->mbox_lock_type == F_RDLCK) {
if (mbox->mbox_lock_id == t->read_lock_id)
t->read_lock_id = 0;
mbox_unlock(mbox, mbox->mbox_lock_id);
i_assert(mbox->mbox_lock_type == F_UNLCK);
}
}
static int mbox_mail_seek(struct index_mail *mail)
{
struct mbox_transaction_context *t =
(struct mbox_transaction_context *)mail->mail.mail.transaction;
struct mail *_mail = &mail->mail.mail;
struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box;
enum mbox_sync_flags sync_flags = 0;
int ret, try;
bool deleted;
if (_mail->expunged || mbox->syncing)
return -1;
if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) {
mail_set_aborted(_mail);
return -1;
}
if (mbox->mbox_stream != NULL &&
istream_raw_mbox_is_corrupted(mbox->mbox_stream)) {
/* clear the corruption by forcing a full resync */
sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC;
}
for (try = 0; try < 2; try++) {
if ((sync_flags & MBOX_SYNC_FORCE_SYNC) != 0) {
/* dirty offsets are broken. make sure we can sync. */
mbox_prepare_resync(_mail);
}
if (mbox->mbox_lock_type == F_UNLCK) {
i_assert(t->read_lock_id == 0);
sync_flags |= MBOX_SYNC_LOCK_READING;
if (mbox_sync(mbox, sync_flags) < 0)
return -1;
t->read_lock_id = mbox_get_cur_lock_id(mbox);
i_assert(t->read_lock_id != 0);
/* refresh index file after mbox has been locked to
make sure we get only up-to-date mbox offsets. */
if (mail_index_refresh(mbox->box.index) < 0) {
mailbox_set_index_error(&mbox->box);
return -1;
}
i_assert(mbox->mbox_lock_type != F_UNLCK);
} else if (t->read_lock_id == 0) {
/* file is already locked by another transaction, but
we must keep it locked for the entire transaction,
so increase the lock counter. */
if (mbox_lock(mbox, mbox->mbox_lock_type,
&t->read_lock_id) < 0)
i_unreached();
}
if (mbox_file_open_stream(mbox) < 0)
return -1;
ret = mbox_file_seek(mbox, _mail->transaction->view,
_mail->seq, &deleted);
if (ret > 0) {
/* success */
break;
}
if (ret < 0) {
if (deleted)
mail_set_expunged(_mail);
return -1;
}
/* we'll need to re-sync it completely */
sync_flags |= MBOX_SYNC_UNDIRTY | MBOX_SYNC_FORCE_SYNC;
}
if (ret == 0) {
mail_storage_set_critical(&mbox->storage->storage,
"Losing sync for mail uid=%u in mbox file %s",
_mail->uid, mailbox_get_path(&mbox->box));
}
return 0;
}
static int mbox_mail_get_received_date(struct mail *_mail, time_t *date_r)
{
struct index_mail *mail = (struct index_mail *)_mail;
struct index_mail_data *data = &mail->data;
struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box;
if (index_mail_get_received_date(_mail, date_r) == 0)
return 0;
if (mbox_mail_seek(mail) < 0)
return -1;
data->received_date =
istream_raw_mbox_get_received_time(mbox->mbox_stream);
if (data->received_date == (time_t)-1) {
/* it's broken and conflicts with our "not found"
return value. change it. */
data->received_date = 0;
}
*date_r = data->received_date;
return 0;
}
static int mbox_mail_get_save_date(struct mail *_mail, time_t *date_r)
{
struct index_mail *mail = (struct index_mail *)_mail;
struct index_mail_data *data = &mail->data;
if (index_mail_get_save_date(_mail, date_r) == 0)
return 0;
/* no way to know this. save the current time into cache and use
that from now on. this works only as long as the index files
are permanent */
data->save_date = ioloop_time;
*date_r = data->save_date;
return 0;
}
static int
mbox_mail_get_md5_header(struct index_mail *mail, const char **value_r)
{
struct mail *_mail = &mail->mail.mail;
static uint8_t empty_md5[16] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box;
const void *ext_data;
if (mail->data.guid != NULL) {
*value_r = mail->data.guid;
return 1;
}
mail_index_lookup_ext(_mail->transaction->view, _mail->seq,
mbox->md5hdr_ext_idx, &ext_data, NULL);
if (ext_data != NULL && memcmp(ext_data, empty_md5, 16) != 0) {
mail->data.guid = p_strdup(mail->mail.data_pool,
binary_to_hex(ext_data, 16));
*value_r = mail->data.guid;
return 1;
} else if (mail_index_is_expunged(_mail->transaction->view, _mail->seq)) {
mail_set_expunged(_mail);
return -1;
} else {
return 0;
}
}
static int
mbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
const char **value_r)
{
struct index_mail *mail = (struct index_mail *)_mail;
struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box;
uoff_t offset;
bool move_offset;
int ret;
switch (field) {
case MAIL_FETCH_FROM_ENVELOPE:
if (mbox_mail_seek(mail) < 0)
return -1;
*value_r = istream_raw_mbox_get_sender(mbox->mbox_stream);
return 0;
case MAIL_FETCH_GUID:
case MAIL_FETCH_HEADER_MD5:
if ((ret = mbox_mail_get_md5_header(mail, value_r)) != 0)
return ret < 0 ? -1 : 0;
/* i guess in theory the empty_md5 is valid and can happen,
but it's almost guaranteed that it means the MD5 sum is
missing. recalculate it. */
if (mbox->mbox_lock_type == F_UNLCK ||
mbox->mbox_stream == NULL) {
offset = 0;
move_offset = FALSE;
} else {
offset = istream_raw_mbox_get_start_offset(mbox->mbox_stream);
move_offset = TRUE;
}
mbox->mbox_save_md5 = TRUE;
if (mbox_sync(mbox, MBOX_SYNC_FORCE_SYNC |
MBOX_SYNC_READONLY) < 0)
return -1;
if (move_offset) {
if (istream_raw_mbox_seek(mbox->mbox_stream,
offset) < 0) {
i_error("mbox %s sync lost during MD5 syncing",
_mail->box->name);
return -1;
}
}
if ((ret = mbox_mail_get_md5_header(mail, value_r)) == 0) {
i_error("mbox %s resyncing didn't save header MD5 values",
_mail->box->name);
return -1;
}
return ret < 0 ? -1 : 0;
default:
break;
}
return index_mail_get_special(_mail, field, value_r);
}
static int
mbox_mail_get_next_offset(struct index_mail *mail, uoff_t *next_offset_r)
{
struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->mail.mail.box;
struct mail_index_view *view;
const struct mail_index_header *hdr;
uint32_t seq;
int trailer_size;
int ret = 1;
*next_offset_r = (uoff_t)-1;
hdr = mail_index_get_header(mail->mail.mail.transaction->view);
if (mail->mail.mail.seq > hdr->messages_count) {
/* we're appending a new message */
return 0;
}
/* We can't really trust trans_view. The next message may already be
expunged from it. Also hdr.messages_count may be incorrect there.
So refresh the index to get the latest changes and get the next
message's offset using a new view. */
i_assert(mbox->mbox_lock_type != F_UNLCK);
if (mbox_sync_header_refresh(mbox) < 0)
return -1;
view = mail_index_view_open(mail->mail.mail.box->index);
hdr = mail_index_get_header(view);
if (!mail_index_lookup_seq(view, mail->mail.mail.uid, &seq))
i_panic("Message unexpectedly expunged from index");
if (seq < hdr->messages_count) {
if (mbox_file_lookup_offset(mbox, view, seq + 1,
next_offset_r) <= 0)
ret = -1;
} else if (mail->mail.mail.box->input != NULL) {
/* opened the mailbox as input stream. we can't trust the
sync_size, since it's wrong with compressed mailboxes */
ret = 0;
} else {
/* last message, use the synced mbox size */
trailer_size =
mbox->storage->storage.set->mail_save_crlf ? 2 : 1;
*next_offset_r = mbox->mbox_hdr.sync_size - trailer_size;
}
mail_index_view_close(&view);
return ret;
}
static int mbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
{
struct index_mail *mail = (struct index_mail *)_mail;
struct index_mail_data *data = &mail->data;
struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box;
struct istream *input;
struct message_size hdr_size;
uoff_t old_offset, body_offset, body_size, next_offset;
if (index_mail_get_physical_size(_mail, size_r) == 0)
return 0;
/* we want to return the header size as seen by mail_get_stream(). */
old_offset = data->stream == NULL ? 0 : data->stream->v_offset;
if (mail_get_stream(_mail, &hdr_size, NULL, &input) < 0)
return -1;
/* our header size varies, so don't do any caching */
if (istream_raw_mbox_get_body_offset(mbox->mbox_stream, &body_offset) < 0) {
mail_storage_set_critical(_mail->box->storage,
"mbox %s: Couldn't get body offset for uid=%u",
mailbox_get_path(&mbox->box), mail->mail.mail.uid);
return -1;
}
/* use the next message's offset to avoid reading through the entire
message body to find out its size */
if (mbox_mail_get_next_offset(mail, &next_offset) > 0)
body_size = next_offset - body_offset;
else
body_size = (uoff_t)-1;
/* verify that the calculated body size is correct */
if (istream_raw_mbox_get_body_size(mbox->mbox_stream,
body_size, &body_size) < 0) {
mail_storage_set_critical(_mail->box->storage,
"mbox %s: Couldn't get body size for uid=%u",
mailbox_get_path(&mbox->box), mail->mail.mail.uid);
return -1;
}
data->physical_size = hdr_size.physical_size + body_size;
*size_r = data->physical_size;
i_stream_seek(input, old_offset);
return 0;
}
static int mbox_mail_init_stream(struct index_mail *mail)
{
struct mbox_mailbox *mbox = (struct mbox_mailbox *)mail->mail.mail.box;
struct istream *raw_stream;
uoff_t hdr_offset, next_offset;
int ret;
if (mbox_mail_seek(mail) < 0)
return -1;
ret = mbox_mail_get_next_offset(mail, &next_offset);
if (ret < 0) {
if (mbox_mail_seek(mail) < 0)
return -1;
ret = mbox_mail_get_next_offset(mail, &next_offset);
if (ret < 0) {
i_warning("mbox %s: Can't find next message offset "
"for uid=%u", mailbox_get_path(&mbox->box),
mail->mail.mail.uid);
}
}
raw_stream = mbox->mbox_stream;
if (istream_raw_mbox_get_header_offset(raw_stream, &hdr_offset) < 0) {
mail_storage_set_critical(mbox->box.storage,
"mbox %s: Couldn't get header offset for uid=%u",
mailbox_get_path(&mbox->box), mail->mail.mail.uid);
return -1;
}
i_stream_seek(raw_stream, hdr_offset);
if (next_offset != (uoff_t)-1)
istream_raw_mbox_set_next_offset(raw_stream, next_offset);
raw_stream = i_stream_create_limit(raw_stream, (uoff_t)-1);
mail->data.stream =
i_stream_create_header_filter(raw_stream,
HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR,
mbox_hide_headers, mbox_hide_headers_count,
*null_header_filter_callback, (void *)NULL);
i_stream_unref(&raw_stream);
return 0;
}
static int mbox_mail_get_stream(struct mail *_mail, bool get_body ATTR_UNUSED,
struct message_size *hdr_size,
struct message_size *body_size,
struct istream **stream_r)
{
struct index_mail *mail = (struct index_mail *)_mail;
if (mail->data.stream == NULL) {
if (mbox_mail_init_stream(mail) < 0)
return -1;
}
return index_mail_init_stream(mail, hdr_size, body_size, stream_r);
}
static void mbox_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving)
{
struct index_mail *mail = (struct index_mail *)_mail;
index_mail_set_seq(_mail, seq, saving);
mail->data.dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE;
}
static bool mbox_mail_set_uid(struct mail *_mail, uint32_t uid)
{
struct index_mail *mail = (struct index_mail *)_mail;
bool ret;
ret = index_mail_set_uid(_mail, uid);
mail->data.dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE;
return ret;
}
struct mail_vfuncs mbox_mail_vfuncs = {
index_mail_close,
index_mail_free,
mbox_mail_set_seq,
mbox_mail_set_uid,
index_mail_set_uid_cache_updates,
index_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,
mbox_mail_get_received_date,
mbox_mail_get_save_date,
index_mail_get_virtual_size,
mbox_mail_get_physical_size,
index_mail_get_first_header,
index_mail_get_headers,
index_mail_get_header_stream,
mbox_mail_get_stream,
index_mail_get_binary_stream,
mbox_mail_get_special,
index_mail_get_real_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,
index_mail_set_cache_corrupted_reason
};