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