~james-page/ubuntu/raring/dovecot/autopkgtest

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2012-06-11 11:11:54 UTC
  • mfrom: (1.15.2) (4.1.27 sid)
  • Revision ID: package-import@ubuntu.com-20120611111154-678cwbdj6ktgsv1h
Tags: 1:2.1.7-1ubuntu1
* Merge from Debian unstable, 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.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + 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/{control,rules}: enable PIE hardening.
  + 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: Added Pre-Depends: dpkg (>= 1.15.6) to dovecot-dbg to support
    xz compression in Ubuntu.
  + d/control: Demote dovecot-common Recommends: to Suggests: to prevent
    install of extra packages on upgrade.
  + d/patches/dovecot-drac.patch: Updated with version for dovecot >= 2.0.0.
  + d/control: Drop B-D on systemd.
* Dropped changes:
  + d/patches/fix-racey-restart.patch: part of 2.1.x, no longer required.

Show diffs side-by-side

added added

removed removed

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