{"id":1417,"date":"2016-04-16T17:57:21","date_gmt":"2016-04-16T17:57:21","guid":{"rendered":"http:\/\/esferas.org\/msqlu\/?p=1417"},"modified":"2016-04-17T08:59:03","modified_gmt":"2016-04-17T08:59:03","slug":"qpsmptd_y_los_complementos","status":"publish","type":"post","link":"https:\/\/esferas.org\/msqlu\/2016\/04\/16\/qpsmptd_y_los_complementos\/","title":{"rendered":"qpsmtpd y los complementos &#8230;"},"content":{"rendered":"<p>&#8230; y mi primera incursi\u00f3n en la ampliaci\u00f3n del programa.<\/p>\n<p><!--more--><\/p>\n<p>Tal y como tengo ahora mismo configurada la recepci\u00f3n de correo en <em>taquiones.net<\/em> no tengo control sobre la comprobaci\u00f3n de los buzones locales; esa tarea est\u00e1 en manos del despachador <em>real<\/em> de correo, <em>exim4<\/em>, que hace lo que puede con ellos ya que no tiene posibilidad de rechazarlos en tiempo de conexi\u00f3n. <em>qpsmtpd<\/em> ya los ha aceptado y se los ha pasado como si fuesen aceptables.<\/p>\n<p>He estado buscando complementos que hiciesen dicha comprobaci\u00f3n y lo m\u00e1s parecido que he encontrado ha sido <a href=\"http:\/\/www.hjp.at\/projekte\/qpsmtpd\/aliases\/\">aliases <\/a>de Peter J. Holzer. Lo he descartado porque el archivo de alias usa las direcciones completas y no s\u00f3lo los nombres locales. Salvo eso est\u00e1 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\u00e9n puede usar otras partes del programa.<\/p>\n<p>Aunque tampoco es que tenga un mont\u00f3n de alias de correo en mis m\u00e1quinas prefiero no tener que aprender una s\u00edntaxis nueva. Si emplease el formato de <a href=\"http:\/\/www.tldp.org\/LDP\/solrhe\/Securing-Optimizing-Linux-RH-Edition-v1.3\/chap22sec179.html\">alias de sendmail<\/a> ni lo hubiese dudado, pero como tambi\u00e9n estoy buscando una excusa para ponerme manos a la obra he empezado con ello. He creado un <a href=\"http:\/\/git.astillas.net\/victor\/qpsmtpd-plugins.git\">peque\u00f1o repositorio<\/a> que contendr\u00e1 el software y me facilitar\u00e1 empaquetarlo y he comenzado a revisar la documentaci\u00f3n:<\/p>\n<ul>\n<li><a href=\"https:\/\/wiki.qpsmtpd.org\/doku.php?id=api:start\">Referencia del API de qpsmptd<\/a><\/li>\n<li><a href=\"https:\/\/wiki.qpsmtpd.org\/doku.php?id=api:start\">Anclajes del API <\/a><\/li>\n<\/ul>\n<h4>Lo que m\u00e1s o menos alcanzo a entender por ahora<\/h4>\n<p>Aunque estoy traduciendo <em>plugin<\/em> por complemento en realidad no est\u00e1n ampliando el programa: <strong>son el programa<\/strong>.<\/p>\n<p><em> qpsmtpd<\/em> est\u00e1 escrito de tal forma que si no se declara la carga de ninguno de ellos no sirve para nada. Incluso tiene uno llamado <em>rcpt_to<\/em> que debe ser insertado a modo de centinela al final de la configuraci\u00f3n para que termine aceptando el mensaje de correo.<\/p>\n<ul>\n<li>Los complementos se cargan al arrancar el programa qpsmtpd.<\/li>\n<li>Deben definirse en el archivo <em>plugins, <\/em>donde aparece su nombre precedido del directorio si estuviesen en uno.<\/li>\n<li>Pueden tambi\u00e9n aceptar par\u00e1metros tras el nombre del complemento.<\/li>\n<li>El sistema est\u00e1 dise\u00f1ado para que los complementos puedan ser cargados <strong>m\u00e1s<\/strong> de una vez con par\u00e1metros diferentes. En ese caso las siguientes instancias reciben un sufijo del tipo\n<pre class=\"inline:true decode:1 \" >:N<\/pre>\n<p> comenzando por cero.<\/li>\n<\/ul>\n<h5>Carga de complementos<\/h5>\n<p>El programa qpsmtd espera encontrarse los complentos en dos sitios:<\/p>\n<ol>\n<li>Directorio <em>plugins<\/em>\/ de su instalaci\u00f3n (en Debian es <em>\/usr\/share\/qpsmtpd\/plugins<\/em>).<\/li>\n<li>Directorios arbitrarios declarados en el archivo <em>config\/plugin_dirs<\/em>.<\/li>\n<li>Directorios habituales de Perl si se declara como un m\u00f3dulo formal de este lenguaje.<\/li>\n<\/ol>\n<p>Ejemplo de declaraci\u00f3n de complementos:<\/p>\n<pre class=\"lang:default decode:true\"># S\u00f3lo el nombre\r\ncheck_relay\r\n\r\n# Directorio y nombre\r\nvirus\/clamdscan\r\n\r\n# Nombre y par\u00e1metros \r\nspamassassin reject_threshold 7\r\n\r\n# Dos instancias con diferentes par\u00e1metros\r\nmy_rcpt_check example.com\r\nmy_rcpt_check:0 example.org\r\n\r\n# M\u00f3dulo Perl formal\r\nMy::Plugin<\/pre>\n<p>Todos descienden de la clase <\/p>\n<pre class=\"inline:true decode:1 \" >Qpsmtpd::Plugin<\/pre>\n<p> que ya incluye al menos la funci\u00f3n <em>plugin_name <\/em>que devuelve su nombre.<\/p>\n<h5>Inicializaci\u00f3n<\/h5>\n<p>Tras cargar el complemento en el programa \u00e9ste lo inicializa llamando a la funci\u00f3n <em>init()<\/em> si existe con los siguientes par\u00e1metros:<\/p>\n<ul>\n<li>\n<pre class=\"inline:true decode:1 \" >$self<\/pre>\n<p> es una referencia al objeto que lo contiene de clase <\/p>\n<pre class=\"inline:true decode:1 \" >Qpsmtpd::Plugin<\/pre>\n<pre class=\"inline:true decode:1 \" ><\/pre>\n<p> y que se ha creado durante la carga.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >$qp<\/pre>\n<p> es una referencia al objeto <\/p>\n<pre class=\"inline:true decode:1 \" >Qpsmtpd<\/pre>\n<p>.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >@args<\/pre>\n<p> es una lista de par\u00e1metros definidos en el archivo <em>config\/plugins<\/em> separados por espacios en blanco.<\/li>\n<\/ul>\n<h5>Configuraci\u00f3n<\/h5>\n<p>Existe un mecanismo de acceso a archivos de configuraci\u00f3n disponible en cada funci\u00f3n con la cadena de m\u00e9todos<\/p>\n<pre class=\"lang:perl decode:true \">@lines = $self-&gt;qp-&gt;config('myconfig');\r\n<\/pre>\n<p>Siendo <em>myconfig<\/em> el nombre del archivo y <em>@lines<\/em> donde se recoge su contenido. El formato es sencillo: texto plano del que se descartan las l\u00edneas vac\u00edas y las que comiencen por el car\u00e1cter <em>#<\/em>.<\/p>\n<p>Estos archivos se leen durante el arranque de <em>qpsmtpd<\/em> y pueden modificarse sin reiniciarlo.<\/p>\n<h5>Registro de eventos<\/h5>\n<p>Para registrar lo que ocurre durante la ejecuci\u00f3n los complementos disponen de un mecanismo que permite reunificar el destino de los mismos.<\/p>\n<pre class=\"lang:perl decode:true\">$self-&gt;qp-&gt;log( $loglevel, $logmessage );\r\n<\/pre>\n<p><em>$loglevel<\/em> contiene los valores habituales para los servidores: LOGDEBUG, LOGINFO, LOGNOTICE, LOGWARN, LOGERROR, LOGCRIT, LOGALERT, LOGEMERG.<\/p>\n<h5>Introspecci\u00f3n<\/h5>\n<p>Los complementos disponen de un mecanismo de introspecci\u00f3n que les permite saber en qu\u00e9 punto est\u00e1n entre otras cosas. Todos son m\u00e9todos que heredan de <em>Qpsmtpd::Plugin<\/em> y son:<\/p>\n<ul>\n<li>\n<pre class=\"inline:true decode:1 \" >plugin_name()<\/pre>\n<p> retorna el nombre del complemento que est\u00e1 funcionando ahora.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >hook_name()<\/pre>\n<p> retorna el nombre de la fase en la que est\u00e1 (nombre del anclaje).<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >auth_user()<\/pre>\n<p>: retorna el nombre del cliente tras la identificaci\u00f3n.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >auth_mechanism()<\/pre>\n<p>: retorna el mecanismo de identificaci\u00f3n empleado.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >connection():<\/pre>\n<p> retorna el objeto <em>Qpsmtpd::Connection<\/em> asociado a la conexi\u00f3n actual.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >transaction()<\/pre>\n<p>: retorna el objeto <em>Qpsmtpd::Transaction<\/em> asociado a la transacci\u00f3n actual.<\/li>\n<\/ul>\n<h5>Comunicaci\u00f3n interna<\/h5>\n<p>Para comunicar informaci\u00f3n entre complementos e incluso entre fases (<em>hooks<\/em>) dentro de un mismo c\u00f3digo existe un mecanismo que permite almacenar y recuperar textos.<\/p>\n<p>Se puede hacer tanto en una conexi\u00f3n (<em>Qpsmtpd::Connection<\/em>) como en una transacci\u00f3n (<em>Qpsmtpd::Transaction<\/em>). La conexi\u00f3n y la transacci\u00f3n 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\u00f3n de un mensaje para despacho. Por si quedan dudas, una conexi\u00f3n puede comprender varias transacciones.<\/p>\n<pre class=\"lang:perl decode:true \"># Consulta y almacenamiento de notas en una transacci\u00f3n\r\n$self-&gt;qp-&gt;transaction-&gt;notes('nota');\r\n$self-&gt;qp-&gt;transaction-&gt;notes('nota','Hola');\r\n\r\n# Consulta y almacenamiento de notas en una conexi\u00f3n\r\n$self-&gt;qp-&gt;connection-&gt;notes('total');\r\n$self-&gt;qp-&gt;connection-&gt;notes('total','zero');\r\n<\/pre>\n<h5>Archivos temporales<\/h5>\n<p><em>qpsmtpd<\/em> pone a disposici\u00f3n de los complementos un juego de funciones para manipular archivos temporales que luego se encarga de limpiar.<\/p>\n<ul>\n<li>\n<pre class=\"inline:true decode:1 \" >temp_file<\/pre>\n<p>() retorna el nombre de un archivo temporal sin abrirlo.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >temp_dir()<\/pre>\n<p> crea y retorna un nombre de directorio \u00fanico bajo el directorio temporal con permisos <em>0700<\/em>.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >spool_dir()<\/pre>\n<p> retorna el directorio temporal global.<\/li>\n<\/ul>\n<h5>C\u00f3digos de retorno<\/h5>\n<p>Los complementos se ejecutan en el orden en que aparecen definidos en la configuraci\u00f3n 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\u00f3digo diferente a <\/p>\n<pre class=\"inline:true decode:1 \" >DECLINED<\/pre>\n<p>. El c\u00f3digo de respuesta es obligatorio pero tambi\u00e9n se puede a\u00f1adir un texto para el cliente.<\/p>\n<p>El m\u00f3dulo <em>Qpsmtpd::Constants<\/em> define los c\u00f3digos de retorno con el siguiente significado:<\/p>\n<ul>\n<li>\n<pre class=\"inline:true decode:1 \" >DECLINED<\/pre>\n<p>: indica que el complemento no va a hacer nada y que la ejecuci\u00f3n debe continuar.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >OK<\/pre>\n<p> significa que se permite realizar la acci\u00f3n (y ya depende de la fase o del contexto).<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >DENY<\/pre>\n<p> es lo contrario: la acci\u00f3n no se permite.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >DENYSOFT<\/pre>\n<p> indica que tambi\u00e9n que la acci\u00f3n no se permite pero con un car\u00e1cter m\u00e1s temporal.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >DENY_DISCONNECT<\/pre>\n<p> deniega la acci\u00f3n y fuerza la desconexi\u00f3n con el cliente.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >DENYSOFT_DISCONNECT<\/pre>\n<p> igual que el anterior pero retornando un c\u00f3digo temporal.<\/li>\n<li>\n<pre class=\"inline:true decode:1 \" >DONE<\/pre>\n<p> se emplea para terminar el proceso de la petici\u00f3n.<\/li>\n<\/ul>\n<h5>Resumiendo &#8230;<\/h5>\n<p>&#8230; hay varios aspectos m\u00e1s pero sin haber escrito una l\u00ednea de c\u00f3digo no tengo ganas de escribir sobre ello. <a href=\"https:\/\/wiki.qpsmtpd.org\/doku.php?id=pod:plugins#introduction\">La referencia<\/a> que los autores han escrito al respecto m\u00e1s el c\u00f3digo disponible deber\u00e1 bastarme para comenzar a crear algo.<\/p>\n<p>&nbsp;<\/p>\n<div id=\"s3gt_translate_tooltip\" class=\"s3gt_translate_tooltip\" style=\"position: absolute; left: 120px; top: 3120px; opacity: 0.8;\"><\/div>\n<div id=\"s3gt_translate_tooltip\" class=\"s3gt_translate_tooltip\" style=\"position: absolute; left: 343px; top: 3847px;\"><\/div>\n","protected":false},"excerpt":{"rendered":"<p>&#8230; y mi primera incursi\u00f3n en la ampliaci\u00f3n del programa.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","webmentions_disabled_pings":false,"webmentions_disabled":false,"footnotes":""},"categories":[7],"tags":[17,566,553],"class_list":["post-1417","post","type-post","status-publish","format-standard","hentry","category-perl","tag-perl","tag-plugin","tag-qpsmtpd"],"_links":{"self":[{"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/posts\/1417","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/comments?post=1417"}],"version-history":[{"count":0,"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/posts\/1417\/revisions"}],"wp:attachment":[{"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/media?parent=1417"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/categories?post=1417"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/esferas.org\/msqlu\/wp-json\/wp\/v2\/tags?post=1417"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}