/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "hash.h"
#include "istream.h"
#include "mail-index-modseq.h"
#include "mail-storage-private.h"
#include "mail-search-build.h"
#include "dsync-transaction-log-scan.h"
#include "dsync-mail.h"
#include "dsync-mailbox.h"
#include "dsync-mailbox-export.h"
struct dsync_mail_guid_instances {
bool requested;
bool searched;
};
struct dsync_mailbox_exporter {
unsigned int hdr_hash_version;
const char *const *hashed_headers;
/* GUID => instances */
unsigned int expunged_guid_idx;
/* uint32_t UID => struct dsync_mail_change */
/* changes sorted by UID */
unsigned int change_idx;
const char *error;
};
{
const char *errstr;
if (error == MAIL_ERROR_EXPUNGED)
return 0;
"Can't lookup %s for UID=%u: %s",
return -1;
}
static bool
char *type_r)
{
const char *const *changes;
unsigned int i, count;
for (i = 0; i < count; i++) {
continue;
switch (changes[i][0]) {
case KEYWORD_CHANGE_ADD:
/* replace with ADD_AND_FINAL */
return FALSE;
case KEYWORD_CHANGE_REMOVE:
/* a final keyword is marked as removed.
this shouldn't normally happen. */
return FALSE;
case KEYWORD_CHANGE_FINAL:
/* no change */
return TRUE;
}
}
return FALSE;
}
static void
{
const char *const *keywords;
unsigned int i;
char type;
}
/* add the final keyword if it's not already there
as +keyword */
const char *keyword_change =
&keyword_change, 1);
}
}
}
static int
const char **hdr_hash_r)
{
*guid_r = "";
*hdr_hash_r = NULL;
/* always try to get GUID, even if we're also getting header hash */
if (!exporter->mails_have_guids) {
/* get header hash also */
if (exporter->no_hdr_hashes) {
*hdr_hash_r = "";
return 1;
}
return 1;
} else if (**guid_r == '\0') {
"sync with header hashes instead";
return -1;
} else {
/* GUIDs are required, we don't need header hash */
return 1;
}
}
static int
{
int ret;
} else {
}
return -1;
if (ret == 0) {
/* the message was expunged during export */
/* find its GUID from log if possible */
if (log_change != NULL)
} else {
}
return 0;
}
static struct dsync_mail_change *
{
} else {
/* move flag changes into a save. this happens only when
last_common_uid isn't known */
}
return change;
}
static void
{
/* GUIDs not supported, mail is requested by UIDs */
return;
}
/* mail UIDs are manually requested */
return;
}
struct dsync_mail_guid_instances, 1);
if (exporter->auto_export_mails)
}
}
static int
{
int ret;
/* update wanted fields in case we didn't already set them for the
search */
if (exporter->export_virtual_sizes)
/* If message is already expunged here, just skip it */
return ret;
if (exporter->export_received_timestamps) {
if (received_timestamp == 0) {
/* don't allow timestamps to be zero. we want to have
asserts verify that the timestamp is set properly. */
received_timestamp = 1;
}
}
if (exporter->export_virtual_sizes) {
}
return 1;
}
static void
{
void *key;
}
}
static void
{
void *key;
/* any flag changes for UIDs above last_common_uid weren't found by
mailbox search, which means they were already expunged. for some
reason the log scanner found flag changes for the message, but not
the expunge. just remove these. */
}
}
static void
{
int ret = 0;
/* we want to know about all mails */
} else {
/* lookup GUIDs for messages with flag changes */
/* lookup new messages */
(uint32_t)-1);
}
if (exporter->last_common_uid == 0) {
/* we're syncing all mails, so we can request the wanted
fields for all the mails */
}
__func__);
T_BEGIN {
else
} T_END;
if (ret < 0)
break;
}
if (mailbox_search_deinit(&search_ctx) < 0 &&
"Mail search failed: %s",
&exporter->mail_error));
}
}
struct dsync_mail_change *const *c2)
{
return -1;
return 1;
return 0;
}
static void
{
void *key;
}
static void
enum mail_attribute_type type)
{
}
static void
struct dsync_transaction_log_scan *log_scan)
{
void *key;
no changes get lost, we need to send all of the messages */
}
/* clone the hash table, since we're changing it. */
*dup_change = *change;
}
}
struct dsync_mailbox_exporter *
struct dsync_transaction_log_scan *log_scan,
unsigned int hdr_hash_version,
const char *const *hashed_headers)
{
4096);
(flags & DSYNC_MAILBOX_EXPORTER_FLAG_TIMESTAMPS) != 0;
(flags & DSYNC_MAILBOX_EXPORTER_FLAG_VSIZES) != 0;
/* first scan transaction log and save any expunges and flag changes */
/* get saves and also find GUIDs for flag changes */
/* get the changes sorted by UID */
return exporter;
}
static int
{
continue;
/* lookup the value mainly to get its last_change value. */
&exporter->mail_error));
break;
}
continue;
}
}
return 1;
}
return 0;
}
static int
{
const char *key;
bool export_all_attrs;
exporter->last_common_uid == 0;
/* note that the order of processing may be important for some
attributes. for example sieve can't set a script active until it's
first been created */
continue;
&value) < 0) {
"Mailbox attribute %s lookup failed: %s", key,
&exporter->mail_error));
return -1;
}
/* readonly attributes can't be changed,
no point in exporting them */
continue;
}
/* the attribute was just deleted?
skip for this sync. */
continue;
}
/* duplicate attribute returned.
shouldn't normally happen, but don't crash. */
continue;
}
if (attr_change != NULL) {
} else {
}
return 1;
}
"Mailbox attribute iteration failed: %s",
&exporter->mail_error));
return -1;
}
/* export shared attributes */
}
}
const struct dsync_mailbox_attribute **attr_r)
{
int ret;
return -1;
} else {
}
if (ret > 0)
return ret;
}
const struct dsync_mail_change **change_r)
{
unsigned int count;
return -1;
return 0;
return 1;
}
static int
{
char *guid;
const char *const_guid;
unsigned int i, count;
/* get a list of messages we want to fetch. if there are more than one
instance for a GUID, use the first one. */
continue;
/* we're on a second round, refetching expunged
messages */
/* no instances left */
const_guid = guid;
&const_guid, 1);
continue;
}
}
}
/* add requested UIDs */
for (i = 0; i < count; i++) {
}
if (!exporter->minimal_dmail_fill) {
}
}
static void
{
return;
"Mail search failed: %s",
&exporter->mail_error));
}
}
{
const char *error_field;
/* GUID found */
/* mail requested by UID */
} else {
"GUID unexpectedly changed for UID=%u GUID=%s",
return -1;
}
else
/* this message was successfully returned, don't try retrying it */
return 1;
}
const struct dsync_mail_request *request)
{
return;
}
return;
}
}
const struct dsync_mail **mail_r)
{
const char *const *guids;
unsigned int count;
int ret;
return -1;
if (!exporter->body_search_initialized) {
if (dsync_mailbox_export_body_search_init(exporter) < 0) {
return -1;
}
}
exporter->search_pos++;
return 1;
}
if (ret < 0) {
return -1;
}
/* the message was expunged. if the GUID has another instance,
try sending it later. */
}
/* if some instances of messages were expunged, retry fetching them
with other instances */
return -1;
}
if (ret > 0) {
/* not finished yet */
}
/* finished with messages. if there are any expunged messages,
return them */
return 1;
}
return 0;
}
{
}
{
return "";
}