smtps.class.php 64 KB

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