CMailFile.class.php 61 KB

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