imap-sync.c revision f2bd9e507b8befdd95a983f86664febf5c19bd95
/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
#include "common.h"
#include "str.h"
#include "ostream.h"
#include "mail-storage.h"
#include "imap-quote.h"
#include "imap-util.h"
#include "imap-sync.h"
#include "commands.h"
struct client_sync_context {
/* if multiple commands are in progress, we may need to wait for them
to finish before syncing mailbox. */
unsigned int counter;
enum mailbox_sync_flags flags;
enum imap_sync_flags imap_flags;
const char *tagline;
};
struct imap_sync_context {
enum imap_sync_flags imap_flags;
struct mailbox_transaction_context *t;
struct mailbox_sync_context *sync_ctx;
struct mailbox_sync_rec sync_rec;
unsigned int messages_count;
unsigned int failed:1;
unsigned int no_newmail:1;
};
{
T_BEGIN {
unsigned int i, count;
for (i = 0; i < count; i++) {
/* since we have to notify about expunged messages,
we expect that all the referenced UIDs exist */
}
/* replace uids with seqs */
} T_END;
}
static void
const struct imap_search_update *update)
{
&ctx->search_adds);
return;
if (update->return_uids)
else {
/* convert to sequences */
}
}
}
}
{
const struct imap_search_update *updates;
unsigned int i, count;
return;
}
} T_END;
}
struct imap_sync_context *
{
struct imap_sync_context *ctx;
/* always send UIDs in FETCH replies */
}
/* send search updates the first time after sync is initialized.
it now contains expunged messages that must be sent before
EXPUNGE replies. */
return ctx;
}
static void
const struct mailbox_status *status,
struct client_command_context *sync_cmd)
{
uint64_t send_modseq = 0;
if (status->sync_delayed_expunges &&
/* if client updates highest-modseq using returned MODSEQs
it loses expunges. try to avoid this by sending it a lower
pre-expunge HIGHESTMODSEQ reply. */
} else if (!status->sync_delayed_expunges &&
/* we've probably send some VANISHED or EXISTS replies which
increased the highest-modseq. notify the client about
this. */
}
if (send_modseq == 0) {
/* no sending */
/* modify the tagged reply directly */
"OK [HIGHESTMODSEQ %llu] %s",
(unsigned long long)send_modseq,
} else {
/* send an untagged OK reply */
"* OK [HIGHESTMODSEQ %llu]",
(unsigned long long)send_modseq));
}
if (!status->sync_delayed_expunges) {
/* no delayed expunges, remember this for future */
}
client->highest_fetch_modseq = 0;
}
struct client_command_context *sync_cmd)
{
struct mailbox_status status;
int ret;
STATUS_HIGHESTMODSEQ, &status) < 0 ||
return -1;
}
/* most clients would get confused by this. disconnect them. */
"Mailbox UIDVALIDITY changed");
}
if (!ctx->no_newmail) {
i_panic("Message count decreased");
}
!ctx->no_newmail) {
}
}
/* send search updates the second time after syncing in done.
}
return ret;
}
{
}
{
enum mail_flags flags;
const char *const *keywords;
if ((flags & MAIL_DELETED) != 0)
str_truncate(str, 0);
MAILBOX_FEATURE_CONDSTORE) != 0) {
}
}
{
str_truncate(str, 0);
}
{
unsigned int i, count;
/* Convert expunge sequences to UIDs and send them in VANISHED line. */
if (count == 0)
return;
for (i = 0; i < count; i++) {
if (start_uid != 0) {
if (!comma)
else
prev_uid);
}
}
}
}
if (!comma)
else
}
}
{
int ret = 1;
for (;;) {
/* get next one */
/* finished */
ret = 1;
break;
}
}
/* don't send change notifications of messages we
haven't even announced to client yet */
continue;
}
}
/* EXPUNGEs must come last */
case MAILBOX_SYNC_TYPE_FLAGS:
ret = 1;
if (ret <= 0)
break;
}
break;
/* Use a single VANISHED line */
ctx->messages_count -=
break;
}
ret = 1;
if (ret <= 0)
break;
str_truncate(str, 0);
}
/* update only after we're finished, so that
the seq2 > messages_count check above
doesn't break */
ctx->messages_count -=
}
break;
case MAILBOX_SYNC_TYPE_MODSEQ:
MAILBOX_FEATURE_CONDSTORE) == 0)
break;
ret = 1;
if (ret <= 0)
break;
}
break;
}
if (ret <= 0) {
/* failure / buffer full */
break;
}
}
return ret;
}
{
else {
return TRUE;
}
}
{
int ret;
return FALSE;
if (ret < 0)
}
/* Finish all commands that waited for this sync. Go through the queue
backwards, so that tagged replies are sent in the same order as
they were received. This fixes problems with clients that rely on
this (Apple Mail 3.2) */
if (cmd_finish_sync(cmd))
}
}
return cmd_finish_sync(sync_cmd);
}
enum mailbox_sync_flags *flags_r,
enum imap_sync_flags *imap_flags_r)
{
struct client_command_context *cmd;
*flags_r = 0;
*imap_flags_r = 0;
fast_count++;
count++;
}
}
if (fast_count != count)
MAILBOX_SYNC_FLAG_FIX_INCONSISTENT)) == 0);
}
{
struct imap_sync_context *ctx;
enum mailbox_sync_flags flags;
enum imap_sync_flags imap_flags;
bool no_newmail;
/* there may be multiple commands waiting. use their combined flags */
client->sync_counter++;
(imap_flags & IMAP_SYNC_FLAG_SAFE) == 0;
if (no_newmail) {
/* expunges might break the client just as badly as new mail
notifications. */
}
/* handle the syncing using sync_cmd. it doesn't actually matter which
one of the pending commands it is. */
if (!cmd_sync_continue(sync_cmd)) {
return FALSE;
}
(void)cmd_sync_delayed(client);
return TRUE;
}
static bool
{
return TRUE;
/* no mailbox selected, no point in delaying the sync */
return TRUE;
}
return FALSE;
}
{
}
enum mailbox_sync_flags flags,
enum imap_sync_flags imap_flags,
{
}
{
return FALSE;
if (cmd_finish_sync(cmd)) {
}
}
}
return ret;
}
{
/* wait until we can send output to client */
return FALSE;
}
/* wait until mailbox can be synced */
return cmd_sync_drop_fast(client);
}
/* separate syncs that can send expunges from those that can't */
if (first_nonexpunge == NULL)
} else {
if (first_expunge == NULL)
first_expunge = cmd;
}
}
}
/* sync expunges after nonexpunges */
}
}
return cmd_sync_drop_fast(client);
return cmd_sync_client(cmd);
}