imap-sync.c revision 07974f50bd55b06fd6d465f2c0e491794786e2fa
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen /* if multiple commands are in progress, we may need to wait for them
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen to finish before syncing mailbox. */
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen unsigned int counter;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen ARRAY_TYPE(seq_range) search_adds, search_removes;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenstatic void uids_to_seqs(struct mailbox *box, ARRAY_TYPE(seq_range) *uids)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned int i, count;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen for (i = 0; i < count; i++) {
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen mailbox_get_seq_range(box, range[i].seq1, range[i].seq2,
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* since we have to notify about expunged messages,
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen we expect that all the referenced UIDs exist */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen i_assert(seq2 - seq1 == range[i].seq2 - range[i].seq1);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* replace uids with seqs */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenimap_sync_send_search_update(struct imap_sync_context *ctx,
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen mailbox_search_result_sync(update->result, &ctx->search_removes,
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen imap_quote_append_string(cmd, update->tag, FALSE);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* convert to sequences */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen uids_to_seqs(ctx->client->mailbox, &ctx->search_removes);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen uids_to_seqs(ctx->client->mailbox, &ctx->search_adds);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen imap_write_seq_range(cmd, &ctx->search_removes);
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen o_stream_send(ctx->client->output, str_data(cmd), str_len(cmd));
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenstatic void imap_sync_send_search_updates(struct imap_sync_context *ctx)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned int i, count;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (!array_is_created(&ctx->client->search_updates))
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (!array_is_created(&ctx->search_removes)) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen updates = array_get(&ctx->client->search_updates, &count);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen imap_sync_send_search_update(ctx, &updates[i]);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainenimap_sync_init(struct client *client, struct mailbox *box,
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen enum imap_sync_flags imap_flags, enum mailbox_sync_flags flags)
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen /* make sure user can't DoS the system by causing Dovecot to create
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen tons of useless namespaces. */
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen mail_user_drop_useless_namespaces(client->user);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen ctx->sync_ctx = mailbox_sync_init(box, flags);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen ctx->mail = mail_alloc(ctx->t, MAIL_FETCH_FLAGS, 0);
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen i_array_init(&ctx->tmp_keywords, client->keywords.announce_count + 8);
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0) {
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen /* always send UIDs in FETCH replies */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* send search updates the first time after sync is initialized.
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen it now contains expunged messages that must be sent before
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen EXPUNGE replies. */
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainenimap_sync_send_highestmodseq(struct imap_sync_context *ctx,
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen client->highest_fetch_modseq > client->sync_last_full_modseq) {
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen /* if client updates highest-modseq using returned MODSEQs
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen it loses expunges. try to avoid this by sending it a lower
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen pre-expunge HIGHESTMODSEQ reply. */
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen status->highest_modseq > client->sync_last_full_modseq &&
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen status->highest_modseq > client->highest_fetch_modseq) {
07974f50bd55b06fd6d465f2c0e491794786e2faTimo Sirainen /* we've probably sent some VANISHED or EXISTS replies which
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen increased the highest-modseq. notify the client about
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen /* no sending */
07974f50bd55b06fd6d465f2c0e491794786e2faTimo Sirainen } else if (sync_cmd->sync != NULL && /* IDLE doesn't have ->sync */
07974f50bd55b06fd6d465f2c0e491794786e2faTimo Sirainen strncmp(sync_cmd->sync->tagline, "OK ", 3) == 0 &&
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen /* modify the tagged reply directly */
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen sync_cmd->sync->tagline = p_strdup_printf(sync_cmd->pool,
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen "OK [HIGHESTMODSEQ %llu] %s",
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen (unsigned long long)send_modseq,
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen /* send an untagged OK reply */
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen "* OK [HIGHESTMODSEQ %llu]",
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen (unsigned long long)send_modseq));
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen /* no delayed expunges, remember this for future */
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen client->sync_last_full_modseq = status->highest_modseq;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainenint imap_sync_deinit(struct imap_sync_context *ctx,
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen if (mailbox_sync_deinit(&ctx->sync_ctx, STATUS_UIDVALIDITY |
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen if (status.uidvalidity != client->uidvalidity) {
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen /* most clients would get confused by this. disconnect them. */
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen "Mailbox UIDVALIDITY changed");
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen t_strdup_printf("* %u EXISTS", status.messages));
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen t_strdup_printf("* %u RECENT", status.recent));
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* send search updates the second time after syncing in done.
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen now it contains added/removed messages. */
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0)
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen imap_sync_send_highestmodseq(ctx, &status, sync_cmd);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainenstatic void imap_sync_add_modseq(struct imap_sync_context *ctx, string_t *str)
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen if (ctx->client->highest_fetch_modseq < modseq)
6384258c2f84e635d8ceffc3eeddad71f7538040Timo Sirainen str_printfa(str, "MODSEQ (%llu)", (unsigned long long)modseq);
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainenstatic int imap_sync_send_flags(struct imap_sync_context *ctx, string_t *str)
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen const char *const *keywords;
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen keywords = client_get_keyword_names(ctx->client, &ctx->tmp_keywords,
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen if (ctx->imap_flags & IMAP_SYNC_FLAG_SEND_UID)
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen return client_send_line(ctx->client, str_c(str));
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainenstatic int imap_sync_send_modseq(struct imap_sync_context *ctx, string_t *str)
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (ctx->imap_flags & IMAP_SYNC_FLAG_SEND_UID)
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen return client_send_line(ctx->client, str_c(str));
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainenstatic void imap_sync_vanished(struct imap_sync_context *ctx)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen unsigned int i, count;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* Convert expunge sequences to UIDs and send them in VANISHED line. */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen for (i = 0; i < count; i++) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen for (seq = seqs[i].seq1; seq <= seqs[i].seq2; seq++) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen o_stream_send(ctx->client->output, str_data(line), str_len(line));
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainenint imap_sync_more(struct imap_sync_context *ctx)
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen /* finish syncing even when client has disconnected. otherwise our
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen internal state (ctx->messages_count) can get messed up and unless
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen we immediately stop handling all commands and syncs we could end up
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen assert-crashing. */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* get next one */
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen if (!mailbox_sync_next(ctx->sync_ctx, &ctx->sync_rec)) {
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen /* finished */
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen if (ctx->sync_rec.seq2 > ctx->messages_count) {
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen /* don't send change notifications of messages we
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen haven't even announced to client yet */
10cfe8a2bdc5ccfc05380689c71c27209327538fTimo Sirainen if (ctx->sync_rec.seq1 > ctx->messages_count) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* EXPUNGEs must come last */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen ctx->sync_rec.type == MAILBOX_SYNC_TYPE_EXPUNGE);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen for (; ctx->seq <= ctx->sync_rec.seq2; ctx->seq++) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* Use a single VANISHED line */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen for (; ctx->seq >= ctx->sync_rec.seq1; ctx->seq--) {
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen ret = client_send_line(ctx->client, str_c(str));
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen /* update only after we're finished, so that
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen the seq2 > messages_count check above
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen doesn't break */
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen for (; ctx->seq <= ctx->sync_rec.seq2; ctx->seq++) {
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen /* buffer full */
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainenbool imap_sync_is_allowed(struct client *client)
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen mailbox_transaction_get_count(client->mailbox) > 0)
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainenstatic bool cmd_finish_sync(struct client_command_context *cmd)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenstatic bool cmd_sync_continue(struct client_command_context *sync_cmd)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct imap_sync_context *ctx = sync_cmd->context;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen /* Finish all commands that waited for this sync. Go through the queue
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen backwards, so that tagged replies are sent in the same order as
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen they were received. This fixes problems with clients that rely on
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen this (Apple Mail 3.2) */
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen for (cmd = client->command_queue; cmd->next != NULL; cmd = cmd->next) ;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC &&
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen cmd->sync->counter+1 == client->sync_counter) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenstatic void get_common_sync_flags(struct client *client,
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen unsigned int count = 0, fast_count = 0, noexpunges_count = 0;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if ((cmd->sync->flags & MAILBOX_SYNC_FLAG_FAST) != 0)
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (cmd->sync->flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES)
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen i_assert(noexpunges_count == 0 || noexpunges_count == count);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen i_assert((*flags_r & (MAILBOX_SYNC_AUTO_STOP |
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenstatic bool cmd_sync_client(struct client_command_context *sync_cmd)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* there may be multiple commands waiting. use their combined flags */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen get_common_sync_flags(client, &flags, &imap_flags);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen no_newmail = (client->workarounds & WORKAROUND_DELAY_NEWMAIL) != 0 &&
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* expunges might break the client just as badly as new mail
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen notifications. */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen ctx = imap_sync_init(client, client->mailbox, imap_flags, flags);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* handle the syncing using sync_cmd. it doesn't actually matter which
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen one of the pending commands it is. */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen sync_cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainencmd_sync_full(struct client_command_context *cmd, enum mailbox_sync_flags flags,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen enum imap_sync_flags imap_flags, const char *tagline,
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen i_assert(client->output_lock == cmd || client->output_lock == NULL);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* no mailbox selected, no point in delaying the sync */
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen cmd->sync = p_new(cmd->pool, struct client_sync_context, 1);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen cmd->sync->tagline = p_strdup(cmd->pool, tagline);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainenbool cmd_sync(struct client_command_context *cmd, enum mailbox_sync_flags flags,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen enum imap_sync_flags imap_flags, const char *tagline)
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen return cmd_sync_full(cmd, flags, imap_flags, tagline, NULL);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainenbool cmd_sync_callback(struct client_command_context *cmd,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen return cmd_sync_full(cmd, flags, imap_flags, NULL, callback);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenstatic bool cmd_sync_drop_fast(struct client *client)
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen for (cmd = client->command_queue; cmd->next != NULL; cmd = cmd->next) ;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC &&
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen (cmd->sync->flags & MAILBOX_SYNC_FLAG_FAST) != 0) {
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen struct client_command_context *cmd, *first_expunge, *first_nonexpunge;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* wait until we can send output to client */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* wait until mailbox can be synced */
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen /* separate syncs that can send expunges from those that can't */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen for (cmd = client->command_queue; cmd != NULL; cmd = cmd->next) {
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen if (cmd->sync->flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) {
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen if (first_expunge != NULL && first_nonexpunge != NULL) {
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen /* sync expunges after nonexpunges */
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen for (cmd = first_expunge; cmd != NULL; cmd = cmd->next) {