Overview

Namespaces

  • Kotchasan
    • Cache
    • Database
    • Http
    • Log
    • Orm
  • None
  • PHP
  • Psr
    • Cache
    • Http
      • Message
    • Log

Classes

  • ArrayIterator
  • DateInterval
  • DOMNode
  • Kotchasan
  • Kotchasan\Accordion
  • Kotchasan\ApiController
  • Kotchasan\ArrayTool
  • Kotchasan\Cache\ApcCache
  • Kotchasan\Cache\Cache
  • Kotchasan\Cache\CacheItem
  • Kotchasan\Cache\FileCache
  • Kotchasan\CKEditor
  • Kotchasan\Collection
  • Kotchasan\Config
  • Kotchasan\Controller
  • Kotchasan\Country
  • Kotchasan\Csv
  • Kotchasan\Curl
  • Kotchasan\Currency
  • Kotchasan\Database
  • Kotchasan\Database\Db
  • Kotchasan\Database\DbCache
  • Kotchasan\Database\Driver
  • Kotchasan\Database\PdoMysqlDriver
  • Kotchasan\Database\Query
  • Kotchasan\Database\QueryBuilder
  • Kotchasan\Database\Schema
  • Kotchasan\Database\Sql
  • Kotchasan\DataTable
  • Kotchasan\Date
  • Kotchasan\DOMNode
  • Kotchasan\DOMParser
  • Kotchasan\Email
  • Kotchasan\File
  • Kotchasan\Files
  • Kotchasan\Form
  • Kotchasan\Grid
  • Kotchasan\Html
  • Kotchasan\Htmldoc
  • Kotchasan\HtmlTable
  • Kotchasan\Http\AbstractMessage
  • Kotchasan\Http\AbstractRequest
  • Kotchasan\Http\Message
  • Kotchasan\Http\NotFound
  • Kotchasan\Http\Request
  • Kotchasan\Http\Response
  • Kotchasan\Http\Stream
  • Kotchasan\Http\UploadedFile
  • Kotchasan\Http\Uri
  • Kotchasan\Image
  • Kotchasan\InputItem
  • Kotchasan\Inputs
  • Kotchasan\KBase
  • Kotchasan\Language
  • Kotchasan\ListItem
  • Kotchasan\Log\AbstractLogger
  • Kotchasan\Log\Logger
  • Kotchasan\Login
  • Kotchasan\Menu
  • Kotchasan\Mime
  • Kotchasan\Model
  • Kotchasan\Number
  • Kotchasan\ObjectTool
  • Kotchasan\Orm\Field
  • Kotchasan\Orm\Recordset
  • Kotchasan\Password
  • Kotchasan\Pdf
  • Kotchasan\Province
  • Kotchasan\Router
  • Kotchasan\Session
  • Kotchasan\Singleton
  • Kotchasan\Tab
  • Kotchasan\TableRow
  • Kotchasan\Template
  • Kotchasan\Text
  • Kotchasan\Validator
  • Kotchasan\View
  • PHPMailer
  • Psr\Log\AbstractLogger
  • Psr\Log\LogLevel
  • Psr\Log\NullLogger
  • SMTP

Interfaces

  • ArrayAccess
  • Countable
  • DateTimeInterface
  • Iterator
  • IteratorAggregate
  • Psr\Cache\CacheItemInterface
  • Psr\Cache\CacheItemPoolInterface
  • Psr\Http\Message\MessageInterface
  • Psr\Http\Message\RequestInterface
  • Psr\Http\Message\ResponseInterface
  • Psr\Http\Message\ServerRequestInterface
  • Psr\Http\Message\StreamInterface
  • Psr\Http\Message\UploadedFileInterface
  • Psr\Http\Message\UriInterface
  • Psr\Log\LoggerAwareInterface
  • Psr\Log\LoggerInterface
  • SeekableIterator
  • Serializable
  • Traversable

Traits

  • Psr\Log\LoggerTrait

Exceptions

  • Exception
  • InvalidArgumentException
  • Kotchasan\ApiException
  • Kotchasan\Cache\Exception
  • Kotchasan\Database\Exception
  • Kotchasan\InputItemException
  • LogicException
  • phpmailerException
  • RuntimeException

Functions

  • createClass
  • debug
  • getClassPath
  • Overview
  • Namespace
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * PHPMailer - PHP email creation and transport class.
   4:  * PHP Version 5.
   5:  *
   6:  * @note This program is distributed in the hope that it will be useful - WITHOUT
   7:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   8:  * FITNESS FOR A PARTICULAR PURPOSE.
   9:  *
  10:  * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
  11:  * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
  12:  * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
  13:  * @author Brent R. Matzelle (original founder)
  14:  * @copyright 2012 - 2014 Marcus Bointon
  15:  * @copyright 2010 - 2012 Jim Jagielski
  16:  * @copyright 2004 - 2009 Andy Prevost
  17:  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  18:  *
  19:  * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
  20:  */
  21: include_once 'class.smtp.php';
  22: 
  23: /**
  24:  * PHPMailer - PHP email creation and transport class.
  25:  *
  26:  * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
  27:  * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
  28:  * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
  29:  * @author Brent R. Matzelle (original founder)
  30:  */
  31: class PHPMailer
  32: {
  33:     /**
  34:      * The PHPMailer Version number.
  35:      *
  36:      * @var string
  37:      */
  38:     public $Version = '5.2.21';
  39:     /**
  40:      * Email priority.
  41:      * Options: null (default), 1 = High, 3 = Normal, 5 = low.
  42:      * When null, the header is not set at all.
  43:      *
  44:      * @var int
  45:      */
  46:     public $Priority = null;
  47:     /**
  48:      * The character set of the message.
  49:      *
  50:      * @var string
  51:      */
  52:     public $CharSet = 'iso-8859-1';
  53:     /**
  54:      * The MIME Content-type of the message.
  55:      *
  56:      * @var string
  57:      */
  58:     public $ContentType = 'text/plain';
  59:     /**
  60:      * The message encoding.
  61:      * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
  62:      *
  63:      * @var string
  64:      */
  65:     public $Encoding = '8bit';
  66:     /**
  67:      * Holds the most recent mailer error message.
  68:      *
  69:      * @var string
  70:      */
  71:     public $ErrorInfo = '';
  72:     /**
  73:      * The From email address for the message.
  74:      *
  75:      * @var string
  76:      */
  77:     public $From = 'root@localhost';
  78:     /**
  79:      * The From name of the message.
  80:      *
  81:      * @var string
  82:      */
  83:     public $FromName = 'Root User';
  84:     /**
  85:      * The Sender email (Return-Path) of the message.
  86:      * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
  87:      *
  88:      * @var string
  89:      */
  90:     public $Sender = '';
  91:     /**
  92:      * The Return-Path of the message.
  93:      * If empty, it will be set to either From or Sender.
  94:      *
  95:      * @var string
  96:      *             it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
  97:      *
  98:      * @deprecated Email senders should never set a return-path header;
  99:      * @see https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
 100:      */
 101:     public $ReturnPath = '';
 102:     /**
 103:      * The Subject of the message.
 104:      *
 105:      * @var string
 106:      */
 107:     public $Subject = '';
 108:     /**
 109:      * An HTML or plain text message body.
 110:      * If HTML then call isHTML(true).
 111:      *
 112:      * @var string
 113:      */
 114:     public $Body = '';
 115:     /**
 116:      * The plain-text message body.
 117:      * This body can be read by mail clients that do not have HTML email
 118:      * capability such as mutt & Eudora.
 119:      * Clients that can read HTML will view the normal Body.
 120:      *
 121:      * @var string
 122:      */
 123:     public $AltBody = '';
 124:     /**
 125:      * An iCal message part body.
 126:      * Only supported in simple alt or alt_inline message types
 127:      * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator.
 128:      *
 129:      * @var string
 130:      *
 131:      * @see http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
 132:      * @see http://kigkonsult.se/iCalcreator/
 133:      */
 134:     public $Ical = '';
 135:     /**
 136:      * The complete compiled MIME message body.
 137:      *
 138:      * @var string
 139:      */
 140:     protected $MIMEBody = '';
 141:     /**
 142:      * The complete compiled MIME message headers.
 143:      *
 144:      * @var string
 145:      */
 146:     protected $MIMEHeader = '';
 147:     /**
 148:      * Extra headers that createHeader() doesn't fold in.
 149:      *
 150:      * @var string
 151:      */
 152:     protected $mailHeader = '';
 153:     /**
 154:      * Word-wrap the message body to this number of chars.
 155:      * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
 156:      *
 157:      * @var int
 158:      */
 159:     public $WordWrap = 0;
 160:     /**
 161:      * Which method to use to send mail.
 162:      * Options: "mail", "sendmail", or "smtp".
 163:      *
 164:      * @var string
 165:      */
 166:     public $Mailer = 'mail';
 167:     /**
 168:      * The path to the sendmail program.
 169:      *
 170:      * @var string
 171:      */
 172:     public $Sendmail = '/usr/sbin/sendmail';
 173:     /**
 174:      * Whether mail() uses a fully sendmail-compatible MTA.
 175:      * One which supports sendmail's "-oi -f" options.
 176:      *
 177:      * @var bool
 178:      */
 179:     public $UseSendmailOptions = true;
 180:     /**
 181:      * Path to PHPMailer plugins.
 182:      * Useful if the SMTP class is not in the PHP include path.
 183:      *
 184:      * @var string
 185:      *
 186:      * @deprecated should not be needed now there is an autoloader
 187:      */
 188:     public $PluginDir = '';
 189:     /**
 190:      * The email address that a reading confirmation should be sent to, also known as read receipt.
 191:      *
 192:      * @var string
 193:      */
 194:     public $ConfirmReadingTo = '';
 195:     /**
 196:      * The hostname to use in the Message-ID header and as default HELO string.
 197:      * If empty, PHPMailer attempts to find one with, in order,
 198:      * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
 199:      * 'localhost.localdomain'.
 200:      *
 201:      * @var string
 202:      */
 203:     public $Hostname = '';
 204:     /**
 205:      * An ID to be used in the Message-ID header.
 206:      * If empty, a unique id will be generated.
 207:      * You can set your own, but it must be in the format "<id@domain>",
 208:      * as defined in RFC5322 section 3.6.4 or it will be ignored.
 209:      *
 210:      * @var string
 211:      *
 212:      * @see https://tools.ietf.org/html/rfc5322#section-3.6.4
 213:      */
 214:     public $MessageID = '';
 215:     /**
 216:      * The message Date to be used in the Date header.
 217:      * If empty, the current date will be added.
 218:      *
 219:      * @var string
 220:      */
 221:     public $MessageDate = '';
 222:     /**
 223:      * SMTP hosts.
 224:      * Either a single hostname or multiple semicolon-delimited hostnames.
 225:      * You can also specify a different port
 226:      * for each host by using this format: [hostname:port]
 227:      * (e.g. "smtp1.example.com:25;smtp2.example.com").
 228:      * You can also specify encryption type, for example:
 229:      * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
 230:      * Hosts will be tried in order.
 231:      *
 232:      * @var string
 233:      */
 234:     public $Host = 'localhost';
 235:     /**
 236:      * The default SMTP server port.
 237:      *
 238:      * @var int
 239:      * @TODO Why is this needed when the SMTP class takes care of it?
 240:      */
 241:     public $Port = 25;
 242:     /**
 243:      * The SMTP HELO of the message.
 244:      * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
 245:      * one with the same method described above for $Hostname.
 246:      *
 247:      * @var string
 248:      *
 249:      * @see PHPMailer::$Hostname
 250:      */
 251:     public $Helo = '';
 252:     /**
 253:      * What kind of encryption to use on the SMTP connection.
 254:      * Options: '', 'ssl' or 'tls'.
 255:      *
 256:      * @var string
 257:      */
 258:     public $SMTPSecure = '';
 259:     /**
 260:      * Whether to enable TLS encryption automatically if a server supports it,
 261:      * even if `SMTPSecure` is not set to 'tls'.
 262:      * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
 263:      *
 264:      * @var bool
 265:      */
 266:     public $SMTPAutoTLS = true;
 267:     /**
 268:      * Whether to use SMTP authentication.
 269:      * Uses the Username and Password properties.
 270:      *
 271:      * @var bool
 272:      *
 273:      * @see PHPMailer::$Username
 274:      * @see PHPMailer::$Password
 275:      */
 276:     public $SMTPAuth = false;
 277:     /**
 278:      * Options array passed to stream_context_create when connecting via SMTP.
 279:      *
 280:      * @var array
 281:      */
 282:     public $SMTPOptions = array();
 283:     /**
 284:      * SMTP username.
 285:      *
 286:      * @var string
 287:      */
 288:     public $Username = '';
 289:     /**
 290:      * SMTP password.
 291:      *
 292:      * @var string
 293:      */
 294:     public $Password = '';
 295:     /**
 296:      * SMTP auth type.
 297:      * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified.
 298:      *
 299:      * @var string
 300:      */
 301:     public $AuthType = '';
 302:     /**
 303:      * SMTP realm.
 304:      * Used for NTLM auth.
 305:      *
 306:      * @var string
 307:      */
 308:     public $Realm = '';
 309:     /**
 310:      * SMTP workstation.
 311:      * Used for NTLM auth.
 312:      *
 313:      * @var string
 314:      */
 315:     public $Workstation = '';
 316:     /**
 317:      * The SMTP server timeout in seconds.
 318:      * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2.
 319:      *
 320:      * @var int
 321:      */
 322:     public $Timeout = 300;
 323:     /**
 324:      * SMTP class debug output mode.
 325:      * Debug output level.
 326:      * Options:
 327:      *  `0` No output
 328:      *  `1` Commands
 329:      *  `2` Data and commands
 330:      *  `3` As 2 plus connection status
 331:      *  `4` Low-level data output.
 332:      *
 333:      * @var int
 334:      *
 335:      * @see SMTP::$do_debug
 336:      */
 337:     public $SMTPDebug = 0;
 338:     /**
 339:      * How to handle debug output.
 340:      * Options:
 341:      *  `echo` Output plain-text as-is, appropriate for CLI
 342:      *  `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
 343:      *  `error_log` Output to error log as configured in php.ini.
 344:      *
 345:      * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
 346:      * <code>
 347:      * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
 348:      * </code>
 349:      *
 350:      * @var string|callable
 351:      *
 352:      * @see SMTP::$Debugoutput
 353:      */
 354:     public $Debugoutput = 'echo';
 355:     /**
 356:      * Whether to keep SMTP connection open after each message.
 357:      * If this is set to true then to close the connection
 358:      * requires an explicit call to smtpClose().
 359:      *
 360:      * @var bool
 361:      */
 362:     public $SMTPKeepAlive = false;
 363:     /**
 364:      * Whether to split multiple to addresses into multiple messages
 365:      * or send them all in one message.
 366:      * Only supported in `mail` and `sendmail` transports, not in SMTP.
 367:      *
 368:      * @var bool
 369:      */
 370:     public $SingleTo = false;
 371:     /**
 372:      * Storage for addresses when SingleTo is enabled.
 373:      *
 374:      * @var array
 375:      * @TODO This should really not be public
 376:      */
 377:     public $SingleToArray = array();
 378:     /**
 379:      * Whether to generate VERP addresses on send.
 380:      * Only applicable when sending via SMTP.
 381:      *
 382:      * @var bool
 383:      *
 384:      * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path
 385:      * @see http://www.postfix.org/VERP_README.html Postfix VERP info
 386:      */
 387:     public $do_verp = false;
 388:     /**
 389:      * Whether to allow sending messages with an empty body.
 390:      *
 391:      * @var bool
 392:      */
 393:     public $AllowEmpty = false;
 394:     /**
 395:      * The default line ending.
 396:      *
 397:      * @note The default remains "\n". We force CRLF where we know
 398:      *        it must be used via self::CRLF.
 399:      *
 400:      * @var string
 401:      */
 402:     public $LE = "\n";
 403:     /**
 404:      * DKIM selector.
 405:      *
 406:      * @var string
 407:      */
 408:     public $DKIM_selector = '';
 409:     /**
 410:      * DKIM Identity.
 411:      * Usually the email address used as the source of the email.
 412:      *
 413:      * @var string
 414:      */
 415:     public $DKIM_identity = '';
 416:     /**
 417:      * DKIM passphrase.
 418:      * Used if your key is encrypted.
 419:      *
 420:      * @var string
 421:      */
 422:     public $DKIM_passphrase = '';
 423:     /**
 424:      * DKIM signing domain name.
 425:      *
 426:      * @example 'example.com'
 427:      *
 428:      * @var string
 429:      */
 430:     public $DKIM_domain = '';
 431:     /**
 432:      * DKIM private key file path.
 433:      *
 434:      * @var string
 435:      */
 436:     public $DKIM_private = '';
 437:     /**
 438:      * DKIM private key string.
 439:      * If set, takes precedence over `$DKIM_private`.
 440:      *
 441:      * @var string
 442:      */
 443:     public $DKIM_private_string = '';
 444:     /**
 445:      * Callback Action function name.
 446:      *
 447:      * The function that handles the result of the send email action.
 448:      * It is called out by send() for each email sent.
 449:      *
 450:      * Value can be any php callable: http://www.php.net/is_callable
 451:      *
 452:      * Parameters:
 453:      *   boolean $result        result of the send action
 454:      *   string  $to            email address of the recipient
 455:      *   string  $cc            cc email addresses
 456:      *   string  $bcc           bcc email addresses
 457:      *   string  $subject       the subject
 458:      *   string  $body          the email body
 459:      *   string  $from          email address of sender
 460:      *
 461:      * @var string
 462:      */
 463:     public $action_function = '';
 464:     /**
 465:      * What to put in the X-Mailer header.
 466:      * Options: An empty string for PHPMailer default, whitespace for none, or a string to use.
 467:      *
 468:      * @var string
 469:      */
 470:     public $XMailer = '';
 471:     /**
 472:      * Which validator to use by default when validating email addresses.
 473:      * May be a callable to inject your own validator, but there are several built-in validators.
 474:      *
 475:      * @var string|callable
 476:      * @static
 477:      *
 478:      * @see PHPMailer::validateAddress()
 479:      */
 480:     public static $validator = 'auto';
 481:     /**
 482:      * An instance of the SMTP sender class.
 483:      *
 484:      * @var SMTP
 485:      */
 486:     protected $smtp = null;
 487:     /**
 488:      * The array of 'to' names and addresses.
 489:      *
 490:      * @var array
 491:      */
 492:     protected $to = array();
 493:     /**
 494:      * The array of 'cc' names and addresses.
 495:      *
 496:      * @var array
 497:      */
 498:     protected $cc = array();
 499:     /**
 500:      * The array of 'bcc' names and addresses.
 501:      *
 502:      * @var array
 503:      */
 504:     protected $bcc = array();
 505:     /**
 506:      * The array of reply-to names and addresses.
 507:      *
 508:      * @var array
 509:      */
 510:     protected $ReplyTo = array();
 511:     /**
 512:      * An array of all kinds of addresses.
 513:      * Includes all of $to, $cc, $bcc.
 514:      *
 515:      * @var array
 516:      *
 517:      * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
 518:      */
 519:     protected $all_recipients = array();
 520:     /**
 521:      * An array of names and addresses queued for validation.
 522:      * In send(), valid and non duplicate entries are moved to $all_recipients
 523:      * and one of $to, $cc, or $bcc.
 524:      * This array is used only for addresses with IDN.
 525:      *
 526:      * @var array
 527:      *
 528:      * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
 529:      * @see PHPMailer::$all_recipients
 530:      */
 531:     protected $RecipientsQueue = array();
 532:     /**
 533:      * An array of reply-to names and addresses queued for validation.
 534:      * In send(), valid and non duplicate entries are moved to $ReplyTo.
 535:      * This array is used only for addresses with IDN.
 536:      *
 537:      * @var array
 538:      *
 539:      * @see PHPMailer::$ReplyTo
 540:      */
 541:     protected $ReplyToQueue = array();
 542:     /**
 543:      * The array of attachments.
 544:      *
 545:      * @var array
 546:      */
 547:     protected $attachment = array();
 548:     /**
 549:      * The array of custom headers.
 550:      *
 551:      * @var array
 552:      */
 553:     protected $CustomHeader = array();
 554:     /**
 555:      * The most recent Message-ID (including angular brackets).
 556:      *
 557:      * @var string
 558:      */
 559:     protected $lastMessageID = '';
 560:     /**
 561:      * The message's MIME type.
 562:      *
 563:      * @var string
 564:      */
 565:     protected $message_type = '';
 566:     /**
 567:      * The array of MIME boundary strings.
 568:      *
 569:      * @var array
 570:      */
 571:     protected $boundary = array();
 572:     /**
 573:      * The array of available languages.
 574:      *
 575:      * @var array
 576:      */
 577:     protected $language = array();
 578:     /**
 579:      * The number of errors encountered.
 580:      *
 581:      * @var int
 582:      */
 583:     protected $error_count = 0;
 584:     /**
 585:      * The S/MIME certificate file path.
 586:      *
 587:      * @var string
 588:      */
 589:     protected $sign_cert_file = '';
 590:     /**
 591:      * The S/MIME key file path.
 592:      *
 593:      * @var string
 594:      */
 595:     protected $sign_key_file = '';
 596:     /**
 597:      * The optional S/MIME extra certificates ("CA Chain") file path.
 598:      *
 599:      * @var string
 600:      */
 601:     protected $sign_extracerts_file = '';
 602:     /**
 603:      * The S/MIME password for the key.
 604:      * Used only if the key is encrypted.
 605:      *
 606:      * @var string
 607:      */
 608:     protected $sign_key_pass = '';
 609:     /**
 610:      * Whether to throw exceptions for errors.
 611:      *
 612:      * @var bool
 613:      */
 614:     protected $exceptions = false;
 615:     /**
 616:      * Unique ID used for message ID and boundaries.
 617:      *
 618:      * @var string
 619:      */
 620:     protected $uniqueid = '';
 621:     /**
 622:      * Error severity: message only, continue processing.
 623:      */
 624:     const STOP_MESSAGE = 0;
 625:     /**
 626:      * Error severity: message, likely ok to continue processing.
 627:      */
 628:     const STOP_CONTINUE = 1;
 629:     /**
 630:      * Error severity: message, plus full stop, critical error reached.
 631:      */
 632:     const STOP_CRITICAL = 2;
 633:     /**
 634:      * SMTP RFC standard line ending.
 635:      */
 636:     const CRLF = "\r\n";
 637:     /**
 638:      * The maximum line length allowed by RFC 2822 section 2.1.1.
 639:      *
 640:      * @var int
 641:      */
 642:     const MAX_LINE_LENGTH = 998;
 643: 
 644:     /**
 645:      * Constructor.
 646:      *
 647:      * @param bool $exceptions Should we throw external exceptions?
 648:      */
 649:     public function __construct($exceptions = null)
 650:     {
 651:         if ($exceptions !== null) {
 652:             $this->exceptions = (bool) $exceptions;
 653:         }
 654:     }
 655: 
 656:     /**
 657:      * Destructor.
 658:      */
 659:     public function __destruct()
 660:     {
 661:         //Close any open SMTP connection nicely
 662:         $this->smtpClose();
 663:     }
 664: 
 665:     /**
 666:      * Call mail() in a safe_mode-aware fashion.
 667:      * Also, unless sendmail_path points to sendmail (or something that
 668:      * claims to be sendmail), don't pass params (not a perfect fix,
 669:      * but it will do).
 670:      *
 671:      * @param string $to      To
 672:      * @param string $subject Subject
 673:      * @param string $body    Message Body
 674:      * @param string $header  Additional Header(s)
 675:      * @param string $params  Params
 676:      *
 677:      * @return bool
 678:      */
 679:     private function mailPassthru($to, $subject, $body, $header, $params)
 680:     {
 681:         //Check overloading of mail function to avoid double-encoding
 682:         if (ini_get('mbstring.func_overload') & 1) {
 683:             $subject = $this->secureHeader($subject);
 684:         } else {
 685:             $subject = $this->encodeHeader($this->secureHeader($subject));
 686:         }
 687: 
 688:         //Can't use additional_parameters in safe_mode, calling mail() with null params breaks
 689:         //@link http://php.net/manual/en/function.mail.php
 690:         if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) {
 691:             $result = @mail($to, $subject, $body, $header);
 692:         } else {
 693:             $result = @mail($to, $subject, $body, $header, $params);
 694:         }
 695: 
 696:         return $result;
 697:     }
 698: 
 699:     /**
 700:      * Output debugging info via user-defined method.
 701:      * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
 702:      *
 703:      * @see PHPMailer::$Debugoutput
 704:      * @see PHPMailer::$SMTPDebug
 705:      *
 706:      * @param string $str
 707:      */
 708:     protected function edebug($str)
 709:     {
 710:         if ($this->SMTPDebug <= 0) {
 711:             return;
 712:         }
 713:         //Avoid clash with built-in function names
 714:         if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
 715:             call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
 716: 
 717:             return;
 718:         }
 719:         switch ($this->Debugoutput) {
 720:             case 'error_log':
 721:                 //Don't output, just log
 722:                 error_log($str);
 723:                 break;
 724:             case 'html':
 725:                 //Cleans up output a bit for a better looking, HTML-safe output
 726:                 echo htmlentities(
 727:                     preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8'
 728:                 )
 729:                     ."<br>\n";
 730:                 break;
 731:             case 'echo':
 732:             default:
 733:                 //Normalize line breaks
 734:                 $str = preg_replace('/\r\n?/ms', "\n", $str);
 735:                 echo gmdate('Y-m-d H:i:s')."\t".str_replace(
 736:                     "\n", "\n                   \t                  ", trim($str)
 737:                 )."\n";
 738:         }
 739:     }
 740: 
 741:     /**
 742:      * Sets message type to HTML or plain.
 743:      *
 744:      * @param bool $isHtml true for HTML mode
 745:      */
 746:     public function isHTML($isHtml = true)
 747:     {
 748:         if ($isHtml) {
 749:             $this->ContentType = 'text/html';
 750:         } else {
 751:             $this->ContentType = 'text/plain';
 752:         }
 753:     }
 754: 
 755:     /**
 756:      * Send messages using SMTP.
 757:      */
 758:     public function isSMTP()
 759:     {
 760:         $this->Mailer = 'smtp';
 761:     }
 762: 
 763:     /**
 764:      * Send messages using PHP's mail() function.
 765:      */
 766:     public function isMail()
 767:     {
 768:         $this->Mailer = 'mail';
 769:     }
 770: 
 771:     /**
 772:      * Send messages using $Sendmail.
 773:      */
 774:     public function isSendmail()
 775:     {
 776:         $ini_sendmail_path = ini_get('sendmail_path');
 777: 
 778:         if (!stristr($ini_sendmail_path, 'sendmail')) {
 779:             $this->Sendmail = '/usr/sbin/sendmail';
 780:         } else {
 781:             $this->Sendmail = $ini_sendmail_path;
 782:         }
 783:         $this->Mailer = 'sendmail';
 784:     }
 785: 
 786:     /**
 787:      * Send messages using qmail.
 788:      */
 789:     public function isQmail()
 790:     {
 791:         $ini_sendmail_path = ini_get('sendmail_path');
 792: 
 793:         if (!stristr($ini_sendmail_path, 'qmail')) {
 794:             $this->Sendmail = '/var/qmail/bin/qmail-inject';
 795:         } else {
 796:             $this->Sendmail = $ini_sendmail_path;
 797:         }
 798:         $this->Mailer = 'qmail';
 799:     }
 800: 
 801:     /**
 802:      * Add a "To" address.
 803:      *
 804:      * @param string $address The email address to send to
 805:      * @param string $name
 806:      *
 807:      * @return bool true on success, false if address already used or invalid in some way
 808:      */
 809:     public function addAddress($address, $name = '')
 810:     {
 811:         return $this->addOrEnqueueAnAddress('to', $address, $name);
 812:     }
 813: 
 814:     /**
 815:      * Add a "CC" address.
 816:      *
 817:      * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
 818:      *
 819:      * @param string $address The email address to send to
 820:      * @param string $name
 821:      *
 822:      * @return bool true on success, false if address already used or invalid in some way
 823:      */
 824:     public function addCC($address, $name = '')
 825:     {
 826:         return $this->addOrEnqueueAnAddress('cc', $address, $name);
 827:     }
 828: 
 829:     /**
 830:      * Add a "BCC" address.
 831:      *
 832:      * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
 833:      *
 834:      * @param string $address The email address to send to
 835:      * @param string $name
 836:      *
 837:      * @return bool true on success, false if address already used or invalid in some way
 838:      */
 839:     public function addBCC($address, $name = '')
 840:     {
 841:         return $this->addOrEnqueueAnAddress('bcc', $address, $name);
 842:     }
 843: 
 844:     /**
 845:      * Add a "Reply-To" address.
 846:      *
 847:      * @param string $address The email address to reply to
 848:      * @param string $name
 849:      *
 850:      * @return bool true on success, false if address already used or invalid in some way
 851:      */
 852:     public function addReplyTo($address, $name = '')
 853:     {
 854:         return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
 855:     }
 856: 
 857:     /**
 858:      * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
 859:      * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
 860:      * be modified after calling this function), addition of such addresses is delayed until send().
 861:      * Addresses that have been added already return false, but do not throw exceptions.
 862:      *
 863:      * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
 864:      * @param string $address The email address to send, resp. to reply to
 865:      * @param string $name
 866:      *
 867:      * @throws phpmailerException
 868:      *
 869:      * @return bool true on success, false if address already used or invalid in some way
 870:      */
 871:     protected function addOrEnqueueAnAddress($kind, $address, $name)
 872:     {
 873:         $address = trim($address);
 874:         $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
 875:         if (($pos = strrpos($address, '@')) === false) {
 876:             // At-sign is misssing.
 877:             $error_message = $this->lang('invalid_address')." (addAnAddress $kind): $address";
 878:             $this->setError($error_message);
 879:             $this->edebug($error_message);
 880:             if ($this->exceptions) {
 881:                 throw new phpmailerException($error_message);
 882:             }
 883: 
 884:             return false;
 885:         }
 886:         $params = array($kind, $address, $name);
 887:         // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
 888:         if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
 889:             if ($kind != 'Reply-To') {
 890:                 if (!array_key_exists($address, $this->RecipientsQueue)) {
 891:                     $this->RecipientsQueue[$address] = $params;
 892: 
 893:                     return true;
 894:                 }
 895:             } else {
 896:                 if (!array_key_exists($address, $this->ReplyToQueue)) {
 897:                     $this->ReplyToQueue[$address] = $params;
 898: 
 899:                     return true;
 900:                 }
 901:             }
 902: 
 903:             return false;
 904:         }
 905:         // Immediately add standard addresses without IDN.
 906: 
 907:         return call_user_func_array(array($this, 'addAnAddress'), $params);
 908:     }
 909: 
 910:     /**
 911:      * Add an address to one of the recipient arrays or to the ReplyTo array.
 912:      * Addresses that have been added already return false, but do not throw exceptions.
 913:      *
 914:      * @param string $kind    One of 'to', 'cc', 'bcc', or 'ReplyTo'
 915:      * @param string $address The email address to send, resp. to reply to
 916:      * @param string $name
 917:      *
 918:      * @throws phpmailerException
 919:      *
 920:      * @return bool true on success, false if address already used or invalid in some way
 921:      */
 922:     protected function addAnAddress($kind, $address, $name = '')
 923:     {
 924:         if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
 925:             $error_message = $this->lang('Invalid recipient kind: ').$kind;
 926:             $this->setError($error_message);
 927:             $this->edebug($error_message);
 928:             if ($this->exceptions) {
 929:                 throw new phpmailerException($error_message);
 930:             }
 931: 
 932:             return false;
 933:         }
 934:         if (!$this->validateAddress($address)) {
 935:             $error_message = $this->lang('invalid_address')." (addAnAddress $kind): $address";
 936:             $this->setError($error_message);
 937:             $this->edebug($error_message);
 938:             if ($this->exceptions) {
 939:                 throw new phpmailerException($error_message);
 940:             }
 941: 
 942:             return false;
 943:         }
 944:         if ($kind != 'Reply-To') {
 945:             if (!array_key_exists(strtolower($address), $this->all_recipients)) {
 946:                 array_push($this->$kind, array($address, $name));
 947:                 $this->all_recipients[strtolower($address)] = true;
 948: 
 949:                 return true;
 950:             }
 951:         } else {
 952:             if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
 953:                 $this->ReplyTo[strtolower($address)] = array($address, $name);
 954: 
 955:                 return true;
 956:             }
 957:         }
 958: 
 959:         return false;
 960:     }
 961: 
 962:     /**
 963:      * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
 964:      * of the form "display name <address>" into an array of name/address pairs.
 965:      * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
 966:      * Note that quotes in the name part are removed.
 967:      *
 968:      * @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
 969:      *
 970:      * @param string $addrstr The address list string
 971:      * @param bool   $useimap Whether to use the IMAP extension to parse the list
 972:      *
 973:      * @return array
 974:      */
 975:     public function parseAddresses($addrstr, $useimap = true)
 976:     {
 977:         $addresses = array();
 978:         if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
 979:             //Use this built-in parser if it's available
 980:             $list = imap_rfc822_parse_adrlist($addrstr, '');
 981:             foreach ($list as $address) {
 982:                 if ($address->host != '.SYNTAX-ERROR.') {
 983:                     if ($this->validateAddress($address->mailbox.'@'.$address->host)) {
 984:                         $addresses[] = array(
 985:                             'name' => (property_exists($address, 'personal') ? $address->personal : ''),
 986:                             'address' => $address->mailbox.'@'.$address->host,
 987:                         );
 988:                     }
 989:                 }
 990:             }
 991:         } else {
 992:             //Use this simpler parser
 993:             $list = explode(',', $addrstr);
 994:             foreach ($list as $address) {
 995:                 $address = trim($address);
 996:                 //Is there a separate name part?
 997:                 if (strpos($address, '<') === false) {
 998:                     //No separate name, just use the whole thing
 999:                     if ($this->validateAddress($address)) {
1000:                         $addresses[] = array(
1001:                             'name' => '',
1002:                             'address' => $address,
1003:                         );
1004:                     }
1005:                 } else {
1006:                     list($name, $email) = explode('<', $address);
1007:                     $email = trim(str_replace('>', '', $email));
1008:                     if ($this->validateAddress($email)) {
1009:                         $addresses[] = array(
1010:                             'name' => trim(str_replace(array('"', "'"), '', $name)),
1011:                             'address' => $email,
1012:                         );
1013:                     }
1014:                 }
1015:             }
1016:         }
1017: 
1018:         return $addresses;
1019:     }
1020: 
1021:     /**
1022:      * Set the From and FromName properties.
1023:      *
1024:      * @param string $address
1025:      * @param string $name
1026:      * @param bool   $auto    Whether to also set the Sender address, defaults to true
1027:      *
1028:      * @throws phpmailerException
1029:      *
1030:      * @return bool
1031:      */
1032:     public function setFrom($address, $name = '', $auto = true)
1033:     {
1034:         $address = trim($address);
1035:         $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
1036:         // Don't validate now addresses with IDN. Will be done in send().
1037:         if (($pos = strrpos($address, '@')) === false or (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and !$this->validateAddress($address)) {
1038:             $error_message = $this->lang('invalid_address')." (setFrom) $address";
1039:             $this->setError($error_message);
1040:             $this->edebug($error_message);
1041:             if ($this->exceptions) {
1042:                 throw new phpmailerException($error_message);
1043:             }
1044: 
1045:             return false;
1046:         }
1047:         $this->From = $address;
1048:         $this->FromName = $name;
1049:         if ($auto) {
1050:             if (empty($this->Sender)) {
1051:                 $this->Sender = $address;
1052:             }
1053:         }
1054: 
1055:         return true;
1056:     }
1057: 
1058:     /**
1059:      * Return the Message-ID header of the last email.
1060:      * Technically this is the value from the last time the headers were created,
1061:      * but it's also the message ID of the last sent message except in
1062:      * pathological cases.
1063:      *
1064:      * @return string
1065:      */
1066:     public function getLastMessageID()
1067:     {
1068:         return $this->lastMessageID;
1069:     }
1070: 
1071:     /**
1072:      * Check that a string looks like an email address.
1073:      *  `auto` Pick best pattern automatically;
1074:      *  `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
1075:      *  `pcre` Use old PCRE implementation;
1076:      *  `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
1077:      *  `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
1078:      *  `noregex` Don't use a regex: super fast, really dumb.
1079:      * Alternatively you may pass in a callable to inject your own validator, for example:
1080:      * PHPMailer::validateAddress('user@example.com', function($address) {
1081:      *     return (strpos($address, '@') !== false);
1082:      * });
1083:      * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
1084:      *
1085:      * @static
1086:      *
1087:      * @param string          $address       The email address to check
1088:      * @param string|callable $patternselect A selector for the validation pattern to use :
1089:      *
1090:      * @return bool
1091:      */
1092:     public static function validateAddress($address, $patternselect = null)
1093:     {
1094:         if (is_null($patternselect)) {
1095:             $patternselect = self::$validator;
1096:         }
1097:         if (is_callable($patternselect)) {
1098:             return call_user_func($patternselect, $address);
1099:         }
1100:         //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
1101:         if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
1102:             return false;
1103:         }
1104:         if (!$patternselect or $patternselect == 'auto') {
1105:             //Check this constant first so it works when extension_loaded() is disabled by safe mode
1106:             //Constant was added in PHP 5.2.4
1107:             if (defined('PCRE_VERSION')) {
1108:                 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
1109:                 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
1110:                     $patternselect = 'pcre8';
1111:                 } else {
1112:                     $patternselect = 'pcre';
1113:                 }
1114:             } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
1115:                 //Fall back to older PCRE
1116:                 $patternselect = 'pcre';
1117:             } else {
1118:                 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
1119:                 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
1120:                     $patternselect = 'php';
1121:                 } else {
1122:                     $patternselect = 'noregex';
1123:                 }
1124:             }
1125:         }
1126:         switch ($patternselect) {
1127:             case 'pcre8':
1128:                 /*
1129:                  * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
1130:                  * Feel free to use and redistribute this code. But please keep this copyright notice.
1131:                  * @copyright 2009-2010 Michael Rushton
1132:                  *
1133:                  * @link http://squiloople.com/2009/12/20/email-address-validation/
1134:                  */
1135:                 return (bool) preg_match(
1136:                     '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)'.
1137:                     '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)'.
1138:                     '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)'.
1139:                     '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*'.
1140:                     '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)'.
1141:                     '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}'.
1142:                     '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:'.
1143:                     '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}'.
1144:                     '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address
1145:                 );
1146:             case 'pcre':
1147:                 //An older regex that doesn't need a recent PCRE
1148:                 return (bool) preg_match(
1149:                     '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>'.
1150:                     '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")'.
1151:                     '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*'.
1152:                     '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})'.
1153:                     '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:'.
1154:                     '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?'.
1155:                     '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:'.
1156:                     '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?'.
1157:                     '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}'.
1158:                     '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', $address
1159:                 );
1160:             case 'html5':
1161:                 /*
1162:                  * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
1163:                  * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
1164:                  */
1165:                 return (bool) preg_match(
1166:                     '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}'.
1167:                     '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', $address
1168:                 );
1169:             case 'noregex':
1170:                 //No PCRE! Do something _very_ approximate!
1171:                 //Check the address is 3 chars or longer and contains an @ that's not the first or last char
1172:                 return strlen($address) >= 3
1173:                     and strpos($address, '@') >= 1
1174:                     and strpos($address, '@') != strlen($address) - 1;
1175:             case 'php':
1176:             default:
1177:                 return (bool) filter_var($address, FILTER_VALIDATE_EMAIL);
1178:         }
1179:     }
1180: 
1181:     /**
1182:      * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
1183:      * "intl" and "mbstring" PHP extensions.
1184:      *
1185:      * @return bool "true" if required functions for IDN support are present
1186:      */
1187:     public function idnSupported()
1188:     {
1189:         // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
1190:         return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
1191:     }
1192: 
1193:     /**
1194:      * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
1195:      * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
1196:      * This function silently returns unmodified address if:
1197:      * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
1198:      * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
1199:      *   or fails for any reason (e.g. domain has characters not allowed in an IDN).
1200:      *
1201:      * @see PHPMailer::$CharSet
1202:      *
1203:      * @param string $address The email address to convert
1204:      *
1205:      * @return string The encoded address in ASCII form
1206:      */
1207:     public function punyencodeAddress($address)
1208:     {
1209:         // Verify we have required functions, CharSet, and at-sign.
1210:         if ($this->idnSupported() and !empty($this->CharSet) and ($pos = strrpos($address, '@')) !== false) {
1211:             $domain = substr($address, ++$pos);
1212:             // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
1213:             if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
1214:                 $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
1215:                 if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
1216:                     idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
1217:                     idn_to_ascii($domain)) !== false) {
1218:                     return substr($address, 0, $pos).$punycode;
1219:                 }
1220:             }
1221:         }
1222: 
1223:         return $address;
1224:     }
1225: 
1226:     /**
1227:      * Create a message and send it.
1228:      * Uses the sending method specified by $Mailer.
1229:      *
1230:      * @throws phpmailerException
1231:      *
1232:      * @return bool false on error - See the ErrorInfo property for details of the error
1233:      */
1234:     public function send()
1235:     {
1236:         try {
1237:             if (!$this->preSend()) {
1238:                 return false;
1239:             }
1240: 
1241:             return $this->postSend();
1242:         } catch (phpmailerException $exc) {
1243:             $this->mailHeader = '';
1244:             $this->setError($exc->getMessage());
1245:             if ($this->exceptions) {
1246:                 throw $exc;
1247:             }
1248: 
1249:             return false;
1250:         }
1251:     }
1252: 
1253:     /**
1254:      * Prepare a message for sending.
1255:      *
1256:      * @throws phpmailerException
1257:      *
1258:      * @return bool
1259:      */
1260:     public function preSend()
1261:     {
1262:         try {
1263:             $this->error_count = 0; // Reset errors
1264:             $this->mailHeader = '';
1265: 
1266:             // Dequeue recipient and Reply-To addresses with IDN
1267:             foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
1268:                 $params[1] = $this->punyencodeAddress($params[1]);
1269:                 call_user_func_array(array($this, 'addAnAddress'), $params);
1270:             }
1271:             if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
1272:                 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
1273:             }
1274: 
1275:             // Validate From, Sender, and ConfirmReadingTo addresses
1276:             foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
1277:                 $this->$address_kind = trim($this->$address_kind);
1278:                 if (empty($this->$address_kind)) {
1279:                     continue;
1280:                 }
1281:                 $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
1282:                 if (!$this->validateAddress($this->$address_kind)) {
1283:                     $error_message = $this->lang('invalid_address').' (punyEncode) '.$this->$address_kind;
1284:                     $this->setError($error_message);
1285:                     $this->edebug($error_message);
1286:                     if ($this->exceptions) {
1287:                         throw new phpmailerException($error_message);
1288:                     }
1289: 
1290:                     return false;
1291:                 }
1292:             }
1293: 
1294:             // Set whether the message is multipart/alternative
1295:             if ($this->alternativeExists()) {
1296:                 $this->ContentType = 'multipart/alternative';
1297:             }
1298: 
1299:             $this->setMessageType();
1300:             // Refuse to send an empty message unless we are specifically allowing it
1301:             if (!$this->AllowEmpty and empty($this->Body)) {
1302:                 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
1303:             }
1304: 
1305:             // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
1306:             $this->MIMEHeader = '';
1307:             $this->MIMEBody = $this->createBody();
1308:             // createBody may have added some headers, so retain them
1309:             $tempheaders = $this->MIMEHeader;
1310:             $this->MIMEHeader = $this->createHeader();
1311:             $this->MIMEHeader .= $tempheaders;
1312: 
1313:             // To capture the complete message when using mail(), create
1314:             // an extra header list which createHeader() doesn't fold in
1315:             if ($this->Mailer == 'mail') {
1316:                 if (count($this->to) > 0) {
1317:                     $this->mailHeader .= $this->addrAppend('To', $this->to);
1318:                 } else {
1319:                     $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
1320:                 }
1321:                 $this->mailHeader .= $this->headerLine(
1322:                     'Subject', $this->encodeHeader($this->secureHeader(trim($this->Subject)))
1323:                 );
1324:             }
1325: 
1326:             // Sign with DKIM if enabled
1327:             if (!empty($this->DKIM_domain) && !empty($this->DKIM_selector) && (!empty($this->DKIM_private_string) || (!empty($this->DKIM_private) && file_exists($this->DKIM_private))
1328:             )
1329:             ) {
1330:                 $header_dkim = $this->DKIM_Add(
1331:                     $this->MIMEHeader.$this->mailHeader, $this->encodeHeader($this->secureHeader($this->Subject)), $this->MIMEBody
1332:                 );
1333:                 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ").self::CRLF.
1334:                 str_replace("\r\n", "\n", $header_dkim).self::CRLF;
1335:             }
1336: 
1337:             return true;
1338:         } catch (phpmailerException $exc) {
1339:             $this->setError($exc->getMessage());
1340:             if ($this->exceptions) {
1341:                 throw $exc;
1342:             }
1343: 
1344:             return false;
1345:         }
1346:     }
1347: 
1348:     /**
1349:      * Actually send a message.
1350:      * Send the email via the selected mechanism.
1351:      *
1352:      * @throws phpmailerException
1353:      *
1354:      * @return bool
1355:      */
1356:     public function postSend()
1357:     {
1358:         try {
1359:             // Choose the mailer and send through it
1360:             switch ($this->Mailer) {
1361:                 case 'sendmail':
1362:                 case 'qmail':
1363:                     return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
1364:                 case 'smtp':
1365:                     return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
1366:                 case 'mail':
1367:                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1368:                 default:
1369:                     $sendMethod = $this->Mailer.'Send';
1370:                     if (method_exists($this, $sendMethod)) {
1371:                         return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
1372:                     }
1373: 
1374:                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
1375:             }
1376:         } catch (phpmailerException $exc) {
1377:             $this->setError($exc->getMessage());
1378:             $this->edebug($exc->getMessage());
1379:             if ($this->exceptions) {
1380:                 throw $exc;
1381:             }
1382:         }
1383: 
1384:         return false;
1385:     }
1386: 
1387:     /**
1388:      * Send mail using the $Sendmail program.
1389:      *
1390:      * @see PHPMailer::$Sendmail
1391:      *
1392:      * @param string $header The message headers
1393:      * @param string $body   The message body
1394:      *
1395:      * @throws phpmailerException
1396:      *
1397:      * @return bool
1398:      */
1399:     protected function sendmailSend($header, $body)
1400:     {
1401:         // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1402:         if (!empty($this->Sender) and self::isShellSafe($this->Sender)) {
1403:             if ($this->Mailer == 'qmail') {
1404:                 $sendmailFmt = '%s -f%s';
1405:             } else {
1406:                 $sendmailFmt = '%s -oi -f%s -t';
1407:             }
1408:         } else {
1409:             if ($this->Mailer == 'qmail') {
1410:                 $sendmailFmt = '%s';
1411:             } else {
1412:                 $sendmailFmt = '%s -oi -t';
1413:             }
1414:         }
1415: 
1416:         // TODO: If possible, this should be changed to escapeshellarg.  Needs thorough testing.
1417:         $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender);
1418: 
1419:         if ($this->SingleTo) {
1420:             foreach ($this->SingleToArray as $toAddr) {
1421:                 if (!@$mail = popen($sendmail, 'w')) {
1422:                     throw new phpmailerException($this->lang('execute').$this->Sendmail, self::STOP_CRITICAL);
1423:                 }
1424:                 fputs($mail, 'To: '.$toAddr."\n");
1425:                 fputs($mail, $header);
1426:                 fputs($mail, $body);
1427:                 $result = pclose($mail);
1428:                 $this->doCallback(
1429:                     ($result == 0), array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From
1430:                 );
1431:                 if ($result != 0) {
1432:                     throw new phpmailerException($this->lang('execute').$this->Sendmail, self::STOP_CRITICAL);
1433:                 }
1434:             }
1435:         } else {
1436:             if (!@$mail = popen($sendmail, 'w')) {
1437:                 throw new phpmailerException($this->lang('execute').$this->Sendmail, self::STOP_CRITICAL);
1438:             }
1439:             fputs($mail, $header);
1440:             fputs($mail, $body);
1441:             $result = pclose($mail);
1442:             $this->doCallback(
1443:                 ($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From
1444:             );
1445:             if ($result != 0) {
1446:                 throw new phpmailerException($this->lang('execute').$this->Sendmail, self::STOP_CRITICAL);
1447:             }
1448:         }
1449: 
1450:         return true;
1451:     }
1452: 
1453:     /**
1454:      * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
1455:      *
1456:      * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
1457:      *
1458:      * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report
1459:      *
1460:      * @param string $string The string to be validated
1461:      *
1462:      * @return bool
1463:      */
1464:     protected static function isShellSafe($string)
1465:     {
1466:         // Future-proof
1467:         if (escapeshellcmd($string) !== $string
1468:             or !in_array(escapeshellarg($string), array("'$string'", "\"$string\""))
1469:         ) {
1470:             return false;
1471:         }
1472: 
1473:         $length = strlen($string);
1474: 
1475:         for ($i = 0; $i < $length; ++$i) {
1476:             $c = $string[$i];
1477: 
1478:             // All other characters have a special meaning in at least one common shell, including = and +.
1479:             // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
1480:             // Note that this does permit non-Latin alphanumeric characters based on the current locale.
1481:             if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
1482:                 return false;
1483:             }
1484:         }
1485: 
1486:         return true;
1487:     }
1488: 
1489:     /**
1490:      * Send mail using the PHP mail() function.
1491:      *
1492:      * @see http://www.php.net/manual/en/book.mail.php
1493:      *
1494:      * @param string $header The message headers
1495:      * @param string $body   The message body
1496:      *
1497:      * @throws phpmailerException
1498:      *
1499:      * @return bool
1500:      */
1501:     protected function mailSend($header, $body)
1502:     {
1503:         $toArr = array();
1504:         foreach ($this->to as $toaddr) {
1505:             $toArr[] = $this->addrFormat($toaddr);
1506:         }
1507:         $to = implode(', ', $toArr);
1508: 
1509:         $params = null;
1510:         //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
1511:         if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1512:             // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
1513:             if (self::isShellSafe($this->Sender)) {
1514:                 $params = sprintf('-f%s', $this->Sender);
1515:             }
1516:         }
1517:         if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) {
1518:             $old_from = ini_get('sendmail_from');
1519:             ini_set('sendmail_from', $this->Sender);
1520:         }
1521:         $result = false;
1522:         if ($this->SingleTo and count($toArr) > 1) {
1523:             foreach ($toArr as $toAddr) {
1524:                 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
1525:                 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1526:             }
1527:         } else {
1528:             $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
1529:             $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
1530:         }
1531:         if (isset($old_from)) {
1532:             ini_set('sendmail_from', $old_from);
1533:         }
1534:         if (!$result) {
1535:             throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
1536:         }
1537: 
1538:         return true;
1539:     }
1540: 
1541:     /**
1542:      * Get an instance to use for SMTP operations.
1543:      * Override this function to load your own SMTP implementation.
1544:      *
1545:      * @return SMTP
1546:      */
1547:     public function getSMTPInstance()
1548:     {
1549:         if (!is_object($this->smtp)) {
1550:             $this->smtp = new SMTP();
1551:         }
1552: 
1553:         return $this->smtp;
1554:     }
1555: 
1556:     /**
1557:      * Send mail via SMTP.
1558:      * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
1559:      * Uses the PHPMailerSMTP class by default.
1560:      *
1561:      * @uses \SMTP
1562:      *
1563:      * @see PHPMailer::getSMTPInstance() to use a different class.
1564:      *
1565:      * @param string $header The message headers
1566:      * @param string $body   The message body
1567:      *
1568:      * @throws phpmailerException
1569:      *
1570:      * @return bool
1571:      */
1572:     protected function smtpSend($header, $body)
1573:     {
1574:         $bad_rcpt = array();
1575:         if (!$this->smtpConnect($this->SMTPOptions)) {
1576:             throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
1577:         }
1578:         if (!empty($this->Sender) and $this->validateAddress($this->Sender)) {
1579:             $smtp_from = $this->Sender;
1580:         } else {
1581:             $smtp_from = $this->From;
1582:         }
1583:         if (!$this->smtp->mail($smtp_from)) {
1584:             $this->setError($this->lang('from_failed').$smtp_from.' : '.implode(',', $this->smtp->getError()));
1585:             throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
1586:         }
1587: 
1588:         // Attempt to send to all recipients
1589:         foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
1590:             foreach ($togroup as $to) {
1591:                 if (!$this->smtp->recipient($to[0])) {
1592:                     $error = $this->smtp->getError();
1593:                     $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
1594:                     $isSent = false;
1595:                 } else {
1596:                     $isSent = true;
1597:                 }
1598:                 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
1599:             }
1600:         }
1601: 
1602:         // Only send the DATA command if we have viable recipients
1603:         if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header.$body)) {
1604:             throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
1605:         }
1606:         if ($this->SMTPKeepAlive) {
1607:             $this->smtp->reset();
1608:         } else {
1609:             $this->smtp->quit();
1610:             $this->smtp->close();
1611:         }
1612:         //Create error message for any bad addresses
1613:         if (count($bad_rcpt) > 0) {
1614:             $errstr = '';
1615:             foreach ($bad_rcpt as $bad) {
1616:                 $errstr .= $bad['to'].': '.$bad['error'];
1617:             }
1618:             throw new phpmailerException(
1619:                 $this->lang('recipients_failed').$errstr, self::STOP_CONTINUE
1620:             );
1621:         }
1622: 
1623:         return true;
1624:     }
1625: 
1626:     /**
1627:      * Initiate a connection to an SMTP server.
1628:      * Returns false if the operation failed.
1629:      *
1630:      * @uses \SMTP
1631:      *
1632:      * @param array $options An array of options compatible with stream_context_create()
1633:      *
1634:      * @throws phpmailerException
1635:      *
1636:      * @return bool
1637:      */
1638:     public function smtpConnect($options = null)
1639:     {
1640:         if (is_null($this->smtp)) {
1641:             $this->smtp = $this->getSMTPInstance();
1642:         }
1643: 
1644:         //If no options are provided, use whatever is set in the instance
1645:         if (is_null($options)) {
1646:             $options = $this->SMTPOptions;
1647:         }
1648: 
1649:         // Already connected?
1650:         if ($this->smtp->connected()) {
1651:             return true;
1652:         }
1653: 
1654:         $this->smtp->setTimeout($this->Timeout);
1655:         $this->smtp->setDebugLevel($this->SMTPDebug);
1656:         $this->smtp->setDebugOutput($this->Debugoutput);
1657:         $this->smtp->setVerp($this->do_verp);
1658:         $hosts = explode(';', $this->Host);
1659:         $lastexception = null;
1660: 
1661:         foreach ($hosts as $hostentry) {
1662:             $hostinfo = array();
1663:             if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
1664:                 // Not a valid host entry
1665:                 continue;
1666:             }
1667:             // $hostinfo[2]: optional ssl or tls prefix
1668:             // $hostinfo[3]: the hostname
1669:             // $hostinfo[4]: optional port number
1670:             // The host string prefix can temporarily override the current setting for SMTPSecure
1671:             // If it's not specified, the default value is used
1672:             $prefix = '';
1673:             $secure = $this->SMTPSecure;
1674:             $tls = ($this->SMTPSecure == 'tls');
1675:             if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
1676:                 $prefix = 'ssl://';
1677:                 $tls = false; // Can't have SSL and TLS at the same time
1678:                 $secure = 'ssl';
1679:             } elseif ($hostinfo[2] == 'tls') {
1680:                 $tls = true;
1681:                 // tls doesn't use a prefix
1682:                 $secure = 'tls';
1683:             }
1684:             //Do we need the OpenSSL extension?
1685:             $sslext = defined('OPENSSL_ALGO_SHA1');
1686:             if ('tls' === $secure or 'ssl' === $secure) {
1687:                 //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
1688:                 if (!$sslext) {
1689:                     throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
1690:                 }
1691:             }
1692:             $host = $hostinfo[3];
1693:             $port = $this->Port;
1694:             $tport = (int) $hostinfo[4];
1695:             if ($tport > 0 and $tport < 65536) {
1696:                 $port = $tport;
1697:             }
1698:             if ($this->smtp->connect($prefix.$host, $port, $this->Timeout, $options)) {
1699:                 try {
1700:                     if ($this->Helo) {
1701:                         $hello = $this->Helo;
1702:                     } else {
1703:                         $hello = $this->serverHostname();
1704:                     }
1705:                     $this->smtp->hello($hello);
1706:                     //Automatically enable TLS encryption if:
1707:                     // * it's not disabled
1708:                     // * we have openssl extension
1709:                     // * we are not already using SSL
1710:                     // * the server offers STARTTLS
1711:                     if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
1712:                         $tls = true;
1713:                     }
1714:                     if ($tls) {
1715:                         if (!$this->smtp->startTLS()) {
1716:                             throw new phpmailerException($this->lang('connect_host'));
1717:                         }
1718:                         // We must resend EHLO after TLS negotiation
1719:                         $this->smtp->hello($hello);
1720:                     }
1721:                     if ($this->SMTPAuth) {
1722:                         if (!$this->smtp->authenticate(
1723:                             $this->Username, $this->Password, $this->AuthType, $this->Realm, $this->Workstation
1724:                         )
1725:                         ) {
1726:                             throw new phpmailerException($this->lang('authenticate'));
1727:                         }
1728:                     }
1729: 
1730:                     return true;
1731:                 } catch (phpmailerException $exc) {
1732:                     $lastexception = $exc;
1733:                     $this->edebug($exc->getMessage());
1734:                     // We must have connected, but then failed TLS or Auth, so close connection nicely
1735:                     $this->smtp->quit();
1736:                 }
1737:             }
1738:         }
1739:         // If we get here, all connection attempts have failed, so close connection hard
1740:         $this->smtp->close();
1741:         // As we've caught all exceptions, just report whatever the last one was
1742:         if ($this->exceptions and !is_null($lastexception)) {
1743:             throw $lastexception;
1744:         }
1745: 
1746:         return false;
1747:     }
1748: 
1749:     /**
1750:      * Close the active SMTP session if one exists.
1751:      */
1752:     public function smtpClose()
1753:     {
1754:         if (is_a($this->smtp, 'SMTP')) {
1755:             if ($this->smtp->connected()) {
1756:                 $this->smtp->quit();
1757:                 $this->smtp->close();
1758:             }
1759:         }
1760:     }
1761: 
1762:     /**
1763:      * Set the language for error messages.
1764:      * Returns false if it cannot load the language file.
1765:      * The default language is English.
1766:      *
1767:      * @param string $langcode  ISO 639-1 2-character language code (e.g. French is "fr")
1768:      * @param string $lang_path Path to the language file directory, with trailing separator (slash)
1769:      *
1770:      * @return bool
1771:      */
1772:     public function setLanguage($langcode = 'en', $lang_path = '')
1773:     {
1774:         // Backwards compatibility for renamed language codes
1775:         $renamed_langcodes = array(
1776:             'br' => 'pt_br',
1777:             'cz' => 'cs',
1778:             'dk' => 'da',
1779:             'no' => 'nb',
1780:             'se' => 'sv',
1781:         );
1782: 
1783:         if (isset($renamed_langcodes[$langcode])) {
1784:             $langcode = $renamed_langcodes[$langcode];
1785:         }
1786: 
1787:         // Define full set of translatable strings in English
1788:         $PHPMAILER_LANG = array(
1789:             'authenticate' => 'SMTP Error: Could not authenticate.',
1790:             'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
1791:             'data_not_accepted' => 'SMTP Error: data not accepted.',
1792:             'empty_message' => 'Message body empty',
1793:             'encoding' => 'Unknown encoding: ',
1794:             'execute' => 'Could not execute: ',
1795:             'file_access' => 'Could not access file: ',
1796:             'file_open' => 'File Error: Could not open file: ',
1797:             'from_failed' => 'The following From address failed: ',
1798:             'instantiate' => 'Could not instantiate mail function.',
1799:             'invalid_address' => 'Invalid address: ',
1800:             'mailer_not_supported' => ' mailer is not supported.',
1801:             'provide_address' => 'You must provide at least one recipient email address.',
1802:             'recipients_failed' => 'SMTP Error: The following recipients failed: ',
1803:             'signing' => 'Signing Error: ',
1804:             'smtp_connect_failed' => 'SMTP connect() failed.',
1805:             'smtp_error' => 'SMTP server error: ',
1806:             'variable_set' => 'Cannot set or reset variable: ',
1807:             'extension_missing' => 'Extension missing: ',
1808:         );
1809:         if (empty($lang_path)) {
1810:             // Calculate an absolute path so it can work if CWD is not here
1811:             $lang_path = dirname(__FILE__).DIRECTORY_SEPARATOR.'language'.DIRECTORY_SEPARATOR;
1812:         }
1813:         //Validate $langcode
1814:         if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) {
1815:             $langcode = 'en';
1816:         }
1817:         $foundlang = true;
1818:         $lang_file = $lang_path.'phpmailer.lang-'.$langcode.'.php';
1819:         // There is no English translation file
1820:         if ($langcode != 'en') {
1821:             // Make sure language file path is readable
1822:             if (!is_readable($lang_file)) {
1823:                 $foundlang = false;
1824:             } else {
1825:                 // Overwrite language-specific strings.
1826:                 // This way we'll never have missing translation keys.
1827:                 $foundlang = include $lang_file;
1828:             }
1829:         }
1830:         $this->language = $PHPMAILER_LANG;
1831: 
1832:         return (bool) $foundlang; // Returns false if language not found
1833:     }
1834: 
1835:     /**
1836:      * Get the array of strings for the current language.
1837:      *
1838:      * @return array
1839:      */
1840:     public function getTranslations()
1841:     {
1842:         return $this->language;
1843:     }
1844: 
1845:     /**
1846:      * Create recipient headers.
1847:      *
1848:      * @param string $type
1849:      * @param array  $addr An array of recipient,
1850:      *
1851:      * @return string
1852:      */
1853:     public function addrAppend($type, $addr)
1854:     {
1855:         $addresses = array();
1856:         foreach ($addr as $address) {
1857:             $addresses[] = $this->addrFormat($address);
1858:         }
1859: 
1860:         return $type.': '.implode(', ', $addresses).$this->LE;
1861:     }
1862: 
1863:     /**
1864:      * Format an address for use in a message header.
1865:      *
1866:      * @param array $addr A 2-element indexed array, element containing an address, element 1 containing a name
1867:      *
1868:      * @return string
1869:      */
1870:     public function addrFormat($addr)
1871:     {
1872:         if (empty($addr[1])) { // No name provided
1873:             return $this->secureHeader($addr[0]);
1874:         } else {
1875:             return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase').' <'.$this->secureHeader(
1876:                 $addr[0]
1877:             ).'>';
1878:         }
1879:     }
1880: 
1881:     /**
1882:      * Word-wrap message.
1883:      * For use with mailers that do not automatically perform wrapping
1884:      * and for quoted-printable encoded messages.
1885:      * Original written by philippe.
1886:      *
1887:      * @param string $message The message to wrap
1888:      * @param int    $length  The line length to wrap to
1889:      * @param bool   $qp_mode Whether to run in Quoted-Printable mode
1890:      *
1891:      * @return string
1892:      */
1893:     public function wrapText($message, $length, $qp_mode = false)
1894:     {
1895:         if ($qp_mode) {
1896:             $soft_break = sprintf(' =%s', $this->LE);
1897:         } else {
1898:             $soft_break = $this->LE;
1899:         }
1900:         // If utf-8 encoding is used, we will need to make sure we don't
1901:         // split multibyte characters when we wrap
1902:         $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
1903:         $lelen = strlen($this->LE);
1904:         $crlflen = strlen(self::CRLF);
1905: 
1906:         $message = $this->fixEOL($message);
1907:         //Remove a trailing line break
1908:         if (substr($message, -$lelen) == $this->LE) {
1909:             $message = substr($message, 0, -$lelen);
1910:         }
1911: 
1912:         //Split message into lines
1913:         $lines = explode($this->LE, $message);
1914:         //Message will be rebuilt in here
1915:         $message = '';
1916:         foreach ($lines as $line) {
1917:             $words = explode(' ', $line);
1918:             $buf = '';
1919:             $firstword = true;
1920:             foreach ($words as $word) {
1921:                 if ($qp_mode and (strlen($word) > $length)) {
1922:                     $space_left = $length - strlen($buf) - $crlflen;
1923:                     if (!$firstword) {
1924:                         if ($space_left > 20) {
1925:                             $len = $space_left;
1926:                             if ($is_utf8) {
1927:                                 $len = $this->utf8CharBoundary($word, $len);
1928:                             } elseif (substr($word, $len - 1, 1) == '=') {
1929:                                 --$len;
1930:                             } elseif (substr($word, $len - 2, 1) == '=') {
1931:                                 $len -= 2;
1932:                             }
1933:                             $part = substr($word, 0, $len);
1934:                             $word = substr($word, $len);
1935:                             $buf .= ' '.$part;
1936:                             $message .= $buf.sprintf('=%s', self::CRLF);
1937:                         } else {
1938:                             $message .= $buf.$soft_break;
1939:                         }
1940:                         $buf = '';
1941:                     }
1942:                     while (strlen($word) > 0) {
1943:                         if ($length <= 0) {
1944:                             break;
1945:                         }
1946:                         $len = $length;
1947:                         if ($is_utf8) {
1948:                             $len = $this->utf8CharBoundary($word, $len);
1949:                         } elseif (substr($word, $len - 1, 1) == '=') {
1950:                             --$len;
1951:                         } elseif (substr($word, $len - 2, 1) == '=') {
1952:                             $len -= 2;
1953:                         }
1954:                         $part = substr($word, 0, $len);
1955:                         $word = substr($word, $len);
1956: 
1957:                         if (strlen($word) > 0) {
1958:                             $message .= $part.sprintf('=%s', self::CRLF);
1959:                         } else {
1960:                             $buf = $part;
1961:                         }
1962:                     }
1963:                 } else {
1964:                     $buf_o = $buf;
1965:                     if (!$firstword) {
1966:                         $buf .= ' ';
1967:                     }
1968:                     $buf .= $word;
1969: 
1970:                     if (strlen($buf) > $length and $buf_o != '') {
1971:                         $message .= $buf_o.$soft_break;
1972:                         $buf = $word;
1973:                     }
1974:                 }
1975:                 $firstword = false;
1976:             }
1977:             $message .= $buf.self::CRLF;
1978:         }
1979: 
1980:         return $message;
1981:     }
1982: 
1983:     /**
1984:      * Find the last character boundary prior to $maxLength in a utf-8
1985:      * quoted-printable encoded string.
1986:      * Original written by Colin Brown.
1987:      *
1988:      * @param string $encodedText utf-8 QP text
1989:      * @param int    $maxLength   Find the last character boundary prior to this length
1990:      *
1991:      * @return int
1992:      */
1993:     public function utf8CharBoundary($encodedText, $maxLength)
1994:     {
1995:         $foundSplitPos = false;
1996:         $lookBack = 3;
1997:         while (!$foundSplitPos) {
1998:             $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
1999:             $encodedCharPos = strpos($lastChunk, '=');
2000:             if (false !== $encodedCharPos) {
2001:                 // Found start of encoded character byte within $lookBack block.
2002:                 // Check the encoded byte value (the 2 chars after the '=')
2003:                 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
2004:                 $dec = hexdec($hex);
2005:                 if ($dec < 128) {
2006:                     // Single byte character.
2007:                     // If the encoded char was found at pos 0, it will fit
2008:                     // otherwise reduce maxLength to start of the encoded char
2009:                     if ($encodedCharPos > 0) {
2010:                         $maxLength = $maxLength - ($lookBack - $encodedCharPos);
2011:                     }
2012:                     $foundSplitPos = true;
2013:                 } elseif ($dec >= 192) {
2014:                     // First byte of a multi byte character
2015:                     // Reduce maxLength to split at start of character
2016:                     $maxLength = $maxLength - ($lookBack - $encodedCharPos);
2017:                     $foundSplitPos = true;
2018:                 } elseif ($dec < 192) {
2019:                     // Middle byte of a multi byte character, look further back
2020:                     $lookBack += 3;
2021:                 }
2022:             } else {
2023:                 // No encoded character found
2024:                 $foundSplitPos = true;
2025:             }
2026:         }
2027: 
2028:         return $maxLength;
2029:     }
2030: 
2031:     /**
2032:      * Apply word wrapping to the message body.
2033:      * Wraps the message body to the number of chars set in the WordWrap property.
2034:      * You should only do this to plain-text bodies as wrapping HTML tags may break them.
2035:      * This is called automatically by createBody(), so you don't need to call it yourself.
2036:      */
2037:     public function setWordWrap()
2038:     {
2039:         if ($this->WordWrap < 1) {
2040:             return;
2041:         }
2042: 
2043:         switch ($this->message_type) {
2044:             case 'alt':
2045:             case 'alt_inline':
2046:             case 'alt_attach':
2047:             case 'alt_inline_attach':
2048:                 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
2049:                 break;
2050:             default:
2051:                 $this->Body = $this->wrapText($this->Body, $this->WordWrap);
2052:                 break;
2053:         }
2054:     }
2055: 
2056:     /**
2057:      * Assemble message headers.
2058:      *
2059:      * @return string The assembled headers
2060:      */
2061:     public function createHeader()
2062:     {
2063:         $result = '';
2064: 
2065:         if ($this->MessageDate == '') {
2066:             $this->MessageDate = self::rfcDate();
2067:         }
2068:         $result .= $this->headerLine('Date', $this->MessageDate);
2069: 
2070:         // To be created automatically by mail()
2071:         if ($this->SingleTo) {
2072:             if ($this->Mailer != 'mail') {
2073:                 foreach ($this->to as $toaddr) {
2074:                     $this->SingleToArray[] = $this->addrFormat($toaddr);
2075:                 }
2076:             }
2077:         } else {
2078:             if (count($this->to) > 0) {
2079:                 if ($this->Mailer != 'mail') {
2080:                     $result .= $this->addrAppend('To', $this->to);
2081:                 }
2082:             } elseif (count($this->cc) == 0) {
2083:                 $result .= $this->headerLine('To', 'undisclosed-recipients:;');
2084:             }
2085:         }
2086: 
2087:         $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
2088: 
2089:         // sendmail and mail() extract Cc from the header before sending
2090:         if (count($this->cc) > 0) {
2091:             $result .= $this->addrAppend('Cc', $this->cc);
2092:         }
2093: 
2094:         // sendmail and mail() extract Bcc from the header before sending
2095:         if ((
2096:             $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
2097:         )
2098:             and count($this->bcc) > 0
2099:         ) {
2100:             $result .= $this->addrAppend('Bcc', $this->bcc);
2101:         }
2102: 
2103:         if (count($this->ReplyTo) > 0) {
2104:             $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
2105:         }
2106: 
2107:         // mail() sets the subject itself
2108:         if ($this->Mailer != 'mail') {
2109:             $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2110:         }
2111: 
2112:         // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4
2113:         // https://tools.ietf.org/html/rfc5322#section-3.6.4
2114:         if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
2115:             $this->lastMessageID = $this->MessageID;
2116:         } else {
2117:             $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
2118:         }
2119:         $result .= $this->headerLine('Message-ID', $this->lastMessageID);
2120:         if (!is_null($this->Priority)) {
2121:             $result .= $this->headerLine('X-Priority', $this->Priority);
2122:         }
2123:         if ($this->XMailer == '') {
2124:             $result .= $this->headerLine(
2125:                 'X-Mailer', 'PHPMailer '.$this->Version.' (https://github.com/PHPMailer/PHPMailer)'
2126:             );
2127:         } else {
2128:             $myXmailer = trim($this->XMailer);
2129:             if ($myXmailer) {
2130:                 $result .= $this->headerLine('X-Mailer', $myXmailer);
2131:             }
2132:         }
2133: 
2134:         if ($this->ConfirmReadingTo != '') {
2135:             $result .= $this->headerLine('Disposition-Notification-To', '<'.$this->ConfirmReadingTo.'>');
2136:         }
2137: 
2138:         // Add custom headers
2139:         foreach ($this->CustomHeader as $header) {
2140:             $result .= $this->headerLine(
2141:                 trim($header[0]), $this->encodeHeader(trim($header[1]))
2142:             );
2143:         }
2144:         if (!$this->sign_key_file) {
2145:             $result .= $this->headerLine('MIME-Version', '1.0');
2146:             $result .= $this->getMailMIME();
2147:         }
2148: 
2149:         return $result;
2150:     }
2151: 
2152:     /**
2153:      * Get the message MIME type headers.
2154:      *
2155:      * @return string
2156:      */
2157:     public function getMailMIME()
2158:     {
2159:         $result = '';
2160:         $ismultipart = true;
2161:         switch ($this->message_type) {
2162:             case 'inline':
2163:                 $result .= $this->headerLine('Content-Type', 'multipart/related;');
2164:                 $result .= $this->textLine("\tboundary=\"".$this->boundary[1].'"');
2165:                 break;
2166:             case 'attach':
2167:             case 'inline_attach':
2168:             case 'alt_attach':
2169:             case 'alt_inline_attach':
2170:                 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2171:                 $result .= $this->textLine("\tboundary=\"".$this->boundary[1].'"');
2172:                 break;
2173:             case 'alt':
2174:             case 'alt_inline':
2175:                 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2176:                 $result .= $this->textLine("\tboundary=\"".$this->boundary[1].'"');
2177:                 break;
2178:             default:
2179:                 // Catches case 'plain': and case '':
2180:                 $result .= $this->textLine('Content-Type: '.$this->ContentType.'; charset='.$this->CharSet);
2181:                 $ismultipart = false;
2182:                 break;
2183:         }
2184:         // RFC1341 part 5 says 7bit is assumed if not specified
2185:         if ($this->Encoding != '7bit') {
2186:             // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2187:             if ($ismultipart) {
2188:                 if ($this->Encoding == '8bit') {
2189:                     $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2190:                 }
2191:                 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2192:             } else {
2193:                 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
2194:             }
2195:         }
2196: 
2197:         if ($this->Mailer != 'mail') {
2198:             $result .= $this->LE;
2199:         }
2200: 
2201:         return $result;
2202:     }
2203: 
2204:     /**
2205:      * Returns the whole MIME message.
2206:      * Includes complete headers and body.
2207:      * Only valid post preSend().
2208:      *
2209:      * @see PHPMailer::preSend()
2210:      *
2211:      * @return string
2212:      */
2213:     public function getSentMIMEMessage()
2214:     {
2215:         return rtrim($this->MIMEHeader.$this->mailHeader, "\n\r").self::CRLF.self::CRLF.$this->MIMEBody;
2216:     }
2217: 
2218:     /**
2219:      * Create unique ID.
2220:      *
2221:      * @return string
2222:      */
2223:     protected function generateId()
2224:     {
2225:         return md5(uniqid(time()));
2226:     }
2227: 
2228:     /**
2229:      * Assemble the message body.
2230:      * Returns an empty string on failure.
2231:      *
2232:      * @throws phpmailerException
2233:      *
2234:      * @return string The assembled message body
2235:      */
2236:     public function createBody()
2237:     {
2238:         $body = '';
2239:         //Create unique IDs and preset boundaries
2240:         $this->uniqueid = $this->generateId();
2241:         $this->boundary[1] = 'b1_'.$this->uniqueid;
2242:         $this->boundary[2] = 'b2_'.$this->uniqueid;
2243:         $this->boundary[3] = 'b3_'.$this->uniqueid;
2244: 
2245:         if ($this->sign_key_file) {
2246:             $body .= $this->getMailMIME().$this->LE;
2247:         }
2248: 
2249:         $this->setWordWrap();
2250: 
2251:         $bodyEncoding = $this->Encoding;
2252:         $bodyCharSet = $this->CharSet;
2253:         //Can we do a 7-bit downgrade?
2254:         if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
2255:             $bodyEncoding = '7bit';
2256:             //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2257:             $bodyCharSet = 'us-ascii';
2258:         }
2259:         //If lines are too long, and we're not already using an encoding that will shorten them,
2260:         //change to quoted-printable transfer encoding for the body part only
2261:         if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
2262:             $bodyEncoding = 'quoted-printable';
2263:         }
2264: 
2265:         $altBodyEncoding = $this->Encoding;
2266:         $altBodyCharSet = $this->CharSet;
2267:         //Can we do a 7-bit downgrade?
2268:         if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
2269:             $altBodyEncoding = '7bit';
2270:             //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit
2271:             $altBodyCharSet = 'us-ascii';
2272:         }
2273:         //If lines are too long, and we're not already using an encoding that will shorten them,
2274:         //change to quoted-printable transfer encoding for the alt body part only
2275:         if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
2276:             $altBodyEncoding = 'quoted-printable';
2277:         }
2278:         //Use this as a preamble in all multipart message types
2279:         $mimepre = 'This is a multi-part message in MIME format.'.$this->LE.$this->LE;
2280:         switch ($this->message_type) {
2281:             case 'inline':
2282:                 $body .= $mimepre;
2283:                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2284:                 $body .= $this->encodeString($this->Body, $bodyEncoding);
2285:                 $body .= $this->LE.$this->LE;
2286:                 $body .= $this->attachAll('inline', $this->boundary[1]);
2287:                 break;
2288:             case 'attach':
2289:                 $body .= $mimepre;
2290:                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
2291:                 $body .= $this->encodeString($this->Body, $bodyEncoding);
2292:                 $body .= $this->LE.$this->LE;
2293:                 $body .= $this->attachAll('attachment', $this->boundary[1]);
2294:                 break;
2295:             case 'inline_attach':
2296:                 $body .= $mimepre;
2297:                 $body .= $this->textLine('--'.$this->boundary[1]);
2298:                 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2299:                 $body .= $this->textLine("\tboundary=\"".$this->boundary[2].'"');
2300:                 $body .= $this->LE;
2301:                 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
2302:                 $body .= $this->encodeString($this->Body, $bodyEncoding);
2303:                 $body .= $this->LE.$this->LE;
2304:                 $body .= $this->attachAll('inline', $this->boundary[2]);
2305:                 $body .= $this->LE;
2306:                 $body .= $this->attachAll('attachment', $this->boundary[1]);
2307:                 break;
2308:             case 'alt':
2309:                 $body .= $mimepre;
2310:                 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2311:                 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2312:                 $body .= $this->LE.$this->LE;
2313:                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
2314:                 $body .= $this->encodeString($this->Body, $bodyEncoding);
2315:                 $body .= $this->LE.$this->LE;
2316:                 if (!empty($this->Ical)) {
2317:                     $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
2318:                     $body .= $this->encodeString($this->Ical, $this->Encoding);
2319:                     $body .= $this->LE.$this->LE;
2320:                 }
2321:                 $body .= $this->endBoundary($this->boundary[1]);
2322:                 break;
2323:             case 'alt_inline':
2324:                 $body .= $mimepre;
2325:                 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2326:                 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2327:                 $body .= $this->LE.$this->LE;
2328:                 $body .= $this->textLine('--'.$this->boundary[1]);
2329:                 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2330:                 $body .= $this->textLine("\tboundary=\"".$this->boundary[2].'"');
2331:                 $body .= $this->LE;
2332:                 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2333:                 $body .= $this->encodeString($this->Body, $bodyEncoding);
2334:                 $body .= $this->LE.$this->LE;
2335:                 $body .= $this->attachAll('inline', $this->boundary[2]);
2336:                 $body .= $this->LE;
2337:                 $body .= $this->endBoundary($this->boundary[1]);
2338:                 break;
2339:             case 'alt_attach':
2340:                 $body .= $mimepre;
2341:                 $body .= $this->textLine('--'.$this->boundary[1]);
2342:                 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2343:                 $body .= $this->textLine("\tboundary=\"".$this->boundary[2].'"');
2344:                 $body .= $this->LE;
2345:                 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2346:                 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2347:                 $body .= $this->LE.$this->LE;
2348:                 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
2349:                 $body .= $this->encodeString($this->Body, $bodyEncoding);
2350:                 $body .= $this->LE.$this->LE;
2351:                 $body .= $this->endBoundary($this->boundary[2]);
2352:                 $body .= $this->LE;
2353:                 $body .= $this->attachAll('attachment', $this->boundary[1]);
2354:                 break;
2355:             case 'alt_inline_attach':
2356:                 $body .= $mimepre;
2357:                 $body .= $this->textLine('--'.$this->boundary[1]);
2358:                 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
2359:                 $body .= $this->textLine("\tboundary=\"".$this->boundary[2].'"');
2360:                 $body .= $this->LE;
2361:                 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
2362:                 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
2363:                 $body .= $this->LE.$this->LE;
2364:                 $body .= $this->textLine('--'.$this->boundary[2]);
2365:                 $body .= $this->headerLine('Content-Type', 'multipart/related;');
2366:                 $body .= $this->textLine("\tboundary=\"".$this->boundary[3].'"');
2367:                 $body .= $this->LE;
2368:                 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
2369:                 $body .= $this->encodeString($this->Body, $bodyEncoding);
2370:                 $body .= $this->LE.$this->LE;
2371:                 $body .= $this->attachAll('inline', $this->boundary[3]);
2372:                 $body .= $this->LE;
2373:                 $body .= $this->endBoundary($this->boundary[2]);
2374:                 $body .= $this->LE;
2375:                 $body .= $this->attachAll('attachment', $this->boundary[1]);
2376:                 break;
2377:             default:
2378:                 // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types
2379:                 //Reset the `Encoding` property in case we changed it for line length reasons
2380:                 $this->Encoding = $bodyEncoding;
2381:                 $body .= $this->encodeString($this->Body, $this->Encoding);
2382:                 break;
2383:         }
2384: 
2385:         if ($this->isError()) {
2386:             $body = '';
2387:         } elseif ($this->sign_key_file) {
2388:             try {
2389:                 if (!defined('PKCS7_TEXT')) {
2390:                     throw new phpmailerException($this->lang('extension_missing').'openssl');
2391:                 }
2392:                 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
2393:                 $file = tempnam(sys_get_temp_dir(), 'mail');
2394:                 if (false === file_put_contents($file, $body)) {
2395:                     throw new phpmailerException($this->lang('signing').' Could not write temp file');
2396:                 }
2397:                 $signed = tempnam(sys_get_temp_dir(), 'signed');
2398:                 //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
2399:                 if (empty($this->sign_extracerts_file)) {
2400:                     $sign = @openssl_pkcs7_sign(
2401:                         $file, $signed, 'file://'.realpath($this->sign_cert_file), array('file://'.realpath($this->sign_key_file), $this->sign_key_pass), null
2402:                     );
2403:                 } else {
2404:                     $sign = @openssl_pkcs7_sign(
2405:                         $file, $signed, 'file://'.realpath($this->sign_cert_file), array('file://'.realpath($this->sign_key_file), $this->sign_key_pass), null, PKCS7_DETACHED, $this->sign_extracerts_file
2406:                     );
2407:                 }
2408:                 if ($sign) {
2409:                     @unlink($file);
2410:                     $body = file_get_contents($signed);
2411:                     @unlink($signed);
2412:                     //The message returned by openssl contains both headers and body, so need to split them up
2413:                     $parts = explode("\n\n", $body, 2);
2414:                     $this->MIMEHeader .= $parts[0].$this->LE.$this->LE;
2415:                     $body = $parts[1];
2416:                 } else {
2417:                     @unlink($file);
2418:                     @unlink($signed);
2419:                     throw new phpmailerException($this->lang('signing').openssl_error_string());
2420:                 }
2421:             } catch (phpmailerException $exc) {
2422:                 $body = '';
2423:                 if ($this->exceptions) {
2424:                     throw $exc;
2425:                 }
2426:             }
2427:         }
2428: 
2429:         return $body;
2430:     }
2431: 
2432:     /**
2433:      * Return the start of a message boundary.
2434:      *
2435:      * @param string $boundary
2436:      * @param string $charSet
2437:      * @param string $contentType
2438:      * @param string $encoding
2439:      *
2440:      * @return string
2441:      */
2442:     protected function getBoundary($boundary, $charSet, $contentType, $encoding)
2443:     {
2444:         $result = '';
2445:         if ($charSet == '') {
2446:             $charSet = $this->CharSet;
2447:         }
2448:         if ($contentType == '') {
2449:             $contentType = $this->ContentType;
2450:         }
2451:         if ($encoding == '') {
2452:             $encoding = $this->Encoding;
2453:         }
2454:         $result .= $this->textLine('--'.$boundary);
2455:         $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
2456:         $result .= $this->LE;
2457:         // RFC1341 part 5 says 7bit is assumed if not specified
2458:         if ($encoding != '7bit') {
2459:             $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
2460:         }
2461:         $result .= $this->LE;
2462: 
2463:         return $result;
2464:     }
2465: 
2466:     /**
2467:      * Return the end of a message boundary.
2468:      *
2469:      * @param string $boundary
2470:      *
2471:      * @return string
2472:      */
2473:     protected function endBoundary($boundary)
2474:     {
2475:         return $this->LE.'--'.$boundary.'--'.$this->LE;
2476:     }
2477: 
2478:     /**
2479:      * Set the message type.
2480:      * PHPMailer only supports some preset message types, not arbitrary MIME structures.
2481:      */
2482:     protected function setMessageType()
2483:     {
2484:         $type = array();
2485:         if ($this->alternativeExists()) {
2486:             $type[] = 'alt';
2487:         }
2488:         if ($this->inlineImageExists()) {
2489:             $type[] = 'inline';
2490:         }
2491:         if ($this->attachmentExists()) {
2492:             $type[] = 'attach';
2493:         }
2494:         $this->message_type = implode('_', $type);
2495:         if ($this->message_type == '') {
2496:             //The 'plain' message_type refers to the message having a single body element, not that it is plain-text
2497:             $this->message_type = 'plain';
2498:         }
2499:     }
2500: 
2501:     /**
2502:      * Format a header line.
2503:      *
2504:      * @param string $name
2505:      * @param string $value
2506:      *
2507:      * @return string
2508:      */
2509:     public function headerLine($name, $value)
2510:     {
2511:         return $name.': '.$value.$this->LE;
2512:     }
2513: 
2514:     /**
2515:      * Return a formatted mail line.
2516:      *
2517:      * @param string $value
2518:      *
2519:      * @return string
2520:      */
2521:     public function textLine($value)
2522:     {
2523:         return $value.$this->LE;
2524:     }
2525: 
2526:     /**
2527:      * Add an attachment from a path on the filesystem.
2528:      * Returns false if the file could not be found or read.
2529:      *
2530:      * @param string $path        path to the attachment
2531:      * @param string $name        overrides the attachment name
2532:      * @param string $encoding    file encoding (see $Encoding)
2533:      * @param string $type        file extension (MIME) type
2534:      * @param string $disposition Disposition to use
2535:      *
2536:      * @throws phpmailerException
2537:      *
2538:      * @return bool
2539:      */
2540:     public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
2541:     {
2542:         try {
2543:             if (!@is_file($path)) {
2544:                 throw new phpmailerException($this->lang('file_access').$path, self::STOP_CONTINUE);
2545:             }
2546: 
2547:             // If a MIME type is not specified, try to work it out from the file name
2548:             if ($type == '') {
2549:                 $type = self::filenameToType($path);
2550:             }
2551: 
2552:             $filename = basename($path);
2553:             if ($name == '') {
2554:                 $name = $filename;
2555:             }
2556: 
2557:             $this->attachment[] = array(
2558:                 0 => $path,
2559:                 1 => $filename,
2560:                 2 => $name,
2561:                 3 => $encoding,
2562:                 4 => $type,
2563:                 5 => false, // isStringAttachment
2564:                 6 => $disposition,
2565:                 7 => 0,
2566:             );
2567:         } catch (phpmailerException $exc) {
2568:             $this->setError($exc->getMessage());
2569:             $this->edebug($exc->getMessage());
2570:             if ($this->exceptions) {
2571:                 throw $exc;
2572:             }
2573: 
2574:             return false;
2575:         }
2576: 
2577:         return true;
2578:     }
2579: 
2580:     /**
2581:      * Return the array of attachments.
2582:      *
2583:      * @return array
2584:      */
2585:     public function getAttachments()
2586:     {
2587:         return $this->attachment;
2588:     }
2589: 
2590:     /**
2591:      * Attach all file, string, and binary attachments to the message.
2592:      * Returns an empty string on failure.
2593:      *
2594:      * @param string $disposition_type
2595:      * @param string $boundary
2596:      *
2597:      * @return string
2598:      */
2599:     protected function attachAll($disposition_type, $boundary)
2600:     {
2601:         // Return text of body
2602:         $mime = array();
2603:         $cidUniq = array();
2604:         $incl = array();
2605: 
2606:         // Add all attachments
2607:         foreach ($this->attachment as $attachment) {
2608:             // Check if it is a valid disposition_filter
2609:             if ($attachment[6] == $disposition_type) {
2610:                 // Check for string attachment
2611:                 $string = '';
2612:                 $path = '';
2613:                 $bString = $attachment[5];
2614:                 if ($bString) {
2615:                     $string = $attachment[0];
2616:                 } else {
2617:                     $path = $attachment[0];
2618:                 }
2619: 
2620:                 $inclhash = md5(serialize($attachment));
2621:                 if (in_array($inclhash, $incl)) {
2622:                     continue;
2623:                 }
2624:                 $incl[] = $inclhash;
2625:                 $name = $attachment[2];
2626:                 $encoding = $attachment[3];
2627:                 $type = $attachment[4];
2628:                 $disposition = $attachment[6];
2629:                 $cid = $attachment[7];
2630:                 if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
2631:                     continue;
2632:                 }
2633:                 $cidUniq[$cid] = true;
2634: 
2635:                 $mime[] = sprintf('--%s%s', $boundary, $this->LE);
2636:                 //Only include a filename property if we have one
2637:                 if (!empty($name)) {
2638:                     $mime[] = sprintf(
2639:                         'Content-Type: %s; name="%s"%s', $type, $this->encodeHeader($this->secureHeader($name)), $this->LE
2640:                     );
2641:                 } else {
2642:                     $mime[] = sprintf(
2643:                         'Content-Type: %s%s', $type, $this->LE
2644:                     );
2645:                 }
2646:                 // RFC1341 part 5 says 7bit is assumed if not specified
2647:                 if ($encoding != '7bit') {
2648:                     $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
2649:                 }
2650: 
2651:                 if ($disposition == 'inline') {
2652:                     $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
2653:                 }
2654: 
2655:                 // If a filename contains any of these chars, it should be quoted,
2656:                 // but not otherwise: RFC2183 & RFC2045 5.1
2657:                 // Fixes a warning in IETF's msglint MIME checker
2658:                 // Allow for bypassing the Content-Disposition header totally
2659:                 if (!(empty($disposition))) {
2660:                     $encoded_name = $this->encodeHeader($this->secureHeader($name));
2661:                     if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
2662:                         $mime[] = sprintf(
2663:                             'Content-Disposition: %s; filename="%s"%s', $disposition, $encoded_name, $this->LE.$this->LE
2664:                         );
2665:                     } else {
2666:                         if (!empty($encoded_name)) {
2667:                             $mime[] = sprintf(
2668:                                 'Content-Disposition: %s; filename=%s%s', $disposition, $encoded_name, $this->LE.$this->LE
2669:                             );
2670:                         } else {
2671:                             $mime[] = sprintf(
2672:                                 'Content-Disposition: %s%s', $disposition, $this->LE.$this->LE
2673:                             );
2674:                         }
2675:                     }
2676:                 } else {
2677:                     $mime[] = $this->LE;
2678:                 }
2679: 
2680:                 // Encode as string attachment
2681:                 if ($bString) {
2682:                     $mime[] = $this->encodeString($string, $encoding);
2683:                     if ($this->isError()) {
2684:                         return '';
2685:                     }
2686:                     $mime[] = $this->LE.$this->LE;
2687:                 } else {
2688:                     $mime[] = $this->encodeFile($path, $encoding);
2689:                     if ($this->isError()) {
2690:                         return '';
2691:                     }
2692:                     $mime[] = $this->LE.$this->LE;
2693:                 }
2694:             }
2695:         }
2696: 
2697:         $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
2698: 
2699:         return implode('', $mime);
2700:     }
2701: 
2702:     /**
2703:      * Encode a file attachment in requested format.
2704:      * Returns an empty string on failure.
2705:      *
2706:      * @param string $path     The full path to the file
2707:      * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2708:      *
2709:      * @throws phpmailerException
2710:      *
2711:      * @return string
2712:      */
2713:     protected function encodeFile($path, $encoding = 'base64')
2714:     {
2715:         try {
2716:             if (!is_readable($path)) {
2717:                 throw new phpmailerException($this->lang('file_open').$path, self::STOP_CONTINUE);
2718:             }
2719:             $magic_quotes = get_magic_quotes_runtime();
2720:             if ($magic_quotes) {
2721:                 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2722:                     set_magic_quotes_runtime(false);
2723:                 } else {
2724:                     //Doesn't exist in PHP 5.4, but we don't need to check because
2725:                     //get_magic_quotes_runtime always returns false in 5.4+
2726:                     //so it will never get here
2727:                     ini_set('magic_quotes_runtime', false);
2728:                 }
2729:             }
2730:             $file_buffer = file_get_contents($path);
2731:             $file_buffer = $this->encodeString($file_buffer, $encoding);
2732:             if ($magic_quotes) {
2733:                 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
2734:                     set_magic_quotes_runtime($magic_quotes);
2735:                 } else {
2736:                     ini_set('magic_quotes_runtime', $magic_quotes);
2737:                 }
2738:             }
2739: 
2740:             return $file_buffer;
2741:         } catch (Exception $exc) {
2742:             $this->setError($exc->getMessage());
2743: 
2744:             return '';
2745:         }
2746:     }
2747: 
2748:     /**
2749:      * Encode a string in requested format.
2750:      * Returns an empty string on failure.
2751:      *
2752:      * @param string $str      The text to encode
2753:      * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
2754:      *
2755:      * @return string
2756:      */
2757:     public function encodeString($str, $encoding = 'base64')
2758:     {
2759:         $encoded = '';
2760:         switch (strtolower($encoding)) {
2761:             case 'base64':
2762:                 $encoded = chunk_split(base64_encode($str), 76, $this->LE);
2763:                 break;
2764:             case '7bit':
2765:             case '8bit':
2766:                 $encoded = $this->fixEOL($str);
2767:                 // Make sure it ends with a line break
2768:                 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
2769:                     $encoded .= $this->LE;
2770:                 }
2771:                 break;
2772:             case 'binary':
2773:                 $encoded = $str;
2774:                 break;
2775:             case 'quoted-printable':
2776:                 $encoded = $this->encodeQP($str);
2777:                 break;
2778:             default:
2779:                 $this->setError($this->lang('encoding').$encoding);
2780:                 break;
2781:         }
2782: 
2783:         return $encoded;
2784:     }
2785: 
2786:     /**
2787:      * Encode a header string optimally.
2788:      * Picks shortest of Q, B, quoted-printable or none.
2789:      *
2790:      * @param string $str
2791:      * @param string $position
2792:      *
2793:      * @return string
2794:      */
2795:     public function encodeHeader($str, $position = 'text')
2796:     {
2797:         $matchcount = 0;
2798:         switch (strtolower($position)) {
2799:             case 'phrase':
2800:                 if (!preg_match('/[\200-\377]/', $str)) {
2801:                     // Can't use addslashes as we don't know the value of magic_quotes_sybase
2802:                     $encoded = addcslashes($str, "\0..\37\177\\\"");
2803:                     if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
2804:                         return $encoded;
2805:                     } else {
2806:                         return "\"$encoded\"";
2807:                     }
2808:                 }
2809:                 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
2810:                 break;
2811:             /*
2812:              * @noinspection PhpMissingBreakStatementInspection
2813:              */
2814:             case 'comment':
2815:                 $matchcount = preg_match_all('/[()"]/', $str, $matches);
2816:             // Intentional fall-through
2817:             // no break
2818:             case 'text':
2819:             default:
2820:                 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
2821:                 break;
2822:         }
2823: 
2824:         //There are no chars that need encoding
2825:         if ($matchcount == 0) {
2826:             return $str;
2827:         }
2828: 
2829:         $maxlen = 75 - 7 - strlen($this->CharSet);
2830:         // Try to select the encoding which should produce the shortest output
2831:         if ($matchcount > strlen($str) / 3) {
2832:             // More than a third of the content will need encoding, so B encoding will be most efficient
2833:             $encoding = 'B';
2834:             if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
2835:                 // Use a custom function which correctly encodes and wraps long
2836:                 // multibyte strings without breaking lines within a character
2837:                 $encoded = $this->base64EncodeWrapMB($str, "\n");
2838:             } else {
2839:                 $encoded = base64_encode($str);
2840:                 $maxlen -= $maxlen % 4;
2841:                 $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
2842:             }
2843:         } else {
2844:             $encoding = 'Q';
2845:             $encoded = $this->encodeQ($str, $position);
2846:             $encoded = $this->wrapText($encoded, $maxlen, true);
2847:             $encoded = str_replace('='.self::CRLF, "\n", trim($encoded));
2848:         }
2849: 
2850:         $encoded = preg_replace('/^(.*)$/m', ' =?'.$this->CharSet."?$encoding?\\1?=", $encoded);
2851:         $encoded = trim(str_replace("\n", $this->LE, $encoded));
2852: 
2853:         return $encoded;
2854:     }
2855: 
2856:     /**
2857:      * Check if a string contains multi-byte characters.
2858:      *
2859:      * @param string $str multi-byte text to wrap encode
2860:      *
2861:      * @return bool
2862:      */
2863:     public function hasMultiBytes($str)
2864:     {
2865:         if (function_exists('mb_strlen')) {
2866:             return strlen($str) > mb_strlen($str, $this->CharSet);
2867:         } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
2868:             return false;
2869:         }
2870:     }
2871: 
2872:     /**
2873:      * Does a string contain any 8-bit chars (in any charset)?
2874:      *
2875:      * @param string $text
2876:      *
2877:      * @return bool
2878:      */
2879:     public function has8bitChars($text)
2880:     {
2881:         return (bool) preg_match('/[\x80-\xFF]/', $text);
2882:     }
2883: 
2884:     /**
2885:      * Encode and wrap long multibyte strings for mail headers
2886:      * without breaking lines within a character.
2887:      * Adapted from a function by paravoid.
2888:      *
2889:      * @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
2890:      *
2891:      * @param string $str       multi-byte text to wrap encode
2892:      * @param string $linebreak string to use as linefeed/end-of-line
2893:      *
2894:      * @return string
2895:      */
2896:     public function base64EncodeWrapMB($str, $linebreak = null)
2897:     {
2898:         $start = '=?'.$this->CharSet.'?B?';
2899:         $end = '?=';
2900:         $encoded = '';
2901:         if ($linebreak === null) {
2902:             $linebreak = $this->LE;
2903:         }
2904: 
2905:         $mb_length = mb_strlen($str, $this->CharSet);
2906:         // Each line must have length <= 75, including $start and $end
2907:         $length = 75 - strlen($start) - strlen($end);
2908:         // Average multi-byte ratio
2909:         $ratio = $mb_length / strlen($str);
2910:         // Base64 has a 4:3 ratio
2911:         $avgLength = floor($length * $ratio * .75);
2912: 
2913:         for ($i = 0; $i < $mb_length; $i += $offset) {
2914:             $lookBack = 0;
2915:             do {
2916:                 $offset = $avgLength - $lookBack;
2917:                 $chunk = mb_substr($str, $i, $offset, $this->CharSet);
2918:                 $chunk = base64_encode($chunk);
2919:                 ++$lookBack;
2920:             } while (strlen($chunk) > $length);
2921:             $encoded .= $chunk.$linebreak;
2922:         }
2923: 
2924:         // Chomp the last linefeed
2925:         $encoded = substr($encoded, 0, -strlen($linebreak));
2926: 
2927:         return $encoded;
2928:     }
2929: 
2930:     /**
2931:      * Encode a string in quoted-printable format.
2932:      * According to RFC2045 section 6.7.
2933:      *
2934:      * @see http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
2935:      *
2936:      * @param string $string   The text to encode
2937:      * @param int    $line_max Number of chars allowed on a line before wrapping
2938:      *
2939:      * @return string
2940:      */
2941:     public function encodeQP($string, $line_max = 76)
2942:     {
2943:         // Use native function if it's available (>= PHP5.3)
2944:         if (function_exists('quoted_printable_encode')) {
2945:             return quoted_printable_encode($string);
2946:         }
2947:         // Fall back to a pure PHP implementation
2948:         $string = str_replace(
2949:             array('%20', '%0D%0A.', '%0D%0A', '%'), array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string)
2950:         );
2951: 
2952:         return preg_replace('/[^\r\n]{'.($line_max - 3).'}[^=\r\n]{2}/', "$0=\r\n", $string);
2953:     }
2954: 
2955:     /**
2956:      * Backward compatibility wrapper for an old QP encoding function that was removed.
2957:      *
2958:      * @deprecated use encodeQP instead
2959:      * @see PHPMailer::encodeQP()
2960:      *
2961:      * @param string $string
2962:      * @param int    $line_max
2963:      * @param bool   $space_conv
2964:      *
2965:      * @return string
2966:      */
2967:     public function encodeQPphp(
2968:         $string, $line_max = 76,
2969: /**
2970:  * @noinspection PhpUnusedParameterInspection
2971:  */$space_conv = false
2972:     ) {
2973:         return $this->encodeQP($string, $line_max);
2974:     }
2975: 
2976:     /**
2977:      * Encode a string using Q encoding.
2978:      *
2979:      * @see http://tools.ietf.org/html/rfc2047
2980:      *
2981:      * @param string $str      the text to encode
2982:      * @param string $position Where the text is going to be used, see the RFC for what that means
2983:      *
2984:      * @return string
2985:      */
2986:     public function encodeQ($str, $position = 'text')
2987:     {
2988:         // There should not be any EOL in the string
2989:         $pattern = '';
2990:         $encoded = str_replace(array("\r", "\n"), '', $str);
2991:         switch (strtolower($position)) {
2992:             case 'phrase':
2993:                 // RFC 2047 section 5.3
2994:                 $pattern = '^A-Za-z0-9!*+\/ -';
2995:                 break;
2996:             /*
2997:              * @noinspection PhpMissingBreakStatementInspection
2998:              */
2999:             case 'comment':
3000:                 // RFC 2047 section 5.2
3001:                 $pattern = '\(\)"';
3002:             // intentional fall-through
3003:             // for this reason we build the $pattern without including delimiters and []
3004:             // no break
3005:             case 'text':
3006:             default:
3007:                 // RFC 2047 section 5.1
3008:                 // Replace every high ascii, control, =, ? and _ characters
3009:                 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377'.$pattern;
3010:                 break;
3011:         }
3012:         $matches = array();
3013:         if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
3014:             // If the string contains an '=', make sure it's the first thing we replace
3015:             // so as to avoid double-encoding
3016:             $eqkey = array_search('=', $matches[0]);
3017:             if (false !== $eqkey) {
3018:                 unset($matches[0][$eqkey]);
3019:                 array_unshift($matches[0], '=');
3020:             }
3021:             foreach (array_unique($matches[0]) as $char) {
3022:                 $encoded = str_replace($char, '='.sprintf('%02X', ord($char)), $encoded);
3023:             }
3024:         }
3025:         // Replace every spaces to _ (more readable than =20)
3026: 
3027:         return str_replace(' ', '_', $encoded);
3028:     }
3029: 
3030:     /**
3031:      * Add a string or binary attachment (non-filesystem).
3032:      * This method can be used to attach ascii or binary data,
3033:      * such as a BLOB record from a database.
3034:      *
3035:      * @param string $string      string attachment data
3036:      * @param string $filename    name of the attachment
3037:      * @param string $encoding    file encoding (see $Encoding)
3038:      * @param string $type        file extension (MIME) type
3039:      * @param string $disposition Disposition to use
3040:      */
3041:     public function addStringAttachment(
3042:         $string, $filename, $encoding = 'base64', $type = '', $disposition = 'attachment'
3043:     ) {
3044:         // If a MIME type is not specified, try to work it out from the file name
3045:         if ($type == '') {
3046:             $type = self::filenameToType($filename);
3047:         }
3048:         // Append to $attachment array
3049:         $this->attachment[] = array(
3050:             0 => $string,
3051:             1 => $filename,
3052:             2 => basename($filename),
3053:             3 => $encoding,
3054:             4 => $type,
3055:             5 => true, // isStringAttachment
3056:             6 => $disposition,
3057:             7 => 0,
3058:         );
3059:     }
3060: 
3061:     /**
3062:      * Add an embedded (inline) attachment from a file.
3063:      * This can include images, sounds, and just about any other document type.
3064:      * These differ from 'regular' attachments in that they are intended to be
3065:      * displayed inline with the message, not just attached for download.
3066:      * This is used in HTML messages that embed the images
3067:      * the HTML refers to using the $cid value.
3068:      *        the content when using an embedded image in HTML.
3069:      *
3070:      * @param string $path        path to the attachment
3071:      * @param string $cid         Content ID of the attachment; Use this to reference
3072:      * @param string $name        overrides the attachment name
3073:      * @param string $encoding    file encoding (see $Encoding)
3074:      * @param string $type        file MIME type
3075:      * @param string $disposition Disposition to use
3076:      *
3077:      * @return bool True on successfully adding an attachment
3078:      */
3079:     public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
3080:     {
3081:         if (!@is_file($path)) {
3082:             $this->setError($this->lang('file_access').$path);
3083: 
3084:             return false;
3085:         }
3086: 
3087:         // If a MIME type is not specified, try to work it out from the file name
3088:         if ($type == '') {
3089:             $type = self::filenameToType($path);
3090:         }
3091: 
3092:         $filename = basename($path);
3093:         if ($name == '') {
3094:             $name = $filename;
3095:         }
3096: 
3097:         // Append to $attachment array
3098:         $this->attachment[] = array(
3099:             0 => $path,
3100:             1 => $filename,
3101:             2 => $name,
3102:             3 => $encoding,
3103:             4 => $type,
3104:             5 => false, // isStringAttachment
3105:             6 => $disposition,
3106:             7 => $cid,
3107:         );
3108: 
3109:         return true;
3110:     }
3111: 
3112:     /**
3113:      * Add an embedded stringified attachment.
3114:      * This can include images, sounds, and just about any other document type.
3115:      * Be sure to set the $type to an image type for images:
3116:      * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
3117:      *        the content when using an embedded image in HTML.
3118:      *
3119:      * @param string $string      the attachment binary data
3120:      * @param string $cid         Content ID of the attachment; Use this to reference
3121:      * @param string $name
3122:      * @param string $encoding    file encoding (see $Encoding)
3123:      * @param string $type        MIME type
3124:      * @param string $disposition Disposition to use
3125:      *
3126:      * @return bool True on successfully adding an attachment
3127:      */
3128:     public function addStringEmbeddedImage(
3129:         $string, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline'
3130:     ) {
3131:         // If a MIME type is not specified, try to work it out from the name
3132:         if ($type == '' and !empty($name)) {
3133:             $type = self::filenameToType($name);
3134:         }
3135: 
3136:         // Append to $attachment array
3137:         $this->attachment[] = array(
3138:             0 => $string,
3139:             1 => $name,
3140:             2 => $name,
3141:             3 => $encoding,
3142:             4 => $type,
3143:             5 => true, // isStringAttachment
3144:             6 => $disposition,
3145:             7 => $cid,
3146:         );
3147: 
3148:         return true;
3149:     }
3150: 
3151:     /**
3152:      * Check if an inline attachment is present.
3153:      *
3154:      * @return bool
3155:      */
3156:     public function inlineImageExists()
3157:     {
3158:         foreach ($this->attachment as $attachment) {
3159:             if ($attachment[6] == 'inline') {
3160:                 return true;
3161:             }
3162:         }
3163: 
3164:         return false;
3165:     }
3166: 
3167:     /**
3168:      * Check if an attachment (non-inline) is present.
3169:      *
3170:      * @return bool
3171:      */
3172:     public function attachmentExists()
3173:     {
3174:         foreach ($this->attachment as $attachment) {
3175:             if ($attachment[6] == 'attachment') {
3176:                 return true;
3177:             }
3178:         }
3179: 
3180:         return false;
3181:     }
3182: 
3183:     /**
3184:      * Check if this message has an alternative body set.
3185:      *
3186:      * @return bool
3187:      */
3188:     public function alternativeExists()
3189:     {
3190:         return !empty($this->AltBody);
3191:     }
3192: 
3193:     /**
3194:      * Clear queued addresses of given kind.
3195:      *
3196:      * @param string $kind 'to', 'cc', or 'bcc'
3197:      */
3198:     public function clearQueuedAddresses($kind)
3199:     {
3200:         $RecipientsQueue = $this->RecipientsQueue;
3201:         foreach ($RecipientsQueue as $address => $params) {
3202:             if ($params[0] == $kind) {
3203:                 unset($this->RecipientsQueue[$address]);
3204:             }
3205:         }
3206:     }
3207: 
3208:     /**
3209:      * Clear all To recipients.
3210:      */
3211:     public function clearAddresses()
3212:     {
3213:         foreach ($this->to as $to) {
3214:             unset($this->all_recipients[strtolower($to[0])]);
3215:         }
3216:         $this->to = array();
3217:         $this->clearQueuedAddresses('to');
3218:     }
3219: 
3220:     /**
3221:      * Clear all CC recipients.
3222:      */
3223:     public function clearCCs()
3224:     {
3225:         foreach ($this->cc as $cc) {
3226:             unset($this->all_recipients[strtolower($cc[0])]);
3227:         }
3228:         $this->cc = array();
3229:         $this->clearQueuedAddresses('cc');
3230:     }
3231: 
3232:     /**
3233:      * Clear all BCC recipients.
3234:      */
3235:     public function clearBCCs()
3236:     {
3237:         foreach ($this->bcc as $bcc) {
3238:             unset($this->all_recipients[strtolower($bcc[0])]);
3239:         }
3240:         $this->bcc = array();
3241:         $this->clearQueuedAddresses('bcc');
3242:     }
3243: 
3244:     /**
3245:      * Clear all ReplyTo recipients.
3246:      */
3247:     public function clearReplyTos()
3248:     {
3249:         $this->ReplyTo = array();
3250:         $this->ReplyToQueue = array();
3251:     }
3252: 
3253:     /**
3254:      * Clear all recipient types.
3255:      */
3256:     public function clearAllRecipients()
3257:     {
3258:         $this->to = array();
3259:         $this->cc = array();
3260:         $this->bcc = array();
3261:         $this->all_recipients = array();
3262:         $this->RecipientsQueue = array();
3263:     }
3264: 
3265:     /**
3266:      * Clear all filesystem, string, and binary attachments.
3267:      */
3268:     public function clearAttachments()
3269:     {
3270:         $this->attachment = array();
3271:     }
3272: 
3273:     /**
3274:      * Clear all custom headers.
3275:      */
3276:     public function clearCustomHeaders()
3277:     {
3278:         $this->CustomHeader = array();
3279:     }
3280: 
3281:     /**
3282:      * Add an error message to the error container.
3283:      *
3284:      * @param string $msg
3285:      */
3286:     protected function setError($msg)
3287:     {
3288:         ++$this->error_count;
3289:         if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
3290:             $lasterror = $this->smtp->getError();
3291:             if (!empty($lasterror['error'])) {
3292:                 $msg .= $this->lang('smtp_error').$lasterror['error'];
3293:                 if (!empty($lasterror['detail'])) {
3294:                     $msg .= ' Detail: '.$lasterror['detail'];
3295:                 }
3296:                 if (!empty($lasterror['smtp_code'])) {
3297:                     $msg .= ' SMTP code: '.$lasterror['smtp_code'];
3298:                 }
3299:                 if (!empty($lasterror['smtp_code_ex'])) {
3300:                     $msg .= ' Additional SMTP info: '.$lasterror['smtp_code_ex'];
3301:                 }
3302:             }
3303:         }
3304:         $this->ErrorInfo = $msg;
3305:     }
3306: 
3307:     /**
3308:      * Return an RFC 822 formatted date.
3309:      *
3310:      * @static
3311:      *
3312:      * @return string
3313:      */
3314:     public static function rfcDate()
3315:     {
3316:         // Set the time zone to whatever the default is to avoid 500 errors
3317:         // Will default to UTC if it's not set properly in php.ini
3318:         date_default_timezone_set(@date_default_timezone_get());
3319: 
3320:         return date('D, j M Y H:i:s O');
3321:     }
3322: 
3323:     /**
3324:      * Get the server hostname.
3325:      * Returns 'localhost.localdomain' if unknown.
3326:      *
3327:      * @return string
3328:      */
3329:     protected function serverHostname()
3330:     {
3331:         $result = 'localhost.localdomain';
3332:         if (!empty($this->Hostname)) {
3333:             $result = $this->Hostname;
3334:         } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
3335:             $result = $_SERVER['SERVER_NAME'];
3336:         } elseif (function_exists('gethostname') && gethostname() !== false) {
3337:             $result = gethostname();
3338:         } elseif (php_uname('n') !== false) {
3339:             $result = php_uname('n');
3340:         }
3341: 
3342:         return $result;
3343:     }
3344: 
3345:     /**
3346:      * Get an error message in the current language.
3347:      *
3348:      * @param string $key
3349:      *
3350:      * @return string
3351:      */
3352:     protected function lang($key)
3353:     {
3354:         if (count($this->language) < 1) {
3355:             $this->setLanguage('en'); // set the default language
3356:         }
3357: 
3358:         if (array_key_exists($key, $this->language)) {
3359:             if ($key == 'smtp_connect_failed') {
3360:                 //Include a link to troubleshooting docs on SMTP connection failure
3361:                 //this is by far the biggest cause of support questions
3362:                 //but it's usually not PHPMailer's fault.
3363:                 return $this->language[$key].' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
3364:             }
3365: 
3366:             return $this->language[$key];
3367:         } else {
3368:             //Return the key as a fallback
3369:             return $key;
3370:         }
3371:     }
3372: 
3373:     /**
3374:      * Check if an error occurred.
3375:      *
3376:      * @return bool true if an error did occur
3377:      */
3378:     public function isError()
3379:     {
3380:         return $this->error_count > 0;
3381:     }
3382: 
3383:     /**
3384:      * Ensure consistent line endings in a string.
3385:      * Changes every end of line from CRLF, CR or LF to $this->LE.
3386:      *
3387:      * @param string $str String to fixEOL
3388:      *
3389:      * @return string
3390:      */
3391:     public function fixEOL($str)
3392:     {
3393:         // Normalise to \n
3394:         $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
3395:         // Now convert LE as needed
3396:         if ($this->LE !== "\n") {
3397:             $nstr = str_replace("\n", $this->LE, $nstr);
3398:         }
3399: 
3400:         return $nstr;
3401:     }
3402: 
3403:     /**
3404:      * Add a custom header.
3405:      * $name value can be overloaded to contain
3406:      * both header name and value (name:value).
3407:      *
3408:      * @param string $name  Custom header name
3409:      * @param string $value Header value
3410:      */
3411:     public function addCustomHeader($name, $value = null)
3412:     {
3413:         if ($value === null) {
3414:             // Value passed in as name:value
3415:             $this->CustomHeader[] = explode(':', $name, 2);
3416:         } else {
3417:             $this->CustomHeader[] = array($name, $value);
3418:         }
3419:     }
3420: 
3421:     /**
3422:      * Returns all custom headers.
3423:      *
3424:      * @return array
3425:      */
3426:     public function getCustomHeaders()
3427:     {
3428:         return $this->CustomHeader;
3429:     }
3430: 
3431:     /**
3432:      * Create a message body from an HTML string.
3433:      * Automatically inlines images and creates a plain-text version by converting the HTML,
3434:      * overwriting any existing values in Body and AltBody.
3435:      * $basedir is used when handling relative image paths, e.g. <img src="images/a.png">
3436:      * will look for an image file in $basedir/images/a.png and convert it to inline.
3437:      * If you don't want to apply these transformations to your HTML, just set Body and AltBody yourself.
3438:      *
3439:      * @param string        $message  HTML message string
3440:      * @param string        $basedir  base directory for relative paths to images
3441:      * @param bool|callable $advanced Whether to use the internal HTML to text converter
3442:      *
3443:      * @return string $message The transformed message Body
3444:      */
3445:     public function msgHTML($message, $basedir = '', $advanced = false)
3446:     {
3447:         preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
3448:         if (array_key_exists(2, $images)) {
3449:             foreach ($images[2] as $imgindex => $url) {
3450:                 // Convert data URIs into embedded images
3451:                 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
3452:                     $data = substr($url, strpos($url, ','));
3453:                     if ($match[2]) {
3454:                         $data = base64_decode($data);
3455:                     } else {
3456:                         $data = rawurldecode($data);
3457:                     }
3458:                     $cid = md5($url).'@phpmailer.0'; // RFC2392 S 2
3459:                     if ($this->addStringEmbeddedImage($data, $cid, 'embed'.$imgindex, 'base64', $match[1])) {
3460:                         $message = str_replace(
3461:                             $images[0][$imgindex], $images[1][$imgindex].'="cid:'.$cid.'"', $message
3462:                         );
3463:                     }
3464:                 } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) {
3465:                     // Do not change urls for absolute images (thanks to corvuscorax)
3466:                     // Do not change urls that are already inline images
3467:                     $filename = basename($url);
3468:                     $directory = dirname($url);
3469:                     if ($directory == '.') {
3470:                         $directory = '';
3471:                     }
3472:                     $cid = md5($url).'@phpmailer.0'; // RFC2392 S 2
3473:                     if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
3474:                         $basedir .= '/';
3475:                     }
3476:                     if (strlen($directory) > 1 && substr($directory, -1) != '/') {
3477:                         $directory .= '/';
3478:                     }
3479:                     if ($this->addEmbeddedImage(
3480:                         $basedir.$directory.$filename, $cid, $filename, 'base64', self::_mime_types((string) self::mb_pathinfo($filename, PATHINFO_EXTENSION))
3481:                     )
3482:                     ) {
3483:                         $message = preg_replace(
3484:                             '/'.$images[1][$imgindex].'=["\']'.preg_quote($url, '/').'["\']/Ui', $images[1][$imgindex].'="cid:'.$cid.'"', $message
3485:                         );
3486:                     }
3487:                 }
3488:             }
3489:         }
3490:         $this->isHTML(true);
3491:         // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
3492:         $this->Body = $this->normalizeBreaks($message);
3493:         $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
3494:         if (!$this->alternativeExists()) {
3495:             $this->AltBody = 'To view this email message, open it in a program that understands HTML!'.
3496:             self::CRLF.self::CRLF;
3497:         }
3498: 
3499:         return $this->Body;
3500:     }
3501: 
3502:     /**
3503:      * Convert an HTML string into plain text.
3504:      * This is used by msgHTML().
3505:      * Note - older versions of this function used a bundled advanced converter
3506:      * which was been removed for license reasons in #232.
3507:      * Example usage:
3508:      * <code>
3509:      * // Use default conversion
3510:      * $plain = $mail->html2text($html);
3511:      * // Use your own custom converter
3512:      * $plain = $mail->html2text($html, function($html) {
3513:      *     $converter = new MyHtml2text($html);
3514:      *     return $converter->get_text();
3515:      * });
3516:      * </code>
3517:      *   or provide your own callable for custom conversion.
3518:      *
3519:      * @param string        $html     The HTML text to convert
3520:      * @param bool|callable $advanced Any boolean value to use the internal converter,
3521:      *
3522:      * @return string
3523:      */
3524:     public function html2text($html, $advanced = false)
3525:     {
3526:         if (is_callable($advanced)) {
3527:             return call_user_func($advanced, $html);
3528:         }
3529: 
3530:         return html_entity_decode(
3531:             trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), ENT_QUOTES, $this->CharSet
3532:         );
3533:     }
3534: 
3535:     /**
3536:      * Get the MIME type for a file extension.
3537:      *
3538:      * @static
3539:      *
3540:      * @param string $ext File extension
3541:      *
3542:      * @return string MIME type of file
3543:      */
3544:     public static function _mime_types($ext = '')
3545:     {
3546:         $mimes = array(
3547:             'xl' => 'application/excel',
3548:             'js' => 'application/javascript',
3549:             'hqx' => 'application/mac-binhex40',
3550:             'cpt' => 'application/mac-compactpro',
3551:             'bin' => 'application/macbinary',
3552:             'doc' => 'application/msword',
3553:             'word' => 'application/msword',
3554:             'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
3555:             'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
3556:             'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
3557:             'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
3558:             'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
3559:             'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
3560:             'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
3561:             'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
3562:             'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
3563:             'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
3564:             'class' => 'application/octet-stream',
3565:             'dll' => 'application/octet-stream',
3566:             'dms' => 'application/octet-stream',
3567:             'exe' => 'application/octet-stream',
3568:             'lha' => 'application/octet-stream',
3569:             'lzh' => 'application/octet-stream',
3570:             'psd' => 'application/octet-stream',
3571:             'sea' => 'application/octet-stream',
3572:             'so' => 'application/octet-stream',
3573:             'oda' => 'application/oda',
3574:             'pdf' => 'application/pdf',
3575:             'ai' => 'application/postscript',
3576:             'eps' => 'application/postscript',
3577:             'ps' => 'application/postscript',
3578:             'smi' => 'application/smil',
3579:             'smil' => 'application/smil',
3580:             'mif' => 'application/vnd.mif',
3581:             'xls' => 'application/vnd.ms-excel',
3582:             'ppt' => 'application/vnd.ms-powerpoint',
3583:             'wbxml' => 'application/vnd.wap.wbxml',
3584:             'wmlc' => 'application/vnd.wap.wmlc',
3585:             'dcr' => 'application/x-director',
3586:             'dir' => 'application/x-director',
3587:             'dxr' => 'application/x-director',
3588:             'dvi' => 'application/x-dvi',
3589:             'gtar' => 'application/x-gtar',
3590:             'php3' => 'application/x-httpd-php',
3591:             'php4' => 'application/x-httpd-php',
3592:             'php' => 'application/x-httpd-php',
3593:             'phtml' => 'application/x-httpd-php',
3594:             'phps' => 'application/x-httpd-php-source',
3595:             'swf' => 'application/x-shockwave-flash',
3596:             'sit' => 'application/x-stuffit',
3597:             'tar' => 'application/x-tar',
3598:             'tgz' => 'application/x-tar',
3599:             'xht' => 'application/xhtml+xml',
3600:             'xhtml' => 'application/xhtml+xml',
3601:             'zip' => 'application/zip',
3602:             'mid' => 'audio/midi',
3603:             'midi' => 'audio/midi',
3604:             'mp2' => 'audio/mpeg',
3605:             'mp3' => 'audio/mpeg',
3606:             'mpga' => 'audio/mpeg',
3607:             'aif' => 'audio/x-aiff',
3608:             'aifc' => 'audio/x-aiff',
3609:             'aiff' => 'audio/x-aiff',
3610:             'ram' => 'audio/x-pn-realaudio',
3611:             'rm' => 'audio/x-pn-realaudio',
3612:             'rpm' => 'audio/x-pn-realaudio-plugin',
3613:             'ra' => 'audio/x-realaudio',
3614:             'wav' => 'audio/x-wav',
3615:             'bmp' => 'image/bmp',
3616:             'gif' => 'image/gif',
3617:             'jpeg' => 'image/jpeg',
3618:             'jpe' => 'image/jpeg',
3619:             'jpg' => 'image/jpeg',
3620:             'png' => 'image/png',
3621:             'tiff' => 'image/tiff',
3622:             'tif' => 'image/tiff',
3623:             'eml' => 'message/rfc822',
3624:             'css' => 'text/css',
3625:             'html' => 'text/html',
3626:             'htm' => 'text/html',
3627:             'shtml' => 'text/html',
3628:             'log' => 'text/plain',
3629:             'text' => 'text/plain',
3630:             'txt' => 'text/plain',
3631:             'rtx' => 'text/richtext',
3632:             'rtf' => 'text/rtf',
3633:             'vcf' => 'text/vcard',
3634:             'vcard' => 'text/vcard',
3635:             'xml' => 'text/xml',
3636:             'xsl' => 'text/xml',
3637:             'mpeg' => 'video/mpeg',
3638:             'mpe' => 'video/mpeg',
3639:             'mpg' => 'video/mpeg',
3640:             'mov' => 'video/quicktime',
3641:             'qt' => 'video/quicktime',
3642:             'rv' => 'video/vnd.rn-realvideo',
3643:             'avi' => 'video/x-msvideo',
3644:             'movie' => 'video/x-sgi-movie',
3645:         );
3646:         if (array_key_exists(strtolower($ext), $mimes)) {
3647:             return $mimes[strtolower($ext)];
3648:         }
3649: 
3650:         return 'application/octet-stream';
3651:     }
3652: 
3653:     /**
3654:      * Map a file name to a MIME type.
3655:      * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
3656:      *
3657:      * @static
3658:      *
3659:      * @param string $filename A file name or full path, does not need to exist as a file
3660:      *
3661:      * @return string
3662:      */
3663:     public static function filenameToType($filename)
3664:     {
3665:         // In case the path is a URL, strip any query string before getting extension
3666:         $qpos = strpos($filename, '?');
3667:         if (false !== $qpos) {
3668:             $filename = substr($filename, 0, $qpos);
3669:         }
3670:         $pathinfo = self::mb_pathinfo($filename);
3671: 
3672:         return self::_mime_types($pathinfo['extension']);
3673:     }
3674: 
3675:     /**
3676:      * Multi-byte-safe pathinfo replacement.
3677:      * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
3678:      * Works similarly to the one in PHP >= 5.2.0
3679:      *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2.
3680:      *
3681:      * @static
3682:      *
3683:      * @see http://www.php.net/manual/en/function.pathinfo.php#107461
3684:      *
3685:      * @param string     $path    A filename or path, does not need to exist as a file
3686:      * @param int|string $options Either a PATHINFO_* constant,
3687:      *
3688:      * @return string|array
3689:      */
3690:     public static function mb_pathinfo($path, $options = null)
3691:     {
3692:         $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
3693:         $pathinfo = array();
3694:         if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
3695:             if (array_key_exists(1, $pathinfo)) {
3696:                 $ret['dirname'] = $pathinfo[1];
3697:             }
3698:             if (array_key_exists(2, $pathinfo)) {
3699:                 $ret['basename'] = $pathinfo[2];
3700:             }
3701:             if (array_key_exists(5, $pathinfo)) {
3702:                 $ret['extension'] = $pathinfo[5];
3703:             }
3704:             if (array_key_exists(3, $pathinfo)) {
3705:                 $ret['filename'] = $pathinfo[3];
3706:             }
3707:         }
3708:         switch ($options) {
3709:             case PATHINFO_DIRNAME:
3710:             case 'dirname':
3711:                 return $ret['dirname'];
3712:             case PATHINFO_BASENAME:
3713:             case 'basename':
3714:                 return $ret['basename'];
3715:             case PATHINFO_EXTENSION:
3716:             case 'extension':
3717:                 return $ret['extension'];
3718:             case PATHINFO_FILENAME:
3719:             case 'filename':
3720:                 return $ret['filename'];
3721:             default:
3722:                 return $ret;
3723:         }
3724:     }
3725: 
3726:     /**
3727:      * Set or reset instance properties.
3728:      * You should avoid this function - it's more verbose, less efficient, more error-prone and
3729:      * harder to debug than setting properties directly.
3730:      * Usage Example:
3731:      * `$mail->set('SMTPSecure', 'tls');`
3732:      *   is the same as:
3733:      * `$mail->SMTPSecure = 'tls';`.
3734:      *
3735:      * @TODO Should this not be using the __set() magic function?
3736:      *
3737:      * @param string $name  The property name to set
3738:      * @param mixed  $value The value to set the property to
3739:      *
3740:      * @return bool
3741:      */
3742:     public function set($name, $value = '')
3743:     {
3744:         if (property_exists($this, $name)) {
3745:             $this->$name = $value;
3746: 
3747:             return true;
3748:         } else {
3749:             $this->setError($this->lang('variable_set').$name);
3750: 
3751:             return false;
3752:         }
3753:     }
3754: 
3755:     /**
3756:      * Strip newlines to prevent header injection.
3757:      *
3758:      * @param string $str
3759:      *
3760:      * @return string
3761:      */
3762:     public function secureHeader($str)
3763:     {
3764:         return trim(str_replace(array("\r", "\n"), '', $str));
3765:     }
3766: 
3767:     /**
3768:      * Normalize line breaks in a string.
3769:      * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
3770:      * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
3771:      *
3772:      * @static
3773:      *
3774:      * @param string $text
3775:      * @param string $breaktype What kind of line break to use, defaults to CRLF
3776:      *
3777:      * @return string
3778:      */
3779:     public static function normalizeBreaks($text, $breaktype = "\r\n")
3780:     {
3781:         return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
3782:     }
3783: 
3784:     /**
3785:      * Set the public and private key files and password for S/MIME signing.
3786:      *
3787:      * @param string $cert_filename
3788:      * @param string $key_filename
3789:      * @param string $key_pass            Password for private key
3790:      * @param string $extracerts_filename Optional path to chain certificate
3791:      */
3792:     public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
3793:     {
3794:         $this->sign_cert_file = $cert_filename;
3795:         $this->sign_key_file = $key_filename;
3796:         $this->sign_key_pass = $key_pass;
3797:         $this->sign_extracerts_file = $extracerts_filename;
3798:     }
3799: 
3800:     /**
3801:      * Quoted-Printable-encode a DKIM header.
3802:      *
3803:      * @param string $txt
3804:      *
3805:      * @return string
3806:      */
3807:     public function DKIM_QP($txt)
3808:     {
3809:         $line = '';
3810:         for ($i = 0; $i < strlen($txt); ++$i) {
3811:             $ord = ord($txt[$i]);
3812:             if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
3813:                 $line .= $txt[$i];
3814:             } else {
3815:                 $line .= '='.sprintf('%02X', $ord);
3816:             }
3817:         }
3818: 
3819:         return $line;
3820:     }
3821: 
3822:     /**
3823:      * Generate a DKIM signature.
3824:      *
3825:      * @param string $signHeader
3826:      *
3827:      * @throws phpmailerException
3828:      *
3829:      * @return string The DKIM signature value
3830:      */
3831:     public function DKIM_Sign($signHeader)
3832:     {
3833:         if (!defined('PKCS7_TEXT')) {
3834:             if ($this->exceptions) {
3835:                 throw new phpmailerException($this->lang('extension_missing').'openssl');
3836:             }
3837: 
3838:             return '';
3839:         }
3840:         $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
3841:         if ('' != $this->DKIM_passphrase) {
3842:             $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
3843:         } else {
3844:             $privKey = openssl_pkey_get_private($privKeyStr);
3845:         }
3846:         //Workaround for missing digest algorithms in old PHP & OpenSSL versions
3847:         //@link http://stackoverflow.com/a/11117338/333340
3848:         if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
3849:             in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
3850:             if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
3851:                 openssl_pkey_free($privKey);
3852: 
3853:                 return base64_encode($signature);
3854:             }
3855:         } else {
3856:             $pinfo = openssl_pkey_get_details($privKey);
3857:             $hash = hash('sha256', $signHeader);
3858:             //'Magic' constant for SHA256 from RFC3447
3859:             //@link https://tools.ietf.org/html/rfc3447#page-43
3860:             $t = '3031300d060960864801650304020105000420'.$hash;
3861:             $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
3862:             $eb = pack('H*', '0001'.str_repeat('FF', $pslen).'00'.$t);
3863: 
3864:             if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
3865:                 openssl_pkey_free($privKey);
3866: 
3867:                 return base64_encode($signature);
3868:             }
3869:         }
3870:         openssl_pkey_free($privKey);
3871: 
3872:         return '';
3873:     }
3874: 
3875:     /**
3876:      * Generate a DKIM canonicalization header.
3877:      *
3878:      * @param string $signHeader Header
3879:      *
3880:      * @return string
3881:      */
3882:     public function DKIM_HeaderC($signHeader)
3883:     {
3884:         $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
3885:         $lines = explode("\r\n", $signHeader);
3886:         foreach ($lines as $key => $line) {
3887:             list($heading, $value) = explode(':', $line, 2);
3888:             $heading = strtolower($heading);
3889:             $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
3890:             $lines[$key] = $heading.':'.trim($value); // Don't forget to remove WSP around the value
3891:         }
3892:         $signHeader = implode("\r\n", $lines);
3893: 
3894:         return $signHeader;
3895:     }
3896: 
3897:     /**
3898:      * Generate a DKIM canonicalization body.
3899:      *
3900:      * @param string $body Message Body
3901:      *
3902:      * @return string
3903:      */
3904:     public function DKIM_BodyC($body)
3905:     {
3906:         if ($body == '') {
3907:             return "\r\n";
3908:         }
3909:         // stabilize line endings
3910:         $body = str_replace("\r\n", "\n", $body);
3911:         $body = str_replace("\n", "\r\n", $body);
3912:         // END stabilize line endings
3913:         while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
3914:             $body = substr($body, 0, strlen($body) - 2);
3915:         }
3916: 
3917:         return $body;
3918:     }
3919: 
3920:     /**
3921:      * Create the DKIM header and body in a new message header.
3922:      *
3923:      * @param string $headers_line Header lines
3924:      * @param string $subject      Subject
3925:      * @param string $body         Body
3926:      *
3927:      * @return string
3928:      */
3929:     public function DKIM_Add($headers_line, $subject, $body)
3930:     {
3931:         $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
3932:         $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
3933:         $DKIMquery = 'dns/txt'; // Query method
3934:         $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
3935:         $subject_header = "Subject: $subject";
3936:         $headers = explode($this->LE, $headers_line);
3937:         $from_header = '';
3938:         $to_header = '';
3939:         $date_header = '';
3940:         $current = '';
3941:         foreach ($headers as $header) {
3942:             if (strpos($header, 'From:') === 0) {
3943:                 $from_header = $header;
3944:                 $current = 'from_header';
3945:             } elseif (strpos($header, 'To:') === 0) {
3946:                 $to_header = $header;
3947:                 $current = 'to_header';
3948:             } elseif (strpos($header, 'Date:') === 0) {
3949:                 $date_header = $header;
3950:                 $current = 'date_header';
3951:             } else {
3952:                 if (!empty($$current) && strpos($header, ' =?') === 0) {
3953:                     $$current .= $header;
3954:                 } else {
3955:                     $current = '';
3956:                 }
3957:             }
3958:         }
3959:         $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
3960:         $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
3961:         $date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
3962:         $subject = str_replace(
3963:             '|', '=7C', $this->DKIM_QP($subject_header)
3964:         ); // Copied header fields (dkim-quoted-printable)
3965:         $body = $this->DKIM_BodyC($body);
3966:         $DKIMlen = strlen($body); // Length of body
3967:         $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
3968:         if ('' == $this->DKIM_identity) {
3969:             $ident = '';
3970:         } else {
3971:             $ident = ' i='.$this->DKIM_identity.';';
3972:         }
3973:         $dkimhdrs = 'DKIM-Signature: v=1; a='.
3974:         $DKIMsignatureType.'; q='.
3975:         $DKIMquery.'; l='.
3976:         $DKIMlen.'; s='.
3977:         $this->DKIM_selector.
3978:         ";\r\n".
3979:         "\tt=".$DKIMtime.'; c='.$DKIMcanonicalization.";\r\n".
3980:         "\th=From:To:Date:Subject;\r\n".
3981:         "\td=".$this->DKIM_domain.';'.$ident."\r\n".
3982:             "\tz=$from\r\n".
3983:             "\t|$to\r\n".
3984:             "\t|$date\r\n".
3985:             "\t|$subject;\r\n".
3986:             "\tbh=".$DKIMb64.";\r\n".
3987:             "\tb=";
3988:         $toSign = $this->DKIM_HeaderC(
3989:             $from_header."\r\n".
3990:             $to_header."\r\n".
3991:             $date_header."\r\n".
3992:             $subject_header."\r\n".
3993:             $dkimhdrs
3994:         );
3995:         $signed = $this->DKIM_Sign($toSign);
3996: 
3997:         return $dkimhdrs.$signed."\r\n";
3998:     }
3999: 
4000:     /**
4001:      * Detect if a string contains a line longer than the maximum line length allowed.
4002:      *
4003:      * @static
4004:      *
4005:      * @param string $str
4006:      *
4007:      * @return bool
4008:      */
4009:     public static function hasLineLongerThanMax($str)
4010:     {
4011:         //+2 to include CRLF line break for a 1000 total
4012:         return (bool) preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
4013:     }
4014: 
4015:     /**
4016:      * Allows for public read access to 'to' property.
4017:      *
4018:      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4019:      *
4020:      * @return array
4021:      */
4022:     public function getToAddresses()
4023:     {
4024:         return $this->to;
4025:     }
4026: 
4027:     /**
4028:      * Allows for public read access to 'cc' property.
4029:      *
4030:      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4031:      *
4032:      * @return array
4033:      */
4034:     public function getCcAddresses()
4035:     {
4036:         return $this->cc;
4037:     }
4038: 
4039:     /**
4040:      * Allows for public read access to 'bcc' property.
4041:      *
4042:      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4043:      *
4044:      * @return array
4045:      */
4046:     public function getBccAddresses()
4047:     {
4048:         return $this->bcc;
4049:     }
4050: 
4051:     /**
4052:      * Allows for public read access to 'ReplyTo' property.
4053:      *
4054:      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4055:      *
4056:      * @return array
4057:      */
4058:     public function getReplyToAddresses()
4059:     {
4060:         return $this->ReplyTo;
4061:     }
4062: 
4063:     /**
4064:      * Allows for public read access to 'all_recipients' property.
4065:      *
4066:      * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
4067:      *
4068:      * @return array
4069:      */
4070:     public function getAllRecipientAddresses()
4071:     {
4072:         return $this->all_recipients;
4073:     }
4074: 
4075:     /**
4076:      * Perform a callback.
4077:      *
4078:      * @param bool   $isSent
4079:      * @param array  $to
4080:      * @param array  $cc
4081:      * @param array  $bcc
4082:      * @param string $subject
4083:      * @param string $body
4084:      * @param string $from
4085:      */
4086:     protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
4087:     {
4088:         if (!empty($this->action_function) && is_callable($this->action_function)) {
4089:             $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
4090:             call_user_func_array($this->action_function, $params);
4091:         }
4092:     }
4093: }
4094: 
4095: /**
4096:  * PHPMailer exception handler.
4097:  */
4098: class phpmailerException extends Exception
4099: {
4100:     /**
4101:      * Prettify error message output.
4102:      *
4103:      * @return string
4104:      */
4105:     public function errorMessage()
4106:     {
4107:         $errorMsg = '<strong>'.$this->getMessage()."</strong><br />\n";
4108: 
4109:         return $errorMsg;
4110:     }
4111: }
4112: 
Kotchasan API documentation generated by ApiGen