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