Migración de mi servidor de correo a IMAP4 y parches en "getmail"

Mi épica migración de tecnología de correo electrónico sigue su curso. La última etapa consiste en lo siguiente:

  1. Instalo Dovecot en mi servidor principal y migro las cuentas POP3 tradicionales a flamantes IMAP4.
  2. Modifico la configuración de correo de mi teléfono para que utilice IMAP4 en vez de POP3. Es importante indicarle que borre los mensajes en la carpeta Papelera al cabo de un mes.
  3. Modifico la configuración de mi proceso getmail para que ahora realice la descarga mediante IMAP4. Un detalle importante es que en vez de borrar el correo descargado directamente, lo mueva a la carpeta Papelera.

El efecto conjunto de los dos últimos puntos es que tengo acceso en el móvil al último mes de correo que he descargado en el ordenador. Esto es importante por si ocurre el típico "ayer te envié un email" y ya nos hemos bajado el correo pero no lo hemos mirado todavía. También es interesante comprobar que quien borra el correo de forma definitiva en el servidor es el móvil, al cabo de un mes de su descarga en el portátil.

Tal y como explicaba en Migrar Thunderbird de "mbox" a "IMAP" (VI): Experiencia tras dos semanas, la descarga de correo con getmail es lenta. Es algo que tengo planificado solucionar... algún día.

La configuración actual de mi getmail es:

 [options]
 message_log = ~/.getmail/getmail.log
 message_log_verbose = true
 verbose = 2
 delivered_to = False
 received = False

 delete = True
 #delete_after = 7
 #read_all = False

 # Lo que sigue pretendía acelerar
 # la descarga ahorrándonos el "dele"
 # tras cada mensaje.
 # Lo que sigue hace que si la conexión
 # se corta, se baje el correo otra vez,
 # porque la actualización de mensajes vistos
 # se hace al final de la conexión. Si el
 # programa muere, pues no se actualiza esa info
 # y todos los mensajes se ven como nuevos y se
 # descargan otra vez.

 #read_all = False
 ## 5 minutos
 #delete_after = .00347


 [retriever]
 type = SimpleIMAPSSLRetriever
 server = imap4.jcea.es
 username = jcea@jcea.es
 password = XXXXXXXXXXXXXXXX
 timeout = 60
 move_on_delete = "Deleted Messages"
 #type = SimplePOP3SSLRetriever
 #delete_dup_msgids = True
 ssl_fingerprints = ('6f66e9fe2e1857829386824c250ba751fbef7590ddfe3670bcf89b0a9f97b85b',)

 [destination]
 type = MDA_external
 path = /usr/local/libexec/dovecot/deliver
 arguments = ('-e', '-d', 'jcea', '-m', 'INBOX')

Adicionalmente, modifico la invocación de getmail para que utilice IDLE, una extensión que permite que reciba el correo nuevo de forma casi instantánea (unos cinco segundos), además de mantener la conexión IMAP4 abierta de forma permanente en vez de lanzar una conexión y procesos nuevos cada minuto. Mi entrada de CRON es la siguiente:

* * * * * /home/jcea/z-getmail-4.47.0/getmail --idle=INBOX >/dev/null 2>/dev/null

Es muy importante tener una única sesión getmail activa en un momento dado. Cuando empleaba POP3, este control lo lleva el propio servidor POP3, ya que solo admite una conexión simultánea. La forma recomendada por el autor para asegurar el tener un único proceso getmail es lanzarlo bajo setlock. La opción no es mala, pero no quiero vincularme a Linux. Dado que el código de getmail es Python, prefiero modificar el código fuente directamente. El resultado es el siguiente:

 --- /tmp/getmail-4.47.0/getmail 2015-02-26 02:10:44.000000000 +0100
 +++ getmail 2016-04-01 13:04:36.545444610 +0200
 @@ -1,3 +1,7 @@
 +#!/usr/local/bin/python
 +
 +# No uso lo siguiente porque entonces usa
 +# la version de Python del sistema, con OpenSSL antiguo y vulnerable.
  #!/usr/bin/env python

  import sys
 @@ -122,6 +126,20 @@
          log.info('more than one config file given with --idle, ignoring\n')
          idle = False

 +
 +    # JCEA - 08/mar/16
 +    import fcntl
 +    lock_jcea = open('/home/jcea/.getmail/getmail.lock', 'a')
 +    try :
 +        fcntl.flock(lock_jcea, fcntl.LOCK_EX | fcntl.LOCK_NB)
 +    except IOError :
 +        log.info('Proceso ya en curso. Muriendo')
 +        raise
 +
 +
 +
 +
 +
      for (configfile, retriever, _filters, destination, options) in configs:
          if options['read_all'] and not options['delete']:
              if idle:
 @@ -194,6 +212,21 @@
                          reason = 'would surpass max_bytes_per_session'
                      try:
                          if retrieve:
 +
 +
 +               # JCEA - 17/mar/16
 +               import os
 +               vfs = os.statvfs('/home/jcea/.thunderbird/vtcvfnl8.default/Mail')
 +               free = vfs.f_bsize * vfs.f_bavail
 +               if free < 100*1024*1024 :
 +                   msg = 'Solo quedan %dKbytes libres en la particion de correo. Abortando' %(free/1024)
 +                   log.critical(msg)
 +                   if configs[0][-1]['logfile']:
 +                       configs[0][-1]['logfile'].write(msg)
 +                   raise RuntimeError(msg)
 +
 +
 +
                              try:
                                  msg = retriever.getmsg(msgid)
                              except getmailRetrievalError, o:

Este fichero diff contiene tres cambios:

  1. Las líneas 4-8 invocan directamente mi versión personalizada de Python. Es mi opción por defecto cuando lo lanzo desde la línea de comando, pero al usar CRON las variables de entorno no contienen /usr/local/bin. Podría haber indicado la ruta completa en la invocación CRON, es cierto. Lo he preferido así.

  2. Las líneas 15-23 realizan el control de concurrencia. Solo se permite un proceso getmail de forma simultánea. Como puede verse, es algo bastante simple y portable, al menos si no estamos usando NFS.

  3. Las líneas 38-47 implementan una verificación añadida que hago cada vez que vamos a descargar un mensaje. Básicamente comprueba que tengamos al menos 100 Megabytes en la partición de correo. Esto es importante porque llenar la partición de correo suele ser muy malo para la salud de Dovecot y, claro, de tu correo. Mejor no jugársela; es preferible dejar de bajar correo cuando estamos achuchados a arriesgarnos a no tener espacio siquiera para borrar mensajes para hacer sitio. Ya lo he sufrido alguna vez y prefiero ahorrármelo en el futuro.

    Un efecto de este cambio es poder prescindir del espacio reservado para root. En mi partición de correo de 20 gigabytes, esto hace que gane un gigabyte adicional (la reserva por defecto es del 5%). El comando es el siguiente:

    # tune2fs -m 0 /dev/mapper/email
    tune2fs 1.41.11 (14-Mar-2010)
    Setting reserved blocks percentage to 0% (0 blocks)
    

Obsérvese que mis parches contienen las rutas "cableadas". Es código para uso propio, no me preocupa.

Actualización 20160320: Este código es incorrecto. tengo una versión actualizada en Parches actualizados para "getmail".