2008-08-15 7 views
24

Ich bin auf der Suche nach gut/arbeiten/einfach zu PHP-Code zum Parsen von rohen E-Mail in Teile zu verwenden.Parsing rohe E-Mail in PHP

Ich habe ein paar Brute-Force-Lösungen geschrieben, aber jedes Mal, eine kleine Änderung/header/Raum/etwas kommt und meine ganzen Parser fehlschlägt und das Projekt fällt auseinander.

Und bevor ich auf PEAR/PECL hingewiesen werde, brauche ich tatsächlichen Code. Mein Gastgeber hat eine verschraubte Konfiguration oder etwas, ich kann nie scheinen, dass die .so's richtig baut. Wenn ich das .so gemacht habe, stellt ein Unterschied in path/environment/php.ini es nicht immer zur Verfügung (Apache gegen cron gegen cli).

Oh, und eine letzte Sache, ich bin Parsen des rohen E-Mail-Textes, nicht POP3 und IMAP NICHT. Es wird über eine .qmail E-Mail-Weiterleitung in das PHP-Skript geleitet.

Ich erwarte nicht SOF, um es für mich zu schreiben, ich bin auf der Suche nach ein paar Tipps/Startpunkte auf es "richtig" zu tun. Dies ist eines dieser Radprobleme, von denen ich weiß, dass sie bereits gelöst wurden.

Antwort

19

Was erhoffen Sie mit am Ende enden? Der Körper, das Subjekt, der Absender, eine Bindung? Sie sollten einige Zeit mit RFC2822 verbringen, um das Format der E-Mail, zu verstehen, aber hier sind die einfachsten Regeln für gut gebildete E-Mail:

HEADERS\n 
\n 
BODY 

Das heißt, die erste leere Zeile (Doppel Newline) sind das Trennzeichen zwischen den Headern und der Körper. Ein Header sieht wie folgt aus:

HSTRING:HTEXT 

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

Der „Körper“ ist wirklich nur alle Daten, die das erste Doppel Newline folgt. (Es gibt verschiedene Regeln, wenn Sie E-Mails über SMTP senden, aber über eine Pipe verarbeiten, ohne dass Sie sich darum kümmern müssen).

Also, wirklich einfach, circa 1982 RFC822 Begriffe, sieht eine E-Mail wie folgt aus: obwohl

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-Mail als komplexer ist. Header können für Zeichensätze oder RFC2047 Pantomime-Wörter oder eine Tonne anderer Sachen kodiert werden, an die ich gerade nicht denke. Die Körper sind wirklich schwierig, Ihren eigenen Code für diese Tage zu rollen, wenn Sie wollen, dass sie bedeutungsvoll sind. Fast alle E-Mails, die von einem MUA generiert werden, sind MIME codiert. Das könnte uuencoded Text sein, es könnte HTML sein, könnte es eine UUENCODED Excel-Tabelle sein.

ich diese Hoffnung hilft, einen Rahmen für einige der sehr elementaren Eimer E-Mail zu verstehen. Wenn Sie mehr Hintergrundinformationen zu dem bereitstellen, was Sie mit den Daten zu tun versuchen, kann ich (oder eine andere Person) möglicherweise eine bessere Richtung angeben.

0

ja, ive der Lage, einen grundlegenden Parser zu schreiben, dass rfc und einige anderen grundlegenden Tutorials basiert. aber es sind die mehrteiligen Mime-verschachtelten Grenzen, die mich ständig vermasseln.

Ich fand heraus, dass MMS (nicht SMS) Nachrichten von meinem Telefon sind nur Standard-E-Mails, so habe ich ein System, das eingehende E-Mails liest, die von (nur von meinem Telefon zu ermöglichen), und verwendet den Körper Teil, um verschiedene Befehle auf meinem Server auszuführen. es ist wie eine Fernbedienung per E-Mail.

Da das System Bilder senden soll, hat es eine Reihe unterschiedlich codierter Teile. ein mms.smil.txt-Teil, ein text/plain (was nutzlos ist, sagt nur, das ist eine html-Nachricht '), ein application/smil-Teil (der Teil, an dem die Telefone picten), ein text/html-Teil mit einer anzeige für meinen träger, dann meine nachricht, aber alles in html, dann endlich ein textfile attachment mit meiner plain message (welches ist das teil, das ich benutze) (wenn ich ein bild als anhang in die nachricht schiebe, wird es bei Anhang 1, Base64 codiert, dann ist mein Textteil als Anhang 2)

Ich hatte es mit dem genauen Mail-Format von meinem Träger arbeiten, aber wenn ich eine Nachricht von jemandem elses Telefon durch es lief, schlug es in einem ganzer Haufen elender Wege.

Ich habe andere Projekte, die ich erweitern möchte, um dieses Telefon-> Mail-> Parse-> Befehlssystem, aber ich muss einen stabilen/soliden/generischen Parser haben, um die verschiedenen Teile aus der Post zu bekommen benutze es.

Mein Endziel wäre, eine Funktion zu haben, die ich die rohe piped Mail einspeisen könnte, und ein großes Array mit assoziativen Sub-Arrays von Headern var: val-Paare und eins für den Body-Text als Ganzes zurück string

Je mehr ich suche, desto mehr finde ich das gleiche: riesige überentwickelte Mail-Handling-Pakete, die alles unter der Sonne tun, was Mails betrifft, oder nutzlos (für mich, in diesem Projekt) Tutorials.

Ich denke, ich werde in die Kugel beißen und nur sorgfältig etwas schreiben, was ich selbst bin.

1

Sie werden wahrscheinlich nicht viel Spaß beim Schreiben Ihres eigenen MIME-Parsers haben. Der Grund, warum Sie "überentwickelte Mail-Handling-Pakete" finden, ist, dass MIME ein wirklich komplexer Satz von Regeln/Formaten/Kodierungen ist. MIME-Teile können rekursiv sein, was Teil des Spaßes ist. Ich denke, Ihre beste Wette ist, den besten MIME-Handler zu schreiben, analysieren Sie eine Nachricht, wegwerfen alles, was nicht text/plain oder text/html ist, und dann erzwingen den Befehl in der eingehenden Zeichenfolge COMMAND: oder etwas ähnliches damit du es im Dreck finden kannst. Wenn Sie mit solchen Regeln beginnen, haben Sie eine gute Chance, mit neuen Providern umzugehen, aber Sie sollten bereit sein, zu optimieren, wenn ein neuer Provider hinzukommt (oder, wenn Ihr aktueller Provider seine Messaging-Architektur ändert).

1

Ich bin mir nicht sicher, ob dies Ihnen helfen wird - hoffe es - aber es wird sicherlich anderen helfen, die mehr über E-Mail erfahren möchten. Marcus Bointon hat eine der besten Präsentationen mit dem Titel "Mail() und Leben nach Mail()" auf der PHP London Konferenz im März dieses Jahres und die slides und MP3 sind online. Er spricht mit einiger Autorität, nachdem er intensiv mit Email und PHP gearbeitet hat.

Meine Wahrnehmung ist, dass Sie in einer Welt des Schmerzes sind, der versucht, einen wirklich generischen Parser zu schreiben.

BEARBEITEN - Die Dateien scheinen auf der PHP-Site in London entfernt worden zu sein; die Dias auf Marcus' own site gefunden: Part 1Part 2 kann sehen, die MP3-nirgendwohin obwohl

1

Parsen von E-Mails in PHP ist keine unmögliche Aufgabe. Was ich meine ist, dass Sie kein Team von Ingenieuren brauchen, um das zu tun; es ist erreichbar als Individuum. Wirklich der schwierigste Teil, den ich fand, war das Erstellen der FSM zum Analysieren eines IMAP BODYSTRUCTURE-Ergebnisses. Nirgendwo im Internet hatte ich das gesehen, also schrieb ich mein eigenes.Meine Routine erzeugt im Grunde eine Anordnung verschachtelter Arrays von der Befehlsausgabe, und die Tiefe, in der sich die Matrix befindet, entspricht ungefähr der Teilenummer, die für die Suche benötigt wird. So behandelt es die verschachtelten MIME-Strukturen ziemlich elegant.

Das Problem ist, dass die PHP-Standard-imap_ * -Funktionen nicht viel Granularität bieten ... also musste ich einen Socket zum IMAP-Port öffnen und die Funktionen zum Senden und Abrufen der notwendigen Informationen schreiben (IMAP FETCH 1 BODY. PEEK [1.2] zum Beispiel), und dazu gehört die RFC-Dokumentation.

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

Das Juwel ist die FSM ... Wenn Sie einen Hintergrund in Comp Sci haben, kann es wirklich wirklich Spaß machen, dies zu machen (sie sind Klammern, die keine normale Grammatik sind;)); Andernfalls wird es ein harter Kampf und/oder hässlicher Code mit traditionellen Methoden. Du brauchst auch etwas Zeit!

Hoffe, das hilft!

4

Ich habe dies zusammengeschustert, ein Code ist nicht meins, aber ich weiß nicht woher er kam ... Ich habe später den robusteren "MimeMailParser" übernommen, aber das funktioniert gut, ich pipe meine Standard-E-Mail dazu cPanel und es funktioniert super. Versuchen

#!/usr/bin/php -q 
<?php 
// Config 
$dbuser = 'emlusr'; 
$dbpass = 'pass'; 
$dbname = 'email'; 
$dbhost = 'localhost'; 
$notify= '[email protected]'; // 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([email protected]_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()); 
} 
?> 
+0

Ich mag diesen Ansatz, und es funktioniert in den meisten Fällen ziemlich gut. Allerdings habe ich bei der Fehlersuche bemerkt, dass es keine Wrapping-Header-Zeilen verarbeiten wird, zum Beispiel wenn die to: -Adressen mehr als eine Zeile verwenden. –

17

die Plancake PHP E-Mail-Parser: https://github.com/plancake/official-library-php-email-parser

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

+0

große Bibliothek dan! Wie werden Sie die Zeichen = 23 und = 40 los? – cwd

+0

@cwd das ist zitiert Textcodierung ich denke. –

+2

Ich benutze diese Lib und es funktioniert 90% der Zeit, aber nicht 100% ... – behz4d

2

Die Birne lib Mail_mimeDecode wird in einfacher PHP geschrieben, die Sie hier sehen können: Mail_mimeDecode source

+0

der Link sollte http://svn.php.net/viewvc/pear/packages/Mail_mimeDecode/trunk/Mail/mimeDecode.php?revision=337165&view=markup – chiliNUT

+0

BTW diese Bibliothek ist hervorragend – chiliNUT

2

eine Bibliothek Es Nachricht in PHP für das Parsen von roher E-Mail ist Array - http://flourishlib.com/api/fMailbox#parseMessage.

Die statische Methode parseMessage() kann eine vollständige MIME E-Mail-Nachricht in das gleiche Format, das fetchMessage() zurückkehrt, minus die uid Schlüssel zum Parsen verwendet werden.

$ parsed_message = fMailbox :: parseMessage (file_get_contents ('/ pfad/zu/email')); Hier

ist ein Beispiel für eine geparste 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' => '<[email protected]>', 
     '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 
); 
+0

Funktioniert wie ein Charme! 8-) –

0

traf ich das gleiche Problem, so schrieb ich die folgende Klasse: Email_Parser. Es nimmt eine rohe E-Mail auf und verwandelt sie in ein nettes Objekt.

Es erfordert PEAR Mail_mimeDecode, aber das sollte einfach über WHM oder direkt von der Befehlszeile installiert werden.

Erhalten Sie sie hier: https://github.com/optimumweb/php-email-reader-parser

2

Diese https://github.com/zbateson/MailMimeParser funktioniert für mich, und brauchen nicht mailparse Erweiterung.

<?php 
echo $message->getHeaderValue('from');   // [email protected] 
echo $message 
    ->getHeader('from') 
    ->getPersonName();       // Person Name 
echo $message->getHeaderValue('subject');  // The email's subject 

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

Einfach PhpMimeParser https://github.com/breakermind/PhpMimeParser Yuo können MIME-Nachrichten von Dateien, string schneiden. 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);