zendframework / zend-mail

Mail component from Zend Framework
BSD 3-Clause "New" or "Revised" License
96 stars 111 forks source link

Problem with special characters in headers #203

Closed jomofcw closed 6 years ago

jomofcw commented 6 years ago

Hello,

Using zend-mail module 2.9.0, I encounter an issue when using some special characters in headers. Here is the stack trace of the error :

[Tue Apr 17 14:28:32.479564 2018] [php7:error] [pid 3647] [client 192.168.1.36:54062] PHP Fatal error:  Uncaught Zend\\Mail\\Header\\Exception\\RuntimeException: Invalid header value detected in /home/myUser/myProject/vendor/zendframework/zend-mail/src/Header/HeaderValue.php:114
Stack trace:
#0 /home/myUser/myProject/vendor/zendframework/zend-mail/src/Header/AbstractAddressList.php(150): Zend\\Mail\\Header\\HeaderValue::assertValid('HIDENAME Eug\\xC3\\xA8n...')
#1 /home/myUser/myProject/vendor/zendframework/zend-mail/src/Header/AbstractAddressList.php(194): Zend\\Mail\\Header\\AbstractAddressList->getFieldValue(true)
#2 /home/myUser/myProject/vendor/zendframework/zend-mail/src/Headers.php(428): Zend\\Mail\\Header\\AbstractAddressList->toString()
#3 /home/myUser/myProject/vendor/zendframework/zend-mail/src/Transport/Sendmail.php(245): Zend\\Mail\\Headers->toString()
#4 /home/myUser/myProject/vendor/zendframework/zend-mail/src/Transport/Sendmail.php(125): Zend\\Mail\\Transport\\Sendmail->prepareHeaders(Object(Zend\\Mail\\Message))
#5 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/email/elementEmail.class.php(160): Zend\\Mail\\Transport\\Sendmail->send(Object(Zend\\Mail\\Message))
#6 /home/myUser/myProject/www/site/source/prive/inc/classe/e-commerce/elementContact.class.php(988): elementEmail->envoyer(Array, Array, 'Message Luminis...')
#7 /home/myUser/myProject/www/site/source/prive/modele/luminis/magasin/general/page/module/contact/processus.inc.php(105): elementContact->enregistrer(Array, Array)
#8 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/page/bloc/elementModule.class.php(61): include('/home/myUser/...')
#9 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/page/bloc/elementModule.class.php(68): elementModule->{closure}(Object(elementModule), '/home/myUser/...', NULL)
#10 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/generale/bloc.class.php(195): elementModule->processus_charger('/home/myUser/...', Array)
#11 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/page/elementPage.class.php(801): IDcms_classe_bloc->assembler(Array)
#12 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/page/elementPage.class.php(1426): elementPage->assembler()
#13 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/page/elementPage.class.php(1435): elementPage->assembler_traiter()
#14 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/moteur.class.php(358): elementPage->assembler_afficher()
#15 /home/myUser/myProject/ressource/iDdev/moteur/inc/classe/moteur.class.php(279): Moteur::proceder_suite()
#16 /home/myUser/myProject/ressource/iDdev/moteur/moteur.php(37): Moteur::proceder()
#17 /home/myUser/myProject/www/site/source/public/_base.php(37): require_once('/home/myUser/...')
#18 {main}
  thrown in /home/myUser/myProject/vendor/zendframework/zend-mail/src/Header/HeaderValue.php on line 114, referer: http://www.mydomain.com/contact

Do you need me to provide more ? Just ask ! Thanks for your work.

michalbundyra commented 6 years ago

@jomofcw Can you provide an example header? What 'special characters' are you using?

jomofcw commented 6 years ago

@webimpress here is the headers that seems to be the problem (after many test case to isolate it) :

$message->setFrom('my@mail.com','HIDENAME Eugène');

The character "è" is the special one which make it crash. Replacing it by "é" still crash (and probably with each others special chars).

michalbundyra commented 6 years ago

@jomofcw Hm... Isn't it right? As I understand from #28 - either è and é are non-us-ascii characters, so these are not allowed in the header value.

jomofcw commented 6 years ago

@webimpress, previously, when I used zend-mail from ZendFramework v1 I add no problem with those special characters... and anyway, it's commonly used in name in france for example. So it may be a solution to use it in header... nop ? Or anyway, zend-mail shall "process" it to avoid this type of error, nop ?

michalbundyra commented 6 years ago

@jomofcw Encode using base64, the question if it should be done automatically or not...

jomofcw commented 6 years ago

@webimpress I understand. Currently, I replace the special chars by its basics équivalent ("è" with "e" for example), but it's not respectfull of the user input...

If I do it on my own, in your opinion, when and how shall I encode it base64 ? Something like that (but it sounds like wrong to me as it return something not human readable) :

$message->setFrom('my@mail.com',base64_encode('HIDENAME Eugène'));
glensc commented 6 years ago

provide complete standalone reproducer.

taking things out of context is not reproducible, the line itself on it's own is not incorrect.

likely the encoding in $message is not set to utf-8 for some reason.

jomofcw commented 6 years ago

@glensc dunno if it's what you mean, but here is the method code where I build and send the message :

    function envoyer($expediteur,$destinataires,$objet,$site    =    null,$chemin_piece_jointe = null)
    {
        if    ($this->assembler(null,$site)&&!empty($this->sortie_resultat))
        {

            $message_parties    =    array();

            $html            =    new Zend\Mime\Part($this->sortie_resultat);
            $html->type        =    Zend\Mime\Mime::TYPE_HTML;
            $html->charset    =    'utf-8';
            $html->encoding    =    Zend\Mime\Mime::ENCODING_QUOTEDPRINTABLE;
            array_push($message_parties,$html);

            if ($chemin_piece_jointe != null)
            {
                // Attachement de la pièce joint au mail
                $piece_jointe                =    new Zend\Mime\Part(fopen($chemin_piece_jointe,'r'));
                $piece_jointe->type            =    'application/xls';
                $chemin_piece_jointe_details=    pathinfo($chemin_piece_jointe);
                $piece_jointe->filename        =    (preg_match('`formulaire_retour_`',$chemin_piece_jointe_details['basename'])?'hors-delais.xls':$chemin_piece_jointe_details['basename']);
                $piece_jointe->disposition    =    Zend\Mime\Mime::DISPOSITION_ATTACHMENT;
                $piece_jointe->encoding        =    Zend\Mime\Mime::ENCODING_BASE64;
                array_push($message_parties,$piece_jointe);
            }

            $corps    =    new Zend\Mime\Message();
            $corps->setParts($message_parties);

            $message    =    new    Zend\Mail\Message('utf-8');

            if    (!is_array($expediteur))
                $expediteur    =    array($expediteur);
            $message->setFrom($expediteur[0],(!empty($expediteur[1])?$expediteur[1]:null));

            if    (!is_array($destinataires))
                $destinataires    =    array('to'=>$destinataires);
            foreach    ($destinataires    as    $type=>$destinataire_liste)
            {
                if    (!is_array($destinataire_liste))
                    $destinataire_liste    =    array($destinataire_liste);
                foreach    ($destinataire_liste    as    $destinataire)
                {
                    if    (!is_array($destinataire))
                        $destinataire    =    array($destinataire);
                    if    (defined('DEV_MODE')&&DEV_MODE)// Filtre pour éviter que des e-mails ne soient envoyé à des clients depuis le DEV
                    {
                        if    (!preg_match(DEV_EMAIL_MASQUE,$destinataire[0]))
                            continue;
                    }
                    if    (empty($type)||!in_array($type,array('cc','bcc','to')))
                        $type    =    'to';

                    switch    ($type)
                    {
                        case    'cc':
                            $message->addCc(strtolower($destinataire[0]),(!empty($destinataire[1])?$destinataire[1]:null));
                            break;
                        case    'bcc':
                            $message->addBcc(strtolower($destinataire[0]),(!empty($destinataire[1])?$destinataire[1]:null));
                            break;
                        case    'to':
                        default:
                            $message->addTo(strtolower($destinataire[0]),(!empty($destinataire[1])?$destinataire[1]:null));
                            break;
                    }
                }
            }
            $message
                ->setReplyTo($expediteur[0],(!empty($expediteur[1])?$expediteur[1]:null))
                ->setSubject(strip_tags($objet))
                ->setBody($corps)
            ;

            if    (count($message_parties)>1)
            {
                $contentTypeHeader = $message->getHeaders()->get('Content-Type');
                $contentTypeHeader->setType('multipart/related');
            }

            $transport = new Zend\Mail\Transport\Sendmail();
            $transport_resultat    =    $transport->send($message);

            return    $transport_resultat;
        }
        return    false;
    }
glensc commented 6 years ago

standalone means that if i take your script it actually runs in my environment. and it contains only necessary code to show the problem, nothing else.

<?php

require 'vendor/autoload.php';

// YOUR CODE HERE
michalbundyra commented 6 years ago

@jomofcw

Try this:

$message->setFrom('my@mail.com', '=?UTF-8?B?'.base64_encode('HIDENAME Eugène').'?=');

But as I said - maybe it should be done automatically inside the library...

jomofcw commented 6 years ago

@webimpress it doesn't change anything (strange...).

@glensc you'll find the code and the stack trace below :

<?php
// phpinfo();
// die('ok');

require_once(__DIR__.'/../../../../vendor/autoload.php');

$expediteur     =   $destinataire   =   array('my@mail.com','NOM Prénom');
$objet          =   'Test e-mail';
$contenu_html   =   '<html><body>Test e-mail</body></html>';

$message_parties    =   array();

$html           =   new Zend\Mime\Part($contenu_html);
$html->type     =   Zend\Mime\Mime::TYPE_HTML;
$html->charset  =   'utf-8';
$html->encoding =   Zend\Mime\Mime::ENCODING_QUOTEDPRINTABLE;
array_push($message_parties,$html);

$corps  =   new Zend\Mime\Message();
$corps->setParts($message_parties);

$message    =   new Zend\Mail\Message('utf-8');

$message
    ->setFrom(strtolower($expediteur[0]),$expediteur[1])
    ->addTo(strtolower($destinataire[0]),$destinataire[1])
    ->setReplyTo($expediteur[0],$expediteur[1])
    ->setSubject(strip_tags($objet))
    ->setBody($corps)
;

if  (count($message_parties)>1)
{
    $contentTypeHeader = $message->getHeaders()->get('Content-Type');
    $contentTypeHeader->setType('multipart/related');
}

$transport = new Zend\Mail\Transport\Sendmail();
$transport_resultat =   $transport->send($message);
[Tue Apr 17 17:40:42.583809 2018] [php7:error] [pid 3647] [client 192.168.1.36:58492] PHP Fatal error:  Uncaught Zend\\Mail\\Header\\Exception\\RuntimeException: Invalid header value detected in /home/myUser/myProject/vendor/zendframework/zend-mail/src/Header/HeaderValue.php:114
Stack trace:
#0 /home/myUser/myProject/vendor/zendframework/zend-mail/src/Header/AbstractAddressList.php(150): Zend\\Mail\\Header\\HeaderValue::assertValid('NOM Pr\\xC3\\xA9nom <nv...')
#1 /home/myUser/myProject/vendor/zendframework/zend-mail/src/Transport/Sendmail.php(171): Zend\\Mail\\Header\\AbstractAddressList->getFieldValue(true)
#2 /home/myUser/myProject/vendor/zendframework/zend-mail/src/Transport/Sendmail.php(122): Zend\\Mail\\Transport\\Sendmail->prepareRecipients(Object(Zend\\Mail\\Message))
#3 /home/myUser/myProject/www/site/source/public/test.php(39): Zend\\Mail\\Transport\\Sendmail->send(Object(Zend\\Mail\\Message))
#4 {main}
  thrown in /home/myUser/myProject/vendor/zendframework/zend-mail/src/Header/HeaderValue.php on line 114

Does it help, please ?

glensc commented 6 years ago

yes. of course, because i was able to run the code on my machine.

i created gist of your problem: https://gist.github.com/glensc/c3787087a9cd083fa3d7d071f0b450c2 (git clone to see history)

but your fix is simple:

@@ -18,7 +18,8 @@ array_push($message_parties,$html);
 $corps =       new Zend\Mime\Message();
 $corps->setParts($message_parties);

-$message       =       new     Zend\Mail\Message('utf-8');
+$message       =       new     Zend\Mail\Message();
+$message->setEncoding('utf-8');

Zend\Mail\Message does not have constructor parameter, you need to use setEncoding to set encoding.

if this solved your problem, please close issue yourself.

froschdesign commented 6 years ago

@jomofcw Look also in the documentation of the message class.

jomofcw commented 6 years ago

@glensc great ! it works ;-). Thanks for your precious help. Thanks to @froschdesign also.

I should have find it on my own, sorry for your time.