Skip to content

function0xMarki/IMAP-Synchronizer

Repository files navigation

IMAP Synchronizer

Interactive, cross-platform tool to inspect, migrate and verify IMAP mailboxes. Works on Windows, macOS and Linux with plain Python 3.8+; its single third-party dependency (rich, for the colorized UI) is installed automatically on first run.

Python Textualize/rich License

Quick start

Double-click the launcher for your system, it installs Python if needed and starts the tool:

Operating system Script
Windows start-windows.bat (installs Python via winget or the official installer)
macOS start-macos.command (installs Homebrew + Python if missing)
Linux start-linux.sh (installs Python via apt/dnf/pacman/zypper)

Or run it directly if you already have Python 3:

python3 imapsynchronizer.py

On the first run the tool installs its only dependency (rich) by itself; everything after that works the same on Windows, macOS and Linux.

Typical session, migrate one mailbox in 6 steps

No configuration files needed:

  1. Start the tool (double-click the launcher, or run python3 imapsynchronizer.py).
  2. Choose option 2, Copy messages from source to destination.
  3. Type the source account details: server, port (press Enter for 993), username and password. Then the same for the destination.
  4. Pick which folders to copy, exclusions accept list numbers and/or full folder names (press Enter for all), choose the destination layout (sandbox under _IMAP-Synchronizer/, or merge into the same folders) and, optionally, a date range.
  5. Review the migration plan on screen and confirm. Progress is shown folder by folder; at the end every message is re-verified and a summary table is displayed (exportable to TXT).
  6. Find the copied mail in the destination account under _IMAP-Synchronizer/<user>_<host>/…. Existing mail in the destination is never touched.

Interrupted, or not sure it finished? Run it again, nothing is ever copied twice; only what is still missing is transferred. To double-check a migration later, use option 3 (Verify source against destination). To see what an account contains before doing anything, use option 1, it only reads.

What it does

The main menu offers:

  1. List account folders and message counts, connects to an account, shows the full folder tree with per-folder message counts, and can export the listing to reports/<account>_<ddmmyy>_<hhmm>.txt.

  2. Copy messages from source to destination, after testing both connections it analyzes the source, shows a migration plan and asks for confirmation. Two destination layouts are offered:

    • Sandbox (default): messages are copied into _IMAP-Synchronizer/<user>_<host>/<original folder structure>, so nothing outside that subtree is ever touched.

    • Merge: messages are added into the destination's own folders (INBOX→INBOX, Sent→Sent, …), mixing with its existing mail, for recovering an old account inside a live one. Standard folders are paired by their special-use role when both servers declare it (RFC 6154), so Sent merges into Sent Items or [Gmail]/Enviados; other folders keep their relative path, and INBOX.-style namespaces are translated automatically. Existing destination mail is never modified and does not affect verification, only the presence of every source message is checked.

    • Folders are created as needed. Names with spaces or non-ASCII characters (ñ, accents, …) are fully supported, on the wire IMAP requires folder names in modified UTF-7 (RFC 3501), and the tool encodes/decodes them transparently, so you always see the real name.

    • Resumable and duplicate-safe: messages already present in the destination (identified by Message-ID, or a header hash when a message has none) are skipped, so re-running an interrupted migration only copies what is still missing.

    • Optional date filter: copy only messages within a from/to date range, entered as ISO yyyy-mm-dd (dd/mm/yyyy is also accepted). Dates are matched against the server arrival date (IMAP INTERNALDATE); on the wire the protocol-mandated d-Mon-yyyy format with English month names is used, independent of the system locale.

    • If the account is on Gmail, the virtual "All Mail" folder is detected (in any language, via its \All flag) and the tool offers to exclude it, copying it would duplicate the whole mailbox.

    • Flags (read/answered/flagged) and original message dates are preserved; the source mailbox is opened read-only and never modified.

    • After copying, every folder is re-checked message-by-message and a verification table is shown; the summary can be exported to TXT.

    • An audit log per migration is written to logs/ with per-folder summaries (total/copied/already present/failed), failures and the verification result.

    • The final report is also stored as an e-mail inside the destination account, in the _IMAP-Synchronizer folder (in both layouts, merge mode creates that folder just for the record), signed with the program name, version, author and repository: a permanent trace of the migration kept next to the mail itself.

    • Built to survive failures: dropped connections are re-established and the operation retried automatically; a folder or message that cannot be processed is reported and skipped without aborting the rest; if the destination rejects a message's custom keyword flags, it is stored with standard flags instead (logged). Only a dead connection (10 consecutive failures) stops the run, and re-running resumes where it left off.

  3. Verify source against destination, full comparison of two accounts (folder structure and message sets), as a mirror (same paths), against the _IMAP-Synchronizer/… subtree of a previous migration, or as a merge check (folders mapped like merge mode; verifies everything from the source exists in the destination while ignoring the destination's own extra mail). Reports missing/extra messages per folder and can export a TXT report. Note: the comparison always covers everything, so after a migration restricted by a date filter it will legitimately report the messages outside that range as missing.

  4. Batch copy multiple accounts from a CSV file, one migration per CSV row, run sequentially without prompts, with a final summary table and report. The menu option creates a template CSV to fill in. Details and examples in [Unattended use](#unattended-use-scheduled-migrations).

  5. Create an unattended job file, writes a JSON job description (accounts, exclusions, date range) for scheduled runs; passwords can be taken from environment variables instead of being stored. Details and examples in [Unattended use](#unattended-use-scheduled-migrations).

  6. Manage saved connection profiles, server/port/username can be saved to imapsynchronizer_profiles.json next to the script to avoid retyping them. Passwords are never stored; they are asked for on every run (hidden input).

Unattended use (scheduled migrations)

Menu options 4 and 5 cover migrations that run without any prompts: they connect, copy, verify, save their TXT report automatically and return an exit code usable from schedulers (cron, launchd, Windows Task Scheduler):

python3 imapsynchronizer.py --job   migration_job.json     # one migration
python3 imapsynchronizer.py --batch batch_migrations.csv   # many migrations

Exit codes: 0 = everything verified OK · 1 = error · 2 = differences found.

Both are resumable exactly like the interactive copy: re-running only copies what is still missing, so a nightly schedule keeps source and destination in sync. The Gmail-style "all mail" virtual folder is always excluded automatically in these modes.

Job file, one migration (--job, menu option 5)

Menu option 5 asks a few questions and writes the JSON file for you. Alternatively, copy the shipped template [migration_job.example.json](migration_job.example.json) and edit it. Job files may contain comment lines starting with # (the template uses them to document every field):

{
  "source":      { "host": "imap.old-server.com", "port": 993,
                   "user": "user@old-server.com", "password_env": "SRC_IMAP_PASS" },
  "destination": { "host": "imap.new-server.com", "port": 993,
                   "user": "user@new-server.com", "password_env": "DST_IMAP_PASS" },
  "exclude_folders": ["Trash", "Junk"],
  "date_from": "",
  "date_to": "",
  "allow_insecure_ssl": false
}
Field Required Meaning
host, user yes IMAP server and account name
port no defaults to 993 (implicit TLS)
password_env one of the two name of an environment variable holding the password (recommended, the file stays credential-free)
password one of the two password in plain text (protect the file!)
exclude_folders no full folder names to skip, case-insensitive (e.g. ["Trash", "[Gmail]/Spam"]); use [] or omit the field to exclude nothing
date_from, date_to no copy only messages that arrived in this inclusive range, yyyy-mm-dd; empty = no limit
merge no true = copy into the destination's own folders (merge mode) instead of _IMAP-Synchronizer/
allow_insecure_ssl no defaults to true (accept invalid/self-signed TLS certificates); set false to require a valid certificate

Run it, providing the passwords through the environment:

# Linux / macOS
export SRC_IMAP_PASS='old-secret'  DST_IMAP_PASS='new-secret'
python3 imapsynchronizer.py --job migration_job.json
:: Windows *(cmd)*
set SRC_IMAP_PASS=old-secret
set DST_IMAP_PASS=new-secret
python imapsynchronizer.py --job migration_job.json

Schedule it, every night at 03:00:

# Linux / macOS:  crontab -e
0 3 * * * SRC_IMAP_PASS=... DST_IMAP_PASS=... /usr/bin/python3 /path/to/imapsynchronizer.py --job /path/to/migration_job.json
:: Windows Task Scheduler (JSON with passwords)
schtasks /Create /SC DAILY /ST 03:00 /TN "IMAP sync" ^
  /TR "cmd /c python C:\path\imapsynchronizer.py --job C:\path\migration_job.json"
:: Windows Task Scheduler (JSON without passwords)
schtasks /Create /SC DAILY /ST 03:00 /TN "IMAP sync" ^
  /TR "cmd /c set SRC_IMAP_PASS=... && set DST_IMAP_PASS=... && python C:\path\imapsynchronizer.py --job C:\path\migration_job.json"

Batch CSV, many migrations (--batch, menu option 4)

One migration per row, run sequentially, with a final summary table and a batch report. Menu option 4 offers to create a template CSV at the path you give it; the same content ships as [batch_migrations.example.csv](batch_migrations.example.csv):

src_host,src_port,src_user,src_password,dst_host,dst_port,dst_user,dst_password,date_from,date_to,exclude,insecure
imap.old-server.com,993,user1@old.com,"pass,word1",imap.new-server.com,993,user1@new.com,"secret2",,,Trash|Junk|Example_exclude,
imap.old-server.com,993,user2@old.com,secret3,imap.new-server.com,993,user2@new.com,secret4,2020-01-01,2025-12-31,,
Column Required Meaning
src_host, src_user, src_password yes source server, account, password
dst_host, dst_user, dst_password yes destination server, account, password
src_port, dst_port no default 993
date_from, date_to no inclusive arrival-date range, yyyy-mm-dd
exclude no folder names to skip, separated by |
insecure no defaults to yes (accept invalid TLS certificates); no = require a valid certificate for that row
merge no yes = copy into the destination's own folders (merge mode)

Notes:

  • Passwords containing commas must be quoted CSV-style: "pass,word".
  • Semicolon-delimited CSVs (as saved by some Excel locales) are detected automatically.
  • The CSV contains passwords in plain text, restrict who can read it.
  • Run from the menu (option 4, asks for confirmation) or scheduled: python3 imapsynchronizer.py --batch batch_migrations.csv.

Passwords

Passwords may contain any characters (ñ, commas, semicolons, quotes, symbols, spaces). Everything you type at the password prompt is taken literally, nothing is split or interpreted. You may optionally wrap the password in quotes ("like this"); one matching surrounding pair is removed, which protects passwords with leading or trailing spaces. Credentials containing non-ASCII characters are transmitted with AUTHENTICATE PLAIN (UTF-8 over base64), since the classic IMAP LOGIN command is ASCII-only.

Notes

  • Server addresses are cleaned automatically: pasting imap://mail.example.com, mail.example.com:993 or a trailing slash works, the host (and port, when present) is extracted for you.
  • Port 993 uses implicit TLS; other ports use STARTTLS when the server offers it (a warning is logged for unencrypted connections).
  • Insecure SSL is the default: certificate verification is skipped unless you answer No to the per-connection question in the menus (or set allow_insecure_ssl/insecure to false in job/batch files). The connection is still encrypted, but the server's identity is not verified, answer No when working over untrusted networks.
  • All exported TXT reports are written to a reports/ folder next to the script (created automatically), so they never mix with the program files.
  • Every start also writes a session error log to logs/ (one file per run, plus one audit log per migration): connection failures, failed messages or folders, unexpected errors, and transient errors that were retried and resolved (e.g. a timeout followed by a successful reconnect), everything an admin needs to debug afterwards.

Development

python3 imapsynchronizer.py --selftest runs the built-in unit checks for the UTF-7 codec, LIST/FETCH response parsing, duplicate detection, password quote handling, date criteria, batch CSV parsing and tree rendering.

About

Interactive, cross-platform tool to inspect, migrate and verify IMAP mailboxes.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors