source: spip-zone/_plugins_/facteur/trunk/classes/facteur.php @ 112651

Last change on this file since 112651 was 112651, checked in by maieul@…, 2 years ago

L'option forcer_from fait deux choses:

  • changer l'adresse d'envoi si domaine différent de celle du webmestre
  • et, si on change l'adresse d'envoi, changer aussi le nom de l'expediteur

Le premier point permet de fonctionner avec des serveurs SMTP stricts,
qui vérifient le domaine.

Toutefois, on peut vouloir garder le nom expediteur, même si on change
l'adresse expeditrice (ce que fait, par ex, Formidable lorsqu'on ne
force pas le champ From).

Une nouvelle option permet cela.

  • Property svn:executable set to *
File size: 14.4 KB
Line 
1<?php
2/*
3 * Plugin Facteur 2
4 * (c) 2009-2011 Collectif SPIP
5 * Distribue sous licence GPL
6 *
7 */
8
9if (!defined("_ECRIRE_INC_VERSION")) return;
10
11include_spip('inc/charsets');
12include_spip('inc/texte');
13include_spip('inc/filtres');
14
15if (!class_exists('PHPMailer')) {
16        include_spip('phpmailer-php5/class.phpmailer');
17        include_spip('phpmailer-php5/class.smtp');
18}
19
20include_spip('facteur_fonctions');
21
22/**
23 * Wrapper de spip_log pour par PHPMailer
24 * @param $message
25 * @param $level
26 */
27function facteur_log_debug($message,$level){
28        spip_log("$level: ".trim($message),"facteur"._LOG_DEBUG);
29}
30
31
32class Facteur extends PHPMailer {
33        /**
34         * From force si From pas dans le bon domaine
35         * @var string
36         */
37        public $ForceFrom = '';
38
39        /**
40         * FromName force si From pas dans le bon domaine
41         * @var string
42         */
43        public $ForceFromName = '';
44
45        /**
46         * @param $email
47         * @param $objet
48         * @param $message_html
49         * @param $message_texte
50         * @param array $options
51         *
52         */
53        public function __construct($email, $objet, $message_html, $message_texte, $options = array()) {
54                // On récupère toutes les options par défaut depuis le formulaire de config
55                $defaut = array();
56                foreach (array(
57                        'adresse_envoi', 'adresse_envoi_email', 'adresse_envoi_nom', 'forcer_from', 'garder_fromname',
58                        'cc', 'bcc',
59                        'smtp', 'smtp_host', 'smtp_port', 'smtp_auth',
60                        'smtp_username', 'smtp_password', 'smtp_secure', 'smtp_sender', 'smtp_tls_allow_self_signed',
61                        'filtre_images', 'filtre_iso_8859',
62                ) as $config) {
63                        $defaut[$config] = isset($GLOBALS['meta']["facteur_$config"]) ? $GLOBALS['meta']["facteur_$config"] : '';
64                }
65                // On fusionne les options avec d'éventuelles surcharges lors de l'appel
66                $options = array_merge($defaut, $options);
67
68                // par defaut on log rien car tres verbeux
69                // on utilise facteur_log_debug qui filtre log SPIP en _LOG_DEBUG
70                $this->SMTPDebug = 0;
71                $this->Debugoutput = "facteur_log_debug";
72                // Il est possible d'avoir beaucoup plus de logs avec 2, 3 ou 4, ce qui logs les échanges complets avec le serveur
73                // utiliser avec un define('_MAX_LOG',1000); car sinon on est limite a 100 lignes par hit et phpMailer est tres verbeux
74                if (defined('_FACTEUR_DEBUG_SMTP')) {
75                        $this->SMTPDebug = _FACTEUR_DEBUG_SMTP ;
76                }
77                $this->exceptions = false;
78
79
80                if (
81                        $options['adresse_envoi'] == 'oui'
82                        and $options['adresse_envoi_email']
83                ) {
84                        $this->From = $options['adresse_envoi_email'];
85                }
86                else {
87                        $this->From = (isset($GLOBALS['meta']["email_envoi"]) AND $GLOBALS['meta']["email_envoi"]) ?
88                                $GLOBALS['meta']["email_envoi"]
89                                : $GLOBALS['meta']['email_webmaster'];
90                }
91
92                // Si plusieurs emails dans le from, pas de Name !
93                if (strpos($this->From,",") === false) {
94                        if (
95                                $options['adresse_envoi'] == 'oui'
96                                and $options['adresse_envoi_nom']
97                        ) {
98                                $this->FromName = $options['adresse_envoi_nom'];
99                        }
100                        // Par défaut, l'envoyeur est le nom du site
101                        else {
102                                $this->FromName = strip_tags(extraire_multi($GLOBALS['meta']['nom_site']));
103                        }
104                }
105
106                // si forcer_from, on sauvegarde le From et FromName par defaut, qui seront utilises
107                // si From n'est pas dans le meme domaine
108                // (utiliser le facteur avec un service externe qui necessite la validation des domaines d'envoi)
109                // On peut avoir une sous-option garder_fromname pour garder le FromName passé
110                if ($options['forcer_from']=='oui'){
111                        $this->ForceFrom = $this->From;
112                        if ($options['garder_fromname']!='oui') {
113                                $this->ForceFromName = $this->FromName;
114                        }
115                }
116
117                $this->CharSet = "utf-8";
118                $this->Mailer = 'mail';
119                $this->Subject = unicode_to_utf_8(charset2unicode($objet,$GLOBALS['meta']['charset']));
120
121                //Pour un envoi multiple de mail, $email doit être un tableau avec les adresses.
122                if (is_array($email)) {
123                        foreach ($email as $cle => $adresseMail) {
124                                if (!$this->AddAddress($adresseMail)) {
125                                        spip_log("Erreur AddAddress $adresseMail : ".print_r($this->ErrorInfo, true), 'facteur.'._LOG_ERREUR);
126                                }
127                        }
128                }
129                elseif (!$this->AddAddress($email)) {
130                        spip_log("Erreur AddAddress $email : ".print_r($this->ErrorInfo, true), 'facteur.'._LOG_ERREUR);
131                }
132
133                // Retour des erreurs
134                if (!empty($options['smtp_sender'])) {
135                        $this->Sender = $options['smtp_sender'];
136                        $this->AddCustomHeader("Errors-To: ".$this->Sender);
137                }
138
139                // Destinataires en copie, seulement s'il n'y a pas de destinataire de test
140                if (!defined('_TEST_EMAIL_DEST')){
141                        if (!empty($options['cc'])) {
142                                $this->AddCC($options['cc']);
143                        }
144                        if (!empty($options['bcc'])) {
145                                $this->AddBCC($options['bcc']);
146                        }
147                }
148
149                // Si on envoie avec un SMTP explicite
150                if (isset($options['smtp']) AND $options['smtp'] == 'oui') {
151                        $this->Mailer   = 'smtp';
152                        $this->Host     = $options['smtp_host'];
153                        $this->Port     = $options['smtp_port'];
154
155                        // SMTP authentifié
156                        if ($options['smtp_auth'] == 'oui') {
157                                $this->SMTPAuth = true;
158                                $this->Username = $options['smtp_username'];
159                                $this->Password = $options['smtp_password'];
160                        }
161                        else {
162                                $this->SMTPAuth = false;
163                        }
164
165                        if ($options['smtp_secure'] == 'ssl') {
166                                $this->SMTPSecure = 'ssl';
167                        }
168                        if ($options['smtp_secure'] == 'tls') {
169                                $this->SMTPSecure = 'tls';
170                        }
171
172                        if ($options['smtp_tls_allow_self_signed'] == 'oui') {
173                                $this->SMTPOptions = array(
174                                        'ssl' => array('allow_self_signed' => true)
175                                );
176                        }
177
178                        // Pour le moment on remet l'ancien fonctionnement :
179                        // on ne doit pas tester les certificats si pas demandé explicitement avec l'option TLS !
180                        $this->SMTPAutoTLS = false;
181                }
182
183                // S'il y a un contenu HTML
184                if (!empty($message_html)) {
185                        $message_html = unicode_to_utf_8(charset2unicode($message_html, $GLOBALS['meta']['charset']));
186
187                        $this->Body = $message_html;
188                        $this->IsHTML(true);
189                        if ($options['filtre_images']) {
190                                $this->JoindreImagesHTML();
191                        }
192
193                        $this->UrlsAbsolues();
194                }
195
196                // S'il y a un contenu texte brut
197                if (!empty($message_texte)) {
198                        $message_texte = unicode_to_utf_8(charset2unicode($message_texte, $GLOBALS['meta']['charset']));
199
200                        // Si pas de HTML on le remplace en tant que contenu principal
201                        if (!$this->Body) {
202                                $this->IsHTML(false);
203                                $this->Body = $message_texte;
204                        }
205                        // Sinon on met le texte brut en contenu alternatif
206                        else {
207                                $this->AltBody = $message_texte;
208                        }
209                }
210
211                if ($options['filtre_iso_8859']) {
212                        $this->ConvertirUtf8VersIso8859();
213                }
214        }
215
216        /**
217         * @param bool $exceptions
218         */
219        public function SetExceptions($exceptions){
220                $this->exceptions = ($exceptions?true:false);
221        }
222
223        /**
224         * Transforme du HTML en texte brut, mais proprement
225         * utilise le filtre facteur_mail_html2text
226         * @uses facteur_mail_html2text()
227         *
228         * @param string $html Le HTML à transformer
229         * @param bool $advanced Inutilisé
230         * @return string Retourne un texte brut formaté correctement
231         */
232        public function html2text($html, $advanced = false){
233                return facteur_mail_html2text($html);
234        }
235
236        /**
237         * Compat ascendante, obsolete
238         * @deprecated
239         */
240        public function ConvertirStylesEnligne() {
241                $this->Body = facteur_convertir_styles_inline($this->Body);
242        }
243
244        /**
245         * Transformer les urls des liens et des images en url absolues
246         * sans toucher aux images embarquees de la forme "cid:..."
247         */
248        protected function UrlsAbsolues($base=null){
249                include_spip('inc/filtres_mini');
250                if (preg_match_all(',(<(a|link)[[:space:]]+[^<>]*href=["\']?)([^"\' ><[:space:]]+)([^<>]*>),imsS',
251                  $this->Body, $liens, PREG_SET_ORDER)) {
252                        foreach ($liens as $lien) {
253                                if (strncmp($lien[3],"cid:",4)!==0){
254                                        $abs = url_absolue($lien[3], $base);
255                                        if ($abs <> $lien[3] and !preg_match('/^#/',$lien[3]))
256                                                $this->Body = str_replace($lien[0], $lien[1].$abs.$lien[4], $this->Body);
257                                }
258                        }
259                }
260                if (preg_match_all(',(<(img|script)[[:space:]]+[^<>]*src=["\']?)([^"\' ><[:space:]]+)([^<>]*>),imsS',
261                  $this->Body, $liens, PREG_SET_ORDER)) {
262                        foreach ($liens as $lien) {
263                                if (strncmp($lien[3],"cid:",4)!==0){
264                                        $abs = url_absolue($lien[3], $base);
265                                        if ($abs <> $lien[3])
266                                                $this->Body = str_replace($lien[0], $lien[1].$abs.$lien[4], $this->Body);
267                                }
268                        }
269                }
270        }
271
272        /**
273         * Embed les images HTML dans l'email
274         */
275        protected function JoindreImagesHTML() {
276                $image_types = array(
277                                                        'gif'   => 'image/gif',
278                                                        'jpg'   => 'image/jpeg',
279                                                        'jpeg'  => 'image/jpeg',
280                                                        'jpe'   => 'image/jpeg',
281                                                        'bmp'   => 'image/bmp',
282                                                        'png'   => 'image/png',
283                                                        'tif'   => 'image/tiff',
284                                                        'tiff'  => 'image/tiff',
285                                                        'swf'   => 'application/x-shockwave-flash'
286                                                );
287                $src_found = array();
288                $images_embeded = array();
289                if (preg_match_all(
290                        '/["\'](([^"\']+)\.('.implode('|', array_keys($image_types)).'))([?][^"\']+)?([#][^"\']+)?["\']/Uims',
291                        $this->Body, $images, PREG_SET_ORDER)) {
292
293                        $adresse_site = $GLOBALS['meta']['adresse_site'].'/';
294                        foreach($images as $im){
295                                $im = array_pad($im, 6, null);
296                                $src_orig = $im[1].$im[4].$im[5];
297                                if (!isset($src_found[$src_orig])){ // deja remplace ? rien a faire (ie la meme image presente plusieurs fois)
298                                        // examiner le src et voir si embedable
299                                        $src = $im[1];
300                                        if ($src AND strncmp($src,$adresse_site,strlen($adresse_site))==0)
301                                                $src = _DIR_RACINE . substr($src,strlen($adresse_site));
302                                        if ($src
303                                          AND !preg_match(",^[a-z0-9]+://,i",$src)
304                                          AND (
305                                              file_exists($f=$src) // l'image a ete generee depuis le meme cote que l'envoi
306                                              OR (_DIR_RACINE AND file_exists($f=_DIR_RACINE.$src)) // l'image a ete generee dans le public et on est dans le prive
307                                              OR (!_DIR_RACINE AND file_exists($f=_DIR_RESTREINT.$src)) // l'image a ete generee dans le prive et on est dans le public
308                                             )
309                                          ){
310                                                if (!isset($images_embeded[$f])){
311                                                        $extension = strtolower($im[3]);
312                                                        $header_extension = $image_types[$extension];
313                                                        $cid = md5($f); // un id unique pour un meme fichier
314                                                        $images_embeded[$f] = $cid; // marquer l'image comme traitee, inutile d'y revenir
315                                                        $this->AddEmbeddedImage($f, $cid, basename($f),'base64',$header_extension);
316                                                }
317
318                                                $this->Body = str_replace($src_orig, "cid:".$images_embeded[$f], $this->Body);
319                                                $src_found[$src_orig] = $f;
320                                        }
321                                }
322                        }
323                }
324        }
325
326
327        /**
328         * Conversion safe d'un texte utf en isotruc
329         * @param string $text
330         * @param string $mode
331         * @return string
332         */
333        protected function safe_utf8_decode($text,$mode='texte_brut') {
334                if (!is_utf8($text))
335                        return ($text);
336
337                if (function_exists('iconv') && $mode == 'texte_brut') {
338                        $text = str_replace('’',"'",$text);
339                        $text = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $text);
340                        return str_replace('&#8217;',"'",$text);
341                }
342                else {
343                        if ($mode == 'texte_brut') {
344                                $text = str_replace('’',"'",$text);
345                        }
346                        $text = unicode2charset(utf_8_to_unicode($text),'iso-8859-1');
347                        return str_replace('&#8217;',"'",$text);
348                }
349        }
350
351        /**
352         * Convertir tout le mail utf en isotruc
353         */
354        protected function ConvertirUtf8VersIso8859() {
355                $this->CharSet  = 'iso-8859-1';
356                $this->Body             = str_ireplace('charset=utf-8', 'charset=iso-8859-1', $this->Body);
357                $this->Body             = $this->safe_utf8_decode($this->Body,'html');
358                $this->AltBody  = $this->safe_utf8_decode($this->AltBody);
359                $this->Subject  = $this->safe_utf8_decode($this->Subject);
360                $this->FromName = $this->safe_utf8_decode($this->FromName);
361        }
362
363        /**
364         * Convertir les accents du body en entites html
365         */
366        protected function ConvertirAccents() {
367                // tableau à compléter au fur et à mesure
368                $cor = array(
369                                                'à' => '&agrave;',
370                                                'â' => '&acirc;',
371                                                'ä' => '&auml;',
372                                                'ç' => '&ccedil;',
373                                                'é' => '&eacute;',
374                                                'è' => '&egrave;',
375                                                'ê' => '&ecirc;',
376                                                'ë' => '&euml;',
377                                                'î' => '&icirc;',
378                                                'ï' => '&iuml;',
379                                                'ò' => '&ograve;',
380                                                'ô' => '&ocirc;',
381                                                'ö' => '&ouml;',
382                                                'ù' => '&ugrave;',
383                                                'û' => '&ucirc;',
384                                                'œ' => '&oelig;',
385                                                '€' => '&euro;'
386                                        );
387
388                $this->Body = strtr($this->Body, $cor);
389        }
390
391
392        /**
393         * Une fonction wrapper pour appeler une methode de phpMailer
394         * en recuperant l'erreur eventuelle, en la loguant via SPIP et en lancant une exception si demandee
395         * @param string $function
396         * @param array $args
397         * @return bool
398         * @throws phpmailerException
399         */
400        protected function callWrapper($function,$args){
401                $exceptions = $this->exceptions;
402                $this->exceptions = true;
403                try {
404                        $retour = call_user_func_array($function,$args);
405                        $this->exceptions = $exceptions;
406                }
407                catch (phpmailerException $exc) {
408                        spip_log((is_array($function)?implode('::',$function):$function)."() : ".$exc->getMessage(),'facteur.'._LOG_ERREUR);
409                        $this->exceptions = $exceptions;
410                        if ($this->exceptions) {
411                                throw $exc;
412                        }
413                        return false;
414                }
415                if ($this->ErrorInfo){
416                        spip_log((is_array($function)?implode('::',$function):$function)."() : ".$this->ErrorInfo,'facteur.'._LOG_ERREUR);
417                }
418
419                return $retour;
420        }
421
422        /*
423         * Appel des fonctions parents via le callWrapper qui se charge de loger les erreurs
424         */
425
426        /**
427         * Avant le Send() on force le From si besoin
428         * @return bool
429         * @throws phpmailerException
430         */
431        public function Send() {
432                if ($this->ForceFrom
433                        AND $this->From!==$this->ForceFrom){
434                        $forcedomain = explode('@',$this->ForceFrom);
435                        $forcedomain = end($forcedomain);
436                        $domain = explode('@',$this->From);
437                        $domain = end($domain);
438                        if ($domain!==$forcedomain){
439                                // le From passe en ReplyTo
440                                $this->AddReplyTo($this->From,$this->FromName);
441                                // on force le From
442                                $this->From = $this->ForceFrom;
443                                if ($this->ForceFromName) {//null si jamais garder_fromname était activé
444                                        $this->FromName = $this->ForceFromName;
445                                }
446                        }
447                }
448                $args = func_get_args();
449                return $this->callWrapper(array('parent','Send'),$args);
450        }
451        public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') {
452                $args = func_get_args();
453                return $this->callWrapper(array('parent','AddAttachment'),$args);
454        }
455        public function AddReplyTo($address, $name = '') {
456                $args = func_get_args();
457                return $this->callWrapper(array('parent','AddReplyTo'),$args);
458        }
459        public function AddBCC($address, $name = '') {
460                $args = func_get_args();
461                return $this->callWrapper(array('parent','AddBCC'),$args);
462        }
463        public function AddCC($address, $name = '') {
464                $args = func_get_args();
465                return $this->callWrapper(array('parent','AddCC'),$args);
466        }
467}
Note: See TracBrowser for help on using the repository browser.