smtps.class.php 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213
  1. <?php
  2. /*
  3. * Copyright (C) Walter Torres <walter@torres.ws> [with a *lot* of help!]
  4. * Copyright (C) 2005-2015 Laurent Destailleur <eldy@users.sourceforge.net>
  5. * Copyright (C) 2006-2011 Regis Houssin
  6. * Copyright (C) 2016 Jonathan TISSEAU <jonathan.tisseau@86dev.fr>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. */
  21. /**
  22. * \file htdocs/core/class/smtps.class.php
  23. * \brief Class to construct and send SMTP compliant email, even to a secure
  24. * SMTP server, regardless of platform.
  25. * Goals:
  26. * - mime compliant
  27. * - multiple Reciptiants
  28. * - TO
  29. * - CC
  30. * - BCC
  31. * - multi-part message
  32. * - plain text
  33. * - HTML
  34. * - inline attachments
  35. * - attachments
  36. * - GPG access
  37. * This Class is based off of 'SMTP PHP MAIL' by Dirk Paehl, http://www.paehl.de
  38. */
  39. /**
  40. * Class to construct and send SMTP compliant email, even
  41. * to a secure SMTP server, regardless of platform.
  42. */
  43. class SMTPs
  44. {
  45. /**
  46. * Host Name or IP of SMTP Server to use
  47. */
  48. private $_smtpsHost = 'localhost';
  49. /**
  50. * SMTP Server Port definition. 25 is default value
  51. * This can be defined via a INI file or via a setter method
  52. */
  53. private $_smtpsPort = '25';
  54. /**
  55. * Secure SMTP Server access ID
  56. * This can be defined via a INI file or via a setter method
  57. */
  58. private $_smtpsID = null;
  59. /**
  60. * Secure SMTP Server access Password
  61. * This can be defined via a INI file or via a setter method
  62. */
  63. private $_smtpsPW = null;
  64. /**
  65. * Token in case we use OAUTH2
  66. */
  67. private $_smtpsToken = null;
  68. /**
  69. * Who sent the Message
  70. * This can be defined via a INI file or via a setter method
  71. */
  72. private $_msgFrom = null;
  73. /**
  74. * Where are replies and errors to be sent to
  75. * This can be defined via a INI file or via a setter method
  76. */
  77. private $_msgReplyTo = null;
  78. /**
  79. * Who will the Message be sent to; TO, CC, BCC
  80. * Multi-diminsional array containg addresses the message will
  81. * be sent TO, CC or BCC
  82. */
  83. private $_msgRecipients = null;
  84. /**
  85. * Message Subject
  86. */
  87. private $_msgSubject = null;
  88. /**
  89. * Message Content
  90. *
  91. * @var array $_msgContent Array of messages
  92. */
  93. private $_msgContent = array();
  94. /**
  95. * Custom X-Headers
  96. */
  97. private $_msgXheader = null;
  98. /**
  99. * Character set
  100. * Defaulted to 'iso-8859-1'
  101. */
  102. private $_smtpsCharSet = 'iso-8859-1';
  103. /**
  104. * Message Sensitivity
  105. * Defaults to ZERO - None
  106. */
  107. private $_msgSensitivity = 0;
  108. /**
  109. * Message Sensitivity
  110. */
  111. private $_arySensitivity = array(false,
  112. 'Personal',
  113. 'Private',
  114. 'Company Confidential');
  115. /**
  116. * Message Sensitivity
  117. * Defaults to 3 - Normal
  118. */
  119. private $_msgPriority = 3;
  120. /**
  121. * Message Priority
  122. */
  123. private $_aryPriority = array('Bulk',
  124. 'Highest',
  125. 'High',
  126. 'Normal',
  127. 'Low',
  128. 'Lowest');
  129. /**
  130. * Content-Transfer-Encoding
  131. * Defaulted to 0 - 7bit
  132. */
  133. private $_smtpsTransEncodeType = 0;
  134. /**
  135. * Content-Transfer-Encoding
  136. */
  137. private $_smtpsTransEncodeTypes = array('7bit', // Simple 7-bit ASCII
  138. '8bit', // 8-bit coding with line termination characters
  139. 'base64', // 3 octets encoded into 4 sextets with offset
  140. 'binary', // Arbitrary binary stream
  141. 'mac-binhex40', // Macintosh binary to hex encoding
  142. 'quoted-printable', // Mostly 7-bit, with 8-bit characters encoded as "=HH"
  143. 'uuencode'); // UUENCODE encoding
  144. /**
  145. * Content-Transfer-Encoding
  146. * Defaulted to '7bit'
  147. */
  148. private $_smtpsTransEncode = '7bit';
  149. /**
  150. * Boundary String for MIME seperation
  151. */
  152. private $_smtpsBoundary = null;
  153. /**
  154. * Related Boundary
  155. */
  156. private $_smtpsRelatedBoundary = null;
  157. /**
  158. * Alternative Boundary
  159. */
  160. private $_smtpsAlternativeBoundary = null;
  161. /**
  162. * Determines the method inwhich the message are to be sent.
  163. * - 'sockets' [0] - conect via network to SMTP server - default
  164. * - 'pipe [1] - use UNIX path to EXE
  165. * - 'phpmail [2] - use the PHP built-in mail function
  166. * NOTE: Only 'sockets' is implemented
  167. */
  168. private $_transportType = 0;
  169. /**
  170. * If '$_transportType' is set to '1', then this variable is used
  171. * to define the UNIX file system path to the sendmail execuable
  172. */
  173. private $_mailPath = '/usr/lib/sendmail';
  174. /**
  175. * Sets the SMTP server timeout in seconds.
  176. */
  177. private $_smtpTimeout = 10;
  178. /**
  179. * Determines whether to calculate message MD5 checksum.
  180. */
  181. private $_smtpMD5 = false;
  182. /**
  183. * Class error codes and messages
  184. */
  185. private $_smtpsErrors = null;
  186. /**
  187. * Defines log level
  188. * 0 - no logging
  189. * 1 - connectivity logging
  190. * 2 - message generation logging
  191. * 3 - detail logging
  192. */
  193. private $_log_level = 0;
  194. /**
  195. * Place Class in" debug" mode
  196. */
  197. private $_debug = false;
  198. // @CHANGE LDR
  199. public $log = '';
  200. public $lastretval = '';
  201. /**
  202. * @var resource
  203. */
  204. public $socket;
  205. /**
  206. * @var int
  207. */
  208. public $errno;
  209. /**
  210. * @var string
  211. */
  212. public $errstr;
  213. private $_errorsTo = '';
  214. private $_deliveryReceipt = 0;
  215. private $_trackId = '';
  216. private $_moreinheader = '';
  217. /**
  218. * An array of options for stream_context_create()
  219. */
  220. private $_options = array();
  221. /**
  222. * Set delivery receipt
  223. *
  224. * @param array $_options An array of options for stream_context_create()
  225. * @return void
  226. */
  227. public function setOptions($_options = array())
  228. {
  229. $this->_options = $_options;
  230. }
  231. /**
  232. * Set delivery receipt
  233. *
  234. * @param int $_val Value
  235. * @return void
  236. */
  237. public function setDeliveryReceipt($_val = 0)
  238. {
  239. $this->_deliveryReceipt = $_val;
  240. }
  241. /**
  242. * get delivery receipt
  243. *
  244. * @return int Delivery receipt
  245. */
  246. public function getDeliveryReceipt()
  247. {
  248. return $this->_deliveryReceipt;
  249. }
  250. /**
  251. * Set trackid
  252. *
  253. * @param string $_val Value
  254. * @return void
  255. */
  256. public function setTrackId($_val = '')
  257. {
  258. $this->_trackId = $_val;
  259. }
  260. /**
  261. * Set moreInHeader
  262. *
  263. * @param string $_val Value
  264. * @return void
  265. */
  266. public function setMoreInHeader($_val = '')
  267. {
  268. $this->_moreinheader = $_val;
  269. }
  270. /**
  271. * get trackid
  272. *
  273. * @return string Track id
  274. */
  275. public function getTrackId()
  276. {
  277. return $this->_trackId;
  278. }
  279. /**
  280. * get moreInHeader
  281. *
  282. * @return string moreInHeader
  283. */
  284. public function getMoreInHeader()
  285. {
  286. return $this->_moreinheader;
  287. }
  288. /**
  289. * Set errors to
  290. *
  291. * @param string $_strErrorsTo Errors to
  292. * @return void
  293. */
  294. public function setErrorsTo($_strErrorsTo)
  295. {
  296. if ($_strErrorsTo) {
  297. $this->_errorsTo = $this->_strip_email($_strErrorsTo);
  298. }
  299. }
  300. /**
  301. * Get errors to
  302. *
  303. * @param boolean $_part Variant
  304. * @return string Errors to
  305. */
  306. public function getErrorsTo($_part = true)
  307. {
  308. $_retValue = '';
  309. if ($_part === true) {
  310. $_retValue = $this->_errorsTo;
  311. } else {
  312. $_retValue = $this->_errorsTo[$_part];
  313. }
  314. return $_retValue;
  315. }
  316. /**
  317. * Set debug
  318. *
  319. * @param boolean $_vDebug Value for debug
  320. * @return void
  321. */
  322. public function setDebug($_vDebug = false)
  323. {
  324. $this->_debug = $_vDebug;
  325. }
  326. /**
  327. * build RECIPIENT List, all addresses who will recieve this message
  328. *
  329. * @return void
  330. */
  331. public function buildRCPTlist()
  332. {
  333. // Pull TO list
  334. $_aryToList = $this->getTO();
  335. }
  336. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  337. /**
  338. * Attempt a connection to mail server
  339. *
  340. * @return mixed $_retVal Boolean indicating success or failure on connection
  341. */
  342. private function _server_connect()
  343. {
  344. // phpcs:enable
  345. // Default return value
  346. $_retVal = true;
  347. // We have to make sure the HOST given is valid
  348. // This is done here because '@fsockopen' will not give me this
  349. // information if it failes to connect because it can't find the HOST
  350. $host = $this->getHost();
  351. $usetls = preg_match('@tls://@i', $host);
  352. $host = preg_replace('@tcp://@i', '', $host); // Remove prefix
  353. $host = preg_replace('@ssl://@i', '', $host); // Remove prefix
  354. $host = preg_replace('@tls://@i', '', $host); // Remove prefix
  355. // @CHANGE LDR
  356. include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
  357. if ((!is_ip($host)) && ((gethostbyname($host)) == $host)) {
  358. $this->_setErr(99, $host.' is either offline or is an invalid host name.');
  359. $_retVal = false;
  360. } else {
  361. if (function_exists('stream_socket_client') && !empty($this->_options)) {
  362. $socket_context = stream_context_create($this->_options); // An array of options for stream_context_create()
  363. $this->socket = @stream_socket_client(
  364. preg_replace('@tls://@i', '', $this->getHost()).// Host to 'hit', IP or domain
  365. ':'.$this->getPort(), // which Port number to use
  366. $this->errno, // actual system level error
  367. $this->errstr, // and any text that goes with the error
  368. $this->_smtpTimeout, // timeout for reading/writing data over the socket
  369. STREAM_CLIENT_CONNECT,
  370. $socket_context // Options for connection
  371. );
  372. } else {
  373. $this->socket = @fsockopen(
  374. preg_replace('@tls://@i', '', $this->getHost()), // Host to 'hit', IP or domain
  375. $this->getPort(), // which Port number to use
  376. $this->errno, // actual system level error
  377. $this->errstr, // and any text that goes with the error
  378. $this->_smtpTimeout // timeout for reading/writing data over the socket
  379. );
  380. }
  381. //See if we can connect to the SMTP server
  382. if (is_resource($this->socket)) {
  383. // Fix from PHP SMTP class by 'Chris Ryan'
  384. // Sometimes the SMTP server takes a little longer to respond
  385. // so we will give it a longer timeout for the first read
  386. // Windows still does not have support for this timeout function
  387. if (function_exists('stream_set_timeout')) {
  388. stream_set_timeout($this->socket, $this->_smtpTimeout, 0);
  389. }
  390. // Check response from Server
  391. if ($_retVal = $this->server_parse($this->socket, "220")) {
  392. $_retVal = $this->socket;
  393. }
  394. } else {
  395. // This connection attempt failed.
  396. // @CHANGE LDR
  397. if (empty($this->errstr)) {
  398. $this->errstr = 'Failed to connect with fsockopen host='.$this->getHost().' port='.$this->getPort();
  399. }
  400. $this->_setErr($this->errno, $this->errstr);
  401. $_retVal = false;
  402. }
  403. }
  404. return $_retVal;
  405. }
  406. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  407. /**
  408. * Attempt mail server authentication for a secure connection
  409. *
  410. * @return boolean|null $_retVal Boolean indicating success or failure of authentication
  411. */
  412. private function _server_authenticate()
  413. {
  414. // phpcs:enable
  415. global $conf;
  416. require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
  417. // Send the RFC2554 specified EHLO.
  418. // This improvment as provided by 'SirSir' to
  419. // accomodate both SMTP AND ESMTP capable servers
  420. $host = $this->getHost();
  421. $usetls = preg_match('@tls://@i', $host);
  422. $host = preg_replace('@tcp://@i', '', $host); // Remove prefix
  423. $host = preg_replace('@ssl://@i', '', $host); // Remove prefix
  424. $host = preg_replace('@tls://@i', '', $host); // Remove prefix
  425. if ($usetls && !empty($conf->global->MAIN_SMTPS_ADD_TLS_TO_HOST_FOR_HELO)) {
  426. $host = 'tls://'.$host;
  427. }
  428. $hosth = $host; // so for example 'localhost' or 'smtp-relay.gmail.com'
  429. if (!empty($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO)) {
  430. if (!is_numeric($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO)) {
  431. // If value of MAIL_SMTP_USE_FROM_FOR_HELO is a string, we use it as domain name
  432. $hosth = $conf->global->MAIL_SMTP_USE_FROM_FOR_HELO;
  433. } elseif ($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO == 1) {
  434. // If value of MAIL_SMTP_USE_FROM_FOR_HELO is 1, we use the domain in the from.
  435. // So if the from to is 'aaa <bbb@ccc.com>', we will keep 'ccc.com'
  436. $hosth = $this->getFrom('addr');
  437. $hosth = preg_replace('/^.*</', '', $hosth);
  438. $hosth = preg_replace('/>.*$/', '', $hosth);
  439. $hosth = preg_replace('/.*@/', '', $hosth);
  440. } elseif ($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO == 2) {
  441. // If value of MAIL_SMTP_USE_FROM_FOR_HELO is 2, we use the domain in the $dolibarr_main_url_root.
  442. global $dolibarr_main_url_root;
  443. $hosth = getDomainFromURL($dolibarr_main_url_root, 1);
  444. }
  445. }
  446. if ($_retVal = $this->socket_send_str('EHLO '.$hosth, '250')) {
  447. if ($usetls) {
  448. /*
  449. The following dialog illustrates how a client and server can start a TLS STARTTLS session:
  450. S: <waits for connection on TCP port 25>
  451. C: <opens connection>
  452. S: 220 mail.imc.org SMTP service ready
  453. C: EHLO mail.ietf.org
  454. S: 250-mail.imc.org offers a warm hug of welcome
  455. S: 250 STARTTLS
  456. C: STARTTLS
  457. S: 220 Go ahead
  458. C: <starts TLS negotiation>
  459. C & S: <negotiate a TLS session>
  460. C & S: <check result of negotiation>
  461. // Second pass EHLO
  462. C: EHLO client-domain.com
  463. S: 250-server-domain.com
  464. S: 250 AUTH LOGIN
  465. C: <continues by sending an SMTP command
  466. Another example here:
  467. S: 220 smtp.server.com Simple Mail Transfer Service Ready
  468. C: EHLO client.example.com
  469. S: 250-smtp.server.com Hello client.example.com
  470. S: 250-SIZE 1000000
  471. S: 250-AUTH LOGIN PLAIN CRAM-MD5
  472. S: 250-STARTTLS
  473. S: 250 HELP
  474. C: STARTTLS
  475. S: 220 TLS go ahead
  476. C: EHLO client.example.com *
  477. S: 250-smtp.server.com Hello client.example.com
  478. S: 250-SIZE 1000000
  479. S: 250-AUTH LOGIN PLAIN CRAM-MD5
  480. S: 250 HELP
  481. C: AUTH LOGIN
  482. S: 334 VXNlcm5hbWU6
  483. C: adlxdkej
  484. S: 334 UGFzc3dvcmQ6
  485. C: lkujsefxlj
  486. S: 235 2.7.0 Authentication successful
  487. C: MAIL FROM:<mail@samlogic.com>
  488. S: 250 OK
  489. C: RCPT TO:<john@mail.com>
  490. S: 250 OK
  491. C: DATA
  492. S: 354 Send message, end with a "." on a line by itself
  493. C: <The message data (body text, subject, e-mail header, attachments etc) is sent>
  494. S .
  495. S: 250 OK, message accepted for delivery: queued as 12345
  496. C: QUIT
  497. S: 221 Bye
  498. */
  499. if (!$_retVal = $this->socket_send_str('STARTTLS', 220)) {
  500. $this->_setErr(131, 'STARTTLS connection is not supported.');
  501. return $_retVal;
  502. }
  503. // Before 5.6.7:
  504. // STREAM_CRYPTO_METHOD_SSLv23_CLIENT = STREAM_CRYPTO_METHOD_SSLv2_CLIENT|STREAM_CRYPTO_METHOD_SSLv3_CLIENT
  505. // STREAM_CRYPTO_METHOD_TLS_CLIENT = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT|STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT|STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
  506. // PHP >= 5.6.7:
  507. // STREAM_CRYPTO_METHOD_SSLv23_CLIENT = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT|STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT|STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
  508. // STREAM_CRYPTO_METHOD_TLS_CLIENT = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT
  509. $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
  510. if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
  511. $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
  512. $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
  513. }
  514. if (!stream_socket_enable_crypto($this->socket, true, $crypto_method)) {
  515. $this->_setErr(132, 'STARTTLS connection failed.');
  516. return $_retVal;
  517. }
  518. // Most servers expect a 2nd pass of EHLO after TLS is established to get another time
  519. // the answer with list of supported AUTH methods. They may differs between non STARTTLS and with STARTTLS.
  520. if (! $_retVal = $this->socket_send_str('EHLO '.$hosth, '250')) {
  521. $this->_setErr(126, '"'.$hosth.'" does not support authenticated connections or temporary error. Error after 2nd sending EHLO '.$hosth.' : '.$this->lastretval);
  522. return $_retVal;
  523. }
  524. }
  525. // Default authentication method is LOGIN
  526. if (empty($conf->global->MAIN_MAIL_SMTPS_AUTH_TYPE)) {
  527. $conf->global->MAIN_MAIL_SMTPS_AUTH_TYPE = 'LOGIN';
  528. }
  529. // Send Authentication to Server
  530. // Check for errors along the way
  531. switch ($conf->global->MAIN_MAIL_SMTPS_AUTH_TYPE) {
  532. case 'NONE':
  533. // Do not send the 'AUTH type' message. For test purpose, if you don't need authentication, it is better to not enter login/pass into setup.
  534. $_retVal = true;
  535. break;
  536. case 'PLAIN':
  537. $this->socket_send_str('AUTH PLAIN', '334');
  538. // The error here just means the ID/password combo doesn't work.
  539. $_retVal = $this->socket_send_str(base64_encode("\0".$this->_smtpsID."\0".$this->_smtpsPW), '235');
  540. break;
  541. case 'XOAUTH2':
  542. // "user=$email\1auth=Bearer $token\1\1"
  543. $user = $this->_smtpsID;
  544. $token = $this->_smtpsToken;
  545. $initRes = "user=".$user."\001auth=Bearer ".$token."\001\001";
  546. $_retVal = $this->socket_send_str('AUTH XOAUTH2 '.base64_encode($initRes), '235');
  547. if (!$_retVal) {
  548. $this->_setErr(130, 'Error when asking for AUTH XOAUTH2');
  549. }
  550. break;
  551. case 'LOGIN': // most common case
  552. default:
  553. $_retVal = $this->socket_send_str('AUTH LOGIN', '334');
  554. if (!$_retVal) {
  555. $this->_setErr(130, 'Error when asking for AUTH LOGIN');
  556. } else {
  557. // User name will not return any error, server will take anything we give it.
  558. $this->socket_send_str(base64_encode($this->_smtpsID), '334');
  559. // The error here just means the ID/password combo doesn't work.
  560. // There is no method to determine which is the problem, ID or password
  561. $_retVal = $this->socket_send_str(base64_encode($this->_smtpsPW), '235');
  562. }
  563. break;
  564. }
  565. if (!$_retVal) {
  566. $this->_setErr(130, 'Invalid Authentication Credentials.');
  567. }
  568. } else {
  569. $this->_setErr(126, '"'.$host.'" does not support authenticated connections or temporary error. Error after sending EHLO '.$hosth.' : '.$this->lastretval);
  570. }
  571. return $_retVal;
  572. }
  573. /**
  574. * Now send the message
  575. *
  576. * @return boolean|null Result
  577. */
  578. public function sendMsg()
  579. {
  580. global $conf;
  581. /**
  582. * Default return value
  583. */
  584. $_retVal = false;
  585. // Connect to Server
  586. if ($this->socket = $this->_server_connect()) {
  587. // If a User ID *and* a password is given, assume Authentication is desired
  588. if (!empty($this->_smtpsID) && (!empty($this->_smtpsPW) || !empty($this->_smtpsToken))) {
  589. // Send the RFC2554 specified EHLO.
  590. $_retVal = $this->_server_authenticate();
  591. } else {
  592. // This is a "normal" SMTP Server "handshack"
  593. // Send the RFC821 specified HELO.
  594. $host = $this->getHost();
  595. $usetls = preg_match('@tls://@i', $host);
  596. $host = preg_replace('@tcp://@i', '', $host); // Remove prefix
  597. $host = preg_replace('@ssl://@i', '', $host); // Remove prefix
  598. $host = preg_replace('@tls://@i', '', $host); // Remove prefix
  599. if ($usetls && !empty($conf->global->MAIN_SMTPS_ADD_TLS_TO_HOST_FOR_HELO)) {
  600. $host = 'tls://'.$host;
  601. }
  602. $hosth = $host;
  603. if (!empty($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO)) {
  604. if (!is_numeric($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO)) {
  605. // If value of MAIL_SMTP_USE_FROM_FOR_HELO is a string, we use it as domain name
  606. $hosth = $conf->global->MAIL_SMTP_USE_FROM_FOR_HELO;
  607. } elseif ($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO == 1) {
  608. // If value of MAIL_SMTP_USE_FROM_FOR_HELO is 1, we use the domain in the from.
  609. // So if the from to is 'aaa <bbb@ccc.com>', we will keep 'ccc.com'
  610. $hosth = $this->getFrom('addr');
  611. $hosth = preg_replace('/^.*</', '', $hosth);
  612. $hosth = preg_replace('/>.*$/', '', $hosth);
  613. $hosth = preg_replace('/.*@/', '', $hosth);
  614. } elseif ($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO == 2) {
  615. // If value of MAIL_SMTP_USE_FROM_FOR_HELO is 2, we use the domain in the $dolibarr_main_url_root.
  616. global $dolibarr_main_url_root;
  617. $hosth = getDomainFromURL($dolibarr_main_url_root, 1);
  618. }
  619. }
  620. // Send the HELO message to the SMTP server
  621. $_retVal = $this->socket_send_str('HELO '.$hosth, '250');
  622. }
  623. // Well, did we get the server answer with correct code ?
  624. if ($_retVal) {
  625. // From this point onward most server response codes should be 250
  626. // Specify who the mail is from....
  627. // This has to be the raw email address, strip the "name" off
  628. $resultmailfrom = $this->socket_send_str('MAIL FROM: '.$this->getFrom('addr'), '250');
  629. if (!$resultmailfrom) {
  630. fclose($this->socket);
  631. return false;
  632. }
  633. // 'RCPT TO:' must be given a single address, so this has to loop
  634. // through the list of addresses, regardless of TO, CC or BCC
  635. // and send it out "single file"
  636. foreach ($this->get_RCPT_list() as $_address) {
  637. /* Note:
  638. * BCC email addresses must be listed in the RCPT TO command list,
  639. * but the BCC header should not be printed under the DATA command.
  640. * http://stackoverflow.com/questions/2750211/sending-bcc-emails-using-a-smtp-server
  641. */
  642. /*
  643. * TODO
  644. * After each 'RCPT TO:' is sent, we need to make sure it was kosher,
  645. * if not, the whole message will fail
  646. * If any email address fails, we will need to RESET the connection,
  647. * mark the last address as "bad" and start the address loop over again.
  648. * If any address fails, the entire message fails.
  649. */
  650. $this->socket_send_str('RCPT TO: <'.$_address.'>', '250');
  651. }
  652. // Tell the server we are ready to start sending data
  653. // with any custom headers...
  654. // This is the last response code we look for until the end of the message.
  655. $this->socket_send_str('DATA', '354');
  656. // Now we are ready for the message...
  657. // Ok, all the ingredients are mixed in let's cook this puppy...
  658. $this->socket_send_str($this->getHeader().$this->getBodyContent()."\r\n".'.', '250');
  659. // Now tell the server we are done and close the socket...
  660. fputs($this->socket, 'QUIT');
  661. } else {
  662. // We got error code into $this->lastretval
  663. }
  664. fclose($this->socket);
  665. }
  666. return $_retVal;
  667. }
  668. // =============================================================
  669. // ** Setter & Getter methods
  670. // ** Basic System configuration
  671. /**
  672. * setConfig() is used to populate select class properties from either
  673. * a user defined INI file or the systems 'php.ini' file
  674. *
  675. * If a user defined INI is to be used, the files complete path is passed
  676. * as the method single parameter. The INI can define any class and/or
  677. * user properties. Only properties defined within this file will be setter
  678. * and/or orverwritten
  679. *
  680. * If the systems 'php.ini' file is to be used, the method is called without
  681. * parameters. In this case, only HOST, PORT and FROM properties will be set
  682. * as they are the only properties that are defined within the 'php.ini'.
  683. *
  684. * If secure SMTP is to be used, the user ID and Password can be defined with
  685. * the user INI file, but the properties are not defined with the systems
  686. * 'php.ini'file, they must be defined via their setter methods
  687. *
  688. * This method can be called twice, if desired. Once without a parameter to
  689. * load the properties as defined within the systems 'php.ini' file, and a
  690. * second time, with a path to a user INI file for other properties to be
  691. * defined.
  692. *
  693. * @param mixed $_strConfigPath path to config file or VOID
  694. * @return boolean
  695. */
  696. public function setConfig($_strConfigPath = null)
  697. {
  698. /**
  699. * Returns constructed SELECT Object string or boolean upon failure
  700. * Default value is set at true
  701. */
  702. $_retVal = true;
  703. // if we have a path...
  704. if (!empty($_strConfigPath)) {
  705. // If the path is not valid, this will NOT generate an error,
  706. // it will simply return false.
  707. if (!@include $_strConfigPath) {
  708. $this->_setErr(110, '"'.$_strConfigPath.'" is not a valid path.');
  709. $_retVal = false;
  710. }
  711. } else {
  712. // Read the Systems php.ini file
  713. // Set these properties ONLY if they are set in the php.ini file.
  714. // Otherwise the default values will be used.
  715. if ($_host = ini_get('SMTPs')) {
  716. $this->setHost($_host);
  717. }
  718. if ($_port = ini_get('smtp_port')) {
  719. $this->setPort($_port);
  720. }
  721. if ($_from = ini_get('sendmail_from')) {
  722. $this->setFrom($_from);
  723. }
  724. }
  725. // Send back what we have
  726. return $_retVal;
  727. }
  728. /**
  729. * Determines the method inwhich the messages are to be sent.
  730. * - 'sockets' [0] - conect via network to SMTP server
  731. * - 'pipe [1] - use UNIX path to EXE
  732. * - 'phpmail [2] - use the PHP built-in mail function
  733. *
  734. * @param int $_type Interger value representing Mail Transport Type
  735. * @return void
  736. */
  737. public function setTransportType($_type = 0)
  738. {
  739. if ((is_numeric($_type)) && (($_type >= 0) && ($_type <= 3))) {
  740. $this->_transportType = $_type;
  741. }
  742. }
  743. /**
  744. * Return the method inwhich the message is to be sent.
  745. * - 'sockets' [0] - conect via network to SMTP server
  746. * - 'pipe [1] - use UNIX path to EXE
  747. * - 'phpmail [2] - use the PHP built-in mail function
  748. *
  749. * @return int $_strHost Host Name or IP of the Mail Server to use
  750. */
  751. public function getTransportType()
  752. {
  753. return $this->_transportType;
  754. }
  755. /**
  756. * Path to the sendmail execuable
  757. *
  758. * @param string $_path Path to the sendmail execuable
  759. * @return boolean
  760. *
  761. */
  762. public function setMailPath($_path)
  763. {
  764. // This feature is not yet implemented
  765. return true;
  766. //if ( $_path ) $this->_mailPath = $_path;
  767. }
  768. /**
  769. * Defines the Host Name or IP of the Mail Server to use.
  770. * This is defaulted to 'localhost'
  771. * This is used only with 'socket' based mail transmission
  772. *
  773. * @param string $_strHost Host Name or IP of the Mail Server to use
  774. * @return void
  775. */
  776. public function setHost($_strHost)
  777. {
  778. if ($_strHost) {
  779. $this->_smtpsHost = $_strHost;
  780. }
  781. }
  782. /**
  783. * Retrieves the Host Name or IP of the Mail Server to use
  784. * This is used only with 'socket' based mail transmission
  785. *
  786. * @return string $_strHost Host Name or IP of the Mail Server to use
  787. */
  788. public function getHost()
  789. {
  790. return $this->_smtpsHost;
  791. }
  792. /**
  793. * Defines the Port Number of the Mail Server to use
  794. * This is defaulted to '25'
  795. * This is used only with 'socket' based mail transmission
  796. *
  797. * @param int $_intPort Port Number of the Mail Server to use
  798. * @return void
  799. */
  800. public function setPort($_intPort)
  801. {
  802. if ((is_numeric($_intPort)) &&
  803. (($_intPort >= 1) && ($_intPort <= 65536))) {
  804. $this->_smtpsPort = $_intPort;
  805. }
  806. }
  807. /**
  808. * Retrieves the Port Number of the Mail Server to use
  809. * This is used only with 'socket' based mail transmission
  810. *
  811. * @return string Port Number of the Mail Server to use
  812. */
  813. public function getPort()
  814. {
  815. return $this->_smtpsPort;
  816. }
  817. /**
  818. * User Name for authentication on Mail Server
  819. *
  820. * @param string $_strID User Name for authentication on Mail Server
  821. * @return void
  822. */
  823. public function setID($_strID)
  824. {
  825. $this->_smtpsID = $_strID;
  826. }
  827. /**
  828. * Retrieves the User Name for authentication on Mail Server
  829. *
  830. * @return string User Name for authentication on Mail Server
  831. */
  832. public function getID()
  833. {
  834. return $this->_smtpsID;
  835. }
  836. /**
  837. * User Password for authentication on Mail Server
  838. *
  839. * @param string $_strPW User Password for authentication on Mail Server
  840. * @return void
  841. */
  842. public function setPW($_strPW)
  843. {
  844. $this->_smtpsPW = $_strPW;
  845. }
  846. /**
  847. * Retrieves the User Password for authentication on Mail Server
  848. *
  849. * @return string User Password for authentication on Mail Server
  850. */
  851. public function getPW()
  852. {
  853. return $this->_smtpsPW;
  854. }
  855. /**
  856. * User token for OAUTH2
  857. *
  858. * @param string $_strToken User token
  859. * @return void
  860. */
  861. public function setToken($_strToken)
  862. {
  863. $this->_smtpsToken = $_strToken;
  864. }
  865. /**
  866. * Retrieves the User token for OAUTH2
  867. *
  868. * @return string User token for OAUTH2
  869. */
  870. public function getToken()
  871. {
  872. return $this->_smtpsToken;
  873. }
  874. /**
  875. * Character set used for current message
  876. * Character set is defaulted to 'iso-8859-1';
  877. *
  878. * @param string $_strCharSet Character set used for current message
  879. * @return void
  880. */
  881. public function setCharSet($_strCharSet)
  882. {
  883. if ($_strCharSet) {
  884. $this->_smtpsCharSet = $_strCharSet;
  885. }
  886. }
  887. /**
  888. * Retrieves the Character set used for current message
  889. *
  890. * @return string $_smtpsCharSet Character set used for current message
  891. */
  892. public function getCharSet()
  893. {
  894. return $this->_smtpsCharSet;
  895. }
  896. /**
  897. * Content-Transfer-Encoding, Defaulted to '7bit'
  898. * This can be changed for 2byte characers sets
  899. * Known Encode Types
  900. * - 7bit Simple 7-bit ASCII
  901. * - 8bit 8-bit coding with line termination characters
  902. * - base64 3 octets encoded into 4 sextets with offset
  903. * - binary Arbitrary binary stream
  904. * - mac-binhex40 Macintosh binary to hex encoding
  905. * - quoted-printable Mostly 7-bit, with 8-bit characters encoded as "=HH"
  906. * - uuencode UUENCODE encoding
  907. *
  908. * @param string $_strTransEncode Content-Transfer-Encoding
  909. * @return void
  910. */
  911. public function setTransEncode($_strTransEncode)
  912. {
  913. if (array_search($_strTransEncode, $this->_smtpsTransEncodeTypes)) {
  914. $this->_smtpsTransEncode = $_strTransEncode;
  915. }
  916. }
  917. /**
  918. * Retrieves the Content-Transfer-Encoding
  919. *
  920. * @return string $_smtpsTransEncode Content-Transfer-Encoding
  921. */
  922. public function getTransEncode()
  923. {
  924. return $this->_smtpsTransEncode;
  925. }
  926. /**
  927. * Content-Transfer-Encoding, Defaulted to '0' [ZERO]
  928. * This can be changed for 2byte characers sets
  929. * Known Encode Types
  930. * - [0] 7bit Simple 7-bit ASCII
  931. * - [1] 8bit 8-bit coding with line termination characters
  932. * - [2] base64 3 octets encoded into 4 sextets with offset
  933. * - [3] binary Arbitrary binary stream
  934. * - [4] mac-binhex40 Macintosh binary to hex encoding
  935. * - [5] quoted-printable Mostly 7-bit, with 8-bit characters encoded as "=HH"
  936. * - [6] uuencode UUENCODE encoding
  937. *
  938. * @param string $_strTransEncodeType Content-Transfer-Encoding
  939. * @return void
  940. *
  941. */
  942. public function setTransEncodeType($_strTransEncodeType)
  943. {
  944. if (array_search($_strTransEncodeType, $this->_smtpsTransEncodeTypes)) {
  945. $this->_smtpsTransEncodeType = $_strTransEncodeType;
  946. }
  947. }
  948. /**
  949. * Retrieves the Content-Transfer-Encoding
  950. *
  951. * @return string Content-Transfer-Encoding
  952. */
  953. public function getTransEncodeType()
  954. {
  955. return $this->_smtpsTransEncodeTypes[$this->_smtpsTransEncodeType];
  956. }
  957. // ** Message Construction
  958. /**
  959. * FROM Address from which mail will be sent
  960. *
  961. * @param string $_strFrom Address from which mail will be sent
  962. * @return void
  963. */
  964. public function setFrom($_strFrom)
  965. {
  966. if ($_strFrom) {
  967. $this->_msgFrom = $this->_strip_email($_strFrom);
  968. }
  969. }
  970. /**
  971. * Retrieves the Address from which mail will be sent
  972. *
  973. * @param boolean $_part To "strip" 'Real name' from address
  974. * @return string Address from which mail will be sent
  975. */
  976. public function getFrom($_part = true)
  977. {
  978. $_retValue = '';
  979. if ($_part === true) {
  980. $_retValue = $this->_msgFrom;
  981. } else {
  982. $_retValue = $this->_msgFrom[$_part];
  983. }
  984. return $_retValue;
  985. }
  986. /**
  987. * Reply-To Address from which mail will be the reply-to
  988. *
  989. * @param string $_strReplyTo Address from which mail will be the reply-to
  990. * @return void
  991. */
  992. public function setReplyTo($_strReplyTo)
  993. {
  994. if ($_strReplyTo) {
  995. $this->_msgReplyTo = $this->_strip_email($_strReplyTo);
  996. }
  997. }
  998. /**
  999. * Retrieves the Address from which mail will be the reply-to
  1000. *
  1001. * @param boolean $_part To "strip" 'Real name' from address
  1002. * @return string Address from which mail will be the reply-to
  1003. */
  1004. public function getReplyTo($_part = true)
  1005. {
  1006. $_retValue = '';
  1007. if ($_part === true) {
  1008. $_retValue = $this->_msgReplyTo;
  1009. } else {
  1010. $_retValue = $this->_msgReplyTo[$_part];
  1011. }
  1012. return $_retValue;
  1013. }
  1014. /**
  1015. * Inserts given addresses into structured format.
  1016. * This method takes a list of given addresses, via an array or a COMMA delimted string, and inserts them into a highly
  1017. * structured array. This array is designed to remove duplicate addresses and to sort them by Domain.
  1018. *
  1019. * @param string $_type TO, CC, or BCC lists to add addrresses into
  1020. * @param mixed $_addrList Array or COMMA delimited string of addresses
  1021. * @return void
  1022. *
  1023. */
  1024. private function _buildAddrList($_type, $_addrList)
  1025. {
  1026. // Pull existing list
  1027. $aryHost = $this->_msgRecipients;
  1028. // Only run this if we have something
  1029. if (!empty($_addrList)) {
  1030. // $_addrList can be a STRING or an array
  1031. if (is_string($_addrList)) {
  1032. // This could be a COMMA delimited string
  1033. if (strstr($_addrList, ',')) {
  1034. // "explode "list" into an array
  1035. $_addrList = explode(',', $_addrList);
  1036. } else {
  1037. // Stick it in an array
  1038. $_addrList = array($_addrList);
  1039. }
  1040. }
  1041. // take the array of addresses and split them further
  1042. foreach ($_addrList as $_strAddr) {
  1043. // Strip off the end '>'
  1044. $_strAddr = str_replace('>', '', $_strAddr);
  1045. // Seperate "Real Name" from eMail address
  1046. $_tmpaddr = null;
  1047. $_tmpaddr = explode('<', $_strAddr);
  1048. // We have a "Real Name" and eMail address
  1049. if (count($_tmpaddr) == 2) {
  1050. $_tmpHost = explode('@', $_tmpaddr[1]);
  1051. $_tmpaddr[0] = trim($_tmpaddr[0], ' ">');
  1052. $aryHost[$_tmpHost[1]][$_type][$_tmpHost[0]] = $_tmpaddr[0];
  1053. } else {
  1054. // We only have an eMail address
  1055. // Strip off the beggining '<'
  1056. $_strAddr = str_replace('<', '', $_strAddr);
  1057. $_tmpHost = explode('@', $_strAddr);
  1058. $_tmpHost[0] = trim($_tmpHost[0]);
  1059. $_tmpHost[1] = trim($_tmpHost[1]);
  1060. $aryHost[$_tmpHost[1]][$_type][$_tmpHost[0]] = '';
  1061. }
  1062. }
  1063. }
  1064. // replace list
  1065. $this->_msgRecipients = $aryHost;
  1066. }
  1067. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1068. /**
  1069. * Returns an array of the various parts of an email address
  1070. * This assumes a well formed address:
  1071. * - "Real name" <username@domain.tld>
  1072. * - "Real Name" is optional
  1073. * - if "Real Name" does not exist, the angle brackets are optional
  1074. * This will split an email address into 4 or 5 parts.
  1075. * - $_aryEmail[org] = orignal string
  1076. * - $_aryEmail[real] = "real name" - if there is one
  1077. * - $_aryEmail[addr] = address part "username@domain.tld"
  1078. * - $_aryEmail[host] = "domain.tld"
  1079. * - $_aryEmail[user] = "userName"
  1080. *
  1081. * @param string $_strAddr Email address
  1082. * @return array An array of the various parts of an email address
  1083. */
  1084. private function _strip_email($_strAddr)
  1085. {
  1086. // phpcs:enable
  1087. // Keep the orginal
  1088. $_aryEmail['org'] = $_strAddr;
  1089. // Set entire string to Lower Case
  1090. $_strAddr = strtolower($_strAddr);
  1091. // Drop "stuff' off the end
  1092. $_strAddr = trim($_strAddr, ' ">');
  1093. // Seperate "Real Name" from eMail address, if we have one
  1094. $_tmpAry = explode('<', $_strAddr);
  1095. // Do we have a "Real name"
  1096. if (count($_tmpAry) == 2) {
  1097. // We may not really have a "Real Name"
  1098. if ($_tmpAry[0]) {
  1099. $_aryEmail['real'] = trim($_tmpAry[0], ' ">');
  1100. }
  1101. $_aryEmail['addr'] = $_tmpAry[1];
  1102. } else {
  1103. $_aryEmail['addr'] = $_tmpAry[0];
  1104. }
  1105. // Pull User Name and Host.tld apart
  1106. $_tmpHost = explode('@', $_aryEmail['addr']);
  1107. $_aryEmail['user'] = $_tmpHost[0];
  1108. $_aryEmail['host'] = $_tmpHost[1];
  1109. // Put the brackets back around the address
  1110. $_aryEmail['addr'] = '<'.$_aryEmail['addr'].'>';
  1111. return $_aryEmail;
  1112. }
  1113. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1114. /**
  1115. * Returns an array of bares addresses for use with 'RCPT TO:'
  1116. * This is a "build as you go" method. Each time this method is called
  1117. * the underlaying array is destroyed and reconstructed.
  1118. *
  1119. * @return array Returns an array of bares addresses
  1120. */
  1121. public function get_RCPT_list()
  1122. {
  1123. // phpcs:enable
  1124. /**
  1125. * An array of bares addresses for use with 'RCPT TO:'
  1126. */
  1127. $_RCPT_list = array();
  1128. // walk down Recipients array and pull just email addresses
  1129. foreach ($this->_msgRecipients as $_host => $_list) {
  1130. foreach ($_list as $_subList) {
  1131. foreach ($_subList as $_name => $_addr) {
  1132. // build RCPT list
  1133. $_RCPT_list[] = $_name.'@'.$_host;
  1134. }
  1135. }
  1136. }
  1137. return $_RCPT_list;
  1138. }
  1139. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1140. /**
  1141. * Returns an array of addresses for a specific type; TO, CC or BCC
  1142. *
  1143. * @param string $_which Which collection of addresses to return ('to', 'cc', 'bcc')
  1144. * @return string|false Array of emaill address
  1145. */
  1146. public function get_email_list($_which = null)
  1147. {
  1148. // phpcs:enable
  1149. // We need to know which address segment to pull
  1150. if ($_which) {
  1151. // Make sure we have addresses to process
  1152. if ($this->_msgRecipients) {
  1153. $_RCPT_list = array();
  1154. // walk down Recipients array and pull just email addresses
  1155. foreach ($this->_msgRecipients as $_host => $_list) {
  1156. if (!empty($this->_msgRecipients[$_host][$_which])) {
  1157. foreach ($this->_msgRecipients[$_host][$_which] as $_addr => $_realName) {
  1158. if ($_realName) { // @CHANGE LDR
  1159. $_realName = '"'.$_realName.'"';
  1160. $_RCPT_list[] = $_realName.' <'.$_addr.'@'.$_host.'>';
  1161. } else {
  1162. $_RCPT_list[] = $_addr.'@'.$_host;
  1163. }
  1164. }
  1165. }
  1166. }
  1167. return implode(', ', $_RCPT_list);
  1168. } else {
  1169. $this->_setErr(101, 'No eMail Address for message to be sent to.');
  1170. return false;
  1171. }
  1172. } else {
  1173. $this->_setErr(102, 'eMail type not defined.');
  1174. return false;
  1175. }
  1176. }
  1177. /**
  1178. * TO Address[es] inwhich to send mail to
  1179. *
  1180. * @param string $_addrTo TO Address[es] inwhich to send mail to
  1181. * @return void
  1182. */
  1183. public function setTO($_addrTo)
  1184. {
  1185. if ($_addrTo) {
  1186. $this->_buildAddrList('to', $_addrTo);
  1187. }
  1188. }
  1189. /**
  1190. * Retrieves the TO Address[es] inwhich to send mail to
  1191. *
  1192. * @return string TO Address[es] inwhich to send mail to
  1193. */
  1194. public function getTo()
  1195. {
  1196. return $this->get_email_list('to');
  1197. }
  1198. /**
  1199. * CC Address[es] inwhich to send mail to
  1200. *
  1201. * @param string $_strCC CC Address[es] inwhich to send mail to
  1202. * @return void
  1203. */
  1204. public function setCC($_strCC)
  1205. {
  1206. if ($_strCC) {
  1207. $this->_buildAddrList('cc', $_strCC);
  1208. }
  1209. }
  1210. /**
  1211. * Retrieves the CC Address[es] inwhich to send mail to
  1212. *
  1213. * @return string CC Address[es] inwhich to send mail to
  1214. */
  1215. public function getCC()
  1216. {
  1217. return $this->get_email_list('cc');
  1218. }
  1219. /**
  1220. * BCC Address[es] inwhich to send mail to
  1221. *
  1222. * @param string $_strBCC Recipients BCC Address[es] inwhich to send mail to
  1223. * @return void
  1224. */
  1225. public function setBCC($_strBCC)
  1226. {
  1227. if ($_strBCC) {
  1228. $this->_buildAddrList('bcc', $_strBCC);
  1229. }
  1230. }
  1231. /**
  1232. * Retrieves the BCC Address[es] inwhich to send mail to
  1233. *
  1234. * @return string BCC Address[es] inwhich to send mail to
  1235. */
  1236. public function getBCC()
  1237. {
  1238. return $this->get_email_list('bcc');
  1239. }
  1240. /**
  1241. * Message Subject
  1242. *
  1243. * @param string $_strSubject Message Subject
  1244. * @return void
  1245. */
  1246. public function setSubject($_strSubject = '')
  1247. {
  1248. if ($_strSubject) {
  1249. $this->_msgSubject = $_strSubject;
  1250. }
  1251. }
  1252. /**
  1253. * Retrieves the Message Subject
  1254. *
  1255. * @return string Message Subject
  1256. */
  1257. public function getSubject()
  1258. {
  1259. return $this->_msgSubject;
  1260. }
  1261. /**
  1262. * Constructes and returns message header
  1263. *
  1264. * @return string Complete message header
  1265. */
  1266. public function getHeader()
  1267. {
  1268. global $conf;
  1269. $_header = 'From: '.$this->getFrom('org')."\r\n"
  1270. . 'To: '.$this->getTO()."\r\n";
  1271. if ($this->getCC()) {
  1272. $_header .= 'Cc: '.$this->getCC()."\r\n";
  1273. }
  1274. /* Note:
  1275. * BCC email addresses must be listed in the RCPT TO command list,
  1276. * but the BCC header should not be printed under the DATA command.
  1277. * So it is included into the function sendMsg() but not here.
  1278. * http://stackoverflow.com/questions/2750211/sending-bcc-emails-using-a-smtp-server
  1279. */
  1280. /*
  1281. if ( $this->getBCC() )
  1282. $_header .= 'Bcc: ' . $this->getBCC() . "\r\n";
  1283. */
  1284. $host = dol_getprefix('email');
  1285. //NOTE: Message-ID should probably contain the username of the user who sent the msg
  1286. $_header .= 'Subject: '.$this->getSubject()."\r\n";
  1287. $_header .= 'Date: '.date("r")."\r\n";
  1288. $trackid = $this->getTrackId();
  1289. if ($trackid) {
  1290. // References is kept in response and Message-ID is returned into In-Reply-To:
  1291. $_header .= 'Message-ID: <'.time().'.SMTPs-dolibarr-'.$trackid.'@'.$host.">\r\n";
  1292. $_header .= 'References: <'.time().'.SMTPs-dolibarr-'.$trackid.'@'.$host.">\r\n";
  1293. $_header .= 'X-Dolibarr-TRACKID: '.$trackid.'@'.$host."\r\n";
  1294. } else {
  1295. $_header .= 'Message-ID: <'.time().'.SMTPs@'.$host.">\r\n";
  1296. }
  1297. if (!empty($_SERVER['REMOTE_ADDR'])) {
  1298. $_header .= "X-RemoteAddr: ".$_SERVER['REMOTE_ADDR']."\r\n";
  1299. }
  1300. if ($this->getMoreInHeader()) {
  1301. $_header .= $this->getMoreInHeader(); // Value must include the "\r\n";
  1302. }
  1303. //$_header .=
  1304. // 'Read-Receipt-To: ' . $this->getFrom( 'org' ) . "\r\n"
  1305. // 'Return-Receipt-To: ' . $this->getFrom( 'org' ) . "\r\n";
  1306. if ($this->getSensitivity()) {
  1307. $_header .= 'Sensitivity: '.$this->getSensitivity()."\r\n";
  1308. }
  1309. if ($this->_msgPriority != 3) {
  1310. $_header .= $this->getPriority();
  1311. }
  1312. // @CHANGE LDR
  1313. if ($this->getDeliveryReceipt()) {
  1314. $_header .= 'Disposition-Notification-To: '.$this->getFrom('addr')."\r\n";
  1315. }
  1316. if ($this->getErrorsTo()) {
  1317. $_header .= 'Errors-To: '.$this->getErrorsTo('addr')."\r\n";
  1318. }
  1319. if ($this->getReplyTo()) {
  1320. $_header .= "Reply-To: ".$this->getReplyTo('addr')."\r\n";
  1321. }
  1322. $_header .= 'X-Mailer: Dolibarr version '.DOL_VERSION.' (using SMTPs Mailer)'."\r\n";
  1323. $_header .= 'X-Dolibarr-Option: '.($conf->global->MAIN_MAIL_USE_MULTI_PART ? 'MAIN_MAIL_USE_MULTI_PART' : 'No MAIN_MAIL_USE_MULTI_PART')."\r\n";
  1324. $_header .= 'Mime-Version: 1.0'."\r\n";
  1325. return $_header;
  1326. }
  1327. /**
  1328. * Message Content
  1329. *
  1330. * @param string $strContent Message Content
  1331. * @param string $strType Type
  1332. * @return void
  1333. */
  1334. public function setBodyContent($strContent, $strType = 'plain')
  1335. {
  1336. //if ( $strContent )
  1337. //{
  1338. if ($strType == 'html') {
  1339. $strMimeType = 'text/html';
  1340. } else {
  1341. $strMimeType = 'text/plain';
  1342. }
  1343. // Make RFC821 Compliant, replace bare linefeeds
  1344. $strContent = preg_replace("/(?<!\r)\n/si", "\r\n", $strContent);
  1345. $strContentAltText = '';
  1346. if ($strType == 'html') {
  1347. // Similar code to forge a text from html is also in CMailFile.class.php
  1348. $strContentAltText = preg_replace('/<head><title>.*<\/style><\/head>/', '', $strContent);
  1349. $strContentAltText = preg_replace("/<br\s*[^>]*>/", " ", $strContentAltText);
  1350. $strContentAltText = html_entity_decode(strip_tags($strContentAltText));
  1351. $strContentAltText = trim(wordwrap($strContentAltText, 75, "\r\n"));
  1352. }
  1353. // Make RFC2045 Compliant
  1354. //$strContent = rtrim(chunk_split($strContent)); // Function chunck_split seems ko if not used on a base64 content
  1355. $strContent = rtrim(wordwrap($strContent, 75, "\r\n")); // TODO Using this method creates unexpected line break on text/plain content.
  1356. $this->_msgContent[$strType] = array();
  1357. $this->_msgContent[$strType]['mimeType'] = $strMimeType;
  1358. $this->_msgContent[$strType]['data'] = $strContent;
  1359. $this->_msgContent[$strType]['dataText'] = $strContentAltText;
  1360. if ($this->getMD5flag()) {
  1361. $this->_msgContent[$strType]['md5'] = dol_hash($strContent, 3);
  1362. }
  1363. //}
  1364. }
  1365. /**
  1366. * Retrieves the Message Content
  1367. *
  1368. * @return string Message Content
  1369. */
  1370. public function getBodyContent()
  1371. {
  1372. global $conf;
  1373. // Generate a new Boundary string
  1374. $this->_setBoundary();
  1375. // What type[s] of content do we have
  1376. $_types = array_keys($this->_msgContent);
  1377. // How many content types do we have
  1378. $keyCount = count($_types);
  1379. // If we have ZERO, we have a problem
  1380. if ($keyCount === 0) {
  1381. die("Sorry, no content");
  1382. } elseif ($keyCount === 1 && empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) {
  1383. // If we have ONE, we can use the simple format
  1384. $_msgData = $this->_msgContent;
  1385. $_msgData = $_msgData[$_types[0]];
  1386. $content = 'Content-Type: '.$_msgData['mimeType'].'; charset="'.$this->getCharSet().'"'."\r\n"
  1387. . 'Content-Transfer-Encoding: '.$this->getTransEncodeType()."\r\n"
  1388. . 'Content-Disposition: inline'."\r\n"
  1389. . 'Content-Description: Message'."\r\n";
  1390. if ($this->getMD5flag()) {
  1391. $content .= 'Content-MD5: '.$_msgData['md5']."\r\n";
  1392. }
  1393. $content .= "\r\n"
  1394. . $_msgData['data']."\r\n";
  1395. } elseif ($keyCount >= 1 || !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) {
  1396. // If we have more than ONE, we use the multi-part format
  1397. // Since this is an actual multi-part message
  1398. // We need to define a content message Boundary
  1399. // NOTE: This was 'multipart/alternative', but Windows based mail servers have issues with this.
  1400. //$content = 'Content-Type: multipart/related; boundary="' . $this->_getBoundary() . '"' . "\r\n";
  1401. $content = 'Content-Type: multipart/mixed; boundary="'.$this->_getBoundary('mixed').'"'."\r\n";
  1402. // . "\r\n"
  1403. // . 'This is a multi-part message in MIME format.' . "\r\n";
  1404. $content .= "Content-Transfer-Encoding: 8bit\r\n";
  1405. $content .= "\r\n";
  1406. $content .= "--".$this->_getBoundary('mixed')."\r\n";
  1407. if (key_exists('image', $this->_msgContent)) { // If inline image found
  1408. $content .= 'Content-Type: multipart/alternative; boundary="'.$this->_getBoundary('alternative').'"'."\r\n";
  1409. $content .= "\r\n";
  1410. $content .= "--".$this->_getBoundary('alternative')."\r\n";
  1411. }
  1412. // $this->_msgContent must be sorted with key 'text' or 'html' first then 'image' then 'attachment'
  1413. // Loop through message content array
  1414. foreach ($this->_msgContent as $type => $_content) {
  1415. if ($type == 'attachment') {
  1416. // loop through all attachments
  1417. foreach ($_content as $_file => $_data) {
  1418. $content .= "--".$this->_getBoundary('mixed')."\r\n"
  1419. . 'Content-Disposition: attachment; filename="'.$_data['fileName'].'"'."\r\n"
  1420. . 'Content-Type: '.$_data['mimeType'].'; name="'.$_data['fileName'].'"'."\r\n"
  1421. . 'Content-Transfer-Encoding: base64'."\r\n"
  1422. . 'Content-Description: '.$_data['fileName']."\r\n";
  1423. if (!empty($_data['cid'])) {
  1424. $content .= "X-Attachment-Id: ".$_data['cid']."\r\n";
  1425. $content .= "Content-ID: <".$_data['cid'].">\r\n";
  1426. }
  1427. if ($this->getMD5flag()) {
  1428. $content .= 'Content-MD5: '.$_data['md5']."\r\n";
  1429. }
  1430. $content .= "\r\n".$_data['data']."\r\n\r\n";
  1431. }
  1432. } elseif ($type == 'image') {
  1433. // @CHANGE LDR
  1434. // loop through all images
  1435. foreach ($_content as $_image => $_data) {
  1436. $content .= "--".$this->_getBoundary('related')."\r\n"; // always related for an inline image
  1437. $content .= 'Content-Type: '.$_data['mimeType'].'; name="'.$_data['imageName'].'"'."\r\n"
  1438. . 'Content-Transfer-Encoding: base64'."\r\n"
  1439. . 'Content-Disposition: inline; filename="'.$_data['imageName'].'"'."\r\n"
  1440. . 'Content-ID: <'.$_data['cid'].'> '."\r\n";
  1441. if ($this->getMD5flag()) {
  1442. $content .= 'Content-MD5: '.$_data['md5']."\r\n";
  1443. }
  1444. $content .= "\r\n"
  1445. . $_data['data']."\r\n";
  1446. }
  1447. // always end related and end alternative after inline images
  1448. $content .= "--".$this->_getBoundary('related')."--\r\n";
  1449. $content .= "\r\n--".$this->_getBoundary('alternative')."--\r\n";
  1450. $content .= "\r\n";
  1451. } else {
  1452. if (key_exists('image', $this->_msgContent)) {
  1453. $content .= "Content-Type: text/plain; charset=".$this->getCharSet()."\r\n";
  1454. $content .= "\r\n".($_content['dataText'] ? $_content['dataText'] : strip_tags($_content['data']))."\r\n"; // Add plain text message
  1455. $content .= "--".$this->_getBoundary('alternative')."\r\n";
  1456. $content .= 'Content-Type: multipart/related; boundary="'.$this->_getBoundary('related').'"'."\r\n";
  1457. $content .= "\r\n";
  1458. $content .= "--".$this->_getBoundary('related')."\r\n";
  1459. }
  1460. if (!key_exists('image', $this->_msgContent) && $_content['dataText'] && !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) {
  1461. // Add plain text message part before html part
  1462. $content .= 'Content-Type: multipart/alternative; boundary="'.$this->_getBoundary('alternative').'"'."\r\n";
  1463. $content .= "\r\n";
  1464. $content .= "--".$this->_getBoundary('alternative')."\r\n";
  1465. $content .= "Content-Type: text/plain; charset=".$this->getCharSet()."\r\n";
  1466. $content .= "\r\n".$_content['dataText']."\r\n";
  1467. $content .= "--".$this->_getBoundary('alternative')."\r\n";
  1468. }
  1469. $content .= 'Content-Type: '.$_content['mimeType'].'; charset='.$this->getCharSet();
  1470. $content .= "\r\n";
  1471. if ($this->getMD5flag()) {
  1472. $content .= 'Content-MD5: '.$_content['md5']."\r\n";
  1473. }
  1474. $content .= "\r\n".$_content['data']."\r\n";
  1475. if (!key_exists('image', $this->_msgContent) && $_content['dataText'] && !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) {
  1476. // Add plain text message part after html part
  1477. $content .= "--".$this->_getBoundary('alternative')."--\r\n";
  1478. }
  1479. $content .= "\r\n";
  1480. }
  1481. }
  1482. $content .= "--".$this->_getBoundary('mixed').'--'."\r\n";
  1483. }
  1484. return $content;
  1485. }
  1486. /**
  1487. * File attachments are added to the content array as sub-arrays,
  1488. * allowing for multiple attachments for each outbound email
  1489. *
  1490. * @param string $strContent File data to attach to message
  1491. * @param string $strFileName File Name to give to attachment
  1492. * @param string $strMimeType File Mime Type of attachment
  1493. * @param string $strCid File Cid of attachment (if defined, to be shown inline)
  1494. * @return void
  1495. */
  1496. public function setAttachment($strContent, $strFileName = 'unknown', $strMimeType = 'unknown', $strCid = '')
  1497. {
  1498. if ($strContent) {
  1499. $strContent = rtrim(chunk_split(base64_encode($strContent), 76, "\r\n")); // 76 max is defined into http://tools.ietf.org/html/rfc2047
  1500. $this->_msgContent['attachment'][$strFileName]['mimeType'] = $strMimeType;
  1501. $this->_msgContent['attachment'][$strFileName]['fileName'] = $strFileName;
  1502. $this->_msgContent['attachment'][$strFileName]['data'] = $strContent;
  1503. $this->_msgContent['attachment'][$strFileName]['cid'] = $strCid; // If defined, it means this attachment must be shown inline
  1504. if ($this->getMD5flag()) {
  1505. $this->_msgContent['attachment'][$strFileName]['md5'] = dol_hash($strContent, 3);
  1506. }
  1507. }
  1508. }
  1509. // @CHANGE LDR
  1510. /**
  1511. * Image attachments are added to the content array as sub-arrays,
  1512. * allowing for multiple images for each outbound email
  1513. *
  1514. * @param string $strContent Image data to attach to message
  1515. * @param string $strImageName Image Name to give to attachment
  1516. * @param string $strMimeType Image Mime Type of attachment
  1517. * @param string $strImageCid CID
  1518. * @return void
  1519. */
  1520. public function setImageInline($strContent, $strImageName = 'unknown', $strMimeType = 'unknown', $strImageCid = 'unknown')
  1521. {
  1522. if ($strContent) {
  1523. $this->_msgContent['image'][$strImageName]['mimeType'] = $strMimeType;
  1524. $this->_msgContent['image'][$strImageName]['imageName'] = $strImageName;
  1525. $this->_msgContent['image'][$strImageName]['cid'] = $strImageCid;
  1526. $this->_msgContent['image'][$strImageName]['data'] = $strContent;
  1527. if ($this->getMD5flag()) {
  1528. $this->_msgContent['image'][$strImageName]['md5'] = dol_hash($strContent, 3);
  1529. }
  1530. }
  1531. }
  1532. // END @CHANGE LDR
  1533. /**
  1534. * Message Content Sensitivity
  1535. * Message Sensitivity values:
  1536. * - [0] None - default
  1537. * - [1] Personal
  1538. * - [2] Private
  1539. * - [3] Company Confidential
  1540. *
  1541. * @param integer $_value Message Sensitivity
  1542. * @return void
  1543. */
  1544. public function setSensitivity($_value = 0)
  1545. {
  1546. if ((is_numeric($_value)) &&
  1547. (($_value >= 0) && ($_value <= 3))) {
  1548. $this->_msgSensitivity = $_value;
  1549. }
  1550. }
  1551. /**
  1552. * Returns Message Content Sensitivity string
  1553. * Message Sensitivity values:
  1554. * - [0] None - default
  1555. * - [1] Personal
  1556. * - [2] Private
  1557. * - [3] Company Confidential
  1558. *
  1559. * @return string|boolean
  1560. */
  1561. public function getSensitivity()
  1562. {
  1563. return $this->_arySensitivity[$this->_msgSensitivity];
  1564. }
  1565. /**
  1566. * Message Content Priority
  1567. * Message Priority values:
  1568. * - [0] 'Bulk'
  1569. * - [1] 'Highest'
  1570. * - [2] 'High'
  1571. * - [3] 'Normal' - default
  1572. * - [4] 'Low'
  1573. * - [5] 'Lowest'
  1574. *
  1575. * @param integer $_value Message Priority
  1576. * @return void
  1577. */
  1578. public function setPriority($_value = 3)
  1579. {
  1580. if ((is_numeric($_value)) &&
  1581. (($_value >= 0) && ($_value <= 5))) {
  1582. $this->_msgPriority = $_value;
  1583. }
  1584. }
  1585. /**
  1586. * Message Content Priority
  1587. * Message Priority values:
  1588. * - [0] 'Bulk'
  1589. * - [1] 'Highest'
  1590. * - [2] 'High'
  1591. * - [3] 'Normal' - default
  1592. * - [4] 'Low'
  1593. * - [5] 'Lowest'
  1594. *
  1595. * @return string
  1596. */
  1597. public function getPriority()
  1598. {
  1599. return 'Importance: '.$this->_aryPriority[$this->_msgPriority]."\r\n"
  1600. . 'Priority: '.$this->_aryPriority[$this->_msgPriority]."\r\n"
  1601. . 'X-Priority: '.$this->_msgPriority.' ('.$this->_aryPriority[$this->_msgPriority].')'."\r\n";
  1602. }
  1603. /**
  1604. * Set flag which determines whether to calculate message MD5 checksum.
  1605. *
  1606. * @param string $_flag Message Priority
  1607. * @return void
  1608. */
  1609. public function setMD5flag($_flag = false)
  1610. {
  1611. $this->_smtpMD5 = $_flag;
  1612. }
  1613. /**
  1614. * Gets flag which determines whether to calculate message MD5 checksum.
  1615. *
  1616. * @return boolean Message Priority
  1617. */
  1618. public function getMD5flag()
  1619. {
  1620. return $this->_smtpMD5;
  1621. }
  1622. /**
  1623. * Message X-Header Content
  1624. * This is a simple "insert". Whatever is given will be placed
  1625. * "as is" into the Xheader array.
  1626. *
  1627. * @param string $strXdata Message X-Header Content
  1628. * @return void
  1629. */
  1630. public function setXheader($strXdata)
  1631. {
  1632. if ($strXdata) {
  1633. $this->_msgXheader[] = $strXdata;
  1634. }
  1635. }
  1636. /**
  1637. * Retrieves the Message X-Header Content
  1638. *
  1639. * @return array $_msgContent Message X-Header Content
  1640. */
  1641. public function getXheader()
  1642. {
  1643. return $this->_msgXheader;
  1644. }
  1645. /**
  1646. * Generates Random string for MIME message Boundary
  1647. *
  1648. * @return void
  1649. */
  1650. private function _setBoundary()
  1651. {
  1652. $this->_smtpsBoundary = "multipart_x.".time().".x_boundary";
  1653. $this->_smtpsRelatedBoundary = 'mul_'.dol_hash(uniqid("dolibarr2"), 3);
  1654. $this->_smtpsAlternativeBoundary = 'mul_'.dol_hash(uniqid("dolibarr3"), 3);
  1655. }
  1656. /**
  1657. * Retrieves the MIME message Boundary
  1658. *
  1659. * @param string $type Type of boundary
  1660. * @return string $_smtpsBoundary MIME message Boundary
  1661. */
  1662. private function _getBoundary($type = 'mixed')
  1663. {
  1664. if ($type == 'mixed') {
  1665. return $this->_smtpsBoundary;
  1666. } elseif ($type == 'related') {
  1667. return $this->_smtpsRelatedBoundary;
  1668. } elseif ($type == 'alternative') {
  1669. return $this->_smtpsAlternativeBoundary;
  1670. }
  1671. return '';
  1672. }
  1673. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1674. /**
  1675. * This function has been modified as provided by SirSir to allow multiline responses when
  1676. * using SMTP Extensions
  1677. *
  1678. * @param resource $socket Socket handler
  1679. * @param string $response Expected response ('250', ...). Example of response we can get:
  1680. * "421 4.7.0 Try again later, closing connection. (EHLO) nb21-20020a1709071c9500b0093d0d964affsm869534ejc.73 - gsmtp"
  1681. * "550 5.7.1 https://support.google.com/a/answer/6140680#invalidcred j21sm814390wre.3"
  1682. * @return boolean True or false
  1683. */
  1684. public function server_parse($socket, $response)
  1685. {
  1686. // phpcs:enable
  1687. /**
  1688. * Returns constructed SELECT Object string or boolean upon failure
  1689. * Default value is set at true
  1690. */
  1691. $_retVal = true;
  1692. $server_response = '';
  1693. // avoid infinite loop
  1694. $limit = 0;
  1695. while (substr($server_response, 3, 1) != ' ' && $limit < 100) {
  1696. if (!($server_response = fgets($socket, 256))) {
  1697. $this->_setErr(121, "Couldn't get mail server response codes");
  1698. $_retVal = false;
  1699. break;
  1700. }
  1701. $this->log .= $server_response;
  1702. $limit++;
  1703. }
  1704. $this->lastretval = substr($server_response, 0, 3);
  1705. if (!(substr($server_response, 0, 3) == $response)) {
  1706. $this->_setErr(120, "Ran into problems sending Mail.\r\nResponse: ".$server_response);
  1707. $_retVal = false;
  1708. }
  1709. return $_retVal;
  1710. }
  1711. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
  1712. /**
  1713. * Send str
  1714. *
  1715. * @param string $_strSend String to send
  1716. * @param string $_returnCode Expected return code
  1717. * @param string $CRLF CRLF
  1718. * @return boolean|null True or false
  1719. */
  1720. public function socket_send_str($_strSend, $_returnCode = null, $CRLF = "\r\n")
  1721. {
  1722. // phpcs:enable
  1723. if ($this->_debug) {
  1724. $this->log .= $_strSend; // @CHANGE LDR for log
  1725. }
  1726. fputs($this->socket, $_strSend.$CRLF);
  1727. if ($this->_debug) {
  1728. $this->log .= ' ('.$_returnCode.')'.$CRLF;
  1729. }
  1730. if ($_returnCode) {
  1731. return $this->server_parse($this->socket, $_returnCode);
  1732. }
  1733. return null;
  1734. }
  1735. // =============================================================
  1736. // ** Error handling methods
  1737. /**
  1738. * Defines errors codes and messages for Class
  1739. *
  1740. * @param int $_errNum Error Code Number
  1741. * @param string $_errMsg Error Message
  1742. * @return void
  1743. */
  1744. private function _setErr($_errNum, $_errMsg)
  1745. {
  1746. $this->_smtpsErrors[] = array(
  1747. 'num' => $_errNum,
  1748. 'msg' => $_errMsg,
  1749. );
  1750. }
  1751. /**
  1752. * Returns applicative errors codes and messages for Class (not the SMTP error code)
  1753. *
  1754. * @return string $_errMsg Error Message
  1755. */
  1756. public function getErrors()
  1757. {
  1758. $_errMsg = array();
  1759. if (is_array($this->_smtpsErrors)) {
  1760. foreach ($this->_smtpsErrors as $_err => $_info) {
  1761. $_errMsg[] = 'Error ['.$_info['num'].']: '.$_info['msg'];
  1762. }
  1763. }
  1764. return implode("\n", $_errMsg);
  1765. }
  1766. }
  1767. // =============================================================
  1768. // ** CSV Version Control Info
  1769. /**
  1770. * Revision 2011/09/12 07:49:59 eldy
  1771. * Doxygen
  1772. *
  1773. * Revision 2011/09/06 06:53:53 hregis
  1774. * Fix: use dol_hash instead md5 php function
  1775. *
  1776. * Revision 2011/09/03 00:14:27 eldy
  1777. * Doxygen
  1778. *
  1779. * Revision 2011/08/28 14:24:23 eldy
  1780. * Doxygen
  1781. *
  1782. * Revision 2011/07/12 22:19:02 eldy
  1783. * Fix: Attachment fails if content was empty
  1784. *
  1785. * Revision 2011/06/20 23:17:50 hregis
  1786. * Fix: use best structure of mail
  1787. *
  1788. * Revision 2010/04/13 20:58:37 eldy
  1789. * Fix: Can provide ip address on smtps. Better error reporting.
  1790. *
  1791. * Revision 2010/04/13 20:30:25 eldy
  1792. * Fix: Can provide ip address on smtps. Better error reporting.
  1793. *
  1794. * Revision 2010/01/12 13:02:07 hregis
  1795. * Fix: missing attach-files
  1796. *
  1797. * Revision 2009/11/01 14:16:30 eldy
  1798. * Fix: Sending mail with SMTPS was not working.
  1799. *
  1800. * Revision 2009/10/20 13:14:47 hregis
  1801. * Fix: function "split" is deprecated since php 5.3.0
  1802. *
  1803. * Revision 2009/05/13 19:10:07 eldy
  1804. * New: Can use inline images.Everything seems to work with thunderbird and webmail gmail. New to be tested on other mail browsers.
  1805. *
  1806. * Revision 2009/05/13 14:49:30 eldy
  1807. * Fix: Make code so much simpler and solve a lot of problem with new version.
  1808. *
  1809. * Revision 2009/02/09 00:04:35 eldy
  1810. * Added support for SMTPS protocol
  1811. *
  1812. * Revision 2008/04/16 23:11:45 eldy
  1813. * New: Add action "Test server connectivity"
  1814. *
  1815. * Revision 1.18 2007/01/12 22:17:08 ongardie
  1816. * - Added full_http_site_root() to utils-misc.php
  1817. * - Made SMTPs' getError() easier to use
  1818. * - Improved activity modified emails
  1819. *
  1820. * Revision 1.17 2006/04/05 03:15:40 ongardie
  1821. * -Fixed method name typo that resulted in a fatal error.
  1822. *
  1823. * Revision 1.16 2006/03/08 04:05:25 jswalter
  1824. * - '$_smtpsTransEncode' was removed and '$_smtpsTransEncodeType' is now used
  1825. * - '$_smtpsTransEncodeType' is defaulted to ZERO
  1826. * - corrected 'setCharSet()' internal vars
  1827. * - defined '$_mailPath'
  1828. * - added '$_smtpMD5' as a class property
  1829. * - added 'setMD5flag()' to set above property
  1830. * - added 'getMD5flag()' to retrieve above property
  1831. * - 'setAttachment()' will add an MD5 checksum to attachements if above property is set
  1832. * - 'setBodyContent()' will add an MD5 checksum to message parts if above property is set
  1833. * - 'getBodyContent()' will insert the MD5 checksum for messages and attachments if above property is set
  1834. * - removed leading dashes from message boundry
  1835. * - added propery "Close message boundry" tomessage block
  1836. * - corrected some comments in various places
  1837. * - removed some incorrect comments in others
  1838. *
  1839. * Revision 1.15 2006/02/21 02:00:07 vanmer
  1840. * - patch to add support for sending to exim mail server
  1841. * - thanks to Diego Ongaro at ETSZONE (diego@etszone.com)
  1842. *
  1843. * Revision 1.14 2005/08/29 16:22:10 jswalter
  1844. * - change 'multipart/alternative' to 'multipart/mixed', but Windows based mail servers have issues with this.
  1845. * Bug 594
  1846. *
  1847. * Revision 1.13 2005/08/21 01:57:30 vanmer
  1848. * - added initialization for array if no recipients exist
  1849. *
  1850. * Revision 1.12 2005/08/20 12:04:30 braverock
  1851. * - remove potentially binary characters from Message-ID
  1852. * - add getHost to get the hostname of the mailserver
  1853. * - add username to Message-ID header
  1854. *
  1855. * Revision 1.11 2005/08/20 11:49:48 braverock
  1856. * - fix typos in boundary
  1857. * - remove potentially illegal characters from boundary
  1858. *
  1859. * Revision 1.10 2005/08/19 20:39:32 jswalter
  1860. * - added _server_connect()' as a seperate method to handle server connectivity.
  1861. * - added '_server_authenticate()' as a seperate method to handle server authentication.
  1862. * - 'sendMsg()' now uses the new methods to handle server communication.
  1863. * - modified 'server_parse()' and 'socket_send_str()' to give error codes and messages.
  1864. *
  1865. * Revision 1.9 2005/08/19 15:40:18 jswalter
  1866. * - IMPORTANT: 'setAttachement()' is now spelled correctly: 'setAttachment()'
  1867. * - added additional comment to several methods
  1868. * - added '$_smtpsTransEncodeTypes' array to limit encode types
  1869. * - added parameters to 'sendMsg()' for future development around debugging and logging
  1870. * - added error code within 'setConfig()' if the given path is not found
  1871. * - 'setTransportType()' now has parameter validation
  1872. * [this still is not implemented]
  1873. * - 'setPort()' now does parameter validation
  1874. * - 'setTransEncode()' now has parameter validation against '$_smtpsTransEncodeTypes'
  1875. * - modified 'get_email_list()' to handle error handling
  1876. * - 'setSensitivity()' now has parameter validation
  1877. * - 'setPriority()' now has parameter validation
  1878. *
  1879. * Revision 1.8 2005/06/24 21:00:20 jswalter
  1880. * - corrected comments
  1881. * - corrected the defualt value for 'setPriority()'
  1882. * - modified 'setAttachement()' to process multiple attachments correctly
  1883. * - modified 'getBodyContent()' to handle multiple attachments
  1884. * Bug 310
  1885. *
  1886. * Revision 1.7 2005/05/19 21:12:34 braverock
  1887. * - replace chunk_split() with wordwrap() to fix funky wrapping of templates
  1888. *
  1889. * Revision 1.6 2005/04/25 04:55:06 jswalter
  1890. * - cloned from Master Version
  1891. *
  1892. * Revision 1.10 2005/04/25 04:54:10 walter
  1893. * - "fixed" 'getBodyContent()' to handle a "simple" text only message
  1894. *
  1895. * Revision 1.9 2005/04/25 03:52:01 walter
  1896. * - replace closing curly bracket. Removed it in last revision!
  1897. *
  1898. * Revision 1.8 2005/04/25 02:29:49 walter
  1899. * - added '$_transportType' and its getter/setter methods.
  1900. * for future use. NOT yet implemented.
  1901. * - in 'sendMsg()', added HOST validation check
  1902. * - added error check for initial Socket Connection
  1903. * - created new method 'socket_send_str()' to process socket
  1904. * communication in a unified means. Socket calls within
  1905. * 'sendMsg()' have been modified to use this new method.
  1906. * - expanded comments in 'setConfig()'
  1907. * - added "error" check on PHP ini file properties. If these
  1908. * properties not set within the INI file, the default values
  1909. * will be used.
  1910. * - modified 'get_RCPT_list()' to reset itself each time it is called
  1911. * - modified 'setBodyContent()' to store data in a sub-array for better
  1912. * parsing within the 'getBodyContent()' method
  1913. * - modified 'getBodyContent()' to process contents array better.
  1914. * Also modified to handle attachements.
  1915. * - added 'setAttachement()' so files and other data can be attached
  1916. * to messages
  1917. * - added '_setErr()' and 'getErrors()' as an attempt to begin an error
  1918. * handling process within this class
  1919. *
  1920. * Revision 1.7 2005/04/13 15:23:50 walter
  1921. * - made 'CC' a conditional insert
  1922. * - made 'BCC' a conditional insert
  1923. * - fixed 'Message-ID'
  1924. * - corrected 'getSensitivity()'
  1925. * - modified '$_aryPriority[]' to proper values
  1926. * - updated 'setConfig()' to handle external Ini or 'php.ini'
  1927. *
  1928. * Revision 1.6 2005/03/15 17:34:06 walter
  1929. * - corrected Message Sensitivity property and method comments
  1930. * - added array to Message Sensitivity
  1931. * - added getSensitivity() method to use new Sensitivity array
  1932. * - created seters and getter for Priority with new Prioity value array property
  1933. * - changed config file include from 'include_once'
  1934. * - modified getHeader() to ustilize new Message Sensitivity and Priorty properties
  1935. *
  1936. * Revision 1.5 2005/03/14 22:25:27 walter
  1937. * - added references
  1938. * - added Message sensitivity as a property with Getter/Setter methods
  1939. * - boundary is now a property with Getter/Setter methods
  1940. * - added 'builtRCPTlist()'
  1941. * - 'sendMsg()' now uses Object properties and methods to build message
  1942. * - 'setConfig()' to load external file
  1943. * - 'setForm()' will "strip" the email address out of "address" string
  1944. * - modifed 'getFrom()' to handle "striping" the email address
  1945. * - '_buildArrayList()' creates a multi-dimensional array of addresses
  1946. * by domain, TO, CC & BCC and then by User Name.
  1947. * - '_strip_email()' pulls email address out of "full Address" string'
  1948. * - 'get_RCPT_list()' pulls out "bare" emaill address form address array
  1949. * - 'getHeader()' builds message Header from Object properties
  1950. * - 'getBodyContent()' builds full messsage body, even multi-part
  1951. *
  1952. * Revision 1.4 2005/03/02 20:53:35 walter
  1953. * - core Setters & Getters defined
  1954. * - added additional Class Properties
  1955. *
  1956. * Revision 1.3 2005/03/02 18:51:51 walter
  1957. * - added base 'Class Properties'
  1958. *
  1959. * Revision 1.2 2005/03/01 19:37:52 walter
  1960. * - CVS logging tags
  1961. * - more comments
  1962. * - more "shell"
  1963. * - some constants
  1964. *
  1965. * Revision 1.1 2005/03/01 19:22:49 walter
  1966. * - initial commit
  1967. * - basic shell with some commets
  1968. *
  1969. */