257 lines
11 KiB
Markdown
257 lines
11 KiB
Markdown
# 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.
|
|
|
|
--------------------------------------------------------------------
|
|
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.
|