Creative Commons License
Excepto donde se indique otra cosa, todo el contenido de este lugar está bajo una licencia de Creative Commons.
Taquiones > sysadmin > Maildrop

Maildrop

Versión

Estas notas referencian la versión 2.0.2-11 del paquete Debian (maildrop).

Introducción

Es un programa muy efectivo para despachar correo en carpetas, pero también bastante puñetero en cuanto a funcionamiento.

Lo primero que quiero registrar es que cuando funciona en modo de despacho directo (deliver mode) no funciona la opción de depuración (verbose level), porque es ignorada completamente. Como tampoco se queja de que la incluyas en la línea de llamada ó en alguno de los archivos de reglas de filtrado, no vas a enterarte de nada de lo ocurre a menos que recuerdes esta circunstancia.

El programa permite que exista un archivo global de reglas de filtrado situado en /etc/maildroprc y que lee antes que el de usuario en $HOME/.mailfilter, por lo que hay que ser cuidadoso con lo que allí guardas ya que lo usarán todos los otros usuarios del sistema.

Depurando

Cuando es necesario depurar el funcionamiento de maildrop la mejor opción es invocarlo desde el usuario final, con el que se han registrado los fallos temporales, código 75 y nombre EX_TEMPFAIL.

# su usuario
$ maildrop -V 9

En este ejemplo estoy cambiando al usuario en cuestión desde root debido a que tengo usuarios virtuales y no es fácil hacerlo desde otra cuenta con menos privilegios.

maildroprc

A continuación, y siguiendo el orden de la página de manual, extractos de su contenido.

Estructura léxica

  • La mayor parte de los espacios en blanco se ignoran
  • Los comentarios se marcan con el carácter sostenido # y alcanzan hasta el final de la línea.
  • maildrop lee el archivo de filtrado y lo valida antes de leer el mensaje de correo. Si hay algún error sintáctico retorna un error temporal EX_TEMPFAIL.
  • El carácter de fin de línea es un token léxico, por lo que para continuarla en otra es necesario añadir una barra atrás \ al final de la misma.

Texto literal

  • Para introducir un texto literal hay que entrecomillarlo, bien con comillas simples, bien con comillas dobles.
  • Los literales consecutivos se concatenan automáticamente.
  • Para escapar un carácter se precede con una barra atrás \.

Sustitución de variables

  • Los nombres de variables deben comenzar por una letra en mayúsculas ó minúsculas, ó un carácter subrayado _.
  • La sustitución de variables sólo se da dentro de textos literales con entrecomillado doble, no el simple.
  • Dentro de estos literales las variables a sustituir pueden nombrarse mediante el prefijo dólar $ y, opcionalmente, encerrados entre llaves.

    MAILBOX="$HOME/Maildir"
    MAILBOX="${HOME}/Maildir"
    

Parámetros de llamada

  • maildrop toma los restantes paŕametros que ha recibido en su llamada e inicializa las variables $1, $2, ... , que pueden utilizarse como cualquier otra variable.

Variables predefinidas

maildrop define automáticamente un juego de variables que nunca importa del entorno, y cuyos valores por defecto pueden ser definidos por el administrador.

Estas variables son:

  • DEFAULT: El buzón predeterminado al que enviar el mensaje si el archivo de reglas de filtrado no define otro valor.
  • FROM:
    El emisor del sobre del mensaje, es decir, la dirección desde la que se efectuó la conexión de correo. Puede estar vacío en caso de mensajes especiales como los rebotes, y su valor se toma de los siguientes lugares en este orden:

    1. El parámetro -f de maildrop.
    2. La línea From del mensaje (diferente del campo From:).
    3. El identificador de usuario que llama al programa.
  • HOME: Directorio raíz del usuario que ejecuta el programa.
  • HOSTNAME: Nombre de red de la máquina que ejecuta el programa (obtenida mediante gethostname(3)).
  • LOCKEXT: Extensión que usan los archivos de bloqueo (dot-lock files): .lock.
  • LOCKREFRESH: Tiempo de refresco (en segundos) del bloqueo del buzón.
  • LOCKSLEEP: Tiempo de espera (en segundos) para intentar bloquear un buzón de correo si ya existe un bloqueo posterior.
  • LOCKTIMEOUT: Tiempo límite (en segundos también) para desbloquear un buzón por la fuerza. maildrop asume que pasado este tiempo no debe existir bloqueos sobre un buzón de correo.
  • LOGNAME: Nombre del usuario receptor del mensaje.
  • MAILDROPOLDREGEXP: Indicador que vuelve al antiguo motor de expresiones regulares si por alguna razón no se quiere utilizar PCRE, vigente desde la versión 2.0. Cuidado_, este sistema será abandonado en versiones futuras, y este parámetro sólo debería servir como herramienta de transición entre reglas de filtrado antiguas y nuevas.
  • MAILFILTER: Nombre del archivo de reglas de filtrado original recibidas por el programa maildrop cuando ha sido llamado. Permite pues obtener el valor del parámetro -M.
  • PATH: Ruta de ejecución de comandos. maildrop toma inicialmente el valor predeterminado en el sistema.
  • SENDMAIL: Agente de despacho de correo. Cuando maildrop recibe como buzón de correo una dirección prefijada con un cierre de admiración ! entiende que debe reenviar el mensaje, y para ello usa el valor de esta variable.
  • SHELL: Programa shell con el cual ejecutar cualquier programa externo.
  • VERBOSE: Nivel de mensajes de depuración cuyo valor inicial es cero (0). Puede variar entre 0 y 9, siendo éste último el que más valor informativo proporciona. Aviso: para no confundir al programa de transporte de correo que llama a maildrop este valor se ignora completamente en modo de despacho de correo (-d).
  • UMASK: Máscara de permisos de creación de archivos en base octal. El valor predeterminado es `077', lo que hace que los buzones sólo puedan ser leídos y modificados por el dueño. Otros valores son:
    • 007: legible y modificable por el usuario y el grupo.
    • 037: legible por el usuario y el grupo pero modificables sólo por el usuario. Es de señalar que esta máscara sólo se aplica a buzones nuevos; los que ya existen no se alteran.

Variables especiales en ejecución

Las siguientes variables se utilizan cuando maildrop está procesando las reglas de filtrado:

  • EXITCODE: Código de salida de maildrop que será cero (0) cuando haya despachado el mensaje satisfactoriamente. En el caso de tener que usar un programa externo ó una tubería (usando las órdenes to ó cc) esta variable toma el valor de salida de dicho recurso externo, y si éste no genera un código interpretable es necesario modificar la variable antes de efectuar el despacho.
  • KEYWORDS: Se emplea únicamente cuando el mensaje va a para a un maildir (tengo pendiente ampliar el tema).
  • LINES: Número de líneas en el mensaje actual, que pueden ser una aproximación al número real.
  • MAILDIRQUOTA: Tamaño máximo de cualquier buzón de correo donde el mensaje vaya a ser despachado.
  • RETURNCODE: Código de salida de un programa externo ejecutado mediante xfilter ó acentos graves (\\).
  • SIZE: Tamaño en bytes del mensaje actual.

Texto no entrecomillado

  • Si un texto literal sólo incluye letras, números ó alguno de estos caracteres _-.:/${}@ puede aparecer sin entrecomillado, aunque se recomienda que se entrecomille siempre.
  • La única excepción a lo anterior es si el texto ocupa más de una línea en el archivo, entonces debe entrecomillarse.

Programas externos

  • El texto que aparezca entre acentos graves (backticks) es interpretado como llamadas a programas mediante el programa $SHELL, y el resultado que produzca se puede recoger en una variable ó usarse en una expresión tras sufrir ciertas transformaciones:

    DIR=`ls`
    
  • Dichas transformaciones son:

    • Los caracteres de fin de línea se reemplazan por espacios.
    • Se eliminan los caracteres sueltos al principio y al final del texto.
  • Importante: el programa exterior recibe una copia del mensaje en su entrada estándar.

Patrones de búsqueda

/patron/:opciones
  • Un patrón especifica el texto a buscar en un mensaje.
  • Los patrones de búsqueda de maildrop son similares a los del programa grep con diferencias menores.
  • No pueden comenzar con un espacio en blanco porque sino la primera barra sería considerada un operador de división numérica. En caso necesario tendremos que usar la fórmula /[ ].../ .
  • La síntaxis general de los patrones de búsqueda se describe en la página de manual pcrepattern con las siguientes excepciones:
    • No funcionan las búsquedas con UTF8.
    • No hay soporte para opciones internas.
    • Los patrones con nombre no están implementados, aunque sí lo están los patrones numerados.

Opciones de búsqueda

  • Las opciones de búsqueda aparecen tras el patrón, separadas con dos puntos, y pueden ser alguna ó todas las siguientes, sin importar el orden:
    • h compara el patrón con el contenido de la cabecera del mensaje
    • b compara el patrón con el contenido del cuerpo del mensaje
    • D diferencia en las búsquedas entre mayúsculas y minúsculas; lo contrario es el funcionamiento predeterminado.
  • Si no aparecen ninguna de las dos opciones b y h la búsqueda se efectúa sólo sobre la cabecera del mensaje.
  • El patrón se aplica a cada línea individualmente, aunque en el caso de las cabeceras, si se encuentran campos multilíneas (terminadas con una barra atrás) se unen en una antes de aplicar el patrón.

Resultados de búsqueda

En el momento en que una búsqueda con patrón resulta verdadera el texto se guarda en la variable MATCH que puede utilizarse a renglón seguido.

El siguiente patrón

/^From:.*/

hace que la línea From: postmaster@localhost sea almacenada en la variable MATCH, mientras que si usamos un patrón con subpatrones como en

/^From:\s+(.*)@(.*)/

obtendremos postmaster en la variable MATCH1 y localhost en la variable MATCH2, es decir, tantas variables MATCH como subpatrones encuentre.

Nota: los subpatrones no son procesados por una instrucción foreach.

Expresiones

  • Los resultados de las expresiones se almacenan alfanuméricamente, aunque la comparación sea numérica.
  • Si es necesario se convierte de texto a número antes de realizar una operación matemática, y el resultado se almacena siempre en texto.

Órdenes

cc expresion

  • Esta órden despacha una copia del mensaje según la expresión indicada y continúa el filtro desde ese punto. La expresión es más delicada de lo que parece, sus posibilidades están descritas en la orden to.

dotlock

dotlock archivo_de_bloqueo {
    ...
}
  • Crea un bloqueo usando un archivo, en lugar de una llamada al sistema flock().
  • maildrop crea si puede el archivo de bloqueo, ejecuta el bloque de código y lo borra cuando termina.

echo expresion

  • Usa expresion como un texto y lo envía a la salida estándar. Útil para depuración.

exception

exception {
    ...
}
  • Atrapa errores fatales encontrados durante la ejecución de código evitando que maildrop aborte la ejecución.
  • Continúa con el filtro tras ejecutar el bloque.

exit

  • Termina incondicionalmente la ejecución del programa, utilizando el valor de $EXITCODE como salida.

flock expresion

flock archivo_a_bloquear {
    ...
}
  • Crea un bloqueo utilizando la llamada al sistema flock().

foreach

foreach /pattern/:options
{
    ...
}

foreach (expression) =~ /pattern/:options
{
    ...
}
  • Ejecuta un bloque de código por cada ocurrencia del patrón de búsqueda.
  • En cada iteración la variable MATCH toma el valor de la cadena encontrada por el patrón.

if

if (expresion) 
{
    ...
}
else 
{
    ...
}
  • Ejecución condicional de código según la expresión.
  • La claúsula else es opcional.
  • Si la expresión toma el valor numerico cero (0) ó el valor textual vacío se considera un valor falso.
  • La síntaxis de la claúsula if es muy estricta y pueden darse muchos errores sintácticos. Es importante asegurarse de que la palabra reservada if y las llaves están en líneas separadas; más concretamente, los paréntesis y las llaves de cierre, y la palabra reservada else deben aparecer al final de la línea, aunque se admiten comentarios. Eso sí, no admite líneas en blanco entre los elementos, ni aunque lleven sólo un comentario.

import variable

  • Importa el contenido de la variable de entorno variable.

include archivo

  • Lee el contenido de un archivo fuente de reglas y lo ejecuta inmediatamente.
  • Cualquier error sintáctico en dicho archivo, dado que esto se hace en tiempo de ejecución, provocará un fallo de maildrop con un código EX_TEMPFAIL.
  • Igualmente si el archivo no existe.

logfile archivo

  • Indica a maildrop que use el archivo como destino donde registrar dónde han ido a parar los mensajes.
  • Si el archivo existe se abre para añadir contenido.

log expresion

  • Usa la expresion como texto para enviar al registro de actividades.

to expresion

  • Despacha el mensaje al destino indicado en la expresión y puede tomar alguno de estos valores:
    • Un archivo de correo en formato mailbox en el caso de que apunte a un archivo.
    • Un buzón de correo en formato maildir en el caso de que apunte a un directorio.
    • Un programa externo mediante una tubería (y usando $SHELL) si comienza con una barra vertical (|).
    • Una lista de direcciones, separadas por comas, si comienza con un carácter cierre de admiración (!), a las que enviar el mensaje (usando $SENDMAIL).
  • Termina inmediatamente después de despachar el mensaje.
  • Antes de situar el correo en un archivo ó buzón se usará un sistema de bloqueo para el mismo (bien flock(), bien dot-lock).

while

while (expresion) 
{
    ...
}
  • La expresión se evalúa contínuamente hasta que se obtiene un resultado falso.
  • Pueden crearse bucles infinitos fácilmente (cuidado).

xfilter external_program

  • Toma el mensaje activo y lo entuba hacía el programa externo indicado.
  • La salida resultante de la llamada sustituye al mensaje activo.
  • El programa externo debe terminar con un código de salida verdadero (0).
  • maildrop terminará con un código de error temporal EX_TEMPFAIL en caso de no recibir un código de salida cero ó en caso de que el programa no lea su entrada estándar.

||

expresion1 || expresion2
  • Operador lógico OR.
  • Si expresion1 es verdadera se toma como resultado para toda la operación.
  • Si expresion1 es falsa se toma como resultado la evaluación de expresion2.

&&

expresion1 && expresion2
  • Si expresion1 es falsa se toma como resultado para toda la operación.
  • Si expresion1 es verdadera se toma como resultado la evaluación de expresion2.

Comparaciones numéricas

  • Existen los siguientes operadores:
    • < menor que
    • <= menor ó igual que
    • > mayor que
    • >= mayor ó igual que
    • == igual que
    • != distinto de
  • Las comparaciones se efectúan previa conversión a valor en punto flotante de ambos operandos.
  • El resultado es, sin embargo, el texto "1" si es verdadero y "0" si es falso.

Comparaciones alfanuméricas

  • Existen los siguientes operadores:
    • lt menor que
    • le menor ó igual que
    • gt mayor que
    • ge mayor ó igual que
    • eq igual que
    • ne distinto de
  • Las comparaciones se efectúan tomando como textos los dos operandos y usando ordenación alfabética sin importar el contenido real.
  • El resultado es el texto "1" si es verdadero y "0" si es falso.

|

expresion1 | expresion2
  • Operación OR a nivel de bits.
  • El resultado es una entero de 32 bits.

&

expresion1 & expresion2
  • Operacion AND a nivel de bits.
  • El resultado es una entero de 32 bits.

Operaciones numéricas

  • Estos son los posibles operadores numéricos:
    • + suma
    • - resta
    • * multiplicación
    • / división
  • Igual que con las comparaciones numéricas, se trabaja con operandos convertidos a cifras en punto flotante.

Búsqueda de patrones contra un texto

Búsqueda de patrones contra el mensaje

!

! expresion
  • Efectúa una negación lógica de la expresión dada.
  • Existe una versión a nivel de bit con el operador ~, que efectúa un complemento a nivel de bits con el valor numérico de la expresion.

Escapando caracteres especiales

escape(expresion)
  • Toma una expresión y retorna su contenido escapando cada carácter especial que encuentre con un carácter barra atrás (\).
  • Los caracteres considerados especiales son: |!$()[]+*?.&;`'-~<>^{}"

Acceso a GDBM

  • Existe un juego de funciones que permite acceder a bases de datos de tipo GDBM: gdbmopen, gdbmclose, gdbmfetch y gdbmstore.
  • Esta capacidad puede ser anulada por el administrador.

Extraer direcciones en formato RFC 2822

ADDR=getaddr($expresion)
  • La función getaddr extrae direcciones según la norma descrita en el RFC 2822 de la expresión pasada como parámetro.
  • No es necesario eliminar los literales To: ó Cc: de la expresión:

    ADDRLIST=""
    foreach /^(To|Cc): .*/
    {
        foreach (getaddr $MATCH) =~ /.+/
        {
        ADDRLIST="$ADDRLIST $MATCH"
        }
    }
    

Buscar una dirección

if (hasaddr(expresion))
{
    ...
}
  • La expresión debe tomar la disposición usuario@dominio.
  • La función hasaddr() retornará 1 si la dirección se encuentra en alguno de estas cabeceras del mensaje: To:, Cc:, Resent-To: ó Resent-Cc:.
  • Las cabeceras sufren un preprocesado acorde al RFC822, tras el que son extraídas las direcciones de correo y descartados los comentarios y nombres.
  • Con las direcciones obtenidas es con las que se compara la expresión, ignorando la diferenciación entre mayúsculas y minúsculas.

Longitud de un texto

if (length(texto) > 80)
{
    ...
}
  • La función length() retorna el número de caracteres que ocupa un texto.

Búsqueda con un archivo de patrones

if (lookup(expresion, archivo, opciones))
{
    ...
}
  • Expresión puede ser cualquier cosa que maildrop entienda como tal.
  • Archivo es la ruta a uno que contiene una lista de patrones. Es relativa al directorio de trabajo de maildrop.
  • El archivo de patrones puede contener líneas en blanco y comentarios (#) que no se tendrán en cuenta.
  • Los patrones aparecen uno por cada línea, de donde se eliminan los carácteres en blanco al principio de la misma, pero no los del final.
  • El texto resultante se utiliza como un patrón de búsqueda contra el texto.
  • En cuanto encuentra la primera concidencia la función retorna "1" y "0" en caso de agotar la lista de patrones.
  • Las opciones son las que se utilizan en una búsqueda normal, aunque la única reconocida por el momento es D.

El siguiente ejemplo comprueba si la dirección del encabezado del mensaje está en una lista negra y lo descarta.

if ( /^To:\s*(.*)/ && lookup( $MATCH1, "badto.dat" ))
{
    exit
}

y el contenido del archivo puede ser algo como

friend@public
^[^@]*$

y que instruye al filtro a coincidir con la dirección friend@public o con una dirección que no contenga una arroba @.

Extrayendo subcadenas

foo = substr(expresion, posicion, longitud)
  • La función substr() toma tantos caracteres como se indique en longitud a partir de la posición dada por posicion.

Leyendo la fecha y la hora

  • La función time retorna la fecha y hora actual, en número de segundos desde el 1 de Enero de 1970.

Convirtiendo a minúsculas

tolower( expresion )
  • La función tolower() devuelve el texto con las letras mayúsculas convertidas a minúsculas.

Convirtiendo a mayúsculas

toupper( expresion )
  • La función toupper() devuelve el texto con las letras minúsculas convertidas a mayúsculas.