]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/class-phpmailer.php
Wordpress 2.3.3
[autoinstalls/wordpress.git] / wp-includes / class-phpmailer.php
1 <?php
2 ////////////////////////////////////////////////////
3 // PHPMailer - PHP email class
4 //
5 // Class for sending email using either
6 // sendmail, PHP mail(), or SMTP.  Methods are
7 // based upon the standard AspEmail(tm) classes.
8 //
9 // Copyright (C) 2001 - 2003  Brent R. Matzelle
10 //
11 // License: LGPL, see LICENSE
12 ////////////////////////////////////////////////////
13
14 /**
15  * PHPMailer - PHP email transport class
16  * @package PHPMailer
17  * @author Brent R. Matzelle
18  * @copyright 2001 - 2003 Brent R. Matzelle
19  */
20 class PHPMailer
21 {
22     /////////////////////////////////////////////////
23     // PUBLIC VARIABLES
24     /////////////////////////////////////////////////
25
26     /**
27      * Email priority (1 = High, 3 = Normal, 5 = low).
28      * @var int
29      */
30     var $Priority          = 3;
31
32     /**
33      * Sets the CharSet of the message.
34      * @var string
35      */
36     var $CharSet           = "UTF-8";
37
38     /**
39      * Sets the Content-type of the message.
40      * @var string
41      */
42     var $ContentType        = "text/plain";
43
44     /**
45      * Sets the Encoding of the message. Options for this are "8bit",
46      * "7bit", "binary", "base64", and "quoted-printable".
47      * @var string
48      */
49     var $Encoding          = "8bit";
50
51     /**
52      * Holds the most recent mailer error message.
53      * @var string
54      */
55     var $ErrorInfo         = "";
56
57     /**
58      * Sets the From email address for the message.
59      * @var string
60      */
61     var $From               = "support@wordpress.com";
62
63     /**
64      * Sets the From name of the message.
65      * @var string
66      */
67     var $FromName           = "Support";
68
69     /**
70      * Sets the Sender email (Return-Path) of the message.  If not empty,
71      * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
72      * @var string
73      */
74     var $Sender            = "";
75
76     /**
77      * Sets the Subject of the message.
78      * @var string
79      */
80     var $Subject           = "";
81
82     /**
83      * Sets the Body of the message.  This can be either an HTML or text body.
84      * If HTML then run IsHTML(true).
85      * @var string
86      */
87     var $Body               = "";
88
89     /**
90      * Sets the text-only body of the message.  This automatically sets the
91      * email to multipart/alternative.  This body can be read by mail
92      * clients that do not have HTML email capability such as mutt. Clients
93      * that can read HTML will view the normal Body.
94      * @var string
95      */
96     var $AltBody           = "";
97
98     /**
99      * Sets word wrapping on the body of the message to a given number of
100      * characters.
101      * @var int
102      */
103     var $WordWrap          = 0;
104
105     /**
106      * Method to send mail: ("mail", "sendmail", or "smtp").
107      * @var string
108      */
109     var $Mailer            = "mail";
110
111     /**
112      * Sets the path of the sendmail program.
113      * @var string
114      */
115     var $Sendmail          = "/usr/sbin/sendmail";
116
117     /**
118      * Path to PHPMailer plugins.  This is now only useful if the SMTP class
119      * is in a different directory than the PHP include path.
120      * @var string
121      */
122     var $PluginDir         = "";
123
124     /**
125      *  Holds PHPMailer version.
126      *  @var string
127      */
128     var $Version           = "1.73";
129
130     /**
131      * Sets the email address that a reading confirmation will be sent.
132      * @var string
133      */
134     var $ConfirmReadingTo  = "";
135
136     /**
137      *  Sets the hostname to use in Message-Id and Received headers
138      *  and as default HELO string. If empty, the value returned
139      *  by SERVER_NAME is used or 'localhost.localdomain'.
140      *  @var string
141      */
142     var $Hostname          = "";
143
144     /////////////////////////////////////////////////
145     // SMTP VARIABLES
146     /////////////////////////////////////////////////
147
148     /**
149      *  Sets the SMTP hosts.  All hosts must be separated by a
150      *  semicolon.  You can also specify a different port
151      *  for each host by using this format: [hostname:port]
152      *  (e.g. "smtp1.example.com:25;smtp2.example.com").
153      *  Hosts will be tried in order.
154      *  @var string
155      */
156     var $Host        = "localhost";
157
158     /**
159      *  Sets the default SMTP server port.
160      *  @var int
161      */
162     var $Port        = 25;
163
164     /**
165      *  Sets the SMTP HELO of the message (Default is $Hostname).
166      *  @var string
167      */
168     var $Helo        = "";
169
170     /**
171      *  Sets SMTP authentication. Utilizes the Username and Password variables.
172      *  @var bool
173      */
174     var $SMTPAuth     = false;
175
176     /**
177      *  Sets SMTP username.
178      *  @var string
179      */
180     var $Username     = "";
181
182     /**
183      *  Sets SMTP password.
184      *  @var string
185      */
186     var $Password     = "";
187
188     /**
189      *  Sets the SMTP server timeout in seconds. This function will not
190      *  work with the win32 version.
191      *  @var int
192      */
193     var $Timeout      = 10;
194
195     /**
196      *  Sets SMTP class debugging on or off.
197      *  @var bool
198      */
199     var $SMTPDebug    = false;
200
201     /**
202      * Prevents the SMTP connection from being closed after each mail
203      * sending.  If this is set to true then to close the connection
204      * requires an explicit call to SmtpClose().
205      * @var bool
206      */
207     var $SMTPKeepAlive = false;
208
209     /**#@+
210      * @access private
211      */
212     var $smtp            = NULL;
213     var $to              = array();
214     var $cc              = array();
215     var $bcc             = array();
216     var $ReplyTo         = array();
217     var $attachment      = array();
218     var $CustomHeader    = array();
219     var $message_type    = "";
220     var $boundary        = array();
221     var $language        = array();
222     var $error_count     = 0;
223     var $LE              = "\n";
224     /**#@-*/
225
226     /////////////////////////////////////////////////
227     // VARIABLE METHODS
228     /////////////////////////////////////////////////
229
230     /**
231      * Sets message type to HTML.
232      * @param bool $bool
233      * @return void
234      */
235     function IsHTML($bool) {
236         if($bool == true)
237             $this->ContentType = "text/html";
238         else
239             $this->ContentType = "text/plain";
240     }
241
242     /**
243      * Sets Mailer to send message using SMTP.
244      * @return void
245      */
246     function IsSMTP() {
247         $this->Mailer = "smtp";
248     }
249
250     /**
251      * Sets Mailer to send message using PHP mail() function.
252      * @return void
253      */
254     function IsMail() {
255         $this->Mailer = "mail";
256     }
257
258     /**
259      * Sets Mailer to send message using the $Sendmail program.
260      * @return void
261      */
262     function IsSendmail() {
263         $this->Mailer = "sendmail";
264     }
265
266     /**
267      * Sets Mailer to send message using the qmail MTA.
268      * @return void
269      */
270     function IsQmail() {
271         $this->Sendmail = "/var/qmail/bin/sendmail";
272         $this->Mailer = "sendmail";
273     }
274
275
276     /////////////////////////////////////////////////
277     // RECIPIENT METHODS
278     /////////////////////////////////////////////////
279
280     /**
281      * Adds a "To" address.
282      * @param string $address
283      * @param string $name
284      * @return void
285      */
286     function AddAddress($address, $name = "") {
287         $cur = count($this->to);
288         $this->to[$cur][0] = trim($address);
289         $this->to[$cur][1] = $name;
290     }
291
292     /**
293      * Adds a "Cc" address. Note: this function works
294      * with the SMTP mailer on win32, not with the "mail"
295      * mailer.
296      * @param string $address
297      * @param string $name
298      * @return void
299     */
300     function AddCC($address, $name = "") {
301         $cur = count($this->cc);
302         $this->cc[$cur][0] = trim($address);
303         $this->cc[$cur][1] = $name;
304     }
305
306     /**
307      * Adds a "Bcc" address. Note: this function works
308      * with the SMTP mailer on win32, not with the "mail"
309      * mailer.
310      * @param string $address
311      * @param string $name
312      * @return void
313      */
314     function AddBCC($address, $name = "") {
315         $cur = count($this->bcc);
316         $this->bcc[$cur][0] = trim($address);
317         $this->bcc[$cur][1] = $name;
318     }
319
320     /**
321      * Adds a "Reply-to" address.
322      * @param string $address
323      * @param string $name
324      * @return void
325      */
326     function AddReplyTo($address, $name = "") {
327         $cur = count($this->ReplyTo);
328         $this->ReplyTo[$cur][0] = trim($address);
329         $this->ReplyTo[$cur][1] = $name;
330     }
331
332
333     /////////////////////////////////////////////////
334     // MAIL SENDING METHODS
335     /////////////////////////////////////////////////
336
337     /**
338      * Creates message and assigns Mailer. If the message is
339      * not sent successfully then it returns false.  Use the ErrorInfo
340      * variable to view description of the error.
341      * @return bool
342      */
343     function Send() {
344         $header = "";
345         $body = "";
346         $result = true;
347
348         if((count($this->to) + count($this->cc) + count($this->bcc)) < 1)
349         {
350             $this->SetError($this->Lang("provide_address"));
351             return false;
352         }
353
354         // Set whether the message is multipart/alternative
355         if(!empty($this->AltBody))
356             $this->ContentType = "multipart/alternative";
357
358         $this->error_count = 0; // reset errors
359         $this->SetMessageType();
360         $header .= $this->CreateHeader();
361         $body = $this->CreateBody();
362
363         if($body == "") { return false; }
364
365         // Choose the mailer
366         switch($this->Mailer)
367         {
368             case "sendmail":
369                 $result = $this->SendmailSend($header, $body);
370                 break;
371             case "mail":
372                 $result = $this->MailSend($header, $body);
373                 break;
374             case "smtp":
375                 $result = $this->SmtpSend($header, $body);
376                 break;
377             default:
378             $this->SetError($this->Mailer . $this->Lang("mailer_not_supported"));
379                 $result = false;
380                 break;
381         }
382
383         return $result;
384     }
385
386     /**
387      * Sends mail using the $Sendmail program.
388      * @access private
389      * @return bool
390      */
391     function SendmailSend($header, $body) {
392         if ($this->Sender != "")
393             $sendmail = sprintf("%s -oi -f %s -t", $this->Sendmail, escapeshellarg($this->Sender));
394         else
395             $sendmail = sprintf("%s -oi -t", $this->Sendmail);
396
397         if(!@$mail = popen($sendmail, "w"))
398         {
399             $this->SetError($this->Lang("execute") . $this->Sendmail);
400             return false;
401         }
402
403         fputs($mail, $header);
404         fputs($mail, $body);
405
406         $result = pclose($mail) >> 8 & 0xFF;
407         if($result != 0)
408         {
409             $this->SetError($this->Lang("execute") . $this->Sendmail);
410             return false;
411         }
412
413         return true;
414     }
415
416     /**
417      * Sends mail using the PHP mail() function.
418      * @access private
419      * @return bool
420      */
421     function MailSend($header, $body) {
422         $to = "";
423         for($i = 0; $i < count($this->to); $i++)
424         {
425             if($i != 0) { $to .= ", "; }
426             $to .= $this->to[$i][0];
427         }
428
429         if ($this->Sender != "" && strlen(ini_get("safe_mode"))< 1)
430         {
431             $old_from = ini_get("sendmail_from");
432             ini_set("sendmail_from", $this->Sender);
433             $params = sprintf("-oi -f %s", $this->Sender);
434             $rt = @mail($to, $this->EncodeHeader($this->Subject), $body,
435                         $header, $params);
436         }
437         else
438             $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, $header);
439
440         if (isset($old_from))
441             ini_set("sendmail_from", $old_from);
442
443         if(!$rt)
444         {
445             $this->SetError($this->Lang("instantiate"));
446             return false;
447         }
448
449         return true;
450     }
451
452     /**
453      * Sends mail via SMTP using PhpSMTP (Author:
454      * Chris Ryan).  Returns bool.  Returns false if there is a
455      * bad MAIL FROM, RCPT, or DATA input.
456      * @access private
457      * @return bool
458      */
459     function SmtpSend($header, $body) {
460         include_once($this->PluginDir . "class-smtp.php");
461         $error = "";
462         $bad_rcpt = array();
463
464         if(!$this->SmtpConnect())
465             return false;
466
467         $smtp_from = ($this->Sender == "") ? $this->From : $this->Sender;
468         if(!$this->smtp->Mail($smtp_from))
469         {
470             $error = $this->Lang("from_failed") . $smtp_from;
471             $this->SetError($error);
472             $this->smtp->Reset();
473             return false;
474         }
475
476         // Attempt to send attach all recipients
477         for($i = 0; $i < count($this->to); $i++)
478         {
479             if(!$this->smtp->Recipient($this->to[$i][0]))
480                 $bad_rcpt[] = $this->to[$i][0];
481         }
482         for($i = 0; $i < count($this->cc); $i++)
483         {
484             if(!$this->smtp->Recipient($this->cc[$i][0]))
485                 $bad_rcpt[] = $this->cc[$i][0];
486         }
487         for($i = 0; $i < count($this->bcc); $i++)
488         {
489             if(!$this->smtp->Recipient($this->bcc[$i][0]))
490                 $bad_rcpt[] = $this->bcc[$i][0];
491         }
492
493         if(count($bad_rcpt) > 0) // Create error message
494         {
495             for($i = 0; $i < count($bad_rcpt); $i++)
496             {
497                 if($i != 0) { $error .= ", "; }
498                 $error .= $bad_rcpt[$i];
499             }
500             $error = $this->Lang("recipients_failed") . $error;
501             $this->SetError($error);
502             $this->smtp->Reset();
503             return false;
504         }
505
506         if(!$this->smtp->Data($header . $body))
507         {
508             $this->SetError($this->Lang("data_not_accepted"));
509             $this->smtp->Reset();
510             return false;
511         }
512         if($this->SMTPKeepAlive == true)
513             $this->smtp->Reset();
514         else
515             $this->SmtpClose();
516
517         return true;
518     }
519
520     /**
521      * Initiates a connection to an SMTP server.  Returns false if the
522      * operation failed.
523      * @access private
524      * @return bool
525      */
526     function SmtpConnect() {
527         if($this->smtp == NULL) { $this->smtp = new SMTP(); }
528
529         $this->smtp->do_debug = $this->SMTPDebug;
530         $hosts = explode(";", $this->Host);
531         $index = 0;
532         $connection = ($this->smtp->Connected());
533
534         // Retry while there is no connection
535         while($index < count($hosts) && $connection == false)
536         {
537             if(strstr($hosts[$index], ":"))
538                 list($host, $port) = explode(":", $hosts[$index]);
539             else
540             {
541                 $host = $hosts[$index];
542                 $port = $this->Port;
543             }
544
545             if($this->smtp->Connect($host, $port, $this->Timeout))
546             {
547                 if ($this->Helo != '')
548                     $this->smtp->Hello($this->Helo);
549                 else
550                     $this->smtp->Hello($this->ServerHostname());
551
552                 if($this->SMTPAuth)
553                 {
554                     if(!$this->smtp->Authenticate($this->Username,
555                                                   $this->Password))
556                     {
557                         $this->SetError($this->Lang("authenticate"));
558                         $this->smtp->Reset();
559                         $connection = false;
560                     }
561                 }
562                 $connection = true;
563             }
564             $index++;
565         }
566         if(!$connection)
567             $this->SetError($this->Lang("connect_host"));
568
569         return $connection;
570     }
571
572     /**
573      * Closes the active SMTP session if one exists.
574      * @return void
575      */
576     function SmtpClose() {
577         if($this->smtp != NULL)
578         {
579             if($this->smtp->Connected())
580             {
581                 $this->smtp->Quit();
582                 $this->smtp->Close();
583             }
584         }
585     }
586
587     /**
588      * Sets the language for all class error messages.  Returns false
589      * if it cannot load the language file.  The default language type
590      * is English.
591      * @param string $lang_type Type of language (e.g. Portuguese: "br")
592      * @param string $lang_path Path to the language file directory
593      * @access public
594      * @return bool
595      */
596     function SetLanguage($lang_type, $lang_path = "language/") {
597         if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php'))
598             include($lang_path.'phpmailer.lang-'.$lang_type.'.php');
599         else if(file_exists($lang_path.'phpmailer.lang-en.php'))
600             include($lang_path.'phpmailer.lang-en.php');
601         else
602         {
603             $this->SetError("Could not load language file");
604             return false;
605         }
606         $this->language = $PHPMAILER_LANG;
607
608         return true;
609     }
610
611     /////////////////////////////////////////////////
612     // MESSAGE CREATION METHODS
613     /////////////////////////////////////////////////
614
615     /**
616      * Creates recipient headers.
617      * @access private
618      * @return string
619      */
620     function AddrAppend($type, $addr) {
621         $addr_str = $type . ": ";
622         $addr_str .= $this->AddrFormat($addr[0]);
623         if(count($addr) > 1)
624         {
625             for($i = 1; $i < count($addr); $i++)
626                 $addr_str .= ", " . $this->AddrFormat($addr[$i]);
627         }
628         $addr_str .= $this->LE;
629
630         return $addr_str;
631     }
632
633     /**
634      * Formats an address correctly.
635      * @access private
636      * @return string
637      */
638     function AddrFormat($addr) {
639         if(empty($addr[1]))
640             $formatted = $addr[0];
641         else
642         {
643             $formatted = $this->EncodeHeader($addr[1], 'phrase') . " <" .
644                          $addr[0] . ">";
645         }
646
647         return $formatted;
648     }
649
650     /**
651      * Wraps message for use with mailers that do not
652      * automatically perform wrapping and for quoted-printable.
653      * Original written by philippe.
654      * @access private
655      * @return string
656      */
657     function WrapText($message, $length, $qp_mode = false) {
658         $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
659
660         $message = $this->FixEOL($message);
661         if (substr($message, -1) == $this->LE)
662             $message = substr($message, 0, -1);
663
664         $line = explode($this->LE, $message);
665         $message = "";
666         for ($i=0 ;$i < count($line); $i++)
667         {
668           $line_part = explode(" ", $line[$i]);
669           $buf = "";
670           for ($e = 0; $e<count($line_part); $e++)
671           {
672               $word = $line_part[$e];
673               if ($qp_mode and (strlen($word) > $length))
674               {
675                 $space_left = $length - strlen($buf) - 1;
676                 if ($e != 0)
677                 {
678                     if ($space_left > 20)
679                     {
680                         $len = $space_left;
681                         if (substr($word, $len - 1, 1) == "=")
682                           $len--;
683                         elseif (substr($word, $len - 2, 1) == "=")
684                           $len -= 2;
685                         $part = substr($word, 0, $len);
686                         $word = substr($word, $len);
687                         $buf .= " " . $part;
688                         $message .= $buf . sprintf("=%s", $this->LE);
689                     }
690                     else
691                     {
692                         $message .= $buf . $soft_break;
693                     }
694                     $buf = "";
695                 }
696                 while (strlen($word) > 0)
697                 {
698                     $len = $length;
699                     if (substr($word, $len - 1, 1) == "=")
700                         $len--;
701                     elseif (substr($word, $len - 2, 1) == "=")
702                         $len -= 2;
703                     $part = substr($word, 0, $len);
704                     $word = substr($word, $len);
705
706                     if (strlen($word) > 0)
707                         $message .= $part . sprintf("=%s", $this->LE);
708                     else
709                         $buf = $part;
710                 }
711               }
712               else
713               {
714                 $buf_o = $buf;
715                 $buf .= ($e == 0) ? $word : (" " . $word);
716
717                 if (strlen($buf) > $length and $buf_o != "")
718                 {
719                     $message .= $buf_o . $soft_break;
720                     $buf = $word;
721                 }
722               }
723           }
724           $message .= $buf . $this->LE;
725         }
726
727         return $message;
728     }
729
730     /**
731      * Set the body wrapping.
732      * @access private
733      * @return void
734      */
735     function SetWordWrap() {
736         if($this->WordWrap < 1)
737             return;
738
739         switch($this->message_type)
740         {
741            case "alt":
742               // fall through
743            case "alt_attachments":
744               $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
745               break;
746            default:
747               $this->Body = $this->WrapText($this->Body, $this->WordWrap);
748               break;
749         }
750     }
751
752     /**
753      * Assembles message header.
754      * @access private
755      * @return string
756      */
757     function CreateHeader() {
758         $result = "";
759
760         // Set the boundaries
761         $uniq_id = md5(uniqid(time()));
762         $this->boundary[1] = "b1_" . $uniq_id;
763         $this->boundary[2] = "b2_" . $uniq_id;
764
765         $result .= $this->HeaderLine("Date", $this->RFCDate());
766         if($this->Sender == "")
767             $result .= $this->HeaderLine("Return-Path", trim($this->From));
768         else
769             $result .= $this->HeaderLine("Return-Path", trim($this->Sender));
770
771         // To be created automatically by mail()
772         if($this->Mailer != "mail")
773         {
774             if(count($this->to) > 0)
775                 $result .= $this->AddrAppend("To", $this->to);
776             else if (count($this->cc) == 0)
777                 $result .= $this->HeaderLine("To", "undisclosed-recipients:;");
778             if(count($this->cc) > 0)
779                 $result .= $this->AddrAppend("Cc", $this->cc);
780         }
781
782         $from = array();
783         $from[0][0] = trim($this->From);
784         $from[0][1] = $this->FromName;
785         $result .= $this->AddrAppend("From", $from);
786
787         // sendmail and mail() extract Bcc from the header before sending
788         if((($this->Mailer == "sendmail") || ($this->Mailer == "mail")) && (count($this->bcc) > 0))
789             $result .= $this->AddrAppend("Bcc", $this->bcc);
790
791         if(count($this->ReplyTo) > 0)
792             $result .= $this->AddrAppend("Reply-to", $this->ReplyTo);
793
794         // mail() sets the subject itself
795         if($this->Mailer != "mail")
796             $result .= $this->HeaderLine("Subject", $this->EncodeHeader(trim($this->Subject)));
797
798         $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
799         $result .= $this->HeaderLine("X-Priority", $this->Priority);
800
801         if($this->ConfirmReadingTo != "")
802         {
803             $result .= $this->HeaderLine("Disposition-Notification-To",
804                        "<" . trim($this->ConfirmReadingTo) . ">");
805         }
806
807         // Add custom headers
808         for($index = 0; $index < count($this->CustomHeader); $index++)
809         {
810             $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]),
811                        $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
812         }
813         $result .= $this->HeaderLine("MIME-Version", "1.0");
814
815         switch($this->message_type)
816         {
817             case "plain":
818                 $result .= $this->HeaderLine("Content-Transfer-Encoding", $this->Encoding);
819                 $result .= sprintf("Content-Type: %s; charset=\"%s\"",
820                                     $this->ContentType, $this->CharSet);
821                 break;
822             case "attachments":
823                 // fall through
824             case "alt_attachments":
825                 if($this->InlineImageExists())
826                 {
827                     $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s",
828                                     "multipart/related", $this->LE, $this->LE,
829                                     $this->boundary[1], $this->LE);
830                 }
831                 else
832                 {
833                     $result .= $this->HeaderLine("Content-Type", "multipart/mixed;");
834                     $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
835                 }
836                 break;
837             case "alt":
838                 $result .= $this->HeaderLine("Content-Type", "multipart/alternative;");
839                 $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
840                 break;
841         }
842
843         if($this->Mailer != "mail")
844             $result .= $this->LE.$this->LE;
845
846         return $result;
847     }
848
849     /**
850      * Assembles the message body.  Returns an empty string on failure.
851      * @access private
852      * @return string
853      */
854     function CreateBody() {
855         $result = "";
856
857         $this->SetWordWrap();
858
859         switch($this->message_type)
860         {
861             case "alt":
862                 $result .= $this->GetBoundary($this->boundary[1], "",
863                                               "text/plain", "");
864                 $result .= $this->EncodeString($this->AltBody, $this->Encoding);
865                 $result .= $this->LE.$this->LE;
866                 $result .= $this->GetBoundary($this->boundary[1], "",
867                                               "text/html", "");
868
869                 $result .= $this->EncodeString($this->Body, $this->Encoding);
870                 $result .= $this->LE.$this->LE;
871
872                 $result .= $this->EndBoundary($this->boundary[1]);
873                 break;
874             case "plain":
875                 $result .= $this->EncodeString($this->Body, $this->Encoding);
876                 break;
877             case "attachments":
878                 $result .= $this->GetBoundary($this->boundary[1], "", "", "");
879                 $result .= $this->EncodeString($this->Body, $this->Encoding);
880                 $result .= $this->LE;
881
882                 $result .= $this->AttachAll();
883                 break;
884             case "alt_attachments":
885                 $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
886                 $result .= sprintf("Content-Type: %s;%s" .
887                                    "\tboundary=\"%s\"%s",
888                                    "multipart/alternative", $this->LE,
889                                    $this->boundary[2], $this->LE.$this->LE);
890
891                 // Create text body
892                 $result .= $this->GetBoundary($this->boundary[2], "",
893                                               "text/plain", "") . $this->LE;
894
895                 $result .= $this->EncodeString($this->AltBody, $this->Encoding);
896                 $result .= $this->LE.$this->LE;
897
898                 // Create the HTML body
899                 $result .= $this->GetBoundary($this->boundary[2], "",
900                                               "text/html", "") . $this->LE;
901
902                 $result .= $this->EncodeString($this->Body, $this->Encoding);
903                 $result .= $this->LE.$this->LE;
904
905                 $result .= $this->EndBoundary($this->boundary[2]);
906
907                 $result .= $this->AttachAll();
908                 break;
909         }
910         if($this->IsError())
911             $result = "";
912
913         return $result;
914     }
915
916     /**
917      * Returns the start of a message boundary.
918      * @access private
919      */
920     function GetBoundary($boundary, $charSet, $contentType, $encoding) {
921         $result = "";
922         if($charSet == "") { $charSet = $this->CharSet; }
923         if($contentType == "") { $contentType = $this->ContentType; }
924         if($encoding == "") { $encoding = $this->Encoding; }
925
926         $result .= $this->TextLine("--" . $boundary);
927         $result .= sprintf("Content-Type: %s; charset = \"%s\"",
928                             $contentType, $charSet);
929         $result .= $this->LE;
930         $result .= $this->HeaderLine("Content-Transfer-Encoding", $encoding);
931         $result .= $this->LE;
932
933         return $result;
934     }
935
936     /**
937      * Returns the end of a message boundary.
938      * @access private
939      */
940     function EndBoundary($boundary) {
941         return $this->LE . "--" . $boundary . "--" . $this->LE;
942     }
943
944     /**
945      * Sets the message type.
946      * @access private
947      * @return void
948      */
949     function SetMessageType() {
950         if(count($this->attachment) < 1 && strlen($this->AltBody) < 1)
951             $this->message_type = "plain";
952         else
953         {
954             if(count($this->attachment) > 0)
955                 $this->message_type = "attachments";
956             if(strlen($this->AltBody) > 0 && count($this->attachment) < 1)
957                 $this->message_type = "alt";
958             if(strlen($this->AltBody) > 0 && count($this->attachment) > 0)
959                 $this->message_type = "alt_attachments";
960         }
961     }
962
963     /**
964      * Returns a formatted header line.
965      * @access private
966      * @return string
967      */
968     function HeaderLine($name, $value) {
969         return $name . ": " . $value . $this->LE;
970     }
971
972     /**
973      * Returns a formatted mail line.
974      * @access private
975      * @return string
976      */
977     function TextLine($value) {
978         return $value . $this->LE;
979     }
980
981     /////////////////////////////////////////////////
982     // ATTACHMENT METHODS
983     /////////////////////////////////////////////////
984
985     /**
986      * Adds an attachment from a path on the filesystem.
987      * Returns false if the file could not be found
988      * or accessed.
989      * @param string $path Path to the attachment.
990      * @param string $name Overrides the attachment name.
991      * @param string $encoding File encoding (see $Encoding).
992      * @param string $type File extension (MIME) type.
993      * @return bool
994      */
995     function AddAttachment($path, $name = "", $encoding = "base64",
996                            $type = "application/octet-stream") {
997         if(!@is_file($path))
998         {
999             $this->SetError($this->Lang("file_access") . $path);
1000             return false;
1001         }
1002
1003         $filename = basename($path);
1004         if($name == "")
1005             $name = $filename;
1006
1007         $cur = count($this->attachment);
1008         $this->attachment[$cur][0] = $path;
1009         $this->attachment[$cur][1] = $filename;
1010         $this->attachment[$cur][2] = $name;
1011         $this->attachment[$cur][3] = $encoding;
1012         $this->attachment[$cur][4] = $type;
1013         $this->attachment[$cur][5] = false; // isStringAttachment
1014         $this->attachment[$cur][6] = "attachment";
1015         $this->attachment[$cur][7] = 0;
1016
1017         return true;
1018     }
1019
1020     /**
1021      * Attaches all fs, string, and binary attachments to the message.
1022      * Returns an empty string on failure.
1023      * @access private
1024      * @return string
1025      */
1026     function AttachAll() {
1027         // Return text of body
1028         $mime = array();
1029
1030         // Add all attachments
1031         for($i = 0; $i < count($this->attachment); $i++)
1032         {
1033             // Check for string attachment
1034             $bString = $this->attachment[$i][5];
1035             if ($bString)
1036                 $string = $this->attachment[$i][0];
1037             else
1038                 $path = $this->attachment[$i][0];
1039
1040             $filename    = $this->attachment[$i][1];
1041             $name        = $this->attachment[$i][2];
1042             $encoding    = $this->attachment[$i][3];
1043             $type        = $this->attachment[$i][4];
1044             $disposition = $this->attachment[$i][6];
1045             $cid         = $this->attachment[$i][7];
1046
1047             $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
1048             $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
1049             $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
1050
1051             if($disposition == "inline")
1052                 $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
1053
1054             $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s",
1055                               $disposition, $name, $this->LE.$this->LE);
1056
1057             // Encode as string attachment
1058             if($bString)
1059             {
1060                 $mime[] = $this->EncodeString($string, $encoding);
1061                 if($this->IsError()) { return ""; }
1062                 $mime[] = $this->LE.$this->LE;
1063             }
1064             else
1065             {
1066                 $mime[] = $this->EncodeFile($path, $encoding);
1067                 if($this->IsError()) { return ""; }
1068                 $mime[] = $this->LE.$this->LE;
1069             }
1070         }
1071
1072         $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
1073
1074         return join("", $mime);
1075     }
1076
1077     /**
1078      * Encodes attachment in requested format.  Returns an
1079      * empty string on failure.
1080      * @access private
1081      * @return string
1082      */
1083     function EncodeFile ($path, $encoding = "base64") {
1084         if(!@$fd = fopen($path, "rb"))
1085         {
1086             $this->SetError($this->Lang("file_open") . $path);
1087             return "";
1088         }
1089         $magic_quotes = get_magic_quotes_runtime();
1090         set_magic_quotes_runtime(0);
1091         $file_buffer = fread($fd, filesize($path));
1092         $file_buffer = $this->EncodeString($file_buffer, $encoding);
1093         fclose($fd);
1094         set_magic_quotes_runtime($magic_quotes);
1095
1096         return $file_buffer;
1097     }
1098
1099     /**
1100      * Encodes string to requested format. Returns an
1101      * empty string on failure.
1102      * @access private
1103      * @return string
1104      */
1105     function EncodeString ($str, $encoding = "base64") {
1106         $encoded = "";
1107         switch(strtolower($encoding)) {
1108           case "base64":
1109               // chunk_split is found in PHP >= 3.0.6
1110               $encoded = chunk_split(base64_encode($str), 76, $this->LE);
1111               break;
1112           case "7bit":
1113           case "8bit":
1114               $encoded = $this->FixEOL($str);
1115               if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1116                 $encoded .= $this->LE;
1117               break;
1118           case "binary":
1119               $encoded = $str;
1120               break;
1121           case "quoted-printable":
1122               $encoded = $this->EncodeQP($str);
1123               break;
1124           default:
1125               $this->SetError($this->Lang("encoding") . $encoding);
1126               break;
1127         }
1128         return $encoded;
1129     }
1130
1131     /**
1132      * Encode a header string to best of Q, B, quoted or none.
1133      * @access private
1134      * @return string
1135      */
1136     function EncodeHeader ($str, $position = 'text') {
1137       $x = 0;
1138
1139       switch (strtolower($position)) {
1140         case 'phrase':
1141           if (!preg_match('/[\200-\377]/', $str)) {
1142             // Can't use addslashes as we don't know what value has magic_quotes_sybase.
1143             $encoded = addcslashes($str, "\0..\37\177\\\"");
1144
1145             if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str))
1146               return ($encoded);
1147             else
1148               return ("\"$encoded\"");
1149           }
1150           $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
1151           break;
1152         case 'comment':
1153           $x = preg_match_all('/[()"]/', $str, $matches);
1154           // Fall-through
1155         case 'text':
1156         default:
1157           $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
1158           break;
1159       }
1160
1161       if ($x == 0)
1162         return ($str);
1163
1164       $maxlen = 75 - 7 - strlen($this->CharSet);
1165       // Try to select the encoding which should produce the shortest output
1166       if (strlen($str)/3 < $x) {
1167         $encoding = 'B';
1168         $encoded = base64_encode($str);
1169         $maxlen -= $maxlen % 4;
1170         $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
1171       } else {
1172         $encoding = 'Q';
1173         $encoded = $this->EncodeQ($str, $position);
1174         $encoded = $this->WrapText($encoded, $maxlen, true);
1175         $encoded = str_replace("=".$this->LE, "\n", trim($encoded));
1176       }
1177
1178       $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
1179       $encoded = trim(str_replace("\n", $this->LE, $encoded));
1180
1181       return $encoded;
1182     }
1183
1184     /**
1185      * Encode string to quoted-printable.
1186      * @access private
1187      * @return string
1188      */
1189     function EncodeQP ($str) {
1190         $encoded = $this->FixEOL($str);
1191         if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1192             $encoded .= $this->LE;
1193
1194         // Replace every high ascii, control and = characters
1195         $encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e',
1196                   "'='.sprintf('%02X', ord('\\1'))", $encoded);
1197         // Replace every spaces and tabs when it's the last character on a line
1198         $encoded = preg_replace("/([\011\040])".$this->LE."/e",
1199                   "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded);
1200
1201         // Maximum line length of 76 characters before CRLF (74 + space + '=')
1202         $encoded = $this->WrapText($encoded, 74, true);
1203
1204         return $encoded;
1205     }
1206
1207     /**
1208      * Encode string to q encoding.
1209      * @access private
1210      * @return string
1211      */
1212     function EncodeQ ($str, $position = "text") {
1213         // There should not be any EOL in the string
1214         $encoded = preg_replace("[\r\n]", "", $str);
1215
1216         switch (strtolower($position)) {
1217           case "phrase":
1218             $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1219             break;
1220           case "comment":
1221             $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1222           case "text":
1223           default:
1224             // Replace every high ascii, control =, ? and _ characters
1225             $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
1226                   "'='.sprintf('%02X', ord('\\1'))", $encoded);
1227             break;
1228         }
1229
1230         // Replace every spaces to _ (more readable than =20)
1231         $encoded = str_replace(" ", "_", $encoded);
1232
1233         return $encoded;
1234     }
1235
1236     /**
1237      * Adds a string or binary attachment (non-filesystem) to the list.
1238      * This method can be used to attach ascii or binary data,
1239      * such as a BLOB record from a database.
1240      * @param string $string String attachment data.
1241      * @param string $filename Name of the attachment.
1242      * @param string $encoding File encoding (see $Encoding).
1243      * @param string $type File extension (MIME) type.
1244      * @return void
1245      */
1246     function AddStringAttachment($string, $filename, $encoding = "base64",
1247                                  $type = "application/octet-stream") {
1248         // Append to $attachment array
1249         $cur = count($this->attachment);
1250         $this->attachment[$cur][0] = $string;
1251         $this->attachment[$cur][1] = $filename;
1252         $this->attachment[$cur][2] = $filename;
1253         $this->attachment[$cur][3] = $encoding;
1254         $this->attachment[$cur][4] = $type;
1255         $this->attachment[$cur][5] = true; // isString
1256         $this->attachment[$cur][6] = "attachment";
1257         $this->attachment[$cur][7] = 0;
1258     }
1259
1260     /**
1261      * Adds an embedded attachment.  This can include images, sounds, and
1262      * just about any other document.  Make sure to set the $type to an
1263      * image type.  For JPEG images use "image/jpeg" and for GIF images
1264      * use "image/gif".
1265      * @param string $path Path to the attachment.
1266      * @param string $cid Content ID of the attachment.  Use this to identify
1267      *        the Id for accessing the image in an HTML form.
1268      * @param string $name Overrides the attachment name.
1269      * @param string $encoding File encoding (see $Encoding).
1270      * @param string $type File extension (MIME) type.
1271      * @return bool
1272      */
1273     function AddEmbeddedImage($path, $cid, $name = "", $encoding = "base64",
1274                               $type = "application/octet-stream") {
1275
1276         if(!@is_file($path))
1277         {
1278             $this->SetError($this->Lang("file_access") . $path);
1279             return false;
1280         }
1281
1282         $filename = basename($path);
1283         if($name == "")
1284             $name = $filename;
1285
1286         // Append to $attachment array
1287         $cur = count($this->attachment);
1288         $this->attachment[$cur][0] = $path;
1289         $this->attachment[$cur][1] = $filename;
1290         $this->attachment[$cur][2] = $name;
1291         $this->attachment[$cur][3] = $encoding;
1292         $this->attachment[$cur][4] = $type;
1293         $this->attachment[$cur][5] = false; // isStringAttachment
1294         $this->attachment[$cur][6] = "inline";
1295         $this->attachment[$cur][7] = $cid;
1296
1297         return true;
1298     }
1299
1300     /**
1301      * Returns true if an inline attachment is present.
1302      * @access private
1303      * @return bool
1304      */
1305     function InlineImageExists() {
1306         $result = false;
1307         for($i = 0; $i < count($this->attachment); $i++)
1308         {
1309             if($this->attachment[$i][6] == "inline")
1310             {
1311                 $result = true;
1312                 break;
1313             }
1314         }
1315
1316         return $result;
1317     }
1318
1319     /////////////////////////////////////////////////
1320     // MESSAGE RESET METHODS
1321     /////////////////////////////////////////////////
1322
1323     /**
1324      * Clears all recipients assigned in the TO array.  Returns void.
1325      * @return void
1326      */
1327     function ClearAddresses() {
1328         $this->to = array();
1329     }
1330
1331     /**
1332      * Clears all recipients assigned in the CC array.  Returns void.
1333      * @return void
1334      */
1335     function ClearCCs() {
1336         $this->cc = array();
1337     }
1338
1339     /**
1340      * Clears all recipients assigned in the BCC array.  Returns void.
1341      * @return void
1342      */
1343     function ClearBCCs() {
1344         $this->bcc = array();
1345     }
1346
1347     /**
1348      * Clears all recipients assigned in the ReplyTo array.  Returns void.
1349      * @return void
1350      */
1351     function ClearReplyTos() {
1352         $this->ReplyTo = array();
1353     }
1354
1355     /**
1356      * Clears all recipients assigned in the TO, CC and BCC
1357      * array.  Returns void.
1358      * @return void
1359      */
1360     function ClearAllRecipients() {
1361         $this->to = array();
1362         $this->cc = array();
1363         $this->bcc = array();
1364     }
1365
1366     /**
1367      * Clears all previously set filesystem, string, and binary
1368      * attachments.  Returns void.
1369      * @return void
1370      */
1371     function ClearAttachments() {
1372         $this->attachment = array();
1373     }
1374
1375     /**
1376      * Clears all custom headers.  Returns void.
1377      * @return void
1378      */
1379     function ClearCustomHeaders() {
1380         $this->CustomHeader = array();
1381     }
1382
1383
1384     /////////////////////////////////////////////////
1385     // MISCELLANEOUS METHODS
1386     /////////////////////////////////////////////////
1387
1388     /**
1389      * Adds the error message to the error container.
1390      * Returns void.
1391      * @access private
1392      * @return void
1393      */
1394     function SetError($msg) {
1395         $this->error_count++;
1396         $this->ErrorInfo = $msg;
1397     }
1398
1399     /**
1400      * Returns the proper RFC 822 formatted date.
1401      * @access private
1402      * @return string
1403      */
1404     function RFCDate() {
1405         $tz = date("Z");
1406         $tzs = ($tz < 0) ? "-" : "+";
1407         $tz = abs($tz);
1408         $tz = ($tz/3600)*100 + ($tz%3600)/60;
1409         $result = sprintf("%s %s%04d", date("D, j M Y H:i:s"), $tzs, $tz);
1410
1411         return $result;
1412     }
1413
1414     /**
1415      * Returns the appropriate server variable.  Should work with both
1416      * PHP 4.1.0+ as well as older versions.  Returns an empty string
1417      * if nothing is found.
1418      * @access private
1419      * @return mixed
1420      */
1421     function ServerVar($varName) {
1422         global $HTTP_SERVER_VARS;
1423         global $HTTP_ENV_VARS;
1424
1425         if(!isset($_SERVER))
1426         {
1427             $_SERVER = $HTTP_SERVER_VARS;
1428             if(!isset($_SERVER["REMOTE_ADDR"]))
1429                 $_SERVER = $HTTP_ENV_VARS; // must be Apache
1430         }
1431
1432         if(isset($_SERVER[$varName]))
1433             return $_SERVER[$varName];
1434         else
1435             return "";
1436     }
1437
1438     /**
1439      * Returns the server hostname or 'localhost.localdomain' if unknown.
1440      * @access private
1441      * @return string
1442      */
1443     function ServerHostname() {
1444         if ($this->Hostname != "")
1445             $result = $this->Hostname;
1446         elseif ($this->ServerVar('SERVER_NAME') != "")
1447             $result = $this->ServerVar('SERVER_NAME');
1448         else
1449             $result = "localhost.localdomain";
1450
1451         return $result;
1452     }
1453
1454     /**
1455      * Returns a message in the appropriate language.
1456      * @access private
1457      * @return string
1458      */
1459     function Lang($key) {
1460         if(count($this->language) < 1)
1461             $this->SetLanguage("en"); // set the default language
1462
1463         if(isset($this->language[$key]))
1464             return $this->language[$key];
1465         else
1466             return "Language string failed to load: " . $key;
1467     }
1468
1469     /**
1470      * Returns true if an error occurred.
1471      * @return bool
1472      */
1473     function IsError() {
1474         return ($this->error_count > 0);
1475     }
1476
1477     /**
1478      * Changes every end of line from CR or LF to CRLF.
1479      * @access private
1480      * @return string
1481      */
1482     function FixEOL($str) {
1483         $str = str_replace("\r\n", "\n", $str);
1484         $str = str_replace("\r", "\n", $str);
1485         $str = str_replace("\n", $this->LE, $str);
1486         return $str;
1487     }
1488
1489     /**
1490      * Adds a custom header.
1491      * @return void
1492      */
1493     function AddCustomHeader($custom_header) {
1494         $this->CustomHeader[] = explode(":", $custom_header, 2);
1495     }
1496 }
1497
1498 ?>