@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.

Support SMTP as the mailer.

Summary:
Support SMTP as the mailer and user could turn on SMTP authentication if needed.
Import PHPMailer as PHPMailerLite doesn't support SMTP.

Make class PhabricatorMailImplementationPHPMailerAdapter final.

Test Plan: N/A

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T2139

Differential Revision: https://secure.phabricator.com/D4063

authored by

Hangjun Ye and committed by
epriestley
f5c2a2ab 433a7ccd

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