summaryrefslogtreecommitdiff
path: root/fix-duplicate-mail-uids.sh
diff options
context:
space:
mode:
Diffstat (limited to 'fix-duplicate-mail-uids.sh')
-rwxr-xr-xfix-duplicate-mail-uids.sh86
1 files changed, 86 insertions, 0 deletions
diff --git a/fix-duplicate-mail-uids.sh b/fix-duplicate-mail-uids.sh
new file mode 100755
index 0000000..5543fe9
--- /dev/null
+++ b/fix-duplicate-mail-uids.sh
@@ -0,0 +1,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}"