bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "imap-common.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "crc32.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "numpack.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "net.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "ostream.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "str.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "str-sanitize.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "imap-util.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "mail-search-build.h"
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen#include "mail-storage-private.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "mailbox-recent-flags.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "imap-client.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "imap-fetch.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "imap-search-args.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "imap-state.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenenum imap_state_type_public {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen IMAP_STATE_TYPE_MAILBOX = 'B',
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen IMAP_STATE_TYPE_ENABLED_FEATURES = 'F',
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen IMAP_STATE_TYPE_SEARCHRES = '1',
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen};
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenenum imap_state_type_internal {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen IMAP_STATE_TYPE_ID_LOGGED = 'I',
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen IMAP_STATE_TYPE_TLS_COMPRESSION = 'C',
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen};
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenenum imap_state_feature {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen IMAP_STATE_FEATURE_CONDSTORE = 'C',
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen IMAP_STATE_FEATURE_QRESYNC = 'Q'
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen};
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstruct mailbox_import_state {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char *vname;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen guid_128_t mailbox_guid;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen bool examined;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint32_t keywords_count, keywords_crc32, uids_crc32;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint32_t uidvalidity, uidnext, messages;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint64_t highest_modseq;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ARRAY_TYPE(seq_range) recent_uids;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen};
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic void
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenexport_seq_range(buffer_t *dest, const ARRAY_TYPE(seq_range) *range)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const struct seq_range *uids;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned int i, count;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint32_t next_uid;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uids = array_get(range, &count);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, count);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen next_uid = 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen for (i = 0; i < count; i++) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(uids[i].seq1 >= next_uid);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (uids[i].seq1 == uids[i].seq2) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, (uids[i].seq1 - next_uid) << 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen } else {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, 1 | ((uids[i].seq1 - next_uid) << 1));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, uids[i].seq2 - uids[i].seq1 - 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen next_uid = uids[i].seq2 + 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_seq_range(const unsigned char **data, const unsigned char *end,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ARRAY_TYPE(seq_range) *range)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint32_t i, count, next_uid, num, uid1, uid2;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (numpack_decode32(data, end, &count) < 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen next_uid = 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen for (i = 0; i < count; i++) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (numpack_decode32(data, end, &num) < 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uid1 = next_uid + (num >> 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if ((num & 1) == 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uid2 = uid1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seq_range_array_add(range, uid1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen } else {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (numpack_decode32(data, end, &num) < 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uid2 = uid1 + num + 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seq_range_array_add_range(range, uid1, uid2);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen next_uid = uid2 + 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint imap_state_export_internal(struct client *client, buffer_t *dest,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* the only IMAP command we allow running is IDLE or X-STATE */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->command_queue_size > 1) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Multiple commands in progress";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->command_queue == NULL ||
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen strcasecmp(client->command_queue->name, "IDLE") != 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* this would require saving the seq <-> uid mapping
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen and restore it on import. quite a lot of trouble if
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen messages have been expunged in the mean time. */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Non-IDLE connections not supported currently";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return client->v.state_export(client, TRUE, dest, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint imap_state_export_external(struct client *client, buffer_t *dest,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->command_queue_size > 1) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Multiple commands in progress";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(client->command_queue_size == 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(strcmp(client->command_queue->name, "X-STATE") == 0);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return client->v.state_export(client, FALSE, dest, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimap_state_import(struct client *client, bool internal,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *data, size_t size, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ssize_t ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen while (size > 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = client->v.state_import(client, internal,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen data, size, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret <= 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(*error_r != NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret < 0 ? -1 : 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert((size_t)ret <= size);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen data += ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size -= ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint imap_state_import_internal(struct client *client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *data, size_t size,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return imap_state_import(client, TRUE, data, size, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint imap_state_import_external(struct client *client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *data, size_t size,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return imap_state_import(client, FALSE, data, size, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimap_state_export_mailbox_mails(buffer_t *dest, struct mailbox *box,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox_transaction_context *trans;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mail_search_args *search_args;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mail_search_context *search_ctx;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mail *mail;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ARRAY_TYPE(seq_range) recent_uids;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint32_t crc = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int ret = 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen search_args = mail_search_build_init();
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mail_search_build_add_all(search_args);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
0dab9cb35a976c49b28a11e28d5570f5191f1a7aMartti Rannanjärvi trans = mailbox_transaction_begin(box, 0,
0dab9cb35a976c49b28a11e28d5570f5191f1a7aMartti Rannanjärvi "unhibernate imap_state_export_mailbox_mails");
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mail_search_args_unref(&search_args);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen t_array_init(&recent_uids, 8);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen while (mailbox_search_next(search_ctx, &mail)) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen crc = crc32_data_more(crc, &mail->uid, sizeof(mail->uid));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if ((mail_get_flags(mail) & MAIL_RECENT) != 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seq_range_array_add(&recent_uids, mail->uid);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (mailbox_search_deinit(&search_ctx) < 0) {
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi *error_r = mailbox_get_last_internal_error(box, NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen (void)mailbox_transaction_commit(&trans);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, crc);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen export_seq_range(dest, &recent_uids);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic uint32_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenmailbox_status_keywords_crc32(const struct mailbox_status *status)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char *const *strp;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint32_t crc = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen array_foreach(status->keywords, strp)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen crc = crc32_str(*strp);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return crc;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimap_state_export_mailbox(buffer_t *dest, struct client *client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox *box, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox_status status;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox_metadata metadata;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char *vname = mailbox_get_vname(box);
f3fa33be556fd5199dcc856b02b58128bdf693a5Timo Sirainen enum mail_error mail_error;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mailbox_get_open_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT |
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen STATUS_MESSAGES | STATUS_HIGHESTMODSEQ |
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen STATUS_KEYWORDS,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen &status);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (status.nonpermanent_modseqs) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Nonpermanent modseqs";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) {
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi *error_r = mailbox_get_last_internal_error(box, &mail_error);
f3fa33be556fd5199dcc856b02b58128bdf693a5Timo Sirainen /* if the selected mailbox can't have a GUID, fail silently */
f3fa33be556fd5199dcc856b02b58128bdf693a5Timo Sirainen return mail_error == MAIL_ERROR_NOTPOSSIBLE ? 0 : -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, IMAP_STATE_TYPE_MAILBOX);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append(dest, vname, strlen(vname)+1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append(dest, metadata.guid, sizeof(metadata.guid));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, client->mailbox_examined ? 1 : 0);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, status.uidvalidity);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, status.uidnext);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, status.messages);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0 &&
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen !client->nonpermanent_modseqs)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, client->sync_last_full_modseq);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen else
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, status.highest_modseq);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* keywords count + CRC32 should be enough to figure out if it
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen needs to be resent */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, array_count(status.keywords));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_encode(dest, mailbox_status_keywords_crc32(&status));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* we're now basically done, but just in case there's a bug add a
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen checksum of the currently existing UIDs and verify it when
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen importing. this also writes the list of recent UIDs. */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return imap_state_export_mailbox_mails(dest, box, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint imap_state_export_base(struct client *client, bool internal,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_t *dest, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_append(dest, "base\n");
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (array_is_created(&client->search_updates) &&
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen array_count(&client->search_updates) > 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* these could be tricky */
bda082b4c7d06cd8ed83794318930385673b7eeeTeemu Huovila *error_r = "CONTEXT=SEARCH updates not supported currently";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->notify_ctx != NULL) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* FIXME: this really should be supported. also IDLE wouldn't
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen be needed if NOTIFY allows sending EXPUNGEs to selected
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mailbox. */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "NOTIFY not supported currently";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->mailbox != NULL) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = imap_state_export_mailbox(dest, client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->mailbox, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret <= 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* IMAP features */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->enabled_features != 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert((client->enabled_features & ~(MAILBOX_FEATURE_CONDSTORE |
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen MAILBOX_FEATURE_QRESYNC)) == 0);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, IMAP_STATE_TYPE_ENABLED_FEATURES);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, IMAP_STATE_FEATURE_CONDSTORE);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, IMAP_STATE_FEATURE_QRESYNC);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, '\0');
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (internal) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->id_logged)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, IMAP_STATE_TYPE_ID_LOGGED);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->tls_compression)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, IMAP_STATE_TYPE_TLS_COMPRESSION);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* IMAP SEARCHRES extension */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (array_is_created(&client->search_saved_uidset) &&
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen array_count(&client->search_saved_uidset) > 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen buffer_append_c(dest, IMAP_STATE_TYPE_SEARCHRES);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen export_seq_range(dest, &client->search_saved_uidset);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_string(const unsigned char **data, const unsigned char *end,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **str_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *p;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen p = memchr(*data, '\0', end - *data);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (p == NULL)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *str_r = (const void *)*data;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *data = p + 1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_send_expunges(struct client *client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const struct mailbox_import_state *state,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned int *expunge_count_r,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox_transaction_context *trans;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mail_search_args *search_args;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mail_search_context *search_ctx;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mail *mail;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint32_t crc = 0, seq, expunged_uid;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ARRAY_TYPE(seq_range) uids_filter, expunged_uids;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ARRAY_TYPE(uint32_t) expunged_seqs;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct seq_range_iter iter;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const uint32_t *seqs;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned int i, expunge_count, n = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen string_t *str;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int ret = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *expunge_count_r = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (state->messages == 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* the mailbox was empty originally - there couldn't be any
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen pending expunges. */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (state->uidnext <= 1) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Invalid UIDNEXT";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* get all the message UIDs expunged since the last known modseq */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen t_array_init(&uids_filter, 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen t_array_init(&expunged_uids, 128);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seq_range_array_add_range(&uids_filter, 1, state->uidnext-1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (!mailbox_get_expunged_uids(client->mailbox, state->highest_modseq,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen &uids_filter, &expunged_uids)) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = t_strdup_printf(
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen "Couldn't get recently expunged UIDs "
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi "(uidnext=%u highest_modseq=%"PRIu64")",
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi state->uidnext, state->highest_modseq);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seq_range_array_iter_init(&iter, &expunged_uids);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen search_args = mail_search_build_init();
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mail_search_build_add_all(search_args);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
0dab9cb35a976c49b28a11e28d5570f5191f1a7aMartti Rannanjärvi trans = mailbox_transaction_begin(client->mailbox, 0,
0dab9cb35a976c49b28a11e28d5570f5191f1a7aMartti Rannanjärvi "unhibernate import_send_expunges");
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mail_search_args_unref(&search_args);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* find sequence numbers for the expunged UIDs */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen t_array_init(&expunged_seqs, array_count(&expunged_uids)+1); seq = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen while (mailbox_search_next(search_ctx, &mail)) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen while (seq_range_array_iter_nth(&iter, n, &expunged_uid) &&
18efe79858a7b4a794c11bc1867fc313c7c07bbeTimo Sirainen expunged_uid < mail->uid && seq < state->messages) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seq++; n++;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen array_append(&expunged_seqs, &seq, 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen crc = crc32_data_more(crc, &expunged_uid,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen sizeof(expunged_uid));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (seq == state->messages)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen break;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen crc = crc32_data_more(crc, &mail->uid, sizeof(mail->uid));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (++seq == state->messages)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen break;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
18efe79858a7b4a794c11bc1867fc313c7c07bbeTimo Sirainen while (seq_range_array_iter_nth(&iter, n, &expunged_uid) &&
18efe79858a7b4a794c11bc1867fc313c7c07bbeTimo Sirainen seq < state->messages) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seq++; n++;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen array_append(&expunged_seqs, &seq, 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen crc = crc32_data_more(crc, &expunged_uid,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen sizeof(expunged_uid));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (mailbox_search_deinit(&search_ctx) < 0) {
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi *error_r = mailbox_get_last_internal_error(client->mailbox, NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen } else if (seq != state->messages) {
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi *error_r = t_strdup_printf("Message count mismatch after "
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi "handling expunges (%u != %u)",
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi seq, state->messages);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen (void)mailbox_transaction_commit(&trans);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret < 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seqs = array_get(&expunged_seqs, &expunge_count);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->messages_count + expunge_count < state->messages) {
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi *error_r = t_strdup_printf("Message count too low after "
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi "handling expunges (%u < %u)",
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi client->messages_count + expunge_count,
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi state->messages);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (crc != state->uids_crc32) {
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi *error_r = t_strdup_printf("Message UIDs CRC32 mismatch (%u != %u)",
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi crc, state->uids_crc32);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) == 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str = t_str_new(32);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen for (i = expunge_count; i > 0; i--) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_truncate(str, 0);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_printfa(str, "* %u EXPUNGE", seqs[i-1]);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client_send_line(client, str_c(str));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen } else {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str = str_new(default_pool, 128);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_append(str, "* VANISHED ");
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen imap_write_seq_range(str, &expunged_uids);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_append(str, "\r\n");
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen o_stream_nsend(client->output, str_data(str), str_len(str));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_free(&str);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *expunge_count_r = expunge_count;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_send_flag_changes(struct client *client,
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen const struct mailbox_import_state *state,
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen unsigned int *flag_change_count_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct imap_fetch_context *fetch_ctx;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mail_search_args *search_args;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ARRAY_TYPE(seq_range) old_uids;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen pool_t pool;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen *flag_change_count_r = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (state->messages == 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen t_array_init(&old_uids, 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen seq_range_array_add_range(&old_uids, 1, state->uidnext-1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen search_args = mail_search_build_init();
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen search_args->args = p_new(search_args->pool, struct mail_search_arg, 1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen search_args->args->type = SEARCH_UIDSET;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen search_args->args->value.seqset = old_uids;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen imap_search_add_changed_since(search_args, state->highest_modseq);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen pool = pool_alloconly_create("imap state flag changes", 1024);
cddfd1355db6b60c71d7ee3c0b4f23b3efcc9ad1Timo Sirainen fetch_ctx = imap_fetch_alloc(client, pool, "unhibernate");
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen pool_unref(&pool);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_flags_init);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_uid_init);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen imap_fetch_init_nofail_handler(fetch_ctx, imap_fetch_modseq_init);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen imap_fetch_begin(fetch_ctx, client->mailbox, search_args);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mail_search_args_unref(&search_args);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* FIXME: ideally do this asynchronously.. */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen while (imap_fetch_more_no_lock_update(fetch_ctx) == 0) ;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = imap_fetch_end(fetch_ctx);
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen *flag_change_count_r = fetch_ctx->fetched_mails_count;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen imap_fetch_free(&fetch_ctx);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic ssize_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_state_mailbox_struct(const unsigned char *data, size_t size,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox_import_state *state_r,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *p = data, *end = data + size;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(state_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen t_array_init(&state_r->recent_uids, 8);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* vname */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (import_string(&p, end, &state_r->vname) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Mailbox state truncated at name";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* GUID */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (end-p < (int)sizeof(state_r->mailbox_guid)) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Mailbox state truncated at GUID";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen memcpy(state_r->mailbox_guid, p, sizeof(state_r->mailbox_guid));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen p += sizeof(state_r->mailbox_guid);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (guid_128_is_empty(state_r->mailbox_guid)) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Empty GUID";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* EXAMINEd vs SELECTed */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (p == end) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Mailbox state truncated at examined-flag";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen state_r->examined = p[0] != 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen p++;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* mailbox state */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (numpack_decode32(&p, end, &state_r->uidvalidity) < 0 ||
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_decode32(&p, end, &state_r->uidnext) < 0 ||
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_decode32(&p, end, &state_r->messages) < 0 ||
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_decode(&p, end, &state_r->highest_modseq) < 0 ||
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_decode32(&p, end, &state_r->keywords_count) < 0 ||
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_decode32(&p, end, &state_r->keywords_crc32) < 0 ||
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen numpack_decode32(&p, end, &state_r->uids_crc32) < 0 ||
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen import_seq_range(&p, end, &state_r->recent_uids) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Mailbox state truncated";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (state_r->uidvalidity == 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Empty UIDVALIDITY";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (state_r->uidnext == 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Empty UIDNEXT";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return p - data;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_state_mailbox_open(struct client *client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const struct mailbox_import_state *state,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mail_namespace *ns;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox *box;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox_metadata metadata;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox_status status;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const struct seq_range *range;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen enum mailbox_flags flags = 0;
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen unsigned int expunge_count, new_mails_count = 0, flag_change_count = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uint32_t uid;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int ret = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ns = mail_namespace_find(client->user->namespaces, state->vname);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ns == NULL) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Namespace not found for mailbox";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (state->examined)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen flags |= MAILBOX_FLAG_READONLY;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen else
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen flags |= MAILBOX_FLAG_DROP_RECENT;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen box = mailbox_alloc(ns->list, state->vname, flags);
61cf001f1944d92eb25f113ba4c08985d6e30d53Timo Sirainen mailbox_set_reason(box, "unhibernate");
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (mailbox_open(box) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = t_strdup_printf("Couldn't open mailbox: %s",
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi mailbox_get_last_internal_error(box, NULL));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mailbox_free(&box);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->enabled_features != 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = mailbox_enable(box, client->enabled_features);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret < 0 || mailbox_sync(box, 0) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = t_strdup_printf("Couldn't sync mailbox: %s",
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi mailbox_get_last_internal_error(box, NULL));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mailbox_free(&box);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* verify that this still looks like the same mailbox */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) {
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi *error_r = mailbox_get_last_internal_error(box, NULL);
7bc3126bb62589fe373f40aa40132e671e4a363dAki Tuomi mailbox_free(&box);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (!guid_128_equals(metadata.guid, state->mailbox_guid)) {
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi *error_r = t_strdup_printf("Mailbox GUID has changed %s->%s",
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi guid_128_to_string(state->mailbox_guid),
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi guid_128_to_string(metadata.guid));
7bc3126bb62589fe373f40aa40132e671e4a363dAki Tuomi mailbox_free(&box);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mailbox_get_open_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT |
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen STATUS_HIGHESTMODSEQ | STATUS_RECENT |
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen STATUS_KEYWORDS, &status);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (status.uidvalidity != state->uidvalidity) {
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi *error_r = t_strdup_printf("Mailbox UIDVALIDITY has changed %u->%u",
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi state->uidvalidity, status.uidvalidity);
7bc3126bb62589fe373f40aa40132e671e4a363dAki Tuomi mailbox_free(&box);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (status.uidnext < state->uidnext) {
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi *error_r = t_strdup_printf("Mailbox UIDNEXT shrank %u -> %u",
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi state->uidnext, status.uidnext);
7bc3126bb62589fe373f40aa40132e671e4a363dAki Tuomi mailbox_free(&box);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (status.highest_modseq < state->highest_modseq) {
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi *error_r = t_strdup_printf("Mailbox HIGHESTMODSEQ shrank %"PRIu64" -> %"PRIu64,
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi state->highest_modseq,
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi status.highest_modseq);
7bc3126bb62589fe373f40aa40132e671e4a363dAki Tuomi mailbox_free(&box);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->mailbox = box;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->mailbox_examined = state->examined;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->messages_count = status.messages;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->uidvalidity = status.uidvalidity;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->notify_uidnext = status.uidnext;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (import_send_expunges(client, state, &expunge_count, error_r) < 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(expunge_count <= state->messages);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (state->messages - expunge_count > client->messages_count) {
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi *error_r = t_strdup_printf("Mailbox message count shrank %u -> %u",
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi client->messages_count,
6028ef7bd1a453212b815b608e1c7b1b21ebca49Aki Tuomi state->messages - expunge_count);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen client_update_mailbox_flags(client, status.keywords);
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen array_foreach(&state->recent_uids, range) {
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen for (uid = range->seq1; uid <= range->seq2; uid++) {
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen uint32_t seq;
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen if (mail_index_lookup_seq(box->view, uid, &seq))
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen mailbox_recent_flags_set_uid_forced(box, uid);
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen }
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen }
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen client->recent_count = mailbox_recent_flags_count(box);
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (state->messages - expunge_count < client->messages_count) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* new messages arrived */
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen new_mails_count = client->messages_count -
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen (state->messages - expunge_count);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client_send_line(client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen t_strdup_printf("* %u EXISTS", client->messages_count));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client_send_line(client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen t_strdup_printf("* %u RECENT", client->recent_count));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (array_count(status.keywords) == state->keywords_count &&
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mailbox_status_keywords_crc32(&status) == state->keywords_crc32) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* no changes to keywords */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->keywords.announce_count = state->keywords_count;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen } else {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client_send_mailbox_flags(client, TRUE);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen if (import_send_flag_changes(client, state, &flag_change_count) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Couldn't send flag changes";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0 &&
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen !client->nonpermanent_modseqs &&
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen status.highest_modseq != state->highest_modseq) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client_send_line(client, t_strdup_printf(
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi "* OK [HIGHESTMODSEQ %"PRIu64"] Highest",
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi status.highest_modseq));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->sync_last_full_modseq = status.highest_modseq;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen i_debug("Unhibernation sync: %u expunges, %u new messages, %u flag changes, %"PRIu64" modseq changes",
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen expunge_count, new_mails_count, flag_change_count,
f32c6ed9db6f4c535f97a2020401572efc8abf86Timo Sirainen status.highest_modseq - state->highest_modseq);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic ssize_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_state_mailbox(struct client *client, const unsigned char *data,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct mailbox_import_state state;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ssize_t ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->mailbox != NULL) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Duplicate mailbox state";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = import_state_mailbox_struct(data, size, &state, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret <= 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(*error_r != NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
7f2cb2e5b258280af90ae99b6d07dfa44109e5beTimo Sirainen if (import_state_mailbox_open(client, &state, error_r) < 0) {
7f2cb2e5b258280af90ae99b6d07dfa44109e5beTimo Sirainen *error_r = t_strdup_printf("Mailbox %s: %s", state.vname, *error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
7f2cb2e5b258280af90ae99b6d07dfa44109e5beTimo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic ssize_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_state_enabled_features(struct client *client, const unsigned char *data,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen enum imap_state_feature feature;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t i = 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen for (i = 0; i < size; i++) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (data[i] == '\0')
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return i+1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen feature = data[i];
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen switch (feature) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen case IMAP_STATE_FEATURE_CONDSTORE:
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->enabled_features |= MAILBOX_FEATURE_CONDSTORE;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen break;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen case IMAP_STATE_FEATURE_QRESYNC:
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->enabled_features |= MAILBOX_FEATURE_QRESYNC;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen break;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen default:
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = t_strdup_printf(
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen "Unknown feature '%c'", feature);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Non-terminated features list";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic ssize_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_state_searchres(struct client *client, const unsigned char *data,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *p = data;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_array_init(&client->search_saved_uidset, 128);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (import_seq_range(&p, data+size, &client->search_saved_uidset) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = "Invalid SEARCHRES seq-range";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return p - data;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic ssize_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_state_id_logged(struct client *client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *data ATTR_UNUSED,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size ATTR_UNUSED,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r ATTR_UNUSED)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->id_logged = TRUE;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic ssize_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimport_state_tls_compression(struct client *client,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *data ATTR_UNUSED,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size ATTR_UNUSED,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r ATTR_UNUSED)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->tls_compression = TRUE;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
f24edebe360d3effe584a884aa7d119daf3fd371Aki Tuomivoid imap_state_import_idle_cmd_tag(struct client *client, const char *tag)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (client->state_import_idle_continue) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* IDLE command continues */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct client_command_context *cmd;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct command *command;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen cmd = client_command_alloc(client);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen cmd->tag = p_strdup(cmd->pool, tag);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen cmd->name = "IDLE";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen command = command_find("IDLE");
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(command != NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen cmd->func = command->func;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen cmd->cmd_flags = command->flags;
1c02804cdc5f1ad830fec081100e951bc67204b4Timo Sirainen client_command_init_finished(cmd);
1c02804cdc5f1ad830fec081100e951bc67204b4Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (command_exec(cmd)) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* IDLE terminated because of an external change, but
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen DONE was already buffered */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client_command_free(&cmd);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client_add_missing_io(client);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen } else {
2933174a1d7af52678eb5d2bf581e8c2a995f6dfTimo Sirainen i_assert(cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT ||
2933174a1d7af52678eb5d2bf581e8c2a995f6dfTimo Sirainen cmd->state == CLIENT_COMMAND_STATE_WAIT_OUTPUT);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen } else {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* we're finishing IDLE command */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client_send_line(client, t_strdup_printf(
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen "%s %s Idle completed.", tag,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen client->state_import_bad_idle_done ? "BAD" : "OK"));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic struct {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen enum imap_state_type_public type;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ssize_t (*import)(struct client *client, const unsigned char *data,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size, const char **error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen} imap_states_public[] = {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen { IMAP_STATE_TYPE_MAILBOX, import_state_mailbox },
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen { IMAP_STATE_TYPE_ENABLED_FEATURES, import_state_enabled_features },
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen { IMAP_STATE_TYPE_SEARCHRES, import_state_searchres }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen};
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic struct {
8b9a69edc162362d47922521161e2cfa07218688Timo Sirainen enum imap_state_type_internal type;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ssize_t (*import)(struct client *client, const unsigned char *data,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size, const char **error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen} imap_states_internal[] = {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen { IMAP_STATE_TYPE_ID_LOGGED, import_state_id_logged },
f24edebe360d3effe584a884aa7d119daf3fd371Aki Tuomi { IMAP_STATE_TYPE_TLS_COMPRESSION, import_state_tls_compression }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen};
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic ssize_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimap_state_try_import_public(struct client *client, const unsigned char *data,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned int i;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ssize_t ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(size > 0);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen for (i = 0; i < N_ELEMENTS(imap_states_public); i++) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (imap_states_public[i].type == data[0]) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = imap_states_public[i].
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen import(client, data+1, size-1, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret < 0 ? -1 : ret+1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -2;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic ssize_t
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenimap_state_try_import_internal(struct client *client, const unsigned char *data,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t size, const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen unsigned int i;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ssize_t ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(size > 0);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen for (i = 0; i < N_ELEMENTS(imap_states_internal); i++) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (imap_states_internal[i].type == data[0]) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = imap_states_internal[i].
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen import(client, data+1, size-1, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret < 0 ? -1 : ret+1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -2;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenssize_t imap_state_import_base(struct client *client, bool internal,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *data, size_t size,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char **error_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const unsigned char *p;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ssize_t ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size_t pos;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(client->mailbox == NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = NULL;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (size < 5 || memcmp(data, "base\n", 5) != 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen p = memchr(data, '\n', size);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (p == NULL)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen p = data + I_MIN(size, 20);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = t_strdup_printf("Unknown state block '%s'",
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_sanitize(t_strdup_until(data, p), 20));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen pos = 5;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen while (pos < size) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = imap_state_try_import_public(client, data+pos,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size-pos, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret == -2 && internal) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = imap_state_try_import_internal(client, data+pos,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen size-pos, error_r);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret < 0 || *error_r != NULL) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret == -2) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *error_r = t_strdup_printf("Unknown type '%c'",
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen data[pos]);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(*error_r != NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return ret < 0 ? -1 : 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_assert(size - pos >= (size_t)ret);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen pos += ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return pos;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}