Pergunta

Estou procurando um código php bom/funcional/simples de usar para analisar e-mail bruto em partes.

Eu escrevi algumas soluções de força bruta, mas sempre, uma pequena mudança/cabeçalho/espaço/alguma coisa aparece e todo o meu analisador falha e o projeto desmorona.

E antes de ser apontado para PEAR/PECL, preciso de código real.Meu host tem alguma configuração complicada ou algo assim, parece que nunca consigo fazer o .so construir corretamente.Se eu conseguir fazer o .so, alguma diferença no path/environment/php.ini nem sempre o disponibiliza (apache vs cron vs cli).

Ah, e uma última coisa, estou analisando o texto bruto do e-mail, NÃO POP3 e NÃO IMAP.Ele está sendo canalizado para o script php por meio de um redirecionamento de e-mail .qmail.

Não espero que o SOF escreva para mim, estou procurando algumas dicas/pontos de partida para fazer isso "certo".Este é um daqueles problemas de “roda” que sei que já foi resolvido.

Foi útil?

Solução

O que você espera terminar no final?O corpo, o sujeito, o remetente, um anexo?Você deveria passar algum tempo com RFC2822 para entender o formato do e-mail, mas aqui estão as regras mais simples para um e-mail bem formado:

HEADERS\n
\n
BODY

Ou seja, a primeira linha em branco (nova linha dupla) é o separador entre HEADERS e BODY.Um HEADER se parece com isto:

HSTRING:HTEXT

HSTRING sempre começa no início de uma linha e não contém espaços em branco ou dois pontos.HTEXT pode conter uma grande variedade de texto, incluindo novas linhas, desde que o caractere de nova linha seja seguido por um espaço em branco.

O "BODY" é na verdade qualquer dado que segue a primeira nova linha dupla.(Existem regras diferentes se você estiver transmitindo e-mails via SMTP, mas processando-o por meio de um canal, você não precisa se preocupar com isso).

Então, de forma bem simples, por volta de 1982 RFC822 termos, um e-mail se parece com este:

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

THIS IS ANY
ARBITRARY DATA
(FOR THE MOST PART)

A maioria dos e-mails modernos é mais complexa do que isso.Os cabeçalhos podem ser codificados para conjuntos de caracteres ou RFC2047 mímica de palavras ou uma tonelada de outras coisas nas quais não estou pensando agora.É realmente difícil criar seu próprio código para os corpos hoje em dia, se você quiser que eles sejam significativos.Quase todos os emails gerados por um MUA serão MIME codificado.Pode ser um texto não codificado, pode ser html, pode ser uma planilha Excel não codificada.

Espero que isso ajude a fornecer uma estrutura para a compreensão de alguns dos grupos mais elementares do e-mail.Se você fornecer mais informações sobre o que está tentando fazer com os dados, eu (ou outra pessoa) poderei fornecer uma orientação melhor.

Outras dicas

Experimente o analisador de e-mail Plancake PHP:https://github.com/plancake/official-library-php-email-parser

Eu o usei para meus projetos.Funciona muito bem, é apenas uma classe e é de código aberto.

Eu juntei isso, algum código não é meu, mas não sei de onde veio ...Mais tarde, adotei o "MimeMailParser" mais robusto, mas funciona bem, envio meu e-mail padrão para ele usando o cPanel e funciona muito bem.

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

Existem funções Mailparse que você pode tentar: http://php.net/manual/en/book.mailparse.php, no entanto, não no conf padrão do php.

Existe uma biblioteca para analisar mensagens de email brutas em array php - http://flourishlib.com/api/fMailbox#parseMessage.

O método estático parseMessage() pode ser usado para analisar um MIME completo mensagem de email no mesmo formato que fetchMessage() retorna, menos A chave uid.

$parsed_mensagem = fMailbox::p arseMessage(file_get_contents('/path/to/email'));

Aqui está um exemplo de uma mensagem analisada:

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

Esse https://github.com/zbateson/MailMimeParser funciona para mim e não precisa da extensão 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

O Pear lib Mail_mimeDecode é escrito em PHP simples que você pode ver aqui: Fonte Mail_mimeDecode

Você provavelmente não se divertirá muito escrevendo seu próprio analisador MIME.A razão pela qual você está encontrando "pacotes de tratamento de correio superdesenvolvidos" é porque MIME é um conjunto realmente complexo de regras/formatos/codificações.As partes MIME podem ser recursivas, o que é parte da diversão.Acho que sua melhor aposta é escrever o melhor manipulador MIME possível, analisar uma mensagem, jogar fora tudo que não seja texto/simples ou texto/html e, em seguida, forçar o comando na string recebida a ser prefixado com COMMAND:ou algo semelhante para que você possa encontrá-lo na lama.Se você começar com regras como essa, terá uma boa chance de lidar com novos provedores, mas deverá estar pronto para ajustar se um novo provedor aparecer (ou, se seu provedor atual decidir mudar sua arquitetura de mensagens).

Analisar email em PHP não é uma tarefa impossível.O que quero dizer é que você não precisa de uma equipe de engenheiros para fazer isso;é alcançável como indivíduo.Na verdade, a parte mais difícil que encontrei foi criar o FSM para analisar um resultado IMAP BODYSTRUCTURE.Em nenhum lugar da Internet eu tinha visto isso, então escrevi o meu próprio.Minha rotina basicamente cria uma matriz de matrizes aninhadas a partir da saída do comando, e a profundidade na matriz corresponde aproximadamente ao(s) número(s) de peça necessário(s) para realizar as pesquisas.Portanto, ele lida com as estruturas MIME aninhadas com bastante elegância.

O problema é que as funções imap_* padrão do PHP não fornecem muita granularidade... então tive que abrir um soquete para a porta IMAP e escrever as funções para enviar e recuperar as informações necessárias (IMAP FETCH 1 BODY.PEEK[1.2] por exemplo), e isso envolve consultar a documentação RFC.

A codificação dos dados (imprimível entre aspas, base64, 7 bits, 8 bits, etc.), comprimento da mensagem, tipo de conteúdo, etc.é tudo fornecido a você;para anexos, texto, html, etc.Talvez você também precise descobrir as nuances do seu servidor de e-mail, já que nem todos os campos são sempre implementados 100%.

A joia é o FSM... se você tem experiência em Comp Sci, pode ser muito divertido fazer isso (a chave é que os colchetes não são uma gramática regular;));caso contrário, será difícil e/ou resultará em código feio, usando métodos tradicionais.Além disso, você precisa de algum tempo!

Espero que isto ajude!

Não tenho certeza se isso será útil para você - espero que sim - mas certamente ajudará outras pessoas interessadas em saber mais sobre e-mail. Marcus Bointon fez uma das melhores apresentações intitulada "Mail() and life after Mail()" na conferência PHP London em março deste ano e o diapositivos e MP3 estão on-line.Ele fala com alguma autoridade, tendo trabalhado extensivamente com email e PHP em um nível profundo.

Minha percepção é que você enfrentará um mundo de dor tentando escrever um analisador verdadeiramente genérico.

EDIT - Os arquivos parecem ter sido removidos do site PHP London;encontrei os slides do Marcus próprio site: Parte 1 Parte 2 Não consegui ver o MP3 em lugar nenhum

sim, consegui escrever um analisador básico, baseado naquele RFC e em alguns outros tutoriais básicos.mas são os limites aninhados de mímica multiparte que continuam me atrapalhando.

descobri que as mensagens MMS (não SMS) enviadas do meu telefone são apenas e-mails padrão, então tenho um sistema que lê o e-mail recebido, verifica o de (para permitir apenas do meu telefone) e usa a parte do corpo para executar diferentes comandos no meu servidor.é como um controle remoto por e-mail.

como o sistema foi projetado para enviar imagens, ele possui várias partes codificadas de maneira diferente.uma parte mms.smil.txt, uma parte text/plain (que é inútil, apenas diz 'esta é uma mensagem html'), uma parte application/smil (que é a parte que os telefones apareceriam), uma parte text/html com um anúncio da minha operadora, depois minha mensagem, mas tudo embrulhado em html, e finalmente um anexo de arquivo de texto com minha mensagem simples (que é a parte que eu uso) (se eu colocar uma imagem como anexo na mensagem, ela será colocada em anexo 1, codificado em base64, então minha parte do texto é anexada como anexo 2)

eu o fiz funcionar com o formato de e-mail exato da minha operadora, mas quando passei uma mensagem do telefone de outra pessoa por meio dele, ele falhou de várias maneiras miseráveis.

Tenho outros projetos para os quais gostaria de estender este sistema de telefone-> correio-> analisar-> comando, mas preciso ter um analisador estável/sólido/genérico para obter as diferentes partes do correio para usá-lo.

meu objetivo final seria ter uma função na qual eu pudesse alimentar o e-mail canalizado bruto e obter de volta uma grande matriz com submatrizes associativas de pares de cabeçalhos var: val e uma para o corpo do texto como uma string inteira

quanto mais procuro sobre isso, mais encontro a mesma coisa:pacotes gigantescos de gerenciamento de e-mail superdesenvolvidos que fazem tudo o que está relacionado a e-mails, ou tutoriais inúteis (para mim, neste projeto).

acho que vou ter que aguentar e escrever algo com cuidado.

Eu encontrei o mesmo problema, então escrevi a seguinte classe:E-mail_Parser.Ele pega um e-mail bruto e o transforma em um objeto legal.

Requer PEAR Mail_mimeDecode, mas deve ser fácil de instalar via WHM ou diretamente da linha de comando.

Venha aqui : https://github.com/optimumweb/php-email-reader-parser

PhpMimeParser simples https://github.com/breakermind/PhpMimeParser Yuo pode cortar mensagens mímicas de arquivos, strings.Obtenha arquivos, HTML e imagens embutidas.

$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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top