1<?php 2/** 3 * Copyright (C) Dan Potter 4 * Copyright (C) Eric Seigne 5 * Copyright (C) 2000-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org> 6 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org> 7 * Copyright (C) 2004-2015 Laurent Destailleur <eldy@users.sourceforge.net> 8 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com> 9 * Copyright (C) 2019-2020 Frédéric France <frederic.france@netlogic.fr> 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 3 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program. If not, see <https://www.gnu.org/licenses/>. 23 * or see https://www.gnu.org/ 24 * 25 * Lots of code inspired from Dan Potter's CMailFile class 26 */ 27 28/** 29 * \file htdocs/core/class/CMailFile.class.php 30 * \brief File of class to send emails (with attachments or not) 31 */ 32 33/** 34 * Class to send emails (with attachments or not) 35 * Usage: $mailfile = new CMailFile($subject,$sendto,$replyto,$message,$filepath,$mimetype,$filename,$cc,$ccc,$deliveryreceipt,$msgishtml,$errors_to,$css,$trackid,$moreinheader,$sendcontext,$replyto); 36 * $mailfile->sendfile(); 37 */ 38class CMailFile 39{ 40 public $sendcontext; 41 public $sendmode; 42 public $sendsetup; 43 44 /** 45 * @var string Subject of email 46 */ 47 public $subject; 48 public $addr_from; // From: Label and EMail of sender (must include '<>'). For example '<myemail@example.com>' or 'John Doe <myemail@example.com>' or '<myemail+trackingid@example.com>'). Note that with gmail smtps, value here is forced by google to account (but not the reply-to). 49 // Sender: Who send the email ("Sender" has sent emails on behalf of "From"). 50 // Use it when the "From" is an email of a domain that is a SPF protected domain, and sending smtp server is not this domain. In such case, add Sender field with an email of the protected domain. 51 // Return-Path: Email where to send bounds. 52 public $reply_to; // Reply-To: Email where to send replies from mailer software (mailer use From if reply-to not defined, Gmail use gmail account if reply-to not defined) 53 public $errors_to; // Errors-To: Email where to send errors. 54 public $addr_to; 55 public $addr_cc; 56 public $addr_bcc; 57 public $trackid; 58 59 public $mixed_boundary; 60 public $related_boundary; 61 public $alternative_boundary; 62 public $deliveryreceipt; 63 64 public $atleastonefile; 65 66 public $eol; 67 public $eol2; 68 69 /** 70 * @var string Error code (or message) 71 */ 72 public $error = ''; 73 74 public $smtps; // Contains SMTPs object (if this method is used) 75 public $phpmailer; // Contains PHPMailer object (if this method is used) 76 77 /** 78 * @var string CSS 79 */ 80 public $css; 81 //! Defined css style for body background 82 public $styleCSS; 83 //! Defined background directly in body tag 84 public $bodyCSS; 85 86 public $msgid; 87 public $headers; 88 public $message; 89 /** 90 * @var array fullfilenames list (full path of filename on file system) 91 */ 92 public $filename_list = array(); 93 /** 94 * @var array mimetypes of files list (List of MIME type of attached files) 95 */ 96 public $mimetype_list = array(); 97 /** 98 * @var array filenames list (List of attached file name in message) 99 */ 100 public $mimefilename_list = array(); 101 102 // Image 103 public $html; 104 public $image_boundary; 105 public $atleastoneimage = 0; // at least one image file with file=xxx.ext into content (TODO Debug this. How can this case be tested. Remove if not used). 106 public $html_images = array(); 107 public $images_encoded = array(); 108 public $image_types = array( 109 'gif' => 'image/gif', 110 'jpg' => 'image/jpeg', 111 'jpeg' => 'image/jpeg', 112 'jpe' => 'image/jpeg', 113 'bmp' => 'image/bmp', 114 'png' => 'image/png', 115 'tif' => 'image/tiff', 116 'tiff' => 'image/tiff', 117 ); 118 119 120 /** 121 * CMailFile 122 * 123 * @param string $subject Topic/Subject of mail 124 * @param string $to Recipients emails (RFC 2822: "Name firstname <email>[, ...]" or "email[, ...]" or "<email>[, ...]"). Note: the keyword '__SUPERVISOREMAIL__' is not allowed here and must be replaced by caller. 125 * @param string $from Sender email (RFC 2822: "Name firstname <email>[, ...]" or "email[, ...]" or "<email>[, ...]") 126 * @param string $msg Message 127 * @param array $filename_list List of files to attach (full path of filename on file system) 128 * @param array $mimetype_list List of MIME type of attached files 129 * @param array $mimefilename_list List of attached file name in message 130 * @param string $addr_cc Email cc 131 * @param string $addr_bcc Email bcc (Note: This is autocompleted with MAIN_MAIL_AUTOCOPY_TO if defined) 132 * @param int $deliveryreceipt Ask a delivery receipt 133 * @param int $msgishtml 1=String IS already html, 0=String IS NOT html, -1=Unknown make autodetection (with fast mode, not reliable) 134 * @param string $errors_to Email for errors-to 135 * @param string $css Css option 136 * @param string $trackid Tracking string (contains type and id of related element) 137 * @param string $moreinheader More in header. $moreinheader must contains the "\r\n" (TODO not supported for other MAIL_SEND_MODE different than 'phpmail' and 'smtps' for the moment) 138 * @param string $sendcontext 'standard', 'emailing', ... (used to define which sending mode and parameters to use) 139 * @param string $replyto Reply-to email (will be set to same value than From by default if not provided) 140 */ 141 public function __construct($subject, $to, $from, $msg, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $addr_cc = "", $addr_bcc = "", $deliveryreceipt = 0, $msgishtml = 0, $errors_to = '', $css = '', $trackid = '', $moreinheader = '', $sendcontext = 'standard', $replyto = '') 142 { 143 global $conf, $dolibarr_main_data_root; 144 145 // Clean values of $mimefilename_list 146 if (is_array($mimefilename_list)) { 147 foreach ($mimefilename_list as $key => $val) { 148 $mimefilename_list[$key] = dol_string_unaccent($mimefilename_list[$key]); 149 } 150 } 151 152 // Add autocopy to if not already in $to (Note: Adding bcc for specific modules are also done from pages) 153 if (!empty($conf->global->MAIN_MAIL_AUTOCOPY_TO) && !preg_match('/'.preg_quote($conf->global->MAIN_MAIL_AUTOCOPY_TO, '/').'/i', $to)) { 154 $addr_bcc .= ($addr_bcc ? ', ' : '').$conf->global->MAIN_MAIL_AUTOCOPY_TO; 155 } 156 157 $this->subject = $subject; 158 $this->addr_to = $to; 159 $this->addr_from = $from; 160 $this->msg = $msg; 161 $this->filename_list = $filename_list; 162 $this->mimetype_list = $mimetype_list; 163 $this->mimefilename_list = $mimefilename_list; 164 $this->addr_cc = $addr_cc; 165 $this->addr_bcc = $addr_bcc; 166 $this->deliveryreceipt = $deliveryreceipt; 167 if (empty($replyto)) { 168 $replyto = $from; 169 } 170 $this->reply_to = $replyto; 171 $this->errors_to = $errors_to; 172 $this->trackid = $trackid; 173 $this->sendcontext = $sendcontext; 174 $this->filename_list = $filename_list; 175 $this->mimetype_list = $mimetype_list; 176 $this->mimefilename_list = $mimefilename_list; 177 178 // Define this->sendmode 179 $this->sendmode = ''; 180 if (!empty($this->sendcontext)) { 181 $smtpContextKey = strtoupper($this->sendcontext); 182 $keyForSMTPSendMode = 'MAIN_MAIL_SENDMODE_'.$smtpContextKey; 183 $smtpContextSendMode = empty($conf->global->{$keyForSMTPSendMode}) ? '' : $conf->global->{$keyForSMTPSendMode}; 184 if (!empty($smtpContextSendMode) && $smtpContextSendMode != 'default') { 185 $this->sendmode = $smtpContextSendMode; 186 } 187 } 188 if (empty($this->sendmode)) { 189 $this->sendmode = $conf->global->MAIN_MAIL_SENDMODE; 190 } 191 if (empty($this->sendmode)) { 192 $this->sendmode = 'mail'; 193 } 194 195 // We define end of line (RFC 821). 196 $this->eol = "\r\n"; 197 // We define end of line for header fields (RFC 822bis section 2.3 says header must contains \r\n). 198 $this->eol2 = "\r\n"; 199 if (!empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA)) { 200 $this->eol = "\n"; 201 $this->eol2 = "\n"; 202 $moreinheader = str_replace("\r\n", "\n", $moreinheader); 203 } 204 205 // On defini mixed_boundary 206 $this->mixed_boundary = "multipart_x.".time().".x_boundary"; 207 208 // On defini related_boundary 209 $this->related_boundary = 'mul_'.dol_hash(uniqid("dolibarr2"), 3); // Force md5 hash (does not contains special chars) 210 211 // On defini alternative_boundary 212 $this->alternative_boundary = 'mul_'.dol_hash(uniqid("dolibarr3"), 3); // Force md5 hash (does not contains special chars) 213 214 dol_syslog("CMailFile::CMailfile: sendmode=".$this->sendmode." charset=".$conf->file->character_set_client." from=$from, to=$to, addr_cc=$addr_cc, addr_bcc=$addr_bcc, errors_to=$errors_to, replyto=$replyto trackid=$trackid sendcontext=$sendcontext", LOG_DEBUG); 215 dol_syslog("CMailFile::CMailfile: subject=".$subject.", deliveryreceipt=".$deliveryreceipt.", msgishtml=".$msgishtml, LOG_DEBUG); 216 217 if (empty($subject)) { 218 dol_syslog("CMailFile::CMailfile: Try to send an email with empty subject"); 219 $this->error = 'ErrorSubjectIsRequired'; 220 return; 221 } 222 if (empty($msg)) { 223 dol_syslog("CMailFile::CMailfile: Try to send an email with empty body"); 224 $msg = '.'; // Avoid empty message (with empty message content, you will see a multipart structure) 225 } 226 227 // Detect if message is HTML (use fast method) 228 if ($msgishtml == -1) { 229 $this->msgishtml = 0; 230 if (dol_textishtml($msg)) { 231 $this->msgishtml = 1; 232 } 233 } else { 234 $this->msgishtml = $msgishtml; 235 } 236 237 global $dolibarr_main_url_root; 238 239 // Define $urlwithroot 240 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); 241 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file 242 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current 243 244 // Replace relative /viewimage to absolute path 245 $msg = preg_replace('/src="'.preg_quote(DOL_URL_ROOT, '/').'\/viewimage\.php/ims', 'src="'.$urlwithroot.'/viewimage.php', $msg, -1); 246 247 if (!empty($conf->global->MAIN_MAIL_FORCE_CONTENT_TYPE_TO_HTML)) { 248 $this->msgishtml = 1; // To force to send everything with content type html. 249 } 250 251 // Detect images 252 if ($this->msgishtml) { 253 $this->html = $msg; 254 255 $findimg = 0; 256 if (!empty($conf->global->MAIN_MAIL_ADD_INLINE_IMAGES_IF_IN_MEDIAS)) { 257 $findimg = $this->findHtmlImages($dolibarr_main_data_root.'/medias'); 258 } 259 260 // Define if there is at least one file 261 if ($findimg) { 262 foreach ($this->html_images as $i => $val) { 263 if ($this->html_images[$i]) { 264 $this->atleastoneimage = 1; 265 dol_syslog("CMailFile::CMailfile: html_images[$i]['name']=".$this->html_images[$i]['name'], LOG_DEBUG); 266 } 267 } 268 } 269 } 270 271 // Define if there is at least one file 272 if (is_array($filename_list)) { 273 foreach ($filename_list as $i => $val) { 274 if ($filename_list[$i]) { 275 $this->atleastonefile = 1; 276 dol_syslog("CMailFile::CMailfile: filename_list[$i]=".$filename_list[$i].", mimetype_list[$i]=".$mimetype_list[$i]." mimefilename_list[$i]=".$mimefilename_list[$i], LOG_DEBUG); 277 } 278 } 279 } 280 281 // Add autocopy to if not already in $to (Note: Adding bcc for specific modules are also done from pages) 282 if (!empty($conf->global->MAIN_MAIL_AUTOCOPY_TO) && !preg_match('/'.preg_quote($conf->global->MAIN_MAIL_AUTOCOPY_TO, '/').'/i', $to)) { 283 $addr_bcc .= ($addr_bcc ? ', ' : '').$conf->global->MAIN_MAIL_AUTOCOPY_TO; 284 } 285 286 $this->addr_to = $to; 287 $this->addr_cc = $addr_cc; 288 $this->addr_bcc = $addr_bcc; 289 $this->reply_to = $replyto; 290 $this->addr_from = $from; 291 $this->subject = $subject; 292 $this->errors_to = $errors_to; 293 $this->deliveryreceipt = $deliveryreceipt; 294 $this->trackid = $trackid; 295 296 if (!empty($conf->global->MAIN_MAIL_FORCE_SENDTO)) { 297 $this->addr_to = $conf->global->MAIN_MAIL_FORCE_SENDTO; 298 $this->addr_cc = ''; 299 $this->addr_bcc = ''; 300 } 301 302 $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED'; 303 if (!empty($this->sendcontext)) { 304 $smtpContextKey = strtoupper($this->sendcontext); 305 $keyForSMTPSendMode = 'MAIN_MAIL_SENDMODE_'.$smtpContextKey; 306 $smtpContextSendMode = empty($conf->global->{$keyForSMTPSendMode}) ? '' : $conf->global->{$keyForSMTPSendMode}; 307 if (!empty($smtpContextSendMode) && $smtpContextSendMode != 'default') { 308 $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED_'.$smtpContextKey; 309 } 310 } 311 312 // We set all data according to choosed sending method. 313 // We also set a value for ->msgid 314 if ($this->sendmode == 'mail') { 315 // Use mail php function (default PHP method) 316 // ------------------------------------------ 317 318 $smtp_headers = ""; 319 $mime_headers = ""; 320 $text_body = ""; 321 $files_encoded = ""; 322 323 // Define smtp_headers (this also set ->msgid) 324 $smtp_headers = $this->write_smtpheaders(); 325 if (!empty($moreinheader)) { 326 $smtp_headers .= $moreinheader; // $moreinheader contains the \r\n 327 } 328 329 // Define mime_headers 330 $mime_headers = $this->write_mimeheaders($filename_list, $mimefilename_list); 331 332 if (!empty($this->html)) { 333 if (!empty($css)) { 334 $this->css = $css; 335 $this->buildCSS(); // Build a css style (mode = all) into this->styleCSS and this->bodyCSS 336 } 337 338 $msg = $this->html; 339 } 340 341 // Define body in text_body 342 $text_body = $this->write_body($msg); 343 344 // Add attachments to text_encoded 345 if (!empty($this->atleastonefile)) { 346 $files_encoded = $this->write_files($filename_list, $mimetype_list, $mimefilename_list); 347 } 348 349 // We now define $this->headers and $this->message 350 $this->headers = $smtp_headers.$mime_headers; 351 // On nettoie le header pour qu'il ne se termine pas par un retour chariot. 352 // This avoid also empty lines at end that can be interpreted as mail injection by email servers. 353 $this->headers = preg_replace("/([\r\n]+)$/i", "", $this->headers); 354 355 //$this->message = $this->eol.'This is a message with multiple parts in MIME format.'.$this->eol; 356 $this->message = 'This is a message with multiple parts in MIME format.'.$this->eol; 357 $this->message .= $text_body.$files_encoded; 358 $this->message .= "--".$this->mixed_boundary."--".$this->eol; 359 } elseif ($this->sendmode == 'smtps') { 360 // Use SMTPS library 361 // ------------------------------------------ 362 363 require_once DOL_DOCUMENT_ROOT.'/core/class/smtps.class.php'; 364 $smtps = new SMTPs(); 365 $smtps->setCharSet($conf->file->character_set_client); 366 367 // Encode subject if required. 368 $subjecttouse = $this->subject; 369 if (!ascii_check($subjecttouse)) { 370 $subjecttouse = $this->encodetorfc2822($subjecttouse); 371 } 372 373 $smtps->setSubject($subjecttouse); 374 $smtps->setTO($this->getValidAddress($this->addr_to, 0, 1)); 375 $smtps->setFrom($this->getValidAddress($this->addr_from, 0, 1)); 376 $smtps->setTrackId($this->trackid); 377 $smtps->setReplyTo($this->getValidAddress($this->reply_to, 0, 1)); 378 379 if (!empty($moreinheader)) { 380 $smtps->setMoreInHeader($moreinheader); 381 } 382 383 if (!empty($this->html)) { 384 if (!empty($css)) { 385 $this->css = $css; 386 $this->buildCSS(); 387 } 388 $msg = $this->html; 389 $msg = $this->checkIfHTML($msg); 390 } 391 392 // Replace . alone on a new line with .. to avoid to have SMTP interpret this as end of message 393 $msg = preg_replace('/(\r|\n)\.(\r|\n)/ims', '\1..\2', $msg); 394 395 if ($this->msgishtml) { 396 $smtps->setBodyContent($msg, 'html'); 397 } else { 398 $smtps->setBodyContent($msg, 'plain'); 399 } 400 401 if ($this->atleastoneimage) { 402 foreach ($this->images_encoded as $img) { 403 $smtps->setImageInline($img['image_encoded'], $img['name'], $img['content_type'], $img['cid']); 404 } 405 } 406 407 if (!empty($this->atleastonefile)) { 408 foreach ($filename_list as $i => $val) { 409 $content = file_get_contents($filename_list[$i]); 410 $smtps->setAttachment($content, $mimefilename_list[$i], $mimetype_list[$i]); 411 } 412 } 413 414 $smtps->setCC($this->addr_cc); 415 $smtps->setBCC($this->addr_bcc); 416 $smtps->setErrorsTo($this->errors_to); 417 $smtps->setDeliveryReceipt($this->deliveryreceipt); 418 if (!empty($conf->global->$keyforsslseflsigned)) { 419 $smtps->setOptions(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true))); 420 } 421 422 $host = dol_getprefix('email'); 423 $this->msgid = time().'.SMTPs-dolibarr-'.$this->trackid.'@'.$host; 424 425 $this->smtps = $smtps; 426 } elseif ($this->sendmode == 'swiftmailer') { 427 // Use Swift Mailer library 428 $host = dol_getprefix('email'); 429 430 require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php'; 431 432 // egulias autoloader lib 433 require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/autoload.php'; 434 435 require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/swift_required.php'; 436 437 // Create the message 438 //$this->message = Swift_Message::newInstance(); 439 $this->message = new Swift_Message(); 440 //$this->message = new Swift_SignedMessage(); 441 // Adding a trackid header to a message 442 $headers = $this->message->getHeaders(); 443 $headers->addTextHeader('X-Dolibarr-TRACKID', $this->trackid.'@'.$host); 444 $this->msgid = time().'.swiftmailer-dolibarr-'.$this->trackid.'@'.$host; 445 $headerID = $this->msgid; 446 $msgid = $headers->get('Message-ID'); 447 $msgid->setId($headerID); 448 $headers->addIdHeader('References', $headerID); 449 // TODO if (! empty($moreinheader)) ... 450 451 // Give the message a subject 452 try { 453 $result = $this->message->setSubject($this->subject); 454 } catch (Exception $e) { 455 $this->errors[] = $e->getMessage(); 456 } 457 458 // Set the From address with an associative array 459 //$this->message->setFrom(array('john@doe.com' => 'John Doe')); 460 if (!empty($this->addr_from)) { 461 try { 462 if (!empty($conf->global->MAIN_FORCE_DISABLE_MAIL_SPOOFING)) { 463 // Prevent email spoofing for smtp server with a strict configuration 464 $regexp = '/([a-z0-9_\.\-\+])+\@(([a-z0-9\-])+\.)+([a-z0-9]{2,4})+/i'; // This regular expression extracts all emails from a string 465 $emailMatchs = preg_match_all($regexp, $from, $adressEmailFrom); 466 $adressEmailFrom = reset($adressEmailFrom); 467 if ($emailMatchs !== false && filter_var($conf->global->MAIN_MAIL_SMTPS_ID, FILTER_VALIDATE_EMAIL) && $conf->global->MAIN_MAIL_SMTPS_ID !== $adressEmailFrom) { 468 $result = $this->message->setFrom($conf->global->MAIN_MAIL_SMTPS_ID); 469 } else { 470 $result = $this->message->setFrom($this->getArrayAddress($this->addr_from)); 471 } 472 } else { 473 $result = $this->message->setFrom($this->getArrayAddress($this->addr_from)); 474 } 475 } catch (Exception $e) { 476 $this->errors[] = $e->getMessage(); 477 } 478 } 479 480 // Set the To addresses with an associative array 481 if (!empty($this->addr_to)) { 482 try { 483 $result = $this->message->setTo($this->getArrayAddress($this->addr_to)); 484 } catch (Exception $e) { 485 $this->errors[] = $e->getMessage(); 486 } 487 } 488 489 if (!empty($this->reply_to)) { 490 try { 491 $result = $this->message->SetReplyTo($this->getArrayAddress($this->reply_to)); 492 } catch (Exception $e) { 493 $this->errors[] = $e->getMessage(); 494 } 495 } 496 497 try { 498 $result = $this->message->setCharSet($conf->file->character_set_client); 499 } catch (Exception $e) { 500 $this->errors[] = $e->getMessage(); 501 } 502 503 if (!empty($this->html)) { 504 if (!empty($css)) { 505 $this->css = $css; 506 $this->buildCSS(); 507 } 508 $msg = $this->html; 509 $msg = $this->checkIfHTML($msg); 510 } 511 512 if ($this->atleastoneimage) { 513 foreach ($this->images_encoded as $img) { 514 //$img['fullpath'],$img['image_encoded'],$img['name'],$img['content_type'],$img['cid'] 515 $attachment = Swift_Image::fromPath($img['fullpath']); 516 // embed image 517 $imgcid = $this->message->embed($attachment); 518 // replace cid by the one created by swiftmail in html message 519 $msg = str_replace("cid:".$img['cid'], $imgcid, $msg); 520 } 521 } 522 523 if ($this->msgishtml) { 524 $this->message->setBody($msg, 'text/html'); 525 // And optionally an alternative body 526 $this->message->addPart(html_entity_decode(strip_tags($msg)), 'text/plain'); 527 } else { 528 $this->message->setBody($msg, 'text/plain'); 529 // And optionally an alternative body 530 $this->message->addPart(dol_nl2br($msg), 'text/html'); 531 } 532 533 if (!empty($this->atleastonefile)) { 534 foreach ($filename_list as $i => $val) { 535 //$this->message->attach(Swift_Attachment::fromPath($filename_list[$i],$mimetype_list[$i])); 536 $attachment = Swift_Attachment::fromPath($filename_list[$i], $mimetype_list[$i]); 537 if (!empty($mimefilename_list[$i])) { 538 $attachment->setFilename($mimefilename_list[$i]); 539 } 540 $this->message->attach($attachment); 541 } 542 } 543 544 if (!empty($this->addr_cc)) { 545 $this->message->setCc($this->getArrayAddress($this->addr_cc)); 546 } 547 if (!empty($this->addr_bcc)) { 548 $this->message->setBcc($this->getArrayAddress($this->addr_bcc)); 549 } 550 //if (! empty($this->errors_to)) $this->message->setErrorsTo($this->getArrayAddress($this->errors_to)); 551 if (isset($this->deliveryreceipt) && $this->deliveryreceipt == 1) { 552 $this->message->setReadReceiptTo($this->getArrayAddress($this->addr_from)); 553 } 554 } else { 555 // Send mail method not correctly defined 556 // -------------------------------------- 557 $this->error = 'Bad value for sendmode'; 558 } 559 } 560 561 562 /** 563 * Send mail that was prepared by constructor. 564 * 565 * @return boolean True if mail sent, false otherwise 566 */ 567 public function sendfile() 568 { 569 global $conf, $db, $langs; 570 571 $errorlevel = error_reporting(); 572 //error_reporting($errorlevel ^ E_WARNING); // Desactive warnings 573 574 $res = false; 575 576 if (empty($conf->global->MAIN_DISABLE_ALL_MAILS)) { 577 require_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; 578 $hookmanager = new HookManager($db); 579 $hookmanager->initHooks(array('mail')); 580 581 $parameters = array(); 582 $action = ''; 583 $reshook = $hookmanager->executeHooks('sendMail', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 584 if ($reshook < 0) { 585 $this->error = "Error in hook maildao sendMail ".$reshook; 586 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 587 588 return $reshook; 589 } 590 if ($reshook == 1) { // Hook replace standard code 591 return true; 592 } 593 594 $sendingmode = $this->sendmode; 595 if ($this->context == 'emailing' && !empty($conf->global->MAILING_NO_USING_PHPMAIL) && $sendingmode == 'mail') { 596 // List of sending methods 597 $listofmethods = array(); 598 $listofmethods['mail'] = 'PHP mail function'; 599 //$listofmethods['simplemail']='Simplemail class'; 600 $listofmethods['smtps'] = 'SMTP/SMTPS socket library'; 601 602 // EMailing feature may be a spam problem, so when you host several users/instance, having this option may force each user to use their own SMTP agent. 603 // You ensure that every user is using its own SMTP server when using the mass emailing module. 604 $linktoadminemailbefore = '<a href="'.DOL_URL_ROOT.'/admin/mails.php">'; 605 $linktoadminemailend = '</a>'; 606 $this->error = $langs->trans("MailSendSetupIs", $listofmethods[$sendingmode]); 607 $this->errors[] = $langs->trans("MailSendSetupIs", $listofmethods[$sendingmode]); 608 $this->error .= '<br>'.$langs->trans("MailSendSetupIs2", $linktoadminemailbefore, $linktoadminemailend, $langs->transnoentitiesnoconv("MAIN_MAIL_SENDMODE"), $listofmethods['smtps']); 609 $this->errors[] = $langs->trans("MailSendSetupIs2", $linktoadminemailbefore, $linktoadminemailend, $langs->transnoentitiesnoconv("MAIN_MAIL_SENDMODE"), $listofmethods['smtps']); 610 if (!empty($conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS)) { 611 $this->error .= '<br>'.$langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS); 612 $this->errors[] = $langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS); 613 } 614 return false; 615 } 616 617 // Check number of recipient is lower or equal than MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL 618 if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL)) { 619 $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL = 10; 620 } 621 $tmparray1 = explode(',', $this->addr_to); 622 if (count($tmparray1) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL) { 623 $this->error = 'Too much recipients in to:'; 624 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING); 625 return false; 626 } 627 if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL)) { 628 $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL = 10; 629 } 630 $tmparray2 = explode(',', $this->addr_cc); 631 if (count($tmparray2) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL) { 632 $this->error = 'Too much recipients in cc:'; 633 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING); 634 return false; 635 } 636 if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL)) { 637 $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL = 10; 638 } 639 $tmparray3 = explode(',', $this->addr_bcc); 640 if (count($tmparray3) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL) { 641 $this->error = 'Too much recipients in bcc:'; 642 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING); 643 return false; 644 } 645 if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL)) { 646 $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL = 10; 647 } 648 if ((count($tmparray1) + count($tmparray2) + count($tmparray3)) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL) { 649 $this->error = 'Too much recipients in to:, cc:, bcc:'; 650 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING); 651 return false; 652 } 653 654 $keyforsmtpserver = 'MAIN_MAIL_SMTP_SERVER'; 655 $keyforsmtpport = 'MAIN_MAIL_SMTP_PORT'; 656 $keyforsmtpid = 'MAIN_MAIL_SMTPS_ID'; 657 $keyforsmtppw = 'MAIN_MAIL_SMTPS_PW'; 658 $keyfortls = 'MAIN_MAIL_EMAIL_TLS'; 659 $keyforstarttls = 'MAIN_MAIL_EMAIL_STARTTLS'; 660 $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED'; 661 if (!empty($this->sendcontext)) { 662 $smtpContextKey = strtoupper($this->sendcontext); 663 $keyForSMTPSendMode = 'MAIN_MAIL_SENDMODE_'.$smtpContextKey; 664 $smtpContextSendMode = empty($conf->global->{$keyForSMTPSendMode}) ? '' : $conf->global->{$keyForSMTPSendMode}; 665 if (!empty($smtpContextSendMode) && $smtpContextSendMode != 'default') { 666 $keyforsmtpserver = 'MAIN_MAIL_SMTP_SERVER_'.$smtpContextKey; 667 $keyforsmtpport = 'MAIN_MAIL_SMTP_PORT_'.$smtpContextKey; 668 $keyforsmtpid = 'MAIN_MAIL_SMTPS_ID_'.$smtpContextKey; 669 $keyforsmtppw = 'MAIN_MAIL_SMTPS_PW_'.$smtpContextKey; 670 $keyfortls = 'MAIN_MAIL_EMAIL_TLS_'.$smtpContextKey; 671 $keyforstarttls = 'MAIN_MAIL_EMAIL_STARTTLS_'.$smtpContextKey; 672 $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED_'.$smtpContextKey; 673 } 674 } 675 676 // Action according to choosed sending method 677 if ($this->sendmode == 'mail') { 678 // Use mail php function (default PHP method) 679 // ------------------------------------------ 680 dol_syslog("CMailFile::sendfile addr_to=".$this->addr_to.", subject=".$this->subject, LOG_DEBUG); 681 dol_syslog("CMailFile::sendfile header=\n".$this->headers, LOG_DEBUG); 682 //dol_syslog("CMailFile::sendfile message=\n".$message); 683 684 // If Windows, sendmail_from must be defined 685 if (isset($_SERVER["WINDIR"])) { 686 if (empty($this->addr_from)) { 687 $this->addr_from = 'robot@example.com'; 688 } 689 @ini_set('sendmail_from', $this->getValidAddress($this->addr_from, 2)); 690 } 691 692 // Force parameters 693 //dol_syslog("CMailFile::sendfile conf->global->".$keyforsmtpserver."=".$conf->global->$keyforsmtpserver." cpnf->global->".$keyforsmtpport."=".$conf->global->$keyforsmtpport, LOG_DEBUG); 694 if (!empty($conf->global->$keyforsmtpserver)) { 695 ini_set('SMTP', $conf->global->$keyforsmtpserver); 696 } 697 if (!empty($conf->global->$keyforsmtpport)) { 698 ini_set('smtp_port', $conf->global->$keyforsmtpport); 699 } 700 701 $res = true; 702 if ($res && !$this->subject) { 703 $this->error = "Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."<br>Subject is empty"; 704 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 705 $res = false; 706 } 707 $dest = $this->getValidAddress($this->addr_to, 2); 708 if ($res && !$dest) { 709 $this->error = "Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."<br>Recipient address '$dest' invalid"; 710 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 711 $res = false; 712 } 713 714 if ($res) { 715 $additionnalparam = ''; // By default 716 if (!empty($conf->global->MAIN_MAIL_ALLOW_SENDMAIL_F)) { 717 // le "Return-Path" (retour des messages bounced) dans les header ne fonctionne pas avec tous les MTA 718 // Le forcage de la valeur grace à l'option -f de sendmail est donc possible si la constante MAIN_MAIL_ALLOW_SENDMAIL_F est definie. 719 // Having this variable defined may create problems with some sendmail (option -f refused) 720 // Having this variable not defined may create problems with some other sendmail (option -f required) 721 $additionnalparam .= ($additionnalparam ? ' ' : '').(!empty($conf->global->MAIN_MAIL_ERRORS_TO) ? '-f'.$this->getValidAddress($conf->global->MAIN_MAIL_ERRORS_TO, 2) : ($this->addr_from != '' ? '-f'.$this->getValidAddress($this->addr_from, 2) : '')); 722 } 723 if (!empty($conf->global->MAIN_MAIL_SENDMAIL_FORCE_BA)) { // To force usage of -ba option. This option tells sendmail to read From: or Sender: to setup sender 724 $additionnalparam .= ($additionnalparam ? ' ' : '').'-ba'; 725 } 726 727 if (!empty($conf->global->MAIN_MAIL_SENDMAIL_FORCE_ADDPARAM)) { 728 $additionnalparam .= ($additionnalparam ? ' ' : '').'-U '.$additionnalparam; // Use -U to add additionnal params 729 } 730 731 $linuxlike = 1; 732 if (preg_match('/^win/i', PHP_OS)) { 733 $linuxlike = 0; 734 } 735 if (preg_match('/^mac/i', PHP_OS)) { 736 $linuxlike = 0; 737 } 738 739 dol_syslog("CMailFile::sendfile: mail start".($linuxlike ? '' : " HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')).", additionnal_parameters=".$additionnalparam, LOG_DEBUG); 740 741 $this->message = stripslashes($this->message); 742 743 if (!empty($conf->global->MAIN_MAIL_DEBUG)) { 744 $this->dump_mail(); 745 } 746 747 // Encode subject if required. 748 $subjecttouse = $this->subject; 749 if (!ascii_check($subjecttouse)) { 750 $subjecttouse = $this->encodetorfc2822($subjecttouse); 751 } 752 753 if (!empty($additionnalparam)) { 754 $res = mail($dest, $subjecttouse, $this->message, $this->headers, $additionnalparam); 755 } else { 756 $res = mail($dest, $subjecttouse, $this->message, $this->headers); 757 } 758 759 if (!$res) { 760 $langs->load("errors"); 761 $this->error = "Failed to send mail with php mail"; 762 if (!$linuxlike) { 763 $this->error .= " to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port'); // This values are value used only for non linuxlike systems 764 } 765 $this->error .= ".<br>"; 766 $this->error .= $langs->trans("ErrorPhpMailDelivery"); 767 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 768 } else { 769 dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG); 770 } 771 } 772 773 if (isset($_SERVER["WINDIR"])) { 774 @ini_restore('sendmail_from'); 775 } 776 777 // Restore parameters 778 if (!empty($conf->global->$keyforsmtpserver)) { 779 ini_restore('SMTP'); 780 } 781 if (!empty($conf->global->$keyforsmtpport)) { 782 ini_restore('smtp_port'); 783 } 784 } elseif ($this->sendmode == 'smtps') { 785 if (!is_object($this->smtps)) { 786 $this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."<br>Constructor of object CMailFile was not initialized without errors."; 787 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 788 return false; 789 } 790 791 // Use SMTPS library 792 // ------------------------------------------ 793 $this->smtps->setTransportType(0); // Only this method is coded in SMTPs library 794 795 // Clean parameters 796 if (empty($conf->global->$keyforsmtpserver)) { 797 $conf->global->$keyforsmtpserver = ini_get('SMTP'); 798 } 799 if (empty($conf->global->$keyforsmtpport)) { 800 $conf->global->$keyforsmtpport = ini_get('smtp_port'); 801 } 802 803 // If we use SSL/TLS 804 $server = $conf->global->$keyforsmtpserver; 805 $secure = ''; 806 if (!empty($conf->global->$keyfortls) && function_exists('openssl_open')) { 807 $secure = 'ssl'; 808 } 809 if (!empty($conf->global->$keyforstarttls) && function_exists('openssl_open')) { 810 $secure = 'tls'; 811 } 812 $server = ($secure ? $secure.'://' : '').$server; 813 814 $port = $conf->global->$keyforsmtpport; 815 816 $this->smtps->setHost($server); 817 $this->smtps->setPort($port); // 25, 465...; 818 819 $loginid = ''; 820 $loginpass = ''; 821 if (!empty($conf->global->$keyforsmtpid)) { 822 $loginid = $conf->global->$keyforsmtpid; 823 $this->smtps->setID($loginid); 824 } 825 if (!empty($conf->global->$keyforsmtppw)) { 826 $loginpass = $conf->global->$keyforsmtppw; 827 $this->smtps->setPW($loginpass); 828 } 829 830 $res = true; 831 $from = $this->smtps->getFrom('org'); 832 if ($res && !$from) { 833 $this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - Sender address '$from' invalid"; 834 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 835 $res = false; 836 } 837 $dest = $this->smtps->getTo(); 838 if ($res && !$dest) { 839 $this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - Recipient address '$dest' invalid"; 840 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 841 $res = false; 842 } 843 844 if ($res) { 845 if (!empty($conf->global->MAIN_MAIL_DEBUG)) { 846 $this->smtps->setDebug(true); 847 } 848 849 $result = $this->smtps->sendMsg(); 850 //print $result; 851 852 if (!empty($conf->global->MAIN_MAIL_DEBUG)) { 853 $this->dump_mail(); 854 } 855 856 $result = $this->smtps->getErrors(); 857 if (empty($this->error) && empty($result)) { 858 dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG); 859 $res = true; 860 } else { 861 if (empty($this->error)) { 862 $this->error = $result; 863 } 864 dol_syslog("CMailFile::sendfile: mail end error with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - ".$this->error, LOG_ERR); 865 $res = false; 866 } 867 } 868 } elseif ($this->sendmode == 'swiftmailer') { 869 // Use Swift Mailer library 870 // ------------------------------------------ 871 require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/swift_required.php'; 872 873 // Clean parameters 874 if (empty($conf->global->$keyforsmtpserver)) { 875 $conf->global->$keyforsmtpserver = ini_get('SMTP'); 876 } 877 if (empty($conf->global->$keyforsmtpport)) { 878 $conf->global->$keyforsmtpport = ini_get('smtp_port'); 879 } 880 881 // If we use SSL/TLS 882 $server = $conf->global->$keyforsmtpserver; 883 $secure = ''; 884 if (!empty($conf->global->$keyfortls) && function_exists('openssl_open')) { 885 $secure = 'ssl'; 886 } 887 if (!empty($conf->global->$keyforstarttls) && function_exists('openssl_open')) { 888 $secure = 'tls'; 889 } 890 891 $this->transport = new Swift_SmtpTransport($server, $conf->global->$keyforsmtpport, $secure); 892 893 if (!empty($conf->global->$keyforsmtpid)) { 894 $this->transport->setUsername($conf->global->$keyforsmtpid); 895 } 896 if (!empty($conf->global->$keyforsmtppw)) { 897 $this->transport->setPassword($conf->global->$keyforsmtppw); 898 } 899 if (!empty($conf->global->$keyforsslseflsigned)) { 900 $this->transport->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false))); 901 } 902 //$smtps->_msgReplyTo = 'reply@web.com'; 903 904 // Switch content encoding to base64 - avoid the doubledot issue with quoted-printable 905 $contentEncoderBase64 = new Swift_Mime_ContentEncoder_Base64ContentEncoder(); 906 $this->message->setEncoder($contentEncoderBase64); 907 908 // Create the Mailer using your created Transport 909 $this->mailer = new Swift_Mailer($this->transport); 910 911 // DKIM SIGN 912 if ($conf->global->MAIN_MAIL_EMAIL_DKIM_ENABLED) { 913 $privateKey = $conf->global->MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY; 914 $domainName = $conf->global->MAIN_MAIL_EMAIL_DKIM_DOMAIN; 915 $selector = $conf->global->MAIN_MAIL_EMAIL_DKIM_SELECTOR; 916 $signer = new Swift_Signers_DKIMSigner($privateKey, $domainName, $selector); 917 $this->message->attachSigner($signer->ignoreHeader('Return-Path')); 918 } 919 920 if (!empty($conf->global->MAIN_MAIL_DEBUG)) { 921 // To use the ArrayLogger 922 $this->logger = new Swift_Plugins_Loggers_ArrayLogger(); 923 // Or to use the Echo Logger 924 //$this->logger = new Swift_Plugins_Loggers_EchoLogger(); 925 $this->mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($this->logger)); 926 } 927 // send mail 928 try { 929 $result = $this->mailer->send($this->message, $failedRecipients); 930 } catch (Exception $e) { 931 $this->error = $e->getMessage(); 932 } 933 if (!empty($conf->global->MAIN_MAIL_DEBUG)) { 934 $this->dump_mail(); 935 } 936 937 $res = true; 938 if (!empty($this->error) || !$result) { 939 if (!empty($failedRecipients)) { 940 $this->error = 'Transport failed for the following addresses: "' . join('", "', $failedRecipients) . '".'; 941 } 942 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 943 $res = false; 944 } else { 945 dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG); 946 } 947 } else { 948 // Send mail method not correctly defined 949 // -------------------------------------- 950 951 return 'Bad value for sendmode'; 952 } 953 954 $parameters = array(); 955 $action = ''; 956 $reshook = $hookmanager->executeHooks('sendMailAfter', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 957 if ($reshook < 0) { 958 $this->error = "Error in hook maildao sendMailAfter ".$reshook; 959 dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR); 960 961 return $reshook; 962 } 963 } else { 964 $this->error = 'No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS'; 965 dol_syslog("CMailFile::sendfile: ".$this->error, LOG_WARNING); 966 } 967 968 error_reporting($errorlevel); // Reactive niveau erreur origine 969 970 return $res; 971 } 972 973 /** 974 * Encode subject according to RFC 2822 - http://en.wikipedia.org/wiki/MIME#Encoded-Word 975 * 976 * @param string $stringtoencode String to encode 977 * @return string string encoded 978 */ 979 public static function encodetorfc2822($stringtoencode) 980 { 981 global $conf; 982 return '=?'.$conf->file->character_set_client.'?B?'.base64_encode($stringtoencode).'?='; 983 } 984 985 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 986 /** 987 * Read a file on disk and return encoded content for emails (mode = 'mail') 988 * 989 * @param string $sourcefile Path to file to encode 990 * @return int|string <0 if KO, encoded string if OK 991 */ 992 private function _encode_file($sourcefile) 993 { 994 // phpcs:enable 995 $newsourcefile = dol_osencode($sourcefile); 996 997 if (is_readable($newsourcefile)) { 998 $contents = file_get_contents($newsourcefile); // Need PHP 4.3 999 $encoded = chunk_split(base64_encode($contents), 76, $this->eol); // 76 max is defined into http://tools.ietf.org/html/rfc2047 1000 return $encoded; 1001 } else { 1002 $this->error = "Error: Can't read file '".$sourcefile."' into _encode_file"; 1003 dol_syslog("CMailFile::encode_file: ".$this->error, LOG_ERR); 1004 return -1; 1005 } 1006 } 1007 1008 1009 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1010 /** 1011 * Write content of a SMTP request into a dump file (mode = all) 1012 * Used for debugging. 1013 * Note that to see full SMTP protocol, you can use tcpdump -w /tmp/smtp -s 2000 port 25" 1014 * 1015 * @return void 1016 */ 1017 public function dump_mail() 1018 { 1019 // phpcs:enable 1020 global $conf, $dolibarr_main_data_root; 1021 1022 if (@is_writeable($dolibarr_main_data_root)) { // Avoid fatal error on fopen with open_basedir 1023 $outputfile = $dolibarr_main_data_root."/dolibarr_mail.log"; 1024 $fp = fopen($outputfile, "w"); 1025 1026 if ($this->sendmode == 'mail') { 1027 fputs($fp, $this->headers); 1028 fputs($fp, $this->eol); // This eol is added by the mail function, so we add it in log 1029 fputs($fp, $this->message); 1030 } elseif ($this->sendmode == 'smtps') { 1031 fputs($fp, $this->smtps->log); // this->smtps->log is filled only if MAIN_MAIL_DEBUG was set to on 1032 } elseif ($this->sendmode == 'swiftmailer') { 1033 fputs($fp, $this->logger->dump()); // this->logger is filled only if MAIN_MAIL_DEBUG was set to on 1034 } 1035 1036 fclose($fp); 1037 if (!empty($conf->global->MAIN_UMASK)) { 1038 @chmod($outputfile, octdec($conf->global->MAIN_UMASK)); 1039 } 1040 } 1041 } 1042 1043 1044 /** 1045 * Correct an uncomplete html string 1046 * 1047 * @param string $msg String 1048 * @return string Completed string 1049 */ 1050 public function checkIfHTML($msg) 1051 { 1052 if (!preg_match('/^[\s\t]*<html/i', $msg)) { 1053 $out = "<html><head><title></title>"; 1054 if (!empty($this->styleCSS)) { 1055 $out .= $this->styleCSS; 1056 } 1057 $out .= "</head><body"; 1058 if (!empty($this->bodyCSS)) { 1059 $out .= $this->bodyCSS; 1060 } 1061 $out .= ">"; 1062 $out .= $msg; 1063 $out .= "</body></html>"; 1064 } else { 1065 $out = $msg; 1066 } 1067 1068 return $out; 1069 } 1070 1071 /** 1072 * Build a css style (mode = all) into this->styleCSS and this->bodyCSS 1073 * 1074 * @return string 1075 */ 1076 public function buildCSS() 1077 { 1078 if (!empty($this->css)) { 1079 // Style CSS 1080 $this->styleCSS = '<style type="text/css">'; 1081 $this->styleCSS .= 'body {'; 1082 1083 if ($this->css['bgcolor']) { 1084 $this->styleCSS .= ' background-color: '.$this->css['bgcolor'].';'; 1085 $this->bodyCSS .= ' bgcolor="'.$this->css['bgcolor'].'"'; 1086 } 1087 if ($this->css['bgimage']) { 1088 // TODO recuperer cid 1089 $this->styleCSS .= ' background-image: url("cid:'.$this->css['bgimage_cid'].'");'; 1090 } 1091 $this->styleCSS .= '}'; 1092 $this->styleCSS .= '</style>'; 1093 } 1094 } 1095 1096 1097 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1098 /** 1099 * Create SMTP headers (mode = 'mail') 1100 * 1101 * @return string headers 1102 */ 1103 public function write_smtpheaders() 1104 { 1105 // phpcs:enable 1106 global $conf; 1107 $out = ""; 1108 1109 $host = dol_getprefix('email'); 1110 1111 // Sender 1112 //$out.= "Sender: ".getValidAddress($this->addr_from,2)).$this->eol2; 1113 $out .= "From: ".$this->getValidAddress($this->addr_from, 3, 1).$this->eol2; 1114 if (!empty($conf->global->MAIN_MAIL_SENDMAIL_FORCE_BA)) { 1115 $out .= "To: ".$this->getValidAddress($this->addr_to, 0, 1).$this->eol2; 1116 } 1117 // Return-Path is important because it is used by SPF. Some MTA does not read Return-Path from header but from command line. See option MAIN_MAIL_ALLOW_SENDMAIL_F for that. 1118 $out .= "Return-Path: ".$this->getValidAddress($this->addr_from, 0, 1).$this->eol2; 1119 if (isset($this->reply_to) && $this->reply_to) { 1120 $out .= "Reply-To: ".$this->getValidAddress($this->reply_to, 2).$this->eol2; 1121 } 1122 if (isset($this->errors_to) && $this->errors_to) { 1123 $out .= "Errors-To: ".$this->getValidAddress($this->errors_to, 2).$this->eol2; 1124 } 1125 1126 // Receiver 1127 if (isset($this->addr_cc) && $this->addr_cc) { 1128 $out .= "Cc: ".$this->getValidAddress($this->addr_cc, 2).$this->eol2; 1129 } 1130 if (isset($this->addr_bcc) && $this->addr_bcc) { 1131 $out .= "Bcc: ".$this->getValidAddress($this->addr_bcc, 2).$this->eol2; // TODO Question: bcc must not be into header, only into SMTP command "RCPT TO". Does php mail support this ? 1132 } 1133 1134 // Delivery receipt 1135 if (isset($this->deliveryreceipt) && $this->deliveryreceipt == 1) { 1136 $out .= "Disposition-Notification-To: ".$this->getValidAddress($this->addr_from, 2).$this->eol2; 1137 } 1138 1139 //$out.= "X-Priority: 3".$this->eol2; 1140 1141 $out .= 'Date: '.date("r").$this->eol2; 1142 1143 $trackid = $this->trackid; 1144 if ($trackid) { 1145 // References is kept in response and Message-ID is returned into In-Reply-To: 1146 $this->msgid = time().'.phpmail-dolibarr-'.$trackid.'@'.$host; 1147 $out .= 'Message-ID: <'.$this->msgid.">".$this->eol2; // Uppercase seems replaced by phpmail 1148 $out .= 'References: <'.$this->msgid.">".$this->eol2; 1149 $out .= 'X-Dolibarr-TRACKID: '.$trackid.'@'.$host.$this->eol2; 1150 } else { 1151 $this->msgid = time().'.phpmail@'.$host; 1152 $out .= 'Message-ID: <'.$this->msgid.">".$this->eol2; 1153 } 1154 1155 if (!empty($_SERVER['REMOTE_ADDR'])) { 1156 $out .= "X-RemoteAddr: ".$_SERVER['REMOTE_ADDR'].$this->eol2; 1157 } 1158 $out .= "X-Mailer: Dolibarr version ".DOL_VERSION." (using php mail)".$this->eol2; 1159 $out .= "Mime-Version: 1.0".$this->eol2; 1160 1161 //$out.= "From: ".$this->getValidAddress($this->addr_from,3,1).$this->eol; 1162 1163 $out .= "Content-Type: multipart/mixed;".$this->eol2." boundary=\"".$this->mixed_boundary."\"".$this->eol2; 1164 $out .= "Content-Transfer-Encoding: 8bit".$this->eol2; // TODO Seems to be ignored. Header is 7bit once received. 1165 1166 dol_syslog("CMailFile::write_smtpheaders smtp_header=\n".$out); 1167 return $out; 1168 } 1169 1170 1171 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1172 /** 1173 * Create header MIME (mode = 'mail') 1174 * 1175 * @param array $filename_list Array of filenames 1176 * @param array $mimefilename_list Array of mime types 1177 * @return string mime headers 1178 */ 1179 public function write_mimeheaders($filename_list, $mimefilename_list) 1180 { 1181 // phpcs:enable 1182 $mimedone = 0; 1183 $out = ""; 1184 1185 if (is_array($filename_list)) { 1186 $filename_list_size = count($filename_list); 1187 for ($i = 0; $i < $filename_list_size; $i++) { 1188 if ($filename_list[$i]) { 1189 if ($mimefilename_list[$i]) { 1190 $filename_list[$i] = $mimefilename_list[$i]; 1191 } 1192 $out .= "X-attachments: $filename_list[$i]".$this->eol2; 1193 } 1194 } 1195 } 1196 1197 dol_syslog("CMailFile::write_mimeheaders mime_header=\n".$out, LOG_DEBUG); 1198 return $out; 1199 } 1200 1201 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1202 /** 1203 * Return email content (mode = 'mail') 1204 * 1205 * @param string $msgtext Message string 1206 * @return string String content 1207 */ 1208 public function write_body($msgtext) 1209 { 1210 // phpcs:enable 1211 global $conf; 1212 1213 $out = ''; 1214 1215 $out .= "--".$this->mixed_boundary.$this->eol; 1216 1217 if ($this->atleastoneimage) { 1218 $out .= "Content-Type: multipart/alternative;".$this->eol." boundary=\"".$this->alternative_boundary."\"".$this->eol; 1219 $out .= $this->eol; 1220 $out .= "--".$this->alternative_boundary.$this->eol; 1221 } 1222 1223 // Make RFC821 Compliant, replace bare linefeeds 1224 $strContent = preg_replace("/(?<!\r)\n/si", "\r\n", $msgtext); // PCRE modifier /s means new lines are common chars 1225 if (!empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA)) { 1226 $strContent = preg_replace("/\r\n/si", "\n", $strContent); // PCRE modifier /s means new lines are common chars 1227 } 1228 1229 $strContentAltText = ''; 1230 if ($this->msgishtml) { 1231 // Similar code to forge a text from html is also in smtps.class.php 1232 $strContentAltText = preg_replace("/<br\s*[^>]*>/", " ", $strContent); 1233 $strContentAltText = html_entity_decode(strip_tags($strContentAltText)); 1234 $strContentAltText = trim(wordwrap($strContentAltText, 75, empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA) ? "\r\n" : "\n")); 1235 1236 // Check if html header already in message, if not complete the message 1237 $strContent = $this->checkIfHTML($strContent); 1238 } 1239 1240 // Make RFC2045 Compliant, split lines 1241 //$strContent = rtrim(chunk_split($strContent)); // Function chunck_split seems ko if not used on a base64 content 1242 // TODO Encode main content into base64 and use the chunk_split, or quoted-printable 1243 $strContent = rtrim(wordwrap($strContent, 75, empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA) ? "\r\n" : "\n")); // TODO Using this method creates unexpected line break on text/plain content. 1244 1245 if ($this->msgishtml) { 1246 if ($this->atleastoneimage) { 1247 $out .= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol; 1248 //$out.= "Content-Transfer-Encoding: 7bit".$this->eol; 1249 $out .= $this->eol.($strContentAltText ? $strContentAltText : strip_tags($strContent)).$this->eol; // Add plain text message 1250 $out .= "--".$this->alternative_boundary.$this->eol; 1251 $out .= "Content-Type: multipart/related;".$this->eol." boundary=\"".$this->related_boundary."\"".$this->eol; 1252 $out .= $this->eol; 1253 $out .= "--".$this->related_boundary.$this->eol; 1254 } 1255 1256 if (!$this->atleastoneimage && $strContentAltText && !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) { // Add plain text message part before html part 1257 $out .= "Content-Type: multipart/alternative;".$this->eol." boundary=\"".$this->alternative_boundary."\"".$this->eol; 1258 $out .= $this->eol; 1259 $out .= "--".$this->alternative_boundary.$this->eol; 1260 $out .= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol; 1261 //$out.= "Content-Transfer-Encoding: 7bit".$this->eol; 1262 $out .= $this->eol.$strContentAltText.$this->eol; 1263 $out .= "--".$this->alternative_boundary.$this->eol; 1264 } 1265 1266 $out .= "Content-Type: text/html; charset=".$conf->file->character_set_client.$this->eol; 1267 //$out.= "Content-Transfer-Encoding: 7bit".$this->eol; // TODO Use base64 1268 $out .= $this->eol.$strContent.$this->eol; 1269 1270 if (!$this->atleastoneimage && $strContentAltText && !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) { // Add plain text message part after html part 1271 $out .= "--".$this->alternative_boundary."--".$this->eol; 1272 } 1273 } else { 1274 $out .= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol; 1275 //$out.= "Content-Transfer-Encoding: 7bit".$this->eol; 1276 $out .= $this->eol.$strContent.$this->eol; 1277 } 1278 1279 $out .= $this->eol; 1280 1281 // Encode images 1282 if ($this->atleastoneimage) { 1283 $out .= $this->write_images($this->images_encoded); 1284 // always end related and end alternative after inline images 1285 $out .= "--".$this->related_boundary."--".$this->eol; 1286 $out .= $this->eol."--".$this->alternative_boundary."--".$this->eol; 1287 $out .= $this->eol; 1288 } 1289 1290 return $out; 1291 } 1292 1293 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1294 /** 1295 * Attach file to email (mode = 'mail') 1296 * 1297 * @param array $filename_list Tableau 1298 * @param array $mimetype_list Tableau 1299 * @param array $mimefilename_list Tableau 1300 * @return string Chaine fichiers encodes 1301 */ 1302 public function write_files($filename_list, $mimetype_list, $mimefilename_list) 1303 { 1304 // phpcs:enable 1305 $out = ''; 1306 1307 $filename_list_size = count($filename_list); 1308 for ($i = 0; $i < $filename_list_size; $i++) { 1309 if ($filename_list[$i]) { 1310 dol_syslog("CMailFile::write_files: i=$i"); 1311 $encoded = $this->_encode_file($filename_list[$i]); 1312 if ($encoded >= 0) { 1313 if ($mimefilename_list[$i]) { 1314 $filename_list[$i] = $mimefilename_list[$i]; 1315 } 1316 if (!$mimetype_list[$i]) { 1317 $mimetype_list[$i] = "application/octet-stream"; 1318 } 1319 1320 $out .= "--".$this->mixed_boundary.$this->eol; 1321 $out .= "Content-Disposition: attachment; filename=\"".$filename_list[$i]."\"".$this->eol; 1322 $out .= "Content-Type: ".$mimetype_list[$i]."; name=\"".$filename_list[$i]."\"".$this->eol; 1323 $out .= "Content-Transfer-Encoding: base64".$this->eol; 1324 $out .= "Content-Description: ".$filename_list[$i].$this->eol; 1325 $out .= $this->eol; 1326 $out .= $encoded; 1327 $out .= $this->eol; 1328 //$out.= $this->eol; 1329 } else { 1330 return $encoded; 1331 } 1332 } 1333 } 1334 1335 return $out; 1336 } 1337 1338 1339 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1340 /** 1341 * Attach an image to email (mode = 'mail') 1342 * 1343 * @param array $images_list Array of array image 1344 * @return string Chaine images encodees 1345 */ 1346 public function write_images($images_list) 1347 { 1348 // phpcs:enable 1349 $out = ''; 1350 1351 if (is_array($images_list)) { 1352 foreach ($images_list as $img) { 1353 dol_syslog("CMailFile::write_images: ".$img["name"]); 1354 1355 $out .= "--".$this->related_boundary.$this->eol; // always related for an inline image 1356 $out .= "Content-Type: ".$img["content_type"]."; name=\"".$img["name"]."\"".$this->eol; 1357 $out .= "Content-Transfer-Encoding: base64".$this->eol; 1358 $out .= "Content-Disposition: inline; filename=\"".$img["name"]."\"".$this->eol; 1359 $out .= "Content-ID: <".$img["cid"].">".$this->eol; 1360 $out .= $this->eol; 1361 $out .= $img["image_encoded"]; 1362 $out .= $this->eol; 1363 } 1364 } 1365 1366 return $out; 1367 } 1368 1369 1370 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1371 /** 1372 * Try to create a socket connection 1373 * 1374 * @param string $host Add ssl:// for SSL/TLS. 1375 * @param int $port Example: 25, 465 1376 * @return int Socket id if ok, 0 if KO 1377 */ 1378 public function check_server_port($host, $port) 1379 { 1380 // phpcs:enable 1381 global $conf; 1382 1383 $_retVal = 0; 1384 $timeout = 5; // Timeout in seconds 1385 1386 if (function_exists('fsockopen')) { 1387 $keyforsmtpserver = 'MAIN_MAIL_SMTP_SERVER'; 1388 $keyforsmtpport = 'MAIN_MAIL_SMTP_PORT'; 1389 $keyforsmtpid = 'MAIN_MAIL_SMTPS_ID'; 1390 $keyforsmtppw = 'MAIN_MAIL_SMTPS_PW'; 1391 $keyfortls = 'MAIN_MAIL_EMAIL_TLS'; 1392 $keyforstarttls = 'MAIN_MAIL_EMAIL_STARTTLS'; 1393 if ($this->sendcontext == 'emailing' && !empty($conf->global->MAIN_MAIL_SENDMODE_EMAILING) && $conf->global->MAIN_MAIL_SENDMODE_EMAILING != 'default') { 1394 $keyforsmtpserver = 'MAIN_MAIL_SMTP_SERVER_EMAILING'; 1395 $keyforsmtpport = 'MAIN_MAIL_SMTP_PORT_EMAILING'; 1396 $keyforsmtpid = 'MAIN_MAIL_SMTPS_ID_EMAILING'; 1397 $keyforsmtppw = 'MAIN_MAIL_SMTPS_PW_EMAILING'; 1398 $keyfortls = 'MAIN_MAIL_EMAIL_TLS_EMAILING'; 1399 $keyforstarttls = 'MAIN_MAIL_EMAIL_STARTTLS_EMAILING'; 1400 } 1401 1402 // If we use SSL/TLS 1403 if (!empty($conf->global->$keyfortls) && function_exists('openssl_open')) { 1404 $host = 'ssl://'.$host; 1405 } 1406 // tls smtp start with no encryption 1407 //if (! empty($conf->global->MAIN_MAIL_EMAIL_STARTTLS) && function_exists('openssl_open')) $host='tls://'.$host; 1408 1409 dol_syslog("Try socket connection to host=".$host." port=".$port); 1410 //See if we can connect to the SMTP server 1411 if ($socket = @fsockopen( 1412 $host, // Host to test, IP or domain. Add ssl:// for SSL/TLS. 1413 $port, // which Port number to use 1414 $errno, // actual system level error 1415 $errstr, // and any text that goes with the error 1416 $timeout // timeout for reading/writing data over the socket 1417 )) { 1418 // Windows still does not have support for this timeout function 1419 if (function_exists('stream_set_timeout')) { 1420 stream_set_timeout($socket, $timeout, 0); 1421 } 1422 1423 dol_syslog("Now we wait for answer 220"); 1424 1425 // Check response from Server 1426 if ($_retVal = $this->server_parse($socket, "220")) { 1427 $_retVal = $socket; 1428 } 1429 } else { 1430 $this->error = utf8_check('Error '.$errno.' - '.$errstr) ? 'Error '.$errno.' - '.$errstr : utf8_encode('Error '.$errno.' - '.$errstr); 1431 } 1432 } 1433 return $_retVal; 1434 } 1435 1436 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1437 /** 1438 * This function has been modified as provided by SirSir to allow multiline responses when 1439 * using SMTP Extensions. 1440 * 1441 * @param resource $socket Socket 1442 * @param string $response Response string 1443 * @return boolean true if success 1444 */ 1445 public function server_parse($socket, $response) 1446 { 1447 // phpcs:enable 1448 $_retVal = true; // Indicates if Object was created or not 1449 $server_response = ''; 1450 1451 while (substr($server_response, 3, 1) != ' ') { 1452 if (!($server_response = fgets($socket, 256))) { 1453 $this->error = "Couldn't get mail server response codes"; 1454 return false; 1455 } 1456 } 1457 1458 if (!(substr($server_response, 0, 3) == $response)) { 1459 $this->error = "Ran into problems sending Mail.\r\nResponse: $server_response"; 1460 $_retVal = false; 1461 } 1462 1463 return $_retVal; 1464 } 1465 1466 /** 1467 * Seearch images into html message and init array this->images_encoded if found 1468 * 1469 * @param string $images_dir Location of physical images files 1470 * @return int >0 if OK, <0 if KO 1471 */ 1472 public function findHtmlImages($images_dir) 1473 { 1474 // Build the list of image extensions 1475 $extensions = array_keys($this->image_types); 1476 1477 $matches = array(); 1478 preg_match_all('/(?:"|\')([^"\']+\.('.implode('|', $extensions).'))(?:"|\')/Ui', $this->html, $matches); // If "xxx.ext" or 'xxx.ext' found 1479 1480 if (!empty($matches)) { 1481 $i = 0; 1482 foreach ($matches[1] as $full) { 1483 if (preg_match('/file=([A-Za-z0-9_\-\/]+[\.]?[A-Za-z0-9]+)?$/i', $full, $regs)) { // If xxx is 'file=aaa' 1484 $img = $regs[1]; 1485 1486 if (file_exists($images_dir.'/'.$img)) { 1487 // Image path in src 1488 $src = preg_quote($full, '/'); 1489 1490 // Image full path 1491 $this->html_images[$i]["fullpath"] = $images_dir.'/'.$img; 1492 1493 // Image name 1494 $this->html_images[$i]["name"] = $img; 1495 1496 // Content type 1497 if (preg_match('/^.+\.(\w{3,4})$/', $img, $reg)) { 1498 $ext = strtolower($reg[1]); 1499 $this->html_images[$i]["content_type"] = $this->image_types[$ext]; 1500 } 1501 1502 // cid 1503 $this->html_images[$i]["cid"] = dol_hash(uniqid(time()), 3); // Force md5 hash (does not contains special chars) 1504 $this->html = preg_replace("/src=\"$src\"|src='$src'/i", "src=\"cid:".$this->html_images[$i]["cid"]."\"", $this->html); 1505 } 1506 $i++; 1507 } 1508 } 1509 1510 if (!empty($this->html_images)) { 1511 $inline = array(); 1512 1513 $i = 0; 1514 1515 foreach ($this->html_images as $img) { 1516 $fullpath = $images_dir.'/'.$img["name"]; 1517 1518 // If duplicate images are embedded, they may show up as attachments, so remove them. 1519 if (!in_array($fullpath, $inline)) { 1520 // Read image file 1521 if ($image = file_get_contents($fullpath)) { 1522 // On garde que le nom de l'image 1523 preg_match('/([A-Za-z0-9_-]+[\.]?[A-Za-z0-9]+)?$/i', $img["name"], $regs); 1524 $imgName = $regs[1]; 1525 1526 $this->images_encoded[$i]['name'] = $imgName; 1527 $this->images_encoded[$i]['fullpath'] = $fullpath; 1528 $this->images_encoded[$i]['content_type'] = $img["content_type"]; 1529 $this->images_encoded[$i]['cid'] = $img["cid"]; 1530 // Encodage de l'image 1531 $this->images_encoded[$i]["image_encoded"] = chunk_split(base64_encode($image), 68, $this->eol); 1532 $inline[] = $fullpath; 1533 } 1534 } 1535 $i++; 1536 } 1537 } else { 1538 return -1; 1539 } 1540 1541 return 1; 1542 } else { 1543 return 0; 1544 } 1545 } 1546 1547 /** 1548 * Return a formatted address string for SMTP protocol 1549 * 1550 * @param string $address Example: 'John Doe <john@doe.com>, Alan Smith <alan@smith.com>' or 'john@doe.com, alan@smith.com' 1551 * @param int $format 0=auto, 1=emails with <>, 2=emails without <>, 3=auto + label between ", 4 label or email, 5 mailto link 1552 * @param int $encode 0=No encode name, 1=Encode name to RFC2822 1553 * @param int $maxnumberofemail 0=No limit. Otherwise, maximum number of emails returned ($address may contains several email separated with ','). Add '...' if there is more. 1554 * @return string If format 0: '<john@doe.com>' or 'John Doe <john@doe.com>' or '=?UTF-8?B?Sm9obiBEb2U=?= <john@doe.com>' 1555 * If format 1: '<john@doe.com>' 1556 * If format 2: 'john@doe.com' 1557 * If format 3: '<john@doe.com>' or '"John Doe" <john@doe.com>' or '"=?UTF-8?B?Sm9obiBEb2U=?=" <john@doe.com>' 1558 * If format 4: 'John Doe' or 'john@doe.com' if no label exists 1559 * If format 5: <a href="mailto:john@doe.com">John Doe</a> or <a href="mailto:john@doe.com">john@doe.com</a> if no label exists 1560 * @see getArrayAddress() 1561 */ 1562 public static function getValidAddress($address, $format, $encode = 0, $maxnumberofemail = 0) 1563 { 1564 global $conf; 1565 1566 $ret = ''; 1567 1568 $arrayaddress = explode(',', $address); 1569 1570 // Boucle sur chaque composant de l'adresse 1571 $i = 0; 1572 foreach ($arrayaddress as $val) { 1573 $regs = array(); 1574 if (preg_match('/^(.*)<(.*)>$/i', trim($val), $regs)) { 1575 $name = trim($regs[1]); 1576 $email = trim($regs[2]); 1577 } else { 1578 $name = ''; 1579 $email = trim($val); 1580 } 1581 1582 if ($email) { 1583 $i++; 1584 1585 $newemail = ''; 1586 if ($format == 5) { 1587 $newemail = $name ? $name : $email; 1588 $newemail = '<a href="mailto:'.$email.'">'.$newemail.'</a>'; 1589 } 1590 if ($format == 4) { 1591 $newemail = $name ? $name : $email; 1592 } 1593 if ($format == 2) { 1594 $newemail = $email; 1595 } 1596 if ($format == 1 || $format == 3) { 1597 $newemail = '<'.$email.'>'; 1598 } 1599 if ($format == 0 || $format == 3) { 1600 if (!empty($conf->global->MAIN_MAIL_NO_FULL_EMAIL)) { 1601 $newemail = '<'.$email.'>'; 1602 } elseif (!$name) { 1603 $newemail = '<'.$email.'>'; 1604 } else { 1605 $newemail = ($format == 3 ? '"' : '').($encode ?self::encodetorfc2822($name) : $name).($format == 3 ? '"' : '').' <'.$email.'>'; 1606 } 1607 } 1608 1609 $ret = ($ret ? $ret.',' : '').$newemail; 1610 1611 // Stop if we have too much records 1612 if ($maxnumberofemail && $i >= $maxnumberofemail) { 1613 if (count($arrayaddress) > $maxnumberofemail) { 1614 $ret .= '...'; 1615 } 1616 break; 1617 } 1618 } 1619 } 1620 1621 return $ret; 1622 } 1623 1624 /** 1625 * Return a formatted array of address string for SMTP protocol 1626 * 1627 * @param string $address Example: 'John Doe <john@doe.com>, Alan Smith <alan@smith.com>' or 'john@doe.com, alan@smith.com' 1628 * @return array array of email => name 1629 * @see getValidAddress() 1630 */ 1631 public static function getArrayAddress($address) 1632 { 1633 global $conf; 1634 1635 $ret = array(); 1636 1637 $arrayaddress = explode(',', $address); 1638 1639 // Boucle sur chaque composant de l'adresse 1640 foreach ($arrayaddress as $val) { 1641 if (preg_match('/^(.*)<(.*)>$/i', trim($val), $regs)) { 1642 $name = trim($regs[1]); 1643 $email = trim($regs[2]); 1644 } else { 1645 $name = null; 1646 $email = trim($val); 1647 } 1648 1649 $ret[$email] = empty($conf->global->MAIN_MAIL_NO_FULL_EMAIL) ? $name : null; 1650 } 1651 1652 return $ret; 1653 } 1654} 1655