#!/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}"