1 /* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
2 */
3
4 #include "lib.h"
5 #include "array.h"
6 #include "str.h"
7 #include "mempool.h"
8 #include "llist.h"
9 #include "istream-private.h"
10 #include "master-service.h"
11 #include "master-service-settings.h"
12 #include "message-parser.h"
13 #include "message-header-encode.h"
14 #include "message-header-decode.h"
15 #include "mail-user.h"
16 #include "mail-storage-private.h"
17 #include "index-mail.h"
18 #include "raw-storage.h"
19
20 #include "rfc2822.h"
21
22 #include "edit-mail.h"
23
24 /*
25 * Forward declarations
26 */
27
28 struct _header_field_index;
29 struct _header_field;
30 struct _header_index;
31 struct _header;
32
33 static struct mail_vfuncs edit_mail_vfuncs;
34
35 struct edit_mail_istream;
36 struct istream *edit_mail_istream_create(struct edit_mail *edmail);
37
38 static struct _header_index *edit_mail_header_clone
39 (struct edit_mail *edmail, struct _header *header);
40
41 /*
42 * Raw storage
43 */
44
45 static struct mail_user *edit_mail_user = NULL;
46 static unsigned int edit_mail_refcount = 0;
47
edit_mail_raw_storage_get(struct mail_user * mail_user)48 static struct mail_user *edit_mail_raw_storage_get(struct mail_user *mail_user)
49 {
50 if ( edit_mail_user == NULL ) {
51 void **sets = master_service_settings_get_others(master_service);
52
53 edit_mail_user = raw_storage_create_from_set(mail_user->set_info, sets[0]);
54 }
55
56 edit_mail_refcount++;
57
58 return edit_mail_user;
59 }
60
edit_mail_raw_storage_drop(void)61 static void edit_mail_raw_storage_drop(void)
62 {
63 i_assert(edit_mail_refcount > 0);
64
65 if ( --edit_mail_refcount != 0)
66 return;
67
68 mail_user_unref(&edit_mail_user);
69 edit_mail_user = NULL;
70 }
71
72 /*
73 * Headers
74 */
75
76 struct _header_field {
77 struct _header *header;
78
79 unsigned int refcount;
80
81 char *data;
82 size_t size;
83 size_t virtual_size;
84 uoff_t offset;
85 unsigned int lines;
86
87 uoff_t body_offset;
88
89 char *utf8_value;
90 };
91
92 struct _header_field_index {
93 struct _header_field_index *prev, *next;
94
95 struct _header_field *field;
96 struct _header_index *header;
97 };
98
99 struct _header {
100 unsigned int refcount;
101
102 char *name;
103 };
104
105 struct _header_index {
106 struct _header_index *prev, *next;
107
108 struct _header *header;
109
110 struct _header_field_index *first, *last;
111
112 unsigned int count;
113 };
114
_header_create(const char * name)115 static inline struct _header *_header_create(const char *name)
116 {
117 struct _header *header;
118
119 header = i_new(struct _header, 1);
120 header->name = i_strdup(name);
121 header->refcount = 1;
122
123 return header;
124 }
125
_header_ref(struct _header * header)126 static inline void _header_ref(struct _header *header)
127 {
128 header->refcount++;
129 }
130
_header_unref(struct _header * header)131 static inline void _header_unref(struct _header *header)
132 {
133 i_assert( header->refcount > 0 );
134 if ( --header->refcount != 0 )
135 return;
136
137 i_free(header->name);
138 i_free(header);
139 }
140
_header_field_create(struct _header * header)141 static inline struct _header_field *_header_field_create(struct _header *header)
142 {
143 struct _header_field *hfield;
144
145 hfield = i_new(struct _header_field, 1);
146 hfield->refcount = 1;
147 hfield->header = header;
148 if ( header != NULL )
149 _header_ref(header);
150
151 return hfield;
152 }
153
_header_field_ref(struct _header_field * hfield)154 static inline void _header_field_ref(struct _header_field *hfield)
155 {
156 hfield->refcount++;
157 }
158
_header_field_unref(struct _header_field * hfield)159 static inline void _header_field_unref(struct _header_field *hfield)
160 {
161 i_assert( hfield->refcount > 0 );
162 if ( --hfield->refcount != 0 )
163 return;
164
165 if ( hfield->header != NULL )
166 _header_unref(hfield->header);
167
168 if ( hfield->data != NULL )
169 i_free(hfield->data);
170 if ( hfield->utf8_value != NULL )
171 i_free(hfield->utf8_value);
172 i_free(hfield);
173 }
174
175 /*
176 * Edit mail object
177 */
178
179 struct edit_mail {
180 struct mail_private mail;
181 struct mail_private *wrapped;
182
183 struct edit_mail *parent;
184 unsigned int refcount;
185
186 struct istream *wrapped_stream;
187 struct istream *stream;
188
189 struct _header_index *headers_head, *headers_tail;
190 struct _header_field_index *header_fields_head, *header_fields_tail;
191 struct message_size hdr_size, body_size;
192
193 struct message_size wrapped_hdr_size, wrapped_body_size;
194
195 struct _header_field_index *header_fields_appended;
196 struct message_size appended_hdr_size;
197
198 bool modified:1;
199 bool snapshot_modified:1;
200 bool crlf:1;
201 bool eoh_crlf:1;
202 bool headers_parsed:1;
203 bool destroying_stream:1;
204 };
205
edit_mail_wrap(struct mail * mail)206 struct edit_mail *edit_mail_wrap(struct mail *mail)
207 {
208 struct mail_private *mailp = (struct mail_private *) mail;
209 struct edit_mail *edmail;
210 struct mail_user *raw_mail_user;
211 struct mailbox *raw_box = NULL;
212 struct mailbox_transaction_context *raw_trans;
213 struct message_size hdr_size, body_size;
214 struct istream *wrapped_stream;
215 uoff_t size_diff;
216 pool_t pool;
217
218 if ( mail_get_stream(mail, &hdr_size, &body_size, &wrapped_stream) < 0 ) {
219 return NULL;
220 }
221
222 /* Create dummy raw mailbox for our wrapper */
223
224 raw_mail_user = edit_mail_raw_storage_get(mail->box->storage->user);
225
226 if ( raw_mailbox_alloc_stream(raw_mail_user, wrapped_stream, (time_t)-1,
227 "editor@example.com", &raw_box) < 0 ) {
228 i_error("edit-mail: failed to open raw box: %s",
229 mailbox_get_last_internal_error(raw_box, NULL));
230 mailbox_free(&raw_box);
231 edit_mail_raw_storage_drop();
232 return NULL;
233 }
234
235 raw_trans = mailbox_transaction_begin(raw_box, 0, __func__);
236
237 /* Create the wrapper mail */
238
239 pool = pool_alloconly_create("edit_mail", 1024);
240 edmail = p_new(pool, struct edit_mail, 1);
241 edmail->refcount = 1;
242 edmail->mail.pool = pool;
243
244 edmail->wrapped = mailp;
245 edmail->wrapped_hdr_size = hdr_size;
246 edmail->wrapped_body_size = body_size;
247
248 edmail->wrapped_stream = wrapped_stream;
249 i_stream_ref(edmail->wrapped_stream);
250
251 /* Determine whether we should use CRLF or LF for the physical message */
252 size_diff = (hdr_size.virtual_size + body_size.virtual_size) -
253 (hdr_size.physical_size + body_size.physical_size);
254 if ( size_diff == 0 || size_diff <= (hdr_size.lines + body_size.lines)/2 )
255 edmail->crlf = edmail->eoh_crlf = TRUE;
256
257 array_create(&edmail->mail.module_contexts, pool, sizeof(void *), 5);
258
259 edmail->mail.v = edit_mail_vfuncs;
260 edmail->mail.mail.seq = 1;
261 edmail->mail.mail.box = raw_box;
262 edmail->mail.mail.transaction = raw_trans;
263 edmail->mail.wanted_fields = mailp->wanted_fields;
264 edmail->mail.wanted_headers = mailp->wanted_headers;
265
266 return edmail;
267 }
268
edit_mail_snapshot(struct edit_mail * edmail)269 struct edit_mail *edit_mail_snapshot(struct edit_mail *edmail)
270 {
271 struct _header_field_index *field_idx, *field_idx_new;
272 struct edit_mail *edmail_new;
273 pool_t pool;
274
275 if ( !edmail->snapshot_modified ) {
276 return edmail;
277 }
278
279 pool = pool_alloconly_create("edit_mail", 1024);
280 edmail_new = p_new(pool, struct edit_mail, 1);
281 edmail_new->refcount = 1;
282 edmail_new->mail.pool = pool;
283
284 edmail_new->wrapped = edmail->wrapped;
285 edmail_new->wrapped_hdr_size = edmail->wrapped_hdr_size;
286 edmail_new->wrapped_body_size = edmail->wrapped_body_size;
287 edmail_new->hdr_size = edmail->hdr_size;
288 edmail_new->body_size = edmail->body_size;
289 edmail_new->appended_hdr_size = edmail->appended_hdr_size;
290
291 edmail_new->wrapped_stream = edmail->wrapped_stream;
292 i_stream_ref(edmail_new->wrapped_stream);
293
294 edmail_new->crlf = edmail->crlf;
295 edmail_new->eoh_crlf = edmail->eoh_crlf;
296
297 array_create(&edmail_new->mail.module_contexts, pool, sizeof(void *), 5);
298
299 edmail_new->mail.v = edit_mail_vfuncs;
300 edmail_new->mail.mail.seq = 1;
301 edmail_new->mail.mail.box = edmail->mail.mail.box;
302 edmail_new->mail.mail.transaction = edmail->mail.mail.transaction;
303 edmail_new->mail.wanted_fields = edmail->mail.wanted_fields;
304 edmail_new->mail.wanted_headers = edmail->mail.wanted_headers;
305
306 edmail_new->stream = NULL;
307
308 if ( edmail->modified ) {
309 field_idx = edmail->header_fields_head;
310 while ( field_idx != NULL ) {
311 struct _header_field_index *next = field_idx->next;
312
313 field_idx_new = i_new(struct _header_field_index, 1);
314
315 field_idx_new->header =
316 edit_mail_header_clone(edmail_new, field_idx->header->header);
317
318 field_idx_new->field = field_idx->field;
319 _header_field_ref(field_idx_new->field);
320
321 DLLIST2_APPEND
322 (&edmail_new->header_fields_head, &edmail_new->header_fields_tail,
323 field_idx_new);
324
325 field_idx_new->header->count++;
326 if ( field_idx->header->first == field_idx )
327 field_idx_new->header->first = field_idx_new;
328 if ( field_idx->header->last == field_idx )
329 field_idx_new->header->last = field_idx_new;
330
331 if ( field_idx == edmail->header_fields_appended )
332 edmail_new->header_fields_appended = field_idx_new;
333
334 field_idx = next;
335 }
336
337 edmail_new->modified = TRUE;
338 }
339
340 edmail_new->headers_parsed = edmail->headers_parsed;
341
342 edmail_new->parent = edmail;
343 //edmail->refcount++;
344
345 return edmail_new;
346 }
347
edit_mail_reset(struct edit_mail * edmail)348 void edit_mail_reset(struct edit_mail *edmail)
349 {
350 struct _header_index *header_idx;
351 struct _header_field_index *field_idx;
352
353 i_stream_unref(&edmail->stream);
354
355 field_idx = edmail->header_fields_head;
356 while ( field_idx != NULL ) {
357 struct _header_field_index *next = field_idx->next;
358
359 _header_field_unref(field_idx->field);
360 i_free(field_idx);
361
362 field_idx = next;
363 }
364
365 header_idx = edmail->headers_head;
366 while ( header_idx != NULL ) {
367 struct _header_index *next = header_idx->next;
368
369 _header_unref(header_idx->header);
370 i_free(header_idx);
371
372 header_idx = next;
373 }
374
375 edmail->modified = FALSE;
376 }
377
edit_mail_unwrap(struct edit_mail ** edmail)378 void edit_mail_unwrap(struct edit_mail **edmail)
379 {
380 struct edit_mail *parent;
381
382 i_assert( (*edmail)->refcount > 0 );
383 if ( --(*edmail)->refcount != 0 )
384 return;
385
386 edit_mail_reset(*edmail);
387 i_stream_unref(&(*edmail)->wrapped_stream);
388
389 parent = (*edmail)->parent;
390
391 if ( parent == NULL ) {
392 mailbox_transaction_rollback(&(*edmail)->mail.mail.transaction);
393 mailbox_free(&(*edmail)->mail.mail.box);
394 edit_mail_raw_storage_drop();
395 }
396
397 pool_unref(&(*edmail)->mail.pool);
398 *edmail = NULL;
399
400 if ( parent != NULL )
401 edit_mail_unwrap(&parent);
402 }
403
edit_mail_get_mail(struct edit_mail * edmail)404 struct mail *edit_mail_get_mail(struct edit_mail *edmail)
405 {
406 /* Return wrapped mail when nothing is modified yet */
407 if ( !edmail->modified )
408 return &edmail->wrapped->mail;
409
410 return &edmail->mail.mail;
411 }
412
413 /*
414 * Editing
415 */
416
edit_mail_modify(struct edit_mail * edmail)417 static inline void edit_mail_modify(struct edit_mail *edmail)
418 {
419 edmail->mail.mail.seq++;
420 edmail->modified = TRUE;
421 edmail->snapshot_modified = TRUE;
422 }
423
424 /* Header modification */
425
_header_value_unfold(const char * value)426 static inline char *_header_value_unfold
427 (const char *value)
428 {
429 string_t *out;
430 unsigned int i;
431
432 for ( i = 0; value[i] != '\0'; i++ ) {
433 if (value[i] == '\r' || value[i] == '\n')
434 break;
435 }
436 if ( value[i] == '\0' ) {
437 return i_strdup(value);
438 }
439
440 out = t_str_new(i + strlen(value+i) + 10);
441 str_append_data(out, value, i);
442 for ( ; value[i] != '\0'; i++ ) {
443 if (value[i] == '\n') {
444 i++;
445 if (value[i] == '\0')
446 break;
447
448 switch ( value[i] ) {
449 case ' ':
450 str_append_c(out, ' ');
451 break;
452 case '\t':
453 default:
454 str_append_c(out, '\t');
455 }
456 } else {
457 if (value[i] != '\r')
458 str_append_c(out, value[i]);
459 }
460 }
461
462 return i_strndup(str_c(out), str_len(out));
463 }
464
edit_mail_header_find(struct edit_mail * edmail,const char * field_name)465 static struct _header_index *edit_mail_header_find
466 (struct edit_mail *edmail, const char *field_name)
467 {
468 struct _header_index *header_idx;
469
470 header_idx = edmail->headers_head;
471 while ( header_idx != NULL ) {
472 if ( strcasecmp(header_idx->header->name, field_name) == 0 )
473 return header_idx;
474
475 header_idx = header_idx->next;
476 }
477
478 return NULL;
479 }
480
edit_mail_header_create(struct edit_mail * edmail,const char * field_name)481 static struct _header_index *edit_mail_header_create
482 (struct edit_mail *edmail, const char *field_name)
483 {
484 struct _header_index *header_idx;
485
486 if ( (header_idx=edit_mail_header_find(edmail, field_name)) == NULL ) {
487 header_idx = i_new(struct _header_index, 1);
488 header_idx->header = _header_create(field_name);
489
490 DLLIST2_APPEND(&edmail->headers_head, &edmail->headers_tail, header_idx);
491 }
492
493 return header_idx;
494 }
495
edit_mail_header_clone(struct edit_mail * edmail,struct _header * header)496 static struct _header_index *edit_mail_header_clone
497 (struct edit_mail *edmail, struct _header *header)
498 {
499 struct _header_index *header_idx;
500
501 header_idx = edmail->headers_head;
502 while ( header_idx != NULL ) {
503 if ( header_idx->header == header )
504 return header_idx;
505
506 header_idx = header_idx->next;
507 }
508
509 header_idx = i_new(struct _header_index, 1);
510 header_idx->header = header;
511 _header_ref(header);
512 DLLIST2_APPEND(&edmail->headers_head, &edmail->headers_tail, header_idx);
513
514 return header_idx;
515 }
516
517 static struct _header_field_index *
edit_mail_header_field_create(struct edit_mail * edmail,const char * field_name,const char * value)518 edit_mail_header_field_create
519 (struct edit_mail *edmail, const char *field_name, const char *value)
520 {
521 struct _header_index *header_idx;
522 struct _header *header;
523 struct _header_field_index *field_idx;
524 struct _header_field *field;
525 unsigned int lines;
526
527 /* Get/create header index item */
528 header_idx = edit_mail_header_create(edmail, field_name);
529 header = header_idx->header;
530
531 /* Create new field index item */
532 field_idx = i_new(struct _header_field_index, 1);
533 field_idx->header = header_idx;
534 field_idx->field = field = _header_field_create(header);
535
536 /* Create header field data (folded if necessary) */
537 T_BEGIN {
538 string_t *enc_value, *data;
539
540 enc_value = t_str_new(strlen(field_name) + strlen(value) + 64);
541 data = t_str_new(strlen(field_name) + strlen(value) + 128);
542
543 message_header_encode(value, enc_value);
544
545 lines = rfc2822_header_append
546 (data, field_name, str_c(enc_value), edmail->crlf, &field->body_offset);
547
548 /* Copy to new field */
549 field->data = i_strndup(str_data(data), str_len(data));
550 field->size = str_len(data);
551 field->virtual_size = ( edmail->crlf ? field->size : field->size + lines );
552 field->lines = lines;
553 } T_END;
554
555 /* Record original (utf8) value */
556 field->utf8_value = _header_value_unfold(value);
557
558 return field_idx;
559 }
560
edit_mail_header_field_delete(struct edit_mail * edmail,struct _header_field_index * field_idx,bool update_index)561 static void edit_mail_header_field_delete
562 (struct edit_mail *edmail, struct _header_field_index *field_idx,
563 bool update_index)
564 {
565 struct _header_index *header_idx = field_idx->header;
566 struct _header_field *field = field_idx->field;
567
568 i_assert( header_idx != NULL );
569
570 edmail->hdr_size.physical_size -= field->size;
571 edmail->hdr_size.virtual_size -= field->virtual_size;
572 edmail->hdr_size.lines -= field->lines;
573
574 header_idx->count--;
575 if ( update_index ) {
576 if ( header_idx->count == 0 ) {
577 DLLIST2_REMOVE(&edmail->headers_head, &edmail->headers_tail, header_idx);
578 _header_unref(header_idx->header);
579 i_free(header_idx);
580 } else if ( header_idx->first == field_idx ) {
581 struct _header_field_index *hfield = header_idx->first->next;
582
583 while ( hfield != NULL && hfield->header != header_idx ) {
584 hfield = hfield->next;
585 }
586
587 i_assert( hfield != NULL );
588 header_idx->first = hfield;
589 } else if ( header_idx->last == field_idx ) {
590 struct _header_field_index *hfield = header_idx->last->prev;
591
592 while ( hfield != NULL && hfield->header != header_idx ) {
593 hfield = hfield->prev;
594 }
595
596 i_assert( hfield != NULL );
597 header_idx->last = hfield;
598 }
599 }
600
601 DLLIST2_REMOVE
602 (&edmail->header_fields_head, &edmail->header_fields_tail, field_idx);
603 _header_field_unref(field_idx->field);
604 i_free(field_idx);
605 }
606
607 static struct _header_field_index *
edit_mail_header_field_replace(struct edit_mail * edmail,struct _header_field_index * field_idx,const char * newname,const char * newvalue,bool update_index)608 edit_mail_header_field_replace
609 (struct edit_mail *edmail, struct _header_field_index *field_idx,
610 const char *newname, const char *newvalue, bool update_index)
611 {
612 struct _header_field_index *field_idx_new;
613 struct _header_index *header_idx = field_idx->header, *header_idx_new;
614 struct _header_field *field = field_idx->field, *field_new;
615
616 i_assert( header_idx != NULL );
617 i_assert( newname != NULL || newvalue != NULL );
618
619 if ( newname == NULL )
620 newname = header_idx->header->name;
621 if ( newvalue == NULL )
622 newvalue = field_idx->field->utf8_value;
623 field_idx_new = edit_mail_header_field_create
624 (edmail, newname, newvalue);
625 field_new = field_idx_new->field;
626 header_idx_new = field_idx_new->header;
627
628 edmail->hdr_size.physical_size -= field->size;
629 edmail->hdr_size.virtual_size -= field->virtual_size;
630 edmail->hdr_size.lines -= field->lines;
631
632 edmail->hdr_size.physical_size += field_new->size;
633 edmail->hdr_size.virtual_size += field_new->virtual_size;
634 edmail->hdr_size.lines += field_new->lines;
635
636 /* Replace header field index */
637 field_idx_new->prev = field_idx->prev;
638 field_idx_new->next = field_idx->next;
639 if ( field_idx->prev != NULL )
640 field_idx->prev->next = field_idx_new;
641 if ( field_idx->next != NULL )
642 field_idx->next->prev = field_idx_new;
643 if (edmail->header_fields_head == field_idx)
644 edmail->header_fields_head = field_idx_new;
645 if (edmail->header_fields_tail == field_idx)
646 edmail->header_fields_tail = field_idx_new;
647
648 if ( header_idx_new == header_idx ) {
649 if (header_idx->first == field_idx)
650 header_idx->first = field_idx_new;
651 if (header_idx->last == field_idx)
652 header_idx->last = field_idx_new;
653 } else {
654 header_idx->count--;
655 header_idx_new->count++;
656
657 if ( update_index ) {
658 if ( header_idx->count == 0 ) {
659 DLLIST2_REMOVE(&edmail->headers_head, &edmail->headers_tail, header_idx);
660 _header_unref(header_idx->header);
661 i_free(header_idx);
662 } else if ( header_idx->first == field_idx ) {
663 struct _header_field_index *hfield = header_idx->first->next;
664
665 while ( hfield != NULL && hfield->header != header_idx ) {
666 hfield = hfield->next;
667 }
668
669 i_assert( hfield != NULL );
670 header_idx->first = hfield;
671 } else if ( header_idx->last == field_idx ) {
672 struct _header_field_index *hfield = header_idx->last->prev;
673
674 while ( hfield != NULL && hfield->header != header_idx ) {
675 hfield = hfield->prev;
676 }
677
678 i_assert( hfield != NULL );
679 header_idx->last = hfield;
680 }
681 if ( header_idx_new->count > 0 ) {
682 struct _header_field_index *hfield;
683
684 hfield = edmail->header_fields_head;
685 while ( hfield != NULL && hfield->header != header_idx_new ) {
686 hfield = hfield->next;
687 }
688
689 i_assert( hfield != NULL );
690 header_idx_new->first = hfield;
691
692 hfield = edmail->header_fields_tail;
693 while ( hfield != NULL && hfield->header != header_idx_new ) {
694 hfield = hfield->prev;
695 }
696
697 i_assert( hfield != NULL );
698 header_idx_new->last = hfield;
699 }
700 }
701 }
702
703 _header_field_unref(field_idx->field);
704 i_free(field_idx);
705 return field_idx_new;
706 }
707
_header_decode(const unsigned char * hdr_data,size_t hdr_data_len)708 static inline char *_header_decode
709 (const unsigned char *hdr_data, size_t hdr_data_len)
710 {
711 string_t *str = t_str_new(512);
712
713 /* hdr_data is already unfolded */
714
715 /* Decode MIME encoded-words. */
716 message_header_decode_utf8
717 ((const unsigned char *)hdr_data, hdr_data_len, str, NULL);
718 return i_strdup(str_c(str));
719 }
720
edit_mail_headers_parse(struct edit_mail * edmail)721 static int edit_mail_headers_parse
722 (struct edit_mail *edmail)
723 {
724 struct message_header_parser_ctx *hparser;
725 enum message_header_parser_flags hparser_flags =
726 MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
727 MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
728 struct message_header_line *hdr;
729 struct _header_index *header_idx;
730 struct _header_field_index *head = NULL, *tail = NULL, *current;
731 string_t *hdr_data;
732 uoff_t offset = 0, body_offset = 0, vsize_diff = 0;
733 unsigned int lines = 0;
734 int ret;
735
736 if ( edmail->headers_parsed ) return 1;
737
738 i_stream_seek(edmail->wrapped_stream, 0);
739 hparser = message_parse_header_init
740 (edmail->wrapped_stream, NULL, hparser_flags);
741
742 T_BEGIN {
743 hdr_data = t_str_new(1024);
744 while ( (ret=message_parse_header_next(hparser, &hdr)) > 0 ) {
745 struct _header_field_index *field_idx_new;
746 struct _header_field *field;
747
748 if ( hdr->eoh ) {
749 /* Record whether header ends in CRLF or LF */
750 edmail->eoh_crlf = hdr->crlf_newline;
751 }
752
753 if ( hdr == NULL || hdr->eoh ) break;
754
755 /* We deny the existence of any `Content-Length:' header. This header is
756 * non-standard and it can wreak havok when the message is modified.
757 */
758 if ( strcasecmp(hdr->name, "Content-Length" ) == 0 )
759 continue;
760
761 if ( hdr->continued ) {
762 /* Continued line of folded header */
763 buffer_append(hdr_data, hdr->value, hdr->value_len);
764 } else {
765 /* First line of header */
766 offset = hdr->name_offset;
767 body_offset = hdr->name_len + hdr->middle_len;
768 str_truncate(hdr_data, 0);
769 buffer_append(hdr_data, hdr->name, hdr->name_len);
770 buffer_append(hdr_data, hdr->middle, hdr->middle_len);
771 buffer_append(hdr_data, hdr->value, hdr->value_len);
772 lines = 0;
773 vsize_diff = 0;
774 }
775
776 if ( !hdr->no_newline ) {
777 lines++;
778
779 if ( hdr->crlf_newline ) {
780 buffer_append(hdr_data, "\r\n", 2);
781 } else {
782 buffer_append(hdr_data, "\n", 1);
783 vsize_diff++;
784 }
785 }
786
787 if ( hdr->continues ) {
788 hdr->use_full_value = TRUE;
789 continue;
790 }
791
792 /* Create new header field index entry */
793
794 field_idx_new = i_new(struct _header_field_index, 1);
795
796 header_idx = edit_mail_header_create(edmail, hdr->name);
797 header_idx->count++;
798 field_idx_new->header = header_idx;
799 field_idx_new->field = field = _header_field_create(header_idx->header);
800
801 i_assert( body_offset > 0 );
802 field->body_offset = body_offset;
803
804 field->utf8_value = _header_decode(hdr->full_value, hdr->full_value_len);
805
806 field->size = str_len(hdr_data);
807 field->virtual_size = field->size + vsize_diff;
808 field->data = i_strndup(str_data(hdr_data), field->size);
809 field->offset = offset;
810 field->lines = lines;
811
812 DLLIST2_APPEND(&head, &tail, field_idx_new);
813
814 edmail->hdr_size.physical_size += field->size;
815 edmail->hdr_size.virtual_size += field->virtual_size;
816 edmail->hdr_size.lines += lines;
817 }
818 } T_END;
819
820 message_parse_header_deinit(&hparser);
821
822 /* blocking i/o required */
823 i_assert( ret != 0 );
824
825 if ( ret < 0 && edmail->wrapped_stream->stream_errno != 0 ) {
826 /* Error; clean up */
827 i_error("read(%s) failed: %s",
828 i_stream_get_name(edmail->wrapped_stream),
829 i_stream_get_error(edmail->wrapped_stream));
830 current = head;
831 while ( current != NULL ) {
832 struct _header_field_index *next = current->next;
833
834 _header_field_unref(current->field);
835 i_free(current);
836
837 current = next;
838 }
839
840 return ret;
841 }
842
843 /* Insert header field index items in main list */
844 if ( head != NULL && tail != NULL ) {
845 if ( edmail->header_fields_appended != NULL ) {
846 if ( edmail->header_fields_head != edmail->header_fields_appended ) {
847 edmail->header_fields_appended->prev->next = head;
848 head->prev = edmail->header_fields_appended->prev;
849 } else {
850 edmail->header_fields_head = head;
851 }
852
853 tail->next = edmail->header_fields_appended;
854 edmail->header_fields_appended->prev = tail;
855 } else if ( edmail->header_fields_tail != NULL ) {
856 edmail->header_fields_tail->next = head;
857 head->prev = edmail->header_fields_tail;
858 edmail->header_fields_tail = tail;
859 } else {
860 edmail->header_fields_head = head;
861 edmail->header_fields_tail = tail;
862 }
863 }
864
865 /* Rebuild header index */
866 current = edmail->header_fields_head;
867 while ( current != NULL ) {
868 if ( current->header->first == NULL )
869 current->header->first = current;
870 current->header->last = current;
871
872 current = current->next;
873 }
874
875 /* Clear appended headers */
876 edmail->header_fields_appended = NULL;
877 edmail->appended_hdr_size.physical_size = 0;
878 edmail->appended_hdr_size.virtual_size = 0;
879 edmail->appended_hdr_size.lines = 0;
880
881 /* Do not parse headers again */
882 edmail->headers_parsed = TRUE;
883
884 return 1;
885 }
886
edit_mail_header_add(struct edit_mail * edmail,const char * field_name,const char * value,bool last)887 void edit_mail_header_add
888 (struct edit_mail *edmail, const char *field_name, const char *value,
889 bool last)
890 {
891 struct _header_index *header_idx;
892 struct _header_field_index *field_idx;
893 struct _header_field *field;
894
895 edit_mail_modify(edmail);
896
897 field_idx = edit_mail_header_field_create(edmail, field_name, value);
898 header_idx = field_idx->header;
899 field = field_idx->field;
900
901 /* Add it to the header field index */
902 if ( last ) {
903 DLLIST2_APPEND
904 (&edmail->header_fields_head, &edmail->header_fields_tail, field_idx);
905
906 header_idx->last = field_idx;
907 if ( header_idx->first == NULL )
908 header_idx->first = field_idx;
909
910 if ( !edmail->headers_parsed ) {
911 if ( edmail->header_fields_appended == NULL ) {
912 /* Record beginning of appended headers */
913 edmail->header_fields_appended = field_idx;
914 }
915
916 edmail->appended_hdr_size.physical_size += field->size;
917 edmail->appended_hdr_size.virtual_size += field->virtual_size;
918 edmail->appended_hdr_size.lines += field->lines;
919 }
920 } else {
921 DLLIST2_PREPEND
922 (&edmail->header_fields_head, &edmail->header_fields_tail, field_idx);
923
924 header_idx->first = field_idx;
925 if ( header_idx->last == NULL )
926 header_idx->last = field_idx;
927 }
928
929 header_idx->count++;
930
931 edmail->hdr_size.physical_size += field->size;
932 edmail->hdr_size.virtual_size += field->virtual_size;
933 edmail->hdr_size.lines += field->lines;
934 }
935
edit_mail_header_delete(struct edit_mail * edmail,const char * field_name,int index)936 int edit_mail_header_delete
937 (struct edit_mail *edmail, const char *field_name, int index)
938 {
939 struct _header_index *header_idx;
940 struct _header_field_index *field_idx;
941 int pos = 0;
942 int ret = 0;
943
944 /* Make sure headers are parsed */
945 if ( edit_mail_headers_parse(edmail) <= 0 )
946 return -1;
947
948 /* Find the header entry */
949 if ( (header_idx=edit_mail_header_find(edmail, field_name)) == NULL ) {
950 /* Not found */
951 return 0;
952 }
953
954 /* Signal modification */
955 edit_mail_modify(edmail);
956
957 /* Iterate through all header fields and remove those that match */
958 field_idx = ( index >= 0 ? header_idx->first : header_idx->last );
959 while ( field_idx != NULL ) {
960 struct _header_field_index *next =
961 ( index >= 0 ? field_idx->next : field_idx->prev );
962
963 if ( field_idx->field->header == header_idx->header ) {
964 bool final;
965
966 if ( index >= 0 ) {
967 pos++;
968 final = ( header_idx->last == field_idx );
969 } else {
970 pos--;
971 final = ( header_idx->first == field_idx );
972 }
973
974 if ( index == 0 || index == pos ) {
975 if ( header_idx->first == field_idx ) header_idx->first = NULL;
976 if ( header_idx->last == field_idx ) header_idx->last = NULL;
977 edit_mail_header_field_delete(edmail, field_idx, FALSE);
978 ret++;
979 }
980
981 if ( final || (index != 0 && index == pos) )
982 break;
983 }
984
985 field_idx = next;
986 }
987
988 if ( index == 0 || header_idx->count == 0 ) {
989 DLLIST2_REMOVE(&edmail->headers_head, &edmail->headers_tail, header_idx);
990 _header_unref(header_idx->header);
991 i_free(header_idx);
992 } else if ( header_idx->first == NULL || header_idx->last == NULL ) {
993 struct _header_field_index *current = edmail->header_fields_head;
994
995 while ( current != NULL ) {
996 if ( current->header == header_idx ) {
997 if ( header_idx->first == NULL )
998 header_idx->first = current;
999 header_idx->last = current;
1000 }
1001 current = current->next;
1002 }
1003 }
1004
1005 return ret;
1006 }
1007
edit_mail_header_replace(struct edit_mail * edmail,const char * field_name,int index,const char * newname,const char * newvalue)1008 int edit_mail_header_replace
1009 (struct edit_mail *edmail, const char *field_name, int index,
1010 const char *newname, const char *newvalue)
1011 {
1012 struct _header_index *header_idx, *header_idx_new;
1013 struct _header_field_index *field_idx, *field_idx_new;
1014 int pos = 0;
1015 int ret = 0;
1016
1017 /* Make sure headers are parsed */
1018 if ( edit_mail_headers_parse(edmail) <= 0 )
1019 return -1;
1020
1021 /* Find the header entry */
1022 if ( (header_idx=edit_mail_header_find(edmail, field_name)) == NULL ) {
1023 /* Not found */
1024 return 0;
1025 }
1026
1027 /* Signal modification */
1028 edit_mail_modify(edmail);
1029
1030 /* Iterate through all header fields and replace those that match */
1031 field_idx = ( index >= 0 ? header_idx->first : header_idx->last );
1032 field_idx_new = NULL;
1033 while ( field_idx != NULL ) {
1034 struct _header_field_index *next =
1035 ( index >= 0 ? field_idx->next : field_idx->prev );
1036
1037 if ( field_idx->field->header == header_idx->header ) {
1038 bool final;
1039
1040 if ( index >= 0 ) {
1041 pos++;
1042 final = ( header_idx->last == field_idx );
1043 } else {
1044 pos--;
1045 final = ( header_idx->first == field_idx );
1046 }
1047
1048 if ( index == 0 || index == pos ) {
1049 if ( header_idx->first == field_idx ) header_idx->first = NULL;
1050 if ( header_idx->last == field_idx ) header_idx->last = NULL;
1051 field_idx_new = edit_mail_header_field_replace
1052 (edmail, field_idx, newname, newvalue, FALSE);
1053 ret++;
1054 }
1055
1056 if ( final || (index != 0 && index == pos) )
1057 break;
1058 }
1059
1060 field_idx = next;
1061 }
1062
1063 /* Update old header index */
1064 if ( header_idx->count == 0 ) {
1065 DLLIST2_REMOVE(&edmail->headers_head, &edmail->headers_tail, header_idx);
1066 _header_unref(header_idx->header);
1067 i_free(header_idx);
1068 } else if ( header_idx->first == NULL || header_idx->last == NULL ) {
1069 struct _header_field_index *current = edmail->header_fields_head;
1070
1071 while ( current != NULL ) {
1072 if ( current->header == header_idx ) {
1073 if ( header_idx->first == NULL )
1074 header_idx->first = current;
1075 header_idx->last = current;
1076 }
1077 current = current->next;
1078 }
1079 }
1080
1081 /* Update new header index */
1082 if ( field_idx_new != NULL ) {
1083 struct _header_field_index *current = edmail->header_fields_head;
1084
1085 header_idx_new = field_idx_new->header;
1086 while ( current != NULL ) {
1087 if ( current->header == header_idx_new ) {
1088 if ( header_idx_new->first == NULL )
1089 header_idx_new->first = current;
1090 header_idx_new->last = current;
1091 }
1092 current = current->next;
1093 }
1094 }
1095
1096 return ret;
1097 }
1098
1099 struct edit_mail_header_iter
1100 {
1101 struct edit_mail *mail;
1102 struct _header_index *header;
1103 struct _header_field_index *current;
1104
1105 bool reverse:1;
1106 };
1107
edit_mail_headers_iterate_init(struct edit_mail * edmail,const char * field_name,bool reverse,struct edit_mail_header_iter ** edhiter_r)1108 int edit_mail_headers_iterate_init
1109 (struct edit_mail *edmail, const char *field_name, bool reverse,
1110 struct edit_mail_header_iter **edhiter_r)
1111 {
1112 struct edit_mail_header_iter *edhiter;
1113 struct _header_index *header_idx = NULL;
1114 struct _header_field_index *current = NULL;
1115
1116 /* Make sure headers are parsed */
1117 if ( edit_mail_headers_parse(edmail) <= 0 ) {
1118 /* Failure */
1119 return -1;
1120 }
1121
1122 header_idx = edit_mail_header_find(edmail, field_name);
1123
1124 if ( field_name != NULL && header_idx == NULL ) {
1125 current = NULL;
1126 } else if ( !reverse ) {
1127 current =
1128 ( header_idx != NULL ? header_idx->first : edmail->header_fields_head );
1129 } else {
1130 current =
1131 ( header_idx != NULL ? header_idx->last : edmail->header_fields_tail );
1132 if ( current->header == NULL )
1133 current = current->prev;
1134 }
1135
1136 if ( current == NULL )
1137 return 0;
1138
1139 edhiter = i_new(struct edit_mail_header_iter, 1);
1140 edhiter->mail = edmail;
1141 edhiter->header = header_idx;
1142 edhiter->reverse = reverse;
1143 edhiter->current = current;
1144
1145 *edhiter_r = edhiter;
1146 return 1;
1147 }
1148
edit_mail_headers_iterate_deinit(struct edit_mail_header_iter ** edhiter)1149 void edit_mail_headers_iterate_deinit
1150 (struct edit_mail_header_iter **edhiter)
1151 {
1152 i_free(*edhiter);
1153 *edhiter = NULL;
1154 }
1155
edit_mail_headers_iterate_get(struct edit_mail_header_iter * edhiter,const char ** value_r)1156 void edit_mail_headers_iterate_get
1157 (struct edit_mail_header_iter *edhiter, const char **value_r)
1158 {
1159 const char *raw;
1160 int i;
1161
1162 i_assert( edhiter->current != NULL && edhiter->current->header != NULL);
1163
1164 raw = edhiter->current->field->utf8_value;
1165 for ( i = strlen(raw)-1; i >= 0; i-- ) {
1166 if ( raw[i] != ' ' && raw[i] != '\t' ) break;
1167 }
1168
1169 *value_r = t_strndup(raw, i+1);
1170 }
1171
edit_mail_headers_iterate_next(struct edit_mail_header_iter * edhiter)1172 bool edit_mail_headers_iterate_next
1173 (struct edit_mail_header_iter *edhiter)
1174 {
1175 if ( edhiter->current == NULL )
1176 return FALSE;
1177
1178 do {
1179 edhiter->current =
1180 ( !edhiter->reverse ? edhiter->current->next : edhiter->current->prev );
1181 } while ( edhiter->current != NULL && edhiter->current->header != NULL &&
1182 edhiter->header != NULL && edhiter->current->header != edhiter->header );
1183
1184 return ( edhiter->current != NULL && edhiter->current->header != NULL);
1185 }
1186
edit_mail_headers_iterate_remove(struct edit_mail_header_iter * edhiter)1187 bool edit_mail_headers_iterate_remove
1188 (struct edit_mail_header_iter *edhiter)
1189 {
1190 struct _header_field_index *field_idx;
1191 bool next;
1192
1193 i_assert( edhiter->current != NULL && edhiter->current->header != NULL);
1194
1195 edit_mail_modify(edhiter->mail);
1196
1197 field_idx = edhiter->current;
1198 next = edit_mail_headers_iterate_next(edhiter);
1199 edit_mail_header_field_delete(edhiter->mail, field_idx, TRUE);
1200
1201 return next;
1202 }
1203
edit_mail_headers_iterate_replace(struct edit_mail_header_iter * edhiter,const char * newname,const char * newvalue)1204 bool edit_mail_headers_iterate_replace
1205 (struct edit_mail_header_iter *edhiter,
1206 const char *newname, const char *newvalue)
1207 {
1208 struct _header_field_index *field_idx;
1209 bool next;
1210
1211 i_assert( edhiter->current != NULL && edhiter->current->header != NULL);
1212
1213 edit_mail_modify(edhiter->mail);
1214
1215 field_idx = edhiter->current;
1216 next = edit_mail_headers_iterate_next(edhiter);
1217 edit_mail_header_field_replace
1218 (edhiter->mail, field_idx, newname, newvalue, TRUE);
1219
1220 return next;
1221 }
1222
1223 /* Body modification */
1224
1225 // FIXME: implement
1226
1227 /*
1228 * Mail API
1229 */
1230
edit_mail_close(struct mail * mail)1231 static void edit_mail_close(struct mail *mail)
1232 {
1233 struct edit_mail *edmail = (struct edit_mail *)mail;
1234
1235 edmail->wrapped->v.close(&edmail->wrapped->mail);
1236 }
1237
edit_mail_free(struct mail * mail)1238 static void edit_mail_free(struct mail *mail)
1239 {
1240 struct edit_mail *edmail = (struct edit_mail *)mail;
1241
1242 edmail->wrapped->v.free(&edmail->wrapped->mail);
1243
1244 edit_mail_unwrap(&edmail);
1245 }
1246
edit_mail_set_seq(struct mail * mail ATTR_UNUSED,uint32_t seq ATTR_UNUSED,bool saving ATTR_UNUSED)1247 static void edit_mail_set_seq
1248 (struct mail *mail ATTR_UNUSED, uint32_t seq ATTR_UNUSED,
1249 bool saving ATTR_UNUSED)
1250 {
1251 i_panic("edit_mail_set_seq() not implemented");
1252 }
1253
edit_mail_set_uid(struct mail * mail ATTR_UNUSED,uint32_t uid ATTR_UNUSED)1254 static bool ATTR_NORETURN edit_mail_set_uid
1255 (struct mail *mail ATTR_UNUSED, uint32_t uid ATTR_UNUSED)
1256 {
1257 i_panic("edit_mail_set_uid() not implemented");
1258 }
1259
edit_mail_set_uid_cache_updates(struct mail * mail,bool set)1260 static void edit_mail_set_uid_cache_updates(struct mail *mail, bool set)
1261 {
1262 struct edit_mail *edmail = (struct edit_mail *)mail;
1263
1264 edmail->wrapped->v.set_uid_cache_updates(&edmail->wrapped->mail, set);
1265 }
1266
edit_mail_add_temp_wanted_fields(struct mail * mail ATTR_UNUSED,enum mail_fetch_field fields ATTR_UNUSED,struct mailbox_header_lookup_ctx * headers ATTR_UNUSED)1267 static void edit_mail_add_temp_wanted_fields
1268 (struct mail *mail ATTR_UNUSED, enum mail_fetch_field fields ATTR_UNUSED,
1269 struct mailbox_header_lookup_ctx *headers ATTR_UNUSED)
1270 {
1271 /* Nothing */
1272 }
1273
edit_mail_get_flags(struct mail * mail)1274 static enum mail_flags edit_mail_get_flags(struct mail *mail)
1275 {
1276 struct edit_mail *edmail = (struct edit_mail *)mail;
1277
1278 return edmail->wrapped->v.get_flags(&edmail->wrapped->mail);
1279 }
1280
edit_mail_get_keywords(struct mail * mail)1281 static const char *const *edit_mail_get_keywords(struct mail *mail)
1282 {
1283 struct edit_mail *edmail = (struct edit_mail *)mail;
1284
1285 return edmail->wrapped->v.get_keywords(&edmail->wrapped->mail);
1286 }
1287
ARRAY_TYPE(keyword_indexes)1288 static const ARRAY_TYPE(keyword_indexes) *edit_mail_get_keyword_indexes
1289 (struct mail *mail)
1290 {
1291 struct edit_mail *edmail = (struct edit_mail *)mail;
1292
1293 return edmail->wrapped->v.get_keyword_indexes(&edmail->wrapped->mail);
1294 }
1295
edit_mail_get_modseq(struct mail * mail)1296 static uint64_t edit_mail_get_modseq(struct mail *mail)
1297 {
1298 struct edit_mail *edmail = (struct edit_mail *)mail;
1299
1300 return edmail->wrapped->v.get_modseq(&edmail->wrapped->mail);
1301 }
1302
edit_mail_get_pvt_modseq(struct mail * mail)1303 static uint64_t edit_mail_get_pvt_modseq(struct mail *mail)
1304 {
1305 struct edit_mail *edmail = (struct edit_mail *)mail;
1306
1307 return edmail->wrapped->v.get_pvt_modseq(&edmail->wrapped->mail);
1308 }
1309
edit_mail_get_parts(struct mail * mail,struct message_part ** parts_r)1310 static int edit_mail_get_parts
1311 (struct mail *mail, struct message_part **parts_r)
1312 {
1313 struct edit_mail *edmail = (struct edit_mail *)mail;
1314
1315 return edmail->wrapped->v.get_parts(&edmail->wrapped->mail, parts_r);
1316 }
1317
edit_mail_get_date(struct mail * mail,time_t * date_r,int * timezone_r)1318 static int edit_mail_get_date
1319 (struct mail *mail, time_t *date_r, int *timezone_r)
1320 {
1321 struct edit_mail *edmail = (struct edit_mail *)mail;
1322
1323 return edmail->wrapped->v.get_date(&edmail->wrapped->mail, date_r, timezone_r);
1324 }
1325
edit_mail_get_received_date(struct mail * mail,time_t * date_r)1326 static int edit_mail_get_received_date(struct mail *mail, time_t *date_r)
1327 {
1328 struct edit_mail *edmail = (struct edit_mail *)mail;
1329
1330 return edmail->wrapped->v.get_received_date(&edmail->wrapped->mail, date_r);
1331 }
1332
edit_mail_get_save_date(struct mail * mail,time_t * date_r)1333 static int edit_mail_get_save_date(struct mail *mail, time_t *date_r)
1334 {
1335 struct edit_mail *edmail = (struct edit_mail *)mail;
1336
1337 return edmail->wrapped->v.get_save_date(&edmail->wrapped->mail, date_r);
1338 }
1339
edit_mail_get_virtual_size(struct mail * mail,uoff_t * size_r)1340 static int edit_mail_get_virtual_size(struct mail *mail, uoff_t *size_r)
1341 {
1342 struct edit_mail *edmail = (struct edit_mail *)mail;
1343
1344 if ( !edmail->headers_parsed ) {
1345 *size_r = edmail->wrapped_hdr_size.virtual_size +
1346 edmail->wrapped_body_size.virtual_size;
1347
1348 if ( !edmail->modified )
1349 return 0;
1350 } else {
1351 *size_r = edmail->wrapped_body_size.virtual_size + 2;
1352 }
1353
1354 *size_r += edmail->hdr_size.virtual_size + edmail->body_size.virtual_size;
1355 return 0;
1356 }
1357
edit_mail_get_physical_size(struct mail * mail,uoff_t * size_r)1358 static int edit_mail_get_physical_size(struct mail *mail, uoff_t *size_r)
1359 {
1360 struct edit_mail *edmail = (struct edit_mail *)mail;
1361
1362 *size_r = 0;
1363 if ( !edmail->headers_parsed ) {
1364 *size_r = edmail->wrapped_hdr_size.physical_size +
1365 edmail->wrapped_body_size.physical_size;
1366
1367 if ( !edmail->modified )
1368 return 0;
1369 } else {
1370 *size_r = edmail->wrapped_body_size.physical_size +
1371 ( edmail->eoh_crlf ? 2 : 1 );
1372 }
1373
1374 *size_r += edmail->hdr_size.physical_size + edmail->body_size.physical_size;
1375 return 0;
1376 }
1377
edit_mail_get_first_header(struct mail * mail,const char * field_name,bool decode_to_utf8,const char ** value_r)1378 static int edit_mail_get_first_header
1379 (struct mail *mail, const char *field_name, bool decode_to_utf8,
1380 const char **value_r)
1381 {
1382 struct edit_mail *edmail = (struct edit_mail *)mail;
1383 struct _header_index *header_idx;
1384 struct _header_field *field;
1385 int ret;
1386
1387 /* Check whether mail headers were modified at all */
1388 if ( !edmail->modified || edmail->headers_head == NULL ) {
1389 /* Unmodified */
1390 return edmail->wrapped->v.get_first_header
1391 (&edmail->wrapped->mail, field_name, decode_to_utf8, value_r);
1392 }
1393
1394 /* Try to find modified header */
1395 if ( (header_idx=edit_mail_header_find(edmail, field_name)) == NULL ||
1396 header_idx->count == 0 ) {
1397
1398 if ( !edmail->headers_parsed ) {
1399 /* No new header */
1400 return edmail->wrapped->v.get_first_header
1401 (&edmail->wrapped->mail, field_name, decode_to_utf8, value_r);
1402 }
1403
1404 *value_r = NULL;
1405 return 0;
1406 }
1407
1408 /* Get the first occurrence */
1409 if ( edmail->header_fields_appended == NULL ) {
1410 /* There are no appended headers, so first is found directly */
1411 field = header_idx->first->field;
1412 } else {
1413 struct _header_field_index *field_idx;
1414
1415 /* Scan prepended headers */
1416 field_idx = edmail->header_fields_head;
1417 while ( field_idx != NULL ) {
1418 if ( field_idx->header == header_idx )
1419 break;
1420
1421 if ( field_idx == edmail->header_fields_appended ) {
1422 field_idx = NULL;
1423 break;
1424 }
1425 field_idx = field_idx->next;
1426 }
1427
1428 if ( field_idx == NULL ) {
1429 /* Check original message */
1430 if ( (ret=edmail->wrapped->v.get_first_header
1431 (&edmail->wrapped->mail, field_name, decode_to_utf8, value_r)) != 0 )
1432 return ret;
1433
1434 /* Use first (apparently appended) header */
1435 field = header_idx->first->field;
1436 } else {
1437 field = field_idx->field;
1438 }
1439 }
1440
1441 if ( decode_to_utf8 )
1442 *value_r = field->utf8_value;
1443 else
1444 *value_r = (const char *) (field->data + field->body_offset);
1445 return 1;
1446 }
1447
edit_mail_get_headers(struct mail * mail,const char * field_name,bool decode_to_utf8,const char * const ** value_r)1448 static int edit_mail_get_headers
1449 (struct mail *mail, const char *field_name, bool decode_to_utf8,
1450 const char *const **value_r)
1451 {
1452 struct edit_mail *edmail = (struct edit_mail *)mail;
1453 struct _header_index *header_idx;
1454 struct _header_field_index *field_idx;
1455 const char *const *headers;
1456 ARRAY(const char *) header_values;
1457
1458 if ( !edmail->modified || edmail->headers_head == NULL ) {
1459 /* Unmodified */
1460 return edmail->wrapped->v.get_headers
1461 (&edmail->wrapped->mail, field_name, decode_to_utf8, value_r);
1462 }
1463
1464 if ( (header_idx=edit_mail_header_find(edmail, field_name)) == NULL ||
1465 header_idx->count == 0 ) {
1466 if ( !edmail->headers_parsed ) {
1467 /* No new header */
1468 return edmail->wrapped->v.get_headers
1469 (&edmail->wrapped->mail, field_name, decode_to_utf8, value_r);
1470 }
1471
1472 p_array_init(&header_values, edmail->mail.pool, 1);
1473 (void)array_append_space(&header_values);
1474 *value_r = array_idx(&header_values, 0);
1475 return 0;
1476 }
1477
1478 /* Merge */
1479
1480 /* Read original headers too if message headers are not parsed */
1481 headers = NULL;
1482 if ( !edmail->headers_parsed && edmail->wrapped->v.get_headers
1483 (&edmail->wrapped->mail, field_name, decode_to_utf8, &headers) < 0 ) {
1484 return -1;
1485 }
1486
1487 /* Fill result array */
1488 p_array_init(&header_values, edmail->mail.pool, 32);
1489 field_idx = header_idx->first;
1490 while ( field_idx != NULL ) {
1491
1492 /* If current field is the first appended one, we need to add original
1493 * headers first.
1494 */
1495 if ( field_idx == edmail->header_fields_appended && headers != NULL ) {
1496 while ( *headers != NULL ) {
1497 array_append(&header_values, headers, 1);
1498
1499 headers++;
1500 }
1501 }
1502
1503 /* Add modified header to the list */
1504 if ( field_idx->field->header == header_idx->header ) {
1505 struct _header_field *field = field_idx->field;
1506
1507 const char *value;
1508 if ( decode_to_utf8 )
1509 value = field->utf8_value;
1510 else
1511 value = (const char *)(field->data + field->body_offset);
1512
1513 array_append(&header_values, &value, 1);
1514
1515 if ( field_idx == header_idx->last )
1516 break;
1517 }
1518
1519 field_idx = field_idx->next;
1520 }
1521
1522 /* Add original headers if necessary */
1523 if ( headers != NULL ) {
1524 while ( *headers != NULL ) {
1525 array_append(&header_values, headers, 1);
1526
1527 headers++;
1528 }
1529 }
1530
1531 (void)array_append_space(&header_values);
1532 *value_r = array_idx(&header_values, 0);
1533 return 1;
1534 }
1535
edit_mail_get_header_stream(struct mail * mail ATTR_UNUSED,struct mailbox_header_lookup_ctx * headers ATTR_UNUSED,struct istream ** stream_r ATTR_UNUSED)1536 static int ATTR_NORETURN edit_mail_get_header_stream
1537 (struct mail *mail ATTR_UNUSED,
1538 struct mailbox_header_lookup_ctx *headers ATTR_UNUSED,
1539 struct istream **stream_r ATTR_UNUSED)
1540 {
1541 // FIXME: implement!
1542 i_panic("edit_mail_get_header_stream() not implemented");
1543 }
1544
edit_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)1545 static int edit_mail_get_stream
1546 (struct mail *mail, bool get_body ATTR_UNUSED, struct message_size *hdr_size,
1547 struct message_size *body_size, struct istream **stream_r)
1548 {
1549 struct edit_mail *edmail = (struct edit_mail *)mail;
1550
1551 if ( edmail->stream == NULL ) {
1552 edmail->stream = edit_mail_istream_create(edmail);
1553 }
1554
1555 if ( hdr_size != NULL ) {
1556 *hdr_size = edmail->wrapped_hdr_size;
1557 hdr_size->physical_size += edmail->hdr_size.physical_size;
1558 hdr_size->virtual_size += edmail->hdr_size.virtual_size;
1559 hdr_size->lines += edmail->hdr_size.lines;
1560 }
1561
1562 if ( body_size != NULL ) {
1563 *body_size = edmail->wrapped_body_size;
1564 }
1565
1566 *stream_r = edmail->stream;
1567 i_stream_seek(edmail->stream, 0);
1568
1569 return 0;
1570 }
1571
edit_mail_get_special(struct mail * mail,enum mail_fetch_field field,const char ** value_r)1572 static int edit_mail_get_special
1573 (struct mail *mail, enum mail_fetch_field field, const char **value_r)
1574 {
1575 struct edit_mail *edmail = (struct edit_mail *)mail;
1576
1577 if ( edmail->modified ) {
1578 /* Block certain fields when modified */
1579
1580 switch (field) {
1581 case MAIL_FETCH_GUID:
1582 /* This is in essence a new message */
1583 *value_r = "";
1584 return 0;
1585 case MAIL_FETCH_STORAGE_ID:
1586 /* Prevent hardlink copying */
1587 *value_r = "";
1588 return 0;
1589 default:
1590 break;
1591 }
1592 }
1593
1594 return edmail->wrapped->v.get_special(&edmail->wrapped->mail, field, value_r);
1595 }
1596
1597 static int
edit_mail_get_backend_mail(struct mail * mail,struct mail ** real_mail_r)1598 edit_mail_get_backend_mail(struct mail *mail, struct mail **real_mail_r)
1599 {
1600 struct edit_mail *edmail = (struct edit_mail *)mail;
1601
1602 *real_mail_r = edit_mail_get_mail(edmail);
1603 return 0;
1604 }
1605
edit_mail_update_flags(struct mail * mail,enum modify_type modify_type,enum mail_flags flags)1606 static void edit_mail_update_flags
1607 (struct mail *mail, enum modify_type modify_type, enum mail_flags flags)
1608 {
1609 struct edit_mail *edmail = (struct edit_mail *)mail;
1610
1611 edmail->wrapped->v.update_flags(&edmail->wrapped->mail, modify_type, flags);
1612 }
1613
edit_mail_update_keywords(struct mail * mail,enum modify_type modify_type,struct mail_keywords * keywords)1614 static void edit_mail_update_keywords
1615 (struct mail *mail, enum modify_type modify_type,
1616 struct mail_keywords *keywords)
1617 {
1618 struct edit_mail *edmail = (struct edit_mail *)mail;
1619
1620 edmail->wrapped->v.update_keywords
1621 (&edmail->wrapped->mail, modify_type, keywords);
1622 }
1623
edit_mail_update_modseq(struct mail * mail,uint64_t min_modseq)1624 static void edit_mail_update_modseq(struct mail *mail, uint64_t min_modseq)
1625 {
1626 struct edit_mail *edmail = (struct edit_mail *)mail;
1627
1628 edmail->wrapped->v.update_modseq(&edmail->wrapped->mail, min_modseq);
1629 }
1630
edit_mail_update_pvt_modseq(struct mail * mail,uint64_t min_pvt_modseq)1631 static void edit_mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq)
1632 {
1633 struct edit_mail *edmail = (struct edit_mail *)mail;
1634
1635 edmail->wrapped->v.update_pvt_modseq(&edmail->wrapped->mail, min_pvt_modseq);
1636 }
1637
edit_mail_update_pop3_uidl(struct mail * mail,const char * uidl)1638 static void edit_mail_update_pop3_uidl(struct mail *mail, const char *uidl)
1639 {
1640 struct edit_mail *edmail = (struct edit_mail *)mail;
1641
1642 if ( edmail->wrapped->v.update_pop3_uidl != NULL )
1643 edmail->wrapped->v.update_pop3_uidl(&edmail->wrapped->mail, uidl);
1644 }
1645
edit_mail_expunge(struct mail * mail ATTR_UNUSED)1646 static void edit_mail_expunge(struct mail *mail ATTR_UNUSED)
1647 {
1648 /* NOOP */
1649 }
1650
edit_mail_set_cache_corrupted(struct mail * mail,enum mail_fetch_field field,const char * reason)1651 static void edit_mail_set_cache_corrupted
1652 (struct mail *mail, enum mail_fetch_field field,
1653 const char *reason)
1654 {
1655 struct edit_mail *edmail = (struct edit_mail *)mail;
1656
1657 edmail->wrapped->v.set_cache_corrupted
1658 (&edmail->wrapped->mail, field, reason);
1659 }
1660
1661 static struct mail_vfuncs edit_mail_vfuncs = {
1662 edit_mail_close,
1663 edit_mail_free,
1664 edit_mail_set_seq,
1665 edit_mail_set_uid,
1666 edit_mail_set_uid_cache_updates,
1667 NULL,
1668 NULL,
1669 edit_mail_add_temp_wanted_fields,
1670 edit_mail_get_flags,
1671 edit_mail_get_keywords,
1672 edit_mail_get_keyword_indexes,
1673 edit_mail_get_modseq,
1674 edit_mail_get_pvt_modseq,
1675 edit_mail_get_parts,
1676 edit_mail_get_date,
1677 edit_mail_get_received_date,
1678 edit_mail_get_save_date,
1679 edit_mail_get_virtual_size,
1680 edit_mail_get_physical_size,
1681 edit_mail_get_first_header,
1682 edit_mail_get_headers,
1683 edit_mail_get_header_stream,
1684 edit_mail_get_stream,
1685 index_mail_get_binary_stream,
1686 edit_mail_get_special,
1687 edit_mail_get_backend_mail,
1688 edit_mail_update_flags,
1689 edit_mail_update_keywords,
1690 edit_mail_update_modseq,
1691 edit_mail_update_pvt_modseq,
1692 edit_mail_update_pop3_uidl,
1693 edit_mail_expunge,
1694 edit_mail_set_cache_corrupted,
1695 NULL,
1696 };
1697
1698 /*
1699 * Edit Mail Stream
1700 */
1701
1702 struct edit_mail_istream {
1703 struct istream_private istream;
1704 pool_t pool;
1705
1706 struct edit_mail *mail;
1707
1708 struct _header_field_index *cur_header;
1709 uoff_t cur_header_v_offset;
1710
1711 bool parent_buffer:1;
1712 bool header_read:1;
1713 bool eof:1;
1714 };
1715
edit_mail_istream_destroy(struct iostream_private * stream)1716 static void edit_mail_istream_destroy(struct iostream_private *stream)
1717 {
1718 struct edit_mail_istream *edstream =
1719 (struct edit_mail_istream *)stream;
1720
1721 i_stream_unref(&edstream->istream.parent);
1722 i_stream_free_buffer(&edstream->istream);
1723 pool_unref(&edstream->pool);
1724 }
1725
merge_from_parent(struct edit_mail_istream * edstream,uoff_t parent_v_offset,uoff_t parent_end_v_offset,uoff_t copy_v_offset)1726 static ssize_t merge_from_parent
1727 (struct edit_mail_istream *edstream, uoff_t parent_v_offset,
1728 uoff_t parent_end_v_offset, uoff_t copy_v_offset)
1729 {
1730 struct istream_private *stream = &edstream->istream;
1731 uoff_t v_offset, append_v_offset;
1732 const unsigned char *data;
1733 size_t pos, cur_pos, parent_bytes_left;
1734 bool parent_buffer = edstream->parent_buffer;
1735 ssize_t ret;
1736
1737 i_assert(parent_v_offset <= parent_end_v_offset);
1738 edstream->parent_buffer = FALSE;
1739
1740 v_offset = stream->istream.v_offset;
1741 if (v_offset >= copy_v_offset) {
1742 i_assert((v_offset - copy_v_offset) <= parent_end_v_offset);
1743 if ((v_offset - copy_v_offset) == parent_end_v_offset) {
1744 /* Parent data is all read */
1745 return 0;
1746 }
1747 }
1748
1749 /* Determine where we are appending more data to the stream */
1750 append_v_offset = v_offset + (stream->pos - stream->skip);
1751
1752 if (v_offset >= copy_v_offset) {
1753 /* Parent buffer used */
1754 cur_pos = (stream->pos - stream->skip);
1755 parent_v_offset += (v_offset - copy_v_offset);
1756 } else {
1757 cur_pos = 0;
1758 i_assert(append_v_offset >= copy_v_offset);
1759 parent_v_offset += (append_v_offset - copy_v_offset);
1760 }
1761
1762 /* Seek parent to required position */
1763 i_stream_seek(stream->parent, parent_v_offset);
1764
1765 /* Read from parent */
1766 data = i_stream_get_data(stream->parent, &pos);
1767 if (pos > cur_pos)
1768 ret = 0;
1769 else do {
1770 /* Use normal read here, since parent data can be returned directly
1771 to caller. */
1772 ret = i_stream_read(stream->parent);
1773
1774 stream->istream.stream_errno = stream->parent->stream_errno;
1775 stream->istream.eof = stream->parent->eof;
1776 edstream->eof = stream->parent->eof;
1777 data = i_stream_get_data(stream->parent, &pos);
1778 /* check again, in case the parent stream had been seeked
1779 backwards and the previous read() didn't get us far
1780 enough. */
1781 } while (pos <= cur_pos && ret > 0);
1782
1783 /* Don't read beyond parent end offset */
1784 if (parent_end_v_offset != (uoff_t)-1) {
1785 parent_bytes_left = (size_t)(parent_end_v_offset - parent_v_offset);
1786 if (pos >= parent_bytes_left) {
1787 pos = parent_bytes_left;
1788 }
1789 }
1790
1791 if (v_offset < copy_v_offset || ret == -2 ||
1792 (parent_buffer && (append_v_offset + 1) >= parent_end_v_offset)) {
1793 /* Merging with our local buffer; copying data from parent */
1794 if (pos > 0) {
1795 size_t avail;
1796
1797 if (parent_buffer) {
1798 stream->pos = stream->skip = 0;
1799 stream->buffer = NULL;
1800 }
1801 if (!i_stream_try_alloc(stream, pos, &avail))
1802 return -2;
1803 pos = (pos > avail ? avail : pos);
1804
1805 memcpy(stream->w_buffer + stream->pos, data, pos);
1806 stream->pos += pos;
1807 stream->buffer = stream->w_buffer;
1808
1809 if (cur_pos >= pos)
1810 ret = 0;
1811 else
1812 ret = (ssize_t)(pos - cur_pos);
1813 } else {
1814 ret = (ret == 0 ? 0 : -1);
1815 }
1816 } else {
1817 /* Just passing buffers from parent; no copying */
1818 ret = (pos > cur_pos ? (ssize_t)(pos - cur_pos) :
1819 (ret == 0 ? 0 : -1));
1820 stream->buffer = data;
1821 stream->pos = pos;
1822 stream->skip = 0;
1823 edstream->parent_buffer = TRUE;
1824 }
1825
1826 i_assert(ret != -1 || stream->istream.eof ||
1827 stream->istream.stream_errno != 0);
1828 return ret;
1829 }
1830
merge_modified_headers(struct edit_mail_istream * edstream)1831 static ssize_t merge_modified_headers(struct edit_mail_istream *edstream)
1832 {
1833 struct istream_private *stream = &edstream->istream;
1834 struct edit_mail *edmail = edstream->mail;
1835 uoff_t v_offset = stream->istream.v_offset, append_v_offset;
1836 size_t appended, written, avail, size;
1837
1838 if (edstream->cur_header == NULL) {
1839 /* No (more) headers */
1840 return 0;
1841 }
1842
1843 /* Caller must already have committed remaining parent data to
1844 our stream buffer. */
1845 i_assert(!edstream->parent_buffer);
1846
1847 /* Add modified headers to buffer */
1848 written = 0;
1849 while ( edstream->cur_header != NULL) {
1850 size_t wsize;
1851
1852 /* Determine what part of the header was already buffered */
1853 append_v_offset = v_offset + (stream->pos - stream->skip);
1854 i_assert(append_v_offset >= edstream->cur_header_v_offset);
1855 if (append_v_offset >= edstream->cur_header_v_offset)
1856 appended = (size_t)(append_v_offset - edstream->cur_header_v_offset);
1857 else
1858 appended = 0;
1859 i_assert(appended <= edstream->cur_header->field->size);
1860
1861 /* Determine how much we want to write */
1862 size = edstream->cur_header->field->size - appended;
1863 if (size > 0) {
1864 /* Determine how much we can write */
1865 if (!i_stream_try_alloc(stream, size, &avail))
1866 return -2;
1867 wsize = (size >= avail ? avail : size);
1868
1869 /* Write (part of) the header to buffer */
1870 memcpy(stream->w_buffer + stream->pos,
1871 edstream->cur_header->field->data + appended, wsize);
1872 stream->pos += wsize;
1873 stream->buffer = stream->w_buffer;
1874 written += wsize;
1875
1876 if (wsize < size) {
1877 /* Could not write whole header; finish here */
1878 break;
1879 }
1880 }
1881
1882 /* Skip to next header */
1883 edstream->cur_header_v_offset +=
1884 edstream->cur_header->field->size;
1885 edstream->cur_header = edstream->cur_header->next;
1886
1887 /* Stop at end of prepended headers if original header is left unparsed */
1888 if ( !edmail->headers_parsed
1889 && edstream->cur_header == edmail->header_fields_appended )
1890 edstream->cur_header = NULL;
1891 }
1892
1893 if (edstream->cur_header == NULL) {
1894 /* Clear offset too, just to be tidy */
1895 edstream->cur_header_v_offset = 0;
1896 }
1897
1898 i_assert(written > 0);
1899 return (ssize_t)written;
1900 }
1901
edit_mail_istream_read(struct istream_private * stream)1902 static ssize_t edit_mail_istream_read(struct istream_private *stream)
1903 {
1904 struct edit_mail_istream *edstream =
1905 (struct edit_mail_istream *)stream;
1906 struct edit_mail *edmail = edstream->mail;
1907 uoff_t v_offset, append_v_offset;
1908 uoff_t parent_v_offset, parent_end_v_offset, copy_v_offset;
1909 uoff_t prep_hdr_size, hdr_size;
1910 ssize_t ret = 0;
1911
1912 if (edstream->eof) {
1913 stream->istream.eof = TRUE;
1914 return -1;
1915 }
1916
1917 if (edstream->parent_buffer && stream->skip == stream->pos) {
1918 edstream->parent_buffer = FALSE;
1919 stream->pos = stream->skip = 0;
1920 stream->buffer = NULL;
1921 }
1922
1923 /* Merge prepended headers */
1924 if (!edstream->parent_buffer) {
1925 if ( (ret=merge_modified_headers(edstream)) != 0 )
1926 return ret;
1927 }
1928 v_offset = stream->istream.v_offset;
1929 append_v_offset = v_offset + (stream->pos - stream->skip);
1930
1931 if ( !edmail->headers_parsed &&
1932 edmail->header_fields_appended != NULL &&
1933 !edstream->header_read) {
1934 /* Output headers from original stream */
1935
1936 /* Size of the prepended header */
1937 i_assert(edmail->hdr_size.physical_size >=
1938 edmail->appended_hdr_size.physical_size);
1939 prep_hdr_size = edmail->hdr_size.physical_size -
1940 edmail->appended_hdr_size.physical_size;
1941
1942 /* Offset of header end or appended header
1943 * Any final CR is dealt with later
1944 */
1945 hdr_size = prep_hdr_size + edmail->wrapped_hdr_size.physical_size;
1946 i_assert(hdr_size > 0);
1947 if ( append_v_offset <= hdr_size - 1 &&
1948 edmail->wrapped_hdr_size.physical_size > 0) {
1949
1950 parent_v_offset = stream->parent_start_offset;
1951 parent_end_v_offset = stream->parent_start_offset +
1952 edmail->wrapped_hdr_size.physical_size - 1;
1953 copy_v_offset = prep_hdr_size;
1954
1955 if ( (ret=merge_from_parent(edstream, parent_v_offset,
1956 parent_end_v_offset, copy_v_offset)) < 0 )
1957 return ret;
1958 append_v_offset = v_offset + (stream->pos - stream->skip);
1959 i_assert(append_v_offset <= hdr_size - 1);
1960
1961 if ( append_v_offset == hdr_size - 1 ) {
1962 /* Strip final CR too when it is present */
1963 if ( stream->buffer != NULL &&
1964 stream->buffer[stream->pos-1] == '\r' ) {
1965 stream->pos--;
1966 append_v_offset--;
1967 ret--;
1968 }
1969
1970 i_assert(ret >= 0);
1971 edstream->cur_header = edmail->header_fields_appended;
1972 edstream->cur_header_v_offset = append_v_offset;
1973 if (!edstream->parent_buffer)
1974 edstream->header_read = TRUE;
1975 }
1976
1977 if (ret != 0)
1978 return ret;
1979 } else {
1980 edstream->header_read = TRUE;
1981 }
1982
1983 /* Merge appended headers */
1984 if ( (ret=merge_modified_headers(edstream)) != 0 )
1985 return ret;
1986 }
1987
1988 /* Header does not come from original mail at all */
1989 if ( edmail->headers_parsed ) {
1990 parent_v_offset = stream->parent_start_offset +
1991 edmail->wrapped_hdr_size.physical_size - ( edmail->eoh_crlf ? 2 : 1);
1992 copy_v_offset = edmail->hdr_size.physical_size;
1993
1994 /* Header comes partially from original mail and headers are added between
1995 header and body.
1996 */
1997 } else if (edmail->header_fields_appended != NULL) {
1998 parent_v_offset = stream->parent_start_offset +
1999 edmail->wrapped_hdr_size.physical_size - ( edmail->eoh_crlf ? 2 : 1);
2000 copy_v_offset = edmail->hdr_size.physical_size +
2001 edmail->wrapped_hdr_size.physical_size - ( edmail->eoh_crlf ? 2 : 1);
2002
2003 /* Header comes partially from original mail, but headers are only prepended.
2004 */
2005 } else {
2006 parent_v_offset = stream->parent_start_offset;
2007 copy_v_offset = edmail->hdr_size.physical_size;
2008 }
2009
2010 return merge_from_parent(edstream,
2011 parent_v_offset, (uoff_t)-1, copy_v_offset);
2012 }
2013
2014 static void
stream_reset_to(struct edit_mail_istream * edstream,uoff_t v_offset)2015 stream_reset_to(struct edit_mail_istream *edstream, uoff_t v_offset)
2016 {
2017 edstream->istream.istream.v_offset = v_offset;
2018 edstream->istream.skip = 0;
2019 edstream->istream.pos = 0;
2020 edstream->istream.buffer = NULL;
2021 edstream->parent_buffer = FALSE;
2022 edstream->eof = FALSE;
2023 i_stream_seek(edstream->istream.parent, 0);
2024 }
2025
edit_mail_istream_seek(struct istream_private * stream,uoff_t v_offset,bool mark ATTR_UNUSED)2026 static void edit_mail_istream_seek
2027 (struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED)
2028 {
2029 struct edit_mail_istream *edstream =
2030 (struct edit_mail_istream *)stream;
2031 struct _header_field_index *cur_header;
2032 struct edit_mail *edmail = edstream->mail;
2033 uoff_t offset;
2034
2035 edstream->header_read = FALSE;
2036 edstream->cur_header = NULL;
2037 edstream->cur_header_v_offset = 0;
2038
2039 /* The beginning */
2040 if ( v_offset == 0 ) {
2041 stream_reset_to(edstream, 0);
2042
2043 if ( edmail->header_fields_head != edmail->header_fields_appended )
2044 edstream->cur_header = edmail->header_fields_head;
2045 return;
2046 }
2047
2048 /* Inside (prepended) headers */
2049 if ( edmail->headers_parsed ) {
2050 offset = edmail->hdr_size.physical_size;
2051 } else {
2052 offset = edmail->hdr_size.physical_size -
2053 edmail->appended_hdr_size.physical_size;
2054 }
2055
2056 if ( v_offset < offset ) {
2057 stream_reset_to(edstream, v_offset);
2058
2059 /* Find the header */
2060 cur_header = edmail->header_fields_head;
2061 i_assert( cur_header != NULL &&
2062 cur_header != edmail->header_fields_appended );
2063 edstream->cur_header_v_offset = 0;
2064 offset = cur_header->field->size;
2065 while ( v_offset > offset ) {
2066 cur_header = cur_header->next;
2067 i_assert( cur_header != NULL &&
2068 cur_header != edmail->header_fields_appended );
2069
2070 edstream->cur_header_v_offset = offset;
2071 offset += cur_header->field->size;
2072 }
2073
2074 edstream->cur_header = cur_header;
2075 return;
2076 }
2077
2078 if ( !edmail->headers_parsed ) {
2079 /* Inside original header */
2080 offset = edmail->hdr_size.physical_size -
2081 edmail->appended_hdr_size.physical_size +
2082 edmail->wrapped_hdr_size.physical_size;
2083 if ( v_offset < offset ) {
2084 stream_reset_to(edstream, v_offset);
2085 return;
2086 }
2087
2088 edstream->header_read = TRUE;
2089
2090 /* Inside appended header */
2091 offset = edmail->hdr_size.physical_size +
2092 edmail->wrapped_hdr_size.physical_size;
2093 if ( v_offset < offset ) {
2094 stream_reset_to(edstream, v_offset);
2095
2096 offset -= edmail->appended_hdr_size.physical_size;
2097
2098 cur_header = edmail->header_fields_appended;
2099 i_assert( cur_header != NULL );
2100 edstream->cur_header_v_offset = offset;
2101 offset += cur_header->field->size;
2102
2103 while ( v_offset > offset ) {
2104 cur_header = cur_header->next;
2105 i_assert( cur_header != NULL );
2106
2107 edstream->cur_header_v_offset = offset;
2108 offset += cur_header->field->size;
2109 }
2110
2111 edstream->cur_header = cur_header;
2112 return;
2113 }
2114 }
2115
2116 stream_reset_to(edstream, v_offset);
2117 edstream->cur_header = NULL;
2118 }
2119
2120 static void ATTR_NORETURN
edit_mail_istream_sync(struct istream_private * stream ATTR_UNUSED)2121 edit_mail_istream_sync(struct istream_private *stream ATTR_UNUSED)
2122 {
2123 i_panic("edit-mail istream sync() not implemented");
2124 }
2125
2126 static int
edit_mail_istream_stat(struct istream_private * stream,bool exact)2127 edit_mail_istream_stat(struct istream_private *stream, bool exact)
2128 {
2129 struct edit_mail_istream *edstream =
2130 (struct edit_mail_istream *)stream;
2131 struct edit_mail *edmail = edstream->mail;
2132 const struct stat *st;
2133
2134 /* Stat the original stream */
2135 if (i_stream_stat(stream->parent, exact, &st) < 0)
2136 return -1;
2137
2138 stream->statbuf = *st;
2139 if (st->st_size == -1 || !exact)
2140 return 0;
2141
2142 if ( !edmail->headers_parsed ) {
2143 if ( !edmail->modified )
2144 return 0;
2145 } else {
2146 stream->statbuf.st_size = edmail->wrapped_body_size.physical_size +
2147 ( edmail->eoh_crlf ? 2 : 1 );
2148 }
2149
2150 stream->statbuf.st_size += edmail->hdr_size.physical_size +
2151 edmail->body_size.physical_size;
2152 return 0;
2153 }
2154
edit_mail_istream_create(struct edit_mail * edmail)2155 struct istream *edit_mail_istream_create
2156 (struct edit_mail *edmail)
2157 {
2158 struct edit_mail_istream *edstream;
2159 struct istream *wrapped = edmail->wrapped_stream;
2160
2161 edstream = i_new(struct edit_mail_istream, 1);
2162 edstream->pool = pool_alloconly_create(MEMPOOL_GROWING
2163 "edit mail stream", 4096);
2164 edstream->mail = edmail;
2165
2166 edstream->istream.max_buffer_size = wrapped->real_stream->max_buffer_size;
2167
2168 edstream->istream.iostream.destroy = edit_mail_istream_destroy;
2169 edstream->istream.read = edit_mail_istream_read;
2170 edstream->istream.seek = edit_mail_istream_seek;
2171 edstream->istream.sync = edit_mail_istream_sync;
2172 edstream->istream.stat = edit_mail_istream_stat;
2173
2174 edstream->istream.istream.readable_fd = FALSE;
2175 edstream->istream.istream.blocking = wrapped->blocking;
2176 edstream->istream.istream.seekable = wrapped->seekable;
2177
2178 if ( edmail->header_fields_head != edmail->header_fields_appended )
2179 edstream->cur_header = edmail->header_fields_head;
2180
2181 i_stream_seek(wrapped, 0);
2182
2183 return i_stream_create(&edstream->istream, wrapped, -1, 0);
2184 }
2185
2186