/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
#include "imap-common.h"
#include "str.h"
#include "ostream.h"
#include "mail-user.h"
#include "mail-storage.h"
#include "mail-search-build.h"
#include "imap-quote.h"
#include "imap-util.h"
#include "imap-fetch.h"
#include "imap-notify.h"
#include "imap-commands.h"
#include "imap-sync-private.h"
{
T_BEGIN {
/* since we have to notify about expunged messages,
we expect that all the referenced UIDs exist */
}
/* replace uids with seqs */
} T_END;
}
{
int ret;
return ret;
/* finished the FETCH */
return -1;
return 1;
}
static int
const struct imap_search_update *update)
{
if (ctx->search_update_notifying)
return search_update_fetch_more(update);
return 1;
/* find the newly appended messages: ctx->messages_count is the message
count before new messages found by sync, client->messages_count is
the number of messages after. */
/* remove messages not in the search_adds list */
return search_update_fetch_more(update);
}
static int
const struct imap_search_update *update,
bool removes_only)
{
if (!ctx->search_update_notifying) {
&ctx->search_adds);
}
return 1;
if (ret == 0) {
return 0;
}
}
if (update->return_uids)
else {
/* convert to sequences */
}
}
}
return ret;
}
static int
{
unsigned int i, count;
return 1;
}
T_BEGIN {
} T_END;
if (ret <= 0)
break;
}
ctx->search_update_idx = i;
return ret;
}
struct imap_sync_context *
{
if (client->notify_immediate_expunges) {
/* NOTIFY enabled without SELECTED-DELAYED */
}
/* make sure user can't DoS the system by causing Dovecot to create
tons of useless namespaces. */
/* 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. */
i_unreached();
ctx->search_update_idx = 0;
return ctx;
}
static void
struct client_command_context *sync_cmd)
{
/* if client updates highest-modseq using returned MODSEQs
it loses expunges. try to avoid this by sending it a lower
pre-expunge HIGHESTMODSEQ reply. */
/* we've probably sent 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 */
} else {
/* send an untagged OK reply */
send_modseq));
}
/* no delayed expunges, remember this for future */
}
client->highest_fetch_modseq = 0;
}
{
return ret;
it can't really fail.. */
(void)mailbox_transaction_commit(&ctx->t);
ret = -1;
}
/* most clients would get confused by this. disconnect them. */
"Mailbox UIDVALIDITY changed");
}
"IMAP session state is inconsistent, please relogin.");
/* we can't trust status information anymore, so don't try to
sync message counts. */
return -1;
}
i_panic("Message count decreased");
}
}
}
return ret;
}
{
/* send FETCH replies for the new mails */
return 0;
if (ret < 0)
}
/* send search updates the second time after syncing in done.
if (ret > 0)
return ret;
}
struct client_command_context *sync_cmd)
{
int ret;
}
return ret;
}
{
}
{
const char *const *keywords;
if ((flags & MAIL_DELETED) != 0)
str_truncate(str, 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
}
}
{
/* NOTIFY: MessageEvent not specified for selected mailbox */
return 1;
}
/* Use a single VANISHED line */
return 1;
}
if (ret == 0) {
/* buffer full, continue later */
return 0;
}
str_truncate(str, 0);
}
return 1;
}
{
return imap_sync_notify_more(ctx);
/* finish syncing even when client has disconnected. otherwise our
internal state (ctx->messages_count) can get messed up and unless
we immediately stop handling all commands and syncs we could end up
assert-crashing. */
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:
/* NOTIFY: FlagChange not specified for
selected mailbox */
break;
}
ret = 1;
if (ret == 0)
break;
}
break;
if (ret > 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;
/* NOTIFY: FlagChange not specified for
selected mailbox. The RFC doesn't explicitly
specify MODSEQ changes, but they're close
enough to flag changes. */
break;
}
ret = 1;
if (ret == 0)
break;
}
break;
}
if (ret == 0) {
/* buffer full */
break;
}
}
if (ret > 0) {
return -1;
return imap_sync_more(ctx);
}
return ret;
}
{
return FALSE;
return FALSE;
return TRUE;
}
{
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) */
}
}
return TRUE;
}
enum mailbox_sync_flags *flags_r,
enum imap_sync_flags *imap_flags_r)
{
*flags_r = 0;
*imap_flags_r = 0;
fast_count++;
count++;
}
}
if (fast_count != count)
}
{
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;
}
return TRUE;
}
{
return TRUE;
/* no mailbox selected, no point in delaying the sync */
return TRUE;
}
return FALSE;
}
{
return FALSE;
continue;
}
}
return ret;
}
{
/* wait until we can send output to client */
return FALSE;
}
if (!imap_sync_is_allowed(client)) {
/* 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);
}
{
bool ret;
T_BEGIN {
} T_END;
return ret;
}