Replaces imapdown.py with a multi-file Go implementation using github.com/emersion/go-imap/v2. All features preserved: SSL/STARTTLS, incremental UID-based downloads, attachment extraction to zip, modified UTF-7 folder name decoding, and full-mode safety checks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.2 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This project downloads all emails from an IMAP server into individual EML files, preserving the folder hierarchy.
Built with Go as a single self-contained binary, fast and cross-platform.
Development Environment
- Go 1.21+ required
- Dependencies:
github.com/emersion/go-imap/v2(auto-installed viago mod tidy) - Build with:
make buildorgo build - Cross-compile with:
make build-all
Running the Application
First, build the binary:
make build
# Or cross-compile for all platforms:
make build-all
Basic usage (incremental mode - only downloads new emails):
./imapdown -server imap.example.com -email user@example.com -user user@example.com -password "password" -ssl
Full download (ignores previous state, requires empty target directory):
./imapdown -server imap.example.com -email user@example.com -user user@example.com -password "password" -ssl -full
Testing/debugging with limited emails:
./imapdown -server imap.example.com -email user@example.com -user user@example.com -password "password" -ssl -limit 10
Custom storage directory:
./imapdown -server imap.example.com -email user@example.com -user user@example.com -password "password" -ssl -output /path/to/backup
Architecture
Implementation Structure
The code is organized into multiple files for clarity:
main.go- Entry point, CLI parsing, orchestrationimap.go- IMAP connection and folder operationsemail.go- Email parsing and attachment extractionstate.go- State file management (JSON)filename.go- Filename sanitization and Modified UTF-7 decodingMakefile- Build targets
State Tracking
- The script maintains a
.imapdown_state.jsonfile in each email account's download folder - Tracks the highest UID (unique identifier) downloaded per IMAP folder
- Format:
{"INBOX": 19334, "INBOX.Archive": 1770, "Sent": 892} - Enables efficient incremental downloads (default mode)
Download Flow
- Parse arguments
- Connect to IMAP server (SSL, STARTTLS, or plain)
- List all folders and decode modified UTF-7 folder names
- For each folder:
- Load last downloaded UID from state file (if incremental mode)
- Search for new messages (UID > last_uid)
- Download each message as RFC822
- Save as
.emlfile with naming:{UID}_{date}_{subject}.eml - Extract attachments into
.zipfile (same base name) - Update state with highest UID
- Save state file
Key Implementation Details
Modified UTF-7 Decoding: IMAP folder names use modified UTF-7 encoding. This is not standard base64 - it uses , instead of / and has special & handling. Implemented in DecodeModifiedUTF7() in filename.go.
Filename Sanitization: Two-stage process:
SanitizeFilename(): Removes invalid filesystem characters, max 50 chars for subjectsSanitizeFolderPath(): Converts IMAP folder separators (.or/) to OS path separators
UID-Based Incremental Updates: Uses IMAP UIDs (not sequence numbers) because UIDs are persistent. When lastUID > 0, searches for UIDs > lastUID. On first run (lastUID == 0), searches for all messages using an empty SearchCriteria. Some servers return the highest UID even when searching for higher UIDs, so there's additional filtering.
Full Mode Safety: -full mode checks if the download folder already contains .eml files and refuses to run. This prevents accidental duplicates. Users must delete the folder first.
Attachment Handling:
- Walks message parts looking for
Content-Disposition: attachmentorinline - Handles duplicate attachment filenames by appending
_{counter} - All attachments for one email go into a single
.zipfile
Output Structure
Without -output flag (default: ./{email_address}):
{email_address}/ # sanitized email address in current directory
├── .imapdown_state.json
├── INBOX/
│ ├── 123_20240115_Meeting_notes.eml
│ └── 124_20240116_Report.zip
└── Sent/
└── 456_20240114_RE_Question.eml
With -output /path/to/backup:
/path/to/backup/ # specified output directory used directly
├── .imapdown_state.json
├── INBOX/
│ ├── 123_20240115_Meeting_notes.eml
│ └── 124_20240116_Report.zip
└── Sent/
└── 456_20240114_RE_Question.eml
Building and Installing
Build for current platform:
make build
Cross-compile for all platforms:
make build-all
# Produces: imapdown-linux-amd64, imapdown-linux-arm64,
# imapdown-darwin-amd64, imapdown-darwin-arm64,
# imapdown-windows-amd64.exe
Install to $GOPATH/bin:
make install
Clean build artifacts:
make clean
Testing
No formal test suite exists. Manual testing approach:
- Use
-limit 10to download a small batch for verification - Test SSL vs STARTTLS connections
- Test incremental mode by running twice
- Verify
.emlfiles open correctly in email clients - Check that folders with special characters (non-ASCII) are handled correctly
- Test first run (no state file) to ensure all messages are downloaded