maildir: remove filename encoded UID when moving messages

The built-in maildir.Dir.Move method performs an OS level file rename,
which allows for preserving file creation time. Commit c98f704874
("move: enable MoveMessages from msgstore") enabled the use of the Move
method for maildir. One particular maildir synchronizer (isync/mbsync)
encodes the UID within the filename of the email and cannot recover if
the UID is preserved during a move. mbsync encodes filenames like so:

	/path/to/email/{maildir-key},U={uid}:2,S

OfflineIMAP encodes the UID within the filename, but also encodes a hash
of the originating folder so that it can recover from a move without a
rename of the underlying file. OfflineIMAP encodes like so:

	/path/to/email{maildir-key},U={uid},FMD5={folder-hash}:2,S

Remove encoded UIDs of the form `,U={uid}` from filenames to prevent
sync issues.

Fixes: https://todo.sr.ht/~rjarry/aerc/75
Fixes: c98f704874 ("move: enable MoveMessages from msgstore")
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
Tim Culverhouse 2022-08-24 09:49:32 -05:00 committed by Robin Jarry
parent 8f8dee8303
commit 1c9fc7b6b1

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"sort" "sort"
"strings" "strings"
@ -12,6 +13,10 @@ import (
"git.sr.ht/~rjarry/aerc/lib/uidstore" "git.sr.ht/~rjarry/aerc/lib/uidstore"
) )
// uidReg matches filename encoded UIDs in maildirs synched with mbsync or
// OfflineIMAP
var uidReg = regexp.MustCompile(`,U=\d+`)
// A Container is a directory which contains other directories which adhere to // A Container is a directory which contains other directories which adhere to
// the Maildir spec // the Maildir spec
type Container struct { type Container struct {
@ -222,6 +227,12 @@ func (c *Container) moveMessage(dest maildir.Dir, src maildir.Dir, uid uint32) e
if !ok { if !ok {
return fmt.Errorf("could not find key for message id %d", uid) return fmt.Errorf("could not find key for message id %d", uid)
} }
err := src.Move(dest, key) path, err := src.Filename(key)
return err if err != nil {
return fmt.Errorf("could not find path for message id %d", uid)
}
// Remove encoded UID information from the key to prevent sync issues
name := uidReg.ReplaceAllString(filepath.Base(path), "")
destPath := filepath.Join(string(dest), "cur", name)
return os.Rename(path, destPath)
} }