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
    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
  {
588
    if($this->smtp == NULL)
589
    {
590
      $this->smtp = new SMTP();
591
    }
592

    
593
    $this->smtp->do_debug = $this->SMTPDebug;
594
    $hosts = explode(';', $this->Host);
595
    $index = 0;
596
    $connection = ($this->smtp->Connected());
597

    
598
    /* Retry while there is no connection */
599
    while($index < count($hosts) && $connection == false)
600
    {
601
      $hostinfo = array();
602
      if(preg_match('/(.+):([0-9]+)/', $hosts[$index], $hostinfo))
603
      {
604
        $host = $hostinfo[1];
605
        $port = $hostinfo[2];
606
      } else {
607
        $host = $hosts[$index];
608
        $port = $this->Port;
609
      }
610

    
611
      if($this->smtp->Connect(((!empty($this->SMTPSecure))?$this->SMTPSecure.'://':'').$host, $port, $this->Timeout)) {
612
        if ($this->Helo != '') {
613
          $this->smtp->Hello($this->Helo);
614
        } else {
615
          $this->smtp->Hello($this->ServerHostname());
616
        }
617

    
618
        $connection = true;
619
        if($this->SMTPAuth) {
620
          if(!$this->smtp->Authenticate($this->Username, $this->Password)) {
621
            $this->SetError($this->Lang('authenticate'));
622
            $this->smtp->Reset();
623
            $connection = false;
624
          }
625
        }
626
      }
627
      $index++;
628
    }
629
    if(!$connection) {
630
      $this->SetError($this->Lang('connect_host'));
631
    }
632

    
633
    return $connection;
634
  }
635

    
636
  /**
637
   * Closes the active SMTP session if one exists.
638
  * @return void
639
   */
640
  function SmtpClose() {
641
    if($this->smtp != NULL) {
642
      if($this->smtp->Connected()) {
643
        $this->smtp->Quit();
644
        $this->smtp->Close();
645
      }
646
    }
647
  }
648

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

    
681
    return true;
682
  }
683

    
684
  /////////////////////////////////////////////////
685
  // METHODS, MESSAGE CREATION
686
  /////////////////////////////////////////////////
687

    
688
  /**
689
   * Creates recipient headers.
690
  * @access private
691
  * @return string
692
   */
693
  function AddrAppend($type, $addr) {
694
    $addr_str = $type . ': ';
695
    $addr_str .= $this->AddrFormat($addr[0]);
696
    if(count($addr) > 1) {
697
      for($i = 1; $i < count($addr); $i++) {
698
        $addr_str .= ', ' . $this->AddrFormat($addr[$i]);
699
      }
700
    }
701
    $addr_str .= $this->LE;
702

    
703
    return $addr_str;
704
  }
705

    
706
  /**
707
   * Formats an address correctly.
708
  * @access private
709
  * @return string
710
   */
711
  function AddrFormat($addr) {
712
    if(empty($addr[1])) {
713
      $formatted = $this->SecureHeader($addr[0]);
714
    } else {
715
      $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
716
    }
717

    
718
    return $formatted;
719
  }
720

    
721
  /**
722
   * Wraps message for use with mailers that do not
723
   * automatically perform wrapping and for quoted-printable.
724
   * Original written by philippe.
725
  * @access private
726
  * @return string
727
   */
728
  function WrapText($message, $length, $qp_mode = false) {
729
    $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
730
    // If utf-8 encoding is used, we will need to make sure we don't
731
    // split multibyte characters when we wrap
732
    $is_utf8 = (strtolower($this->CharSet) == "utf-8");
733

    
734
    $message = $this->FixEOL($message);
735
    if (substr($message, -1) == $this->LE) {
736
      $message = substr($message, 0, -1);
737
    }
738

    
739
    $line = explode($this->LE, $message);
740
    $message = '';
741
    for ($i=0 ;$i < count($line); $i++) {
742
      $line_part = explode(' ', $line[$i]);
743
      $buf = '';
744
      for ($e = 0; $e<count($line_part); $e++) {
745
        $word = $line_part[$e];
746
        if ($qp_mode and (strlen($word) > $length)) {
747
          $space_left = $length - strlen($buf) - 1;
748
          if ($e != 0) {
749
            if ($space_left > 20) {
750
              $len = $space_left;
751
              if ($is_utf8) {
752
                $len = $this->UTF8CharBoundary($word, $len);
753
              } elseif (substr($word, $len - 1, 1) == "=") {
754
                $len--;
755
              } elseif (substr($word, $len - 2, 1) == "=") {
756
                $len -= 2;
757
              }
758
              $part = substr($word, 0, $len);
759
              $word = substr($word, $len);
760
              $buf .= ' ' . $part;
761
              $message .= $buf . sprintf("=%s", $this->LE);
762
            } else {
763
              $message .= $buf . $soft_break;
764
            }
765
            $buf = '';
766
          }
767
          while (strlen($word) > 0) {
768
            $len = $length;
769
            if ($is_utf8) {
770
              $len = $this->UTF8CharBoundary($word, $len);
771
            } elseif (substr($word, $len - 1, 1) == "=") {
772
              $len--;
773
            } elseif (substr($word, $len - 2, 1) == "=") {
774
              $len -= 2;
775
            }
776
            $part = substr($word, 0, $len);
777
            $word = substr($word, $len);
778

    
779
            if (strlen($word) > 0) {
780
              $message .= $part . sprintf("=%s", $this->LE);
781
            } else {
782
              $buf = $part;
783
            }
784
          }
785
        } else {
786
          $buf_o = $buf;
787
          $buf .= ($e == 0) ? $word : (' ' . $word);
788

    
789
          if (strlen($buf) > $length and $buf_o != '') {
790
            $message .= $buf_o . $soft_break;
791
            $buf = $word;
792
          }
793
        }
794
      }
795
      $message .= $buf . $this->LE;
796
    }
797

    
798
    return $message;
799
  }
800

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

    
842
  /**
843
   * Set the body wrapping.
844
  * @access private
845
  * @return void
846
   */
847
  function SetWordWrap() {
848
    if($this->WordWrap < 1) {
849
      return;
850
    }
851

    
852
    switch($this->message_type) {
853
      case 'alt':
854
        /* fall through */
855
      case 'alt_attachments':
856
        $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
857
        break;
858
      default:
859
        $this->Body = $this->WrapText($this->Body, $this->WordWrap);
860
        break;
861
    }
862
  }
863

    
864
  /**
865
   * Assembles message header.
866
  * @access private
867
  * @return string
868
   */
869
  function CreateHeader() {
870
    $result = '';
871

    
872
    /* Set the boundaries */
873
    $uniq_id = md5(uniqid(time()));
874
    $this->boundary[1] = 'b1_' . $uniq_id;
875
    $this->boundary[2] = 'b2_' . $uniq_id;
876

    
877
    $result .= $this->HeaderLine('Date', $this->RFCDate());
878
    if($this->Sender == '') {
879
      $result .= $this->HeaderLine('Return-Path', trim($this->From));
880
    } else {
881
      $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
882
    }
883

    
884
    /* To be created automatically by mail() */
885
    if($this->Mailer != 'mail') {
886
      if(count($this->to) > 0) {
887
        $result .= $this->AddrAppend('To', $this->to);
888
      } elseif (count($this->cc) == 0) {
889
        $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
890
      }
891
    }
892

    
893
    $from = array();
894
    $from[0][0] = trim($this->From);
895
    $from[0][1] = $this->FromName;
896
    $result .= $this->AddrAppend('From', $from);
897

    
898
    /* sendmail and mail() extract Cc from the header before sending */
899
    if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) {
900
      $result .= $this->AddrAppend('Cc', $this->cc);
901
    }
902

    
903
    /* sendmail and mail() extract Bcc from the header before sending */
904
    if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
905
      $result .= $this->AddrAppend('Bcc', $this->bcc);
906
    }
907

    
908
    if(count($this->ReplyTo) > 0) {
909
      $result .= $this->AddrAppend('Reply-To', $this->ReplyTo);
910
    }
911

    
912
    /* mail() sets the subject itself */
913
    if($this->Mailer != 'mail') {
914
      $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
915
    }
916

    
917
    if($this->MessageID != '') {
918
      $result .= $this->HeaderLine('Message-ID',$this->MessageID);
919
    } else {
920
      $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
921
    }
922
    $result .= $this->HeaderLine('X-Priority', $this->Priority);
923
    $result .= $this->HeaderLine('X-Mailer', 'PHPMailer (phpmailer.sourceforge.net) [version ' . $this->Version . ']');
924

    
925
    if($this->ConfirmReadingTo != '') {
926
      $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
927
    }
928

    
929
    // Add custom headers
930
    for($index = 0; $index < count($this->CustomHeader); $index++) {
931
      $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
932
    }
933
    if (!$this->sign_key_file) {
934
      $result .= $this->HeaderLine('MIME-Version', '1.0');
935
      $result .= $this->GetMailMIME();
936
    }
937

    
938
    return $result;
939
  }
940

    
941
  /**
942
   * Returns the message MIME.
943
  * @access private
944
  * @return string
945
   */
946
  function GetMailMIME() {
947
    $result = '';
948
    switch($this->message_type) {
949
      case 'plain':
950
        $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
951
        $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
952
        break;
953
      case 'attachments':
954
        /* fall through */
955
      case 'alt_attachments':
956
        if($this->InlineImageExists()){
957
          $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
958
        } else {
959
          $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
960
          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
961
        }
962
        break;
963
      case 'alt':
964
        $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
965
        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
966
        break;
967
    }
968

    
969
    if($this->Mailer != 'mail') {
970
      $result .= $this->LE.$this->LE;
971
    }
972

    
973
    return $result;
974
  }
975

    
976
  /**
977
   * Assembles the message body.  Returns an empty string on failure.
978
  * @access private
979
  * @return string
980
   */
981
  function CreateBody() {
982
    $result = '';
983
    if ($this->sign_key_file) {
984
      $result .= $this->GetMailMIME();
985
    }
986

    
987
    $this->SetWordWrap();
988

    
989
    switch($this->message_type) {
990
      case 'alt':
991
        $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
992
        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
993
        $result .= $this->LE.$this->LE;
994
        $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
995
        $result .= $this->EncodeString($this->Body, $this->Encoding);
996
        $result .= $this->LE.$this->LE;
997
        $result .= $this->EndBoundary($this->boundary[1]);
998
        break;
999
      case 'plain':
1000
        $result .= $this->EncodeString($this->Body, $this->Encoding);
1001
        break;
1002
      case 'attachments':
1003
        $result .= $this->GetBoundary($this->boundary[1], '', '', '');
1004
        $result .= $this->EncodeString($this->Body, $this->Encoding);
1005
        $result .= $this->LE;
1006
        $result .= $this->AttachAll();
1007
        break;
1008
      case 'alt_attachments':
1009
        $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
1010
        $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
1011
        $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
1012
        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
1013
        $result .= $this->LE.$this->LE;
1014
        $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
1015
        $result .= $this->EncodeString($this->Body, $this->Encoding);
1016
        $result .= $this->LE.$this->LE;
1017
        $result .= $this->EndBoundary($this->boundary[2]);
1018
        $result .= $this->AttachAll();
1019
        break;
1020
    }
1021

    
1022
    if($this->IsError()) {
1023
      $result = '';
1024
    } else if ($this->sign_key_file) {
1025
      $file = tempnam("", "mail");
1026
      $fp = fopen($file, "w");
1027
      fwrite($fp, $result);
1028
      fclose($fp);
1029
      $signed = tempnam("", "signed");
1030

    
1031
      if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), null)) {
1032
        $fp = fopen($signed, "r");
1033
        $result = fread($fp, filesize($this->sign_key_file));
1034
        $result = '';
1035
        while(!feof($fp)){
1036
          $result = $result . fread($fp, 1024);
1037
        }
1038
        fclose($fp);
1039
      } else {
1040
        $this->SetError($this->Lang("signing").openssl_error_string());
1041
        $result = '';
1042
      }
1043

    
1044
      unlink($file);
1045
      unlink($signed);
1046
    }
1047

    
1048
    return $result;
1049
  }
1050

    
1051
  /**
1052
   * Returns the start of a message boundary.
1053
  * @access private
1054
   */
1055
  function GetBoundary($boundary, $charSet, $contentType, $encoding) {
1056
    $result = '';
1057
    if($charSet == '') {
1058
      $charSet = $this->CharSet;
1059
    }
1060
    if($contentType == '') {
1061
      $contentType = $this->ContentType;
1062
    }
1063
    if($encoding == '') {
1064
      $encoding = $this->Encoding;
1065
    }
1066
    $result .= $this->TextLine('--' . $boundary);
1067
    $result .= sprintf("Content-Type: %s; charset =\"%s\"", $contentType, $charSet);
1068
    $result .= $this->LE;
1069
    $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
1070
    $result .= $this->LE;
1071

    
1072
    return $result;
1073
  }
1074

    
1075
  /**
1076
   * Returns the end of a message boundary.
1077
  * @access private
1078
   */
1079
  function EndBoundary($boundary) {
1080
    return $this->LE . '--' . $boundary . '--' . $this->LE;
1081
  }
1082

    
1083
  /**
1084
   * Sets the message type.
1085
  * @access private
1086
  * @return void
1087
   */
1088
  function SetMessageType() {
1089
    if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
1090
      $this->message_type = 'plain';
1091
    } else {
1092
      if(count($this->attachment) > 0) {
1093
        $this->message_type = 'attachments';
1094
      }
1095
      if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
1096
        $this->message_type = 'alt';
1097
      }
1098
      if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
1099
        $this->message_type = 'alt_attachments';
1100
      }
1101
    }
1102
  }
1103

    
1104
  /* Returns a formatted header line.
1105
  * @access private
1106
  * @return string
1107
   */
1108
  function HeaderLine($name, $value) {
1109
    return $name . ': ' . $value . $this->LE;
1110
  }
1111

    
1112
  /**
1113
   * Returns a formatted mail line.
1114
  * @access private
1115
  * @return string
1116
   */
1117
  function TextLine($value) {
1118
    return $value . $this->LE;
1119
  }
1120

    
1121
  /////////////////////////////////////////////////
1122
  // CLASS METHODS, ATTACHMENTS
1123
  /////////////////////////////////////////////////
1124

    
1125
  /**
1126
   * Adds an attachment from a path on the filesystem.
1127
   * Returns false if the file could not be found
1128
   * or accessed.
1129
  * @param string $path Path to the attachment.
1130
  * @param string $name Overrides the attachment name.
1131
  * @param string $encoding File encoding (see $Encoding).
1132
  * @param string $type File extension (MIME) type.
1133
  * @return bool
1134
   */
1135
  function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1136
    if(!@is_file($path)) {
1137
      $this->SetError($this->Lang('file_access') . $path);
1138
      return false;
1139
    }
1140

    
1141
    $filename = basename($path);
1142
    if($name == '') {
1143
      $name = $filename;
1144
    }
1145

    
1146
    $cur = count($this->attachment);
1147
    $this->attachment[$cur][0] = $path;
1148
    $this->attachment[$cur][1] = $filename;
1149
    $this->attachment[$cur][2] = $name;
1150
    $this->attachment[$cur][3] = $encoding;
1151
    $this->attachment[$cur][4] = $type;
1152
    $this->attachment[$cur][5] = false; // isStringAttachment
1153
    $this->attachment[$cur][6] = 'attachment';
1154
    $this->attachment[$cur][7] = 0;
1155

    
1156
    return true;
1157
  }
1158

    
1159
  /**
1160
   * Attaches all fs, string, and binary attachments to the message.
1161
   * Returns an empty string on failure.
1162
  * @access private
1163
  * @return string
1164
   */
1165
  function AttachAll() {
1166
    /* Return text of body */
1167
    $mime = array();
1168

    
1169
    /* Add all attachments */
1170
    for($i = 0; $i < count($this->attachment); $i++) {
1171
      /* Check for string attachment */
1172
      $bString = $this->attachment[$i][5];
1173
      if ($bString) {
1174
        $string = $this->attachment[$i][0];
1175
      } else {
1176
        $path = $this->attachment[$i][0];
1177
      }
1178

    
1179
      $filename    = $this->attachment[$i][1];
1180
      $name        = $this->attachment[$i][2];
1181
      $encoding    = $this->attachment[$i][3];
1182
      $type        = $this->attachment[$i][4];
1183
      $disposition = $this->attachment[$i][6];
1184
      $cid         = $this->attachment[$i][7];
1185

    
1186
      $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
1187
      $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $this->EncodeHeader($this->SecureHeader($name)), $this->LE);
1188
      $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
1189

    
1190
      if($disposition == 'inline') {
1191
        $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
1192
      }
1193

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

    
1196
      /* Encode as string attachment */
1197
      if($bString) {
1198
        $mime[] = $this->EncodeString($string, $encoding);
1199
        if($this->IsError()) {
1200
          return '';
1201
        }
1202
        $mime[] = $this->LE.$this->LE;
1203
      } else {
1204
        $mime[] = $this->EncodeFile($path, $encoding);
1205
        if($this->IsError()) {
1206
          return '';
1207
        }
1208
        $mime[] = $this->LE.$this->LE;
1209
      }
1210
    }
1211

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

    
1214
    return join('', $mime);
1215
  }
1216

    
1217
  /**
1218
   * Encodes attachment in requested format.  Returns an
1219
   * empty string on failure.
1220
  * @access private
1221
  * @return string
1222
   */
1223
  function EncodeFile ($path, $encoding = 'base64') {
1224
    if(!@$fd = fopen($path, 'rb')) {
1225
      $this->SetError($this->Lang('file_open') . $path);
1226
      return '';
1227
    }
1228

    
1229
    if(version_compare(PHP_VERSION, '5.3.0', '<'))
1230
    {
1231
        $magic_quotes = get_magic_quotes_runtime();
1232
        set_magic_quotes_runtime(0); // Disable magic_quotes_runtime
1233
    }
1234

    
1235
    $file_buffer = fread($fd, filesize($path));
1236
    $file_buffer = $this->EncodeString($file_buffer, $encoding);
1237
    fclose($fd);
1238

    
1239
    if(version_compare(PHP_VERSION, '5.3.0', '<'))
1240
    {
1241
        set_magic_quotes_runtime($magic_quotes);
1242
    }
1243

    
1244
    return $file_buffer;
1245
  }
1246

    
1247
  /**
1248
   * Encodes string to requested format. Returns an
1249
   * empty string on failure.
1250
  * @access private
1251
  * @return string
1252
   */
1253
  function EncodeString ($str, $encoding = 'base64') {
1254
    $encoded = '';
1255
    switch(strtolower($encoding)) {
1256
      case 'base64':
1257
        /* chunk_split is found in PHP >= 3.0.6 */
1258
        $encoded = chunk_split(base64_encode($str), 76, $this->LE);
1259
        break;
1260
      case '7bit':
1261
      case '8bit':
1262
        $encoded = $this->FixEOL($str);
1263
        if (substr($encoded, -(strlen($this->LE))) != $this->LE)
1264
          $encoded .= $this->LE;
1265
        break;
1266
      case 'binary':
1267
        $encoded = $str;
1268
        break;
1269
      case 'quoted-printable':
1270
        $encoded = $this->EncodeQP($str);
1271
        break;
1272
      default:
1273
        $this->SetError($this->Lang('encoding') . $encoding);
1274
        break;
1275
    }
1276
    return $encoded;
1277
  }
1278

    
1279
  /**
1280
   * Encode a header string to best of Q, B, quoted or none.
1281
  * @access private
1282
  * @return string
1283
   */
1284
  function EncodeHeader ($str, $position = 'text') {
1285
    $x = 0;
1286

    
1287
    switch (strtolower($position)) {
1288
      case 'phrase':
1289
        if (!preg_match('/[\200-\377]/', $str)) {
1290
          /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */
1291
          $encoded = addcslashes($str, "\0..\37\177\\\"");
1292
          if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
1293
            return ($encoded);
1294
          } else {
1295
            return ("\"$encoded\"");
1296
          }
1297
        }
1298
        $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
1299
        break;
1300
      case 'comment':
1301
        $x = preg_match_all('/[()"]/', $str, $matches);
1302
        /* Fall-through */
1303
      case 'text':
1304
      default:
1305
        $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
1306
        break;
1307
    }
1308

    
1309
    if ($x == 0) {
1310
      return ($str);
1311
    }
1312

    
1313
    $maxlen = 75 - 7 - strlen($this->CharSet);
1314
    /* Try to select the encoding which should produce the shortest output */
1315
    if (strlen($str)/3 < $x) {
1316
      $encoding = 'B';
1317
      if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
1318
     // Use a custom function which correctly encodes and wraps long
1319
     // multibyte strings without breaking lines within a character
1320
        $encoded = $this->Base64EncodeWrapMB($str);
1321
      } else {
1322
        $encoded = base64_encode($str);
1323
        $maxlen -= $maxlen % 4;
1324
        $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
1325
      }
1326
    } else {
1327
      $encoding = 'Q';
1328
      $encoded = $this->EncodeQ($str, $position);
1329
      $encoded = $this->WrapText($encoded, $maxlen, true);
1330
      $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
1331
    }
1332

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

    
1336
    return $encoded;
1337
  }
1338

    
1339
  /**
1340
   * Checks if a string contains multibyte characters.
1341
  * @access private
1342
  * @param string $str multi-byte text to wrap encode
1343
  * @return bool
1344
   */
1345
  function HasMultiBytes($str) {
1346
    if (function_exists('mb_strlen')) {
1347
      return (strlen($str) > mb_strlen($str, $this->CharSet));
1348
    } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
1349
      return False;
1350
    }
1351
  }
1352

    
1353
  /**
1354
   * Correctly encodes and wraps long multibyte strings for mail headers
1355
   * without breaking lines within a character.
1356
   * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
1357
  * @access private
1358
  * @param string $str multi-byte text to wrap encode
1359
  * @return string
1360
   */
1361
  function Base64EncodeWrapMB($str) {
1362
    $start = "=?".$this->CharSet."?B?";
1363
    $end = "?=";
1364
    $encoded = "";
1365

    
1366
    $mb_length = mb_strlen($str, $this->CharSet);
1367
    // Each line must have length <= 75, including $start and $end
1368
    $length = 75 - strlen($start) - strlen($end);
1369
    // Average multi-byte ratio
1370
    $ratio = $mb_length / strlen($str);
1371
    // Base64 has a 4:3 ratio
1372
    $offset = $avgLength = floor($length * $ratio * .75);
1373

    
1374
    for ($i = 0; $i < $mb_length; $i += $offset) {
1375
      $lookBack = 0;
1376

    
1377
      do {
1378
        $offset = $avgLength - $lookBack;
1379
        $chunk = mb_substr($str, $i, $offset, $this->CharSet);
1380
        $chunk = base64_encode($chunk);
1381
        $lookBack++;
1382
      }
1383
      while (strlen($chunk) > $length);
1384

    
1385
      $encoded .= $chunk . $this->LE;
1386
    }
1387

    
1388
    // Chomp the last linefeed
1389
    $encoded = substr($encoded, 0, -strlen($this->LE));
1390
    return $encoded;
1391
  }
1392

    
1393
  /**
1394
   * Encode string to quoted-printable.
1395
  * @access private
1396
  * @return string
1397
   */
1398
  function EncodeQP( $input = '', $line_max = 76, $space_conv = false ) {
1399
    $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
1400
    $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
1401
    $eol = "\r\n";
1402
    $escape = '=';
1403
    $output = '';
1404
    while( list(, $line) = each($lines) ) {
1405
      $linlen = strlen($line);
1406
      $newline = '';
1407
      for($i = 0; $i < $linlen; $i++) {
1408
        $c = substr( $line, $i, 1 );
1409
        $dec = ord( $c );
1410
        if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
1411
          $c = '=2E';
1412
        }
1413
        if ( $dec == 32 ) {
1414
          if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
1415
            $c = '=20';
1416
          } else if ( $space_conv ) {
1417
            $c = '=20';
1418
          }
1419
        } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
1420
          $h2 = floor($dec/16);
1421
          $h1 = floor($dec%16);
1422
          $c = $escape.$hex[$h2].$hex[$h1];
1423
        }
1424
        if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
1425
          $output .= $newline.$escape.$eol; //  soft line break; " =\r\n" is okay
1426
          $newline = '';
1427
          // check if newline first character will be point or not
1428
          if ( $dec == 46 ) {
1429
            $c = '=2E';
1430
          }
1431
        }
1432
        $newline .= $c;
1433
      } // end of for
1434
      $output .= $newline.$eol;
1435
    } // end of while
1436
    return $output;
1437
  }
1438

    
1439
  /**
1440
   * Encode string to q encoding.
1441
  * @access private
1442
  * @return string
1443
   */
1444
  function EncodeQ ($str, $position = 'text') {
1445
    /* There should not be any EOL in the string */
1446
    $encoded = preg_replace("[\r\n]", '', $str);
1447

    
1448
    switch (strtolower($position)) {
1449
      case 'phrase':
1450
        $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1451
        break;
1452
      case 'comment':
1453
        $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
1454
      case 'text':
1455
      default:
1456
        /* Replace every high ascii, control =, ? and _ characters */
1457
        $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
1458
              "'='.sprintf('%02X', ord('\\1'))", $encoded);
1459
        break;
1460
    }
1461

    
1462
    /* Replace every spaces to _ (more readable than =20) */
1463
    $encoded = str_replace(' ', '_', $encoded);
1464

    
1465
    return $encoded;
1466
  }
1467

    
1468
  /**
1469
   * Adds a string or binary attachment (non-filesystem) to the list.
1470
   * This method can be used to attach ascii or binary data,
1471
   * such as a BLOB record from a database.
1472
  * @param string $string String attachment data.
1473
  * @param string $filename Name of the attachment.
1474
  * @param string $encoding File encoding (see $Encoding).
1475
  * @param string $type File extension (MIME) type.
1476
  * @return void
1477
   */
1478
  function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
1479
    /* Append to $attachment array */
1480
    $cur = count($this->attachment);
1481
    $this->attachment[$cur][0] = $string;
1482
    $this->attachment[$cur][1] = $filename;
1483
    $this->attachment[$cur][2] = $filename;
1484
    $this->attachment[$cur][3] = $encoding;
1485
    $this->attachment[$cur][4] = $type;
1486
    $this->attachment[$cur][5] = true; // isString
1487
    $this->attachment[$cur][6] = 'attachment';
1488
    $this->attachment[$cur][7] = 0;
1489
  }
1490

    
1491
  /**
1492
   * Adds an embedded attachment.  This can include images, sounds, and
1493
   * just about any other document.  Make sure to set the $type to an
1494
   * image type.  For JPEG images use "image/jpeg" and for GIF images
1495
   * use "image/gif".
1496
  * @param string $path Path to the attachment.
1497
  * @param string $cid Content ID of the attachment.  Use this to identify
1498
   *        the Id for accessing the image in an HTML form.
1499
  * @param string $name Overrides the attachment name.
1500
  * @param string $encoding File encoding (see $Encoding).
1501
  * @param string $type File extension (MIME) type.
1502
  * @return bool
1503
   */
1504
  function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
1505

    
1506
    if(!@is_file($path)) {
1507
      $this->SetError($this->Lang('file_access') . $path);
1508
      return false;
1509
    }
1510

    
1511
    $filename = basename($path);
1512
    if($name == '') {
1513
      $name = $filename;
1514
    }
1515

    
1516
    /* Append to $attachment array */
1517
    $cur = count($this->attachment);
1518
    $this->attachment[$cur][0] = $path;
1519
    $this->attachment[$cur][1] = $filename;
1520
    $this->attachment[$cur][2] = $name;
1521
    $this->attachment[$cur][3] = $encoding;
1522
    $this->attachment[$cur][4] = $type;
1523
    $this->attachment[$cur][5] = false;
1524
    $this->attachment[$cur][6] = 'inline';
1525
    $this->attachment[$cur][7] = $cid;
1526

    
1527
    return true;
1528
  }
1529

    
1530
  /**
1531
   * Returns true if an inline attachment is present.
1532
  * @access private
1533
  * @return bool
1534
   */
1535
  function InlineImageExists() {
1536
    $result = false;
1537
    for($i = 0; $i < count($this->attachment); $i++) {
1538
      if($this->attachment[$i][6] == 'inline') {
1539
        $result = true;
1540
        break;
1541
      }
1542
    }
1543

    
1544
    return $result;
1545
  }
1546

    
1547
  /////////////////////////////////////////////////
1548
  // CLASS METHODS, MESSAGE RESET
1549
  /////////////////////////////////////////////////
1550

    
1551
  /**
1552
   * Clears all recipients assigned in the TO array.  Returns void.
1553
  * @return void
1554
   */
1555
  function ClearAddresses() {
1556
    $this->to = array();
1557
  }
1558

    
1559
  /**
1560
   * Clears all recipients assigned in the CC array.  Returns void.
1561
  * @return void
1562
   */
1563
  function ClearCCs() {
1564
    $this->cc = array();
1565
  }
1566

    
1567
  /**
1568
   * Clears all recipients assigned in the BCC array.  Returns void.
1569
  * @return void
1570
   */
1571
  function ClearBCCs() {
1572
    $this->bcc = array();
1573
  }
1574

    
1575
  /**
1576
   * Clears all recipients assigned in the ReplyTo array.  Returns void.
1577
  * @return void
1578
   */
1579
  function ClearReplyTos() {
1580
    $this->ReplyTo = array();
1581
  }
1582

    
1583
  /**
1584
   * Clears all recipients assigned in the TO, CC and BCC
1585
   * array.  Returns void.
1586
  * @return void
1587
   */
1588
  function ClearAllRecipients() {
1589
    $this->to = array();
1590
    $this->cc = array();
1591
    $this->bcc = array();
1592
  }
1593

    
1594
  /**
1595
   * Clears all previously set filesystem, string, and binary
1596
   * attachments.  Returns void.
1597
  * @return void
1598
   */
1599
  function ClearAttachments() {
1600
    $this->attachment = array();
1601
  }
1602

    
1603
  /**
1604
   * Clears all custom headers.  Returns void.
1605
  * @return void
1606
   */
1607
  function ClearCustomHeaders() {
1608
    $this->CustomHeader = array();
1609
  }
1610

    
1611
  /////////////////////////////////////////////////
1612
  // CLASS METHODS, MISCELLANEOUS
1613
  /////////////////////////////////////////////////
1614

    
1615
  /**
1616
   * Adds the error message to the error container.
1617
   * Returns void.
1618
  * @access private
1619
  * @return void
1620
   */
1621
  function SetError($msg) {
1622
    $this->error_count++;
1623
    $this->ErrorInfo = $msg;
1624
  }
1625

    
1626
  /**
1627
   * Returns the proper RFC 822 formatted date.
1628
  * @access private
1629
  * @return string
1630
   */
1631
  function RFCDate() {
1632
    $tz = date('Z');
1633
    $tzs = ($tz < 0) ? '-' : '+';
1634
    $tz = abs($tz);
1635
    $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
1636
    $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
1637

    
1638
    return $result;
1639
  }
1640

    
1641
  /**
1642
   * Returns the appropriate server variable.  Should work with both
1643
   * PHP 4.1.0+ as well as older versions.  Returns an empty string
1644
   * if nothing is found.
1645
  * @access private
1646
  * @return mixed
1647
   */
1648
  function ServerVar($varName) {
1649
    global $HTTP_SERVER_VARS;
1650
    global $HTTP_ENV_VARS;
1651

    
1652
    if(!isset($_SERVER)) {
1653
      $_SERVER = $HTTP_SERVER_VARS;
1654
      if(!isset($_SERVER['REMOTE_ADDR'])) {
1655
        $_SERVER = $HTTP_ENV_VARS; // must be Apache
1656
      }
1657
    }
1658

    
1659
    if(isset($_SERVER[$varName])) {
1660
      return $_SERVER[$varName];
1661
    } else {
1662
      return '';
1663
    }
1664
  }
1665

    
1666
  /**
1667
   * Returns the server hostname or 'localhost.localdomain' if unknown.
1668
  * @access private
1669
  * @return string
1670
   */
1671
  function ServerHostname() {
1672
    if ($this->Hostname != '') {
1673
      $result = $this->Hostname;
1674
    } elseif ($this->ServerVar('SERVER_NAME') != '') {
1675
      $result = $this->ServerVar('SERVER_NAME');
1676
    } else {
1677
      $result = 'localhost.localdomain';
1678
    }
1679

    
1680
    return $result;
1681
  }
1682

    
1683
  /**
1684
   * Returns a message in the appropriate language.
1685
  * @access private
1686
  * @return string
1687
   */
1688
  function Lang($key) {
1689
    if(count($this->language) < 1) {
1690
      $this->SetLanguage('en'); // set the default language
1691
    }
1692

    
1693
    if(isset($this->language[$key])) {
1694
      return $this->language[$key];
1695
    } else {
1696
      return 'Language string failed to load: ' . $key;
1697
    }
1698
  }
1699

    
1700
  /**
1701
   * Returns true if an error occurred.
1702
  * @return bool
1703
   */
1704
  function IsError() {
1705
    return ($this->error_count > 0);
1706
  }
1707

    
1708
  /**
1709
   * Changes every end of line from CR or LF to CRLF.
1710
  * @access private
1711
  * @return string
1712
   */
1713
  function FixEOL($str) {
1714
    $str = str_replace("\r\n", "\n", $str);
1715
    $str = str_replace("\r", "\n", $str);
1716
    $str = str_replace("\n", $this->LE, $str);
1717
    return $str;
1718
  }
1719

    
1720
  /**
1721
   * Adds a custom header.
1722
  * @return void
1723
   */
1724
  function AddCustomHeader($custom_header) {
1725
    $this->CustomHeader[] = explode(':', $custom_header, 2);
1726
  }
1727

    
1728
  /**
1729
   * Evaluates the message and returns modifications for inline images and backgrounds
1730
  * @access public
1731
  * @return $message
1732
   */
1733
  function MsgHTML($message,$basedir='') {
1734
    preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
1735
    if(isset($images[2])) {
1736
      foreach($images[2] as $i => $url) {
1737
        // do not change urls for absolute images (thanks to corvuscorax)
1738
        if (!preg_match('/^[A-z][A-z]*:\/\//',$url)) {
1739
          $filename = basename($url);
1740
          $directory = dirname($url);
1741
          ($directory == '.')?$directory='':'';
1742
          $cid = 'cid:' . md5($filename);
1743
          $fileParts = split("\.", $filename);
1744
          $ext = $fileParts[1];
1745
          $mimeType = $this->_mime_types($ext);
1746
          if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }
1747
          if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }
1748
          if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {
1749
            $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
1750
          }
1751
        }
1752
      }
1753
    }
1754
    $this->IsHTML(true);
1755
    $this->Body = $message;
1756
    $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));
1757
    if ( !empty($textMsg) && empty($this->AltBody) ) {
1758
      $this->AltBody = html_entity_decode($textMsg);
1759
    }
1760
    if ( empty($this->AltBody) ) {
1761
      $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n";
1762
    }
1763
  }
1764

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

    
1862
  /**
1863
   * Set (or reset) Class Objects (variables)
1864
   *
1865
   * Usage Example:
1866
   * $page->set('X-Priority', '3');
1867
   *
1868
  * @access public
1869
  * @param string $name Parameter Name
1870
  * @param mixed $value Parameter Value
1871
   * NOTE: will not work with arrays, there are no arrays to set/reset
1872
   */
1873
  function set ( $name, $value = '' ) {
1874
    if ( isset($this->$name) ) {
1875
      $this->$name = $value;
1876
    } else {
1877
      $this->SetError('Cannot set or reset variable ' . $name);
1878
      return false;
1879
    }
1880
  }
1881

    
1882
  /**
1883
   * Read a file from a supplied filename and return it.
1884
   *
1885
  * @access public
1886
  * @param string $filename Parameter File Name
1887
   */
1888
  function getFile($filename) {
1889
    $return = '';
1890
    if ($fp = fopen($filename, 'rb')) {
1891
      while (!feof($fp)) {
1892
        $return .= fread($fp, 1024);
1893
      }
1894
      fclose($fp);
1895
      return $return;
1896
    } else {
1897
      return false;
1898
    }
1899
  }
1900

    
1901
  /**
1902
   * Strips newlines to prevent header injection.
1903
  * @access private
1904
  * @param string $str String
1905
  * @return string
1906
   */
1907
  function SecureHeader($str) {
1908
    $str = trim($str);
1909
    $str = str_replace("\r", "", $str);
1910
    $str = str_replace("\n", "", $str);
1911
    return $str;
1912
  }
1913

    
1914
  /**
1915
   * Set the private key file and password to sign the message.
1916
   *
1917
  * @access public
1918
  * @param string $key_filename Parameter File Name
1919
  * @param string $key_pass Password for private key
1920
   */
1921
  function Sign($cert_filename, $key_filename, $key_pass) {
1922
    $this->sign_cert_file = $cert_filename;
1923
    $this->sign_key_file = $key_filename;
1924
    $this->sign_key_pass = $key_pass;
1925
  }
1926

    
1927
}
1928

    
1929
?>
(4-4/7)