blob: 5543fe94dc7d09c5082d5efcb573ce2effc518d7 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
#!/usr/bin/env bash
#
# fix-duplicate-uids.sh - Automatically fix duplicate UIDs in maildir
#
# this is needed because sometimes iOS mail and mbsync download or move the same
# file, and i end up with UID collisions. if i just delete the UID from the
# filename, then mu will generate a new one that doesn't collide.
# Configuration - edit these paths as needed
MAIL_DIR="$HOME/Mail"
# Color output helpers
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Create secure temporary files
TEMP_FILE=$(mktemp)
trap 'rm -f "$TEMP_FILE"' EXIT
# Process each mailbox separately
find "$MAIL_DIR" -type d -name cur | while read -r mailbox_cur; do
# Get the actual mailbox path (parent of cur)
mailbox=$(dirname "$mailbox_cur")
mailbox_name=$(basename "$mailbox")
echo -e "${YELLOW}Checking mailbox: ${mailbox}${NC}"
# Find duplicate UIDs in this specific mailbox
find "$mailbox_cur" -type f -name "*,U=*:*" \
| sed -E 's/.*,U=([0-9]+).*/\1/' \
| sort \
| uniq -c \
| awk '$1 > 1 {print $2}' \
> "$TEMP_FILE"
if [ ! -s "$TEMP_FILE" ]; then
echo -e " ${GREEN}No duplicate UIDs found.${NC}"
continue
fi
echo -e " ${YELLOW}Found $(wc -l < "$TEMP_FILE") duplicate UID(s).${NC}"
# Process each duplicate UID
while read -r uid; do
echo -e " ${YELLOW}Fixing duplicate UID: ${uid}${NC}"
# Find all files with this UID in the current mailbox
files=$(find "$mailbox_cur" -type f -name "*,U=${uid}:*" | sort)
# Find the oldest file with this UID (by creation time)
oldest_file=""
oldest_time=9999999999
while read -r file; do
# Extract the timestamp from the filename (typical maildir format)
timestamp=$(basename "$file" | cut -d. -f1)
if [[ $timestamp =~ ^[0-9]+$ ]]; then
if [ "$timestamp" -lt "$oldest_time" ]; then
oldest_time=$timestamp
oldest_file=$file
fi
else
# If we can't extract timestamp from filename, use the file's mtime
file_time=$(stat -c %Y "$file")
if [ "$file_time" -lt "$oldest_time" ]; then
oldest_time=$file_time
oldest_file=$file
fi
fi
done <<< "$files"
echo " Keeping original: $(basename "$oldest_file")"
# Rename all other files with this UID (removing the UID part)
while read -r file; do
if [ "$file" != "$oldest_file" ]; then
new_file=$(echo "$file" | sed -E 's/(.*),U=[0-9]+:(.*)/\1\2/')
echo " Renaming: $(basename "$file") → $(basename "$new_file")"
mv "$file" "$new_file"
fi
done <<< "$files"
done < "$TEMP_FILE"
done
echo -e "${GREEN}Fix complete! Run 'mbsync -a' to resync with new UIDs.${NC}"
|