File: 1.02.3a/server/base/phpmailer/class.phpmailer.php (View as HTML)

  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: ?>
1887: