qpsmtpd y los complementos …

… y mi primera incursión en la ampliación del programa.

Tal y como tengo ahora mismo configurada la recepción de correo en taquiones.net no tengo control sobre la comprobación de los buzones locales; esa tarea está en manos del despachador real de correo, exim4, que hace lo que puede con ellos ya que no tiene posibilidad de rechazarlos en tiempo de conexión. qpsmtpd ya los ha aceptado y se los ha pasado como si fuesen aceptables.

He estado buscando complementos que hiciesen dicha comprobación y lo más parecido que he encontrado ha sido aliases de Peter J. Holzer. Lo he descartado porque el archivo de alias usa las direcciones completas y no sólo los nombres locales. Salvo eso está muy bien; expande los alias para que puedan ser reutilizados en otros complementos, como un gestor de listas de correo, se me ocurre, y permite tener opciones para dichas direcciones que también puede usar otras partes del programa.

Aunque tampoco es que tenga un montón de alias de correo en mis máquinas prefiero no tener que aprender una síntaxis nueva. Si emplease el formato de alias de sendmail ni lo hubiese dudado, pero como también estoy buscando una excusa para ponerme manos a la obra he empezado con ello. He creado un pequeño repositorio que contendrá el software y me facilitará empaquetarlo y he comenzado a revisar la documentación:

Lo que más o menos alcanzo a entender por ahora

Aunque estoy traduciendo plugin por complemento en realidad no están ampliando el programa: son el programa.

qpsmtpd está escrito de tal forma que si no se declara la carga de ninguno de ellos no sirve para nada. Incluso tiene uno llamado rcpt_to que debe ser insertado a modo de centinela al final de la configuración para que termine aceptando el mensaje de correo.

  • Los complementos se cargan al arrancar el programa qpsmtpd.
  • Deben definirse en el archivo plugins, donde aparece su nombre precedido del directorio si estuviesen en uno.
  • Pueden también aceptar parámetros tras el nombre del complemento.
  • El sistema está diseñado para que los complementos puedan ser cargados más de una vez con parámetros diferentes. En ese caso las siguientes instancias reciben un sufijo del tipo
    :N

    comenzando por cero.

Carga de complementos

El programa qpsmtd espera encontrarse los complentos en dos sitios:

  1. Directorio plugins/ de su instalación (en Debian es /usr/share/qpsmtpd/plugins).
  2. Directorios arbitrarios declarados en el archivo config/plugin_dirs.
  3. Directorios habituales de Perl si se declara como un módulo formal de este lenguaje.

Ejemplo de declaración de complementos:

# Sólo el nombre
check_relay

# Directorio y nombre
virus/clamdscan

# Nombre y parámetros 
spamassassin reject_threshold 7

# Dos instancias con diferentes parámetros
my_rcpt_check example.com
my_rcpt_check:0 example.org

# Módulo Perl formal
My::Plugin

Todos descienden de la clase

Qpsmtpd::Plugin

que ya incluye al menos la función plugin_name que devuelve su nombre.

Inicialización

Tras cargar el complemento en el programa éste lo inicializa llamando a la función init() si existe con los siguientes parámetros:

  • $self

    es una referencia al objeto que lo contiene de clase

    Qpsmtpd::Plugin
    
    

    y que se ha creado durante la carga.

  • $qp

    es una referencia al objeto

    Qpsmtpd

    .

  • @args

    es una lista de parámetros definidos en el archivo config/plugins separados por espacios en blanco.

Configuración

Existe un mecanismo de acceso a archivos de configuración disponible en cada función con la cadena de métodos

@lines = $self->qp->config('myconfig');

Siendo myconfig el nombre del archivo y @lines donde se recoge su contenido. El formato es sencillo: texto plano del que se descartan las líneas vacías y las que comiencen por el carácter #.

Estos archivos se leen durante el arranque de qpsmtpd y pueden modificarse sin reiniciarlo.

Registro de eventos

Para registrar lo que ocurre durante la ejecución los complementos disponen de un mecanismo que permite reunificar el destino de los mismos.

$self->qp->log( $loglevel, $logmessage );

$loglevel contiene los valores habituales para los servidores: LOGDEBUG, LOGINFO, LOGNOTICE, LOGWARN, LOGERROR, LOGCRIT, LOGALERT, LOGEMERG.

Introspección

Los complementos disponen de un mecanismo de introspección que les permite saber en qué punto están entre otras cosas. Todos son métodos que heredan de Qpsmtpd::Plugin y son:

  • plugin_name()

    retorna el nombre del complemento que está funcionando ahora.

  • hook_name()

    retorna el nombre de la fase en la que está (nombre del anclaje).

  • auth_user()

    : retorna el nombre del cliente tras la identificación.

  • auth_mechanism()

    : retorna el mecanismo de identificación empleado.

  • connection():

    retorna el objeto Qpsmtpd::Connection asociado a la conexión actual.

  • transaction()

    : retorna el objeto Qpsmtpd::Transaction asociado a la transacción actual.

Comunicación interna

Para comunicar información entre complementos e incluso entre fases (hooks) dentro de un mismo código existe un mecanismo que permite almacenar y recuperar textos.

Se puede hacer tanto en una conexión (Qpsmtpd::Connection) como en una transacción (Qpsmtpd::Transaction). La conexión y la transacción se diferencian en su tiempo de vida. La primera comprende desde que se abre el puerto hasta que se cierra y la segunda abarca la recepción de un mensaje para despacho. Por si quedan dudas, una conexión puede comprender varias transacciones.

# Consulta y almacenamiento de notas en una transacción
$self->qp->transaction->notes('nota');
$self->qp->transaction->notes('nota','Hola');

# Consulta y almacenamiento de notas en una conexión
$self->qp->connection->notes('total');
$self->qp->connection->notes('total','zero');
Archivos temporales

qpsmtpd pone a disposición de los complementos un juego de funciones para manipular archivos temporales que luego se encarga de limpiar.

  • temp_file

    () retorna el nombre de un archivo temporal sin abrirlo.

  • temp_dir()

    crea y retorna un nombre de directorio único bajo el directorio temporal con permisos 0700.

  • spool_dir()

    retorna el directorio temporal global.

Códigos de retorno

Los complementos se ejecutan en el orden en que aparecen definidos en la configuración del programa y durante su funcionamiento pueden forman una cadena de respuestas que permita seguir con el siguiente de la lista. Esto es, todos los complementos para una fase determinada son invocados hasta que uno retorna un código diferente a

DECLINED

. El código de respuesta es obligatorio pero también se puede añadir un texto para el cliente.

El módulo Qpsmtpd::Constants define los códigos de retorno con el siguiente significado:

  • DECLINED

    : indica que el complemento no va a hacer nada y que la ejecución debe continuar.

  • OK

    significa que se permite realizar la acción (y ya depende de la fase o del contexto).

  • DENY

    es lo contrario: la acción no se permite.

  • DENYSOFT

    indica que también que la acción no se permite pero con un carácter más temporal.

  • DENY_DISCONNECT

    deniega la acción y fuerza la desconexión con el cliente.

  • DENYSOFT_DISCONNECT

    igual que el anterior pero retornando un código temporal.

  • DONE

    se emplea para terminar el proceso de la petición.

Resumiendo …

… hay varios aspectos más pero sin haber escrito una línea de código no tengo ganas de escribir sobre ello. La referencia que los autores han escrito al respecto más el código disponible deberá bastarme para comenzar a crear algo.