@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PHPMailer: Merge class.phpmailer-lite.php into class.phpmailer.php

Summary:
Remove `class.phpmailer-lite.php` whose upstream is nowhere to be found, to potentially lay out a path to future PHPMailer updates.

Closes T15877

Test Plan:
Unclear, apart from carefully diff'ing and reading code.
Maybe https://we.phorge.it/book/phorge/article/configuring_outbound_email/ could come handy though.

Reviewers: O1 Blessed Committers, mainframe98

Reviewed By: O1 Blessed Committers, mainframe98

Subscribers: mainframe98, keithzg, tobiaswiese, valerio.bozzolan, Matthew, Cigaryno

Tags: #mail

Maniphest Tasks: T15877

Differential Revision: https://we.phorge.it/D25723

+132 -2192
-2175
externals/phpmailer/class.phpmailer-lite.php
··· 1 - <?php 2 - /*~ class.phpmailer-lite.php 3 - .---------------------------------------------------------------------------. 4 - | Software: PHPMailer Lite - PHP email class | 5 - | Version: 5.1 | 6 - | Contact: via sourceforge.net support pages (also www.codeworxtech.com) | 7 - | Info: http://phpmailer.sourceforge.net | 8 - | Support: http://sourceforge.net/projects/phpmailer/ | 9 - | ------------------------------------------------------------------------- | 10 - | Admin: Andy Prevost (project admininistrator) | 11 - | Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net | 12 - | : Marcus Bointon (coolbru) coolbru@users.sourceforge.net | 13 - | Founder: Brent R. Matzelle (original founder) | 14 - | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved. | 15 - | Copyright (c) 2001-2003, Brent R. Matzelle | 16 - | ------------------------------------------------------------------------- | 17 - | License: Distributed under the Lesser General Public License (LGPL) | 18 - | http://www.gnu.org/copyleft/lesser.html | 19 - | This program is distributed in the hope that it will be useful - WITHOUT | 20 - | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 21 - | FITNESS FOR A PARTICULAR PURPOSE. | 22 - | ------------------------------------------------------------------------- | 23 - | We offer a number of paid services (www.codeworxtech.com): | 24 - | - Web Hosting on highly optimized fast and secure servers | 25 - | - Technology Consulting | 26 - | - Oursourcing (highly qualified programmers and graphic designers) | 27 - '---------------------------------------------------------------------------' 28 - */ 29 - 30 - /** 31 - * PHPMailer Lite - PHP email transport class 32 - * NOTE: Requires PHP version 5 or later 33 - * @package PHPMailer Lite 34 - * @author Andy Prevost 35 - * @author Marcus Bointon 36 - * @copyright 2004 - 2009 Andy Prevost 37 - * @version $Id: class.phpmailer-lite.php 447 2009-09-12 13:21:38Z codeworxtech $ 38 - * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 39 - */ 40 - 41 - if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n"); 42 - 43 - class PHPMailerLite { 44 - 45 - public static function newFromMessage( 46 - PhabricatorMailExternalMessage $message) { 47 - 48 - $mailer = new self($use_exceptions = true); 49 - 50 - // By default, PHPMailerLite sends one mail per recipient. We handle 51 - // combining or separating To and Cc higher in the stack, so tell it to 52 - // send mail exactly like we ask. 53 - $mailer->SingleTo = false; 54 - 55 - $mailer->CharSet = 'utf-8'; 56 - $mailer->Encoding = 'base64'; 57 - 58 - $subject = $message->getSubject(); 59 - if ($subject !== null) { 60 - $mailer->Subject = $subject; 61 - } 62 - 63 - $from_address = $message->getFromAddress(); 64 - if ($from_address) { 65 - $mailer->SetFrom( 66 - $from_address->getAddress(), 67 - (string)$from_address->getDisplayName(), 68 - $crazy_side_effects = false); 69 - } 70 - 71 - $reply_address = $message->getReplyToAddress(); 72 - if ($reply_address) { 73 - $mailer->AddReplyTo( 74 - $reply_address->getAddress(), 75 - (string)$reply_address->getDisplayName()); 76 - } 77 - 78 - $to_addresses = $message->getToAddresses(); 79 - if ($to_addresses) { 80 - foreach ($to_addresses as $address) { 81 - $mailer->AddAddress( 82 - $address->getAddress(), 83 - (string)$address->getDisplayName()); 84 - } 85 - } 86 - 87 - $cc_addresses = $message->getCCAddresses(); 88 - if ($cc_addresses) { 89 - foreach ($cc_addresses as $address) { 90 - $mailer->AddCC( 91 - $address->getAddress(), 92 - (string)$address->getDisplayName()); 93 - } 94 - } 95 - 96 - $headers = $message->getHeaders(); 97 - if ($headers) { 98 - foreach ($headers as $header) { 99 - $name = $header->getName(); 100 - $value = $header->getValue(); 101 - 102 - if (phutil_utf8_strtolower($name) === 'message-id') { 103 - $mailer->MessageID = $value; 104 - } else { 105 - $mailer->AddCustomHeader("{$name}: {$value}"); 106 - } 107 - } 108 - } 109 - 110 - $attachments = $message->getAttachments(); 111 - if ($attachments) { 112 - foreach ($attachments as $attachment) { 113 - $mailer->AddStringAttachment( 114 - $attachment->getData(), 115 - $attachment->getFilename(), 116 - 'base64', 117 - $attachment->getMimeType()); 118 - } 119 - } 120 - 121 - $text_body = $message->getTextBody(); 122 - if ($text_body !== null) { 123 - $mailer->Body = $text_body; 124 - } 125 - 126 - $html_body = $message->getHTMLBody(); 127 - if ($html_body !== null) { 128 - $mailer->IsHTML(true); 129 - $mailer->Body = $html_body; 130 - if ($text_body !== null) { 131 - $mailer->AltBody = $text_body; 132 - } 133 - } 134 - 135 - return $mailer; 136 - } 137 - 138 - 139 - 140 - 141 - ///////////////////////////////////////////////// 142 - // PROPERTIES, PUBLIC 143 - ///////////////////////////////////////////////// 144 - 145 - /** 146 - * Email priority (1 = High, 3 = Normal, 5 = low). 147 - * @var int 148 - */ 149 - public $Priority = 3; 150 - 151 - /** 152 - * Sets the CharSet of the message. 153 - * @var string 154 - */ 155 - public $CharSet = 'iso-8859-1'; 156 - 157 - /** 158 - * Sets the Content-type of the message. 159 - * @var string 160 - */ 161 - public $ContentType = 'text/plain'; 162 - 163 - /** 164 - * Sets the Encoding of the message. Options for this are 165 - * "8bit", "7bit", "binary", "base64", and "quoted-printable". 166 - * @var string 167 - */ 168 - public $Encoding = '8bit'; 169 - 170 - /** 171 - * Holds the most recent mailer error message. 172 - * @var string 173 - */ 174 - public $ErrorInfo = ''; 175 - 176 - /** 177 - * Sets the From email address for the message. 178 - * @var string 179 - */ 180 - public $From = 'root@localhost'; 181 - 182 - /** 183 - * Sets the From name of the message. 184 - * @var string 185 - */ 186 - public $FromName = 'Root User'; 187 - 188 - /** 189 - * Sets the Sender email (Return-Path) of the message. If not empty, 190 - * will be sent via -f to sendmail 191 - * @var string 192 - */ 193 - public $Sender = ''; 194 - 195 - /** 196 - * Sets the Subject of the message. 197 - * @var string 198 - */ 199 - public $Subject = ''; 200 - 201 - /** 202 - * Sets the Body of the message. This can be either an HTML or text body. 203 - * If HTML then run IsHTML(true). 204 - * @var string 205 - */ 206 - public $Body = ''; 207 - 208 - /** 209 - * Sets the text-only body of the message. This automatically sets the 210 - * email to multipart/alternative. This body can be read by mail 211 - * clients that do not have HTML email capability such as mutt. Clients 212 - * that can read HTML will view the normal Body. 213 - * @var string 214 - */ 215 - public $AltBody = ''; 216 - 217 - /** 218 - * Sets word wrapping on the body of the message to a given number of 219 - * characters. 220 - * @var int 221 - */ 222 - public $WordWrap = 0; 223 - 224 - /** 225 - * Method to send mail: ("mail", or "sendmail"). 226 - * @var string 227 - */ 228 - public $Mailer = 'sendmail'; 229 - 230 - /** 231 - * Sets the path of the sendmail program. 232 - * @var string 233 - */ 234 - public $Sendmail = '/usr/sbin/sendmail'; 235 - 236 - /** 237 - * Sets the email address that a reading confirmation will be sent. 238 - * @var string 239 - */ 240 - public $ConfirmReadingTo = ''; 241 - 242 - /** 243 - * Sets the hostname to use in Message-Id and Received headers 244 - * and as default HELO string. If empty, the value returned 245 - * by SERVER_NAME is used or 'localhost.localdomain'. 246 - * @var string 247 - */ 248 - public $Hostname = ''; 249 - 250 - /** 251 - * Sets the message ID to be used in the Message-Id header. 252 - * If empty, a unique id will be generated. 253 - * @var string 254 - */ 255 - public $MessageID = ''; 256 - 257 - /** 258 - * Provides the ability to have the TO field process individual 259 - * emails, instead of sending to entire TO addresses 260 - * @var bool 261 - */ 262 - public $SingleTo = true; 263 - 264 - /** 265 - * If SingleTo is true, this provides the array to hold the email addresses 266 - * @var bool 267 - */ 268 - public $SingleToArray = array(); 269 - 270 - /** 271 - * Provides the ability to change the line ending 272 - * @var string 273 - */ 274 - public $LE = "\n"; 275 - 276 - /** 277 - * Used with DKIM DNS Resource Record 278 - * @var string 279 - */ 280 - public $DKIM_selector = 'phpmailer'; 281 - 282 - /** 283 - * Used with DKIM DNS Resource Record 284 - * optional, in format of email address 'you@yourdomain.com' 285 - * @var string 286 - */ 287 - public $DKIM_identity = ''; 288 - 289 - /** 290 - * Used with DKIM DNS Resource Record 291 - * required, in format of base domain 'yourdomain.com' 292 - * @var string 293 - */ 294 - public $DKIM_domain = ''; 295 - 296 - /** 297 - * Used with DKIM Digital Signing process 298 - * optional 299 - * @var string 300 - */ 301 - public $DKIM_passphrase = ''; 302 - 303 - /** 304 - * Used with DKIM DNS Resource Record 305 - * required, private key (read from /.htprivkey) 306 - * @var string 307 - */ 308 - public $DKIM_private = ''; 309 - 310 - /** 311 - * Callback Action function name 312 - * the function that handles the result of the send email action. Parameters: 313 - * bool $result result of the send action 314 - * string $to email address of the recipient 315 - * string $cc cc email addresses 316 - * string $bcc bcc email addresses 317 - * string $subject the subject 318 - * string $body the email body 319 - * @var string 320 - */ 321 - public $action_function = ''; //'callbackAction'; 322 - 323 - /** 324 - * Sets the PHPMailer Version number 325 - * @var string 326 - */ 327 - public $Version = 'Lite 5.1'; 328 - 329 - ///////////////////////////////////////////////// 330 - // PROPERTIES, PRIVATE AND PROTECTED 331 - ///////////////////////////////////////////////// 332 - 333 - private $to = array(); 334 - private $cc = array(); 335 - private $bcc = array(); 336 - private $ReplyTo = array(); 337 - private $all_recipients = array(); 338 - private $attachment = array(); 339 - private $CustomHeader = array(); 340 - private $message_type = ''; 341 - private $boundary = array(); 342 - protected $language = array(); 343 - private $error_count = 0; 344 - private $sign_cert_file = ""; 345 - private $sign_key_file = ""; 346 - private $sign_key_pass = ""; 347 - private $exceptions = false; 348 - 349 - ///////////////////////////////////////////////// 350 - // CONSTANTS 351 - ///////////////////////////////////////////////// 352 - 353 - const STOP_MESSAGE = 0; // message only, continue processing 354 - const STOP_CONTINUE = 1; // message?, likely ok to continue processing 355 - const STOP_CRITICAL = 2; // message, plus full stop, critical error reached 356 - 357 - ///////////////////////////////////////////////// 358 - // METHODS, VARIABLES 359 - ///////////////////////////////////////////////// 360 - 361 - /** 362 - * Constructor 363 - * @param boolean $exceptions Should we throw external exceptions? 364 - */ 365 - public function __construct($exceptions = false) { 366 - $this->exceptions = ($exceptions == true); 367 - } 368 - 369 - /** 370 - * Sets message type to HTML. 371 - * @param bool $ishtml 372 - * @return void 373 - */ 374 - public function IsHTML($ishtml = true) { 375 - if ($ishtml) { 376 - $this->ContentType = 'text/html'; 377 - } else { 378 - $this->ContentType = 'text/plain'; 379 - } 380 - } 381 - 382 - /** 383 - * Sets Mailer to send message using PHP mail() function. 384 - * @return void 385 - */ 386 - public function IsMail() { 387 - $this->Mailer = 'mail'; 388 - } 389 - 390 - /** 391 - * Sets Mailer to send message using the $Sendmail program. 392 - * @return void 393 - */ 394 - public function IsSendmail() { 395 - if (!stristr(ini_get('sendmail_path'), 'sendmail')) { 396 - $this->Sendmail = '/var/qmail/bin/sendmail'; 397 - } 398 - $this->Mailer = 'sendmail'; 399 - } 400 - 401 - /** 402 - * Sets Mailer to send message using the qmail MTA. 403 - * @return void 404 - */ 405 - public function IsQmail() { 406 - if (stristr(ini_get('sendmail_path'), 'qmail')) { 407 - $this->Sendmail = '/var/qmail/bin/sendmail'; 408 - } 409 - $this->Mailer = 'sendmail'; 410 - } 411 - 412 - ///////////////////////////////////////////////// 413 - // METHODS, RECIPIENTS 414 - ///////////////////////////////////////////////// 415 - 416 - /** 417 - * Adds a "To" address. 418 - * @param string $address 419 - * @param string $name 420 - * @return boolean true on success, false if address already used 421 - */ 422 - public function AddAddress($address, $name = '') { 423 - return $this->AddAnAddress('to', $address, $name); 424 - } 425 - 426 - /** 427 - * Adds a "Cc" address. 428 - * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. 429 - * @param string $address 430 - * @param string $name 431 - * @return boolean true on success, false if address already used 432 - */ 433 - public function AddCC($address, $name = '') { 434 - return $this->AddAnAddress('cc', $address, $name); 435 - } 436 - 437 - /** 438 - * Adds a "Bcc" address. 439 - * Note: this function works with the SMTP mailer on win32, not with the "mail" mailer. 440 - * @param string $address 441 - * @param string $name 442 - * @return boolean true on success, false if address already used 443 - */ 444 - public function AddBCC($address, $name = '') { 445 - return $this->AddAnAddress('bcc', $address, $name); 446 - } 447 - 448 - /** 449 - * Adds a "Reply-to" address. 450 - * @param string $address 451 - * @param string $name 452 - * @return boolean 453 - */ 454 - public function AddReplyTo($address, $name = '') { 455 - return $this->AddAnAddress('ReplyTo', $address, $name); 456 - } 457 - 458 - /** 459 - * Adds an address to one of the recipient arrays 460 - * Addresses that have been added already return false, but do not throw exceptions 461 - * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' 462 - * @param string $address The email address to send to 463 - * @param string $name 464 - * @return boolean true on success, false if address already used or invalid in some way 465 - * @access private 466 - */ 467 - private function AddAnAddress($kind, $address, $name = '') { 468 - if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) { 469 - echo 'Invalid recipient array: ' . $kind; 470 - return false; 471 - } 472 - $address = trim($address); 473 - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 474 - if (!self::ValidateAddress($address)) { 475 - $this->SetError($this->Lang('invalid_address').': '. $address); 476 - if ($this->exceptions) { 477 - throw new phpmailerException($this->Lang('invalid_address').': '.$address); 478 - } 479 - echo $this->Lang('invalid_address').': '.$address; 480 - return false; 481 - } 482 - if ($kind != 'ReplyTo') { 483 - if (!isset($this->all_recipients[strtolower($address)])) { 484 - array_push($this->$kind, array($address, $name)); 485 - $this->all_recipients[strtolower($address)] = true; 486 - return true; 487 - } 488 - } else { 489 - if (!array_key_exists(strtolower($address), $this->ReplyTo)) { 490 - $this->ReplyTo[strtolower($address)] = array($address, $name); 491 - return true; 492 - } 493 - } 494 - return false; 495 - } 496 - 497 - /** 498 - * Set the From and FromName properties 499 - * @param string $address 500 - * @param string $name 501 - * @return boolean 502 - */ 503 - public function SetFrom($address, $name = '',$auto=1) { 504 - $address = trim($address); 505 - $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim 506 - if (!self::ValidateAddress($address)) { 507 - $this->SetError($this->Lang('invalid_address').': '. $address); 508 - if ($this->exceptions) { 509 - throw new phpmailerException($this->Lang('invalid_address').': '.$address); 510 - } 511 - echo $this->Lang('invalid_address').': '.$address; 512 - return false; 513 - } 514 - $this->From = $address; 515 - $this->FromName = $name; 516 - if ($auto) { 517 - if (empty($this->ReplyTo)) { 518 - $this->AddAnAddress('ReplyTo', $address, $name); 519 - } 520 - if (empty($this->Sender)) { 521 - $this->Sender = $address; 522 - } 523 - } 524 - return true; 525 - } 526 - 527 - /** 528 - * Check that a string looks roughly like an email address should 529 - * Static so it can be used without instantiation 530 - * Tries to use PHP built-in validator in the filter extension (from PHP 5.2), falls back to a reasonably competent regex validator 531 - * Conforms approximately to RFC2822 532 - * @link http://www.hexillion.com/samples/#Regex Original pattern found here 533 - * @param string $address The email address to check 534 - * @return boolean 535 - * @static 536 - * @access public 537 - */ 538 - public static function ValidateAddress($address) { 539 - if (function_exists('filter_var')) { //Introduced in PHP 5.2 540 - if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) { 541 - return false; 542 - } else { 543 - return true; 544 - } 545 - } else { 546 - return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address); 547 - } 548 - } 549 - 550 - ///////////////////////////////////////////////// 551 - // METHODS, MAIL SENDING 552 - ///////////////////////////////////////////////// 553 - 554 - /** 555 - * Creates message and assigns Mailer. If the message is 556 - * not sent successfully then it returns false. Use the ErrorInfo 557 - * variable to view description of the error. 558 - * @return bool 559 - */ 560 - public function Send() { 561 - try { 562 - if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { 563 - throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL); 564 - } 565 - 566 - // Set whether the message is multipart/alternative 567 - if(!empty($this->AltBody)) { 568 - $this->ContentType = 'multipart/alternative'; 569 - } 570 - 571 - $this->error_count = 0; // reset errors 572 - $this->SetMessageType(); 573 - $header = $this->CreateHeader(); 574 - $body = $this->CreateBody(); 575 - 576 - if (empty($this->Body)) { 577 - throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL); 578 - } 579 - 580 - // digitally sign with DKIM if enabled 581 - if ($this->DKIM_domain && $this->DKIM_private) { 582 - $header_dkim = $this->DKIM_Add($header,$this->Subject,$body); 583 - $header = str_replace("\r\n","\n",$header_dkim) . $header; 584 - } 585 - 586 - // Choose the mailer and send through it 587 - switch($this->Mailer) { 588 - 589 - case 'amazon-ses': 590 - return $this->customMailer->executeSend( 591 - $header. 592 - $body); 593 - 594 - case 'sendmail': 595 - $sendAction = $this->SendmailSend($header, $body); 596 - return $sendAction; 597 - default: 598 - $sendAction = $this->MailSend($header, $body); 599 - return $sendAction; 600 - } 601 - 602 - } catch (phpmailerException $e) { 603 - $this->SetError($e->getMessage()); 604 - if ($this->exceptions) { 605 - throw $e; 606 - } 607 - echo $e->getMessage()."\n"; 608 - return false; 609 - } 610 - } 611 - 612 - /** 613 - * Sends mail using the $Sendmail program. 614 - * @param string $header The message headers 615 - * @param string $body The message body 616 - * @access protected 617 - * @return bool 618 - */ 619 - protected function SendmailSend($header, $body) { 620 - if ($this->Sender != '') { 621 - $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); 622 - } else { 623 - $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); 624 - } 625 - 626 - if ($this->SingleTo === true) { 627 - foreach ($this->SingleToArray as $key => $val) { 628 - $mail = new ExecFuture('%C', $sendmail); 629 - $mail->write("To: {$val}\n", true); 630 - $mail->write($header.$body); 631 - $mail->resolvex(); 632 - } 633 - } else { 634 - $mail = new ExecFuture('%C', $sendmail); 635 - $mail->write($header.$body); 636 - $mail->resolvex(); 637 - } 638 - 639 - return true; 640 - } 641 - 642 - /** 643 - * Sends mail using the PHP mail() function. 644 - * @param string $header The message headers 645 - * @param string $body The message body 646 - * @access protected 647 - * @return bool 648 - */ 649 - protected function MailSend($header, $body) { 650 - $toArr = array(); 651 - foreach($this->to as $t) { 652 - $toArr[] = $this->AddrFormat($t); 653 - } 654 - $to = implode(', ', $toArr); 655 - 656 - $params = sprintf("-oi -f %s", $this->Sender); 657 - if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) { 658 - $old_from = ini_get('sendmail_from'); 659 - ini_set('sendmail_from', $this->Sender); 660 - if ($this->SingleTo === true && count($toArr) > 1) { 661 - foreach ($toArr as $key => $val) { 662 - $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); 663 - // implement call back function if it exists 664 - $isSent = ($rt == 1) ? 1 : 0; 665 - $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body); 666 - } 667 - } else { 668 - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); 669 - // implement call back function if it exists 670 - $isSent = ($rt == 1) ? 1 : 0; 671 - $this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body); 672 - } 673 - } else { 674 - if ($this->SingleTo === true && count($toArr) > 1) { 675 - foreach ($toArr as $key => $val) { 676 - $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params); 677 - // implement call back function if it exists 678 - $isSent = ($rt == 1) ? 1 : 0; 679 - $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body); 680 - } 681 - } else { 682 - $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header); 683 - // implement call back function if it exists 684 - $isSent = ($rt == 1) ? 1 : 0; 685 - $this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body); 686 - } 687 - } 688 - if (isset($old_from)) { 689 - ini_set('sendmail_from', $old_from); 690 - } 691 - if(!$rt) { 692 - throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL); 693 - } 694 - return true; 695 - } 696 - 697 - /** 698 - * Sets the language for all class error messages. 699 - * Returns false if it cannot load the language file. The default language is English. 700 - * @param string $langcode ISO 639-1 2-character language code (e.g. Portuguese: "br") 701 - * @param string $lang_path Path to the language file directory 702 - * @access public 703 - */ 704 - function SetLanguage($langcode = 'en', $lang_path = 'language/') { 705 - //Define full set of translatable strings 706 - $PHPMAILER_LANG = array( 707 - 'provide_address' => 'You must provide at least one recipient email address.', 708 - 'mailer_not_supported' => ' mailer is not supported.', 709 - 'execute' => 'Could not execute: ', 710 - 'instantiate' => 'Could not instantiate mail function.', 711 - 'from_failed' => 'The following From address failed: ', 712 - 'file_access' => 'Could not access file: ', 713 - 'file_open' => 'File Error: Could not open file: ', 714 - 'encoding' => 'Unknown encoding: ', 715 - 'signing' => 'Signing Error: ', 716 - 'empty_message' => 'Message body empty', 717 - 'invalid_address' => 'Invalid address', 718 - 'variable_set' => 'Cannot set or reset variable: ' 719 - ); 720 - //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"! 721 - $l = true; 722 - if ($langcode != 'en') { //There is no English translation file 723 - $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php'; 724 - } 725 - $this->language = $PHPMAILER_LANG; 726 - return ($l == true); //Returns false if language not found 727 - } 728 - 729 - /** 730 - * Return the current array of language strings 731 - * @return array 732 - */ 733 - public function GetTranslations() { 734 - return $this->language; 735 - } 736 - 737 - ///////////////////////////////////////////////// 738 - // METHODS, MESSAGE CREATION 739 - ///////////////////////////////////////////////// 740 - 741 - /** 742 - * Creates recipient headers. 743 - * @access public 744 - * @return string 745 - */ 746 - public function AddrAppend($type, $addr) { 747 - $addr_str = $type . ': '; 748 - $addresses = array(); 749 - foreach ($addr as $a) { 750 - $addresses[] = $this->AddrFormat($a); 751 - } 752 - $addr_str .= implode(', ', $addresses); 753 - $addr_str .= $this->LE; 754 - 755 - // NOTE: This is a narrow hack to fix an issue with 1000+ characters of 756 - // recipients, described in T12372. 757 - $addr_str = wordwrap($addr_str, 75, "\n "); 758 - 759 - return $addr_str; 760 - } 761 - 762 - /** 763 - * Formats an address correctly. 764 - * @access public 765 - * @return string 766 - */ 767 - public function AddrFormat($addr) { 768 - if (empty($addr[1])) { 769 - return $this->SecureHeader($addr[0]); 770 - } else { 771 - return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">"; 772 - } 773 - } 774 - 775 - /** 776 - * Wraps message for use with mailers that do not 777 - * automatically perform wrapping and for quoted-printable. 778 - * Original written by philippe. 779 - * @param string $message The message to wrap 780 - * @param integer $length The line length to wrap to 781 - * @param boolean $qp_mode Whether to run in Quoted-Printable mode 782 - * @access public 783 - * @return string 784 - */ 785 - public function WrapText($message, $length, $qp_mode = false) { 786 - $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; 787 - // If utf-8 encoding is used, we will need to make sure we don't 788 - // split multibyte characters when we wrap 789 - $is_utf8 = (strtolower($this->CharSet) == "utf-8"); 790 - 791 - $message = $this->FixEOL($message); 792 - if (substr($message, -1) == $this->LE) { 793 - $message = substr($message, 0, -1); 794 - } 795 - 796 - $line = explode($this->LE, $message); 797 - $message = ''; 798 - for ($i=0 ;$i < count($line); $i++) { 799 - $line_part = explode(' ', $line[$i]); 800 - $buf = ''; 801 - for ($e = 0; $e<count($line_part); $e++) { 802 - $word = $line_part[$e]; 803 - if ($qp_mode and (strlen($word) > $length)) { 804 - $space_left = $length - strlen($buf) - 1; 805 - if ($e != 0) { 806 - if ($space_left > 20) { 807 - $len = $space_left; 808 - if ($is_utf8) { 809 - $len = $this->UTF8CharBoundary($word, $len); 810 - } elseif (substr($word, $len - 1, 1) == "=") { 811 - $len--; 812 - } elseif (substr($word, $len - 2, 1) == "=") { 813 - $len -= 2; 814 - } 815 - $part = substr($word, 0, $len); 816 - $word = substr($word, $len); 817 - $buf .= ' ' . $part; 818 - $message .= $buf . sprintf("=%s", $this->LE); 819 - } else { 820 - $message .= $buf . $soft_break; 821 - } 822 - $buf = ''; 823 - } 824 - while (strlen($word) > 0) { 825 - $len = $length; 826 - if ($is_utf8) { 827 - $len = $this->UTF8CharBoundary($word, $len); 828 - } elseif (substr($word, $len - 1, 1) == "=") { 829 - $len--; 830 - } elseif (substr($word, $len - 2, 1) == "=") { 831 - $len -= 2; 832 - } 833 - $part = substr($word, 0, $len); 834 - $word = substr($word, $len); 835 - 836 - if (strlen($word) > 0) { 837 - $message .= $part . sprintf("=%s", $this->LE); 838 - } else { 839 - $buf = $part; 840 - } 841 - } 842 - } else { 843 - $buf_o = $buf; 844 - $buf .= ($e == 0) ? $word : (' ' . $word); 845 - 846 - if (strlen($buf) > $length and $buf_o != '') { 847 - $message .= $buf_o . $soft_break; 848 - $buf = $word; 849 - } 850 - } 851 - } 852 - $message .= $buf . $this->LE; 853 - } 854 - 855 - return $message; 856 - } 857 - 858 - /** 859 - * Finds last character boundary prior to maxLength in a utf-8 860 - * quoted (printable) encoded string. 861 - * Original written by Colin Brown. 862 - * @access public 863 - * @param string $encodedText utf-8 QP text 864 - * @param int $maxLength find last character boundary prior to this length 865 - * @return int 866 - */ 867 - public function UTF8CharBoundary($encodedText, $maxLength) { 868 - $foundSplitPos = false; 869 - $lookBack = 3; 870 - while (!$foundSplitPos) { 871 - $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); 872 - $encodedCharPos = strpos($lastChunk, "="); 873 - if ($encodedCharPos !== false) { 874 - // Found start of encoded character byte within $lookBack block. 875 - // Check the encoded byte value (the 2 chars after the '=') 876 - $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); 877 - $dec = hexdec($hex); 878 - if ($dec < 128) { // Single byte character. 879 - // If the encoded char was found at pos 0, it will fit 880 - // otherwise reduce maxLength to start of the encoded char 881 - $maxLength = ($encodedCharPos == 0) ? $maxLength : 882 - $maxLength - ($lookBack - $encodedCharPos); 883 - $foundSplitPos = true; 884 - } elseif ($dec >= 192) { // First byte of a multi byte character 885 - // Reduce maxLength to split at start of character 886 - $maxLength = $maxLength - ($lookBack - $encodedCharPos); 887 - $foundSplitPos = true; 888 - } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back 889 - $lookBack += 3; 890 - } 891 - } else { 892 - // No encoded character found 893 - $foundSplitPos = true; 894 - } 895 - } 896 - return $maxLength; 897 - } 898 - 899 - /** 900 - * Set the body wrapping. 901 - * @access public 902 - * @return void 903 - */ 904 - public function SetWordWrap() { 905 - if($this->WordWrap < 1) { 906 - return; 907 - } 908 - switch($this->message_type) { 909 - case 'alt': 910 - case 'alt_attachments': 911 - $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap); 912 - break; 913 - default: 914 - $this->Body = $this->WrapText($this->Body, $this->WordWrap); 915 - break; 916 - } 917 - } 918 - 919 - /** 920 - * Assembles message header. 921 - * @access public 922 - * @return string The assembled header 923 - */ 924 - public function CreateHeader() { 925 - $result = ''; 926 - 927 - // Set the boundaries 928 - $uniq_id = md5(uniqid(time())); 929 - $this->boundary[1] = 'b1_' . $uniq_id; 930 - $this->boundary[2] = 'b2_' . $uniq_id; 931 - 932 - $result .= $this->HeaderLine('Date', self::RFCDate()); 933 - if($this->Sender == '') { 934 - $result .= $this->HeaderLine('Return-Path', trim($this->From)); 935 - } else { 936 - $result .= $this->HeaderLine('Return-Path', trim($this->Sender)); 937 - } 938 - 939 - // To be created automatically by mail() 940 - if($this->Mailer != 'mail') { 941 - if ($this->SingleTo === true) { 942 - foreach($this->to as $t) { 943 - $this->SingleToArray[] = $this->AddrFormat($t); 944 - } 945 - } else { 946 - if(count($this->to) > 0) { 947 - $result .= $this->AddrAppend('To', $this->to); 948 - } elseif (count($this->cc) == 0) { 949 - $result .= $this->HeaderLine('To', 'undisclosed-recipients:;'); 950 - } 951 - } 952 - } 953 - 954 - $from = array(); 955 - $from[0][0] = trim($this->From); 956 - $from[0][1] = $this->FromName; 957 - $result .= $this->AddrAppend('From', $from); 958 - 959 - // sendmail and mail() extract Cc from the header before sending 960 - if(count($this->cc) > 0) { 961 - $result .= $this->AddrAppend('Cc', $this->cc); 962 - } 963 - 964 - // sendmail and mail() extract Bcc from the header before sending 965 - if(count($this->bcc) > 0) { 966 - $result .= $this->AddrAppend('Bcc', $this->bcc); 967 - } 968 - 969 - if(count($this->ReplyTo) > 0) { 970 - $result .= $this->AddrAppend('Reply-to', $this->ReplyTo); 971 - } 972 - 973 - // mail() sets the subject itself 974 - if($this->Mailer != 'mail') { 975 - $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject))); 976 - } 977 - 978 - if($this->MessageID != '') { 979 - $result .= $this->HeaderLine('Message-ID',$this->MessageID); 980 - } else { 981 - $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); 982 - } 983 - $result .= $this->HeaderLine('X-Priority', $this->Priority); 984 - 985 - if($this->ConfirmReadingTo != '') { 986 - $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); 987 - } 988 - 989 - // Add custom headers 990 - for($index = 0; $index < count($this->CustomHeader); $index++) { 991 - $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1]))); 992 - } 993 - if (!$this->sign_key_file) { 994 - $result .= $this->HeaderLine('MIME-Version', '1.0'); 995 - $result .= $this->GetMailMIME(); 996 - } 997 - 998 - return $result; 999 - } 1000 - 1001 - /** 1002 - * Returns the message MIME. 1003 - * @access public 1004 - * @return string 1005 - */ 1006 - public function GetMailMIME() { 1007 - $result = ''; 1008 - switch($this->message_type) { 1009 - case 'plain': 1010 - $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding); 1011 - $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet); 1012 - break; 1013 - case 'attachments': 1014 - case 'alt_attachments': 1015 - if($this->InlineImageExists()){ 1016 - $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE); 1017 - } else { 1018 - $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;'); 1019 - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); 1020 - } 1021 - break; 1022 - case 'alt': 1023 - $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;'); 1024 - $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"'); 1025 - break; 1026 - } 1027 - 1028 - if($this->Mailer != 'mail') { 1029 - $result .= $this->LE.$this->LE; 1030 - } 1031 - 1032 - return $result; 1033 - } 1034 - 1035 - /** 1036 - * Assembles the message body. Returns an empty string on failure. 1037 - * @access public 1038 - * @return string The assembled message body 1039 - */ 1040 - public function CreateBody() { 1041 - $body = ''; 1042 - 1043 - if ($this->sign_key_file) { 1044 - $body .= $this->GetMailMIME(); 1045 - } 1046 - 1047 - $this->SetWordWrap(); 1048 - 1049 - switch($this->message_type) { 1050 - case 'alt': 1051 - $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', ''); 1052 - $body .= $this->EncodeString($this->AltBody, $this->Encoding); 1053 - $body .= $this->LE.$this->LE; 1054 - $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', ''); 1055 - $body .= $this->EncodeString($this->Body, $this->Encoding); 1056 - $body .= $this->LE.$this->LE; 1057 - $body .= $this->EndBoundary($this->boundary[1]); 1058 - break; 1059 - case 'plain': 1060 - $body .= $this->EncodeString($this->Body, $this->Encoding); 1061 - break; 1062 - case 'attachments': 1063 - $body .= $this->GetBoundary($this->boundary[1], '', '', ''); 1064 - $body .= $this->EncodeString($this->Body, $this->Encoding); 1065 - $body .= $this->LE; 1066 - $body .= $this->AttachAll(); 1067 - break; 1068 - case 'alt_attachments': 1069 - $body .= sprintf("--%s%s", $this->boundary[1], $this->LE); 1070 - $body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE); 1071 - $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body 1072 - $body .= $this->EncodeString($this->AltBody, $this->Encoding); 1073 - $body .= $this->LE.$this->LE; 1074 - $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body 1075 - $body .= $this->EncodeString($this->Body, $this->Encoding); 1076 - $body .= $this->LE.$this->LE; 1077 - $body .= $this->EndBoundary($this->boundary[2]); 1078 - $body .= $this->AttachAll(); 1079 - break; 1080 - } 1081 - 1082 - if ($this->IsError()) { 1083 - $body = ''; 1084 - } elseif ($this->sign_key_file) { 1085 - try { 1086 - $file = tempnam('', 'mail'); 1087 - file_put_contents($file, $body); //TODO check this worked 1088 - $signed = tempnam("", "signed"); 1089 - if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) { 1090 - @unlink($file); 1091 - @unlink($signed); 1092 - $body = file_get_contents($signed); 1093 - } else { 1094 - @unlink($file); 1095 - @unlink($signed); 1096 - throw new phpmailerException($this->Lang("signing").openssl_error_string()); 1097 - } 1098 - } catch (phpmailerException $e) { 1099 - $body = ''; 1100 - if ($this->exceptions) { 1101 - throw $e; 1102 - } 1103 - } 1104 - } 1105 - 1106 - return $body; 1107 - } 1108 - 1109 - /** 1110 - * Returns the start of a message boundary. 1111 - * @access private 1112 - */ 1113 - private function GetBoundary($boundary, $charSet, $contentType, $encoding) { 1114 - $result = ''; 1115 - if($charSet == '') { 1116 - $charSet = $this->CharSet; 1117 - } 1118 - if($contentType == '') { 1119 - $contentType = $this->ContentType; 1120 - } 1121 - if($encoding == '') { 1122 - $encoding = $this->Encoding; 1123 - } 1124 - $result .= $this->TextLine('--' . $boundary); 1125 - $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet); 1126 - $result .= $this->LE; 1127 - $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding); 1128 - $result .= $this->LE; 1129 - 1130 - return $result; 1131 - } 1132 - 1133 - /** 1134 - * Returns the end of a message boundary. 1135 - * @access private 1136 - */ 1137 - private function EndBoundary($boundary) { 1138 - return $this->LE . '--' . $boundary . '--' . $this->LE; 1139 - } 1140 - 1141 - /** 1142 - * Sets the message type. 1143 - * @access private 1144 - * @return void 1145 - */ 1146 - private function SetMessageType() { 1147 - if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) { 1148 - $this->message_type = 'plain'; 1149 - } else { 1150 - if(count($this->attachment) > 0) { 1151 - $this->message_type = 'attachments'; 1152 - } 1153 - if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) { 1154 - $this->message_type = 'alt'; 1155 - } 1156 - if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) { 1157 - $this->message_type = 'alt_attachments'; 1158 - } 1159 - } 1160 - } 1161 - 1162 - /** 1163 - * Returns a formatted header line. 1164 - * @access public 1165 - * @return string 1166 - */ 1167 - public function HeaderLine($name, $value) { 1168 - return $name . ': ' . $value . $this->LE; 1169 - } 1170 - 1171 - /** 1172 - * Returns a formatted mail line. 1173 - * @access public 1174 - * @return string 1175 - */ 1176 - public function TextLine($value) { 1177 - return $value . $this->LE; 1178 - } 1179 - 1180 - ///////////////////////////////////////////////// 1181 - // CLASS METHODS, ATTACHMENTS 1182 - ///////////////////////////////////////////////// 1183 - 1184 - /** 1185 - * Adds an attachment from a path on the filesystem. 1186 - * Returns false if the file could not be found 1187 - * or accessed. 1188 - * @param string $path Path to the attachment. 1189 - * @param string $name Overrides the attachment name. 1190 - * @param string $encoding File encoding (see $Encoding). 1191 - * @param string $type File extension (MIME) type. 1192 - * @return bool 1193 - */ 1194 - public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { 1195 - try { 1196 - if ( !@is_file($path) ) { 1197 - throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE); 1198 - } 1199 - $filename = basename($path); 1200 - if ( $name == '' ) { 1201 - $name = $filename; 1202 - } 1203 - 1204 - $this->attachment[] = array( 1205 - 0 => $path, 1206 - 1 => $filename, 1207 - 2 => $name, 1208 - 3 => $encoding, 1209 - 4 => $type, 1210 - 5 => false, // isStringAttachment 1211 - 6 => 'attachment', 1212 - 7 => 0 1213 - ); 1214 - 1215 - } catch (phpmailerException $e) { 1216 - $this->SetError($e->getMessage()); 1217 - if ($this->exceptions) { 1218 - throw $e; 1219 - } 1220 - echo $e->getMessage()."\n"; 1221 - if ( $e->getCode() == self::STOP_CRITICAL ) { 1222 - return false; 1223 - } 1224 - } 1225 - return true; 1226 - } 1227 - 1228 - /** 1229 - * Return the current array of attachments 1230 - * @return array 1231 - */ 1232 - public function GetAttachments() { 1233 - return $this->attachment; 1234 - } 1235 - 1236 - /** 1237 - * Attaches all fs, string, and binary attachments to the message. 1238 - * Returns an empty string on failure. 1239 - * @access private 1240 - * @return string 1241 - */ 1242 - private function AttachAll() { 1243 - // Return text of body 1244 - $mime = array(); 1245 - $cidUniq = array(); 1246 - $incl = array(); 1247 - 1248 - // Add all attachments 1249 - foreach ($this->attachment as $attachment) { 1250 - // Check for string attachment 1251 - $bString = $attachment[5]; 1252 - if ($bString) { 1253 - $string = $attachment[0]; 1254 - } else { 1255 - $path = $attachment[0]; 1256 - } 1257 - 1258 - if (in_array($attachment[0], $incl)) { continue; } 1259 - $filename = $attachment[1]; 1260 - $name = $attachment[2]; 1261 - $encoding = $attachment[3]; 1262 - $type = $attachment[4]; 1263 - $disposition = $attachment[6]; 1264 - $cid = $attachment[7]; 1265 - $incl[] = $attachment[0]; 1266 - if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; } 1267 - $cidUniq[$cid] = true; 1268 - 1269 - $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); 1270 - $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE); 1271 - $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); 1272 - 1273 - if($disposition == 'inline') { 1274 - $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); 1275 - } 1276 - 1277 - $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE); 1278 - 1279 - // Encode as string attachment 1280 - if($bString) { 1281 - $mime[] = $this->EncodeString($string, $encoding); 1282 - if($this->IsError()) { 1283 - return ''; 1284 - } 1285 - $mime[] = $this->LE.$this->LE; 1286 - } else { 1287 - $mime[] = $this->EncodeFile($path, $encoding); 1288 - if($this->IsError()) { 1289 - return ''; 1290 - } 1291 - $mime[] = $this->LE.$this->LE; 1292 - } 1293 - } 1294 - 1295 - $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); 1296 - 1297 - return join('', $mime); 1298 - } 1299 - 1300 - /** 1301 - * Encodes attachment in requested format. 1302 - * Returns an empty string on failure. 1303 - * @param string $path The full path to the file 1304 - * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 1305 - * @see EncodeFile() 1306 - * @access private 1307 - * @return string 1308 - */ 1309 - private function EncodeFile($path, $encoding = 'base64') { 1310 - try { 1311 - if (!is_readable($path)) { 1312 - throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); 1313 - } 1314 - if (PHP_VERSION < 6) { 1315 - $magic_quotes = get_magic_quotes_runtime(); 1316 - set_magic_quotes_runtime(0); 1317 - } 1318 - $file_buffer = file_get_contents($path); 1319 - $file_buffer = $this->EncodeString($file_buffer, $encoding); 1320 - if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); } 1321 - return $file_buffer; 1322 - } catch (Exception $e) { 1323 - $this->SetError($e->getMessage()); 1324 - return ''; 1325 - } 1326 - } 1327 - 1328 - /** 1329 - * Encodes string to requested format. 1330 - * Returns an empty string on failure. 1331 - * @param string $str The text to encode 1332 - * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' 1333 - * @access public 1334 - * @return string 1335 - */ 1336 - public function EncodeString ($str, $encoding = 'base64') { 1337 - $encoded = ''; 1338 - switch(strtolower($encoding)) { 1339 - case 'base64': 1340 - $encoded = chunk_split(base64_encode($str), 76, $this->LE); 1341 - break; 1342 - case '7bit': 1343 - case '8bit': 1344 - $encoded = $this->FixEOL($str); 1345 - //Make sure it ends with a line break 1346 - if (substr($encoded, -(strlen($this->LE))) != $this->LE) 1347 - $encoded .= $this->LE; 1348 - break; 1349 - case 'binary': 1350 - $encoded = $str; 1351 - break; 1352 - case 'quoted-printable': 1353 - $encoded = $this->EncodeQP($str); 1354 - break; 1355 - default: 1356 - $this->SetError($this->Lang('encoding') . $encoding); 1357 - break; 1358 - } 1359 - return $encoded; 1360 - } 1361 - 1362 - /** 1363 - * Encode a header string to best (shortest) of Q, B, quoted or none. 1364 - * @access public 1365 - * @return string 1366 - */ 1367 - public function EncodeHeader($str, $position = 'text') { 1368 - $x = 0; 1369 - 1370 - switch (strtolower($position)) { 1371 - case 'phrase': 1372 - if (!preg_match('/[\200-\377]/', $str)) { 1373 - // Can't use addslashes as we don't know what value has magic_quotes_sybase 1374 - $encoded = addcslashes($str, "\0..\37\177\\\""); 1375 - if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { 1376 - return ($encoded); 1377 - } else { 1378 - return ("\"$encoded\""); 1379 - } 1380 - } 1381 - $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); 1382 - break; 1383 - case 'comment': 1384 - $x = preg_match_all('/[()"]/', $str, $matches); 1385 - // Fall-through 1386 - case 'text': 1387 - default: 1388 - $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); 1389 - break; 1390 - } 1391 - 1392 - if ($x == 0) { 1393 - return ($str); 1394 - } 1395 - 1396 - $maxlen = 75 - 7 - strlen($this->CharSet); 1397 - // Try to select the encoding which should produce the shortest output 1398 - if (strlen($str)/3 < $x) { 1399 - $encoding = 'B'; 1400 - if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) { 1401 - // Use a custom function which correctly encodes and wraps long 1402 - // multibyte strings without breaking lines within a character 1403 - $encoded = $this->Base64EncodeWrapMB($str); 1404 - } else { 1405 - $encoded = base64_encode($str); 1406 - $maxlen -= $maxlen % 4; 1407 - $encoded = trim(chunk_split($encoded, $maxlen, "\n")); 1408 - } 1409 - } else { 1410 - $encoding = 'Q'; 1411 - $encoded = $this->EncodeQ($str, $position); 1412 - $encoded = $this->WrapText($encoded, $maxlen, true); 1413 - $encoded = str_replace('='.$this->LE, "\n", trim($encoded)); 1414 - } 1415 - 1416 - $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded); 1417 - $encoded = trim(str_replace("\n", $this->LE, $encoded)); 1418 - 1419 - return $encoded; 1420 - } 1421 - 1422 - /** 1423 - * Checks if a string contains multibyte characters. 1424 - * @access public 1425 - * @param string $str multi-byte text to wrap encode 1426 - * @return bool 1427 - */ 1428 - public function HasMultiBytes($str) { 1429 - if (function_exists('mb_strlen')) { 1430 - return (strlen($str) > mb_strlen($str, $this->CharSet)); 1431 - } else { // Assume no multibytes (we can't handle without mbstring functions anyway) 1432 - return false; 1433 - } 1434 - } 1435 - 1436 - /** 1437 - * Correctly encodes and wraps long multibyte strings for mail headers 1438 - * without breaking lines within a character. 1439 - * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php 1440 - * @access public 1441 - * @param string $str multi-byte text to wrap encode 1442 - * @return string 1443 - */ 1444 - public function Base64EncodeWrapMB($str) { 1445 - $start = "=?".$this->CharSet."?B?"; 1446 - $end = "?="; 1447 - $encoded = ""; 1448 - 1449 - $mb_length = mb_strlen($str, $this->CharSet); 1450 - // Each line must have length <= 75, including $start and $end 1451 - $length = 75 - strlen($start) - strlen($end); 1452 - // Average multi-byte ratio 1453 - $ratio = $mb_length / strlen($str); 1454 - // Base64 has a 4:3 ratio 1455 - $offset = $avgLength = floor($length * $ratio * .75); 1456 - 1457 - for ($i = 0; $i < $mb_length; $i += $offset) { 1458 - $lookBack = 0; 1459 - 1460 - do { 1461 - $offset = $avgLength - $lookBack; 1462 - $chunk = mb_substr($str, $i, $offset, $this->CharSet); 1463 - $chunk = base64_encode($chunk); 1464 - $lookBack++; 1465 - } 1466 - while (strlen($chunk) > $length); 1467 - 1468 - $encoded .= $chunk . $this->LE; 1469 - } 1470 - 1471 - // Chomp the last linefeed 1472 - $encoded = substr($encoded, 0, -strlen($this->LE)); 1473 - return $encoded; 1474 - } 1475 - 1476 - /** 1477 - * Encode string to quoted-printable. 1478 - * Only uses standard PHP, slow, but will always work 1479 - * @access public 1480 - * @param string $string the text to encode 1481 - * @param integer $line_max Number of chars allowed on a line before wrapping 1482 - * @return string 1483 - */ 1484 - public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) { 1485 - $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); 1486 - $lines = preg_split('/(?:\r\n|\r|\n)/', $input); 1487 - $eol = "\r\n"; 1488 - $escape = '='; 1489 - $output = ''; 1490 - foreach ($lines as $line) { 1491 - $linlen = strlen($line); 1492 - $newline = ''; 1493 - for($i = 0; $i < $linlen; $i++) { 1494 - $c = substr( $line, $i, 1 ); 1495 - $dec = ord( $c ); 1496 - if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E 1497 - $c = '=2E'; 1498 - } 1499 - if ( $dec == 32 ) { 1500 - if ( $i == ( $linlen - 1 ) ) { // convert space at eol only 1501 - $c = '=20'; 1502 - } else if ( $space_conv ) { 1503 - $c = '=20'; 1504 - } 1505 - } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required 1506 - $h2 = floor($dec/16); 1507 - $h1 = floor($dec%16); 1508 - $c = $escape.$hex[$h2].$hex[$h1]; 1509 - } 1510 - if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted 1511 - $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay 1512 - $newline = ''; 1513 - // check if newline first character will be point or not 1514 - if ( $dec == 46 ) { 1515 - $c = '=2E'; 1516 - } 1517 - } 1518 - $newline .= $c; 1519 - } // end of for 1520 - $output .= $newline.$eol; 1521 - } // end of foreach 1522 - return $output; 1523 - } 1524 - 1525 - /** 1526 - * Encode string to RFC2045 (6.7) quoted-printable format 1527 - * Uses a PHP5 stream filter to do the encoding about 64x faster than the old version 1528 - * Also results in same content as you started with after decoding 1529 - * @see EncodeQPphp() 1530 - * @access public 1531 - * @param string $string the text to encode 1532 - * @param integer $line_max Number of chars allowed on a line before wrapping 1533 - * @param boolean $space_conv Dummy param for compatibility with existing EncodeQP function 1534 - * @return string 1535 - * @author Marcus Bointon 1536 - */ 1537 - public function EncodeQP($string, $line_max = 76, $space_conv = false) { 1538 - if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) 1539 - return quoted_printable_encode($string); 1540 - } 1541 - $filters = stream_get_filters(); 1542 - if (!in_array('convert.*', $filters)) { //Got convert stream filter? 1543 - return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation 1544 - } 1545 - $fp = fopen('php://temp/', 'r+'); 1546 - $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks 1547 - $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE); 1548 - $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params); 1549 - fputs($fp, $string); 1550 - rewind($fp); 1551 - $out = stream_get_contents($fp); 1552 - stream_filter_remove($s); 1553 - $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange 1554 - fclose($fp); 1555 - return $out; 1556 - } 1557 - 1558 - /** 1559 - * NOTE: Phabricator patch to remove use of "/e". See D2147. 1560 - */ 1561 - private function encodeQCallback(array $matches) { 1562 - return '='.sprintf('%02X', ord($matches[1])); 1563 - } 1564 - 1565 - /** 1566 - * Encode string to q encoding. 1567 - * @link http://tools.ietf.org/html/rfc2047 1568 - * @param string $str the text to encode 1569 - * @param string $position Where the text is going to be used, see the RFC for what that means 1570 - * @access public 1571 - * @return string 1572 - */ 1573 - public function EncodeQ ($str, $position = 'text') { 1574 - 1575 - // NOTE: Phabricator patch to remove use of "/e". See D2147. 1576 - 1577 - // There should not be any EOL in the string 1578 - $encoded = preg_replace('/[\r\n]*/', '', $str); 1579 - 1580 - switch (strtolower($position)) { 1581 - case 'phrase': 1582 - $encoded = preg_replace_callback( 1583 - "/([^A-Za-z0-9!*+\/ -])/", 1584 - array($this, 'encodeQCallback'), 1585 - $encoded); 1586 - break; 1587 - case 'comment': 1588 - $encoded = preg_replace_callback( 1589 - "/([\(\)\"])/", 1590 - array($this, 'encodeQCallback'), 1591 - $encoded); 1592 - break; 1593 - case 'text': 1594 - default: 1595 - // Replace every high ascii, control =, ? and _ characters 1596 - $encoded = preg_replace_callback( 1597 - '/([\000-\011\013\014\016-\037\075\077\137\177-\377])/', 1598 - array($this, 'encodeQCallback'), 1599 - $encoded); 1600 - break; 1601 - } 1602 - 1603 - // Replace every spaces to _ (more readable than =20) 1604 - $encoded = str_replace(' ', '_', $encoded); 1605 - 1606 - return $encoded; 1607 - } 1608 - 1609 - /** 1610 - * Adds a string or binary attachment (non-filesystem) to the list. 1611 - * This method can be used to attach ascii or binary data, 1612 - * such as a BLOB record from a database. 1613 - * @param string $string String attachment data. 1614 - * @param string $filename Name of the attachment. 1615 - * @param string $encoding File encoding (see $Encoding). 1616 - * @param string $type File extension (MIME) type. 1617 - * @return void 1618 - */ 1619 - public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') { 1620 - // Append to $attachment array 1621 - $this->attachment[] = array( 1622 - 0 => $string, 1623 - 1 => $filename, 1624 - 2 => basename($filename), 1625 - 3 => $encoding, 1626 - 4 => $type, 1627 - 5 => true, // isStringAttachment 1628 - 6 => 'attachment', 1629 - 7 => 0 1630 - ); 1631 - } 1632 - 1633 - /** 1634 - * Adds an embedded attachment. This can include images, sounds, and 1635 - * just about any other document. Make sure to set the $type to an 1636 - * image type. For JPEG images use "image/jpeg" and for GIF images 1637 - * use "image/gif". 1638 - * @param string $path Path to the attachment. 1639 - * @param string $cid Content ID of the attachment. Use this to identify 1640 - * the Id for accessing the image in an HTML form. 1641 - * @param string $name Overrides the attachment name. 1642 - * @param string $encoding File encoding (see $Encoding). 1643 - * @param string $type File extension (MIME) type. 1644 - * @return bool 1645 - */ 1646 - public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') { 1647 - 1648 - if ( !@is_file($path) ) { 1649 - $this->SetError($this->Lang('file_access') . $path); 1650 - return false; 1651 - } 1652 - 1653 - $filename = basename($path); 1654 - if ( $name == '' ) { 1655 - $name = $filename; 1656 - } 1657 - 1658 - // Append to $attachment array 1659 - $this->attachment[] = array( 1660 - 0 => $path, 1661 - 1 => $filename, 1662 - 2 => $name, 1663 - 3 => $encoding, 1664 - 4 => $type, 1665 - 5 => false, // isStringAttachment 1666 - 6 => 'inline', 1667 - 7 => $cid 1668 - ); 1669 - 1670 - return true; 1671 - } 1672 - 1673 - /** 1674 - * Returns true if an inline attachment is present. 1675 - * @access public 1676 - * @return bool 1677 - */ 1678 - public function InlineImageExists() { 1679 - foreach($this->attachment as $attachment) { 1680 - if ($attachment[6] == 'inline') { 1681 - return true; 1682 - } 1683 - } 1684 - return false; 1685 - } 1686 - 1687 - ///////////////////////////////////////////////// 1688 - // CLASS METHODS, MESSAGE RESET 1689 - ///////////////////////////////////////////////// 1690 - 1691 - /** 1692 - * Clears all recipients assigned in the TO array. Returns void. 1693 - * @return void 1694 - */ 1695 - public function ClearAddresses() { 1696 - foreach($this->to as $to) { 1697 - unset($this->all_recipients[strtolower($to[0])]); 1698 - } 1699 - $this->to = array(); 1700 - } 1701 - 1702 - /** 1703 - * Clears all recipients assigned in the CC array. Returns void. 1704 - * @return void 1705 - */ 1706 - public function ClearCCs() { 1707 - foreach($this->cc as $cc) { 1708 - unset($this->all_recipients[strtolower($cc[0])]); 1709 - } 1710 - $this->cc = array(); 1711 - } 1712 - 1713 - /** 1714 - * Clears all recipients assigned in the BCC array. Returns void. 1715 - * @return void 1716 - */ 1717 - public function ClearBCCs() { 1718 - foreach($this->bcc as $bcc) { 1719 - unset($this->all_recipients[strtolower($bcc[0])]); 1720 - } 1721 - $this->bcc = array(); 1722 - } 1723 - 1724 - /** 1725 - * Clears all recipients assigned in the ReplyTo array. Returns void. 1726 - * @return void 1727 - */ 1728 - public function ClearReplyTos() { 1729 - $this->ReplyTo = array(); 1730 - } 1731 - 1732 - /** 1733 - * Clears all recipients assigned in the TO, CC and BCC 1734 - * array. Returns void. 1735 - * @return void 1736 - */ 1737 - public function ClearAllRecipients() { 1738 - $this->to = array(); 1739 - $this->cc = array(); 1740 - $this->bcc = array(); 1741 - $this->all_recipients = array(); 1742 - } 1743 - 1744 - /** 1745 - * Clears all previously set filesystem, string, and binary 1746 - * attachments. Returns void. 1747 - * @return void 1748 - */ 1749 - public function ClearAttachments() { 1750 - $this->attachment = array(); 1751 - } 1752 - 1753 - /** 1754 - * Clears all custom headers. Returns void. 1755 - * @return void 1756 - */ 1757 - public function ClearCustomHeaders() { 1758 - $this->CustomHeader = array(); 1759 - } 1760 - 1761 - ///////////////////////////////////////////////// 1762 - // CLASS METHODS, MISCELLANEOUS 1763 - ///////////////////////////////////////////////// 1764 - 1765 - /** 1766 - * Adds the error message to the error container. 1767 - * @access protected 1768 - * @return void 1769 - */ 1770 - protected function SetError($msg) { 1771 - $this->error_count++; 1772 - $this->ErrorInfo = $msg; 1773 - } 1774 - 1775 - /** 1776 - * Returns the proper RFC 822 formatted date. 1777 - * @access public 1778 - * @return string 1779 - * @static 1780 - */ 1781 - public static function RFCDate() { 1782 - $tz = date('Z'); 1783 - $tzs = ($tz < 0) ? '-' : '+'; 1784 - $tz = abs($tz); 1785 - $tz = (int)($tz/3600)*100 + ($tz%3600)/60; 1786 - $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz); 1787 - 1788 - return $result; 1789 - } 1790 - 1791 - /** 1792 - * Returns the server hostname or 'localhost.localdomain' if unknown. 1793 - * @access private 1794 - * @return string 1795 - */ 1796 - private function ServerHostname() { 1797 - if (!empty($this->Hostname)) { 1798 - $result = $this->Hostname; 1799 - } elseif (isset($_SERVER['SERVER_NAME'])) { 1800 - $result = $_SERVER['SERVER_NAME']; 1801 - } else { 1802 - $result = 'localhost.localdomain'; 1803 - } 1804 - 1805 - return $result; 1806 - } 1807 - 1808 - /** 1809 - * Returns a message in the appropriate language. 1810 - * @access private 1811 - * @return string 1812 - */ 1813 - private function Lang($key) { 1814 - if(count($this->language) < 1) { 1815 - $this->SetLanguage('en'); // set the default language 1816 - } 1817 - 1818 - if(isset($this->language[$key])) { 1819 - return $this->language[$key]; 1820 - } else { 1821 - return 'Language string failed to load: ' . $key; 1822 - } 1823 - } 1824 - 1825 - /** 1826 - * Returns true if an error occurred. 1827 - * @access public 1828 - * @return bool 1829 - */ 1830 - public function IsError() { 1831 - return ($this->error_count > 0); 1832 - } 1833 - 1834 - /** 1835 - * Changes every end of line from CR or LF to CRLF. 1836 - * @access private 1837 - * @return string 1838 - */ 1839 - private function FixEOL($str) { 1840 - $str = str_replace("\r\n", "\n", $str); 1841 - $str = str_replace("\r", "\n", $str); 1842 - $str = str_replace("\n", $this->LE, $str); 1843 - return $str; 1844 - } 1845 - 1846 - /** 1847 - * Adds a custom header. 1848 - * @access public 1849 - * @return void 1850 - */ 1851 - public function AddCustomHeader($custom_header) { 1852 - $this->CustomHeader[] = explode(':', $custom_header, 2); 1853 - } 1854 - 1855 - /** 1856 - * Evaluates the message and returns modifications for inline images and backgrounds 1857 - * @access public 1858 - * @return $message 1859 - */ 1860 - public function MsgHTML($message, $basedir = '') { 1861 - preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images); 1862 - if(isset($images[2])) { 1863 - foreach($images[2] as $i => $url) { 1864 - // do not change urls for absolute images (thanks to corvuscorax) 1865 - if (!preg_match('#^[A-z]+://#',$url)) { 1866 - $filename = basename($url); 1867 - $directory = dirname($url); 1868 - ($directory == '.')?$directory='':''; 1869 - $cid = 'cid:' . md5($filename); 1870 - $ext = pathinfo($filename, PATHINFO_EXTENSION); 1871 - $mimeType = self::_mime_types($ext); 1872 - if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; } 1873 - if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; } 1874 - if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) { 1875 - $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message); 1876 - } 1877 - } 1878 - } 1879 - } 1880 - $this->IsHTML(true); 1881 - $this->Body = $message; 1882 - $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message))); 1883 - if (!empty($textMsg) && empty($this->AltBody)) { 1884 - $this->AltBody = html_entity_decode($textMsg); 1885 - } 1886 - if (empty($this->AltBody)) { 1887 - $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; 1888 - } 1889 - } 1890 - 1891 - /** 1892 - * Gets the MIME type of the embedded or inline image 1893 - * @param string File extension 1894 - * @access public 1895 - * @return string MIME type of ext 1896 - * @static 1897 - */ 1898 - public static function _mime_types($ext = '') { 1899 - $mimes = array( 1900 - 'hqx' => 'application/mac-binhex40', 1901 - 'cpt' => 'application/mac-compactpro', 1902 - 'doc' => 'application/msword', 1903 - 'bin' => 'application/macbinary', 1904 - 'dms' => 'application/octet-stream', 1905 - 'lha' => 'application/octet-stream', 1906 - 'lzh' => 'application/octet-stream', 1907 - 'exe' => 'application/octet-stream', 1908 - 'class' => 'application/octet-stream', 1909 - 'psd' => 'application/octet-stream', 1910 - 'so' => 'application/octet-stream', 1911 - 'sea' => 'application/octet-stream', 1912 - 'dll' => 'application/octet-stream', 1913 - 'oda' => 'application/oda', 1914 - 'pdf' => 'application/pdf', 1915 - 'ai' => 'application/postscript', 1916 - 'eps' => 'application/postscript', 1917 - 'ps' => 'application/postscript', 1918 - 'smi' => 'application/smil', 1919 - 'smil' => 'application/smil', 1920 - 'mif' => 'application/vnd.mif', 1921 - 'xls' => 'application/vnd.ms-excel', 1922 - 'ppt' => 'application/vnd.ms-powerpoint', 1923 - 'wbxml' => 'application/vnd.wap.wbxml', 1924 - 'wmlc' => 'application/vnd.wap.wmlc', 1925 - 'dcr' => 'application/x-director', 1926 - 'dir' => 'application/x-director', 1927 - 'dxr' => 'application/x-director', 1928 - 'dvi' => 'application/x-dvi', 1929 - 'gtar' => 'application/x-gtar', 1930 - 'php' => 'application/x-httpd-php', 1931 - 'php4' => 'application/x-httpd-php', 1932 - 'php3' => 'application/x-httpd-php', 1933 - 'phtml' => 'application/x-httpd-php', 1934 - 'phps' => 'application/x-httpd-php-source', 1935 - 'js' => 'application/x-javascript', 1936 - 'swf' => 'application/x-shockwave-flash', 1937 - 'sit' => 'application/x-stuffit', 1938 - 'tar' => 'application/x-tar', 1939 - 'tgz' => 'application/x-tar', 1940 - 'xhtml' => 'application/xhtml+xml', 1941 - 'xht' => 'application/xhtml+xml', 1942 - 'zip' => 'application/zip', 1943 - 'mid' => 'audio/midi', 1944 - 'midi' => 'audio/midi', 1945 - 'mpga' => 'audio/mpeg', 1946 - 'mp2' => 'audio/mpeg', 1947 - 'mp3' => 'audio/mpeg', 1948 - 'aif' => 'audio/x-aiff', 1949 - 'aiff' => 'audio/x-aiff', 1950 - 'aifc' => 'audio/x-aiff', 1951 - 'ram' => 'audio/x-pn-realaudio', 1952 - 'rm' => 'audio/x-pn-realaudio', 1953 - 'rpm' => 'audio/x-pn-realaudio-plugin', 1954 - 'ra' => 'audio/x-realaudio', 1955 - 'rv' => 'video/vnd.rn-realvideo', 1956 - 'wav' => 'audio/x-wav', 1957 - 'bmp' => 'image/bmp', 1958 - 'gif' => 'image/gif', 1959 - 'jpeg' => 'image/jpeg', 1960 - 'jpg' => 'image/jpeg', 1961 - 'jpe' => 'image/jpeg', 1962 - 'png' => 'image/png', 1963 - 'tiff' => 'image/tiff', 1964 - 'tif' => 'image/tiff', 1965 - 'css' => 'text/css', 1966 - 'html' => 'text/html', 1967 - 'htm' => 'text/html', 1968 - 'shtml' => 'text/html', 1969 - 'txt' => 'text/plain', 1970 - 'text' => 'text/plain', 1971 - 'log' => 'text/plain', 1972 - 'rtx' => 'text/richtext', 1973 - 'rtf' => 'text/rtf', 1974 - 'xml' => 'text/xml', 1975 - 'xsl' => 'text/xml', 1976 - 'mpeg' => 'video/mpeg', 1977 - 'mpg' => 'video/mpeg', 1978 - 'mpe' => 'video/mpeg', 1979 - 'qt' => 'video/quicktime', 1980 - 'mov' => 'video/quicktime', 1981 - 'avi' => 'video/x-msvideo', 1982 - 'movie' => 'video/x-sgi-movie', 1983 - 'word' => 'application/msword', 1984 - 'xl' => 'application/excel', 1985 - 'eml' => 'message/rfc822' 1986 - ); 1987 - return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)]; 1988 - } 1989 - 1990 - /** 1991 - * Set (or reset) Class Objects (variables) 1992 - * 1993 - * Usage Example: 1994 - * $page->set('X-Priority', '3'); 1995 - * 1996 - * @access public 1997 - * @param string $name Parameter Name 1998 - * @param mixed $value Parameter Value 1999 - * NOTE: will not work with arrays, there are no arrays to set/reset 2000 - * @todo Should this not be using __set() magic function? 2001 - */ 2002 - public function set($name, $value = '') { 2003 - try { 2004 - if (isset($this->$name) ) { 2005 - $this->$name = $value; 2006 - } else { 2007 - throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL); 2008 - } 2009 - } catch (Exception $e) { 2010 - $this->SetError($e->getMessage()); 2011 - if ($e->getCode() == self::STOP_CRITICAL) { 2012 - return false; 2013 - } 2014 - } 2015 - return true; 2016 - } 2017 - 2018 - /** 2019 - * Strips newlines to prevent header injection. 2020 - * @access public 2021 - * @param string $str String 2022 - * @return string 2023 - */ 2024 - public function SecureHeader($str) { 2025 - $str = str_replace("\r", '', $str); 2026 - $str = str_replace("\n", '', $str); 2027 - return trim($str); 2028 - } 2029 - 2030 - /** 2031 - * Set the private key file and password to sign the message. 2032 - * 2033 - * @access public 2034 - * @param string $key_filename Parameter File Name 2035 - * @param string $key_pass Password for private key 2036 - */ 2037 - public function Sign($cert_filename, $key_filename, $key_pass) { 2038 - $this->sign_cert_file = $cert_filename; 2039 - $this->sign_key_file = $key_filename; 2040 - $this->sign_key_pass = $key_pass; 2041 - } 2042 - 2043 - /** 2044 - * Set the private key file and password to sign the message. 2045 - * 2046 - * @access public 2047 - * @param string $key_filename Parameter File Name 2048 - * @param string $key_pass Password for private key 2049 - */ 2050 - public function DKIM_QP($txt) { 2051 - $line=""; 2052 - for ($i=0;$i<strlen($txt);$i++) { 2053 - $ord=ord($txt[$i]); 2054 - if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) { 2055 - $line.=$txt[$i]; 2056 - } else { 2057 - $line.="=".sprintf("%02X",$ord); 2058 - } 2059 - } 2060 - return $line; 2061 - } 2062 - 2063 - /** 2064 - * Generate DKIM signature 2065 - * 2066 - * @access public 2067 - * @param string $s Header 2068 - */ 2069 - public function DKIM_Sign($s) { 2070 - $privKeyStr = file_get_contents($this->DKIM_private); 2071 - if ($this->DKIM_passphrase!='') { 2072 - $privKey = openssl_pkey_get_private($privKeyStr,$this->DKIM_passphrase); 2073 - } else { 2074 - $privKey = $privKeyStr; 2075 - } 2076 - if (openssl_sign($s, $signature, $privKey)) { 2077 - return base64_encode($signature); 2078 - } 2079 - } 2080 - 2081 - /** 2082 - * Generate DKIM Canonicalization Header 2083 - * 2084 - * @access public 2085 - * @param string $s Header 2086 - */ 2087 - public function DKIM_HeaderC($s) { 2088 - $s=preg_replace("/\r\n\s+/"," ",$s); 2089 - $lines=explode("\r\n",$s); 2090 - foreach ($lines as $key=>$line) { 2091 - list($heading,$value)=explode(":",$line,2); 2092 - $heading=strtolower($heading); 2093 - $value=preg_replace("/\s+/"," ",$value) ; // Compress useless spaces 2094 - $lines[$key]=$heading.":".trim($value) ; // Don't forget to remove WSP around the value 2095 - } 2096 - $s=implode("\r\n",$lines); 2097 - return $s; 2098 - } 2099 - 2100 - /** 2101 - * Generate DKIM Canonicalization Body 2102 - * 2103 - * @access public 2104 - * @param string $body Message Body 2105 - */ 2106 - public function DKIM_BodyC($body) { 2107 - if ($body == '') return "\r\n"; 2108 - // stabilize line endings 2109 - $body=str_replace("\r\n","\n",$body); 2110 - $body=str_replace("\n","\r\n",$body); 2111 - // END stabilize line endings 2112 - while (substr($body,strlen($body)-4,4) == "\r\n\r\n") { 2113 - $body=substr($body,0,strlen($body)-2); 2114 - } 2115 - return $body; 2116 - } 2117 - 2118 - /** 2119 - * Create the DKIM header, body, as new header 2120 - * 2121 - * @access public 2122 - * @param string $headers_line Header lines 2123 - * @param string $subject Subject 2124 - * @param string $body Body 2125 - */ 2126 - public function DKIM_Add($headers_line,$subject,$body) { 2127 - $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms 2128 - $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body 2129 - $DKIMquery = 'dns/txt'; // Query method 2130 - $DKIMtime = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) 2131 - $subject_header = "Subject: $subject"; 2132 - $headers = explode("\r\n",$headers_line); 2133 - foreach($headers as $header) { 2134 - if (strpos($header,'From:') === 0) { 2135 - $from_header=$header; 2136 - } elseif (strpos($header,'To:') === 0) { 2137 - $to_header=$header; 2138 - } 2139 - } 2140 - $from = str_replace('|','=7C',$this->DKIM_QP($from_header)); 2141 - $to = str_replace('|','=7C',$this->DKIM_QP($to_header)); 2142 - $subject = str_replace('|','=7C',$this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable 2143 - $body = $this->DKIM_BodyC($body); 2144 - $DKIMlen = strlen($body) ; // Length of body 2145 - $DKIMb64 = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body 2146 - $ident = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";"; 2147 - $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n". 2148 - "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n". 2149 - "\th=From:To:Subject;\r\n". 2150 - "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n". 2151 - "\tz=$from\r\n". 2152 - "\t|$to\r\n". 2153 - "\t|$subject;\r\n". 2154 - "\tbh=" . $DKIMb64 . ";\r\n". 2155 - "\tb="; 2156 - $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); 2157 - $signed = $this->DKIM_Sign($toSign); 2158 - return "X-PHPMAILER-DKIM: phpmailer.sourceforge.net\r\n".$dkimhdrs.$signed."\r\n"; 2159 - } 2160 - 2161 - protected function doCallback($isSent,$to,$cc,$bcc,$subject,$body) { 2162 - if (!empty($this->action_function) && function_exists($this->action_function)) { 2163 - $params = array($isSent,$to,$cc,$bcc,$subject,$body); 2164 - call_user_func_array($this->action_function,$params); 2165 - } 2166 - } 2167 - } 2168 - 2169 - class phpmailerException extends Exception { 2170 - public function errorMessage() { 2171 - $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n"; 2172 - return $errorMsg; 2173 - } 2174 - } 2175 - ?>
+121 -8
externals/phpmailer/class.phpmailer.php
··· 42 42 43 43 class PHPMailer { 44 44 45 + // PHORGE CODE BEGIN 46 + public static function newFromMessage( 47 + PhabricatorMailExternalMessage $message) { 48 + 49 + $mailer = new self($use_exceptions = true); 50 + 51 + // By default, PHPMailerLite sends one mail per recipient. We handle 52 + // combining or separating To and Cc higher in the stack, so tell it to 53 + // send mail exactly like we ask. 54 + $mailer->SingleTo = false; 55 + 56 + $mailer->CharSet = 'utf-8'; 57 + $mailer->Encoding = 'base64'; 58 + 59 + $subject = $message->getSubject(); 60 + if ($subject !== null) { 61 + $mailer->Subject = $subject; 62 + } 63 + 64 + $from_address = $message->getFromAddress(); 65 + if ($from_address) { 66 + $mailer->SetFrom( 67 + $from_address->getAddress(), 68 + (string)$from_address->getDisplayName(), 69 + $crazy_side_effects = false); 70 + } 71 + 72 + $reply_address = $message->getReplyToAddress(); 73 + if ($reply_address) { 74 + $mailer->AddReplyTo( 75 + $reply_address->getAddress(), 76 + (string)$reply_address->getDisplayName()); 77 + } 78 + 79 + $to_addresses = $message->getToAddresses(); 80 + if ($to_addresses) { 81 + foreach ($to_addresses as $address) { 82 + $mailer->AddAddress( 83 + $address->getAddress(), 84 + (string)$address->getDisplayName()); 85 + } 86 + } 87 + 88 + $cc_addresses = $message->getCCAddresses(); 89 + if ($cc_addresses) { 90 + foreach ($cc_addresses as $address) { 91 + $mailer->AddCC( 92 + $address->getAddress(), 93 + (string)$address->getDisplayName()); 94 + } 95 + } 96 + 97 + $headers = $message->getHeaders(); 98 + if ($headers) { 99 + foreach ($headers as $header) { 100 + $name = $header->getName(); 101 + $value = $header->getValue(); 102 + 103 + if (phutil_utf8_strtolower($name) === 'message-id') { 104 + $mailer->MessageID = $value; 105 + } else { 106 + $mailer->AddCustomHeader("{$name}: {$value}"); 107 + } 108 + } 109 + } 110 + 111 + $attachments = $message->getAttachments(); 112 + if ($attachments) { 113 + foreach ($attachments as $attachment) { 114 + $mailer->AddStringAttachment( 115 + $attachment->getData(), 116 + $attachment->getFilename(), 117 + 'base64', 118 + $attachment->getMimeType()); 119 + } 120 + } 121 + 122 + $text_body = $message->getTextBody(); 123 + if ($text_body !== null) { 124 + $mailer->Body = $text_body; 125 + } 126 + 127 + $html_body = $message->getHTMLBody(); 128 + if ($html_body !== null) { 129 + $mailer->IsHTML(true); 130 + $mailer->Body = $html_body; 131 + if ($text_body !== null) { 132 + $mailer->AltBody = $text_body; 133 + } 134 + } 135 + 136 + return $mailer; 137 + } 138 + // PHORGE CODE END 139 + 140 + 45 141 ///////////////////////////////////////////////// 46 142 // PROPERTIES, PUBLIC 47 143 ///////////////////////////////////////////////// ··· 242 338 * emails, instead of sending to entire TO addresses 243 339 * @var bool 244 340 */ 245 - public $SingleTo = false; 341 + public $SingleTo = true; 246 342 247 343 /** 248 344 * If SingleTo is true, this provides the array to hold the email addresses ··· 250 346 */ 251 347 public $SingleToArray = array(); 252 348 253 - /** 349 + /** 254 350 * Provides the ability to change the line ending 255 351 * @var string 256 352 */ ··· 275 371 * @var string 276 372 */ 277 373 public $DKIM_domain = ''; 374 + 375 + /** 376 + * Used with DKIM Digital Signing process 377 + * optional 378 + * @var string 379 + */ 380 + public $DKIM_passphrase = ''; 278 381 279 382 /** 280 383 * Used with DKIM DNS Resource Record ··· 451 554 */ 452 555 private function AddAnAddress($kind, $address, $name = '') { 453 556 if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) { 454 - echo 'Invalid recipient array: ' . kind; 557 + echo 'Invalid recipient array: ' . $kind; 455 558 return false; 456 559 } 457 560 $address = trim($address); ··· 570 673 571 674 // Choose the mailer and send through it 572 675 switch($this->Mailer) { 676 + case 'amazon-ses': 677 + return $this->customMailer->executeSend( 678 + $header. 679 + $body); 573 680 case 'sendmail': 574 681 return $this->SendmailSend($header, $body); 575 682 case 'smtp': ··· 601 708 } else { 602 709 $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); 603 710 } 604 - 605 711 if ($this->SingleTo === true) { 606 712 foreach ($this->SingleToArray as $key => $val) { 607 713 $mail = new ExecFuture('%C', $sendmail); ··· 614 720 $mail->write($header.$body); 615 721 $mail->resolvex(); 616 722 } 617 - 618 723 return true; 619 724 } 620 725 ··· 887 992 $addr_str .= implode(', ', $addresses); 888 993 $addr_str .= $this->LE; 889 994 995 + // NOTE: This is a narrow hack to fix an issue with 1000+ characters of 996 + // recipients, described in https://secure.phabricator.com/T12372. 997 + $addr_str = wordwrap($addr_str, 75, "\n "); 998 + 890 999 return $addr_str; 891 1000 } 892 1001 ··· 1110 1219 1111 1220 if($this->MessageID != '') { 1112 1221 $result .= $this->HeaderLine('Message-ID',$this->MessageID); 1222 + } else { 1223 + $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE); 1113 1224 } 1114 1225 $result .= $this->HeaderLine('X-Priority', $this->Priority); 1115 1226 $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.sourceforge.net)'); ··· 1688 1799 } 1689 1800 1690 1801 /** 1691 - * NOTE: Phabricator patch to remove use of "/e". See D2147. 1802 + * NOTE: Phorge patch to remove use of "/e". 1803 + * See https://we.phorge.it/rPf7b569e5d9b747141ee081729d3ac0270b18b13a 1692 1804 */ 1693 1805 private function encodeQCallback(array $matches) { 1694 1806 return '='.sprintf('%02X', ord($matches[1])); ··· 1704 1816 */ 1705 1817 public function EncodeQ ($str, $position = 'text') { 1706 1818 1707 - // NOTE: Phabricator patch to remove use of "/e". See D2147. 1819 + // NOTE: Phorge patch to remove use of "/e". 1820 + // See https://we.phorge.it/rPf7b569e5d9b747141ee081729d3ac0270b18b13a 1708 1821 1709 1822 // There should not be any EOL in the string 1710 1823 $encoded = preg_replace('/[\r\n]*/', '', $str); ··· 2293 2406 "\tb="; 2294 2407 $toSign = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs); 2295 2408 $signed = $this->DKIM_Sign($toSign); 2296 - return "X-PHPMAILER-DKIM: phpmailer.worxware.com\r\n".$dkimhdrs.$signed."\r\n"; 2409 + return "X-PHPMAILER-DKIM: phpmailer.sourceforge.net\r\n".$dkimhdrs.$signed."\r\n"; 2297 2410 } 2298 2411 2299 2412 protected function doCallback($isSent,$to,$cc,$bcc,$subject,$body) {
+3 -3
src/applications/config/option/PhabricatorMetaMTAConfigOptions.php
··· 133 133 134 134 $adapter_description = $this->deformat(pht(<<<EODOC 135 135 Adapter class to use to transmit mail to the MTA. The default uses 136 - PHPMailerLite, which will invoke "sendmail". This is appropriate if sendmail 137 - actually works on your host, but if you haven't configured mail it may not be so 138 - great. A number of other mailers are available (e.g., SES, SendGrid, SMTP, 136 + PHPMailer, which will invoke "mail". This is appropriate if mail actually 137 + works on your host, but if you haven't configured mail it may not be so great. 138 + A number of other mailers are available (e.g., SES, SendGrid, SMTP, Sendmail, 139 139 custom mailers). This option is deprecated in favor of 'cluster.mailers'. 140 140 EODOC 141 141 ));
+3 -3
src/applications/metamta/adapter/PhabricatorMailAmazonSESAdapter.php
··· 32 32 } 33 33 34 34 /** 35 - * @phutil-external-symbol class PHPMailerLite 35 + * @phutil-external-symbol class PHPMailer 36 36 */ 37 37 public function sendMessage(PhabricatorMailExternalMessage $message) { 38 38 $root = phutil_get_library_root('phabricator'); 39 39 $root = dirname($root); 40 - require_once $root.'/externals/phpmailer/class.phpmailer-lite.php'; 40 + require_once $root.'/externals/phpmailer/class.phpmailer.php'; 41 41 42 - $mailer = PHPMailerLite::newFromMessage($message); 42 + $mailer = PHPMailer::newFromMessage($message); 43 43 44 44 $mailer->Mailer = 'amazon-ses'; 45 45 $mailer->customMailer = $this;
+5 -3
src/applications/metamta/adapter/PhabricatorMailSendmailAdapter.php
··· 32 32 } 33 33 34 34 /** 35 - * @phutil-external-symbol class PHPMailerLite 35 + * @phutil-external-symbol class PHPMailer 36 36 */ 37 37 public function sendMessage(PhabricatorMailExternalMessage $message) { 38 38 $root = phutil_get_library_root('phabricator'); 39 39 $root = dirname($root); 40 - require_once $root.'/externals/phpmailer/class.phpmailer-lite.php'; 40 + require_once $root.'/externals/phpmailer/class.phpmailer.php'; 41 41 42 - $mailer = PHPMailerLite::newFromMessage($message); 42 + $mailer = PHPMailer::newFromMessage($message); 43 + 44 + $mailer->IsSendmail(); 43 45 $mailer->Send(); 44 46 } 45 47