Coverage Report

Created: 2026-09-14 07:03

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