dsync-mailbox-export.c revision d8bdf558c7ba173fc47a194633d9bd97af1b9c74
/* Copyright (c) 2013 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 {
struct dsync_transaction_log_scan *log_scan;
struct mailbox_transaction_context *trans;
struct mail_search_context *search_ctx;
/* GUID => instances */
unsigned int requested_uid_search_idx;
unsigned int expunged_guid_idx;
/* uint32_t UID => struct dsync_mail_change */
/* changes sorted by UID */
unsigned int change_idx;
struct mailbox_attribute_iter *attr_iter;
struct hash_iterate_context *attr_change_iter;
enum mail_attribute_type attr_type;
struct dsync_mailbox_attribute attr;
struct dsync_mail_change change;
struct dsync_mail dsync_mail;
const char *error;
unsigned int body_search_initialized:1;
unsigned int auto_export_mails:1;
unsigned int mails_have_guids:1;
unsigned int return_all_mails:1;
};
{
const char *errstr;
enum mail_error error;
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 */
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 *
{
struct dsync_mail_change *change;
} else {
/* move flag changes into a save. this happens only when
last_common_uid isn't known */
}
return change;
}
static void
{
struct dsync_mail_guid_instances *instances;
/* 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
{
struct dsync_mail_change *change;
int ret;
/* If message is already expunged here, just skip it */
return ret;
return 1;
}
static void
{
struct hash_iterate_context *iter;
void *key;
struct dsync_mail_change *change;
}
}
static void
{
struct hash_iterate_context *iter;
void *key;
struct dsync_mail_change *change;
/* 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
{
struct mail_search_context *search_ctx;
struct mail_search_args *search_args;
struct mail_search_arg *sarg;
int ret;
/* we want to know about all mails */
} else {
/* lookup GUIDs for messages with flag changes */
/* lookup new messages */
(uint32_t)-1);
}
0, NULL);
else
if (ret < 0)
break;
}
if (mailbox_search_deinit(&search_ctx) < 0 &&
"Mail search failed: %s",
}
}
struct dsync_mail_change *const *c2)
{
return -1;
return 1;
return 0;
}
static void
{
struct hash_iterate_context *iter;
void *key;
struct dsync_mail_change *change;
}
static void
enum mail_attribute_type type)
{
}
static void
struct dsync_transaction_log_scan *log_scan)
{
struct hash_iterate_context *iter;
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,
{
struct dsync_mailbox_exporter *exporter;
4096);
/* 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
{
struct dsync_mailbox_attribute *attr;
struct mail_attribute_value value;
continue;
/* lookup the value mainly to get its last_change value. */
break;
}
continue;
}
}
return 1;
}
return 0;
}
static int
{
struct dsync_mailbox_attribute *attr_change;
const char *key;
struct mail_attribute_value value;
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,
return -1;
}
/* readonly attributes can't be changed,
no point in exporting them */
continue;
}
/* the attribute was just deleted?
skip for this sync. */
continue;
}
if (attr_change != NULL) {
} else {
}
return 1;
}
"Mailbox attribute iteration failed: %s",
return -1;
}
/* export shared attributes */
}
}
const struct dsync_mailbox_attribute *
{
return NULL;
if (dsync_mailbox_export_iter_next_attr(exporter) <= 0)
return NULL;
} else {
return NULL;
}
}
const struct dsync_mail_change *
{
struct dsync_mail_change *const *changes;
unsigned int count;
return NULL;
return NULL;
}
static int
{
struct mail_search_args *search_args;
struct mail_search_arg *sarg;
struct hash_iterate_context *iter;
char *guid;
const char *const_guid;
struct dsync_mail_guid_instances *instances;
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 */
}
}
static void
{
return;
"Mail search failed: %s",
}
}
{
struct dsync_mail_guid_instances *instances;
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)
{
struct dsync_mail_guid_instances *instances;
return;
}
return;
}
}
const struct dsync_mail *
{
const char *const *guids;
unsigned int count;
int ret;
return NULL;
if (!exporter->body_search_initialized) {
if (dsync_mailbox_export_body_search_init(exporter) < 0) {
return NULL;
}
}
return &exporter->dsync_mail;
if (ret < 0) {
return NULL;
}
/* 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 NULL;
}
if (ret > 0) {
/* not finished yet */
return dsync_mailbox_export_next_mail(exporter);
}
/* finished with messages. if there are any expunged messages,
return them */
return &exporter->dsync_mail;
}
return NULL;
}
const char **error_r)
{
}