CodeIgniter installed
[living-lab-site.git] / system / libraries / Email.php
1 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2 /**
3  * CodeIgniter
4  *
5  * An open source application development framework for PHP 5.1.6 or newer
6  *
7  * @package             CodeIgniter
8  * @author              ExpressionEngine Dev Team
9  * @copyright   Copyright (c) 2008 - 2011, EllisLab, Inc.
10  * @license             http://codeigniter.com/user_guide/license.html
11  * @link                http://codeigniter.com
12  * @since               Version 1.0
13  * @filesource
14  */
15
16 // ------------------------------------------------------------------------
17
18 /**
19  * CodeIgniter Email Class
20  *
21  * Permits email to be sent using Mail, Sendmail, or SMTP.
22  *
23  * @package             CodeIgniter
24  * @subpackage  Libraries
25  * @category    Libraries
26  * @author              ExpressionEngine Dev Team
27  * @link                http://codeigniter.com/user_guide/libraries/email.html
28  */
29 class CI_Email {
30
31         var     $useragent              = "CodeIgniter";
32         var     $mailpath               = "/usr/sbin/sendmail"; // Sendmail path
33         var     $protocol               = "mail";       // mail/sendmail/smtp
34         var     $smtp_host              = "";           // SMTP Server.  Example: mail.earthlink.net
35         var     $smtp_user              = "";           // SMTP Username
36         var     $smtp_pass              = "";           // SMTP Password
37         var     $smtp_port              = "25";         // SMTP Port
38         var     $smtp_timeout   = 5;            // SMTP Timeout in seconds
39         var     $wordwrap               = TRUE;         // TRUE/FALSE  Turns word-wrap on/off
40         var     $wrapchars              = "76";         // Number of characters to wrap at.
41         var     $mailtype               = "text";       // text/html  Defines email formatting
42         var     $charset                = "utf-8";      // Default char set: iso-8859-1 or us-ascii
43         var     $multipart              = "mixed";      // "mixed" (in the body) or "related" (separate)
44         var $alt_message        = '';           // Alternative message for HTML emails
45         var     $validate               = FALSE;        // TRUE/FALSE.  Enables email validation
46         var     $priority               = "3";          // Default priority (1 - 5)
47         var     $newline                = "\n";         // Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
48         var $crlf                       = "\n";         // The RFC 2045 compliant CRLF for quoted-printable is "\r\n".  Apparently some servers,
49                                                                         // even on the receiving end think they need to muck with CRLFs, so using "\n", while
50                                                                         // distasteful, is the only thing that seems to work for all environments.
51         var $send_multipart     = TRUE;         // TRUE/FALSE - Yahoo does not like multipart alternative, so this is an override.  Set to FALSE for Yahoo.
52         var     $bcc_batch_mode = FALSE;        // TRUE/FALSE  Turns on/off Bcc batch feature
53         var     $bcc_batch_size = 200;          // If bcc_batch_mode = TRUE, sets max number of Bccs in each batch
54         var $_safe_mode         = FALSE;
55         var     $_subject               = "";
56         var     $_body                  = "";
57         var     $_finalbody             = "";
58         var     $_alt_boundary  = "";
59         var     $_atc_boundary  = "";
60         var     $_header_str    = "";
61         var     $_smtp_connect  = "";
62         var     $_encoding              = "8bit";
63         var $_IP                        = FALSE;
64         var     $_smtp_auth             = FALSE;
65         var $_replyto_flag      = FALSE;
66         var     $_debug_msg             = array();
67         var     $_recipients    = array();
68         var     $_cc_array              = array();
69         var     $_bcc_array             = array();
70         var     $_headers               = array();
71         var     $_attach_name   = array();
72         var     $_attach_type   = array();
73         var     $_attach_disp   = array();
74         var     $_protocols             = array('mail', 'sendmail', 'smtp');
75         var     $_base_charsets = array('us-ascii', 'iso-2022-');       // 7-bit charsets (excluding language suffix)
76         var     $_bit_depths    = array('7bit', '8bit');
77         var     $_priorities    = array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
78
79
80         /**
81          * Constructor - Sets Email Preferences
82          *
83          * The constructor can be passed an array of config values
84          */
85         public function __construct($config = array())
86         {
87                 if (count($config) > 0)
88                 {
89                         $this->initialize($config);
90                 }
91                 else
92                 {
93                         $this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
94                         $this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
95                 }
96
97                 log_message('debug', "Email Class Initialized");
98         }
99
100         // --------------------------------------------------------------------
101
102         /**
103          * Initialize preferences
104          *
105          * @access      public
106          * @param       array
107          * @return      void
108          */
109         public function initialize($config = array())
110         {
111                 foreach ($config as $key => $val)
112                 {
113                         if (isset($this->$key))
114                         {
115                                 $method = 'set_'.$key;
116
117                                 if (method_exists($this, $method))
118                                 {
119                                         $this->$method($val);
120                                 }
121                                 else
122                                 {
123                                         $this->$key = $val;
124                                 }
125                         }
126                 }
127                 $this->clear();
128
129                 $this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
130                 $this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
131
132                 return $this;
133         }
134
135         // --------------------------------------------------------------------
136
137         /**
138          * Initialize the Email Data
139          *
140          * @access      public
141          * @return      void
142          */
143         public function clear($clear_attachments = FALSE)
144         {
145                 $this->_subject         = "";
146                 $this->_body            = "";
147                 $this->_finalbody       = "";
148                 $this->_header_str      = "";
149                 $this->_replyto_flag = FALSE;
150                 $this->_recipients      = array();
151                 $this->_cc_array        = array();
152                 $this->_bcc_array       = array();
153                 $this->_headers         = array();
154                 $this->_debug_msg       = array();
155
156                 $this->_set_header('User-Agent', $this->useragent);
157                 $this->_set_header('Date', $this->_set_date());
158
159                 if ($clear_attachments !== FALSE)
160                 {
161                         $this->_attach_name = array();
162                         $this->_attach_type = array();
163                         $this->_attach_disp = array();
164                 }
165
166                 return $this;
167         }
168
169         // --------------------------------------------------------------------
170
171         /**
172          * Set FROM
173          *
174          * @access      public
175          * @param       string
176          * @param       string
177          * @return      void
178          */
179         public function from($from, $name = '')
180         {
181                 if (preg_match( '/\<(.*)\>/', $from, $match))
182                 {
183                         $from = $match['1'];
184                 }
185
186                 if ($this->validate)
187                 {
188                         $this->validate_email($this->_str_to_array($from));
189                 }
190
191                 // prepare the display name
192                 if ($name != '')
193                 {
194                         // only use Q encoding if there are characters that would require it
195                         if ( ! preg_match('/[\200-\377]/', $name))
196                         {
197                                 // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes
198                                 $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"';
199                         }
200                         else
201                         {
202                                 $name = $this->_prep_q_encoding($name, TRUE);
203                         }
204                 }
205
206                 $this->_set_header('From', $name.' <'.$from.'>');
207                 $this->_set_header('Return-Path', '<'.$from.'>');
208
209                 return $this;
210         }
211
212         // --------------------------------------------------------------------
213
214         /**
215          * Set Reply-to
216          *
217          * @access      public
218          * @param       string
219          * @param       string
220          * @return      void
221          */
222         public function reply_to($replyto, $name = '')
223         {
224                 if (preg_match( '/\<(.*)\>/', $replyto, $match))
225                 {
226                         $replyto = $match['1'];
227                 }
228
229                 if ($this->validate)
230                 {
231                         $this->validate_email($this->_str_to_array($replyto));
232                 }
233
234                 if ($name == '')
235                 {
236                         $name = $replyto;
237                 }
238
239                 if (strncmp($name, '"', 1) != 0)
240                 {
241                         $name = '"'.$name.'"';
242                 }
243
244                 $this->_set_header('Reply-To', $name.' <'.$replyto.'>');
245                 $this->_replyto_flag = TRUE;
246
247                 return $this;
248         }
249
250         // --------------------------------------------------------------------
251
252         /**
253          * Set Recipients
254          *
255          * @access      public
256          * @param       string
257          * @return      void
258          */
259         public function to($to)
260         {
261                 $to = $this->_str_to_array($to);
262                 $to = $this->clean_email($to);
263
264                 if ($this->validate)
265                 {
266                         $this->validate_email($to);
267                 }
268
269                 if ($this->_get_protocol() != 'mail')
270                 {
271                         $this->_set_header('To', implode(", ", $to));
272                 }
273
274                 switch ($this->_get_protocol())
275                 {
276                         case 'smtp'             :
277                                 $this->_recipients = $to;
278                         break;
279                         case 'sendmail' :
280                         case 'mail'             :
281                                 $this->_recipients = implode(", ", $to);
282                         break;
283                 }
284
285                 return $this;
286         }
287
288         // --------------------------------------------------------------------
289
290         /**
291          * Set CC
292          *
293          * @access      public
294          * @param       string
295          * @return      void
296          */
297         public function cc($cc)
298         {
299                 $cc = $this->_str_to_array($cc);
300                 $cc = $this->clean_email($cc);
301
302                 if ($this->validate)
303                 {
304                         $this->validate_email($cc);
305                 }
306
307                 $this->_set_header('Cc', implode(", ", $cc));
308
309                 if ($this->_get_protocol() == "smtp")
310                 {
311                         $this->_cc_array = $cc;
312                 }
313
314                 return $this;
315         }
316
317         // --------------------------------------------------------------------
318
319         /**
320          * Set BCC
321          *
322          * @access      public
323          * @param       string
324          * @param       string
325          * @return      void
326          */
327         public function bcc($bcc, $limit = '')
328         {
329                 if ($limit != '' && is_numeric($limit))
330                 {
331                         $this->bcc_batch_mode = TRUE;
332                         $this->bcc_batch_size = $limit;
333                 }
334
335                 $bcc = $this->_str_to_array($bcc);
336                 $bcc = $this->clean_email($bcc);
337
338                 if ($this->validate)
339                 {
340                         $this->validate_email($bcc);
341                 }
342
343                 if (($this->_get_protocol() == "smtp") OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
344                 {
345                         $this->_bcc_array = $bcc;
346                 }
347                 else
348                 {
349                         $this->_set_header('Bcc', implode(", ", $bcc));
350                 }
351
352                 return $this;
353         }
354
355         // --------------------------------------------------------------------
356
357         /**
358          * Set Email Subject
359          *
360          * @access      public
361          * @param       string
362          * @return      void
363          */
364         public function subject($subject)
365         {
366                 $subject = $this->_prep_q_encoding($subject);
367                 $this->_set_header('Subject', $subject);
368                 return $this;
369         }
370
371         // --------------------------------------------------------------------
372
373         /**
374          * Set Body
375          *
376          * @access      public
377          * @param       string
378          * @return      void
379          */
380         public function message($body)
381         {
382                 $this->_body = stripslashes(rtrim(str_replace("\r", "", $body)));
383                 return $this;
384         }
385
386         // --------------------------------------------------------------------
387
388         /**
389          * Assign file attachments
390          *
391          * @access      public
392          * @param       string
393          * @return      void
394          */
395         public function attach($filename, $disposition = 'attachment')
396         {
397                 $this->_attach_name[] = $filename;
398                 $this->_attach_type[] = $this->_mime_types(next(explode('.', basename($filename))));
399                 $this->_attach_disp[] = $disposition; // Can also be 'inline'  Not sure if it matters
400                 return $this;
401         }
402
403         // --------------------------------------------------------------------
404
405         /**
406          * Add a Header Item
407          *
408          * @access      private
409          * @param       string
410          * @param       string
411          * @return      void
412          */
413         private function _set_header($header, $value)
414         {
415                 $this->_headers[$header] = $value;
416         }
417
418         // --------------------------------------------------------------------
419
420         /**
421          * Convert a String to an Array
422          *
423          * @access      private
424          * @param       string
425          * @return      array
426          */
427         private function _str_to_array($email)
428         {
429                 if ( ! is_array($email))
430                 {
431                         if (strpos($email, ',') !== FALSE)
432                         {
433                                 $email = preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY);
434                         }
435                         else
436                         {
437                                 $email = trim($email);
438                                 settype($email, "array");
439                         }
440                 }
441                 return $email;
442         }
443
444         // --------------------------------------------------------------------
445
446         /**
447          * Set Multipart Value
448          *
449          * @access      public
450          * @param       string
451          * @return      void
452          */
453         public function set_alt_message($str = '')
454         {
455                 $this->alt_message = ($str == '') ? '' : $str;
456                 return $this;
457         }
458
459         // --------------------------------------------------------------------
460
461         /**
462          * Set Mailtype
463          *
464          * @access      public
465          * @param       string
466          * @return      void
467          */
468         public function set_mailtype($type = 'text')
469         {
470                 $this->mailtype = ($type == 'html') ? 'html' : 'text';
471                 return $this;
472         }
473
474         // --------------------------------------------------------------------
475
476         /**
477          * Set Wordwrap
478          *
479          * @access      public
480          * @param       string
481          * @return      void
482          */
483         public function set_wordwrap($wordwrap = TRUE)
484         {
485                 $this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE;
486                 return $this;
487         }
488
489         // --------------------------------------------------------------------
490
491         /**
492          * Set Protocol
493          *
494          * @access      public
495          * @param       string
496          * @return      void
497          */
498         public function set_protocol($protocol = 'mail')
499         {
500                 $this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol);
501                 return $this;
502         }
503
504         // --------------------------------------------------------------------
505
506         /**
507          * Set Priority
508          *
509          * @access      public
510          * @param       integer
511          * @return      void
512          */
513         public function set_priority($n = 3)
514         {
515                 if ( ! is_numeric($n))
516                 {
517                         $this->priority = 3;
518                         return;
519                 }
520
521                 if ($n < 1 OR $n > 5)
522                 {
523                         $this->priority = 3;
524                         return;
525                 }
526
527                 $this->priority = $n;
528                 return $this;
529         }
530
531         // --------------------------------------------------------------------
532
533         /**
534          * Set Newline Character
535          *
536          * @access      public
537          * @param       string
538          * @return      void
539          */
540         public function set_newline($newline = "\n")
541         {
542                 if ($newline != "\n" AND $newline != "\r\n" AND $newline != "\r")
543                 {
544                         $this->newline  = "\n";
545                         return;
546                 }
547
548                 $this->newline  = $newline;
549
550                 return $this;
551         }
552
553         // --------------------------------------------------------------------
554
555         /**
556          * Set CRLF
557          *
558          * @access      public
559          * @param       string
560          * @return      void
561          */
562         public function set_crlf($crlf = "\n")
563         {
564                 if ($crlf != "\n" AND $crlf != "\r\n" AND $crlf != "\r")
565                 {
566                         $this->crlf     = "\n";
567                         return;
568                 }
569
570                 $this->crlf     = $crlf;
571
572                 return $this;
573         }
574
575         // --------------------------------------------------------------------
576
577         /**
578          * Set Message Boundary
579          *
580          * @access      private
581          * @return      void
582          */
583         private function _set_boundaries()
584         {
585                 $this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
586                 $this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
587         }
588
589         // --------------------------------------------------------------------
590
591         /**
592          * Get the Message ID
593          *
594          * @access      private
595          * @return      string
596          */
597         private function _get_message_id()
598         {
599                 $from = $this->_headers['Return-Path'];
600                 $from = str_replace(">", "", $from);
601                 $from = str_replace("<", "", $from);
602
603                 return  "<".uniqid('').strstr($from, '@').">";
604         }
605
606         // --------------------------------------------------------------------
607
608         /**
609          * Get Mail Protocol
610          *
611          * @access      private
612          * @param       bool
613          * @return      string
614          */
615         private function _get_protocol($return = TRUE)
616         {
617                 $this->protocol = strtolower($this->protocol);
618                 $this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;
619
620                 if ($return == TRUE)
621                 {
622                         return $this->protocol;
623                 }
624         }
625
626         // --------------------------------------------------------------------
627
628         /**
629          * Get Mail Encoding
630          *
631          * @access      private
632          * @param       bool
633          * @return      string
634          */
635         private function _get_encoding($return = TRUE)
636         {
637                 $this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;
638
639                 foreach ($this->_base_charsets as $charset)
640                 {
641                         if (strncmp($charset, $this->charset, strlen($charset)) == 0)
642                         {
643                                 $this->_encoding = '7bit';
644                         }
645                 }
646
647                 if ($return == TRUE)
648                 {
649                         return $this->_encoding;
650                 }
651         }
652
653         // --------------------------------------------------------------------
654
655         /**
656          * Get content type (text/html/attachment)
657          *
658          * @access      private
659          * @return      string
660          */
661         private function _get_content_type()
662         {
663                 if      ($this->mailtype == 'html' &&  count($this->_attach_name) == 0)
664                 {
665                         return 'html';
666                 }
667                 elseif  ($this->mailtype == 'html' &&  count($this->_attach_name)  > 0)
668                 {
669                         return 'html-attach';
670                 }
671                 elseif  ($this->mailtype == 'text' &&  count($this->_attach_name)  > 0)
672                 {
673                         return 'plain-attach';
674                 }
675                 else
676                 {
677                         return 'plain';
678                 }
679         }
680
681         // --------------------------------------------------------------------
682
683         /**
684          * Set RFC 822 Date
685          *
686          * @access      private
687          * @return      string
688          */
689         private function _set_date()
690         {
691                 $timezone = date("Z");
692                 $operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+';
693                 $timezone = abs($timezone);
694                 $timezone = floor($timezone/3600) * 100 + ($timezone % 3600 ) / 60;
695
696                 return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);
697         }
698
699         // --------------------------------------------------------------------
700
701         /**
702          * Mime message
703          *
704          * @access      private
705          * @return      string
706          */
707         private function _get_mime_message()
708         {
709                 return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
710         }
711
712         // --------------------------------------------------------------------
713
714         /**
715          * Validate Email Address
716          *
717          * @access      public
718          * @param       string
719          * @return      bool
720          */
721         public function validate_email($email)
722         {
723                 if ( ! is_array($email))
724                 {
725                         $this->_set_error_message('email_must_be_array');
726                         return FALSE;
727                 }
728
729                 foreach ($email as $val)
730                 {
731                         if ( ! $this->valid_email($val))
732                         {
733                                 $this->_set_error_message('email_invalid_address', $val);
734                                 return FALSE;
735                         }
736                 }
737
738                 return TRUE;
739         }
740
741         // --------------------------------------------------------------------
742
743         /**
744          * Email Validation
745          *
746          * @access      public
747          * @param       string
748          * @return      bool
749          */
750         public function valid_email($address)
751         {
752                 return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE;
753         }
754
755         // --------------------------------------------------------------------
756
757         /**
758          * Clean Extended Email Address: Joe Smith <joe@smith.com>
759          *
760          * @access      public
761          * @param       string
762          * @return      string
763          */
764         public function clean_email($email)
765         {
766                 if ( ! is_array($email))
767                 {
768                         if (preg_match('/\<(.*)\>/', $email, $match))
769                         {
770                                 return $match['1'];
771                         }
772                         else
773                         {
774                                 return $email;
775                         }
776                 }
777
778                 $clean_email = array();
779
780                 foreach ($email as $addy)
781                 {
782                         if (preg_match( '/\<(.*)\>/', $addy, $match))
783                         {
784                                 $clean_email[] = $match['1'];
785                         }
786                         else
787                         {
788                                 $clean_email[] = $addy;
789                         }
790                 }
791
792                 return $clean_email;
793         }
794
795         // --------------------------------------------------------------------
796
797         /**
798          * Build alternative plain text message
799          *
800          * This public function provides the raw message for use
801          * in plain-text headers of HTML-formatted emails.
802          * If the user hasn't specified his own alternative message
803          * it creates one by stripping the HTML
804          *
805          * @access      private
806          * @return      string
807          */
808         private function _get_alt_message()
809         {
810                 if ($this->alt_message != "")
811                 {
812                         return $this->word_wrap($this->alt_message, '76');
813                 }
814
815                 if (preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match))
816                 {
817                         $body = $match['1'];
818                 }
819                 else
820                 {
821                         $body = $this->_body;
822                 }
823
824                 $body = trim(strip_tags($body));
825                 $body = preg_replace( '#<!--(.*)--\>#', "", $body);
826                 $body = str_replace("\t", "", $body);
827
828                 for ($i = 20; $i >= 3; $i--)
829                 {
830                         $n = "";
831
832                         for ($x = 1; $x <= $i; $x ++)
833                         {
834                                 $n .= "\n";
835                         }
836
837                         $body = str_replace($n, "\n\n", $body);
838                 }
839
840                 return $this->word_wrap($body, '76');
841         }
842
843         // --------------------------------------------------------------------
844
845         /**
846          * Word Wrap
847          *
848          * @access      public
849          * @param       string
850          * @param       integer
851          * @return      string
852          */
853         public function word_wrap($str, $charlim = '')
854         {
855                 // Se the character limit
856                 if ($charlim == '')
857                 {
858                         $charlim = ($this->wrapchars == "") ? "76" : $this->wrapchars;
859                 }
860
861                 // Reduce multiple spaces
862                 $str = preg_replace("| +|", " ", $str);
863
864                 // Standardize newlines
865                 if (strpos($str, "\r") !== FALSE)
866                 {
867                         $str = str_replace(array("\r\n", "\r"), "\n", $str);
868                 }
869
870                 // If the current word is surrounded by {unwrap} tags we'll
871                 // strip the entire chunk and replace it with a marker.
872                 $unwrap = array();
873                 if (preg_match_all("|(\{unwrap\}.+?\{/unwrap\})|s", $str, $matches))
874                 {
875                         for ($i = 0; $i < count($matches['0']); $i++)
876                         {
877                                 $unwrap[] = $matches['1'][$i];
878                                 $str = str_replace($matches['1'][$i], "{{unwrapped".$i."}}", $str);
879                         }
880                 }
881
882                 // Use PHP's native public function to do the initial wordwrap.
883                 // We set the cut flag to FALSE so that any individual words that are
884                 // too long get left alone.  In the next step we'll deal with them.
885                 $str = wordwrap($str, $charlim, "\n", FALSE);
886
887                 // Split the string into individual lines of text and cycle through them
888                 $output = "";
889                 foreach (explode("\n", $str) as $line)
890                 {
891                         // Is the line within the allowed character count?
892                         // If so we'll join it to the output and continue
893                         if (strlen($line) <= $charlim)
894                         {
895                                 $output .= $line.$this->newline;
896                                 continue;
897                         }
898
899                         $temp = '';
900                         while ((strlen($line)) > $charlim)
901                         {
902                                 // If the over-length word is a URL we won't wrap it
903                                 if (preg_match("!\[url.+\]|://|wwww.!", $line))
904                                 {
905                                         break;
906                                 }
907
908                                 // Trim the word down
909                                 $temp .= substr($line, 0, $charlim-1);
910                                 $line = substr($line, $charlim-1);
911                         }
912
913                         // If $temp contains data it means we had to split up an over-length
914                         // word into smaller chunks so we'll add it back to our current line
915                         if ($temp != '')
916                         {
917                                 $output .= $temp.$this->newline.$line;
918                         }
919                         else
920                         {
921                                 $output .= $line;
922                         }
923
924                         $output .= $this->newline;
925                 }
926
927                 // Put our markers back
928                 if (count($unwrap) > 0)
929                 {
930                         foreach ($unwrap as $key => $val)
931                         {
932                                 $output = str_replace("{{unwrapped".$key."}}", $val, $output);
933                         }
934                 }
935
936                 return $output;
937         }
938
939         // --------------------------------------------------------------------
940
941         /**
942          * Build final headers
943          *
944          * @access      private
945          * @param       string
946          * @return      string
947          */
948         private function _build_headers()
949         {
950                 $this->_set_header('X-Sender', $this->clean_email($this->_headers['From']));
951                 $this->_set_header('X-Mailer', $this->useragent);
952                 $this->_set_header('X-Priority', $this->_priorities[$this->priority - 1]);
953                 $this->_set_header('Message-ID', $this->_get_message_id());
954                 $this->_set_header('Mime-Version', '1.0');
955         }
956
957         // --------------------------------------------------------------------
958
959         /**
960          * Write Headers as a string
961          *
962          * @access      private
963          * @return      void
964          */
965         private function _write_headers()
966         {
967                 if ($this->protocol == 'mail')
968                 {
969                         $this->_subject = $this->_headers['Subject'];
970                         unset($this->_headers['Subject']);
971                 }
972
973                 reset($this->_headers);
974                 $this->_header_str = "";
975
976                 foreach ($this->_headers as $key => $val)
977                 {
978                         $val = trim($val);
979
980                         if ($val != "")
981                         {
982                                 $this->_header_str .= $key.": ".$val.$this->newline;
983                         }
984                 }
985
986                 if ($this->_get_protocol() == 'mail')
987                 {
988                         $this->_header_str = rtrim($this->_header_str);
989                 }
990         }
991
992         // --------------------------------------------------------------------
993
994         /**
995          * Build Final Body and attachments
996          *
997          * @access      private
998          * @return      void
999          */
1000         private function _build_message()
1001         {
1002                 if ($this->wordwrap === TRUE  AND  $this->mailtype != 'html')
1003                 {
1004                         $this->_body = $this->word_wrap($this->_body);
1005                 }
1006
1007                 $this->_set_boundaries();
1008                 $this->_write_headers();
1009
1010                 $hdr = ($this->_get_protocol() == 'mail') ? $this->newline : '';
1011                 $body = '';
1012
1013                 switch ($this->_get_content_type())
1014                 {
1015                         case 'plain' :
1016
1017                                 $hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1018                                 $hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding();
1019
1020                                 if ($this->_get_protocol() == 'mail')
1021                                 {
1022                                         $this->_header_str .= $hdr;
1023                                         $this->_finalbody = $this->_body;
1024                                 }
1025                                 else
1026                                 {
1027                                         $this->_finalbody = $hdr . $this->newline . $this->newline . $this->_body;
1028                                 }
1029
1030                                 return;
1031
1032                         break;
1033                         case 'html' :
1034
1035                                 if ($this->send_multipart === FALSE)
1036                                 {
1037                                         $hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1038                                         $hdr .= "Content-Transfer-Encoding: quoted-printable";
1039                                 }
1040                                 else
1041                                 {
1042                                         $hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline . $this->newline;
1043
1044                                         $body .= $this->_get_mime_message() . $this->newline . $this->newline;
1045                                         $body .= "--" . $this->_alt_boundary . $this->newline;
1046
1047                                         $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1048                                         $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
1049                                         $body .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
1050
1051                                         $body .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1052                                         $body .= "Content-Transfer-Encoding: quoted-printable" . $this->newline . $this->newline;
1053                                 }
1054
1055                                 $this->_finalbody = $body . $this->_prep_quoted_printable($this->_body) . $this->newline . $this->newline;
1056
1057
1058                                 if ($this->_get_protocol() == 'mail')
1059                                 {
1060                                         $this->_header_str .= $hdr;
1061                                 }
1062                                 else
1063                                 {
1064                                         $this->_finalbody = $hdr . $this->_finalbody;
1065                                 }
1066
1067
1068                                 if ($this->send_multipart !== FALSE)
1069                                 {
1070                                         $this->_finalbody .= "--" . $this->_alt_boundary . "--";
1071                                 }
1072
1073                                 return;
1074
1075                         break;
1076                         case 'plain-attach' :
1077
1078                                 $hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline . $this->newline;
1079
1080                                 if ($this->_get_protocol() == 'mail')
1081                                 {
1082                                         $this->_header_str .= $hdr;
1083                                 }
1084
1085                                 $body .= $this->_get_mime_message() . $this->newline . $this->newline;
1086                                 $body .= "--" . $this->_atc_boundary . $this->newline;
1087
1088                                 $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1089                                 $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
1090
1091                                 $body .= $this->_body . $this->newline . $this->newline;
1092
1093                         break;
1094                         case 'html-attach' :
1095
1096                                 $hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline . $this->newline;
1097
1098                                 if ($this->_get_protocol() == 'mail')
1099                                 {
1100                                         $this->_header_str .= $hdr;
1101                                 }
1102
1103                                 $body .= $this->_get_mime_message() . $this->newline . $this->newline;
1104                                 $body .= "--" . $this->_atc_boundary . $this->newline;
1105
1106                                 $body .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline .$this->newline;
1107                                 $body .= "--" . $this->_alt_boundary . $this->newline;
1108
1109                                 $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1110                                 $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
1111                                 $body .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
1112
1113                                 $body .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1114                                 $body .= "Content-Transfer-Encoding: quoted-printable" . $this->newline . $this->newline;
1115
1116                                 $body .= $this->_prep_quoted_printable($this->_body) . $this->newline . $this->newline;
1117                                 $body .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;
1118
1119                         break;
1120                 }
1121
1122                 $attachment = array();
1123
1124                 $z = 0;
1125
1126                 for ($i=0; $i < count($this->_attach_name); $i++)
1127                 {
1128                         $filename = $this->_attach_name[$i];
1129                         $basename = basename($filename);
1130                         $ctype = $this->_attach_type[$i];
1131
1132                         if ( ! file_exists($filename))
1133                         {
1134                                 $this->_set_error_message('email_attachment_missing', $filename);
1135                                 return FALSE;
1136                         }
1137
1138                         $h  = "--".$this->_atc_boundary.$this->newline;
1139                         $h .= "Content-type: ".$ctype."; ";
1140                         $h .= "name=\"".$basename."\"".$this->newline;
1141                         $h .= "Content-Disposition: ".$this->_attach_disp[$i].";".$this->newline;
1142                         $h .= "Content-Transfer-Encoding: base64".$this->newline;
1143
1144                         $attachment[$z++] = $h;
1145                         $file = filesize($filename) +1;
1146
1147                         if ( ! $fp = fopen($filename, FOPEN_READ))
1148                         {
1149                                 $this->_set_error_message('email_attachment_unreadable', $filename);
1150                                 return FALSE;
1151                         }
1152
1153                         $attachment[$z++] = chunk_split(base64_encode(fread($fp, $file)));
1154                         fclose($fp);
1155                 }
1156
1157                 $body .= implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";
1158
1159
1160                 if ($this->_get_protocol() == 'mail')
1161                 {
1162                         $this->_finalbody = $body;
1163                 }
1164                 else
1165                 {
1166                         $this->_finalbody = $hdr . $body;
1167                 }
1168
1169                 return;
1170         }
1171
1172         // --------------------------------------------------------------------
1173
1174         /**
1175          * Prep Quoted Printable
1176          *
1177          * Prepares string for Quoted-Printable Content-Transfer-Encoding
1178          * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
1179          *
1180          * @access      private
1181          * @param       string
1182          * @param       integer
1183          * @return      string
1184          */
1185         private function _prep_quoted_printable($str, $charlim = '')
1186         {
1187                 // Set the character limit
1188                 // Don't allow over 76, as that will make servers and MUAs barf
1189                 // all over quoted-printable data
1190                 if ($charlim == '' OR $charlim > '76')
1191                 {
1192                         $charlim = '76';
1193                 }
1194
1195                 // Reduce multiple spaces
1196                 $str = preg_replace("| +|", " ", $str);
1197
1198                 // kill nulls
1199                 $str = preg_replace('/\x00+/', '', $str);
1200
1201                 // Standardize newlines
1202                 if (strpos($str, "\r") !== FALSE)
1203                 {
1204                         $str = str_replace(array("\r\n", "\r"), "\n", $str);
1205                 }
1206
1207                 // We are intentionally wrapping so mail servers will encode characters
1208                 // properly and MUAs will behave, so {unwrap} must go!
1209                 $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
1210
1211                 // Break into an array of lines
1212                 $lines = explode("\n", $str);
1213
1214                 $escape = '=';
1215                 $output = '';
1216
1217                 foreach ($lines as $line)
1218                 {
1219                         $length = strlen($line);
1220                         $temp = '';
1221
1222                         // Loop through each character in the line to add soft-wrap
1223                         // characters at the end of a line " =\r\n" and add the newly
1224                         // processed line(s) to the output (see comment on $crlf class property)
1225                         for ($i = 0; $i < $length; $i++)
1226                         {
1227                                 // Grab the next character
1228                                 $char = substr($line, $i, 1);
1229                                 $ascii = ord($char);
1230
1231                                 // Convert spaces and tabs but only if it's the end of the line
1232                                 if ($i == ($length - 1))
1233                                 {
1234                                         $char = ($ascii == '32' OR $ascii == '9') ? $escape.sprintf('%02s', dechex($ascii)) : $char;
1235                                 }
1236
1237                                 // encode = signs
1238                                 if ($ascii == '61')
1239                                 {
1240                                         $char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));  // =3D
1241                                 }
1242
1243                                 // If we're at the character limit, add the line to the output,
1244                                 // reset our temp variable, and keep on chuggin'
1245                                 if ((strlen($temp) + strlen($char)) >= $charlim)
1246                                 {
1247                                         $output .= $temp.$escape.$this->crlf;
1248                                         $temp = '';
1249                                 }
1250
1251                                 // Add the character to our temporary line
1252                                 $temp .= $char;
1253                         }
1254
1255                         // Add our completed line to the output
1256                         $output .= $temp.$this->crlf;
1257                 }
1258
1259                 // get rid of extra CRLF tacked onto the end
1260                 $output = substr($output, 0, strlen($this->crlf) * -1);
1261
1262                 return $output;
1263         }
1264
1265         // --------------------------------------------------------------------
1266
1267         /**
1268          * Prep Q Encoding
1269          *
1270          * Performs "Q Encoding" on a string for use in email headers.  It's related
1271          * but not identical to quoted-printable, so it has its own method
1272          *
1273          * @access      public
1274          * @param       str
1275          * @param       bool    // set to TRUE for processing From: headers
1276          * @return      str
1277          */
1278         private function _prep_q_encoding($str, $from = FALSE)
1279         {
1280                 $str = str_replace(array("\r", "\n"), array('', ''), $str);
1281
1282                 // Line length must not exceed 76 characters, so we adjust for
1283                 // a space, 7 extra characters =??Q??=, and the charset that we will add to each line
1284                 $limit = 75 - 7 - strlen($this->charset);
1285
1286                 // these special characters must be converted too
1287                 $convert = array('_', '=', '?');
1288
1289                 if ($from === TRUE)
1290                 {
1291                         $convert[] = ',';
1292                         $convert[] = ';';
1293                 }
1294
1295                 $output = '';
1296                 $temp = '';
1297
1298                 for ($i = 0, $length = strlen($str); $i < $length; $i++)
1299                 {
1300                         // Grab the next character
1301                         $char = substr($str, $i, 1);
1302                         $ascii = ord($char);
1303
1304                         // convert ALL non-printable ASCII characters and our specials
1305                         if ($ascii < 32 OR $ascii > 126 OR in_array($char, $convert))
1306                         {
1307                                 $char = '='.dechex($ascii);
1308                         }
1309
1310                         // handle regular spaces a bit more compactly than =20
1311                         if ($ascii == 32)
1312                         {
1313                                 $char = '_';
1314                         }
1315
1316                         // If we're at the character limit, add the line to the output,
1317                         // reset our temp variable, and keep on chuggin'
1318                         if ((strlen($temp) + strlen($char)) >= $limit)
1319                         {
1320                                 $output .= $temp.$this->crlf;
1321                                 $temp = '';
1322                         }
1323
1324                         // Add the character to our temporary line
1325                         $temp .= $char;
1326                 }
1327
1328                 $str = $output.$temp;
1329
1330                 // wrap each line with the shebang, charset, and transfer encoding
1331                 // the preceding space on successive lines is required for header "folding"
1332                 $str = trim(preg_replace('/^(.*)$/m', ' =?'.$this->charset.'?Q?$1?=', $str));
1333
1334                 return $str;
1335         }
1336
1337         // --------------------------------------------------------------------
1338
1339         /**
1340          * Send Email
1341          *
1342          * @access      public
1343          * @return      bool
1344          */
1345         public function send()
1346         {
1347                 if ($this->_replyto_flag == FALSE)
1348                 {
1349                         $this->reply_to($this->_headers['From']);
1350                 }
1351
1352                 if (( ! isset($this->_recipients) AND ! isset($this->_headers['To']))  AND
1353                         ( ! isset($this->_bcc_array) AND ! isset($this->_headers['Bcc'])) AND
1354                         ( ! isset($this->_headers['Cc'])))
1355                 {
1356                         $this->_set_error_message('email_no_recipients');
1357                         return FALSE;
1358                 }
1359
1360                 $this->_build_headers();
1361
1362                 if ($this->bcc_batch_mode  AND  count($this->_bcc_array) > 0)
1363                 {
1364                         if (count($this->_bcc_array) > $this->bcc_batch_size)
1365                                 return $this->batch_bcc_send();
1366                 }
1367
1368                 $this->_build_message();
1369
1370                 if ( ! $this->_spool_email())
1371                 {
1372                         return FALSE;
1373                 }
1374                 else
1375                 {
1376                         return TRUE;
1377                 }
1378         }
1379
1380         // --------------------------------------------------------------------
1381
1382         /**
1383          * Batch Bcc Send.  Sends groups of BCCs in batches
1384          *
1385          * @access      public
1386          * @return      bool
1387          */
1388         public function batch_bcc_send()
1389         {
1390                 $float = $this->bcc_batch_size -1;
1391
1392                 $set = "";
1393
1394                 $chunk = array();
1395
1396                 for ($i = 0; $i < count($this->_bcc_array); $i++)
1397                 {
1398                         if (isset($this->_bcc_array[$i]))
1399                         {
1400                                 $set .= ", ".$this->_bcc_array[$i];
1401                         }
1402
1403                         if ($i == $float)
1404                         {
1405                                 $chunk[] = substr($set, 1);
1406                                 $float = $float + $this->bcc_batch_size;
1407                                 $set = "";
1408                         }
1409
1410                         if ($i == count($this->_bcc_array)-1)
1411                         {
1412                                 $chunk[] = substr($set, 1);
1413                         }
1414                 }
1415
1416                 for ($i = 0; $i < count($chunk); $i++)
1417                 {
1418                         unset($this->_headers['Bcc']);
1419                         unset($bcc);
1420
1421                         $bcc = $this->_str_to_array($chunk[$i]);
1422                         $bcc = $this->clean_email($bcc);
1423
1424                         if ($this->protocol != 'smtp')
1425                         {
1426                                 $this->_set_header('Bcc', implode(", ", $bcc));
1427                         }
1428                         else
1429                         {
1430                                 $this->_bcc_array = $bcc;
1431                         }
1432
1433                         $this->_build_message();
1434                         $this->_spool_email();
1435                 }
1436         }
1437
1438         // --------------------------------------------------------------------
1439
1440         /**
1441          * Unwrap special elements
1442          *
1443          * @access      private
1444          * @return      void
1445          */
1446         private function _unwrap_specials()
1447         {
1448                 $this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_remove_nl_callback'), $this->_finalbody);
1449         }
1450
1451         // --------------------------------------------------------------------
1452
1453         /**
1454          * Strip line-breaks via callback
1455          *
1456          * @access      private
1457          * @return      string
1458          */
1459         private function _remove_nl_callback($matches)
1460         {
1461                 if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE)
1462                 {
1463                         $matches[1] = str_replace(array("\r\n", "\r", "\n"), '', $matches[1]);
1464                 }
1465
1466                 return $matches[1];
1467         }
1468
1469         // --------------------------------------------------------------------
1470
1471         /**
1472          * Spool mail to the mail server
1473          *
1474          * @access      private
1475          * @return      bool
1476          */
1477         private function _spool_email()
1478         {
1479                 $this->_unwrap_specials();
1480
1481                 switch ($this->_get_protocol())
1482                 {
1483                         case 'mail'     :
1484
1485                                         if ( ! $this->_send_with_mail())
1486                                         {
1487                                                 $this->_set_error_message('email_send_failure_phpmail');
1488                                                 return FALSE;
1489                                         }
1490                         break;
1491                         case 'sendmail' :
1492
1493                                         if ( ! $this->_send_with_sendmail())
1494                                         {
1495                                                 $this->_set_error_message('email_send_failure_sendmail');
1496                                                 return FALSE;
1497                                         }
1498                         break;
1499                         case 'smtp'     :
1500
1501                                         if ( ! $this->_send_with_smtp())
1502                                         {
1503                                                 $this->_set_error_message('email_send_failure_smtp');
1504                                                 return FALSE;
1505                                         }
1506                         break;
1507
1508                 }
1509
1510                 $this->_set_error_message('email_sent', $this->_get_protocol());
1511                 return TRUE;
1512         }
1513
1514         // --------------------------------------------------------------------
1515
1516         /**
1517          * Send using mail()
1518          *
1519          * @access      private
1520          * @return      bool
1521          */
1522         private function _send_with_mail()
1523         {
1524                 if ($this->_safe_mode == TRUE)
1525                 {
1526                         if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str))
1527                         {
1528                                 return FALSE;
1529                         }
1530                         else
1531                         {
1532                                 return TRUE;
1533                         }
1534                 }
1535                 else
1536                 {
1537                         // most documentation of sendmail using the "-f" flag lacks a space after it, however
1538                         // we've encountered servers that seem to require it to be in place.
1539
1540                         if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$this->clean_email($this->_headers['From'])))
1541                         {
1542                                 return FALSE;
1543                         }
1544                         else
1545                         {
1546                                 return TRUE;
1547                         }
1548                 }
1549         }
1550
1551         // --------------------------------------------------------------------
1552
1553         /**
1554          * Send using Sendmail
1555          *
1556          * @access      private
1557          * @return      bool
1558          */
1559         private function _send_with_sendmail()
1560         {
1561                 $fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w');
1562
1563                 if ($fp === FALSE OR $fp === NULL)
1564                 {
1565                         // server probably has popen disabled, so nothing we can do to get a verbose error.
1566                         return FALSE;
1567                 }
1568
1569                 fputs($fp, $this->_header_str);
1570                 fputs($fp, $this->_finalbody);
1571
1572                 $status = pclose($fp);
1573
1574                 if (version_compare(PHP_VERSION, '4.2.3') == -1)
1575                 {
1576                         $status = $status >> 8 & 0xFF;
1577                 }
1578
1579                 if ($status != 0)
1580                 {
1581                         $this->_set_error_message('email_exit_status', $status);
1582                         $this->_set_error_message('email_no_socket');
1583                         return FALSE;
1584                 }
1585
1586                 return TRUE;
1587         }
1588
1589         // --------------------------------------------------------------------
1590
1591         /**
1592          * Send using SMTP
1593          *
1594          * @access      private
1595          * @return      bool
1596          */
1597         private function _send_with_smtp()
1598         {
1599                 if ($this->smtp_host == '')
1600                 {
1601                         $this->_set_error_message('email_no_hostname');
1602                         return FALSE;
1603                 }
1604
1605                 $this->_smtp_connect();
1606                 $this->_smtp_authenticate();
1607
1608                 $this->_send_command('from', $this->clean_email($this->_headers['From']));
1609
1610                 foreach ($this->_recipients as $val)
1611                 {
1612                         $this->_send_command('to', $val);
1613                 }
1614
1615                 if (count($this->_cc_array) > 0)
1616                 {
1617                         foreach ($this->_cc_array as $val)
1618                         {
1619                                 if ($val != "")
1620                                 {
1621                                         $this->_send_command('to', $val);
1622                                 }
1623                         }
1624                 }
1625
1626                 if (count($this->_bcc_array) > 0)
1627                 {
1628                         foreach ($this->_bcc_array as $val)
1629                         {
1630                                 if ($val != "")
1631                                 {
1632                                         $this->_send_command('to', $val);
1633                                 }
1634                         }
1635                 }
1636
1637                 $this->_send_command('data');
1638
1639                 // perform dot transformation on any lines that begin with a dot
1640                 $this->_send_data($this->_header_str . preg_replace('/^\./m', '..$1', $this->_finalbody));
1641
1642                 $this->_send_data('.');
1643
1644                 $reply = $this->_get_smtp_data();
1645
1646                 $this->_set_error_message($reply);
1647
1648                 if (strncmp($reply, '250', 3) != 0)
1649                 {
1650                         $this->_set_error_message('email_smtp_error', $reply);
1651                         return FALSE;
1652                 }
1653
1654                 $this->_send_command('quit');
1655                 return TRUE;
1656         }
1657
1658         // --------------------------------------------------------------------
1659
1660         /**
1661          * SMTP Connect
1662          *
1663          * @access      private
1664          * @param       string
1665          * @return      string
1666          */
1667         private function _smtp_connect()
1668         {
1669                 $this->_smtp_connect = fsockopen($this->smtp_host,
1670                                                                                 $this->smtp_port,
1671                                                                                 $errno,
1672                                                                                 $errstr,
1673                                                                                 $this->smtp_timeout);
1674
1675                 if ( ! is_resource($this->_smtp_connect))
1676                 {
1677                         $this->_set_error_message('email_smtp_error', $errno." ".$errstr);
1678                         return FALSE;
1679                 }
1680
1681                 $this->_set_error_message($this->_get_smtp_data());
1682                 return $this->_send_command('hello');
1683         }
1684
1685         // --------------------------------------------------------------------
1686
1687         /**
1688          * Send SMTP command
1689          *
1690          * @access      private
1691          * @param       string
1692          * @param       string
1693          * @return      string
1694          */
1695         private function _send_command($cmd, $data = '')
1696         {
1697                 switch ($cmd)
1698                 {
1699                         case 'hello' :
1700
1701                                         if ($this->_smtp_auth OR $this->_get_encoding() == '8bit')
1702                                                 $this->_send_data('EHLO '.$this->_get_hostname());
1703                                         else
1704                                                 $this->_send_data('HELO '.$this->_get_hostname());
1705
1706                                                 $resp = 250;
1707                         break;
1708                         case 'from' :
1709
1710                                                 $this->_send_data('MAIL FROM:<'.$data.'>');
1711
1712                                                 $resp = 250;
1713                         break;
1714                         case 'to'       :
1715
1716                                                 $this->_send_data('RCPT TO:<'.$data.'>');
1717
1718                                                 $resp = 250;
1719                         break;
1720                         case 'data'     :
1721
1722                                                 $this->_send_data('DATA');
1723
1724                                                 $resp = 354;
1725                         break;
1726                         case 'quit'     :
1727
1728                                                 $this->_send_data('QUIT');
1729
1730                                                 $resp = 221;
1731                         break;
1732                 }
1733
1734                 $reply = $this->_get_smtp_data();
1735
1736                 $this->_debug_msg[] = "<pre>".$cmd.": ".$reply."</pre>";
1737
1738                 if (substr($reply, 0, 3) != $resp)
1739                 {
1740                         $this->_set_error_message('email_smtp_error', $reply);
1741                         return FALSE;
1742                 }
1743
1744                 if ($cmd == 'quit')
1745                 {
1746                         fclose($this->_smtp_connect);
1747                 }
1748
1749                 return TRUE;
1750         }
1751
1752         // --------------------------------------------------------------------
1753
1754         /**
1755          *  SMTP Authenticate
1756          *
1757          * @access      private
1758          * @return      bool
1759          */
1760         private function _smtp_authenticate()
1761         {
1762                 if ( ! $this->_smtp_auth)
1763                 {
1764                         return TRUE;
1765                 }
1766
1767                 if ($this->smtp_user == ""  AND  $this->smtp_pass == "")
1768                 {
1769                         $this->_set_error_message('email_no_smtp_unpw');
1770                         return FALSE;
1771                 }
1772
1773                 $this->_send_data('AUTH LOGIN');
1774
1775                 $reply = $this->_get_smtp_data();
1776
1777                 if (strncmp($reply, '334', 3) != 0)
1778                 {
1779                         $this->_set_error_message('email_failed_smtp_login', $reply);
1780                         return FALSE;
1781                 }
1782
1783                 $this->_send_data(base64_encode($this->smtp_user));
1784
1785                 $reply = $this->_get_smtp_data();
1786
1787                 if (strncmp($reply, '334', 3) != 0)
1788                 {
1789                         $this->_set_error_message('email_smtp_auth_un', $reply);
1790                         return FALSE;
1791                 }
1792
1793                 $this->_send_data(base64_encode($this->smtp_pass));
1794
1795                 $reply = $this->_get_smtp_data();
1796
1797                 if (strncmp($reply, '235', 3) != 0)
1798                 {
1799                         $this->_set_error_message('email_smtp_auth_pw', $reply);
1800                         return FALSE;
1801                 }
1802
1803                 return TRUE;
1804         }
1805
1806         // --------------------------------------------------------------------
1807
1808         /**
1809          * Send SMTP data
1810          *
1811          * @access      private
1812          * @return      bool
1813          */
1814         private function _send_data($data)
1815         {
1816                 if ( ! fwrite($this->_smtp_connect, $data . $this->newline))
1817                 {
1818                         $this->_set_error_message('email_smtp_data_failure', $data);
1819                         return FALSE;
1820                 }
1821                 else
1822                 {
1823                         return TRUE;
1824                 }
1825         }
1826
1827         // --------------------------------------------------------------------
1828
1829         /**
1830          * Get SMTP data
1831          *
1832          * @access      private
1833          * @return      string
1834          */
1835         private function _get_smtp_data()
1836         {
1837                 $data = "";
1838
1839                 while ($str = fgets($this->_smtp_connect, 512))
1840                 {
1841                         $data .= $str;
1842
1843                         if (substr($str, 3, 1) == " ")
1844                         {
1845                                 break;
1846                         }
1847                 }
1848
1849                 return $data;
1850         }
1851
1852         // --------------------------------------------------------------------
1853
1854         /**
1855          * Get Hostname
1856          *
1857          * @access      private
1858          * @return      string
1859          */
1860         private function _get_hostname()
1861         {
1862                 return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
1863         }
1864
1865         // --------------------------------------------------------------------
1866
1867         /**
1868          * Get IP
1869          *
1870          * @access      private
1871          * @return      string
1872          */
1873         private function _get_ip()
1874         {
1875                 if ($this->_IP !== FALSE)
1876                 {
1877                         return $this->_IP;
1878                 }
1879
1880                 $cip = (isset($_SERVER['HTTP_CLIENT_IP']) AND $_SERVER['HTTP_CLIENT_IP'] != "") ? $_SERVER['HTTP_CLIENT_IP'] : FALSE;
1881                 $rip = (isset($_SERVER['REMOTE_ADDR']) AND $_SERVER['REMOTE_ADDR'] != "") ? $_SERVER['REMOTE_ADDR'] : FALSE;
1882                 $fip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND $_SERVER['HTTP_X_FORWARDED_FOR'] != "") ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE;
1883
1884                 if ($cip && $rip)       $this->_IP = $cip;
1885                 elseif ($rip)           $this->_IP = $rip;
1886                 elseif ($cip)           $this->_IP = $cip;
1887                 elseif ($fip)           $this->_IP = $fip;
1888
1889                 if (strpos($this->_IP, ',') !== FALSE)
1890                 {
1891                         $x = explode(',', $this->_IP);
1892                         $this->_IP = end($x);
1893                 }
1894
1895                 if ( ! preg_match( "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $this->_IP))
1896                 {
1897                         $this->_IP = '0.0.0.0';
1898                 }
1899
1900                 unset($cip);
1901                 unset($rip);
1902                 unset($fip);
1903
1904                 return $this->_IP;
1905         }
1906
1907         // --------------------------------------------------------------------
1908
1909         /**
1910          * Get Debug Message
1911          *
1912          * @access      public
1913          * @return      string
1914          */
1915         public function print_debugger()
1916         {
1917                 $msg = '';
1918
1919                 if (count($this->_debug_msg) > 0)
1920                 {
1921                         foreach ($this->_debug_msg as $val)
1922                         {
1923                                 $msg .= $val;
1924                         }
1925                 }
1926
1927                 $msg .= "<pre>".$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';
1928                 return $msg;
1929         }
1930
1931         // --------------------------------------------------------------------
1932
1933         /**
1934          * Set Message
1935          *
1936          * @access      private
1937          * @param       string
1938          * @return      string
1939          */
1940         private function _set_error_message($msg, $val = '')
1941         {
1942                 $CI =& get_instance();
1943                 $CI->lang->load('email');
1944
1945                 if (FALSE === ($line = $CI->lang->line($msg)))
1946                 {
1947                         $this->_debug_msg[] = str_replace('%s', $val, $msg)."<br />";
1948                 }
1949                 else
1950                 {
1951                         $this->_debug_msg[] = str_replace('%s', $val, $line)."<br />";
1952                 }
1953         }
1954
1955         // --------------------------------------------------------------------
1956
1957         /**
1958          * Mime Types
1959          *
1960          * @access      private
1961          * @param       string
1962          * @return      string
1963          */
1964         private function _mime_types($ext = "")
1965         {
1966                 $mimes = array( 'hqx'   =>      'application/mac-binhex40',
1967                                                 'cpt'   =>      'application/mac-compactpro',
1968                                                 'doc'   =>      'application/msword',
1969                                                 'bin'   =>      'application/macbinary',
1970                                                 'dms'   =>      'application/octet-stream',
1971                                                 'lha'   =>      'application/octet-stream',
1972                                                 'lzh'   =>      'application/octet-stream',
1973                                                 'exe'   =>      'application/octet-stream',
1974                                                 'class' =>      'application/octet-stream',
1975                                                 'psd'   =>      'application/octet-stream',
1976                                                 'so'    =>      'application/octet-stream',
1977                                                 'sea'   =>      'application/octet-stream',
1978                                                 'dll'   =>      'application/octet-stream',
1979                                                 'oda'   =>      'application/oda',
1980                                                 'pdf'   =>      'application/pdf',
1981                                                 'ai'    =>      'application/postscript',
1982                                                 'eps'   =>      'application/postscript',
1983                                                 'ps'    =>      'application/postscript',
1984                                                 'smi'   =>      'application/smil',
1985                                                 'smil'  =>      'application/smil',
1986                                                 'mif'   =>      'application/vnd.mif',
1987                                                 'xls'   =>      'application/vnd.ms-excel',
1988                                                 'ppt'   =>      'application/vnd.ms-powerpoint',
1989                                                 'wbxml' =>      'application/vnd.wap.wbxml',
1990                                                 'wmlc'  =>      'application/vnd.wap.wmlc',
1991                                                 'dcr'   =>      'application/x-director',
1992                                                 'dir'   =>      'application/x-director',
1993                                                 'dxr'   =>      'application/x-director',
1994                                                 'dvi'   =>      'application/x-dvi',
1995                                                 'gtar'  =>      'application/x-gtar',
1996                                                 'php'   =>      'application/x-httpd-php',
1997                                                 'php4'  =>      'application/x-httpd-php',
1998                                                 'php3'  =>      'application/x-httpd-php',
1999                                                 'phtml' =>      'application/x-httpd-php',
2000                                                 'phps'  =>      'application/x-httpd-php-source',
2001                                                 'js'    =>      'application/x-javascript',
2002                                                 'swf'   =>      'application/x-shockwave-flash',
2003                                                 'sit'   =>      'application/x-stuffit',
2004                                                 'tar'   =>      'application/x-tar',
2005                                                 'tgz'   =>      'application/x-tar',
2006                                                 'xhtml' =>      'application/xhtml+xml',
2007                                                 'xht'   =>      'application/xhtml+xml',
2008                                                 'zip'   =>      'application/zip',
2009                                                 'mid'   =>      'audio/midi',
2010                                                 'midi'  =>      'audio/midi',
2011                                                 'mpga'  =>      'audio/mpeg',
2012                                                 'mp2'   =>      'audio/mpeg',
2013                                                 'mp3'   =>      'audio/mpeg',
2014                                                 'aif'   =>      'audio/x-aiff',
2015                                                 'aiff'  =>      'audio/x-aiff',
2016                                                 'aifc'  =>      'audio/x-aiff',
2017                                                 'ram'   =>      'audio/x-pn-realaudio',
2018                                                 'rm'    =>      'audio/x-pn-realaudio',
2019                                                 'rpm'   =>      'audio/x-pn-realaudio-plugin',
2020                                                 'ra'    =>      'audio/x-realaudio',
2021                                                 'rv'    =>      'video/vnd.rn-realvideo',
2022                                                 'wav'   =>      'audio/x-wav',
2023                                                 'bmp'   =>      'image/bmp',
2024                                                 'gif'   =>      'image/gif',
2025                                                 'jpeg'  =>      'image/jpeg',
2026                                                 'jpg'   =>      'image/jpeg',
2027                                                 'jpe'   =>      'image/jpeg',
2028                                                 'png'   =>      'image/png',
2029                                                 'tiff'  =>      'image/tiff',
2030                                                 'tif'   =>      'image/tiff',
2031                                                 'css'   =>      'text/css',
2032                                                 'html'  =>      'text/html',
2033                                                 'htm'   =>      'text/html',
2034                                                 'shtml' =>      'text/html',
2035                                                 'txt'   =>      'text/plain',
2036                                                 'text'  =>      'text/plain',
2037                                                 'log'   =>      'text/plain',
2038                                                 'rtx'   =>      'text/richtext',
2039                                                 'rtf'   =>      'text/rtf',
2040                                                 'xml'   =>      'text/xml',
2041                                                 'xsl'   =>      'text/xml',
2042                                                 'mpeg'  =>      'video/mpeg',
2043                                                 'mpg'   =>      'video/mpeg',
2044                                                 'mpe'   =>      'video/mpeg',
2045                                                 'qt'    =>      'video/quicktime',
2046                                                 'mov'   =>      'video/quicktime',
2047                                                 'avi'   =>      'video/x-msvideo',
2048                                                 'movie' =>      'video/x-sgi-movie',
2049                                                 'doc'   =>      'application/msword',
2050                                                 'word'  =>      'application/msword',
2051                                                 'xl'    =>      'application/excel',
2052                                                 'eml'   =>      'message/rfc822'
2053                                         );
2054
2055                 return ( ! isset($mimes[strtolower($ext)])) ? "application/x-unknown-content-type" : $mimes[strtolower($ext)];
2056         }
2057
2058 }
2059 // END CI_Email class
2060
2061 /* End of file Email.php */
2062 /* Location: ./system/libraries/Email.php */