source: hadoop-register/etc/phpMailer/class.phpmailer.php @ 253

Last change on this file since 253 was 57, checked in by jazz, 16 years ago
  • 第一版 Hadoop 叢集之帳號申請網頁 by Wade
File size: 56.5 KB
Line 
1<?php
2/*~ class.phpmailer.php
3.---------------------------------------------------------------------------.
4|  Software: PHPMailer - PHP email class                                    |
5|   Version: 2.3                                                            |
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|    Author: Andy Prevost (project admininistrator)                         |
11|    Author: Brent R. Matzelle (original founder)                           |
12| Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved.               |
13| Copyright (c) 2001-2003, Brent R. Matzelle                                |
14| ------------------------------------------------------------------------- |
15|   License: Distributed under the Lesser General Public License (LGPL)     |
16|            http://www.gnu.org/copyleft/lesser.html                        |
17| This program is distributed in the hope that it will be useful - WITHOUT  |
18| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
19| FITNESS FOR A PARTICULAR PURPOSE.                                         |
20| ------------------------------------------------------------------------- |
21| We offer a number of paid services (www.codeworxtech.com):                |
22| - Web Hosting on highly optimized fast and secure servers                 |
23| - Technology Consulting                                                   |
24| - Oursourcing (highly qualified programmers and graphic designers)        |
25'---------------------------------------------------------------------------'
26
27/**
28 * PHPMailer - PHP email transport class
29 * NOTE: Designed for use with PHP version 5 and up
30 * @package PHPMailer
31 * @author Andy Prevost
32 * @copyright 2004 - 2008 Andy Prevost
33 */
34
35class PHPMailer {
36
37  /////////////////////////////////////////////////
38  // PROPERTIES, PUBLIC
39  /////////////////////////////////////////////////
40
41  /**
42   * Email priority (1 = High, 3 = Normal, 5 = low).
43   * @var int
44   */
45  public $Priority          = 3;
46
47  /**
48   * Sets the CharSet of the message.
49   * @var string
50   */
51  public $CharSet           = 'iso-8859-1';
52
53  /**
54   * Sets the Content-type of the message.
55   * @var string
56   */
57  public $ContentType       = 'text/plain';
58
59  /**
60   * Sets the Encoding of the message. Options for this are "8bit",
61   * "7bit", "binary", "base64", and "quoted-printable".
62   * @var string
63   */
64  public $Encoding          = '8bit';
65
66  /**
67   * Holds the most recent mailer error message.
68   * @var string
69   */
70  public $ErrorInfo         = '';
71
72  /**
73   * Sets the From email address for the message.
74   * @var string
75   */
76  public $From              = 'root@localhost';
77
78  /**
79   * Sets the From name of the message.
80   * @var string
81   */
82  public $FromName          = 'Root User';
83
84  /**
85   * Sets the Sender email (Return-Path) of the message.  If not empty,
86   * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
87   * @var string
88   */
89  public $Sender            = '';
90
91  /**
92   * Sets the Subject of the message.
93   * @var string
94   */
95  public $Subject           = '';
96
97  /**
98   * Sets the Body of the message.  This can be either an HTML or text body.
99   * If HTML then run IsHTML(true).
100   * @var string
101   */
102  public $Body              = '';
103
104  /**
105   * Sets the text-only body of the message.  This automatically sets the
106   * email to multipart/alternative.  This body can be read by mail
107   * clients that do not have HTML email capability such as mutt. Clients
108   * that can read HTML will view the normal Body.
109   * @var string
110   */
111  public $AltBody           = '';
112
113  /**
114   * Sets word wrapping on the body of the message to a given number of
115   * characters.
116   * @var int
117   */
118  public $WordWrap          = 0;
119
120  /**
121   * Method to send mail: ("mail", "sendmail", or "smtp").
122   * @var string
123   */
124  public $Mailer            = 'mail';
125
126  /**
127   * Sets the path of the sendmail program.
128   * @var string
129   */
130  public $Sendmail          = '/usr/sbin/sendmail';
131
132  /**
133   * Path to PHPMailer plugins.  This is now only useful if the SMTP class
134   * is in a different directory than the PHP include path.
135   * @var string
136   */
137  public $PluginDir         = '';
138
139  /**
140   * Holds PHPMailer version.
141   * @var string
142   */
143  public $Version           = "2.3";
144
145  /**
146   * Sets the email address that a reading confirmation will be sent.
147   * @var string
148   */
149  public $ConfirmReadingTo  = '';
150
151  /**
152   * Sets the hostname to use in Message-Id and Received headers
153   * and as default HELO string. If empty, the value returned
154   * by SERVER_NAME is used or 'localhost.localdomain'.
155   * @var string
156   */
157  public $Hostname          = '';
158
159  /**
160   * Sets the message ID to be used in the Message-Id header.
161   * If empty, a unique id will be generated.
162   * @var string
163   */
164  public $MessageID      = '';
165
166  /////////////////////////////////////////////////
167  // PROPERTIES FOR SMTP
168  /////////////////////////////////////////////////
169
170  /**
171   * Sets the SMTP hosts.  All hosts must be separated by a
172   * semicolon.  You can also specify a different port
173   * for each host by using this format: [hostname:port]
174   * (e.g. "smtp1.example.com:25;smtp2.example.com").
175   * Hosts will be tried in order.
176   * @var string
177   */
178  public $Host        = 'localhost';
179
180  /**
181   * Sets the default SMTP server port.
182   * @var int
183   */
184  public $Port        = 25;
185
186  /**
187   * Sets the SMTP HELO of the message (Default is $Hostname).
188   * @var string
189   */
190  public $Helo        = '';
191
192  /**
193   * Sets connection prefix.
194   * Options are "", "ssl" or "tls"
195   * @var string
196   */
197  public $SMTPSecure = "";
198
199  /**
200   * Sets SMTP authentication. Utilizes the Username and Password variables.
201   * @var bool
202   */
203  public $SMTPAuth     = false;
204
205  /**
206   * Sets SMTP username.
207   * @var string
208   */
209  public $Username     = '';
210
211  /**
212   * Sets SMTP password.
213   * @var string
214   */
215  public $Password     = '';
216
217  /**
218   * Sets the SMTP server timeout in seconds. This function will not
219   * work with the win32 version.
220   * @var int
221   */
222  public $Timeout      = 10;
223
224  /**
225   * Sets SMTP class debugging on or off.
226   * @var bool
227   */
228  public $SMTPDebug    = false;
229
230  /**
231   * Prevents the SMTP connection from being closed after each mail
232   * sending.  If this is set to true then to close the connection
233   * requires an explicit call to SmtpClose().
234   * @var bool
235   */
236  public $SMTPKeepAlive = false;
237
238  /**
239   * Provides the ability to have the TO field process individual
240   * emails, instead of sending to entire TO addresses
241   * @var bool
242   */
243  public $SingleTo = false;
244
245  /**
246   * Provides the ability to change the line ending
247   * @var string
248   */
249  public $LE              = "\r\n";
250
251  /////////////////////////////////////////////////
252  // PROPERTIES, PRIVATE
253  /////////////////////////////////////////////////
254
255  private $smtp            = NULL;
256  private $to              = array();
257  private $cc              = array();
258  private $bcc             = array();
259  private $ReplyTo         = array();
260  private $attachment      = array();
261  private $CustomHeader    = array();
262  private $message_type    = '';
263  private $boundary        = array();
264  private $language        = array();
265  private $error_count     = 0;
266  private $sign_cert_file  = "";
267  private $sign_key_file   = "";
268  private $sign_key_pass   = "";
269
270  /////////////////////////////////////////////////
271  // METHODS, VARIABLES
272  /////////////////////////////////////////////////
273
274  /**
275   * Sets message type to HTML.
276   * @param bool $bool
277   * @return void
278   */
279  public function IsHTML($bool) {
280    if($bool == true) {
281      $this->ContentType = 'text/html';
282    } else {
283      $this->ContentType = 'text/plain';
284    }
285  }
286
287  /**
288   * Sets Mailer to send message using SMTP.
289   * @return void
290   */
291  public function IsSMTP() {
292    $this->Mailer = 'smtp';
293  }
294
295  /**
296   * Sets Mailer to send message using PHP mail() function.
297   * @return void
298   */
299  public function IsMail() {
300    $this->Mailer = 'mail';
301  }
302
303  /**
304   * Sets Mailer to send message using the $Sendmail program.
305   * @return void
306   */
307  public function IsSendmail() {
308    $this->Mailer = 'sendmail';
309  }
310
311  /**
312   * Sets Mailer to send message using the qmail MTA.
313   * @return void
314   */
315  public function IsQmail() {
316    $this->Sendmail = '/var/qmail/bin/sendmail';
317    $this->Mailer   = 'sendmail';
318  }
319
320  /////////////////////////////////////////////////
321  // METHODS, RECIPIENTS
322  /////////////////////////////////////////////////
323
324  /**
325   * Adds a "To" address.
326   * @param string $address
327   * @param string $name
328   * @return void
329   */
330  public function AddAddress($address, $name = '') {
331    $cur = count($this->to);
332    $this->to[$cur][0] = trim($address);
333    $this->to[$cur][1] = $name;
334  }
335
336  /**
337   * Adds a "Cc" address. Note: this function works
338   * with the SMTP mailer on win32, not with the "mail"
339   * mailer.
340   * @param string $address
341   * @param string $name
342   * @return void
343   */
344  public function AddCC($address, $name = '') {
345    $cur = count($this->cc);
346    $this->cc[$cur][0] = trim($address);
347    $this->cc[$cur][1] = $name;
348  }
349
350  /**
351   * Adds a "Bcc" address. Note: this function works
352   * with the SMTP mailer on win32, not with the "mail"
353   * mailer.
354   * @param string $address
355   * @param string $name
356   * @return void
357   */
358  public function AddBCC($address, $name = '') {
359    $cur = count($this->bcc);
360    $this->bcc[$cur][0] = trim($address);
361    $this->bcc[$cur][1] = $name;
362  }
363
364  /**
365   * Adds a "Reply-to" address.
366   * @param string $address
367   * @param string $name
368   * @return void
369   */
370  public function AddReplyTo($address, $name = '') {
371    $cur = count($this->ReplyTo);
372    $this->ReplyTo[$cur][0] = trim($address);
373    $this->ReplyTo[$cur][1] = $name;
374  }
375
376  /////////////////////////////////////////////////
377  // METHODS, MAIL SENDING
378  /////////////////////////////////////////////////
379
380  /**
381   * Creates message and assigns Mailer. If the message is
382   * not sent successfully then it returns false.  Use the ErrorInfo
383   * variable to view description of the error.
384   * @return bool
385   */
386  public function Send() {
387    $header = '';
388    $body = '';
389    $result = true;
390
391    if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
392      $this->SetError($this->Lang('provide_address'));
393      return false;
394    }
395
396    /* Set whether the message is multipart/alternative */
397    if(!empty($this->AltBody)) {
398      $this->ContentType = 'multipart/alternative';
399    }
400
401    $this->error_count = 0; // reset errors
402    $this->SetMessageType();
403    $header .= $this->CreateHeader();
404    $body = $this->CreateBody();
405
406    if($body == '') {
407      return false;
408    }
409
410    /* Choose the mailer */
411    switch($this->Mailer) {
412      case 'sendmail':
413        $result = $this->SendmailSend($header, $body);
414        break;
415      case 'smtp':
416        $result = $this->SmtpSend($header, $body);
417        break;
418      case 'mail':
419        $result = $this->MailSend($header, $body);
420        break;
421      default:
422        $result = $this->MailSend($header, $body);
423        break;
424        //$this->SetError($this->Mailer . $this->Lang('mailer_not_supported'));
425        //$result = false;
426        //break;
427    }
428
429    return $result;
430  }
431
432  /**
433   * Sends mail using the $Sendmail program.
434   * @access public
435   * @return bool
436   */
437  public function SendmailSend($header, $body) {
438    if ($this->Sender != '') {
439      $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
440    } else {
441      $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
442    }
443
444    if(!@$mail = popen($sendmail, 'w')) {
445      $this->SetError($this->Lang('execute') . $this->Sendmail);
446      return false;
447    }
448
449    fputs($mail, $header);
450    fputs($mail, $body);
451
452    $result = pclose($mail);
453    if (version_compare(phpversion(), '4.2.3') == -1) {
454      $result = $result >> 8 & 0xFF;
455    }
456    if($result != 0) {
457      $this->SetError($this->Lang('execute') . $this->Sendmail);
458      return false;
459    }
460
461    return true;
462  }
463
464  /**
465   * Sends mail using the PHP mail() function.
466   * @access public
467   * @return bool
468   */
469  public function MailSend($header, $body) {
470
471    $to = '';
472    for($i = 0; $i < count($this->to); $i++) {
473      if($i != 0) { $to .= ', '; }
474      $to .= $this->AddrFormat($this->to[$i]);
475    }
476
477    $toArr = split(',', $to);
478
479    $params = sprintf("-oi -f %s", $this->Sender);
480    if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) {
481      $old_from = ini_get('sendmail_from');
482      ini_set('sendmail_from', $this->Sender);
483      if ($this->SingleTo === true && count($toArr) > 1) {
484        foreach ($toArr as $key => $val) {
485          $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
486        }
487      } else {
488        $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
489      }
490    } else {
491      if ($this->SingleTo === true && count($toArr) > 1) {
492        foreach ($toArr as $key => $val) {
493          $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
494        }
495      } else {
496        $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);
497      }
498    }
499
500    if (isset($old_from)) {
501      ini_set('sendmail_from', $old_from);
502    }
503
504    if(!$rt) {
505      $this->SetError($this->Lang('instantiate'));
506      return false;
507    }
508
509    return true;
510  }
511
512  /**
513   * Sends mail via SMTP using PhpSMTP (Author:
514   * Chris Ryan).  Returns bool.  Returns false if there is a
515   * bad MAIL FROM, RCPT, or DATA input.
516   * @access public
517   * @return bool
518   */
519  public function SmtpSend($header, $body) {
520    include_once($this->PluginDir . 'class.smtp.php');
521    $error = '';
522    $bad_rcpt = array();
523
524    if(!$this->SmtpConnect()) {
525      return false;
526    }
527
528    $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
529    if(!$this->smtp->Mail($smtp_from)) {
530      $error = $this->Lang('from_failed') . $smtp_from;
531      $this->SetError($error);
532      $this->smtp->Reset();
533      return false;
534    }
535
536    /* Attempt to send attach all recipients */
537    for($i = 0; $i < count($this->to); $i++) {
538      if(!$this->smtp->Recipient($this->to[$i][0])) {
539        $bad_rcpt[] = $this->to[$i][0];
540      }
541    }
542    for($i = 0; $i < count($this->cc); $i++) {
543      if(!$this->smtp->Recipient($this->cc[$i][0])) {
544        $bad_rcpt[] = $this->cc[$i][0];
545      }
546    }
547    for($i = 0; $i < count($this->bcc); $i++) {
548      if(!$this->smtp->Recipient($this->bcc[$i][0])) {
549        $bad_rcpt[] = $this->bcc[$i][0];
550      }
551    }
552
553    if(count($bad_rcpt) > 0) { // Create error message
554      for($i = 0; $i < count($bad_rcpt); $i++) {
555        if($i != 0) {
556          $error .= ', ';
557        }
558        $error .= $bad_rcpt[$i];
559      }
560      $error = $this->Lang('recipients_failed') . $error;
561      $this->SetError($error);
562      $this->smtp->Reset();
563      return false;
564    }
565
566    if(!$this->smtp->Data($header . $body)) {
567      $this->SetError($this->Lang('data_not_accepted'));
568      $this->smtp->Reset();
569      return false;
570    }
571    if($this->SMTPKeepAlive == true) {
572      $this->smtp->Reset();
573    } else {
574      $this->SmtpClose();
575    }
576
577    return true;
578  }
579
580  /**
581   * Initiates a connection to an SMTP server.  Returns false if the
582   * operation failed.
583   * @access public
584   * @return bool
585   */
586  public function SmtpConnect() {
587    if($this->smtp == NULL) {
588      $this->smtp = new SMTP();
589    }
590
591    $this->smtp->do_debug = $this->SMTPDebug;
592    $hosts = explode(';', $this->Host);
593    $index = 0;
594    $connection = ($this->smtp->Connected());
595
596    /* Retry while there is no connection */
597    while($index < count($hosts) && $connection == false) {
598      $hostinfo = array();
599      if(eregi('^(.+):([0-9]+)$', $hosts[$index], $hostinfo)) {
600        $host = $hostinfo[1];
601        $port = $hostinfo[2];
602      } else {
603        $host = $hosts[$index];
604        $port = $this->Port;
605      }
606
607      $tls = ($this->SMTPSecure == 'tls');
608      $ssl = ($this->SMTPSecure == 'ssl');
609
610      if($this->smtp->Connect(($ssl ? 'ssl://':'').$host, $port, $this->Timeout)) {
611
612        $hello = ($this->Helo != '' ? $this->Helo : $this->ServerHostname());
613        $this->smtp->Hello($hello);
614
615        if($tls) {
616          if(!$this->smtp->StartTLS()) {
617            $this->SetError($this->Lang("tls"));
618            $this->smtp->Reset();
619            $connection = false;
620          }
621
622          //We must resend HELLO after tls negociation
623          $this->smtp->Hello($hello);
624        }
625
626        $connection = true;
627        if($this->SMTPAuth) {
628          if(!$this->smtp->Authenticate($this->Username, $this->Password)) {
629            $this->SetError($this->Lang('authenticate'));
630            $this->smtp->Reset();
631            $connection = false;
632          }
633        }
634      }
635      $index++;
636    }
637    if(!$connection) {
638      $this->SetError($this->Lang('connect_host'));
639    }
640
641    return $connection;
642  }
643
644  /**
645   * Closes the active SMTP session if one exists.
646   * @return void
647   */
648  public function SmtpClose() {
649    if($this->smtp != NULL) {
650      if($this->smtp->Connected()) {
651        $this->smtp->Quit();
652        $this->smtp->Close();
653      }
654    }
655  }
656
657  /**
658   * Sets the language for all class error messages.  Returns false
659   * if it cannot load the language file.  The default language type
660   * is English.
661   * @param string $lang_type Type of language (e.g. Portuguese: "br")
662   * @param string $lang_path Path to the language file directory
663   * @access public
664   * @return bool
665   */
666  function SetLanguage($lang_type = 'en', $lang_path = 'language/') {
667    if( !(@include $lang_path.'phpmailer.lang-'.$lang_type.'.php') ) {
668      $PHPMAILER_LANG = array();
669      $PHPMAILER_LANG["provide_address"]      = 'You must provide at least one ' .
670      $PHPMAILER_LANG["mailer_not_supported"] = ' mailer is not supported.';
671      $PHPMAILER_LANG["execute"]              = 'Could not execute: ';
672      $PHPMAILER_LANG["instantiate"]          = 'Could not instantiate mail function.';
673      $PHPMAILER_LANG["authenticate"]         = 'SMTP Error: Could not authenticate.';
674      $PHPMAILER_LANG["from_failed"]          = 'The following From address failed: ';
675      $PHPMAILER_LANG["recipients_failed"]    = 'SMTP Error: The following ' .
676      $PHPMAILER_LANG["data_not_accepted"]    = 'SMTP Error: Data not accepted.';
677      $PHPMAILER_LANG["connect_host"]         = 'SMTP Error: Could not connect to SMTP host.';
678      $PHPMAILER_LANG["file_access"]          = 'Could not access file: ';
679      $PHPMAILER_LANG["file_open"]            = 'File Error: Could not open file: ';
680      $PHPMAILER_LANG["encoding"]             = 'Unknown encoding: ';
681      $PHPMAILER_LANG["signing"]              = 'Signing Error: ';
682    }
683    $this->language = $PHPMAILER_LANG;
684    return true;
685  }
686
687  /////////////////////////////////////////////////
688  // METHODS, MESSAGE CREATION
689  /////////////////////////////////////////////////
690
691  /**
692   * Creates recipient headers.
693   * @access public
694   * @return string
695   */
696  public function AddrAppend($type, $addr) {
697    $addr_str = $type . ': ';
698    $addr_str .= $this->AddrFormat($addr[0]);
699    if(count($addr) > 1) {
700      for($i = 1; $i < count($addr); $i++) {
701        $addr_str .= ', ' . $this->AddrFormat($addr[$i]);
702      }
703    }
704    $addr_str .= $this->LE;
705
706    return $addr_str;
707  }
708
709  /**
710   * Formats an address correctly.
711   * @access public
712   * @return string
713   */
714  public function AddrFormat($addr) {
715    if(empty($addr[1])) {
716      $formatted = $this->SecureHeader($addr[0]);
717    } else {
718      $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
719    }
720
721    return $formatted;
722  }
723
724  /**
725   * Wraps message for use with mailers that do not
726   * automatically perform wrapping and for quoted-printable.
727   * Original written by philippe.
728   * @access public
729   * @return string
730   */
731  public function WrapText($message, $length, $qp_mode = false) {
732    $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
733    // If utf-8 encoding is used, we will need to make sure we don't
734    // split multibyte characters when we wrap
735    $is_utf8 = (strtolower($this->CharSet) == "utf-8");
736
737    $message = $this->FixEOL($message);
738    if (substr($message, -1) == $this->LE) {
739      $message = substr($message, 0, -1);
740    }
741
742    $line = explode($this->LE, $message);
743    $message = '';
744    for ($i=0 ;$i < count($line); $i++) {
745      $line_part = explode(' ', $line[$i]);
746      $buf = '';
747      for ($e = 0; $e<count($line_part); $e++) {
748        $word = $line_part[$e];
749        if ($qp_mode and (strlen($word) > $length)) {
750          $space_left = $length - strlen($buf) - 1;
751          if ($e != 0) {
752            if ($space_left > 20) {
753              $len = $space_left;
754              if ($is_utf8) {
755                $len = $this->UTF8CharBoundary($word, $len);
756              } elseif (substr($word, $len - 1, 1) == "=") {
757                $len--;
758              } elseif (substr($word, $len - 2, 1) == "=") {
759                $len -= 2;
760              }
761              $part = substr($word, 0, $len);
762              $word = substr($word, $len);
763              $buf .= ' ' . $part;
764              $message .= $buf . sprintf("=%s", $this->LE);
765            } else {
766              $message .= $buf . $soft_break;
767            }
768            $buf = '';
769          }
770          while (strlen($word) > 0) {
771            $len = $length;
772            if ($is_utf8) {
773              $len = $this->UTF8CharBoundary($word, $len);
774            } elseif (substr($word, $len - 1, 1) == "=") {
775              $len--;
776            } elseif (substr($word, $len - 2, 1) == "=") {
777              $len -= 2;
778            }
779            $part = substr($word, 0, $len);
780            $word = substr($word, $len);
781
782            if (strlen($word) > 0) {
783              $message .= $part . sprintf("=%s", $this->LE);
784            } else {
785              $buf = $part;
786            }
787          }
788        } else {
789          $buf_o = $buf;
790          $buf .= ($e == 0) ? $word : (' ' . $word);
791
792          if (strlen($buf) > $length and $buf_o != '') {
793            $message .= $buf_o . $soft_break;
794            $buf = $word;
795          }
796        }
797      }
798      $message .= $buf . $this->LE;
799    }
800
801    return $message;
802  }
803
804  /**
805   * Finds last character boundary prior to maxLength in a utf-8
806   * quoted (printable) encoded string.
807   * Original written by Colin Brown.
808   * @access public
809   * @param string $encodedText utf-8 QP text
810   * @param int    $maxLength   find last character boundary prior to this length
811   * @return int
812   */
813  public function UTF8CharBoundary($encodedText, $maxLength) {
814    $foundSplitPos = false;
815    $lookBack = 3;
816    while (!$foundSplitPos) {
817      $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
818      $encodedCharPos = strpos($lastChunk, "=");
819      if ($encodedCharPos !== false) {
820        // Found start of encoded character byte within $lookBack block.
821        // Check the encoded byte value (the 2 chars after the '=')
822        $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
823        $dec = hexdec($hex);
824        if ($dec < 128) { // Single byte character.
825          // If the encoded char was found at pos 0, it will fit
826          // otherwise reduce maxLength to start of the encoded char
827          $maxLength = ($encodedCharPos == 0) ? $maxLength :
828          $maxLength - ($lookBack - $encodedCharPos);
829          $foundSplitPos = true;
830        } elseif ($dec >= 192) { // First byte of a multi byte character
831          // Reduce maxLength to split at start of character
832          $maxLength = $maxLength - ($lookBack - $encodedCharPos);
833          $foundSplitPos = true;
834        } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
835          $lookBack += 3;
836        }
837      } else {
838        // No encoded character found
839        $foundSplitPos = true;
840      }
841    }
842    return $maxLength;
843  }
844
845
846  /**
847   * Set the body wrapping.
848   * @access public
849   * @return void
850   */
851  public function SetWordWrap() {
852    if($this->WordWrap < 1) {
853      return;
854    }
855
856    switch($this->message_type) {
857      case 'alt':
858        /* fall through */
859      case 'alt_attachments':
860        $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
861        break;
862      default:
863        $this->Body = $this->WrapText($this->Body, $this->WordWrap);
864        break;
865    }
866  }
867
868  /**
869   * Assembles message header.
870   * @access public
871   * @return string
872   */
873  public function CreateHeader() {
874    $result = '';
875
876    /* Set the boundaries */
877    $uniq_id = md5(uniqid(time()));
878    $this->boundary[1] = 'b1_' . $uniq_id;
879    $this->boundary[2] = 'b2_' . $uniq_id;
880
881    $result .= $this->HeaderLine('Date', $this->RFCDate());
882    if($this->Sender == '') {
883      $result .= $this->HeaderLine('Return-Path', trim($this->From));
884    } else {
885      $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
886    }
887
888    /* To be created automatically by mail() */
889    if($this->Mailer != 'mail') {
890      if(count($this->to) > 0) {
891        $result .= $this->AddrAppend('To', $this->to);
892      } elseif (count($this->cc) == 0) {
893        $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
894      }
895    }
896
897    $from = array();
898    $from[0][0] = trim($this->From);
899    $from[0][1] = $this->FromName;
900    $result .= $this->AddrAppend('From', $from);
901
902    /* sendmail and mail() extract Cc from the header before sending */
903    if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) {
904      $result .= $this->AddrAppend('Cc', $this->cc);
905    }
906
907    /* sendmail and mail() extract Bcc from the header before sending */
908    if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
909      $result .= $this->AddrAppend('Bcc', $this->bcc);
910    }
911
912    if(count($this->ReplyTo) > 0) {
913      $result .= $this->AddrAppend('Reply-to', $this->ReplyTo);
914    }
915
916    /* mail() sets the subject itself */
917    if($this->Mailer != 'mail') {
918      $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
919    }
920
921    if($this->MessageID != '') {
922      $result .= $this->HeaderLine('Message-ID',$this->MessageID);
923    } else {
924      $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
925    }
926    $result .= $this->HeaderLine('X-Priority', $this->Priority);
927    $result .= $this->HeaderLine('X-Mailer', 'PHPMailer (phpmailer.codeworxtech.com) [version ' . $this->Version . ']');
928
929    if($this->ConfirmReadingTo != '') {
930      $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
931    }
932
933    // Add custom headers
934    for($index = 0; $index < count($this->CustomHeader); $index++) {
935      $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
936    }
937    if (!$this->sign_key_file) {
938      $result .= $this->HeaderLine('MIME-Version', '1.0');
939      $result .= $this->GetMailMIME();
940    }
941
942    return $result;
943  }
944
945  /**
946   * Returns the message MIME.
947   * @access public
948   * @return string
949   */
950  public function GetMailMIME() {
951    $result = '';
952    switch($this->message_type) {
953      case 'plain':
954        $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
955        $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
956        break;
957      case 'attachments':
958        /* fall through */
959      case 'alt_attachments':
960        if($this->InlineImageExists()){
961          $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
962        } else {
963          $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
964          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
965        }
966        break;
967      case 'alt':
968        $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
969        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
970        break;
971    }
972
973    if($this->Mailer != 'mail') {
974      $result .= $this->LE.$this->LE;
975    }
976
977    return $result;
978  }
979
980  /**
981   * Assembles the message body.  Returns an empty string on failure.
982   * @access public
983   * @return string
984   */
985  public function CreateBody() {
986    $result = '';
987
988    if ($this->sign_key_file) {
989      $result .= $this->GetMailMIME();
990    }
991
992    $this->SetWordWrap();
993
994    switch($this->message_type) {
995      case 'alt':
996        $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
997        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
998        $result .= $this->LE.$this->LE;
999        $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
1000        $result .= $this->EncodeString($this->Body, $this->Encoding);
1001        $result .= $this->LE.$this->LE;
1002        $result .= $this->EndBoundary($this->boundary[1]);
1003        break;
1004      case 'plain':
1005        $result .= $this->EncodeString($this->Body, $this->Encoding);
1006        break;
1007      case 'attachments':
1008        $result .= $this->GetBoundary($this->boundary[1], '', '', '');
1009        $result .= $this->EncodeString($this->Body, $this->Encoding);
1010        $result .= $this->LE;
1011        $result .= $this->AttachAll();
1012        break;
1013      case 'alt_attachments':
1014        $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
1015        $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
1016        $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
1017        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
1018        $result .= $this->LE.$this->LE;
1019        $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
1020        $result .= $this->EncodeString($this->Body, $this->Encoding);
1021        $result .= $this->LE.$this->LE;
1022        $result .= $this->EndBoundary($this->boundary[2]);
1023        $result .= $this->AttachAll();
1024        break;
1025    }
1026
1027    if($this->IsError()) {
1028      $result = '';
1029    } else if ($this->sign_key_file) {
1030      $file = tempnam("", "mail");
1031      $fp = fopen($file, "w");
1032      fwrite($fp, $result);
1033      fclose($fp);
1034      $signed = tempnam("", "signed");
1035
1036      if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), null)) {
1037        $fp = fopen($signed, "r");
1038        $result = '';
1039        while(!feof($fp)){
1040          $result = $result . fread($fp, 1024);
1041        }
1042        fclose($fp);
1043      } else {
1044        $this->SetError($this->Lang("signing").openssl_error_string());
1045        $result = '';
1046      }
1047
1048      unlink($file);
1049      unlink($signed);
1050    }
1051
1052    return $result;
1053  }
1054
1055  /**
1056   * Returns the start of a message boundary.
1057   * @access public
1058   */
1059  public function GetBoundary($boundary, $charSet, $contentType, $encoding) {
1060    $result = '';
1061    if($charSet == '') {
1062      $charSet = $this->CharSet;
1063    }
1064    if($contentType == '') {
1065      $contentType = $this->ContentType;
1066    }
1067    if($encoding == '') {
1068      $encoding = $this->Encoding;
1069    }
1070    $result .= $this->TextLine('--' . $boundary);
1071    $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
1072    $result .= $this->LE;
1073    $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
1074    $result .= $this->LE;
1075
1076    return $result;
1077  }
1078
1079  /**
1080   * Returns the end of a message boundary.
1081   * @access public
1082   */
1083  public function EndBoundary($boundary) {
1084    return $this->LE . '--' . $boundary . '--' . $this->LE;
1085  }
1086
1087  /**
1088   * Sets the message type.
1089   * @access public
1090   * @return void
1091   */
1092  public function SetMessageType() {
1093    if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
1094      $this->message_type = 'plain';
1095    } else {
1096      if(count($this->attachment) > 0) {
1097        $this->message_type = 'attachments';
1098      }
1099      if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
1100        $this->message_type = 'alt';
1101      }
1102      if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
1103        $this->message_type = 'alt_attachments';
1104      }
1105    }
1106  }
1107
1108  /* Returns a formatted header line.
1109   * @access public
1110   * @return string
1111   */
1112  public function HeaderLine($name, $value) {
1113    return $name . ': ' . $value . $this->LE;
1114  }
1115
1116  /**
1117   * Returns a formatted mail line.
1118   * @access public
1119   * @return string
1120   */
1121  public function TextLine($value) {
1122    return $value . $this->LE;
1123  }
1124
1125  /////////////////////////////////////////////////
1126  // CLASS METHODS, ATTACHMENTS
1127  /////////////////////////////////////////////////
1128
1129  /**
1130   * Adds an attachment from a path on the filesystem.
1131   * Returns false if the file could not be found
1132   * or accessed.
1133   * @param string $path Path to the attachment.
1134   * @param string $name Overrides the attachment name.
1135   * @param string $encoding File encoding (see $Encoding).
1136   * @param string $type File extension (MIME) type.
1137   * @return bool
1138   */
1139  public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1140    if(!@is_file($path)) {
1141      $this->SetError($this->Lang('file_access') . $path);
1142      return false;
1143    }
1144
1145    $filename = basename($path);
1146    if($name == '') {
1147      $name = $filename;
1148    }
1149
1150    $cur = count($this->attachment);
1151    $this->attachment[$cur][0] = $path;
1152    $this->attachment[$cur][1] = $filename;
1153    $this->attachment[$cur][2] = $name;
1154    $this->attachment[$cur][3] = $encoding;
1155    $this->attachment[$cur][4] = $type;
1156    $this->attachment[$cur][5] = false; // isStringAttachment
1157    $this->attachment[$cur][6] = 'attachment';
1158    $this->attachment[$cur][7] = 0;
1159
1160    return true;
1161  }
1162
1163  /**
1164   * Attaches all fs, string, and binary attachments to the message.
1165   * Returns an empty string on failure.
1166   * @access public
1167   * @return string
1168   */
1169  public function AttachAll() {
1170    /* Return text of body */
1171    $mime = array();
1172
1173    /* Add all attachments */
1174    for($i = 0; $i < count($this->attachment); $i++) {
1175      /* Check for string attachment */
1176      $bString = $this->attachment[$i][5];
1177      if ($bString) {
1178        $string = $this->attachment[$i][0];
1179      } else {
1180        $path = $this->attachment[$i][0];
1181      }
1182
1183      $filename    = $this->attachment[$i][1];
1184      $name        = $this->attachment[$i][2];
1185      $encoding    = $this->attachment[$i][3];
1186      $type        = $this->attachment[$i][4];
1187      $disposition = $this->attachment[$i][6];
1188      $cid         = $this->attachment[$i][7];
1189
1190      $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
1191      //$mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
1192      $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);
1193      $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
1194
1195      if($disposition == 'inline') {
1196        $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
1197      }
1198
1199      //$mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LE.$this->LE);
1200      $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
1201
1202      /* Encode as string attachment */
1203      if($bString) {
1204        $mime[] = $this->EncodeString($string, $encoding);
1205        if($this->IsError()) {
1206          return '';
1207        }
1208        $mime[] = $this->LE.$this->LE;
1209      } else {
1210        $mime[] = $this->EncodeFile($path, $encoding);
1211        if($this->IsError()) {
1212          return '';
1213        }
1214        $mime[] = $this->LE.$this->LE;
1215      }
1216    }
1217
1218    $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
1219
1220    return join('', $mime);
1221  }
1222
1223  /**
1224   * Encodes attachment in requested format.  Returns an
1225   * empty string on failure.
1226   * @access public
1227   * @return string
1228   */
1229  public function EncodeFile ($path, $encoding = 'base64') {
1230    if(!@$fd = fopen($path, 'rb')) {
1231      $this->SetError($this->Lang('file_open') . $path);
1232      return '';
1233    }
1234    if (function_exists('get_magic_quotes')) {
1235        function get_magic_quotes() {
1236            return false;
1237        }
1238}
1239    if (PHP_VERSION < 6) {
1240      $magic_quotes = get_magic_quotes_runtime();
1241      set_magic_quotes_runtime(0);
1242    }
1243    $file_buffer  = file_get_contents($path);
1244    $file_buffer  = $this->EncodeString($file_buffer, $encoding);
1245    fclose($fd);
1246    if (PHP_VERSION < 6) { set_magic_quotes_runtime($magic_quotes); }
1247    return $file_buffer;
1248  }
1249
1250  /**
1251   * Encodes string to requested format. Returns an
1252   * empty string on failure.
1253   * @access public
1254   * @return string
1255   */
1256  public function EncodeString ($str, $encoding = 'base64') {
1257    $encoded = '';
1258    switch(strtolower($encoding)) {
1259      case 'base64':
1260        $encoded = chunk_split(base64_encode($str), 76, $this->LE);
1261        break;
1262      case '7bit':
1263      case '8bit':
1264        $encoded = $this->FixEOL($str);
1265        if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1266          $encoded .= $this->LE;
1267        break;
1268      case 'binary':
1269        $encoded = $str;
1270        break;
1271      case 'quoted-printable':
1272        $encoded = $this->EncodeQP($str);
1273        break;
1274      default:
1275        $this->SetError($this->Lang('encoding') . $encoding);
1276        break;
1277    }
1278    return $encoded;
1279  }
1280
1281  /**
1282   * Encode a header string to best of Q, B, quoted or none.
1283   * @access public
1284   * @return string
1285   */
1286  public function EncodeHeader ($str, $position = 'text') {
1287    $x = 0;
1288
1289    switch (strtolower($position)) {
1290      case 'phrase':
1291        if (!preg_match('/[\200-\377]/', $str)) {
1292          /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */
1293          $encoded = addcslashes($str, "\0..\37\177\\\"");
1294          if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
1295            return ($encoded);
1296          } else {
1297            return ("\"$encoded\"");
1298          }
1299        }
1300        $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
1301        break;
1302      case 'comment':
1303        $x = preg_match_all('/[()"]/', $str, $matches);
1304        /* Fall-through */
1305      case 'text':
1306      default:
1307        $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
1308        break;
1309    }
1310
1311    if ($x == 0) {
1312      return ($str);
1313    }
1314
1315    $maxlen = 75 - 7 - strlen($this->CharSet);
1316    /* Try to select the encoding which should produce the shortest output */
1317    if (strlen($str)/3 < $x) {
1318      $encoding = 'B';
1319      if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
1320     // Use a custom function which correctly encodes and wraps long
1321     // multibyte strings without breaking lines within a character
1322        $encoded = $this->Base64EncodeWrapMB($str);
1323      } else {
1324        $encoded = base64_encode($str);
1325        $maxlen -= $maxlen % 4;
1326        $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
1327      }
1328    } else {
1329      $encoding = 'Q';
1330      $encoded = $this->EncodeQ($str, $position);
1331      $encoded = $this->WrapText($encoded, $maxlen, true);
1332      $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
1333    }
1334
1335    $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
1336    $encoded = trim(str_replace("\n", $this->LE, $encoded));
1337
1338    return $encoded;
1339  }
1340
1341  /**
1342   * Checks if a string contains multibyte characters.
1343   * @access public
1344   * @param string $str multi-byte text to wrap encode
1345   * @return bool
1346   */
1347  public function HasMultiBytes($str) {
1348    if (function_exists('mb_strlen')) {
1349      return (strlen($str) > mb_strlen($str, $this->CharSet));
1350    } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
1351      return False;
1352    }
1353  }
1354
1355  /**
1356   * Correctly encodes and wraps long multibyte strings for mail headers
1357   * without breaking lines within a character.
1358   * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
1359   * @access public
1360   * @param string $str multi-byte text to wrap encode
1361   * @return string
1362   */
1363  public function Base64EncodeWrapMB($str) {
1364    $start = "=?".$this->CharSet."?B?";
1365    $end = "?=";
1366    $encoded = "";
1367
1368    $mb_length = mb_strlen($str, $this->CharSet);
1369    // Each line must have length <= 75, including $start and $end
1370    $length = 75 - strlen($start) - strlen($end);
1371    // Average multi-byte ratio
1372    $ratio = $mb_length / strlen($str);
1373    // Base64 has a 4:3 ratio
1374    $offset = $avgLength = floor($length * $ratio * .75);
1375
1376    for ($i = 0; $i < $mb_length; $i += $offset) {
1377      $lookBack = 0;
1378
1379      do {
1380        $offset = $avgLength - $lookBack;
1381        $chunk = mb_substr($str, $i, $offset, $this->CharSet);
1382        $chunk = base64_encode($chunk);
1383        $lookBack++;
1384      }
1385      while (strlen($chunk) > $length);
1386
1387      $encoded .= $chunk . $this->LE;
1388    }
1389
1390    // Chomp the last linefeed
1391    $encoded = substr($encoded, 0, -strlen($this->LE));
1392    return $encoded;
1393  }
1394
1395  /**
1396  * Encode string to quoted-printable.
1397  * @access public
1398  * @param string $string the text to encode
1399  * @param integer $line_max Number of chars allowed on a line before wrapping
1400  * @return string
1401  */
1402  public function EncodeQP( $input = '', $line_max = 76, $space_conv = false ) {
1403    $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
1404    $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
1405    $eol = "\r\n";
1406    $escape = '=';
1407    $output = '';
1408    while( list(, $line) = each($lines) ) {
1409      $linlen = strlen($line);
1410      $newline = '';
1411      for($i = 0; $i < $linlen; $i++) {
1412        $c = substr( $line, $i, 1 );
1413        $dec = ord( $c );
1414        if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
1415          $c = '=2E';
1416        }
1417        if ( $dec == 32 ) {
1418          if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
1419            $c = '=20';
1420          } else if ( $space_conv ) {
1421            $c = '=20';
1422          }
1423        } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
1424          $h2 = floor($dec/16);
1425          $h1 = floor($dec%16);
1426          $c = $escape.$hex[$h2].$hex[$h1];
1427        }
1428        if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
1429          $output .= $newline.$escape.$eol; //  soft line break; " =\r\n" is okay
1430          $newline = '';
1431          // check if newline first character will be point or not
1432          if ( $dec == 46 ) {
1433            $c = '=2E';
1434          }
1435        }
1436        $newline .= $c;
1437      } // end of for
1438      $output .= $newline.$eol;
1439    } // end of while
1440    return $output;
1441  }
1442
1443  /**
1444   * Encode string to q encoding.
1445   * @access public
1446   * @return string
1447   */
1448  public function EncodeQ ($str, $position = 'text') {
1449    /* There should not be any EOL in the string */
1450    $encoded = preg_replace("[\r\n]", '', $str);
1451
1452    switch (strtolower($position)) {
1453      case 'phrase':
1454        $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1455        break;
1456      case 'comment':
1457        $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1458      case 'text':
1459      default:
1460        /* Replace every high ascii, control =, ? and _ characters */
1461        $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
1462              "'='.sprintf('%02X', ord('\\1'))", $encoded);
1463        break;
1464    }
1465
1466    /* Replace every spaces to _ (more readable than =20) */
1467    $encoded = str_replace(' ', '_', $encoded);
1468
1469    return $encoded;
1470  }
1471
1472  /**
1473   * Adds a string or binary attachment (non-filesystem) to the list.
1474   * This method can be used to attach ascii or binary data,
1475   * such as a BLOB record from a database.
1476   * @param string $string String attachment data.
1477   * @param string $filename Name of the attachment.
1478   * @param string $encoding File encoding (see $Encoding).
1479   * @param string $type File extension (MIME) type.
1480   * @return void
1481   */
1482  public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
1483    /* Append to $attachment array */
1484    $cur = count($this->attachment);
1485    $this->attachment[$cur][0] = $string;
1486    $this->attachment[$cur][1] = $filename;
1487    $this->attachment[$cur][2] = $filename;
1488    $this->attachment[$cur][3] = $encoding;
1489    $this->attachment[$cur][4] = $type;
1490    $this->attachment[$cur][5] = true; // isString
1491    $this->attachment[$cur][6] = 'attachment';
1492    $this->attachment[$cur][7] = 0;
1493  }
1494
1495  /**
1496   * Adds an embedded attachment.  This can include images, sounds, and
1497   * just about any other document.  Make sure to set the $type to an
1498   * image type.  For JPEG images use "image/jpeg" and for GIF images
1499   * use "image/gif".
1500   * @param string $path Path to the attachment.
1501   * @param string $cid Content ID of the attachment.  Use this to identify
1502   *        the Id for accessing the image in an HTML form.
1503   * @param string $name Overrides the attachment name.
1504   * @param string $encoding File encoding (see $Encoding).
1505   * @param string $type File extension (MIME) type.
1506   * @return bool
1507   */
1508  public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1509
1510    if(!@is_file($path)) {
1511      $this->SetError($this->Lang('file_access') . $path);
1512      return false;
1513    }
1514
1515    $filename = basename($path);
1516    if($name == '') {
1517      $name = $filename;
1518    }
1519
1520    /* Append to $attachment array */
1521    $cur = count($this->attachment);
1522    $this->attachment[$cur][0] = $path;
1523    $this->attachment[$cur][1] = $filename;
1524    $this->attachment[$cur][2] = $name;
1525    $this->attachment[$cur][3] = $encoding;
1526    $this->attachment[$cur][4] = $type;
1527    $this->attachment[$cur][5] = false;
1528    $this->attachment[$cur][6] = 'inline';
1529    $this->attachment[$cur][7] = $cid;
1530
1531    return true;
1532  }
1533
1534  /**
1535   * Returns true if an inline attachment is present.
1536   * @access public
1537   * @return bool
1538   */
1539  public function InlineImageExists() {
1540    $result = false;
1541    for($i = 0; $i < count($this->attachment); $i++) {
1542      if($this->attachment[$i][6] == 'inline') {
1543        $result = true;
1544        break;
1545      }
1546    }
1547
1548    return $result;
1549  }
1550
1551  /////////////////////////////////////////////////
1552  // CLASS METHODS, MESSAGE RESET
1553  /////////////////////////////////////////////////
1554
1555  /**
1556   * Clears all recipients assigned in the TO array.  Returns void.
1557   * @return void
1558   */
1559  public function ClearAddresses() {
1560    $this->to = array();
1561  }
1562
1563  /**
1564   * Clears all recipients assigned in the CC array.  Returns void.
1565   * @return void
1566   */
1567  public function ClearCCs() {
1568    $this->cc = array();
1569  }
1570
1571  /**
1572   * Clears all recipients assigned in the BCC array.  Returns void.
1573   * @return void
1574   */
1575  public function ClearBCCs() {
1576    $this->bcc = array();
1577  }
1578
1579  /**
1580   * Clears all recipients assigned in the ReplyTo array.  Returns void.
1581   * @return void
1582   */
1583  public function ClearReplyTos() {
1584    $this->ReplyTo = array();
1585  }
1586
1587  /**
1588   * Clears all recipients assigned in the TO, CC and BCC
1589   * array.  Returns void.
1590   * @return void
1591   */
1592  public function ClearAllRecipients() {
1593    $this->to = array();
1594    $this->cc = array();
1595    $this->bcc = array();
1596  }
1597
1598  /**
1599   * Clears all previously set filesystem, string, and binary
1600   * attachments.  Returns void.
1601   * @return void
1602   */
1603  public function ClearAttachments() {
1604    $this->attachment = array();
1605  }
1606
1607  /**
1608   * Clears all custom headers.  Returns void.
1609   * @return void
1610   */
1611  public function ClearCustomHeaders() {
1612    $this->CustomHeader = array();
1613  }
1614
1615  /////////////////////////////////////////////////
1616  // CLASS METHODS, MISCELLANEOUS
1617  /////////////////////////////////////////////////
1618
1619  /**
1620   * Adds the error message to the error container.
1621   * Returns void.
1622   * @access private
1623   * @return void
1624   */
1625  private function SetError($msg) {
1626    $this->error_count++;
1627    $this->ErrorInfo = $msg;
1628  }
1629
1630  /**
1631   * Returns the proper RFC 822 formatted date.
1632   * @access private
1633   * @return string
1634   */
1635  private static function RFCDate() {
1636    $tz = date('Z');
1637    $tzs = ($tz < 0) ? '-' : '+';
1638    $tz = abs($tz);
1639    $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
1640    $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
1641
1642    return $result;
1643  }
1644
1645  /**
1646   * Returns the server hostname or 'localhost.localdomain' if unknown.
1647   * @access private
1648   * @return string
1649   */
1650  private function ServerHostname() {
1651    if (!empty($this->Hostname)) {
1652      $result = $this->Hostname;
1653    } elseif (isset($_SERVER['SERVER_NAME'])) {
1654      $result = $_SERVER['SERVER_NAME'];
1655    } else {
1656      $result = "localhost.localdomain";
1657    }
1658
1659    return $result;
1660  }
1661
1662  /**
1663   * Returns a message in the appropriate language.
1664   * @access private
1665   * @return string
1666   */
1667  private function Lang($key) {
1668    if(count($this->language) < 1) {
1669      $this->SetLanguage('en'); // set the default language
1670    }
1671
1672    if(isset($this->language[$key])) {
1673      return $this->language[$key];
1674    } else {
1675      return 'Language string failed to load: ' . $key;
1676    }
1677  }
1678
1679  /**
1680   * Returns true if an error occurred.
1681   * @access public
1682   * @return bool
1683   */
1684  public function IsError() {
1685    return ($this->error_count > 0);
1686  }
1687
1688  /**
1689   * Changes every end of line from CR or LF to CRLF.
1690   * @access private
1691   * @return string
1692   */
1693  private function FixEOL($str) {
1694    $str = str_replace("\r\n", "\n", $str);
1695    $str = str_replace("\r", "\n", $str);
1696    $str = str_replace("\n", $this->LE, $str);
1697    return $str;
1698  }
1699
1700  /**
1701   * Adds a custom header.
1702   * @access public
1703   * @return void
1704   */
1705  public function AddCustomHeader($custom_header) {
1706    $this->CustomHeader[] = explode(':', $custom_header, 2);
1707  }
1708
1709  /**
1710   * Evaluates the message and returns modifications for inline images and backgrounds
1711   * @access public
1712   * @return $message
1713   */
1714  public function MsgHTML($message,$basedir='') {
1715    preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
1716    if(isset($images[2])) {
1717      foreach($images[2] as $i => $url) {
1718        // do not change urls for absolute images (thanks to corvuscorax)
1719        if (!preg_match('/^[A-z][A-z]*:\/\//',$url)) {
1720          $filename = basename($url);
1721          $directory = dirname($url);
1722          ($directory == '.')?$directory='':'';
1723          $cid = 'cid:' . md5($filename);
1724          $fileParts = split("\.", $filename);
1725          $ext = $fileParts[1];
1726          $mimeType = $this->_mime_types($ext);
1727          if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }
1728          if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }
1729          if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {
1730            $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
1731          }
1732        }
1733      }
1734    }
1735    $this->IsHTML(true);
1736    $this->Body = $message;
1737    $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));
1738    if ( !empty($textMsg) && empty($this->AltBody) ) {
1739      $this->AltBody = html_entity_decode($textMsg);
1740    }
1741    if ( empty($this->AltBody) ) {
1742      $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n";
1743    }
1744  }
1745
1746  /**
1747   * Gets the mime type of the embedded or inline image
1748   * @access public
1749   * @return mime type of ext
1750   */
1751  public function _mime_types($ext = '') {
1752    $mimes = array(
1753      'hqx'   =>  'application/mac-binhex40',
1754      'cpt'   =>  'application/mac-compactpro',
1755      'doc'   =>  'application/msword',
1756      'bin'   =>  'application/macbinary',
1757      'dms'   =>  'application/octet-stream',
1758      'lha'   =>  'application/octet-stream',
1759      'lzh'   =>  'application/octet-stream',
1760      'exe'   =>  'application/octet-stream',
1761      'class' =>  'application/octet-stream',
1762      'psd'   =>  'application/octet-stream',
1763      'so'    =>  'application/octet-stream',
1764      'sea'   =>  'application/octet-stream',
1765      'dll'   =>  'application/octet-stream',
1766      'oda'   =>  'application/oda',
1767      'pdf'   =>  'application/pdf',
1768      'ai'    =>  'application/postscript',
1769      'eps'   =>  'application/postscript',
1770      'ps'    =>  'application/postscript',
1771      'smi'   =>  'application/smil',
1772      'smil'  =>  'application/smil',
1773      'mif'   =>  'application/vnd.mif',
1774      'xls'   =>  'application/vnd.ms-excel',
1775      'ppt'   =>  'application/vnd.ms-powerpoint',
1776      'wbxml' =>  'application/vnd.wap.wbxml',
1777      'wmlc'  =>  'application/vnd.wap.wmlc',
1778      'dcr'   =>  'application/x-director',
1779      'dir'   =>  'application/x-director',
1780      'dxr'   =>  'application/x-director',
1781      'dvi'   =>  'application/x-dvi',
1782      'gtar'  =>  'application/x-gtar',
1783      'php'   =>  'application/x-httpd-php',
1784      'php4'  =>  'application/x-httpd-php',
1785      'php3'  =>  'application/x-httpd-php',
1786      'phtml' =>  'application/x-httpd-php',
1787      'phps'  =>  'application/x-httpd-php-source',
1788      'js'    =>  'application/x-javascript',
1789      'swf'   =>  'application/x-shockwave-flash',
1790      'sit'   =>  'application/x-stuffit',
1791      'tar'   =>  'application/x-tar',
1792      'tgz'   =>  'application/x-tar',
1793      'xhtml' =>  'application/xhtml+xml',
1794      'xht'   =>  'application/xhtml+xml',
1795      'zip'   =>  'application/zip',
1796      'mid'   =>  'audio/midi',
1797      'midi'  =>  'audio/midi',
1798      'mpga'  =>  'audio/mpeg',
1799      'mp2'   =>  'audio/mpeg',
1800      'mp3'   =>  'audio/mpeg',
1801      'aif'   =>  'audio/x-aiff',
1802      'aiff'  =>  'audio/x-aiff',
1803      'aifc'  =>  'audio/x-aiff',
1804      'ram'   =>  'audio/x-pn-realaudio',
1805      'rm'    =>  'audio/x-pn-realaudio',
1806      'rpm'   =>  'audio/x-pn-realaudio-plugin',
1807      'ra'    =>  'audio/x-realaudio',
1808      'rv'    =>  'video/vnd.rn-realvideo',
1809      'wav'   =>  'audio/x-wav',
1810      'bmp'   =>  'image/bmp',
1811      'gif'   =>  'image/gif',
1812      'jpeg'  =>  'image/jpeg',
1813      'jpg'   =>  'image/jpeg',
1814      'jpe'   =>  'image/jpeg',
1815      'png'   =>  'image/png',
1816      'tiff'  =>  'image/tiff',
1817      'tif'   =>  'image/tiff',
1818      'css'   =>  'text/css',
1819      'html'  =>  'text/html',
1820      'htm'   =>  'text/html',
1821      'shtml' =>  'text/html',
1822      'txt'   =>  'text/plain',
1823      'text'  =>  'text/plain',
1824      'log'   =>  'text/plain',
1825      'rtx'   =>  'text/richtext',
1826      'rtf'   =>  'text/rtf',
1827      'xml'   =>  'text/xml',
1828      'xsl'   =>  'text/xml',
1829      'mpeg'  =>  'video/mpeg',
1830      'mpg'   =>  'video/mpeg',
1831      'mpe'   =>  'video/mpeg',
1832      'qt'    =>  'video/quicktime',
1833      'mov'   =>  'video/quicktime',
1834      'avi'   =>  'video/x-msvideo',
1835      'movie' =>  'video/x-sgi-movie',
1836      'doc'   =>  'application/msword',
1837      'word'  =>  'application/msword',
1838      'xl'    =>  'application/excel',
1839      'eml'   =>  'message/rfc822'
1840    );
1841    return ( ! isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
1842  }
1843
1844  /**
1845   * Set (or reset) Class Objects (variables)
1846   *
1847   * Usage Example:
1848   * $page->set('X-Priority', '3');
1849   *
1850   * @access public
1851   * @param string $name Parameter Name
1852   * @param mixed $value Parameter Value
1853   * NOTE: will not work with arrays, there are no arrays to set/reset
1854   */
1855  public function set ( $name, $value = '' ) {
1856    if ( isset($this->$name) ) {
1857      $this->$name = $value;
1858    } else {
1859      $this->SetError('Cannot set or reset variable ' . $name);
1860      return false;
1861    }
1862  }
1863
1864  /**
1865   * Read a file from a supplied filename and return it.
1866   *
1867   * @access public
1868   * @param string $filename Parameter File Name
1869   */
1870  public function getFile($filename) {
1871    $return = '';
1872    if ($fp = fopen($filename, 'rb')) {
1873      while (!feof($fp)) {
1874        $return .= fread($fp, 1024);
1875      }
1876      fclose($fp);
1877      return $return;
1878    } else {
1879      return false;
1880    }
1881  }
1882
1883  /**
1884   * Strips newlines to prevent header injection.
1885   * @access public
1886   * @param string $str String
1887   * @return string
1888   */
1889  public function SecureHeader($str) {
1890    $str = trim($str);
1891    $str = str_replace("\r", "", $str);
1892    $str = str_replace("\n", "", $str);
1893    return $str;
1894  }
1895
1896  /**
1897   * Set the private key file and password to sign the message.
1898   *
1899   * @access public
1900   * @param string $key_filename Parameter File Name
1901   * @param string $key_pass Password for private key
1902   */
1903  public function Sign($cert_filename, $key_filename, $key_pass) {
1904    $this->sign_cert_file = $cert_filename;
1905    $this->sign_key_file = $key_filename;
1906    $this->sign_key_pass = $key_pass;
1907  }
1908}
1909
1910?>
Note: See TracBrowser for help on using the repository browser.