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