summaryrefslogtreecommitdiff
path: root/document.rest.txt
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2009-04-16 20:42:26 +0200
committerdakkar <dakkar@thenautilus.net>2009-04-16 20:42:26 +0200
commit4d35d00f7766272c3d8da1532003fc9caa160717 (patch)
tree8a1bfe3de8577f2a3be90a440bcddbee9e6d6d8a /document.rest.txt
downloadMaildirIndexer-4d35d00f7766272c3d8da1532003fc9caa160717.tar.gz
MaildirIndexer-4d35d00f7766272c3d8da1532003fc9caa160717.tar.bz2
MaildirIndexer-4d35d00f7766272c3d8da1532003fc9caa160717.zip
first version
Diffstat (limited to 'document.rest.txt')
-rw-r--r--document.rest.txt209
1 files changed, 209 insertions, 0 deletions
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