~ubuntu-branches/ubuntu/quantal/dovecot/quantal-updates

« back to all changes in this revision

Viewing changes to src/lib-storage/index/imapc/imapc-mail-fetch.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 "istream.h"
 
6
#include "istream-header-filter.h"
 
7
#include "imap-arg.h"
 
8
#include "imap-date.h"
 
9
#include "imapc-client.h"
 
10
#include "imapc-mail.h"
 
11
#include "imapc-storage.h"
 
12
 
 
13
static void
 
14
imapc_mail_prefetch_callback(const struct imapc_command_reply *reply,
 
15
                             void *context)
 
16
{
 
17
        struct imapc_mail *mail = context;
 
18
        struct imapc_mailbox *mbox =
 
19
                (struct imapc_mailbox *)mail->imail.mail.mail.box;
 
20
 
 
21
        i_assert(mail->fetch_count > 0);
 
22
 
 
23
        if (--mail->fetch_count == 0) {
 
24
                struct imapc_mail *const *fetch_mails;
 
25
                unsigned int i, count;
 
26
 
 
27
                fetch_mails = array_get(&mbox->fetch_mails, &count);
 
28
                for (i = 0; i < count; i++) {
 
29
                        if (fetch_mails[i] == mail) {
 
30
                                array_delete(&mbox->fetch_mails, i, 1);
 
31
                                break;
 
32
                        }
 
33
                }
 
34
                i_assert(i != count);
 
35
                mail->fetching_fields = 0;
 
36
        }
 
37
 
 
38
        if (reply->state == IMAPC_COMMAND_STATE_OK)
 
39
                ;
 
40
        else if (reply->state == IMAPC_COMMAND_STATE_NO) {
 
41
                imapc_copy_error_from_reply(mbox->storage, MAIL_ERROR_PARAMS,
 
42
                                            reply);
 
43
        } else if (reply->state == IMAPC_COMMAND_STATE_DISCONNECTED) {
 
44
                /* The disconnection message was already logged */
 
45
                mail_storage_set_internal_error(&mbox->storage->storage);
 
46
        } else {
 
47
                mail_storage_set_critical(&mbox->storage->storage,
 
48
                        "imapc: Mail prefetch failed: %s", reply->text_full);
 
49
        }
 
50
        pool_unref(&mail->imail.mail.pool);
 
51
        imapc_client_stop(mbox->storage->client);
 
52
}
 
53
 
 
54
static int
 
55
imapc_mail_send_fetch(struct mail *_mail, enum mail_fetch_field fields)
 
56
{
 
57
        struct imapc_mail *mail = (struct imapc_mail *)_mail;
 
58
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 
59
        struct imapc_command *cmd;
 
60
        struct mail_index_view *view;
 
61
        string_t *str;
 
62
        uint32_t seq;
 
63
 
 
64
        if (_mail->lookup_abort != MAIL_LOOKUP_ABORT_NEVER)
 
65
                return -1;
 
66
 
 
67
        /* drop any fields that we may already be fetching currently */
 
68
        fields &= ~mail->fetching_fields;
 
69
        if (fields == 0)
 
70
                return 0;
 
71
 
 
72
        if (!_mail->saving) {
 
73
                /* if we already know that the mail is expunged,
 
74
                   don't try to FETCH it */
 
75
                view = mbox->delayed_sync_view != NULL ?
 
76
                        mbox->delayed_sync_view : mbox->box.view;
 
77
                if (!mail_index_lookup_seq(view, _mail->uid, &seq) ||
 
78
                    mail_index_is_expunged(view, seq)) {
 
79
                        mail_set_expunged(_mail);
 
80
                        return -1;
 
81
                }
 
82
        } else if (mbox->client_box == NULL) {
 
83
                /* opened as save-only. we'll need to fetch the mail,
 
84
                   so actually SELECT/EXAMINE the mailbox */
 
85
                i_assert(mbox->box.opened);
 
86
 
 
87
                if (imapc_mailbox_select(mbox) < 0)
 
88
                        return -1;
 
89
        }
 
90
 
 
91
        if ((fields & MAIL_FETCH_STREAM_BODY) != 0)
 
92
                fields |= MAIL_FETCH_STREAM_HEADER;
 
93
 
 
94
        str = t_str_new(64);
 
95
        str_printfa(str, "UID FETCH %u (", _mail->uid);
 
96
        if ((fields & MAIL_FETCH_RECEIVED_DATE) != 0)
 
97
                str_append(str, "INTERNALDATE ");
 
98
        if ((fields & MAIL_FETCH_PHYSICAL_SIZE) != 0)
 
99
                str_append(str, "RFC822.SIZE ");
 
100
        if ((fields & MAIL_FETCH_GUID) != 0) {
 
101
                str_append(str, mbox->guid_fetch_field_name);
 
102
                str_append_c(str, ' ');
 
103
        }
 
104
 
 
105
        if ((fields & MAIL_FETCH_STREAM_BODY) != 0)
 
106
                str_append(str, "BODY.PEEK[] ");
 
107
        else if ((fields & MAIL_FETCH_STREAM_HEADER) != 0)
 
108
                str_append(str, "BODY.PEEK[HEADER] ");
 
109
        str_truncate(str, str_len(str)-1);
 
110
        str_append_c(str, ')');
 
111
 
 
112
        pool_ref(mail->imail.mail.pool);
 
113
        mail->fetching_fields |= fields;
 
114
        if (mail->fetch_count++ == 0)
 
115
                array_append(&mbox->fetch_mails, &mail, 1);
 
116
 
 
117
        cmd = imapc_client_mailbox_cmd(mbox->client_box,
 
118
                                       imapc_mail_prefetch_callback, mail);
 
119
        imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_RETRIABLE);
 
120
        imapc_command_send(cmd, str_c(str));
 
121
        mail->imail.data.prefetch_sent = TRUE;
 
122
        return 0;
 
123
}
 
124
 
 
125
static void imapc_mail_cache_get(struct imapc_mail *mail,
 
126
                                 struct imapc_mail_cache *cache)
 
127
{
 
128
        if (mail->body_fetched)
 
129
                return;
 
130
 
 
131
        if (cache->fd != -1) {
 
132
                mail->fd = cache->fd;
 
133
                mail->imail.data.stream =
 
134
                        i_stream_create_fd(mail->fd, 0, FALSE);
 
135
                cache->fd = -1;
 
136
        } else if (cache->buf != NULL) {
 
137
                mail->body = cache->buf;
 
138
                mail->imail.data.stream =
 
139
                        i_stream_create_from_data(mail->body->data,
 
140
                                                  mail->body->used);
 
141
                cache->buf = NULL;
 
142
        } else {
 
143
                return;
 
144
        }
 
145
        mail->body_fetched = TRUE;
 
146
        imapc_mail_init_stream(mail, TRUE);
 
147
}
 
148
 
 
149
bool imapc_mail_prefetch(struct mail *_mail)
 
150
{
 
151
        struct imapc_mail *mail = (struct imapc_mail *)_mail;
 
152
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box;
 
153
        struct index_mail_data *data = &mail->imail.data;
 
154
        enum mail_fetch_field fields = 0;
 
155
 
 
156
        if (mbox->prev_mail_cache.uid == _mail->uid)
 
157
                imapc_mail_cache_get(mail, &mbox->prev_mail_cache);
 
158
 
 
159
        if ((data->wanted_fields & MAIL_FETCH_RECEIVED_DATE) != 0 &&
 
160
            data->received_date == (time_t)-1)
 
161
                fields |= MAIL_FETCH_RECEIVED_DATE;
 
162
        if ((data->wanted_fields & MAIL_FETCH_PHYSICAL_SIZE) != 0 &&
 
163
            data->physical_size == (uoff_t)-1 &&
 
164
            IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
 
165
                fields |= MAIL_FETCH_PHYSICAL_SIZE;
 
166
        if ((data->wanted_fields & MAIL_FETCH_GUID) != 0 &&
 
167
            data->guid == NULL && mbox->guid_fetch_field_name != NULL)
 
168
                fields |= MAIL_FETCH_GUID;
 
169
 
 
170
        if (data->stream == NULL && data->access_part != 0) {
 
171
                if ((data->access_part & (READ_BODY | PARSE_BODY)) != 0)
 
172
                        fields |= MAIL_FETCH_STREAM_BODY;
 
173
                else
 
174
                        fields |= MAIL_FETCH_STREAM_HEADER;
 
175
        }
 
176
        if (fields != 0) T_BEGIN {
 
177
                (void)imapc_mail_send_fetch(_mail, fields);
 
178
        } T_END;
 
179
        return !mail->imail.data.prefetch_sent;
 
180
}
 
181
 
 
182
static bool
 
183
imapc_mail_have_fields(struct imapc_mail *imail, enum mail_fetch_field fields)
 
184
{
 
185
        if ((fields & MAIL_FETCH_RECEIVED_DATE) != 0) {
 
186
                if (imail->imail.data.received_date == (time_t)-1)
 
187
                        return FALSE;
 
188
                fields &= ~MAIL_FETCH_RECEIVED_DATE;
 
189
        }
 
190
        if ((fields & MAIL_FETCH_PHYSICAL_SIZE) != 0) {
 
191
                if (imail->imail.data.physical_size == (uoff_t)-1)
 
192
                        return FALSE;
 
193
                fields &= ~MAIL_FETCH_PHYSICAL_SIZE;
 
194
        }
 
195
        if ((fields & MAIL_FETCH_GUID) != 0) {
 
196
                if (imail->imail.data.guid == NULL)
 
197
                        return FALSE;
 
198
                fields &= ~MAIL_FETCH_GUID;
 
199
        }
 
200
        if ((fields & (MAIL_FETCH_STREAM_HEADER |
 
201
                       MAIL_FETCH_STREAM_BODY)) != 0) {
 
202
                if (imail->imail.data.stream == NULL)
 
203
                        return FALSE;
 
204
                fields &= ~(MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY);
 
205
        }
 
206
        i_assert(fields == 0);
 
207
        return TRUE;
 
208
}
 
209
 
 
210
int imapc_mail_fetch(struct mail *_mail, enum mail_fetch_field fields)
 
211
{
 
212
        struct imapc_mail *imail = (struct imapc_mail *)_mail;
 
213
        struct imapc_mailbox *mbox =
 
214
                (struct imapc_mailbox *)_mail->box;
 
215
        int ret;
 
216
 
 
217
        if ((fields & MAIL_FETCH_GUID) != 0 &&
 
218
            mbox->guid_fetch_field_name == NULL) {
 
219
                mail_storage_set_error(_mail->box->storage,
 
220
                        MAIL_ERROR_NOTPOSSIBLE,
 
221
                        "Message GUID not available in this server");
 
222
                return -1;
 
223
        }
 
224
 
 
225
        T_BEGIN {
 
226
                ret = imapc_mail_send_fetch(_mail, fields);
 
227
        } T_END;
 
228
        if (ret < 0)
 
229
                return -1;
 
230
 
 
231
        /* we'll continue waiting until we've got all the fields we wanted,
 
232
           or until all FETCH replies have been received (i.e. some FETCHes
 
233
           failed) */
 
234
        while (!imapc_mail_have_fields(imail, fields) && imail->fetch_count > 0)
 
235
                imapc_storage_run(mbox->storage);
 
236
        return 0;
 
237
}
 
238
 
 
239
static bool imapc_find_lfile_arg(const struct imapc_untagged_reply *reply,
 
240
                                 const struct imap_arg *arg, int *fd_r)
 
241
{
 
242
        const struct imap_arg *list;
 
243
        unsigned int i, count;
 
244
 
 
245
        for (i = 0; i < reply->file_args_count; i++) {
 
246
                const struct imapc_arg_file *farg = &reply->file_args[i];
 
247
 
 
248
                if (farg->parent_arg == arg->parent &&
 
249
                    imap_arg_get_list_full(arg->parent, &list, &count) &&
 
250
                    farg->list_idx < count && &list[farg->list_idx] == arg) {
 
251
                        *fd_r = farg->fd;
 
252
                        return TRUE;
 
253
                }
 
254
        }
 
255
        return FALSE;
 
256
}
 
257
 
 
258
static void imapc_stream_filter(struct istream **input)
 
259
{
 
260
        static const char *imapc_hide_headers[] = {
 
261
                /* Added by MS Exchange 2010 when \Flagged flag is set.
 
262
                   This violates IMAP guarantee of messages being immutable. */
 
263
                "X-Message-Flag"
 
264
        };
 
265
        struct istream *filter_input;
 
266
 
 
267
        filter_input = i_stream_create_header_filter(*input,
 
268
                HEADER_FILTER_EXCLUDE,
 
269
                imapc_hide_headers, N_ELEMENTS(imapc_hide_headers),
 
270
                null_header_filter_callback, NULL);
 
271
        i_stream_unref(input);
 
272
        *input = filter_input;
 
273
}
 
274
 
 
275
void imapc_mail_init_stream(struct imapc_mail *mail, bool have_body)
 
276
{
 
277
        struct index_mail *imail = &mail->imail;
 
278
        struct mail *_mail = &imail->mail.mail;
 
279
        struct istream *input;
 
280
        uoff_t size;
 
281
        int ret;
 
282
 
 
283
        i_stream_set_name(imail->data.stream,
 
284
                          t_strdup_printf("imapc mail uid=%u", _mail->uid));
 
285
        index_mail_set_read_buffer_size(_mail, imail->data.stream);
 
286
 
 
287
        imapc_stream_filter(&imail->data.stream);
 
288
        if (imail->mail.v.istream_opened != NULL) {
 
289
                if (imail->mail.v.istream_opened(_mail,
 
290
                                                 &imail->data.stream) < 0) {
 
291
                        index_mail_close_streams(imail);
 
292
                        return;
 
293
                }
 
294
        } else if (have_body) {
 
295
                ret = i_stream_get_size(imail->data.stream, TRUE, &size);
 
296
                if (ret < 0) {
 
297
                        index_mail_close_streams(imail);
 
298
                        return;
 
299
                }
 
300
                i_assert(ret != 0);
 
301
                imail->data.physical_size = size;
 
302
                /* we'll assume that the remote server is working properly and
 
303
                   sending CRLF linefeeds */
 
304
                imail->data.virtual_size = size;
 
305
        }
 
306
 
 
307
        imail->data.stream_has_only_header = !have_body;
 
308
        if (index_mail_init_stream(imail, NULL, NULL, &input) < 0)
 
309
                index_mail_close_streams(imail);
 
310
}
 
311
 
 
312
static void
 
313
imapc_fetch_stream(struct imapc_mail *mail,
 
314
                   const struct imapc_untagged_reply *reply,
 
315
                   const struct imap_arg *arg, bool body)
 
316
{
 
317
        struct index_mail *imail = &mail->imail;
 
318
        const char *value;
 
319
        int fd;
 
320
 
 
321
        if (imail->data.stream != NULL) {
 
322
                if (!body)
 
323
                        return;
 
324
                /* maybe the existing stream has no body. replace it. */
 
325
                index_mail_close_streams(imail);
 
326
                if (mail->fd != -1) {
 
327
                        if (close(mail->fd) < 0)
 
328
                                i_error("close(imapc mail) failed: %m");
 
329
                        mail->fd = -1;
 
330
                }
 
331
        }
 
332
 
 
333
        if (arg->type == IMAP_ARG_LITERAL_SIZE) {
 
334
                if (!imapc_find_lfile_arg(reply, arg, &fd))
 
335
                        return;
 
336
                if ((fd = dup(fd)) == -1) {
 
337
                        i_error("dup() failed: %m");
 
338
                        return;
 
339
                }
 
340
                mail->fd = fd;
 
341
                imail->data.stream = i_stream_create_fd(fd, 0, FALSE);
 
342
        } else {
 
343
                if (!imap_arg_get_nstring(arg, &value))
 
344
                        return;
 
345
                if (value == NULL) {
 
346
                        mail_set_expunged(&imail->mail.mail);
 
347
                        return;
 
348
                }
 
349
                if (mail->body == NULL) {
 
350
                        mail->body = buffer_create_dynamic(default_pool,
 
351
                                                           arg->str_len + 1);
 
352
                }
 
353
                buffer_set_used_size(mail->body, 0);
 
354
                buffer_append(mail->body, value, arg->str_len);
 
355
                imail->data.stream = i_stream_create_from_data(mail->body->data,
 
356
                                                               mail->body->used);
 
357
        }
 
358
        mail->body_fetched = body;
 
359
 
 
360
        imapc_mail_init_stream(mail, body);
 
361
}
 
362
 
 
363
void imapc_mail_fetch_update(struct imapc_mail *mail,
 
364
                             const struct imapc_untagged_reply *reply,
 
365
                             const struct imap_arg *args)
 
366
{
 
367
        struct imapc_mailbox *mbox =
 
368
                (struct imapc_mailbox *)mail->imail.mail.mail.box;
 
369
        const char *key, *value;
 
370
        unsigned int i;
 
371
        uoff_t size;
 
372
        time_t t;
 
373
        int tz;
 
374
        bool match = FALSE;
 
375
 
 
376
        for (i = 0; args[i].type != IMAP_ARG_EOL; i += 2) {
 
377
                if (!imap_arg_get_atom(&args[i], &key) ||
 
378
                    args[i+1].type == IMAP_ARG_EOL)
 
379
                        break;
 
380
 
 
381
                if (strcasecmp(key, "BODY[]") == 0) {
 
382
                        imapc_fetch_stream(mail, reply, &args[i+1], TRUE);
 
383
                        match = TRUE;
 
384
                } else if (strcasecmp(key, "BODY[HEADER]") == 0) {
 
385
                        imapc_fetch_stream(mail, reply, &args[i+1], FALSE);
 
386
                        match = TRUE;
 
387
                } else if (strcasecmp(key, "INTERNALDATE") == 0) {
 
388
                        if (imap_arg_get_astring(&args[i+1], &value) &&
 
389
                            imap_parse_datetime(value, &t, &tz))
 
390
                                mail->imail.data.received_date = t;
 
391
                        match = TRUE;
 
392
                } else if (strcasecmp(key, "RFC822.SIZE") == 0) {
 
393
                        if (imap_arg_get_atom(&args[i+1], &value) &&
 
394
                            str_to_uoff(value, &size) == 0 &&
 
395
                            IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_RFC822_SIZE))
 
396
                                mail->imail.data.physical_size = size;
 
397
                        match = TRUE;
 
398
                } else if (strcasecmp(key, "X-GM-MSGID") == 0 ||
 
399
                           strcasecmp(key, "X-GUID") == 0) {
 
400
                        if (imap_arg_get_astring(&args[i+1], &value)) {
 
401
                                mail->imail.data.guid =
 
402
                                        p_strdup(mail->imail.mail.pool, value);
 
403
                        }
 
404
                        match = TRUE;
 
405
                }
 
406
        }
 
407
        if (!match) {
 
408
                /* this is only a FETCH FLAGS update for the wanted mail */
 
409
        } else {
 
410
                imapc_client_stop(mbox->storage->client);
 
411
        }
 
412
}