问题
Fatal error: Uncaught exception 'Zend\Mail\Exception\RuntimeException' with message 'Malformed header detected' in /var/www/html/plugins/ZendFramework/ZendFramework-2.4.7/library/Zend/Mail/Headers.php on line 88
This is the error message that I get when I try use the Zend\Mail\Storage\Imap::getMessage()
function. If I comment out the line, where the error is thrown(to be more precise I have to comment out both lines where this same error is thrown from the same function), I am able to read the subject of the message, but later on on some e-mails I get a:
Fatal error: Uncaught exception 'Zend\Mail\Header\Exception\InvalidArgumentException' with message 'Invalid header value detected' in /var/www/html/plugins/ZendFramework/ZendFramework-2.4.7/library/Zend/Mail/Header/GenericHeader.php on line 61
As I was testing the plugin, I didn't use any other extension or plugin while I got these errors, only the xDebug php extension, for debugging purposes. Here's my whole code:
try {
$mail = new Zend\Mail\Storage\Imap(array('host' => 'my.mail.host',
'user' => 'mymail@mymail.com',
'password' => 'xxxxxxxxx'));
}
catch (Zend\Mail\Exception $e) {
print_r($e);
}
$undecodable=array();
$mail->selectFolder('INBOX');
$list=array(1,2,3,4);
foreach ($list as $mid) {
$message=$mail->getMessage($mid);
$content=array(
'html' => array(
'part' => null,
'content' => null
),
'plain' => array(
'part' => null,
'content' => null
)
);
foreach (new RecursiveIteratorIterator($message) as $part) {
try {
$strtok=strtok($part->contentType, ';');
if (($strtok == 'text/html') || ($strtok == 'text/plain')) {
switch ($part->contentTransferEncoding) {
case 'base64':
$contentDecoded = base64_decode($part->getContent());
break;
case 'quoted-printable':
$contentDecoded = quoted_printable_decode($part->getContent());
break;
case 'default':
$contentDecoded='undecodable';
$undecodable[]=$part->contentTransferEncoding;
break;
}
$strtok=explode("/",$strtok);
$strtok=$strtok[1];
$content[$strtok]['part']=$part;
$content[$strtok]['content']=$contentDecoded;
}
} catch (Zend\Mail\Exception $e) {
// ignore
}
}
echo "Message: " . $message->subject."<BR>";
if (!empty($content['html']['content'])) echo "HTML part found! ";
if (!empty($content['plain']['content'])) echo "Plain part found! ";
echo "<BR><BR>Next message";
}
exit;
NOTE: The message id's are correct, I can fetch all of the by telneting the mail server, they all exist
回答1:
This is same problem that appears in Zend\Mail\Message::fromString() method. The method actually goes OK - since it takes the RAW message and creates the object. The problems however starts when you try to reach any of message headers using class methods. Header getters are implemented in an lazy-loading pattern - so the fact object consists "wrong" headers is not recognized once the object is created by Zend\Mail\Message::fromString() method.
The problem in my case appears once headers consists local characters (for Poland examples of them might be: ź, ć, ń, ę, ą etc. - other countries might have more or less of such as well). I was checking messages sent and received using different mailservers (Exchange, sendmail, Lotus) and different email clients (Outlook, Thunderbird, Windows Mail) - the headers in which these characters appeared were very different - e.g. Exchange (or Outlook - haven't checked that in 100% :) ) was adding Topic-Thread header (as far as I know for message threading) and the full topic was put in there - so this header, among others (such as Topic) also was throwing Exception once tried to be retrieved from the object.
The problem with Zend\Mail\Message::fromString() method was that it actually didn't allowed to specify encoding for the data that is retrieved from RAW message. It simply takes the data and puts into some storage inside the object - and the data itself is retrieved on a lazy-loading basis. So once you try to retrieve the header - the getter is throwing an Exception that data is in wrong format.
What I did was creating an inheriting class and changing the way Zend\Mail\Message::fromString() method works. Please find the code below:
public static function fromString(string $rawMessage, string $encoding = 'UTF-8') {
$message = parent::fromString($rawMessage);
foreach($message->getHeaders()->toArray() as $headerName => $headerValue) {
try {
$message->getHeaders()->get($headerName);
} catch (\Zend\Mail\Header\Exception\InvalidArgumentException $e) { // catches only if Header is wrongly structured
$message->getHeaders()->removeHeader($headerName);
$header = new \Zend\Mail\Header\GenericHeader();
$header->setEncoding($encoding);
$header->setFieldName($headerName);
$header->setFieldValue($headerValue);
$message->getHeaders()->addHeader($header);
}
}
$message->getHeaders()->setEncoding($encoding); // All headers are encoded in $encoding
return $message;
}
So what you can see in here is that I am introducing additional parameter $encoding for this method that allows you to specify the encoding for the message. The parameter has a default value of UTF-8 - mainly so method will be compatible downwards (eventhough PHP is throwing a Notice it is not :) ). Then it fires the standard, parent method to create an object - as I described before it will not fail yet :) Then we have a code that tries to "cleanse" the headers - by calling everyone of them and catching the InvalidArgumentException if thrown. If this exception is raised (and catched by try...catch... code) - the header is removed and recreated, but with proper $encoding. After all headers are checked and potentially replaced by correctly-encoded ones - I am calling additionally the setEncoding() method against all the headers again - for all headers that were not "replaced" in foreach() { try...catch.. } block - so all headers will be encoded in same encoding. I know you might think why don't you simply execute this setEncoding() method for all existing headers ?. Well - I tried - and this one fires the exception as well :) So as I assume, the method simply calls every header, but once calling - the lazy-loading causes Exception to be thrown.
The whole idea was also described in the following Article.
来源:https://stackoverflow.com/questions/32840448/zendmail-error-in-headers