Синтаксический анализ тела электронной почты с 7bit Content-Transfer-Encoding-PHP

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

Я думаю, что, сейчас, я наполовину выучил RFC 2822 ("формат интернет-сообщений" руководящие принципы документа), читать через код обработки электронной почты для полудюжины CMSes с открытым исходным кодом,а также читать bajillion форум сообщений, сообщений в блоге и т.д. работа с обработкой электронной почты в РНР.

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

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

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

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

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_*), но они, очевидно, нуждаются в большем декодировании, прежде чем я смогу передать их как открытый текст. Есть ли способ надежно преобразовать все сообщения с предположительно-7Bit кодировками в открытый текст?

3 ответов


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

Я построил более надежной 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(), но иногда PHP выдавал исключения, которые были расплывчатыми и бесполезными, поэтому я отказался от этого подхода.


Я знаю, это старый вопрос.... Но сейчас я сталкиваюсь с этой проблемой, и кажется, что у 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; Не $encoding = $structure->encoding Но!--2-->

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

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