~ubuntu-branches/ubuntu/wily/dovecot/wily-proposed

« back to all changes in this revision

Viewing changes to pigeonhole/src/lib-sieve/util/edit-mail.c

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2015-09-14 13:58:42 UTC
  • mfrom: (4.1.54 sid)
  • Revision ID: package-import@ubuntu.com-20150914135842-jhpp689shskxt0hu
Tags: 1:2.2.18-2ubuntu1
* Merge with Debian (after 552 days); remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
* d/control: Drop dovecot-postfix package as its no longer required.
* Rename init.d script to work with the dh_installinit --name option, so
  that it comes back. (LP: #1323274)
* d/dovecot-core.config: Drop db_input for ssl-cert-exists; this message
  not actually an error, is documented in the README.Debian, and blocks
  automated upgrades (LP: #1278897).

Show diffs side-by-side

added added

removed removed

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