Mail2fax gateway reloaded

I gateway mail2fax e fax2mail sono strumenti largamente utilizzati quando in rete è presente un server fax software.
La definizione di gateway si deve al fatto che si parla di soluzioni che riescono a configurare un vero e proprio punto di collegamento tra due entità assolutamente diverse: la rete PSTN e le email.
Solitamente si tende a raccogliere sotto una unica definizione le funzionalità di entrambi tanto che genericamente si parla di "mail2fax gateway" riferendosi sia alla capacità di inoltro a caselle email dei fax in arrivo, sia a quella di spedizione via fax di particolari messaggi email.
Va da sè che i due strumenti dai quali non si può certamente prescindere se si vuole realizzare un mail2fax gateway sono un server fax software (per gestire le comunicazioni da e verso la linea PSTN) e un mail transport agent (per il trasporto dei messaggi email).

In ambiente GNU/Linux, i prodotti in assoluto più utilizzati per perseguire una finalità del genere sono HylaFAX (fax server) e Postfix (MTA).

L'implementazione di un mail2fax gateway è abbastanza semplice e la sua logica di funzionamento lineare:

  • fax in entrata: il messaggio viene ricevuto dal fax server (HylaFAX) che lo "passa" all'MTA (Postfix) affinchè lo recapiti nelle mailbox stabilite;
  • fax in uscita: un semplice messaggio email viene "passato" dall'MTA (Postfix) al fax server (HylaFAX) affinchè possa essere inoltrato sulla rete telefonica

Se per quanto riguarda le funzionalità relative ad i fax in entrata, le operazioni da compiere sono in sè banali (basta aver cura di impostare a dovere pochi file di configurazione di HylaFAX), per quanto riguarda quelle riguardanti i fax in uscita le cose a volte possono complicarsi un pò.

Per riuscire a spedire un fax inviando una semplice email ad un indirizzo del tipo numerotelefonico@fax, tendenzialmente si consiglia di creare un service di Postfix affinchè l'email in questione venga "passata" (in pipe) a faxmail. Sarà poi compito di quest'ultimo (che funge da vero e proprio mail2fax gateway) procedere alle operazioni necessarie all'invio del fax.
Il service viene configurato aggiungendo all'interno di /etc/postfix/master.cf un blocco del tipo:

fax  unix  -       n       n       -       1       pipe
  flags=        user=fax argv=/usr/bin/faxmail -d -n ${user}

L'esperienza ha però dimostrato che a volte tutto ciò si rivela insufficiente sia per le limitazioni intrinseche di faxmail, sia per la scarsa "flessibilità" di tale meccanismo.

Faxmail, ad esempio, non è capace di "gestire" gli invii multipli. In sostanza, quando al service di Postfix viene passata un'email indirizzata a più recipient, faxmail è in grado di gestire solo il primo di essi. Questa è la ragione per cui si deve istruire Postfix a limitare i recipient multipli impostando in /etc/postfix/main.cf:

fax_destination_recipient_limit = 1

In questo modo, per un'email indirizzata ad n destinatari il sistema effettuerà n spedizioni distinte. Una alla volta.

Riguardo alla scarsa "flessibilità", invece, un esempio potrebbe essere quello della situazione in cui ci sia necessità di inviare via fax soltanto gli allegati del messaggio di posta elettronica, escludendo quindi la pagina in cui sono contenute sia le informazioni relative a mittente e destinatario che corpo del messaggio e firma dell'email.
Alcuni client di posta (come quelli presenti in alcune apparecchiature multifunzione come le Olivetti dCopia o le HP MFP), anche se in fase di invio di una email si dovesse scegliere di eliminare del tutto il corpo dell'email, lo rigenererebbero automaticamente. Fallirebbe pure il workaround di sostituire al corpo dell'email uno o più spazi vuoti visto che questi sarebbero considerati (giustamente) caratteri di stampa a tutti gli effetti e la pagina, anche se bianca, inviata comunque.

Per far fronte ad esigenze di questo tipo è possibile metter da parte faxmail e cercare una soluzione gateway diversa.
Il sito e la mailing list di HylaFAX sono fonti preziose di documentazione e di spunti interessanti sia per quanto riguarda le configurazioni di base che per quanto riguarda quelle più avanzate.
Il buon Lee Howard ("papà putativo" di HylaFAX), proprio sul sito ufficiale, ha pubblicato uno script dimostrativo (mailtosendfax) riguardo all'implementazione di un mail2fax gateway che si appoggi a sendfax piuttosto che a faxmail.

Lo schema logico di funzionamento è il medesimo solo che l'email non è passata a faxmail bensì direttamente allo script:

fax  unix  -       n       n       -       1       pipe
  flags=        user=fax argv=/path/di/mail2sendfax.sh

Oggi quello script si rivela obsoleto e in parecchi casi (ad esempio per quanto riguarda i sistemi Debian più recenti, dall'avvento di Squeeze in poi) non più funzionante.
Continua certamente a costituire una eccellente "fonte d'ispirazione" ma, nella sua scrittura originaria, ha bisogno di un sostanzioso makeup perchè possa ritornare a funzionare come si deve.

La variabile $RANDOM

Così come pubblicato sul sito di HylaFAX lo script si basa anzitutto sulla creazione di una directory temporanea avente nome random per ogni job interessato (si parla ovviamente di fax in uscita).
Per impostare il nome di questa directory ci si avvale della variabile $RANDOM che in bash è una variabile d'ambiente che genera un numero casuale.
Il problema deriva dal fatto che la shell invocata dallo script non è bash ma sh...
Da Squeeze in poi /bin/sh non è altro che un link simbolico alla shell dash e in dash la variabile $RANDOM non esiste.
La conseguenza è che, lanciando lo script così com'è, nessuna directory dal nome random viene creata, al suo posto sempre e solo una directory dal nome /tmp/faxtmp. il cui contenuto, ovviamente, può venire sovrascritto in qualsiasi momento (si pensi a cosa succederebbe nel caso di un inoltro di poco successivo).
Si rivela necessario, quindi, o continuare ad usare la variabile $RANDOM invocando lo script in una shell bash (sostituendo lo sha-bang #!/bin/sh con #!/bin/bash), oppure generare in altro modo il numero random e quindi il nome della directory da creare.
Ad esempio:

RANDOMFAX=/tmp/faxtmp.$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d" ")

Split dell'email

Dopo la creazione della directory temporanea, lo script prevede lo split dell'email ricevuta in pipe da Postfix, l'estrazione di alcune variabili necessarie all'invio (ad esempio il numero telefonico del destinatario) ed infine l'invocazione di sendfax, incaricato del delivery vero e proprio.
Lo split dell'email è demandato a metamail che però, a causa di un bug, è stato rimosso già da un pò dai repository di Debian.
Al posto di metamail si possono utilizzare altri strumenti che espletano le stesse funzioni, ad esempio mpack, sostituendo l'istruzione metamail con qualcosa del genere:

munpack -C $RANDOMFAX _message_ > /dev/null 2>&

Il messaggio verrà splittato, all'interno della directory $RANDOMFAX, nei suoi allegati e in un file avente estensione .desc (contenente indirizzo mittente, indirizzo destinatario, oggetto, corpo e firma dell'email originaria).

Spedizione via fax dei soli allegati di posta

Una volta splittato il messaggio email, basterà eliminare ciò che non serve:

rm -f $RANDOMFAX/_message_
for f in $RANDOMFAX/*; do if ; then rm -f $f; fi; done

ed invocare sendfax passandogli tutto ciò che rimane all'interno della directory temporanea, ovvero... gli allegati:

sendfax -R -f "$FROMPATH" -n -d "$TONUMBER" $RANDOMFAX/*

(le variabili $FROMPATH e $TONUMBER, estratte dal messaggio prima della sua eliminazione, sono quelle relative rispettivamente a mittente e destinatario)

Invio fax a destinatari multipli

Il problema risiede nell'estrazione dei destinatari dagli header delle email.
Essi infatti si presentano così:

Received: from debian.localnet (unknown )
    by testfax.some.domain (Postfix) with ESMTPS id 4F14A3605D2;
    Tue, 13 Nov 2012 08:12:11 +0100 (CET)
From: someuser@testfax.some.domain
Organization: Test Org
To: 095222222@fax,
 095333333@fax,
 095444444@fax
Subject: This is the first test
Date: Tue, 13 Nov 2012 08:45:16 +0100
User-Agent: KMail/1.13.5 (Linux/2.6.32-5-686; KDE/4.4.5; i686; ; )
MIME-Version: 1.0
...

Filtrare i destinatari così come suggerito nello script non va bene perchè l'istruzione:

grep -e "^to:" -i $RANDOMFAX/_message_ | sed q

restituirebbe soltanto la stringa:

To: 095222222@fax,

Tutti gli altri destinatari verrebbero ignorati con la conseguenza, in questo caso, di istruire sendfax non di spedire un messaggio ai tre numeri 095222222, 095333333 e 095444444, bensì di spedire un messaggio per tre volte a 095222222.

Una soluzione potrebbe essere quella di "forzare" l'immissione di un parametro "univoco" relativo ad ogni destinatario negli header dell'email e di estrarre da questo il numero del destinatario.
In questo caso è sufficiente impostare il flag D nel service di /etc/postfix/master.cf che diventerebbe:

fax  unix  -       n       n       -       1       pipe
  flags=D        user=fax argv=/path/dello/script

Nel man di Postfix si legge:

...
       flags=BDFORXhqu.> (optional)
              Optional  message  processing  flags. By default, a
              message is copied unchanged.

              

              D      Prepend  a "Delivered-To: recipient" message
                     header with the envelope recipient  address.
                     Note: for this to work, the transport_desti-
                     nation_recipient_limit must be 1  (see  SIN-
                     GLE-RECIPIENT DELIVERY above for details).
...

Quel flag, quindi, farà in modo che in cima agli headers di ogni email da passare allo script venga inserita l'informazione relativa all'indirizzo di delivery:

Delivered-To: 095222222@fax
Received: from debian.localnet (unknown )
        by testfax.some.domain (Postfix) with ESMTPS id 95C643603DF;
        Wed, 14 Nov 2012 15:01:09 +0100 (CET)
From: someuser@testfax.some.domain
...

A quel punto, modificando le istruzioni grep relative al destinatario da

TOLINE=`grep -e "^to:" -i $RANDOMFAX/_message_ | sed q`
TONUMBER=`echo $TOLINE| sed -e 's/^o://g' -e 's/*\(.**\)@.*/\1/'`

a

TOLINE=`grep -e "^Delivered-To:" -i $RANDOMFAX/_message_ | sed q`
TONUMBER=`echo $TOLINE| sed -e 's/^Delivered-To://g' -e 's/*\(.**\)@.*/\1/'`

per ognuna delle email passate in pipe allo script si riuscirà ad estrarre dagli header il corretto riferimento per l'inoltro del fax.

È la somma che fa il totale

Un esempio di script, sicuramente scarno ma comprendente tutte le modifiche discusse fino adesso, potrebbe essere:

#!/bin/sh

# mail2fax reloaded
# 
# This script allows you to send faxes through your HylaFAX box.
# It uses mpack to strip out email attachments and sendfax to do
# the fax delivery.

### Temporary dir stuff
RANDOMFAX=/tmp/faxtmp.$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d" ")

if ; then mkdir $RANDOMFAX; fi
rm -f $RANDOMFAX/*

### Some makeup (maybe not necessary anymore)
sed 's/multipart\/alternative/multipart\/mixed/' > $RANDOMFAX/_message_

### Extract variables from _message_
TOLINE=`grep -e "^Delivered-To:" -i $RANDOMFAX/_message_ | sed q`
TONUMBER=`echo $TOLINE| sed -e 's/^Delivered-To://g' -e 's/*\(.**\)@.*/\1/'`
FROMLINE=`grep -e "^from:" -i $RANDOMFAX/_message_ | sed q`
FROMPATH=`echo $FROMLINE | sed -e 's/^rom:*//g' -e 's/"//g'`

### Split message
munpack -C $RANDOMFAX _message_ > /dev/null 2>&1

### Delete all but the attachments
rm -f $RANDOMFAX/_message_
for f in $RANDOMFAX/*; do if ; then rm -f $f; fi; done

### Delivery time!
sendfax -R -f "$FROMPATH" -n -d "$TONUMBER" $RANDOMFAX/*

### Remove temporary dir
rm -rf $RANDOMFAX

È evidente che, come indicato da Lee Howard, la flessibilità di una soluzione del genere è davvero notevole.
Che si vogliano implementare funzionalità di logging, che si vogliano passare i parametri necessari ad un'eventuale cover page, etc., il fortunello amministratore di sistema può plasmare facilmente (o riscrivere del tutto) lo script secondo necessità.
Insomma, c'è soltanto da sbizzarirsi. smiley

L'obiettivo di questo post è solo quello di indicare dei semplici correttivi da apportare allo script di Lee visto che, nella sua stesura originale, oggi non si rivela più funzionante.

My 2 hylacents!