Parsing Email Body с 7BIT Content-Transfer-Encoding – PHP

В последнее время я внедряю некоторые функции обработки электронной почты на основе PHP / IMAP, и большинство из них отлично работают, за исключением декодирования тела сообщения (в некоторых случаях).

Я думаю, что к настоящему времени я полузанял RFC 2822 (руководство по формату «Формат интернет-сообщений»), прочитал код обработки электронной почты для полдюжины CMS с открытым исходным кодом и прочитал сообщения на форуме bajillion, сообщения в блогах, и т. д., занимающихся обработкой электронной почты на PHP.

Я также разблокировал и полностью переписал класс для PHP, Imap , и класс обрабатывает электронную почту с уважением. У меня есть несколько полезных методов для обнаружения автоответчиков (для работы вне офиса, старых адресов и т. Д.), Декодирования base64 и 8bit сообщения и т. д.

Тем не менее, одна вещь, которую я просто не могу надежно работать (или, иногда, вообще), когда приходит сообщение с Content-Transfer-Encoding: 7bit .

Похоже, что разные почтовые клиенты / службы интерпретируют 7BIT чтобы иметь в виду разные вещи. Я получил несколько писем, которые предположительно являются 7BIT , которые на самом деле кодируются Base64. У меня есть некоторые, которые на самом деле закодированы в кавычках. И некоторые, которые никоим образом не закодированы. И некоторые, которые являются HTML, но не указаны как HTML, и они также перечислены как 7BIT

Вот несколько примеров (сокращений) тел сообщений, полученных с 7-битными кодировками:

1:

 A random message=20 Sent from my iPhone 

2:

 PGh0bWwgeG1sbnM6dj0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTp2bWwi IHhtbG5zOm89InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206b2ZmaWNlOm9m 

3:

 tangerine apricot pepper.=0A=C2=A0=0ALet me know if you have any availabili= ty over the next month or so. =0A=C2=A0=0AThank you,=0ANames Withheld=0A908= -319-5916=0A=C2=A0=0A=C2=A0=0A=C2=A0=0A=0A=0A______________________________= __=0AFrom: Names Witheld =0ATo: Names Withheld= 

Все они отправляются с кодировками «7Bit» (ну, по крайней мере, согласно PHP / imap_* ), но они явно нуждаются в расшифровке, прежде чем я смогу передать их в виде открытого текста. Есть ли способ надежно преобразовать все сообщения с предположительно-7-битными кодировками в открытый текст?

Проведя немного больше времени, я решил просто написать эвристическое обнаружение, как Макс предложил в комментариях к моему первоначальному вопросу.

Я построил более мощный decode7Bit() в Imap.php , который проходит через кучу общих кодированных символов (например, =A0 ) и заменяет их эквивалентами UTF-8, а затем также декодирует сообщения, если они выглядят так, как будто они кодируются base64:

 /** * Decodes 7-Bit text. * * PHP seems to think that most emails are 7BIT-encoded, therefore this * decoding method assumes that text passed through may actually be base64- * encoded, quoted-printable encoded, or just plain text. Instead of passing * the email directly through a particular decoding function, this method * runs through a bunch of common encoding schemes to try to decode everything * and simply end up with something *resembling* plain text. * * Results are not guaranteed, but it's pretty good at what it does. * * @param $text (string) * 7-Bit text to convert. * * @return (string) * Decoded text. */ public function decode7Bit($text) { // If there are no spaces on the first line, assume that the body is // actually base64-encoded, and decode it. $lines = explode("\r\n", $text); $first_line_words = explode(' ', $lines[0]); if ($first_line_words[0] == $lines[0]) { $text = base64_decode($text); } // Manually convert common encoded characters into their UTF-8 equivalents. $characters = array( '=20' => ' ', // space. '=E2=80=99' => "'", // single quote. '=0A' => "\r\n", // line break. '=A0' => ' ', // non-breaking space. '=C2=A0' => ' ', // non-breaking space. "=\r\n" => '', // joined line. '=E2=80=A6' => '…', // ellipsis. '=E2=80=A2' => '•', // bullet. ); // Loop through the encoded characters and replace any that are found. foreach ($characters as $key => $value) { $text = str_replace($key, $value, $text); } return $text; } 

Это было взято из версии 1.0-beta2 класса Imap для PHP, который у меня есть на GitHub.

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

Я знаю, что это старый вопрос … Но сейчас я сталкиваюсь с этой проблемой, и теперь кажется, что у PHP есть решение.

эта функция imap_fetchstructure () даст вам тип кодирования.

 0 7BIT 1 8BIT 2 BINARY 3 BASE64 4 QUOTED-PRINTABLE 5 OTHER 

оттуда вы должны иметь возможность создать такую ​​функцию, чтобы декодировать сообщение

 function _encodeMessage($msg, $type){ if($type == 0){ return mb_convert_encoding($msg, "UTF-8", "auto"); } elseif($type == 1){ return imap_8bit($msg); //imap_utf8 } elseif($type == 2){ return imap_base64(imap_binary($msg)); } elseif($type == 3){ return imap_base64($msg); } elseif($type == 4){ return imap_qprint($msg); //return quoted_printable_decode($msg); } else { return $msg; } } 

и вы можете вызвать эту функцию так

 $struct = imap_fetchstructure($conn, $messageNumber, 0); $message = imap_fetchbody($conn, $messageNumber, 1); $message = _encodeMessage($message, $struct->encoding); echo $message; 

Я надеюсь, что это помогает кому-то 🙂

$structure = imap_fetchstructure; NOT $encoding = $structure->encoding BUT $encoding = $structure->parts[ $p ]->encoding

Я думаю, что у меня была такая же проблема, теперь она решена. (7 бит не конвертировался в UTF-8, продолжал получать ASCII) Я думал, что у меня 7 бит, но сменил код на «НО». Я получил $encoding=4 , а не $encoding=0 это значит, что мне нужно imap_qprint($body) и mb_convert_encoding($body, 'UTF-8', $charset) чтобы получить то, что я хотел.

В любом случае проверьте номер кодировки! (должно быть 4 не равно нулю)