diff --git a/src/doveadm/doveadm-dsync.c b/src/doveadm/doveadm-dsync.c
index 94e1599c47..22a48c25f5 100644
--- a/src/doveadm/doveadm-dsync.c
+++ b/src/doveadm/doveadm-dsync.c
@@ -17,6 +17,7 @@
#include "settings-parser.h"
#include "imap-util.h"
#include "master-service.h"
+#include "master-service-settings.h"
#include "master-service-ssl-settings.h"
#include "mail-storage.h"
#include "mail-storage-service.h"
@@ -103,6 +104,7 @@ struct dsync_cmd_context {
bool local_location_from_arg:1;
bool replicator_notify:1;
bool exited:1;
+ bool empty_hdr_workaround:1;
};
static bool legacy_dsync = FALSE;
@@ -573,6 +575,7 @@ cmd_dsync_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
set.lock_timeout_secs = ctx->lock_timeout;
set.state = ctx->state_input;
set.mailbox_alt_char = doveadm_settings->dsync_alt_char[0];
+
if (array_count(&ctx->exclude_mailboxes) > 0) {
/* array is NULL-terminated in init() */
set.exclude_mailboxes = array_idx(&ctx->exclude_mailboxes, 0);
@@ -622,6 +625,8 @@ cmd_dsync_run(struct doveadm_mail_cmd_context *_ctx, struct mail_user *user)
brain_flags |= DSYNC_BRAIN_FLAG_NO_MAIL_SYNC;
if (ctx->oneway)
brain_flags |= DSYNC_BRAIN_FLAG_NO_BACKUP_OVERWRITE;
+ if (ctx->empty_hdr_workaround)
+ brain_flags |= DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND;
if (doveadm_debug)
brain_flags |= DSYNC_BRAIN_FLAG_DEBUG;
@@ -1064,6 +1069,8 @@ static struct doveadm_mail_cmd_context *cmd_dsync_alloc(void)
DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE);
p_array_init(&ctx->exclude_mailboxes, ctx->ctx.pool, 4);
p_array_init(&ctx->namespace_prefixes, ctx->ctx.pool, 4);
+ if ((doveadm_settings->parsed_features & DSYNC_FEATURE_EMPTY_HDR_WORKAROUND) != 0)
+ ctx->empty_hdr_workaround = TRUE;
return &ctx->ctx;
}
@@ -1179,6 +1186,7 @@ static struct doveadm_mail_cmd_context *cmd_dsync_server_alloc(void)
ctx->ctx.v.parse_arg = cmd_mailbox_dsync_server_parse_arg;
ctx->ctx.v.run = cmd_dsync_server_run;
ctx->sync_type = DSYNC_BRAIN_SYNC_TYPE_CHANGED;
+
ctx->fd_in = STDIN_FILENO;
ctx->fd_out = STDOUT_FILENO;
return &ctx->ctx;
diff --git a/src/doveadm/doveadm-settings.c b/src/doveadm/doveadm-settings.c
index df1228415d..0923f3799a 100644
--- a/src/doveadm/doveadm-settings.c
+++ b/src/doveadm/doveadm-settings.c
@@ -71,6 +71,7 @@ static const struct setting_define doveadm_setting_defines[] = {
DEF(SET_STR, ssl_client_ca_file),
DEF(SET_STR, director_username_hash),
DEF(SET_STR, doveadm_api_key),
+ DEF(SET_STR, dsync_features),
{ SET_STRLIST, "plugin", offsetof(struct doveadm_settings, plugin_envs), NULL },
@@ -92,6 +93,7 @@ const struct doveadm_settings doveadm_default_settings = {
.doveadm_allowed_commands = "",
.dsync_alt_char = "_",
.dsync_remote_cmd = "ssh -l%{login} %{host} doveadm dsync-server -u%u -U",
+ .dsync_features = "",
.ssl_client_ca_dir = "",
.ssl_client_ca_file = "",
.director_username_hash = "%Lu",
@@ -129,6 +131,43 @@ fix_base_path(struct doveadm_settings *set, pool_t pool, const char **str)
}
/* */
+struct dsync_feature_list {
+ const char *name;
+ enum dsync_features num;
+};
+
+static const struct dsync_feature_list dsync_feature_list[] = {
+ { "empty_header_workaround", DSYNC_FEATURE_EMPTY_HDR_WORKAROUND },
+ { NULL, 0 }
+};
+
+static int
+dsync_settings_parse_features(struct doveadm_settings *set,
+ const char **error_r)
+{
+ enum dsync_features features = 0;
+ const struct dsync_feature_list *list;
+ const char *const *str;
+
+ str = t_strsplit_spaces(set->dsync_features, " ,");
+ for (; *str != NULL; str++) {
+ list = dsync_feature_list;
+ for (; list->name != NULL; list++) {
+ if (strcasecmp(*str, list->name) == 0) {
+ features |= list->num;
+ break;
+ }
+ }
+ if (list->name == NULL) {
+ *error_r = t_strdup_printf("dsync_features: "
+ "Unknown feature: %s", *str);
+ return -1;
+ }
+ }
+ set->parsed_features = features;
+ return 0;
+}
+
static bool doveadm_settings_check(void *_set, pool_t pool ATTR_UNUSED,
const char **error_r)
{
@@ -142,6 +181,8 @@ static bool doveadm_settings_check(void *_set, pool_t pool ATTR_UNUSED,
*error_r = "dsync_alt_char must not be empty";
return FALSE;
}
+ if (dsync_settings_parse_features(set, error_r) != 0)
+ return FALSE;
return TRUE;
}
/* */
diff --git a/src/doveadm/doveadm-settings.h b/src/doveadm/doveadm-settings.h
index 07ecd3d940..6ea049a4d8 100644
--- a/src/doveadm/doveadm-settings.h
+++ b/src/doveadm/doveadm-settings.h
@@ -3,6 +3,12 @@
#include "net.h"
+/* */
+enum dsync_features {
+ DSYNC_FEATURE_EMPTY_HDR_WORKAROUND = 0x1,
+};
+/* */
+
struct doveadm_settings {
const char *base_dir;
const char *libexec_dir;
@@ -22,7 +28,8 @@ struct doveadm_settings {
const char *ssl_client_ca_file;
const char *director_username_hash;
const char *doveadm_api_key;
-
+ const char *dsync_features;
+ enum dsync_features parsed_features;
ARRAY(const char *) plugin_envs;
};
diff --git a/src/doveadm/doveadm.c b/src/doveadm/doveadm.c
index 67a8034238..b73fa76fdc 100644
--- a/src/doveadm/doveadm.c
+++ b/src/doveadm/doveadm.c
@@ -269,6 +269,8 @@ static void doveadm_read_settings(void)
set = master_service_settings_get_others(master_service)[0];
doveadm_settings = settings_dup(&doveadm_setting_parser_info, set,
pool_datastack_create());
+
+ doveadm_settings->parsed_features = set->parsed_features; /* copy this value by hand */
}
static struct doveadm_cmd *doveadm_cmdline_commands[] = {
diff --git a/src/doveadm/dsync/dsync-brain-mailbox.c b/src/doveadm/dsync/dsync-brain-mailbox.c
index bd380645bb..c7ffcd080b 100644
--- a/src/doveadm/dsync/dsync-brain-mailbox.c
+++ b/src/doveadm/dsync/dsync-brain-mailbox.c
@@ -220,6 +220,8 @@ dsync_brain_sync_mailbox_init_remote(struct dsync_brain *brain,
import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_NO_NOTIFY;
if (brain->hdr_hash_v2)
import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_HDR_HASH_V2;
+ if (brain->empty_hdr_workaround)
+ import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_EMPTY_HDR_WORKAROUND;
brain->box_importer = brain->backup_send ? NULL :
dsync_mailbox_import_init(brain->box, brain->virtual_all_box,
diff --git a/src/doveadm/dsync/dsync-brain-private.h b/src/doveadm/dsync/dsync-brain-private.h
index 67e9e9cac4..dc9938bd33 100644
--- a/src/doveadm/dsync/dsync-brain-private.h
+++ b/src/doveadm/dsync/dsync-brain-private.h
@@ -114,6 +114,7 @@ struct dsync_brain {
bool no_notify:1;
bool hdr_hash_v2:1;
bool failed:1;
+ bool empty_hdr_workaround:1;
};
extern const char *dsync_box_state_names[DSYNC_BOX_STATE_DONE+1];
diff --git a/src/doveadm/dsync/dsync-brain.c b/src/doveadm/dsync/dsync-brain.c
index 8e43d3d102..1b1e7823ae 100644
--- a/src/doveadm/dsync/dsync-brain.c
+++ b/src/doveadm/dsync/dsync-brain.c
@@ -143,6 +143,7 @@ dsync_brain_set_flags(struct dsync_brain *brain, enum dsync_brain_flags flags)
brain->no_mailbox_renames =
(flags & DSYNC_BRAIN_FLAG_NO_MAILBOX_RENAMES) != 0;
brain->no_notify = (flags & DSYNC_BRAIN_FLAG_NO_NOTIFY) != 0;
+ brain->empty_hdr_workaround = (flags & DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND) != 0;
}
static void
diff --git a/src/doveadm/dsync/dsync-brain.h b/src/doveadm/dsync/dsync-brain.h
index 168e92f138..3f8db7e3a6 100644
--- a/src/doveadm/dsync/dsync-brain.h
+++ b/src/doveadm/dsync/dsync-brain.h
@@ -32,7 +32,9 @@ enum dsync_brain_flags {
be removed once the renaming logic has no more bugs.. */
DSYNC_BRAIN_FLAG_NO_MAILBOX_RENAMES = 0x200,
/* Add MAILBOX_TRANSACTION_FLAG_NO_NOTIFY to transactions. */
- DSYNC_BRAIN_FLAG_NO_NOTIFY = 0x400
+ DSYNC_BRAIN_FLAG_NO_NOTIFY = 0x400,
+ /* Workaround missing Date/Message-ID headers */
+ DSYNC_BRAIN_FLAG_EMPTY_HDR_WORKAROUND = 0x800,
};
enum dsync_brain_sync_type {
diff --git a/src/doveadm/dsync/dsync-ibc-stream.c b/src/doveadm/dsync/dsync-ibc-stream.c
index 76bd155285..c03cfe6e22 100644
--- a/src/doveadm/dsync/dsync-ibc-stream.c
+++ b/src/doveadm/dsync/dsync-ibc-stream.c
@@ -731,6 +731,8 @@ dsync_ibc_stream_send_handshake(struct dsync_ibc *_ibc,
dsync_serializer_encode_add(encoder, "purge_remote", "");
if ((set->brain_flags & DSYNC_BRAIN_FLAG_NO_NOTIFY) != 0)
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", "");
dsync_serializer_encode_finish(&encoder, str);
dsync_ibc_stream_send_string(ibc, str);
@@ -839,6 +841,8 @@ dsync_ibc_stream_recv_handshake(struct dsync_ibc *_ibc,
set->brain_flags |= DSYNC_BRAIN_FLAG_PURGE_REMOTE;
if (dsync_deserializer_decode_try(decoder, "no_notify", &value))
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;
set->hdr_hash_v2 = ibc->minor_version >= DSYNC_PROTOCOL_MINOR_HAVE_HDR_HASH_V2;
*set_r = set;
diff --git a/src/doveadm/dsync/dsync-mail.h b/src/doveadm/dsync/dsync-mail.h
index 78a1a25084..89cc143dd0 100644
--- a/src/doveadm/dsync/dsync-mail.h
+++ b/src/doveadm/dsync/dsync-mail.h
@@ -88,6 +88,12 @@ dsync_mail_get_hash_headers(struct mailbox *box);
int dsync_mail_get_hdr_hash(struct mail *mail, unsigned int version,
const char **hdr_hash_r);
+static inline bool dsync_mail_hdr_hash_is_empty(const char *hdr_hash)
+{
+ /* md5(\n) */
+ return strcmp(hdr_hash, "68b329da9893e34099c7d8ad5cb9c940") == 0;
+}
+
int dsync_mail_fill(struct mail *mail, bool minimal_fill,
struct dsync_mail *dmail_r, const char **error_field_r);
int dsync_mail_fill_nonminimal(struct mail *mail, struct dsync_mail *dmail_r,
diff --git a/src/doveadm/dsync/dsync-mailbox-import.c b/src/doveadm/dsync/dsync-mailbox-import.c
index 877305cd5a..c8192a8541 100644
--- a/src/doveadm/dsync/dsync-mailbox-import.c
+++ b/src/doveadm/dsync/dsync-mailbox-import.c
@@ -122,6 +122,7 @@ struct dsync_mailbox_importer {
bool mails_have_guids:1;
bool mails_use_guid128:1;
bool delete_mailbox:1;
+ bool empty_hdr_workaround:1;
};
static const char *dsync_mail_change_type_names[] = {
@@ -278,6 +279,8 @@ dsync_mailbox_import_init(struct mailbox *box,
(flags & DSYNC_MAILBOX_IMPORT_FLAG_MAILS_USE_GUID128) != 0;
importer->hdr_hash_version =
(flags & DSYNC_MAILBOX_IMPORT_FLAG_HDR_HASH_V2) != 0 ? 2 : 1;
+ importer->empty_hdr_workaround =
+ (flags & DSYNC_MAILBOX_IMPORT_FLAG_EMPTY_HDR_WORKAROUND) != 0;
mailbox_get_open_status(importer->box, STATUS_UIDNEXT |
STATUS_HIGHESTMODSEQ | STATUS_HIGHESTPVTMODSEQ,
@@ -782,7 +785,16 @@ static bool dsync_mailbox_try_save_cur(struct dsync_mailbox_importer *importer,
i_assert(save_change->type != DSYNC_MAIL_CHANGE_TYPE_EXPUNGE);
}
- diff = importer_mail_cmp(&m1, &m2);
+ if (importer->empty_hdr_workaround && !importer->mails_have_guids &&
+ importer->cur_mail != NULL && save_change != NULL &&
+ (dsync_mail_hdr_hash_is_empty(m1.guid) ||
+ dsync_mail_hdr_hash_is_empty(m2.guid))) {
+ /* one of the headers is empty. assume it's broken and that
+ the header matches what we have currently. */
+ diff = 0;
+ } else {
+ diff = importer_mail_cmp(&m1, &m2);
+ }
if (diff < 0) {
/* add a record for local mail */
i_assert(importer->cur_mail != NULL);
diff --git a/src/doveadm/dsync/dsync-mailbox-import.h b/src/doveadm/dsync/dsync-mailbox-import.h
index 8139d650ed..a0492495d4 100644
--- a/src/doveadm/dsync/dsync-mailbox-import.h
+++ b/src/doveadm/dsync/dsync-mailbox-import.h
@@ -11,7 +11,8 @@ enum dsync_mailbox_import_flags {
DSYNC_MAILBOX_IMPORT_FLAG_MAILS_HAVE_GUIDS = 0x10,
DSYNC_MAILBOX_IMPORT_FLAG_MAILS_USE_GUID128 = 0x20,
DSYNC_MAILBOX_IMPORT_FLAG_NO_NOTIFY = 0x40,
- DSYNC_MAILBOX_IMPORT_FLAG_HDR_HASH_V2 = 0x80
+ DSYNC_MAILBOX_IMPORT_FLAG_HDR_HASH_V2 = 0x80,
+ DSYNC_MAILBOX_IMPORT_FLAG_EMPTY_HDR_WORKAROUND = 0x100
};
struct mailbox;