Bei jeder eingehenden E-Mail ein PHP Script ausführen
Nach einer kleinen Weihnachtspause gibts heute einen kleinen Trick, um bei jeder eingehenden E-Mail ein PHP-Script aufzurufen. In diesem Script kann man beispielsweise die E-Mail verändern, parsen und Informationen auslesen, den Inhalt in eine Datenbank packen oder andere nette Sachen, die man mit E-Mails machen kann.
Die Methode die ich normalerweise dafür nutze ist ein kleines Cronjob-Script, das periodisch auf dem IMAP-Server nachschaut ob eine neue E-Mail da ist, und dann wird der Inhalt der E-Mail abgeholt.
Postfix anpassen
Heute möchte ich eine Alternative vorstellen, die in Echtzeit die E-Mail empfängt und sie vor der Ablage im Postfach sogar noch verändern kann. Es gibt allerdings einen großen Nachteil: Man muss Zugang zum Mailserver haben, sprich per SSH auf den Server können und die Konfiguration von Postfix ändern können. Aber der Reihe nach.
Zuerst benötigen wir einen neuen Transport in Postfix, der ein Shellscript aufruft. Dazu muss die folgende Zeile in die master.cf eingefügt werden (Ordner: /etc/postfix):
myhook unix - n n - - pipe flags=F user=www-data argv=/data/scripts/email_process.sh ${sender} ${size} ${recipient}
Der Name „myhook“ kann natürlich frei gewählt werden, solange er auch an den 2 anderen Stellen geändert wird die noch weiter unten kommen. Das .sh Script das aufgerufen wird erstellen wir weiter unten.
Der Transport wird aktuell noch nicht genutzt, wir sagen Postfix nun dass alle E-Mails, die an die E-Mail-Adresse bot@phpgangsta.de geschickt werden, über den neuen „myhook“-Transport gesendet werden sollen. Dazu erstellen wir eine neue Datei /etc/postfix/access:
bot@phpgangsta.de FILTER myhook:dummy
Damit Postfix diese Datei nutzen kann muss sie noch umgewandelt werden:
sudo postmap access
Es wurde eine neue access.db Datei erstellt. Diese werden wir nun in Postfix nutzen. Dazu fügen wir folgende Zeile in die main.cf ein, bzw. ergänzen den Wert in die Liste falls smtpd_recipient_restrictions bereits existiert:
smtpd_recipient_restrictions = check_recipient_access hash:/etc/postfix/access
Damit der Transport jede E-Mail einzeln bekommt müssen wir in der selben Datei noch die folgende Zeile eingefügen:
myhook_destination_recipient_limit = 1
Damit sind alle Änderungen an Postfix vorgenommen. Nach einem Restart werden alle E-Mails mit dem Empfänger bot@phpgangsta.de einzeln an das Shell-Script übergeben. Falls die E-Mail, nachdem sie durch uns bearbeitet wurde, noch in das Postfach gelegt werden sollen, dann müssen wir folgendes kleine Helferscript zwischenschalten. Sollte nach der Bearbeitung durch PHP nichts mehr passieren, dann kann statt des .sh Scripts auch direkt das PHP-Script genommen werden.
#!/bin/bash /data/scripts/email_process.php $1 $2 $3 | /usr/sbin/sendmail -i -f $1 $3
Hier wird PHP aufgerufen mit 3 Parametern: Empfänger, Größe und Sender. Die Ausgabe, die das PHP-Script liefert, wird via sendmail wieder an den Postfix gesendet, wo die E-Mail dann endgültig ins Postfach zugestellt wird.
Das PHP-Script, das die Arbeit macht
Das PHP-Script sieht dann beispielsweise so aus:
#!/usr/bin/php <?php $sender = $argv[1]; $size = $argv[2]; $receiver = $argv[3]; // read from stdin $fd = fopen("php://stdin", "r"); $email = ""; while (!feof($fd)) { $email .= fread($fd, 1024); } fclose($fd); // Nun kann die E-Mail geparst und bearbeitet werden... // Der Inhalt kann in eine Datenbank geschrieben werden, die Anhänge extrahiert und abgelegt werden, Zusatzinhalt kann hinzugefügt werden echo $email;
Hier kann wie gesagt alles mit der E-Mail gemacht werden was man sich vorstellen kann. Die komplette E-Mail inkl. Anhänge kann geparst und verändert werden. Am Ende wird die (eventuell veränderte) E-Mail ausgegeben, damit sie an sendmail übergeben wird um im Postfach landet (siehe .sh Script oben). Wenn die E-Mail nicht im Postfach landen soll kann das echo und das .sh Script natürlich weggelassen werden.
Damit sowohl das Shell-Script als auch das PHP-Script ausgeführt werden können müssen die die Rechte stimmen.
sudo chmod 755 /data/scripts/email_process.sh sudo chmod 755 /data/scripts/email_process.php
Das war’s schon, E-Mail-Verarbeitung in Echtzeit und noch bevor die E-Mail im IMAP-Postfach landet!
Das über Postfix ist ein cooler weg, so habe ich 2003 einen sehr coolen dienst programmiert der nannte sich Moblogg, da konnte man Bilder vom Handy direkt auf seinen Blogg hochladen, hat damals nur kein schwein benutzt 🙂
Nachteil ist aber wirklich das man für die meisten Webanwendungen keinen Zugriff auf den Postfix server hat, also ist in den meisten fällen das auslesen eines mail postfaches via CRON flexibeler, und da emails eh nie in echtzeit ankommen auch nicht so schlimm.
Johny
11 Jan 13 at 08:57
Wie im Artikel beschrieben hatte ich mal ein Script geschrieben, das Bounces auswertet und den Website-Benutzer darauf hinweist, dass E-Mails an ihn unzustellbar sind.
Da das Auswerten nicht zeitkritisch ist, bin ich dann aber auch auf die Variante mit Cronjob + POP/IMAP umgestiegen. Die hat zudem den Vorteil, dass E-Mails auch dann noch entgegengenommen werden, wenn das PHP-Script mal nicht funktionieren sollte (z.B. weil die DB down ist).
Steffen
11 Jan 13 at 09:16
Sowas hätte ich gerne noch für den Postausgang. Wobei mir der Inhalt da größtenteils egal ist . Mir geht es nur um die Anzahl der gesendeten Emails festzuhalten und bei Auffälligkeiten Alarm zu schlagen.
Heinzi
11 Jan 13 at 10:38
Genial Michael, sowas habe ich schon immer gesucht. Bin noch neu auf dem Sektor der Serververwaltung, deshalb sind mir solche Tuts sehr willkommen!
Danke! 😉
Sascha
11 Jan 13 at 11:47
hi, ich habe das vor ein paar jahren mit exim umgesetzt. eine mail geht mit einem bestimmten betreff (eine art passwort) an eine bestimmte email adresse. in der email ist ein anhang der ausgewertet wird (xml und foto) daraus wird dann (on demand) diverse drucksachen erstellt (visitenkarten, etiketten, briefpapier usw) und an den sender zurückgeschickt.
das ganze system läuft nachwievor bestens.
volker
12 Jan 13 at 13:35
Eine Frage hätte ich noch: ist es sehr einfach, die Anhänge zu extrahieren?
Außerdem: werden die Anhänge (sollte man die Mail später im Postfach noch ablegen wollen) auch an das PHP-Script weitergeleitet?
Sascha
15 Jan 13 at 08:24
@Sascha: Ja, das extrahieren ist einfach wenn man die passende Klasse dafür nimmt. Das PHP-Script erhält die komplette E-Mail, die sieht dann ungefähr so aus:
http://en.wikipedia.org/wiki/MIME#Multipart_messages
Also mit Headern, Boundaries und allen MIME-Parts.
Parsen kannst du diesen Quelltext dann beispielsweise mit Zend_Mail_Message, hier ist beschrieben wie das geht:
http://www.dmertl.com/blog/?p=7
Dann kannst du auf alles recht einfach zugreifen: Betreff, Von, An, Anhänge etc.
Michael Kliewe
15 Jan 13 at 11:44
@Heinzi: dem schliesse ich mich an …
wär ja zu schön informiert zu werden wenn sich mal wieder ein Kunde einen Virus eingefangen hat und als Spamschleuder fungiert
Hansi
16 Jan 13 at 14:15
Danke gute Hilfe, danach hatte ich gesucht.
Cooler Blog!
Aber wie Heinzi für den Post Ausgang könnte ich es auch gebrauchen.
Wäre cool wenn das noch kommt. Werde aufjedenfall öfter vorbeischauen 😉
Weiter so.
Lucas
24 Jan 13 at 17:18
Mit dem Postausgang ist das nicht viel anders, nur das Du statt
smtpd_recipient_restrictions = check_recipient_access hash:/etc/postfix/access
eben
smtpd_sender_restrictions = check_sender_access hash:/etc/postfix/sender_access
benuzt. Daher würde ich das Beispiel auch mit ‚recipient_access‘ nachbauen – um Konfusion zu vermeiden.
Für das zählen der Mails – nicht das Rad neu erfinden – ‚pflogsumm‘ regelt so ziemlich alles 🙂
Michael
29 Jan 13 at 18:19
Ein schöner Trick. Kann ich gut gebrauchen. Danke.
Peter Demel
18 Sep 13 at 14:12
Danke für die Anleitung, hat mir schon sehr geholfen!
Gibt es eine Möglichkeit, den Mailinhalt und den Betreff ähnlich dem Senden, Absender zu übergeben?
email_process.sh ${sender} ${size} ${recipient}
z.B.: ${mail} ${subject}
so funktioniert es leider nicht.
Danke und Gruß
Marcello
25 Jan 15 at 19:26
Eine schöne variante, Danke für diesen guten Trick, muss ich direkt ausprobieren !
Babe
11 Apr 18 at 17:24