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.8 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.8
      • 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
  • None

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