11 KiB
imap_migrate
Migrar buzones de correo usando protocolo IMAP==================================================================== Migrador y respaldador de cuentas IMAP
Este programa copia o migra el contenido completo de una cuenta de correo IMAP (carpetas, mensajes, adjuntos, flags y fechas) desde un servidor origen (A) hacia un servidor destino (B).
Puede usarse tanto para migrar una cuenta a un servidor nuevo como para mantener un respaldo periódico de una cuenta en otro servidor.
El programa NUNCA borra, mueve ni modifica mensajes en el servidor origen.
- Requisitos
- Python 3.10 o superior.
- No se requieren dependencias externas (solo librería estándar).
- Creación del entorno virtual
Linux / macOS:
python3 -m venv python-env
source python-env/bin/activate
Windows:
python -m venv python-env
python-env\Scripts\activate
- Instalación de dependencias
pip install --upgrade pip
pip install -r requirements.txt
(requirements.txt no declara dependencias externas: todo el programa usa la librería estándar de Python.)
- Ejecución
Ejemplo básico (las contraseñas se piden de forma interactiva si se omiten):
python3 imap_migrate.py \
--aserver mail.oldserver.com \
--auser usuario@dominio.com \
--bserver mail.newserver.com \
--buser usuario@dominio.com
Ejemplo completo, con contraseñas por parámetro:
python3 imap_migrate.py \
--aserver imap.servidor-a.com --aport 993 \
--auser usuario@dominio.com --apassword 'passwordA' \
--bserver imap.servidor-b.com --bport 993 \
--buser usuario@dominio.com --bpassword 'passwordB'
Ejemplo usando una IP directa pero validando el certificado TLS contra el nombre de host real (útil cuando el DNS no resuelve o se quiere evitar la resolución DNS):
python3 imap_migrate.py \
--aserver mail.oldserver.com --aip 203.0.113.10 \
--auser usuario@dominio.com \
--bserver mail.newserver.com \
--buser usuario@dominio.com
Ejemplo con servidor destino de certificado autofirmado (ver sección 6 para la advertencia de seguridad):
python3 imap_migrate.py \
--aserver mail.oldserver.com --auser usuario@dominio.com \
--bserver mail.interno.local --buser usuario@dominio.com \
--b-no-verify-tls
- Parámetros disponibles
Servidor origen (A): --aserver Host del servidor IMAP origen (obligatorio) --aport Puerto (por defecto 993) --auser Usuario/cuenta en el servidor origen (obligatorio) --apassword Password (si se omite, se solicita interactivamente o se toma de la variable de entorno IMAP_A_PASSWORD) --adomain Dominio lógico de la cuenta (informativo / uso futuro para migración de múltiples cuentas) --aip IP a la que conectar, usando --aserver como nombre de host para la validación TLS --a-no-verify-tls Deshabilita la verificación de certificado TLS SOLO para el servidor origen (inseguro) --a-no-ssl Conecta sin SSL/TLS al servidor origen (inseguro)
Servidor destino (B): --bserver, --bport, --buser, --bpassword, --bdomain, --bip, --b-no-verify-tls, --b-no-ssl Mismo significado que los anteriores, aplicados al servidor destino. La contraseña también puede tomarse de la variable de entorno IMAP_B_PASSWORD.
Generales: --no-verify-tls Deshabilita la verificación de certificado TLS en AMBOS servidores (inseguro). Muestra una advertencia de seguridad en consola y en el log. --timeout Timeout de conexión en segundos (por defecto 30) --retries Número de reintentos ante errores de red/IMAP (por defecto 3) --state-file Archivo de estado de la migración (por defecto migration-state.json) --log-dir Directorio de logs (por defecto logs/) --header-batch-size Cantidad de mensajes cuyos encabezados se analizan por cada lote de red (por defecto 500) --verbose Muestra en consola el mismo detalle que se registra en el archivo de log -h, --help Muestra la ayuda completa
- Certificados autofirmados / servidores internos
Por defecto el programa valida los certificados TLS de ambos servidores. Si alguno usa un certificado autofirmado (típico en servidores internos), se puede deshabilitar la validación con:
--no-verify-tls (para ambos servidores)
--a-no-verify-tls (solo para el servidor origen)
--b-no-verify-tls (solo para el servidor destino)
Al usar cualquiera de estas opciones se imprime y registra una advertencia de seguridad explícita. Úsese solo con servidores de confianza.
- Cómo reanudar / re-ejecutar una migración incremental
El programa mantiene un archivo de estado (por defecto "migration-state.json" en el directorio de trabajo) con, por cada carpeta:
- el UID de origen hasta el cual ya se analizó de forma segura;
- la huella (fingerprint, basada en Message-ID o en tamaño+fecha)
de cada mensaje ya copiado;
- el UIDVALIDITY visto la última vez.
Para reanudar una migración interrumpida (por CTRL+C, pérdida de conexión, reinicio del servidor, etc.) o para hacer una nueva pasada incremental (por ejemplo, para respaldar los mensajes nuevos desde la última ejecución), simplemente se vuelve a ejecutar el mismo comando apuntando al mismo --state-file:
python3 imap_migrate.py --aserver ... --auser ... --bserver ... --buser ...
El programa detectará automáticamente qué mensajes ya fueron copiados y solo transferirá los mensajes nuevos o los que quedaron pendientes por un error anterior. Si un mensaje falló en una ejecución anterior, se reintenta en la siguiente ejecución sin volver a analizar toda la carpeta desde cero.
Si el UIDVALIDITY de una carpeta cambia en el servidor origen (esto puede pasar tras una reconstrucción del buzón), el programa vuelve a escanear esa carpeta completa, pero sigue evitando duplicados gracias a que la huella de cada mensaje se basa principalmente en su Message-ID.
- Cómo detener correctamente una migración
Presionar CTRL+C durante la ejecución hace que el programa:
1. termine de forma segura la operación IMAP en curso;
2. guarde el estado de la migración;
3. cierre las conexiones con ambos servidores;
4. muestre las estadísticas parciales acumuladas;
5. finalice.
La siguiente ejecución continuará desde el punto en que se quedó.
- Dónde están los logs
Los logs se escriben en el directorio indicado por --log-dir (por defecto "logs/"), con un archivo por día:
logs/migration-2026-09-09.log
El log incluye inicio de ejecución, conexiones, reconexiones, creación de carpetas, mensajes copiados/omitidos, errores, reintentos y el resumen final. Nunca se registran contraseñas.
- Dónde está el archivo de estado
Por defecto: "migration-state.json" en el directorio desde el que se ejecuta el programa (configurable con --state-file). Nunca contiene contraseñas.
- Códigos de salida
0 = migración terminada correctamente
1 = migración terminada con errores (ver el resumen final y el log)
2 = error de parámetros
3 = error de conexión con alguno de los servidores
- Ejecutar las pruebas
Las pruebas usan únicamente "unittest" (librería estándar) y mocks; no requieren servidores IMAP reales.
python3 -m unittest discover -s tests -t .
- Estructura del proyecto
imap_migrate.py Punto de entrada de línea de comandos
config.py Parseo de argumentos y configuración
imap_client.py Cliente IMAP (conexión, carpetas, fetch, append)
migrator.py Orquestador de la migración (análisis + copia)
state.py Persistencia del estado de migración
fingerprint.py Cálculo de huellas para detectar duplicados
progress.py Progreso en consola, ETA, resumen final
logger.py Configuración de logging
utf7.py Codificación UTF-7 modificada para nombres de carpeta
requirements.txt
Leame.txt
logs/ Logs de ejecución
tests/ Pruebas unitarias
- Migración de múltiples cuentas (extensión futura)
Actualmente el programa migra una cuenta por ejecución. El diseño (Migrator recibe un objeto de configuración por cuenta) permite extenderlo fácilmente para leer un archivo "usuarios.csv" o "usuarios.json" con varias cuentas y ejecutar la migración de cada una en un bucle, sin cambios estructurales.