Project

General

Profile

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