When I try to send mail from admin order, if customer name has special German/Danish characters, email is not sending. Sending perfectly for other customers.
The error I found was
Invalid header value
I traced the error to file
vendor/laminas/laminas-http/src/Header/HeaderValue.php
/**
* Assert a header value is valid.
*
* @param string $value
* @throws ExceptionRuntimeException For invalid values.
* @return void
*/
public static function assertValid($value)
{
if (! self::isValid($value)) {
throw new ExceptionInvalidArgumentException('Invalid header value');
}
}
This function was getting called from
vendor/laminas/laminas-mail/src/Header/AbstractAddressList.php
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
{
$emails = [];
$encoding = $this->getEncoding();
foreach ($this->getAddressList() as $address) {
$email = $address->getEmail();
$name = $address->getName();
// quote $name if value requires so
if (! empty($name) && (false !== strpos($name, ',') || false !== strpos($name, ';'))) {
// FIXME: what if name contains double quote?
$name = sprintf('"%s"', $name);
}
if ($format === HeaderInterface::FORMAT_ENCODED
&& 'ASCII' !== $encoding
) {
if (! empty($name)) {
$name = HeaderWrap::mimeEncodeValue($name, $encoding);
}
if (preg_match('/^(.+)@([^@]+)$/', $email, $matches)) {
$localPart = $matches[1];
$hostname = $this->idnToAscii($matches[2]);
$email = sprintf('%s@%s', $localPart, $hostname);
}
}
if (empty($name)) {
$emails[] = $email;
} else {
$emails[] = sprintf('%s <%s>', $name, $email);
}
}
// Ensure the values are valid before sending them.
if ($format !== HeaderInterface::FORMAT_RAW) {
foreach ($emails as $email) {
HeaderValue::assertValid($email);
}
}
return implode(',' . Headers::FOLDING, $emails);
}
I have found a class MagentoFrameworkFilterRemoveAccents
which replaces special characters with their usable counterparts, but as the function assertValid
is a static function inside a final class, and function getFieldValue
is inside an abstract class, I am unable to replace the characters.
2
Answers
I got the solution by following Tyler's instruction to go back up the stack trace. Let me explain.
Class
LaminasMailHeaderAbstractAddressList::getFieldValue
uses the following code to get customer name and emailHere
$this->getAddressList()
comes fromWhere
$this->addressList
gets assigned onIt uses
LaminasMailAddressList
class. So going in thereis used to set name and email. Here it creates object of class
LaminasMailAddress
. So going in there, I foundModifying this return value was fixing my problem. So I created a plugin.
In these situations, (when the file you want to change is located inside
vendor
but outside ofvendor/mgento
,) you’re better off looking further up the stack trace for another place to make your changes. For instance, you could create a Preference forMagentoFrameworkMailEmailMessage::convertAddressArrayToAddressList
and scrub the data there. Or, you can go further up and create a plugin forMagentoFrameworkMailMessage::addTo
, etc.