Coverage Report

Created: 2026-08-08 06:43

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->data = i_strndup(str_data(data), str_len(data));
556
0
    field->size = str_len(data);
557
0
    field->virtual_size = (edmail->crlf ?
558
0
               field->size : field->size + lines);
559
0
    field->lines = lines;
560
0
  } T_END;
561
562
  /* Record original (utf8) value */
563
0
  field->utf8_value = _header_value_unfold(value);
564
565
0
  return field_idx;
566
0
}
567
568
static void
569
edit_mail_header_field_delete(struct edit_mail *edmail,
570
            struct _header_field_index *field_idx,
571
            bool update_index)
572
0
{
573
0
  struct _header_index *header_idx = field_idx->header;
574
0
  struct _header_field *field = field_idx->field;
575
576
0
  i_assert(header_idx != NULL);
577
578
0
  edmail->hdr_size.physical_size -= field->size;
579
0
  edmail->hdr_size.virtual_size -= field->virtual_size;
580
0
  edmail->hdr_size.lines -= field->lines;
581
582
0
  header_idx->count--;
583
0
  if (update_index) {
584
0
    if (header_idx->count == 0) {
585
0
      DLLIST2_REMOVE(&edmail->headers_head,
586
0
               &edmail->headers_tail, header_idx);
587
0
      _header_unref(header_idx->header);
588
0
      i_free(header_idx);
589
0
    } else if (header_idx->first == field_idx) {
590
0
      struct _header_field_index *hfield =
591
0
        header_idx->first->next;
592
593
0
      while (hfield != NULL && hfield->header != header_idx)
594
0
        hfield = hfield->next;
595
596
0
      i_assert(hfield != NULL);
597
0
      header_idx->first = hfield;
598
0
    } else if (header_idx->last == field_idx) {
599
0
      struct _header_field_index *hfield =
600
0
        header_idx->last->prev;
601
602
0
      while (hfield != NULL && hfield->header != header_idx)
603
0
        hfield = hfield->prev;
604
605
0
      i_assert(hfield != NULL);
606
0
      header_idx->last = hfield;
607
0
    }
608
0
  }
609
610
0
  DLLIST2_REMOVE(&edmail->header_fields_head, &edmail->header_fields_tail,
611
0
           field_idx);
612
0
  _header_field_unref(field_idx->field);
613
0
  i_free(field_idx);
614
0
}
615
616
static struct _header_field_index *
617
edit_mail_header_field_replace(struct edit_mail *edmail,
618
             struct _header_field_index *field_idx,
619
             const char *newname, const char *newvalue,
620
             bool update_index)
621
0
{
622
0
  struct _header_field_index *field_idx_new;
623
0
  struct _header_index *header_idx = field_idx->header, *header_idx_new;
624
0
  struct _header_field *field = field_idx->field, *field_new;
625
626
0
  i_assert(header_idx != NULL);
627
0
  i_assert(newname != NULL || newvalue != NULL);
628
629
0
  if (newname == NULL)
630
0
    newname = header_idx->header->name;
631
0
  if (newvalue == NULL)
632
0
    newvalue = field_idx->field->utf8_value;
633
0
  field_idx_new = edit_mail_header_field_create(
634
0
    edmail, newname, newvalue);
635
0
  field_new = field_idx_new->field;
636
0
  header_idx_new = field_idx_new->header;
637
638
0
  edmail->hdr_size.physical_size -= field->size;
639
0
  edmail->hdr_size.virtual_size -= field->virtual_size;
640
0
  edmail->hdr_size.lines -= field->lines;
641
642
0
  edmail->hdr_size.physical_size += field_new->size;
643
0
  edmail->hdr_size.virtual_size += field_new->virtual_size;
644
0
  edmail->hdr_size.lines += field_new->lines;
645
646
  /* Replace header field index */
647
0
  field_idx_new->prev = field_idx->prev;
648
0
  field_idx_new->next = field_idx->next;
649
0
  if (field_idx->prev != NULL)
650
0
    field_idx->prev->next = field_idx_new;
651
0
  if (field_idx->next != NULL)
652
0
    field_idx->next->prev = field_idx_new;
653
0
  if (edmail->header_fields_head == field_idx)
654
0
    edmail->header_fields_head = field_idx_new;
655
0
  if (edmail->header_fields_tail == field_idx)
656
0
    edmail->header_fields_tail = field_idx_new;
657
658
0
  if (header_idx_new == header_idx) {
659
0
    if (header_idx->first == field_idx)
660
0
      header_idx->first = field_idx_new;
661
0
    if (header_idx->last == field_idx)
662
0
      header_idx->last = field_idx_new;
663
0
  } else {
664
0
    header_idx->count--;
665
0
    header_idx_new->count++;
666
667
0
    if (update_index) {
668
0
      if (header_idx->count == 0) {
669
0
        DLLIST2_REMOVE(&edmail->headers_head,
670
0
                 &edmail->headers_tail,
671
0
                 header_idx);
672
0
        _header_unref(header_idx->header);
673
0
        i_free(header_idx);
674
0
      } else if (header_idx->first == field_idx) {
675
0
        struct _header_field_index *hfield =
676
0
          header_idx->first->next;
677
678
0
        while (hfield != NULL &&
679
0
               hfield->header != header_idx)
680
0
          hfield = hfield->next;
681
682
0
        i_assert(hfield != NULL);
683
0
        header_idx->first = hfield;
684
0
      } else if (header_idx->last == field_idx) {
685
0
        struct _header_field_index *hfield =
686
0
          header_idx->last->prev;
687
688
0
        while (hfield != NULL &&
689
0
               hfield->header != header_idx)
690
0
          hfield = hfield->prev;
691
692
0
        i_assert(hfield != NULL);
693
0
        header_idx->last = hfield;
694
0
      }
695
0
      if (header_idx_new->count > 0) {
696
0
        struct _header_field_index *hfield;
697
698
0
        hfield = edmail->header_fields_head;
699
0
        while (hfield != NULL &&
700
0
               hfield->header != header_idx_new)
701
0
          hfield = hfield->next;
702
703
0
        i_assert(hfield != NULL);
704
0
        header_idx_new->first = hfield;
705
706
0
        hfield = edmail->header_fields_tail;
707
0
        while (hfield != NULL &&
708
0
               hfield->header != header_idx_new)
709
0
          hfield = hfield->prev;
710
711
0
        i_assert(hfield != NULL);
712
0
        header_idx_new->last = hfield;
713
0
      }
714
0
    }
715
0
  }
716
717
0
  _header_field_unref(field_idx->field);
718
0
  i_free(field_idx);
719
0
  return field_idx_new;
720
0
}
721
722
static inline char *
723
_header_decode(const unsigned char *hdr_data, size_t hdr_data_len)
724
0
{
725
0
  string_t *str = t_str_new(512);
726
727
  /* hdr_data is already unfolded */
728
729
  /* Decode MIME encoded-words. */
730
0
  message_header_decode_utf8((const unsigned char *)hdr_data,
731
0
           hdr_data_len, str, NULL);
732
0
  return i_strdup(str_c(str));
733
0
}
734
735
static int edit_mail_headers_parse(struct edit_mail *edmail)
736
0
{
737
0
  struct message_header_parser_ctx *hparser;
738
0
  enum message_header_parser_flags hparser_flags =
739
0
    MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
740
0
    MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
741
0
  struct message_header_line *hdr;
742
0
  struct _header_index *header_idx;
743
0
  struct _header_field_index *head = NULL, *tail = NULL, *current;
744
0
  string_t *hdr_data;
745
0
  uoff_t offset = 0, body_offset = 0, vsize_diff = 0;
746
0
  unsigned int lines = 0;
747
0
  int ret;
748
749
0
  if (edmail->headers_parsed)
750
0
    return 1;
751
752
0
  i_stream_seek(edmail->wrapped_stream, 0);
753
0
  hparser = message_parse_header_init(edmail->wrapped_stream, NULL,
754
0
              hparser_flags);
755
756
0
  T_BEGIN {
757
0
    hdr_data = t_str_new(1024);
758
0
    while ((ret = message_parse_header_next(hparser, &hdr)) > 0) {
759
0
      struct _header_field_index *field_idx_new;
760
0
      struct _header_field *field;
761
762
0
      i_assert(hdr != NULL);
763
0
      if (hdr->eoh) {
764
        /* Record whether header ends in CRLF or LF */
765
0
        edmail->eoh_crlf = hdr->crlf_newline;
766
0
        break;
767
0
      }
768
769
      /* Skip bad headers */
770
0
      if (hdr->name_len == 0)
771
0
        continue;
772
      /* We deny the existence of any 'Content-Length:'
773
         header. This header is non-standard and it can wreak
774
         havok when the message is modified.
775
       */
776
0
      if (strcasecmp(hdr->name, "Content-Length" ) == 0)
777
0
        continue;
778
779
0
      if (hdr->continued) {
780
        /* Continued line of folded header */
781
0
        buffer_append(hdr_data, hdr->value,
782
0
                hdr->value_len);
783
0
      } else {
784
        /* First line of header */
785
0
        offset = hdr->name_offset;
786
0
        body_offset = hdr->name_len + hdr->middle_len;
787
0
        str_truncate(hdr_data, 0);
788
0
        buffer_append(hdr_data, hdr->name,
789
0
                hdr->name_len);
790
0
        buffer_append(hdr_data, hdr->middle,
791
0
                hdr->middle_len);
792
0
        buffer_append(hdr_data, hdr->value,
793
0
                hdr->value_len);
794
0
        lines = 0;
795
0
        vsize_diff = 0;
796
0
      }
797
798
0
      if (!hdr->no_newline) {
799
0
        lines++;
800
801
0
        if (hdr->crlf_newline) {
802
0
          buffer_append(hdr_data, "\r\n", 2);
803
0
        } else {
804
0
          buffer_append(hdr_data, "\n", 1);
805
0
          vsize_diff++;
806
0
        }
807
0
      }
808
809
0
      if (hdr->continues) {
810
0
        hdr->use_full_value = TRUE;
811
0
        continue;
812
0
      }
813
814
      /* Create new header field index entry */
815
816
0
      field_idx_new = i_new(struct _header_field_index, 1);
817
818
0
      header_idx = edit_mail_header_create(edmail, hdr->name);
819
0
      header_idx->count++;
820
0
      field_idx_new->header = header_idx;
821
0
      field_idx_new->field = field =
822
0
        _header_field_create(header_idx->header);
823
824
0
      i_assert(body_offset > 0);
825
0
      field->body_offset = body_offset;
826
827
0
      field->utf8_value = _header_decode(hdr->full_value,
828
0
                 hdr->full_value_len);
829
830
0
      field->size = str_len(hdr_data);
831
0
      field->virtual_size = field->size + vsize_diff;
832
0
      field->data = i_strndup(str_data(hdr_data),
833
0
            field->size);
834
0
      field->offset = offset;
835
0
      field->lines = lines;
836
837
0
      DLLIST2_APPEND(&head, &tail, field_idx_new);
838
839
0
      edmail->hdr_size.physical_size += field->size;
840
0
      edmail->hdr_size.virtual_size += field->virtual_size;
841
0
      edmail->hdr_size.lines += lines;
842
0
    }
843
0
  } T_END;
844
845
0
  message_parse_header_deinit(&hparser);
846
847
  /* Blocking i/o required */
848
0
  i_assert(ret != 0);
849
850
0
  if (ret < 0 && edmail->wrapped_stream->stream_errno != 0) {
851
    /* Error; clean up */
852
0
    i_error("read(%s) failed: %s",
853
0
      i_stream_get_name(edmail->wrapped_stream),
854
0
      i_stream_get_error(edmail->wrapped_stream));
855
0
    current = head;
856
0
    while (current != NULL) {
857
0
      struct _header_field_index *next = current->next;
858
859
0
      _header_field_unref(current->field);
860
0
      i_free(current);
861
862
0
      current = next;
863
0
    }
864
865
0
    return ret;
866
0
  }
867
868
  /* Insert header field index items in main list */
869
0
  if (head != NULL && tail != NULL) {
870
0
    if (edmail->header_fields_appended != NULL) {
871
0
      if (edmail->header_fields_head !=
872
0
          edmail->header_fields_appended) {
873
0
        if (edmail->header_fields_appended->prev != NULL)
874
0
          edmail->header_fields_appended->prev->next = head;
875
0
        head->prev = edmail->header_fields_appended->prev;
876
0
      } else {
877
0
        edmail->header_fields_head = head;
878
0
      }
879
880
0
      tail->next = edmail->header_fields_appended;
881
0
      edmail->header_fields_appended->prev = tail;
882
0
    } else if (edmail->header_fields_tail != NULL) {
883
0
      edmail->header_fields_tail->next = head;
884
0
      head->prev = edmail->header_fields_tail;
885
0
      edmail->header_fields_tail = tail;
886
0
    } else {
887
0
      edmail->header_fields_head = head;
888
0
      edmail->header_fields_tail = tail;
889
0
    }
890
0
  }
891
892
  /* Rebuild header index */
893
0
  current = edmail->header_fields_head;
894
0
  while (current != NULL) {
895
0
    if (current->header->first == NULL)
896
0
      current->header->first = current;
897
0
    current->header->last = current;
898
899
0
    current = current->next;
900
0
  }
901
902
  /* Clear appended headers */
903
0
  edmail->header_fields_appended = NULL;
904
0
  edmail->appended_hdr_size.physical_size = 0;
905
0
  edmail->appended_hdr_size.virtual_size = 0;
906
0
  edmail->appended_hdr_size.lines = 0;
907
908
  /* Do not parse headers again */
909
0
  edmail->headers_parsed = TRUE;
910
911
0
  return 1;
912
0
}
913
914
void edit_mail_header_add(struct edit_mail *edmail, const char *field_name,
915
        const char *value, bool last)
916
0
{
917
0
  struct _header_index *header_idx;
918
0
  struct _header_field_index *field_idx;
919
0
  struct _header_field *field;
920
921
0
  edit_mail_modify(edmail);
922
923
0
  field_idx = edit_mail_header_field_create(edmail, field_name, value);
924
0
  header_idx = field_idx->header;
925
0
  field = field_idx->field;
926
927
  /* Add it to the header field index */
928
0
  if (last) {
929
0
    DLLIST2_APPEND(&edmail->header_fields_head,
930
0
             &edmail->header_fields_tail, field_idx);
931
932
0
    header_idx->last = field_idx;
933
0
    if (header_idx->first == NULL)
934
0
      header_idx->first = field_idx;
935
936
0
    if (!edmail->headers_parsed)  {
937
0
      if (edmail->header_fields_appended == NULL) {
938
        /* Record beginning of appended headers */
939
0
        edmail->header_fields_appended = field_idx;
940
0
      }
941
942
0
      edmail->appended_hdr_size.physical_size += field->size;
943
0
      edmail->appended_hdr_size.virtual_size += field->virtual_size;
944
0
      edmail->appended_hdr_size.lines += field->lines;
945
0
    }
946
0
  } else {
947
0
    DLLIST2_PREPEND(&edmail->header_fields_head,
948
0
        &edmail->header_fields_tail, field_idx);
949
950
0
    header_idx->first = field_idx;
951
0
    if (header_idx->last == NULL)
952
0
      header_idx->last = field_idx;
953
0
  }
954
955
0
  header_idx->count++;
956
957
0
  edmail->hdr_size.physical_size += field->size;
958
0
  edmail->hdr_size.virtual_size += field->virtual_size;
959
0
  edmail->hdr_size.lines += field->lines;
960
0
}
961
962
int edit_mail_header_delete(struct edit_mail *edmail, const char *field_name,
963
          int index)
964
0
{
965
0
  struct _header_index *header_idx;
966
0
  struct _header_field_index *field_idx;
967
0
  int pos = 0;
968
0
  int ret = 0;
969
970
  /* Make sure headers are parsed */
971
0
  if (edit_mail_headers_parse(edmail) <= 0)
972
0
    return -1;
973
974
  /* Find the header entry */
975
0
  header_idx = edit_mail_header_find(edmail, field_name);
976
0
  if (header_idx == NULL) {
977
    /* Not found */
978
0
    return 0;
979
0
  }
980
981
  /* Signal modification */
982
0
  edit_mail_modify(edmail);
983
984
  /* Iterate through all header fields and remove those that match */
985
0
  field_idx = (index >= 0 ? header_idx->first : header_idx->last);
986
0
  while (field_idx != NULL) {
987
0
    struct _header_field_index *next =
988
0
      (index >= 0 ? field_idx->next : field_idx->prev);
989
990
0
    if (field_idx->field->header == header_idx->header) {
991
0
      bool final;
992
993
0
      if (index >= 0) {
994
0
        pos++;
995
0
        final = (header_idx->last == field_idx);
996
0
      } else {
997
0
        pos--;
998
0
        final = (header_idx->first == field_idx);
999
0
      }
1000
1001
0
      if (index == 0 || index == pos) {
1002
0
        if (header_idx->first == field_idx)
1003
0
          header_idx->first = NULL;
1004
0
        if (header_idx->last == field_idx)
1005
0
          header_idx->last = NULL;
1006
0
        edit_mail_header_field_delete(
1007
0
          edmail, field_idx, FALSE);
1008
0
        ret++;
1009
0
      }
1010
1011
0
      if (final || (index != 0 && index == pos))
1012
0
        break;
1013
0
    }
1014
1015
0
    field_idx = next;
1016
0
  }
1017
1018
0
  if (index == 0 || header_idx->count == 0) {
1019
0
    DLLIST2_REMOVE(&edmail->headers_head,
1020
0
             &edmail->headers_tail, header_idx);
1021
0
    _header_unref(header_idx->header);
1022
0
    i_free(header_idx);
1023
0
  } else if (header_idx->first == NULL || header_idx->last == NULL) {
1024
0
    struct _header_field_index *current =
1025
0
      edmail->header_fields_head;
1026
1027
0
    while (current != NULL) {
1028
0
      if (current->header == header_idx) {
1029
0
        if (header_idx->first == NULL)
1030
0
          header_idx->first = current;
1031
0
        header_idx->last = current;
1032
0
      }
1033
0
      current = current->next;
1034
0
    }
1035
0
  }
1036
1037
0
  return ret;
1038
0
}
1039
1040
int edit_mail_header_replace(struct edit_mail *edmail,
1041
           const char *field_name, int index,
1042
           const char *newname, const char *newvalue)
1043
0
{
1044
0
  struct _header_index *header_idx, *header_idx_new;
1045
0
  struct _header_field_index *field_idx, *field_idx_new;
1046
0
  int pos = 0;
1047
0
  int ret = 0;
1048
1049
  /* Make sure headers are parsed */
1050
0
  if (edit_mail_headers_parse(edmail) <= 0)
1051
0
    return -1;
1052
1053
  /* Find the header entry */
1054
0
  header_idx = edit_mail_header_find(edmail, field_name);
1055
0
  if (header_idx == NULL) {
1056
    /* Not found */
1057
0
    return 0;
1058
0
  }
1059
1060
  /* Signal modification */
1061
0
  edit_mail_modify(edmail);
1062
1063
  /* Iterate through all header fields and replace those that match */
1064
0
  field_idx = (index >= 0 ? header_idx->first : header_idx->last);
1065
0
  field_idx_new = NULL;
1066
0
  while (field_idx != NULL) {
1067
0
    struct _header_field_index *next =
1068
0
      (index >= 0 ? field_idx->next : field_idx->prev);
1069
1070
0
    if (field_idx->field->header == header_idx->header) {
1071
0
      bool final;
1072
1073
0
      if (index >= 0) {
1074
0
        pos++;
1075
0
        final = (header_idx->last == field_idx);
1076
0
      } else {
1077
0
        pos--;
1078
0
        final = (header_idx->first == field_idx);
1079
0
      }
1080
1081
0
      if (index == 0 || index == pos) {
1082
0
        if (header_idx->first == field_idx)
1083
0
          header_idx->first = NULL;
1084
0
        if (header_idx->last == field_idx)
1085
0
          header_idx->last = NULL;
1086
0
        field_idx_new = edit_mail_header_field_replace(
1087
0
          edmail, field_idx, newname, newvalue,
1088
0
          FALSE);
1089
0
        ret++;
1090
0
      }
1091
1092
0
      if (final || (index != 0 && index == pos))
1093
0
        break;
1094
0
    }
1095
1096
0
    field_idx = next;
1097
0
  }
1098
1099
  /* Update old header index */
1100
0
  if (header_idx->count == 0) {
1101
0
    DLLIST2_REMOVE(&edmail->headers_head, &edmail->headers_tail,
1102
0
             header_idx);
1103
0
    _header_unref(header_idx->header);
1104
0
    i_free(header_idx);
1105
0
  } else if (header_idx->first == NULL || header_idx->last == NULL) {
1106
0
    struct _header_field_index *current =
1107
0
      edmail->header_fields_head;
1108
1109
0
    while (current != NULL) {
1110
0
      if (current->header == header_idx) {
1111
0
        if (header_idx->first == NULL)
1112
0
          header_idx->first = current;
1113
0
        header_idx->last = current;
1114
0
      }
1115
0
      current = current->next;
1116
0
    }
1117
0
  }
1118
1119
  /* Update new header index */
1120
0
  if (field_idx_new != NULL) {
1121
0
    struct _header_field_index *current =
1122
0
      edmail->header_fields_head;
1123
1124
0
    header_idx_new = field_idx_new->header;
1125
0
    while (current != NULL) {
1126
0
      if (current->header == header_idx_new) {
1127
0
        if (header_idx_new->first == NULL)
1128
0
          header_idx_new->first = current;
1129
0
        header_idx_new->last = current;
1130
0
      }
1131
0
      current = current->next;
1132
0
    }
1133
0
  }
1134
1135
0
  return ret;
1136
0
}
1137
1138
struct edit_mail_header_iter
1139
{
1140
  struct edit_mail *mail;
1141
  struct _header_index *header;
1142
  struct _header_field_index *current;
1143
1144
  bool reverse:1;
1145
};
1146
1147
int edit_mail_headers_iterate_init(struct edit_mail *edmail,
1148
           const char *field_name, bool reverse,
1149
           struct edit_mail_header_iter **edhiter_r)
1150
0
{
1151
0
  struct edit_mail_header_iter *edhiter;
1152
0
  struct _header_index *header_idx = NULL;
1153
0
  struct _header_field_index *current = NULL;
1154
1155
  /* Make sure headers are parsed */
1156
0
  if (edit_mail_headers_parse(edmail) <= 0) {
1157
    /* Failure */
1158
0
    return -1;
1159
0
  }
1160
1161
0
  header_idx = edit_mail_header_find(edmail, field_name);
1162
1163
0
  if (field_name != NULL && header_idx == NULL) {
1164
0
    current = NULL;
1165
0
  } else if (!reverse) {
1166
0
    current = (header_idx != NULL ?
1167
0
         header_idx->first : edmail->header_fields_head);
1168
0
  } else {
1169
0
    current = (header_idx != NULL ?
1170
0
         header_idx->last : edmail->header_fields_tail);
1171
0
    if (current != NULL && current->header == NULL)
1172
0
      current = current->prev;
1173
0
  }
1174
1175
0
  if (current ==  NULL)
1176
0
    return 0;
1177
1178
0
  edhiter = i_new(struct edit_mail_header_iter, 1);
1179
0
  edhiter->mail = edmail;
1180
0
  edhiter->header = header_idx;
1181
0
  edhiter->reverse = reverse;
1182
0
  edhiter->current = current;
1183
1184
0
  *edhiter_r = edhiter;
1185
0
  return 1;
1186
0
}
1187
1188
void edit_mail_headers_iterate_deinit(struct edit_mail_header_iter **edhiter)
1189
0
{
1190
0
  i_free(*edhiter);
1191
0
  *edhiter = NULL;
1192
0
}
1193
1194
void edit_mail_headers_iterate_get(struct edit_mail_header_iter *edhiter,
1195
           const char **value_r)
1196
0
{
1197
0
  const char *raw;
1198
0
  int i;
1199
1200
0
  i_assert(edhiter->current != NULL && edhiter->current->header != NULL);
1201
1202
0
  raw = edhiter->current->field->utf8_value;
1203
0
  for (i = strlen(raw)-1; i >= 0; i--) {
1204
0
    if (raw[i] != ' ' && raw[i] != '\t')
1205
0
      break;
1206
0
  }
1207
1208
0
  *value_r = t_strndup(raw, i+1);
1209
0
}
1210
1211
bool edit_mail_headers_iterate_next(struct edit_mail_header_iter *edhiter)
1212
0
{
1213
0
  if (edhiter->current == NULL)
1214
0
    return FALSE;
1215
1216
0
  do {
1217
0
    edhiter->current = (!edhiter->reverse ?
1218
0
            edhiter->current->next :
1219
0
            edhiter->current->prev );
1220
0
  } while (edhiter->current != NULL && edhiter->current->header != NULL &&
1221
0
     edhiter->header != NULL &&
1222
0
     edhiter->current->header != edhiter->header);
1223
1224
0
  return (edhiter->current != NULL && edhiter->current->header != NULL);
1225
0
}
1226
1227
bool edit_mail_headers_iterate_remove(struct edit_mail_header_iter *edhiter)
1228
0
{
1229
0
  struct _header_field_index *field_idx;
1230
0
  bool next;
1231
1232
0
  i_assert(edhiter->current != NULL && edhiter->current->header != NULL);
1233
1234
0
  edit_mail_modify(edhiter->mail);
1235
1236
0
  field_idx = edhiter->current;
1237
0
  next = edit_mail_headers_iterate_next(edhiter);
1238
0
  edit_mail_header_field_delete(edhiter->mail, field_idx, TRUE);
1239
1240
0
  return next;
1241
0
}
1242
1243
bool edit_mail_headers_iterate_replace(struct edit_mail_header_iter *edhiter,
1244
               const char *newname,
1245
               const char *newvalue)
1246
0
{
1247
0
  struct _header_field_index *field_idx;
1248
0
  bool next;
1249
1250
0
  i_assert(edhiter->current != NULL && edhiter->current->header != NULL);
1251
1252
0
  edit_mail_modify(edhiter->mail);
1253
1254
0
  field_idx = edhiter->current;
1255
0
  next = edit_mail_headers_iterate_next(edhiter);
1256
0
  edit_mail_header_field_replace(edhiter->mail, field_idx,
1257
0
               newname, newvalue, TRUE);
1258
1259
0
  return next;
1260
0
}
1261
1262
/* Body modification */
1263
1264
// FIXME: implement
1265
1266
/*
1267
 * Mail API
1268
 */
1269
1270
static void edit_mail_close(struct mail *mail)
1271
0
{
1272
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1273
1274
0
  edmail->wrapped->v.close(&edmail->wrapped->mail);
1275
0
}
1276
1277
static void edit_mail_free(struct mail *mail)
1278
0
{
1279
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1280
1281
0
  edmail->wrapped->v.free(&edmail->wrapped->mail);
1282
1283
0
  edit_mail_unwrap(&edmail);
1284
0
}
1285
1286
static void
1287
edit_mail_set_seq(struct mail *mail ATTR_UNUSED, uint32_t seq ATTR_UNUSED,
1288
      bool saving ATTR_UNUSED)
1289
0
{
1290
0
  i_panic("edit_mail_set_seq() not implemented");
1291
0
}
1292
1293
static bool ATTR_NORETURN
1294
edit_mail_set_uid(struct mail *mail ATTR_UNUSED, uint32_t uid ATTR_UNUSED)
1295
0
{
1296
0
  i_panic("edit_mail_set_uid() not implemented");
1297
0
}
1298
1299
static void edit_mail_set_uid_cache_updates(struct mail *mail, bool set)
1300
0
{
1301
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1302
1303
0
  edmail->wrapped->v.set_uid_cache_updates(&edmail->wrapped->mail, set);
1304
0
}
1305
1306
static void
1307
edit_mail_add_temp_wanted_fields(
1308
  struct mail *mail ATTR_UNUSED, enum mail_fetch_field fields ATTR_UNUSED,
1309
  struct mailbox_header_lookup_ctx *headers ATTR_UNUSED)
1310
0
{
1311
  /* Nothing */
1312
0
}
1313
1314
static enum mail_flags edit_mail_get_flags(struct mail *mail)
1315
0
{
1316
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1317
1318
0
  return edmail->wrapped->v.get_flags(&edmail->wrapped->mail);
1319
0
}
1320
1321
static const char *const *edit_mail_get_keywords(struct mail *mail)
1322
0
{
1323
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1324
1325
0
  return edmail->wrapped->v.get_keywords(&edmail->wrapped->mail);
1326
0
}
1327
1328
static const ARRAY_TYPE(keyword_indexes) *
1329
edit_mail_get_keyword_indexes(struct mail *mail)
1330
0
{
1331
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1332
1333
0
  return edmail->wrapped->v.get_keyword_indexes(&edmail->wrapped->mail);
1334
0
}
1335
1336
static uint64_t edit_mail_get_modseq(struct mail *mail)
1337
0
{
1338
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1339
1340
0
  return edmail->wrapped->v.get_modseq(&edmail->wrapped->mail);
1341
0
}
1342
1343
static uint64_t edit_mail_get_pvt_modseq(struct mail *mail)
1344
0
{
1345
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1346
1347
0
  return edmail->wrapped->v.get_pvt_modseq(&edmail->wrapped->mail);
1348
0
}
1349
1350
static int edit_mail_get_parts(struct mail *mail, struct message_part **parts_r)
1351
0
{
1352
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1353
1354
0
  return edmail->wrapped->v.get_parts(&edmail->wrapped->mail, parts_r);
1355
0
}
1356
1357
static int
1358
edit_mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r)
1359
0
{
1360
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1361
1362
0
  return edmail->wrapped->v.get_date(&edmail->wrapped->mail,
1363
0
             date_r, timezone_r);
1364
0
}
1365
1366
static int edit_mail_get_received_date(struct mail *mail, time_t *date_r)
1367
0
{
1368
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1369
1370
0
  return edmail->wrapped->v.get_received_date(&edmail->wrapped->mail,
1371
0
                date_r);
1372
0
}
1373
1374
static int edit_mail_get_save_date(struct mail *mail, time_t *date_r)
1375
0
{
1376
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1377
1378
0
  return edmail->wrapped->v.get_save_date(&edmail->wrapped->mail, date_r);
1379
0
}
1380
1381
static int edit_mail_get_virtual_size(struct mail *mail, uoff_t *size_r)
1382
0
{
1383
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1384
1385
0
  if (!edmail->headers_parsed) {
1386
0
    *size_r = (edmail->wrapped_hdr_size.virtual_size +
1387
0
         edmail->wrapped_body_size.virtual_size);
1388
1389
0
    if (!edmail->modified)
1390
0
      return 0;
1391
0
  } else {
1392
0
    *size_r = edmail->wrapped_body_size.virtual_size + 2;
1393
0
  }
1394
1395
0
  *size_r += (edmail->hdr_size.virtual_size +
1396
0
        edmail->body_size.virtual_size);
1397
0
  return 0;
1398
0
}
1399
1400
static int edit_mail_get_physical_size(struct mail *mail, uoff_t *size_r)
1401
0
{
1402
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1403
1404
0
  *size_r = 0;
1405
0
  if (!edmail->headers_parsed) {
1406
0
    *size_r = (edmail->wrapped_hdr_size.physical_size +
1407
0
         edmail->wrapped_body_size.physical_size);
1408
1409
0
    if (!edmail->modified)
1410
0
      return 0;
1411
0
  } else {
1412
0
    *size_r = (edmail->wrapped_body_size.physical_size +
1413
0
         (edmail->eoh_crlf ? 2 : 1));
1414
0
  }
1415
1416
0
  *size_r += (edmail->hdr_size.physical_size +
1417
0
        edmail->body_size.physical_size);
1418
0
  return 0;
1419
0
}
1420
1421
static int
1422
edit_mail_get_first_header(struct mail *mail, const char *field_name,
1423
         bool decode_to_utf8, const char **value_r)
1424
0
{
1425
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1426
0
  struct _header_index *header_idx;
1427
0
  struct _header_field *field;
1428
0
  int ret;
1429
1430
  /* Check whether mail headers were modified at all */
1431
0
  if (!edmail->modified || edmail->headers_head == NULL) {
1432
    /* Unmodified */
1433
0
    return edmail->wrapped->v.get_first_header(
1434
0
      &edmail->wrapped->mail, field_name, decode_to_utf8,
1435
0
      value_r);
1436
0
  }
1437
1438
  /* Try to find modified header */
1439
0
  header_idx = edit_mail_header_find(edmail, field_name);
1440
0
  if (header_idx == NULL || header_idx->count == 0 ) {
1441
0
    if (!edmail->headers_parsed) {
1442
      /* No new header */
1443
0
      return edmail->wrapped->v.get_first_header(
1444
0
        &edmail->wrapped->mail, field_name,
1445
0
        decode_to_utf8, value_r);
1446
0
    }
1447
1448
0
    *value_r = NULL;
1449
0
    return 0;
1450
0
  }
1451
1452
  /* Get the first occurrence */
1453
0
  if (edmail->header_fields_appended == NULL) {
1454
    /* There are no appended headers, so first is found directly */
1455
0
    field = header_idx->first->field;
1456
0
  } else {
1457
0
    struct _header_field_index *field_idx;
1458
1459
    /* Scan prepended headers */
1460
0
    field_idx = edmail->header_fields_head;
1461
0
    while (field_idx != NULL) {
1462
0
      if (field_idx->header == header_idx)
1463
0
        break;
1464
1465
0
      if (field_idx == edmail->header_fields_appended) {
1466
0
        field_idx = NULL;
1467
0
        break;
1468
0
      }
1469
0
      field_idx = field_idx->next;
1470
0
    }
1471
1472
0
    if (field_idx == NULL) {
1473
      /* Check original message */
1474
0
      ret = edmail->wrapped->v.get_first_header(
1475
0
        &edmail->wrapped->mail, field_name,
1476
0
        decode_to_utf8, value_r);
1477
0
      if (ret != 0)
1478
0
        return ret;
1479
1480
      /* Use first (apparently appended) header */
1481
0
      field = header_idx->first->field;
1482
0
    } else {
1483
0
      field = field_idx->field;
1484
0
    }
1485
0
  }
1486
1487
0
  if (decode_to_utf8)
1488
0
    *value_r = field->utf8_value;
1489
0
  else
1490
0
    *value_r = (const char *)(field->data + field->body_offset);
1491
0
  return 1;
1492
0
}
1493
1494
static int
1495
edit_mail_get_headers(struct mail *mail, const char *field_name,
1496
          bool decode_to_utf8, const char *const **value_r)
1497
0
{
1498
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1499
0
  struct _header_index *header_idx;
1500
0
  struct _header_field_index *field_idx;
1501
0
  const char *const *headers;
1502
0
  ARRAY(const char *) header_values;
1503
1504
0
  if (!edmail->modified || edmail->headers_head == NULL) {
1505
    /* Unmodified */
1506
0
    return edmail->wrapped->v.get_headers(
1507
0
      &edmail->wrapped->mail, field_name, decode_to_utf8,
1508
0
      value_r);
1509
0
  }
1510
1511
0
  header_idx = edit_mail_header_find(edmail, field_name);
1512
0
  if (header_idx == NULL || header_idx->count == 0 ) {
1513
0
    if (!edmail->headers_parsed) {
1514
      /* No new header */
1515
0
      return edmail->wrapped->v.get_headers(
1516
0
        &edmail->wrapped->mail, field_name,
1517
0
        decode_to_utf8, value_r);
1518
0
    }
1519
1520
0
    p_array_init(&header_values, edmail->mail.pool, 1);
1521
0
    (void)array_append_space(&header_values);
1522
0
    *value_r = array_idx(&header_values, 0);
1523
0
    return 0;
1524
0
  }
1525
1526
  /* Merge */
1527
1528
  /* Read original headers too if message headers are not parsed */
1529
0
  headers = NULL;
1530
0
  if (!edmail->headers_parsed &&
1531
0
      edmail->wrapped->v.get_headers(&edmail->wrapped->mail, field_name,
1532
0
             decode_to_utf8, &headers) < 0)
1533
0
    return -1;
1534
1535
  /* Fill result array */
1536
0
  p_array_init(&header_values, edmail->mail.pool, 32);
1537
0
  field_idx = header_idx->first;
1538
0
  while (field_idx != NULL) {
1539
    /* If current field is the first appended one, we need to add
1540
       original headers first.
1541
     */
1542
0
    if (field_idx == edmail->header_fields_appended &&
1543
0
        headers != NULL) {
1544
0
      while (*headers != NULL) {
1545
0
        array_append(&header_values, headers, 1);
1546
0
        headers++;
1547
0
      }
1548
0
    }
1549
1550
    /* Add modified header to the list */
1551
0
    if (field_idx->field->header == header_idx->header) {
1552
0
      struct _header_field *field = field_idx->field;
1553
1554
0
      const char *value;
1555
0
      if (decode_to_utf8)
1556
0
        value = field->utf8_value;
1557
0
      else {
1558
0
        value = (const char *)(field->data +
1559
0
                   field->body_offset);
1560
0
      }
1561
1562
0
      array_append(&header_values, &value, 1);
1563
1564
0
      if (field_idx == header_idx->last)
1565
0
        break;
1566
0
    }
1567
1568
0
    field_idx = field_idx->next;
1569
0
  }
1570
1571
  /* Add original headers if necessary */
1572
0
  if (headers != NULL) {
1573
0
    while (*headers != NULL) {
1574
0
      array_append(&header_values, headers, 1);
1575
0
      headers++;
1576
0
    }
1577
0
  }
1578
1579
0
  (void)array_append_space(&header_values);
1580
0
  *value_r = array_idx(&header_values, 0);
1581
0
  return 1;
1582
0
}
1583
1584
static int ATTR_NORETURN
1585
edit_mail_get_header_stream(
1586
  struct mail *mail ATTR_UNUSED,
1587
  struct mailbox_header_lookup_ctx *headers ATTR_UNUSED,
1588
  struct istream **stream_r ATTR_UNUSED)
1589
0
{
1590
  // FIXME: implement!
1591
0
  i_panic("edit_mail_get_header_stream() not implemented");
1592
0
}
1593
1594
static int
1595
edit_mail_get_stream(struct mail *mail, bool get_body ATTR_UNUSED,
1596
         struct message_size *hdr_size,
1597
         struct message_size *body_size, struct istream **stream_r)
1598
0
{
1599
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1600
1601
0
  if (edmail->stream == NULL)
1602
0
    edmail->stream = edit_mail_istream_create(edmail);
1603
1604
0
  if (hdr_size != NULL) {
1605
0
    *hdr_size = edmail->wrapped_hdr_size;
1606
0
    hdr_size->physical_size += edmail->hdr_size.physical_size;
1607
0
    hdr_size->virtual_size += edmail->hdr_size.virtual_size;
1608
0
    hdr_size->lines += edmail->hdr_size.lines;
1609
0
  }
1610
1611
0
  if (body_size != NULL)
1612
0
    *body_size = edmail->wrapped_body_size;
1613
1614
0
  *stream_r = edmail->stream;
1615
0
  i_stream_seek(edmail->stream, 0);
1616
1617
0
  return 0;
1618
0
}
1619
1620
static int
1621
edit_mail_get_special(struct mail *mail, enum mail_fetch_field field,
1622
          const char **value_r)
1623
0
{
1624
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1625
1626
0
  if (edmail->modified) {
1627
    /* Block certain fields when modified */
1628
1629
0
    switch (field) {
1630
0
    case MAIL_FETCH_GUID:
1631
      /* This is in essence a new message */
1632
0
      *value_r = "";
1633
0
      return 0;
1634
0
    case MAIL_FETCH_STORAGE_ID:
1635
      /* Prevent hardlink copying */
1636
0
      *value_r = "";
1637
0
      return 0;
1638
0
    default:
1639
0
      break;
1640
0
    }
1641
0
  }
1642
1643
0
  return edmail->wrapped->v.get_special(&edmail->wrapped->mail,
1644
0
                field, value_r);
1645
0
}
1646
1647
static int
1648
edit_mail_get_backend_mail(struct mail *mail, struct mail **real_mail_r)
1649
0
{
1650
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1651
1652
0
  *real_mail_r = edit_mail_get_mail(edmail);
1653
0
  return 0;
1654
0
}
1655
1656
static void
1657
edit_mail_update_flags(struct mail *mail, enum modify_type modify_type,
1658
           enum mail_flags flags)
1659
0
{
1660
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1661
1662
0
  edmail->wrapped->v.update_flags(&edmail->wrapped->mail,
1663
0
          modify_type, flags);
1664
0
}
1665
1666
static void
1667
edit_mail_update_keywords(struct mail *mail, enum modify_type modify_type,
1668
        struct mail_keywords *keywords)
1669
0
{
1670
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1671
1672
0
  edmail->wrapped->v.update_keywords(&edmail->wrapped->mail,
1673
0
             modify_type, keywords);
1674
0
}
1675
1676
static void edit_mail_update_modseq(struct mail *mail, uint64_t min_modseq)
1677
0
{
1678
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1679
1680
0
  edmail->wrapped->v.update_modseq(&edmail->wrapped->mail, min_modseq);
1681
0
}
1682
1683
static void
1684
edit_mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq)
1685
0
{
1686
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1687
1688
0
  edmail->wrapped->v.update_pvt_modseq(&edmail->wrapped->mail,
1689
0
               min_pvt_modseq);
1690
0
}
1691
1692
static void edit_mail_update_pop3_uidl(struct mail *mail, const char *uidl)
1693
0
{
1694
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1695
1696
0
  if (edmail->wrapped->v.update_pop3_uidl != NULL) {
1697
0
    edmail->wrapped->v.update_pop3_uidl(
1698
0
      &edmail->wrapped->mail, uidl);
1699
0
  }
1700
0
}
1701
1702
static void edit_mail_expunge(struct mail *mail ATTR_UNUSED)
1703
0
{
1704
  /* NOOP */
1705
0
}
1706
1707
static void
1708
edit_mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field,
1709
            const char *reason)
1710
0
{
1711
0
  struct edit_mail *edmail = (struct edit_mail *)mail;
1712
1713
0
  edmail->wrapped->v.set_cache_corrupted(&edmail->wrapped->mail,
1714
0
                 field, reason);
1715
0
}
1716
1717
static struct mail_vfuncs edit_mail_vfuncs = {
1718
  edit_mail_close,
1719
  edit_mail_free,
1720
  edit_mail_set_seq,
1721
  edit_mail_set_uid,
1722
  edit_mail_set_uid_cache_updates,
1723
  NULL,
1724
  NULL,
1725
  edit_mail_add_temp_wanted_fields,
1726
  edit_mail_get_flags,
1727
  edit_mail_get_keywords,
1728
  edit_mail_get_keyword_indexes,
1729
  edit_mail_get_modseq,
1730
  edit_mail_get_pvt_modseq,
1731
  edit_mail_get_parts,
1732
  edit_mail_get_date,
1733
  edit_mail_get_received_date,
1734
  edit_mail_get_save_date,
1735
  edit_mail_get_virtual_size,
1736
  edit_mail_get_physical_size,
1737
  edit_mail_get_first_header,
1738
  edit_mail_get_headers,
1739
  edit_mail_get_header_stream,
1740
  edit_mail_get_stream,
1741
  index_mail_get_binary_stream,
1742
  edit_mail_get_special,
1743
  edit_mail_get_backend_mail,
1744
  edit_mail_update_flags,
1745
  edit_mail_update_keywords,
1746
  edit_mail_update_modseq,
1747
  edit_mail_update_pvt_modseq,
1748
  edit_mail_update_pop3_uidl,
1749
  edit_mail_expunge,
1750
  edit_mail_set_cache_corrupted,
1751
  NULL,
1752
};
1753
1754
/*
1755
 * Edit Mail Stream
1756
 */
1757
1758
struct edit_mail_istream {
1759
  struct istream_private istream;
1760
  pool_t pool;
1761
1762
  struct edit_mail *mail;
1763
1764
  struct _header_field_index *cur_header;
1765
  uoff_t cur_header_v_offset;
1766
1767
  bool parent_buffer:1;
1768
  bool header_read:1;
1769
  bool eof:1;
1770
};
1771
1772
static void edit_mail_istream_destroy(struct iostream_private *stream)
1773
0
{
1774
0
  struct edit_mail_istream *edstream =
1775
0
    (struct edit_mail_istream *)stream;
1776
1777
0
  i_stream_unref(&edstream->istream.parent);
1778
0
  i_stream_free_buffer(&edstream->istream);
1779
0
  pool_unref(&edstream->pool);
1780
0
}
1781
1782
static ssize_t
1783
merge_from_parent(struct edit_mail_istream *edstream, uoff_t parent_v_offset,
1784
      uoff_t parent_end_v_offset, uoff_t copy_v_offset)
1785
0
{
1786
0
  struct istream_private *stream = &edstream->istream;
1787
0
  uoff_t v_offset, append_v_offset;
1788
0
  const unsigned char *data;
1789
0
  size_t pos, cur_pos, parent_bytes_left;
1790
0
  bool parent_buffer = edstream->parent_buffer;
1791
0
  ssize_t ret;
1792
1793
0
  i_assert(parent_v_offset <= parent_end_v_offset);
1794
0
  edstream->parent_buffer = FALSE;
1795
1796
0
  v_offset = stream->istream.v_offset;
1797
0
  if (v_offset >= copy_v_offset) {
1798
0
    i_assert((v_offset - copy_v_offset) <= parent_end_v_offset);
1799
0
    if ((v_offset - copy_v_offset) == parent_end_v_offset) {
1800
      /* Parent data is all read */
1801
0
      return 0;
1802
0
    }
1803
0
  }
1804
1805
  /* Determine where we are appending more data to the stream */
1806
0
  append_v_offset = v_offset + (stream->pos - stream->skip);
1807
1808
0
  if (v_offset >= copy_v_offset) {
1809
    /* Parent buffer used */
1810
0
    cur_pos = (stream->pos - stream->skip);
1811
0
    parent_v_offset += (v_offset - copy_v_offset);
1812
0
  } else {
1813
0
    cur_pos = 0;
1814
0
    i_assert(append_v_offset >= copy_v_offset);
1815
0
    parent_v_offset += (append_v_offset - copy_v_offset);
1816
0
  }
1817
1818
  /* Seek parent to required position */
1819
0
  i_stream_seek(stream->parent, parent_v_offset);
1820
1821
  /* Read from parent */
1822
0
  data = i_stream_get_data(stream->parent, &pos);
1823
0
  if (pos > cur_pos)
1824
0
    ret = 0;
1825
0
  else do {
1826
    /* Use normal read here, since parent data can be returned
1827
       directly to caller. */
1828
0
    ret = i_stream_read(stream->parent);
1829
1830
0
    stream->istream.stream_errno = stream->parent->stream_errno;
1831
0
    stream->istream.eof = stream->parent->eof;
1832
0
    edstream->eof = stream->parent->eof;
1833
0
    data = i_stream_get_data(stream->parent, &pos);
1834
    /* Check again, in case the parent stream had been seeked
1835
       backwards and the previous read() didn't get us far
1836
       enough. */
1837
0
  } while (pos <= cur_pos && ret > 0);
1838
1839
  /* Don't read beyond parent end offset */
1840
0
  if (parent_end_v_offset != (uoff_t)-1) {
1841
0
    parent_bytes_left = (size_t)(parent_end_v_offset -
1842
0
               parent_v_offset);
1843
0
    if (pos >= parent_bytes_left) {
1844
0
      pos = parent_bytes_left;
1845
0
    }
1846
0
  }
1847
1848
0
  if (v_offset < copy_v_offset || ret == -2 ||
1849
0
      (parent_buffer && (append_v_offset + 1) >= parent_end_v_offset)) {
1850
    /* Merging with our local buffer; copying data from parent */
1851
0
    if (pos > 0) {
1852
0
      size_t avail;
1853
1854
0
      if (parent_buffer) {
1855
0
        stream->pos = stream->skip = 0;
1856
0
        stream->buffer = NULL;
1857
0
      }
1858
0
      if (!i_stream_try_alloc(stream, pos, &avail))
1859
0
        return -2;
1860
0
      pos = (pos > avail ? avail : pos);
1861
1862
0
      memcpy(stream->w_buffer + stream->pos, data, pos);
1863
0
      stream->pos += pos;
1864
0
      stream->buffer = stream->w_buffer;
1865
1866
0
      if (cur_pos >= pos)
1867
0
        ret = 0;
1868
0
      else
1869
0
        ret = (ssize_t)(pos - cur_pos);
1870
0
    } else {
1871
0
      ret = (ret == 0 ? 0 : -1);
1872
0
    }
1873
0
  } else {
1874
    /* Just passing buffers from parent; no copying */
1875
0
    ret = (pos > cur_pos ?
1876
0
           (ssize_t)(pos - cur_pos) : (ret == 0 ? 0 : -1));
1877
0
    stream->buffer = data;
1878
0
    stream->pos = pos;
1879
0
    stream->skip = 0;
1880
0
    edstream->parent_buffer = TRUE;
1881
0
  }
1882
1883
0
  i_assert(ret != -1 || stream->istream.eof ||
1884
0
     stream->istream.stream_errno != 0);
1885
0
  return ret;
1886
0
}
1887
1888
static ssize_t merge_modified_headers(struct edit_mail_istream *edstream)
1889
0
{
1890
0
  struct istream_private *stream = &edstream->istream;
1891
0
  struct edit_mail *edmail = edstream->mail;
1892
0
  uoff_t v_offset = stream->istream.v_offset, append_v_offset;
1893
0
  size_t appended, written, avail, size;
1894
1895
0
  if (edstream->cur_header == NULL) {
1896
    /* No (more) headers */
1897
0
    return 0;
1898
0
  }
1899
1900
  /* Caller must already have committed remaining parent data to
1901
     our stream buffer. */
1902
0
  i_assert(!edstream->parent_buffer);
1903
1904
  /* Add modified headers to buffer */
1905
0
  written = 0;
1906
0
  while (edstream->cur_header != NULL) {
1907
0
    size_t wsize;
1908
1909
    /* Determine what part of the header was already buffered */
1910
0
    append_v_offset = v_offset + (stream->pos - stream->skip);
1911
0
    i_assert(append_v_offset >= edstream->cur_header_v_offset);
1912
0
    if (append_v_offset >= edstream->cur_header_v_offset)
1913
0
      appended = (size_t)(append_v_offset -
1914
0
              edstream->cur_header_v_offset);
1915
0
    else
1916
0
      appended = 0;
1917
0
    i_assert(appended <= edstream->cur_header->field->size);
1918
1919
    /* Determine how much we want to write */
1920
0
    size = edstream->cur_header->field->size - appended;
1921
0
    if (size > 0) {
1922
      /* Determine how much we can write */
1923
0
      if (!i_stream_try_alloc(stream, size, &avail)) {
1924
0
        if (written == 0)
1925
0
          return -2;
1926
0
        break;
1927
0
      }
1928
0
      wsize = (size >= avail ? avail : size);
1929
1930
      /* Write (part of) the header to buffer */
1931
0
      memcpy(stream->w_buffer + stream->pos,
1932
0
             edstream->cur_header->field->data + appended,
1933
0
             wsize);
1934
0
      stream->pos += wsize;
1935
0
      stream->buffer = stream->w_buffer;
1936
0
      written += wsize;
1937
1938
0
      if (wsize < size) {
1939
        /* Could not write whole header; finish here */
1940
0
        break;
1941
0
      }
1942
0
    }
1943
1944
    /* Skip to next header */
1945
0
    edstream->cur_header_v_offset +=
1946
0
      edstream->cur_header->field->size;
1947
0
    edstream->cur_header = edstream->cur_header->next;
1948
1949
    /* Stop at end of prepended headers if original header is left
1950
       unparsed */
1951
0
    if (!edmail->headers_parsed &&
1952
0
        edstream->cur_header == edmail->header_fields_appended)
1953
0
      edstream->cur_header = NULL;
1954
0
  }
1955
1956
0
  if (edstream->cur_header == NULL) {
1957
    /* Clear offset too, just to be tidy */
1958
0
    edstream->cur_header_v_offset = 0;
1959
0
  }
1960
1961
0
  i_assert(written > 0);
1962
0
  return (ssize_t)written;
1963
0
}
1964
1965
static ssize_t edit_mail_istream_read(struct istream_private *stream)
1966
0
{
1967
0
  struct edit_mail_istream *edstream =
1968
0
    (struct edit_mail_istream *)stream;
1969
0
  struct edit_mail *edmail = edstream->mail;
1970
0
  uoff_t v_offset, append_v_offset;
1971
0
  uoff_t parent_v_offset, parent_end_v_offset, copy_v_offset;
1972
0
  uoff_t prep_hdr_size, hdr_size;
1973
0
  ssize_t ret = 0;
1974
1975
0
  if (edstream->eof) {
1976
0
    stream->istream.eof = TRUE;
1977
0
    return -1;
1978
0
  }
1979
1980
0
  if (edstream->parent_buffer && stream->skip == stream->pos) {
1981
0
    edstream->parent_buffer = FALSE;
1982
0
    stream->pos = stream->skip = 0;
1983
0
    stream->buffer = NULL;
1984
0
  }
1985
1986
  /* Merge prepended headers */
1987
0
  if (!edstream->parent_buffer) {
1988
0
    ret = merge_modified_headers(edstream);
1989
0
    if (ret != 0)
1990
0
      return ret;
1991
0
  }
1992
0
  v_offset = stream->istream.v_offset;
1993
0
  append_v_offset = v_offset + (stream->pos - stream->skip);
1994
1995
0
  if (!edmail->headers_parsed && edmail->header_fields_appended != NULL &&
1996
0
      !edstream->header_read) {
1997
    /* Output headers from original stream */
1998
1999
    /* Size of the prepended header */
2000
0
    i_assert(edmail->hdr_size.physical_size >=
2001
0
       edmail->appended_hdr_size.physical_size);
2002
0
    prep_hdr_size = (edmail->hdr_size.physical_size -
2003
0
         edmail->appended_hdr_size.physical_size);
2004
2005
    /* Calculate offset of header end or appended header. Any final
2006
       CR is dealt with later.
2007
     */
2008
0
    hdr_size = (prep_hdr_size +
2009
0
          edmail->wrapped_hdr_size.physical_size);
2010
0
    if (hdr_size <= (edmail->eoh_crlf ? 2 : 1)) {
2011
      /* Corner case that doesn't happen in practice (the
2012
         original message is never empty). */
2013
0
      edstream->cur_header = edmail->header_fields_appended;
2014
0
      edstream->cur_header_v_offset = v_offset;
2015
0
      edstream->header_read = TRUE;
2016
0
    } else if (append_v_offset <= (hdr_size - 1) &&
2017
0
         edmail->wrapped_hdr_size.physical_size > 0) {
2018
0
      parent_v_offset = stream->parent_start_offset;
2019
0
      parent_end_v_offset =
2020
0
        (stream->parent_start_offset +
2021
0
         edmail->wrapped_hdr_size.physical_size - 1);
2022
0
      copy_v_offset = prep_hdr_size;
2023
2024
0
      ret = merge_from_parent(edstream, parent_v_offset,
2025
0
            parent_end_v_offset,
2026
0
            copy_v_offset);
2027
0
      if (ret < 0)
2028
0
        return ret;
2029
0
      append_v_offset = (v_offset +
2030
0
             (stream->pos - stream->skip));
2031
0
      i_assert(append_v_offset <= hdr_size - 1);
2032
2033
0
      if (append_v_offset == hdr_size - 1) {
2034
        /* Strip final CR too when it is present */
2035
0
        if (stream->buffer != NULL &&
2036
0
            stream->buffer[stream->pos-1] == '\r') {
2037
0
          stream->pos--;
2038
0
          append_v_offset--;
2039
0
          ret--;
2040
0
        }
2041
2042
0
        i_assert(ret >= 0);
2043
0
        edstream->cur_header =
2044
0
          edmail->header_fields_appended;
2045
0
        edstream->cur_header_v_offset = append_v_offset;
2046
0
        if (!edstream->parent_buffer)
2047
0
          edstream->header_read = TRUE;
2048
0
      }
2049
2050
0
      if (ret != 0)
2051
0
        return ret;
2052
0
    } else {
2053
0
      edstream->header_read = TRUE;
2054
0
    }
2055
2056
    /* Merge appended headers */
2057
0
    ret = merge_modified_headers(edstream);
2058
0
    if (ret != 0)
2059
0
      return ret;
2060
0
  }
2061
2062
  /* Corner case that doesn't happen in practice (the original message is
2063
     never empty). */
2064
0
  if (edmail->wrapped_hdr_size.physical_size <=
2065
0
      (edmail->eoh_crlf ? 2 : 1)) {
2066
0
    parent_v_offset = stream->parent_start_offset;
2067
0
    copy_v_offset = edmail->hdr_size.physical_size;
2068
  /* Header does not come from original mail at all */
2069
0
  } else if (edmail->headers_parsed) {
2070
0
    parent_v_offset = (stream->parent_start_offset +
2071
0
           edmail->wrapped_hdr_size.physical_size -
2072
0
           (edmail->eoh_crlf ? 2 : 1));
2073
0
    copy_v_offset = edmail->hdr_size.physical_size;
2074
  /* Header comes partially from original mail and headers are added
2075
     between header and body. */
2076
0
  } else if (edmail->header_fields_appended != NULL) {
2077
0
    parent_v_offset = (stream->parent_start_offset +
2078
0
           edmail->wrapped_hdr_size.physical_size -
2079
0
           (edmail->eoh_crlf ? 2 : 1));
2080
0
    copy_v_offset = (edmail->hdr_size.physical_size +
2081
0
         edmail->wrapped_hdr_size.physical_size -
2082
0
         (edmail->eoh_crlf ? 2 : 1));
2083
  /* Header comes partially from original mail, but headers are only
2084
     prepended. */
2085
0
  } else {
2086
0
    parent_v_offset = stream->parent_start_offset;
2087
0
    copy_v_offset = edmail->hdr_size.physical_size;
2088
0
  }
2089
2090
0
  ret = merge_from_parent(edstream, parent_v_offset, (uoff_t)-1,
2091
0
        copy_v_offset);
2092
0
  if (ret != 0)
2093
0
    return ret;
2094
2095
0
  stream->istream.eof = stream->parent->eof;
2096
0
  edstream->eof = stream->parent->eof;
2097
0
  return -1;
2098
0
}
2099
2100
static void
2101
stream_reset_to(struct edit_mail_istream *edstream, uoff_t v_offset)
2102
0
{
2103
0
  edstream->istream.istream.v_offset = v_offset;
2104
0
  edstream->istream.skip = 0;
2105
0
  edstream->istream.pos = 0;
2106
0
  edstream->istream.buffer = NULL;
2107
0
  edstream->parent_buffer = FALSE;
2108
0
  edstream->eof = FALSE;
2109
0
  i_stream_seek(edstream->istream.parent, 0);
2110
0
}
2111
2112
static void
2113
edit_mail_istream_seek(struct istream_private *stream, uoff_t v_offset,
2114
           bool mark ATTR_UNUSED)
2115
0
{
2116
0
  struct edit_mail_istream *edstream =
2117
0
    (struct edit_mail_istream *)stream;
2118
0
  struct _header_field_index *cur_header;
2119
0
  struct edit_mail *edmail = edstream->mail;
2120
0
  uoff_t offset;
2121
2122
0
  edstream->header_read = FALSE;
2123
0
  edstream->cur_header = NULL;
2124
0
  edstream->cur_header_v_offset = 0;
2125
2126
  /* The beginning */
2127
0
  if (v_offset == 0) {
2128
0
    stream_reset_to(edstream, 0);
2129
2130
0
    if (edmail->header_fields_head !=
2131
0
        edmail->header_fields_appended)
2132
0
      edstream->cur_header = edmail->header_fields_head;
2133
0
    return;
2134
0
  }
2135
2136
  /* Inside (prepended) headers */
2137
0
  if (edmail->headers_parsed) {
2138
0
    offset = edmail->hdr_size.physical_size;
2139
0
  } else {
2140
0
    offset = (edmail->hdr_size.physical_size -
2141
0
        edmail->appended_hdr_size.physical_size);
2142
0
  }
2143
2144
0
  if (v_offset < offset) {
2145
0
    stream_reset_to(edstream, v_offset);
2146
2147
    /* Find the header */
2148
0
    cur_header = edmail->header_fields_head;
2149
0
    i_assert(cur_header != NULL &&
2150
0
       cur_header != edmail->header_fields_appended);
2151
0
    edstream->cur_header_v_offset = 0;
2152
0
    offset = cur_header->field->size;
2153
0
    while (v_offset > offset) {
2154
0
      cur_header = cur_header->next;
2155
0
      i_assert(cur_header != NULL &&
2156
0
         cur_header != edmail->header_fields_appended);
2157
2158
0
      edstream->cur_header_v_offset = offset;
2159
0
      offset += cur_header->field->size;
2160
0
    }
2161
2162
0
    edstream->cur_header = cur_header;
2163
0
    return;
2164
0
  }
2165
2166
0
  if (!edmail->headers_parsed) {
2167
    /* Inside original header */
2168
0
    offset = (edmail->hdr_size.physical_size -
2169
0
        edmail->appended_hdr_size.physical_size +
2170
0
        edmail->wrapped_hdr_size.physical_size);
2171
0
    if (v_offset < offset) {
2172
0
      stream_reset_to(edstream, v_offset);
2173
0
      return;
2174
0
    }
2175
2176
0
    edstream->header_read = TRUE;
2177
2178
    /* Inside appended header */
2179
0
    offset = (edmail->hdr_size.physical_size +
2180
0
        edmail->wrapped_hdr_size.physical_size);
2181
0
    if (v_offset < offset) {
2182
0
      stream_reset_to(edstream, v_offset);
2183
2184
0
      offset -= edmail->appended_hdr_size.physical_size;
2185
2186
0
      cur_header = edmail->header_fields_appended;
2187
0
      i_assert(cur_header != NULL);
2188
0
      edstream->cur_header_v_offset = offset;
2189
0
      offset += cur_header->field->size;
2190
2191
0
      while (v_offset > offset) {
2192
0
        cur_header = cur_header->next;
2193
0
        i_assert(cur_header != NULL);
2194
2195
0
        edstream->cur_header_v_offset = offset;
2196
0
        offset += cur_header->field->size;
2197
0
      }
2198
2199
0
      edstream->cur_header = cur_header;
2200
0
      return;
2201
0
    }
2202
0
  }
2203
2204
0
  stream_reset_to(edstream, v_offset);
2205
0
  edstream->cur_header = NULL;
2206
0
}
2207
2208
static void ATTR_NORETURN
2209
edit_mail_istream_sync(struct istream_private *stream ATTR_UNUSED)
2210
0
{
2211
0
  i_panic("edit-mail istream sync() not implemented");
2212
0
}
2213
2214
static int
2215
edit_mail_istream_stat(struct istream_private *stream, bool exact)
2216
0
{
2217
0
  struct edit_mail_istream *edstream =
2218
0
    (struct edit_mail_istream *)stream;
2219
0
  struct edit_mail *edmail = edstream->mail;
2220
0
  const struct stat *st;
2221
2222
  /* Stat the original stream */
2223
0
  if (i_stream_stat(stream->parent, exact, &st) < 0)
2224
0
    return -1;
2225
2226
0
  stream->statbuf = *st;
2227
0
  if (st->st_size == -1 || !exact)
2228
0
    return 0;
2229
2230
0
  if (!edmail->headers_parsed) {
2231
0
    if (!edmail->modified)
2232
0
      return 0;
2233
0
  } else {
2234
0
    stream->statbuf.st_size =
2235
0
      (edmail->wrapped_body_size.physical_size +
2236
0
       (edmail->eoh_crlf ? 2 : 1));
2237
0
  }
2238
2239
0
  stream->statbuf.st_size += (edmail->hdr_size.physical_size +
2240
0
            edmail->body_size.physical_size);
2241
0
  return 0;
2242
0
}
2243
2244
struct istream *edit_mail_istream_create(struct edit_mail *edmail)
2245
0
{
2246
0
  struct edit_mail_istream *edstream;
2247
0
  struct istream *wrapped = edmail->wrapped_stream;
2248
2249
0
  edstream = i_new(struct edit_mail_istream, 1);
2250
0
  edstream->pool = pool_alloconly_create(MEMPOOL_GROWING
2251
0
                 "edit mail stream", 4096);
2252
0
  edstream->mail = edmail;
2253
2254
0
  edstream->istream.max_buffer_size =
2255
0
    wrapped->real_stream->max_buffer_size;
2256
2257
0
  edstream->istream.iostream.destroy = edit_mail_istream_destroy;
2258
0
  edstream->istream.read = edit_mail_istream_read;
2259
0
  edstream->istream.seek = edit_mail_istream_seek;
2260
0
  edstream->istream.sync = edit_mail_istream_sync;
2261
0
  edstream->istream.stat = edit_mail_istream_stat;
2262
2263
0
  edstream->istream.istream.readable_fd = FALSE;
2264
0
  edstream->istream.istream.blocking = wrapped->blocking;
2265
0
  edstream->istream.istream.seekable = wrapped->seekable;
2266
2267
0
  if (edmail->header_fields_head != edmail->header_fields_appended)
2268
0
    edstream->cur_header = edmail->header_fields_head;
2269
2270
0
  i_stream_seek(wrapped, 0);
2271
2272
0
  return i_stream_create(&edstream->istream, wrapped, -1,
2273
0
             ISTREAM_HIDDEN_INPUTS_NONE, 0);
2274
0
}