cmd-select.c revision cb29b57f69861c07e1c0a5015d77ad5938b6dd5d
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "imap-common.h"
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen#include "seq-range-array.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "time-util.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "imap-commands.h"
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen#include "mail-search-build.h"
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen#include "imap-search-args.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "imap-seqset.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "imap-fetch.h"
97cb20eb77d486ef67eac50567e3080faca025c1Timo Sirainen#include "imap-sync.h"
97cb20eb77d486ef67eac50567e3080faca025c1Timo Sirainen
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include <stdlib.h>
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen
366eb7178f2c90d97134e0c2d1958f93fcdaba12Timo Sirainenstruct imap_select_context {
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen struct client_command_context *cmd;
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen struct mail_namespace *ns;
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen struct mailbox *box;
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen
863ea896fb31a16d1baec31e57650243b5547db6Timo Sirainen struct imap_fetch_context *fetch_ctx;
863ea896fb31a16d1baec31e57650243b5547db6Timo Sirainen
863ea896fb31a16d1baec31e57650243b5547db6Timo Sirainen uint32_t qresync_uid_validity;
471e447023ab73a73f0f78da2afc0c55905330ddTimo Sirainen uint64_t qresync_modseq;
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen ARRAY_TYPE(seq_range) qresync_known_uids;
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen ARRAY_TYPE(uint32_t) qresync_sample_seqset;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen ARRAY_TYPE(uint32_t) qresync_sample_uidset;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen unsigned int condstore:1;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen};
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenstatic int select_qresync_get_uids(struct imap_select_context *ctx,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen const ARRAY_TYPE(seq_range) *seqset,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const ARRAY_TYPE(seq_range) *uidset)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct seq_range *uid_range;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct seq_range_iter seq_iter;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen unsigned int i, uid_count, diff, n = 0;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen uint32_t seq;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* change all n:m ranges to n,m and store the results */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uid_range = array_get(uidset, &uid_count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen seq_range_array_iter_init(&seq_iter, seqset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_array_init(&ctx->qresync_sample_uidset, uid_count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_array_init(&ctx->qresync_sample_seqset, uid_count);
55a210942dc7da58b2fd0b11bed8da6b030af5c1Timo Sirainen for (i = 0; i < uid_count; i++) {
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen if (!seq_range_array_iter_nth(&seq_iter, n++, &seq))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
52d2b356e3ddb4e59ee09c10d47add9d3280284bAki Tuomi array_append(&ctx->qresync_sample_uidset,
52d2b356e3ddb4e59ee09c10d47add9d3280284bAki Tuomi &uid_range[i].seq1, 1);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen array_append(&ctx->qresync_sample_seqset, &seq, 1);
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen diff = uid_range[i].seq2 - uid_range[i].seq1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (diff > 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen n += diff - 1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!seq_range_array_iter_nth(&seq_iter, n++, &seq))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen array_append(&ctx->qresync_sample_uidset,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen &uid_range[i].seq2, 1);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen array_append(&ctx->qresync_sample_seqset, &seq, 1);
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen }
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen }
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen if (seq_range_array_iter_nth(&seq_iter, n, &seq))
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen return -1;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return 0;
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen}
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainen
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainenstatic bool
88b8aea03a24ef7a9efc30399080487b7eb03537Timo Sirainenselect_parse_qresync_known_set(struct imap_select_context *ctx,
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen const struct imap_arg *args)
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen{
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen ARRAY_TYPE(seq_range) seqset, uidset;
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen const char *str;
651fc0f1e43fef3e02e0e7b5f498973b05f641d7Timo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen t_array_init(&seqset, 32);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (!imap_arg_get_atom(args, &str) ||
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen imap_seq_set_nostar_parse(str, &seqset) < 0) {
d9fda7e3a0fa5551547ac3e3054b837fc77f4bfbTimo Sirainen client_send_command_error(ctx->cmd,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen "Invalid QRESYNC known-sequence-set");
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return FALSE;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen args++;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_array_init(&uidset, 32);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!imap_arg_get_atom(args, &str) ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen imap_seq_set_nostar_parse(str, &uidset) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_send_command_error(ctx->cmd,
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen "Invalid QRESYNC known-uid-set");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return FALSE;
45e62043058738e294f89504c319d852e25943ccTimo Sirainen }
45e62043058738e294f89504c319d852e25943ccTimo Sirainen args++;
45e62043058738e294f89504c319d852e25943ccTimo Sirainen
45e62043058738e294f89504c319d852e25943ccTimo Sirainen if (select_qresync_get_uids(ctx, &seqset, &uidset) < 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_send_command_error(ctx->cmd, "Invalid QRESYNC sets");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return FALSE;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen if (!IMAP_ARG_IS_EOL(args)) {
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen client_send_command_error(ctx->cmd,
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen "Too many parameters to QRESYNC known set");
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen return FALSE;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenselect_parse_qresync(struct imap_select_context *ctx,
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen const struct imap_arg *args)
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct imap_arg *list_args;
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen const char *str;
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen unsigned int count;
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if ((ctx->cmd->client->enabled_features &
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen MAILBOX_FEATURE_QRESYNC) == 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_command_error(ctx->cmd, "QRESYNC not enabled");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen return FALSE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (!imap_arg_get_list_full(args, &args, &count)) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_command_error(ctx->cmd,
c0d1bfc45e224251cb549de8d8804861e8acb517Timo Sirainen "QRESYNC parameters missing");
c0d1bfc45e224251cb549de8d8804861e8acb517Timo Sirainen return FALSE;
c0d1bfc45e224251cb549de8d8804861e8acb517Timo Sirainen }
c0d1bfc45e224251cb549de8d8804861e8acb517Timo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (!imap_arg_get_atom(&args[0], &str) ||
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen str_to_uint32(str, &ctx->qresync_uid_validity) < 0 ||
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen !imap_arg_get_atom(&args[1], &str) ||
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen str_to_uint64(str, &ctx->qresync_modseq) < 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_send_command_error(ctx->cmd,
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen "Invalid QRESYNC parameters");
6321d9d33937c7fc13a8ff04c220a9e377efeeb8Timo Sirainen return FALSE;
6321d9d33937c7fc13a8ff04c220a9e377efeeb8Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen args += 2;
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen i_array_init(&ctx->qresync_known_uids, 64);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (imap_arg_get_atom(args, &str)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (imap_seq_set_nostar_parse(str, &ctx->qresync_known_uids) < 0) {
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen client_send_command_error(ctx->cmd,
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen "Invalid QRESYNC known-uids");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen args++;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen } else {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen seq_range_array_add_range(&ctx->qresync_known_uids,
6ded8819b9002150a95a7615e4f64f091c250464Timo Sirainen 1, (uint32_t)-1);
6ded8819b9002150a95a7615e4f64f091c250464Timo Sirainen }
2f8da04d700cc23fcd6630226a4866e828b761bdTimo Sirainen if (imap_arg_get_list(args, &list_args)) {
2f8da04d700cc23fcd6630226a4866e828b761bdTimo Sirainen if (!select_parse_qresync_known_set(ctx, list_args))
b87a4156eca6dcf6b29c504eb0cb9be2fdb11b63Timo Sirainen return FALSE;
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen args++;
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (!IMAP_ARG_IS_EOL(args)) {
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen client_send_command_error(ctx->cmd,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen "Invalid QRESYNC parameters");
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen return FALSE;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen return TRUE;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
static bool
select_parse_options(struct imap_select_context *ctx,
const struct imap_arg *args)
{
const char *name;
while (!IMAP_ARG_IS_EOL(args)) {
if (!imap_arg_get_atom(args, &name)) {
client_send_command_error(ctx->cmd,
"SELECT options contain non-atoms.");
return FALSE;
}
name = t_str_ucase(name);
args++;
if (strcmp(name, "CONDSTORE") == 0)
ctx->condstore = TRUE;
else if (strcmp(name, "QRESYNC") == 0) {
if (!select_parse_qresync(ctx, args))
return FALSE;
args++;
} else {
client_send_command_error(ctx->cmd,
"Unknown FETCH modifier");
return FALSE;
}
}
return TRUE;
}
static void select_context_free(struct imap_select_context *ctx)
{
if (array_is_created(&ctx->qresync_known_uids))
array_free(&ctx->qresync_known_uids);
if (array_is_created(&ctx->qresync_sample_seqset))
array_free(&ctx->qresync_sample_seqset);
if (array_is_created(&ctx->qresync_sample_uidset))
array_free(&ctx->qresync_sample_uidset);
}
static void cmd_select_finish(struct imap_select_context *ctx, int ret)
{
const char *resp_code;
if (ret < 0) {
if (ctx->box != NULL)
mailbox_free(&ctx->box);
ctx->cmd->client->mailbox = NULL;
} else {
resp_code = mailbox_is_readonly(ctx->box) ?
"READ-ONLY" : "READ-WRITE";
client_send_tagline(ctx->cmd, t_strdup_printf(
"OK [%s] %s completed", resp_code,
ctx->cmd->client->mailbox_examined ? "Examine" : "Select"));
}
select_context_free(ctx);
}
static bool cmd_select_continue(struct client_command_context *cmd)
{
struct imap_select_context *ctx = cmd->context;
int ret;
if (imap_fetch_more(ctx->fetch_ctx, cmd) == 0) {
/* unfinished */
return FALSE;
}
ret = imap_fetch_end(ctx->fetch_ctx);
if (ret < 0)
client_send_box_error(ctx->cmd, ctx->box);
imap_fetch_free(&ctx->fetch_ctx);
cmd_select_finish(ctx, ret);
return TRUE;
}
static int select_qresync(struct imap_select_context *ctx)
{
struct imap_fetch_context *fetch_ctx;
struct mail_search_args *search_args;
struct imap_fetch_qresync_args qresync_args;
search_args = mail_search_build_init();
search_args->args = p_new(search_args->pool, struct mail_search_arg, 1);
search_args->args->type = SEARCH_UIDSET;
search_args->args->value.seqset = ctx->qresync_known_uids;
imap_search_add_changed_since(search_args, ctx->qresync_modseq);
memset(&qresync_args, 0, sizeof(qresync_args));
qresync_args.qresync_sample_seqset = &ctx->qresync_sample_seqset;
qresync_args.qresync_sample_uidset = &ctx->qresync_sample_uidset;
if (imap_fetch_send_vanished(ctx->cmd->client, ctx->box,
search_args, &qresync_args) < 0) {
mail_search_args_unref(&search_args);
return -1;
}
fetch_ctx = imap_fetch_alloc(ctx->cmd->client, ctx->cmd->pool);
imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_uid_init);
imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_flags_init);
imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_modseq_init);
imap_fetch_begin(fetch_ctx, ctx->box, search_args);
mail_search_args_unref(&search_args);
if (imap_fetch_more(fetch_ctx, ctx->cmd) == 0) {
/* unfinished */
ctx->fetch_ctx = fetch_ctx;
ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT;
ctx->cmd->func = cmd_select_continue;
ctx->cmd->context = ctx;
return 0;
}
if (imap_fetch_end(fetch_ctx) < 0)
return -1;
imap_fetch_free(&fetch_ctx);
return 1;
}
static int
select_open(struct imap_select_context *ctx, const char *mailbox, bool readonly)
{
struct client *client = ctx->cmd->client;
struct mailbox_status status;
enum mailbox_flags flags = 0;
int ret = 0;
if (readonly)
flags |= MAILBOX_FLAG_READONLY;
else
flags |= MAILBOX_FLAG_DROP_RECENT;
ctx->box = mailbox_alloc(ctx->ns->list, mailbox, flags);
if (mailbox_open(ctx->box) < 0) {
client_send_box_error(ctx->cmd, ctx->box);
mailbox_free(&ctx->box);
return -1;
}
if (client->enabled_features != 0)
ret = mailbox_enable(ctx->box, client->enabled_features);
if (ret < 0 ||
mailbox_sync(ctx->box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
client_send_box_error(ctx->cmd, ctx->box);
return -1;
}
mailbox_get_open_status(ctx->box, STATUS_MESSAGES | STATUS_RECENT |
STATUS_FIRST_UNSEEN_SEQ | STATUS_UIDVALIDITY |
STATUS_UIDNEXT | STATUS_KEYWORDS |
STATUS_HIGHESTMODSEQ, &status);
client->mailbox = ctx->box;
client->mailbox_examined = readonly;
client->messages_count = status.messages;
client->recent_count = status.recent;
client->uidvalidity = status.uidvalidity;
client->notify_uidnext = status.uidnext;
client_update_mailbox_flags(client, status.keywords);
client_send_mailbox_flags(client, TRUE);
client_send_line(client,
t_strdup_printf("* %u EXISTS", status.messages));
client_send_line(client,
t_strdup_printf("* %u RECENT", status.recent));
if (status.first_unseen_seq != 0) {
client_send_line(client,
t_strdup_printf("* OK [UNSEEN %u] First unseen.",
status.first_unseen_seq));
}
client_send_line(client,
t_strdup_printf("* OK [UIDVALIDITY %u] UIDs valid",
status.uidvalidity));
client_send_line(client,
t_strdup_printf("* OK [UIDNEXT %u] Predicted next UID",
status.uidnext));
client->nonpermanent_modseqs = status.nonpermanent_modseqs;
if (status.nonpermanent_modseqs) {
client_send_line(client,
"* OK [NOMODSEQ] No permanent modsequences");
} else if (!status.no_modseq_tracking) {
client_send_line(client,
t_strdup_printf("* OK [HIGHESTMODSEQ %llu] Highest",
(unsigned long long)status.highest_modseq));
client->sync_last_full_modseq = status.highest_modseq;
}
if (ctx->qresync_uid_validity == status.uidvalidity &&
status.uidvalidity != 0 && !client->nonpermanent_modseqs) {
if ((ret = select_qresync(ctx)) < 0) {
client_send_box_error(ctx->cmd, ctx->box);
return -1;
}
} else {
ret = 1;
}
return ret;
}
static void close_selected_mailbox(struct client *client)
{
struct mailbox *box;
if (client->mailbox == NULL)
return;
client_search_updates_free(client);
box = client->mailbox;
client->mailbox = NULL;
mailbox_free(&box);
/* CLOSED response is required by QRESYNC */
client_send_line(client, "* OK [CLOSED] Previous mailbox closed.");
}
bool cmd_select_full(struct client_command_context *cmd, bool readonly)
{
struct client *client = cmd->client;
struct imap_select_context *ctx;
const struct imap_arg *args, *list_args;
const char *mailbox, *error;
int ret;
/* <mailbox> [(optional parameters)] */
if (!client_read_args(cmd, 0, 0, &args))
return FALSE;
if (!imap_arg_get_astring(args, &mailbox)) {
close_selected_mailbox(client);
client_send_command_error(cmd, "Invalid arguments.");
return FALSE;
}
ctx = p_new(cmd->pool, struct imap_select_context, 1);
ctx->cmd = cmd;
ctx->ns = client_find_namespace_full(cmd->client, &mailbox, &error);
if (ctx->ns == NULL) {
close_selected_mailbox(client);
client_send_tagline(cmd, error);
return TRUE;
}
if (imap_arg_get_list(&args[1], &list_args)) {
if (!select_parse_options(ctx, list_args)) {
select_context_free(ctx);
close_selected_mailbox(client);
return TRUE;
}
}
i_assert(client->mailbox_change_lock == NULL);
client->mailbox_change_lock = cmd;
close_selected_mailbox(client);
if (ctx->condstore) {
/* Enable while no mailbox is opened to avoid sending
HIGHESTMODSEQ for previously opened mailbox */
(void)client_enable(client, MAILBOX_FEATURE_CONDSTORE);
}
ret = select_open(ctx, mailbox, readonly);
if (ret == 0)
return FALSE;
cmd_select_finish(ctx, ret);
return TRUE;
}
bool cmd_select(struct client_command_context *cmd)
{
return cmd_select_full(cmd, FALSE);
}