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

« back to all changes in this revision

Viewing changes to src/lib-storage/index/imapc/imapc-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) 2011-2012 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "str.h"
 
5
#include "hex-binary.h"
 
6
#include "sha1.h"
 
7
#include "istream.h"
 
8
#include "imap-envelope.h"
 
9
#include "imapc-msgmap.h"
 
10
#include "imapc-mail.h"
 
11
#include "imapc-client.h"
 
12
#include "imapc-storage.h"
 
13
 
 
14
struct mail *
 
15
imapc_mail_alloc(struct mailbox_transaction_context *t,
 
16
                 enum mail_fetch_field wanted_fields,
 
17
                 struct mailbox_header_lookup_ctx *wanted_headers)
 
18
{
 
19
        struct imapc_mail *mail;
 
20
        pool_t pool;
 
21
 
 
22
        pool = pool_alloconly_create("mail", 2048);
 
23
        mail = p_new(pool, struct imapc_mail, 1);
 
24
        mail->imail.mail.pool = pool;
 
25
        mail->fd = -1;
 
26
 
 
27
        index_mail_init(&mail->imail, t, wanted_fields, wanted_headers);
 
28
        return &mail->imail.mail.mail;
 
29
}
 
30
 
 
31
static bool imapc_mail_is_expunged(struct mail *_mail)
 
32
{
 
33
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 
34
        struct imapc_msgmap *msgmap;
 
35
        uint32_t lseq, rseq;
 
36
 
 
37
        if (mbox->sync_view != NULL) {
 
38
                /* check if another session has already expunged it */
 
39
                if (!mail_index_lookup_seq(mbox->sync_view, _mail->uid, &lseq))
 
40
                        return TRUE;
 
41
        }
 
42
 
 
43
        /* check if we've received EXPUNGE for it */
 
44
        msgmap = imapc_client_mailbox_get_msgmap(mbox->client_box);
 
45
        if (!imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq))
 
46
                return TRUE;
 
47
 
 
48
        /* we may be running against a server that hasn't bothered sending
 
49
           us an EXPUNGE. see if NOOP sends it. */
 
50
        imapc_mailbox_noop(mbox);
 
51
 
 
52
        return !imapc_msgmap_uid_to_rseq(msgmap, _mail->uid, &rseq);
 
53
}
 
54
 
 
55
static int imapc_mail_failed(struct mail *mail, const char *field)
 
56
{
 
57
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->box;
 
58
 
 
59
        if (mail->expunged || imapc_mail_is_expunged(mail)) {
 
60
                mail_set_expunged(mail);
 
61
                return -1;
 
62
        } else if (!imapc_client_mailbox_is_opened(mbox->client_box)) {
 
63
                /* we've already logged a disconnection error */
 
64
                mail_storage_set_internal_error(mail->box->storage);
 
65
                return -1;
 
66
        } else {
 
67
                mail_storage_set_critical(mail->box->storage,
 
68
                        "imapc: Remote server didn't send %s for UID %u in %s",
 
69
                        field, mail->uid, mail->box->vname);
 
70
                return 0;
 
71
        }
 
72
}
 
73
 
 
74
static int imapc_mail_get_received_date(struct mail *_mail, time_t *date_r)
 
75
{
 
76
        struct index_mail *mail = (struct index_mail *)_mail;
 
77
        struct index_mail_data *data = &mail->data;
 
78
 
 
79
        if (index_mail_get_received_date(_mail, date_r) == 0)
 
80
                return 0;
 
81
 
 
82
        if (data->received_date == (time_t)-1) {
 
83
                if (imapc_mail_fetch(_mail, MAIL_FETCH_RECEIVED_DATE) < 0)
 
84
                        return -1;
 
85
                if (data->received_date == (time_t)-1) {
 
86
                        if (imapc_mail_failed(_mail, "INTERNALDATE") < 0)
 
87
                                return -1;
 
88
                        /* assume that the server never returns INTERNALDATE
 
89
                           for this mail (see BODY[] failure handling) */
 
90
                        data->received_date = 0;
 
91
                }
 
92
        }
 
93
        *date_r = data->received_date;
 
94
        return 0;
 
95
}
 
96
 
 
97
static int imapc_mail_get_save_date(struct mail *_mail, time_t *date_r)
 
98
{
 
99
        struct index_mail *mail = (struct index_mail *)_mail;
 
100
        struct index_mail_data *data = &mail->data;
 
101
 
 
102
        if (data->save_date == (time_t)-1) {
 
103
                /* FIXME */
 
104
                return -1;
 
105
        }
 
106
        *date_r = data->save_date;
 
107
        return 0;
 
108
}
 
109
 
 
110
static int imapc_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
 
111
{
 
112
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 
113
        struct index_mail *mail = (struct index_mail *)_mail;
 
114
        struct index_mail_data *data = &mail->data;
 
115
        struct istream *input;
 
116
        uoff_t old_offset;
 
117
        int ret;
 
118
 
 
119
        if (data->physical_size == (uoff_t)-1) {
 
120
                (void)index_mail_get_physical_size(_mail, size_r);
 
121
                if (data->physical_size != (uoff_t)-1) {
 
122
                        *size_r = data->physical_size;
 
123
                        return 0;
 
124
                }
 
125
        }
 
126
 
 
127
        if (IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE) &&
 
128
            data->stream == NULL) {
 
129
                /* trust RFC822.SIZE to be correct */
 
130
                if (imapc_mail_fetch(_mail, MAIL_FETCH_PHYSICAL_SIZE) < 0)
 
131
                        return -1;
 
132
                if (data->physical_size == (uoff_t)-1) {
 
133
                        if (imapc_mail_failed(_mail, "RFC822.SIZE") < 0)
 
134
                                return -1;
 
135
                        /* assume that the server never returns RFC822.SIZE
 
136
                           for this mail (see BODY[] failure handling) */
 
137
                        data->physical_size = 0;
 
138
                }
 
139
                *size_r = data->physical_size;
 
140
                return 0;
 
141
        }
 
142
 
 
143
        old_offset = data->stream == NULL ? 0 : data->stream->v_offset;
 
144
        if (mail_get_stream(_mail, NULL, NULL, &input) < 0)
 
145
                return -1;
 
146
        i_stream_seek(data->stream, old_offset);
 
147
 
 
148
        ret = i_stream_get_size(data->stream, TRUE,
 
149
                                &data->physical_size);
 
150
        if (ret <= 0) {
 
151
                i_assert(ret != 0);
 
152
                mail_storage_set_critical(_mail->box->storage,
 
153
                                          "imapc: stat(%s) failed: %m",
 
154
                                          i_stream_get_name(data->stream));
 
155
                return -1;
 
156
        }
 
157
        *size_r = data->physical_size;
 
158
        return 0;
 
159
}
 
160
 
 
161
static int
 
162
imapc_mail_get_stream(struct mail *_mail, bool get_body,
 
163
                      struct message_size *hdr_size,
 
164
                      struct message_size *body_size, struct istream **stream_r)
 
165
{
 
166
        struct imapc_mail *mail = (struct imapc_mail *)_mail;
 
167
        struct index_mail_data *data = &mail->imail.data;
 
168
        enum mail_fetch_field fetch_field;
 
169
 
 
170
        if (get_body && !mail->body_fetched &&
 
171
            mail->imail.data.stream != NULL) {
 
172
                /* we've fetched the header, but we need the body now too */
 
173
                index_mail_close_streams(&mail->imail);
 
174
        }
 
175
 
 
176
        if (data->stream == NULL) {
 
177
                if (!data->initialized) {
 
178
                        /* coming here from mail_set_seq() */
 
179
                        return mail_set_aborted(_mail);
 
180
                }
 
181
                fetch_field = get_body ||
 
182
                        (data->access_part & READ_BODY) != 0 ?
 
183
                        MAIL_FETCH_STREAM_BODY : MAIL_FETCH_STREAM_HEADER;
 
184
                if (imapc_mail_fetch(_mail, fetch_field) < 0)
 
185
                        return -1;
 
186
 
 
187
                if (data->stream == NULL) {
 
188
                        if (imapc_mail_failed(_mail, "BODY[]") < 0)
 
189
                                return -1;
 
190
                        i_assert(data->stream == NULL);
 
191
 
 
192
                        /* this could be either a temporary server bug, or the
 
193
                           server may permanently just not return anything for
 
194
                           this mail. the latter happens at least with Exchange
 
195
                           when trying to fetch calendar "mails", so we'll just
 
196
                           return them as empty mails instead of disconnecting
 
197
                           the client. */
 
198
                        mail->body_fetched = TRUE;
 
199
                        data->stream = i_stream_create_from_data(NULL, 0);
 
200
                        imapc_mail_init_stream(mail, TRUE);
 
201
                }
 
202
        }
 
203
 
 
204
        return index_mail_init_stream(&mail->imail, hdr_size, body_size,
 
205
                                      stream_r);
 
206
}
 
207
 
 
208
static bool
 
209
imapc_mail_has_headers_in_cache(struct index_mail *mail,
 
210
                                struct mailbox_header_lookup_ctx *headers)
 
211
{
 
212
        struct mail *_mail = &mail->mail.mail;
 
213
        unsigned int i;
 
214
 
 
215
        for (i = 0; i < headers->count; i++) {
 
216
                if (mail_cache_field_exists(_mail->transaction->cache_view,
 
217
                                            _mail->seq, headers->idx[i]) <= 0)
 
218
                        return FALSE;
 
219
        }
 
220
        return TRUE;
 
221
}
 
222
 
 
223
static void index_mail_update_access_parts(struct index_mail *mail)
 
224
{
 
225
        struct mail *_mail = &mail->mail.mail;
 
226
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 
227
        struct index_mail_data *data = &mail->data;
 
228
        struct mailbox_header_lookup_ctx *header_ctx;
 
229
        time_t date;
 
230
        uoff_t size;
 
231
 
 
232
        if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0)
 
233
                (void)index_mail_get_received_date(_mail, &date);
 
234
        if ((data->wanted_fields & MAIL_FETCH_PHYSICAL_SIZE) != 0) {
 
235
                if (index_mail_get_physical_size(_mail, &size) < 0 &&
 
236
                    !IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
 
237
                        data->access_part |= READ_HDR | READ_BODY;
 
238
        }
 
239
 
 
240
        if (data->access_part == 0 && data->wanted_headers != NULL) {
 
241
                /* see if all wanted headers exist in cache */
 
242
                if (!imapc_mail_has_headers_in_cache(mail, data->wanted_headers))
 
243
                        data->access_part |= PARSE_HDR;
 
244
        }
 
245
        if (data->access_part == 0 &&
 
246
            (data->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0) {
 
247
                /* the common code already checked this partially,
 
248
                   but we need a guaranteed correct answer */
 
249
                header_ctx = mailbox_header_lookup_init(_mail->box,
 
250
                                                        imap_envelope_headers);
 
251
                if (!imapc_mail_has_headers_in_cache(mail, header_ctx))
 
252
                        data->access_part |= PARSE_HDR;
 
253
                mailbox_header_lookup_unref(&header_ctx);
 
254
        }
 
255
}
 
256
 
 
257
static void imapc_mail_set_seq(struct mail *_mail, uint32_t seq, bool saving)
 
258
{
 
259
        struct imapc_mail *imail = (struct imapc_mail *)_mail;
 
260
        struct index_mail *mail = &imail->imail;
 
261
 
 
262
        index_mail_set_seq(_mail, seq, saving);
 
263
 
 
264
        /* searching code handles prefetching internally,
 
265
           elsewhere we want to do it immediately */
 
266
        if (!mail->search_mail && !_mail->saving)
 
267
                (void)imapc_mail_prefetch(_mail);
 
268
}
 
269
 
 
270
static void
 
271
imapc_mail_add_temp_wanted_fields(struct mail *_mail,
 
272
                                  enum mail_fetch_field fields,
 
273
                                  struct mailbox_header_lookup_ctx *headers)
 
274
{
 
275
        struct index_mail *mail = (struct index_mail *)_mail;
 
276
 
 
277
        index_mail_add_temp_wanted_fields(_mail, fields, headers);
 
278
        index_mail_update_access_parts(mail);
 
279
}
 
280
 
 
281
static void imapc_mail_close(struct mail *_mail)
 
282
{
 
283
        struct imapc_mail *mail = (struct imapc_mail *)_mail;
 
284
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 
285
        struct imapc_mail_cache *cache = &mbox->prev_mail_cache;
 
286
 
 
287
        while (mail->fetch_count > 0)
 
288
                imapc_storage_run(mbox->storage);
 
289
 
 
290
        index_mail_close(_mail);
 
291
 
 
292
        if (mail->body_fetched) {
 
293
                imapc_mail_cache_free(cache);
 
294
                cache->uid = _mail->uid;
 
295
                if (cache->fd != -1) {
 
296
                        cache->fd = mail->fd;
 
297
                        mail->fd = -1;
 
298
                } else {
 
299
                        cache->buf = mail->body;
 
300
                        mail->body = NULL;
 
301
                }
 
302
        }
 
303
        if (mail->fd != -1) {
 
304
                if (close(mail->fd) < 0)
 
305
                        i_error("close(imapc mail) failed: %m");
 
306
                mail->fd = -1;
 
307
        }
 
308
        if (mail->body != NULL)
 
309
                buffer_free(&mail->body);
 
310
}
 
311
 
 
312
static int imapc_mail_get_hdr_hash(struct index_mail *imail)
 
313
{
 
314
        struct istream *input;
 
315
        const unsigned char *data;
 
316
        size_t size;
 
317
        uoff_t old_offset;
 
318
        struct sha1_ctxt sha1_ctx;
 
319
        unsigned char sha1_output[SHA1_RESULTLEN];
 
320
        const char *sha1_str;
 
321
 
 
322
        sha1_init(&sha1_ctx);
 
323
        old_offset = imail->data.stream == NULL ? 0 :
 
324
                imail->data.stream->v_offset;
 
325
        if (mail_get_hdr_stream(&imail->mail.mail, NULL, &input) < 0)
 
326
                return -1;
 
327
        while (i_stream_read_data(input, &data, &size, 0) > 0) {
 
328
                sha1_loop(&sha1_ctx, data, size);
 
329
                i_stream_skip(input, size);
 
330
        }
 
331
        i_stream_seek(imail->data.stream, old_offset);
 
332
        sha1_result(&sha1_ctx, sha1_output);
 
333
 
 
334
        sha1_str = binary_to_hex(sha1_output, sizeof(sha1_output));
 
335
        imail->data.guid = p_strdup(imail->data_pool, sha1_str);
 
336
        return 0;
 
337
}
 
338
 
 
339
static int imapc_mail_get_guid(struct mail *_mail, const char **value_r)
 
340
{
 
341
        struct index_mail *imail = (struct index_mail *)_mail;
 
342
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 
343
        const enum index_cache_field cache_idx =
 
344
                imail->ibox->cache_fields[MAIL_CACHE_GUID].idx;
 
345
        string_t *str;
 
346
 
 
347
        if (imail->data.guid != NULL) {
 
348
                *value_r = imail->data.guid;
 
349
                return 0;
 
350
        }
 
351
 
 
352
        str = str_new(imail->data_pool, 64);
 
353
        if (mail_cache_lookup_field(_mail->transaction->cache_view,
 
354
                                    str, imail->mail.mail.seq, cache_idx) > 0) {
 
355
                *value_r = str_c(str);
 
356
                return 0;
 
357
        }
 
358
 
 
359
        /* GUID not in cache, fetch it */
 
360
        if (mbox->guid_fetch_field_name != NULL) {
 
361
                if (imapc_mail_fetch(_mail, MAIL_FETCH_GUID) < 0)
 
362
                        return -1;
 
363
                if (imail->data.guid == NULL) {
 
364
                        (void)imapc_mail_failed(_mail, mbox->guid_fetch_field_name);
 
365
                        return -1;
 
366
                }
 
367
        } else {
 
368
                /* use hash of message headers as the GUID */
 
369
                if (imapc_mail_get_hdr_hash(imail) < 0)
 
370
                        return -1;
 
371
        }
 
372
 
 
373
        index_mail_cache_add_idx(imail, cache_idx,
 
374
                                 imail->data.guid, strlen(imail->data.guid)+1);
 
375
        *value_r = imail->data.guid;
 
376
        return 0;
 
377
}
 
378
 
 
379
static int
 
380
imapc_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
 
381
                       const char **value_r)
 
382
{
 
383
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 
384
 
 
385
        switch (field) {
 
386
        case MAIL_FETCH_GUID:
 
387
                if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GUID_FORCED) &&
 
388
                    mbox->guid_fetch_field_name == NULL) {
 
389
                        /* GUIDs not supported by server */
 
390
                        break;
 
391
                }
 
392
                *value_r = "";
 
393
                return imapc_mail_get_guid(_mail, value_r);
 
394
        default:
 
395
                break;
 
396
        }
 
397
 
 
398
        return index_mail_get_special(_mail, field, value_r);
 
399
}
 
400
 
 
401
struct mail_vfuncs imapc_mail_vfuncs = {
 
402
        imapc_mail_close,
 
403
        index_mail_free,
 
404
        imapc_mail_set_seq,
 
405
        index_mail_set_uid,
 
406
        index_mail_set_uid_cache_updates,
 
407
        imapc_mail_prefetch,
 
408
        index_mail_precache,
 
409
        imapc_mail_add_temp_wanted_fields,
 
410
 
 
411
        index_mail_get_flags,
 
412
        index_mail_get_keywords,
 
413
        index_mail_get_keyword_indexes,
 
414
        index_mail_get_modseq,
 
415
        index_mail_get_parts,
 
416
        index_mail_get_date,
 
417
        imapc_mail_get_received_date,
 
418
        imapc_mail_get_save_date,
 
419
        index_mail_get_virtual_size,
 
420
        imapc_mail_get_physical_size,
 
421
        index_mail_get_first_header,
 
422
        index_mail_get_headers,
 
423
        index_mail_get_header_stream,
 
424
        imapc_mail_get_stream,
 
425
        imapc_mail_get_special,
 
426
        index_mail_get_real_mail,
 
427
        index_mail_update_flags,
 
428
        index_mail_update_keywords,
 
429
        index_mail_update_modseq,
 
430
        NULL,
 
431
        index_mail_expunge,
 
432
        index_mail_set_cache_corrupted,
 
433
        index_mail_opened
 
434
};