comparison cake/libs/controller/components/email.php @ 0:261e66bd5a0c

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