source: spip-zone/_plugins_/facteur/trunk/inc/envoyer_mail.php @ 118885

Last change on this file since 118885 was 118885, checked in by Cerdic, 8 months ago

renommage de variable + php doc flag important

File size: 13.2 KB
Line 
1<?php
2/**
3 * Plugin Facteur 4
4 * (c) 2009-2019 Collectif SPIP
5 * Distribue sous licence GPL
6 *
7 * @package SPIP\Facteur\Inc\Envoyer_mails
8 */
9
10if (!defined("_ECRIRE_INC_VERSION")) return;
11
12if (!defined('_LOG_FACTEUR')) {
13        define('_LOG_FACTEUR',_LOG_INFO);
14}
15
16include_spip('classes/facteur');
17// inclure le fichier natif de SPIP, pour les fonctions annexes
18include_once _DIR_RESTREINT."inc/envoyer_mail.php";
19
20/**
21 * @param string $destinataire
22 * @param string $sujet
23 * @param string|array $message
24 *   au format string, c'est un corps d'email au format texte, comme supporte nativement par le core
25 *   au format array, c'est un corps etendu qui peut contenir
26 *     string texte : le corps d'email au format texte
27 *     string html : le corps d'email au format html
28 *     string from : email de l'envoyeur (prioritaire sur argument $from de premier niveau, deprecie)
29 *     string nom_envoyeur : un nom d'envoyeur pour completer l'email from
30 *     string cc : destinataires en copie conforme
31 *     string bcc : destinataires en copie conforme cachee
32 *     string|array repondre_a : une ou plusieurs adresses à qui répondre.
33 *       On peut aussi donner une liste de tableaux du type :
34 *         array('email' => 'test@exemple.com', 'nom' => 'Adresse de test')
35 *       pour spécifier un nom d'envoyeur pour chaque adresse.
36 *     string nom_repondre_a : le nom d'envoyeur pour compléter l'email repondre_a
37 *     string adresse_erreur : addresse de retour en cas d'erreur d'envoi
38 *     array pieces_jointes : listes de pieces a embarquer dans l'email, chacune au format array :
39 *       string chemin : chemin file system pour trouver le fichier a embarquer
40 *       string nom : nom du document tel qu'apparaissant dans l'email
41 *       string encodage : encodage a utiliser, parmi 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
42 *       string mime : mime type du document
43 *     array headers : tableau d'en-tetes personalises, une entree par ligne d'en-tete
44 *     bool exceptions : lancer une exception en cas d'erreur (false par defaut)
45 *     bool important : un flag pour signaler les messages important qui necessitent un feedback en cas d'erreur
46 * @param string $from (deprecie, utiliser l'entree from de $message)
47 * @param string $headers (deprecie, utiliser l'entree headers de $message)
48 * @return bool
49 */
50function inc_envoyer_mail($destinataire, $sujet, $message, $from = "", $headers = "") {
51        $message_html   = '';
52        $message_texte  = '';
53        $nom_envoyeur = $cc = $bcc = $repondre_a = '';
54        $pieces_jointes = array();
55        $important = false;
56
57        // si $message est un tableau -> fonctionnalites etendues
58        // avec entrees possible : html, texte, pieces_jointes, nom_envoyeur, ...
59        if (is_array($message)) {
60                $message_html   = isset($message['html']) ? $message['html'] : "";
61                $message_texte  = isset($message['texte']) ? nettoyer_caracteres_mail($message['texte']) : "";
62                $pieces_jointes = isset($message['pieces_jointes']) ? $message['pieces_jointes'] : array();
63                $nom_envoyeur   = isset($message['nom_envoyeur']) ? $message['nom_envoyeur'] : "";
64                $from = isset($message['from']) ? $message['from']: $from;
65                $cc   = isset($message['cc']) ? $message['cc'] : "";
66                $bcc  = isset($message['bcc']) ? $message['bcc'] : "";
67                $repondre_a = isset($message['repondre_a']) ? $message['repondre_a'] : "";
68                $nom_repondre_a = isset($message['nom_repondre_a']) ? $message['nom_repondre_a'] : '';
69                $adresse_erreur = isset($message['adresse_erreur']) ? $message['adresse_erreur'] : "";
70                $headers = isset($message['headers']) ? $message['headers'] : $headers;
71                if (is_string($headers)){
72                        $headers = array_map('trim',explode("\n",$headers));
73                        $headers = array_filter($headers);
74                }
75                $important = (isset($message['important']) ? !!$message['important'] : $important);
76        }
77        // si $message est une chaine -> compat avec la fonction native SPIP
78        // gerer le cas ou le corps est du html avec un Content-Type: text/html dans les headers
79        else {
80                if (preg_match(',Content-Type:\s*text/html,ims',$headers)){
81                        $message_html   = $message;
82                }
83                else {
84                        // Autodetection : tester si le mail est en HTML
85                        if (strpos($headers,"Content-Type:")===false
86                                AND strpos($message,"<")!==false // eviter les tests suivants si possible
87                                AND $ttrim = trim($message)
88                                AND substr($ttrim,0,1)=="<"
89                                AND substr($ttrim,-1,1)==">"
90                                AND stripos($ttrim,"</html>")!==false){
91
92                                $message_html   = $message;
93                        }
94                        // c'est vraiment un message texte
95                        else
96                                $message_texte  = nettoyer_caracteres_mail($message);
97                }
98                $headers = array_map('trim',explode("\n",$headers));
99                $headers = array_filter($headers);
100        }
101
102        if(!strlen($sujet)){
103                if ($message_html) {
104                        // dans ce cas on ruse un peu : extraire le sujet du title
105                        if (preg_match(",<title>(.*)</title>,Uims",$message_html,$m))
106                                $sujet = $m[1];
107                        else {
108                                $ttrim = $message_html;
109                                // fallback, on prend le body si on le trouve
110                                if (preg_match(",<body[^>]*>(.*)</body>,Uims",$message_html,$m))
111                                        $ttrim = $m[1];
112
113                                // et on extrait la premiere ligne de vrai texte...
114                                // nettoyer le html et les retours chariots
115                                $ttrim = textebrut($ttrim);
116                                $ttrim = str_replace("\r\n", "\r", $ttrim);
117                                $ttrim = str_replace("\r", "\n", $ttrim);
118                                // decouper
119                                $ttrim = explode("\n",trim($ttrim));
120                                // extraire la premiere ligne de texte brut
121                                $sujet = array_shift($ttrim);
122                        }
123                }
124                else {
125                        // et on extrait la premiere ligne de vrai texte...
126                        // nettoyer le html et les retours chariots
127                        $ttrim = textebrut($message_texte);
128                        $ttrim = str_replace("\r\n", "\r", $ttrim);
129                        $ttrim = str_replace("\r", "\n", $ttrim);
130                        // decouper
131                        $ttrim = explode("\n",trim($ttrim));
132                        // extraire la premiere ligne de texte brut
133                        $sujet = array_shift($ttrim);
134                }
135        }
136
137        $sujet = nettoyer_titre_email($sujet);
138
139        // si le mail est en texte brut, on l'encapsule dans un modele surchargeable
140        // pour garder le texte brut, il suffit de faire un modele qui renvoie uniquement #ENV*{texte}
141        if ($message_texte AND ! $message_html){
142                $message_html = recuperer_fond("emails/texte",array('texte'=>$message_texte,'sujet'=>$sujet));
143        }
144        // si le mail est en HTML sans alternative, la generer
145        if ($message_html AND !$message_texte){
146                $message_texte = facteur_mail_html2text($message_html);
147        }
148
149        $exceptions = false;
150        if (is_array($message) AND isset($message['exceptions'])){
151                $exceptions = $message['exceptions'];
152        }
153
154        // mode TEST : forcer l'email
155        if (defined('_TEST_EMAIL_DEST')) {
156                if (!_TEST_EMAIL_DEST){
157                        spip_log($e=_T('facteur:erreur_envoi_bloque_constante'), 'mail.' . _LOG_ERREUR);
158                        if ($exceptions) {
159                                throw new Exception($e);
160                        }
161                        return false;
162                }
163                else
164                        $destinataire = _TEST_EMAIL_DEST;
165        }
166
167        // plusieurs destinataires peuvent etre fournis separes par des virgules
168        // c'est un format standard dans l'envoi de mail
169        // les passer au format array pour phpMailer
170        // mais ne pas casser si on a deja un array en entree
171        // si pas destinataire du courriel on renvoie false (eviter les warning PHP : ligne 464 de phpmailer-php5/class.phpmailer.php
172        // suppression des adresses de courriels invalides, si aucune valide, renvoyer false (eviter un warning PHP : ligne 464 de phpmailer-php5/class.phpmailer.php)
173        if (is_array($destinataire))
174                $destinataire = implode(", ",$destinataire);
175
176        if(strlen($destinataire) > 0){
177                $destinataire = array_map('trim',explode(",",$destinataire));
178                foreach ($destinataire as $key => $value) {
179                        if(!email_valide($value))
180                                unset($destinataire[$key]);
181                }
182                if(count($destinataire) == 0) {
183                        spip_log($e="Aucune adresse email de destination valable pour l'envoi du courriel.", 'mail.' . _LOG_ERREUR);
184                        if ($exceptions) {
185                                throw new Exception($e);
186                        }
187                        return false;
188                }
189        }
190        else {
191                if ($bcc) {
192                        // On peut envoyer de mail que en bcc
193                        $destinataire = '';
194                } else {
195                        spip_log($e="Aucune adresse email de destination valable pour l'envoi du courriel.", 'mail.' . _LOG_ERREUR);
196                        if ($exceptions) {
197                                throw new Exception($e);
198                        }
199                        return false;
200                }
201        }
202
203        // On crée l'objet Facteur (PHPMailer) pour le manipuler ensuite
204        $options = array();
205        if ($exceptions){
206                $options['exceptions'] = $exceptions;
207        }
208        include_spip('inc/facteur');
209        $facteur = facteur_factory($options);
210
211        $facteur->setDest($destinataire);
212        $facteur->setObjet($sujet);
213        $facteur->setMessage($message_html, $message_texte);
214
215        // On ajoute le courriel de l'envoyeur s'il est fournit par la fonction
216        if (empty($from) AND empty($facteur->From)) {
217                $from = $GLOBALS['meta']["email_envoi"];
218                if (empty($from) OR !email_valide($from)) {
219                        spip_log("Meta email_envoi invalide. Le mail sera probablement vu comme spam.", 'mail.' . _LOG_ERREUR);
220                        if(is_array($destinataire) && count($destinataire) > 0)
221                                $from = $destinataire[0];
222                        else
223                                $from = $destinataire;
224                }
225        }
226
227        // "Marie Toto <Marie@toto.com>"
228        if (preg_match(",^([^<>\"]*)<([^<>\"]+)>$,i",$from,$m)){
229                $nom_envoyeur = trim($m[1]);
230                $from = trim($m[2]);
231        }
232        if (!empty($from)){
233                $facteur->From = $from;
234                // la valeur par défaut de la config n'est probablement pas valable pour ce mail,
235                // on l'écrase pour cet envoi
236                $facteur->FromName = '';
237        }
238
239        // On ajoute le nom de l'envoyeur s'il fait partie des options
240        if ($nom_envoyeur){
241                $facteur->FromName = $nom_envoyeur;
242        }
243
244        // Si plusieurs emails dans le from, pas de Name !
245        if (strpos($facteur->From,",")!==false){
246                $facteur->FromName = "";
247        }
248
249        // S'il y a des copies à envoyer
250        if ($cc){
251                if (is_array($cc))
252                        foreach ($cc as $courriel)
253                                $facteur->AddCC($courriel);
254                else
255                        $facteur->AddCC($cc);
256        }
257
258        // S'il y a des copies cachées à envoyer
259        if ($bcc){
260                if (is_array($bcc))
261                        foreach ($bcc as $courriel)
262                                $facteur->AddBCC($courriel);
263                else
264                        $facteur->AddBCC($bcc);
265        }
266
267        // S'il y a une adresse de reply-to
268        if ($repondre_a) {
269                if (is_array($repondre_a)) {
270                        foreach ($repondre_a as $courriel) {
271                                if (is_array($courriel)) {
272                                        $facteur->AddReplyTo($courriel['email'], $courriel['nom']);
273                                } else {
274                                        $facteur->AddReplyTo($courriel);
275                                }
276                        }
277                } elseif ($nom_repondre_a) {
278                        $facteur->AddReplyTo($repondre_a, $nom_repondre_a);
279                } else {
280                        $facteur->AddReplyTo($repondre_a);
281                }
282        }
283
284        // S'il y a des pièces jointes on les ajoute proprement
285        if (count($pieces_jointes)) {
286                foreach ($pieces_jointes as $piece) {
287                        $facteur->AddAttachment(
288                                $piece['chemin'],
289                                isset($piece['nom']) ? $piece['nom']:'',
290                                (isset($piece['encodage']) AND in_array($piece['encodage'],array('base64', '7bit', '8bit', 'binary', 'quoted-printable'))) ? $piece['encodage']:'base64',
291                                isset($piece['mime']) ? $piece['mime']:Facteur::_mime_types(pathinfo($piece['chemin'], PATHINFO_EXTENSION))
292                        );
293                }
294        }
295
296        // Si une adresse email a été spécifiée pour les retours en erreur, on l'ajoute
297        if (!empty($adresse_erreur))
298                $facteur->Sender = $adresse_erreur;
299
300        if ($important) {
301                $facteur->setImportant();
302        }
303
304        // si entetes personalises : les ajouter
305        // attention aux collisions : si on utilise l'option cc de $message
306        // et qu'on envoie en meme temps un header Cc: xxx, yyy
307        // on aura 2 lignes Cc: dans les headers
308        if (!empty($headers)) {
309                foreach($headers as $h){
310                        // verifions le format correct : il faut au moins un ":" dans le header
311                        // et on filtre le Content-Type: qui sera de toute facon fourni par facteur
312                        if (strpos($h,":")!==false
313                          AND strncmp($h,"Content-Type:",13)!==0)
314                                $facteur->AddCustomHeader($h);
315                }
316        }
317
318        // On passe dans un pipeline pour modifier tout le facteur avant l'envoi
319        $facteur = pipeline('facteur_pre_envoi', $facteur);
320
321        // Et c'est parti on envoie enfin
322        $backtrace = facteur_backtrace();
323        $trace = $facteur->getMessageLog();
324        spip_log("mail via facteur\n$trace",'mail'._LOG_FACTEUR);
325        spip_log("mail\n$backtrace\n$trace",'facteur'._LOG_FACTEUR);
326        $retour = $facteur->Send();
327
328        if (!$retour){
329                spip_log("Erreur Envoi mail via Facteur : ".print_r($facteur->ErrorInfo,true),'mail.'._LOG_ERREUR);
330
331                if ($important
332                  and $dest_alertes = $this->Sender) {
333                        $dest = (is_array($destinataire) ? implode(', ', $destinataire) : $destinataire);
334                        $sujet_alerte = _L("Fail envoi mail pour @dest@ (était: @sujet@)", array('email' => $dest, 'sujet' => $sujet));
335                        $facteur->setDest($dest_alertes);
336                        $facteur->setObjet($sujet_alerte);
337                        $facteur->Send();
338                }
339        }
340
341        return $retour ;
342}
343
344/**
345 * Retourne la pile de fonctions utilisée pour envoyer un mail
346 *
347 * @note
348 *     Ignore les fonctions `include_once`, `include_spip`, `find_in_path`
349 * @return array|string
350 *     pile d'appel
351 **/
352function facteur_backtrace($limit=10) {
353        $trace = debug_backtrace();
354        $caller = array_shift($trace);
355        while (count($trace) and (empty($trace[0]['file']) or $trace[0]['file'] === $caller['file'] or $trace[0]['file'] === __FILE__)) {
356                array_shift($trace);
357        }
358
359        $message = count($trace) ? $trace[0]['file'] . " L" . $trace[0]['line'] : "";
360        $f = array();
361        while (count($trace) and $t = array_shift($trace) and count($f)<$limit) {
362                if (in_array($t['function'], array('include_once', 'include_spip', 'find_in_path'))) {
363                        break;
364                }
365                $f[] = $t['function'];
366        }
367        if (count($f)) {
368                $message .= " [" . implode("(),", $f) . "()]";
369        }
370
371        return $message;
372}
Note: See TracBrowser for help on using the repository browser.