Mitgespielt (Linux-Magazin, Juli 2001)

Ein Skript zapft einen POP3-Mailserver an, fängt Emails aus einer Mailingliste ab und lässt sie einen Newsreader sortiert anzeigen.

Neulich gelang es mir doch glatt, ein paar Codezeilen per Vorschlag und Patch in die Perl-Distribution einzubringen. Um allerdings mit den Herren Bundesligaspielern der Perlwelt in Kontakt zu treten, muss man sich auf die Mailingliste Perl5-Porters (kurz P5P) einschreiben ([2]) oder diese auf dem Web [3] verfolgen. Simon Cozens schreibt übrigens seit einiger Zeit hochinformative wöchentliche Zusammenfassungen darüber, was in dieser äußerst regen Gemeinde so passiert ([4]).

Um nun das Geschehen auf P5P zu verfolgen ohne meine Mailbox aufzufüllen, kam mir folgende Idee: Wie wär's damit, P5P zu abonnieren, in eine POP3-Mailbox meines Hosting-Services umzuleiten, diese in regelmäßigen Zeitabständen automatisch mittels eines Skripts abzufragen, die eingegangenen Emails als Postings in einen lokalen Newsserver einzuspeisen und die so entstehenden Nachrichtenstränge (Threads) dann schön thematisch geordnet mittels meines Lieblings-Newsreaders tin zu lesen? tin stammt ja noch aus der Prä-Web-Ära und ist textbasiert -- und er erinnert mich noch an die Zeiten, als nur die Coolsten der Coolen über einen Internetanschluss verfügten. Seufz!

Abbildung 1: Der gute alte tin liest die P5P-Mailingliste.

Wie immer stehen zwischen einer noch so abgefahrenen Idee und deren Verwirklichung nur eine halbe Stunde fröhlicher Entwicklungsarbeit, wenn man Perl in seiner Werkzeugkiste führt. Das heute vorgestellte Skript pop2news.pl holt alle neu eingegangenen Emails von einem POP3-Mail-Account ab, löscht sie von dort und speist sie als Postings in einen Newsserver ein.

Listing 1: pop2news.pl

    01 #!/usr/bin/perl
    02 
    03 use strict;
    04 use warnings;
    05 
    06 my $POPHOST    = 'my.mail.server.com';
    07 my $POPPORT    = 110;
    08 my $POPTIMEOUT = 60;
    09 my $POPUSERID  = 'myuserid';
    10 my $POPPASSWD  = 'topsecret';
    11 my $NNTPSERVER = 'localhost';
    12 
    13 use Net::POP3;
    14 use Mail::Internet;
    15 use Net::NNTP;
    16 
    17 my $nntp = Net::NNTP->new($NNTPSERVER);
    18 die "News Server down\n" unless $nntp;
    19 $nntp->quit;
    20 
    21     # Verbindung zum POP-Server aufbauen
    22 my $pop = Net::POP3->new($POPHOST, 
    23                          Timeout => $POPTIMEOUT, 
    24                          Port    => $POPPORT);
    25 
    26 die "Cannot connect to $POPHOST ($pop)\n" unless $pop;
    27     
    28 my $nof_messages = $pop->login($POPUSERID, 
    29                                $POPPASSWD) or
    30     die "$POPHOST rejected our login\n";
    31 
    32     # Wortlos abbrechen, falls keine Nachrichten
    33     # vorliegen 
    34 exit 0 if $nof_messages == 0;
    35 
    36 foreach my $mesgno (keys %{$pop->list()}) {
    37 
    38     my $message = $pop->get($mesgno);
    39     my $mail = Mail::Internet->new($message);
    40 
    41         # Newsgroups-Header einfügen
    42     $mail->add("Newsgroups", "p5p");
    43 
    44         # An den Newsserver senden
    45     $mail->nntppost(Host => $NNTPSERVER) or
    46         warn "Cannot post to news server ($!)";
    47 
    48     $pop->delete($mesgno);
    49 }
    50 
    51 $pop->quit();

Die Pragmas use strict und use warnings (-w in prä-5.6.0-Versionen) in den Zeilen 3 und 4 sollten jedes Skript einleiten, um zu gewährleisten, dass sich keine Leichtsinnsfehler einschleichen. Die Konfigurationssektion zwischen den Zeilen 6 und 11 definiert Parameter für die verwendeten POP3- und Newsserver. $POPHOST ist der Rechner, auf dem der POP3-Server läuft und Port 110 ist Standard für den POP3-Service. (Während die Portangabe normalerweise entfallen kann, enthält die schon etwas veraltete Perl-Distribution 5.6.0 einen Fehler, der den Port doch notwendig macht). $POPTIMEOUT bestimmt, dass das Skript den POP3-Kontakt nach 60 Sekunden abbricht, falls sich bis dahin nichts rührt. $POPUSERID und $POPPASSWD legen fest, mit welcher User-Id und mit welchem Passwort sich der Benutzer auf dem POP3-Account einloggt. Als Newsserver verwenden wir einen lokalen, dessen Installation weiter unten im Abschnitt Installation besprochen wird.

Alles schon erfunden

Mit den Modulen Net::POP3, dem implizit damit verwendeten Net::NNTP und schließlich Mail::Internet ist das Skript keine Kunst mehr: Zeile 17 baut die Verbindung zum POP3-Server auf und gibt eine Referenz auf ein Net::POP3-Objekt zurück -- oder undef, falls etwas nicht klappt. Die login()-Methode aus Zeile 23 gibt dem POP3-Server die Benutzerkennung und das Passwort. Sie antwortet im Fehlerfall mit undef und gibt sonst die Anzahl der zur Verfügung stehenden Nachrichten zurück. Ist diese 0, bricht Zeile 29 ordnungsgemäß mit exit 0 ab, denn dann gibt es nichts zu tun.

Die foreach-Schleife ab Zeile 31 iteriert über alle Schlüssel des Hashs, den die list()-Methode des Net::POP3-Objekts als Referenz zurückliefert. Heraus kommen die Nachrichtennummern der auf dem POP3-Server vorliegenden Emails, die zugehörigen Hash-Werte geben die Nachrichtenlänge in Zeichen an. Die get()-Methode des Net::POP3-Objekts in Zeile 33 holt den Header und den Body der eingegangenen Email ein. Zeile 34 erzeugt daraus zur weiteren Bearbeitung ein Mail::Internet-Objekt. Die anschließend aufgerufene add-Methode fügt als neuen Header den Namen der Newsgroup auf dem lokalen Newsserver ein: p5p. Die in Zeile 41 aufgerufene nntppost()-Methode des Mail::Internet-Objekts nutzt implizit das Net::NNTP-Modul und sendet die Nachricht an den Newsserver. Klappt dies, löscht die delete-Methode in Zeile 43 die Nachricht vom POP3-Server. Am Ende bricht die quit-Methode in Zeile 46 die Verbindung ab.

Installation

Die verwendeten Zusatzmodule installieren sich wie immer am einfachsten mit der CPAN-Shell:

    perl -MCPAN -eshell
    cpan> install Net::POP3
    cpan> install Mail::Internet

Net::POP3 liegt der libwww-Bibliothek bei und Mail::Internet der MailTools-Distribution.

Das Skript pop2news.pl wandert in irgendein bin-Verzeichnis, aus dem es der weiter unten beschriebene Crontab-Prozess aufruft.

Nun zum lokalen News-Server: Falls der News-Dämon innd der verwendeten Linux-Installation noch nicht beiliegt, kann man ihn von http://www.redhat.com/apps/download abholen. Damit lokale Benutzer (also auch unser Skript) dem Server beliebig viele Nachrichten aufdrängen dürfen, muss etwa folgendes nach /etc/news/nnrp.access:

    # Default to no access
    *::::!*
    # Allow access from localhost
    localhost:Read Post:::*
    127.0.0.1:Read Post:::*

Falls innd noch nicht läuft (was man leicht mit ps aux | grep innd herausfindet), wird er mittels

    /usr/bin/inndstart

gestartet. Damit dies automatisch beim Systemstart geschieht, kann man diese Zeile auch nach /etc/rc.d/rc.local einhängen. Dem laufenden Newsserver drängen wir dann mit

    /usr/bin/ctlinnd newgroup \
                     p5p y Mike

die neue Newsgroup p5p auf. Der Parameter y erlaubt lokales Posten und Mike ist derjenige, der die Newsgroup aus der Taufe hob.

Automatisch abfragen

Einen Cronjob, der alle 10 Minuten pop2news.pl aufruft, erzeugt schließlich folgende Eingabe in dem mittels crontab -e aufgerufenen Editor:

    00,10,20,30,40,50 * * * * \
       /home/mschilli/bin/p2n.pl

Den Newsreader tin, den ich aus nostalgischen Gründen immer noch benutze, startet ein kleines Shell-Skript rn, in dem folgende Kommandos (für die Bash-Shell) stehen:

    export NNTPSERVER=localhost
    tin -f /home/mschilli/.localnewsrc

Den Status, also welche Newsgruppen er angezeigt und welche Nachrichten bereits gelesen wurden, speichert tin hier nicht im sonst üblichen ~/.newsrc, sondern in einem eigenen .localnewsrc, damit es nicht mit den Nachrichtennummern des sonst benutzten externen Newsservers kollidiert.

Der Newsserver sollte sich dann durch den Cronjob langsam mit eintrudelnden Nachrichten aus der Perl-Bundesliga füllen. Per Newsreader gesendete Nachrichten gelangen natürlich nicht zurück auf die P5P-Mailingliste -- aber man kann damit herrlich Heute bin ich Larry Wall spielen. Viel Spaß damit!

Infos

[1]
Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2001/07/Perl

[2]
Die Email-Adresse, um sich auf die Perl5-Porters-Mailingliste zu setzen: perl5-porters-subscribe@perl.org

[3]
Die P5P-Mailingliste auf dem Web: http://www.activestate.com/ASPN/Mail/browse/perl5-porters

[4]
Das P5P-Digest-Archiv von Simon Cozens: http://www.perl.com/pub/q/archivep5p

Michael Schilli

arbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com.