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

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