dolibarr  x.y.z
CMailFile.class.php
Go to the documentation of this file.
1 <?php
33 use OAuth\Common\Storage\DoliStorage;
34 use OAuth\Common\Consumer\Credentials;
40 class CMailFile
41 {
42  public $sendcontext;
43  public $sendmode;
44  public $sendsetup;
45 
49  public $subject;
50  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).
51  // Sender: Who send the email ("Sender" has sent emails on behalf of "From").
52  // 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.
53  // Return-Path: Email where to send bounds.
54  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)
55  public $errors_to; // Errors-To: Email where to send errors.
56  public $addr_to;
57  public $addr_cc;
58  public $addr_bcc;
59  public $trackid;
60 
61  public $mixed_boundary;
62  public $related_boundary;
63  public $alternative_boundary;
64  public $deliveryreceipt;
65 
66  public $atleastonefile;
67 
68  public $eol;
69  public $eol2;
70 
74  public $error = '';
75 
79  public $errors = array();
80 
81  public $smtps; // Contains SMTPs object (if this method is used)
82  public $phpmailer; // Contains PHPMailer object (if this method is used)
83 
87  public $transport;
88 
92  public $mailer;
93 
97  public $logger;
98 
102  public $css;
104  public $styleCSS;
106  public $bodyCSS;
107 
108  public $msgid;
109  public $headers;
110  public $message;
114  public $filename_list = array();
118  public $mimetype_list = array();
122  public $mimefilename_list = array();
126  public $cid_list = array();
127 
128  // Image
129  public $html;
130  public $image_boundary;
131  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).
132  public $html_images = array();
133  public $images_encoded = array();
134  public $image_types = array(
135  'gif' => 'image/gif',
136  'jpg' => 'image/jpeg',
137  'jpeg' => 'image/jpeg',
138  'jpe' => 'image/jpeg',
139  'bmp' => 'image/bmp',
140  'png' => 'image/png',
141  'tif' => 'image/tiff',
142  'tiff' => 'image/tiff',
143  );
144 
145 
168  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 = '', $upload_dir_tmp = '')
169  {
170  global $conf, $dolibarr_main_data_root, $user;
171 
172  // Clean values of $mimefilename_list
173  if (is_array($mimefilename_list)) {
174  foreach ($mimefilename_list as $key => $val) {
175  $mimefilename_list[$key] = dol_string_unaccent($mimefilename_list[$key]);
176  }
177  }
178 
179  $cid_list = array();
180 
181  $this->sendcontext = $sendcontext;
182 
183  // Define this->sendmode ('mail', 'smtps', 'siwftmailer', ...) according to $sendcontext ('standard', 'emailing', 'ticket')
184  $this->sendmode = '';
185  if (!empty($this->sendcontext)) {
186  $smtpContextKey = strtoupper($this->sendcontext);
187  $smtpContextSendMode = getDolGlobalString('MAIN_MAIL_SENDMODE_'.$smtpContextKey);
188  if (!empty($smtpContextSendMode) && $smtpContextSendMode != 'default') {
189  $this->sendmode = $smtpContextSendMode;
190  }
191  }
192  if (empty($this->sendmode)) {
193  $this->sendmode = (!empty($conf->global->MAIN_MAIL_SENDMODE) ? $conf->global->MAIN_MAIL_SENDMODE : 'mail');
194  }
195 
196  // We define end of line (RFC 821).
197  $this->eol = "\r\n";
198  // We define end of line for header fields (RFC 822bis section 2.3 says header must contains \r\n).
199  $this->eol2 = "\r\n";
200  if (!empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA)) {
201  $this->eol = "\n";
202  $this->eol2 = "\n";
203  $moreinheader = str_replace("\r\n", "\n", $moreinheader);
204  }
205 
206  // On defini mixed_boundary
207  $this->mixed_boundary = "multipart_x.".time().".x_boundary";
208 
209  // On defini related_boundary
210  $this->related_boundary = 'mul_'.dol_hash(uniqid("dolibarr2"), 3); // Force md5 hash (does not contains special chars)
211 
212  // On defini alternative_boundary
213  $this->alternative_boundary = 'mul_'.dol_hash(uniqid("dolibarr3"), 3); // Force md5 hash (does not contains special chars)
214 
215  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 upload_dir_tmp=$upload_dir_tmp", LOG_DEBUG);
216  dol_syslog("CMailFile::CMailfile: subject=".$subject.", deliveryreceipt=".$deliveryreceipt.", msgishtml=".$msgishtml, LOG_DEBUG);
217 
218  if (empty($subject)) {
219  dol_syslog("CMailFile::CMailfile: Try to send an email with empty subject");
220  $this->error = 'ErrorSubjectIsRequired';
221  return;
222  }
223  if (empty($msg)) {
224  dol_syslog("CMailFile::CMailfile: Try to send an email with empty body");
225  $msg = '.'; // Avoid empty message (with empty message content, you will see a multipart structure)
226  }
227 
228  // Detect if message is HTML (use fast method)
229  if ($msgishtml == -1) {
230  $this->msgishtml = 0;
231  if (dol_textishtml($msg)) {
232  $this->msgishtml = 1;
233  }
234  } else {
235  $this->msgishtml = $msgishtml;
236  }
237 
238  global $dolibarr_main_url_root;
239 
240  // Define $urlwithroot
241  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
242  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
243  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
244 
245  // Replace relative /viewimage to absolute path
246  $msg = preg_replace('/src="'.preg_quote(DOL_URL_ROOT, '/').'\/viewimage\.php/ims', 'src="'.$urlwithroot.'/viewimage.php', $msg, -1);
247 
248  if (!empty($conf->global->MAIN_MAIL_FORCE_CONTENT_TYPE_TO_HTML)) {
249  $this->msgishtml = 1; // To force to send everything with content type html.
250  }
251 
252  // Detect images
253  if ($this->msgishtml) {
254  $this->html = $msg;
255 
256  $findimg = 0;
257  if (!empty($conf->global->MAIN_MAIL_ADD_INLINE_IMAGES_IF_IN_MEDIAS)) {
258  // Search into the body for <img tags of links in medias files to replace them with an embedded file
259  // Note because media links are public, this should be useless, except avoid blocking images with email browser.
260  // This convert an embedd file with src="/viewimage.php?modulepart... into a cid link
261  $findimg = $this->findHtmlImages($dolibarr_main_data_root.'/medias');
262  }
263  if (!empty($conf->global->MAIN_MAIL_ADD_INLINE_IMAGES_IF_DATA)) {
264  // Search into the body for <img src="data:image/ext;base64,..." to replace them with an embedded file
265  // This convert an embedded file with src="data:image... into a cid link + attached file
266  $findimg = $this->findHtmlImagesIsSrcData($upload_dir_tmp);
267  }
268 
269  // Set atleastoneimage if there is at least one embedded file (into ->html_images)
270  if ($findimg > 0) {
271  foreach ($this->html_images as $i => $val) {
272  if ($this->html_images[$i]) {
273  $this->atleastoneimage = 1;
274  if ($this->html_images[$i]['type'] == 'cidfromdata') {
275  $posindice = count($filename_list);
276  $filename_list[$posindice] = $this->html_images[$i]['fullpath'];
277  $mimetype_list[$posindice] = $this->html_images[$i]['content_type'];
278  $mimefilename_list[$posindice] = $this->html_images[$i]['name'];
279  $cid_list[$posindice] = $this->html_images[$i]['cid'];
280  }
281  dol_syslog("CMailFile::CMailfile: html_images[$i]['name']=".$this->html_images[$i]['name'], LOG_DEBUG);
282  }
283  }
284  }
285  }
286 
287  // Set atleastoneimage if there is at least one file (into $filename_list array)
288  if (is_array($filename_list)) {
289  foreach ($filename_list as $i => $val) {
290  if ($filename_list[$i]) {
291  $this->atleastonefile = 1;
292  dol_syslog("CMailFile::CMailfile: filename_list[$i]=".$filename_list[$i].", mimetype_list[$i]=".$mimetype_list[$i]." mimefilename_list[$i]=".$mimefilename_list[$i]." cid_list[$i]=".$cid_list[$i], LOG_DEBUG);
293  }
294  }
295  }
296 
297  // Add auto copy to if not already in $to (Note: Adding bcc for specific modules are also done from pages)
298  // For example MAIN_MAIL_AUTOCOPY_TO can be 'email@example.com, __USER_EMAIL__, ...'
299  if (!empty($conf->global->MAIN_MAIL_AUTOCOPY_TO)) {
300  $listofemailstoadd = explode(',', $conf->global->MAIN_MAIL_AUTOCOPY_TO);
301  foreach ($listofemailstoadd as $key => $val) {
302  $emailtoadd = $listofemailstoadd[$key];
303  if (trim($emailtoadd) == '__USER_EMAIL__') {
304  if (!empty($user) && !empty($user->email)) {
305  $emailtoadd = $user->email;
306  } else {
307  $emailtoadd = '';
308  }
309  }
310  if ($emailtoadd && preg_match('/'.preg_quote($emailtoadd, '/').'/i', $to)) {
311  $emailtoadd = ''; // Email already in the "To"
312  }
313  if ($emailtoadd) {
314  $listofemailstoadd[$key] = $emailtoadd;
315  } else {
316  unset($listofemailstoadd[$key]);
317  }
318  }
319  if (!empty($listofemailstoadd)) {
320  $addr_bcc .= ($addr_bcc ? ', ' : '').join(', ', $listofemailstoadd);
321  }
322  }
323 
324  $this->subject = $subject;
325  $this->addr_to = dol_sanitizeEmail($to);
326  $this->addr_from = dol_sanitizeEmail($from);
327  $this->msg = $msg;
328  $this->addr_cc = dol_sanitizeEmail($addr_cc);
329  $this->addr_bcc = dol_sanitizeEmail($addr_bcc);
330  $this->deliveryreceipt = $deliveryreceipt;
331  if (empty($replyto)) {
332  $replyto = dol_sanitizeEmail($from);
333  }
334  $this->reply_to = dol_sanitizeEmail($replyto);
335  $this->errors_to = dol_sanitizeEmail($errors_to);
336  $this->trackid = $trackid;
337  // Set arrays with attached files info
338  $this->filename_list = $filename_list;
339  $this->mimetype_list = $mimetype_list;
340  $this->mimefilename_list = $mimefilename_list;
341  $this->cid_list = $cid_list;
342 
343  if (!empty($conf->global->MAIN_MAIL_FORCE_SENDTO)) {
344  $this->addr_to = dol_sanitizeEmail($conf->global->MAIN_MAIL_FORCE_SENDTO);
345  $this->addr_cc = '';
346  $this->addr_bcc = '';
347  }
348 
349  $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED';
350  if (!empty($this->sendcontext)) {
351  $smtpContextKey = strtoupper($this->sendcontext);
352  $smtpContextSendMode = getDolGlobalString('MAIN_MAIL_SENDMODE_'.$smtpContextKey);
353  if (!empty($smtpContextSendMode) && $smtpContextSendMode != 'default') {
354  $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED_'.$smtpContextKey;
355  }
356  }
357 
358  // We set all data according to choosed sending method.
359  // We also set a value for ->msgid
360  if ($this->sendmode == 'mail') {
361  // Use mail php function (default PHP method)
362  // ------------------------------------------
363 
364  $smtp_headers = "";
365  $mime_headers = "";
366  $text_body = "";
367  $files_encoded = "";
368 
369  // Define smtp_headers (this also set ->msgid)
370  $smtp_headers = $this->write_smtpheaders();
371  if (!empty($moreinheader)) {
372  $smtp_headers .= $moreinheader; // $moreinheader contains the \r\n
373  }
374 
375  // Define mime_headers
376  $mime_headers = $this->write_mimeheaders($filename_list, $mimefilename_list);
377 
378  if (!empty($this->html)) {
379  if (!empty($css)) {
380  $this->css = $css;
381  $this->buildCSS(); // Build a css style (mode = all) into this->styleCSS and this->bodyCSS
382  }
383 
384  $msg = $this->html;
385  }
386 
387  // Define body in text_body
388  $text_body = $this->write_body($msg);
389 
390  // Add attachments to text_encoded
391  if (!empty($this->atleastonefile)) {
392  $files_encoded = $this->write_files($filename_list, $mimetype_list, $mimefilename_list, $cid_list);
393  }
394 
395  // We now define $this->headers and $this->message
396  $this->headers = $smtp_headers.$mime_headers;
397  // On nettoie le header pour qu'il ne se termine pas par un retour chariot.
398  // This avoid also empty lines at end that can be interpreted as mail injection by email servers.
399  $this->headers = preg_replace("/([\r\n]+)$/i", "", $this->headers);
400 
401  //$this->message = $this->eol.'This is a message with multiple parts in MIME format.'.$this->eol;
402  $this->message = 'This is a message with multiple parts in MIME format.'.$this->eol;
403  $this->message .= $text_body.$files_encoded;
404  $this->message .= "--".$this->mixed_boundary."--".$this->eol;
405  } elseif ($this->sendmode == 'smtps') {
406  // Use SMTPS library
407  // ------------------------------------------
408 
409  require_once DOL_DOCUMENT_ROOT.'/core/class/smtps.class.php';
410  $smtps = new SMTPs();
411  $smtps->setCharSet($conf->file->character_set_client);
412 
413  // Encode subject if required.
414  $subjecttouse = $this->subject;
415  if (!ascii_check($subjecttouse)) {
416  $subjecttouse = $this->encodetorfc2822($subjecttouse);
417  }
418 
419  $smtps->setSubject($subjecttouse);
420  $smtps->setTO($this->getValidAddress($this->addr_to, 0, 1));
421  $smtps->setFrom($this->getValidAddress($this->addr_from, 0, 1));
422  $smtps->setTrackId($this->trackid);
423  $smtps->setReplyTo($this->getValidAddress($this->reply_to, 0, 1));
424 
425  if (!empty($moreinheader)) {
426  $smtps->setMoreInHeader($moreinheader);
427  }
428 
429  if (!empty($this->html)) {
430  if (!empty($css)) {
431  $this->css = $css;
432  $this->buildCSS();
433  }
434  $msg = $this->html;
435  $msg = $this->checkIfHTML($msg);
436  }
437 
438  // Replace . alone on a new line with .. to avoid to have SMTP interpret this as end of message
439  $msg = preg_replace('/(\r|\n)\.(\r|\n)/ims', '\1..\2', $msg);
440 
441  if ($this->msgishtml) {
442  $smtps->setBodyContent($msg, 'html');
443  } else {
444  $smtps->setBodyContent($msg, 'plain');
445  }
446 
447  if ($this->atleastoneimage) {
448  foreach ($this->images_encoded as $img) {
449  $smtps->setImageInline($img['image_encoded'], $img['name'], $img['content_type'], $img['cid']);
450  }
451  }
452 
453  if (!empty($this->atleastonefile)) {
454  foreach ($filename_list as $i => $val) {
455  $content = file_get_contents($filename_list[$i]);
456  $smtps->setAttachment($content, $mimefilename_list[$i], $mimetype_list[$i], $cid_list[$i]);
457  }
458  }
459 
460  $smtps->setCC($this->addr_cc);
461  $smtps->setBCC($this->addr_bcc);
462  $smtps->setErrorsTo($this->errors_to);
463  $smtps->setDeliveryReceipt($this->deliveryreceipt);
464  if (!empty($conf->global->$keyforsslseflsigned)) {
465  $smtps->setOptions(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true)));
466  }
467 
468  $host = dol_getprefix('email');
469  $this->msgid = time().'.SMTPs-dolibarr-'.$this->trackid.'@'.$host;
470 
471  $this->smtps = $smtps;
472  } elseif ($this->sendmode == 'swiftmailer') {
473  // Use Swift Mailer library
474  $host = dol_getprefix('email');
475 
476  require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php';
477 
478  // egulias autoloader lib
479  require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/autoload.php';
480 
481  require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/swift_required.php';
482 
483  // Create the message
484  //$this->message = Swift_Message::newInstance();
485  $this->message = new Swift_Message();
486  //$this->message = new Swift_SignedMessage();
487  // Adding a trackid header to a message
488  $headers = $this->message->getHeaders();
489  $headers->addTextHeader('X-Dolibarr-TRACKID', $this->trackid.'@'.$host);
490  $this->msgid = time().'.swiftmailer-dolibarr-'.$this->trackid.'@'.$host;
491  $headerID = $this->msgid;
492  $msgid = $headers->get('Message-ID');
493  $msgid->setId($headerID);
494  $headers->addIdHeader('References', $headerID);
495  // TODO if (!empty($moreinheader)) ...
496 
497  // Give the message a subject
498  try {
499  $result = $this->message->setSubject($this->subject);
500  } catch (Exception $e) {
501  $this->errors[] = $e->getMessage();
502  }
503 
504  // Set the From address with an associative array
505  //$this->message->setFrom(array('john@doe.com' => 'John Doe'));
506  if (!empty($this->addr_from)) {
507  try {
508  if (!empty($conf->global->MAIN_FORCE_DISABLE_MAIL_SPOOFING)) {
509  // Prevent email spoofing for smtp server with a strict configuration
510  $regexp = '/([a-z0-9_\.\-\+])+\@(([a-z0-9\-])+\.)+([a-z0-9]{2,4})+/i'; // This regular expression extracts all emails from a string
511  $adressEmailFrom = array();
512  $emailMatchs = preg_match_all($regexp, $from, $adressEmailFrom);
513  $adressEmailFrom = reset($adressEmailFrom);
514  if ($emailMatchs !== false && filter_var($conf->global->MAIN_MAIL_SMTPS_ID, FILTER_VALIDATE_EMAIL) && $conf->global->MAIN_MAIL_SMTPS_ID !== $adressEmailFrom) {
515  $this->message->setFrom($conf->global->MAIN_MAIL_SMTPS_ID);
516  } else {
517  $this->message->setFrom($this->getArrayAddress($this->addr_from));
518  }
519  } else {
520  $this->message->setFrom($this->getArrayAddress($this->addr_from));
521  }
522  } catch (Exception $e) {
523  $this->errors[] = $e->getMessage();
524  }
525  }
526 
527  // Set the To addresses with an associative array
528  if (!empty($this->addr_to)) {
529  try {
530  $this->message->setTo($this->getArrayAddress($this->addr_to));
531  } catch (Exception $e) {
532  $this->errors[] = $e->getMessage();
533  }
534  }
535 
536  if (!empty($this->reply_to)) {
537  try {
538  $this->message->SetReplyTo($this->getArrayAddress($this->reply_to));
539  } catch (Exception $e) {
540  $this->errors[] = $e->getMessage();
541  }
542  }
543 
544  try {
545  $this->message->setCharSet($conf->file->character_set_client);
546  } catch (Exception $e) {
547  $this->errors[] = $e->getMessage();
548  }
549 
550  if (!empty($this->html)) {
551  if (!empty($css)) {
552  $this->css = $css;
553  $this->buildCSS();
554  }
555  $msg = $this->html;
556  $msg = $this->checkIfHTML($msg);
557  }
558 
559  if ($this->atleastoneimage) {
560  foreach ($this->images_encoded as $img) {
561  //$img['fullpath'],$img['image_encoded'],$img['name'],$img['content_type'],$img['cid']
562  $attachment = Swift_Image::fromPath($img['fullpath']);
563  // embed image
564  $imgcid = $this->message->embed($attachment);
565  // replace cid by the one created by swiftmail in html message
566  $msg = str_replace("cid:".$img['cid'], $imgcid, $msg);
567  }
568  }
569 
570  if ($this->msgishtml) {
571  $this->message->setBody($msg, 'text/html');
572  // And optionally an alternative body
573  $this->message->addPart(html_entity_decode(strip_tags($msg)), 'text/plain');
574  } else {
575  $this->message->setBody($msg, 'text/plain');
576  // And optionally an alternative body
577  $this->message->addPart(dol_nl2br($msg), 'text/html');
578  }
579 
580  if (!empty($this->atleastonefile)) {
581  foreach ($filename_list as $i => $val) {
582  //$this->message->attach(Swift_Attachment::fromPath($filename_list[$i],$mimetype_list[$i]));
583  $attachment = Swift_Attachment::fromPath($filename_list[$i], $mimetype_list[$i]);
584  if (!empty($mimefilename_list[$i])) {
585  $attachment->setFilename($mimefilename_list[$i]);
586  }
587  $this->message->attach($attachment);
588  }
589  }
590 
591  if (!empty($this->addr_cc)) {
592  try {
593  $this->message->setCc($this->getArrayAddress($this->addr_cc));
594  } catch (Exception $e) {
595  $this->errors[] = $e->getMessage();
596  }
597  }
598  if (!empty($this->addr_bcc)) {
599  try {
600  $this->message->setBcc($this->getArrayAddress($this->addr_bcc));
601  } catch (Exception $e) {
602  $this->errors[] = $e->getMessage();
603  }
604  }
605  //if (!empty($this->errors_to)) $this->message->setErrorsTo($this->getArrayAddress($this->errors_to));
606  if (isset($this->deliveryreceipt) && $this->deliveryreceipt == 1) {
607  try {
608  $this->message->setReadReceiptTo($this->getArrayAddress($this->addr_from));
609  } catch (Exception $e) {
610  $this->errors[] = $e->getMessage();
611  }
612  }
613  } else {
614  // Send mail method not correctly defined
615  // --------------------------------------
616  $this->error = 'Bad value for sendmode';
617  }
618  }
619 
620 
626  public function sendfile()
627  {
628  global $conf, $db, $langs;
629 
630  $errorlevel = error_reporting();
631  //error_reporting($errorlevel ^ E_WARNING); // Desactive warnings
632 
633  $res = false;
634 
635  if (empty($conf->global->MAIN_DISABLE_ALL_MAILS)) {
636  require_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
637  $hookmanager = new HookManager($db);
638  $hookmanager->initHooks(array('mail'));
639 
640  $parameters = array();
641  $action = '';
642  $reshook = $hookmanager->executeHooks('sendMail', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
643  if ($reshook < 0) {
644  $this->error = "Error in hook maildao sendMail ".$reshook;
645  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
646 
647  return $reshook;
648  }
649  if ($reshook == 1) { // Hook replace standard code
650  return true;
651  }
652 
653  $sendingmode = $this->sendmode;
654  if ($this->sendcontext == 'emailing' && !empty($conf->global->MAILING_NO_USING_PHPMAIL) && $sendingmode == 'mail') {
655  // List of sending methods
656  $listofmethods = array();
657  $listofmethods['mail'] = 'PHP mail function';
658  //$listofmethods['simplemail']='Simplemail class';
659  $listofmethods['smtps'] = 'SMTP/SMTPS socket library';
660 
661  // 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.
662  // You ensure that every user is using its own SMTP server when using the mass emailing module.
663  $linktoadminemailbefore = '<a href="'.DOL_URL_ROOT.'/admin/mails.php">';
664  $linktoadminemailend = '</a>';
665  $this->error = $langs->trans("MailSendSetupIs", $listofmethods[$sendingmode]);
666  $this->errors[] = $langs->trans("MailSendSetupIs", $listofmethods[$sendingmode]);
667  $this->error .= '<br>'.$langs->trans("MailSendSetupIs2", $linktoadminemailbefore, $linktoadminemailend, $langs->transnoentitiesnoconv("MAIN_MAIL_SENDMODE"), $listofmethods['smtps']);
668  $this->errors[] = $langs->trans("MailSendSetupIs2", $linktoadminemailbefore, $linktoadminemailend, $langs->transnoentitiesnoconv("MAIN_MAIL_SENDMODE"), $listofmethods['smtps']);
669  if (!empty($conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS)) {
670  $this->error .= '<br>'.$langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS);
671  $this->errors[] = $langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS);
672  }
673  return false;
674  }
675 
676  // Check number of recipient is lower or equal than MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL
677  if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL)) {
678  $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL = 10;
679  }
680  $tmparray1 = explode(',', $this->addr_to);
681  if (count($tmparray1) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_TO_IN_SAME_EMAIL) {
682  $this->error = 'Too much recipients in to:';
683  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING);
684  return false;
685  }
686  if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL)) {
687  $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL = 10;
688  }
689  $tmparray2 = explode(',', $this->addr_cc);
690  if (count($tmparray2) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_CC_IN_SAME_EMAIL) {
691  $this->error = 'Too much recipients in cc:';
692  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING);
693  return false;
694  }
695  if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL)) {
696  $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL = 10;
697  }
698  $tmparray3 = explode(',', $this->addr_bcc);
699  if (count($tmparray3) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_BCC_IN_SAME_EMAIL) {
700  $this->error = 'Too much recipients in bcc:';
701  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING);
702  return false;
703  }
704  if (empty($conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL)) {
705  $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL = 10;
706  }
707  if ((count($tmparray1) + count($tmparray2) + count($tmparray3)) > $conf->global->MAIL_MAX_NB_OF_RECIPIENTS_IN_SAME_EMAIL) {
708  $this->error = 'Too much recipients in to:, cc:, bcc:';
709  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_WARNING);
710  return false;
711  }
712 
713  $keyforsmtpserver = 'MAIN_MAIL_SMTP_SERVER';
714  $keyforsmtpport = 'MAIN_MAIL_SMTP_PORT';
715  $keyforsmtpid = 'MAIN_MAIL_SMTPS_ID';
716  $keyforsmtppw = 'MAIN_MAIL_SMTPS_PW';
717  $keyforsmtpauthtype = 'MAIN_MAIL_SMTPS_AUTH_TYPE';
718  $keyforsmtpoauthservice = 'MAIN_MAIL_SMTPS_OAUTH_SERVICE';
719  $keyfortls = 'MAIN_MAIL_EMAIL_TLS';
720  $keyforstarttls = 'MAIN_MAIL_EMAIL_STARTTLS';
721  $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED';
722  if (!empty($this->sendcontext)) {
723  $smtpContextKey = strtoupper($this->sendcontext);
724  $smtpContextSendMode = getDolGlobalString('MAIN_MAIL_SENDMODE_'.$smtpContextKey);
725  if (!empty($smtpContextSendMode) && $smtpContextSendMode != 'default') {
726  $keyforsmtpserver = 'MAIN_MAIL_SMTP_SERVER_'.$smtpContextKey;
727  $keyforsmtpport = 'MAIN_MAIL_SMTP_PORT_'.$smtpContextKey;
728  $keyforsmtpid = 'MAIN_MAIL_SMTPS_ID_'.$smtpContextKey;
729  $keyforsmtppw = 'MAIN_MAIL_SMTPS_PW_'.$smtpContextKey;
730  $keyforsmtpauthtype = 'MAIN_MAIL_SMTPS_AUTH_TYPE_'.$smtpContextKey;
731  $keyforsmtpoauthservice = 'MAIN_MAIL_SMTPS_OAUTH_SERVICE_'.$smtpContextKey;
732  $keyfortls = 'MAIN_MAIL_EMAIL_TLS_'.$smtpContextKey;
733  $keyforstarttls = 'MAIN_MAIL_EMAIL_STARTTLS_'.$smtpContextKey;
734  $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED_'.$smtpContextKey;
735  }
736  }
737 
738  // Action according to choosed sending method
739  if ($this->sendmode == 'mail') {
740  // Use mail php function (default PHP method)
741  // ------------------------------------------
742  dol_syslog("CMailFile::sendfile addr_to=".$this->addr_to.", subject=".$this->subject, LOG_DEBUG);
743  dol_syslog("CMailFile::sendfile header=\n".$this->headers, LOG_DEBUG);
744  //dol_syslog("CMailFile::sendfile message=\n".$message);
745 
746  // If Windows, sendmail_from must be defined
747  if (isset($_SERVER["WINDIR"])) {
748  if (empty($this->addr_from)) {
749  $this->addr_from = 'robot@example.com';
750  }
751  @ini_set('sendmail_from', $this->getValidAddress($this->addr_from, 2));
752  }
753 
754  // Force parameters
755  //dol_syslog("CMailFile::sendfile conf->global->".$keyforsmtpserver."=".$conf->global->$keyforsmtpserver." cpnf->global->".$keyforsmtpport."=".$conf->global->$keyforsmtpport, LOG_DEBUG);
756  if (!empty($conf->global->$keyforsmtpserver)) {
757  ini_set('SMTP', $conf->global->$keyforsmtpserver);
758  }
759  if (!empty($conf->global->$keyforsmtpport)) {
760  ini_set('smtp_port', $conf->global->$keyforsmtpport);
761  }
762 
763  $res = true;
764  if ($res && !$this->subject) {
765  $this->error = "Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."<br>Subject is empty";
766  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
767  $res = false;
768  }
769  $dest = $this->getValidAddress($this->addr_to, 2);
770  if ($res && !$dest) {
771  $this->error = "Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."<br>Recipient address '$dest' invalid";
772  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
773  $res = false;
774  }
775 
776  if ($res) {
777  $additionnalparam = ''; // By default
778  if (!empty($conf->global->MAIN_MAIL_ALLOW_SENDMAIL_F)) {
779  // le "Return-Path" (retour des messages bounced) dans les header ne fonctionne pas avec tous les MTA
780  // Le forcage de la valeur grace à l'option -f de sendmail est donc possible si la constante MAIN_MAIL_ALLOW_SENDMAIL_F est definie.
781  // Having this variable defined may create problems with some sendmail (option -f refused)
782  // Having this variable not defined may create problems with some other sendmail (option -f required)
783  $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) : ''));
784  }
785  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
786  $additionnalparam .= ($additionnalparam ? ' ' : '').'-ba';
787  }
788 
789  if (!empty($conf->global->MAIN_MAIL_SENDMAIL_FORCE_ADDPARAM)) {
790  $additionnalparam .= ($additionnalparam ? ' ' : '').'-U '.$additionnalparam; // Use -U to add additionnal params
791  }
792 
793  $linuxlike = 1;
794  if (preg_match('/^win/i', PHP_OS)) {
795  $linuxlike = 0;
796  }
797  if (preg_match('/^mac/i', PHP_OS)) {
798  $linuxlike = 0;
799  }
800 
801  dol_syslog("CMailFile::sendfile: mail start".($linuxlike ? '' : " HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')).", additionnal_parameters=".$additionnalparam, LOG_DEBUG);
802 
803  $this->message = stripslashes($this->message);
804 
805  if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
806  $this->dump_mail();
807  }
808 
809  // Encode subject if required.
810  $subjecttouse = $this->subject;
811  if (!ascii_check($subjecttouse)) {
812  $subjecttouse = $this->encodetorfc2822($subjecttouse);
813  }
814 
815  if (!empty($additionnalparam)) {
816  $res = mail($dest, $subjecttouse, $this->message, $this->headers, $additionnalparam);
817  } else {
818  $res = mail($dest, $subjecttouse, $this->message, $this->headers);
819  }
820 
821  if (!$res) {
822  $langs->load("errors");
823  $this->error = "Failed to send mail with php mail";
824  if (!$linuxlike) {
825  $this->error .= " to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port'); // This values are value used only for non linuxlike systems
826  }
827  $this->error .= ".<br>";
828  $this->error .= $langs->trans("ErrorPhpMailDelivery");
829  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
830  } else {
831  dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
832  }
833  }
834 
835  if (isset($_SERVER["WINDIR"])) {
836  @ini_restore('sendmail_from');
837  }
838 
839  // Restore parameters
840  if (!empty($conf->global->$keyforsmtpserver)) {
841  ini_restore('SMTP');
842  }
843  if (!empty($conf->global->$keyforsmtpport)) {
844  ini_restore('smtp_port');
845  }
846  } elseif ($this->sendmode == 'smtps') {
847  if (!is_object($this->smtps)) {
848  $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.";
849  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
850  return false;
851  }
852 
853  // Use SMTPS library
854  // ------------------------------------------
855  $this->smtps->setTransportType(0); // Only this method is coded in SMTPs library
856 
857  // Clean parameters
858  if (empty($conf->global->$keyforsmtpserver)) {
859  $conf->global->$keyforsmtpserver = ini_get('SMTP');
860  }
861  if (empty($conf->global->$keyforsmtpport)) {
862  $conf->global->$keyforsmtpport = ini_get('smtp_port');
863  }
864 
865  // If we use SSL/TLS
866  $server = $conf->global->$keyforsmtpserver;
867  $secure = '';
868  if (!empty($conf->global->$keyfortls) && function_exists('openssl_open')) {
869  $secure = 'ssl';
870  }
871  if (!empty($conf->global->$keyforstarttls) && function_exists('openssl_open')) {
872  $secure = 'tls';
873  }
874  $server = ($secure ? $secure.'://' : '').$server;
875 
876  $port = $conf->global->$keyforsmtpport;
877 
878  $this->smtps->setHost($server);
879  $this->smtps->setPort($port); // 25, 465...;
880 
881  $loginid = '';
882  $loginpass = '';
883  if (!empty($conf->global->$keyforsmtpid)) {
884  $loginid = $conf->global->$keyforsmtpid;
885  $this->smtps->setID($loginid);
886  }
887  if (!empty($conf->global->$keyforsmtppw)) {
888  $loginpass = $conf->global->$keyforsmtppw;
889  $this->smtps->setPW($loginpass);
890  }
891 
892  if (getDolGlobalString($keyforsmtpauthtype) === "XOAUTH2") {
893  require_once DOL_DOCUMENT_ROOT.'/core/lib/oauth.lib.php'; // define $supportedoauth2array
894  $keyforsupportedoauth2array = $conf->global->$keyforsmtpoauthservice;
895  if (preg_match('/^.*-/', $keyforsupportedoauth2array)) {
896  $keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array);
897  } else {
898  $keyforprovider = '';
899  }
900  $keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
901  $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME';
902 
903  if (isset($supportedoauth2array)) {
904  $OAUTH_SERVICENAME = (empty($supportedoauth2array[$keyforsupportedoauth2array]['name']) ? 'Unknown' : $supportedoauth2array[$keyforsupportedoauth2array]['name'].($keyforprovider ? '-'.$keyforprovider : ''));
905  } else {
906  $OAUTH_SERVICENAME = 'Unknown';
907  }
908 
909  require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
910 
911  $storage = new DoliStorage($db, $conf);
912  try {
913  $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
914  $expire = false;
915  // Is token expired or will token expire in the next 30 seconds
916  if (is_object($tokenobj)) {
917  $expire = ($tokenobj->getEndOfLife() !== -9002 && $tokenobj->getEndOfLife() !== -9001 && time() > ($tokenobj->getEndOfLife() - 30));
918  }
919  // Token expired so we refresh it
920  if (is_object($tokenobj) && $expire) {
921  $credentials = new Credentials(
922  getDolGlobalString('OAUTH_'.getDolGlobalString('MAIN_MAIL_SMTPS_OAUTH_SERVICE').'_ID'),
923  getDolGlobalString('OAUTH_'.getDolGlobalString('MAIN_MAIL_SMTPS_OAUTH_SERVICE').'_SECRET'),
924  getDolGlobalString('OAUTH_'.getDolGlobalString('MAIN_MAIL_SMTPS_OAUTH_SERVICE').'_URLAUTHORIZE')
925  );
926  $serviceFactory = new \OAuth\ServiceFactory();
927  $oauthname = explode('-', $OAUTH_SERVICENAME);
928  // ex service is Google-Emails we need only the first part Google
929  $apiService = $serviceFactory->createService($oauthname[0], $credentials, $storage, array());
930  // We have to save the token because Google give it only once
931  $refreshtoken = $tokenobj->getRefreshToken();
932  $tokenobj = $apiService->refreshAccessToken($tokenobj);
933  $tokenobj->setRefreshToken($refreshtoken);
934  $storage->storeAccessToken($OAUTH_SERVICENAME, $tokenobj);
935  }
936 
937  $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
938  if (is_object($tokenobj)) {
939  $this->smtps->setToken($tokenobj->getAccessToken());
940  } else {
941  $this->error = "Token not found";
942  }
943  } catch (Exception $e) {
944  // Return an error if token not found
945  $this->error = $e->getMessage();
946  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
947  }
948  }
949 
950  $res = true;
951  $from = $this->smtps->getFrom('org');
952  if ($res && !$from) {
953  $this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - Sender address '$from' invalid";
954  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
955  $res = false;
956  }
957  $dest = $this->smtps->getTo();
958  if ($res && !$dest) {
959  $this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - Recipient address '$dest' invalid";
960  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
961  $res = false;
962  }
963 
964  if ($res) {
965  if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
966  $this->smtps->setDebug(true);
967  }
968 
969  $result = $this->smtps->sendMsg();
970 
971  if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
972  $this->dump_mail();
973  }
974 
975  $result = $this->smtps->getErrors();
976  if (empty($this->error) && empty($result)) {
977  dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
978  $res = true;
979  } else {
980  if (empty($this->error)) {
981  $this->error = $result;
982  }
983  dol_syslog("CMailFile::sendfile: mail end error with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - ".$this->error, LOG_ERR);
984  $res = false;
985  }
986  }
987  } elseif ($this->sendmode == 'swiftmailer') {
988  // Use Swift Mailer library
989  // ------------------------------------------
990  require_once DOL_DOCUMENT_ROOT.'/includes/swiftmailer/lib/swift_required.php';
991 
992  // Clean parameters
993  if (empty($conf->global->$keyforsmtpserver)) {
994  $conf->global->$keyforsmtpserver = ini_get('SMTP');
995  }
996  if (empty($conf->global->$keyforsmtpport)) {
997  $conf->global->$keyforsmtpport = ini_get('smtp_port');
998  }
999 
1000  // If we use SSL/TLS
1001  $server = $conf->global->$keyforsmtpserver;
1002  $secure = '';
1003  if (!empty($conf->global->$keyfortls) && function_exists('openssl_open')) {
1004  $secure = 'ssl';
1005  }
1006  if (!empty($conf->global->$keyforstarttls) && function_exists('openssl_open')) {
1007  $secure = 'tls';
1008  }
1009 
1010  $this->transport = new Swift_SmtpTransport($server, $conf->global->$keyforsmtpport, $secure);
1011 
1012  if (!empty($conf->global->$keyforsmtpid)) {
1013  $this->transport->setUsername($conf->global->$keyforsmtpid);
1014  }
1015  if (!empty($conf->global->$keyforsmtppw) && getDolGlobalString($keyforsmtpauthtype) != "XOAUTH2") {
1016  $this->transport->setPassword($conf->global->$keyforsmtppw);
1017  }
1018  if (getDolGlobalString($keyforsmtpauthtype) === "XOAUTH2") {
1019  require_once DOL_DOCUMENT_ROOT.'/core/lib/oauth.lib.php'; // define $supportedoauth2array
1020  $keyforsupportedoauth2array = getDolGlobalString($keyforsmtpoauthservice);
1021  if (preg_match('/^.*-/', $keyforsupportedoauth2array)) {
1022  $keyforprovider = preg_replace('/^.*-/', '', $keyforsupportedoauth2array);
1023  } else {
1024  $keyforprovider = '';
1025  }
1026  $keyforsupportedoauth2array = preg_replace('/-.*$/', '', $keyforsupportedoauth2array);
1027  $keyforsupportedoauth2array = 'OAUTH_'.$keyforsupportedoauth2array.'_NAME';
1028 
1029  $OAUTH_SERVICENAME = (empty($supportedoauth2array[$keyforsupportedoauth2array]['name']) ? 'Unknown' : $supportedoauth2array[$keyforsupportedoauth2array]['name'].($keyforprovider ? '-'.$keyforprovider : ''));
1030 
1031  require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
1032 
1033  $storage = new DoliStorage($db, $conf);
1034 
1035  try {
1036  $tokenobj = $storage->retrieveAccessToken($OAUTH_SERVICENAME);
1037  $expire = false;
1038  // Is token expired or will token expire in the next 30 seconds
1039  if (is_object($tokenobj)) {
1040  $expire = ($tokenobj->getEndOfLife() !== -9002 && $tokenobj->getEndOfLife() !== -9001 && time() > ($tokenobj->getEndOfLife() - 30));
1041  }
1042  // Token expired so we refresh it
1043  if (is_object($tokenobj) && $expire) {
1044  $credentials = new Credentials(
1045  getDolGlobalString('OAUTH_'.getDolGlobalString('MAIN_MAIL_SMTPS_OAUTH_SERVICE').'_ID'),
1046  getDolGlobalString('OAUTH_'.getDolGlobalString('MAIN_MAIL_SMTPS_OAUTH_SERVICE').'_SECRET'),
1047  getDolGlobalString('OAUTH_'.getDolGlobalString('MAIN_MAIL_SMTPS_OAUTH_SERVICE').'_URLAUTHORIZE')
1048  );
1049  $serviceFactory = new \OAuth\ServiceFactory();
1050  $oauthname = explode('-', $OAUTH_SERVICENAME);
1051  // ex service is Google-Emails we need only the first part Google
1052  $apiService = $serviceFactory->createService($oauthname[0], $credentials, $storage, array());
1053  // We have to save the token because Google give it only once
1054  $refreshtoken = $tokenobj->getRefreshToken();
1055  $tokenobj = $apiService->refreshAccessToken($tokenobj);
1056  $tokenobj->setRefreshToken($refreshtoken);
1057  $storage->storeAccessToken($OAUTH_SERVICENAME, $tokenobj);
1058  }
1059  if (is_object($tokenobj)) {
1060  $this->transport->setAuthMode('XOAUTH2');
1061  $this->transport->setPassword($tokenobj->getAccessToken());
1062  } else {
1063  $this->errors[] = "Token not found";
1064  }
1065  } catch (Exception $e) {
1066  // Return an error if token not found
1067  $this->errors[] = $e->getMessage();
1068  dol_syslog("CMailFile::sendfile: mail end error=".$e->getMessage(), LOG_ERR);
1069  }
1070  }
1071  if (!empty($conf->global->$keyforsslseflsigned)) {
1072  $this->transport->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false)));
1073  }
1074  //$smtps->_msgReplyTo = 'reply@web.com';
1075 
1076  // Switch content encoding to base64 - avoid the doubledot issue with quoted-printable
1077  $contentEncoderBase64 = new Swift_Mime_ContentEncoder_Base64ContentEncoder();
1078  $this->message->setEncoder($contentEncoderBase64);
1079 
1080  // Create the Mailer using your created Transport
1081  $this->mailer = new Swift_Mailer($this->transport);
1082 
1083  // DKIM SIGN
1084  if ($conf->global->MAIN_MAIL_EMAIL_DKIM_ENABLED) {
1085  $privateKey = $conf->global->MAIN_MAIL_EMAIL_DKIM_PRIVATE_KEY;
1086  $domainName = $conf->global->MAIN_MAIL_EMAIL_DKIM_DOMAIN;
1087  $selector = $conf->global->MAIN_MAIL_EMAIL_DKIM_SELECTOR;
1088  $signer = new Swift_Signers_DKIMSigner($privateKey, $domainName, $selector);
1089  $this->message->attachSigner($signer->ignoreHeader('Return-Path'));
1090  }
1091 
1092  if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
1093  // To use the ArrayLogger
1094  $this->logger = new Swift_Plugins_Loggers_ArrayLogger();
1095  // Or to use the Echo Logger
1096  //$this->logger = new Swift_Plugins_Loggers_EchoLogger();
1097  $this->mailer->registerPlugin(new Swift_Plugins_LoggerPlugin($this->logger));
1098  }
1099  // send mail
1100  try {
1101  $result = $this->mailer->send($this->message, $failedRecipients);
1102  } catch (Exception $e) {
1103  $this->errors[] = $e->getMessage();
1104  }
1105  if (!empty($conf->global->MAIN_MAIL_DEBUG)) {
1106  $this->dump_mail();
1107  }
1108 
1109  $res = true;
1110  if (!empty($this->error) || !empty($this->errors) || !$result) {
1111  if (!empty($failedRecipients)) {
1112  $this->errors[] = 'Transport failed for the following addresses: "' . join('", "', $failedRecipients) . '".';
1113  }
1114  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
1115  $res = false;
1116  } else {
1117  dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
1118  }
1119  } else {
1120  // Send mail method not correctly defined
1121  // --------------------------------------
1122 
1123  return 'Bad value for sendmode';
1124  }
1125 
1126  $parameters = array('sent' => $res);
1127  $action = '';
1128  $reshook = $hookmanager->executeHooks('sendMailAfter', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1129  if ($reshook < 0) {
1130  $this->error = "Error in hook maildao sendMailAfter ".$reshook;
1131  dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
1132 
1133  return $reshook;
1134  }
1135  } else {
1136  $this->error = 'No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS';
1137  dol_syslog("CMailFile::sendfile: ".$this->error, LOG_WARNING);
1138  }
1139 
1140  error_reporting($errorlevel); // Reactive niveau erreur origine
1141 
1142  return $res;
1143  }
1144 
1151  public static function encodetorfc2822($stringtoencode)
1152  {
1153  global $conf;
1154  return '=?'.$conf->file->character_set_client.'?B?'.base64_encode($stringtoencode).'?=';
1155  }
1156 
1157  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1164  private function _encode_file($sourcefile)
1165  {
1166  // phpcs:enable
1167  $newsourcefile = dol_osencode($sourcefile);
1168 
1169  if (is_readable($newsourcefile)) {
1170  $contents = file_get_contents($newsourcefile); // Need PHP 4.3
1171  $encoded = chunk_split(base64_encode($contents), 76, $this->eol); // 76 max is defined into http://tools.ietf.org/html/rfc2047
1172  return $encoded;
1173  } else {
1174  $this->error = "Error: Can't read file '".$sourcefile."' into _encode_file";
1175  dol_syslog("CMailFile::encode_file: ".$this->error, LOG_ERR);
1176  return -1;
1177  }
1178  }
1179 
1180 
1181  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1189  public function dump_mail()
1190  {
1191  // phpcs:enable
1192  global $conf, $dolibarr_main_data_root;
1193 
1194  if (@is_writeable($dolibarr_main_data_root)) { // Avoid fatal error on fopen with open_basedir
1195  $outputfile = $dolibarr_main_data_root."/dolibarr_mail.log";
1196  $fp = fopen($outputfile, "w");
1197 
1198  if ($this->sendmode == 'mail') {
1199  fputs($fp, $this->headers);
1200  fputs($fp, $this->eol); // This eol is added by the mail function, so we add it in log
1201  fputs($fp, $this->message);
1202  } elseif ($this->sendmode == 'smtps') {
1203  fputs($fp, $this->smtps->log); // this->smtps->log is filled only if MAIN_MAIL_DEBUG was set to on
1204  } elseif ($this->sendmode == 'swiftmailer') {
1205  fputs($fp, $this->logger->dump()); // this->logger is filled only if MAIN_MAIL_DEBUG was set to on
1206  }
1207 
1208  fclose($fp);
1209  if (!empty($conf->global->MAIN_UMASK)) {
1210  @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
1211  }
1212  }
1213  }
1214 
1215 
1222  public function checkIfHTML($msg)
1223  {
1224  if (!preg_match('/^[\s\t]*<html/i', $msg)) {
1225  $out = "<html><head><title></title>";
1226  if (!empty($this->styleCSS)) {
1227  $out .= $this->styleCSS;
1228  }
1229  $out .= "</head><body";
1230  if (!empty($this->bodyCSS)) {
1231  $out .= $this->bodyCSS;
1232  }
1233  $out .= ">";
1234  $out .= $msg;
1235  $out .= "</body></html>";
1236  } else {
1237  $out = $msg;
1238  }
1239 
1240  return $out;
1241  }
1242 
1248  public function buildCSS()
1249  {
1250  if (!empty($this->css)) {
1251  // Style CSS
1252  $this->styleCSS = '<style type="text/css">';
1253  $this->styleCSS .= 'body {';
1254 
1255  if ($this->css['bgcolor']) {
1256  $this->styleCSS .= ' background-color: '.$this->css['bgcolor'].';';
1257  $this->bodyCSS .= ' bgcolor="'.$this->css['bgcolor'].'"';
1258  }
1259  if ($this->css['bgimage']) {
1260  // TODO recuperer cid
1261  $this->styleCSS .= ' background-image: url("cid:'.$this->css['bgimage_cid'].'");';
1262  }
1263  $this->styleCSS .= '}';
1264  $this->styleCSS .= '</style>';
1265  }
1266  }
1267 
1268 
1269  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1275  public function write_smtpheaders()
1276  {
1277  // phpcs:enable
1278  global $conf;
1279  $out = "";
1280 
1281  $host = dol_getprefix('email');
1282 
1283  // Sender
1284  //$out.= "Sender: ".getValidAddress($this->addr_from,2)).$this->eol2;
1285  $out .= "From: ".$this->getValidAddress($this->addr_from, 3, 1).$this->eol2;
1286  if (!empty($conf->global->MAIN_MAIL_SENDMAIL_FORCE_BA)) {
1287  $out .= "To: ".$this->getValidAddress($this->addr_to, 0, 1).$this->eol2;
1288  }
1289  // 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.
1290  $out .= "Return-Path: ".$this->getValidAddress($this->addr_from, 0, 1).$this->eol2;
1291  if (isset($this->reply_to) && $this->reply_to) {
1292  $out .= "Reply-To: ".$this->getValidAddress($this->reply_to, 2).$this->eol2;
1293  }
1294  if (isset($this->errors_to) && $this->errors_to) {
1295  $out .= "Errors-To: ".$this->getValidAddress($this->errors_to, 2).$this->eol2;
1296  }
1297 
1298  // Receiver
1299  if (isset($this->addr_cc) && $this->addr_cc) {
1300  $out .= "Cc: ".$this->getValidAddress($this->addr_cc, 2).$this->eol2;
1301  }
1302  if (isset($this->addr_bcc) && $this->addr_bcc) {
1303  $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 ?
1304  }
1305 
1306  // Delivery receipt
1307  if (isset($this->deliveryreceipt) && $this->deliveryreceipt == 1) {
1308  $out .= "Disposition-Notification-To: ".$this->getValidAddress($this->addr_from, 2).$this->eol2;
1309  }
1310 
1311  //$out.= "X-Priority: 3".$this->eol2;
1312 
1313  $out .= 'Date: '.date("r").$this->eol2;
1314 
1315  $trackid = $this->trackid;
1316  if ($trackid) {
1317  // References is kept in response and Message-ID is returned into In-Reply-To:
1318  $this->msgid = time().'.phpmail-dolibarr-'.$trackid.'@'.$host;
1319  $out .= 'Message-ID: <'.$this->msgid.">".$this->eol2; // Uppercase seems replaced by phpmail
1320  $out .= 'References: <'.$this->msgid.">".$this->eol2;
1321  $out .= 'X-Dolibarr-TRACKID: '.$trackid.'@'.$host.$this->eol2;
1322  } else {
1323  $this->msgid = time().'.phpmail@'.$host;
1324  $out .= 'Message-ID: <'.$this->msgid.">".$this->eol2;
1325  }
1326 
1327  if (!empty($_SERVER['REMOTE_ADDR'])) {
1328  $out .= "X-RemoteAddr: ".$_SERVER['REMOTE_ADDR'].$this->eol2;
1329  }
1330  $out .= "X-Mailer: Dolibarr version ".DOL_VERSION." (using php mail)".$this->eol2;
1331  $out .= "Mime-Version: 1.0".$this->eol2;
1332 
1333  //$out.= "From: ".$this->getValidAddress($this->addr_from,3,1).$this->eol;
1334 
1335  $out .= "Content-Type: multipart/mixed;".$this->eol2." boundary=\"".$this->mixed_boundary."\"".$this->eol2;
1336  $out .= "Content-Transfer-Encoding: 8bit".$this->eol2; // TODO Seems to be ignored. Header is 7bit once received.
1337 
1338  dol_syslog("CMailFile::write_smtpheaders smtp_header=\n".$out);
1339  return $out;
1340  }
1341 
1342 
1343  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1351  public function write_mimeheaders($filename_list, $mimefilename_list)
1352  {
1353  // phpcs:enable
1354  $mimedone = 0;
1355  $out = "";
1356 
1357  if (is_array($filename_list)) {
1358  $filename_list_size = count($filename_list);
1359  for ($i = 0; $i < $filename_list_size; $i++) {
1360  if ($filename_list[$i]) {
1361  if ($mimefilename_list[$i]) {
1362  $filename_list[$i] = $mimefilename_list[$i];
1363  }
1364  $out .= "X-attachments: $filename_list[$i]".$this->eol2;
1365  }
1366  }
1367  }
1368 
1369  dol_syslog("CMailFile::write_mimeheaders mime_header=\n".$out, LOG_DEBUG);
1370  return $out;
1371  }
1372 
1373  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1380  public function write_body($msgtext)
1381  {
1382  // phpcs:enable
1383  global $conf;
1384 
1385  $out = '';
1386 
1387  $out .= "--".$this->mixed_boundary.$this->eol;
1388 
1389  if ($this->atleastoneimage) {
1390  $out .= "Content-Type: multipart/alternative;".$this->eol." boundary=\"".$this->alternative_boundary."\"".$this->eol;
1391  $out .= $this->eol;
1392  $out .= "--".$this->alternative_boundary.$this->eol;
1393  }
1394 
1395  // Make RFC821 Compliant, replace bare linefeeds
1396  $strContent = preg_replace("/(?<!\r)\n/si", "\r\n", $msgtext); // PCRE modifier /s means new lines are common chars
1397  if (!empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA)) {
1398  $strContent = preg_replace("/\r\n/si", "\n", $strContent); // PCRE modifier /s means new lines are common chars
1399  }
1400 
1401  $strContentAltText = '';
1402  if ($this->msgishtml) {
1403  // Similar code to forge a text from html is also in smtps.class.php
1404  $strContentAltText = preg_replace("/<br\s*[^>]*>/", " ", $strContent);
1405  // TODO We could replace <img ...> with [Filename.ext] like Gmail do.
1406  $strContentAltText = html_entity_decode(strip_tags($strContentAltText)); // Remove any HTML tags
1407  $strContentAltText = trim(wordwrap($strContentAltText, 75, empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA) ? "\r\n" : "\n"));
1408 
1409  // Check if html header already in message, if not complete the message
1410  $strContent = $this->checkIfHTML($strContent);
1411  }
1412 
1413  // Make RFC2045 Compliant, split lines
1414  //$strContent = rtrim(chunk_split($strContent)); // Function chunck_split seems ko if not used on a base64 content
1415  // TODO Encode main content into base64 and use the chunk_split, or quoted-printable
1416  $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.
1417 
1418  if ($this->msgishtml) {
1419  if ($this->atleastoneimage) {
1420  $out .= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol;
1421  //$out.= "Content-Transfer-Encoding: 7bit".$this->eol;
1422  $out .= $this->eol.($strContentAltText ? $strContentAltText : strip_tags($strContent)).$this->eol; // Add plain text message
1423  $out .= "--".$this->alternative_boundary.$this->eol;
1424  $out .= "Content-Type: multipart/related;".$this->eol." boundary=\"".$this->related_boundary."\"".$this->eol;
1425  $out .= $this->eol;
1426  $out .= "--".$this->related_boundary.$this->eol;
1427  }
1428 
1429  if (!$this->atleastoneimage && $strContentAltText && !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) { // Add plain text message part before html part
1430  $out .= "Content-Type: multipart/alternative;".$this->eol." boundary=\"".$this->alternative_boundary."\"".$this->eol;
1431  $out .= $this->eol;
1432  $out .= "--".$this->alternative_boundary.$this->eol;
1433  $out .= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol;
1434  //$out.= "Content-Transfer-Encoding: 7bit".$this->eol;
1435  $out .= $this->eol.$strContentAltText.$this->eol;
1436  $out .= "--".$this->alternative_boundary.$this->eol;
1437  }
1438 
1439  $out .= "Content-Type: text/html; charset=".$conf->file->character_set_client.$this->eol;
1440  //$out.= "Content-Transfer-Encoding: 7bit".$this->eol; // TODO Use base64
1441  $out .= $this->eol.$strContent.$this->eol;
1442 
1443  if (!$this->atleastoneimage && $strContentAltText && !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) { // Add plain text message part after html part
1444  $out .= "--".$this->alternative_boundary."--".$this->eol;
1445  }
1446  } else {
1447  $out .= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol;
1448  //$out.= "Content-Transfer-Encoding: 7bit".$this->eol;
1449  $out .= $this->eol.$strContent.$this->eol;
1450  }
1451 
1452  $out .= $this->eol;
1453 
1454  // Encode images
1455  if ($this->atleastoneimage) {
1456  $out .= $this->write_images($this->images_encoded);
1457  // always end related and end alternative after inline images
1458  $out .= "--".$this->related_boundary."--".$this->eol;
1459  $out .= $this->eol."--".$this->alternative_boundary."--".$this->eol;
1460  $out .= $this->eol;
1461  }
1462 
1463  return $out;
1464  }
1465 
1466  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1476  private function write_files($filename_list, $mimetype_list, $mimefilename_list, $cidlist)
1477  {
1478  // phpcs:enable
1479  $out = '';
1480 
1481  $filename_list_size = count($filename_list);
1482  for ($i = 0; $i < $filename_list_size; $i++) {
1483  if ($filename_list[$i]) {
1484  dol_syslog("CMailFile::write_files: i=$i");
1485  $encoded = $this->_encode_file($filename_list[$i]);
1486  if ($encoded >= 0) {
1487  if ($mimefilename_list[$i]) {
1488  $filename_list[$i] = $mimefilename_list[$i];
1489  }
1490  if (!$mimetype_list[$i]) {
1491  $mimetype_list[$i] = "application/octet-stream";
1492  }
1493 
1494  $out .= "--".$this->mixed_boundary.$this->eol;
1495  $out .= "Content-Disposition: attachment; filename=\"".$filename_list[$i]."\"".$this->eol;
1496  $out .= "Content-Type: ".$mimetype_list[$i]."; name=\"".$filename_list[$i]."\"".$this->eol;
1497  $out .= "Content-Transfer-Encoding: base64".$this->eol;
1498  $out .= "Content-Description: ".$filename_list[$i].$this->eol;
1499  if (!empty($cidlist) && is_array($cidlist) && $cidlist[$i]) {
1500  $out .= "X-Attachment-Id: ".$cidlist[$i].$this->eol;
1501  $out .= "Content-ID: <".$cidlist[$i].'>'.$this->eol;
1502  }
1503  $out .= $this->eol;
1504  $out .= $encoded;
1505  $out .= $this->eol;
1506  //$out.= $this->eol;
1507  } else {
1508  return $encoded;
1509  }
1510  }
1511  }
1512 
1513  return $out;
1514  }
1515 
1516 
1517  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1524  public function write_images($images_list)
1525  {
1526  // phpcs:enable
1527  $out = '';
1528 
1529  if (is_array($images_list)) {
1530  foreach ($images_list as $img) {
1531  dol_syslog("CMailFile::write_images: ".$img["name"]);
1532 
1533  $out .= "--".$this->related_boundary.$this->eol; // always related for an inline image
1534  $out .= "Content-Type: ".$img["content_type"]."; name=\"".$img["name"]."\"".$this->eol;
1535  $out .= "Content-Transfer-Encoding: base64".$this->eol;
1536  $out .= "Content-Disposition: inline; filename=\"".$img["name"]."\"".$this->eol;
1537  $out .= "Content-ID: <".$img["cid"].">".$this->eol;
1538  $out .= $this->eol;
1539  $out .= $img["image_encoded"];
1540  $out .= $this->eol;
1541  }
1542  }
1543 
1544  return $out;
1545  }
1546 
1547 
1548  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1556  public function check_server_port($host, $port)
1557  {
1558  // phpcs:enable
1559  global $conf;
1560 
1561  $_retVal = 0;
1562  $timeout = 5; // Timeout in seconds
1563 
1564  if (function_exists('fsockopen')) {
1565  $keyforsmtpserver = 'MAIN_MAIL_SMTP_SERVER';
1566  $keyforsmtpport = 'MAIN_MAIL_SMTP_PORT';
1567  $keyforsmtpid = 'MAIN_MAIL_SMTPS_ID';
1568  $keyforsmtppw = 'MAIN_MAIL_SMTPS_PW';
1569  $keyforsmtpauthtype = 'MAIN_MAIL_SMTPS_AUTH_TYPE';
1570  $keyforsmtpoauthservice = 'MAIN_MAIL_SMTPS_OAUTH_SERVICE';
1571  $keyfortls = 'MAIN_MAIL_EMAIL_TLS';
1572  $keyforstarttls = 'MAIN_MAIL_EMAIL_STARTTLS';
1573  $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED';
1574 
1575  if (!empty($this->sendcontext)) {
1576  $smtpContextKey = strtoupper($this->sendcontext);
1577  $smtpContextSendMode = getDolGlobalString('MAIN_MAIL_SENDMODE_'.$smtpContextKey);
1578  if (!empty($smtpContextSendMode) && $smtpContextSendMode != 'default') {
1579  $keyforsmtpserver = 'MAIN_MAIL_SMTP_SERVER_'.$smtpContextKey;
1580  $keyforsmtpport = 'MAIN_MAIL_SMTP_PORT_'.$smtpContextKey;
1581  $keyforsmtpid = 'MAIN_MAIL_SMTPS_ID_'.$smtpContextKey;
1582  $keyforsmtppw = 'MAIN_MAIL_SMTPS_PW_'.$smtpContextKey;
1583  $keyforsmtpauthtype = 'MAIN_MAIL_SMTPS_AUTH_TYPE_'.$smtpContextKey;
1584  $keyforsmtpoauthservice = 'MAIN_MAIL_SMTPS_OAUTH_SERVICE_'.$smtpContextKey;
1585  $keyfortls = 'MAIN_MAIL_EMAIL_TLS_'.$smtpContextKey;
1586  $keyforstarttls = 'MAIN_MAIL_EMAIL_STARTTLS_'.$smtpContextKey;
1587  $keyforsslseflsigned = 'MAIN_MAIL_EMAIL_SMTP_ALLOW_SELF_SIGNED_'.$smtpContextKey;
1588  }
1589  }
1590 
1591  // If we use SSL/TLS
1592  if (!empty($conf->global->$keyfortls) && function_exists('openssl_open')) {
1593  $host = 'ssl://'.$host;
1594  }
1595  // tls smtp start with no encryption
1596  //if (!empty($conf->global->MAIN_MAIL_EMAIL_STARTTLS) && function_exists('openssl_open')) $host='tls://'.$host;
1597 
1598  dol_syslog("Try socket connection to host=".$host." port=".$port);
1599  //See if we can connect to the SMTP server
1600  $errno = 0; $errstr = '';
1601  if ($socket = @fsockopen(
1602  $host, // Host to test, IP or domain. Add ssl:// for SSL/TLS.
1603  $port, // which Port number to use
1604  $errno, // actual system level error
1605  $errstr, // and any text that goes with the error
1606  $timeout // timeout for reading/writing data over the socket
1607  )) {
1608  // Windows still does not have support for this timeout function
1609  if (function_exists('stream_set_timeout')) {
1610  stream_set_timeout($socket, $timeout, 0);
1611  }
1612 
1613  dol_syslog("Now we wait for answer 220");
1614 
1615  // Check response from Server
1616  if ($_retVal = $this->server_parse($socket, "220")) {
1617  $_retVal = $socket;
1618  }
1619  } else {
1620  $this->error = utf8_check('Error '.$errno.' - '.$errstr) ? 'Error '.$errno.' - '.$errstr : utf8_encode('Error '.$errno.' - '.$errstr);
1621  }
1622  }
1623  return $_retVal;
1624  }
1625 
1626  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1635  public function server_parse($socket, $response)
1636  {
1637  // phpcs:enable
1638  $_retVal = true; // Indicates if Object was created or not
1639  $server_response = '';
1640 
1641  while (substr($server_response, 3, 1) != ' ') {
1642  if (!($server_response = fgets($socket, 256))) {
1643  $this->error = "Couldn't get mail server response codes";
1644  return false;
1645  }
1646  }
1647 
1648  if (!(substr($server_response, 0, 3) == $response)) {
1649  $this->error = "Ran into problems sending Mail.\r\nResponse: $server_response";
1650  $_retVal = false;
1651  }
1652 
1653  return $_retVal;
1654  }
1655 
1662  private function findHtmlImages($images_dir)
1663  {
1664  // Build the array of image extensions
1665  $extensions = array_keys($this->image_types);
1666 
1667  // We search (into mail body this->html), if we find some strings like "... file=xxx.img"
1668  // For example when:
1669  // <img alt="" src="/viewimage.php?modulepart=medias&amp;entity=1&amp;file=image/picture.jpg" style="height:356px; width:1040px" />
1670  $matches = array();
1671  preg_match_all('/(?:"|\')([^"\']+\.('.implode('|', $extensions).'))(?:"|\')/Ui', $this->html, $matches); // If "xxx.ext" or 'xxx.ext' found
1672 
1673  if (!empty($matches)) {
1674  $i = 0;
1675  // We are interested in $matches[1] only (the second set of parenthesis into regex)
1676  foreach ($matches[1] as $full) {
1677  $regs = array();
1678  if (preg_match('/file=([A-Za-z0-9_\-\/]+[\.]?[A-Za-z0-9]+)?$/i', $full, $regs)) { // If xxx is 'file=aaa'
1679  $img = $regs[1];
1680 
1681  if (file_exists($images_dir.'/'.$img)) {
1682  // Image path in src
1683  $src = preg_quote($full, '/');
1684  // Image full path
1685  $this->html_images[$i]["fullpath"] = $images_dir.'/'.$img;
1686  // Image name
1687  $this->html_images[$i]["name"] = $img;
1688  // Content type
1689  $regext = array();
1690  if (preg_match('/^.+\.(\w{3,4})$/', $img, $regext)) {
1691  $ext = strtolower($regext[1]);
1692  $this->html_images[$i]["content_type"] = $this->image_types[$ext];
1693  }
1694  // cid
1695  $this->html_images[$i]["cid"] = dol_hash(uniqid(time()), 3); // Force md5 hash (does not contains special chars)
1696  // type
1697  $this->html_images[$i]["type"] = 'cidfromurl';
1698 
1699  $this->html = preg_replace("/src=\"$src\"|src='$src'/i", "src=\"cid:".$this->html_images[$i]["cid"]."\"", $this->html);
1700  }
1701  $i++;
1702  }
1703  }
1704 
1705  if (!empty($this->html_images)) {
1706  $inline = array();
1707 
1708  $i = 0;
1709 
1710  foreach ($this->html_images as $img) {
1711  $fullpath = $images_dir.'/'.$img["name"];
1712 
1713  // If duplicate images are embedded, they may show up as attachments, so remove them.
1714  if (!in_array($fullpath, $inline)) {
1715  // Read image file
1716  if ($image = file_get_contents($fullpath)) {
1717  // On garde que le nom de l'image
1718  $regs = array();
1719  preg_match('/([A-Za-z0-9_-]+[\.]?[A-Za-z0-9]+)?$/i', $img["name"], $regs);
1720  $imgName = $regs[1];
1721 
1722  $this->images_encoded[$i]['name'] = $imgName;
1723  $this->images_encoded[$i]['fullpath'] = $fullpath;
1724  $this->images_encoded[$i]['content_type'] = $img["content_type"];
1725  $this->images_encoded[$i]['cid'] = $img["cid"];
1726  // Encodage de l'image
1727  $this->images_encoded[$i]["image_encoded"] = chunk_split(base64_encode($image), 68, $this->eol);
1728  $inline[] = $fullpath;
1729  }
1730  }
1731  $i++;
1732  }
1733  } else {
1734  return -1;
1735  }
1736 
1737  return 1;
1738  } else {
1739  return 0;
1740  }
1741  }
1742 
1749  private function findHtmlImagesIsSrcData($images_dir)
1750  {
1751  global $conf;
1752 
1753  // Build the array of image extensions
1754  $extensions = array_keys($this->image_types);
1755 
1756  if ($images_dir && !dol_is_dir($images_dir)) {
1757  dol_mkdir($images_dir, DOL_DATA_ROOT);
1758  }
1759 
1760  // Uncomment this for debug
1761  /*
1762  global $dolibarr_main_data_root;
1763  $outputfile = $dolibarr_main_data_root."/dolibarr_mail.log";
1764  $fp = fopen($outputfile, "w+");
1765  fwrite($fp, $this->html);
1766  fclose($fp);
1767  */
1768 
1769  // We search (into mail body this->html), if we find some strings like "... file=xxx.img"
1770  // For example when:
1771  // <img alt="" src="/viewimage.php?modulepart=medias&amp;entity=1&amp;file=image/picture.jpg" style="height:356px; width:1040px" />
1772  $matches = array();
1773  preg_match_all('/src="data:image\/('.implode('|', $extensions).');base64,([^"]+)"/Ui', $this->html, $matches); // If "xxx.ext" or 'xxx.ext' found
1774 
1775  if (!empty($matches) && !empty($matches[1])) {
1776  if (empty($images_dir)) {
1777  // No temp directory provided, so we are not able to support convertion of data:image into physical images.
1778  $this->error = 'NoTempDirProvidedInCMailConstructorSoCantConvertDataImgOnDisk';
1779  return -1;
1780  }
1781 
1782  $i = 0;
1783  foreach ($matches[1] as $key => $ext) {
1784  // We save the image to send in disk
1785  $filecontent = $matches[2][$key];
1786  $cid = 'cid000'.dol_hash($this->html, 'md5');
1787  $destfiletmp = $images_dir.'/'.$cid.'.'.$ext;
1788 
1789  $fhandle = @fopen($destfiletmp, 'w');
1790  if ($fhandle) {
1791  $nbofbyteswrote = fwrite($fhandle, base64_decode($filecontent));
1792  fclose($fhandle);
1793  @chmod($destfiletmp, octdec($conf->global->MAIN_UMASK));
1794  } else {
1795  $this->errors[] = "Failed to open file '".$destfiletmp."' for write";
1796  return -1;
1797  }
1798 
1799  if (file_exists($destfiletmp)) {
1800  // Image full path
1801  $this->html_images[$i]["fullpath"] = $destfiletmp;
1802  // Image name
1803  $this->html_images[$i]["name"] = basename($destfiletmp);
1804  // Content type
1805  $this->html_images[$i]["content_type"] = $this->image_types[strtolower($ext)];
1806  // cid
1807  $this->html_images[$i]["cid"] = $cid;
1808  // type
1809  $this->html_images[$i]["type"] = 'cidfromdata';
1810 
1811  $this->html = str_replace('src="data:image/'.$ext.';base64,'.$filecontent.'"', 'src="cid:'.$this->html_images[$i]["cid"].'"', $this->html);
1812  }
1813  $i++;
1814  }
1815 
1816  return 1;
1817  } else {
1818  return 0;
1819  }
1820  }
1821 
1837  public static function getValidAddress($address, $format, $encode = 0, $maxnumberofemail = 0)
1838  {
1839  global $conf;
1840 
1841  $ret = '';
1842 
1843  $arrayaddress = explode(',', $address);
1844 
1845  // Boucle sur chaque composant de l'adresse
1846  $i = 0;
1847  foreach ($arrayaddress as $val) {
1848  $regs = array();
1849  if (preg_match('/^(.*)<(.*)>$/i', trim($val), $regs)) {
1850  $name = trim($regs[1]);
1851  $email = trim($regs[2]);
1852  } else {
1853  $name = '';
1854  $email = trim($val);
1855  }
1856 
1857  if ($email) {
1858  $i++;
1859 
1860  $newemail = '';
1861  if ($format == 5) {
1862  $newemail = $name ? $name : $email;
1863  $newemail = '<a href="mailto:'.$email.'">'.$newemail.'</a>';
1864  }
1865  if ($format == 4) {
1866  $newemail = $name ? $name : $email;
1867  }
1868  if ($format == 2) {
1869  $newemail = $email;
1870  }
1871  if ($format == 1 || $format == 3) {
1872  $newemail = '<'.$email.'>';
1873  }
1874  if ($format == 0 || $format == 3) {
1875  if (!empty($conf->global->MAIN_MAIL_NO_FULL_EMAIL)) {
1876  $newemail = '<'.$email.'>';
1877  } elseif (!$name) {
1878  $newemail = '<'.$email.'>';
1879  } else {
1880  $newemail = ($format == 3 ? '"' : '').($encode ?self::encodetorfc2822($name) : $name).($format == 3 ? '"' : '').' <'.$email.'>';
1881  }
1882  }
1883 
1884  $ret = ($ret ? $ret.',' : '').$newemail;
1885 
1886  // Stop if we have too much records
1887  if ($maxnumberofemail && $i >= $maxnumberofemail) {
1888  if (count($arrayaddress) > $maxnumberofemail) {
1889  $ret .= '...';
1890  }
1891  break;
1892  }
1893  }
1894  }
1895 
1896  return $ret;
1897  }
1898 
1906  public static function getArrayAddress($address)
1907  {
1908  global $conf;
1909 
1910  $ret = array();
1911 
1912  $arrayaddress = explode(',', $address);
1913 
1914  // Boucle sur chaque composant de l'adresse
1915  foreach ($arrayaddress as $val) {
1916  if (preg_match('/^(.*)<(.*)>$/i', trim($val), $regs)) {
1917  $name = trim($regs[1]);
1918  $email = trim($regs[2]);
1919  } else {
1920  $name = null;
1921  $email = trim($val);
1922  }
1923 
1924  $ret[$email] = empty($conf->global->MAIN_MAIL_NO_FULL_EMAIL) ? $name : null;
1925  }
1926 
1927  return $ret;
1928  }
1929 }
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
_encode_file($sourcefile)
Read a file on disk and return encoded content for emails (mode = 'mail')
write_body($msgtext)
Return email content (mode = 'mail')
$bodyCSS
Defined background directly in body tag.
dump_mail()
Write content of a SMTP request into a dump file (mode = all) Used for debugging.
sendfile()
Send mail that was prepared by constructor.
static encodetorfc2822($stringtoencode)
Encode subject according to RFC 2822 - http://en.wikipedia.org/wiki/MIME#Encoded-Word.
checkIfHTML($msg)
Correct an uncomplete html string.
static getValidAddress($address, $format, $encode=0, $maxnumberofemail=0)
Return a formatted address string for SMTP protocol.
write_images($images_list)
Attach an image to email (mode = 'mail')
server_parse($socket, $response)
This function has been modified as provided by SirSir to allow multiline responses when using SMTP Ex...
__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='', $upload_dir_tmp='')
CMailFile.
write_smtpheaders()
Create SMTP headers (mode = 'mail')
findHtmlImagesIsSrcData($images_dir)
Seearch images with data:image format into html message.
$styleCSS
Defined css style for body background.
write_mimeheaders($filename_list, $mimefilename_list)
Create header MIME (mode = 'mail')
check_server_port($host, $port)
Try to create a socket connection.
buildCSS()
Build a css style (mode = all) into this->styleCSS and this->bodyCSS.
write_files($filename_list, $mimetype_list, $mimefilename_list, $cidlist)
Attach file to email (mode = 'mail')
static getArrayAddress($address)
Return a formatted array of address string for SMTP protocol.
findHtmlImages($images_dir)
Seearch images into html message and init array this->images_encoded if found.
Class to manage hooks.
Class to construct and send SMTP compliant email, even to a secure SMTP server, regardless of platfor...
Definition: smtps.class.php:47
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:450
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
ascii_check($str)
Check if a string is in ASCII.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
if(!function_exists('utf8_encode')) if(!function_exists('utf8_decode')) getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_sanitizeEmail($stringtoclean)
Clean a string to use it as an Email.
dol_hash($chain, $type='0')
Returns a hash (non reversible encryption) of a string.