From 4d35d00f7766272c3d8da1532003fc9caa160717 Mon Sep 17 00:00:00 2001 From: dakkar Date: Thu, 16 Apr 2009 20:42:26 +0200 Subject: first version --- document.rest.txt | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 document.rest.txt (limited to 'document.rest.txt') diff --git a/document.rest.txt b/document.rest.txt new file mode 100644 index 0000000..2fabe97 --- /dev/null +++ b/document.rest.txt @@ -0,0 +1,209 @@ +================================================================== + Un sistema automatico di incartellamento della posta elettronica +================================================================== + +ovvero, come io gestisco la mia posta elettronica +================================================= + +:Author: dakkar@thenautilus.net +:Date: 2009-01-10 + +Il mio archivio di posta elettronica conta, al momento, 74711 +messaggi. Sono iscritto a circa 40 mailing list (per la maggior parte +di esse non tengo copia di tutti i messaggi). Come faccio a +raccapezzarmici? + +Innanzi tutto, ogni mailing list ha una sua cartella, e |procmail|_ è +sufficiente per gestire questa parte; ad esempio, per mandare nella +cartella apposita i messaggi della lista `mongers`:: + + # mongers + :0: + * (^From|^TO|List-Id:).*mongers[@.](lists\.)?perl\.it + .mail.Perl.mongers/ + +Come potreste notare, se aveste perso un eccesso di tempo a studiare +|procmail|, la mia posta è archiviata in formato |maildir|_. + +Altre regole |procmail| mettono in cartelle apposite i messaggi +provenienti da mittenti particolari (gente che conosco, negozi, etc). + +Accedo alla posta tramite |dovecot|_ (server) e |claws|_ (client). + +*Non* uso una cartella "messaggi inviati": |claws| è configurato in +modo da salvare i messaggi che scrivo nella cartella che "sto +guardando" [#savehere]_. In pratica, il risultato è che le mie +risposte stanno nella stessa cartella dei messaggi a cui +rispondono. In questo modo ho sempre i thread *interamente* visibili. + +.. [#savehere] Tasto destro sulla radice delle cartelle (nella lista + cartelle), "Properties…", sezione "Compose", marcare "Save copy of + outgoing messages to this folder instead of Sent", marcare anche il + chekcbox a destra ("Apply to subfolders"), pulsante "Apply". + +A questo punto, se le mie cartelle fossero tutte e sole quelle in cui +|procmail| mette i messaggi, sarei a posto. "Peccato" che mi servano +anche cartelle di altro genere: ad esempio, per argomento. I messaggi +finiscono in queste cartelle solo perché ce li sposto io, manualmente, +dal client. Per completare l'automatizzazione del tutto, manca un +componente: un sistema per cui un messaggio di risposta che arriva, +finisca nella stessa cartella in cui sta il messaggio a cui risponde, +anche se quest'ultimo è stato spostato a mano. + +Incartellamento delle risposte +------------------------------ + +Come dice la |rfc2822|_, se un messaggio è una risposta, dovrà avere +un campo (nell'header) ``In-Reply-To:`` il cui valore sia il "message id" del +messaggio a cui sta rispondendo. Inoltre, il campo ``References:`` +contiene i "message id" di tutta una sequenza di messaggi del thread +[#thread]_. + +.. [#thread] Sì, lo so che è un po' più complicato di così, ma questa + è un'approssimazione sufficiente per i nostri fini. Chi vuole i + dettagli può leggersi la |rfc2822|_ e |jwz|_. + +Di conseguenza, per sapere in quale cartella mettere un messaggio, +basta trovare in quale cartella stia il messaggio il cui campo +``Message-ID:`` abbia lo stesso valore di uno dei "message id" +riferiti nel messaggio in arrivo. Ovviamente, se il messaggio in +arrivo riferisce più di un messaggio, potremmo avere una situazione +ambigua; per fortuna, per come sono usati di solito i campi dei +riferimenti, l'ambiguità si risolve esaminando prima il valore in +``In-Reply-To:``, poi quelli in ``References:`` dall'ultimo al primo +(al solito, i dettagli sono nella |rfc2822|). Che esista un solo +messaggio con un dato "message id" è vero a meno che io non abbia due +copie di uno stesso messaggio; possiamo ignorare il problema. + +Un'implementazione banale consisterebbe in poco più che un ``grep`` +ricorsivo. Funziona, ma è un po' lenta: rischia di impiegare svariati +secondi, e andrebbe eseguita per ciascun messaggio che arriva. + +Un metodo più furbo sta nel tenersi un indice che mappi i "message id" +alle cartelle. Per tenerlo aggiornato, però, dobbiamo tenere conto che +nuovi messaggi vengono aggiunti (o perché arrivano, o perché sono +copie dei messaggi che io invio) e quelli esistenti spostati. Ci serve +un modo per intercettare questi cambiamenti, e aggiornare l'indice. + +Il problema di notare modifiche in una cartella è tanto comune (serve, +ad esempio, ai file manager) che Linux fornisce una soluzione a +livello di kernel: |inotify|_. Siccome usarlo non è esattamente +banale, mi sono appoggiato a |inotify-tools|_. + +A questo punto ho tutti i pezzi che servono per scrivere il server che +gestisca l'indice dei messaggi: + +- all'inizio, il server si scandisce l'archivio di posta e si segna in + quale cartella sta ciascun "message id" +- nel frattempo, usa ``inotifywait`` (parte di |inotify-tools|) per + tenere traccia delle modifiche +- il server ascolta su una socket, e quando gli viene inviato l'header + di un messaggio, ne estrae i campi dei riferimenti, e risponde con + il nome della cartella in cui deve andare questo messaggio (o nulla, + se il messaggio non è una risposta, o non abbiamo il messaggio cui + risponde) + +Avendo questo, si scrive un semplice client:: + + #!/bin/bash + netcat localhost 9000 | tr -d '\015\012' + +e due regole |procmail|:: + + :0 hi + AUTOFOLDER=| ~/bin/find_folder + + :0 + * AUTOFOLDER ?? ^\.mail\. + $AUTOFOLDER/ + +e i messaggi finiscono automaticamente dove devono. + +.. warning:: |procmail| e ``Out of memory`` + + Alcune versioni di |procmail| (di sicuro la 3.22-r7 distribuita in + Gentoo) hanno un bug che causa un'allocazione esagerata di memoria + quando si tenta di catturare l'output di un programma in una + variabile (come nelle regole precedenti). Se vi capita, controllate + se la vostra distribuzione ha una versione corretta; altrimenti, + potete sempre applicare la patch_. + +Come è fatto il server +---------------------- + +Dalla descrizione del server si può notare come esso debba occuparsi +di svariate funzioni contemporaneamente: scandire l'archivio, ricevere +le notifiche di cambiamenti, rispondere alle richieste del client. In +Perl ci sono sostanzialmente due strategie per affrontare il problema: + +* gestione asincrona degli eventi (ad esempio con |poe|_) +* multi-threading + +A me i thread, pure con i piccoli problemi che hanno in Perl, +risultano più "naturali". Ho perciò strutturato il server in questo +modo: + +- un thread riceve le notifiche da ``inotifywait``, e aggiorna l'indice +- un thread scandisce l'archivio, e aggiorna l'indice (questo thread + termina una volta scandito tutto l'indice) +- il thread principale si sospende sulla socket +- quando arriva una richiesta sulla socket, un nuovo thread viene + creato per servirla + +Il programma_ non è molto complesso. Il grosso del lavoro è fatto da +|email-simple|_ (per estrarre i campi dai messaggi) e |file-next|_ +(per scandire l'archivio). + +Un paio di note sulle strutture dati: per gestire correttamente +aggiunte, rimozioni e spostamenti, e non confondersi con le manovre +che fa |dovecot| [#manovre]_, uso due hash: ``%files2id`` mappa dalla +coppia mailbox-nomefile al "message id" (ovvero, +``$files2id{$mailbox}->{$file}=$id``); ``%id2mailbox`` mappa da un +"message id" a una *lista* di mailbox. Sì, una lista: questo sia per +non avere errori in caso di messaggi duplicati, sia per le menzionate +manovre di |dovecot|. Il server restituisce sempre l'ultima mailbox +nella lista, che è tenuta in ordine cronologico. + +Inoltre, siccome queste strutture sono lette e scritte da thread +diversi, è necessario assicurare la mutua esclusione degli accessi +tramite lock. + +.. [#manovre] Ad esempio, per spostare un messaggio, prima lo duplica + nella cartella di destinazione, poi lo cancella da quella di + origine. + +Altro materiale +--------------- + +* il programma_, colorato da |ppi| +* il sorgente_ nudo e crudo +* il pod_ + +.. |procmail| replace:: ``procmail`` +.. _procmail: http://www.procmail.org/ +.. |maildir| replace:: ``MailDir++`` +.. _maildir: http://www.inter7.com/courierimap/README.maildirquota.html +.. |dovecot| replace:: ``dovecot`` +.. _dovecot: http://www.dovecot.org/ +.. |claws| replace:: `Claws Mail` +.. _claws: http://www.claws-mail.org/ +.. |rfc2822| replace:: `RFC` 2822 +.. _rfc2822: http://www.faqs.org/rfcs/rfc2822.html +.. |jwz| replace:: l'algoritmo di threading di Jamie Zawinski +.. _jwz: http://www.jwz.org/doc/threading.html +.. |inotify| replace:: ``inotify`` +.. _inotify: http://en.wikipedia.org/wiki/Inotify +.. |inotify-tools| replace:: ``inotify-tools`` +.. _`inotify-tools`: http://inotify-tools.sourceforge.net/ +.. |poe| replace:: ``POE`` +.. _poe: http://search.cpan.org/dist/POE/ +.. |email-simple| replace:: ``Email::Simple`` +.. _`email-simple`: http://search.cpan.org/dist/Email-Simple/ +.. |file-next| replace:: ``File::Next`` +.. _`file-next`: http://search.cpan.org/dist/File-Next/ +.. |ppi| replace:: ``PPI::HTML`` +.. _ppi: http://search.cpan.org/dist/PPI-HTML/ +.. _programma: maildir-indexer.pl.html +.. _sorgente: maildir-indexer.pl +.. _pod: maildir-indexer.pod.html +.. _patch: http://bugs.gentoo.org/200006 -- cgit v1.2.3