1 /* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "index-mail.h"
6 #include "virtual-storage.h"
7 #include "virtual-transaction.h"
8
9 struct virtual_mail {
10 struct index_mail imail;
11
12 enum mail_fetch_field wanted_fields;
13 struct mailbox_header_lookup_ctx *wanted_headers;
14
15 /* temp_wanted_fields for this mail. Used only when mail doesn't have
16 a backend mail yet. */
17 enum mail_fetch_field delayed_temp_fields;
18 struct mailbox_header_lookup_ctx *delayed_temp_headers;
19
20 /* currently active mail */
21 struct mail *cur_backend_mail;
22 struct virtual_mail_index_record cur_vrec;
23
24 /* all allocated mails */
25 ARRAY(struct mail *) backend_mails;
26
27 /* mail is lost if backend_mail doesn't point to correct mail */
28 bool cur_lost:1;
29 };
30
31 struct mail *
virtual_mail_alloc(struct mailbox_transaction_context * t,enum mail_fetch_field wanted_fields,struct mailbox_header_lookup_ctx * wanted_headers)32 virtual_mail_alloc(struct mailbox_transaction_context *t,
33 enum mail_fetch_field wanted_fields,
34 struct mailbox_header_lookup_ctx *wanted_headers)
35 {
36 struct virtual_mailbox *mbox = (struct virtual_mailbox *)t->box;
37 struct virtual_mail *vmail;
38 pool_t mail_pool, data_pool;
39
40 mail_pool = pool_alloconly_create("vmail", 1024);
41 data_pool = pool_alloconly_create("virtual index_mail", 512);
42 vmail = p_new(mail_pool, struct virtual_mail, 1);
43 vmail->wanted_fields = wanted_fields;
44 vmail->wanted_headers = wanted_headers;
45 if (vmail->wanted_headers != NULL)
46 mailbox_header_lookup_ref(vmail->wanted_headers);
47 /* Do not pass wanted_fields or wanted_headers to index_mail_init.
48 It will just cause unwanted behaviour, as we only want these
49 to be passed to backend mails. */
50 index_mail_init(&vmail->imail, t, 0, NULL, mail_pool, data_pool);
51 vmail->imail.mail.v = virtual_mail_vfuncs;
52 i_array_init(&vmail->backend_mails, array_count(&mbox->backend_boxes));
53 return &vmail->imail.mail.mail;
54 }
55
virtual_mail_close(struct mail * mail)56 static void virtual_mail_close(struct mail *mail)
57 {
58 struct virtual_mail *vmail = (struct virtual_mail *)mail;
59 struct mail **mails;
60 unsigned int i, count;
61
62 if (mail->seq != 0) {
63 mailbox_header_lookup_unref(&vmail->delayed_temp_headers);
64 vmail->delayed_temp_fields = 0;
65 }
66
67 mails = array_get_modifiable(&vmail->backend_mails, &count);
68 for (i = 0; i < count; i++) {
69 struct mail_private *p = (struct mail_private *)mails[i];
70
71 if (vmail->imail.freeing)
72 mail_free(&mails[i]);
73 else
74 p->v.close(mails[i]);
75 }
76 if (vmail->imail.freeing) {
77 array_free(&vmail->backend_mails);
78 mailbox_header_lookup_unref(&vmail->wanted_headers);
79 }
80 index_mail_close(mail);
81 }
82
83 static struct mail *
backend_mail_find(struct virtual_mail * vmail,struct mailbox * box)84 backend_mail_find(struct virtual_mail *vmail, struct mailbox *box)
85 {
86 struct mail *const *mails;
87 unsigned int i, count;
88
89 mails = array_get(&vmail->backend_mails, &count);
90 for (i = 0; i < count; i++) {
91 if (mails[i]->box == box)
92 return mails[i];
93 }
94 return NULL;
95 }
96
backend_mail_get(struct virtual_mail * vmail,struct mail ** backend_mail_r)97 static int backend_mail_get(struct virtual_mail *vmail,
98 struct mail **backend_mail_r)
99 {
100 struct mail *mail = &vmail->imail.mail.mail;
101 struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box;
102 struct virtual_backend_box *bbox;
103
104 *backend_mail_r = NULL;
105
106 if (vmail->cur_backend_mail != NULL) {
107 if (vmail->cur_lost) {
108 mail_set_expunged(&vmail->imail.mail.mail);
109 return -1;
110 }
111 *backend_mail_r = vmail->cur_backend_mail;
112 return 0;
113 }
114
115 bbox = virtual_backend_box_lookup(mbox, vmail->cur_vrec.mailbox_id);
116 i_assert(bbox != NULL);
117
118 vmail->cur_backend_mail = backend_mail_find(vmail, bbox->box);
119 if (vmail->cur_backend_mail == NULL) {
120 if (!bbox->box->opened &&
121 virtual_backend_box_open(mbox, bbox) < 0) {
122 virtual_box_copy_error(mail->box, bbox->box);
123 return -1;
124 }
125 (void)virtual_mail_set_backend_mail(mail, bbox);
126 i_assert(vmail->cur_backend_mail != NULL);
127 }
128 virtual_backend_box_accessed(mbox, bbox);
129 vmail->cur_lost = !mail_set_uid(vmail->cur_backend_mail,
130 vmail->cur_vrec.real_uid);
131 mail->expunged = vmail->cur_lost || vmail->cur_backend_mail->expunged;
132 if (vmail->cur_lost) {
133 mail_set_expunged(&vmail->imail.mail.mail);
134 return -1;
135 }
136 /* headers need to be converted to backend-headers, so go through
137 the virtual add_temp_wanted_fields() again. */
138 mail_add_temp_wanted_fields(mail, vmail->delayed_temp_fields,
139 vmail->delayed_temp_headers);
140 *backend_mail_r = vmail->cur_backend_mail;
141 return 0;
142 }
143
144 struct mail *
virtual_mail_set_backend_mail(struct mail * mail,struct virtual_backend_box * bbox)145 virtual_mail_set_backend_mail(struct mail *mail,
146 struct virtual_backend_box *bbox)
147 {
148 struct virtual_mail *vmail = (struct virtual_mail *)mail;
149 struct mail_private *backend_pmail;
150 struct mailbox_transaction_context *backend_trans;
151 struct mailbox_header_lookup_ctx *backend_headers;
152
153 i_assert(bbox->box->opened);
154
155 backend_trans = virtual_transaction_get(mail->transaction, bbox->box);
156
157 backend_headers = vmail->wanted_headers == NULL ? NULL :
158 mailbox_header_lookup_init(bbox->box,
159 vmail->wanted_headers->name);
160 vmail->cur_backend_mail =
161 mail_alloc(backend_trans, vmail->wanted_fields, backend_headers);
162 mailbox_header_lookup_unref(&backend_headers);
163
164 backend_pmail = (struct mail_private *)vmail->cur_backend_mail;
165 backend_pmail->vmail = mail;
166 array_push_back(&vmail->backend_mails, &vmail->cur_backend_mail);
167 return vmail->cur_backend_mail;
168 }
169
virtual_mail_set_unattached_backend_mail(struct mail * mail,struct mail * backend_mail)170 void virtual_mail_set_unattached_backend_mail(struct mail *mail,
171 struct mail *backend_mail)
172 {
173 struct virtual_mail *vmail = (struct virtual_mail *)mail;
174 struct mail_private *backend_pmail;
175
176 vmail->cur_backend_mail = backend_mail;
177
178 backend_pmail = (struct mail_private *)backend_mail;
179 backend_pmail->vmail = mail;
180 }
181
virtual_mail_set_seq(struct mail * mail,uint32_t seq,bool saving)182 static void virtual_mail_set_seq(struct mail *mail, uint32_t seq, bool saving)
183 {
184 struct virtual_mail *vmail = (struct virtual_mail *)mail;
185 struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box;
186 const void *data;
187
188 i_assert(!saving);
189
190 mail_index_lookup_ext(mail->transaction->view, seq,
191 mbox->virtual_ext_id, &data, NULL);
192 memcpy(&vmail->cur_vrec, data, sizeof(vmail->cur_vrec));
193
194 index_mail_set_seq(mail, seq, saving);
195
196 vmail->cur_backend_mail = NULL;
197 }
198
virtual_mail_set_uid(struct mail * mail,uint32_t uid)199 static bool virtual_mail_set_uid(struct mail *mail, uint32_t uid)
200 {
201 uint32_t seq;
202
203 if (!mail_index_lookup_seq(mail->transaction->view, uid, &seq))
204 return FALSE;
205
206 virtual_mail_set_seq(mail, seq, FALSE);
207 return TRUE;
208 }
209
virtual_mail_set_uid_cache_updates(struct mail * mail,bool set)210 static void virtual_mail_set_uid_cache_updates(struct mail *mail, bool set)
211 {
212 struct virtual_mail *vmail = (struct virtual_mail *)mail;
213 struct mail *backend_mail;
214 struct mail_private *p;
215
216 if (backend_mail_get(vmail, &backend_mail) < 0)
217 return;
218 p = (struct mail_private *)backend_mail;
219 p->v.set_uid_cache_updates(backend_mail, set);
220 }
221
virtual_mail_prefetch(struct mail * mail)222 static bool virtual_mail_prefetch(struct mail *mail)
223 {
224 struct virtual_mail *vmail = (struct virtual_mail *)mail;
225 struct mail *backend_mail;
226 struct mail_private *p;
227
228 if (backend_mail_get(vmail, &backend_mail) < 0)
229 return TRUE;
230 p = (struct mail_private *)backend_mail;
231 return p->v.prefetch(backend_mail);
232 }
233
virtual_mail_precache(struct mail * mail)234 static int virtual_mail_precache(struct mail *mail)
235 {
236 struct virtual_mail *vmail = (struct virtual_mail *)mail;
237 struct mail *backend_mail;
238 struct mail_private *p;
239
240 if (backend_mail_get(vmail, &backend_mail) < 0)
241 return -1;
242 p = (struct mail_private *)backend_mail;
243 return p->v.precache(backend_mail);
244 }
245
246 static void
virtual_mail_add_temp_wanted_fields(struct mail * mail,enum mail_fetch_field fields,struct mailbox_header_lookup_ctx * headers)247 virtual_mail_add_temp_wanted_fields(struct mail *mail,
248 enum mail_fetch_field fields,
249 struct mailbox_header_lookup_ctx *headers)
250 {
251 struct virtual_mail *vmail = (struct virtual_mail *)mail;
252 struct mail *backend_mail;
253 struct mailbox_header_lookup_ctx *backend_headers, *new_headers;
254
255 if (mail->seq == 0) {
256 /* No mail set yet. Delay until it is set. */
257 vmail->delayed_temp_fields |= fields;
258 if (vmail->delayed_temp_headers == NULL)
259 vmail->delayed_temp_headers = headers;
260 else {
261 new_headers = mailbox_header_lookup_merge(
262 vmail->delayed_temp_headers, headers);
263 mailbox_header_lookup_unref(&vmail->delayed_temp_headers);
264 vmail->delayed_temp_headers = new_headers;
265 }
266 return;
267 }
268
269 if (backend_mail_get(vmail, &backend_mail) < 0)
270 return;
271 /* convert header indexes to backend mailbox's header indexes */
272 backend_headers = headers == NULL ? NULL :
273 mailbox_header_lookup_init(backend_mail->box, headers->name);
274 mail_add_temp_wanted_fields(backend_mail, fields, backend_headers);
275 mailbox_header_lookup_unref(&backend_headers);
276 }
277
278 static int
virtual_mail_get_parts(struct mail * mail,struct message_part ** parts_r)279 virtual_mail_get_parts(struct mail *mail, struct message_part **parts_r)
280 {
281 struct virtual_mail *vmail = (struct virtual_mail *)mail;
282 struct mail *backend_mail;
283
284 if (backend_mail_get(vmail, &backend_mail) < 0)
285 return -1;
286 if (mail_get_parts(backend_mail, parts_r) < 0) {
287 virtual_box_copy_error(mail->box, backend_mail->box);
288 return -1;
289 }
290 return 0;
291 }
292
293 static int
virtual_mail_get_date(struct mail * mail,time_t * date_r,int * timezone_r)294 virtual_mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r)
295 {
296 struct virtual_mail *vmail = (struct virtual_mail *)mail;
297 struct mail *backend_mail;
298 int tz;
299
300 if (timezone_r == NULL)
301 timezone_r = &tz;
302
303 if (backend_mail_get(vmail, &backend_mail) < 0)
304 return -1;
305 if (mail_get_date(backend_mail, date_r, timezone_r) < 0) {
306 virtual_box_copy_error(mail->box, backend_mail->box);
307 return -1;
308 }
309 return 0;
310 }
311
virtual_mail_get_received_date(struct mail * mail,time_t * date_r)312 static int virtual_mail_get_received_date(struct mail *mail, time_t *date_r)
313 {
314 struct virtual_mail *vmail = (struct virtual_mail *)mail;
315 struct mail *backend_mail;
316
317 if (backend_mail_get(vmail, &backend_mail) < 0)
318 return -1;
319 if (mail_get_received_date(backend_mail, date_r) < 0) {
320 virtual_box_copy_error(mail->box, backend_mail->box);
321 return -1;
322 }
323 return 0;
324 }
325
virtual_mail_get_save_date(struct mail * mail,time_t * date_r)326 static int virtual_mail_get_save_date(struct mail *mail, time_t *date_r)
327 {
328 struct virtual_mail *vmail = (struct virtual_mail *)mail;
329 struct mail *backend_mail;
330 int ret;
331
332 if (backend_mail_get(vmail, &backend_mail) < 0)
333 return -1;
334 ret = mail_get_save_date(backend_mail, date_r);
335 if (ret < 0)
336 virtual_box_copy_error(mail->box, backend_mail->box);
337 return ret;
338 }
339
virtual_mail_get_virtual_mail_size(struct mail * mail,uoff_t * size_r)340 static int virtual_mail_get_virtual_mail_size(struct mail *mail, uoff_t *size_r)
341 {
342 struct virtual_mail *vmail = (struct virtual_mail *)mail;
343 struct mail *backend_mail;
344
345 if (backend_mail_get(vmail, &backend_mail) < 0)
346 return -1;
347 if (mail_get_virtual_size(backend_mail, size_r) < 0) {
348 virtual_box_copy_error(mail->box, backend_mail->box);
349 return -1;
350 }
351 return 0;
352 }
353
virtual_mail_get_physical_size(struct mail * mail,uoff_t * size_r)354 static int virtual_mail_get_physical_size(struct mail *mail, uoff_t *size_r)
355 {
356 struct virtual_mail *vmail = (struct virtual_mail *)mail;
357 struct mail *backend_mail;
358
359 if (backend_mail_get(vmail, &backend_mail) < 0)
360 return -1;
361 if (mail_get_physical_size(backend_mail, size_r) < 0) {
362 virtual_box_copy_error(mail->box, backend_mail->box);
363 return -1;
364 }
365 return 0;
366 }
367
368 static int
virtual_mail_get_first_header(struct mail * mail,const char * field,bool decode_to_utf8,const char ** value_r)369 virtual_mail_get_first_header(struct mail *mail, const char *field,
370 bool decode_to_utf8, const char **value_r)
371 {
372 struct virtual_mail *vmail = (struct virtual_mail *)mail;
373 struct mail *backend_mail;
374 struct mail_private *p;
375 int ret;
376
377 if (backend_mail_get(vmail, &backend_mail) < 0)
378 return -1;
379 p = (struct mail_private *)backend_mail;
380 ret = p->v.get_first_header(backend_mail, field,
381 decode_to_utf8, value_r);
382 if (ret < 0) {
383 virtual_box_copy_error(mail->box, backend_mail->box);
384 return -1;
385 }
386 return ret;
387 }
388
389 static int
virtual_mail_get_headers(struct mail * mail,const char * field,bool decode_to_utf8,const char * const ** value_r)390 virtual_mail_get_headers(struct mail *mail, const char *field,
391 bool decode_to_utf8, const char *const **value_r)
392 {
393 struct virtual_mail *vmail = (struct virtual_mail *)mail;
394 struct mail *backend_mail;
395 struct mail_private *p;
396
397 if (backend_mail_get(vmail, &backend_mail) < 0)
398 return -1;
399 p = (struct mail_private *)backend_mail;
400 if (p->v.get_headers(backend_mail, field, decode_to_utf8, value_r) < 0) {
401 virtual_box_copy_error(mail->box, backend_mail->box);
402 return -1;
403 }
404 return 0;
405 }
406
407 static int
virtual_mail_get_header_stream(struct mail * mail,struct mailbox_header_lookup_ctx * headers,struct istream ** stream_r)408 virtual_mail_get_header_stream(struct mail *mail,
409 struct mailbox_header_lookup_ctx *headers,
410 struct istream **stream_r)
411 {
412 struct virtual_mail *vmail = (struct virtual_mail *)mail;
413 struct mail *backend_mail;
414 struct mailbox_header_lookup_ctx *backend_headers;
415 int ret;
416
417 if (backend_mail_get(vmail, &backend_mail) < 0)
418 return -1;
419
420 backend_headers = mailbox_header_lookup_init(backend_mail->box,
421 headers->name);
422 ret = mail_get_header_stream(backend_mail, backend_headers, stream_r);
423 mailbox_header_lookup_unref(&backend_headers);
424 if (ret < 0) {
425 virtual_box_copy_error(mail->box, backend_mail->box);
426 return -1;
427 }
428 return 0;
429 }
430
431 static int
virtual_mail_get_stream(struct mail * mail,bool get_body,struct message_size * hdr_size,struct message_size * body_size,struct istream ** stream_r)432 virtual_mail_get_stream(struct mail *mail, bool get_body,
433 struct message_size *hdr_size,
434 struct message_size *body_size,
435 struct istream **stream_r)
436 {
437 struct virtual_mail *vmail = (struct virtual_mail *)mail;
438 struct mail_private *vp = (struct mail_private *)mail;
439 struct mail *backend_mail;
440 const char *reason = t_strdup_printf("virtual mailbox %s: Opened mail UID=%u: %s",
441 mailbox_get_vname(mail->box), mail->uid, vp->get_stream_reason);
442 int ret;
443
444 if (backend_mail_get(vmail, &backend_mail) < 0)
445 return -1;
446
447 if (get_body) {
448 ret = mail_get_stream_because(backend_mail, hdr_size, body_size,
449 reason, stream_r);
450 } else {
451 ret = mail_get_hdr_stream_because(backend_mail, hdr_size,
452 reason, stream_r);
453 }
454
455 if (ret < 0) {
456 virtual_box_copy_error(mail->box, backend_mail->box);
457 return -1;
458 }
459 return 0;
460 }
461
462 static int
virtual_mail_get_binary_stream(struct mail * mail,const struct message_part * part,bool include_hdr,uoff_t * size_r,unsigned int * lines_r,bool * binary_r,struct istream ** stream_r)463 virtual_mail_get_binary_stream(struct mail *mail,
464 const struct message_part *part,
465 bool include_hdr, uoff_t *size_r,
466 unsigned int *lines_r, bool *binary_r,
467 struct istream **stream_r)
468 {
469 struct virtual_mail *vmail = (struct virtual_mail *)mail;
470 struct mail *backend_mail;
471
472 if (backend_mail_get(vmail, &backend_mail) < 0)
473 return -1;
474
475 struct mail_private *p = (struct mail_private *)backend_mail;
476 if (p->v.get_binary_stream(backend_mail, part, include_hdr,
477 size_r, lines_r, binary_r, stream_r) < 0) {
478 virtual_box_copy_error(mail->box, backend_mail->box);
479 return -1;
480 }
481 return 0;
482 }
483
484 static int
virtual_mail_get_special(struct mail * mail,enum mail_fetch_field field,const char ** value_r)485 virtual_mail_get_special(struct mail *mail, enum mail_fetch_field field,
486 const char **value_r)
487 {
488 struct virtual_mail *vmail = (struct virtual_mail *)mail;
489 struct mail *backend_mail;
490
491 if (backend_mail_get(vmail, &backend_mail) < 0)
492 return -1;
493 if (mail_get_special(backend_mail, field, value_r) < 0) {
494 virtual_box_copy_error(mail->box, backend_mail->box);
495 return -1;
496 }
497 return 0;
498 }
499
virtual_mail_get_backend_mail(struct mail * mail,struct mail ** real_mail_r)500 static int virtual_mail_get_backend_mail(struct mail *mail,
501 struct mail **real_mail_r)
502 {
503 struct virtual_mail *vmail = (struct virtual_mail *)mail;
504 struct mail *backend_mail;
505
506 if (backend_mail_get(vmail, &backend_mail) < 0)
507 return -1;
508
509 if (mail_get_backend_mail(backend_mail, real_mail_r) < 0)
510 return -1;
511 return 0;
512 }
513
virtual_mail_update_pop3_uidl(struct mail * mail,const char * uidl)514 static void virtual_mail_update_pop3_uidl(struct mail *mail, const char *uidl)
515 {
516 struct virtual_mail *vmail = (struct virtual_mail *)mail;
517 struct mail *backend_mail;
518
519 if (backend_mail_get(vmail, &backend_mail) < 0)
520 return;
521 mail_update_pop3_uidl(backend_mail, uidl);
522 }
523
virtual_mail_expunge(struct mail * mail)524 static void virtual_mail_expunge(struct mail *mail)
525 {
526 struct virtual_mail *vmail = (struct virtual_mail *)mail;
527 struct mail *backend_mail;
528
529 if (backend_mail_get(vmail, &backend_mail) < 0)
530 return;
531 mail_expunge(backend_mail);
532 }
533
534 static void
virtual_mail_set_cache_corrupted(struct mail * mail,enum mail_fetch_field field,const char * reason)535 virtual_mail_set_cache_corrupted(struct mail *mail,
536 enum mail_fetch_field field,
537 const char *reason)
538 {
539 struct virtual_mail *vmail = (struct virtual_mail *)mail;
540 struct mail *backend_mail;
541
542 if (backend_mail_get(vmail, &backend_mail) < 0)
543 return;
544 mail_set_cache_corrupted(backend_mail, field, reason);
545 }
546
547 struct mail_vfuncs virtual_mail_vfuncs = {
548 virtual_mail_close,
549 index_mail_free,
550 virtual_mail_set_seq,
551 virtual_mail_set_uid,
552 virtual_mail_set_uid_cache_updates,
553 virtual_mail_prefetch,
554 virtual_mail_precache,
555 virtual_mail_add_temp_wanted_fields,
556
557 index_mail_get_flags,
558 index_mail_get_keywords,
559 index_mail_get_keyword_indexes,
560 index_mail_get_modseq,
561 index_mail_get_pvt_modseq,
562 virtual_mail_get_parts,
563 virtual_mail_get_date,
564 virtual_mail_get_received_date,
565 virtual_mail_get_save_date,
566 virtual_mail_get_virtual_mail_size,
567 virtual_mail_get_physical_size,
568 virtual_mail_get_first_header,
569 virtual_mail_get_headers,
570 virtual_mail_get_header_stream,
571 virtual_mail_get_stream,
572 virtual_mail_get_binary_stream,
573 virtual_mail_get_special,
574 virtual_mail_get_backend_mail,
575 index_mail_update_flags,
576 index_mail_update_keywords,
577 index_mail_update_modseq,
578 index_mail_update_pvt_modseq,
579 virtual_mail_update_pop3_uidl,
580 virtual_mail_expunge,
581 virtual_mail_set_cache_corrupted,
582 NULL,
583 };
584