анализ необработанной электронной почты в php

StackOverflow https://stackoverflow.com/questions/12896

  •  08-06-2019
  •  | 
  •  

Вопрос

Я ищу хороший/работающий/простой в использовании PHP-код для разбора необработанной электронной почты на части.

Я написал пару решений методом грубой силы, но каждый раз одно небольшое изменение/заголовок/пробел/что-то происходит, и весь мой парсер дает сбой, и проект разваливается.

И прежде чем мне укажут на PEAR/PECL, мне нужен реальный код.У моего хоста какая-то странная конфигурация или что-то в этом роде, и я никогда не смогу правильно построить .so.Если я получу .so, некоторая разница в пути/среде/php.ini не всегда делает его доступным (apache против cron против cli).

Да, и последнее: я анализирую необработанный текст электронной почты, НЕ POP3 и НЕ IMAP.Он передается в php-скрипт через перенаправление электронной почты .qmail.

Я не ожидаю, что SOF напишет это за меня, я ищу несколько советов/отправных точек, как сделать это «правильно».Это одна из тех «колесных» проблем, которые, как я знаю, уже решены.

Это было полезно?

Решение

Что вы надеетесь получить в итоге?Тело, тема, отправитель, вложение?Вам следует провести некоторое время с RFC2822 чтобы понять формат письма, вот простейшие правила правильного составления письма:

HEADERS\n
\n
BODY

То есть первая пустая строка (двойная новая строка) является разделителем между ЗАГОЛОВКАМИ и ТЕЛОМ.ЗАГОЛОВОК выглядит следующим образом:

HSTRING:HTEXT

HSTRING всегда начинается в начале строки и не содержит пробелов или двоеточий.HTEXT может содержать самый разнообразный текст, включая символы новой строки, если за символом новой строки следует пробел.

«ТЕЛО» на самом деле представляет собой любые данные, следующие за первым двойным символом новой строки.(Если вы передаете почту через SMTP, действуют другие правила, но при ее обработке по каналу вам не о чем беспокоиться).

Итак, очень просто, примерно 1982 год. RFC822 с точки зрения электронной почты, электронное письмо выглядит так:

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

THIS IS ANY
ARBITRARY DATA
(FOR THE MOST PART)

Однако большая часть современной электронной почты устроена сложнее.Заголовки могут быть закодированы для кодировок или RFC2047 слова-мимы или масса других вещей, о которых я сейчас не думаю.В наши дни телам действительно сложно адаптировать свой собственный код, если вы хотите, чтобы он имел смысл.Почти все электронные письма, генерируемые MUA, будут МИМ закодировано.Это может быть текст в кодировке uu, это может быть HTML или электронная таблица Excel в кодировке uu.

Я надеюсь, что это поможет создать основу для понимания некоторых элементарных аспектов электронной почты.Если вы предоставите больше информации о том, что вы пытаетесь сделать с данными, я (или кто-то другой), возможно, смогу дать лучшее направление.

Другие советы

Попробуйте парсер электронной почты Plancake PHP:https://github.com/plancake/official-library-php-email-parser

Я использовал его для своих проектов.Он отлично работает, это всего лишь один класс и он с открытым исходным кодом.

Я собрал это вместе, часть кода не моя, но я не знаю, откуда он взялся...Позже я принял более надежный «MimeMailParser», но он работает нормально, я передаю на него свою электронную почту по умолчанию с помощью cPanel, и он отлично работает.

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

Есть функции Mailparse, которые вы можете попробовать: http://php.net/manual/en/book.mailparse.php, однако не в конфигурации php по умолчанию.

Существует библиотека для анализа необработанного сообщения электронной почты в массив php – http://flourishlib.com/api/fMailbox#parseMessage.

Статический метод ParSemessage () может использоваться для анализа полного сообщения электронной почты MIME в тот же формат, который возвращает fetchMessage (), за исключением ключа UID.

$ parsed_message = fmailbox :: parsemessage (file_get_contents ('/path/to/email'));

Вот пример проанализированного сообщения:

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

Этот https://github.com/zbateson/MailMimeParser у меня работает, и мне не нужно расширение 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

Библиотека Pear Mail_mimeDecode написана на простом PHP, который вы можете увидеть здесь: Источник Mail_mimeDecode

Вероятно, вы не получите большого удовольствия от написания собственного парсера MIME.Причина, по которой вы находите «чрезмерно развитые пакеты обработки почты», заключается в том, что MIME представляет собой действительно сложный набор правил/форматов/кодировок.Части MIME могут быть рекурсивными, и это забавно.Я думаю, что лучше всего написать лучший обработчик MIME, который вы можете, проанализировать сообщение, выбросить все, что не является text/plain или text/html, а затем заставить команду во входящей строке иметь префикс COMMAND:или что-то подобное, чтобы можно было найти это в грязи.Если вы начнете с таких правил, у вас есть хорошие шансы работать с новыми провайдерами, но вы должны быть готовы к их корректировке, если появится новый провайдер (или, черт возьми, если ваш текущий провайдер решит изменить свою архитектуру обмена сообщениями).

Анализ электронной почты в PHP не является невыполнимой задачей.Я имею в виду, что для этого вам не нужна команда инженеров;это достижимо как личность.На самом деле самой сложной частью, которую я нашел, было создание FSM для анализа результата IMAP BODYSTRUCTURE.Нигде в Интернете я этого не видел, поэтому написал свой.Моя программа в основном создает массив вложенных массивов из выходных данных команды, и глубина массива примерно соответствует номеру(ам) детали, необходимому для выполнения поиска.Таким образом, он довольно изящно обрабатывает вложенные структуры MIME.

Проблема в том, что функции PHP по умолчанию imap_* не обеспечивают большой детализации... поэтому мне пришлось открыть сокет для порта IMAP и написать функции для отправки и получения необходимой информации (IMAP FETCH 1 BODY.PEEK[1.2] например), и для этого необходимо изучить документацию RFC.

Кодировка данных (quoted-printable, base64, 7bit, 8bit и т. д.), длина сообщения, тип контента и т. д.все вам предоставлено;для вложений, текста, HTML и т. д.Возможно, вам придется разобраться и в нюансах вашего почтового сервера, так как не все поля всегда выполняются на 100%.

Жемчужиной является FSM... если у вас есть опыт работы в Comp Sci, сделать это может быть очень весело (ключевым моментом является то, что скобки - это не обычная грамматика;));в противном случае это будет борьба и/или приведет к уродливому коду с использованием традиционных методов.Также вам нужно время!

Надеюсь это поможет!

Я не уверен, что это поможет вам (надеюсь на это), но это наверняка поможет другим, кто хочет узнать больше об электронной почте. Маркус Бойнтон сделал одну из лучших презентаций под названием «Mail() и жизнь после Mail()» на конференции PHP в Лондоне в марте этого года, и слайды и МП3 находятся онлайн.Он говорит с некоторым авторитетом, поскольку много работал с электронной почтой и PHP на глубоком уровне.

Я считаю, что вас ждет огромная боль, пытаясь написать действительно универсальный парсер.

РЕДАКТИРОВАТЬ. Файлы, похоже, были удалены на сайте PHP в Лондоне;нашел слайды у Маркуса собственный сайт: Часть 1 Часть 2 Но MP3 нигде не увидел.

да, мне удалось написать базовый парсер на основе этого RFC и некоторых других базовых руководств.но меня продолжают смущать многочастные вложенные границы пантомимы.

Я обнаружил, что сообщения MMS (не SMS), отправленные с моего телефона, представляют собой обычные электронные письма, поэтому у меня есть система, которая читает входящее электронное письмо, проверяет отправителя (чтобы разрешить только с моего телефона) и использует часть тела для запуска различных команды на моем сервере.это что-то вроде дистанционного управления по электронной почте.

поскольку система предназначена для отправки изображений, в ней есть множество частей, закодированных по-разному.часть mms.smil.txt, text/plain (которая бесполезна, просто говорит: «это html-сообщение»), часть application/smil (часть, которую распознают телефоны), часть text/html с рекламой для моего оператора связи, затем мое сообщение, но все завернутое в HTML, затем, наконец, вложение текстового файла с моим простым сообщением (которое я использую) (если я помещаю изображение в качестве вложения в сообщение, оно помещается в вложение 1, в кодировке Base64, затем моя текстовая часть прикрепляется как вложение 2)

у меня он работал с тем же форматом почты, что и у моего оператора связи, но когда я пропустил через него сообщение с чужого телефона, он потерпел неудачу по целому ряду ужасных причин.

У меня есть другие проекты, на которые я хотел бы расширить эту систему телефона->почты->анализа->команд, но мне нужен стабильный/надежный/универсальный анализатор, чтобы извлекать из почты различные части и использовать ее.

моей конечной целью было бы иметь функцию, в которую я мог бы передать необработанную почту по конвейеру и вернуть большой массив с ассоциативными подмассивами пар заголовков var:val и один для основного текста в виде целой строки

чем больше и больше я ищу по этому поводу, тем больше нахожу одно и то же:гигантские переразвитые пакеты обработки почты, которые делают все, что связано с почтой, или бесполезные (для меня в этом проекте) учебные пособия.

Я думаю, мне придется стиснуть зубы и просто тщательно написать что-нибудь самому.

Эта библиотека работает очень хорошо:

http://www.phpclasses.org/package/3169-PHP-Decode-MIME-e-mail-messages.html

Я столкнулся с той же проблемой, поэтому написал следующий класс:Электронная почта_Парсер.Он принимает необработанное электронное письмо и превращает его в красивый объект.

Для этого требуется PEAR Mail_mimeDecode, но его легко установить через WHM или прямо из командной строки.

Получи это здесь : https://github.com/optimumweb/php-email-reader-parser

Простой PhpMimeParser https://github.com/breakermind/PhpMimeParser Юо может вырезать mime-сообщения из файлов, строк.Получайте файлы, HTML и встроенные изображения.

$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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top