Smtp.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. <?php
  2. /**
  3. *
  4. * Cube Framework $Id$ e6pUWQQEujuO/qzzCywy7l5NUnPu06eGkFtSjAhxspk=
  5. *
  6. * @link http://codecu.be/framework
  7. * @copyright Copyright (c) 2017 CodeCube SRL
  8. * @license http://codecu.be/framework/license Commercial License
  9. *
  10. * @version 1.9 [rev.1.9.01]
  11. */
  12. namespace Cube\Mail\Transport;
  13. /**
  14. * mailer transport class - using smtp protocol
  15. *
  16. * Class Smtp
  17. *
  18. * @package Cube\Mail\Transport
  19. */
  20. class Smtp extends AbstractTransport
  21. {
  22. /**
  23. * SMTP line break constant.
  24. *
  25. * @var string
  26. */
  27. const CRLF = "\r\n";
  28. /**
  29. *
  30. * SMTP server connection
  31. *
  32. * @var mixed
  33. */
  34. protected $_connection = null;
  35. /**
  36. *
  37. * Local client hostname or i.p.
  38. *
  39. * @var string
  40. */
  41. protected $_name = 'localhost';
  42. /**
  43. *
  44. * Remote smtp hostname or i.p.
  45. *
  46. * @var string
  47. */
  48. protected $_host;
  49. /**
  50. * smtp server port
  51. *
  52. * @var int
  53. */
  54. protected $_port = 25;
  55. /**
  56. *
  57. * connection protocol "tcp" or "ssl"
  58. *
  59. * @var string
  60. */
  61. protected $_protocol = 'tcp';
  62. /**
  63. *
  64. * tls
  65. *
  66. * @var bool
  67. */
  68. protected $_tls = false;
  69. /**
  70. *
  71. * SMTP username
  72. *
  73. * @var string
  74. */
  75. protected $_username;
  76. /**
  77. *
  78. * SMTP password
  79. *
  80. * @var string
  81. */
  82. protected $_password;
  83. /**
  84. *
  85. * debug messages
  86. *
  87. * @var string
  88. */
  89. protected $_debug;
  90. /**
  91. *
  92. * class constructor
  93. *
  94. * @param string $host
  95. * @param array $config
  96. */
  97. public function __construct($host = 'localhost', array $config = array())
  98. {
  99. parent::__construct($config);
  100. $this->setHost($host);
  101. }
  102. /**
  103. *
  104. * get local server name
  105. *
  106. * @return string
  107. */
  108. public function getName()
  109. {
  110. return $this->_name;
  111. }
  112. /**
  113. *
  114. * set local server name
  115. *
  116. * @param string $name
  117. */
  118. public function setName($name)
  119. {
  120. $this->_name = $name;
  121. }
  122. /**
  123. *
  124. * get remote smtp hostname
  125. *
  126. * @return string
  127. */
  128. public function getHost()
  129. {
  130. return $this->_host;
  131. }
  132. /**
  133. *
  134. * set remote smtp hostname
  135. *
  136. * @param string $host
  137. *
  138. * @return $this
  139. */
  140. public function setHost($host)
  141. {
  142. $this->_host = $host;
  143. return $this;
  144. }
  145. /**
  146. *
  147. * get smtp port
  148. *
  149. * @return int
  150. */
  151. public function getPort()
  152. {
  153. return $this->_port;
  154. }
  155. /**
  156. *
  157. * set remote smtp port
  158. *
  159. * 7.8: if port is 465, use ssl
  160. *
  161. * @param int $port
  162. *
  163. * @return $this
  164. */
  165. public function setPort($port)
  166. {
  167. $this->_port = $port;
  168. if ($port == '465') {
  169. $this->setProtocol('ssl');
  170. }
  171. return $this;
  172. }
  173. /**
  174. *
  175. * get secure string
  176. *
  177. * @return string
  178. */
  179. public function getProtocol()
  180. {
  181. return $this->_protocol;
  182. }
  183. /**
  184. *
  185. * set protocol
  186. *
  187. * @param string $protocol
  188. *
  189. * @return $this
  190. */
  191. public function setProtocol($protocol)
  192. {
  193. if ('tls' == $protocol) {
  194. $this->_protocol = 'tcp';
  195. $this->_tls = true;
  196. }
  197. else {
  198. $this->_protocol = $protocol;
  199. $this->_tls = false;
  200. }
  201. return $this;
  202. }
  203. /**
  204. *
  205. * get smtp username
  206. *
  207. * @return string
  208. */
  209. public function getUsername()
  210. {
  211. return $this->_username;
  212. }
  213. /**
  214. *
  215. * set smtp username
  216. *
  217. * @param string $username
  218. *
  219. * @return $this
  220. */
  221. public function setUsername($username)
  222. {
  223. $this->_username = $username;
  224. return $this;
  225. }
  226. /**
  227. *
  228. * get smtp password
  229. *
  230. * @return string
  231. */
  232. public function getPassword()
  233. {
  234. return $this->_password;
  235. }
  236. /**
  237. *
  238. * set smtp password
  239. *
  240. * @param string $password
  241. *
  242. * @return $this
  243. */
  244. public function setPassword($password)
  245. {
  246. $this->_password = $password;
  247. return $this;
  248. }
  249. /**
  250. *
  251. * get output messages from the smtp server
  252. *
  253. * @return string
  254. */
  255. public function getDebug()
  256. {
  257. return (string)$this->_debug;
  258. }
  259. /**
  260. *
  261. * connect method
  262. *
  263. * @return bool
  264. */
  265. public function connect()
  266. {
  267. $hostname = (($this->_protocol == 'ssl') ? 'ssl://' : '') . $this->_host;
  268. $this->_connection = fsockopen($hostname, $this->_port, $errno, $errstr, 30);
  269. // response
  270. if ($this->_getCode() !== 220) {
  271. return false;
  272. }
  273. fputs($this->_connection, 'EHLO ' . $this->_name . "\r\n");
  274. if ($this->_getCode() !== 250) {
  275. fputs($this->_connection, 'HELO ' . $this->_name . "\r\n");
  276. if ($this->_getCode() !== 250) {
  277. return false;
  278. }
  279. }
  280. if ($this->_tls === true) {
  281. fputs($this->_connection, 'STARTTLS' . "\r\n");
  282. if ($this->_getCode() !== 220) {
  283. return false;
  284. }
  285. stream_socket_enable_crypto($this->_connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
  286. fputs($this->_connection, 'EHLO ' . $this->_name . "\r\n");
  287. if ($this->_getCode() !== 250) {
  288. fputs($this->_connection, 'HELO ' . $this->_name . "\r\n");
  289. if ($this->_getCode() !== 250) {
  290. return false;
  291. }
  292. }
  293. }
  294. if ($this->_host != 'localhost') {
  295. fputs($this->_connection, 'AUTH LOGIN' . "\r\n");
  296. if ($this->_getCode() !== 334) {
  297. return false;
  298. }
  299. fputs($this->_connection, base64_encode($this->_username) . "\r\n");
  300. if ($this->_getCode() !== 334) {
  301. return false;
  302. }
  303. fputs($this->_connection, base64_encode($this->_password) . "\r\n");
  304. if ($this->_getCode() !== 235) {
  305. return false;
  306. }
  307. }
  308. return true;
  309. }
  310. /**
  311. *
  312. * disconnect method
  313. *
  314. * @return $this
  315. */
  316. public function disconnect()
  317. {
  318. if ($this->isConnection()) {
  319. $this->_command('QUIT');
  320. fclose($this->_connection);
  321. }
  322. return $this;
  323. }
  324. /**
  325. *
  326. * check if we have an open connection
  327. *
  328. * @1.9: add additional headers
  329. * @1.9: if message is in html, generate text/plain version as well
  330. *
  331. * @return bool
  332. */
  333. public function isConnection()
  334. {
  335. return ($this->_connection) ? true : false;
  336. }
  337. /**
  338. *
  339. * send mail method
  340. *
  341. * @return bool
  342. */
  343. public function send()
  344. {
  345. $result = false;
  346. if ($this->connect()) {
  347. // deliver the email
  348. $mail = $this->getMail();
  349. $from = $mail->getFrom();
  350. $replyTo = $mail->getReplyTo();
  351. $cc = $mail->getCc();
  352. $bcc = $mail->getBcc();
  353. $contentType = $mail->getContentType();
  354. $charset = $mail->getCharset();
  355. $body = $mail->getBody();
  356. foreach ($mail->getTo() as $to) {
  357. $this->_command("MAIL FROM:<{$from['address']}>");
  358. $this->_command("RCPT TO:<" . $to['address'] . ">");
  359. $this->_command("DATA");
  360. fputs($this->_connection, "To: " . $this->_formatAddress($to) . self::CRLF
  361. . "From: " . $this->_formatAddress($from) . self::CRLF
  362. . "Subject: " . $mail->getSubject() . self::CRLF);
  363. if (count($cc) > 0) {
  364. fputs($this->_connection, "Cc: {$cc['address']}" . self::CRLF);
  365. }
  366. if (count($bcc) > 0) {
  367. fputs($this->_connection, "Bcc: {$cc['address']}" . self::CRLF);
  368. }
  369. if (count($replyTo) > 0) {
  370. fputs($this->_connection, "Reply-to: {$replyTo['address']}" . self::CRLF);
  371. }
  372. fputs($this->_connection, "X-Sender: <{$from['address']}>" . self::CRLF
  373. . "Return-Path: <{$from['address']}>" . self::CRLF
  374. . "Errors-To: <{$from['address']}>" . self::CRLF
  375. . "Date: " . $mail->getDate() . self::CRLF
  376. . "Message-ID: " . $mail->getMessageId() . self::CRLF
  377. . "X-Mailer: Cube Framework/SMTP" . self::CRLF
  378. . "X-Priority: 3" . self::CRLF
  379. . "MIME-Version: 1.0" . self::CRLF
  380. . "Content-Type: " . sprintf('%s; charset="%s"', $contentType, $charset) . self::CRLF
  381. . self::CRLF
  382. . $body . self::CRLF
  383. . "." . self::CRLF);
  384. $this->_getServerResponse();
  385. }
  386. $result = true;
  387. }
  388. // disconnect
  389. $this->disconnect();
  390. // return
  391. return $result;
  392. }
  393. protected function _command($command, $description = null, $result = null)
  394. {
  395. $code = null;
  396. if ($this->isConnection()) {
  397. fputs($this->_connection, $command . self::CRLF);
  398. if ($description === null) {
  399. $description = $command;
  400. }
  401. $this->_debug .= '<code>' . $description . '</code>';
  402. $code = $this->_getCode();
  403. }
  404. return ($code === $result) ? true : false;
  405. }
  406. /**
  407. *
  408. * format an address field
  409. *
  410. * @param array $data array of data
  411. *
  412. * @return string formatted address
  413. */
  414. protected function _formatAddress($data)
  415. {
  416. $address = array();
  417. if (array_key_exists('address', $data)) {
  418. $data = array($data);
  419. }
  420. foreach ((array)$data as $field) {
  421. if (isset($field['name'])) {
  422. $address[] = $field['name'] . ' <' . $field['address'] . '>';
  423. }
  424. else {
  425. $address[] = $field['address'];
  426. }
  427. }
  428. return implode('; ', $address);
  429. }
  430. /**
  431. *
  432. * get server response
  433. *
  434. * @return string
  435. */
  436. protected function _getServerResponse()
  437. {
  438. $response = "";
  439. while ($str = fgets($this->_connection, 4096)) {
  440. $response .= $str;
  441. if (substr($str, 3, 1) == " ") {
  442. break;
  443. }
  444. }
  445. $this->_debug .= '<code>' . $response . '</code><br/>';
  446. return $response;
  447. }
  448. /**
  449. *
  450. * get the code from the server response
  451. *
  452. * @return int
  453. */
  454. protected function _getCode()
  455. {
  456. // filter code from response
  457. return (int)substr($this->_getServerResponse(), 0, 3);
  458. }
  459. }