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

Last change on this file since 108986 was 108986, checked in by brunobergot@…, 3 years ago

version 3.5.1 : permettre d'utiliser l'option du certificat autosigné aussi avec ssl

complépement à r100655

  • Property svn:executable set to *
File size: 14.2 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',
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                if ($options['forcer_from']=='oui'){
110                        $this->ForceFrom = $this->From;
111                        $this->ForceFromName = $this->FromName;
112                }
113
114                $this->CharSet = "utf-8";
115                $this->Mailer = 'mail';
116                $this->Subject = unicode_to_utf_8(charset2unicode($objet,$GLOBALS['meta']['charset']));
117
118                //Pour un envoi multiple de mail, $email doit être un tableau avec les adresses.
119                if (is_array($email)) {
120                        foreach ($email as $cle => $adresseMail) {
121                                if (!$this->AddAddress($adresseMail)) {
122                                        spip_log("Erreur AddAddress $adresseMail : ".print_r($this->ErrorInfo, true), 'facteur.'._LOG_ERREUR);
123                                }
124                        }
125                }
126                elseif (!$this->AddAddress($email)) {
127                        spip_log("Erreur AddAddress $email : ".print_r($this->ErrorInfo, true), 'facteur.'._LOG_ERREUR);
128                }
129
130                // Retour des erreurs
131                if (!empty($options['smtp_sender'])) {
132                        $this->Sender = $options['smtp_sender'];
133                        $this->AddCustomHeader("Errors-To: ".$this->Sender);
134                }
135
136                // Destinataires en copie, seulement s'il n'y a pas de destinataire de test
137                if (!defined('_TEST_EMAIL_DEST')){
138                        if (!empty($options['cc'])) {
139                                $this->AddCC($options['cc']);
140                        }
141                        if (!empty($options['bcc'])) {
142                                $this->AddBCC($options['bcc']);
143                        }
144                }
145
146                // Si on envoie avec un SMTP explicite
147                if (isset($options['smtp']) AND $options['smtp'] == 'oui') {
148                        $this->Mailer   = 'smtp';
149                        $this->Host     = $options['smtp_host'];
150                        $this->Port     = $options['smtp_port'];
151
152                        // SMTP authentifié
153                        if ($options['smtp_auth'] == 'oui') {
154                                $this->SMTPAuth = true;
155                                $this->Username = $options['smtp_username'];
156                                $this->Password = $options['smtp_password'];
157                        }
158                        else {
159                                $this->SMTPAuth = false;
160                        }
161
162                        if ($options['smtp_secure'] == 'ssl') {
163                                $this->SMTPSecure = 'ssl';
164                        }
165                        if ($options['smtp_secure'] == 'tls') {
166                                $this->SMTPSecure = 'tls';
167                        }
168
169                        if ($options['smtp_tls_allow_self_signed'] == 'oui') {
170                                $this->SMTPOptions = array(
171                                        'ssl' => array('allow_self_signed' => true)
172                                );
173                        }
174
175                        // Pour le moment on remet l'ancien fonctionnement :
176                        // on ne doit pas tester les certificats si pas demandé explicitement avec l'option TLS !
177                        $this->SMTPAutoTLS = false;
178                }
179
180                // S'il y a un contenu HTML
181                if (!empty($message_html)) {
182                        $message_html = unicode_to_utf_8(charset2unicode($message_html, $GLOBALS['meta']['charset']));
183
184                        $this->Body = $message_html;
185                        $this->IsHTML(true);
186                        if ($options['filtre_images']) {
187                                $this->JoindreImagesHTML();
188                        }
189
190                        $this->UrlsAbsolues();
191                }
192
193                // S'il y a un contenu texte brut
194                if (!empty($message_texte)) {
195                        $message_texte = unicode_to_utf_8(charset2unicode($message_texte, $GLOBALS['meta']['charset']));
196
197                        // Si pas de HTML on le remplace en tant que contenu principal
198                        if (!$this->Body) {
199                                $this->IsHTML(false);
200                                $this->Body = $message_texte;
201                        }
202                        // Sinon on met le texte brut en contenu alternatif
203                        else {
204                                $this->AltBody = $message_texte;
205                        }
206                }
207
208                if ($options['filtre_iso_8859']) {
209                        $this->ConvertirUtf8VersIso8859();
210                }
211        }
212
213        /**
214         * @param bool $exceptions
215         */
216        public function SetExceptions($exceptions){
217                $this->exceptions = ($exceptions?true:false);
218        }
219
220        /**
221         * Transforme du HTML en texte brut, mais proprement
222         * utilise le filtre facteur_mail_html2text
223         * @uses facteur_mail_html2text()
224         *
225         * @param string $html Le HTML à transformer
226         * @param bool $advanced Inutilisé
227         * @return string Retourne un texte brut formaté correctement
228         */
229        public function html2text($html, $advanced = false){
230                return facteur_mail_html2text($html);
231        }
232
233        /**
234         * Compat ascendante, obsolete
235         * @deprecated
236         */
237        public function ConvertirStylesEnligne() {
238                $this->Body = facteur_convertir_styles_inline($this->Body);
239        }
240
241        /**
242         * Transformer les urls des liens et des images en url absolues
243         * sans toucher aux images embarquees de la forme "cid:..."
244         */
245        protected function UrlsAbsolues($base=null){
246                include_spip('inc/filtres_mini');
247                if (preg_match_all(',(<(a|link)[[:space:]]+[^<>]*href=["\']?)([^"\' ><[:space:]]+)([^<>]*>),imsS',
248                  $this->Body, $liens, PREG_SET_ORDER)) {
249                        foreach ($liens as $lien) {
250                                if (strncmp($lien[3],"cid:",4)!==0){
251                                        $abs = url_absolue($lien[3], $base);
252                                        if ($abs <> $lien[3] and !preg_match('/^#/',$lien[3]))
253                                                $this->Body = str_replace($lien[0], $lien[1].$abs.$lien[4], $this->Body);
254                                }
255                        }
256                }
257                if (preg_match_all(',(<(img|script)[[:space:]]+[^<>]*src=["\']?)([^"\' ><[:space:]]+)([^<>]*>),imsS',
258                  $this->Body, $liens, PREG_SET_ORDER)) {
259                        foreach ($liens as $lien) {
260                                if (strncmp($lien[3],"cid:",4)!==0){
261                                        $abs = url_absolue($lien[3], $base);
262                                        if ($abs <> $lien[3])
263                                                $this->Body = str_replace($lien[0], $lien[1].$abs.$lien[4], $this->Body);
264                                }
265                        }
266                }
267        }
268
269        /**
270         * Embed les images HTML dans l'email
271         */
272        protected function JoindreImagesHTML() {
273                $image_types = array(
274                                                        'gif'   => 'image/gif',
275                                                        'jpg'   => 'image/jpeg',
276                                                        'jpeg'  => 'image/jpeg',
277                                                        'jpe'   => 'image/jpeg',
278                                                        'bmp'   => 'image/bmp',
279                                                        'png'   => 'image/png',
280                                                        'tif'   => 'image/tiff',
281                                                        'tiff'  => 'image/tiff',
282                                                        'swf'   => 'application/x-shockwave-flash'
283                                                );
284                $src_found = array();
285                $images_embeded = array();
286                if (preg_match_all(
287                        '/["\'](([^"\']+)\.('.implode('|', array_keys($image_types)).'))([?][^"\']+)?([#][^"\']+)?["\']/Uims',
288                        $this->Body, $images, PREG_SET_ORDER)) {
289
290                        $adresse_site = $GLOBALS['meta']['adresse_site'].'/';
291                        foreach($images as $im){
292                                $im = array_pad($im, 6, null);
293                                $src_orig = $im[1].$im[4].$im[5];
294                                if (!isset($src_found[$src_orig])){ // deja remplace ? rien a faire (ie la meme image presente plusieurs fois)
295                                        // examiner le src et voir si embedable
296                                        $src = $im[1];
297                                        if ($src AND strncmp($src,$adresse_site,strlen($adresse_site))==0)
298                                                $src = _DIR_RACINE . substr($src,strlen($adresse_site));
299                                        if ($src
300                                          AND !preg_match(",^[a-z0-9]+://,i",$src)
301                                          AND (
302                                              file_exists($f=$src) // l'image a ete generee depuis le meme cote que l'envoi
303                                              OR (_DIR_RACINE AND file_exists($f=_DIR_RACINE.$src)) // l'image a ete generee dans le public et on est dans le prive
304                                              OR (!_DIR_RACINE AND file_exists($f=_DIR_RESTREINT.$src)) // l'image a ete generee dans le prive et on est dans le public
305                                             )
306                                          ){
307                                                if (!isset($images_embeded[$f])){
308                                                        $extension = strtolower($im[3]);
309                                                        $header_extension = $image_types[$extension];
310                                                        $cid = md5($f); // un id unique pour un meme fichier
311                                                        $images_embeded[$f] = $cid; // marquer l'image comme traitee, inutile d'y revenir
312                                                        $this->AddEmbeddedImage($f, $cid, basename($f),'base64',$header_extension);
313                                                }
314
315                                                $this->Body = str_replace($src_orig, "cid:".$images_embeded[$f], $this->Body);
316                                                $src_found[$src_orig] = $f;
317                                        }
318                                }
319                        }
320                }
321        }
322
323
324        /**
325         * Conversion safe d'un texte utf en isotruc
326         * @param string $text
327         * @param string $mode
328         * @return string
329         */
330        protected function safe_utf8_decode($text,$mode='texte_brut') {
331                if (!is_utf8($text))
332                        return ($text);
333
334                if (function_exists('iconv') && $mode == 'texte_brut') {
335                        $text = str_replace('’',"'",$text);
336                        $text = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $text);
337                        return str_replace('&#8217;',"'",$text);
338                }
339                else {
340                        if ($mode == 'texte_brut') {
341                                $text = str_replace('’',"'",$text);
342                        }
343                        $text = unicode2charset(utf_8_to_unicode($text),'iso-8859-1');
344                        return str_replace('&#8217;',"'",$text);
345                }
346        }
347
348        /**
349         * Convertir tout le mail utf en isotruc
350         */
351        protected function ConvertirUtf8VersIso8859() {
352                $this->CharSet  = 'iso-8859-1';
353                $this->Body             = str_ireplace('charset=utf-8', 'charset=iso-8859-1', $this->Body);
354                $this->Body             = $this->safe_utf8_decode($this->Body,'html');
355                $this->AltBody  = $this->safe_utf8_decode($this->AltBody);
356                $this->Subject  = $this->safe_utf8_decode($this->Subject);
357                $this->FromName = $this->safe_utf8_decode($this->FromName);
358        }
359
360        /**
361         * Convertir les accents du body en entites html
362         */
363        protected function ConvertirAccents() {
364                // tableau à compléter au fur et à mesure
365                $cor = array(
366                                                'à' => '&agrave;',
367                                                'â' => '&acirc;',
368                                                'ä' => '&auml;',
369                                                'ç' => '&ccedil;',
370                                                'é' => '&eacute;',
371                                                'è' => '&egrave;',
372                                                'ê' => '&ecirc;',
373                                                'ë' => '&euml;',
374                                                'î' => '&icirc;',
375                                                'ï' => '&iuml;',
376                                                'ò' => '&ograve;',
377                                                'ô' => '&ocirc;',
378                                                'ö' => '&ouml;',
379                                                'ù' => '&ugrave;',
380                                                'û' => '&ucirc;',
381                                                'œ' => '&oelig;',
382                                                '€' => '&euro;'
383                                        );
384
385                $this->Body = strtr($this->Body, $cor);
386        }
387
388
389        /**
390         * Une fonction wrapper pour appeler une methode de phpMailer
391         * en recuperant l'erreur eventuelle, en la loguant via SPIP et en lancant une exception si demandee
392         * @param string $function
393         * @param array $args
394         * @return bool
395         * @throws phpmailerException
396         */
397        protected function callWrapper($function,$args){
398                $exceptions = $this->exceptions;
399                $this->exceptions = true;
400                try {
401                        $retour = call_user_func_array($function,$args);
402                        $this->exceptions = $exceptions;
403                }
404                catch (phpmailerException $exc) {
405                        spip_log((is_array($function)?implode('::',$function):$function)."() : ".$exc->getMessage(),'facteur.'._LOG_ERREUR);
406                        $this->exceptions = $exceptions;
407                        if ($this->exceptions) {
408                                throw $exc;
409                        }
410                        return false;
411                }
412                if ($this->ErrorInfo){
413                        spip_log((is_array($function)?implode('::',$function):$function)."() : ".$this->ErrorInfo,'facteur.'._LOG_ERREUR);
414                }
415
416                return $retour;
417        }
418
419        /*
420         * Appel des fonctions parents via le callWrapper qui se charge de loger les erreurs
421         */
422
423        /**
424         * Avant le Send() on force le From si besoin
425         * @return bool
426         * @throws phpmailerException
427         */
428        public function Send() {
429                if ($this->ForceFrom
430                        AND $this->From!==$this->ForceFrom){
431                        $forcedomain = explode('@',$this->ForceFrom);
432                        $forcedomain = end($forcedomain);
433                        $domain = explode('@',$this->From);
434                        $domain = end($domain);
435                        if ($domain!==$forcedomain){
436                                // le From passe en ReplyTo
437                                $this->AddReplyTo($this->From,$this->FromName);
438                                // on force le From
439                                $this->From = $this->ForceFrom;
440                                $this->FromName = $this->ForceFromName;
441                        }
442                }
443                $args = func_get_args();
444                return $this->callWrapper(array('parent','Send'),$args);
445        }
446        public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') {
447                $args = func_get_args();
448                return $this->callWrapper(array('parent','AddAttachment'),$args);
449        }
450        public function AddReplyTo($address, $name = '') {
451                $args = func_get_args();
452                return $this->callWrapper(array('parent','AddReplyTo'),$args);
453        }
454        public function AddBCC($address, $name = '') {
455                $args = func_get_args();
456                return $this->callWrapper(array('parent','AddBCC'),$args);
457        }
458        public function AddCC($address, $name = '') {
459                $args = func_get_args();
460                return $this->callWrapper(array('parent','AddCC'),$args);
461        }
462}
Note: See TracBrowser for help on using the repository browser.