dsync-mailbox-state.c revision b9e830a81455faf3c0dadfc9dbf0c7dc8aca955c
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "lib.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "array.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "base64.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "crc32.h"
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#include "hash.h"
a991cfe2157e58ee43bc580f517ce9ef0dfb7acfStephan Bosch#include "dsync-mailbox-state.h"
de0181258ab66b527ad8dc7e51a8efa76b4658d0Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#define DSYNC_STATE_MAJOR_VERSION 1
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#define DSYNC_STATE_MINOR_VERSION 0
0d5c9a80e91a4073d5fd6820e9ddce2755221f64Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch#define V0_MAILBOX_SIZE (GUID_128_SIZE + 4 + 4 + 8 + 8)
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch#define MAILBOX_SIZE (GUID_128_SIZE + 4 + 4 + 8 + 8 + 4)
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic void put_uint32(buffer_t *output, uint32_t num)
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch uint8_t tmp[sizeof(uint32_t)];
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch cpu32_to_le_unaligned(num, tmp);
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen buffer_append(output, tmp, sizeof(tmp));
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen}
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainenvoid dsync_mailbox_states_export(const HASH_TABLE_TYPE(dsync_mailbox_state) states,
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen string_t *output)
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen{
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen struct hash_iterate_context *iter;
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen struct dsync_mailbox_state *state;
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen uint8_t *guid;
1d048c5050f03c24251e5af8087e640de21b2d62Timo Sirainen buffer_t *buf = buffer_create_dynamic(pool_datastack_create(), 128);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch uint32_t crc = 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_append_c(buf, DSYNC_STATE_MAJOR_VERSION);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_append_c(buf, DSYNC_STATE_MINOR_VERSION);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_append_c(buf, '\0');
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_append_c(buf, '\0');
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch iter = hash_table_iterate_init(states);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch while (hash_table_iterate(iter, states, &guid, &state)) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_append(buf, state->mailbox_guid,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch sizeof(state->mailbox_guid));
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch put_uint32(buf, state->last_uidvalidity);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch put_uint32(buf, state->last_common_uid);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch put_uint32(buf, state->last_common_modseq & 0xffffffffU);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch put_uint32(buf, state->last_common_modseq >> 32);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch put_uint32(buf, state->last_common_pvt_modseq & 0xffffffffU);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch put_uint32(buf, state->last_common_pvt_modseq >> 32);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch put_uint32(buf, state->last_messages_count); /* v1 */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (buf->used % 3 == 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch crc = crc32_data_more(crc, buf->data, buf->used);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch base64_encode(buf->data, buf->used, output);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_set_used_size(buf, 0);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch }
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch hash_table_iterate_deinit(&iter);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch crc = crc32_data_more(crc, buf->data, buf->used);
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch put_uint32(buf, crc);
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch base64_encode(buf->data, buf->used, output);
9145c8b5eda526d05bd4a7ced20f6f6f2ff8df03Stephan Bosch}
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Boschstatic int dsync_mailbox_states_retry_import_v0(const buffer_t *buf)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const unsigned char *data = buf->data;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* v0 had no version header and no last_messages_count */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((buf->used-4) % V0_MAILBOX_SIZE != 0 ||
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch le32_to_cpu_unaligned(data + buf->used-4) != crc32_data(data, buf->used-4))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return -1;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* looks like valid v0 format, silently treat it as empty state */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return 0;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch}
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch
7384b4e78eaab44693c985192276e31322155e32Stephan Boschint dsync_mailbox_states_import(HASH_TABLE_TYPE(dsync_mailbox_state) states,
6d573191bea1a64d6046be070487a5705a2d0204Stephan Bosch pool_t pool, const char *input,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const char **error_r)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch{
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct dsync_mailbox_state *state;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch buffer_t *buf;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch uint8_t *guid_p;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch const unsigned char *data;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch size_t pos;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch unsigned int i, count;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch buf = buffer_create_dynamic(pool_datastack_create(), strlen(input));
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch if (base64_decode(input, strlen(input), &pos, buf) < 0) {
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch *error_r = "Invalid base64 data";
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch return -1;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* v1: 4 byte header, mailboxes[], CRC32 */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch data = buf->data;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch if (buf->used == 4 && le32_to_cpu_unaligned(data) == 0) {
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch /* v0: Empty state */
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch return 0;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch if (buf->used < 8) {
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch *error_r = "Input too small";
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch return -1;
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch }
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch if ((buf->used-8) % MAILBOX_SIZE != 0) {
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch *error_r = "Invalid input size";
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch return dsync_mailbox_states_retry_import_v0(buf);
4219de12b28f1936219e27501b9c4b27a4f8d53cStephan Bosch }
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch if (le32_to_cpu_unaligned(data + buf->used-4) != crc32_data(data, buf->used-4)) {
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch *error_r = "CRC32 mismatch";
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch return dsync_mailbox_states_retry_import_v0(buf);
30f35cf5d1e1374d7fab4231e86144fc106a8e79Stephan Bosch }
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch data += 4;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch count = (buf->used-8) / MAILBOX_SIZE;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch for (i = 0; i < count; i++, data += MAILBOX_SIZE) {
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch state = p_new(pool, struct dsync_mailbox_state, 1);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch memcpy(state->mailbox_guid, data, GUID_128_SIZE);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch state->last_uidvalidity = le32_to_cpu_unaligned(data + GUID_128_SIZE);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch state->last_common_uid = le32_to_cpu_unaligned(data + GUID_128_SIZE + 4);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch state->last_common_modseq = le64_to_cpu_unaligned(data + GUID_128_SIZE + 8);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch state->last_common_pvt_modseq = le64_to_cpu_unaligned(data + GUID_128_SIZE + 16);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch state->last_messages_count = le32_to_cpu_unaligned(data + GUID_128_SIZE + 24);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch guid_p = state->mailbox_guid;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch hash_table_insert(states, guid_p, state);
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch }
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch return 0;
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch}
a62fe4b300e2f591e939993aec4cac1e7ae30ad1Stephan Bosch