Mercurial > hg > Members > shoshi > webvirt
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 } |