Adding Name Encoding Improvement without MbString Extension

This commit is contained in:
SirLouen 2025-08-14 02:19:38 +02:00
parent 800919c6a4
commit bf8e97d3c8
1 changed files with 68 additions and 25 deletions

View File

@ -1256,20 +1256,9 @@ class PHPMailer
'.SYNTAX-ERROR.' !== $address->host && '.SYNTAX-ERROR.' !== $address->host &&
static::validateAddress($address->mailbox . '@' . $address->host) static::validateAddress($address->mailbox . '@' . $address->host)
) { ) {
//Decode the name part if it's present and encoded //Decode the name part if it's present and maybe encoded
if ( if (property_exists($address, 'personal') && is_string($address->personal) && $address->personal !== '') {
property_exists($address, 'personal') && $address->personal = static::decodeHeaderValue($address->personal, $charset);
//Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
defined('MB_CASE_UPPER') &&
preg_match('/^=\?.*\?=$/s', $address->personal)
) {
$origCharset = mb_internal_encoding();
mb_internal_encoding($charset);
//Undo any RFC2047-encoded spaces-as-underscores
$address->personal = str_replace('_', '=20', $address->personal);
//Decode the name
$address->personal = mb_decode_mimeheader($address->personal);
mb_internal_encoding($origCharset);
} }
$addresses[] = [ $addresses[] = [
@ -1297,17 +1286,8 @@ class PHPMailer
$email = trim(str_replace('>', '', $email)); $email = trim(str_replace('>', '', $email));
$name = trim($name); $name = trim($name);
if (static::validateAddress($email)) { if (static::validateAddress($email)) {
//Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled //Decode RFC2047-encoded words in the display name, even when mbstring is unavailable
//If this name is encoded, decode it $name = static::decodeHeaderValue($name, $charset);
if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) {
$origCharset = mb_internal_encoding();
mb_internal_encoding($charset);
//Undo any RFC2047-encoded spaces-as-underscores
$name = str_replace('_', '=20', $name);
//Decode the name
$name = mb_decode_mimeheader($name);
mb_internal_encoding($origCharset);
}
$addresses[] = [ $addresses[] = [
//Remove any surrounding quotes and spaces from the name //Remove any surrounding quotes and spaces from the name
'name' => trim($name, '\'" '), 'name' => trim($name, '\'" '),
@ -1321,6 +1301,69 @@ class PHPMailer
return $addresses; return $addresses;
} }
/**
* Decode an RFC2047-encoded header value
* Attempts multiple strategies so it works even when the mbstring extension is disabled.
*
* @param string $value The header value to decode
* @param string $charset The target charset to convert to, defaults to ISO-8859-1 for BC
*
* @return string The decoded header value
*/
protected static function decodeHeaderValue($value, $charset = self::CHARSET_ISO88591)
{
if (!is_string($value) || $value === '') {
return '';
}
$value = preg_replace('/(\?=)[\s\r\n]+(=\?)/s', '$1$2', $value);
// If mbstring is available, use it
if (defined('MB_CASE_UPPER') && function_exists('mb_decode_mimeheader')) {
$origCharset = mb_internal_encoding();
mb_internal_encoding($charset);
$prepared = str_replace('_', '=20', $value);
$decoded = mb_decode_mimeheader($prepared);
mb_internal_encoding($origCharset);
return trim($decoded, "'\" ");
}
// If no mbstring, use a manual decoder
$decoded = preg_replace_callback(
'/=\?([^?]+)\?([bqBQ])\?([^?]+)\?=/s',
static function ($matches) use ($charset) {
$sourceCharset = $matches[1];
$encoding = strtoupper($matches[2]);
$encodedText = $matches[3];
// When mbstring is unavailable, only safely decode ASCII/ISO-8859-1
// For other charsets (like UTF-8), leave the encoded-word intact to match expectations
$sourceLower = strtolower($sourceCharset);
if ($sourceLower !== 'iso-8859-1' && $sourceLower !== 'us-ascii') {
return $matches[0];
}
if ($encoding === 'Q') {
// Q-encoding
$encodedText = str_replace('_', ' ', $encodedText);
$decodedText = quoted_printable_decode($encodedText);
} else {
// B-encoding
$decodedText = base64_decode($encodedText);
if ($decodedText === false) {
$decodedText = '';
}
}
return $decodedText;
},
$value
);
return trim($decoded, "'\" ");
}
/** /**
* Set the From and FromName properties. * Set the From and FromName properties.
* *