set.import_commit_msgs_interval = ctx->import_commit_msgs_interval;
set.state = ctx->state_input;
set.mailbox_alt_char = doveadm_settings->dsync_alt_char[0];
-
+ if (*doveadm_settings->dsync_hashed_headers == '\0') {
+ i_error("dsync_hashed_headers must not be empty");
+ ctx->ctx.exit_code = EX_USAGE;
+ return -1;
+ }
+ set.hashed_headers =
+ t_strsplit_spaces(doveadm_settings->dsync_hashed_headers, " ,");
if (array_count(&ctx->exclude_mailboxes) > 0) {
/* array is NULL-terminated in init() */
set.exclude_mailboxes = array_idx(&ctx->exclude_mailboxes, 0);
DEF(SET_STR, dsync_features),
DEF(SET_UINT, dsync_commit_msgs_interval),
DEF(SET_STR, doveadm_http_rawlog_dir),
+ DEF(SET_STR, dsync_hashed_headers),
{ SET_STRLIST, "plugin", offsetof(struct doveadm_settings, plugin_envs), NULL },
.dsync_alt_char = "_",
.dsync_remote_cmd = "ssh -l%{login} %{host} doveadm dsync-server -u%u -U",
.dsync_features = "",
+ .dsync_hashed_headers = "Date Message-ID",
.dsync_commit_msgs_interval = 100,
.ssl_client_ca_dir = "",
.ssl_client_ca_file = "",
fix_base_path(set, pool, &set->auth_socket_path);
fix_base_path(set, pool, &set->doveadm_socket_path);
#endif
+ if (*set->dsync_hashed_headers == '\0') {
+ *error_r = "dsync_hashed_headers must not be empty";
+ return FALSE;
+ }
if (*set->dsync_alt_char == '\0') {
*error_r = "dsync_alt_char must not be empty";
return FALSE;
const char *director_username_hash;
const char *doveadm_api_key;
const char *dsync_features;
+ const char *dsync_hashed_headers;
unsigned int dsync_commit_msgs_interval;
const char *doveadm_http_rawlog_dir;
enum dsync_features parsed_features;
brain->sync_max_size,
brain->sync_flag,
brain->import_commit_msgs_interval,
- import_flags, brain->hdr_hash_version);
+ import_flags, brain->hdr_hash_version,
+ brain->hashed_headers);
}
int dsync_brain_sync_mailbox_open(struct dsync_brain *brain,
dsync_mailbox_export_init(brain->box, brain->log_scan,
last_common_uid,
exporter_flags,
- brain->hdr_hash_version);
+ brain->hdr_hash_version,
+ brain->hashed_headers);
dsync_brain_sync_mailbox_init_remote(brain, remote_dsync_box);
return 1;
}
const char *changes_during_sync;
enum mail_error mail_error;
+ const char *const *hashed_headers;
+
bool master_brain:1;
bool mail_requests:1;
bool backup_send:1;
brain->lock_timeout = set->lock_timeout_secs;
brain->import_commit_msgs_interval = set->import_commit_msgs_interval;
brain->master_brain = TRUE;
+ brain->hashed_headers =
+ (const char*const*)p_strarray_dup(brain->pool, set->hashed_headers);
dsync_brain_set_flags(brain, flags);
if (set->virtual_all_box != NULL)
ibc_set.hdr_hash_v2 = TRUE;
ibc_set.lock_timeout = set->lock_timeout_secs;
ibc_set.import_commit_msgs_interval = set->import_commit_msgs_interval;
+ ibc_set.hashed_headers = set->hashed_headers;
/* reverse the backup direction for the slave */
ibc_set.brain_flags = flags & ~(DSYNC_BRAIN_FLAG_BACKUP_SEND |
DSYNC_BRAIN_FLAG_BACKUP_RECV);
brain->sync_type = ibc_set->sync_type;
dsync_brain_set_flags(brain, ibc_set->brain_flags);
+ if (ibc_set->hashed_headers != NULL)
+ brain->hashed_headers =
+ p_strarray_dup(brain->pool, (const char*const*)ibc_set->hashed_headers);
/* this flag is only set on the remote slave brain */
brain->purge = (ibc_set->brain_flags &
DSYNC_BRAIN_FLAG_PURGE_REMOTE) != 0;
/* Sync only mails which contains / doesn't contain this flag.
'-' at the beginning means this flag must not exist. */
const char *sync_flag;
+ /* Headers to hash (defaults to Date, Message-ID) */
+ const char *const *hashed_headers;
/* If non-zero, use dsync lock file for this user */
unsigned int lock_timeout_secs;
"send_mail_requests backup_send backup_recv lock_timeout "
"no_mail_sync no_mailbox_renames no_backup_overwrite purge_remote "
"no_notify sync_since_timestamp sync_max_size sync_flags sync_until_timestamp "
- "virtual_all_box empty_hdr_workaround import_commit_msgs_interval"
+ "virtual_all_box empty_hdr_workaround import_commit_msgs_interval "
+ "hashed_headers"
},
{ .name = "mailbox_state",
.chr = 'S',
dsync_serializer_encode_add(encoder, "no_notify", "");
if ((set->brain_flags & DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND) != 0)
dsync_serializer_encode_add(encoder, "empty_hdr_workaround", "");
-
+ /* this can be NULL in slave */
+ string_t *str2 = t_str_new(32);
+ if (set->hashed_headers != NULL) {
+ for(const char *const *ptr = set->hashed_headers; *ptr != NULL; ptr++) {
+ str_append_tabescaped(str2, *ptr);
+ str_append_c(str2, '\t');
+ }
+ }
+ dsync_serializer_encode_add(encoder, "hashed_headers", str_c(str2));
dsync_serializer_encode_finish(&encoder, str);
dsync_ibc_stream_send_string(ibc, str);
}
set->brain_flags |= DSYNC_BRAIN_FLAG_NO_NOTIFY;
if (dsync_deserializer_decode_try(decoder, "empty_hdr_workaround", &value))
set->brain_flags |= DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND;
+ if (dsync_deserializer_decode_try(decoder, "hashed_headers", &value))
+ set->hashed_headers = (const char*const*)p_strsplit_tabescaped(pool, value);
set->hdr_hash_v2 = ibc->minor_version >= DSYNC_PROTOCOL_MINOR_HAVE_HDR_HASH_V2;
set->hdr_hash_v3 = ibc->minor_version >= DSYNC_PROTOCOL_MINOR_HAVE_HDR_HASH_V3;
uoff_t sync_max_size;
/* Sync only mails with specified flags. */
const char *sync_flags;
+ /* Hashed headers */
+ const char *const *hashed_headers;
enum dsync_brain_sync_type sync_type;
enum dsync_brain_flags brain_flags;
#include "mail-storage.h"
#include "dsync-mail.h"
-/* These should be good enough to identify all normal mails. Received: header
- would make it even better, but those can be somewhat large. Also these
- fields can be looked up using IMAP ENVELOPE, which is more efficient in
- some IMAP servers. */
-static const char *hashed_headers[] = {
- "Date", "Message-ID", NULL
-};
-
struct mailbox_header_lookup_ctx *
-dsync_mail_get_hash_headers(struct mailbox *box)
+dsync_mail_get_hash_headers(struct mailbox *box, const char *const *hashed_headers)
{
return mailbox_header_lookup_init(box, hashed_headers);
}
int dsync_mail_get_hdr_hash(struct mail *mail, unsigned int version,
- const char **hdr_hash_r)
+ const char *const *hashed_headers, const char **hdr_hash_r)
{
struct istream *hdr_input, *input;
struct mailbox_header_lookup_ctx *hdr_ctx;
};
struct mailbox_header_lookup_ctx *
-dsync_mail_get_hash_headers(struct mailbox *box);
+dsync_mail_get_hash_headers(struct mailbox *box, const char *const *hashed_headers);
int dsync_mail_get_hdr_hash(struct mail *mail, unsigned int version,
- const char **hdr_hash_r);
+ const char *const *hashed_headers, const char **hdr_hash_r);
static inline bool dsync_mail_hdr_hash_is_empty(const char *hdr_hash)
{
/* md5(\n) */
unsigned int search_pos, search_count;
unsigned int hdr_hash_version;
+ const char *const *hashed_headers;
+
/* GUID => instances */
HASH_TABLE(char *, struct dsync_mail_guid_instances *) export_guids;
ARRAY_TYPE(seq_range) requested_uids;
*hdr_hash_r = "";
return 1;
}
- if (dsync_mail_get_hdr_hash(mail, exporter->hdr_hash_version, hdr_hash_r) < 0)
+ if (dsync_mail_get_hdr_hash(mail, exporter->hdr_hash_version,
+ exporter->hashed_headers, hdr_hash_r) < 0)
return dsync_mail_error(exporter, mail, "hdr-stream");
return 1;
} else if (**guid_r == '\0') {
struct dsync_transaction_log_scan *log_scan,
uint32_t last_common_uid,
enum dsync_mailbox_exporter_flags flags,
- unsigned int hdr_hash_version)
+ unsigned int hdr_hash_version,
+ const char *const *hashed_headers)
{
struct dsync_mailbox_exporter *exporter;
pool_t pool;
exporter->hdr_hash_version = hdr_hash_version;
exporter->no_hdr_hashes =
(flags & DSYNC_MAILBOX_EXPORTER_FLAG_NO_HDR_HASHES) != 0;
+ exporter->hashed_headers = hashed_headers;
+
p_array_init(&exporter->requested_uids, pool, 16);
p_array_init(&exporter->search_uids, pool, 16);
hash_table_create(&exporter->export_guids, pool, 0, str_hash, strcmp);
p_array_init(&exporter->expunged_guids, pool, 16);
if (!exporter->mails_have_guids && !exporter->no_hdr_hashes)
- exporter->wanted_headers = dsync_mail_get_hash_headers(box);
+ exporter->wanted_headers =
+ dsync_mail_get_hash_headers(box, exporter->hashed_headers);
/* first scan transaction log and save any expunges and flag changes */
dsync_mailbox_export_log_scan(exporter, log_scan);
struct dsync_transaction_log_scan *log_scan,
uint32_t last_common_uid,
enum dsync_mailbox_exporter_flags flags,
- unsigned int hdr_hash_version);
+ unsigned int hdr_hash_version,
+ const char *const *hashed_headers);
/* Returns 1 if attribute was returned, 0 if no more attributes, -1 on error */
int dsync_mailbox_export_next_attr(struct dsync_mailbox_exporter *exporter,
const struct dsync_mailbox_attribute **attr_r);
unsigned int hdr_hash_version;
unsigned int commit_msgs_interval;
+ const char *const *hashed_headers;
+
enum mail_flags sync_flag;
const char *sync_keyword;
bool sync_flag_dontwant;
const char *sync_flag,
unsigned int commit_msgs_interval,
enum dsync_mailbox_import_flags flags,
- unsigned int hdr_hash_version)
+ unsigned int hdr_hash_version,
+ const char *const *hashed_headers)
{
struct dsync_mailbox_importer *importer;
struct mailbox_status status;
importer->sync_until_timestamp = sync_until_timestamp;
importer->sync_max_size = sync_max_size;
importer->stateful_import = importer->last_common_uid_found;
+ importer->hashed_headers = hashed_headers;
+
if (sync_flag != NULL) {
if (sync_flag[0] == '-') {
importer->sync_flag_dontwant = TRUE;
} else {
if (dsync_mail_get_hdr_hash(importer->cur_mail,
importer->hdr_hash_version,
+ importer->hashed_headers,
&hdr_hash) < 0) {
dsync_mail_error(importer, importer->cur_mail,
"header hash");
}
if (dsync_mail_get_hdr_hash(importer->cur_mail,
- importer->hdr_hash_version, &hdr_hash) < 0) {
+ importer->hdr_hash_version,
+ importer->hashed_headers, &hdr_hash) < 0) {
dsync_mail_error(importer, importer->cur_mail, "hdr-stream");
*result_r = "Error fetching header stream";
return -1;
const char *sync_flag,
unsigned int commit_msgs_interval,
enum dsync_mailbox_import_flags flags,
- unsigned int hdr_hash_version);
+ unsigned int hdr_hash_version,
+ const char *const *hashed_headers);
int dsync_mailbox_import_attribute(struct dsync_mailbox_importer *importer,
const struct dsync_mailbox_attribute *attr);
int dsync_mailbox_import_change(struct dsync_mailbox_importer *importer,