dsync-mailbox-import.c revision 31d32d39dd09be0625a6d92ee715155f5d679515
0N/A/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */ 0N/A /* linked list of mails for this GUID */ 0N/A /* if non-NULL, this mail exists in both local and remote. this link 0N/A points to the other side. */ 0N/A /* the final UID for the message */ 0N/A /* the original local UID, or 0 if exists only remotely */ 0N/A /* the original remote UID, or 0 if exists only remotely */ 0N/A/* for quickly testing that two-way sync doesn't actually do any unexpected 0N/A /* UID => struct dsync_mail_change */ 0N/A /* GUID => struct importer_new_mail */ 0N/A /* UID => struct importer_new_mail */ 0N/A /* this flag causes cur_guid to be looked up later */ i_error(
"Mailbox %s: Failed to get attribute %s: %s",
/* we have no knowledge of this attribute */ *
cmp_r = -
1;
/* quiet gcc */ /* at least one of them is a stream. make both of them streams. */ /* remote has a value and local doesn't -> use it */ /* remote doesn't have a value, bt local does -> skip */ /* attribute doesn't exist on either side -> ignore */ /* we haven't seen this locally -> use whatever remote has */ /* we're doing incremental syncing, and we can see that the attribute was changed remotely, but not locally -> use it */ /* we're doing incremental syncing, and we can see that the attribute was changed locally, but not remotely -> ignore */ /* remote has a newer timestamp -> use it */ /* remote has an older timestamp -> ignore */ /* the timestamps are the same. now we're down to guessing the right answer, unless the values are actually equal, so check that first. next try to use modseqs, but if even they are the same, fallback to just picking one based on the /* remote has a higher modseq -> use it */ /* remote has an older modseq -> ignore */ i_error(
"Mailbox %s: Failed to set attribute %s: %s",
/* the attributes aren't vital, don't fail everything just i_error(
"Mailbox %s: Can't lookup %s for UID=%u: %s",
/* this message exists locally, but remote didn't send expunge-change for it. if the message's uid <= last-common-uid, it should be deleted */ /* make sure next_local_seq gets updated in case we came here /* first mail for this GUID */ /* mail exists only locally. we don't want to request it, and we'll assume it has no duplicate /* first mail for this UID */ /* 1) add the newmail to the end of the linked list /* add a record for local mail */ /* NOTE: assumes save_change is allocated from importer pool */ i_warning(
"Mailbox %s doesn't match previous state: %s " "(dsync must be run again without the state)",
/* backend doesn't support GUIDs. if hdr_hash is set, we could verify it, but since this message really is supposed to match, it's probably too much trouble. */ /* verify that GUID matches, just in case */ "Unexpected GUID mismatch for UID=%u: %s != %s",
"Unexpected GUID mismatch (2) for UID=%u: %s != %s",
/* don't change flags that are currently identical in both sides */ /* see if there are conflicting final flags */ const char *
const *
names;
for (i = 0; i <
count; i++) {
const char *
const *
namep;
for (i = 0; i <
32; i++) {
if ((
bits & (
1U << i)) == 0)
/* local_changes and remote_changes are assumed to have no /* we'll assign a common index for each keyword name and place the changes to separate bit arrays. */ /* this message has no keywords */ /* @UNSAFE: create large enough arrays to fit all keyword indexes. */ for (i = 0; i <
count; i++) {
/* get local changes. use existing indexes for names when they exist. */ for (i = 0; i <
count; i++) {
for (i = 0; i <
count; i++) {
/* dsync backup: just make the local look like remote. */ /* identical modseq, we'll just have to pick one. Note that both brains need to pick the same one, otherwise /* update modseqs. try to anticipate when we have to increase modseq to get it closer to what remote has (although we can't guess it /* we've already verified that the GUID matches. apply flag changes if there are any. */ /* this is a new mail. its UID may or may not conflict with an existing local mail, we'll figure it out later. */ /* the local mail is expunged. we'll decide later if we want to save this mail locally or expunge it form remote. */ /* expunge the message, unless its GUID unexpectedly doesn't /* already expunged locally, we can ignore this. uid=last_common_uid if we managed to verify from transaction log that the GUIDs match */ /* already verified that the GUID matches */ /* we don't know yet if we should expunge this message or not. queue it until we do. */ /* If there are local mails after last_common_uid which we skipped while trying to match the next message, we need to now go back */ unsigned int n, i,
count;
/* expunge the messages whose expunge-decision we delayed previously */ /* we expunge messages only up to last_common_uid, /* handle pending saves */ for (i = 0; i <
count; i++) {
/* we have GUIDs, verify them */ /* verify hdr_hash if it exists */ /* the message was already expunged, so we don't know its header. return "unknown". */ i_error(
"Mailbox %s: GUIDs not supported, " "sync with header hashes instead",
/* remote doesn't support GUIDs, can't verify expunge */ /* local message is expunged. see if we can find its GUID from transaction log and check if the GUIDs match. The GUID in log is a 128bit GUID, so we may need to convert the remote's GUID string to 128bit GUID first. */ /* GUID mismatch for two expunged mails. dsync can't update GUIDs for already expunged messages, so we can't immediately determine that the rest of the messages are a mismatch. so for now we'll just skip over this pair. */ /* try to find the matching local mail */ /* no more local mails. we can still try to match expunged mails though. */ /* mail doesn't exist remotely either, don't bother looking it up locally. */ /* couldn't match it for an expunged mail. use the last message with a matching GUID as the last common /* we can't know if this UID matches */ /* we have a matching local UID. check GUID to see if it's really the same mail or not */ /* mismatch - found the first non-common UID */ /* a) uid <= last_common_uid for flag changes and expunges. this happens only when last_common_uid was originally given as parameter to importer. when we're finding the last_common_uid ourself, uid>last_common_uid always in here, because last_common_uid_found=TRUE only after we find the first b) uid > last_common_uid for i) new messages, ii) expunges that were sent "just in case" */ /* a) uid < last_common_uid can never happen */ /* b) uid = last_common_uid if we've verified that the messages' GUIDs match so far. c) uid > last_common_uid: i) TYPE_EXPUNGE change has GUID=NULL, so we couldn't verify yet if it matches our local message, ii) local message is expunged and we couldn't /* figure out what UID to use for the mail */ /* we can use the linked message's UID and expunge /* skip processing the linked mail */ i_error(
"Mailbox %s: Can't lookup %s for UID=%u: %s",
"Unexpected GUID mismatch (3) for UID=%u: %s != %s",
/* commit the transaction once in a while, so if we fail we don't /* optimize by first trying to use the latest UID */ /* now try to use any of them by iterating through them. (would be easier&faster to just iterate backwards, but probably too much trouble to add such API) */ /* wanted_uids contains UIDs that need to exist at the end. those that don't already exist in local_uids have a higher UID than any /* we have exactly the UID we want. keep it. */ /* we no longer want this local UID. */ /* reuse as many existing messages as possible by changing their UIDs */ /* expunge all unwanted messages */ /* mark mails whose UIDs we got to be skipped over later */ /* we've assigned all wanted UIDs */ /* try to find one existing message that we can use to copy to the /* get the list of the current local UIDs and the wanted UIDs. find the first remote instance that we can request in case there are /* no local instance. request from remote */ /* successfully handled all the mails locally */ /* handle pending expunges and flag updates */ /* skip common local mails */ /* if there are any local mails left, add them to newmails list */ i_error(
"Mailbox %s: Search failed: %s",
/* save mails from local sources where possible, request the rest from remote */ static const char *
const *
for (i = 0; i <
count; i++) {
/* FIXME: if there already are private flags, they get lost because saving can't handle updating private index. they get added on the next sync though. if this is fixed here, set min_pvt_modseq also. */ /* try to save the mail by copying an existing mail */ /* copy using the source mail */ /* fallback to saving from remote stream */ /* it was just expunged in remote, skip it */ i_error(
"Mailbox %s: Saving failed: %s",
i_error(
"Mailbox %s: read(msg input) failed: %m",
i_error(
"Mailbox %s: Saving failed: %s",
i_error(
"Mailbox %s: Saving failed: %s",
/* save all instances of the message */ i_error(
"Mailbox %s: Remote sent unwanted message body for " i_error(
"Mailbox %s: Couldn't move mail within mailbox: %s",
i_error(
"Mailbox %s: mail search failed: %s",
i_error(
"Mailbox %s: UID reassign commit failed: %s",
i_debug(
"Mailbox %s: Renumbered %u of %u unwanted UIDs",
/* wanted_uids contains the UIDs we tried to save mails with. if nothing changed during dsync, we should have the expected UIDs (saved_uids) and all is well. if any new messages got inserted during dsync, we'll need to fix up the UIDs and let the next dsync fix up the other side. for example: remote uids = 5,7,9 = wanted_uids locally added new uid=5 -> we'll now need to reassign UIDs 5 and 10. to be fully future-proof we'll reassign all UIDs between [original local uidnext .. highest UID we think we know] that aren't in saved_uids. */ /* create uidset for the list of UIDs we don't want to exist */ /* conflicting changes during sync, revert our last-common-uid i_error(
"Mailbox %s: Save commit failed: %s",
/* removed wanted_uids that weren't actually saved */ /* remember the UIDs that were successfully saved */ /* commit flag changes and expunges */ i_error(
"Mailbox %s: Commit failed: %s",
/* update mailbox metadata if we successfully saved "min_first_recent_uid=%u min_highest_modseq=%llu " "min_highest_pvt_modseq=%llu",
i_error(
"Mailbox %s: Update failed: %s",
/* sync mailbox to finish flag changes and expunges. */ i_error(
"Mailbox %s: Sync failed: %s",
/* give new UIDs to messages that got saved with unwanted UIDs. do it only if the whole transaction succeeded. */ i_error(
"Mailbox %s: Remote didn't send mail GUID=%s (UID=%u)",
i_error(
"Mailbox %s: Remote didn't send mail UID=%u",
i_error(
"Mailbox %s: Search failed: %s",
/* local changes occurred during dsync. we exported changes up to local_initial_highestmodseq, so all of the changes have happened after it. we want the next run to see those changes, so return it as the last common modseq */