/src/dovecot/src/lib-storage/mail.c
Line | Count | Source |
1 | | /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "ioloop.h" |
5 | | #include "buffer.h" |
6 | | #include "hash.h" |
7 | | #include "hex-binary.h" |
8 | | #include "crc32.h" |
9 | | #include "sha1.h" |
10 | | #include "hostpid.h" |
11 | | #include "istream.h" |
12 | | #include "mail-cache.h" |
13 | | #include "mail-storage-private.h" |
14 | | #include "message-id.h" |
15 | | #include "message-part-data.h" |
16 | | #include "imap-bodystructure.h" |
17 | | |
18 | | #include <time.h> |
19 | | |
20 | | struct mail *mail_alloc(struct mailbox_transaction_context *t, |
21 | | enum mail_fetch_field wanted_fields, |
22 | | struct mailbox_header_lookup_ctx *wanted_headers) |
23 | 0 | { |
24 | 0 | struct mail *mail; |
25 | |
|
26 | 0 | i_assert(wanted_headers == NULL || wanted_headers->box == t->box); |
27 | | |
28 | 0 | T_BEGIN { |
29 | 0 | mail = t->box->v.mail_alloc(t, wanted_fields, wanted_headers); |
30 | 0 | hook_mail_allocated(mail); |
31 | 0 | } T_END; |
32 | | |
33 | 0 | return mail; |
34 | 0 | } |
35 | | |
36 | | void mail_free(struct mail **mail) |
37 | 0 | { |
38 | 0 | struct mail_private *p = (struct mail_private *)*mail; |
39 | | |
40 | | /* make sure mailbox_search_*() users don't try to free the mail |
41 | | directly */ |
42 | 0 | i_assert(!p->search_mail); |
43 | | |
44 | 0 | p->v.free(*mail); |
45 | 0 | *mail = NULL; |
46 | 0 | } |
47 | | |
48 | | void mail_set_seq(struct mail *mail, uint32_t seq) |
49 | 0 | { |
50 | 0 | struct mail_private *p = (struct mail_private *)mail; |
51 | |
|
52 | 0 | T_BEGIN { |
53 | 0 | p->v.set_seq(mail, seq, FALSE); |
54 | 0 | } T_END; |
55 | 0 | } |
56 | | |
57 | | void mail_set_seq_saving(struct mail *mail, uint32_t seq) |
58 | 0 | { |
59 | 0 | struct mail_private *p = (struct mail_private *)mail; |
60 | |
|
61 | 0 | T_BEGIN { |
62 | 0 | p->v.set_seq(mail, seq, TRUE); |
63 | 0 | } T_END; |
64 | 0 | } |
65 | | |
66 | | bool mail_set_uid(struct mail *mail, uint32_t uid) |
67 | 0 | { |
68 | 0 | struct mail_private *p = (struct mail_private *)mail; |
69 | 0 | bool ret; |
70 | |
|
71 | 0 | T_BEGIN { |
72 | 0 | ret = p->v.set_uid(mail, uid); |
73 | 0 | } T_END; |
74 | 0 | return ret; |
75 | 0 | } |
76 | | |
77 | | bool mail_prefetch(struct mail *mail) |
78 | 0 | { |
79 | 0 | struct mail_private *p = (struct mail_private *)mail; |
80 | 0 | bool ret; |
81 | |
|
82 | 0 | T_BEGIN { |
83 | 0 | ret = p->v.prefetch(mail); |
84 | 0 | } T_END; |
85 | 0 | return ret; |
86 | 0 | } |
87 | | |
88 | | void mail_add_temp_wanted_fields(struct mail *mail, |
89 | | enum mail_fetch_field fields, |
90 | | struct mailbox_header_lookup_ctx *headers) |
91 | 0 | { |
92 | 0 | struct mail_private *p = (struct mail_private *)mail; |
93 | |
|
94 | 0 | i_assert(headers == NULL || headers->box == mail->box); |
95 | | |
96 | 0 | p->v.add_temp_wanted_fields(mail, fields, headers); |
97 | 0 | } |
98 | | |
99 | | static bool index_mail_get_age_days(struct mail *mail, int *days_r) |
100 | 0 | { |
101 | 0 | int age_days; |
102 | 0 | const struct mail_index_header *hdr = |
103 | 0 | mail_index_get_header(mail->transaction->view); |
104 | 0 | int n_days = N_ELEMENTS(hdr->day_first_uid); |
105 | |
|
106 | 0 | for (age_days = 0; age_days < n_days; age_days++) { |
107 | 0 | if (mail->uid >= hdr->day_first_uid[age_days]) |
108 | 0 | break; |
109 | 0 | } |
110 | |
|
111 | 0 | if (age_days == n_days) { |
112 | | /* mail is too old, cannot determine its age from |
113 | | day_first_uid[]. */ |
114 | 0 | return FALSE; |
115 | 0 | } |
116 | | |
117 | 0 | if (hdr->day_stamp != 0) { |
118 | | /* offset for hdr->day_stamp */ |
119 | 0 | age_days += (ioloop_time - hdr->day_stamp) / (3600 * 24); |
120 | 0 | } |
121 | 0 | *days_r = age_days; |
122 | 0 | return TRUE; |
123 | 0 | } |
124 | | |
125 | | void mail_event_create(struct mail *mail) |
126 | 0 | { |
127 | 0 | struct mail_private *p = (struct mail_private *)mail; |
128 | 0 | int age_days; |
129 | |
|
130 | 0 | if (p->_event != NULL) |
131 | 0 | return; |
132 | 0 | p->_event = event_create(mail->box->event); |
133 | 0 | event_add_category(p->_event, &event_category_mail); |
134 | 0 | event_add_int(p->_event, "seq", mail->seq); |
135 | 0 | event_add_int(p->_event, "uid", mail->uid); |
136 | | /* Add mail age field to event. */ |
137 | 0 | if (index_mail_get_age_days(mail, &age_days)) |
138 | 0 | event_add_int(p->_event, "mail_age_days", age_days); |
139 | |
|
140 | 0 | T_BEGIN { |
141 | 0 | char uid_buf[MAX_INT_STRLEN]; |
142 | 0 | const char *prefix; |
143 | 0 | if (p->mail.uid == 0) { |
144 | 0 | i_assert(p->mail.saving); |
145 | 0 | prefix = "Saving mail: "; |
146 | 0 | } else { |
147 | 0 | prefix = t_strconcat( |
148 | 0 | p->mail.saving ? "Saving mail UID " : "UID ", |
149 | 0 | dec2str_buf(uid_buf, p->mail.uid), |
150 | 0 | ": ", |
151 | 0 | NULL); |
152 | 0 | } |
153 | 0 | event_set_append_log_prefix(p->_event, prefix); |
154 | 0 | } T_END; |
155 | 0 | } |
156 | | |
157 | | struct event *mail_event(struct mail *mail) |
158 | 0 | { |
159 | 0 | struct mail_private *p = (struct mail_private *)mail; |
160 | |
|
161 | 0 | mail_event_create(mail); |
162 | 0 | return p->_event; |
163 | 0 | } |
164 | | |
165 | | enum mail_flags mail_get_flags(struct mail *mail) |
166 | 0 | { |
167 | 0 | struct mail_private *p = (struct mail_private *)mail; |
168 | |
|
169 | 0 | return p->v.get_flags(mail); |
170 | 0 | } |
171 | | |
172 | | uint64_t mail_get_modseq(struct mail *mail) |
173 | 0 | { |
174 | 0 | struct mail_private *p = (struct mail_private *)mail; |
175 | |
|
176 | 0 | return p->v.get_modseq(mail); |
177 | 0 | } |
178 | | |
179 | | uint64_t mail_get_pvt_modseq(struct mail *mail) |
180 | 0 | { |
181 | 0 | struct mail_private *p = (struct mail_private *)mail; |
182 | |
|
183 | 0 | return p->v.get_pvt_modseq(mail); |
184 | 0 | } |
185 | | |
186 | | const char *const *mail_get_keywords(struct mail *mail) |
187 | 0 | { |
188 | 0 | struct mail_private *p = (struct mail_private *)mail; |
189 | |
|
190 | 0 | return p->v.get_keywords(mail); |
191 | 0 | } |
192 | | |
193 | | const ARRAY_TYPE(keyword_indexes) *mail_get_keyword_indexes(struct mail *mail) |
194 | 0 | { |
195 | 0 | struct mail_private *p = (struct mail_private *)mail; |
196 | |
|
197 | 0 | return p->v.get_keyword_indexes(mail); |
198 | 0 | } |
199 | | |
200 | | int mail_get_parts(struct mail *mail, struct message_part **parts_r) |
201 | 0 | { |
202 | 0 | struct mail_private *p = (struct mail_private *)mail; |
203 | 0 | int ret; |
204 | |
|
205 | 0 | T_BEGIN { |
206 | 0 | ret = p->v.get_parts(mail, parts_r); |
207 | 0 | } T_END; |
208 | 0 | return ret; |
209 | 0 | } |
210 | | |
211 | | int mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r) |
212 | 0 | { |
213 | 0 | struct mail_private *p = (struct mail_private *)mail; |
214 | 0 | int ret; |
215 | |
|
216 | 0 | T_BEGIN { |
217 | 0 | ret = p->v.get_date(mail, date_r, timezone_r); |
218 | 0 | } T_END; |
219 | 0 | return ret; |
220 | 0 | } |
221 | | |
222 | | int mail_get_received_date(struct mail *mail, time_t *date_r) |
223 | 0 | { |
224 | 0 | struct mail_private *p = (struct mail_private *)mail; |
225 | 0 | int ret; |
226 | |
|
227 | 0 | T_BEGIN { |
228 | 0 | ret = p->v.get_received_date(mail, date_r); |
229 | 0 | } T_END; |
230 | 0 | return ret; |
231 | 0 | } |
232 | | |
233 | | int mail_get_save_date(struct mail *mail, time_t *date_r) |
234 | 0 | { |
235 | 0 | struct mail_private *p = (struct mail_private *)mail; |
236 | 0 | int ret; |
237 | |
|
238 | 0 | T_BEGIN { |
239 | 0 | ret = p->v.get_save_date(mail, date_r); |
240 | 0 | } T_END; |
241 | 0 | return ret; |
242 | 0 | } |
243 | | |
244 | | int mail_get_virtual_size(struct mail *mail, uoff_t *size_r) |
245 | 0 | { |
246 | 0 | struct mail_private *p = (struct mail_private *)mail; |
247 | 0 | int ret; |
248 | |
|
249 | 0 | T_BEGIN { |
250 | 0 | ret = p->v.get_virtual_size(mail, size_r); |
251 | 0 | } T_END; |
252 | 0 | return ret; |
253 | 0 | } |
254 | | |
255 | | int mail_get_physical_size(struct mail *mail, uoff_t *size_r) |
256 | 0 | { |
257 | 0 | struct mail_private *p = (struct mail_private *)mail; |
258 | 0 | int ret; |
259 | |
|
260 | 0 | T_BEGIN { |
261 | 0 | ret = p->v.get_physical_size(mail, size_r); |
262 | 0 | } T_END; |
263 | 0 | return ret; |
264 | 0 | } |
265 | | |
266 | | int mail_get_first_header(struct mail *mail, const char *field, |
267 | | const char **value_r) |
268 | 0 | { |
269 | 0 | struct mail_private *p = (struct mail_private *)mail; |
270 | 0 | int ret; |
271 | |
|
272 | 0 | T_BEGIN { |
273 | 0 | ret = p->v.get_first_header(mail, field, FALSE, value_r); |
274 | 0 | } T_END; |
275 | 0 | return ret; |
276 | 0 | } |
277 | | |
278 | | int mail_get_first_header_utf8(struct mail *mail, const char *field, |
279 | | const char **value_r) |
280 | 0 | { |
281 | 0 | struct mail_private *p = (struct mail_private *)mail; |
282 | 0 | int ret; |
283 | |
|
284 | 0 | T_BEGIN { |
285 | 0 | ret = p->v.get_first_header(mail, field, TRUE, value_r); |
286 | 0 | } T_END; |
287 | 0 | return ret; |
288 | 0 | } |
289 | | |
290 | | int mail_get_headers(struct mail *mail, const char *field, |
291 | | const char *const **value_r) |
292 | 0 | { |
293 | 0 | struct mail_private *p = (struct mail_private *)mail; |
294 | 0 | int ret; |
295 | |
|
296 | 0 | T_BEGIN { |
297 | 0 | ret = p->v.get_headers(mail, field, FALSE, value_r); |
298 | 0 | } T_END; |
299 | 0 | return ret; |
300 | 0 | } |
301 | | |
302 | | int mail_get_headers_utf8(struct mail *mail, const char *field, |
303 | | const char *const **value_r) |
304 | 0 | { |
305 | 0 | struct mail_private *p = (struct mail_private *)mail; |
306 | 0 | int ret; |
307 | |
|
308 | 0 | T_BEGIN { |
309 | 0 | ret = p->v.get_headers(mail, field, TRUE, value_r); |
310 | 0 | } T_END; |
311 | 0 | return ret; |
312 | 0 | } |
313 | | |
314 | | int mail_get_header_stream(struct mail *mail, |
315 | | struct mailbox_header_lookup_ctx *headers, |
316 | | struct istream **stream_r) |
317 | 0 | { |
318 | 0 | struct mail_private *p = (struct mail_private *)mail; |
319 | 0 | int ret; |
320 | |
|
321 | 0 | i_assert(headers->count > 0); |
322 | 0 | i_assert(headers->box == mail->box); |
323 | | |
324 | 0 | T_BEGIN { |
325 | 0 | ret = p->v.get_header_stream(mail, headers, stream_r); |
326 | 0 | } T_END; |
327 | 0 | return ret; |
328 | 0 | } |
329 | | |
330 | | void mail_set_aborted(struct mail *mail) |
331 | 0 | { |
332 | 0 | mail_storage_set_error(mail->box->storage, MAIL_ERROR_LOOKUP_ABORTED, |
333 | 0 | "Mail field not cached"); |
334 | 0 | } |
335 | | |
336 | | int mail_get_stream(struct mail *mail, struct message_size *hdr_size, |
337 | | struct message_size *body_size, struct istream **stream_r) |
338 | 0 | { |
339 | 0 | return mail_get_stream_because(mail, hdr_size, body_size, |
340 | 0 | "mail stream", stream_r); |
341 | 0 | } |
342 | | |
343 | | int mail_get_stream_because(struct mail *mail, struct message_size *hdr_size, |
344 | | struct message_size *body_size, |
345 | | const char *reason, struct istream **stream_r) |
346 | 0 | { |
347 | 0 | struct mail_private *p = (struct mail_private *)mail; |
348 | 0 | int ret; |
349 | |
|
350 | 0 | if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { |
351 | 0 | mail_set_aborted(mail); |
352 | 0 | return -1; |
353 | 0 | } |
354 | 0 | T_BEGIN { |
355 | 0 | p->get_stream_reason = reason; |
356 | 0 | ret = p->v.get_stream(mail, TRUE, hdr_size, body_size, stream_r); |
357 | 0 | p->get_stream_reason = ""; |
358 | 0 | } T_END; |
359 | 0 | i_assert(ret < 0 || (*stream_r)->blocking); |
360 | 0 | return ret; |
361 | 0 | } |
362 | | |
363 | | int mail_get_hdr_stream(struct mail *mail, struct message_size *hdr_size, |
364 | | struct istream **stream_r) |
365 | 0 | { |
366 | 0 | return mail_get_hdr_stream_because(mail, hdr_size, "header stream", stream_r); |
367 | 0 | } |
368 | | |
369 | | int mail_get_hdr_stream_because(struct mail *mail, |
370 | | struct message_size *hdr_size, |
371 | | const char *reason, struct istream **stream_r) |
372 | 0 | { |
373 | 0 | struct mail_private *p = (struct mail_private *)mail; |
374 | 0 | int ret; |
375 | |
|
376 | 0 | if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { |
377 | 0 | mail_set_aborted(mail); |
378 | 0 | return -1; |
379 | 0 | } |
380 | 0 | T_BEGIN { |
381 | 0 | p->get_stream_reason = reason; |
382 | 0 | ret = p->v.get_stream(mail, FALSE, hdr_size, NULL, stream_r); |
383 | 0 | p->get_stream_reason = ""; |
384 | 0 | } T_END; |
385 | 0 | i_assert(ret < 0 || (*stream_r)->blocking); |
386 | 0 | return ret; |
387 | 0 | } |
388 | | |
389 | | int mail_get_binary_stream(struct mail *mail, const struct message_part *part, |
390 | | bool include_hdr, |
391 | | struct mail_binary_properties *bprops_r, |
392 | | struct istream **stream_r) |
393 | 0 | { |
394 | 0 | struct mail_private *p = (struct mail_private *)mail; |
395 | 0 | int ret; |
396 | |
|
397 | 0 | if (bprops_r != NULL) |
398 | 0 | i_zero(bprops_r); |
399 | |
|
400 | 0 | if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { |
401 | 0 | mail_set_aborted(mail); |
402 | 0 | return -1; |
403 | 0 | } |
404 | 0 | T_BEGIN { |
405 | 0 | ret = p->v.get_binary_stream(mail, part, include_hdr, |
406 | 0 | bprops_r, stream_r); |
407 | 0 | } T_END; |
408 | 0 | i_assert(ret < 0 || (*stream_r)->blocking); |
409 | 0 | return ret; |
410 | 0 | } |
411 | | |
412 | | int mail_get_binary_properties(struct mail *mail, |
413 | | const struct message_part *part, |
414 | | bool include_hdr, |
415 | | struct mail_binary_properties *bprops_r) |
416 | 0 | { |
417 | 0 | struct mail_private *p = (struct mail_private *)mail; |
418 | 0 | int ret; |
419 | |
|
420 | 0 | if (bprops_r != NULL) |
421 | 0 | i_zero(bprops_r); |
422 | |
|
423 | 0 | T_BEGIN { |
424 | 0 | ret = p->v.get_binary_stream(mail, part, include_hdr, |
425 | 0 | bprops_r, NULL); |
426 | 0 | } T_END; |
427 | 0 | return ret; |
428 | 0 | } |
429 | | |
430 | | int mail_get_special(struct mail *mail, enum mail_fetch_field field, |
431 | | const char **value_r) |
432 | 0 | { |
433 | 0 | struct mail_private *p = (struct mail_private *)mail; |
434 | |
|
435 | 0 | if (p->v.get_special(mail, field, value_r) < 0) |
436 | 0 | return -1; |
437 | 0 | i_assert(*value_r != NULL); |
438 | 0 | return 0; |
439 | 0 | } |
440 | | |
441 | | int mail_get_backend_mail(struct mail *mail, struct mail **real_mail_r) |
442 | 0 | { |
443 | 0 | struct mail_private *p = (struct mail_private *)mail; |
444 | 0 | return p->v.get_backend_mail(mail, real_mail_r); |
445 | 0 | } |
446 | | |
447 | | static int mail_get_message_id_full(struct mail *mail, |
448 | | const char **value_r, |
449 | | bool require_valid) |
450 | 0 | { |
451 | 0 | const char *hdr_value, *msgid_bare; |
452 | 0 | int ret; |
453 | |
|
454 | 0 | ret = mail_get_first_header(mail, "Message-ID", &hdr_value); |
455 | 0 | if (ret <= 0) { |
456 | 0 | *value_r = NULL; |
457 | 0 | return ret; |
458 | 0 | } |
459 | | |
460 | 0 | *value_r = hdr_value; /* save it now as next function alters it */ |
461 | 0 | msgid_bare = message_id_get_next(&hdr_value); |
462 | |
|
463 | 0 | if (msgid_bare != NULL) { |
464 | | /* Complete the message ID with surrounding `<' and `>'. */ |
465 | 0 | *value_r = t_strconcat("<", msgid_bare, ">", NULL); |
466 | 0 | return 1; |
467 | 0 | } else if (!require_valid) { |
468 | | /* *value_r already set above */ |
469 | 0 | return 1; |
470 | 0 | } else { |
471 | 0 | *value_r = NULL; |
472 | 0 | return 0; |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | | int mail_get_message_id(struct mail *mail, const char **value_r) |
477 | 0 | { |
478 | 0 | return mail_get_message_id_full(mail, value_r, TRUE/*require_valid*/); |
479 | 0 | } |
480 | | |
481 | | int mail_get_message_id_no_validation(struct mail *mail, const char **value_r) |
482 | 0 | { |
483 | 0 | return mail_get_message_id_full(mail, value_r, FALSE/*require_valid*/); |
484 | 0 | } |
485 | | |
486 | | void mail_update_flags(struct mail *mail, enum modify_type modify_type, |
487 | | enum mail_flags flags) |
488 | 0 | { |
489 | 0 | struct mail_private *p = (struct mail_private *)mail; |
490 | |
|
491 | 0 | p->v.update_flags(mail, modify_type, flags); |
492 | 0 | } |
493 | | |
494 | | void mail_update_keywords(struct mail *mail, enum modify_type modify_type, |
495 | | struct mail_keywords *keywords) |
496 | 0 | { |
497 | 0 | struct mail_private *p = (struct mail_private *)mail; |
498 | |
|
499 | 0 | p->v.update_keywords(mail, modify_type, keywords); |
500 | 0 | } |
501 | | |
502 | | void mail_update_modseq(struct mail *mail, uint64_t min_modseq) |
503 | 0 | { |
504 | 0 | struct mail_private *p = (struct mail_private *)mail; |
505 | |
|
506 | 0 | p->v.update_modseq(mail, min_modseq); |
507 | 0 | } |
508 | | |
509 | | void mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq) |
510 | 0 | { |
511 | 0 | struct mail_private *p = (struct mail_private *)mail; |
512 | |
|
513 | 0 | p->v.update_pvt_modseq(mail, min_pvt_modseq); |
514 | 0 | } |
515 | | |
516 | | void mail_update_pop3_uidl(struct mail *mail, const char *uidl) |
517 | 0 | { |
518 | 0 | struct mail_private *p = (struct mail_private *)mail; |
519 | |
|
520 | 0 | if (p->v.update_pop3_uidl != NULL) |
521 | 0 | p->v.update_pop3_uidl(mail, uidl); |
522 | 0 | } |
523 | | |
524 | | void mail_expunge(struct mail *mail) |
525 | 0 | { |
526 | 0 | struct mail_private *p = (struct mail_private *)mail; |
527 | |
|
528 | 0 | T_BEGIN { |
529 | 0 | p->v.expunge(mail); |
530 | 0 | mail_expunge_requested_event(mail); |
531 | 0 | } T_END; |
532 | 0 | } |
533 | | |
534 | | void mail_autoexpunge(struct mail *mail) |
535 | 0 | { |
536 | 0 | struct mail_private *p = (struct mail_private *)mail; |
537 | 0 | p->autoexpunged = TRUE; |
538 | 0 | mail_expunge(mail); |
539 | 0 | p->autoexpunged = FALSE; |
540 | 0 | } |
541 | | |
542 | | void mail_set_expunged(struct mail *mail) |
543 | 0 | { |
544 | 0 | mail_storage_set_error(mail->box->storage, MAIL_ERROR_EXPUNGED, |
545 | 0 | "Message was expunged"); |
546 | 0 | mail->expunged = TRUE; |
547 | 0 | } |
548 | | |
549 | | int mail_precache(struct mail *mail) |
550 | 0 | { |
551 | 0 | struct mail_private *p = (struct mail_private *)mail; |
552 | 0 | int ret; |
553 | |
|
554 | 0 | T_BEGIN { |
555 | 0 | ret = p->v.precache(mail); |
556 | 0 | } T_END; |
557 | 0 | return ret; |
558 | 0 | } |
559 | | |
560 | | void mail_set_cache_corrupted(struct mail *mail, |
561 | | enum mail_fetch_field field, |
562 | | const char *reason) |
563 | 0 | { |
564 | 0 | struct mail_private *p = (struct mail_private *)mail; |
565 | 0 | p->v.set_cache_corrupted(mail, field, reason); |
566 | 0 | } |
567 | | |
568 | | void mail_generate_guid_128_hash(const char *guid, guid_128_t guid_128_r) |
569 | 0 | { |
570 | 0 | unsigned char sha1_sum[SHA1_RESULTLEN]; |
571 | 0 | buffer_t buf; |
572 | |
|
573 | 0 | if (guid_128_from_string(guid, guid_128_r) < 0) { |
574 | | /* not 128bit hex. use a hash of it instead. */ |
575 | 0 | buffer_create_from_data(&buf, guid_128_r, GUID_128_SIZE); |
576 | 0 | buffer_set_used_size(&buf, 0); |
577 | 0 | sha1_get_digest(guid, strlen(guid), sha1_sum); |
578 | | #if SHA1_RESULTLEN < GUID_128_SIZE |
579 | | # error not possible |
580 | | #endif |
581 | 0 | buffer_append(&buf, |
582 | 0 | sha1_sum + SHA1_RESULTLEN - GUID_128_SIZE, |
583 | 0 | GUID_128_SIZE); |
584 | 0 | } |
585 | 0 | } |
586 | | |
587 | | static bool |
588 | | mail_message_has_attachment(struct message_part *part, |
589 | | const struct message_part_attachment_settings *set) |
590 | 0 | { |
591 | 0 | bool has_attachment = FALSE; |
592 | 0 | for (; part != NULL && !has_attachment; part = part->next) T_BEGIN { |
593 | 0 | has_attachment = message_part_is_attachment(part, set) || |
594 | 0 | mail_message_has_attachment(part->children, set); |
595 | 0 | } T_END; |
596 | | |
597 | 0 | return has_attachment; |
598 | 0 | } |
599 | | |
600 | | bool mail_has_attachment_keywords(struct mail *mail) |
601 | 0 | { |
602 | 0 | const char *const *kw = mail_get_keywords(mail); |
603 | 0 | return (str_array_icase_find(kw, MAIL_KEYWORD_HAS_ATTACHMENT) != |
604 | 0 | str_array_icase_find(kw, MAIL_KEYWORD_HAS_NO_ATTACHMENT)); |
605 | 0 | } |
606 | | |
607 | | static int mail_parse_parts(struct mail *mail, struct message_part **parts_r) |
608 | 0 | { |
609 | 0 | const char *structure, *error; |
610 | 0 | struct mail_private *pmail = (struct mail_private*)mail; |
611 | | |
612 | | /* need to get bodystructure first */ |
613 | 0 | if (mail_get_special(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, |
614 | 0 | &structure) < 0) { |
615 | | /* Don't bother logging an error. See |
616 | | mail_set_attachment_keywords(). */ |
617 | 0 | return -1; |
618 | 0 | } |
619 | 0 | if (imap_bodystructure_parse_full(structure, pmail->data_pool, parts_r, |
620 | 0 | &error) < 0) { |
621 | 0 | mail_set_cache_corrupted(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, |
622 | 0 | error); |
623 | 0 | return -1; |
624 | 0 | } |
625 | 0 | return 0; |
626 | 0 | } |
627 | | |
628 | | int mail_set_attachment_keywords(struct mail *mail) |
629 | 0 | { |
630 | 0 | int ret; |
631 | 0 | const struct mail_storage_settings *mail_set = |
632 | 0 | mail_storage_get_settings(mailbox_get_storage(mail->box)); |
633 | |
|
634 | 0 | const char *const keyword_has_attachment[] = { |
635 | 0 | MAIL_KEYWORD_HAS_ATTACHMENT, |
636 | 0 | NULL, |
637 | 0 | }; |
638 | 0 | const char *const keyword_has_no_attachment[] = { |
639 | 0 | MAIL_KEYWORD_HAS_NO_ATTACHMENT, |
640 | 0 | NULL |
641 | 0 | }; |
642 | 0 | struct message_part_attachment_settings set = { |
643 | 0 | .content_type_filter = |
644 | 0 | mail_set->parsed_mail_attachment_content_type_filter, |
645 | 0 | .exclude_inlined = |
646 | 0 | mail_set->parsed_mail_attachment_exclude_inlined, |
647 | 0 | }; |
648 | 0 | struct mail_keywords *kw_has = NULL, *kw_has_not = NULL; |
649 | | |
650 | | /* walk all parts and see if there is an attachment */ |
651 | 0 | struct message_part *parts; |
652 | 0 | if (mail_get_parts(mail, &parts) < 0) { |
653 | | /* Callers don't really care about the exact error, and |
654 | | critical errors were already logged. Most importantly we |
655 | | don't want to log MAIL_ERROR_LOOKUP_ABORTED since that is |
656 | | an expected error. */ |
657 | 0 | ret = -1; |
658 | 0 | } else if (parts->data == NULL && |
659 | 0 | mail_parse_parts(mail, &parts) < 0) { |
660 | 0 | ret = -1; |
661 | 0 | } else if (mailbox_keywords_create(mail->box, keyword_has_attachment, &kw_has) < 0 || |
662 | 0 | mailbox_keywords_create(mail->box, keyword_has_no_attachment, &kw_has_not) < 0) { |
663 | 0 | mail_set_critical(mail, "Failed to add attachment keywords: " |
664 | 0 | "mailbox_keywords_create(%s) failed: %s", |
665 | 0 | mailbox_get_vname(mail->box), |
666 | 0 | mail_storage_get_last_internal_error(mail->box->storage, NULL)); |
667 | 0 | ret = -1; |
668 | 0 | } else { |
669 | 0 | bool has_attachment = mail_message_has_attachment(parts, &set); |
670 | | |
671 | | /* make sure only one of the keywords gets set */ |
672 | 0 | mail_update_keywords(mail, MODIFY_REMOVE, has_attachment ? kw_has_not : kw_has); |
673 | 0 | mail_update_keywords(mail, MODIFY_ADD, has_attachment ? kw_has : kw_has_not); |
674 | 0 | ret = has_attachment ? 1 : 0; |
675 | 0 | } |
676 | |
|
677 | 0 | if (kw_has != NULL) |
678 | 0 | mailbox_keywords_unref(&kw_has); |
679 | 0 | if (kw_has_not != NULL) |
680 | 0 | mailbox_keywords_unref(&kw_has_not); |
681 | |
|
682 | 0 | return ret; |
683 | 0 | } |
684 | | |
685 | | bool mail_stream_access_start(struct mail *mail) |
686 | 0 | { |
687 | 0 | if (mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER) { |
688 | 0 | mail_set_aborted(mail); |
689 | 0 | return FALSE; |
690 | 0 | } |
691 | 0 | mail->mail_stream_accessed = TRUE; |
692 | 0 | mail_event_create(mail); |
693 | 0 | return TRUE; |
694 | 0 | } |
695 | | |
696 | | bool mail_metadata_access_start(struct mail *mail) |
697 | 0 | { |
698 | 0 | if (mail->lookup_abort >= MAIL_LOOKUP_ABORT_NOT_IN_CACHE) { |
699 | 0 | mail_set_aborted(mail); |
700 | 0 | return FALSE; |
701 | 0 | } |
702 | 0 | mail->mail_metadata_accessed = TRUE; |
703 | 0 | mail_event_create(mail); |
704 | 0 | return TRUE; |
705 | 0 | } |
706 | | |
707 | | void mail_opened_event(struct mail *mail) |
708 | 0 | { |
709 | 0 | struct mail_private *pmail = |
710 | 0 | container_of(mail, struct mail_private, mail); |
711 | | |
712 | | /* If istream is opened twice for the same mail, count it as a single |
713 | | mail_opened event. Their cost is effectively the same, so having |
714 | | two events would just be confusing the statistics. */ |
715 | 0 | if (pmail->mail_opened_event_sent) |
716 | 0 | return; |
717 | 0 | pmail->mail_opened_event_sent = TRUE; |
718 | |
|
719 | 0 | struct event_passthrough *e = |
720 | 0 | event_create_passthrough(mail_event(mail))-> |
721 | 0 | set_name("mail_opened")-> |
722 | 0 | add_str("reason", pmail->get_stream_reason); |
723 | 0 | if (pmail->get_stream_reason != NULL) |
724 | 0 | e_debug(e->event(), "Opened mail because: %s", |
725 | 0 | pmail->get_stream_reason); |
726 | 0 | else |
727 | 0 | e_debug(e->event(), "Opened mail"); |
728 | 0 | } |
729 | | |
730 | | void mail_metadata_accessed_event(struct event *mail_event) |
731 | 0 | { |
732 | 0 | struct event_passthrough *e = |
733 | 0 | event_create_passthrough(mail_event)-> |
734 | 0 | set_name("mail_metadata_accessed"); |
735 | 0 | e_debug(e->event(), "Mail metadata accessed"); |
736 | 0 | } |
737 | | |
738 | | void mail_expunge_requested_event(struct mail *mail) |
739 | 0 | { |
740 | 0 | struct event_passthrough *e = |
741 | 0 | event_create_passthrough(mail_event(mail))-> |
742 | 0 | set_name("mail_expunge_requested")-> |
743 | 0 | add_int("uid", mail->uid)-> |
744 | 0 | add_int("seq", mail->seq); |
745 | 0 | e_debug(e->event(), "Expunge requested"); |
746 | 0 | } |