Un semplice gestore di Mailing List in Perl


Home Page | Commenti | Articoli | Faq | Documenti | Ricerca | Archivio | Storie dalla Sala Macchine | Contribuire | Imposta lingua:en it | Login/Register


Una mailing list e' il sistema piu' semplice per distribuire informazioni tra un gruppo di persone connesse dalla posta elettronica.

A differenza del Web consente a molteplici persone di interagire inviando messaggi di posta elettronica che vengono automaticamente re-inviati ad ogni membro della lista.

Esistono svariate mailing list, ed esistono innumerevoli gestori di mailing list, programmi piu' o meno complessi che consentono di aggiungere o rimuovere gli appartenenti dalla lista in modo interattivo, creare "archivi" di messaggi, ricercare nei messaggi precedenti eccetera eccetera.

Perche' mi sono occupato di mailing list? Perche' mi e' toccato farlo, in particolare perche' in questa gabbia di matti in cui mi trovo ne gestiamo svariate, ed ogni programma di gestione che mi riusciva di trovare aveva tutto meno quello che mi serviva.

Cioe' che volevo io:

Il problema e' che cio' che volevo io non si trova a meno di mettersi a modificare pesantemente quello che e' gia' fatto. Quindi ho deciso di mettermi al lavoro e risolvere il mio problema.

Ho cominciato quindi a pensare a cosa mi serviva ed a come ottenerlo.

Per prima cosa volevo un sistema che riconoscesse gli utenti locali indipendentemente dall'alias usato per inviare posta. Questo e' un problema mio particolare perche' questo branco di matti si diverte a cambiare Alias ogni due per tre, e quindi mi serve un sistema che non richiede di aggiungere ogni possibile alias all'elenco degli iscritti ma che sia in grado di individuare il "vero" nome dell'utente anche in base ad un'alias.

Come cavolo scopro se un'utente esiste o no? Semplice: si cerca nell'elenco degli utenti /etc/passwd e nell'elenco degli alias /etc/aliases.

Ma prima, devo individuare il nome da cercare. Questo viene fatto nella prima fase di elaborazione del messaggio.


if( m/^From: .*$/ ) {

	# 1.
	$from=$_;
	if(m/^From: [^<]*<([^@]*)@.*$/){
		$from =~ s/^From: [^<]*<([^@]*)@.*$/$1/;
	}
	if(m/^From: ([^@]*)@.*$/){
		$from =~ s/^From: ([^@]*)@.*$/$1/;
	}

	# 2.
	open(USER,"/etc/aliases") || 
		die("Can't access the alias list");

	USERS: while(<USER>) {
		chomp();
		if(m/$from/) {
			$real=$_;
			$real=~s/^[^:]*:([^\b]*)$/$1/;
			$real=~s/ //g;
		}
		last USERS if /$from/;
	}
	close(USER);

	# 3.
	if( $real eq "" ) {

		open(USER,"/etc/passwd") || die(
			"Can't open the user list\n");

		USERS: while(<USER>) {
			if(m/$from/) {
				$real=$_;
				$real=~s/^([^:]*):.*$/$1/;
			}
			last USERS if /$from/;
		}
		close(USER);
	}

	# 4.
	if( $real eq "" ) {
		print LOGFILE "$from is not a user here!\n";
		exit 0;
	}
}

Molto rapidamente:

  1. Recupero il mittente del messaggio
    Utilizzo due diverse regex qui' per gestire sia indirizzi del tipo From: "nome e cognome" che From: indirizzo@server, il primo tipo e' usato particolarmente da Outlook et similia.

  2. Cerco il nome nel file /etc/aliases
    E se trovato recupero il vero nome dell'utente, nel caso in cui non riesca a trovare il nome nel file aliases,

  3. Lo cerco in /etc/passwd
    In questo caso il nome e' gia' quello dell'utente e non mi serve di "trasformarlo" in nessun modo. In un modo o nell'altro arrivato a questo punto, o ho il nome oppure

  4. Non ho nessun utente locale,
    In questo caso scrivo un messaggio di errore nel log e termino.

Ovviamente il messaggio deve essere inviato ad un qualche alias che poi lo rigira al mio "gestore", ma io non voglio un gestore per ogni mailing list, io voglio un gestore unico che mi gestisca molteplici liste.

Per questo, mi serve di sapere a chi sto' scrivendo. Il passo successivo e' quindi cercare la parte To: del messaggio.


if( m/^To: .*$/ ) {

	# 1.
	$to=$_;
	$to=~s/^To: <+([^@]*)@.*$/$1/;
	$list=$list.$to.".lst";

	# 2.
	if(! -r $list) {
		print LOGFILE "There is no \"$list\" list here!\n";
		exit 0;
	} else {

		# 3.
		open(LIST,"$list") ||
			die("Can't open the list file $list\n");
		LIST: while(<LIST>) {
			chomp();
			if(m/$real/) {
				$ok=1;
			}
			last LIST if /$real/;
		}
		close(LIST);
	}
}

  1. recupero la parte "To"
    Qui' non ho bisogno di troppa sofisticazione perche' non mi risulta che ci siano mailer che usano tante sofisticherie nell'indirizzo di destinazione.

  2. costruisco il nome della lista aggiungendo un .lst al destinatario

  3. cerco nel file il nome del mittente
    qui' uso il "vero" nome dell'utente, e non l'eventuale Alias che e' usato per inviare la posta, in questo modo nel file della lista devo mettere solo il vero nome dell'utente e posso ignorare tutti gli alias che l'utente puo' avere.

Il Subject originale del messaggio deve essere salvato, cosi' come il corpo stesso del messaggio. Tutto il resto invece puo' essere tranquillamente buttato alle ortiche.


if(m/Subject: .*/) {
	$sub=$_;
	$sub=~s/Subject: (.*)$/$1/;
	next ALINE;
}
	
# save the body
if(m/^$/) {
	$add=1;
	next ALINE;
}
if($add == 1) { $orig[$line++]="$_\n"; }

Questo non mi sembra che richieda molte spiegazioni...

A questo punto non mi rimane altro che verificare se mittente e' valido ed inviare le mail ad i destinatari (scritti nel file della lista di cui ora so il nome).

Per spedire le mail ricorro ad un package Perl chiamato Mail::Sendmail. Questo per evitarmi un serio mal di testa ed un sacco di problemi con altri marchingegni strani.


open(LIST,"$list") || die("Can't open mailing list $list");
while(<LIST>) {
	chomp();
	%mail=(
		From => "$to\@$domain",
		To => "$_\@$domain",
		Subject => "$sub",
		Message => "@orig"
	);
	$mail{'X-Loop'}="Some";
	if( ! sendmail %mail) {
		print LOGFILE "Error sending mail to $_:
		$Mail::Sendmail::error!";
	}
}
close(LIST);

Qui' non c'e' molto da spiegare, leggo la lista degli appartenenti e per ogni utente creo una mail. Alla mail aggiungo un'header "X-Loop" in modo da poter riconoscere le mail successivamente.

Da notare, che qui' uso lo stesso nome della mailing list come mittente (From), in modo che una eventuale risposta alla mail ricevuta sia di nuovo inviata alla mailing list stessa.

In caso di errore scrivo un messaggio nel log.

Vi sono vari modi per richiamare questo accrocchio direttamente dal sistema di posta, ma purtroppo dipendono da quale server di posta si utilizza.

Se si utilizza qMail, la cosa si fa' molto semplice, in quanto basta creare un file ".qmail-nomelista" in $QMAIL_HOME/alias che richiami il gestore.


|/var/qmail/alias/lstman.pl

Come si puo' vedere ho chiamato il mio gestore "lstman.pl" e lo richiamo direttamente. In questo modo quando si invia una mail a nomelista@server, la mail viene processata direttamente dal gestore che puo' fare il suo lavoro.

Con altri mail server si possono utilizzare diversi sistemi, per esempio creare un'utente ad-hoc "nomelista" e poi mettere un bel file .procmailrc nella home dir dell'utente contenente la stessa riga. L'unica cosa da tenere in considerazione e' che la directory dove il file di log viene scritto deve essere scrivibile dall'utente che viene usato dal server di posta. Nel caso di qMail questo utente e' "alias".

Allo stesso modo tutti i file necessari devono essere leggibili dallo stesso utente.

Qui' potete scaricare il codice completo del gestore, completo meno il modulo Mail::Sendmail che trovate altrove (e' sempre meglio avere l'ultima versione).

Un paio di aggiunte sono state fatte: per esempio posso stabilire se accettare o meno soggetti nulli nelle mail ed il dominio deve essere configurato prima.

L'indirizzo di posta del mittente puo' diventare un problema, io ho verificato con Outlook Express ed Eudora, ma non ho idea di come si puo' comportare con altre cose.


I commenti sono aggiunti quando e soprattutto se ho il tempo di guardarli e dopo aver eliminato le cagate, spam, tentativi di phishing et similia. Quindi non trattenete il respiro.

Nessun messaggio this document does not accept new posts

Precedente Successivo

Davide Bianchi, lavora come Unix/Linux System Administrator presso una societa' di Hosting in Olanda.

Volete contribuire? Leggete come!.
 
 

Il presente sito e' frutto del sudore della mia fronte (e delle mie dita), se siete interessati a ripubblicare uno degli articoli, documenti o qualunque altra cosa presente in questo sito per cortesia datemene comunicazione (o all'autore dell'articolo se non sono io), cosi' il giorno che faccio delle aggiunte potro' avvisarvi e magari mandarvi il testo aggiornato.


Questo sito era composto con VIM, ora e' composto con VIM ed il famosissimo CMS FdT.

Questo sito non e' ottimizzato per la visione con nessun browser particolare, ne' richiede l'uso di font particolari o risoluzioni speciali. Siete liberi di vederlo come vi pare e piace, o come disse qualcuno: "Finalmente uno dei POCHI siti che ancora funzionano con IE5 dentro Windows 3.1".

Web Interoperability Pleadge Support This Project
Powered By Gojira