Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/phpmailer/class.phpmailer.php
Go to the documentation of this file.
00001 <?php
00002 /*~ class.phpmailer.php
00003 .---------------------------------------------------------------------------.
00004 |  Software: PHPMailer - PHP email class                                    |
00005 |   Version: 5.1                                                            |
00006 |   Contact: via sourceforge.net support pages (also www.worxware.com)      |
00007 |      Info: http://phpmailer.sourceforge.net                               |
00008 |   Support: http://sourceforge.net/projects/phpmailer/                     |
00009 | ------------------------------------------------------------------------- |
00010 |     Admin: Andy Prevost (project admininistrator)                         |
00011 |   Authors: Andy Prevost (codeworxtech) codeworxtech@users.sourceforge.net |
00012 |          : Marcus Bointon (coolbru) coolbru@users.sourceforge.net         |
00013 |   Founder: Brent R. Matzelle (original founder)                           |
00014 | Copyright (c) 2004-2009, Andy Prevost. All Rights Reserved.               |
00015 | Copyright (c) 2001-2003, Brent R. Matzelle                                |
00016 | ------------------------------------------------------------------------- |
00017 |   License: Distributed under the Lesser General Public License (LGPL)     |
00018 |            http://www.gnu.org/copyleft/lesser.html                        |
00019 | This program is distributed in the hope that it will be useful - WITHOUT  |
00020 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
00021 | FITNESS FOR A PARTICULAR PURPOSE.                                         |
00022 | ------------------------------------------------------------------------- |
00023 | We offer a number of paid services (www.worxware.com):                    |
00024 | - Web Hosting on highly optimized fast and secure servers                 |
00025 | - Technology Consulting                                                   |
00026 | - Oursourcing (highly qualified programmers and graphic designers)        |
00027 '---------------------------------------------------------------------------'
00028 */
00029 
00041 if (version_compare(PHP_VERSION, '5.0.0', '<') ) exit("Sorry, this version of PHPMailer will only run on PHP version 5 or greater!\n");
00042 
00043 class PHPMailer {
00044 
00046   // PROPERTIES, PUBLIC
00048 
00053   public $Priority          = 3;
00054 
00059   public $CharSet           = 'iso-8859-1';
00060 
00065   public $ContentType       = 'text/plain';
00066 
00072   public $Encoding          = '8bit';
00073 
00078   public $ErrorInfo         = '';
00079 
00084   public $From              = 'root@localhost';
00085 
00090   public $FromName          = 'Root User';
00091 
00097   public $Sender            = '';
00098 
00103   public $Subject           = '';
00104 
00110   public $Body              = '';
00111 
00119   public $AltBody           = '';
00120 
00126   public $WordWrap          = 0;
00127 
00132   public $Mailer            = 'mail';
00133 
00138   public $Sendmail          = '/usr/sbin/sendmail';
00139 
00145   public $PluginDir         = '';
00146 
00151   public $ConfirmReadingTo  = '';
00152 
00159   public $Hostname          = '';
00160 
00166   public $MessageID         = '';
00167 
00169   // PROPERTIES FOR SMTP
00171 
00180   public $Host          = 'localhost';
00181 
00186   public $Port          = 25;
00187 
00192   public $Helo          = '';
00193 
00199   public $SMTPSecure    = '';
00200 
00205   public $SMTPAuth      = false;
00206 
00211   public $Username      = '';
00212 
00217   public $Password      = '';
00218 
00224   public $Timeout       = 10;
00225 
00230   public $SMTPDebug     = false;
00231 
00238   public $SMTPKeepAlive = false;
00239 
00245   public $SingleTo      = false;
00246 
00251   public $SingleToArray = array();
00252 
00257   public $LE              = "\n";
00258 
00263   public $DKIM_selector   = 'phpmailer';
00264 
00270   public $DKIM_identity   = '';
00271 
00277   public $DKIM_domain     = '';
00278 
00284   public $DKIM_private    = '';
00285 
00297   public $action_function = ''; //'callbackAction';
00298 
00303   public $Version         = '5.1';
00304 
00306   // PROPERTIES, PRIVATE AND PROTECTED
00308 
00309   private   $smtp           = NULL;
00310   private   $to             = array();
00311   private   $cc             = array();
00312   private   $bcc            = array();
00313   private   $ReplyTo        = array();
00314   private   $all_recipients = array();
00315   private   $attachment     = array();
00316   private   $CustomHeader   = array();
00317   private   $message_type   = '';
00318   private   $boundary       = array();
00319   protected $language       = array();
00320   private   $error_count    = 0;
00321   private   $sign_cert_file = "";
00322   private   $sign_key_file  = "";
00323   private   $sign_key_pass  = "";
00324   private   $exceptions     = false;
00325 
00327   // CONSTANTS
00329 
00330   const STOP_MESSAGE  = 0; // message only, continue processing
00331   const STOP_CONTINUE = 1; // message?, likely ok to continue processing
00332   const STOP_CRITICAL = 2; // message, plus full stop, critical error reached
00333 
00335   // METHODS, VARIABLES
00337 
00342   public function __construct($exceptions = false) {
00343     $this->exceptions = ($exceptions == true);
00344   }
00345 
00351   public function IsHTML($ishtml = true) {
00352     if ($ishtml) {
00353       $this->ContentType = 'text/html';
00354     } else {
00355       $this->ContentType = 'text/plain';
00356     }
00357   }
00358 
00363   public function IsSMTP() {
00364     $this->Mailer = 'smtp';
00365   }
00366 
00371   public function IsMail() {
00372     $this->Mailer = 'mail';
00373   }
00374 
00379   public function IsSendmail() {
00380     if (!stristr(ini_get('sendmail_path'), 'sendmail')) {
00381       $this->Sendmail = '/var/qmail/bin/sendmail';
00382     }
00383     $this->Mailer = 'sendmail';
00384   }
00385 
00390   public function IsQmail() {
00391     if (stristr(ini_get('sendmail_path'), 'qmail')) {
00392       $this->Sendmail = '/var/qmail/bin/sendmail';
00393     }
00394     $this->Mailer = 'sendmail';
00395   }
00396 
00398   // METHODS, RECIPIENTS
00400 
00407   public function AddAddress($address, $name = '') {
00408     return $this->AddAnAddress('to', $address, $name);
00409   }
00410 
00418   public function AddCC($address, $name = '') {
00419     return $this->AddAnAddress('cc', $address, $name);
00420   }
00421 
00429   public function AddBCC($address, $name = '') {
00430     return $this->AddAnAddress('bcc', $address, $name);
00431   }
00432 
00439   public function AddReplyTo($address, $name = '') {
00440     return $this->AddAnAddress('ReplyTo', $address, $name);
00441   }
00442 
00452   private function AddAnAddress($kind, $address, $name = '') {
00453     if (!preg_match('/^(to|cc|bcc|ReplyTo)$/', $kind)) {
00454       echo 'Invalid recipient array: ' . kind;
00455       return false;
00456     }
00457     $address = trim($address);
00458     $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
00459     if (!self::ValidateAddress($address)) {
00460       $this->SetError($this->Lang('invalid_address').': '. $address);
00461       if ($this->exceptions) {
00462         throw new phpmailerException($this->Lang('invalid_address').': '.$address);
00463       }
00464       echo $this->Lang('invalid_address').': '.$address;
00465       return false;
00466     }
00467     if ($kind != 'ReplyTo') {
00468       if (!isset($this->all_recipients[strtolower($address)])) {
00469         array_push($this->$kind, array($address, $name));
00470         $this->all_recipients[strtolower($address)] = true;
00471         return true;
00472       }
00473     } else {
00474       if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
00475         $this->ReplyTo[strtolower($address)] = array($address, $name);
00476       return true;
00477     }
00478   }
00479   return false;
00480 }
00481 
00488   public function SetFrom($address, $name = '',$auto=1) {
00489     $address = trim($address);
00490     $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
00491     if (!self::ValidateAddress($address)) {
00492       $this->SetError($this->Lang('invalid_address').': '. $address);
00493       if ($this->exceptions) {
00494         throw new phpmailerException($this->Lang('invalid_address').': '.$address);
00495       }
00496       echo $this->Lang('invalid_address').': '.$address;
00497       return false;
00498     }
00499     $this->From = $address;
00500     $this->FromName = $name;
00501     if ($auto) {
00502       if (empty($this->ReplyTo)) {
00503         $this->AddAnAddress('ReplyTo', $address, $name);
00504       }
00505       if (empty($this->Sender)) {
00506         $this->Sender = $address;
00507       }
00508     }
00509     return true;
00510   }
00511 
00523   public static function ValidateAddress($address) {
00524     if (function_exists('filter_var')) { //Introduced in PHP 5.2
00525       if(filter_var($address, FILTER_VALIDATE_EMAIL) === FALSE) {
00526         return false;
00527       } else {
00528         return true;
00529       }
00530     } else {
00531       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);
00532     }
00533   }
00534 
00536   // METHODS, MAIL SENDING
00538 
00545   public function Send() {
00546     try {
00547       if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
00548         throw new phpmailerException($this->Lang('provide_address'), self::STOP_CRITICAL);
00549       }
00550 
00551       // Set whether the message is multipart/alternative
00552       if(!empty($this->AltBody)) {
00553         $this->ContentType = 'multipart/alternative';
00554       }
00555 
00556       $this->error_count = 0; // reset errors
00557       $this->SetMessageType();
00558       $header = $this->CreateHeader();
00559       $body = $this->CreateBody();
00560 
00561       if (empty($this->Body)) {
00562         throw new phpmailerException($this->Lang('empty_message'), self::STOP_CRITICAL);
00563       }
00564 
00565       // digitally sign with DKIM if enabled
00566       if ($this->DKIM_domain && $this->DKIM_private) {
00567         $header_dkim = $this->DKIM_Add($header,$this->Subject,$body);
00568         $header = str_replace("\r\n","\n",$header_dkim) . $header;
00569       }
00570 
00571       // Choose the mailer and send through it
00572       switch($this->Mailer) {
00573         case 'sendmail':
00574           return $this->SendmailSend($header, $body);
00575         case 'smtp':
00576           return $this->SmtpSend($header, $body);
00577         default:
00578           return $this->MailSend($header, $body);
00579       }
00580 
00581     } catch (phpmailerException $e) {
00582       $this->SetError($e->getMessage());
00583       if ($this->exceptions) {
00584         throw $e;
00585       }
00586       echo $e->getMessage()."\n";
00587       return false;
00588     }
00589   }
00590 
00598   protected function SendmailSend($header, $body) {
00599     if ($this->Sender != '') {
00600       $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
00601     } else {
00602       $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
00603     }
00604     if ($this->SingleTo === true) {
00605       foreach ($this->SingleToArray as $key => $val) {
00606         if(!@$mail = popen($sendmail, 'w')) {
00607           throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
00608         }
00609         fputs($mail, "To: " . $val . "\n");
00610         fputs($mail, $header);
00611         fputs($mail, $body);
00612         $result = pclose($mail);
00613         // implement call back function if it exists
00614         $isSent = ($result == 0) ? 1 : 0;
00615         $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);
00616         if($result != 0) {
00617           throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
00618         }
00619       }
00620     } else {
00621       if(!@$mail = popen($sendmail, 'w')) {
00622         throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
00623       }
00624       fputs($mail, $header);
00625       fputs($mail, $body);
00626       $result = pclose($mail);
00627       // implement call back function if it exists
00628       $isSent = ($result == 0) ? 1 : 0;
00629       $this->doCallback($isSent,$this->to,$this->cc,$this->bcc,$this->Subject,$body);
00630       if($result != 0) {
00631         throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
00632       }
00633     }
00634     return true;
00635   }
00636 
00644   protected function MailSend($header, $body) {
00645     $toArr = array();
00646     foreach($this->to as $t) {
00647       $toArr[] = $this->AddrFormat($t);
00648     }
00649     $to = implode(', ', $toArr);
00650 
00651     $params = sprintf("-oi -f %s", $this->Sender);
00652     if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) {
00653       $old_from = ini_get('sendmail_from');
00654       ini_set('sendmail_from', $this->Sender);
00655       if ($this->SingleTo === true && count($toArr) > 1) {
00656         foreach ($toArr as $key => $val) {
00657           $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
00658           // implement call back function if it exists
00659           $isSent = ($rt == 1) ? 1 : 0;
00660           $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);
00661         }
00662       } else {
00663         $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
00664         // implement call back function if it exists
00665         $isSent = ($rt == 1) ? 1 : 0;
00666         $this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body);
00667       }
00668     } else {
00669       if ($this->SingleTo === true && count($toArr) > 1) {
00670         foreach ($toArr as $key => $val) {
00671           $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
00672           // implement call back function if it exists
00673           $isSent = ($rt == 1) ? 1 : 0;
00674           $this->doCallback($isSent,$val,$this->cc,$this->bcc,$this->Subject,$body);
00675         }
00676       } else {
00677         $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);
00678         // implement call back function if it exists
00679         $isSent = ($rt == 1) ? 1 : 0;
00680         $this->doCallback($isSent,$to,$this->cc,$this->bcc,$this->Subject,$body);
00681       }
00682     }
00683     if (isset($old_from)) {
00684       ini_set('sendmail_from', $old_from);
00685     }
00686     if(!$rt) {
00687       throw new phpmailerException($this->Lang('instantiate'), self::STOP_CRITICAL);
00688     }
00689     return true;
00690   }
00691 
00701   protected function SmtpSend($header, $body) {
00702     require_once $this->PluginDir . 'class.smtp.php';
00703     $bad_rcpt = array();
00704 
00705     if(!$this->SmtpConnect()) {
00706       throw new phpmailerException($this->Lang('smtp_connect_failed'), self::STOP_CRITICAL);
00707     }
00708     $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
00709     if(!$this->smtp->Mail($smtp_from)) {
00710       throw new phpmailerException($this->Lang('from_failed') . $smtp_from, self::STOP_CRITICAL);
00711     }
00712 
00713     // Attempt to send attach all recipients
00714     foreach($this->to as $to) {
00715       if (!$this->smtp->Recipient($to[0])) {
00716         $bad_rcpt[] = $to[0];
00717         // implement call back function if it exists
00718         $isSent = 0;
00719         $this->doCallback($isSent,$to[0],'','',$this->Subject,$body);
00720       } else {
00721         // implement call back function if it exists
00722         $isSent = 1;
00723         $this->doCallback($isSent,$to[0],'','',$this->Subject,$body);
00724       }
00725     }
00726     foreach($this->cc as $cc) {
00727       if (!$this->smtp->Recipient($cc[0])) {
00728         $bad_rcpt[] = $cc[0];
00729         // implement call back function if it exists
00730         $isSent = 0;
00731         $this->doCallback($isSent,'',$cc[0],'',$this->Subject,$body);
00732       } else {
00733         // implement call back function if it exists
00734         $isSent = 1;
00735         $this->doCallback($isSent,'',$cc[0],'',$this->Subject,$body);
00736       }
00737     }
00738     foreach($this->bcc as $bcc) {
00739       if (!$this->smtp->Recipient($bcc[0])) {
00740         $bad_rcpt[] = $bcc[0];
00741         // implement call back function if it exists
00742         $isSent = 0;
00743         $this->doCallback($isSent,'','',$bcc[0],$this->Subject,$body);
00744       } else {
00745         // implement call back function if it exists
00746         $isSent = 1;
00747         $this->doCallback($isSent,'','',$bcc[0],$this->Subject,$body);
00748       }
00749     }
00750 
00751 
00752     if (count($bad_rcpt) > 0 ) { //Create error message for any bad addresses
00753       $badaddresses = implode(', ', $bad_rcpt);
00754       throw new phpmailerException($this->Lang('recipients_failed') . $badaddresses);
00755     }
00756     if(!$this->smtp->Data($header . $body)) {
00757       throw new phpmailerException($this->Lang('data_not_accepted'), self::STOP_CRITICAL);
00758     }
00759     if($this->SMTPKeepAlive == true) {
00760       $this->smtp->Reset();
00761     }
00762     return true;
00763   }
00764 
00772   public function SmtpConnect() {
00773     if(is_null($this->smtp)) {
00774       $this->smtp = new SMTP();
00775     }
00776 
00777     $this->smtp->do_debug = $this->SMTPDebug;
00778     $hosts = explode(';', $this->Host);
00779     $index = 0;
00780     $connection = $this->smtp->Connected();
00781 
00782     // Retry while there is no connection
00783     try {
00784       while($index < count($hosts) && !$connection) {
00785         $hostinfo = array();
00786         if (preg_match('/^(.+):([0-9]+)$/', $hosts[$index], $hostinfo)) {
00787           $host = $hostinfo[1];
00788           $port = $hostinfo[2];
00789         } else {
00790           $host = $hosts[$index];
00791           $port = $this->Port;
00792         }
00793 
00794         $tls = ($this->SMTPSecure == 'tls');
00795         $ssl = ($this->SMTPSecure == 'ssl');
00796 
00797         if ($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) {
00798 
00799           $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname());
00800           $this->smtp->Hello($hello);
00801 
00802           if ($tls) {
00803             if (!$this->smtp->StartTLS()) {
00804               throw new phpmailerException($this->Lang('tls'));
00805             }
00806 
00807             //We must resend HELO after tls negotiation
00808             $this->smtp->Hello($hello);
00809           }
00810 
00811           $connection = true;
00812           if ($this->SMTPAuth) {
00813             if (!$this->smtp->Authenticate($this->Username, $this->Password)) {
00814               throw new phpmailerException($this->Lang('authenticate'));
00815             }
00816           }
00817         }
00818         $index++;
00819         if (!$connection) {
00820           throw new phpmailerException($this->Lang('connect_host'));
00821         }
00822       }
00823     } catch (phpmailerException $e) {
00824       $this->smtp->Reset();
00825       throw $e;
00826     }
00827     return true;
00828   }
00829 
00834   public function SmtpClose() {
00835     if(!is_null($this->smtp)) {
00836       if($this->smtp->Connected()) {
00837         $this->smtp->Quit();
00838         $this->smtp->Close();
00839       }
00840     }
00841   }
00842 
00850   function SetLanguage($langcode = 'en', $lang_path = 'language/') {
00851     //Define full set of translatable strings
00852     $PHPMAILER_LANG = array(
00853       'provide_address' => 'You must provide at least one recipient email address.',
00854       'mailer_not_supported' => ' mailer is not supported.',
00855       'execute' => 'Could not execute: ',
00856       'instantiate' => 'Could not instantiate mail function.',
00857       'authenticate' => 'SMTP Error: Could not authenticate.',
00858       'from_failed' => 'The following From address failed: ',
00859       'recipients_failed' => 'SMTP Error: The following recipients failed: ',
00860       'data_not_accepted' => 'SMTP Error: Data not accepted.',
00861       'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
00862       'file_access' => 'Could not access file: ',
00863       'file_open' => 'File Error: Could not open file: ',
00864       'encoding' => 'Unknown encoding: ',
00865       'signing' => 'Signing Error: ',
00866       'smtp_error' => 'SMTP server error: ',
00867       'empty_message' => 'Message body empty',
00868       'invalid_address' => 'Invalid address',
00869       'variable_set' => 'Cannot set or reset variable: '
00870     );
00871     //Overwrite language-specific strings. This way we'll never have missing translations - no more "language string failed to load"!
00872     $l = true;
00873     if ($langcode != 'en') { //There is no English translation file
00874       $l = @include $lang_path.'phpmailer.lang-'.$langcode.'.php';
00875     }
00876     $this->language = $PHPMAILER_LANG;
00877     return ($l == true); //Returns false if language not found
00878   }
00879 
00884   public function GetTranslations() {
00885     return $this->language;
00886   }
00887 
00889   // METHODS, MESSAGE CREATION
00891 
00897   public function AddrAppend($type, $addr) {
00898     $addr_str = $type . ': ';
00899     $addresses = array();
00900     foreach ($addr as $a) {
00901       $addresses[] = $this->AddrFormat($a);
00902     }
00903     $addr_str .= implode(', ', $addresses);
00904     $addr_str .= $this->LE;
00905 
00906     return $addr_str;
00907   }
00908 
00914   public function AddrFormat($addr) {
00915     if (empty($addr[1])) {
00916       return $this->SecureHeader($addr[0]);
00917     } else {
00918       return $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
00919     }
00920   }
00921 
00932   public function WrapText($message, $length, $qp_mode = false) {
00933     $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
00934     // If utf-8 encoding is used, we will need to make sure we don't
00935     // split multibyte characters when we wrap
00936     $is_utf8 = (strtolower($this->CharSet) == "utf-8");
00937 
00938     $message = $this->FixEOL($message);
00939     if (substr($message, -1) == $this->LE) {
00940       $message = substr($message, 0, -1);
00941     }
00942 
00943     $line = explode($this->LE, $message);
00944     $message = '';
00945     for ($i=0 ;$i < count($line); $i++) {
00946       $line_part = explode(' ', $line[$i]);
00947       $buf = '';
00948       for ($e = 0; $e<count($line_part); $e++) {
00949         $word = $line_part[$e];
00950         if ($qp_mode and (strlen($word) > $length)) {
00951           $space_left = $length - strlen($buf) - 1;
00952           if ($e != 0) {
00953             if ($space_left > 20) {
00954               $len = $space_left;
00955               if ($is_utf8) {
00956                 $len = $this->UTF8CharBoundary($word, $len);
00957               } elseif (substr($word, $len - 1, 1) == "=") {
00958                 $len--;
00959               } elseif (substr($word, $len - 2, 1) == "=") {
00960                 $len -= 2;
00961               }
00962               $part = substr($word, 0, $len);
00963               $word = substr($word, $len);
00964               $buf .= ' ' . $part;
00965               $message .= $buf . sprintf("=%s", $this->LE);
00966             } else {
00967               $message .= $buf . $soft_break;
00968             }
00969             $buf = '';
00970           }
00971           while (strlen($word) > 0) {
00972             $len = $length;
00973             if ($is_utf8) {
00974               $len = $this->UTF8CharBoundary($word, $len);
00975             } elseif (substr($word, $len - 1, 1) == "=") {
00976               $len--;
00977             } elseif (substr($word, $len - 2, 1) == "=") {
00978               $len -= 2;
00979             }
00980             $part = substr($word, 0, $len);
00981             $word = substr($word, $len);
00982 
00983             if (strlen($word) > 0) {
00984               $message .= $part . sprintf("=%s", $this->LE);
00985             } else {
00986               $buf = $part;
00987             }
00988           }
00989         } else {
00990           $buf_o = $buf;
00991           $buf .= ($e == 0) ? $word : (' ' . $word);
00992 
00993           if (strlen($buf) > $length and $buf_o != '') {
00994             $message .= $buf_o . $soft_break;
00995             $buf = $word;
00996           }
00997         }
00998       }
00999       $message .= $buf . $this->LE;
01000     }
01001 
01002     return $message;
01003   }
01004 
01014   public function UTF8CharBoundary($encodedText, $maxLength) {
01015     $foundSplitPos = false;
01016     $lookBack = 3;
01017     while (!$foundSplitPos) {
01018       $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
01019       $encodedCharPos = strpos($lastChunk, "=");
01020       if ($encodedCharPos !== false) {
01021         // Found start of encoded character byte within $lookBack block.
01022         // Check the encoded byte value (the 2 chars after the '=')
01023         $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
01024         $dec = hexdec($hex);
01025         if ($dec < 128) { // Single byte character.
01026           // If the encoded char was found at pos 0, it will fit
01027           // otherwise reduce maxLength to start of the encoded char
01028           $maxLength = ($encodedCharPos == 0) ? $maxLength :
01029           $maxLength - ($lookBack - $encodedCharPos);
01030           $foundSplitPos = true;
01031         } elseif ($dec >= 192) { // First byte of a multi byte character
01032           // Reduce maxLength to split at start of character
01033           $maxLength = $maxLength - ($lookBack - $encodedCharPos);
01034           $foundSplitPos = true;
01035         } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
01036           $lookBack += 3;
01037         }
01038       } else {
01039         // No encoded character found
01040         $foundSplitPos = true;
01041       }
01042     }
01043     return $maxLength;
01044   }
01045 
01046 
01052   public function SetWordWrap() {
01053     if($this->WordWrap < 1) {
01054       return;
01055     }
01056 
01057     switch($this->message_type) {
01058       case 'alt':
01059       case 'alt_attachments':
01060         $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
01061         break;
01062       default:
01063         $this->Body = $this->WrapText($this->Body, $this->WordWrap);
01064         break;
01065     }
01066   }
01067 
01073   public function CreateHeader() {
01074     $result = '';
01075 
01076     // Set the boundaries
01077     $uniq_id = md5(uniqid(time()));
01078     $this->boundary[1] = 'b1_' . $uniq_id;
01079     $this->boundary[2] = 'b2_' . $uniq_id;
01080 
01081     $result .= $this->HeaderLine('Date', self::RFCDate());
01082     if($this->Sender == '') {
01083       $result .= $this->HeaderLine('Return-Path', trim($this->SecureHeader($this->From))); // Moodle modification
01084     } else {
01085       $result .= $this->HeaderLine('Return-Path', trim($this->SecureHeader($this->Sender))); // Moodle modification
01086     }
01087 
01088     // To be created automatically by mail()
01089     if($this->Mailer != 'mail') {
01090       if ($this->SingleTo === true) {
01091         foreach($this->to as $t) {
01092           $this->SingleToArray[] = $this->AddrFormat($t);
01093         }
01094       } else {
01095         if(count($this->to) > 0) {
01096           $result .= $this->AddrAppend('To', $this->to);
01097         } elseif (count($this->cc) == 0) {
01098           $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
01099         }
01100       }
01101     }
01102 
01103     $from = array();
01104     $from[0][0] = trim($this->From);
01105     $from[0][1] = $this->FromName;
01106     $result .= $this->AddrAppend('From', $from);
01107 
01108     // sendmail and mail() extract Cc from the header before sending
01109     if(count($this->cc) > 0) {
01110       $result .= $this->AddrAppend('Cc', $this->cc);
01111     }
01112 
01113     // sendmail and mail() extract Bcc from the header before sending
01114     if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
01115       $result .= $this->AddrAppend('Bcc', $this->bcc);
01116     }
01117 
01118     if(count($this->ReplyTo) > 0) {
01119       $result .= $this->AddrAppend('Reply-to', $this->ReplyTo);
01120     }
01121 
01122     // mail() sets the subject itself
01123     if($this->Mailer != 'mail') {
01124       $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
01125     }
01126 
01127     if($this->MessageID != '') {
01128       $result .= $this->HeaderLine('Message-ID',$this->MessageID);
01129     } else {
01130       $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
01131     }
01132     $result .= $this->HeaderLine('X-Priority', $this->Priority);
01133     $result .= $this->HeaderLine('X-Mailer', 'PHPMailer '.$this->Version.' (phpmailer.worxware.com)');
01134 
01135     if($this->ConfirmReadingTo != '') {
01136       $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
01137     }
01138 
01139     // Add custom headers
01140     for($index = 0; $index < count($this->CustomHeader); $index++) {
01141       $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
01142     }
01143     if (!$this->sign_key_file) {
01144       $result .= $this->HeaderLine('MIME-Version', '1.0');
01145       $result .= $this->GetMailMIME();
01146     }
01147 
01148     return $result;
01149   }
01150 
01156   public function GetMailMIME() {
01157     $result = '';
01158     switch($this->message_type) {
01159       case 'plain':
01160         $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
01161         $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
01162         break;
01163       case 'attachments':
01164       case 'alt_attachments':
01165         if($this->InlineImageExists()){
01166           $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
01167         } else {
01168           $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
01169           $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
01170         }
01171         break;
01172       case 'alt':
01173         $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
01174         $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
01175         break;
01176     }
01177 
01178     if($this->Mailer != 'mail') {
01179       $result .= $this->LE.$this->LE;
01180     }
01181 
01182     return $result;
01183   }
01184 
01190   public function CreateBody() {
01191     $body = '';
01192 
01193     if ($this->sign_key_file) {
01194       $body .= $this->GetMailMIME();
01195     }
01196 
01197     $this->SetWordWrap();
01198 
01199     switch($this->message_type) {
01200       case 'alt':
01201         $body .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
01202         $body .= $this->EncodeString($this->AltBody, $this->Encoding);
01203         $body .= $this->LE.$this->LE;
01204         $body .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
01205         $body .= $this->EncodeString($this->Body, $this->Encoding);
01206         $body .= $this->LE.$this->LE;
01207         $body .= $this->EndBoundary($this->boundary[1]);
01208         break;
01209       case 'plain':
01210         $body .= $this->EncodeString($this->Body, $this->Encoding);
01211         break;
01212       case 'attachments':
01213         $body .= $this->GetBoundary($this->boundary[1], '', '', '');
01214         $body .= $this->EncodeString($this->Body, $this->Encoding);
01215         $body .= $this->LE;
01216         $body .= $this->AttachAll();
01217         break;
01218       case 'alt_attachments':
01219         $body .= sprintf("--%s%s", $this->boundary[1], $this->LE);
01220         $body .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
01221         $body .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
01222         $body .= $this->EncodeString($this->AltBody, $this->Encoding);
01223         $body .= $this->LE.$this->LE;
01224         $body .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
01225         $body .= $this->EncodeString($this->Body, $this->Encoding);
01226         $body .= $this->LE.$this->LE;
01227         $body .= $this->EndBoundary($this->boundary[2]);
01228         $body .= $this->AttachAll();
01229         break;
01230     }
01231 
01232     if ($this->IsError()) {
01233       $body = '';
01234     } elseif ($this->sign_key_file) {
01235       try {
01236         $file = tempnam('', 'mail');
01237         file_put_contents($file, $body); //TODO check this worked
01238         $signed = tempnam("", "signed");
01239         if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) {
01240           @unlink($file);
01241           @unlink($signed);
01242           $body = file_get_contents($signed);
01243         } else {
01244           @unlink($file);
01245           @unlink($signed);
01246           throw new phpmailerException($this->Lang("signing").openssl_error_string());
01247         }
01248       } catch (phpmailerException $e) {
01249         $body = '';
01250         if ($this->exceptions) {
01251           throw $e;
01252         }
01253       }
01254     }
01255 
01256     return $body;
01257   }
01258 
01263   private function GetBoundary($boundary, $charSet, $contentType, $encoding) {
01264     $result = '';
01265     if($charSet == '') {
01266       $charSet = $this->CharSet;
01267     }
01268     if($contentType == '') {
01269       $contentType = $this->ContentType;
01270     }
01271     if($encoding == '') {
01272       $encoding = $this->Encoding;
01273     }
01274     $result .= $this->TextLine('--' . $boundary);
01275     $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
01276     $result .= $this->LE;
01277     $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
01278     $result .= $this->LE;
01279 
01280     return $result;
01281   }
01282 
01287   private function EndBoundary($boundary) {
01288     return $this->LE . '--' . $boundary . '--' . $this->LE;
01289   }
01290 
01296   private function SetMessageType() {
01297     if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
01298       $this->message_type = 'plain';
01299     } else {
01300       if(count($this->attachment) > 0) {
01301         $this->message_type = 'attachments';
01302       }
01303       if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
01304         $this->message_type = 'alt';
01305       }
01306       if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
01307         $this->message_type = 'alt_attachments';
01308       }
01309     }
01310   }
01311 
01317   public function HeaderLine($name, $value) {
01318     return $name . ': ' . $value . $this->LE;
01319   }
01320 
01326   public function TextLine($value) {
01327     return $value . $this->LE;
01328   }
01329 
01331   // CLASS METHODS, ATTACHMENTS
01333 
01344   public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
01345     try {
01346       if ( !@is_file($path) ) {
01347         throw new phpmailerException($this->Lang('file_access') . $path, self::STOP_CONTINUE);
01348       }
01349       $filename = basename($path);
01350       if ( $name == '' ) {
01351         $name = $filename;
01352       }
01353 
01354       $this->attachment[] = array(
01355         0 => $path,
01356         1 => $filename,
01357         2 => $name,
01358         3 => $encoding,
01359         4 => $type,
01360         5 => false,  // isStringAttachment
01361         6 => 'attachment',
01362         7 => 0
01363       );
01364 
01365     } catch (phpmailerException $e) {
01366       $this->SetError($e->getMessage());
01367       if ($this->exceptions) {
01368         throw $e;
01369       }
01370       echo $e->getMessage()."\n";
01371       if ( $e->getCode() == self::STOP_CRITICAL ) {
01372         return false;
01373       }
01374     }
01375     return true;
01376   }
01377 
01382   public function GetAttachments() {
01383     return $this->attachment;
01384   }
01385 
01392   private function AttachAll() {
01393     // Return text of body
01394     $mime = array();
01395     $cidUniq = array();
01396     $incl = array();
01397 
01398     // Add all attachments
01399     foreach ($this->attachment as $attachment) {
01400       // Check for string attachment
01401       $bString = $attachment[5];
01402       if ($bString) {
01403         $string = $attachment[0];
01404       } else {
01405         $path = $attachment[0];
01406       }
01407 
01408       if (in_array($attachment[0], $incl)) { continue; }
01409       $filename    = $attachment[1];
01410       $name        = $attachment[2];
01411       $encoding    = $attachment[3];
01412       $type        = $attachment[4];
01413       $disposition = $attachment[6];
01414       $cid         = $attachment[7];
01415       $incl[]      = $attachment[0];
01416       if ( $disposition == 'inline' && isset($cidUniq[$cid]) ) { continue; }
01417       $cidUniq[$cid] = true;
01418 
01419       $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
01420       $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);
01421       $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
01422 
01423       if($disposition == 'inline') {
01424         $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
01425       }
01426 
01427       $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
01428 
01429       // Encode as string attachment
01430       if($bString) {
01431         $mime[] = $this->EncodeString($string, $encoding);
01432         if($this->IsError()) {
01433           return '';
01434         }
01435         $mime[] = $this->LE.$this->LE;
01436       } else {
01437         $mime[] = $this->EncodeFile($path, $encoding);
01438         if($this->IsError()) {
01439           return '';
01440         }
01441         $mime[] = $this->LE.$this->LE;
01442       }
01443     }
01444 
01445     $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
01446 
01447     return join('', $mime);
01448   }
01449 
01459   private function EncodeFile($path, $encoding = 'base64') {
01460     try {
01461       if (!is_readable($path)) {
01462         throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE);
01463       }
01464       if (function_exists('get_magic_quotes')) {
01465         function get_magic_quotes() {
01466           return false;
01467         }
01468       }
01469       if (PHP_VERSION < 6) {
01470         $magic_quotes = get_magic_quotes_runtime();
01471         set_magic_quotes_runtime(0);
01472       }
01473       $file_buffer  = file_get_contents($path);
01474       $file_buffer  = $this->EncodeString($file_buffer, $encoding);
01475       if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); }
01476       return $file_buffer;
01477     } catch (Exception $e) {
01478       $this->SetError($e->getMessage());
01479       return '';
01480     }
01481   }
01482 
01491   public function EncodeString ($str, $encoding = 'base64') {
01492     $encoded = '';
01493     switch(strtolower($encoding)) {
01494       case 'base64':
01495         $encoded = chunk_split(base64_encode($str), 76, $this->LE);
01496         break;
01497       case '7bit':
01498       case '8bit':
01499         $encoded = $this->FixEOL($str);
01500         //Make sure it ends with a line break
01501         if (substr($encoded, -(strlen($this->LE))) != $this->LE)
01502           $encoded .= $this->LE;
01503         break;
01504       case 'binary':
01505         $encoded = $str;
01506         break;
01507       case 'quoted-printable':
01508         $encoded = $this->EncodeQP($str);
01509         break;
01510       default:
01511         $this->SetError($this->Lang('encoding') . $encoding);
01512         break;
01513     }
01514     return $encoded;
01515   }
01516 
01522   public function EncodeHeader($str, $position = 'text') {
01523     $x = 0;
01524 
01525     switch (strtolower($position)) {
01526       case 'phrase':
01527         if (!preg_match('/[\200-\377]/', $str)) {
01528           // Can't use addslashes as we don't know what value has magic_quotes_sybase
01529           $encoded = addcslashes($str, "\0..\37\177\\\"");
01530           if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
01531             return ($encoded);
01532           } else {
01533             return ("\"$encoded\"");
01534           }
01535         }
01536         $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
01537         break;
01538       case 'comment':
01539         $x = preg_match_all('/[()"]/', $str, $matches);
01540         // Fall-through
01541       case 'text':
01542       default:
01543         $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
01544         break;
01545     }
01546 
01547     if ($x == 0) {
01548       return ($str);
01549     }
01550 
01551     $maxlen = 75 - 7 - strlen($this->CharSet);
01552     // Try to select the encoding which should produce the shortest output
01553     if (strlen($str)/3 < $x) {
01554       $encoding = 'B';
01555       if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
01556         // Use a custom function which correctly encodes and wraps long
01557         // multibyte strings without breaking lines within a character
01558         $encoded = $this->Base64EncodeWrapMB($str);
01559       } else {
01560         $encoded = base64_encode($str);
01561         $maxlen -= $maxlen % 4;
01562         $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
01563       }
01564     } else {
01565       $encoding = 'Q';
01566       $encoded = $this->EncodeQ($str, $position);
01567       $encoded = $this->WrapText($encoded, $maxlen, true);
01568       $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
01569     }
01570 
01571     $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
01572     $encoded = trim(str_replace("\n", $this->LE, $encoded));
01573 
01574     return $encoded;
01575   }
01576 
01583   public function HasMultiBytes($str) {
01584     if (function_exists('mb_strlen')) {
01585       return (strlen($str) > mb_strlen($str, $this->CharSet));
01586     } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
01587       return false;
01588     }
01589   }
01590 
01599   public function Base64EncodeWrapMB($str) {
01600     $start = "=?".$this->CharSet."?B?";
01601     $end = "?=";
01602     $encoded = "";
01603 
01604     $mb_length = mb_strlen($str, $this->CharSet);
01605     // Each line must have length <= 75, including $start and $end
01606     $length = 75 - strlen($start) - strlen($end);
01607     // Average multi-byte ratio
01608     $ratio = $mb_length / strlen($str);
01609     // Base64 has a 4:3 ratio
01610     $offset = $avgLength = floor($length * $ratio * .75);
01611 
01612     for ($i = 0; $i < $mb_length; $i += $offset) {
01613       $lookBack = 0;
01614 
01615       do {
01616         $offset = $avgLength - $lookBack;
01617         $chunk = mb_substr($str, $i, $offset, $this->CharSet);
01618         $chunk = base64_encode($chunk);
01619         $lookBack++;
01620       }
01621       while (strlen($chunk) > $length);
01622 
01623       $encoded .= $chunk . $this->LE;
01624     }
01625 
01626     // Chomp the last linefeed
01627     $encoded = substr($encoded, 0, -strlen($this->LE));
01628     return $encoded;
01629   }
01630 
01639   public function EncodeQPphp( $input = '', $line_max = 76, $space_conv = false) {
01640     $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
01641     $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
01642     $eol = "\r\n";
01643     $escape = '=';
01644     $output = '';
01645     while( list(, $line) = each($lines) ) {
01646       $linlen = strlen($line);
01647       $newline = '';
01648       for($i = 0; $i < $linlen; $i++) {
01649         $c = substr( $line, $i, 1 );
01650         $dec = ord( $c );
01651         if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
01652           $c = '=2E';
01653         }
01654         if ( $dec == 32 ) {
01655           if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
01656             $c = '=20';
01657           } else if ( $space_conv ) {
01658             $c = '=20';
01659           }
01660         } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
01661           $h2 = floor($dec/16);
01662           $h1 = floor($dec%16);
01663           $c = $escape.$hex[$h2].$hex[$h1];
01664         }
01665         if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
01666           $output .= $newline.$escape.$eol; //  soft line break; " =\r\n" is okay
01667           $newline = '';
01668           // check if newline first character will be point or not
01669           if ( $dec == 46 ) {
01670             $c = '=2E';
01671           }
01672         }
01673         $newline .= $c;
01674       } // end of for
01675       $output .= $newline.$eol;
01676     } // end of while
01677     return $output;
01678   }
01679 
01692   public function EncodeQP($string, $line_max = 76, $space_conv = false) {
01693     if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3)
01694       return quoted_printable_encode($string);
01695     }
01696     $filters = stream_get_filters();
01697     if (!in_array('convert.*', $filters)) { //Got convert stream filter?
01698       return $this->EncodeQPphp($string, $line_max, $space_conv); //Fall back to old implementation
01699     }
01700     $fp = fopen('php://temp/', 'r+');
01701     $string = preg_replace('/\r\n?/', $this->LE, $string); //Normalise line breaks
01702     $params = array('line-length' => $line_max, 'line-break-chars' => $this->LE);
01703     $s = stream_filter_append($fp, 'convert.quoted-printable-encode', STREAM_FILTER_READ, $params);
01704     fputs($fp, $string);
01705     rewind($fp);
01706     $out = stream_get_contents($fp);
01707     stream_filter_remove($s);
01708     $out = preg_replace('/^\./m', '=2E', $out); //Encode . if it is first char on a line, workaround for bug in Exchange
01709     fclose($fp);
01710     return $out;
01711   }
01712 
01721   public function EncodeQ ($str, $position = 'text') {
01722     // There should not be any EOL in the string
01723     $encoded = preg_replace('/[\r\n]*/', '', $str);
01724 
01725     switch (strtolower($position)) {
01726       case 'phrase':
01727         $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
01728         break;
01729       case 'comment':
01730         $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
01731       case 'text':
01732       default:
01733         // Replace every high ascii, control =, ? and _ characters
01734         //TODO using /e (equivalent to eval()) is probably not a good idea
01735         $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
01736               "'='.sprintf('%02X', ord('\\1'))", $encoded);
01737         break;
01738     }
01739 
01740     // Replace every spaces to _ (more readable than =20)
01741     $encoded = str_replace(' ', '_', $encoded);
01742 
01743     return $encoded;
01744   }
01745 
01756   public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
01757     // Append to $attachment array
01758     $this->attachment[] = array(
01759       0 => $string,
01760       1 => $filename,
01761       2 => basename($filename),
01762       3 => $encoding,
01763       4 => $type,
01764       5 => true,  // isStringAttachment
01765       6 => 'attachment',
01766       7 => 0
01767     );
01768   }
01769 
01783   public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
01784 
01785     if ( !@is_file($path) ) {
01786       $this->SetError($this->Lang('file_access') . $path);
01787       return false;
01788     }
01789 
01790     $filename = basename($path);
01791     if ( $name == '' ) {
01792       $name = $filename;
01793     }
01794 
01795     // Append to $attachment array
01796     $this->attachment[] = array(
01797       0 => $path,
01798       1 => $filename,
01799       2 => $name,
01800       3 => $encoding,
01801       4 => $type,
01802       5 => false,  // isStringAttachment
01803       6 => 'inline',
01804       7 => $cid
01805     );
01806 
01807     return true;
01808   }
01809 
01815   public function InlineImageExists() {
01816     foreach($this->attachment as $attachment) {
01817       if ($attachment[6] == 'inline') {
01818         return true;
01819       }
01820     }
01821     return false;
01822   }
01823 
01825   // CLASS METHODS, MESSAGE RESET
01827 
01832   public function ClearAddresses() {
01833     foreach($this->to as $to) {
01834       unset($this->all_recipients[strtolower($to[0])]);
01835     }
01836     $this->to = array();
01837   }
01838 
01843   public function ClearCCs() {
01844     foreach($this->cc as $cc) {
01845       unset($this->all_recipients[strtolower($cc[0])]);
01846     }
01847     $this->cc = array();
01848   }
01849 
01854   public function ClearBCCs() {
01855     foreach($this->bcc as $bcc) {
01856       unset($this->all_recipients[strtolower($bcc[0])]);
01857     }
01858     $this->bcc = array();
01859   }
01860 
01865   public function ClearReplyTos() {
01866     $this->ReplyTo = array();
01867   }
01868 
01874   public function ClearAllRecipients() {
01875     $this->to = array();
01876     $this->cc = array();
01877     $this->bcc = array();
01878     $this->all_recipients = array();
01879   }
01880 
01886   public function ClearAttachments() {
01887     $this->attachment = array();
01888   }
01889 
01894   public function ClearCustomHeaders() {
01895     $this->CustomHeader = array();
01896   }
01897 
01899   // CLASS METHODS, MISCELLANEOUS
01901 
01907   protected function SetError($msg) {
01908     $this->error_count++;
01909     if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
01910       $lasterror = $this->smtp->getError();
01911       if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
01912         $msg .= '<p>' . $this->Lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
01913       }
01914     }
01915     $this->ErrorInfo = $msg;
01916   }
01917 
01924   public static function RFCDate() {
01925     $tz = date('Z');
01926     $tzs = ($tz < 0) ? '-' : '+';
01927     $tz = abs($tz);
01928     $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
01929     $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
01930 
01931     return $result;
01932   }
01933 
01939   private function ServerHostname() {
01940     if (!empty($this->Hostname)) {
01941       $result = $this->Hostname;
01942     } elseif (isset($_SERVER['SERVER_NAME'])) {
01943       $result = $_SERVER['SERVER_NAME'];
01944     } else {
01945       $result = 'localhost.localdomain';
01946     }
01947 
01948     return $result;
01949   }
01950 
01956   private function Lang($key) {
01957     if(count($this->language) < 1) {
01958       $this->SetLanguage('en'); // set the default language
01959     }
01960 
01961     if(isset($this->language[$key])) {
01962       return $this->language[$key];
01963     } else {
01964       return 'Language string failed to load: ' . $key;
01965     }
01966   }
01967 
01973   public function IsError() {
01974     return ($this->error_count > 0);
01975   }
01976 
01982   private function FixEOL($str) {
01983     $str = str_replace("\r\n", "\n", $str);
01984     $str = str_replace("\r", "\n", $str);
01985     $str = str_replace("\n", $this->LE, $str);
01986     return $str;
01987   }
01988 
01994   public function AddCustomHeader($custom_header) {
01995     $this->CustomHeader[] = explode(':', $custom_header, 2);
01996   }
01997 
02003   public function MsgHTML($message, $basedir = '') {
02004     preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
02005     if(isset($images[2])) {
02006       foreach($images[2] as $i => $url) {
02007         // do not change urls for absolute images (thanks to corvuscorax)
02008         if (!preg_match('#^[A-z]+://#',$url)) {
02009           $filename = basename($url);
02010           $directory = dirname($url);
02011           ($directory == '.')?$directory='':'';
02012           $cid = 'cid:' . md5($filename);
02013           $ext = pathinfo($filename, PATHINFO_EXTENSION);
02014           $mimeType  = self::_mime_types($ext);
02015           if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }
02016           if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }
02017           if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {
02018             $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
02019           }
02020         }
02021       }
02022     }
02023     $this->IsHTML(true);
02024     $this->Body = $message;
02025     $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));
02026     if (!empty($textMsg) && empty($this->AltBody)) {
02027       $this->AltBody = html_entity_decode($textMsg);
02028     }
02029     if (empty($this->AltBody)) {
02030       $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n";
02031     }
02032   }
02033 
02041   public static function _mime_types($ext = '') {
02042     $mimes = array(
02043       'hqx'   =>  'application/mac-binhex40',
02044       'cpt'   =>  'application/mac-compactpro',
02045       'doc'   =>  'application/msword',
02046       'bin'   =>  'application/macbinary',
02047       'dms'   =>  'application/octet-stream',
02048       'lha'   =>  'application/octet-stream',
02049       'lzh'   =>  'application/octet-stream',
02050       'exe'   =>  'application/octet-stream',
02051       'class' =>  'application/octet-stream',
02052       'psd'   =>  'application/octet-stream',
02053       'so'    =>  'application/octet-stream',
02054       'sea'   =>  'application/octet-stream',
02055       'dll'   =>  'application/octet-stream',
02056       'oda'   =>  'application/oda',
02057       'pdf'   =>  'application/pdf',
02058       'ai'    =>  'application/postscript',
02059       'eps'   =>  'application/postscript',
02060       'ps'    =>  'application/postscript',
02061       'smi'   =>  'application/smil',
02062       'smil'  =>  'application/smil',
02063       'mif'   =>  'application/vnd.mif',
02064       'xls'   =>  'application/vnd.ms-excel',
02065       'ppt'   =>  'application/vnd.ms-powerpoint',
02066       'wbxml' =>  'application/vnd.wap.wbxml',
02067       'wmlc'  =>  'application/vnd.wap.wmlc',
02068       'dcr'   =>  'application/x-director',
02069       'dir'   =>  'application/x-director',
02070       'dxr'   =>  'application/x-director',
02071       'dvi'   =>  'application/x-dvi',
02072       'gtar'  =>  'application/x-gtar',
02073       'php'   =>  'application/x-httpd-php',
02074       'php4'  =>  'application/x-httpd-php',
02075       'php3'  =>  'application/x-httpd-php',
02076       'phtml' =>  'application/x-httpd-php',
02077       'phps'  =>  'application/x-httpd-php-source',
02078       'js'    =>  'application/x-javascript',
02079       'swf'   =>  'application/x-shockwave-flash',
02080       'sit'   =>  'application/x-stuffit',
02081       'tar'   =>  'application/x-tar',
02082       'tgz'   =>  'application/x-tar',
02083       'xhtml' =>  'application/xhtml+xml',
02084       'xht'   =>  'application/xhtml+xml',
02085       'zip'   =>  'application/zip',
02086       'mid'   =>  'audio/midi',
02087       'midi'  =>  'audio/midi',
02088       'mpga'  =>  'audio/mpeg',
02089       'mp2'   =>  'audio/mpeg',
02090       'mp3'   =>  'audio/mpeg',
02091       'aif'   =>  'audio/x-aiff',
02092       'aiff'  =>  'audio/x-aiff',
02093       'aifc'  =>  'audio/x-aiff',
02094       'ram'   =>  'audio/x-pn-realaudio',
02095       'rm'    =>  'audio/x-pn-realaudio',
02096       'rpm'   =>  'audio/x-pn-realaudio-plugin',
02097       'ra'    =>  'audio/x-realaudio',
02098       'rv'    =>  'video/vnd.rn-realvideo',
02099       'wav'   =>  'audio/x-wav',
02100       'bmp'   =>  'image/bmp',
02101       'gif'   =>  'image/gif',
02102       'jpeg'  =>  'image/jpeg',
02103       'jpg'   =>  'image/jpeg',
02104       'jpe'   =>  'image/jpeg',
02105       'png'   =>  'image/png',
02106       'tiff'  =>  'image/tiff',
02107       'tif'   =>  'image/tiff',
02108       'css'   =>  'text/css',
02109       'html'  =>  'text/html',
02110       'htm'   =>  'text/html',
02111       'shtml' =>  'text/html',
02112       'txt'   =>  'text/plain',
02113       'text'  =>  'text/plain',
02114       'log'   =>  'text/plain',
02115       'rtx'   =>  'text/richtext',
02116       'rtf'   =>  'text/rtf',
02117       'xml'   =>  'text/xml',
02118       'xsl'   =>  'text/xml',
02119       'mpeg'  =>  'video/mpeg',
02120       'mpg'   =>  'video/mpeg',
02121       'mpe'   =>  'video/mpeg',
02122       'qt'    =>  'video/quicktime',
02123       'mov'   =>  'video/quicktime',
02124       'avi'   =>  'video/x-msvideo',
02125       'movie' =>  'video/x-sgi-movie',
02126       'doc'   =>  'application/msword',
02127       'word'  =>  'application/msword',
02128       'xl'    =>  'application/excel',
02129       'eml'   =>  'message/rfc822'
02130     );
02131     return (!isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
02132   }
02133 
02146   public function set($name, $value = '') {
02147     try {
02148       if (isset($this->$name) ) {
02149         $this->$name = $value;
02150       } else {
02151         throw new phpmailerException($this->Lang('variable_set') . $name, self::STOP_CRITICAL);
02152       }
02153     } catch (Exception $e) {
02154       $this->SetError($e->getMessage());
02155       if ($e->getCode() == self::STOP_CRITICAL) {
02156         return false;
02157       }
02158     }
02159     return true;
02160   }
02161 
02168   public function SecureHeader($str) {
02169     $str = str_replace("\r", '', $str);
02170     $str = str_replace("\n", '', $str);
02171     return trim($str);
02172   }
02173 
02181   public function Sign($cert_filename, $key_filename, $key_pass) {
02182     $this->sign_cert_file = $cert_filename;
02183     $this->sign_key_file = $key_filename;
02184     $this->sign_key_pass = $key_pass;
02185   }
02186 
02194   public function DKIM_QP($txt) {
02195     $tmp="";
02196     $line="";
02197     for ($i=0;$i<strlen($txt);$i++) {
02198       $ord=ord($txt[$i]);
02199       if ( ((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E)) ) {
02200         $line.=$txt[$i];
02201       } else {
02202         $line.="=".sprintf("%02X",$ord);
02203       }
02204     }
02205     return $line;
02206   }
02207 
02214   public function DKIM_Sign($s) {
02215     $privKeyStr = file_get_contents($this->DKIM_private);
02216     if ($this->DKIM_passphrase!='') {
02217       $privKey = openssl_pkey_get_private($privKeyStr,$this->DKIM_passphrase);
02218     } else {
02219       $privKey = $privKeyStr;
02220     }
02221     if (openssl_sign($s, $signature, $privKey)) {
02222       return base64_encode($signature);
02223     }
02224   }
02225 
02232   public function DKIM_HeaderC($s) {
02233     $s=preg_replace("/\r\n\s+/"," ",$s);
02234     $lines=explode("\r\n",$s);
02235     foreach ($lines as $key=>$line) {
02236       list($heading,$value)=explode(":",$line,2);
02237       $heading=strtolower($heading);
02238       $value=preg_replace("/\s+/"," ",$value) ; // Compress useless spaces
02239       $lines[$key]=$heading.":".trim($value) ; // Don't forget to remove WSP around the value
02240     }
02241     $s=implode("\r\n",$lines);
02242     return $s;
02243   }
02244 
02251   public function DKIM_BodyC($body) {
02252     if ($body == '') return "\r\n";
02253     // stabilize line endings
02254     $body=str_replace("\r\n","\n",$body);
02255     $body=str_replace("\n","\r\n",$body);
02256     // END stabilize line endings
02257     while (substr($body,strlen($body)-4,4) == "\r\n\r\n") {
02258       $body=substr($body,0,strlen($body)-2);
02259     }
02260     return $body;
02261   }
02262 
02271   public function DKIM_Add($headers_line,$subject,$body) {
02272     $DKIMsignatureType    = 'rsa-sha1'; // Signature & hash algorithms
02273     $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
02274     $DKIMquery            = 'dns/txt'; // Query method
02275     $DKIMtime             = time() ; // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
02276     $subject_header       = "Subject: $subject";
02277     $headers              = explode("\r\n",$headers_line);
02278     foreach($headers as $header) {
02279       if (strpos($header,'From:') === 0) {
02280         $from_header=$header;
02281       } elseif (strpos($header,'To:') === 0) {
02282         $to_header=$header;
02283       }
02284     }
02285     $from     = str_replace('|','=7C',$this->DKIM_QP($from_header));
02286     $to       = str_replace('|','=7C',$this->DKIM_QP($to_header));
02287     $subject  = str_replace('|','=7C',$this->DKIM_QP($subject_header)) ; // Copied header fields (dkim-quoted-printable
02288     $body     = $this->DKIM_BodyC($body);
02289     $DKIMlen  = strlen($body) ; // Length of body
02290     $DKIMb64  = base64_encode(pack("H*", sha1($body))) ; // Base64 of packed binary SHA-1 hash of body
02291     $ident    = ($this->DKIM_identity == '')? '' : " i=" . $this->DKIM_identity . ";";
02292     $dkimhdrs = "DKIM-Signature: v=1; a=" . $DKIMsignatureType . "; q=" . $DKIMquery . "; l=" . $DKIMlen . "; s=" . $this->DKIM_selector . ";\r\n".
02293                 "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n".
02294                 "\th=From:To:Subject;\r\n".
02295                 "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n".
02296                 "\tz=$from\r\n".
02297                 "\t|$to\r\n".
02298                 "\t|$subject;\r\n".
02299                 "\tbh=" . $DKIMb64 . ";\r\n".
02300                 "\tb=";
02301     $toSign   = $this->DKIM_HeaderC($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs);
02302     $signed   = $this->DKIM_Sign($toSign);
02303     return "X-PHPMAILER-DKIM: phpmailer.worxware.com\r\n".$dkimhdrs.$signed."\r\n";
02304   }
02305 
02306   protected function doCallback($isSent,$to,$cc,$bcc,$subject,$body) {
02307     if (!empty($this->action_function) && function_exists($this->action_function)) {
02308       $params = array($isSent,$to,$cc,$bcc,$subject,$body);
02309       call_user_func_array($this->action_function,$params);
02310     }
02311   }
02312 }
02313 
02314 class phpmailerException extends Exception {
02315   public function errorMessage() {
02316     $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
02317     return $errorMsg;
02318   }
02319 }
02320 ?>
 All Data Structures Namespaces Files Functions Variables Enumerations