Archiviatore di “e-mail letta”
Premessa
A parte lo spam evidente, molto del quale oramai viene filtrato direttamente dai mailserver delle mie caselle “offshore”, tendo a tenere i messaggi di posta “vecchi” per motivi storici ed affettivi: a volte, girovagando per i folder della posta, leggere un vecchi(ssim)o messaggio in cui “Tizio ti scriveva di…” è bello… fa pensare al passato con piacere, fa ricordare dei corrispondenti di una volta, un pò come nei tempi andati si tenevano le vecchie lettere.
Ma a parte l’incipit romantico… quando la casella di posta “inbound” inizia a riempirsi, anche il client di posta inizia a rallentare, aprendola. In parole povere: arrivato a più di 13.000 messaggi decisi di “trovare un rimedio” senza però perder nulla. Nella pratica il “rimedio” è consistito nello spostare le mail dalla “inbound” a folder interni, in modo da alleggerire la casella d’arrivo lasciandovi dentro unicamente i messaggi non letti (e quelli che, per qualche motivo, decido di non filtrare).
Quest’attività la può fare qualsiasi client di posta, a me però l’idea non piaceva: architetturalmente considero il client di posta un “lettore”, ed un lettore “legge” la posta (ok, permette anche di “rispondere”) ma non la dovrebbe “gestire” (o, in altre parole, dovrebbe essere “trasparente” rispetto alle e-mail).
Per gestire la posta mi pare più sensato lavorare server-side, possibilmente in modo automatico: in questo modo ci si svincola dalle particolarità dei client (in genere ognuno ha il proprio modo di “trattare la posta”, le proprie “regole”… guarda caso mai compatibili tra di loro) e, volendo, è possibile passare da un client all’altro senza pregiudicare il lavoro di archiviazione, un pò come è possibile cambiare browser web senza che i siti debbano essere modificati (almeno in teoria ;-)).
Panoramica
Nel mondo “degli utenti” in genere la mail viene gestita tutta “lato utente” (o client-side): il client (spesso Outlook) si occupa di ricevere i messaggi dalla(e) casella(e) dell’utente, poste sui server dei vari Provider (es. Libero.it, Tiscali.it, Tin.it, ecc.), la inserisce nella “casella della posta in arrivo“, gestisce i vari “flag” (letta, non letta, risposta, inoltrata, ecc.), permette di cancellare messaggi o di spostarli in altri folder… il tutto manualmente oppure, nel migliore dei casi, gestito da apposite “regole di gestione della posta” che, come dicevo, sono particolari per ogni client: le regole di OutLook non si possono utilizzare (direttamente) in Lotus Notes, in Thunderbird o in Pegasus.
Il risultato pratico di tutto ciò è che, volendo cambiare client di posta, si deve solo sperare che quello “nuovo” abbia dei “filtri d’importazione” che permettano di importare la posta esistente dalle cartelle attuali alle nuove (perchè lasciarle stare pare sia escluso: ognuno si gestisce le proprie in modo “proprietario”), i filtri, le regole e tutte le personalizzazioni… a volte và bene ed a volte no (ed in questo caso c’è un grosso lavoro da fare, per “migrare” al nuovo sistema… a volte l’utente si scoraggia e rimane all’attuale). In genere il client più famoso fa “le sue cose” mentre quelli meno famosi hanno una serie di convertitori per permettere il passaggio, il più possibile indolore, dal più famoso a loro stessi… ma “il più possibile” non significa “totalmente indolore”.
La visione client stand-alone
In realtà la domanda è: visto che l’e-mail ha un formato standard (definito per la maggior parte in RFC822 e svariati altri) perchè il client non si limita a leggerla (ed alle altre due attività tipiche di un client: rispondere, inoltrare)? La risposta è semplice: in generale, nel mondo, esistono sistemi “monoblocco” (es. Windows) che non sono pensati come “client/server” ma piuttosto come “client stand-alone”.
In generale infatti un sistema unix/linux è pensato come “multi-utente” (anche quando ha un solo utente: è la filosofia del sistema che conta) e le varie attività avvengono “come se si stesse lavorando su un host da (potenziali) n-mila utenti”: l’unica differenza è la scala delle varie azioni, non il modo.
I sistemi “stand-alone” invece danno per scontato che, anche qualora più utenti usino il sistema, l’utilizzo avverrà sempre “uno alla volta”… in pratica sono “single-user by design”.
Questo porta a tutta una serie di scelte, negli strumenti (browser web, e-mail, gestione dei file, ecc.) che non tengono conto delle logiche “lato server”: nella pratica ogni client penserà di “essere da solo” a dover fare tutto il lavoro… da qui il funzionamento di OutLook e compagnia bella.
La visione lato-server
Nel mondo dei server (quasi) tutte le “azioni” non son pensate per un uso interattivo bensì per la trattazione da parte di programmi, spesso messi in sequenza: in genere l’utente (a volte visto come utOnto: un “male necessario” -__-”) interviene nella catena tramite appositi “wrapper” che “si agganciano” al processo in corso il quale può benissimo funzionare senza il minimo intervento dello stesso (anzi, molti BOFH sono concordi nell’affermare che “senza gli utenti le cose funzionerebbero (molto) meglio” :-DDD).
Per quanto riguarda la posta, ad esempio, una delle possibilità è che un primo prodotto (ad es. FetchMail) prelevi la posta da una o più caselle remote, la passi ad un secondo prodotto (lo scanner anti-spam, ad es. SpamAssassin) che, dopo averla verificata, la passa ad un terzo (scanner antivirus) che la passa ad un quarto… e via così finchè l’ultimo della catena la posiziona in quel che viene chiamato “inbox” (inbound (e-mail) box), posto generalmente in una zona del filesystem non-user-oriented (di solito in /var/mail/ si trovano tutte le “inbox” degli utenti presenti sul sistema, un file per user, con il nome dell’utente: ad es. l’inbox dell’utente root si chiamerà “root”).
| / | (”root” directory o “radice”) | |
| |— | /bin/ | —. |
| |— | /boot/ | \ |
| |— | /dev/ | (varie directories di sistema) |
| : | / | |
| |— | /etc/ | —’ |
| |— | /home/ | (directory “degli utenti”, v. in seguito) |
| |— | /lib/ | |
| : | ||
| |— | /usr/ | |
| ‘— | /var/ | (directory dei file “variabili”) |
| (Il filesystem di un server Linux) |
| : | ||||
| /var/ | ||||
| ‘— | /mail/ | |||
| |— | mario | |||
| |— | luigi | |||
| : | ||||
| ‘— | root | |||
| : |
| (La directory in cui la posta “arriva”: inbox) |
Ogni utente “gestisce” poi la sua posta tramite un set di folders posti, generalmente, nella subdirectory Mail della sua “home directory”.
Nella pratica, il server ha una directory (/home/) in cui si trovano tutte le directory particolari dei singoli utenti, quindi /home/mario/ contiene “tutti i file di Mario“, come /home/luigi/ conterrà quelli di Luigi: fogli di calcolo, documenti di testo, memo, immagini… diciamo che “tutto quello che non riguarda ‘tutto il server‘ o ‘tutti gli utenti‘ generalmente viene inserito nelle ‘home’ degli utenti singoli“: questo porta molta pulizia, nel sistema, distinguendo tra “quel che è server” da “quel che non lo è” (per le utilities di gestione del sistema, fare questa distinzione è spesso importante).
In ogni home, per gli utenti che hanno e-mail sul sistema, viene quindi creata una directory Mail (o anche mail o maildir, a seconda del sistema) ove viene raggruppata “tutta la posta dell’utente“.
| : | |||||
| /home/ | |||||
| |— | /mario/ | ||||
| |— | /Mail/ | ||||
| |— | /vacanze/ | ||||
| |— | mare | ||||
| ‘— | montagna | ||||
| ‘— | /fatture/ | ||||
| |— | acqua-gas | ||||
| ‘— | luce | ||||
| ‘— | /luigi/ | ||||
| |— | /Mail/ | ||||
| |— | /lavoro/ | ||||
| |— | riunioni | ||||
| ‘— | progetti | ||||
| ‘— | /sport/ | ||||
| |— | sci | ||||
| ‘— | nuoto | ||||
| : |
| (Le directory degli utenti, home, e la posta, Mail) |
Gestione dell’e-mail
Come già accennavo, per gestire la posta i sistemi Linux usano (tra i tanti) ProcMail che, leggendo le sue “regole di gestione” da vari file (non solo /etc/procmail.conf), “filtra” la posta in arrivo adottando dei “comportamenti”: ad es. “butta via il messaggio“, oppure “inoltra il messaggio su $tal_argomento all’utente $utente_interessato“, o ancora “lancia $programma per gestire $messaggio” e tanto, tanto altro.
Sfruttando le meccaniche server-side possiamo quindi, fra le altre cose, automatizzare l’archiviazione delle mail “lette” per tutti gli utenti del server.
Nella pratica agiremo come segue…
L’archiviazione
Per archiviare le mail presenti nell’inbox basta utilizzare:
L’archiviazione: 1 - lo script bash
Questo script, dopo la definizione di un paio di funzioni di utility (debugmsg e debugenter), svolge un primo step di verifica dei prerequisiti controllando:
a) se è stato passato uno “username” (necessario per sapere a quale utente dev’essere archiviata la posta)
b) se sono presenti le utilities di sistema fondamentali (richieste: formail, procmail) e se sono presenti il file di configurazione di procmail (i filtri per l’archiviazione) e la casella inbox dell’utente (in /var/mail/)
c) se son presenti le utilities opzionali: logger, from e wc, in caso di assenza non si ferma il processo ma verrà meno la funzionalità di “logging”. In pratica, se tutte le utilities son presenti, l’archiviatore lascerà nel log di sistema una traccia del proprio lavoro fornendo il numero delle mail trovate in inbox prima e dopo l’archiviazione: siccome non è una funzionalità vitale, in caso di assenza di una delle utilities opzionali l’archiviatore lavora ugualmente ma non esegue alcun report.
Se i controlli hanno esito positivo, verrà innanzi a tutto “messa offline” la inbox dell’utente, per evitare che il processo di archiviazione interferisca con quello (sempre temporizzato) di ricezione della mail: in pratica si “sposta” la inbox su un file temporaneo e la si ricrea ex novo, con gli stessi permessi, per la ricezione della mail che può avvenire durante il processo di analisi della inbox temporanea (e che potrebbe anche esser lungo, a seconda del sistema hardware impiegato).
A questo punto parte l’analisi/archiviazione vera e propria: tramite formail viene passata a procmail tutta la inbox temporanea. Procmail userà, per l’analisi, il file custom di regole presente nella home dell’utente (e di proprietà di root, dato che altrimenti Procmail si “insospettisce”, venendo eseguito come root su file/directories non-di-root)
Il file di regole di procmail ha una struttura “particolare”, adatta al task di archiviazione (per dire che non è il “solito” file procmail.conf che si trova in /etc/ nè il file .forward che si trova nella home dell’utente), ma lo vedremo in dettaglio dopo aver finito d’analizzare lo script (v. sotto). Basti sapere, per il momento, che verificando il flag “R” (come riportato dall’RFC2076) Procmail è in grado di controllare se una mail è stata letta o meno: tramite il file di filtro otteniamo che ogni mail “non letta” venga inserita in un file $userid.new nelle directory dove si trova la inbox (da qui la necessità di eseguire procmail da root).
In seguito all’analisi (ed archiviazione delle mail nei folder “interni” al Mail dell’utente) si tratta di “ricompattare” la inbox unendo alle (possibili) nuove mail arrivate quelle “non lette” (presenti nel file $userid.new) e quelle “non filtrate” (presenti nel file $userid.other): in pratica spostiamo (temporaneamente) rapidamente la inbox in inbox.new2, quindi creiamo una “nuova” inbox copiandoci dentro per prime le mail non lette (da $userid.new), aggiungendo la nuova mail (da $userid.new2) ed infine inserendovi le mail non archiviate (da $userid.other).
Creata la nuova inbox è il momento di far pulizia dei file “nuovi” ($userid.new, $userid.new2 e $userid.other) e della inbox pre-archiviazione ($userid.tmp).
Avendo quindi registrato le mail presenti prima e quelle attuali, se le utilities opzionali sono presenti si potrà scrivere nel log di sistema l’esito del lavoro svolto: archiving process results: NNN initial, MMM current, AAA archived.
Il codice dello script si trova QUI.
(nota: è possibile che il giro inbox -> inbox.new2, inbox.new + inbox.new2 + inbox.other -> inbox sia eccessivamente contorto: è probabile che basti copiare nella “nuova inbox” i file inbox.new ed inbox.other ottimizzando lievemente la parte finale del processo)
L’archiviazione: 2 - il file di configurazione procmailrc
Come scrivevo poc’anzi, dovendo spostare, copiare e rimuovere file, è necessario agire tramite uno script: per questo motivo non possiamo usare, per far girare procmail, i file standard /etc/procmail.conf (system-wide) o /home/$userid/.forward (user-wide) perchè mancherebbe l’azione dello script sulle inbox, inoltre l’archiviazione agirebbe ogni volta che viene richiamato procmail, e questo non lo si vuole.
(nota: tramite procmail è possibile eseguire script, per l’automazione di cui sopra, ma mi pare una strada più complessa di quella, tutto sommato semplice e lineare, che ho adottato tramite il “task di archiviazione”, tutto qui, oltre al fatto che stiamo agendo su una (o più) inbox “offline” in modo batch)
Ogni utente del sistema che vorrà fruire del processo di archiviazione automatica dovrà avere, nella sua home directory, un file procmailrc contenente le seguenti direttive/filtro:
1) la prima direttiva (che segue le definizioni di ambiente per procmail) definisce la creazione di un file $userid.new in cui verranno poste tutte le mail non lette.
2) nella seconda sezione si trovano i filtri per l’archiviazione vera e propria: prima quelli per gli alias (se ne usate) e poi quelli per l’account principale: tutti i messaggi che corrispondono ai criteri di selezione verranno copiati nei folder definiti dai filtri e presenti nella directory Mail dell’utente: se un file non dovesse esser presente procmail lo crea al momento (nota: siccome il task gira come root, è preferibile che i file in Mail siano già presenti, altrimenti verranno creati con le permission di root e l’utente non li potrà leggere: quando create/modificate i filtri fate attenzione a questo particolare).
3) l’ultima sezione del file è fondamentale per evitare di perdere le mail che, anche se già lette, non corrispondono ad alcun criterio di filtro: queste verranno copiate in un file $userid.other il cui contenuto, tramite lo script, verrà poi reinserito in coda all’inbox.
Un sample del file procmailrc (da configurare ed inserire in /home/$userid/) si trova QUI.
L’archiviazione: 3 - la entry in crontab
Perchè tutto il processo avvenga in modo automatico si sfrutta Cron: inserendo una apposita entry nel suo file di configurazione (/etc/crontab) si chiede a Cron di eseguire, ad una data ora (o con la periodicità voluta), lo script di archiviazione.
Tenuto conto del tipo di lavoro da eseguire e della mole di dati da archiviare (poche mail al giorno, a volte nessuna) ho deciso di far lanciare l’archiviazione una sola volta al giorno, di mattina alle 7:30, quando (in teoria) gli utenti sono a dormire e/o difficilmente stanno guardando la propria mail.
Basta inserire in crontab:
# 20090307=LC archive user's mail everyday at 07:30
30 7 * * * root /usr/local/sbin/readmailarch lcoianiz
La prima riga è un commento e non viene letta da Cron, la seconda è il comando.
Si può notare come, in questo momento, io faccia eseguire l’archiviazione unicamente per un utente: avendo più utenti (realmente) attivi sul sistema è possibile (1) inserire ulteriori entries in crontab, per avere un’esecuzione parallela dei task di archiviazione (nessun file conflitta: i task possono girare in concorrenza) oppure (2) modificare il (singolo) task di archiviazione per effettuare la sua azione “per tutti gli utenti che hanno inbox presenti in /var/mail/” (e che hanno file procmailrc in /home/$userid/).
Dovendo scegliere, preferirei la prima alternativa: oltre ad aver un maggior controllo sugli utenti sui quali avviene l’archiviazione, si agisce anche in parallelo (e, cosa da non disprezzare, lo script di archiviazione va già bene com’è ;-)).
L’archiviazione: 4 - le utilities del sistema operativo
Di base, il task di archiviazione richiede la presenza almeno di:
se queste non vengono trovate il task si ferma e, se eseguito manualmente, segnala l’errore.
In aggiunta è raccomandato installare:
In linea di principio l’archiviazione può esser lanciata in qualsiasi momento, per questo motivo, prima di inserire la configurazione in crontab sarebbe meglio effettuare qualche prova lanciando readmailarch da root e vedendone gli esiti.
In fase di test è altresì consigliato abilitare le funzionalità di debug che, oltre a visualizzare i messaggi relativi alle attività effettuate, EVITANO di cancellare automaticamente il file $userid.tmp (la inbox originale) alla fine del processo: per sicurezza è meglio che, constatato il buon funzionamento del task, la cancellazione avvenga manualmente.
(si dà comunque per scontato che, dato il tipo di dati trattati, venga eseguito un backup sia della inbox che della propria directory Mail prima di iniziare qualsiasi test).
Qualsiasi feedback su eventuali problemi, consigli o altro (magari gli improperi teneteli per voi :-P) sarà il benvenuto: luca_AT_coianiz.it
Buona archiviazione ;-)




