PageRenderTime 33ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 1ms

/htdocs/core/class/CMailFile.class.php

https://bitbucket.org/speedealing/speedealing
PHP | 1066 lines | 813 code | 107 blank | 146 comment | 92 complexity | d5875725af906629d8970f0cb1d7ac88 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1, GPL-3.0, MIT
  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-2012 Laurent Destailleur <eldy@users.sourceforge.net>
  8. * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. * or see http://www.gnu.org/
  23. *
  24. * Lots of code inspired from Dan Potter's CMailFile class
  25. */
  26. /**
  27. * \file htdocs/core/class/CMailFile.class.php
  28. * \brief File of class to send emails (with attachments or not)
  29. */
  30. /**
  31. * Class to send emails (with attachments or not)
  32. * Usage: $mailfile = new CMailFile($subject,$sendto,$replyto,$message,$filepath,$mimetype,$filename,$cc,$ccc,$deliveryreceipt,$msgishtml,$errors_to);
  33. * $mailfile->sendfile();
  34. */
  35. class CMailFile
  36. {
  37. var $subject; // Topic: Subject of email
  38. var $addr_from; // From: Label of sender (name but can contains an email inside <>)
  39. // Sender: Who send the email ("Sender" has sent emails on behalf of "From").
  40. // Use it with an email from a sending host from is a SPF protected domain and sending host is not this domain.
  41. // Return-Path: Email where to send bounds.
  42. var $errors_to; // Errors-To: Email where to send errors.
  43. var $addr_to;
  44. var $addr_cc;
  45. var $addr_bcc;
  46. var $mixed_boundary;
  47. var $related_boundary;
  48. var $alternative_boundary;
  49. var $deliveryreceipt;
  50. var $eol;
  51. var $atleastonefile=0;
  52. var $error='';
  53. var $smtps; // Contains SMTPs object (if this method is used)
  54. var $phpmailer; // Contains PHPMailer object (if this method is used)
  55. //CSS
  56. var $css;
  57. //! Defined css style for body background
  58. var $styleCSS;
  59. //! Defined bacckground directly in body tag
  60. var $bodyCSS;
  61. // Image
  62. var $html;
  63. var $image_boundary;
  64. var $atleastoneimage=0;
  65. var $html_images=array();
  66. var $images_encoded=array();
  67. var $image_types = array('gif' => 'image/gif',
  68. 'jpg' => 'image/jpeg',
  69. 'jpeg' => 'image/jpeg',
  70. 'jpe' => 'image/jpeg',
  71. 'bmp' => 'image/bmp',
  72. 'png' => 'image/png',
  73. 'tif' => 'image/tiff',
  74. 'tiff' => 'image/tiff');
  75. /**
  76. * CMailFile
  77. *
  78. * @param string $subject Topic/Subject of mail
  79. * @param string $to Recipients emails (RFC 2822: "Nom prenom <email>[, ...]" ou "email[, ...]" ou "<email>[, ...]")
  80. * @param string $from Sender email (RFC 2822: "Nom prenom <email>[, ...]" ou "email[, ...]" ou "<email>[, ...]")
  81. * @param string $msg Message
  82. * @param array $filename_list List of files to attach (full path of filename on file system)
  83. * @param array $mimetype_list List of MIME type of attached files
  84. * @param array $mimefilename_list List of attached file name in message
  85. * @param string $addr_cc Email cc
  86. * @param string $addr_bcc Email bcc
  87. * @param int $deliveryreceipt Ask a delivery receipt
  88. * @param int $msgishtml 1=String IS already html, 0=String IS NOT html, -1=Unknown need autodetection
  89. * @param string $errors_to Email errors
  90. * @param string $css Css option
  91. */
  92. function __construct($subject,$to,$from,$msg,
  93. $filename_list=array(),$mimetype_list=array(),$mimefilename_list=array(),
  94. $addr_cc="",$addr_bcc="",$deliveryreceipt=0,$msgishtml=0,$errors_to='',$css='')
  95. {
  96. global $conf;
  97. // We define end of line (RFC 822bis section 2.3)
  98. $this->eol="\r\n";
  99. // eol2 is for header fields to manage bugged MTA with option MAIN_FIX_FOR_BUGGED_MTA
  100. $this->eol2=$this->eol;
  101. if (! empty($conf->global->MAIN_FIX_FOR_BUGGED_MTA)) $this->eol2="\n";
  102. // On defini mixed_boundary
  103. $this->mixed_boundary = "multipart_x." . time() . ".x_boundary";
  104. // On defini related_boundary
  105. $this->related_boundary = 'mul_'.dol_hash(uniqid("dolibarr2"));
  106. // On defini alternative_boundary
  107. $this->alternative_boundary = 'mul_'.dol_hash(uniqid("dolibarr3"));
  108. // If ending method not defined
  109. if (empty($conf->global->MAIN_MAIL_SENDMODE)) $conf->global->MAIN_MAIL_SENDMODE='mail';
  110. dol_syslog("CMailFile::CMailfile: MAIN_MAIL_SENDMODE=".$conf->global->MAIN_MAIL_SENDMODE." charset=".$conf->file->character_set_client." from=$from, to=$to, addr_cc=$addr_cc, addr_bcc=$addr_bcc, errors_to=$errors_to", LOG_DEBUG);
  111. dol_syslog("CMailFile::CMailfile: subject=$subject, deliveryreceipt=$deliveryreceipt, msgishtml=$msgishtml", LOG_DEBUG);
  112. // Detect if message is HTML (use fast method)
  113. if ($msgishtml == -1)
  114. {
  115. $this->msgishtml = 0;
  116. if (dol_textishtml($msg)) $this->msgishtml = 1;
  117. }
  118. else
  119. {
  120. $this->msgishtml = $msgishtml;
  121. }
  122. // Detect images
  123. if ($this->msgishtml)
  124. {
  125. $this->html = $msg;
  126. $findimg = $this->findHtmlImages($conf->fckeditor->dir_output);
  127. // Define if there is at least one file
  128. if ($findimg)
  129. {
  130. foreach ($this->html_images as $i => $val)
  131. {
  132. if ($this->html_images[$i])
  133. {
  134. $this->atleastoneimage=1;
  135. dol_syslog("CMailFile::CMailfile: html_images[$i]['name']=".$this->html_images[$i]['name'], LOG_DEBUG);
  136. }
  137. }
  138. }
  139. }
  140. // Define if there is at least one file
  141. foreach ($filename_list as $i => $val)
  142. {
  143. if ($filename_list[$i])
  144. {
  145. $this->atleastonefile=1;
  146. dol_syslog("CMailFile::CMailfile: filename_list[$i]=".$filename_list[$i].", mimetype_list[$i]=".$mimetype_list[$i]." mimefilename_list[$i]=".$mimefilename_list[$i], LOG_DEBUG);
  147. }
  148. }
  149. // Add autocopy to
  150. if (! empty($conf->global->MAIN_MAIL_AUTOCOPY_TO)) $addr_bcc.=($addr_bcc?', ':'').$conf->global->MAIN_MAIL_AUTOCOPY_TO;
  151. // Action according to choosed sending method
  152. if ($conf->global->MAIN_MAIL_SENDMODE == 'mail')
  153. {
  154. // Use mail php function (default PHP method)
  155. // ------------------------------------------
  156. $smtp_headers = "";
  157. $mime_headers = "";
  158. $text_body = "";
  159. $files_encoded = "";
  160. // Define smtp_headers
  161. $this->subject = $subject;
  162. $this->addr_from = $from;
  163. $this->errors_to = $errors_to;
  164. $this->addr_to = $to;
  165. $this->addr_cc = $addr_cc;
  166. $this->addr_bcc = $addr_bcc;
  167. $this->deliveryreceipt = $deliveryreceipt;
  168. $smtp_headers = $this->write_smtpheaders();
  169. // Define mime_headers
  170. $mime_headers = $this->write_mimeheaders($filename_list, $mimefilename_list);
  171. if (! empty($this->html))
  172. {
  173. if (!empty($css))
  174. {
  175. $this->css = $css;
  176. $this->buildCSS(); // Build a css style (mode = all) into this->styleCSS and this->bodyCSS
  177. }
  178. $msg = $this->html;
  179. }
  180. // Define body in text_body
  181. $text_body = $this->write_body($msg);
  182. // Encode images
  183. if ($this->atleastoneimage)
  184. {
  185. $images_encoded = $this->write_images($this->images_encoded);
  186. // always end related and end alternative after inline images
  187. $images_encoded.= "--" . $this->related_boundary . "--" . $this->eol;
  188. $images_encoded.= $this->eol . "--" . $this->alternative_boundary . "--" . $this->eol;
  189. $images_encoded.= $this->eol;
  190. }
  191. // Add attachments to text_encoded
  192. if ($this->atleastonefile)
  193. {
  194. $files_encoded = $this->write_files($filename_list,$mimetype_list,$mimefilename_list);
  195. }
  196. // We now define $this->headers and $this->message
  197. $this->headers = $smtp_headers . $mime_headers;
  198. // On nettoie le header pour qu'il ne se termine pas par un retour chariot.
  199. // Ceci evite aussi les lignes vides en fin qui peuvent etre interpretees
  200. // comme des injections mail par les serveurs de messagerie.
  201. $this->headers = preg_replace("/([\r\n]+)$/i","",$this->headers);
  202. $this->message = 'This is a message with multiple parts in MIME format.'.$this->eol;
  203. $this->message.= $text_body . $images_encoded . $files_encoded;
  204. $this->message.= "--" . $this->mixed_boundary . "--" . $this->eol;
  205. }
  206. else if ($conf->global->MAIN_MAIL_SENDMODE == 'smtps')
  207. {
  208. // Use SMTPS library
  209. // ------------------------------------------
  210. require_once DOL_DOCUMENT_ROOT.'/core/class/smtps.class.php';
  211. $smtps = new SMTPs();
  212. $smtps->setCharSet($conf->file->character_set_client);
  213. $smtps->setSubject($this->encodetorfc2822($subject));
  214. $smtps->setTO($this->getValidAddress($to,0,1));
  215. $smtps->setFrom($this->getValidAddress($from,0,1));
  216. if (! empty($this->html))
  217. {
  218. if (!empty($css))
  219. {
  220. $this->css = $css;
  221. $this->styleCSS = $this->buildCSS();
  222. }
  223. $msg = $this->html;
  224. $msg = $this->checkIfHTML($msg);
  225. }
  226. if ($this->msgishtml) $smtps->setBodyContent($msg,'html');
  227. else $smtps->setBodyContent($msg,'plain');
  228. if ($this->atleastoneimage)
  229. {
  230. foreach ($this->images_encoded as $img)
  231. {
  232. $smtps->setImageInline($img['image_encoded'],$img['name'],$img['content_type'],$img['cid']);
  233. }
  234. }
  235. if ($this->atleastonefile)
  236. {
  237. foreach ($filename_list as $i => $val)
  238. {
  239. $content=file_get_contents($filename_list[$i]);
  240. $smtps->setAttachment($content,$mimefilename_list[$i],$mimetype_list[$i]);
  241. }
  242. }
  243. $smtps->setCC($addr_cc);
  244. $smtps->setBCC($addr_bcc);
  245. $smtps->setErrorsTo($errors_to);
  246. $smtps->setDeliveryReceipt($deliveryreceipt);
  247. $this->smtps=$smtps;
  248. }
  249. // TODO not stable, in progress
  250. else if ($conf->global->MAIN_MAIL_SENDMODE == 'phpmailer')
  251. {
  252. // Use PHPMailer library
  253. // ------------------------------------------
  254. require_once DOL_DOCUMENT_ROOT.'/includes/phpmailer/class.phpmailer.php';
  255. $this->phpmailer = new PHPMailer();
  256. $this->phpmailer->CharSet = $conf->file->character_set_client;
  257. $this->phpmailer->Subject($this->encodetorfc2822($subject));
  258. $this->phpmailer->setTO($this->getValidAddress($to,0,1));
  259. $this->phpmailer->SetFrom($this->getValidAddress($from,0,1));
  260. if (! empty($this->html))
  261. {
  262. if (!empty($css))
  263. {
  264. $this->css = $css;
  265. $this->styleCSS = $this->buildCSS();
  266. }
  267. $msg = $this->html;
  268. $msg = $this->checkIfHTML($msg);
  269. }
  270. if ($this->msgishtml) $smtps->setBodyContent($msg,'html');
  271. else $smtps->setBodyContent($msg,'plain');
  272. if ($this->atleastoneimage)
  273. {
  274. foreach ($this->images_encoded as $img)
  275. {
  276. $smtps->setImageInline($img['image_encoded'],$img['name'],$img['content_type'],$img['cid']);
  277. }
  278. }
  279. if ($this->atleastonefile)
  280. {
  281. foreach ($filename_list as $i => $val)
  282. {
  283. $content=file_get_contents($filename_list[$i]);
  284. $smtps->setAttachment($content,$mimefilename_list[$i],$mimetype_list[$i]);
  285. }
  286. }
  287. $smtps->setCC($addr_cc);
  288. $smtps->setBCC($addr_bcc);
  289. $smtps->setErrorsTo($errors_to);
  290. $smtps->setDeliveryReceipt($deliveryreceipt);
  291. $this->smtps=$smtps;
  292. }
  293. else
  294. {
  295. // Send mail method not correctly defined
  296. // --------------------------------------
  297. return 'Bad value for MAIN_MAIL_SENDMODE constant';
  298. }
  299. }
  300. /**
  301. * Send mail that was prepared by constructor
  302. *
  303. * @return boolean True if mail sent, false otherwise
  304. */
  305. function sendfile()
  306. {
  307. global $conf;
  308. $errorlevel=error_reporting();
  309. error_reporting($errorlevel ^ E_WARNING); // Desactive warnings
  310. $res=false;
  311. if (empty($conf->global->MAIN_DISABLE_ALL_MAILS))
  312. {
  313. // Action according to choosed sending method
  314. if ($conf->global->MAIN_MAIL_SENDMODE == 'mail')
  315. {
  316. // Use mail php function (default PHP method)
  317. // ------------------------------------------
  318. dol_syslog("CMailFile::sendfile addr_to=".$this->addr_to.", subject=".$this->subject, LOG_DEBUG);
  319. dol_syslog("CMailFile::sendfile header=\n".$this->headers, LOG_DEBUG);
  320. //dol_syslog("CMailFile::sendfile message=\n".$message);
  321. // If Windows, sendmail_from must be defined
  322. if (isset($_SERVER["WINDIR"]))
  323. {
  324. if (empty($this->addr_from)) $this->addr_from = 'robot@mydomain.com';
  325. @ini_set('sendmail_from',$this->getValidAddress($this->addr_from,2));
  326. }
  327. // Forcage parametres
  328. if (! empty($conf->global->MAIN_MAIL_SMTP_SERVER)) ini_set('SMTP',$conf->global->MAIN_MAIL_SMTP_SERVER);
  329. if (! empty($conf->global->MAIN_MAIL_SMTP_PORT)) ini_set('smtp_port',$conf->global->MAIN_MAIL_SMTP_PORT);
  330. $dest=$this->getValidAddress($this->addr_to,2);
  331. if (! $dest)
  332. {
  333. $this->error="Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."<br>Recipient address '$dest' invalid";
  334. dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
  335. }
  336. else
  337. {
  338. dol_syslog("CMailFile::sendfile: mail start HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port'), LOG_DEBUG);
  339. $bounce = ''; // By default
  340. if (! empty($conf->global->MAIN_MAIL_ALLOW_SENDMAIL_F))
  341. {
  342. // le return-path dans les header ne fonctionne pas avec tous les MTA
  343. // Le passage par -f est donc possible si la constante MAIN_MAIL_ALLOW_SENDMAIL_F est definie.
  344. // La variable definie pose des pb avec certains sendmail securisee (option -f refusee car dangereuse)
  345. $bounce .= ($bounce?' ':'').(! empty($conf->global->MAIN_MAIL_ERRORS_TO) ? '-f' . $conf->global->MAIN_MAIL_ERRORS_TO : ($this->addr_from != '' ? '-f' . $this->addr_from : '') );
  346. }
  347. 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
  348. {
  349. $bounce .= ($bounce?' ':'').'-ba';
  350. }
  351. $this->message=stripslashes($this->message);
  352. if (! empty($conf->global->MAIN_MAIL_DEBUG)) $this->dump_mail();
  353. if (! empty($bounce)) $res = mail($dest,$this->encodetorfc2822($this->subject),$this->message,$this->headers, $bounce);
  354. else $res = mail($dest,$this->encodetorfc2822($this->subject),$this->message,$this->headers);
  355. if (! $res)
  356. {
  357. $this->error="Failed to send mail with php mail to HOST=".ini_get('SMTP').", PORT=".ini_get('smtp_port')."<br>Check your server logs and your firewalls setup";
  358. dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
  359. }
  360. else
  361. {
  362. dol_syslog("CMailFile::sendfile: mail end success", LOG_DEBUG);
  363. }
  364. }
  365. if (isset($_SERVER["WINDIR"]))
  366. {
  367. @ini_restore('sendmail_from');
  368. }
  369. // Forcage parametres
  370. if (! empty($conf->global->MAIN_MAIL_SMTP_SERVER)) ini_restore('SMTP');
  371. if (! empty($conf->global->MAIN_MAIL_SMTP_PORT)) ini_restore('smtp_port');
  372. }
  373. else if ($conf->global->MAIN_MAIL_SENDMODE == 'smtps')
  374. {
  375. // Use SMTPS library
  376. // ------------------------------------------
  377. $this->smtps->setTransportType(0); // Only this method is coded in SMTPs library
  378. // Forcage parametres
  379. if (empty($conf->global->MAIN_MAIL_SMTP_SERVER)) $conf->global->MAIN_MAIL_SMTP_SERVER=ini_get('SMTP');
  380. if (empty($conf->global->MAIN_MAIL_SMTP_PORT)) $conf->global->MAIN_MAIL_SMTP_PORT=ini_get('smtp_port');
  381. // If we use SSL/TLS
  382. $server=$conf->global->MAIN_MAIL_SMTP_SERVER;
  383. if (! empty($conf->global->MAIN_MAIL_EMAIL_TLS) && function_exists('openssl_open')) $server='ssl://'.$server;
  384. $this->smtps->setHost($server);
  385. $this->smtps->setPort($conf->global->MAIN_MAIL_SMTP_PORT); // 25, 465...;
  386. if (! empty($conf->global->MAIN_MAIL_SMTPS_ID)) $this->smtps->setID($conf->global->MAIN_MAIL_SMTPS_ID);
  387. if (! empty($conf->global->MAIN_MAIL_SMTPS_PW)) $this->smtps->setPW($conf->global->MAIN_MAIL_SMTPS_PW);
  388. //$smtps->_msgReplyTo = 'reply@web.com';
  389. $res=true;
  390. $from=$this->smtps->getFrom('org');
  391. if (! $from)
  392. {
  393. $this->error="Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->MAIN_MAIL_SMTP_PORT."<br>Sender address '$from' invalid";
  394. dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
  395. $res=false;
  396. }
  397. $dest=$this->smtps->getTo();
  398. if (! $dest)
  399. {
  400. $this->error="Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->MAIN_MAIL_SMTP_PORT."<br>Recipient address '$dest' invalid";
  401. dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
  402. $res=false;
  403. }
  404. if ($res)
  405. {
  406. if (! empty($conf->global->MAIN_MAIL_DEBUG)) $this->smtps->setDebug(true);
  407. $result=$this->smtps->sendMsg();
  408. //print $result;
  409. if (! empty($conf->global->MAIN_MAIL_DEBUG)) $this->dump_mail();
  410. $result=$this->smtps->getErrors();
  411. if (empty($this->error) && empty($result)) $res=true;
  412. else
  413. {
  414. if (empty($this->error)) $this->error=$result;
  415. dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
  416. $res=false;
  417. }
  418. }
  419. }
  420. else
  421. {
  422. // Send mail method not correctly defined
  423. // --------------------------------------
  424. return 'Bad value for MAIN_MAIL_SENDMODE constant';
  425. }
  426. }
  427. else
  428. {
  429. $this->error='No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS';
  430. dol_syslog("CMailFile::sendfile: ".$this->error, LOG_WARNING);
  431. }
  432. error_reporting($errorlevel); // Reactive niveau erreur origine
  433. return $res;
  434. }
  435. // Encode subject according to RFC 2822 - http://en.wikipedia.org/wiki/MIME#Encoded-Word
  436. function encodetorfc2822($stringtoencode)
  437. {
  438. global $conf;
  439. return '=?'.$conf->file->character_set_client.'?B?'.base64_encode($stringtoencode).'?=';
  440. }
  441. /**
  442. * Read a file on disk and return encoded content for emails (mode = 'mail')
  443. *
  444. * @param string $sourcefile Path to file to encode
  445. * @return int <0 if KO, encoded string if OK
  446. */
  447. function _encode_file($sourcefile)
  448. {
  449. $newsourcefile=dol_osencode($sourcefile);
  450. if (is_readable($newsourcefile))
  451. {
  452. $contents = file_get_contents($newsourcefile); // Need PHP 4.3
  453. $encoded = chunk_split(base64_encode($contents), 76, $this->eol); // 76 max is defined into http://tools.ietf.org/html/rfc2047
  454. return $encoded;
  455. }
  456. else
  457. {
  458. $this->error="Error: Can't read file '".$sourcefile."' into _encode_file";
  459. dol_syslog("CMailFile::encode_file: ".$this->error, LOG_ERR);
  460. return -1;
  461. }
  462. }
  463. /**
  464. * Write content of a SMTP request into a dump file (mode = all)
  465. * Used for debugging.
  466. *
  467. * @return void
  468. */
  469. function dump_mail()
  470. {
  471. global $conf,$dolibarr_main_data_root;
  472. if (@is_writeable($dolibarr_main_data_root)) // Avoid fatal error on fopen with open_basedir
  473. {
  474. $outputfile=$dolibarr_main_data_root."/dolibarr_mail.log";
  475. $fp = fopen($outputfile,"w");
  476. if ($conf->global->MAIN_MAIL_SENDMODE == 'mail')
  477. {
  478. fputs($fp, $this->headers);
  479. fputs($fp, $this->eol); // This eol is added by the mail function, so we add it in log
  480. fputs($fp, $this->message);
  481. }
  482. elseif ($conf->global->MAIN_MAIL_SENDMODE == 'smtps')
  483. {
  484. fputs($fp, $this->smtps->log);
  485. }
  486. fclose($fp);
  487. if (! empty($conf->global->MAIN_UMASK))
  488. @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
  489. }
  490. }
  491. /**
  492. * Correct an uncomplete html string
  493. *
  494. * @param string $msg String
  495. * @return string Completed string
  496. */
  497. function checkIfHTML($msg)
  498. {
  499. if (!preg_match('/^[\s\t]*<html/i',$msg))
  500. {
  501. $out = "<html><head><title></title>";
  502. if (!empty($this->styleCSS)) $out.= $this->styleCSS;
  503. $out.= "</head><body";
  504. if (!empty($this->bodyCSS)) $out.= $this->bodyCSS;
  505. $out.= ">";
  506. $out.= $msg;
  507. $out.= "</body></html>";
  508. }
  509. else
  510. {
  511. $out = $msg;
  512. }
  513. return $out;
  514. }
  515. /**
  516. * Build a css style (mode = all) into this->styleCSS and this->bodyCSS
  517. *
  518. * @return css
  519. */
  520. function buildCSS()
  521. {
  522. if (! empty($this->css))
  523. {
  524. // Style CSS
  525. $this->styleCSS = '<style type="text/css">';
  526. $this->styleCSS.= 'body {';
  527. if ($this->css['bgcolor'])
  528. {
  529. $this->styleCSS.= ' background-color: '.$this->css['bgcolor'].';';
  530. $this->bodyCSS.= ' BGCOLOR="'.$this->css['bgcolor'].'"';
  531. }
  532. if ($this->css['bgimage'])
  533. {
  534. // TODO recuperer cid
  535. $this->styleCSS.= ' background-image: url("cid:'.$this->css['bgimage_cid'].'");';
  536. }
  537. $this->styleCSS.= '}';
  538. $this->styleCSS.= '</style>';
  539. }
  540. }
  541. /**
  542. * Create SMTP headers (mode = 'mail')
  543. *
  544. * @return smtp headers
  545. */
  546. function write_smtpheaders()
  547. {
  548. global $conf;
  549. $out = "";
  550. $host = dol_getprefix();
  551. // Sender
  552. //$out.= "Sender: ".getValidAddress($this->addr_from,2)).$this->eol2;
  553. $out.= "From: ".$this->getValidAddress($this->addr_from,3,1).$this->eol2;
  554. $out.= "Return-Path: ".$this->getValidAddress($this->addr_from,0,1).$this->eol2;
  555. if (isset($this->reply_to) && $this->reply_to) $out.= "Reply-To: ".$this->getValidAddress($this->reply_to,2).$this->eol2;
  556. if (isset($this->errors_to) && $this->errors_to) $out.= "Errors-To: ".$this->getValidAddress($this->errors_to,2).$this->eol2;
  557. // Receiver
  558. if (isset($this->addr_cc) && $this->addr_cc) $out.= "Cc: ".$this->getValidAddress($this->addr_cc,2).$this->eol2;
  559. if (isset($this->addr_bcc) && $this->addr_bcc) $out.= "Bcc: ".$this->getValidAddress($this->addr_bcc,2).$this->eol2;
  560. // Delivery receipt
  561. if (isset($this->deliveryreceipt) && $this->deliveryreceipt == 1) $out.= "Disposition-Notification-To: ".$this->getValidAddress($this->addr_from,2).$this->eol2;
  562. //$out.= "X-Priority: 3".$this->eol2;
  563. $out.= 'Date: ' . date("r") . $this->eol2;
  564. $out.= 'Message-ID: <' . time() . '.phpmail@' . $host . ">" . $this->eol2;
  565. $out.= "X-Mailer: Dolibarr version " . DOL_VERSION ." (using php mail)".$this->eol2;
  566. $out.= "Mime-Version: 1.0".$this->eol2;
  567. //$out.= "From: ".$this->getValidAddress($this->addr_from,3,1).$this->eol;
  568. $out.= "Content-Type: multipart/mixed; boundary=\"".$this->mixed_boundary."\"".$this->eol2;
  569. $out.= "Content-Transfer-Encoding: 8bit".$this->eol2;
  570. dol_syslog("CMailFile::write_smtpheaders smtp_header=\n".$out);
  571. return $out;
  572. }
  573. /**
  574. * Create header MIME (mode = 'mail')
  575. *
  576. * @param array $filename_list Array of filenames
  577. * @param array $mimefilename_list Array of mime types
  578. * @return array mime headers
  579. */
  580. function write_mimeheaders($filename_list, $mimefilename_list)
  581. {
  582. $mimedone=0;
  583. $out = "";
  584. if ($filename_list)
  585. {
  586. $filename_list_size=count($filename_list);
  587. for($i=0;$i < $filename_list_size;$i++)
  588. {
  589. if ($filename_list[$i])
  590. {
  591. if ($mimefilename_list[$i]) $filename_list[$i] = $mimefilename_list[$i];
  592. $out.= "X-attachments: $filename_list[$i]".$this->eol2;
  593. }
  594. }
  595. }
  596. dol_syslog("CMailFile::write_mimeheaders mime_header=\n".$out, LOG_DEBUG);
  597. return $out;
  598. }
  599. /**
  600. * Return email content (mode = 'mail')
  601. *
  602. * @param string $msgtext Message string
  603. * @return string String content
  604. */
  605. function write_body($msgtext)
  606. {
  607. global $conf;
  608. $out='';
  609. $out.= "--" . $this->mixed_boundary . $this->eol;
  610. if ($this->atleastoneimage)
  611. {
  612. $out.= "Content-Type: multipart/alternative; boundary=\"".$this->alternative_boundary."\"".$this->eol;
  613. $out.= $this->eol;
  614. $out.= "--" . $this->alternative_boundary . $this->eol;
  615. }
  616. if ($this->msgishtml)
  617. {
  618. // Check if html header already in message
  619. $strContent = $this->checkIfHTML($msgtext);
  620. }
  621. else
  622. {
  623. $strContent.= $msgtext;
  624. }
  625. // Make RFC821 Compliant, replace bare linefeeds
  626. $strContent = preg_replace("/(?<!\r)\n/si", "\r\n", $strContent);
  627. //$strContent = rtrim(chunk_split($strContent)); // Function chunck_split seems bugged
  628. $strContent = rtrim(wordwrap($strContent));
  629. if ($this->msgishtml)
  630. {
  631. if ($this->atleastoneimage)
  632. {
  633. $out.= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol;
  634. $out.= $this->eol.strip_tags($strContent).$this->eol; // Add plain text message
  635. $out.= "--" . $this->alternative_boundary . $this->eol;
  636. $out.= "Content-Type: multipart/related; boundary=\"".$this->related_boundary."\"".$this->eol;
  637. $out.= $this->eol;
  638. $out.= "--" . $this->related_boundary . $this->eol;
  639. }
  640. $out.= "Content-Type: text/html; charset=".$conf->file->character_set_client.$this->eol;
  641. $out.= $this->eol.$strContent.$this->eol;
  642. }
  643. else
  644. {
  645. $out.= "Content-Type: text/plain; charset=".$conf->file->character_set_client.$this->eol;
  646. $out.= $this->eol.$strContent.$this->eol;
  647. }
  648. $out.= $this->eol;
  649. return $out;
  650. }
  651. /**
  652. * Attach file to email (mode = 'mail')
  653. *
  654. * @param array $filename_list Tableau
  655. * @param array $mimetype_list Tableau
  656. * @param array $mimefilename_list Tableau
  657. * @return string Chaine fichiers encodes
  658. */
  659. function write_files($filename_list,$mimetype_list,$mimefilename_list)
  660. {
  661. $out = '';
  662. $filename_list_size=count($filename_list);
  663. for($i=0;$i < $filename_list_size;$i++)
  664. {
  665. if ($filename_list[$i])
  666. {
  667. dol_syslog("CMailFile::write_files: i=$i");
  668. $encoded = $this->_encode_file($filename_list[$i]);
  669. if ($encoded >= 0)
  670. {
  671. if ($mimefilename_list[$i]) $filename_list[$i] = $mimefilename_list[$i];
  672. if (! $mimetype_list[$i]) { $mimetype_list[$i] = "application/octet-stream"; }
  673. $out.= "--" . $this->mixed_boundary . $this->eol;
  674. $out.= "Content-Disposition: attachment; filename=\"".$filename_list[$i]."\"".$this->eol;
  675. $out.= "Content-Type: " . $mimetype_list[$i] . "; name=\"".$filename_list[$i]."\"".$this->eol;
  676. $out.= "Content-Transfer-Encoding: base64".$this->eol;
  677. $out.= "Content-Description: File Attachment".$this->eol;
  678. $out.= $this->eol;
  679. $out.= $encoded;
  680. $out.= $this->eol;
  681. //$out.= $this->eol;
  682. }
  683. else
  684. {
  685. return $encoded;
  686. }
  687. }
  688. }
  689. return $out;
  690. }
  691. /**
  692. * Attach an image to email (mode = 'mail')
  693. *
  694. * @param array $images_list Tableau
  695. * @return string Chaine images encodees
  696. */
  697. function write_images($images_list)
  698. {
  699. $out = '';
  700. if ($images_list)
  701. {
  702. foreach ($images_list as $img)
  703. {
  704. dol_syslog("CMailFile::write_images: i=$i");
  705. $out.= "--" . $this->related_boundary . $this->eol; // always related for an inline image
  706. $out.= "Content-Type: " . $img["content_type"] . "; name=\"".$img["name"]."\"".$this->eol;
  707. $out.= "Content-Transfer-Encoding: base64".$this->eol;
  708. $out.= "Content-Disposition: inline; filename=\"".$img["name"]."\"".$this->eol;
  709. $out.= "Content-ID: <".$img["cid"].">".$this->eol;
  710. $out.= $this->eol;
  711. $out.= $img["image_encoded"];
  712. $out.= $this->eol;
  713. }
  714. }
  715. return $out;
  716. }
  717. /**
  718. * Try to create a socket connection
  719. *
  720. * @param string $host Add ssl:// for SSL/TLS.
  721. * @param int $port Example: 25, 465
  722. * @return int Socket id if ok, 0 if KO
  723. */
  724. function check_server_port($host,$port)
  725. {
  726. $_retVal=0;
  727. $timeout=5; // Timeout in seconds
  728. if (function_exists('fsockopen'))
  729. {
  730. dol_syslog("Try socket connection to host=".$host." port=".$port);
  731. //See if we can connect to the SMTP server
  732. if ($socket = @fsockopen(
  733. $host, // Host to test, IP or domain. Add ssl:// for SSL/TLS.
  734. $port, // which Port number to use
  735. $errno, // actual system level error
  736. $errstr, // and any text that goes with the error
  737. $timeout
  738. )) // timeout for reading/writing data over the socket
  739. {
  740. // Windows still does not have support for this timeout function
  741. if (function_exists('stream_set_timeout')) stream_set_timeout($socket, $timeout, 0);
  742. dol_syslog("Now we wait for answer 220");
  743. // Check response from Server
  744. if ( $_retVal = $this->server_parse($socket, "220") ) $_retVal = $socket;
  745. }
  746. else
  747. {
  748. $this->error = utf8_check('Error '.$errno.' - '.$errstr)?'Error '.$errno.' - '.$errstr:utf8_encode('Error '.$errno.' - '.$errstr);
  749. }
  750. }
  751. return $_retVal;
  752. }
  753. /**
  754. * This function has been modified as provided by SirSir to allow multiline responses when
  755. * using SMTP Extensions.
  756. *
  757. * @param Socket $socket Socket
  758. * @param string $response Response string
  759. * @return boolean true if success
  760. */
  761. function server_parse($socket, $response)
  762. {
  763. $_retVal = true; // Indicates if Object was created or not
  764. $server_response = '';
  765. while ( substr($server_response,3,1) != ' ' )
  766. {
  767. if( !( $server_response = fgets($socket, 256) ) )
  768. {
  769. $this->error="Couldn't get mail server response codes";
  770. $_retVal = false;
  771. }
  772. }
  773. if( !( substr($server_response, 0, 3) == $response ) )
  774. {
  775. $this->error="Ran into problems sending Mail.\r\nResponse: $server_response";
  776. $_retVal = false;
  777. }
  778. return $_retVal;
  779. }
  780. /**
  781. * Seearch images into html message and init array this->images_encoded if found
  782. *
  783. * @param string $images_dir Location of physical images files
  784. * @return int >0 if OK, <0 if KO
  785. */
  786. function findHtmlImages($images_dir)
  787. {
  788. // Build the list of image extensions
  789. $extensions = array_keys($this->image_types);
  790. preg_match_all('/(?:"|\')([^"\']+\.('.implode('|', $extensions).'))(?:"|\')/Ui', $this->html, $matches);
  791. if ($matches)
  792. {
  793. $i=0;
  794. foreach ($matches[1] as $full)
  795. {
  796. if (preg_match('/file=([A-Za-z0-9_\-\/]+[\.]?[A-Za-z0-9]+)?$/i',$full,$regs))
  797. {
  798. $img = $regs[1];
  799. if (file_exists($images_dir.'/'.$img))
  800. {
  801. // Image path in src
  802. $src = preg_quote($full,'/');
  803. // Image full path
  804. $this->html_images[$i]["fullpath"] = $images_dir.'/'.$img;
  805. // Image name
  806. $this->html_images[$i]["name"] = $img;
  807. // Content type
  808. $ext = preg_replace('/^.*\.(\w{3,4})$/e', 'strtolower("$1")', $img);
  809. $this->html_images[$i]["content_type"] = $this->image_types[$ext];
  810. // cid
  811. $this->html_images[$i]["cid"] = dol_hash(uniqid(time()));
  812. $this->html = preg_replace("/src=\"$src\"|src='$src'/i", "src=\"cid:".$this->html_images[$i]["cid"]."\"", $this->html);
  813. }
  814. $i++;
  815. }
  816. }
  817. if (!empty($this->html_images))
  818. {
  819. $inline = array();
  820. $i=0;
  821. foreach ($this->html_images as $img)
  822. {
  823. $fullpath = $images_dir.'/'.$img["name"];
  824. // If duplicate images are embedded, they may show up as attachments, so remove them.
  825. if (!in_array($fullpath,$inline))
  826. {
  827. // Read image file
  828. if ($image = file_get_contents($fullpath))
  829. {
  830. // On garde que le nom de l'image
  831. preg_match('/([A-Za-z0-9_-]+[\.]?[A-Za-z0-9]+)?$/i',$img["name"],$regs);
  832. $imgName = $regs[1];
  833. $this->images_encoded[$i]['name'] = $imgName;
  834. $this->images_encoded[$i]['content_type'] = $img["content_type"];
  835. $this->images_encoded[$i]['cid'] = $img["cid"];
  836. // Encodage de l'image
  837. $this->images_encoded[$i]["image_encoded"] = chunk_split(base64_encode($image), 68, $this->eol);
  838. $inline[] = $fullpath;
  839. }
  840. }
  841. $i++;
  842. }
  843. }
  844. else
  845. {
  846. return -1;
  847. }
  848. return 1;
  849. }
  850. else
  851. {
  852. return 0;
  853. }
  854. }
  855. /**
  856. * Return an address for SMTP protocol
  857. *
  858. * @param string $adresses Example: 'John Doe <john@doe.com>' or 'john@doe.com'
  859. * @param int $format 0=auto, 1=emails with <>, 2=emails without <>, 3=auto + label between "
  860. * @param int $encode 1=Encode name to RFC2822
  861. * @return string If format 0: '<john@doe.com>' or 'John Doe <john@doe.com>' or '=?UTF-8?B?Sm9obiBEb2U=?= <john@doe.com>'
  862. * If format 1: '<john@doe.com>'
  863. * If format 2: 'john@doe.com'
  864. * If format 3: '<john@doe.com>' or '"John Doe" <john@doe.com>' or '"=?UTF-8?B?Sm9obiBEb2U=?=" <john@doe.com>'
  865. */
  866. function getValidAddress($adresses,$format,$encode='')
  867. {
  868. global $conf;
  869. $ret='';
  870. $arrayaddress=explode(',',$adresses);
  871. // Boucle sur chaque composant de l'adresse
  872. foreach($arrayaddress as $val)
  873. {
  874. if (preg_match('/^(.*)<(.*)>$/i',trim($val),$regs))
  875. {
  876. $name = trim($regs[1]);
  877. $email = trim($regs[2]);
  878. }
  879. else
  880. {
  881. $name = '';
  882. $email = trim($val);
  883. }
  884. if ($email)
  885. {
  886. $newemail='';
  887. if ($format == 2)
  888. {
  889. $newemail=$email;
  890. }
  891. if ($format == 1 || $format == 3)
  892. {
  893. $newemail='<'.$email.'>';
  894. }
  895. if ($format == 0 || $format == 3)
  896. {
  897. if (! empty($conf->global->MAIN_MAIL_NO_FULL_EMAIL)) $newemail='<'.$email.'>';
  898. elseif (! $name) $newemail='<'.$email.'>';
  899. else $newemail=($format==3?'"':'').($encode?$this->encodetorfc2822($name):$name).($format==3?'"':'').' <'.$email.'>';
  900. }
  901. $ret=($ret ? $ret.',' : '').$newemail;
  902. }
  903. }
  904. return $ret;
  905. }
  906. }
  907. ?>