Project

General

Profile

1
<?php
2
/*~ class.phpmailer.php
3
.---------------------------------------------------------------------------.
4
|  Software: PHPMailer - PHP email class                                    |
5
|   Version: 2.0.4                                                          |
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
 * @package PHPMailer
30
 * @author Andy Prevost
31
 * @copyright 2004 - 2009 Andy Prevost
32
 */
33

    
34
class PHPMailer {
35

    
36
  /////////////////////////////////////////////////
37
  // PROPERTIES, PUBLIC
38
  /////////////////////////////////////////////////
39

    
40
  /**
41
   * Email priority (1 = High, 3 = Normal, 5 = low).
42
   * @var int
43
   */
44
  var $Priority          = 3;
45

    
46
  /**
47
   * Sets the CharSet of the message.
48
   * @var string
49
   */
50
  var $CharSet           = 'iso-8859-1';
51

    
52
  /**
53
   * Sets the Content-type of the message.
54
   * @var string
55
   */
56
  var $ContentType        = 'text/plain';
57

    
58
  /**
59
   * Sets the Encoding of the message. Options for this are "8bit",
60
   * "7bit", "binary", "base64", and "quoted-printable".
61
   * @var string
62
   */
63
  var $Encoding          = '8bit';
64

    
65
  /**
66
   * Holds the most recent mailer error message.
67
   * @var string
68
   */
69
  var $ErrorInfo         = '';
70

    
71
  /**
72
   * Sets the From email address for the message.
73
   * @var string
74
   */
75
  var $From              = 'root@localhost';
76

    
77
  /**
78
   * Sets the From name of the message.
79
   * @var string
80
   */
81
  var $FromName          = 'Root User';
82

    
83
  /**
84
   * Sets the Sender email (Return-Path) of the message.  If not empty,
85
   * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
86
   * @var string
87
   */
88
  var $Sender            = '';
89

    
90
  /**
91
   * Sets the Subject of the message.
92
   * @var string
93
   */
94
  var $Subject           = '';
95

    
96
  /**
97
   * Sets the Body of the message.  This can be either an HTML or text body.
98
   * If HTML then run IsHTML(true).
99
   * @var string
100
   */
101
  var $Body              = '';
102

    
103
  /**
104
   * Sets the text-only body of the message.  This automatically sets the
105
   * email to multipart/alternative.  This body can be read by mail
106
   * clients that do not have HTML email capability such as mutt. Clients
107
   * that can read HTML will view the normal Body.
108
   * @var string
109
   */
110
  var $AltBody           = '';
111

    
112
  /**
113
   * Sets word wrapping on the body of the message to a given number of
114
   * characters.
115
   * @var int
116
   */
117
  var $WordWrap          = 0;
118

    
119
  /**
120
   * Method to send mail: ("mail", "sendmail", or "smtp").
121
   * @var string
122
   */
123
  var $Mailer            = 'mail';
124

    
125
  /**
126
   * Sets the path of the sendmail program.
127
   * @var string
128
   */
129
  var $Sendmail          = '/usr/sbin/sendmail';
130

    
131
  /**
132
   * Path to PHPMailer plugins.  This is now only useful if the SMTP class
133
   * is in a different directory than the PHP include path.
134
   * @var string
135
   */
136
  var $PluginDir         = '';
137

    
138
  /**
139
   * Holds PHPMailer version.
140
   * @var string
141
   */
142
  var $Version           = "2.0.4";
143

    
144
  /**
145
   * Sets the email address that a reading confirmation will be sent.
146
   * @var string
147
   */
148
  var $ConfirmReadingTo  = '';
149

    
150
  /**
151
   * Sets the hostname to use in Message-Id and Received headers
152
   * and as default HELO string. If empty, the value returned
153
   * by SERVER_NAME is used or 'localhost.localdomain'.
154
   * @var string
155
   */
156
  var $Hostname          = '';
157

    
158
  /**
159
   * Sets the message ID to be used in the Message-Id header.
160
   * If empty, a unique id will be generated.
161
   * @var string
162
   */
163
  var $MessageID         = '';
164

    
165
  /////////////////////////////////////////////////
166
  // PROPERTIES FOR SMTP
167
  /////////////////////////////////////////////////
168

    
169
  /**
170
   * Sets the SMTP hosts.  All hosts must be separated by a
171
   * semicolon.  You can also specify a different port
172
   * for each host by using this format: [hostname:port]
173
   * (e.g. "smtp1.example.com:25;smtp2.example.com").
174
   * Hosts will be tried in order.
175
   * @var string
176
   */
177
  var $Host        = 'localhost';
178

    
179
  /**
180
   * Sets the default SMTP server port.
181
   * @var int
182
   */
183
  var $Port        = 25;
184

    
185
  /**
186
   * Sets the SMTP HELO of the message (Default is $Hostname).
187
   * @var string
188
   */
189
  var $Helo        = '';
190

    
191
  /**
192
   * Sets connection prefix.
193
   * Options are "", "ssl" or "tls"
194
   * @var string
195
   */
196
  var $SMTPSecure = "";
197

    
198
  /**
199
   * Sets SMTP authentication. Utilizes the Username and Password variables.
200
   * @var bool
201
   */
202
  var $SMTPAuth     = false;
203

    
204
  /**
205
   * Sets SMTP username.
206
   * @var string
207
   */
208
  var $Username     = '';
209

    
210
  /**
211
   * Sets SMTP password.
212
   * @var string
213
   */
214
  var $Password     = '';
215

    
216
  /**
217
   * Sets the SMTP server timeout in seconds. This function will not
218
   * work with the win32 version.
219
   * @var int
220
   */
221
  var $Timeout      = 10;
222

    
223
  /**
224
   * Sets SMTP class debugging on or off.
225
   * @var bool
226
   */
227
  var $SMTPDebug    = false;
228

    
229
  /**
230
   * Prevents the SMTP connection from being closed after each mail
231
   * sending.  If this is set to true then to close the connection
232
   * requires an explicit call to SmtpClose().
233
   * @var bool
234
   */
235
  var $SMTPKeepAlive = false;
236

    
237
  /**
238
   * Provides the ability to have the TO field process individual
239
   * emails, instead of sending to entire TO addresses
240
   * @var bool
241
   */
242
  var $SingleTo = false;
243

    
244
  /////////////////////////////////////////////////
245
  // PROPERTIES, PRIVATE
246
  /////////////////////////////////////////////////
247

    
248
  var $smtp            = NULL;
249
  var $to              = array();
250
  var $cc              = array();
251
  var $bcc             = array();
252
  var $ReplyTo         = array();
253
  var $attachment      = array();
254
  var $CustomHeader    = array();
255
  var $message_type    = '';
256
  var $boundary        = array();
257
  var $language        = array();
258
  var $error_count     = 0;
259
  var $LE              = "\n";
260
  var $sign_cert_file  = "";
261
  var $sign_key_file   = "";
262
  var $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
  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
  function IsSMTP() {
286
    $this->Mailer = 'smtp';
287
  }
288

    
289
  /**
290
   * Sets Mailer to send message using PHP mail() function.
291
   * @return void
292
   */
293
  function IsMail() {
294
    $this->Mailer = 'mail';
295
  }
296

    
297
  /**
298
   * Sets Mailer to send message using the $Sendmail program.
299
   * @return void
300
   */
301
  function IsSendmail() {
302
    $this->Mailer = 'sendmail';
303
  }
304

    
305
  /**
306
   * Sets Mailer to send message using the qmail MTA.
307
   * @return void
308
   */
309
  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
  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
  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
  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
  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
  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 private
429
   * @return bool
430
   */
431
  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
    return true;
455
  }
456

    
457
  /**
458
   * Sends mail using the PHP mail() function.
459
   * @access private
460
   * @return bool
461
   */
462
  function MailSend($header, $body) {
463

    
464
    $to = '';
465
    for($i = 0; $i < count($this->to); $i++) {
466
      if($i != 0) { $to .= ', '; }
467
      $to .= $this->AddrFormat($this->to[$i]);
468
    }
469

    
470
    $toArr = split(',', $to);
471

    
472
    $params = sprintf("-oi -f %s", $this->Sender);
473
    if ($this->Sender != '' && strlen(ini_get('safe_mode')) < 1) {
474
      $old_from = ini_get('sendmail_from');
475
      ini_set('sendmail_from', $this->Sender);
476
      if ($this->SingleTo === true && count($toArr) > 1) {
477
        foreach ($toArr as $key => $val) {
478
          $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
479
        }
480
      } else {
481
        $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
482
      }
483
    } else {
484
      if ($this->SingleTo === true && count($toArr) > 1) {
485
        foreach ($toArr as $key => $val) {
486
          $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
487
        }
488
      } else {
489
        $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);
490
      }
491
    }
492

    
493
    if (isset($old_from)) {
494
      ini_set('sendmail_from', $old_from);
495
    }
496

    
497
    if(!$rt) {
498
      $this->SetError($this->Lang('instantiate'));
499
      return false;
500
    }
501

    
502
    return true;
503
  }
504

    
505
  /**
506
   * Sends mail via SMTP using PhpSMTP (Author:
507
   * Chris Ryan).  Returns bool.  Returns false if there is a
508
   * bad MAIL FROM, RCPT, or DATA input.
509
   * @access private
510
   * @return bool
511
   */
512
  function SmtpSend($header, $body) {
513
    include_once($this->PluginDir . 'class.smtp.php');
514
    $error = '';
515
    $bad_rcpt = array();
516

    
517
    if(!$this->SmtpConnect()) {
518
      return false;
519
    }
520

    
521
    $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
522
    if(!$this->smtp->Mail($smtp_from)) {
523
      $error = $this->Lang('from_failed') . $smtp_from;
524
      $this->SetError($error);
525
      $this->smtp->Reset();
526
      return false;
527
    }
528

    
529
    /* Attempt to send attach all recipients */
530
    for($i = 0; $i < count($this->to); $i++) {
531
      if(!$this->smtp->Recipient($this->to[$i][0])) {
532
        $bad_rcpt[] = $this->to[$i][0];
533
      }
534
    }
535
    for($i = 0; $i < count($this->cc); $i++) {
536
      if(!$this->smtp->Recipient($this->cc[$i][0])) {
537
        $bad_rcpt[] = $this->cc[$i][0];
538
      }
539
    }
540
    for($i = 0; $i < count($this->bcc); $i++) {
541
      if(!$this->smtp->Recipient($this->bcc[$i][0])) {
542
        $bad_rcpt[] = $this->bcc[$i][0];
543
      }
544
    }
545

    
546
    if(count($bad_rcpt) > 0) { // Create error message
547
      for($i = 0; $i < count($bad_rcpt); $i++) {
548
        if($i != 0) {
549
          $error .= ', ';
550
        }
551
        $error .= $bad_rcpt[$i];
552
      }
553
      $error = $this->Lang('recipients_failed') . $error;
554
      $this->SetError($error);
555
      $this->smtp->Reset();
556
      return false;
557
    }
558

    
559
    if(!$this->smtp->Data($header . $body)) {
560
      $this->SetError($this->Lang('data_not_accepted'));
561
      $this->smtp->Reset();
562
      return false;
563
    }
564
    if($this->SMTPKeepAlive == true) {
565
      $this->smtp->Reset();
566
    } else {
567
      $this->SmtpClose();
568
    }
569

    
570
    return true;
571
  }
572

    
573
  /**
574
   * Initiates a connection to an SMTP server.  Returns false if the
575
   * operation failed.
576
   * @access private
577
   * @return bool
578
   */
579
  function SmtpConnect() {
580
    if($this->smtp == NULL) {
581
      $this->smtp = new SMTP();
582
    }
583

    
584
    $this->smtp->do_debug = $this->SMTPDebug;
585
    $hosts = explode(';', $this->Host);
586
    $index = 0;
587
    $connection = ($this->smtp->Connected());
588

    
589
    /* Retry while there is no connection */
590
    while($index < count($hosts) && $connection == false) {
591
      $hostinfo = array();
592
      if(eregi('^(.+):([0-9]+)$', $hosts[$index], $hostinfo)) {
593
        $host = $hostinfo[1];
594
        $port = $hostinfo[2];
595
      } else {
596
        $host = $hosts[$index];
597
        $port = $this->Port;
598
      }
599

    
600
      if($this->smtp->Connect(((!empty($this->SMTPSecure))?$this->SMTPSecure.'://':'').$host, $port, $this->Timeout)) {
601
        if ($this->Helo != '') {
602
          $this->smtp->Hello($this->Helo);
603
        } else {
604
          $this->smtp->Hello($this->ServerHostname());
605
        }
606

    
607
        $connection = true;
608
        if($this->SMTPAuth) {
609
          if(!$this->smtp->Authenticate($this->Username, $this->Password)) {
610
            $this->SetError($this->Lang('authenticate'));
611
            $this->smtp->Reset();
612
            $connection = false;
613
          }
614
        }
615
      }
616
      $index++;
617
    }
618
    if(!$connection) {
619
      $this->SetError($this->Lang('connect_host'));
620
    }
621

    
622
    return $connection;
623
  }
624

    
625
  /**
626
   * Closes the active SMTP session if one exists.
627
   * @return void
628
   */
629
  function SmtpClose() {
630
    if($this->smtp != NULL) {
631
      if($this->smtp->Connected()) {
632
        $this->smtp->Quit();
633
        $this->smtp->Close();
634
      }
635
    }
636
  }
637

    
638
  /**
639
   * Sets the language for all class error messages.  Returns false
640
   * if it cannot load the language file.  The default language type
641
   * is English.
642
   * @param string $lang_type Type of language (e.g. Portuguese: "br")
643
   * @param string $lang_path Path to the language file directory
644
   * @access public
645
   * @return bool
646
   */
647
  function SetLanguage($lang_type, $lang_path = 'language/') {
648
    if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php')) {
649
      include($lang_path.'phpmailer.lang-'.$lang_type.'.php');
650
    } elseif (file_exists($lang_path.'phpmailer.lang-en.php')) {
651
      include($lang_path.'phpmailer.lang-en.php');
652
    } else {
653
      $PHPMAILER_LANG = array();
654
      $PHPMAILER_LANG["provide_address"]      = 'You must provide at least one ' .
655
      $PHPMAILER_LANG["mailer_not_supported"] = ' mailer is not supported.';
656
      $PHPMAILER_LANG["execute"]              = 'Could not execute: ';
657
      $PHPMAILER_LANG["instantiate"]          = 'Could not instantiate mail function.';
658
      $PHPMAILER_LANG["authenticate"]         = 'SMTP Error: Could not authenticate.';
659
      $PHPMAILER_LANG["from_failed"]          = 'The following From address failed: ';
660
      $PHPMAILER_LANG["recipients_failed"]    = 'SMTP Error: The following ' .
661
      $PHPMAILER_LANG["data_not_accepted"]    = 'SMTP Error: Data not accepted.';
662
      $PHPMAILER_LANG["connect_host"]         = 'SMTP Error: Could not connect to SMTP host.';
663
      $PHPMAILER_LANG["file_access"]          = 'Could not access file: ';
664
      $PHPMAILER_LANG["file_open"]            = 'File Error: Could not open file: ';
665
      $PHPMAILER_LANG["encoding"]             = 'Unknown encoding: ';
666
      $PHPMAILER_LANG["signing"]              = 'Signing Error: ';
667
    }
668
    $this->language = $PHPMAILER_LANG;
669

    
670
    return true;
671
  }
672

    
673
  /////////////////////////////////////////////////
674
  // METHODS, MESSAGE CREATION
675
  /////////////////////////////////////////////////
676

    
677
  /**
678
   * Creates recipient headers.
679
   * @access private
680
   * @return string
681
   */
682
  function AddrAppend($type, $addr) {
683
    $addr_str = $type . ': ';
684
    $addr_str .= $this->AddrFormat($addr[0]);
685
    if(count($addr) > 1) {
686
      for($i = 1; $i < count($addr); $i++) {
687
        $addr_str .= ', ' . $this->AddrFormat($addr[$i]);
688
      }
689
    }
690
    $addr_str .= $this->LE;
691

    
692
    return $addr_str;
693
  }
694

    
695
  /**
696
   * Formats an address correctly.
697
   * @access private
698
   * @return string
699
   */
700
  function AddrFormat($addr) {
701
    if(empty($addr[1])) {
702
      $formatted = $this->SecureHeader($addr[0]);
703
    } else {
704
      $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
705
    }
706

    
707
    return $formatted;
708
  }
709

    
710
  /**
711
   * Wraps message for use with mailers that do not
712
   * automatically perform wrapping and for quoted-printable.
713
   * Original written by philippe.
714
   * @access private
715
   * @return string
716
   */
717
  function WrapText($message, $length, $qp_mode = false) {
718
    $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
719
    // If utf-8 encoding is used, we will need to make sure we don't
720
    // split multibyte characters when we wrap
721
    $is_utf8 = (strtolower($this->CharSet) == "utf-8");
722

    
723
    $message = $this->FixEOL($message);
724
    if (substr($message, -1) == $this->LE) {
725
      $message = substr($message, 0, -1);
726
    }
727

    
728
    $line = explode($this->LE, $message);
729
    $message = '';
730
    for ($i=0 ;$i < count($line); $i++) {
731
      $line_part = explode(' ', $line[$i]);
732
      $buf = '';
733
      for ($e = 0; $e<count($line_part); $e++) {
734
        $word = $line_part[$e];
735
        if ($qp_mode and (strlen($word) > $length)) {
736
          $space_left = $length - strlen($buf) - 1;
737
          if ($e != 0) {
738
            if ($space_left > 20) {
739
              $len = $space_left;
740
              if ($is_utf8) {
741
                $len = $this->UTF8CharBoundary($word, $len);
742
              } elseif (substr($word, $len - 1, 1) == "=") {
743
                $len--;
744
              } elseif (substr($word, $len - 2, 1) == "=") {
745
                $len -= 2;
746
              }
747
              $part = substr($word, 0, $len);
748
              $word = substr($word, $len);
749
              $buf .= ' ' . $part;
750
              $message .= $buf . sprintf("=%s", $this->LE);
751
            } else {
752
              $message .= $buf . $soft_break;
753
            }
754
            $buf = '';
755
          }
756
          while (strlen($word) > 0) {
757
            $len = $length;
758
            if ($is_utf8) {
759
              $len = $this->UTF8CharBoundary($word, $len);
760
            } elseif (substr($word, $len - 1, 1) == "=") {
761
              $len--;
762
            } elseif (substr($word, $len - 2, 1) == "=") {
763
              $len -= 2;
764
            }
765
            $part = substr($word, 0, $len);
766
            $word = substr($word, $len);
767

    
768
            if (strlen($word) > 0) {
769
              $message .= $part . sprintf("=%s", $this->LE);
770
            } else {
771
              $buf = $part;
772
            }
773
          }
774
        } else {
775
          $buf_o = $buf;
776
          $buf .= ($e == 0) ? $word : (' ' . $word);
777

    
778
          if (strlen($buf) > $length and $buf_o != '') {
779
            $message .= $buf_o . $soft_break;
780
            $buf = $word;
781
          }
782
        }
783
      }
784
      $message .= $buf . $this->LE;
785
    }
786

    
787
    return $message;
788
  }
789

    
790
  /**
791
   * Finds last character boundary prior to maxLength in a utf-8
792
   * quoted (printable) encoded string.
793
   * Original written by Colin Brown.
794
   * @access private
795
   * @param string $encodedText utf-8 QP text
796
   * @param int    $maxLength   find last character boundary prior to this length
797
   * @return int
798
   */
799
  function UTF8CharBoundary($encodedText, $maxLength) {
800
    $foundSplitPos = false;
801
    $lookBack = 3;
802
    while (!$foundSplitPos) {
803
      $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
804
      $encodedCharPos = strpos($lastChunk, "=");
805
      if ($encodedCharPos !== false) {
806
        // Found start of encoded character byte within $lookBack block.
807
        // Check the encoded byte value (the 2 chars after the '=')
808
        $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
809
        $dec = hexdec($hex);
810
        if ($dec < 128) { // Single byte character.
811
          // If the encoded char was found at pos 0, it will fit
812
          // otherwise reduce maxLength to start of the encoded char
813
          $maxLength = ($encodedCharPos == 0) ? $maxLength :
814
          $maxLength - ($lookBack - $encodedCharPos);
815
          $foundSplitPos = true;
816
        } elseif ($dec >= 192) { // First byte of a multi byte character
817
          // Reduce maxLength to split at start of character
818
          $maxLength = $maxLength - ($lookBack - $encodedCharPos);
819
          $foundSplitPos = true;
820
        } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
821
          $lookBack += 3;
822
        }
823
      } else {
824
        // No encoded character found
825
        $foundSplitPos = true;
826
      }
827
    }
828
    return $maxLength;
829
  }
830

    
831
  /**
832
   * Set the body wrapping.
833
   * @access private
834
   * @return void
835
   */
836
  function SetWordWrap() {
837
    if($this->WordWrap < 1) {
838
      return;
839
    }
840

    
841
    switch($this->message_type) {
842
      case 'alt':
843
        /* fall through */
844
      case 'alt_attachments':
845
        $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
846
        break;
847
      default:
848
        $this->Body = $this->WrapText($this->Body, $this->WordWrap);
849
        break;
850
    }
851
  }
852

    
853
  /**
854
   * Assembles message header.
855
   * @access private
856
   * @return string
857
   */
858
  function CreateHeader() {
859
    $result = '';
860

    
861
    /* Set the boundaries */
862
    $uniq_id = md5(uniqid(time()));
863
    $this->boundary[1] = 'b1_' . $uniq_id;
864
    $this->boundary[2] = 'b2_' . $uniq_id;
865

    
866
    $result .= $this->HeaderLine('Date', $this->RFCDate());
867
    if($this->Sender == '') {
868
      $result .= $this->HeaderLine('Return-Path', trim($this->From));
869
    } else {
870
      $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
871
    }
872

    
873
    /* To be created automatically by mail() */
874
    if($this->Mailer != 'mail') {
875
      if(count($this->to) > 0) {
876
        $result .= $this->AddrAppend('To', $this->to);
877
      } elseif (count($this->cc) == 0) {
878
        $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
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.sourceforge.net) [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
    if (!$this->sign_key_file) {
923
      $result .= $this->HeaderLine('MIME-Version', '1.0');
924
      $result .= $this->GetMailMIME();
925
    }
926

    
927
    return $result;
928
  }
929

    
930
  /**
931
   * Returns the message MIME.
932
   * @access private
933
   * @return string
934
   */
935
  function GetMailMIME() {
936
    $result = '';
937
    switch($this->message_type) {
938
      case 'plain':
939
        $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
940
        $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
941
        break;
942
      case 'attachments':
943
        /* fall through */
944
      case 'alt_attachments':
945
        if($this->InlineImageExists()){
946
          $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
947
        } else {
948
          $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
949
          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
950
        }
951
        break;
952
      case 'alt':
953
        $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
954
        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
955
        break;
956
    }
957

    
958
    if($this->Mailer != 'mail') {
959
      $result .= $this->LE.$this->LE;
960
    }
961

    
962
    return $result;
963
  }
964

    
965
  /**
966
   * Assembles the message body.  Returns an empty string on failure.
967
   * @access private
968
   * @return string
969
   */
970
  function CreateBody() {
971
    $result = '';
972
    if ($this->sign_key_file) {
973
      $result .= $this->GetMailMIME();
974
    }
975

    
976
    $this->SetWordWrap();
977

    
978
    switch($this->message_type) {
979
      case 'alt':
980
        $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
981
        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
982
        $result .= $this->LE.$this->LE;
983
        $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
984
        $result .= $this->EncodeString($this->Body, $this->Encoding);
985
        $result .= $this->LE.$this->LE;
986
        $result .= $this->EndBoundary($this->boundary[1]);
987
        break;
988
      case 'plain':
989
        $result .= $this->EncodeString($this->Body, $this->Encoding);
990
        break;
991
      case 'attachments':
992
        $result .= $this->GetBoundary($this->boundary[1], '', '', '');
993
        $result .= $this->EncodeString($this->Body, $this->Encoding);
994
        $result .= $this->LE;
995
        $result .= $this->AttachAll();
996
        break;
997
      case 'alt_attachments':
998
        $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
999
        $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
1000
        $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
1001
        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
1002
        $result .= $this->LE.$this->LE;
1003
        $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
1004
        $result .= $this->EncodeString($this->Body, $this->Encoding);
1005
        $result .= $this->LE.$this->LE;
1006
        $result .= $this->EndBoundary($this->boundary[2]);
1007
        $result .= $this->AttachAll();
1008
        break;
1009
    }
1010

    
1011
    if($this->IsError()) {
1012
      $result = '';
1013
    } else if ($this->sign_key_file) {
1014
      $file = tempnam("", "mail");
1015
      $fp = fopen($file, "w");
1016
      fwrite($fp, $result);
1017
      fclose($fp);
1018
      $signed = tempnam("", "signed");
1019

    
1020
      if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), null)) {
1021
        $fp = fopen($signed, "r");
1022
        $result = fread($fp, filesize($this->sign_key_file));
1023
        $result = '';
1024
        while(!feof($fp)){
1025
          $result = $result . fread($fp, 1024);
1026
        }
1027
        fclose($fp);
1028
      } else {
1029
        $this->SetError($this->Lang("signing").openssl_error_string());
1030
        $result = '';
1031
      }
1032

    
1033
      unlink($file);
1034
      unlink($signed);
1035
    }
1036

    
1037
    return $result;
1038
  }
1039

    
1040
  /**
1041
   * Returns the start of a message boundary.
1042
   * @access private
1043
   */
1044
  function GetBoundary($boundary, $charSet, $contentType, $encoding) {
1045
    $result = '';
1046
    if($charSet == '') {
1047
      $charSet = $this->CharSet;
1048
    }
1049
    if($contentType == '') {
1050
      $contentType = $this->ContentType;
1051
    }
1052
    if($encoding == '') {
1053
      $encoding = $this->Encoding;
1054
    }
1055
    $result .= $this->TextLine('--' . $boundary);
1056
    $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
1057
    $result .= $this->LE;
1058
    $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
1059
    $result .= $this->LE;
1060

    
1061
    return $result;
1062
  }
1063

    
1064
  /**
1065
   * Returns the end of a message boundary.
1066
   * @access private
1067
   */
1068
  function EndBoundary($boundary) {
1069
    return $this->LE . '--' . $boundary . '--' . $this->LE;
1070
  }
1071

    
1072
  /**
1073
   * Sets the message type.
1074
   * @access private
1075
   * @return void
1076
   */
1077
  function SetMessageType() {
1078
    if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
1079
      $this->message_type = 'plain';
1080
    } else {
1081
      if(count($this->attachment) > 0) {
1082
        $this->message_type = 'attachments';
1083
      }
1084
      if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
1085
        $this->message_type = 'alt';
1086
      }
1087
      if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
1088
        $this->message_type = 'alt_attachments';
1089
      }
1090
    }
1091
  }
1092

    
1093
  /* Returns a formatted header line.
1094
   * @access private
1095
   * @return string
1096
   */
1097
  function HeaderLine($name, $value) {
1098
    return $name . ': ' . $value . $this->LE;
1099
  }
1100

    
1101
  /**
1102
   * Returns a formatted mail line.
1103
   * @access private
1104
   * @return string
1105
   */
1106
  function TextLine($value) {
1107
    return $value . $this->LE;
1108
  }
1109

    
1110
  /////////////////////////////////////////////////
1111
  // CLASS METHODS, ATTACHMENTS
1112
  /////////////////////////////////////////////////
1113

    
1114
  /**
1115
   * Adds an attachment from a path on the filesystem.
1116
   * Returns false if the file could not be found
1117
   * or accessed.
1118
   * @param string $path Path to the attachment.
1119
   * @param string $name Overrides the attachment name.
1120
   * @param string $encoding File encoding (see $Encoding).
1121
   * @param string $type File extension (MIME) type.
1122
   * @return bool
1123
   */
1124
  function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1125
    if(!@is_file($path)) {
1126
      $this->SetError($this->Lang('file_access') . $path);
1127
      return false;
1128
    }
1129

    
1130
    $filename = basename($path);
1131
    if($name == '') {
1132
      $name = $filename;
1133
    }
1134

    
1135
    $cur = count($this->attachment);
1136
    $this->attachment[$cur][0] = $path;
1137
    $this->attachment[$cur][1] = $filename;
1138
    $this->attachment[$cur][2] = $name;
1139
    $this->attachment[$cur][3] = $encoding;
1140
    $this->attachment[$cur][4] = $type;
1141
    $this->attachment[$cur][5] = false; // isStringAttachment
1142
    $this->attachment[$cur][6] = 'attachment';
1143
    $this->attachment[$cur][7] = 0;
1144

    
1145
    return true;
1146
  }
1147

    
1148
  /**
1149
   * Attaches all fs, string, and binary attachments to the message.
1150
   * Returns an empty string on failure.
1151
   * @access private
1152
   * @return string
1153
   */
1154
  function AttachAll() {
1155
    /* Return text of body */
1156
    $mime = array();
1157

    
1158
    /* Add all attachments */
1159
    for($i = 0; $i < count($this->attachment); $i++) {
1160
      /* Check for string attachment */
1161
      $bString = $this->attachment[$i][5];
1162
      if ($bString) {
1163
        $string = $this->attachment[$i][0];
1164
      } else {
1165
        $path = $this->attachment[$i][0];
1166
      }
1167

    
1168
      $filename    = $this->attachment[$i][1];
1169
      $name        = $this->attachment[$i][2];
1170
      $encoding    = $this->attachment[$i][3];
1171
      $type        = $this->attachment[$i][4];
1172
      $disposition = $this->attachment[$i][6];
1173
      $cid         = $this->attachment[$i][7];
1174

    
1175
      $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
1176
      $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);
1177
      $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
1178

    
1179
      if($disposition == 'inline') {
1180
        $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
1181
      }
1182

    
1183
      $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $this->EncodeHeader($this->SecureHeader($name)), $this->LE.$this->LE);
1184

    
1185
      /* Encode as string attachment */
1186
      if($bString) {
1187
        $mime[] = $this->EncodeString($string, $encoding);
1188
        if($this->IsError()) {
1189
          return '';
1190
        }
1191
        $mime[] = $this->LE.$this->LE;
1192
      } else {
1193
        $mime[] = $this->EncodeFile($path, $encoding);
1194
        if($this->IsError()) {
1195
          return '';
1196
        }
1197
        $mime[] = $this->LE.$this->LE;
1198
      }
1199
    }
1200

    
1201
    $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
1202

    
1203
    return join('', $mime);
1204
  }
1205

    
1206
  /**
1207
   * Encodes attachment in requested format.  Returns an
1208
   * empty string on failure.
1209
   * @access private
1210
   * @return string
1211
   */
1212
  function EncodeFile ($path, $encoding = 'base64') {
1213
    if(!@$fd = fopen($path, 'rb')) {
1214
      $this->SetError($this->Lang('file_open') . $path);
1215
      return '';
1216
    }
1217
    $magic_quotes = get_magic_quotes_runtime();
1218
    set_magic_quotes_runtime(0);
1219
    $file_buffer = fread($fd, filesize($path));
1220
    $file_buffer = $this->EncodeString($file_buffer, $encoding);
1221
    fclose($fd);
1222
    set_magic_quotes_runtime($magic_quotes);
1223

    
1224
    return $file_buffer;
1225
  }
1226

    
1227
  /**
1228
   * Encodes string to requested format. Returns an
1229
   * empty string on failure.
1230
   * @access private
1231
   * @return string
1232
   */
1233
  function EncodeString ($str, $encoding = 'base64') {
1234
    $encoded = '';
1235
    switch(strtolower($encoding)) {
1236
      case 'base64':
1237
        /* chunk_split is found in PHP >= 3.0.6 */
1238
        $encoded = chunk_split(base64_encode($str), 76, $this->LE);
1239
        break;
1240
      case '7bit':
1241
      case '8bit':
1242
        $encoded = $this->FixEOL($str);
1243
        if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1244
          $encoded .= $this->LE;
1245
        break;
1246
      case 'binary':
1247
        $encoded = $str;
1248
        break;
1249
      case 'quoted-printable':
1250
        $encoded = $this->EncodeQP($str);
1251
        break;
1252
      default:
1253
        $this->SetError($this->Lang('encoding') . $encoding);
1254
        break;
1255
    }
1256
    return $encoded;
1257
  }
1258

    
1259
  /**
1260
   * Encode a header string to best of Q, B, quoted or none.
1261
   * @access private
1262
   * @return string
1263
   */
1264
  function EncodeHeader ($str, $position = 'text') {
1265
    $x = 0;
1266

    
1267
    switch (strtolower($position)) {
1268
      case 'phrase':
1269
        if (!preg_match('/[\200-\377]/', $str)) {
1270
          /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */
1271
          $encoded = addcslashes($str, "\0..\37\177\\\"");
1272
          if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
1273
            return ($encoded);
1274
          } else {
1275
            return ("\"$encoded\"");
1276
          }
1277
        }
1278
        $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
1279
        break;
1280
      case 'comment':
1281
        $x = preg_match_all('/[()"]/', $str, $matches);
1282
        /* Fall-through */
1283
      case 'text':
1284
      default:
1285
        $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
1286
        break;
1287
    }
1288

    
1289
    if ($x == 0) {
1290
      return ($str);
1291
    }
1292

    
1293
    $maxlen = 75 - 7 - strlen($this->CharSet);
1294
    /* Try to select the encoding which should produce the shortest output */
1295
    if (strlen($str)/3 < $x) {
1296
      $encoding = 'B';
1297
      if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
1298
     // Use a custom function which correctly encodes and wraps long
1299
     // multibyte strings without breaking lines within a character
1300
        $encoded = $this->Base64EncodeWrapMB($str);
1301
      } else {
1302
        $encoded = base64_encode($str);
1303
        $maxlen -= $maxlen % 4;
1304
        $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
1305
      }
1306
    } else {
1307
      $encoding = 'Q';
1308
      $encoded = $this->EncodeQ($str, $position);
1309
      $encoded = $this->WrapText($encoded, $maxlen, true);
1310
      $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
1311
    }
1312

    
1313
    $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
1314
    $encoded = trim(str_replace("\n", $this->LE, $encoded));
1315

    
1316
    return $encoded;
1317
  }
1318

    
1319
  /**
1320
   * Checks if a string contains multibyte characters.
1321
   * @access private
1322
   * @param string $str multi-byte text to wrap encode
1323
   * @return bool
1324
   */
1325
  function HasMultiBytes($str) {
1326
    if (function_exists('mb_strlen')) {
1327
      return (strlen($str) > mb_strlen($str, $this->CharSet));
1328
    } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
1329
      return False;
1330
    }
1331
  }
1332

    
1333
  /**
1334
   * Correctly encodes and wraps long multibyte strings for mail headers
1335
   * without breaking lines within a character.
1336
   * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
1337
   * @access private
1338
   * @param string $str multi-byte text to wrap encode
1339
   * @return string
1340
   */
1341
  function Base64EncodeWrapMB($str) {
1342
    $start = "=?".$this->CharSet."?B?";
1343
    $end = "?=";
1344
    $encoded = "";
1345

    
1346
    $mb_length = mb_strlen($str, $this->CharSet);
1347
    // Each line must have length <= 75, including $start and $end
1348
    $length = 75 - strlen($start) - strlen($end);
1349
    // Average multi-byte ratio
1350
    $ratio = $mb_length / strlen($str);
1351
    // Base64 has a 4:3 ratio
1352
    $offset = $avgLength = floor($length * $ratio * .75);
1353

    
1354
    for ($i = 0; $i < $mb_length; $i += $offset) {
1355
      $lookBack = 0;
1356

    
1357
      do {
1358
        $offset = $avgLength - $lookBack;
1359
        $chunk = mb_substr($str, $i, $offset, $this->CharSet);
1360
        $chunk = base64_encode($chunk);
1361
        $lookBack++;
1362
      }
1363
      while (strlen($chunk) > $length);
1364

    
1365
      $encoded .= $chunk . $this->LE;
1366
    }
1367

    
1368
    // Chomp the last linefeed
1369
    $encoded = substr($encoded, 0, -strlen($this->LE));
1370
    return $encoded;
1371
  }
1372

    
1373
  /**
1374
   * Encode string to quoted-printable.
1375
   * @access private
1376
   * @return string
1377
   */
1378
  function EncodeQP( $input = '', $line_max = 76, $space_conv = false ) {
1379
    $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
1380
    $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
1381
    $eol = "\r\n";
1382
    $escape = '=';
1383
    $output = '';
1384
    while( list(, $line) = each($lines) ) {
1385
      $linlen = strlen($line);
1386
      $newline = '';
1387
      for($i = 0; $i < $linlen; $i++) {
1388
        $c = substr( $line, $i, 1 );
1389
        $dec = ord( $c );
1390
        if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
1391
          $c = '=2E';
1392
        }
1393
        if ( $dec == 32 ) {
1394
          if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
1395
            $c = '=20';
1396
          } else if ( $space_conv ) {
1397
            $c = '=20';
1398
          }
1399
        } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
1400
          $h2 = floor($dec/16);
1401
          $h1 = floor($dec%16);
1402
          $c = $escape.$hex[$h2].$hex[$h1];
1403
        }
1404
        if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
1405
          $output .= $newline.$escape.$eol; //  soft line break; " =\r\n" is okay
1406
          $newline = '';
1407
          // check if newline first character will be point or not
1408
          if ( $dec == 46 ) {
1409
            $c = '=2E';
1410
          }
1411
        }
1412
        $newline .= $c;
1413
      } // end of for
1414
      $output .= $newline.$eol;
1415
    } // end of while
1416
    return $output;
1417
  }
1418

    
1419
  /**
1420
   * Encode string to q encoding.
1421
   * @access private
1422
   * @return string
1423
   */
1424
  function EncodeQ ($str, $position = 'text') {
1425
    /* There should not be any EOL in the string */
1426
    $encoded = preg_replace("[\r\n]", '', $str);
1427

    
1428
    switch (strtolower($position)) {
1429
      case 'phrase':
1430
        $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1431
        break;
1432
      case 'comment':
1433
        $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1434
      case 'text':
1435
      default:
1436
        /* Replace every high ascii, control =, ? and _ characters */
1437
        $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
1438
              "'='.sprintf('%02X', ord('\\1'))", $encoded);
1439
        break;
1440
    }
1441

    
1442
    /* Replace every spaces to _ (more readable than =20) */
1443
    $encoded = str_replace(' ', '_', $encoded);
1444

    
1445
    return $encoded;
1446
  }
1447

    
1448
  /**
1449
   * Adds a string or binary attachment (non-filesystem) to the list.
1450
   * This method can be used to attach ascii or binary data,
1451
   * such as a BLOB record from a database.
1452
   * @param string $string String attachment data.
1453
   * @param string $filename Name of the attachment.
1454
   * @param string $encoding File encoding (see $Encoding).
1455
   * @param string $type File extension (MIME) type.
1456
   * @return void
1457
   */
1458
  function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
1459
    /* Append to $attachment array */
1460
    $cur = count($this->attachment);
1461
    $this->attachment[$cur][0] = $string;
1462
    $this->attachment[$cur][1] = $filename;
1463
    $this->attachment[$cur][2] = $filename;
1464
    $this->attachment[$cur][3] = $encoding;
1465
    $this->attachment[$cur][4] = $type;
1466
    $this->attachment[$cur][5] = true; // isString
1467
    $this->attachment[$cur][6] = 'attachment';
1468
    $this->attachment[$cur][7] = 0;
1469
  }
1470

    
1471
  /**
1472
   * Adds an embedded attachment.  This can include images, sounds, and
1473
   * just about any other document.  Make sure to set the $type to an
1474
   * image type.  For JPEG images use "image/jpeg" and for GIF images
1475
   * use "image/gif".
1476
   * @param string $path Path to the attachment.
1477
   * @param string $cid Content ID of the attachment.  Use this to identify
1478
   *        the Id for accessing the image in an HTML form.
1479
   * @param string $name Overrides the attachment name.
1480
   * @param string $encoding File encoding (see $Encoding).
1481
   * @param string $type File extension (MIME) type.
1482
   * @return bool
1483
   */
1484
  function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1485

    
1486
    if(!@is_file($path)) {
1487
      $this->SetError($this->Lang('file_access') . $path);
1488
      return false;
1489
    }
1490

    
1491
    $filename = basename($path);
1492
    if($name == '') {
1493
      $name = $filename;
1494
    }
1495

    
1496
    /* Append to $attachment array */
1497
    $cur = count($this->attachment);
1498
    $this->attachment[$cur][0] = $path;
1499
    $this->attachment[$cur][1] = $filename;
1500
    $this->attachment[$cur][2] = $name;
1501
    $this->attachment[$cur][3] = $encoding;
1502
    $this->attachment[$cur][4] = $type;
1503
    $this->attachment[$cur][5] = false;
1504
    $this->attachment[$cur][6] = 'inline';
1505
    $this->attachment[$cur][7] = $cid;
1506

    
1507
    return true;
1508
  }
1509

    
1510
  /**
1511
   * Returns true if an inline attachment is present.
1512
   * @access private
1513
   * @return bool
1514
   */
1515
  function InlineImageExists() {
1516
    $result = false;
1517
    for($i = 0; $i < count($this->attachment); $i++) {
1518
      if($this->attachment[$i][6] == 'inline') {
1519
        $result = true;
1520
        break;
1521
      }
1522
    }
1523

    
1524
    return $result;
1525
  }
1526

    
1527
  /////////////////////////////////////////////////
1528
  // CLASS METHODS, MESSAGE RESET
1529
  /////////////////////////////////////////////////
1530

    
1531
  /**
1532
   * Clears all recipients assigned in the TO array.  Returns void.
1533
   * @return void
1534
   */
1535
  function ClearAddresses() {
1536
    $this->to = array();
1537
  }
1538

    
1539
  /**
1540
   * Clears all recipients assigned in the CC array.  Returns void.
1541
   * @return void
1542
   */
1543
  function ClearCCs() {
1544
    $this->cc = array();
1545
  }
1546

    
1547
  /**
1548
   * Clears all recipients assigned in the BCC array.  Returns void.
1549
   * @return void
1550
   */
1551
  function ClearBCCs() {
1552
    $this->bcc = array();
1553
  }
1554

    
1555
  /**
1556
   * Clears all recipients assigned in the ReplyTo array.  Returns void.
1557
   * @return void
1558
   */
1559
  function ClearReplyTos() {
1560
    $this->ReplyTo = array();
1561
  }
1562

    
1563
  /**
1564
   * Clears all recipients assigned in the TO, CC and BCC
1565
   * array.  Returns void.
1566
   * @return void
1567
   */
1568
  function ClearAllRecipients() {
1569
    $this->to = array();
1570
    $this->cc = array();
1571
    $this->bcc = array();
1572
  }
1573

    
1574
  /**
1575
   * Clears all previously set filesystem, string, and binary
1576
   * attachments.  Returns void.
1577
   * @return void
1578
   */
1579
  function ClearAttachments() {
1580
    $this->attachment = array();
1581
  }
1582

    
1583
  /**
1584
   * Clears all custom headers.  Returns void.
1585
   * @return void
1586
   */
1587
  function ClearCustomHeaders() {
1588
    $this->CustomHeader = array();
1589
  }
1590

    
1591
  /////////////////////////////////////////////////
1592
  // CLASS METHODS, MISCELLANEOUS
1593
  /////////////////////////////////////////////////
1594

    
1595
  /**
1596
   * Adds the error message to the error container.
1597
   * Returns void.
1598
   * @access private
1599
   * @return void
1600
   */
1601
  function SetError($msg) {
1602
    $this->error_count++;
1603
    $this->ErrorInfo = $msg;
1604
  }
1605

    
1606
  /**
1607
   * Returns the proper RFC 822 formatted date.
1608
   * @access private
1609
   * @return string
1610
   */
1611
  function RFCDate() {
1612
    $tz = date('Z');
1613
    $tzs = ($tz < 0) ? '-' : '+';
1614
    $tz = abs($tz);
1615
    $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
1616
    $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
1617

    
1618
    return $result;
1619
  }
1620

    
1621
  /**
1622
   * Returns the appropriate server variable.  Should work with both
1623
   * PHP 4.1.0+ as well as older versions.  Returns an empty string
1624
   * if nothing is found.
1625
   * @access private
1626
   * @return mixed
1627
   */
1628
  function ServerVar($varName) {
1629
    global $HTTP_SERVER_VARS;
1630
    global $HTTP_ENV_VARS;
1631

    
1632
    if(!isset($_SERVER)) {
1633
      $_SERVER = $HTTP_SERVER_VARS;
1634
      if(!isset($_SERVER['REMOTE_ADDR'])) {
1635
        $_SERVER = $HTTP_ENV_VARS; // must be Apache
1636
      }
1637
    }
1638

    
1639
    if(isset($_SERVER[$varName])) {
1640
      return $_SERVER[$varName];
1641
    } else {
1642
      return '';
1643
    }
1644
  }
1645

    
1646
  /**
1647
   * Returns the server hostname or 'localhost.localdomain' if unknown.
1648
   * @access private
1649
   * @return string
1650
   */
1651
  function ServerHostname() {
1652
    if ($this->Hostname != '') {
1653
      $result = $this->Hostname;
1654
    } elseif ($this->ServerVar('SERVER_NAME') != '') {
1655
      $result = $this->ServerVar('SERVER_NAME');
1656
    } else {
1657
      $result = 'localhost.localdomain';
1658
    }
1659

    
1660
    return $result;
1661
  }
1662

    
1663
  /**
1664
   * Returns a message in the appropriate language.
1665
   * @access private
1666
   * @return string
1667
   */
1668
  function Lang($key) {
1669
    if(count($this->language) < 1) {
1670
      $this->SetLanguage('en'); // set the default language
1671
    }
1672

    
1673
    if(isset($this->language[$key])) {
1674
      return $this->language[$key];
1675
    } else {
1676
      return 'Language string failed to load: ' . $key;
1677
    }
1678
  }
1679

    
1680
  /**
1681
   * Returns true if an error occurred.
1682
   * @return bool
1683
   */
1684
  function IsError() {
1685
    return ($this->error_count > 0);
1686
  }
1687

    
1688
  /**
1689
   * Changes every end of line from CR or LF to CRLF.
1690
   * @access private
1691
   * @return string
1692
   */
1693
  function FixEOL($str) {
1694
    $str = str_replace("\r\n", "\n", $str);
1695
    $str = str_replace("\r", "\n", $str);
1696
    $str = str_replace("\n", $this->LE, $str);
1697
    return $str;
1698
  }
1699

    
1700
  /**
1701
   * Adds a custom header.
1702
   * @return void
1703
   */
1704
  function AddCustomHeader($custom_header) {
1705
    $this->CustomHeader[] = explode(':', $custom_header, 2);
1706
  }
1707

    
1708
  /**
1709
   * Evaluates the message and returns modifications for inline images and backgrounds
1710
   * @access public
1711
   * @return $message
1712
   */
1713
  function MsgHTML($message,$basedir='') {
1714
    preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
1715
    if(isset($images[2])) {
1716
      foreach($images[2] as $i => $url) {
1717
        // do not change urls for absolute images (thanks to corvuscorax)
1718
        if (!preg_match('/^[A-z][A-z]*:\/\//',$url)) {
1719
          $filename = basename($url);
1720
          $directory = dirname($url);
1721
          ($directory == '.')?$directory='':'';
1722
          $cid = 'cid:' . md5($filename);
1723
          $fileParts = split("\.", $filename);
1724
          $ext = $fileParts[1];
1725
          $mimeType = $this->_mime_types($ext);
1726
          if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }
1727
          if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }
1728
          if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {
1729
            $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
1730
          }
1731
        }
1732
      }
1733
    }
1734
    $this->IsHTML(true);
1735
    $this->Body = $message;
1736
    $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));
1737
    if ( !empty($textMsg) && empty($this->AltBody) ) {
1738
      $this->AltBody = html_entity_decode($textMsg);
1739
    }
1740
    if ( empty($this->AltBody) ) {
1741
      $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n";
1742
    }
1743
  }
1744

    
1745
  /**
1746
   * Gets the mime type of the embedded or inline image
1747
   * @access private
1748
   * @return mime type of ext
1749
   */
1750
  function _mime_types($ext = '') {
1751
    $mimes = array(
1752
      'ai'    =>  'application/postscript',
1753
      'aif'   =>  'audio/x-aiff',
1754
      'aifc'  =>  'audio/x-aiff',
1755
      'aiff'  =>  'audio/x-aiff',
1756
      'avi'   =>  'video/x-msvideo',
1757
      'bin'   =>  'application/macbinary',
1758
      'bmp'   =>  'image/bmp',
1759
      'class' =>  'application/octet-stream',
1760
      'cpt'   =>  'application/mac-compactpro',
1761
      'css'   =>  'text/css',
1762
      'dcr'   =>  'application/x-director',
1763
      'dir'   =>  'application/x-director',
1764
      'dll'   =>  'application/octet-stream',
1765
      'dms'   =>  'application/octet-stream',
1766
      'doc'   =>  'application/msword',
1767
      'dvi'   =>  'application/x-dvi',
1768
      'dxr'   =>  'application/x-director',
1769
      'eml'   =>  'message/rfc822',
1770
      'eps'   =>  'application/postscript',
1771
      'exe'   =>  'application/octet-stream',
1772
      'gif'   =>  'image/gif',
1773
      'gtar'  =>  'application/x-gtar',
1774
      'htm'   =>  'text/html',
1775
      'html'  =>  'text/html',
1776
      'jpe'   =>  'image/jpeg',
1777
      'jpeg'  =>  'image/jpeg',
1778
      'jpg'   =>  'image/jpeg',
1779
      'hqx'   =>  'application/mac-binhex40',
1780
      'js'    =>  'application/x-javascript',
1781
      'lha'   =>  'application/octet-stream',
1782
      'log'   =>  'text/plain',
1783
      'lzh'   =>  'application/octet-stream',
1784
      'mid'   =>  'audio/midi',
1785
      'midi'  =>  'audio/midi',
1786
      'mif'   =>  'application/vnd.mif',
1787
      'mov'   =>  'video/quicktime',
1788
      'movie' =>  'video/x-sgi-movie',
1789
      'mp2'   =>  'audio/mpeg',
1790
      'mp3'   =>  'audio/mpeg',
1791
      'mpe'   =>  'video/mpeg',
1792
      'mpeg'  =>  'video/mpeg',
1793
      'mpg'   =>  'video/mpeg',
1794
      'mpga'  =>  'audio/mpeg',
1795
      'oda'   =>  'application/oda',
1796
      'pdf'   =>  'application/pdf',
1797
      'php'   =>  'application/x-httpd-php',
1798
      'php3'  =>  'application/x-httpd-php',
1799
      'php4'  =>  'application/x-httpd-php',
1800
      'phps'  =>  'application/x-httpd-php-source',
1801
      'phtml' =>  'application/x-httpd-php',
1802
      'png'   =>  'image/png',
1803
      'ppt'   =>  'application/vnd.ms-powerpoint',
1804
      'ps'    =>  'application/postscript',
1805
      'psd'   =>  'application/octet-stream',
1806
      'qt'    =>  'video/quicktime',
1807
      'ra'    =>  'audio/x-realaudio',
1808
      'ram'   =>  'audio/x-pn-realaudio',
1809
      'rm'    =>  'audio/x-pn-realaudio',
1810
      'rpm'   =>  'audio/x-pn-realaudio-plugin',
1811
      'rtf'   =>  'text/rtf',
1812
      'rtx'   =>  'text/richtext',
1813
      'rv'    =>  'video/vnd.rn-realvideo',
1814
      'sea'   =>  'application/octet-stream',
1815
      'shtml' =>  'text/html',
1816
      'sit'   =>  'application/x-stuffit',
1817
      'so'    =>  'application/octet-stream',
1818
      'smi'   =>  'application/smil',
1819
      'smil'  =>  'application/smil',
1820
      'swf'   =>  'application/x-shockwave-flash',
1821
      'tar'   =>  'application/x-tar',
1822
      'text'  =>  'text/plain',
1823
      'txt'   =>  'text/plain',
1824
      'tgz'   =>  'application/x-tar',
1825
      'tif'   =>  'image/tiff',
1826
      'tiff'  =>  'image/tiff',
1827
      'wav'   =>  'audio/x-wav',
1828
      'wbxml' =>  'application/vnd.wap.wbxml',
1829
      'wmlc'  =>  'application/vnd.wap.wmlc',
1830
      'word'  =>  'application/msword',
1831
      'xht'   =>  'application/xhtml+xml',
1832
      'xhtml' =>  'application/xhtml+xml',
1833
      'xl'    =>  'application/excel',
1834
      'xls'   =>  'application/vnd.ms-excel',
1835
      'xml'   =>  'text/xml',
1836
      'xsl'   =>  'text/xml',
1837
      'zip'   =>  'application/zip'
1838
    );
1839
    return ( ! isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
1840
  }
1841

    
1842
  /**
1843
   * Set (or reset) Class Objects (variables)
1844
   *
1845
   * Usage Example:
1846
   * $page->set('X-Priority', '3');
1847
   *
1848
   * @access public
1849
   * @param string $name Parameter Name
1850
   * @param mixed $value Parameter Value
1851
   * NOTE: will not work with arrays, there are no arrays to set/reset
1852
   */
1853
  function set ( $name, $value = '' ) {
1854
    if ( isset($this->$name) ) {
1855
      $this->$name = $value;
1856
    } else {
1857
      $this->SetError('Cannot set or reset variable ' . $name);
1858
      return false;
1859
    }
1860
  }
1861

    
1862
  /**
1863
   * Read a file from a supplied filename and return it.
1864
   *
1865
   * @access public
1866
   * @param string $filename Parameter File Name
1867
   */
1868
  function getFile($filename) {
1869
    $return = '';
1870
    if ($fp = fopen($filename, 'rb')) {
1871
      while (!feof($fp)) {
1872
        $return .= fread($fp, 1024);
1873
      }
1874
      fclose($fp);
1875
      return $return;
1876
    } else {
1877
      return false;
1878
    }
1879
  }
1880

    
1881
  /**
1882
   * Strips newlines to prevent header injection.
1883
   * @access private
1884
   * @param string $str String
1885
   * @return string
1886
   */
1887
  function SecureHeader($str) {
1888
    $str = trim($str);
1889
    $str = str_replace("\r", "", $str);
1890
    $str = str_replace("\n", "", $str);
1891
    return $str;
1892
  }
1893

    
1894
  /**
1895
   * Set the private key file and password to sign the message.
1896
   *
1897
   * @access public
1898
   * @param string $key_filename Parameter File Name
1899
   * @param string $key_pass Password for private key
1900
   */
1901
  function Sign($cert_filename, $key_filename, $key_pass) {
1902
    $this->sign_cert_file = $cert_filename;
1903
    $this->sign_key_file = $key_filename;
1904
    $this->sign_key_pass = $key_pass;
1905
  }
1906

    
1907
}
1908

    
1909
?>
(4-4/7)