Archivo de la etiqueta: qmail

Nuevo proyecto en 5 horas: UsayTira.me, direcciones de correo de usar y tirar explicado paso a paso

Parece que últimamente me aburro mucho :P. Hace unos días, leyendo un artículo, se me ocurrió de nuevo explicar cómo se hacen esos sistemas de correo instantáneos que se suelen utilizar para registrarse en webs y que después no te envíen spam :P. La idea me pareció muy adecuada para poner un ejemplo práctico de algo que vimos hace tiempo sobre otras utilidades para un servidor de correo y, tal y como me ocurrió hace unos meses, lo que era un artículo se convierte en proyecto.

La idea, por tanto, es crear un sistema que, sin necesidad de ningún registro, te permita crear una cuenta de correo y recibir y leer emails en ella por espacio de una hora, al cabo de este tiempo la cuenta se autodestruye y todos los emails serán devueltos. En nuestra aplicación tendremos dos opciones para crear la cuenta, aleatoria o personalizada, creo que no hacen falta más explicaciones. Una vez usas una cuenta puedes volver a ella más tarde cuando la necesites volviendo a crear una cuenta personalizada con el mismo usuario. Esto es útil, por ejemplo, para que te recuerden la contraseña que utilizaste para registrar en aquella web de descarga de películas y de la que ya no te acuerdas ;).

Qué necesitamos

  • Servidor Linux con Qmail como MTA.
  • Apache, PHP y MySQL.
  • Pear MimeDecode: para procesar los correos entrantes con PHP.
  • Una plantilla superchula de FreeCssTemplates
  • Jquery: para todo lo que es ajax y Javascript
  • ZeroClipboard:  para copiar y pegar automáticamente
  • Jquery ScrollTo: pluggin para desplazar el scroll automáticamente.
  • Una imagen de “Cargando” para las acciones ajax que personalizas aquí.
  • Diccionarios de palabras “aleatorias”. Aquí hay unos cuantos.
  • Adodb (opcional) para el acceso a base de datos.

Eso es todo, sólo hay que juntar las piezas adecuadamente.

Preparando Qmail

Nuestro primer paso, antes de ponernos con los temas puramente web, será configurar adecuadamente el servidor de correo para nuestro propósito. Para ello necesitamos crear un usuario del sistema que reciba todo el correo dirigido a un dominio. Esto lo podemos hacer del siguiente modo:

adduser -g users -s /dev/null usaytirame
passwd usaytirame UNACLAVESUPERCOMPLICADA

Añadimos nuestro nombre de host en:

/var/qmail/control/rcpthosts

usaytira.me

Y el usuario al que dirigiremos los correos en:

/var/qmail/control/virtualdomains

usaytira.me:usaytirame

Reiniciando Qmail conseguiremos que los correos enviados a cualquier cuenta del dominio vayan al buzón del usuario indicado, es decir cualquiercosa[arroba]usaytira.me.

Pero no queremos que los correos vayan al buzón del usuario sino simplemente procesarlos. Para eso editamos el archivo:

/home/usaytirame/.qmail-default

|preline /usr/bin/php /home/usaytirame/procesa.php

Sólo con esa línea. Con este comando conseguimos redirigir los correos entrantes a un script en el que podremos recuperarlos y reutilizarlos a nuestro antojo como veremos a continuación.

Procesando los emails entrantes

Vamos a comenzar por crear una base de datos donde guardaremos los emails recibidos. Como queremos poder acceder a los emails dirigidos a cada cuenta independientemente, haremos dos tablas, una (emails) la utilizaremos como tabla maestra para las cuentas de correo que se creen y en la otra (correos) iremos almacenando los emails recibidos para cada una de esas cuentas.  La estructura sería poco más o menos la siguiente:

CREATE TABLE IF NOT EXISTS `emails` (
  `idEmail` int(11) NOT NULL auto_increment,
  `fecha` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `email` varchar(255) NOT NULL,
  `ip` varchar(15) NOT NULL,
  PRIMARY KEY  (`idEmail`),
  KEY `email` (`email`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `correos` (
  `id` int(11) NOT NULL auto_increment,
  `idEmail` int(11) NOT NULL,
  `de` varchar(255) NOT NULL,
  `para` varchar(255) NOT NULL,
  `subject` varchar(255) NOT NULL,
  `fecha` varchar(255) NOT NULL,
  `body` text NOT NULL,
  `fullmail` text NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `idEmail` (`idEmail`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Creo que es suficientemente descriptiva. El campo fecha de emails nos servirá para controlar los 60 minutos de duración máxima.

Pues ya tenemos todo preparado. Veamos cómo procesamos los emails.

/home/usaytirame/procesa.php

include ("mimeDecode.php");

$email=file("php://stdin");
$email=implode("", $email);

$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$params['input'] = $email;
$structure = Mail_mimeDecode::decode($params);

$subject = quoted_printable_decode(trim($structure->headers['subject']));
$ddate = quoted_printable_decode(trim($structure->headers['date']));
$from = quoted_printable_decode(trim($structure->headers['from']));
$to = quoted_printable_decode(trim($structure->headers['to']));

if(ereg("<(.*)>", $to, $p))
    $to=$p[1];
$to=strtolower($to);

Tenemos la primera parte del procesado preparada. De aquí debemos destacar:

$email=file("php://stdin");

Con esto leemos desde la entrada estandar el contenido del email. Recordad que no estamos en una aplicación web sino en un script ejecutado en la consola del sistema.

La otra línea importante es:

$structure = Mail_mimeDecode::decode($params);

Con ella conseguimos procesar el email y separar su estructura en un array asociativo con los distintos parámetros. Os ayudará mucho ver el contenido de ese array:

print_r($structure)

Llegados a este punto podemos hacer una prueba. Necesitamos el contenido completo de un email, podemos sacarlo de nuestro cliente de correo o del propio servidor.

cat prueba.eml | /home/usaytirame/procesa.php

Si todo ha ido bien veremos en pantalla un array con la estructura del correo.

No voy a detallar todo el proceso ya que alargaría mucho el artículo. Vamos con el siguiente paso:

$query="select idEmail from emails where email=".$conn->quote($to);
$rs=$conn->Execute($query);
if($rs->recordcount()==0){
        exit(100);
}else{
        $idEmail=$rs->fields['idEmail'];
        $content = get_content($structure);
        $query="insert into correos
            (idEmail, de, para, subject, fecha, body, fullmail)
            VALUES
           ($idEmail,
            ".$conn->Quote($from).",
            ".$conn->Quote($to).",
            ".$conn->Quote($subject).",
            ".$conn->Quote($ddate).",
            ".$conn->Quote($content).",
            ".$conn->Quote($email).")";
        $rs=$conn->Execute($query);
}

En la primera parte del script comprobamos si la cuenta a la que va destinado el email existe en nuestra base de datos, si no, muy importante, devolvemos un código 100 que indica a qmail que debe devolver el correo ya que no existe el usuario.

Si la cuenta existe recogemos el cuerpo del mensaje. Yo lo hago con la función get_content, que analiza la estructura del correo y devuelve el contenido. Esta parte os la dejo a vosotros. Básicamente consiste en comprobar las distintas partes que puede tener un correo y devolver lo que estimemos oportuno. Un detalle muy importante a tener en cuenta es la codificación tanto del email como de nuestra base de datos y la aplicación web. En mi caso las dos últimas están en UTF-8, con lo que debo convertir todos los textos del email a esta misma codificación. La estructura que teníamos inicialmente en un array tendrá parámetros que nos indican el charset en el que viene el email. Las funciones de conversión de PHP te pueden ser útiles: iconv, utf8_encode, etc.Finalmente introducimos todos los campos del email en la base de datos.

Puedes volver a probar a procesar el email de prueba tal y como hicimos antes. Recuerda que debes añadir el registro de la tabla emails para que  guarde el correo, si no la cuenta no existirá. Una vez te funcione desde la línea de comandos ya puedes probar a enviarte un correo real :).

La aplicación web

No creo que hacer la parte web propiamente dicha necesite muchas explicaciones. Tenemos ya todos los elementos preparados, sólo debemos añadir los formularios para crear la cuenta de correo (aleatoria o personalizada) y, con un poco de ajax, ir cargando los correos a medida que van llegando. No  hay más truco.

Añadiré, eso sí, algunas aclaraciones interesantes.

Para crear las cuentas aleatorias, en vez de utilizar una secuencia aleatoria de números y letras, que daría como resultado algo ininteligible, usamos los diccionarios que comentaba más arriba. Los importamos en una tabla de la base de datos y simplemente tenemos que buscar aleatoriamente una palabra que no esté utilizada todavía como cuenta de correo, sencillo y muy impactante visualmente ya que estás ofreciendo cuentas legibles y con sentido.

Como en cualquier otra aplicación accesible públicamente, hay que añadir algún tipo de mecanismo de seguridad. En mi caso lo he hecho implementando una blacklist de direcciones IP. Cada vez que se crea una cuenta actualizo en una base de datos el número de cuentas que se han creado desde esa IP, si pasa del límite que estimemos oportuno, esa IP se pasa a la tabla de lista negra y cuando intente crear una nueva cuenta no se le dejará.

Nos falta una cosa: Eliminar las cuentas que tienen más de una hora. Muy sencillo, una tarea en el CRON que ejecuta un script que lanza una consulta a la base de datos que elimina las cuentas (y sus correos asociados) que se crearon hace más de 60 minutos.

Finalmente he añadido la opción de reiniciar esos 60 minutos de tiempo, simplemente actualizando el timestamp de la base de datos y algunos efectos visuales para plegar y desplegar los mensajes usando Jquery.

No hay mucho más, en unas cinco horas tenemos la aplicación hecha y funcionando.

Conclusiones

Como conclusión, la misma que hice hace unos meses con el primer proyecto. La copio tal cual porque es igual de válida.

Bueno, y todo este rollo ¿para qué?. Pues muy sencillo, para que veais que hoy en día la tecnología está al alcance de todos, es sencillo y rápido crear un proyecto en Internet, hay de todo por todas las esquinas, la tecnología no es lo importante, lo que verdaderamente cuenta es cómo mueves ese producto tecnológico para rentabilizarlo y obtener un beneficio de él.

Ya tengo mi proyecto superchulo funcionando, sólo me ha costado unas 5 horas de trabajo. Le he puesto un poco de Adsense por aquí y por allí. ¿Y ahora qué? ¿A esperar a que la gente entre y me haga millonario? 😛 Es mucho más complicado que eso como todos sabéis, primero tienes que tener una masa de usuarios elevada que le dé movimiento al proyecto y después tienes que conseguir que la mayoría de ellos sean gente normal, no gente técnica, usuarios avanzados que no pagamos por nada ni pinchamos en publicidad 😛 .

Hoy en día, en Internet, como en cualquier negocio, las técnicas de marketing y venta son mucho más importantes que la tecnología en sí misma, es duro reconocerlo, pero es así. De nada sirve que tengas el mejor producto del mundo mundial si no consigues que la gente lo utilice y se deje dinero, así de claro. Si tienes los conocimientos adecuados para mover el negocio, no te preocupes, la tecnología te la aporta cualquier partner por un módico precio, pero poner en manos de otro toda la estrategia de ventas de tu negocio no está tan claro ¿no?.

Espero que os sirva de algo el artículo. He querido mostrar, fundamentalmente, cómo utilizando algunas librerías que puedes obtener sin coste puedes hacer algo realmente útil y funcional con muy poco esfuerzo. Seguro que sacáis alguna idea.

Perdón por el rollo 😛 , al final me ha costado mucho más escribir el artículo que implementarlo.

Podíamos haber añadido una opción que he visto por ahí que consiste en crear una cuenta automáticamente cada vez que entra un email para una cuenta que no existe, pero estaríamos creando cuentas para todo el spam que recibamos, así que prefiero no hacerlo. Si quisierais hacerlo creo que ya sabéis cómo.

Monitorizando Qmail con Cacti

Hace ya mucho tiempo que no hablamos de Cacti, mi sistema de monitorización preferido. Hoy veremos como controlar con él la actividad de nuestro Qmail.

Habitualmente la instalación de Qmail se realiza utilizando las daemonTools, pero nunca me han gustado mucho así que lanzo el servicio qmail-send con un script propio, mientras que los servidores smtpd y pop3d los integro con xinetd. Ya sé que a muchos no les gustará esto último, pero para mi es muy cómodo :P. El caso es que con este sistema, estos últimos demonios escapan del control de multilog, el sistema de logging de las daemontools, mala suerte. Veremos en este artículo cómo monitorizar el servicio de envío y recepción de Qmail pero no veremos nada relativo a smtp o pop3 debido a que no tengo esos logs. Si los tuvieses sería muy sencillo añadir estas gráficas de actividad, incluso las de spamassasin o clamav.

Supondremos que nuestro multilog deja sus archivos en /var/log/qmail, sustituye esto por tu ruta real, esa es la mía :P.

Lo primero que tendremos que hacer será instalar el paquete qmailmrtg7. Este software permite crear gráficas mrtg a partir de los logs de multilog. Será la herramienta que utilicemos para obtener los datos. NO es necesario que configuremos todo el qmailmrtg7, sólo necesitamos el ejecutable. Podemos comprobar que funciona bien haciendo:

[osus@servidor ~]#/usr/local/bin/qmailmrtg7 q /var/qmail/queue
636
0

Como se puede ver, nos devuelve dos líneas, en la primera los mensajes en cola y en la segunda los mensajes sin procesar. Esto marcha bien.

Configuremos ahora el servidor snmp para que podamos obtener estos valores a través de Cacti, para ellos simplemente añadimos al archivo de configuración las siguientes líneas:

/etc/snmpd/snmpd.conf

extend .1.3.6.1.4.1.18689.1.3 qmail-message-status /usr/local/bin/qmailmrtg7 s /var/log/qmail
extend .1.3.6.1.4.1.18689.1.3 qmail-bytes-transfer /usr/local/bin/qmailmrtg7 b /var/log/qmail
extend .1.3.6.1.4.1.18689.1.3 qmail-smtp-concurrency /usr/local/bin/qmailmrtg7 t /var/qmail/supervise/qmail-smtpd/log/main
extend .1.3.6.1.4.1.18689.1.3 qmail-smtp-sessions /usr/local/bin/qmailmrtg7 a /var/qmail/supervise/qmail-smtpd/log/main
extend .1.3.6.1.4.1.18689.1.3 qmail-queue /usr/local/bin/qmailmrtg7 q /var/qmail/queue
extend .1.3.6.1.4.1.18689.1.3 qmail-messages /usr/local/bin/qmailmrtg7 m /var/log/qmail
extend .1.3.6.1.4.1.18689.1.3 qmail-concurrency /usr/local/bin/qmailmrtg7 c /var/log/qmail

Ahora podemos probar que funciona buscando, por ejemplo, el tamaño de la cola que veíamos antes:

[osus@servidor ~]# snmpwalk -Os -c COMUNIDAD -v 2c SERVIDOR  .1.3.6.1.4.1.18689.1.3.4.1.2.11.113.109.97.105.108.45.113.117.101.117.101.1
enterprises.18689.1.3.4.1.2.11.113.109.97.105.108.45.113.117.101.117.101.1 = STRING: "636"

Debes sustituir COMUNIDAD y SERVIDOR por los datos de tu servidor snmpd. Ya tenemos todo preparado, vamos a por las gráficas.

Cuando comencé a buscar plantillas de Cacti para Qmail encontré bastantes referencias, pero no conseguí que funcionasen, unas simplemente me daban errores al importarlas y otras tenían los OID del snmp incorrectas y no recopilaban nada, así que lo que hice fue una mezcla de todas ellas modificadas por mi. Aquí podéis descargar mis cacti_qmail_templates, espero que os sirvan a todos. Lo importante es que los “data templates” se importen bien, a partir de ellos puedes crear tus propias gráficas. Yo os adjunto una plantilla que crea una gráfica común de todos los indicadores, cacti_graph_template_qmail_-_statistics.xml, cuyo resultado es lo que véis:

qmail_cactiPero también podéis crear gráficas independientes para cada indicador, mucho más claras, sin duda, que todas juntas. Debéis importar, en este caso, todas las demás plantillas que aparecen en el archivo.

qmail_cacti2Eso es todo, ahora podemos ver el tráfico de nuestro MTA visualmente y buscar potenciales problemas.

Descarga los templates de cacti para Qmail.

Cómo comprobar SMTP autentificado

Siempre me pasa lo mismo, cada vez que hago algún cambio en el servidor de correo (Qmail) me vuelvo tonto para probar que funciona bien ya que con el SMTP autentificado no es tan sencillo como introducir tu usuario y tu contraseña.

Tenemos dos métodos para probarlo, el primero requiere de una sola cadena en base64 con el usuario y la clave del usuario. Para sacarlo utilizamos una pequeña expresión en PERL:

perl -MMIME::Base64 -e 'print encode_base64("00USUARIO00CLAVE")'
AFVTVUFSSU8AQ0xBVkU=

Debes sustituir USUARIO y CLAVE por los datos adecuados. Presta atención a los 00 al inicio de cada parámetro, deben seguir ahí. Si tu usuario lleva el dominio completo debes añadir una barra inversa a la @, por ejemplo [email protected].

Con este dato ya podemos probar el servidor:

[osus@host ~]# telnet dominio.com 25
Trying 10.10.10.10...
Connected to dominio.com (10.10.10.10).
Escape character is '^]'.
220 dominio.com ESMTP
EHLO dominio.com
250-dominio.com
250-AUTH=LOGIN PLAIN
250-PIPELINING
250 8BITMIME
AUTH PLAIN AFVTVUFSSU8AQ0xBVkU=
235 ok, go ahead (#2.0.0)

Como véis, después de saludar al servidor (ehlo dominio.com) le decimos en una sóla línea que queremos autentificarnos y cuales son nuestros datos (AUTH PLAIN tucadenaBase64). Eso es todo. Si la autentificación no fuese correcta recibiríamos la respuesta adecuada.

AUTH PLAIN AFVTVUFSSU8AQ0xBVkU=
535 authorization failed (#5.7.0)

En el segundo método enviamos el usuario y la claves por separado, también en base64.

perl -MMIME::Base64 -e 'print encode_base64("usuario")'
dXN1YXJpbw==
perl -MMIME::Base64 -e 'print encode_base64("clave")'
Y2xhdmU=

Para probarlo debemos enviar por un lado el nombre de usuario y por otro su contraseña:

[osus@servidor ~]# telnet dominio.com 25
Trying 10.10.10.10...
Connected to dominio.com (10.10.10.10).
Escape character is '^]'.
220 dominio.com ESMTP
ehlo dominio.com
250-dominio.com
250-AUTH=LOGIN PLAIN
250-PIPELINING
250 8BITMIME
AUTH LOGIN
334 VXNlcm5hbWU6
dXN1YXJpbw==
334 UGFzc3dvcmQ6
Y2xhdmU=
235 ok, go ahead (#2.0.0)

Como véis es parecido al primer sistema pero aquí enviamos el usuario y la contraseña. Después de saludar (ehlo dominio.com) le indicamos que queremos autentificarnos (AUTH LOGIN). Ahora nos pide el usuario (VXNlcm5hbWU6 es Username: en base64) y la clave (UGFzc3dvcmQ6 es Password:). Hemos terminado.

El resto del protocolo SMTP funciona como si estuviese sin autentificar.

Problemas con checkpassword

Otro de los puntos que suele darme algún tipo de problema es la configuración del sistema que utilizan tanto qmail-smtpd como qmail-pop3d para autentificarse: checkpassword. Por defecto no se puede probar directamente ya que lo importante es el código de error que devuelve. Para comprobar su funcionamiento debemos hacer:

perl -e 'printf"%s%sY123456","USUARIO","CLAVE"' | /bin/checkpassword id 3<&0
uid=511(USUARIO) gid=100(users) groups=100(users)

Si la autentificación es correcta nos devolvera algo similar a lo que se indica, si no, no obtendremos respuesta.

Es importante tener en cuenta que el usuario que ejecuta los demonios de Qmail (normalmente qmaild) debe poder ejecutar checkpasswd y que éste debe tener permiso setuid de manera que se ejecute siempre como root independientemente del usuario que lo haga en realidad. Esto debe ser así ya que tiene que acceder a /etc/passwd.

chmod 4755 /bin/checkpassword

Espero que a alguien le sirvan estos apuntes :P, a mi seguro que me ahorrarán tiempo dentro de algunos meses cuando lo necesite de nuevo.

Otras utilidades para un servidor de correo

A veces un servidor de correo puede sernos de muchísima utilidad si sabemos cómo manejarlo correctamente. En el artículo de hoy veremos como utilizar nuestro MTA para ejecutar automáticamente acciones cuando se recibe un determinado email o con los parámetros que definamos. Una vez conozcamos la teoría plantearemos dos casos prácticos como ejemplo.

Para comenzar necesitaremos un servidor Linux con Qmail como MTA. Supongo que cualquier otro servidor de correo servirá (Sendmail, Postfix), yo lo personalizo en Qmail porque es el que conozco y utilizo, pero estoy seguro de que con los demás se puede hacer lo mismo.

La teoría

Para entender cómo funciona la idea debemos entender primero cómo Qmail realiza la entrega de mensajes en los buzones locales. Es un tema sobre el que hay bastante literatura buscando en Google pero que puede no quedar muy claro en una lectura rápida. Es el famoso lío de los archivos .qmail.

Cada vez que se crea un usuario del sistema se debe crear, si va a recibir emails, un archivo .qmail-default en su directorio de usuario.

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-default
./Maildir/

Normalmente este archivo se crea automáticamente ya que al instalar qmail se habrá creado una copia de este archivo en el esqueleto de creación de usuarios /etc/skel:

[jiglesias@lerez ~]# ls -la /etc/skel
total 56
drwxr-xr-x  4 root root  4096 Jul 29 18:29 .
drwxr-xr-x 97 root root 12288 Dec 30 04:08 ..
-rw-r--r--  1 root root    33 Feb  1  2008 .bash_logout
-rw-r--r--  1 root root   176 Feb  1  2008 .bash_profile
-rw-r--r--  1 root root   124 Feb  1  2008 .bashrc
drwx------  5 root root  4096 Aug 29  2007 Maildir
-rw-r--r--  1 root root    12 Jan  2  2008 .qmail-default
-rw-r--r--  1 root root   658 Sep 12  2006 .zshrc

Para el que no lo sepa, el esqueleto son los archivos que se copiarán al directorio de usuario (con los permisos de éste) cada vez que se crea uno nuevo. Si quieres que todos tus usuarios tengan algún archivo automáticamente, éste es tu sitio. En nuestro caso vemos que además del .qmail-default está el directorio Maildir, el de entrega por defecto del correo en qmail. Teniendo un usuario este archivo y este directorio, podrá recibir correo.

Vale vale, vas muy deprisa. Todavía no has explicado para que sirve el .qmail-default ese.  Cierto. Los archivos .qmail indican las reglas de entrega de los mensajes en base a dos parámetros:

  • El nombre del archivo .qmail-xx hace referencia a la cuenta de correo sobre la que actúa.
  • El contenido indica qué hacer con el correo.

Supongamos un usuario (jiglesias)  que recibe el correo de dos cuentas distintas (jiglesias@… y osus@…).

Por defecto todo su correo irá a su buzón ya que es lo que indica el archivo .qmail-default. Queremos ahora que el comportamiento sea distinto dependiendo de la cuenta a la que vaya dirigido, creamos entonces los archivos .qmail para las direcciones:

  • .qmail-jiglesias : controla el correo que vaya a jiglesias@…
  • .qmail-osus : controla el correo que vaya a osus@…

Podemos incluso ir un poco más lejos con un archivo .qmail-jiglesias-default, y controlaríamos el correo que vaya a cualquier dirección del tipo jiglesias-XXXX@…, es decir, cualquier dirección que comience por jiglesias- será controlada por este archivo .qmail.

En el caso básico, que es el que veíamos, la entrega se realiza al buzón de correo del usuario (el directorio Maildir) pero podríamos hacer otras cosas en función del contenido del archivo .qmail encargado de procesar la entrega del correo:

Reenvío a otra cuenta:

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-jiglesias
[email protected]

Reenvío a un programa/script:

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-osus
|preline /usr/bin/programa

Combinación de los anteriores

[jiglesias@lerez ~]# cat /home/jiglesias/.qmail-jiglesias
./Maildir/
[email protected]
|preline /usr/bin/programa

El caso que nos interesa es el segundo, es decir, pasar la entrega del email a un script que se encargará de analizar el email y tomar decisiones.

Te habrá llamado la atención el |preline del archivo .qmail. Es el sistema que prepara un email para ser procesado y entregado a otro script añadiendo algunas cabeceras. La salida del script que reciba el email es importante ya que de ella depende el resultado de la entrega final de email, en concreto es importante saber que si queremos rechazar un email habrá que terminar el script con un exit (100), esto indicará a qmail que debe rechazar ese mensaje. Veremos más adelante la utilidad de esta salida.

La práctica

Una vez sabemos cómo pasar el control de un email recibido a un script, veamos como tratarlo. Lo haremos con un script PHP. En nuestro archivo .qmail haremos algo como:

[jiglesias@lerez ~]# more .qmail-jiglesias
|preline /usr/bin/php /home/jiglesias/prueba.php

Con esto hemos terminado el trabajo en el servidor de correo. Veamos ahora como parsear el email desde PHP.

Lo primero que debemos hacer es recoger el contenido del email desde el script a través de la entrada estándar,  después ya podemos procesar el email como una cadena de texto.

<?php
$email=file("php://stdin");
$email=implode("", $email);
?>

Con estas sencillas dos líneas de código tendremos en nuestro script el contenido del email. Ahora sólamente debemos procesarlo. Podemos hacerlo línea por línea por nuestra cuenta o apoyarnos en alguna librería. Yo utilizo Mail_mimeDecode de Pear. La ventaja de esta librería es que podemos obtener, además del texto del email, los archivos adjuntos.

<?php
$email=file("php://stdin");
$email=implode("", $email);
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$params['input'] = $email;
$structure = Mail_mimeDecode::decode($params);
$subject = trim($structure->headers['subject']);
$ddate = trim($structure->headers['date']);
$from = addslashes(trim($structure->headers['from']));
if(ereg("<(.*)>", $from, $p)) $from=$p[1];
if(ereg(""(.*)"", trim($structure->headers['from']), $pp))
    $nombre=$pp[1];
?>

Así podemos ya procesar el email y tomar las decisiones que consideremos oportunas. Podremos insertarlo en una base de datos, lanzar otros procesos automatizados, enviar avisos por SMS… lo que se nos ocurra.

Casos prácticos

La pregunta clave, después de ver la teoría, sería ¿para qué me sirve esto?.  Os propongo dos aplicaciones que yo he hecho.

Sistema de soporte

El típico sistema de tickets de soporte. En el asunto del email se arrastra el identificador del ticket, por ejemplo [#123445]. Tendremos que analizar el asunto y comprobar si aparece el patrón predefinido. Si no existe estamos ante un nuevo ticket e insertamos los datos en nuestra base de datos, en caso contrario es una respuesta a una incidencia anterior y ahí tendremos el identificador. Sencillo ¿no?. Podemos incluso adjuntar a nuestras indicendias archivos que puedan llegar en el email.

Envío de archivos desde el movil

El segundo ejemplo sería semejante al anterior técnicamente pero distinto conceptualmente. La mayoría de los móviles (salvo los de gama alta) no pueden enviar archivos desde los formularios wap (<input type=”file”>). La alternativa es que el usuario envie un email o un MMS (la mayoría de operadoras permiten el envío a direcciones de email) con sus archivos. Nuestro script procesará el contenido del mail recibido, decodificará los archivos y los tratará como sea oportuno.

Son dos sencillos ejemplos de cómo utilizar el email para automatizar tareas, pero, como he comentado, podríamos hacer todo lo que se nos ocurra, desde enviar un SMS de aviso hasta incluso reiniciar nuestro servidor o lanzar cualquier otra tarea.

Error con el smtp autentificado con los clientes de Nokia a través de Qmail

Como os decía hace unos días, recientemente he cambiado el Nokia N70 que llevaba desde hace algo más de dos años por un Nokia E51. La característica más importante de este nuevo terminal es la disponibilidad de conexión WIFI, con lo cual, si tienes un poco de suerte y encuentras un punto de acceso abierto, puedes tener acceso a tu correo desde cualquier punto. No he tenido muchos problemas para encontrar puntos de acceso y habitualmente he podido leer el correo. El problema llega a la hora de enviarlo ya que siempre obtengo un error en el envío. Cansado de no saber qué ocurría y dado que voy a estar unos días fuera de casa, decidí investigar un poco.

Comencé por ver cual era la comunicación del cliente de correo con mi servidor. Para ello lancé un sniffer en mi máquina, en este caso Wireshark, nombre actual del Ethereal de toda la vida. Para no volverme loco con todo el tráfico que iba a ver añadí un filtro, de manera que sólo se mostraría el tráfico que venía desde la IP de mi conexión ADSL (11.22.33.44), así sólo vería lo que estaba generando mi propio móvil.

tshark -i2 -f "src host 111.222.333.444"

Y este fué el resultado:

3.197223 11.22.33.44 -> 55.66.77.88 TCP 49537 > smtp [SYN] Seq=0 Win=64240 Len=0 MSS=1460 TSV=1218449730 TSER=0 WS=0
3.197263 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 TSV=389435772 TSER=1218449730 WS=7
3.347274 11.22.33.44 -> 55.66.77.88 TCP 49537 > smtp [ACK] Seq=1 Ack=1 Win=64240 Len=0 TSV=1218663480 TSER=389435772
3.356257 55.66.77.88 -> 11.22.33.44 SMTP Response: 220 servidor ESMTP
3.509426 11.22.33.44 -> 55.66.77.88 SMTP Command: EHLO
3.509451 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [ACK] Seq=29 Ack=22 Win=5888 Len=0 TSV=389436084 TSER=1218828105
3.509490 55.66.77.88 -> 11.22.33.44 SMTP Response: 250-servidor
3.663467 11.22.33.44 -> 55.66.77.88 SMTP Command: AUTH CRAM-MD5
3.813384 11.22.33.44 -> 55.66.77.88 SMTP Command: amlxxxxxxWFzIDkxNTM4MxxxxxxyYjVjOTdmYxxxxxxxDFhZDg2MjEw
3.853554 55.66.77.88 -> 11.22.33.44 TCP smtp > 49537 [ACK] Seq=197 Ack=95 Win=5888 Len=0 TSV=389436428 TSER=1219132230
8.817212 55.66.77.88 -> 11.22.33.44 SMTP Response: 535 authorization failed (#5.7.0)

Es decir, me estaba fallando la autentificación a través de CRAM-MD5 a pesar de que en el programa de correo del móvil tenía correctamente introducidos el usuario y la clave y el servidor, obviamente, se presentaba diciendo que soportaba este método.

[osus@servidor ~]# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
220 servidor ESMTP
ehlo
250-servidor
250-AUTH LOGIN CRAM-MD5 PLAIN
250-PIPELINING
250 8BITMIME

Investigando un poco averigué que para habilitar la autentificación CRAM-MD5 en Qmail, el MTA de correo que utilizo, además de instalar el parche qmail-smtp-auth es necesario sustituir el original checkpasswd  por cmd5checkpw. Con este argumento caí en la cuenta de que nunca jamás he tenido esta autentificación funcionando en mis servidores 😀 , el servidor indicaba que sí pero la realidad era que no 😛 . Ocho años utilizando Qmail y ahora me doy cuenta de que no funcionaba.

No quería complicarme mucho así que, si hasta ahora no la tenía, tampoco me importaba seguir así. Pensé entonces que, si en vez de responder a los clientes que soportaba CRAM-MD5, no se lo decía, el cliente del E51 escogería el AUTH LOGIN de toda la vida y funcionaría. ¿Cómo hacer esto? Sencillo, Qmail es de código abierto y tenemos el código fuente a nuestra disposición.

Parcheando Qmail

En los fuentes de mi Qmail (con todos sus parches) busqué la cadena en cuestión:

grep  CRAM-MD5 *

Y ahí me apareció el objetivo de nuestro parche, qmail-smtpd.c.

Dicho y hecho, abrimos el archivo con vi y buscamos la cadena en cuestión (:/CRAM-MD5) llegando a:

out("rn250-AUTH LOGIN CRAM-MD5 PLAIN");

Esto va a ser sencillo. Modificamos esa/s línea/s de manera que quede/n:

out("rn250-AUTH LOGIN PLAIN");

De este modo, cuando el cliente pregunte le diremos que soportamos LOGIN y PLAIN, pero no CRAM-MD5. He explicado esto en plural porque es más que probable que tengas dos líneas iguales y no sólo una. Puedes dejar sólo una modificada como he indicado o modificar las dos.

Compilamos ahora con un simple make. Como sólo hemos  modificado este archivo únicamente se compilará este. Hacemos una copia de seguridad de nuestro /var/qmail/bin/qmail-smtpd (por si algo no va bien) y copiamos el nuevo a esta ruta dejando los permisos, owner y group igual que tenía el que había.

Si ahora pruebas a enviar un email desde el Nokia E51 comprobarás que funciona correctamente, el cliente del teléfono escoge otro de los sistemas. Utilizando de nuevo el sniffer verás que ahora escoge AUTH LOGIN como método de autentificación.

Ya puedo enviar correo desde mis servidores con el teléfono. Gracias a un sencillo ejercicio de escucha del tráfico de red hemos averiguado cual era el problema.