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 Exception\RuntimeException For invalid values.
* @return void
*/
public static function assertValid($value)
{
if (! self::isValid($value)) {
throw new Exception\InvalidArgumentException('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 Magento\Framework\Filter\RemoveAccents
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.
CodePudding user response:
In these situations, (when the file you want to change is located inside vendor
but outside of vendor/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 for \Magento\Framework\Mail\EmailMessage::convertAddressArrayToAddressList
and scrub the data there. Or, you can go further up and create a plugin for \Magento\Framework\Mail\Message::addTo
, etc.
CodePudding user response:
I got the solution by following Tyler's instruction to go back up the stack trace. Let me explain.
Class Laminas\Mail\Header\AbstractAddressList::getFieldValue
uses the following code to get customer name and email
foreach ($this->getAddressList() as $address) {
$email = $address->getEmail();
$name = $address->getName();
Here $this->getAddressList()
comes from
/**
* Get address list managed by this header
*
* @return AddressList
*/
public function getAddressList()
{
if (null === $this->addressList) {
$this->setAddressList(new AddressList());
}
return $this->addressList;
}
Where $this->addressList
gets assigned on
/**
* Set address list for this header
*
* @param AddressList $addressList
*/
public function setAddressList(AddressList $addressList)
{
$this->addressList = $addressList;
}
It uses Laminas\Mail\AddressList
class. So going in there
/**
* Create an address object
*
* @param string $email
* @param string|null $name
* @return Address
*/
protected function createAddress($email, $name)
{
return new Address($email, $name);
}
is used to set name and email. Here it creates object of class Laminas\Mail\Address
. So going in there, I found
/**
* Retrieve name, if any
*
* @return null|string
*/
public function getName()
{
return $this->name;
}
Modifying this return value was fixing my problem. So I created a plugin.
public function afterGetName(\Laminas\Mail\Address $subject,$result)
{
$removeAccent = new \Magento\Framework\Filter\RemoveAccents(true);
return $removeAccent->filter($result);
}