EmailComponent.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. <?php
  2. /**
  3. * Email Component
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package cake
  16. * @subpackage cake.cake.libs.controller.components
  17. * @since CakePHP(tm) v 1.2.0.3467
  18. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  19. */
  20. App::uses('Multibyte', 'Core');
  21. /**
  22. * EmailComponent
  23. *
  24. * This component is used for handling Internet Message Format based
  25. * based on the standard outlined in http://www.rfc-editor.org/rfc/rfc2822.txt
  26. *
  27. * @package cake
  28. * @subpackage cake.cake.libs.controller.components
  29. * @link http://book.cakephp.org/view/1283/Email
  30. *
  31. */
  32. class EmailComponent extends Component {
  33. /**
  34. * Recipient of the email
  35. *
  36. * @var string
  37. * @access public
  38. */
  39. public $to = null;
  40. /**
  41. * The mail which the email is sent from
  42. *
  43. * @var string
  44. * @access public
  45. */
  46. public $from = null;
  47. /**
  48. * The email the recipient will reply to
  49. *
  50. * @var string
  51. * @access public
  52. */
  53. public $replyTo = null;
  54. /**
  55. * The read receipt email
  56. *
  57. * @var string
  58. * @access public
  59. */
  60. public $readReceipt = null;
  61. /**
  62. * The mail that will be used in case of any errors like
  63. * - Remote mailserver down
  64. * - Remote user has exceeded his quota
  65. * - Unknown user
  66. *
  67. * @var string
  68. * @access public
  69. */
  70. public $return = null;
  71. /**
  72. * Carbon Copy
  73. *
  74. * List of email's that should receive a copy of the email.
  75. * The Recipient WILL be able to see this list
  76. *
  77. * @var array
  78. * @access public
  79. */
  80. public $cc = array();
  81. /**
  82. * Blind Carbon Copy
  83. *
  84. * List of email's that should receive a copy of the email.
  85. * The Recipient WILL NOT be able to see this list
  86. *
  87. * @var array
  88. * @access public
  89. */
  90. public $bcc = array();
  91. /**
  92. * The subject of the email
  93. *
  94. * @var string
  95. * @access public
  96. */
  97. public $subject = null;
  98. /**
  99. * Associative array of a user defined headers
  100. * Keys will be prefixed 'X-' as per RFC2822 Section 4.7.5
  101. *
  102. * @var array
  103. * @access public
  104. */
  105. public $headers = array();
  106. /**
  107. * List of additional headers
  108. *
  109. * These will NOT be used if you are using safemode and mail()
  110. *
  111. * @var string
  112. * @access public
  113. */
  114. public $additionalParams = null;
  115. /**
  116. * Layout for the View
  117. *
  118. * @var string
  119. * @access public
  120. */
  121. public $layout = 'default';
  122. /**
  123. * Template for the view
  124. *
  125. * @var string
  126. * @access public
  127. */
  128. public $template = null;
  129. /**
  130. * as per RFC2822 Section 2.1.1
  131. *
  132. * @var integer
  133. * @access public
  134. */
  135. public $lineLength = 70;
  136. /**
  137. * @deprecated see lineLength
  138. */
  139. protected $_lineLength = null;
  140. /**
  141. * What format should the email be sent in
  142. *
  143. * Supported formats:
  144. * - text
  145. * - html
  146. * - both
  147. *
  148. * @var string
  149. * @access public
  150. */
  151. public $sendAs = 'text';
  152. /**
  153. * What method should the email be sent by
  154. *
  155. * Supported methods:
  156. * - mail
  157. * - smtp
  158. * - debug
  159. *
  160. * @var string
  161. * @access public
  162. */
  163. public $delivery = 'mail';
  164. /**
  165. * charset the email is sent in
  166. *
  167. * @var string
  168. * @access public
  169. */
  170. public $charset = 'utf-8';
  171. /**
  172. * List of files that should be attached to the email.
  173. *
  174. * Can be both absolute and relative paths
  175. *
  176. * @var array
  177. * @access public
  178. */
  179. public $attachments = array();
  180. /**
  181. * What mailer should EmailComponent identify itself as
  182. *
  183. * @var string
  184. * @access public
  185. */
  186. public $xMailer = 'CakePHP Email Component';
  187. /**
  188. * The list of paths to search if an attachment isnt absolute
  189. *
  190. * @var array
  191. * @access public
  192. */
  193. public $filePaths = array();
  194. /**
  195. * List of options to use for smtp mail method
  196. *
  197. * Options is:
  198. * - port
  199. * - host
  200. * - timeout
  201. * - username
  202. * - password
  203. * - client
  204. *
  205. * @var array
  206. * @access public
  207. * @link http://book.cakephp.org/view/1290/Sending-A-Message-Using-SMTP
  208. */
  209. public $smtpOptions = array();
  210. /**
  211. * Placeholder for any errors that might happen with the
  212. * smtp mail methods
  213. *
  214. * @var string
  215. * @access public
  216. */
  217. public $smtpError = null;
  218. /**
  219. * Contains the rendered plain text message if one was sent.
  220. *
  221. * @var string
  222. * @access public
  223. */
  224. public $textMessage = null;
  225. /**
  226. * Contains the rendered HTML message if one was sent.
  227. *
  228. * @var string
  229. * @access public
  230. */
  231. public $htmlMessage = null;
  232. /**
  233. * Whether to generate a Message-ID header for the
  234. * e-mail. True to generate a Message-ID, False to let
  235. * it be handled by sendmail (or similar) or a string
  236. * to completely override the Message-ID.
  237. *
  238. * @var mixed
  239. * @access public
  240. */
  241. public $messageId = true;
  242. /**
  243. * Temporary store of message header lines
  244. *
  245. * @var array
  246. * @access protected
  247. */
  248. protected $_header = array();
  249. /**
  250. * If set, boundary to use for multipart mime messages
  251. *
  252. * @var string
  253. * @access protected
  254. */
  255. protected $_boundary = null;
  256. /**
  257. * Temporary store of message lines
  258. *
  259. * @var array
  260. * @access protected
  261. */
  262. protected $_message = array();
  263. /**
  264. * Variable that holds SMTP connection
  265. *
  266. * @var resource
  267. * @access protected
  268. */
  269. protected $_smtpConnection = null;
  270. /**
  271. * Constructor
  272. *
  273. * @param ComponentCollection $collection A ComponentCollection this component can use to lazy load its components
  274. * @param array $settings Array of configuration settings.
  275. */
  276. public function __construct(ComponentCollection $collection, $settings = array()) {
  277. $this->Controller = $collection->getController();
  278. parent::__construct($collection, $settings);
  279. }
  280. /**
  281. * Initialize component
  282. *
  283. * @param object $controller Instantiating controller
  284. */
  285. public function initialize(&$controller) {
  286. if (Configure::read('App.encoding') !== null) {
  287. $this->charset = Configure::read('App.encoding');
  288. }
  289. }
  290. /**
  291. * Startup component
  292. *
  293. * @param object $controller Instantiating controller
  294. */
  295. public function startup(&$controller) {}
  296. /**
  297. * Send an email using the specified content, template and layout
  298. *
  299. * @param mixed $content Either an array of text lines, or a string with contents
  300. * If you are rendering a template this variable will be sent to the templates as `$content`
  301. * @param string $template Template to use when sending email
  302. * @param string $layout Layout to use to enclose email body
  303. * @return boolean Success
  304. */
  305. public function send($content = null, $template = null, $layout = null) {
  306. $this->_createHeader();
  307. if ($template) {
  308. $this->template = $template;
  309. }
  310. if ($layout) {
  311. $this->layout = $layout;
  312. }
  313. if (is_array($content)) {
  314. $content = implode("\n", $content) . "\n";
  315. }
  316. $this->htmlMessage = $this->textMessage = null;
  317. if ($content) {
  318. if ($this->sendAs === 'html') {
  319. $this->htmlMessage = $content;
  320. } elseif ($this->sendAs === 'text') {
  321. $this->textMessage = $content;
  322. } else {
  323. $this->htmlMessage = $this->textMessage = $content;
  324. }
  325. }
  326. if ($this->sendAs === 'text') {
  327. $message = $this->_wrap($content);
  328. } else {
  329. $message = $this->_wrap($content, 998);
  330. }
  331. if ($this->template === null) {
  332. $message = $this->_formatMessage($message);
  333. } else {
  334. $message = $this->_render($message);
  335. }
  336. $message[] = '';
  337. $this->_message = $message;
  338. if (!empty($this->attachments)) {
  339. $this->_attachFiles();
  340. }
  341. if (!is_null($this->_boundary)) {
  342. $this->_message[] = '';
  343. $this->_message[] = '--' . $this->_boundary . '--';
  344. $this->_message[] = '';
  345. }
  346. $_method = '_' . $this->delivery;
  347. $sent = $this->$_method();
  348. $this->_header = array();
  349. $this->_message = array();
  350. return $sent;
  351. }
  352. /**
  353. * Reset all EmailComponent internal variables to be able to send out a new email.
  354. *
  355. * @link http://book.cakephp.org/view/1285/Sending-Multiple-Emails-in-a-loop
  356. */
  357. public function reset() {
  358. $this->template = null;
  359. $this->to = array();
  360. $this->from = null;
  361. $this->replyTo = null;
  362. $this->return = null;
  363. $this->cc = array();
  364. $this->bcc = array();
  365. $this->subject = null;
  366. $this->additionalParams = null;
  367. $this->smtpError = null;
  368. $this->attachments = array();
  369. $this->htmlMessage = null;
  370. $this->textMessage = null;
  371. $this->messageId = true;
  372. $this->_header = array();
  373. $this->_boundary = null;
  374. $this->_message = array();
  375. }
  376. /**
  377. * Render the contents using the current layout and template.
  378. *
  379. * @param string $content Content to render
  380. * @return array Email ready to be sent
  381. * @access private
  382. */
  383. function _render($content) {
  384. $viewClass = $this->Controller->view;
  385. if ($viewClass != 'View') {
  386. list($plugin, $viewClass) = pluginSplit($viewClass);
  387. $viewClass = $viewClass . 'View';
  388. App::import('View', $this->Controller->view);
  389. }
  390. $View = new $viewClass($this->Controller, false);
  391. $View->layout = $this->layout;
  392. $msg = array();
  393. $content = implode("\n", $content);
  394. if ($this->sendAs === 'both') {
  395. $htmlContent = $content;
  396. if (!empty($this->attachments)) {
  397. $msg[] = '--' . $this->_boundary;
  398. $msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->_boundary . '"';
  399. $msg[] = '';
  400. }
  401. $msg[] = '--alt-' . $this->_boundary;
  402. $msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
  403. $msg[] = 'Content-Transfer-Encoding: 7bit';
  404. $msg[] = '';
  405. $content = $View->element('email' . DS . 'text' . DS . $this->template, array('content' => $content), true);
  406. $View->layoutPath = 'email' . DS . 'text';
  407. $content = explode("\n", $this->textMessage = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
  408. $msg = array_merge($msg, $content);
  409. $msg[] = '';
  410. $msg[] = '--alt-' . $this->_boundary;
  411. $msg[] = 'Content-Type: text/html; charset=' . $this->charset;
  412. $msg[] = 'Content-Transfer-Encoding: 7bit';
  413. $msg[] = '';
  414. $htmlContent = $View->element('email' . DS . 'html' . DS . $this->template, array('content' => $htmlContent), true);
  415. $View->layoutPath = 'email' . DS . 'html';
  416. $htmlContent = explode("\n", $this->htmlMessage = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($htmlContent)));
  417. $msg = array_merge($msg, $htmlContent);
  418. $msg[] = '';
  419. $msg[] = '--alt-' . $this->_boundary . '--';
  420. $msg[] = '';
  421. return $msg;
  422. }
  423. if (!empty($this->attachments)) {
  424. if ($this->sendAs === 'html') {
  425. $msg[] = '';
  426. $msg[] = '--' . $this->_boundary;
  427. $msg[] = 'Content-Type: text/html; charset=' . $this->charset;
  428. $msg[] = 'Content-Transfer-Encoding: 7bit';
  429. $msg[] = '';
  430. } else {
  431. $msg[] = '--' . $this->_boundary;
  432. $msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
  433. $msg[] = 'Content-Transfer-Encoding: 7bit';
  434. $msg[] = '';
  435. }
  436. }
  437. $content = $View->element('email' . DS . $this->sendAs . DS . $this->template, array('content' => $content), true);
  438. $View->layoutPath = 'email' . DS . $this->sendAs;
  439. $content = explode("\n", $rendered = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
  440. if ($this->sendAs === 'html') {
  441. $this->htmlMessage = $rendered;
  442. } else {
  443. $this->textMessage = $rendered;
  444. }
  445. $msg = array_merge($msg, $content);
  446. return $msg;
  447. }
  448. /**
  449. * Create unique boundary identifier
  450. *
  451. * @access private
  452. */
  453. function _createboundary() {
  454. $this->_boundary = md5(uniqid(time()));
  455. }
  456. /**
  457. * Sets headers for the message
  458. *
  459. * @access public
  460. * @param array Associative array containing headers to be set.
  461. */
  462. function header($headers) {
  463. foreach ($headers as $header => $value) {
  464. $this->_header[] = sprintf('%s: %s', trim($header), trim($value));
  465. }
  466. }
  467. /**
  468. * Create emails headers including (but not limited to) from email address, reply to,
  469. * bcc and cc.
  470. *
  471. * @access private
  472. */
  473. function _createHeader() {
  474. $headers = array();
  475. if ($this->delivery == 'smtp') {
  476. if (is_array($this->to)) {
  477. $headers['To'] = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
  478. } else {
  479. $headers['To'] = $this->_formatAddress($this->to);
  480. }
  481. }
  482. $headers['From'] = $this->_formatAddress($this->from);
  483. if (!empty($this->replyTo)) {
  484. $headers['Reply-To'] = $this->_formatAddress($this->replyTo);
  485. }
  486. if (!empty($this->return)) {
  487. $headers['Return-Path'] = $this->_formatAddress($this->return);
  488. }
  489. if (!empty($this->readReceipt)) {
  490. $headers['Disposition-Notification-To'] = $this->_formatAddress($this->readReceipt);
  491. }
  492. if (!empty($this->cc)) {
  493. $headers['cc'] = implode(', ', array_map(array($this, '_formatAddress'), $this->cc));
  494. }
  495. if (!empty($this->bcc) && $this->delivery != 'smtp') {
  496. $headers['Bcc'] = implode(', ', array_map(array($this, '_formatAddress'), $this->bcc));
  497. }
  498. if ($this->delivery == 'smtp') {
  499. $headers['Subject'] = $this->_encode($this->subject);
  500. }
  501. if ($this->messageId !== false) {
  502. if ($this->messageId === true) {
  503. $headers['Message-ID'] = '<' . String::UUID() . '@' . env('HTTP_HOST') . '>';
  504. } else {
  505. $headers['Message-ID'] = $this->messageId;
  506. }
  507. }
  508. $headers['X-Mailer'] = $this->xMailer;
  509. if (!empty($this->headers)) {
  510. foreach ($this->headers as $key => $val) {
  511. $headers['X-' . $key] = $val;
  512. }
  513. }
  514. if (!empty($this->attachments)) {
  515. $this->_createBoundary();
  516. $headers['MIME-Version'] = '1.0';
  517. $headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"';
  518. $headers[] = 'This part of the E-mail should never be seen. If';
  519. $headers[] = 'you are reading this, consider upgrading your e-mail';
  520. $headers[] = 'client to a MIME-compatible client.';
  521. } elseif ($this->sendAs === 'text') {
  522. $headers['Content-Type'] = 'text/plain; charset=' . $this->charset;
  523. } elseif ($this->sendAs === 'html') {
  524. $headers['Content-Type'] = 'text/html; charset=' . $this->charset;
  525. } elseif ($this->sendAs === 'both') {
  526. $headers['Content-Type'] = 'multipart/alternative; boundary="alt-' . $this->_boundary . '"';
  527. }
  528. $headers['Content-Transfer-Encoding'] = '7bit';
  529. $this->header($headers);
  530. }
  531. /**
  532. * Format the message by seeing if it has attachments.
  533. *
  534. * @param string $message Message to format
  535. * @access private
  536. */
  537. function _formatMessage($message) {
  538. if (!empty($this->attachments)) {
  539. $prefix = array('--' . $this->_boundary);
  540. if ($this->sendAs === 'text') {
  541. $prefix[] = 'Content-Type: text/plain; charset=' . $this->charset;
  542. } elseif ($this->sendAs === 'html') {
  543. $prefix[] = 'Content-Type: text/html; charset=' . $this->charset;
  544. } elseif ($this->sendAs === 'both') {
  545. $prefix[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->_boundary . '"';
  546. }
  547. $prefix[] = 'Content-Transfer-Encoding: 7bit';
  548. $prefix[] = '';
  549. $message = array_merge($prefix, $message);
  550. }
  551. return $message;
  552. }
  553. /**
  554. * Attach files by adding file contents inside boundaries.
  555. *
  556. * @access private
  557. * @TODO: modify to use the core File class?
  558. */
  559. function _attachFiles() {
  560. $files = array();
  561. foreach ($this->attachments as $filename => $attachment) {
  562. $file = $this->_findFiles($attachment);
  563. if (!empty($file)) {
  564. if (is_int($filename)) {
  565. $filename = basename($file);
  566. }
  567. $files[$filename] = $file;
  568. }
  569. }
  570. foreach ($files as $filename => $file) {
  571. $handle = fopen($file, 'rb');
  572. $data = fread($handle, filesize($file));
  573. $data = chunk_split(base64_encode($data)) ;
  574. fclose($handle);
  575. $this->_message[] = '--' . $this->_boundary;
  576. $this->_message[] = 'Content-Type: application/octet-stream';
  577. $this->_message[] = 'Content-Transfer-Encoding: base64';
  578. $this->_message[] = 'Content-Disposition: attachment; filename="' . basename($filename) . '"';
  579. $this->_message[] = '';
  580. $this->_message[] = $data;
  581. $this->_message[] = '';
  582. }
  583. }
  584. /**
  585. * Find the specified attachment in the list of file paths
  586. *
  587. * @param string $attachment Attachment file name to find
  588. * @return string Path to located file
  589. * @access private
  590. */
  591. function _findFiles($attachment) {
  592. if (file_exists($attachment)) {
  593. return $attachment;
  594. }
  595. foreach ($this->filePaths as $path) {
  596. if (file_exists($path . DS . $attachment)) {
  597. $file = $path . DS . $attachment;
  598. return $file;
  599. }
  600. }
  601. return null;
  602. }
  603. /**
  604. * Wrap the message using EmailComponent::$lineLength
  605. *
  606. * @param string $message Message to wrap
  607. * @param integer $lineLength Max length of line
  608. * @return array Wrapped message
  609. * @access protected
  610. */
  611. function _wrap($message, $lineLength = null) {
  612. $message = $this->_strip($message, true);
  613. $message = str_replace(array("\r\n","\r"), "\n", $message);
  614. $lines = explode("\n", $message);
  615. $formatted = array();
  616. if ($this->_lineLength !== null) {
  617. trigger_error(__('_lineLength cannot be accessed please use lineLength'), E_USER_WARNING);
  618. $this->lineLength = $this->_lineLength;
  619. }
  620. if (!$lineLength) {
  621. $lineLength = $this->lineLength;
  622. }
  623. foreach ($lines as $line) {
  624. if (substr($line, 0, 1) == '.') {
  625. $line = '.' . $line;
  626. }
  627. $formatted = array_merge($formatted, explode("\n", wordwrap($line, $lineLength, "\n", true)));
  628. }
  629. $formatted[] = '';
  630. return $formatted;
  631. }
  632. /**
  633. * Encode the specified string using the current charset
  634. *
  635. * @param string $subject String to encode
  636. * @return string Encoded string
  637. * @access private
  638. */
  639. function _encode($subject) {
  640. $subject = $this->_strip($subject);
  641. $nl = "\r\n";
  642. if ($this->delivery == 'mail') {
  643. $nl = '';
  644. }
  645. $internalEncoding = function_exists('mb_internal_encoding');
  646. if ($internalEncoding) {
  647. $restore = mb_internal_encoding();
  648. mb_internal_encoding($this->charset);
  649. }
  650. $return = mb_encode_mimeheader($subject, $this->charset, 'B', $nl);
  651. if ($internalEncoding) {
  652. mb_internal_encoding($restore);
  653. }
  654. return $return;
  655. }
  656. /**
  657. * Format a string as an email address
  658. *
  659. * @param string $string String representing an email address
  660. * @return string Email address suitable for email headers or smtp pipe
  661. * @access private
  662. */
  663. function _formatAddress($string, $smtp = false) {
  664. $hasAlias = preg_match('/((.*)\s)?<(.+)>/', $string, $matches);
  665. if ($smtp && $hasAlias) {
  666. return $this->_strip('<' . $matches[3] . '>');
  667. } elseif ($smtp) {
  668. return $this->_strip('<' . $string . '>');
  669. }
  670. if ($hasAlias && !empty($matches[2])) {
  671. return $this->_strip($matches[2] . ' <' . $matches[3] . '>');
  672. }
  673. return $this->_strip($string);
  674. }
  675. /**
  676. * Remove certain elements (such as bcc:, to:, %0a) from given value.
  677. * Helps prevent header injection / mainipulation on user content.
  678. *
  679. * @param string $value Value to strip
  680. * @param boolean $message Set to true to indicate main message content
  681. * @return string Stripped value
  682. * @access private
  683. */
  684. function _strip($value, $message = false) {
  685. $search = '%0a|%0d|Content-(?:Type|Transfer-Encoding)\:';
  686. $search .= '|charset\=|mime-version\:|multipart/mixed|(?:[^a-z]to|b?cc)\:.*';
  687. if ($message !== true) {
  688. $search .= '|\r|\n';
  689. }
  690. $search = '#(?:' . $search . ')#i';
  691. while (preg_match($search, $value)) {
  692. $value = preg_replace($search, '', $value);
  693. }
  694. return $value;
  695. }
  696. /**
  697. * Wrapper for PHP mail function used for sending out emails
  698. *
  699. * @return bool Success
  700. * @access private
  701. */
  702. function _mail() {
  703. $header = implode("\r\n", $this->_header);
  704. $message = implode("\r\n", $this->_message);
  705. if (is_array($this->to)) {
  706. $to = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
  707. } else {
  708. $to = $this->to;
  709. }
  710. if (ini_get('safe_mode')) {
  711. return @mail($to, $this->_encode($this->subject), $message, $header);
  712. }
  713. return @mail($to, $this->_encode($this->subject), $message, $header, $this->additionalParams);
  714. }
  715. /**
  716. * Sends out email via SMTP
  717. *
  718. * @return bool Success
  719. * @access private
  720. */
  721. function _smtp() {
  722. App::import('Core', 'CakeSocket');
  723. $defaults = array(
  724. 'host' => 'localhost',
  725. 'port' => 25,
  726. 'protocol' => 'smtp',
  727. 'timeout' => 30
  728. );
  729. $this->smtpOptions = array_merge($defaults, $this->smtpOptions);
  730. $this->_smtpConnection = new CakeSocket($this->smtpOptions);
  731. if (!$this->_smtpConnection->connect()) {
  732. $this->smtpError = $this->_smtpConnection->lastError();
  733. return false;
  734. } elseif (!$this->_smtpSend(null, '220')) {
  735. return false;
  736. }
  737. $httpHost = env('HTTP_HOST');
  738. if (isset($this->smtpOptions['client'])) {
  739. $host = $this->smtpOptions['client'];
  740. } elseif (!empty($httpHost)) {
  741. $host = $httpHost;
  742. } else {
  743. $host = 'localhost';
  744. }
  745. if (!$this->_smtpSend("EHLO {$host}", '250') && !$this->_smtpSend("HELO {$host}", '250')) {
  746. return false;
  747. }
  748. if (isset($this->smtpOptions['username']) && isset($this->smtpOptions['password'])) {
  749. $authRequired = $this->_smtpSend('AUTH LOGIN', '334|503');
  750. if ($authRequired == '334') {
  751. if (!$this->_smtpSend(base64_encode($this->smtpOptions['username']), '334')) {
  752. return false;
  753. }
  754. if (!$this->_smtpSend(base64_encode($this->smtpOptions['password']), '235')) {
  755. return false;
  756. }
  757. } elseif ($authRequired != '503') {
  758. return false;
  759. }
  760. }
  761. if (!$this->_smtpSend('MAIL FROM: ' . $this->_formatAddress($this->from, true))) {
  762. return false;
  763. }
  764. if (!is_array($this->to)) {
  765. $tos = array($this->to);
  766. } else {
  767. $tos = $this->to;
  768. }
  769. foreach ($tos as $to) {
  770. if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($to, true))) {
  771. return false;
  772. }
  773. }
  774. foreach ($this->cc as $cc) {
  775. if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($cc, true))) {
  776. return false;
  777. }
  778. }
  779. foreach ($this->bcc as $bcc) {
  780. if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($bcc, true))) {
  781. return false;
  782. }
  783. }
  784. if (!$this->_smtpSend('DATA', '354')) {
  785. return false;
  786. }
  787. $header = implode("\r\n", $this->_header);
  788. $message = implode("\r\n", $this->_message);
  789. if (!$this->_smtpSend($header . "\r\n\r\n" . $message . "\r\n\r\n\r\n.")) {
  790. return false;
  791. }
  792. $this->_smtpSend('QUIT', false);
  793. $this->_smtpConnection->disconnect();
  794. return true;
  795. }
  796. /**
  797. * Protected method for sending data to SMTP connection
  798. *
  799. * @param string $data data to be sent to SMTP server
  800. * @param mixed $checkCode code to check for in server response, false to skip
  801. * @return bool Success
  802. * @access protected
  803. */
  804. function _smtpSend($data, $checkCode = '250') {
  805. if (!is_null($data)) {
  806. $this->_smtpConnection->write($data . "\r\n");
  807. }
  808. while ($checkCode !== false) {
  809. $response = '';
  810. $startTime = time();
  811. while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->smtpOptions['timeout'])) {
  812. $response .= $this->_smtpConnection->read();
  813. }
  814. if (substr($response, -2) !== "\r\n") {
  815. $this->smtpError = 'timeout';
  816. return false;
  817. }
  818. $response = end(explode("\r\n", rtrim($response, "\r\n")));
  819. if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
  820. if ($code[2] === '-') {
  821. continue;
  822. }
  823. return $code[1];
  824. }
  825. $this->smtpError = $response;
  826. return false;
  827. }
  828. return true;
  829. }
  830. /**
  831. * Set as controller flash message a debug message showing current settings in component
  832. *
  833. * @return boolean Success
  834. * @access private
  835. */
  836. function _debug() {
  837. $nl = "\n";
  838. $header = implode($nl, $this->_header);
  839. $message = implode($nl, $this->_message);
  840. $fm = '<pre>';
  841. if (is_array($this->to)) {
  842. $to = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
  843. } else {
  844. $to = $this->to;
  845. }
  846. $fm .= sprintf('%s %s%s', 'To:', $to, $nl);
  847. $fm .= sprintf('%s %s%s', 'From:', $this->from, $nl);
  848. $fm .= sprintf('%s %s%s', 'Subject:', $this->_encode($this->subject), $nl);
  849. $fm .= sprintf('%s%3$s%3$s%s', 'Header:', $header, $nl);
  850. $fm .= sprintf('%s%3$s%3$s%s', 'Parameters:', $this->additionalParams, $nl);
  851. $fm .= sprintf('%s%3$s%3$s%s', 'Message:', $message, $nl);
  852. $fm .= '</pre>';
  853. if (isset($this->Controller->Session)) {
  854. $this->Controller->Session->setFlash($fm, 'default', null, 'email');
  855. return true;
  856. }
  857. return $fm;
  858. }
  859. }