CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.4 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.4
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • AbstractTransport
  • CakeEmail
  • DebugTransport
  • MailTransport
  • SmtpTransport
   1: <?php
   2: /**
   3:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
   4:  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
   5:  *
   6:  * Licensed under The MIT License
   7:  * For full copyright and license information, please see the LICENSE.txt
   8:  * Redistributions of files must retain the above copyright notice.
   9:  *
  10:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11:  * @link          http://cakephp.org CakePHP(tm) Project
  12:  * @package       Cake.Network.Email
  13:  * @since         CakePHP(tm) v 2.0.0
  14:  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
  15:  */
  16: 
  17: App::uses('Validation', 'Utility');
  18: App::uses('Multibyte', 'I18n');
  19: App::uses('AbstractTransport', 'Network/Email');
  20: App::uses('File', 'Utility');
  21: App::uses('String', 'Utility');
  22: App::uses('View', 'View');
  23: 
  24: /**
  25:  * CakePHP email class.
  26:  *
  27:  * This class is used for handling Internet Message Format based
  28:  * based on the standard outlined in http://www.rfc-editor.org/rfc/rfc2822.txt
  29:  *
  30:  * @package       Cake.Network.Email
  31:  */
  32: class CakeEmail {
  33: 
  34: /**
  35:  * Default X-Mailer
  36:  *
  37:  * @var string
  38:  */
  39:     const EMAIL_CLIENT = 'CakePHP Email';
  40: 
  41: /**
  42:  * Line length - no should more - RFC 2822 - 2.1.1
  43:  *
  44:  * @var integer
  45:  */
  46:     const LINE_LENGTH_SHOULD = 78;
  47: 
  48: /**
  49:  * Line length - no must more - RFC 2822 - 2.1.1
  50:  *
  51:  * @var integer
  52:  */
  53:     const LINE_LENGTH_MUST = 998;
  54: 
  55: /**
  56:  * Type of message - HTML
  57:  *
  58:  * @var string
  59:  */
  60:     const MESSAGE_HTML = 'html';
  61: 
  62: /**
  63:  * Type of message - TEXT
  64:  *
  65:  * @var string
  66:  */
  67:     const MESSAGE_TEXT = 'text';
  68: 
  69: /**
  70:  * Recipient of the email
  71:  *
  72:  * @var array
  73:  */
  74:     protected $_to = array();
  75: 
  76: /**
  77:  * The mail which the email is sent from
  78:  *
  79:  * @var array
  80:  */
  81:     protected $_from = array();
  82: 
  83: /**
  84:  * The sender email
  85:  *
  86:  * @var array
  87:  */
  88:     protected $_sender = array();
  89: 
  90: /**
  91:  * The email the recipient will reply to
  92:  *
  93:  * @var array
  94:  */
  95:     protected $_replyTo = array();
  96: 
  97: /**
  98:  * The read receipt email
  99:  *
 100:  * @var array
 101:  */
 102:     protected $_readReceipt = array();
 103: 
 104: /**
 105:  * The mail that will be used in case of any errors like
 106:  * - Remote mailserver down
 107:  * - Remote user has exceeded his quota
 108:  * - Unknown user
 109:  *
 110:  * @var array
 111:  */
 112:     protected $_returnPath = array();
 113: 
 114: /**
 115:  * Carbon Copy
 116:  *
 117:  * List of email's that should receive a copy of the email.
 118:  * The Recipient WILL be able to see this list
 119:  *
 120:  * @var array
 121:  */
 122:     protected $_cc = array();
 123: 
 124: /**
 125:  * Blind Carbon Copy
 126:  *
 127:  * List of email's that should receive a copy of the email.
 128:  * The Recipient WILL NOT be able to see this list
 129:  *
 130:  * @var array
 131:  */
 132:     protected $_bcc = array();
 133: 
 134: /**
 135:  * Message ID
 136:  *
 137:  * @var boolean|string True to generate, False to ignore, String with value
 138:  */
 139:     protected $_messageId = true;
 140: 
 141: /**
 142:  * Domain for messageId generation.
 143:  * Needs to be manually set for CLI mailing as env('HTTP_HOST') is empty
 144:  *
 145:  * @var string
 146:  */
 147:     protected $_domain = null;
 148: 
 149: /**
 150:  * The subject of the email
 151:  *
 152:  * @var string
 153:  */
 154:     protected $_subject = '';
 155: 
 156: /**
 157:  * Associative array of a user defined headers
 158:  * Keys will be prefixed 'X-' as per RFC2822 Section 4.7.5
 159:  *
 160:  * @var array
 161:  */
 162:     protected $_headers = array();
 163: 
 164: /**
 165:  * Layout for the View
 166:  *
 167:  * @var string
 168:  */
 169:     protected $_layout = 'default';
 170: 
 171: /**
 172:  * Template for the view
 173:  *
 174:  * @var string
 175:  */
 176:     protected $_template = '';
 177: 
 178: /**
 179:  * View for render
 180:  *
 181:  * @var string
 182:  */
 183:     protected $_viewRender = 'View';
 184: 
 185: /**
 186:  * Vars to sent to render
 187:  *
 188:  * @var array
 189:  */
 190:     protected $_viewVars = array();
 191: 
 192: /**
 193:  * Theme for the View
 194:  *
 195:  * @var array
 196:  */
 197:     protected $_theme = null;
 198: 
 199: /**
 200:  * Helpers to be used in the render
 201:  *
 202:  * @var array
 203:  */
 204:     protected $_helpers = array('Html');
 205: 
 206: /**
 207:  * Text message
 208:  *
 209:  * @var string
 210:  */
 211:     protected $_textMessage = '';
 212: 
 213: /**
 214:  * Html message
 215:  *
 216:  * @var string
 217:  */
 218:     protected $_htmlMessage = '';
 219: 
 220: /**
 221:  * Final message to send
 222:  *
 223:  * @var array
 224:  */
 225:     protected $_message = array();
 226: 
 227: /**
 228:  * Available formats to be sent.
 229:  *
 230:  * @var array
 231:  */
 232:     protected $_emailFormatAvailable = array('text', 'html', 'both');
 233: 
 234: /**
 235:  * What format should the email be sent in
 236:  *
 237:  * @var string
 238:  */
 239:     protected $_emailFormat = 'text';
 240: 
 241: /**
 242:  * What method should the email be sent
 243:  *
 244:  * @var string
 245:  */
 246:     protected $_transportName = 'Mail';
 247: 
 248: /**
 249:  * Instance of transport class
 250:  *
 251:  * @var AbstractTransport
 252:  */
 253:     protected $_transportClass = null;
 254: 
 255: /**
 256:  * Charset the email body is sent in
 257:  *
 258:  * @var string
 259:  */
 260:     public $charset = 'utf-8';
 261: 
 262: /**
 263:  * Charset the email header is sent in
 264:  * If null, the $charset property will be used as default
 265:  *
 266:  * @var string
 267:  */
 268:     public $headerCharset = null;
 269: 
 270: /**
 271:  * The application wide charset, used to encode headers and body
 272:  *
 273:  * @var string
 274:  */
 275:     protected $_appCharset = null;
 276: 
 277: /**
 278:  * List of files that should be attached to the email.
 279:  *
 280:  * Only absolute paths
 281:  *
 282:  * @var array
 283:  */
 284:     protected $_attachments = array();
 285: 
 286: /**
 287:  * If set, boundary to use for multipart mime messages
 288:  *
 289:  * @var string
 290:  */
 291:     protected $_boundary = null;
 292: 
 293: /**
 294:  * Configuration to transport
 295:  *
 296:  * @var string|array
 297:  */
 298:     protected $_config = array();
 299: 
 300: /**
 301:  * 8Bit character sets
 302:  *
 303:  * @var array
 304:  */
 305:     protected $_charset8bit = array('UTF-8', 'SHIFT_JIS');
 306: 
 307: /**
 308:  * Define Content-Type charset name
 309:  *
 310:  * @var array
 311:  */
 312:     protected $_contentTypeCharset = array(
 313:         'ISO-2022-JP-MS' => 'ISO-2022-JP'
 314:     );
 315: 
 316: /**
 317:  * Regex for email validation
 318:  * If null, it will use built in regex
 319:  *
 320:  * @var string
 321:  */
 322:     protected $_emailPattern = null;
 323: 
 324: /**
 325:  * The class name used for email configuration.
 326:  *
 327:  * @var string
 328:  */
 329:     protected $_configClass = 'EmailConfig';
 330: 
 331: /**
 332:  * Constructor
 333:  *
 334:  * @param array|string $config Array of configs, or string to load configs from email.php
 335:  */
 336:     public function __construct($config = null) {
 337:         $this->_appCharset = Configure::read('App.encoding');
 338:         if ($this->_appCharset !== null) {
 339:             $this->charset = $this->_appCharset;
 340:         }
 341:         $this->_domain = preg_replace('/\:\d+$/', '', env('HTTP_HOST'));
 342:         if (empty($this->_domain)) {
 343:             $this->_domain = php_uname('n');
 344:         }
 345: 
 346:         if ($config) {
 347:             $this->config($config);
 348:         }
 349:         if (empty($this->headerCharset)) {
 350:             $this->headerCharset = $this->charset;
 351:         }
 352:     }
 353: 
 354: /**
 355:  * From
 356:  *
 357:  * @param string|array $email
 358:  * @param string $name
 359:  * @return array|CakeEmail
 360:  * @throws SocketException
 361:  */
 362:     public function from($email = null, $name = null) {
 363:         if ($email === null) {
 364:             return $this->_from;
 365:         }
 366:         return $this->_setEmailSingle('_from', $email, $name, __d('cake_dev', 'From requires only 1 email address.'));
 367:     }
 368: 
 369: /**
 370:  * Sender
 371:  *
 372:  * @param string|array $email
 373:  * @param string $name
 374:  * @return array|CakeEmail
 375:  * @throws SocketException
 376:  */
 377:     public function sender($email = null, $name = null) {
 378:         if ($email === null) {
 379:             return $this->_sender;
 380:         }
 381:         return $this->_setEmailSingle('_sender', $email, $name, __d('cake_dev', 'Sender requires only 1 email address.'));
 382:     }
 383: 
 384: /**
 385:  * Reply-To
 386:  *
 387:  * @param string|array $email
 388:  * @param string $name
 389:  * @return array|CakeEmail
 390:  * @throws SocketException
 391:  */
 392:     public function replyTo($email = null, $name = null) {
 393:         if ($email === null) {
 394:             return $this->_replyTo;
 395:         }
 396:         return $this->_setEmailSingle('_replyTo', $email, $name, __d('cake_dev', 'Reply-To requires only 1 email address.'));
 397:     }
 398: 
 399: /**
 400:  * Read Receipt (Disposition-Notification-To header)
 401:  *
 402:  * @param string|array $email
 403:  * @param string $name
 404:  * @return array|CakeEmail
 405:  * @throws SocketException
 406:  */
 407:     public function readReceipt($email = null, $name = null) {
 408:         if ($email === null) {
 409:             return $this->_readReceipt;
 410:         }
 411:         return $this->_setEmailSingle('_readReceipt', $email, $name, __d('cake_dev', 'Disposition-Notification-To requires only 1 email address.'));
 412:     }
 413: 
 414: /**
 415:  * Return Path
 416:  *
 417:  * @param string|array $email
 418:  * @param string $name
 419:  * @return array|CakeEmail
 420:  * @throws SocketException
 421:  */
 422:     public function returnPath($email = null, $name = null) {
 423:         if ($email === null) {
 424:             return $this->_returnPath;
 425:         }
 426:         return $this->_setEmailSingle('_returnPath', $email, $name, __d('cake_dev', 'Return-Path requires only 1 email address.'));
 427:     }
 428: 
 429: /**
 430:  * To
 431:  *
 432:  * @param string|array $email Null to get, String with email, Array with email as key, name as value or email as value (without name)
 433:  * @param string $name
 434:  * @return array|CakeEmail
 435:  */
 436:     public function to($email = null, $name = null) {
 437:         if ($email === null) {
 438:             return $this->_to;
 439:         }
 440:         return $this->_setEmail('_to', $email, $name);
 441:     }
 442: 
 443: /**
 444:  * Add To
 445:  *
 446:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 447:  * @param string $name
 448:  * @return CakeEmail $this
 449:  */
 450:     public function addTo($email, $name = null) {
 451:         return $this->_addEmail('_to', $email, $name);
 452:     }
 453: 
 454: /**
 455:  * Cc
 456:  *
 457:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 458:  * @param string $name
 459:  * @return array|CakeEmail
 460:  */
 461:     public function cc($email = null, $name = null) {
 462:         if ($email === null) {
 463:             return $this->_cc;
 464:         }
 465:         return $this->_setEmail('_cc', $email, $name);
 466:     }
 467: 
 468: /**
 469:  * Add Cc
 470:  *
 471:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 472:  * @param string $name
 473:  * @return CakeEmail $this
 474:  */
 475:     public function addCc($email, $name = null) {
 476:         return $this->_addEmail('_cc', $email, $name);
 477:     }
 478: 
 479: /**
 480:  * Bcc
 481:  *
 482:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 483:  * @param string $name
 484:  * @return array|CakeEmail
 485:  */
 486:     public function bcc($email = null, $name = null) {
 487:         if ($email === null) {
 488:             return $this->_bcc;
 489:         }
 490:         return $this->_setEmail('_bcc', $email, $name);
 491:     }
 492: 
 493: /**
 494:  * Add Bcc
 495:  *
 496:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 497:  * @param string $name
 498:  * @return CakeEmail $this
 499:  */
 500:     public function addBcc($email, $name = null) {
 501:         return $this->_addEmail('_bcc', $email, $name);
 502:     }
 503: 
 504: /**
 505:  * Charset setter/getter
 506:  *
 507:  * @param string $charset
 508:  * @return string $this->charset
 509:  */
 510:     public function charset($charset = null) {
 511:         if ($charset === null) {
 512:             return $this->charset;
 513:         }
 514:         $this->charset = $charset;
 515:         if (empty($this->headerCharset)) {
 516:             $this->headerCharset = $charset;
 517:         }
 518:         return $this->charset;
 519:     }
 520: 
 521: /**
 522:  * HeaderCharset setter/getter
 523:  *
 524:  * @param string $charset
 525:  * @return string $this->charset
 526:  */
 527:     public function headerCharset($charset = null) {
 528:         if ($charset === null) {
 529:             return $this->headerCharset;
 530:         }
 531:         return $this->headerCharset = $charset;
 532:     }
 533: 
 534: /**
 535:  * EmailPattern setter/getter
 536:  *
 537:  * @param string $regex for email address validation
 538:  * @return string|CakeEmail
 539:  */
 540:     public function emailPattern($regex = null) {
 541:         if ($regex === null) {
 542:             return $this->_emailPattern;
 543:         }
 544:         $this->_emailPattern = $regex;
 545:         return $this;
 546:     }
 547: 
 548: /**
 549:  * Set email
 550:  *
 551:  * @param string $varName
 552:  * @param string|array $email
 553:  * @param string $name
 554:  * @return CakeEmail $this
 555:  * @throws SocketException
 556:  */
 557:     protected function _setEmail($varName, $email, $name) {
 558:         if (!is_array($email)) {
 559:             if (!Validation::email($email, false, $this->_emailPattern)) {
 560:                 throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
 561:             }
 562:             if ($name === null) {
 563:                 $name = $email;
 564:             }
 565:             $this->{$varName} = array($email => $name);
 566:             return $this;
 567:         }
 568:         $list = array();
 569:         foreach ($email as $key => $value) {
 570:             if (is_int($key)) {
 571:                 $key = $value;
 572:             }
 573:             if (!Validation::email($key, false, $this->_emailPattern)) {
 574:                 throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
 575:             }
 576:             $list[$key] = $value;
 577:         }
 578:         $this->{$varName} = $list;
 579:         return $this;
 580:     }
 581: 
 582: /**
 583:  * Set only 1 email
 584:  *
 585:  * @param string $varName
 586:  * @param string|array $email
 587:  * @param string $name
 588:  * @param string $throwMessage
 589:  * @return CakeEmail $this
 590:  * @throws SocketException
 591:  */
 592:     protected function _setEmailSingle($varName, $email, $name, $throwMessage) {
 593:         $current = $this->{$varName};
 594:         $this->_setEmail($varName, $email, $name);
 595:         if (count($this->{$varName}) !== 1) {
 596:             $this->{$varName} = $current;
 597:             throw new SocketException($throwMessage);
 598:         }
 599:         return $this;
 600:     }
 601: 
 602: /**
 603:  * Add email
 604:  *
 605:  * @param string $varName
 606:  * @param string|array $email
 607:  * @param string $name
 608:  * @return CakeEmail $this
 609:  * @throws SocketException
 610:  */
 611:     protected function _addEmail($varName, $email, $name) {
 612:         if (!is_array($email)) {
 613:             if (!Validation::email($email, false, $this->_emailPattern)) {
 614:                 throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
 615:             }
 616:             if ($name === null) {
 617:                 $name = $email;
 618:             }
 619:             $this->{$varName}[$email] = $name;
 620:             return $this;
 621:         }
 622:         $list = array();
 623:         foreach ($email as $key => $value) {
 624:             if (is_int($key)) {
 625:                 $key = $value;
 626:             }
 627:             if (!Validation::email($key, false, $this->_emailPattern)) {
 628:                 throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
 629:             }
 630:             $list[$key] = $value;
 631:         }
 632:         $this->{$varName} = array_merge($this->{$varName}, $list);
 633:         return $this;
 634:     }
 635: 
 636: /**
 637:  * Get/Set Subject.
 638:  *
 639:  * @param string $subject
 640:  * @return string|CakeEmail
 641:  */
 642:     public function subject($subject = null) {
 643:         if ($subject === null) {
 644:             return $this->_subject;
 645:         }
 646:         $this->_subject = $this->_encode((string)$subject);
 647:         return $this;
 648:     }
 649: 
 650: /**
 651:  * Sets headers for the message
 652:  *
 653:  * @param array $headers Associative array containing headers to be set.
 654:  * @return CakeEmail $this
 655:  * @throws SocketException
 656:  */
 657:     public function setHeaders($headers) {
 658:         if (!is_array($headers)) {
 659:             throw new SocketException(__d('cake_dev', '$headers should be an array.'));
 660:         }
 661:         $this->_headers = $headers;
 662:         return $this;
 663:     }
 664: 
 665: /**
 666:  * Add header for the message
 667:  *
 668:  * @param array $headers
 669:  * @return object $this
 670:  * @throws SocketException
 671:  */
 672:     public function addHeaders($headers) {
 673:         if (!is_array($headers)) {
 674:             throw new SocketException(__d('cake_dev', '$headers should be an array.'));
 675:         }
 676:         $this->_headers = array_merge($this->_headers, $headers);
 677:         return $this;
 678:     }
 679: 
 680: /**
 681:  * Get list of headers
 682:  *
 683:  * ### Includes:
 684:  *
 685:  * - `from`
 686:  * - `replyTo`
 687:  * - `readReceipt`
 688:  * - `returnPath`
 689:  * - `to`
 690:  * - `cc`
 691:  * - `bcc`
 692:  * - `subject`
 693:  *
 694:  * @param array $include
 695:  * @return array
 696:  */
 697:     public function getHeaders($include = array()) {
 698:         if ($include == array_values($include)) {
 699:             $include = array_fill_keys($include, true);
 700:         }
 701:         $defaults = array_fill_keys(array('from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'bcc', 'subject'), false);
 702:         $include += $defaults;
 703: 
 704:         $headers = array();
 705:         $relation = array(
 706:             'from' => 'From',
 707:             'replyTo' => 'Reply-To',
 708:             'readReceipt' => 'Disposition-Notification-To',
 709:             'returnPath' => 'Return-Path'
 710:         );
 711:         foreach ($relation as $var => $header) {
 712:             if ($include[$var]) {
 713:                 $var = '_' . $var;
 714:                 $headers[$header] = current($this->_formatAddress($this->{$var}));
 715:             }
 716:         }
 717:         if ($include['sender']) {
 718:             if (key($this->_sender) === key($this->_from)) {
 719:                 $headers['Sender'] = '';
 720:             } else {
 721:                 $headers['Sender'] = current($this->_formatAddress($this->_sender));
 722:             }
 723:         }
 724: 
 725:         foreach (array('to', 'cc', 'bcc') as $var) {
 726:             if ($include[$var]) {
 727:                 $classVar = '_' . $var;
 728:                 $headers[ucfirst($var)] = implode(', ', $this->_formatAddress($this->{$classVar}));
 729:             }
 730:         }
 731: 
 732:         $headers += $this->_headers;
 733:         if (!isset($headers['X-Mailer'])) {
 734:             $headers['X-Mailer'] = self::EMAIL_CLIENT;
 735:         }
 736:         if (!isset($headers['Date'])) {
 737:             $headers['Date'] = date(DATE_RFC2822);
 738:         }
 739:         if ($this->_messageId !== false) {
 740:             if ($this->_messageId === true) {
 741:                 $headers['Message-ID'] = '<' . str_replace('-', '', String::UUID()) . '@' . $this->_domain . '>';
 742:             } else {
 743:                 $headers['Message-ID'] = $this->_messageId;
 744:             }
 745:         }
 746: 
 747:         if ($include['subject']) {
 748:             $headers['Subject'] = $this->_subject;
 749:         }
 750: 
 751:         $headers['MIME-Version'] = '1.0';
 752:         if (!empty($this->_attachments) || $this->_emailFormat === 'both') {
 753:             $headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"';
 754:         } elseif ($this->_emailFormat === 'text') {
 755:             $headers['Content-Type'] = 'text/plain; charset=' . $this->_getContentTypeCharset();
 756:         } elseif ($this->_emailFormat === 'html') {
 757:             $headers['Content-Type'] = 'text/html; charset=' . $this->_getContentTypeCharset();
 758:         }
 759:         $headers['Content-Transfer-Encoding'] = $this->_getContentTransferEncoding();
 760: 
 761:         return $headers;
 762:     }
 763: 
 764: /**
 765:  * Format addresses
 766:  *
 767:  * If the address contains non alphanumeric/whitespace characters, it will
 768:  * be quoted as characters like `:` and `,` are known to cause issues
 769:  * in address header fields.
 770:  *
 771:  * @param array $address
 772:  * @return array
 773:  */
 774:     protected function _formatAddress($address) {
 775:         $return = array();
 776:         foreach ($address as $email => $alias) {
 777:             if ($email === $alias) {
 778:                 $return[] = $email;
 779:             } else {
 780:                 $encoded = $this->_encode($alias);
 781:                 if ($encoded === $alias && preg_match('/[^a-z0-9 ]/i', $encoded)) {
 782:                     $encoded = '"' . str_replace('"', '\"', $encoded) . '"';
 783:                 }
 784:                 $return[] = sprintf('%s <%s>', $encoded, $email);
 785:             }
 786:         }
 787:         return $return;
 788:     }
 789: 
 790: /**
 791:  * Template and layout
 792:  *
 793:  * @param boolean|string $template Template name or null to not use
 794:  * @param boolean|string $layout Layout name or null to not use
 795:  * @return array|CakeEmail
 796:  */
 797:     public function template($template = false, $layout = false) {
 798:         if ($template === false) {
 799:             return array(
 800:                 'template' => $this->_template,
 801:                 'layout' => $this->_layout
 802:             );
 803:         }
 804:         $this->_template = $template;
 805:         if ($layout !== false) {
 806:             $this->_layout = $layout;
 807:         }
 808:         return $this;
 809:     }
 810: 
 811: /**
 812:  * View class for render
 813:  *
 814:  * @param string $viewClass
 815:  * @return string|CakeEmail
 816:  */
 817:     public function viewRender($viewClass = null) {
 818:         if ($viewClass === null) {
 819:             return $this->_viewRender;
 820:         }
 821:         $this->_viewRender = $viewClass;
 822:         return $this;
 823:     }
 824: 
 825: /**
 826:  * Variables to be set on render
 827:  *
 828:  * @param array $viewVars
 829:  * @return array|CakeEmail
 830:  */
 831:     public function viewVars($viewVars = null) {
 832:         if ($viewVars === null) {
 833:             return $this->_viewVars;
 834:         }
 835:         $this->_viewVars = array_merge($this->_viewVars, (array)$viewVars);
 836:         return $this;
 837:     }
 838: 
 839: /**
 840:  * Theme to use when rendering
 841:  *
 842:  * @param string $theme
 843:  * @return string|CakeEmail
 844:  */
 845:     public function theme($theme = null) {
 846:         if ($theme === null) {
 847:             return $this->_theme;
 848:         }
 849:         $this->_theme = $theme;
 850:         return $this;
 851:     }
 852: 
 853: /**
 854:  * Helpers to be used in render
 855:  *
 856:  * @param array $helpers
 857:  * @return array|CakeEmail
 858:  */
 859:     public function helpers($helpers = null) {
 860:         if ($helpers === null) {
 861:             return $this->_helpers;
 862:         }
 863:         $this->_helpers = (array)$helpers;
 864:         return $this;
 865:     }
 866: 
 867: /**
 868:  * Email format
 869:  *
 870:  * @param string $format
 871:  * @return string|CakeEmail
 872:  * @throws SocketException
 873:  */
 874:     public function emailFormat($format = null) {
 875:         if ($format === null) {
 876:             return $this->_emailFormat;
 877:         }
 878:         if (!in_array($format, $this->_emailFormatAvailable)) {
 879:             throw new SocketException(__d('cake_dev', 'Format not available.'));
 880:         }
 881:         $this->_emailFormat = $format;
 882:         return $this;
 883:     }
 884: 
 885: /**
 886:  * Transport name
 887:  *
 888:  * @param string $name
 889:  * @return string|CakeEmail
 890:  */
 891:     public function transport($name = null) {
 892:         if ($name === null) {
 893:             return $this->_transportName;
 894:         }
 895:         $this->_transportName = (string)$name;
 896:         $this->_transportClass = null;
 897:         return $this;
 898:     }
 899: 
 900: /**
 901:  * Return the transport class
 902:  *
 903:  * @return CakeEmail
 904:  * @throws SocketException
 905:  */
 906:     public function transportClass() {
 907:         if ($this->_transportClass) {
 908:             return $this->_transportClass;
 909:         }
 910:         list($plugin, $transportClassname) = pluginSplit($this->_transportName, true);
 911:         $transportClassname .= 'Transport';
 912:         App::uses($transportClassname, $plugin . 'Network/Email');
 913:         if (!class_exists($transportClassname)) {
 914:             throw new SocketException(__d('cake_dev', 'Class "%s" not found.', $transportClassname));
 915:         } elseif (!method_exists($transportClassname, 'send')) {
 916:             throw new SocketException(__d('cake_dev', 'The "%s" does not have a %s method.', $transportClassname, 'send()'));
 917:         }
 918: 
 919:         return $this->_transportClass = new $transportClassname();
 920:     }
 921: 
 922: /**
 923:  * Message-ID
 924:  *
 925:  * @param boolean|string $message True to generate a new Message-ID, False to ignore (not send in email), String to set as Message-ID
 926:  * @return boolean|string|CakeEmail
 927:  * @throws SocketException
 928:  */
 929:     public function messageId($message = null) {
 930:         if ($message === null) {
 931:             return $this->_messageId;
 932:         }
 933:         if (is_bool($message)) {
 934:             $this->_messageId = $message;
 935:         } else {
 936:             if (!preg_match('/^\<.+@.+\>$/', $message)) {
 937:                 throw new SocketException(__d('cake_dev', 'Invalid format for Message-ID. The text should be something like "<[email protected]>"'));
 938:             }
 939:             $this->_messageId = $message;
 940:         }
 941:         return $this;
 942:     }
 943: 
 944: /**
 945:  * Domain as top level (the part after @)
 946:  *
 947:  * @param string $domain Manually set the domain for CLI mailing
 948:  * @return string|CakeEmail
 949:  */
 950:     public function domain($domain = null) {
 951:         if ($domain === null) {
 952:             return $this->_domain;
 953:         }
 954:         $this->_domain = $domain;
 955:         return $this;
 956:     }
 957: 
 958: /**
 959:  * Add attachments to the email message
 960:  *
 961:  * Attachments can be defined in a few forms depending on how much control you need:
 962:  *
 963:  * Attach a single file:
 964:  *
 965:  * {{{
 966:  * $email->attachments('path/to/file');
 967:  * }}}
 968:  *
 969:  * Attach a file with a different filename:
 970:  *
 971:  * {{{
 972:  * $email->attachments(array('custom_name.txt' => 'path/to/file.txt'));
 973:  * }}}
 974:  *
 975:  * Attach a file and specify additional properties:
 976:  *
 977:  * {{{
 978:  * $email->attachments(array('custom_name.png' => array(
 979:  *      'file' => 'path/to/file',
 980:  *      'mimetype' => 'image/png',
 981:  *      'contentId' => 'abc123',
 982:  *      'contentDisposition' => false
 983:  * ));
 984:  * }}}
 985:  *
 986:  * Attach a file from string and specify additional properties:
 987:  *
 988:  * {{{
 989:  * $email->attachments(array('custom_name.png' => array(
 990:  *      'data' => file_get_contents('path/to/file'),
 991:  *      'mimetype' => 'image/png'
 992:  * ));
 993:  * }}}
 994:  *
 995:  * The `contentId` key allows you to specify an inline attachment. In your email text, you
 996:  * can use `<img src="cid:abc123" />` to display the image inline.
 997:  *
 998:  * The `contentDisposition` key allows you to disable the `Content-Disposition` header, this can improve
 999:  * attachment compatibility with outlook email clients.
1000:  *
1001:  * @param string|array $attachments String with the filename or array with filenames
1002:  * @return array|CakeEmail Either the array of attachments when getting or $this when setting.
1003:  * @throws SocketException
1004:  */
1005:     public function attachments($attachments = null) {
1006:         if ($attachments === null) {
1007:             return $this->_attachments;
1008:         }
1009:         $attach = array();
1010:         foreach ((array)$attachments as $name => $fileInfo) {
1011:             if (!is_array($fileInfo)) {
1012:                 $fileInfo = array('file' => $fileInfo);
1013:             }
1014:             if (!isset($fileInfo['file'])) {
1015:                 if (!isset($fileInfo['data'])) {
1016:                     throw new SocketException(__d('cake_dev', 'No file or data specified.'));
1017:                 }
1018:                 if (is_int($name)) {
1019:                     throw new SocketException(__d('cake_dev', 'No filename specified.'));
1020:                 }
1021:                 $fileInfo['data'] = chunk_split(base64_encode($fileInfo['data']), 76, "\r\n");
1022:             } else {
1023:                 $fileName = $fileInfo['file'];
1024:                 $fileInfo['file'] = realpath($fileInfo['file']);
1025:                 if ($fileInfo['file'] === false || !file_exists($fileInfo['file'])) {
1026:                     throw new SocketException(__d('cake_dev', 'File not found: "%s"', $fileName));
1027:                 }
1028:                 if (is_int($name)) {
1029:                     $name = basename($fileInfo['file']);
1030:                 }
1031:             }
1032:             if (!isset($fileInfo['mimetype'])) {
1033:                 $fileInfo['mimetype'] = 'application/octet-stream';
1034:             }
1035:             $attach[$name] = $fileInfo;
1036:         }
1037:         $this->_attachments = $attach;
1038:         return $this;
1039:     }
1040: 
1041: /**
1042:  * Add attachments
1043:  *
1044:  * @param string|array $attachments String with the filename or array with filenames
1045:  * @return CakeEmail $this
1046:  * @throws SocketException
1047:  * @see CakeEmail::attachments()
1048:  */
1049:     public function addAttachments($attachments) {
1050:         $current = $this->_attachments;
1051:         $this->attachments($attachments);
1052:         $this->_attachments = array_merge($current, $this->_attachments);
1053:         return $this;
1054:     }
1055: 
1056: /**
1057:  * Get generated message (used by transport classes)
1058:  *
1059:  * @param string $type Use MESSAGE_* constants or null to return the full message as array
1060:  * @return string|array String if have type, array if type is null
1061:  */
1062:     public function message($type = null) {
1063:         switch ($type) {
1064:             case self::MESSAGE_HTML:
1065:                 return $this->_htmlMessage;
1066:             case self::MESSAGE_TEXT:
1067:                 return $this->_textMessage;
1068:         }
1069:         return $this->_message;
1070:     }
1071: 
1072: /**
1073:  * Configuration to use when send email
1074:  *
1075:  * ### Usage
1076:  *
1077:  * Load configuration from `app/Config/email.php`:
1078:  *
1079:  * `$email->config('default');`
1080:  *
1081:  * Merge an array of configuration into the instance:
1082:  *
1083:  * `$email->config(array('to' => '[email protected]'));`
1084:  *
1085:  * @param string|array $config String with configuration name (from email.php), array with config or null to return current config
1086:  * @return string|array|CakeEmail
1087:  */
1088:     public function config($config = null) {
1089:         if ($config === null) {
1090:             return $this->_config;
1091:         }
1092:         if (!is_array($config)) {
1093:             $config = (string)$config;
1094:         }
1095: 
1096:         $this->_applyConfig($config);
1097:         return $this;
1098:     }
1099: 
1100: /**
1101:  * Send an email using the specified content, template and layout
1102:  *
1103:  * @param string|array $content String with message or array with messages
1104:  * @return array
1105:  * @throws SocketException
1106:  */
1107:     public function send($content = null) {
1108:         if (empty($this->_from)) {
1109:             throw new SocketException(__d('cake_dev', 'From is not specified.'));
1110:         }
1111:         if (empty($this->_to) && empty($this->_cc) && empty($this->_bcc)) {
1112:             throw new SocketException(__d('cake_dev', 'You need to specify at least one destination for to, cc or bcc.'));
1113:         }
1114: 
1115:         if (is_array($content)) {
1116:             $content = implode("\n", $content) . "\n";
1117:         }
1118: 
1119:         $this->_message = $this->_render($this->_wrap($content));
1120: 
1121:         $contents = $this->transportClass()->send($this);
1122:         if (!empty($this->_config['log'])) {
1123:             $config = array(
1124:                 'level' => LOG_DEBUG,
1125:                 'scope' => 'email'
1126:             );
1127:             if ($this->_config['log'] !== true) {
1128:                 if (!is_array($this->_config['log'])) {
1129:                     $this->_config['log'] = array('level' => $this->_config['log']);
1130:                 }
1131:                 $config = array_merge($config, $this->_config['log']);
1132:             }
1133:             CakeLog::write(
1134:                 $config['level'],
1135:                 PHP_EOL . $contents['headers'] . PHP_EOL . $contents['message'],
1136:                 $config['scope']
1137:             );
1138:         }
1139:         return $contents;
1140:     }
1141: 
1142: /**
1143:  * Static method to fast create an instance of CakeEmail
1144:  *
1145:  * @param string|array $to Address to send (see CakeEmail::to()). If null, will try to use 'to' from transport config
1146:  * @param string $subject String of subject or null to use 'subject' from transport config
1147:  * @param string|array $message String with message or array with variables to be used in render
1148:  * @param string|array $transportConfig String to use config from EmailConfig or array with configs
1149:  * @param boolean $send Send the email or just return the instance pre-configured
1150:  * @return CakeEmail Instance of CakeEmail
1151:  * @throws SocketException
1152:  */
1153:     public static function deliver($to = null, $subject = null, $message = null, $transportConfig = 'fast', $send = true) {
1154:         $class = __CLASS__;
1155:         $instance = new $class($transportConfig);
1156:         if ($to !== null) {
1157:             $instance->to($to);
1158:         }
1159:         if ($subject !== null) {
1160:             $instance->subject($subject);
1161:         }
1162:         if (is_array($message)) {
1163:             $instance->viewVars($message);
1164:             $message = null;
1165:         } elseif ($message === null && array_key_exists('message', $config = $instance->config())) {
1166:             $message = $config['message'];
1167:         }
1168: 
1169:         if ($send === true) {
1170:             $instance->send($message);
1171:         }
1172: 
1173:         return $instance;
1174:     }
1175: 
1176: /**
1177:  * Apply the config to an instance
1178:  *
1179:  * @param array $config
1180:  * @return void
1181:  * @throws ConfigureException When configuration file cannot be found, or is missing
1182:  *   the named config.
1183:  */
1184:     protected function _applyConfig($config) {
1185:         if (is_string($config)) {
1186:             if (!class_exists($this->_configClass) && !config('email')) {
1187:                 throw new ConfigureException(__d('cake_dev', '%s not found.', APP . 'Config' . DS . 'email.php'));
1188:             }
1189:             $configs = new $this->_configClass();
1190:             if (!isset($configs->{$config})) {
1191:                 throw new ConfigureException(__d('cake_dev', 'Unknown email configuration "%s".', $config));
1192:             }
1193:             $config = $configs->{$config};
1194:         }
1195:         $this->_config = array_merge($this->_config, $config);
1196:         if (!empty($config['charset'])) {
1197:             $this->charset = $config['charset'];
1198:         }
1199:         if (!empty($config['headerCharset'])) {
1200:             $this->headerCharset = $config['headerCharset'];
1201:         }
1202:         if (empty($this->headerCharset)) {
1203:             $this->headerCharset = $this->charset;
1204:         }
1205:         $simpleMethods = array(
1206:             'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc',
1207:             'messageId', 'domain', 'subject', 'viewRender', 'viewVars', 'attachments',
1208:             'transport', 'emailFormat', 'theme', 'helpers', 'emailPattern'
1209:         );
1210:         foreach ($simpleMethods as $method) {
1211:             if (isset($config[$method])) {
1212:                 $this->$method($config[$method]);
1213:                 unset($config[$method]);
1214:             }
1215:         }
1216:         if (isset($config['headers'])) {
1217:             $this->setHeaders($config['headers']);
1218:             unset($config['headers']);
1219:         }
1220:         if (array_key_exists('template', $config)) {
1221:             $layout = false;
1222:             if (array_key_exists('layout', $config)) {
1223:                 $layout = $config['layout'];
1224:                 unset($config['layout']);
1225:             }
1226:             $this->template($config['template'], $layout);
1227:             unset($config['template']);
1228:         }
1229:         $this->transportClass()->config($config);
1230:     }
1231: 
1232: /**
1233:  * Reset all CakeEmail internal variables to be able to send out a new email.
1234:  *
1235:  * @return CakeEmail $this
1236:  */
1237:     public function reset() {
1238:         $this->_to = array();
1239:         $this->_from = array();
1240:         $this->_sender = array();
1241:         $this->_replyTo = array();
1242:         $this->_readReceipt = array();
1243:         $this->_returnPath = array();
1244:         $this->_cc = array();
1245:         $this->_bcc = array();
1246:         $this->_messageId = true;
1247:         $this->_subject = '';
1248:         $this->_headers = array();
1249:         $this->_layout = 'default';
1250:         $this->_template = '';
1251:         $this->_viewRender = 'View';
1252:         $this->_viewVars = array();
1253:         $this->_theme = null;
1254:         $this->_helpers = array('Html');
1255:         $this->_textMessage = '';
1256:         $this->_htmlMessage = '';
1257:         $this->_message = '';
1258:         $this->_emailFormat = 'text';
1259:         $this->_transportName = 'Mail';
1260:         $this->_transportClass = null;
1261:         $this->charset = 'utf-8';
1262:         $this->headerCharset = null;
1263:         $this->_attachments = array();
1264:         $this->_config = array();
1265:         $this->_emailPattern = null;
1266:         return $this;
1267:     }
1268: 
1269: /**
1270:  * Encode the specified string using the current charset
1271:  *
1272:  * @param string $text String to encode
1273:  * @return string Encoded string
1274:  */
1275:     protected function _encode($text) {
1276:         $internalEncoding = function_exists('mb_internal_encoding');
1277:         if ($internalEncoding) {
1278:             $restore = mb_internal_encoding();
1279:             mb_internal_encoding($this->_appCharset);
1280:         }
1281:         if (empty($this->headerCharset)) {
1282:             $this->headerCharset = $this->charset;
1283:         }
1284:         $return = mb_encode_mimeheader($text, $this->headerCharset, 'B');
1285:         if ($internalEncoding) {
1286:             mb_internal_encoding($restore);
1287:         }
1288:         return $return;
1289:     }
1290: 
1291: /**
1292:  * Translates a string for one charset to another if the App.encoding value
1293:  * differs and the mb_convert_encoding function exists
1294:  *
1295:  * @param string $text The text to be converted
1296:  * @param string $charset the target encoding
1297:  * @return string
1298:  */
1299:     protected function _encodeString($text, $charset) {
1300:         if ($this->_appCharset === $charset || !function_exists('mb_convert_encoding')) {
1301:             return $text;
1302:         }
1303:         return mb_convert_encoding($text, $charset, $this->_appCharset);
1304:     }
1305: 
1306: /**
1307:  * Wrap the message to follow the RFC 2822 - 2.1.1
1308:  *
1309:  * @param string $message Message to wrap
1310:  * @param integer $wrapLength The line length
1311:  * @return array Wrapped message
1312:  */
1313:     protected function _wrap($message, $wrapLength = CakeEmail::LINE_LENGTH_MUST) {
1314:         if (strlen($message) === 0) {
1315:             return array('');
1316:         }
1317:         $message = str_replace(array("\r\n", "\r"), "\n", $message);
1318:         $lines = explode("\n", $message);
1319:         $formatted = array();
1320:         $cut = ($wrapLength == CakeEmail::LINE_LENGTH_MUST);
1321: 
1322:         foreach ($lines as $line) {
1323:             if (empty($line)) {
1324:                 $formatted[] = '';
1325:                 continue;
1326:             }
1327:             if (strlen($line) < $wrapLength) {
1328:                 $formatted[] = $line;
1329:                 continue;
1330:             }
1331:             if (!preg_match('/<[a-z]+.*>/i', $line)) {
1332:                 $formatted = array_merge(
1333:                     $formatted,
1334:                     explode("\n", wordwrap($line, $wrapLength, "\n", $cut))
1335:                 );
1336:                 continue;
1337:             }
1338: 
1339:             $tagOpen = false;
1340:             $tmpLine = $tag = '';
1341:             $tmpLineLength = 0;
1342:             for ($i = 0, $count = strlen($line); $i < $count; $i++) {
1343:                 $char = $line[$i];
1344:                 if ($tagOpen) {
1345:                     $tag .= $char;
1346:                     if ($char === '>') {
1347:                         $tagLength = strlen($tag);
1348:                         if ($tagLength + $tmpLineLength < $wrapLength) {
1349:                             $tmpLine .= $tag;
1350:                             $tmpLineLength += $tagLength;
1351:                         } else {
1352:                             if ($tmpLineLength > 0) {
1353:                                 $formatted = array_merge(
1354:                                     $formatted,
1355:                                     explode("\n", wordwrap(trim($tmpLine), $wrapLength, "\n", $cut))
1356:                                 );
1357:                                 $tmpLine = '';
1358:                                 $tmpLineLength = 0;
1359:                             }
1360:                             if ($tagLength > $wrapLength) {
1361:                                 $formatted[] = $tag;
1362:                             } else {
1363:                                 $tmpLine = $tag;
1364:                                 $tmpLineLength = $tagLength;
1365:                             }
1366:                         }
1367:                         $tag = '';
1368:                         $tagOpen = false;
1369:                     }
1370:                     continue;
1371:                 }
1372:                 if ($char === '<') {
1373:                     $tagOpen = true;
1374:                     $tag = '<';
1375:                     continue;
1376:                 }
1377:                 if ($char === ' ' && $tmpLineLength >= $wrapLength) {
1378:                     $formatted[] = $tmpLine;
1379:                     $tmpLineLength = 0;
1380:                     continue;
1381:                 }
1382:                 $tmpLine .= $char;
1383:                 $tmpLineLength++;
1384:                 if ($tmpLineLength === $wrapLength) {
1385:                     $nextChar = $line[$i + 1];
1386:                     if ($nextChar === ' ' || $nextChar === '<') {
1387:                         $formatted[] = trim($tmpLine);
1388:                         $tmpLine = '';
1389:                         $tmpLineLength = 0;
1390:                         if ($nextChar === ' ') {
1391:                             $i++;
1392:                         }
1393:                     } else {
1394:                         $lastSpace = strrpos($tmpLine, ' ');
1395:                         if ($lastSpace === false) {
1396:                             continue;
1397:                         }
1398:                         $formatted[] = trim(substr($tmpLine, 0, $lastSpace));
1399:                         $tmpLine = substr($tmpLine, $lastSpace + 1);
1400: 
1401:                         $tmpLineLength = strlen($tmpLine);
1402:                     }
1403:                 }
1404:             }
1405:             if (!empty($tmpLine)) {
1406:                 $formatted[] = $tmpLine;
1407:             }
1408:         }
1409:         $formatted[] = '';
1410:         return $formatted;
1411:     }
1412: 
1413: /**
1414:  * Create unique boundary identifier
1415:  *
1416:  * @return void
1417:  */
1418:     protected function _createBoundary() {
1419:         if (!empty($this->_attachments) || $this->_emailFormat === 'both') {
1420:             $this->_boundary = md5(uniqid(time()));
1421:         }
1422:     }
1423: 
1424: /**
1425:  * Attach non-embedded files by adding file contents inside boundaries.
1426:  *
1427:  * @param string $boundary Boundary to use. If null, will default to $this->_boundary
1428:  * @return array An array of lines to add to the message
1429:  */
1430:     protected function _attachFiles($boundary = null) {
1431:         if ($boundary === null) {
1432:             $boundary = $this->_boundary;
1433:         }
1434: 
1435:         $msg = array();
1436:         foreach ($this->_attachments as $filename => $fileInfo) {
1437:             if (!empty($fileInfo['contentId'])) {
1438:                 continue;
1439:             }
1440:             $data = isset($fileInfo['data']) ? $fileInfo['data'] : $this->_readFile($fileInfo['file']);
1441: 
1442:             $msg[] = '--' . $boundary;
1443:             $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
1444:             $msg[] = 'Content-Transfer-Encoding: base64';
1445:             if (
1446:                 !isset($fileInfo['contentDisposition']) ||
1447:                 $fileInfo['contentDisposition']
1448:             ) {
1449:                 $msg[] = 'Content-Disposition: attachment; filename="' . $filename . '"';
1450:             }
1451:             $msg[] = '';
1452:             $msg[] = $data;
1453:             $msg[] = '';
1454:         }
1455:         return $msg;
1456:     }
1457: 
1458: /**
1459:  * Read the file contents and return a base64 version of the file contents.
1460:  *
1461:  * @param string $path The absolute path to the file to read.
1462:  * @return string File contents in base64 encoding
1463:  */
1464:     protected function _readFile($path) {
1465:         $File = new File($path);
1466:         return chunk_split(base64_encode($File->read()));
1467:     }
1468: 
1469: /**
1470:  * Attach inline/embedded files to the message.
1471:  *
1472:  * @param string $boundary Boundary to use. If null, will default to $this->_boundary
1473:  * @return array An array of lines to add to the message
1474:  */
1475:     protected function _attachInlineFiles($boundary = null) {
1476:         if ($boundary === null) {
1477:             $boundary = $this->_boundary;
1478:         }
1479: 
1480:         $msg = array();
1481:         foreach ($this->_attachments as $filename => $fileInfo) {
1482:             if (empty($fileInfo['contentId'])) {
1483:                 continue;
1484:             }
1485:             $data = isset($fileInfo['data']) ? $fileInfo['data'] : $this->_readFile($fileInfo['file']);
1486: 
1487:             $msg[] = '--' . $boundary;
1488:             $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
1489:             $msg[] = 'Content-Transfer-Encoding: base64';
1490:             $msg[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
1491:             $msg[] = 'Content-Disposition: inline; filename="' . $filename . '"';
1492:             $msg[] = '';
1493:             $msg[] = $data;
1494:             $msg[] = '';
1495:         }
1496:         return $msg;
1497:     }
1498: 
1499: /**
1500:  * Render the body of the email.
1501:  *
1502:  * @param array $content Content to render
1503:  * @return array Email body ready to be sent
1504:  */
1505:     protected function _render($content) {
1506:         $this->_textMessage = $this->_htmlMessage = '';
1507: 
1508:         $content = implode("\n", $content);
1509:         $rendered = $this->_renderTemplates($content);
1510: 
1511:         $this->_createBoundary();
1512:         $msg = array();
1513: 
1514:         $contentIds = array_filter((array)Hash::extract($this->_attachments, '{s}.contentId'));
1515:         $hasInlineAttachments = count($contentIds) > 0;
1516:         $hasAttachments = !empty($this->_attachments);
1517:         $hasMultipleTypes = count($rendered) > 1;
1518: 
1519:         $boundary = $relBoundary = $textBoundary = $this->_boundary;
1520: 
1521:         if ($hasInlineAttachments) {
1522:             $msg[] = '--' . $boundary;
1523:             $msg[] = 'Content-Type: multipart/related; boundary="rel-' . $boundary . '"';
1524:             $msg[] = '';
1525:             $relBoundary = $textBoundary = 'rel-' . $boundary;
1526:         }
1527: 
1528:         if ($hasMultipleTypes) {
1529:             $msg[] = '--' . $relBoundary;
1530:             $msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $boundary . '"';
1531:             $msg[] = '';
1532:             $textBoundary = 'alt-' . $boundary;
1533:         }
1534: 
1535:         if (isset($rendered['text'])) {
1536:             if ($textBoundary !== $boundary || $hasAttachments) {
1537:                 $msg[] = '--' . $textBoundary;
1538:                 $msg[] = 'Content-Type: text/plain; charset=' . $this->_getContentTypeCharset();
1539:                 $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
1540:                 $msg[] = '';
1541:             }
1542:             $this->_textMessage = $rendered['text'];
1543:             $content = explode("\n", $this->_textMessage);
1544:             $msg = array_merge($msg, $content);
1545:             $msg[] = '';
1546:         }
1547: 
1548:         if (isset($rendered['html'])) {
1549:             if ($textBoundary !== $boundary || $hasAttachments) {
1550:                 $msg[] = '--' . $textBoundary;
1551:                 $msg[] = 'Content-Type: text/html; charset=' . $this->_getContentTypeCharset();
1552:                 $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
1553:                 $msg[] = '';
1554:             }
1555:             $this->_htmlMessage = $rendered['html'];
1556:             $content = explode("\n", $this->_htmlMessage);
1557:             $msg = array_merge($msg, $content);
1558:             $msg[] = '';
1559:         }
1560: 
1561:         if ($hasMultipleTypes) {
1562:             $msg[] = '--' . $textBoundary . '--';
1563:             $msg[] = '';
1564:         }
1565: 
1566:         if ($hasInlineAttachments) {
1567:             $attachments = $this->_attachInlineFiles($relBoundary);
1568:             $msg = array_merge($msg, $attachments);
1569:             $msg[] = '';
1570:             $msg[] = '--' . $relBoundary . '--';
1571:             $msg[] = '';
1572:         }
1573: 
1574:         if ($hasAttachments) {
1575:             $attachments = $this->_attachFiles($boundary);
1576:             $msg = array_merge($msg, $attachments);
1577:         }
1578:         if ($hasAttachments || $hasMultipleTypes) {
1579:             $msg[] = '';
1580:             $msg[] = '--' . $boundary . '--';
1581:             $msg[] = '';
1582:         }
1583:         return $msg;
1584:     }
1585: 
1586: /**
1587:  * Gets the text body types that are in this email message
1588:  *
1589:  * @return array Array of types. Valid types are 'text' and 'html'
1590:  */
1591:     protected function _getTypes() {
1592:         $types = array($this->_emailFormat);
1593:         if ($this->_emailFormat === 'both') {
1594:             $types = array('html', 'text');
1595:         }
1596:         return $types;
1597:     }
1598: 
1599: /**
1600:  * Build and set all the view properties needed to render the templated emails.
1601:  * If there is no template set, the $content will be returned in a hash
1602:  * of the text content types for the email.
1603:  *
1604:  * @param string $content The content passed in from send() in most cases.
1605:  * @return array The rendered content with html and text keys.
1606:  */
1607:     protected function _renderTemplates($content) {
1608:         $types = $this->_getTypes();
1609:         $rendered = array();
1610:         if (empty($this->_template)) {
1611:             foreach ($types as $type) {
1612:                 $rendered[$type] = $this->_encodeString($content, $this->charset);
1613:             }
1614:             return $rendered;
1615:         }
1616:         $viewClass = $this->_viewRender;
1617:         if ($viewClass !== 'View') {
1618:             list($plugin, $viewClass) = pluginSplit($viewClass, true);
1619:             $viewClass .= 'View';
1620:             App::uses($viewClass, $plugin . 'View');
1621:         }
1622: 
1623:         $View = new $viewClass(null);
1624:         $View->viewVars = $this->_viewVars;
1625:         $View->helpers = $this->_helpers;
1626: 
1627:         if ($this->_theme) {
1628:             $View->theme = $this->_theme;
1629:         }
1630: 
1631:         $View->loadHelpers();
1632: 
1633:         list($templatePlugin, $template) = pluginSplit($this->_template);
1634:         list($layoutPlugin, $layout) = pluginSplit($this->_layout);
1635:         if ($templatePlugin) {
1636:             $View->plugin = $templatePlugin;
1637:         } elseif ($layoutPlugin) {
1638:             $View->plugin = $layoutPlugin;
1639:         }
1640: 
1641:         if ($View->get('content') === null) {
1642:             $View->set('content', $content);
1643:         }
1644: 
1645:         // Convert null to false, as View needs false to disable
1646:         // the layout.
1647:         if ($this->_layout === null) {
1648:             $this->_layout = false;
1649:         }
1650: 
1651:         foreach ($types as $type) {
1652:             $View->hasRendered = false;
1653:             $View->viewPath = $View->layoutPath = 'Emails' . DS . $type;
1654: 
1655:             $render = $View->render($this->_template, $this->_layout);
1656:             $render = str_replace(array("\r\n", "\r"), "\n", $render);
1657:             $rendered[$type] = $this->_encodeString($render, $this->charset);
1658:         }
1659: 
1660:         foreach ($rendered as $type => $content) {
1661:             $rendered[$type] = $this->_wrap($content);
1662:             $rendered[$type] = implode("\n", $rendered[$type]);
1663:             $rendered[$type] = rtrim($rendered[$type], "\n");
1664:         }
1665:         return $rendered;
1666:     }
1667: 
1668: /**
1669:  * Return the Content-Transfer Encoding value based on the set charset
1670:  *
1671:  * @return void
1672:  */
1673:     protected function _getContentTransferEncoding() {
1674:         $charset = strtoupper($this->charset);
1675:         if (in_array($charset, $this->_charset8bit)) {
1676:             return '8bit';
1677:         }
1678:         return '7bit';
1679:     }
1680: 
1681: /**
1682:  * Return charset value for Content-Type.
1683:  *
1684:  * Checks fallback/compatibility types which include workarounds
1685:  * for legacy japanese character sets.
1686:  *
1687:  * @return string
1688:  */
1689:     protected function _getContentTypeCharset() {
1690:         $charset = strtoupper($this->charset);
1691:         if (array_key_exists($charset, $this->_contentTypeCharset)) {
1692:             return strtoupper($this->_contentTypeCharset[$charset]);
1693:         }
1694:         return strtoupper($this->charset);
1695:     }
1696: 
1697: }
1698: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs