análisis de correo electrónico sin procesar en php

7 minutos de lectura

avatar de usuario
Uberfuzzy

Estoy buscando un código PHP bueno/funcional/sencillo de usar para analizar el correo electrónico sin procesar en partes.

He escrito un par de soluciones de fuerza bruta, pero cada vez, aparece un pequeño cambio/encabezado/espacio/algo y todo mi analizador falla y el proyecto se desmorona.

Y antes de que me apunten a PEAR/PECL, necesito un código real. Mi host tiene una configuración complicada o algo así, parece que nunca puedo hacer que los .so se construyan correctamente. Si obtengo el .así hecho, alguna diferencia en la ruta/entorno/php.ini no siempre lo hace disponible (Apache vs cron vs CLI).

Ah, y una última cosa, estoy analizando el texto del correo electrónico sin procesar, NO POP3 y NO IMAP. Se está canalizando al script PHP a través de una redirección de correo electrónico .qmail.

No espero que SOF lo escriba por mí, estoy buscando algunos consejos/puntos de partida para hacerlo “bien”. Este es uno de esos problemas de “rueda” que sé que ya se ha solucionado.

¿Con qué esperas terminar al final? ¿El cuerpo, el sujeto, el remitente, un archivo adjunto? Deberías pasar algún tiempo con RFC2822 para comprender el formato del correo, pero estas son las reglas más simples para un correo electrónico bien formado:

HEADERS\n
\n
BODY

Es decir, la primera línea en blanco (doble salto de línea) es el separador entre los ENCABEZADOS y el CUERPO. UN ENCABEZADO se ve así:

HSTRING:HTEXT

HSTRING siempre comienza al principio de una línea y no contiene espacios en blanco ni dos puntos. HTEXT puede contener una amplia variedad de texto, incluidas las líneas nuevas, siempre que el carácter de línea nueva vaya seguido de un espacio en blanco.

El “CUERPO” es realmente cualquier dato que sigue a la primera doble línea nueva. (Existen reglas diferentes si está transmitiendo correo a través de SMTP, pero no tiene que preocuparse por procesarlo a través de una tubería).

Entonces, en muy simple, alrededor de 1982 RFC822 términos, un correo electrónico se ve así:

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

THIS IS ANY
ARBITRARY DATA
(FOR THE MOST PART)

Sin embargo, la mayoría de los correos electrónicos modernos son más complejos que eso. Los encabezados se pueden codificar para juegos de caracteres o RFC2047 mimo, o un montón de otras cosas en las que no estoy pensando en este momento. Los cuerpos son realmente difíciles de implementar en su propio código en estos días si desea que sean significativos. Casi todo el correo electrónico generado por un MUA será MÍMICA codificado. Eso podría ser texto no codificado, podría ser html, podría ser una hoja de cálculo de Excel no codificada.

Espero que esto ayude a proporcionar un marco para comprender algunos de los cubos de correo electrónico muy elementales. Si proporciona más antecedentes sobre lo que está tratando de hacer con los datos, yo (u otra persona) podría proporcionar una mejor dirección.

Pruebe el analizador de correo electrónico PHP de Plancake:
https://github.com/plancake/official-library-php-email-parser

Lo he usado para mis proyectos. Funciona muy bien, es solo una clase y es de código abierto.

  • gran biblioteca dan! ¿Cómo se deshace de los caracteres de tipo =23 y =40?

    – cwd

    4 de abril de 2013 a las 1:35

  • @cwd eso es codificación de texto citado, supongo.

    –Jeyanth Kumar

    18 de junio de 2013 a las 10:05

  • para deshacerse de =23 etc., necesita usar el quoted_printable_decode función.

    – Alex W.

    18 de agosto de 2015 a las 18:57

  • Puedo confirmar que Plancake es chatarra. Parece hacer un mal trabajo al manejar los correos electrónicos enviados desde Outlook. No se ha actualizado en cinco años.

    – rgb defectuoso

    18 de abril de 2017 a las 18:37

avatar de usuario
carter cole

Lo improvisé, parte del código no es mío, pero no sé de dónde vino… Más tarde adopté el “MimeMailParser” más robusto, pero funciona bien, canalizo mi correo electrónico predeterminado usando cPanel y funciona estupendo.

#!/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());
}
?>

  • Me gusta este enfoque, y funciona bastante bien en su mayor parte. Sin embargo, he notado en la solución de problemas que no manejará las líneas de encabezado de ajuste, por ejemplo, si las direcciones to: usan más de una línea.

    – Jason Plata

    27 de marzo de 2017 a las 16:05

Hay funciones de Mailparse que puedes probar: http://php.net/manual/en/book.mailparse.phpno en php conf predeterminado, sin embargo.

Hay una biblioteca para analizar mensajes de correo electrónico sin procesar en una matriz php: http://flourishlib.com/api/fMailbox#parseMessage.

El método estático parseMessage() se puede usar para analizar un mensaje de correo electrónico MIME completo en el mismo formato que devuelve fetchMessage(), menos la clave uid.

$parsed_message = fMailbox::parseMessage(file_get_contents(‘/path/to/email’));

Aquí hay un ejemplo de un mensaje analizado:

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
);

avatar de usuario
tomaszkane

Este https://github.com/zbateson/MailMimeParser funciona para mí y no necesita la extensión mailparse.

<?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

avatar de usuario
Profe

El Mail_mimeDecode de Pear lib está escrito en PHP simple que puedes ver aquí: Fuente Mail_mimeDecode

¿Ha sido útil esta solución?