Commit inicial
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
====================================================================
|
||||
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.
|
||||
Reference in New Issue
Block a user