Files
2026-09-10 09:24:22 -06:00

255 lines
10 KiB
Plaintext

====================================================================
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.
--------------------------------------------------------------------
1. Requisitos
--------------------------------------------------------------------
- Python 3.10 o superior.
- No se requieren dependencias externas (solo librería estándar).
--------------------------------------------------------------------
2. 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
--------------------------------------------------------------------
3. 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.)
--------------------------------------------------------------------
4. 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
--------------------------------------------------------------------
5. 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
--------------------------------------------------------------------
6. 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.
--------------------------------------------------------------------
7. 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.
--------------------------------------------------------------------
8. 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ó.
--------------------------------------------------------------------
9. 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.
--------------------------------------------------------------------
10. 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.
--------------------------------------------------------------------
11. 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
--------------------------------------------------------------------
12. 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 .
--------------------------------------------------------------------
13. 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
--------------------------------------------------------------------
14. 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.