Analizando el cuerpo del correo electrónico con Content 7BIT-Transfer-Encoding – PHP

Recientemente he estado implementando algunas funciones de manejo de correo electrónico basadas en PHP / IMAP, y tengo todo funcionando a la perfección, a excepción de la desencoding del cuerpo del mensaje (en algunas circunstancias).

Creo que, hasta ahora, he memorizado a medias el RFC 2822 (las pautas para el documento ‘Formato de mensaje de Internet’), he leído el código de manejo de correo electrónico de media docena de CMS de código abierto y he leído un montón de mensajes en el foro, publicaciones de blog, etc. que se ocupan del manejo del correo electrónico en PHP.

También he bifurcado y reescrito completamente una clase para PHP, Imap , y la clase maneja bien el correo electrónico. Tengo algunos métodos útiles para detectar respuestas automáticas (para fuera de la oficina, direcciones antiguas, etc.), decodificar base64 y 8 bits. mensajes, etc.

Sin embargo, una cosa que simplemente no puedo hacer funcionar de manera confiable (o, a veces, en absoluto) es cuando aparece un mensaje con Content-Transfer-Encoding: 7bit .

Parece que diferentes clientes / servicios de correo electrónico interpretan que 7BIT significa diferentes cosas. Recibí algunos correos electrónicos supuestamente de 7 7BIT que, en realidad, están codificados en Base64. Obtuve algunos que en realidad están codificados, codificados para imprimir. Y algunos que no están codificados de ninguna manera. Y algunos que son HTML, pero no están indicados como HTML, y también están listados como 7BIT

Aquí hay algunos ejemplos (recortes) de cuerpos de mensaje recibidos con codificaciones de 7 bits:

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= 

Todos estos se envían con codificaciones ‘7 bits’ (bueno, al menos de acuerdo con PHP / imap_* ), pero obviamente necesitan más deencoding antes de que pueda pasarlos como texto sin formato. ¿Hay alguna forma de convertir de manera confiable todos los mensajes con codificaciones supuestamente de 7 bits a texto sin formato?

Después de pasar un poco más de tiempo, decidí escribir algo de detección heurística, como Max sugirió en los comentarios sobre mi pregunta original.

decode7Bit() método decode7Bit() más robusto en Imap.php , que pasa por un grupo de caracteres codificados comunes (como =A0 ) y los reemplaza por sus equivalentes UTF-8, y luego también decodifica los mensajes si se ven como están codificados en 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; } 

Esto fue tomado de la versión 1.0-beta2 de la clase Imap para PHP que tengo en GitHub.

Si tiene alguna idea para hacer esto más eficiente, hágamelo saber. Originalmente intenté ejecutar todo a través de quoted_printable_decode() , pero a veces PHP arrojaba excepciones que eran vagas e inútiles, así que abandoné ese enfoque.

Sé que esta es una vieja pregunta … Pero me estoy encontrando con este problema ahora y parece que PHP tiene una solución ahora.

esta función imap_fetchstructure () le dará el tipo de encoding.

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

a partir de ahí, debería poder crear una función como esta para decodificar el mensaje

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

y puedes llamar a esta función como tal

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

Espero que esto ayude a alguien 🙂

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

Creo que tuve el mismo problema, ahora está resuelto. (7bit no se convirtió a UTF-8, siguió obteniendo ASCII) Pensé que tenía 7 bits, pero cambiando el código a “PERO” obtuve $encoding=4 , not $encoding=0 que significa que tengo que imap_qprint($body) y mb_convert_encoding($body, 'UTF-8', $charset) para obtener lo que quería.

¡De todos modos, comprueba el número de encoding! (debe ser 4 no cero)