Анализ текста электронной почты с 7-битным контентом- Передача-Кодирование- PHP


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

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

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

Однако единственное, что я просто не могу надежно работать (или, иногда, вообще), - это когда приходит сообщение с 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=

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

Author: geerlingguy, 2012-10-02

3 answers

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

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

 9
Author: geerlingguy, 2015-04-20 16:01:50

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

Надеюсь, это кому-то поможет:)

 5
Author: Jaylen, 2015-03-16 21:34:18

$structure = imap_fetchstructure; НЕ $encoding = $structure->encoding НО $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, а не ноль)

 0
Author: taka02, 2017-06-15 03:16:39