/src/dovecot/src/lib-storage/index/imapc/imapc-mail.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str.h" |
5 | | #include "hex-binary.h" |
6 | | #include "sha1.h" |
7 | | #include "istream.h" |
8 | | #include "message-part-data.h" |
9 | | #include "imap-envelope.h" |
10 | | #include "imapc-msgmap.h" |
11 | | #include "imapc-mail.h" |
12 | | #include "imapc-storage.h" |
13 | | |
14 | | static bool imapc_mail_get_cached_guid(struct mail *_mail); |
15 | | |
16 | | struct mail * |
17 | | imapc_mail_alloc(struct mailbox_transaction_context *t, |
18 | | enum mail_fetch_field wanted_fields, |
19 | | struct mailbox_header_lookup_ctx *wanted_headers) |
20 | 0 | { |
21 | 0 | struct imapc_mail *mail; |
22 | 0 | pool_t pool; |
23 | |
|
24 | 0 | pool = pool_alloconly_create("mail", 2048); |
25 | 0 | mail = p_new(pool, struct imapc_mail, 1); |
26 | 0 | mail->fd = -1; |
27 | |
|
28 | 0 | index_mail_init(&mail->imail, t, wanted_fields, wanted_headers, pool, NULL); |
29 | 0 | return &mail->imail.mail.mail; |
30 | 0 | } |
31 | | |
32 | | static bool imapc_mail_is_expunged(struct mail *_mail) |
33 | 0 | { |
34 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
35 | 0 | struct imapc_msgmap *msgmap; |
36 | 0 | uint32_t lseq, rseq; |
37 | |
|
38 | 0 | if (!mbox->initial_sync_done) { |
39 | | /* unknown at this point */ |
40 | 0 | return FALSE; |
41 | 0 | } |
42 | | |
43 | 0 | if (mbox->sync_view != NULL) { |
44 | | /* check if another session has already expunged it */ |
45 | 0 | if (!mail_index_lookup_seq(mbox->sync_view, _mail->uid, &lseq)) |
46 | 0 | return TRUE; |
47 | 0 | } |
48 | | |
49 | | /* check if we've received EXPUNGE for it */ |
50 | 0 | msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); |
51 | 0 | if (!imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq)) |
52 | 0 | return TRUE; |
53 | | |
54 | | /* we may be running against a server that hasn't bothered sending |
55 | | us an EXPUNGE. see if NOOP sends it. */ |
56 | 0 | imapc_mailbox_noop(mbox); |
57 | 0 | if (!mbox->initial_sync_done) { |
58 | | /* NOOP caused a reconnection and desync */ |
59 | 0 | return FALSE; |
60 | 0 | } |
61 | | |
62 | 0 | return !imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq); |
63 | 0 | } |
64 | | |
65 | | static int imapc_mail_failed(struct mail *mail, const char *field) |
66 | 0 | { |
67 | 0 | struct imapc_mail *imail = IMAPC_MAIL(mail); |
68 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(mail->box); |
69 | 0 | bool fix_broken_mail = FALSE; |
70 | |
|
71 | 0 | if (mail->expunged || imapc_mail_is_expunged(mail)) { |
72 | 0 | mail_set_expunged(mail); |
73 | 0 | } else if (!imapc_client_mailbox_is_opened(mbox->client_box)) { |
74 | | /* we've already logged a disconnection error */ |
75 | 0 | mail_storage_set_internal_error(mail->box->storage); |
76 | 0 | } else { |
77 | | /* By default we'll assume that this is a critical failure, |
78 | | because we don't want to lose any data. We can be here |
79 | | either because it's a temporary failure on the server or |
80 | | it's a permanent failure. Unfortunately we can't know |
81 | | which case it is, so permanent failures need to be worked |
82 | | around by setting imapc_features=fetch-fix-broken-mails. |
83 | | |
84 | | One reason for permanent failures was that earlier Exchange |
85 | | versions failed to return any data for messages in Calendars |
86 | | mailbox. This seems to be fixed in newer versions. |
87 | | */ |
88 | 0 | fix_broken_mail = imail->fetch_ignore_if_missing; |
89 | 0 | mail_set_critical(mail, |
90 | 0 | "imapc: Remote server didn't send %s%s (FETCH replied: %s)", |
91 | 0 | field, fix_broken_mail ? " - treating it as empty" : "", |
92 | 0 | imail->last_fetch_reply); |
93 | 0 | } |
94 | 0 | return fix_broken_mail ? 0 : -1; |
95 | 0 | } |
96 | | |
97 | | static uint64_t imapc_mail_get_modseq(struct mail *_mail) |
98 | 0 | { |
99 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
100 | 0 | struct imapc_msgmap *msgmap; |
101 | 0 | const uint64_t *modseqs; |
102 | 0 | unsigned int count; |
103 | 0 | uint32_t rseq; |
104 | |
|
105 | 0 | if (!imapc_mailbox_has_modseqs(mbox)) |
106 | 0 | return index_mail_get_modseq(_mail); |
107 | | |
108 | 0 | msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box); |
109 | 0 | if (imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq)) { |
110 | 0 | modseqs = array_get(&mbox->rseq_modseqs, &count); |
111 | 0 | if (rseq <= count) |
112 | 0 | return modseqs[rseq-1]; |
113 | 0 | } |
114 | 0 | return 1; /* unknown modseq */ |
115 | 0 | } |
116 | | |
117 | | static int imapc_mail_get_received_date(struct mail *_mail, time_t *date_r) |
118 | 0 | { |
119 | 0 | struct index_mail *mail = INDEX_MAIL(_mail); |
120 | 0 | struct index_mail_data *data = &mail->data; |
121 | |
|
122 | 0 | if (index_mail_get_received_date(_mail, date_r) == 0) |
123 | 0 | return 0; |
124 | | |
125 | 0 | if (data->received_date == (time_t)-1) { |
126 | 0 | if (imapc_mail_fetch(_mail, MAIL_FETCH_RECEIVED_DATE, NULL) < 0) |
127 | 0 | return -1; |
128 | 0 | if (data->received_date == (time_t)-1) { |
129 | 0 | if (imapc_mail_failed(_mail, "INTERNALDATE") < 0) |
130 | 0 | return -1; |
131 | | /* assume that the server never returns INTERNALDATE |
132 | | for this mail (see BODY[] failure handling) */ |
133 | 0 | data->received_date = 0; |
134 | 0 | } |
135 | 0 | } |
136 | 0 | *date_r = data->received_date; |
137 | 0 | return 0; |
138 | 0 | } |
139 | | |
140 | | static int imapc_mail_get_save_date(struct mail *_mail, time_t *date_r) |
141 | 0 | { |
142 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
143 | 0 | struct index_mail *mail = INDEX_MAIL(_mail); |
144 | 0 | struct index_mail_data *data = &mail->data; |
145 | |
|
146 | 0 | if (data->save_date != 0 && index_mail_get_save_date(_mail, date_r) > 0) |
147 | 0 | return 1; |
148 | | |
149 | 0 | if (HAS_NO_BITS(mbox->capabilities, IMAPC_CAPABILITY_SAVEDATE)) { |
150 | 0 | data->save_date = 0; |
151 | 0 | } else if (data->save_date == (time_t)-1) { |
152 | 0 | if (imapc_mail_fetch(_mail, MAIL_FETCH_SAVE_DATE, NULL) < 0) |
153 | 0 | return -1; |
154 | 0 | if (data->save_date == (time_t)-1 && |
155 | 0 | imapc_mail_failed(_mail, "SAVEDATE") < 0) |
156 | 0 | return -1; |
157 | 0 | } |
158 | 0 | if (data->save_date == (time_t)-1 || data->save_date == 0) { |
159 | 0 | if (imapc_mail_get_received_date(_mail, date_r) < 0) |
160 | 0 | return -1; |
161 | 0 | return 0; |
162 | 0 | } |
163 | 0 | *date_r = data->save_date; |
164 | 0 | return 1; |
165 | 0 | } |
166 | | |
167 | | static int imapc_mail_get_physical_size(struct mail *_mail, uoff_t *size_r) |
168 | 0 | { |
169 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
170 | 0 | struct index_mail *mail = INDEX_MAIL(_mail); |
171 | 0 | struct index_mail_data *data = &mail->data; |
172 | 0 | struct istream *input; |
173 | 0 | uoff_t old_offset; |
174 | 0 | int ret; |
175 | |
|
176 | 0 | if (data->physical_size == UOFF_T_MAX) |
177 | 0 | (void)index_mail_get_physical_size(_mail, size_r); |
178 | 0 | if (data->physical_size != UOFF_T_MAX) { |
179 | 0 | *size_r = data->physical_size; |
180 | 0 | return 0; |
181 | 0 | } |
182 | | |
183 | 0 | if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_FETCH_SIZE) && |
184 | 0 | data->stream == NULL) { |
185 | | /* Trust RFC822.SIZE to be correct enough to present to the |
186 | | IMAP client. However, it can be wrong in some implementation |
187 | | so try not to trust it too much. */ |
188 | 0 | if (imapc_mail_fetch(_mail, MAIL_FETCH_PHYSICAL_SIZE, NULL) < 0) |
189 | 0 | return -1; |
190 | 0 | if (data->physical_size == UOFF_T_MAX) { |
191 | 0 | if (imapc_mail_failed(_mail, "RFC822.SIZE") < 0) |
192 | 0 | return -1; |
193 | | /* assume that the server never returns RFC822.SIZE |
194 | | for this mail (see BODY[] failure handling) */ |
195 | 0 | data->physical_size = 0; |
196 | 0 | } |
197 | 0 | *size_r = data->physical_size; |
198 | 0 | return 0; |
199 | 0 | } |
200 | | |
201 | 0 | old_offset = data->stream == NULL ? 0 : data->stream->v_offset; |
202 | 0 | if (mail_get_stream(_mail, NULL, NULL, &input) < 0) |
203 | 0 | return -1; |
204 | 0 | i_assert(data->stream != NULL); |
205 | 0 | i_stream_seek(data->stream, old_offset); |
206 | |
|
207 | 0 | ret = i_stream_get_size(data->stream, TRUE, |
208 | 0 | &data->physical_size); |
209 | 0 | if (ret <= 0) { |
210 | 0 | i_assert(ret != 0); |
211 | 0 | mail_set_critical(_mail, "imapc: stat(%s) failed: %m", |
212 | 0 | i_stream_get_name(data->stream)); |
213 | 0 | return -1; |
214 | 0 | } |
215 | 0 | *size_r = data->physical_size; |
216 | 0 | return 0; |
217 | 0 | } |
218 | | |
219 | | static int imapc_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r) |
220 | 0 | { |
221 | 0 | struct index_mail *mail = INDEX_MAIL(_mail); |
222 | 0 | struct index_mail_data *data = &mail->data; |
223 | |
|
224 | 0 | if (imapc_mail_get_physical_size(_mail, size_r) < 0) |
225 | 0 | return -1; |
226 | 0 | data->virtual_size = data->physical_size; |
227 | 0 | return 0; |
228 | 0 | } |
229 | | |
230 | | static int |
231 | | imapc_mail_get_header_stream(struct mail *_mail, |
232 | | struct mailbox_header_lookup_ctx *headers, |
233 | | struct istream **stream_r) |
234 | 0 | { |
235 | 0 | struct imapc_mail *mail = IMAPC_MAIL(_mail); |
236 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
237 | 0 | enum mail_lookup_abort old_abort = _mail->lookup_abort; |
238 | 0 | int ret; |
239 | |
|
240 | 0 | if (mail->imail.data.access_part != 0 || |
241 | 0 | IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_FETCH_HEADERS)) { |
242 | | /* we're going to be reading the header/body anyway */ |
243 | 0 | return index_mail_get_header_stream(_mail, headers, stream_r); |
244 | 0 | } |
245 | | |
246 | | /* see if the wanted headers are already in cache */ |
247 | 0 | _mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL; |
248 | 0 | ret = index_mail_get_header_stream(_mail, headers, stream_r); |
249 | 0 | _mail->lookup_abort = old_abort; |
250 | 0 | if (ret == 0) |
251 | 0 | return 0; |
252 | | |
253 | | /* fetch only the wanted headers */ |
254 | 0 | if (imapc_mail_fetch(_mail, 0, headers->name) < 0) |
255 | 0 | return -1; |
256 | | /* the headers should cached now. */ |
257 | 0 | return index_mail_get_header_stream(_mail, headers, stream_r); |
258 | 0 | } |
259 | | |
260 | | static int |
261 | | imapc_mail_get_headers(struct mail *_mail, const char *field, |
262 | | bool decode_to_utf8, const char *const **value_r) |
263 | 0 | { |
264 | 0 | struct mailbox_header_lookup_ctx *headers; |
265 | 0 | const char *header_names[2]; |
266 | 0 | const unsigned char *data; |
267 | 0 | size_t size; |
268 | 0 | struct istream *input; |
269 | 0 | int ret; |
270 | |
|
271 | 0 | header_names[0] = field; |
272 | 0 | header_names[1] = NULL; |
273 | 0 | headers = mailbox_header_lookup_init(_mail->box, header_names); |
274 | 0 | ret = mail_get_header_stream(_mail, headers, &input); |
275 | 0 | mailbox_header_lookup_unref(&headers); |
276 | 0 | if (ret < 0) |
277 | 0 | return -1; |
278 | | |
279 | 0 | while (i_stream_read_more(input, &data, &size) > 0) |
280 | 0 | i_stream_skip(input, size); |
281 | | /* the header should cached now. */ |
282 | 0 | return index_mail_get_headers(_mail, field, decode_to_utf8, value_r); |
283 | 0 | } |
284 | | |
285 | | static int |
286 | | imapc_mail_get_first_header(struct mail *_mail, const char *field, |
287 | | bool decode_to_utf8, const char **value_r) |
288 | 0 | { |
289 | 0 | const char *const *values; |
290 | 0 | int ret; |
291 | |
|
292 | 0 | ret = imapc_mail_get_headers(_mail, field, decode_to_utf8, &values); |
293 | 0 | if (ret <= 0) |
294 | 0 | return ret; |
295 | 0 | *value_r = values[0]; |
296 | 0 | return 1; |
297 | 0 | } |
298 | | |
299 | | static int |
300 | | imapc_mail_get_stream(struct mail *_mail, bool get_body, |
301 | | struct message_size *hdr_size, |
302 | | struct message_size *body_size, struct istream **stream_r) |
303 | 0 | { |
304 | 0 | struct imapc_mail *mail = IMAPC_MAIL(_mail); |
305 | 0 | struct index_mail_data *data = &mail->imail.data; |
306 | 0 | enum mail_fetch_field fetch_field; |
307 | |
|
308 | 0 | if (get_body && !mail->body_fetched && |
309 | 0 | mail->imail.data.stream != NULL) { |
310 | | /* we've fetched the header, but we need the body now too */ |
311 | 0 | index_mail_close_streams(&mail->imail); |
312 | | /* don't re-use any cached header sizes. we may be |
313 | | intentionally downloading the full body because the header |
314 | | wasn't returned correctly (e.g. pop3-migration does this) */ |
315 | 0 | data->hdr_size_set = FALSE; |
316 | 0 | } |
317 | | |
318 | | /* See if we can get it from cache. If the wanted_fields/headers are |
319 | | set properly, this is usually already done by prefetching. */ |
320 | 0 | imapc_mail_try_init_stream_from_cache(mail); |
321 | |
|
322 | 0 | if (data->stream == NULL) { |
323 | 0 | if (!data->initialized) { |
324 | | /* coming here from mail_set_seq() */ |
325 | 0 | mail_set_aborted(_mail); |
326 | 0 | return -1; |
327 | 0 | } |
328 | 0 | if (_mail->expunged) { |
329 | | /* We already detected that the mail is expunged. |
330 | | Don't spend time trying to FETCH it again. */ |
331 | 0 | mail_set_expunged(_mail); |
332 | 0 | return -1; |
333 | 0 | } |
334 | 0 | fetch_field = get_body || |
335 | 0 | (data->access_part & READ_BODY) != 0 ? |
336 | 0 | MAIL_FETCH_STREAM_BODY : MAIL_FETCH_STREAM_HEADER; |
337 | 0 | if (imapc_mail_fetch(_mail, fetch_field, NULL) < 0) |
338 | 0 | return -1; |
339 | | |
340 | 0 | if (data->stream == NULL) { |
341 | 0 | if (imapc_mail_failed(_mail, "BODY[]") < 0) |
342 | 0 | return -1; |
343 | 0 | i_assert(data->stream == NULL); |
344 | | |
345 | | /* return the broken email as empty */ |
346 | 0 | mail->body_fetched = TRUE; |
347 | 0 | data->stream = i_stream_create_from_data(NULL, 0); |
348 | 0 | imapc_mail_init_stream(mail); |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | 0 | return index_mail_init_stream(&mail->imail, hdr_size, body_size, |
353 | 0 | stream_r); |
354 | 0 | } |
355 | | |
356 | | bool imapc_mail_has_headers_in_cache(struct index_mail *mail, |
357 | | struct mailbox_header_lookup_ctx *headers) |
358 | 0 | { |
359 | 0 | struct mail *_mail = &mail->mail.mail; |
360 | 0 | unsigned int i; |
361 | |
|
362 | 0 | for (i = 0; i < headers->count; i++) { |
363 | 0 | if (mail_cache_field_exists(_mail->transaction->cache_view, |
364 | 0 | _mail->seq, headers->idx[i]) <= 0) |
365 | 0 | return FALSE; |
366 | 0 | } |
367 | 0 | return TRUE; |
368 | 0 | } |
369 | | |
370 | | void imapc_mail_update_access_parts(struct index_mail *mail) |
371 | 0 | { |
372 | 0 | struct mail *_mail = &mail->mail.mail; |
373 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
374 | 0 | struct index_mail_data *data = &mail->data; |
375 | 0 | struct mailbox_header_lookup_ctx *header_ctx; |
376 | 0 | const char *str; |
377 | 0 | time_t date; |
378 | 0 | uoff_t size; |
379 | |
|
380 | 0 | if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0) |
381 | 0 | (void)index_mail_get_received_date(_mail, &date); |
382 | 0 | if ((data->wanted_fields & MAIL_FETCH_SAVE_DATE) != 0) { |
383 | 0 | if (index_mail_get_save_date(_mail, &date) < 0 && |
384 | 0 | HAS_NO_BITS(mbox->capabilities, |
385 | 0 | IMAPC_CAPABILITY_SAVEDATE)) { |
386 | 0 | (void)index_mail_get_received_date(_mail, &date); |
387 | 0 | data->save_date = data->received_date; |
388 | 0 | } |
389 | 0 | } |
390 | 0 | if ((data->wanted_fields & (MAIL_FETCH_PHYSICAL_SIZE | |
391 | 0 | MAIL_FETCH_VIRTUAL_SIZE)) != 0) { |
392 | 0 | if (index_mail_get_physical_size(_mail, &size) < 0 && |
393 | 0 | IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_FETCH_SIZE)) |
394 | 0 | data->access_part |= READ_HDR | READ_BODY; |
395 | 0 | } |
396 | 0 | if ((data->wanted_fields & MAIL_FETCH_GUID) != 0) |
397 | 0 | (void)imapc_mail_get_cached_guid(_mail); |
398 | 0 | if ((data->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0) |
399 | 0 | (void)index_mail_get_cached_body(mail, &str); |
400 | 0 | if ((data->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0) |
401 | 0 | (void)index_mail_get_cached_bodystructure(mail, &str); |
402 | |
|
403 | 0 | if (data->access_part == 0 && data->wanted_headers != NULL && |
404 | 0 | IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_FETCH_HEADERS)) { |
405 | | /* see if all wanted headers exist in cache */ |
406 | 0 | if (!imapc_mail_has_headers_in_cache(mail, data->wanted_headers)) |
407 | 0 | data->access_part |= PARSE_HDR; |
408 | 0 | } |
409 | 0 | if (data->access_part == 0 && |
410 | 0 | (data->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0 && |
411 | 0 | IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_FETCH_HEADERS)) { |
412 | | /* the common code already checked this partially, |
413 | | but we need a guaranteed correct answer */ |
414 | 0 | header_ctx = mailbox_header_lookup_init(_mail->box, |
415 | 0 | message_part_envelope_headers); |
416 | 0 | if (!imapc_mail_has_headers_in_cache(mail, header_ctx)) |
417 | 0 | data->access_part |= PARSE_HDR; |
418 | 0 | mailbox_header_lookup_unref(&header_ctx); |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | | static void imapc_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving) |
423 | 0 | { |
424 | 0 | struct imapc_mail *imail = IMAPC_MAIL(_mail); |
425 | 0 | struct index_mail *mail = &imail->imail; |
426 | 0 | struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; |
427 | |
|
428 | 0 | index_mail_set_seq(_mail, seq, saving); |
429 | 0 | if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_FETCH_SIZE)) { |
430 | | /* RFC822.SIZE may be read from vsize record or cache. It may |
431 | | not be exactly correct. */ |
432 | 0 | mail->data.inexact_total_sizes = TRUE; |
433 | 0 | } |
434 | | |
435 | | /* searching code handles prefetching internally, |
436 | | elsewhere we want to do it immediately */ |
437 | 0 | if (!mail->mail.search_mail && !_mail->saving) |
438 | 0 | (void)imapc_mail_prefetch(_mail); |
439 | 0 | } |
440 | | |
441 | | static void |
442 | | imapc_mail_add_temp_wanted_fields(struct mail *_mail, |
443 | | enum mail_fetch_field fields, |
444 | | struct mailbox_header_lookup_ctx *headers) |
445 | 0 | { |
446 | 0 | struct index_mail *mail = INDEX_MAIL(_mail); |
447 | |
|
448 | 0 | index_mail_add_temp_wanted_fields(_mail, fields, headers); |
449 | 0 | if (_mail->seq != 0) |
450 | 0 | imapc_mail_update_access_parts(mail); |
451 | 0 | } |
452 | | |
453 | | static void imapc_mail_close(struct mail *_mail) |
454 | 0 | { |
455 | 0 | struct imapc_mail *mail = IMAPC_MAIL(_mail); |
456 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
457 | 0 | struct imapc_mail_cache *cache = &mbox->prev_mail_cache; |
458 | |
|
459 | 0 | if (mail->fetch_count > 0) { |
460 | 0 | imapc_mail_fetch_flush(mbox); |
461 | 0 | while (mail->fetch_count > 0) |
462 | 0 | imapc_mailbox_run_nofetch(mbox); |
463 | 0 | } |
464 | |
|
465 | 0 | index_mail_close(_mail); |
466 | |
|
467 | 0 | mail->fetching_headers = NULL; |
468 | 0 | if (mail->body_fetched) { |
469 | 0 | imapc_mail_cache_free(cache); |
470 | 0 | cache->uid = _mail->uid; |
471 | 0 | if (mail->fd != -1) { |
472 | 0 | cache->fd = mail->fd; |
473 | 0 | mail->fd = -1; |
474 | 0 | } else { |
475 | 0 | cache->buf = mail->body; |
476 | 0 | mail->body = NULL; |
477 | 0 | } |
478 | 0 | } |
479 | 0 | i_close_fd(&mail->fd); |
480 | 0 | buffer_free(&mail->body); |
481 | 0 | mail->header_fetched = FALSE; |
482 | 0 | mail->body_fetched = FALSE; |
483 | |
|
484 | 0 | i_assert(mail->fetch_count == 0); |
485 | 0 | } |
486 | | |
487 | | static int imapc_mail_get_hdr_hash(struct index_mail *imail) |
488 | 0 | { |
489 | 0 | struct istream *input; |
490 | 0 | const unsigned char *data; |
491 | 0 | size_t size; |
492 | 0 | uoff_t old_offset; |
493 | 0 | struct message_size hdr_size; |
494 | 0 | struct sha1_ctxt sha1_ctx; |
495 | 0 | unsigned char sha1_output[SHA1_RESULTLEN]; |
496 | 0 | const char *sha1_str; |
497 | |
|
498 | 0 | sha1_init(&sha1_ctx); |
499 | 0 | old_offset = imail->data.stream == NULL ? 0 : |
500 | 0 | imail->data.stream->v_offset; |
501 | 0 | if (mail_get_hdr_stream(&imail->mail.mail, &hdr_size, &input) < 0) |
502 | 0 | return -1; |
503 | 0 | i_assert(imail->data.stream != NULL); |
504 | | |
505 | 0 | input = i_stream_create_limit(input, hdr_size.physical_size); |
506 | 0 | while (i_stream_read_more(input, &data, &size) > 0) { |
507 | 0 | sha1_loop(&sha1_ctx, data, size); |
508 | 0 | i_stream_skip(input, size); |
509 | 0 | } |
510 | 0 | i_stream_unref(&input); |
511 | 0 | i_stream_seek(imail->data.stream, old_offset); |
512 | 0 | sha1_result(&sha1_ctx, sha1_output); |
513 | |
|
514 | 0 | sha1_str = binary_to_hex(sha1_output, sizeof(sha1_output)); |
515 | 0 | imail->data.guid = p_strdup(imail->mail.data_pool, sha1_str); |
516 | 0 | return 0; |
517 | 0 | } |
518 | | |
519 | | static bool imapc_mail_get_cached_guid(struct mail *_mail) |
520 | 0 | { |
521 | 0 | struct index_mail *imail = INDEX_MAIL(_mail); |
522 | 0 | const enum index_cache_field cache_idx = |
523 | 0 | imail->ibox->cache_fields[MAIL_CACHE_GUID].idx; |
524 | 0 | string_t *str; |
525 | |
|
526 | 0 | if (imail->data.guid != NULL) { |
527 | 0 | if (mail_cache_field_can_add(_mail->transaction->cache_trans, |
528 | 0 | _mail->seq, cache_idx)) { |
529 | | /* GUID was prefetched - add to cache */ |
530 | 0 | index_mail_cache_add_idx(imail, cache_idx, |
531 | 0 | imail->data.guid, strlen(imail->data.guid)); |
532 | 0 | } |
533 | 0 | return TRUE; |
534 | 0 | } |
535 | | |
536 | 0 | str = str_new(imail->mail.data_pool, 64); |
537 | 0 | if (mail_cache_lookup_field(_mail->transaction->cache_view, |
538 | 0 | str, imail->mail.mail.seq, cache_idx) > 0) { |
539 | 0 | imail->data.guid = str_c(str); |
540 | 0 | return TRUE; |
541 | 0 | } |
542 | 0 | return FALSE; |
543 | 0 | } |
544 | | |
545 | | static int imapc_mail_get_guid(struct mail *_mail, const char **value_r) |
546 | 0 | { |
547 | 0 | struct index_mail *imail = INDEX_MAIL(_mail); |
548 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
549 | 0 | const enum index_cache_field cache_idx = |
550 | 0 | imail->ibox->cache_fields[MAIL_CACHE_GUID].idx; |
551 | |
|
552 | 0 | if (imapc_mail_get_cached_guid(_mail)) { |
553 | 0 | *value_r = imail->data.guid; |
554 | 0 | return 0; |
555 | 0 | } |
556 | | |
557 | | /* GUID not in cache, fetch it */ |
558 | 0 | if (mbox->guid_fetch_field_name != NULL) { |
559 | 0 | if (imapc_mail_fetch(_mail, MAIL_FETCH_GUID, NULL) < 0) |
560 | 0 | return -1; |
561 | 0 | if (imail->data.guid == NULL) { |
562 | 0 | (void)imapc_mail_failed(_mail, mbox->guid_fetch_field_name); |
563 | 0 | return -1; |
564 | 0 | } |
565 | 0 | } else { |
566 | | /* use hash of message headers as the GUID */ |
567 | 0 | if (imapc_mail_get_hdr_hash(imail) < 0) |
568 | 0 | return -1; |
569 | 0 | } |
570 | | |
571 | 0 | index_mail_cache_add_idx(imail, cache_idx, |
572 | 0 | imail->data.guid, strlen(imail->data.guid)); |
573 | 0 | *value_r = imail->data.guid; |
574 | 0 | return 0; |
575 | 0 | } |
576 | | |
577 | | static int |
578 | | imapc_mail_get_special(struct mail *_mail, enum mail_fetch_field field, |
579 | | const char **value_r) |
580 | 0 | { |
581 | 0 | struct imapc_mailbox *mbox = IMAPC_MAILBOX(_mail->box); |
582 | 0 | struct index_mail *imail = INDEX_MAIL(_mail); |
583 | 0 | uint64_t num; |
584 | |
|
585 | 0 | switch (field) { |
586 | 0 | case MAIL_FETCH_GUID: |
587 | 0 | if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GUID_FORCED) && |
588 | 0 | mbox->guid_fetch_field_name == NULL) { |
589 | | /* GUIDs not supported by server */ |
590 | 0 | break; |
591 | 0 | } |
592 | 0 | *value_r = ""; |
593 | 0 | return imapc_mail_get_guid(_mail, value_r); |
594 | 0 | case MAIL_FETCH_UIDL_BACKEND: |
595 | 0 | if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GMAIL_MIGRATION)) |
596 | 0 | break; |
597 | 0 | if (imapc_mail_get_guid(_mail, value_r) < 0) |
598 | 0 | return -1; |
599 | 0 | if (str_to_uint64(*value_r, &num) < 0) { |
600 | 0 | mail_set_critical(_mail, |
601 | 0 | "X-GM-MSGID not 64bit integer as expected for POP3 UIDL generation: %s", *value_r); |
602 | 0 | return -1; |
603 | 0 | } |
604 | | |
605 | 0 | *value_r = p_strdup_printf(imail->mail.data_pool, |
606 | 0 | "GmailId%"PRIx64, num); |
607 | 0 | return 0; |
608 | 0 | case MAIL_FETCH_IMAP_BODY: |
609 | 0 | if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_FETCH_BODYSTRUCTURE)) |
610 | 0 | break; |
611 | | |
612 | 0 | if (index_mail_get_cached_body(imail, value_r)) |
613 | 0 | return 0; |
614 | 0 | if (imapc_mail_fetch(_mail, field, NULL) < 0) |
615 | 0 | return -1; |
616 | 0 | if (imail->data.body == NULL) { |
617 | 0 | (void)imapc_mail_failed(_mail, "BODY"); |
618 | 0 | return -1; |
619 | 0 | } |
620 | 0 | *value_r = imail->data.body; |
621 | 0 | return 0; |
622 | 0 | case MAIL_FETCH_IMAP_BODYSTRUCTURE: |
623 | 0 | if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_NO_FETCH_BODYSTRUCTURE)) |
624 | 0 | break; |
625 | | |
626 | 0 | if (index_mail_get_cached_bodystructure(imail, value_r)) |
627 | 0 | return 0; |
628 | 0 | if (imapc_mail_fetch(_mail, field, NULL) < 0) |
629 | 0 | return -1; |
630 | 0 | if (imail->data.bodystructure == NULL) { |
631 | 0 | (void)imapc_mail_failed(_mail, "BODYSTRUCTURE"); |
632 | 0 | return -1; |
633 | 0 | } |
634 | 0 | *value_r = imail->data.bodystructure; |
635 | 0 | return 0; |
636 | 0 | default: |
637 | 0 | break; |
638 | 0 | } |
639 | | |
640 | 0 | return index_mail_get_special(_mail, field, value_r); |
641 | 0 | } |
642 | | |
643 | | struct mail_vfuncs imapc_mail_vfuncs = { |
644 | | imapc_mail_close, |
645 | | index_mail_free, |
646 | | imapc_mail_set_seq, |
647 | | index_mail_set_uid, |
648 | | index_mail_set_uid_cache_updates, |
649 | | imapc_mail_prefetch, |
650 | | index_mail_precache, |
651 | | imapc_mail_add_temp_wanted_fields, |
652 | | |
653 | | index_mail_get_flags, |
654 | | index_mail_get_keywords, |
655 | | index_mail_get_keyword_indexes, |
656 | | imapc_mail_get_modseq, |
657 | | index_mail_get_pvt_modseq, |
658 | | index_mail_get_parts, |
659 | | index_mail_get_date, |
660 | | imapc_mail_get_received_date, |
661 | | imapc_mail_get_save_date, |
662 | | imapc_mail_get_virtual_size, |
663 | | imapc_mail_get_physical_size, |
664 | | imapc_mail_get_first_header, |
665 | | imapc_mail_get_headers, |
666 | | imapc_mail_get_header_stream, |
667 | | imapc_mail_get_stream, |
668 | | index_mail_get_binary_stream, |
669 | | imapc_mail_get_special, |
670 | | index_mail_get_backend_mail, |
671 | | index_mail_update_flags, |
672 | | index_mail_update_keywords, |
673 | | index_mail_update_modseq, |
674 | | index_mail_update_pvt_modseq, |
675 | | NULL, |
676 | | index_mail_expunge, |
677 | | index_mail_set_cache_corrupted, |
678 | | index_mail_opened, |
679 | | }; |