From 249bf7eb77d97029ec3b87ad5269aec869c06c51 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 19 Nov 2020 16:24:15 +0200 Subject: Make sure UIDs are monotonically increased in dotmail, mh, and maildir. * include/mailutils/sys/amd.h (_MU_AMD_PROP_UIDNEXT): New define. (MU_AMD_PROP): Remove. (MU_AMD_F_PROP): New flag. (_amd_data): New member: flags. Remove next_uid (amd_reset_uidvalidity): Change return type. (amd_update_uidnext,amd_alloc_uid): New protos. * include/mailutils/sys/dotmail.h (mu_dotmail_mailbox): New member uidvalidity_changed. * libmailutils/base/amd.c (_amd_prop_create): Set MU_AMD_F_PROP flag if the prop file exists. (amd_initial_scan): New function. (amd_get_uidvalidity, amd_set_uidvalidity) (amd_uidnext): Rewrite using amd_initial_scan. (amd_reset_uidvalidity): Return int. (amd_update_uidnext,amd_alloc_uid): New functions. * libproto/dotmail/dotmail.c (dotmail_close): Flush UID parameters if uidvalidity_changed is set. (dotmail_flush): Take three-state mode as its third argument. (dotmail_flush_unlocked): Likewise. If operating in FLUSH_UIDVALIDITY mode, clear all other modifications to the messages in the mailbox. Clear uidvalidity_changed on success. (dotmail_expunge, dotmail_sync): Update call to dotmail_flush. (dotmail_alloc_next_uid): Set uidvalidity_changed. (dotmail_rescan_unlocked): Check if the assigned UID is greater than the previously assigned one and less than the UIDNEXT value. Raise uidvalidity_changed if UIDs were re-allocated. (mu_dotmail_mailbox_uid_setup): Set uidvalidity_scanned. * libproto/dotmail/tests/Makefile.am: Add new test. * libproto/dotmail/tests/testsuite.at: Likewise. * libproto/dotmail/tests/uidnext.at: New test. * libproto/maildir/mbox.c: Use amd_update_uidnext to synchronize the computed and predicted uidnext value. (maildir_next_uid): Remove. * libproto/maildir/tests/Makefile.am: Add new test. * libproto/maildir/tests/testsuite.at: Likewise. * libproto/maildir/tests/uidnext.at: New test. * libproto/mh/mbox.c (_mh_next_seq): Remove function. (mh_scan0): Use amd_update_uidnext to synchronize the computed and predicted uidnext value. * libproto/mh/tests/Makefile.am: Add new test. * libproto/mh/tests/testsuite.at: Likewise. * libproto/mh/tests/uidnext.at: New test. --- libmailutils/base/amd.c | 96 ++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 37 deletions(-) (limited to 'libmailutils') diff --git a/libmailutils/base/amd.c b/libmailutils/base/amd.c index 797c4ff8f..9913dc7e1 100644 --- a/libmailutils/base/amd.c +++ b/libmailutils/base/amd.c @@ -228,7 +228,7 @@ _amd_prop_create (struct _amd_data *amd) if (access (mhprop->filename, F_OK) == 0) { - amd->capabilities |= MU_AMD_PROP; + amd->flags |= MU_AMD_F_PROP; } rc = mu_property_create_init (&amd->prop, mu_mh_property_init, mhprop); @@ -1471,38 +1471,36 @@ amd_sync (mu_mailbox_t mailbox) return 0; } -static int -amd_get_uidvalidity (mu_mailbox_t mailbox, unsigned long *puidvalidity) +static inline int +amd_initial_scan (struct _amd_data *amd) { - struct _amd_data *amd = mailbox->data; - int status = amd_messages_count (mailbox, NULL); - if (status != 0) - return status; - /* If we did not start scanning yet do it now. */ - if (amd->msg_count == 0) + if (!(amd->flags & MU_AMD_F_INIT_SCAN)) { - status = _amd_scan0 (amd, 1, NULL, 0); + int status = _amd_scan0 (amd, 1, NULL, 0); if (status != 0) - return status; + return status; + amd->flags |= MU_AMD_F_INIT_SCAN; } + return 0; +} - return _amd_prop_fetch_ulong (amd, _MU_AMD_PROP_UIDVALIDITY, puidvalidity); +static int +amd_get_uidvalidity (mu_mailbox_t mailbox, unsigned long *pval) +{ + struct _amd_data *amd = mailbox->data; + int status = amd_initial_scan (amd); + if (status != 0) + return status; + return _amd_prop_fetch_ulong (amd, _MU_AMD_PROP_UIDVALIDITY, pval); } static int amd_set_uidvalidity (mu_mailbox_t mailbox, unsigned long uidvalidity) { struct _amd_data *amd = mailbox->data; - int status = amd_messages_count (mailbox, NULL); + int status = amd_initial_scan (amd); if (status != 0) return status; - /* If we did not start scanning yet do it now. */ - if (amd->msg_count == 0) - { - status = _amd_scan0 (amd, 1, NULL, 0); - if (status != 0) - return status; - } return _amd_prop_store_off (amd, _MU_AMD_PROP_UIDVALIDITY, uidvalidity); } @@ -1510,23 +1508,10 @@ static int amd_uidnext (mu_mailbox_t mailbox, size_t *puidnext) { struct _amd_data *amd = mailbox->data; - int status; - - if (!amd->next_uid) - return ENOSYS; - status = mu_mailbox_messages_count (mailbox, NULL); + int status = amd_initial_scan (amd); if (status != 0) return status; - /* If we did not start a scanning yet do it now. */ - if (amd->msg_count == 0) - { - status = _amd_scan0 (amd, 1, NULL, 0); - if (status != 0) - return status; - } - if (puidnext) - *puidnext = amd->next_uid (amd); - return 0; + return _amd_prop_fetch_size (amd, _MU_AMD_PROP_UIDNEXT, puidnext); } /* FIXME: effectively the same as mbox_cleanup */ @@ -2171,12 +2156,49 @@ amd_remove_dir (const char *name) return rc; } -void +int amd_reset_uidvalidity (struct _amd_data *amd) { struct timeval tv; gettimeofday (&tv, NULL); - _amd_prop_store_off (amd, _MU_AMD_PROP_UIDVALIDITY, tv.tv_sec); + return _amd_prop_store_off (amd, _MU_AMD_PROP_UIDVALIDITY, tv.tv_sec); +} + +int +amd_update_uidnext (struct _amd_data *amd, size_t *newval) +{ + int rc; + size_t curval; + + rc = _amd_prop_fetch_size (amd, _MU_AMD_PROP_UIDNEXT, &curval); + if (rc == MU_ERR_NOENT) + curval = 1; + else if (rc) + return rc; + if (*newval < curval) + { + *newval = curval; + return 0; + } + return _amd_prop_store_off (amd, _MU_AMD_PROP_UIDNEXT, *newval); +} + +int +amd_alloc_uid (struct _amd_data *amd, size_t *newval) +{ + int rc; + size_t retval; + + rc = _amd_prop_fetch_size (amd, _MU_AMD_PROP_UIDNEXT, &retval); + if (rc == MU_ERR_NOENT) + retval = 1; + else if (rc) + return rc; + rc = _amd_prop_store_off (amd, _MU_AMD_PROP_UIDNEXT, retval + 1); + if (rc) + return rc; + *newval = retval; + return 0; } -- cgit v1.2.1