1
1
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
4
#include "seq-range-array.h"
4
5
#include "commands.h"
6
#include "mail-search-build.h"
7
#include "imap-seqset.h"
8
#include "imap-fetch.h"
5
9
#include "imap-sync.h"
7
bool cmd_select_full(struct client_command_context *cmd, bool readonly)
9
struct client *client = cmd->client;
13
struct imap_select_context {
14
struct client_command_context *cmd;
10
15
struct mail_storage *storage;
11
16
struct mailbox *box;
18
struct imap_fetch_context *fetch_ctx;
20
uint32_t qresync_uid_validity;
21
uint64_t qresync_modseq;
22
ARRAY_TYPE(seq_range) qresync_known_uids;
23
ARRAY_TYPE(uint32_t) qresync_sample_seqset;
24
ARRAY_TYPE(uint32_t) qresync_sample_uidset;
26
unsigned int condstore:1;
29
static int select_qresync_get_uids(struct imap_select_context *ctx,
30
const ARRAY_TYPE(seq_range) *seqset,
31
const ARRAY_TYPE(seq_range) *uidset)
33
const struct seq_range *seq_range, *uid_range;
34
struct seq_range_iter seq_iter;
35
unsigned int i, seq_count, uid_count, diff, n = 0;
38
/* change all n:m ranges to n,m and store the results */
39
seq_range = array_get(seqset, &seq_count);
40
uid_range = array_get(uidset, &uid_count);
42
seq_range_array_iter_init(&seq_iter, seqset);
43
i_array_init(&ctx->qresync_sample_uidset, uid_count);
44
i_array_init(&ctx->qresync_sample_seqset, uid_count);
45
for (i = 0; i < uid_count; i++) {
46
if (!seq_range_array_iter_nth(&seq_iter, n++, &seq))
48
array_append(&ctx->qresync_sample_uidset,
49
&uid_range[i].seq1, 1);
50
array_append(&ctx->qresync_sample_seqset, &seq, 1);
52
diff = uid_range[i].seq2 - uid_range[i].seq1;
55
if (!seq_range_array_iter_nth(&seq_iter, n++, &seq))
58
array_append(&ctx->qresync_sample_uidset,
59
&uid_range[i].seq2, 1);
60
array_append(&ctx->qresync_sample_seqset, &seq, 1);
63
if (seq_range_array_iter_nth(&seq_iter, n, &seq))
69
select_parse_qresync(struct imap_select_context *ctx,
70
const struct imap_arg *args)
72
ARRAY_TYPE(seq_range) seqset, uidset;
75
if ((ctx->cmd->client->enabled_features &
76
MAILBOX_FEATURE_QRESYNC) == 0) {
77
client_send_command_error(ctx->cmd, "QRESYNC not enabled");
80
if (args->type != IMAP_ARG_LIST) {
81
client_send_command_error(ctx->cmd,
82
"QRESYNC parameters missing");
85
args = IMAP_ARG_LIST_ARGS(args);
86
for (count = 0; args[count].type != IMAP_ARG_EOL; count++) ;
88
if (count < 2 || count > 4 ||
89
args[0].type != IMAP_ARG_ATOM ||
90
args[1].type != IMAP_ARG_ATOM ||
91
(count > 2 && args[2].type != IMAP_ARG_ATOM) ||
92
(count > 3 && args[3].type != IMAP_ARG_LIST)) {
93
client_send_command_error(ctx->cmd,
94
"Invalid QRESYNC parameters");
97
ctx->qresync_uid_validity =
98
strtoul(IMAP_ARG_STR_NONULL(&args[0]), NULL, 10);
100
strtoull(IMAP_ARG_STR_NONULL(&args[1]), NULL, 10);
102
i_array_init(&ctx->qresync_known_uids, 64);
103
if (imap_seq_set_parse(IMAP_ARG_STR_NONULL(&args[2]),
104
&ctx->qresync_known_uids) < 0) {
105
client_send_command_error(ctx->cmd,
106
"Invalid QRESYNC known-uids");
110
i_array_init(&ctx->qresync_known_uids, 64);
111
seq_range_array_add_range(&ctx->qresync_known_uids,
115
args = IMAP_ARG_LIST_ARGS(&args[3]);
116
if (args[0].type != IMAP_ARG_ATOM ||
117
args[1].type != IMAP_ARG_ATOM ||
118
args[2].type != IMAP_ARG_EOL) {
119
client_send_command_error(ctx->cmd,
120
"Invalid QRESYNC known set parameters");
123
t_array_init(&seqset, 32);
124
if (imap_seq_set_parse(IMAP_ARG_STR_NONULL(&args[0]),
126
client_send_command_error(ctx->cmd,
127
"Invalid QRESYNC known-sequence-set");
130
t_array_init(&uidset, 32);
131
if (imap_seq_set_parse(IMAP_ARG_STR_NONULL(&args[1]),
133
client_send_command_error(ctx->cmd,
134
"Invalid QRESYNC known-uid-set");
137
if (select_qresync_get_uids(ctx, &seqset, &uidset) < 0) {
138
client_send_command_error(ctx->cmd,
139
"Invalid QRESYNC sets");
147
select_parse_options(struct imap_select_context *ctx,
148
const struct imap_arg *args)
152
while (args->type != IMAP_ARG_EOL) {
153
if (args->type != IMAP_ARG_ATOM) {
154
client_send_command_error(ctx->cmd,
155
"SELECT options contain non-atoms.");
158
name = t_str_ucase(IMAP_ARG_STR(args));
161
if (strcmp(name, "CONDSTORE") == 0)
162
ctx->condstore = TRUE;
163
else if (strcmp(name, "QRESYNC") == 0) {
164
if (!select_parse_qresync(ctx, args))
168
client_send_command_error(ctx->cmd,
169
"Unknown FETCH modifier");
176
static void select_context_free(struct imap_select_context *ctx)
178
if (array_is_created(&ctx->qresync_known_uids))
179
array_free(&ctx->qresync_known_uids);
180
if (array_is_created(&ctx->qresync_sample_seqset))
181
array_free(&ctx->qresync_sample_seqset);
182
if (array_is_created(&ctx->qresync_sample_uidset))
183
array_free(&ctx->qresync_sample_uidset);
186
static void cmd_select_finish(struct imap_select_context *ctx, int ret)
189
if (ctx->box != NULL)
190
mailbox_close(&ctx->box);
191
client_send_storage_error(ctx->cmd, ctx->storage);
192
ctx->cmd->client->mailbox = NULL;
194
client_send_tagline(ctx->cmd, mailbox_is_readonly(ctx->box) ?
195
"OK [READ-ONLY] Select completed." :
196
"OK [READ-WRITE] Select completed.");
198
select_context_free(ctx);
201
static bool cmd_select_continue(struct client_command_context *cmd)
203
struct imap_select_context *ctx = cmd->context;
206
if ((ret = imap_fetch_more(ctx->fetch_ctx)) == 0) {
211
ret = imap_fetch_deinit(ctx->fetch_ctx);
212
cmd_select_finish(ctx, ret);
216
static int select_qresync(struct imap_select_context *ctx)
218
struct imap_fetch_context *fetch_ctx;
219
struct mail_search_args *search_args;
221
search_args = mail_search_build_init();
222
search_args->args = p_new(search_args->pool, struct mail_search_arg, 1);
223
search_args->args->type = SEARCH_UIDSET;
224
search_args->args->value.seqset = ctx->qresync_known_uids;
226
fetch_ctx = imap_fetch_init(ctx->cmd, ctx->box);
227
if (fetch_ctx == NULL)
230
fetch_ctx->search_args = search_args;
231
fetch_ctx->send_vanished = TRUE;
232
fetch_ctx->qresync_sample_seqset = &ctx->qresync_sample_seqset;
233
fetch_ctx->qresync_sample_uidset = &ctx->qresync_sample_uidset;
235
if (!imap_fetch_add_changed_since(fetch_ctx, ctx->qresync_modseq) ||
236
!imap_fetch_init_handler(fetch_ctx, "UID", NULL) ||
237
!imap_fetch_init_handler(fetch_ctx, "FLAGS", NULL) ||
238
!imap_fetch_init_handler(fetch_ctx, "MODSEQ", NULL)) {
239
(void)imap_fetch_deinit(fetch_ctx);
243
if (imap_fetch_begin(fetch_ctx) == 0) {
244
if (imap_fetch_more(fetch_ctx) == 0) {
246
ctx->fetch_ctx = fetch_ctx;
247
ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT;
249
ctx->cmd->func = cmd_select_continue;
250
ctx->cmd->context = ctx;
255
return imap_fetch_deinit(fetch_ctx);
259
select_open(struct imap_select_context *ctx, const char *mailbox, bool readonly)
261
struct client *client = ctx->cmd->client;
12
262
struct mailbox_status status;
16
if (!client_read_string_args(cmd, 1, &mailbox))
19
if (client->mailbox != NULL) {
20
box = client->mailbox;
21
client->mailbox = NULL;
23
storage = mailbox_get_storage(box);
24
if (mailbox_close(&box) < 0)
25
client_send_untagged_storage_error(client, storage);
28
storage = client_find_storage(cmd, &mailbox);
32
box = mailbox_open(storage, mailbox, NULL, !readonly ? 0 :
33
(MAILBOX_OPEN_READONLY | MAILBOX_OPEN_KEEP_RECENT));
35
client_send_storage_error(cmd, storage);
39
if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ,
263
enum mailbox_open_flags open_flags = 0;
266
open_flags |= MAILBOX_OPEN_READONLY | MAILBOX_OPEN_KEEP_RECENT;
267
ctx->box = mailbox_open(&ctx->storage, mailbox, NULL, open_flags);
268
if (ctx->box == NULL)
271
if (client->enabled_features != 0)
272
mailbox_enable(ctx->box, client->enabled_features);
273
if (mailbox_sync(ctx->box, MAILBOX_SYNC_FLAG_FULL_READ,
40
274
STATUS_MESSAGES | STATUS_RECENT |
41
275
STATUS_FIRST_UNSEEN_SEQ | STATUS_UIDVALIDITY |
42
STATUS_UIDNEXT | STATUS_KEYWORDS, &status) < 0) {
43
client_send_storage_error(cmd, storage);
276
STATUS_UIDNEXT | STATUS_KEYWORDS |
277
STATUS_HIGHESTMODSEQ, &status) < 0)
48
/* set client's mailbox only after getting status to make sure
49
we're not sending any expunge/exists replies too early to client */
50
client->mailbox = box;
280
client->mailbox = ctx->box;
51
281
client->select_counter++;
52
282
client->mailbox_examined = readonly;
54
283
client->messages_count = status.messages;
55
284
client->recent_count = status.recent;
56
285
client->uidvalidity = status.uidvalidity;
77
306
t_strdup_printf("* OK [UIDNEXT %u] Predicted next UID",
80
client_send_tagline(cmd, mailbox_is_readonly(box) ?
81
"OK [READ-ONLY] Select completed." :
82
"OK [READ-WRITE] Select completed.");
309
if (status.nonpermanent_modseqs) {
310
client_send_line(client,
311
"* OK [NOMODSEQ] No permanent modsequences");
313
client_send_line(client,
314
t_strdup_printf("* OK [HIGHESTMODSEQ %llu] Highest",
315
(unsigned long long)status.highest_modseq));
316
client->sync_last_full_modseq = status.highest_modseq;
319
if (ctx->qresync_uid_validity == status.uidvalidity) {
320
if (select_qresync(ctx) < 0)
326
bool cmd_select_full(struct client_command_context *cmd, bool readonly)
328
struct client *client = cmd->client;
330
struct imap_select_context *ctx;
331
const struct imap_arg *args;
335
/* <mailbox> [(optional parameters)] */
336
if (!client_read_args(cmd, 0, 0, &args))
339
if (!IMAP_ARG_TYPE_IS_STRING(args[0].type)) {
340
client_send_command_error(cmd, "Invalid arguments.");
343
mailbox = IMAP_ARG_STR(&args[0]);
345
ctx = p_new(cmd->pool, struct imap_select_context, 1);
347
ctx->storage = client_find_storage(cmd, &mailbox);
348
if (ctx->storage == NULL)
351
if (args[1].type == IMAP_ARG_LIST) {
352
if (!select_parse_options(ctx, IMAP_ARG_LIST_ARGS(&args[1]))) {
353
select_context_free(ctx);
358
i_assert(client->mailbox_change_lock == NULL);
359
client->mailbox_change_lock = cmd;
361
if (client->mailbox != NULL) {
362
struct mail_storage *old_storage =
363
mailbox_get_storage(client->mailbox);
365
client_search_updates_free(client);
366
box = client->mailbox;
367
client->mailbox = NULL;
369
if (mailbox_close(&box) < 0)
370
client_send_untagged_storage_error(client, old_storage);
371
/* CLOSED response is required by QRESYNC */
372
client_send_line(client,
373
"* OK [CLOSED] Previous mailbox closed.");
376
if (ctx->condstore) {
377
/* Enable while no mailbox is opened to avoid sending
378
HIGHESTMODSEQ for previously opened mailbox */
379
client_enable(client, MAILBOX_FEATURE_CONDSTORE);
382
ret = select_open(ctx, mailbox, readonly);
383
cmd_select_finish(ctx, ret);