Frage

Ich suche guten/funktionierenden/einfach zu verwendenden PHP-Code zum Parsen von Roh-E-Mails in Teile.

Ich habe ein paar Brute-Force-Lösungen geschrieben, aber jedes Mal kommt eine kleine Änderung/Header/Leerzeichen/etwas und mein gesamter Parser schlägt fehl und das Projekt bricht zusammen.

Und bevor ich auf PEAR/PECL hingewiesen werde, benötige ich tatsächlichen Code.Mein Host hat eine verrückte Konfiguration oder so, ich schaffe es scheinbar nie, die .so's richtig zu erstellen.Wenn ich die .so-Datei erstellt habe, ist sie aufgrund eines Unterschieds in path/environment/php.ini nicht immer verfügbar (Apache vs. Cron vs. CLI).

Oh, und noch eine letzte Sache: Ich analysiere den Rohtext der E-Mail, NICHT POP3 und NICHT IMAP.Es wird über eine .qmail-E-Mail-Umleitung an das PHP-Skript weitergeleitet.

Ich erwarte nicht, dass SOF es für mich schreibt, ich suche nach Tipps/Anfangspunkten, um es „richtig“ zu machen.Dies ist eines dieser „Rad“-Probleme, von denen ich weiß, dass sie bereits gelöst wurden.

War es hilfreich?

Lösung

Was erhoffen Sie sich am Ende?Der Text, der Betreff, der Absender, ein Anhang?Du solltest etwas Zeit damit verbringen RFC2822 Um das Format der E-Mail zu verstehen, finden Sie hier die einfachsten Regeln für wohlgeformte E-Mails:

HEADERS\n
\n
BODY

Das heißt, die erste Leerzeile (doppelter Zeilenumbruch) ist das Trennzeichen zwischen den HEADERS und dem BODY.Ein HEADER sieht so aus:

HSTRING:HTEXT

HSTRING beginnt immer am Anfang einer Zeile und enthält keine Leerzeichen oder Doppelpunkte.HTEXT kann eine Vielzahl von Texten enthalten, einschließlich Zeilenumbrüchen, sofern auf das Zeilenumbruchzeichen ein Leerzeichen folgt.

Der „KÖRPER“ besteht eigentlich nur aus beliebigen Daten, die auf den ersten doppelten Zeilenumbruch folgen.(Es gelten andere Regeln, wenn Sie E-Mails über SMTP übertragen, aber wenn Sie sie über eine Pipe verarbeiten, müssen Sie sich darüber keine Sorgen machen.)

Also ganz einfach, etwa 1982 RFC822 Bedingungen, eine E-Mail sieht so aus:

HEADER: HEADER TEXT
HEADER: MORE HEADER TEXT
  INCLUDING A LINE CONTINUATION
HEADER: LAST HEADER

THIS IS ANY
ARBITRARY DATA
(FOR THE MOST PART)

Die meisten modernen E-Mails sind jedoch komplexer.Header können für Zeichensätze oder codiert werden RFC2047 Mimikwörter oder eine Menge anderer Dinge, an die ich gerade nicht denke.Heutzutage ist es wirklich schwierig, den eigenen Code so zu gestalten, dass er aussagekräftig ist.Fast alle E-Mails, die von einem MUA generiert werden, werden dies tun MIME codiert.Das könnte uuencodierter Text sein, es könnte HTML sein, es könnte eine uuencodierte Excel-Tabelle sein.

Ich hoffe, dass dies dazu beiträgt, einen Rahmen für das Verständnis einiger der elementarsten E-Mail-Bereiche zu schaffen.Wenn Sie mehr Hintergrundinformationen darüber liefern, was Sie mit den Daten tun möchten, kann ich (oder jemand anderes) möglicherweise eine bessere Anleitung geben.

Andere Tipps

Probieren Sie den PHP-E-Mail-Parser von Plancake aus:https://github.com/plancake/official-library-php-email-parser

Ich habe es für meine Projekte verwendet.Es funktioniert großartig, es ist nur eine Klasse und es ist Open Source.

Ich habe das zusammengebastelt, ein Teil des Codes gehört nicht mir, aber ich weiß nicht, woher er kommt ...Später habe ich den robusteren „MimeMailParser“ übernommen, aber das funktioniert gut. Ich leite meine Standard-E-Mail über cPanel daran weiter und es funktioniert großartig.

#!/usr/bin/php -q
<?php
// Config
$dbuser = 'emlusr';
$dbpass = 'pass';
$dbname = 'email';
$dbhost = 'localhost';
$notify= 'services@.com'; // an email address required in case of errors
function mailRead($iKlimit = "") 
    { 
        // Purpose: 
        //   Reads piped mail from STDIN 
        // 
        // Arguements: 
        //   $iKlimit (integer, optional): specifies after how many kilobytes reading of mail should stop 
        //   Defaults to 1024k if no value is specified 
        //     A value of -1 will cause reading to continue until the entire message has been read 
        // 
        // Return value: 
        //   A string containing the entire email, headers, body and all. 

        // Variable perparation         
            // Set default limit of 1024k if no limit has been specified 
            if ($iKlimit == "") { 
                $iKlimit = 1024; 
            } 

            // Error strings 
            $sErrorSTDINFail = "Error - failed to read mail from STDIN!"; 

        // Attempt to connect to STDIN 
        $fp = fopen("php://stdin", "r"); 

        // Failed to connect to STDIN? (shouldn't really happen) 
        if (!$fp) { 
            echo $sErrorSTDINFail; 
            exit(); 
        } 

        // Create empty string for storing message 
        $sEmail = ""; 

        // Read message up until limit (if any) 
        if ($iKlimit == -1) { 
            while (!feof($fp)) { 
                $sEmail .= fread($fp, 1024); 
            }                     
        } else { 
            while (!feof($fp) && $i_limit < $iKlimit) { 
                $sEmail .= fread($fp, 1024); 
                $i_limit++; 
            }         
        } 

        // Close connection to STDIN 
        fclose($fp); 

        // Return message 
        return $sEmail; 
    }  
$email = mailRead();

// handle email
$lines = explode("\n", $email);

// empty vars
$from = "";
$subject = "";
$headers = "";
$message = "";
$splittingheaders = true;
for ($i=0; $i < count($lines); $i++) {
    if ($splittingheaders) {
        // this is a header
        $headers .= $lines[$i]."\n";

        // look out for special headers
        if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
            $subject = $matches[1];
        }
        if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
            $from = $matches[1];
        }
        if (preg_match("/^To: (.*)/", $lines[$i], $matches)) {
            $to = $matches[1];
        }
    } else {
        // not a header, but message
        $message .= $lines[$i]."\n";
    }

    if (trim($lines[$i])=="") {
        // empty line, header section has ended
        $splittingheaders = false;
    }
}

if ($conn = @mysql_connect($dbhost,$dbuser,$dbpass)) {
  if(!@mysql_select_db($dbname,$conn))
    mail($email,'Email Logger Error',"There was an error selecting the email logger database.\n\n".mysql_error());
  $from    = mysql_real_escape_string($from);
  $to    = mysql_real_escape_string($to);
  $subject = mysql_real_escape_string($subject);
  $headers = mysql_real_escape_string($headers);
  $message = mysql_real_escape_string($message);
  $email   = mysql_real_escape_string($email);
  $result = @mysql_query("INSERT INTO email_log (`to`,`from`,`subject`,`headers`,`message`,`source`) VALUES('$to','$from','$subject','$headers','$message','$email')");
  if (mysql_affected_rows() == 0)
    mail($notify,'Email Logger Error',"There was an error inserting into the email logger database.\n\n".mysql_error());
} else {
  mail($notify,'Email Logger Error',"There was an error connecting the email logger database.\n\n".mysql_error());
}
?>

Es gibt Mailparse-Funktionen, die Sie ausprobieren könnten: http://php.net/manual/en/book.mailparse.php, jedoch nicht in der Standard-PHP-Konfiguration.

Es gibt eine Bibliothek zum Parsen von Roh-E-Mail-Nachrichten in ein PHP-Array - http://flourishlib.com/api/fMailbox#parseMessage.

Die statische Methode Parseemessage () kann verwendet werden, um eine vollständige MIME -E -Mail -Nachricht in demselben Format zu unterziehen, das FetchMessage () zurückgibt, abzüglich des UID -Schlüssels.

$ parsed_message = fmailBox :: parSemessage (Datei_get_Contents ('/path/to/mail'));

Hier ist ein Beispiel einer geparsten Nachricht:

array(
    'received' => '28 Apr 2010 22:00:38 -0400',
    'headers'  => array(
        'received' => array(
            0 => '(qmail 25838 invoked from network); 28 Apr 2010 22:00:38 -0400',
            1 => 'from example.com (HELO ?192.168.10.2?) (example) by example.com with (DHE-RSA-AES256-SHA encrypted) SMTP; 28 Apr 2010 22:00:38 -0400'
        ),
        'message-id' => '<4BD8E815.1050209@flourishlib.com>',
        'date' => 'Wed, 28 Apr 2010 21:59:49 -0400',
        'from' => array(
            'personal' => 'Will Bond',
            'mailbox'  => 'tests',
            'host'     => 'flourishlib.com'
        ),
        'user-agent'   => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.9) Gecko/20100317 Thunderbird/3.0.4',
        'mime-version' => '1.0',
        'to' => array(
            0 => array(
                'mailbox' => 'tests',
                'host'    => 'flourishlib.com'
            )
        ),
        'subject' => 'This message is encrypted'
    ),
    'text'      => 'This message is encrypted',
    'decrypted' => TRUE,
    'uid'       => 15
);

Das https://github.com/zbateson/MailMimeParser Funktioniert bei mir und benötige keine Mailparse-Erweiterung.

<?php
echo $message->getHeaderValue('from');          // user@example.com
echo $message
    ->getHeader('from')
    ->getPersonName();                          // Person Name
echo $message->getHeaderValue('subject');       // The email's subject

echo $message->getTextContent();                // or getHtmlContent

Die Pear-Bibliothek Mail_mimeDecode ist in einfachem PHP geschrieben, das Sie hier sehen können: Mail_mimeDecode-Quelle

Sie werden wahrscheinlich nicht viel Spaß daran haben, Ihren eigenen MIME-Parser zu schreiben.Der Grund, warum Sie „überentwickelte Mail-Verarbeitungspakete“ finden, liegt darin, dass MIME ein wirklich komplexer Satz von Regeln/Formaten/Kodierungen ist.MIME-Teile können rekursiv sein, was einen Teil des Spaßes ausmacht.Ich denke, das Beste ist, den bestmöglichen MIME-Handler zu schreiben, eine Nachricht zu analysieren, alles wegzuwerfen, was nicht text/plain oder text/html ist, und dann zu erzwingen, dass dem Befehl in der eingehenden Zeichenfolge COMMAND vorangestellt wird:oder ähnliches, damit man es im Dreck finden kann.Wenn Sie mit solchen Regeln beginnen, haben Sie eine gute Chance, mit neuen Anbietern klarzukommen, aber Sie sollten bereit sein, Anpassungen vorzunehmen, wenn ein neuer Anbieter hinzukommt (oder zum Teufel, wenn Ihr aktueller Anbieter beschließt, seine Messaging-Architektur zu ändern).

Das Parsen von E-Mails in PHP ist keine unmögliche Aufgabe.Was ich meine ist, dass man dafür kein Team von Ingenieuren braucht;es ist als Einzelperson erreichbar.Der wirklich schwierigste Teil, den ich fand, war die Erstellung des FSM zum Parsen eines IMAP BODYSTRUCTURE-Ergebnisses.Nirgendwo im Internet hatte ich das gesehen, also habe ich mein eigenes geschrieben.Meine Routine erstellt grundsätzlich ein Array verschachtelter Arrays aus der Befehlsausgabe, und die Tiefe, in der sich eines im Array befindet, entspricht in etwa der/den Teilenummer(n), die zum Durchführen der Suchvorgänge benötigt werden.Daher werden die verschachtelten MIME-Strukturen recht elegant verarbeitet.

Das Problem ist, dass die standardmäßigen imap_*-Funktionen von PHP nicht viel Granularität bieten ... also musste ich einen Socket zum IMAP-Port öffnen und die Funktionen schreiben, um die notwendigen Informationen zu senden und abzurufen (IMAP FETCH 1 BODY.PEEK[1.2] (zum Beispiel), und dazu gehört ein Blick in die RFC-Dokumentation.

Die Kodierung der Daten (quoted-printable, base64, 7bit, 8bit usw.), Länge der Nachricht, Inhaltstyp usw.wird Ihnen alles zur Verfügung gestellt;für Anhänge, Text, HTML usw.Möglicherweise müssen Sie auch die Nuancen Ihres Mailservers herausfinden, da nicht alle Felder immer zu 100 % implementiert sind.

Das Juwel ist das FSM ... wenn Sie über Kenntnisse in Comp Sci verfügen, kann es wirklich Spaß machen, dies zu erstellen (der Schlüssel ist, dass Klammern keine reguläre Grammatik sind ;)).Andernfalls wird es schwierig und/oder führt zu hässlichem Code, wenn herkömmliche Methoden verwendet werden.Auch Sie brauchen etwas Zeit!

Hoffe das hilft!

Ich bin mir nicht sicher, ob das für Sie hilfreich sein wird – ich hoffe es –, aber es wird sicherlich anderen helfen, die mehr über E-Mail erfahren möchten. Marcus Bointon hielt im März dieses Jahres auf der PHP-Konferenz in London einen der besten Vorträge mit dem Titel „Mail() and life after Mail()“. Folien Und MP3 sind online.Er spricht mit einiger Autorität, da er sich intensiv mit E-Mail und PHP beschäftigt hat.

Ich habe den Eindruck, dass Ihnen große Schmerzen bevorstehen, wenn Sie versuchen, einen wirklich generischen Parser zu schreiben.

BEARBEITEN – Die Dateien scheinen auf der PHP-London-Site entfernt worden zu sein;habe die Folien auf Marcus' gefunden eigene Seite: Teil 1 Teil 2 Konnte die MP3 jedoch nirgends sehen

Ja, ich konnte einen einfachen Parser schreiben, der auf diesem RFC und einigen anderen grundlegenden Tutorials basiert.Aber es sind die mehrteiligen, verschachtelten Grenzen, die mich immer wieder durcheinander bringen.

Ich habe herausgefunden, dass von meinem Telefon gesendete MMS-Nachrichten (keine SMS) nur Standard-E-Mails sind. Daher habe ich ein System, das die eingehenden E-Mails liest, den Absender überprüft (um nur von meinem Telefon aus zulässig zu sein) und den Textteil verwendet, um sie anders auszuführen Befehle auf meinem Server.Es ist so etwas wie eine Fernbedienung per E-Mail.

Da das System zum Senden von Bildern konzipiert ist, verfügt es über eine Reihe unterschiedlich codierter Teile.ein mms.smil.txt-Teil, ein Text/Plain-Teil (der nutzlos ist und nur sagt: „Dies ist eine HTML-Nachricht“), ein Application/Smil-Teil (den der Teil erkennt, den Telefone erkennen würden), ein Text/HTML-Teil mit einer Werbung für meinen Mobilfunkanbieter, dann meine Nachricht, aber alles in HTML verpackt, dann schließlich ein Textdateianhang mit meiner einfachen Nachricht (den Teil, den ich verwende) (wenn ich ein Bild als Anhang in die Nachricht schiebe, wird es angehängt Anhang 1, Base64 kodiert, dann wird mein Textteil als Anhang 2 angehängt)

Ich hatte es mit dem genauen E-Mail-Format meines Mobilfunkanbieters funktionieren lassen, aber als ich eine Nachricht vom Telefon eines anderen darüber laufen ließ, scheiterte es auf eine ganze Reihe kläglicher Art und Weise.

Ich habe andere Projekte, auf die ich dieses Telefon->Mail->Parsen->Befehlssystem erweitern möchte, aber ich benötige einen stabilen/soliden/generischen Parser, um die verschiedenen Teile aus der E-Mail herauszuholen und ihn verwenden zu können.

Mein Endziel wäre es, eine Funktion zu haben, in die ich die rohen weitergeleiteten E-Mails einspeisen und ein großes Array mit assoziativen Unterarrays von Header-Var:Val-Paaren und eines für den Textkörper als ganze Zeichenfolge zurückerhalten könnte

Je mehr ich danach suche, desto mehr finde ich das Gleiche:Riesige überentwickelte Mail-Verarbeitungspakete, die alles tun, was mit E-Mails zu tun hat, oder nutzlose (für mich in diesem Projekt) Tutorials.

Ich denke, ich muss in den sauren Apfel beißen und einfach selbst etwas sorgfältig schreiben.

Ich bin auf das gleiche Problem gestoßen, also habe ich die folgende Klasse geschrieben:Email_Parser.Es nimmt eine Roh-E-Mail auf und verwandelt sie in ein schönes Objekt.

Es erfordert PEAR Mail_mimeDecode, aber das sollte einfach über WHM oder direkt von der Befehlszeile aus zu installieren sein.

Holen Sie es sich hier: https://github.com/optimumweb/php-email-reader-parser

Einfacher PHPMimeParser https://github.com/breakermind/PhpMimeParser Yuo kann MIME-Nachrichten aus Dateien und Zeichenfolgen ausschneiden.Holen Sie sich Dateien, HTML und Inline-Bilder.

$str = file_get_contents('mime-mixed-related-alternative.eml');

// MimeParser
$m = new PhpMimeParser($str);

// Emails
print_r($m->mTo);
print_r($m->mFrom);

// Message
echo $m->mSubject;
echo $m->mHtml;
echo $m->mText;

// Attachments and inline images
print_r($m->mFiles);
print_r($m->mInlineList);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top