dsync-brain-mails.c revision 4292c8c20c9740e42a214d190036b0cc2b618dbf
7126N/A/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
7126N/A
7126N/A#include "lib.h"
7126N/A#include "istream.h"
7126N/A#include "dsync-ibc.h"
7126N/A#include "dsync-mail.h"
7126N/A#include "dsync-mailbox-import.h"
7126N/A#include "dsync-mailbox-export.h"
7126N/A#include "dsync-brain-private.h"
7126N/A
7126N/Aconst char *dsync_box_state_names[DSYNC_BOX_STATE_DONE+1] = {
7126N/A "mailbox",
7126N/A "changes",
7126N/A "attributes",
7126N/A "mail_requests",
7126N/A "mails",
7126N/A "recv_last_common",
7126N/A "done"
7126N/A};
7126N/A
7126N/Astatic bool dsync_brain_master_sync_recv_mailbox(struct dsync_brain *brain)
7126N/A{
7126N/A const struct dsync_mailbox *dsync_box;
7126N/A enum dsync_ibc_recv_ret ret;
7126N/A
7126N/A i_assert(brain->master_brain);
7126N/A
7126N/A if ((ret = dsync_ibc_recv_mailbox(brain->ibc, &dsync_box)) == 0)
7126N/A return FALSE;
7236N/A if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
7236N/A i_error("Remote sent end-of-list instead of a mailbox");
7126N/A brain->failed = TRUE;
7236N/A return TRUE;
7126N/A }
7126N/A if (memcmp(dsync_box->mailbox_guid, brain->local_dsync_box.mailbox_guid,
7126N/A sizeof(dsync_box->mailbox_guid)) != 0) {
7126N/A i_error("Remote sent mailbox with a wrong GUID");
7126N/A brain->failed = TRUE;
7126N/A return TRUE;
7126N/A }
7126N/A
7126N/A if (dsync_box->mailbox_lost) {
7126N/A /* remote lost the mailbox. it's probably already deleted, but
7126N/A verify it on next sync just to be sure */
7126N/A dsync_brain_sync_mailbox_deinit(brain);
7236N/A return TRUE;
7126N/A }
7217N/A dsync_brain_mailbox_update_pre(brain, brain->box,
7126N/A &brain->local_dsync_box, dsync_box);
7126N/A
7126N/A if (brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_CHANGED &&
7126N/A !dsync_boxes_need_sync(&brain->local_dsync_box, dsync_box)) {
7126N/A /* no fields appear to have changed, skip this mailbox */
7126N/A dsync_brain_sync_mailbox_deinit(brain);
7126N/A return TRUE;
7126N/A }
7126N/A if (dsync_brain_sync_mailbox_open(brain, dsync_box) < 0)
7263N/A return TRUE;
7217N/A dsync_brain_sync_init_box_states(brain);
7236N/A return TRUE;
7236N/A}
7126N/A
7126N/Astatic bool dsync_brain_recv_mailbox_attribute(struct dsync_brain *brain)
7126N/A{
7126N/A const struct dsync_mailbox_attribute *attr;
7126N/A enum dsync_ibc_recv_ret ret;
7126N/A
7126N/A if ((ret = dsync_ibc_recv_mailbox_attribute(brain->ibc, &attr)) == 0)
7126N/A return FALSE;
7126N/A if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
7126N/A brain->box_recv_state = DSYNC_BOX_STATE_CHANGES;
7126N/A return TRUE;
7126N/A }
7126N/A if (dsync_mailbox_import_attribute(brain->box_importer, attr) < 0)
7126N/A brain->failed = TRUE;
7126N/A return TRUE;
7126N/A}
7126N/A
7126N/Astatic void dsync_brain_send_mailbox_attribute(struct dsync_brain *brain)
7126N/A{
7126N/A const struct dsync_mailbox_attribute *attr;
7126N/A
7126N/A while ((attr = dsync_mailbox_export_next_attr(brain->box_exporter)) != NULL) {
7126N/A if (dsync_ibc_send_mailbox_attribute(brain->ibc, attr) == 0)
7126N/A return;
7126N/A }
7126N/A dsync_ibc_send_end_of_list(brain->ibc);
7126N/A brain->box_send_state = DSYNC_BOX_STATE_CHANGES;
7126N/A}
7126N/A
7126N/Astatic bool dsync_brain_recv_mail_change(struct dsync_brain *brain)
7126N/A{
7126N/A const struct dsync_mail_change *change;
7126N/A enum dsync_ibc_recv_ret ret;
7126N/A
7126N/A if ((ret = dsync_ibc_recv_change(brain->ibc, &change)) == 0)
7126N/A return FALSE;
7126N/A if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
7126N/A dsync_mailbox_import_changes_finish(brain->box_importer);
7126N/A if (brain->mail_requests && brain->box_exporter != NULL)
7126N/A brain->box_recv_state = DSYNC_BOX_STATE_MAIL_REQUESTS;
7126N/A else
7126N/A brain->box_recv_state = DSYNC_BOX_STATE_MAILS;
7126N/A return TRUE;
7141N/A }
7126N/A if (dsync_mailbox_import_change(brain->box_importer, change) < 0)
7126N/A brain->failed = TRUE;
7126N/A return TRUE;
7126N/A}
7126N/A
7126N/Astatic void dsync_brain_send_mail_change(struct dsync_brain *brain)
7126N/A{
7126N/A const struct dsync_mail_change *change;
7126N/A
7126N/A while ((change = dsync_mailbox_export_next(brain->box_exporter)) != NULL) {
7126N/A if (dsync_ibc_send_change(brain->ibc, change) == 0)
7126N/A return;
7126N/A }
7126N/A dsync_ibc_send_end_of_list(brain->ibc);
7126N/A if (brain->mail_requests && brain->box_importer != NULL)
7126N/A brain->box_send_state = DSYNC_BOX_STATE_MAIL_REQUESTS;
7126N/A else
7126N/A brain->box_send_state = DSYNC_BOX_STATE_MAILS;
7126N/A}
7126N/A
7126N/Astatic bool dsync_brain_recv_mail_request(struct dsync_brain *brain)
7126N/A{
7126N/A const struct dsync_mail_request *request;
7126N/A enum dsync_ibc_recv_ret ret;
7126N/A
7126N/A i_assert(brain->mail_requests);
7236N/A i_assert(brain->box_exporter != NULL);
7126N/A
7126N/A if ((ret = dsync_ibc_recv_mail_request(brain->ibc, &request)) == 0)
7126N/A return FALSE;
7126N/A if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
7126N/A brain->box_recv_state = brain->box_importer != NULL ?
7126N/A DSYNC_BOX_STATE_MAILS :
7126N/A DSYNC_BOX_STATE_RECV_LAST_COMMON;
7126N/A return TRUE;
7126N/A }
7126N/A dsync_mailbox_export_want_mail(brain->box_exporter, request);
7126N/A return TRUE;
7126N/A}
7126N/A
7126N/Astatic bool dsync_brain_send_mail_request(struct dsync_brain *brain)
7126N/A{
7126N/A const struct dsync_mail_request *request;
7126N/A
7126N/A i_assert(brain->mail_requests);
7126N/A
7126N/A while ((request = dsync_mailbox_import_next_request(brain->box_importer)) != NULL) {
7126N/A if (dsync_ibc_send_mail_request(brain->ibc, request) == 0)
7126N/A return TRUE;
7126N/A }
7236N/A if (brain->box_recv_state < DSYNC_BOX_STATE_MAIL_REQUESTS)
7247N/A return FALSE;
7126N/A
7126N/A dsync_ibc_send_end_of_list(brain->ibc);
7126N/A if (brain->box_exporter != NULL)
7126N/A brain->box_send_state = DSYNC_BOX_STATE_MAILS;
7126N/A else {
7126N/A i_assert(brain->box_recv_state != DSYNC_BOX_STATE_DONE);
7126N/A brain->box_send_state = DSYNC_BOX_STATE_DONE;
7126N/A }
7236N/A return TRUE;
7126N/A}
7126N/A
7126N/Astatic void dsync_brain_sync_half_finished(struct dsync_brain *brain)
7236N/A{
7126N/A struct dsync_mailbox_state state;
7126N/A const char *error;
7126N/A
7126N/A if (brain->box_recv_state < DSYNC_BOX_STATE_RECV_LAST_COMMON ||
7126N/A brain->box_send_state < DSYNC_BOX_STATE_RECV_LAST_COMMON)
7126N/A return;
7126N/A
7126N/A /* finished with this mailbox */
7126N/A if (brain->box_exporter != NULL) {
7126N/A if (dsync_mailbox_export_deinit(&brain->box_exporter,
7126N/A &error) < 0) {
7126N/A i_error("Exporting mailbox %s failed: %s",
7126N/A mailbox_get_vname(brain->box), error);
7126N/A brain->failed = TRUE;
7126N/A return;
7236N/A }
7236N/A }
7236N/A
7236N/A memset(&state, 0, sizeof(state));
7236N/A memcpy(state.mailbox_guid, brain->local_dsync_box.mailbox_guid,
7126N/A sizeof(state.mailbox_guid));
7236N/A state.last_uidvalidity = brain->local_dsync_box.uid_validity;
7126N/A if (brain->box_importer == NULL) {
7126N/A /* this mailbox didn't exist on remote */
7126N/A state.last_common_uid = brain->local_dsync_box.uid_next-1;
7236N/A state.last_common_modseq =
7236N/A brain->local_dsync_box.highest_modseq;
7236N/A state.last_common_pvt_modseq =
7236N/A brain->local_dsync_box.highest_pvt_modseq;
7236N/A } else {
7126N/A if (dsync_mailbox_import_deinit(&brain->box_importer, TRUE,
7236N/A &state.last_common_uid,
7236N/A &state.last_common_modseq,
7236N/A &state.last_common_pvt_modseq,
7236N/A &state.changes_during_sync) < 0) {
7236N/A brain->failed = TRUE;
7126N/A return;
7126N/A }
7126N/A if (state.changes_during_sync)
7236N/A brain->changes_during_sync = TRUE;
7126N/A }
7126N/A brain->mailbox_state = state;
7126N/A dsync_ibc_send_mailbox_state(brain->ibc, &state);
7126N/A}
7182N/A
7182N/Astatic bool dsync_brain_recv_mail(struct dsync_brain *brain)
7182N/A{
7182N/A struct dsync_mail *mail;
7182N/A enum dsync_ibc_recv_ret ret;
7182N/A
7182N/A if ((ret = dsync_ibc_recv_mail(brain->ibc, &mail)) == 0)
7182N/A return FALSE;
7182N/A if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
7182N/A brain->box_recv_state = DSYNC_BOX_STATE_RECV_LAST_COMMON;
7182N/A dsync_brain_sync_half_finished(brain);
7182N/A return TRUE;
7182N/A }
7126N/A if (brain->debug) {
7126N/A i_debug("brain %c: import mail uid %u guid %s",
7126N/A brain->master_brain ? 'M' : 'S', mail->uid, mail->guid);
7126N/A }
7126N/A dsync_mailbox_import_mail(brain->box_importer, mail);
7126N/A if (mail->input != NULL)
7126N/A i_stream_unref(&mail->input);
7126N/A return TRUE;
7126N/A}
7126N/A
7236N/Astatic bool dsync_brain_send_mail(struct dsync_brain *brain)
7126N/A{
7263N/A const struct dsync_mail *mail;
7263N/A bool changed = FALSE;
7126N/A
7126N/A while ((mail = dsync_mailbox_export_next_mail(brain->box_exporter)) != NULL) {
7182N/A changed = TRUE;
7182N/A if (dsync_ibc_send_mail(brain->ibc, mail) == 0)
7182N/A return TRUE;
7182N/A }
7182N/A if (brain->mail_requests &&
7182N/A brain->box_recv_state < DSYNC_BOX_STATE_MAILS) {
7182N/A /* wait for mail requests to finish */
7182N/A return changed;
7126N/A }
7126N/A
7126N/A brain->box_send_state = DSYNC_BOX_STATE_DONE;
7182N/A dsync_ibc_send_end_of_list(brain->ibc);
7182N/A
7126N/A dsync_brain_sync_half_finished(brain);
7126N/A return TRUE;
7126N/A}
7126N/A
7126N/Astatic bool dsync_brain_recv_last_common(struct dsync_brain *brain)
7126N/A{
7236N/A enum dsync_ibc_recv_ret ret;
7236N/A struct dsync_mailbox_state state;
7126N/A
7126N/A if ((ret = dsync_ibc_recv_mailbox_state(brain->ibc, &state)) == 0)
7126N/A return FALSE;
7126N/A if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
7141N/A i_error("Remote sent end-of-list instead of a mailbox state");
7141N/A brain->failed = TRUE;
7141N/A return TRUE;
7141N/A }
7126N/A i_assert(brain->box_send_state == DSYNC_BOX_STATE_DONE);
7126N/A i_assert(memcmp(state.mailbox_guid, brain->local_dsync_box.mailbox_guid,
7126N/A sizeof(state.mailbox_guid)) == 0);
7126N/A
7236N/A /* normally the last_common_* values should be the same in local and
7126N/A remote, but during unexpected changes they may differ. use the
7126N/A values that are lower as the final state. */
7126N/A if (brain->mailbox_state.last_common_uid > state.last_common_uid)
7126N/A brain->mailbox_state.last_common_uid = state.last_common_uid;
7236N/A if (brain->mailbox_state.last_common_modseq > state.last_common_modseq)
7126N/A brain->mailbox_state.last_common_modseq = state.last_common_modseq;
7126N/A if (brain->mailbox_state.last_common_pvt_modseq > state.last_common_pvt_modseq)
7126N/A brain->mailbox_state.last_common_pvt_modseq = state.last_common_pvt_modseq;
7126N/A if (state.changes_during_sync)
7126N/A brain->changes_during_sync = TRUE;
7126N/A
7126N/A dsync_brain_sync_mailbox_deinit(brain);
7126N/A return TRUE;
7126N/A}
7126N/A
7126N/Abool dsync_brain_sync_mails(struct dsync_brain *brain)
7126N/A{
7126N/A bool changed = FALSE;
7126N/A
7126N/A i_assert(brain->box != NULL);
7126N/A
7126N/A if (brain->debug) {
7126N/A i_debug("brain %c: in box '%s' recv_state=%s send_state=%s",
7126N/A brain->master_brain ? 'M' : 'S',
7126N/A mailbox_get_vname(brain->box),
7126N/A dsync_box_state_names[brain->box_recv_state],
7126N/A dsync_box_state_names[brain->box_send_state]);
7126N/A }
7126N/A switch (brain->box_recv_state) {
7126N/A case DSYNC_BOX_STATE_MAILBOX:
7126N/A changed = dsync_brain_master_sync_recv_mailbox(brain);
7126N/A break;
7126N/A case DSYNC_BOX_STATE_ATTRIBUTES:
7126N/A changed = dsync_brain_recv_mailbox_attribute(brain);
7126N/A break;
7126N/A case DSYNC_BOX_STATE_CHANGES:
7236N/A changed = dsync_brain_recv_mail_change(brain);
7126N/A break;
7126N/A case DSYNC_BOX_STATE_MAIL_REQUESTS:
7126N/A changed = dsync_brain_recv_mail_request(brain);
7126N/A break;
7126N/A case DSYNC_BOX_STATE_MAILS:
7126N/A changed = dsync_brain_recv_mail(brain);
7126N/A break;
7126N/A case DSYNC_BOX_STATE_RECV_LAST_COMMON:
7126N/A changed = dsync_brain_recv_last_common(brain);
7126N/A break;
7126N/A case DSYNC_BOX_STATE_DONE:
7126N/A break;
7126N/A }
7126N/A
7126N/A if (!dsync_ibc_is_send_queue_full(brain->ibc) && !brain->failed) {
7126N/A switch (brain->box_send_state) {
7126N/A case DSYNC_BOX_STATE_MAILBOX:
7126N/A /* wait for mailbox to be received first */
7126N/A break;
7126N/A case DSYNC_BOX_STATE_ATTRIBUTES:
7126N/A dsync_brain_send_mailbox_attribute(brain);
7126N/A changed = TRUE;
7126N/A break;
7126N/A case DSYNC_BOX_STATE_CHANGES:
7126N/A dsync_brain_send_mail_change(brain);
7126N/A changed = TRUE;
7247N/A break;
7126N/A case DSYNC_BOX_STATE_MAIL_REQUESTS:
7126N/A if (dsync_brain_send_mail_request(brain))
7126N/A changed = TRUE;
7247N/A break;
7126N/A case DSYNC_BOX_STATE_MAILS:
7126N/A if (dsync_brain_send_mail(brain))
7126N/A changed = TRUE;
7236N/A break;
7126N/A case DSYNC_BOX_STATE_RECV_LAST_COMMON:
7126N/A i_unreached();
7126N/A case DSYNC_BOX_STATE_DONE:
7126N/A break;
7236N/A }
7236N/A }
7236N/A if (brain->debug) {
7236N/A i_debug("brain %c: out box '%s' recv_state=%s send_state=%s changed=%d",
7263N/A brain->master_brain ? 'M' : 'S',
7236N/A brain->box == NULL ? "" : mailbox_get_vname(brain->box),
7236N/A dsync_box_state_names[brain->box_recv_state],
7236N/A dsync_box_state_names[brain->box_send_state], changed);
7236N/A }
7236N/A return changed;
7236N/A}
7236N/A