blob: cda9025936de16182e19c3c9f9fffd6a7b59d619 [file] [log] [blame]
Derek Allard2067d1a2008-11-13 22:59:24 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
Greg Aker741de1c2010-11-10 14:52:57 -06005 * An open source application development framework for PHP 5.1.6 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
Derek Jones7f3719f2010-01-05 13:35:37 +00009 * @copyright Copyright (c) 2008 - 2010, EllisLab, Inc.
Derek Allard2067d1a2008-11-13 22:59:24 +000010 * @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 */
29class 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 */
Greg Akera9263282010-11-10 15:26:43 -060085 function __construct($config = array())
Derek Allard2067d1a2008-11-13 22:59:24 +000086 {
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 function initialize($config = array())
110 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000111 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 }
Eric Barnes6113f542010-12-29 13:36:12 -0500127 $this->clear();
Derek Allard2067d1a2008-11-13 22:59:24 +0000128
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 }
Barry Mienydd671972010-10-04 16:33:58 +0200132
Derek Allard2067d1a2008-11-13 22:59:24 +0000133 // --------------------------------------------------------------------
134
135 /**
136 * Initialize the Email Data
137 *
138 * @access public
139 * @return void
140 */
141 function clear($clear_attachments = FALSE)
142 {
143 $this->_subject = "";
144 $this->_body = "";
145 $this->_finalbody = "";
146 $this->_header_str = "";
147 $this->_replyto_flag = FALSE;
148 $this->_recipients = array();
Derek Jonesd1606352010-09-01 11:16:07 -0500149 $this->_cc_array = array();
150 $this->_bcc_array = array();
Derek Allard2067d1a2008-11-13 22:59:24 +0000151 $this->_headers = array();
152 $this->_debug_msg = array();
153
154 $this->_set_header('User-Agent', $this->useragent);
155 $this->_set_header('Date', $this->_set_date());
156
157 if ($clear_attachments !== FALSE)
158 {
159 $this->_attach_name = array();
160 $this->_attach_type = array();
161 $this->_attach_disp = array();
162 }
Eric Barnes6113f542010-12-29 13:36:12 -0500163
Greg Akera769deb2010-11-10 15:47:29 -0600164 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000165 }
Barry Mienydd671972010-10-04 16:33:58 +0200166
Derek Allard2067d1a2008-11-13 22:59:24 +0000167 // --------------------------------------------------------------------
168
169 /**
170 * Set FROM
171 *
172 * @access public
173 * @param string
174 * @param string
175 * @return void
176 */
177 function from($from, $name = '')
178 {
179 if (preg_match( '/\<(.*)\>/', $from, $match))
180 {
181 $from = $match['1'];
182 }
183
184 if ($this->validate)
185 {
186 $this->validate_email($this->_str_to_array($from));
187 }
188
189 // prepare the display name
190 if ($name != '')
191 {
192 // only use Q encoding if there are characters that would require it
193 if ( ! preg_match('/[\200-\377]/', $name))
194 {
195 // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes
Derek Jonesc630bcf2008-11-17 21:09:45 +0000196 $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"';
Derek Allard2067d1a2008-11-13 22:59:24 +0000197 }
198 else
199 {
200 $name = $this->_prep_q_encoding($name, TRUE);
201 }
202 }
203
204 $this->_set_header('From', $name.' <'.$from.'>');
205 $this->_set_header('Return-Path', '<'.$from.'>');
Eric Barnes6113f542010-12-29 13:36:12 -0500206
Greg Akera769deb2010-11-10 15:47:29 -0600207 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000208 }
Barry Mienydd671972010-10-04 16:33:58 +0200209
Derek Allard2067d1a2008-11-13 22:59:24 +0000210 // --------------------------------------------------------------------
211
212 /**
213 * Set Reply-to
214 *
215 * @access public
216 * @param string
217 * @param string
218 * @return void
219 */
220 function reply_to($replyto, $name = '')
221 {
222 if (preg_match( '/\<(.*)\>/', $replyto, $match))
223 {
224 $replyto = $match['1'];
225 }
226
227 if ($this->validate)
228 {
229 $this->validate_email($this->_str_to_array($replyto));
230 }
231
232 if ($name == '')
233 {
234 $name = $replyto;
235 }
236
237 if (strncmp($name, '"', 1) != 0)
238 {
239 $name = '"'.$name.'"';
240 }
241
242 $this->_set_header('Reply-To', $name.' <'.$replyto.'>');
243 $this->_replyto_flag = TRUE;
Greg Akera769deb2010-11-10 15:47:29 -0600244
245 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000246 }
Barry Mienydd671972010-10-04 16:33:58 +0200247
Derek Allard2067d1a2008-11-13 22:59:24 +0000248 // --------------------------------------------------------------------
249
250 /**
251 * Set Recipients
252 *
253 * @access public
254 * @param string
255 * @return void
256 */
257 function to($to)
258 {
259 $to = $this->_str_to_array($to);
260 $to = $this->clean_email($to);
261
262 if ($this->validate)
263 {
264 $this->validate_email($to);
265 }
266
267 if ($this->_get_protocol() != 'mail')
268 {
269 $this->_set_header('To', implode(", ", $to));
270 }
271
272 switch ($this->_get_protocol())
273 {
274 case 'smtp' : $this->_recipients = $to;
275 break;
276 case 'sendmail' : $this->_recipients = implode(", ", $to);
277 break;
278 case 'mail' : $this->_recipients = implode(", ", $to);
279 break;
280 }
Eric Barnes6113f542010-12-29 13:36:12 -0500281
Greg Akera769deb2010-11-10 15:47:29 -0600282 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000283 }
Barry Mienydd671972010-10-04 16:33:58 +0200284
Derek Allard2067d1a2008-11-13 22:59:24 +0000285 // --------------------------------------------------------------------
286
287 /**
288 * Set CC
289 *
290 * @access public
291 * @param string
292 * @return void
293 */
294 function cc($cc)
295 {
296 $cc = $this->_str_to_array($cc);
297 $cc = $this->clean_email($cc);
298
299 if ($this->validate)
300 {
301 $this->validate_email($cc);
302 }
303
304 $this->_set_header('Cc', implode(", ", $cc));
305
306 if ($this->_get_protocol() == "smtp")
307 {
308 $this->_cc_array = $cc;
309 }
Eric Barnes6113f542010-12-29 13:36:12 -0500310
Greg Akera769deb2010-11-10 15:47:29 -0600311 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000312 }
Barry Mienydd671972010-10-04 16:33:58 +0200313
Derek Allard2067d1a2008-11-13 22:59:24 +0000314 // --------------------------------------------------------------------
315
316 /**
317 * Set BCC
318 *
319 * @access public
320 * @param string
321 * @param string
322 * @return void
323 */
324 function bcc($bcc, $limit = '')
325 {
326 if ($limit != '' && is_numeric($limit))
327 {
328 $this->bcc_batch_mode = TRUE;
329 $this->bcc_batch_size = $limit;
330 }
331
332 $bcc = $this->_str_to_array($bcc);
333 $bcc = $this->clean_email($bcc);
334
335 if ($this->validate)
336 {
337 $this->validate_email($bcc);
338 }
339
340 if (($this->_get_protocol() == "smtp") OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
341 {
342 $this->_bcc_array = $bcc;
343 }
344 else
345 {
346 $this->_set_header('Bcc', implode(", ", $bcc));
347 }
Eric Barnes6113f542010-12-29 13:36:12 -0500348
Greg Akera769deb2010-11-10 15:47:29 -0600349 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000350 }
Barry Mienydd671972010-10-04 16:33:58 +0200351
Derek Allard2067d1a2008-11-13 22:59:24 +0000352 // --------------------------------------------------------------------
353
354 /**
355 * Set Email Subject
356 *
357 * @access public
358 * @param string
359 * @return void
360 */
361 function subject($subject)
362 {
363 $subject = $this->_prep_q_encoding($subject);
364 $this->_set_header('Subject', $subject);
Greg Akera769deb2010-11-10 15:47:29 -0600365 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000366 }
Barry Mienydd671972010-10-04 16:33:58 +0200367
Derek Allard2067d1a2008-11-13 22:59:24 +0000368 // --------------------------------------------------------------------
369
370 /**
371 * Set Body
372 *
373 * @access public
374 * @param string
375 * @return void
376 */
377 function message($body)
378 {
379 $this->_body = stripslashes(rtrim(str_replace("\r", "", $body)));
Greg Akera769deb2010-11-10 15:47:29 -0600380 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000381 }
Barry Mienydd671972010-10-04 16:33:58 +0200382
Derek Allard2067d1a2008-11-13 22:59:24 +0000383 // --------------------------------------------------------------------
384
385 /**
386 * Assign file attachments
387 *
388 * @access public
389 * @param string
390 * @return void
391 */
392 function attach($filename, $disposition = 'attachment')
393 {
394 $this->_attach_name[] = $filename;
395 $this->_attach_type[] = $this->_mime_types(next(explode('.', basename($filename))));
396 $this->_attach_disp[] = $disposition; // Can also be 'inline' Not sure if it matters
Greg Akera769deb2010-11-10 15:47:29 -0600397 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000398 }
399
400 // --------------------------------------------------------------------
401
402 /**
403 * Add a Header Item
404 *
405 * @access private
406 * @param string
407 * @param string
408 * @return void
409 */
410 function _set_header($header, $value)
411 {
412 $this->_headers[$header] = $value;
413 }
Barry Mienydd671972010-10-04 16:33:58 +0200414
Derek Allard2067d1a2008-11-13 22:59:24 +0000415 // --------------------------------------------------------------------
416
417 /**
418 * Convert a String to an Array
419 *
420 * @access private
421 * @param string
422 * @return array
423 */
424 function _str_to_array($email)
425 {
426 if ( ! is_array($email))
427 {
428 if (strpos($email, ',') !== FALSE)
429 {
430 $email = preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY);
431 }
432 else
433 {
434 $email = trim($email);
435 settype($email, "array");
436 }
437 }
438 return $email;
439 }
Barry Mienydd671972010-10-04 16:33:58 +0200440
Derek Allard2067d1a2008-11-13 22:59:24 +0000441 // --------------------------------------------------------------------
442
443 /**
444 * Set Multipart Value
445 *
446 * @access public
447 * @param string
448 * @return void
449 */
450 function set_alt_message($str = '')
451 {
452 $this->alt_message = ($str == '') ? '' : $str;
Greg Akera769deb2010-11-10 15:47:29 -0600453 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000454 }
Barry Mienydd671972010-10-04 16:33:58 +0200455
Derek Allard2067d1a2008-11-13 22:59:24 +0000456 // --------------------------------------------------------------------
457
458 /**
459 * Set Mailtype
460 *
461 * @access public
462 * @param string
463 * @return void
464 */
465 function set_mailtype($type = 'text')
466 {
467 $this->mailtype = ($type == 'html') ? 'html' : 'text';
Greg Akera769deb2010-11-10 15:47:29 -0600468 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000469 }
Barry Mienydd671972010-10-04 16:33:58 +0200470
Derek Allard2067d1a2008-11-13 22:59:24 +0000471 // --------------------------------------------------------------------
472
473 /**
474 * Set Wordwrap
475 *
476 * @access public
477 * @param string
478 * @return void
479 */
480 function set_wordwrap($wordwrap = TRUE)
481 {
482 $this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE;
Greg Akera769deb2010-11-10 15:47:29 -0600483 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000484 }
Barry Mienydd671972010-10-04 16:33:58 +0200485
Derek Allard2067d1a2008-11-13 22:59:24 +0000486 // --------------------------------------------------------------------
487
488 /**
489 * Set Protocol
490 *
491 * @access public
492 * @param string
493 * @return void
494 */
495 function set_protocol($protocol = 'mail')
496 {
497 $this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol);
Greg Akera769deb2010-11-10 15:47:29 -0600498 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000499 }
Barry Mienydd671972010-10-04 16:33:58 +0200500
Derek Allard2067d1a2008-11-13 22:59:24 +0000501 // --------------------------------------------------------------------
502
503 /**
504 * Set Priority
505 *
506 * @access public
507 * @param integer
508 * @return void
509 */
510 function set_priority($n = 3)
511 {
512 if ( ! is_numeric($n))
513 {
514 $this->priority = 3;
515 return;
516 }
517
518 if ($n < 1 OR $n > 5)
519 {
520 $this->priority = 3;
521 return;
522 }
523
524 $this->priority = $n;
Greg Akera769deb2010-11-10 15:47:29 -0600525 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000526 }
Barry Mienydd671972010-10-04 16:33:58 +0200527
Derek Allard2067d1a2008-11-13 22:59:24 +0000528 // --------------------------------------------------------------------
529
530 /**
531 * Set Newline Character
532 *
533 * @access public
534 * @param string
535 * @return void
536 */
537 function set_newline($newline = "\n")
538 {
539 if ($newline != "\n" AND $newline != "\r\n" AND $newline != "\r")
540 {
541 $this->newline = "\n";
542 return;
543 }
544
545 $this->newline = $newline;
Eric Barnes6113f542010-12-29 13:36:12 -0500546
Greg Akera769deb2010-11-10 15:47:29 -0600547 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000548 }
Barry Mienydd671972010-10-04 16:33:58 +0200549
Derek Allard2067d1a2008-11-13 22:59:24 +0000550 // --------------------------------------------------------------------
551
552 /**
553 * Set CRLF
554 *
555 * @access public
556 * @param string
557 * @return void
558 */
559 function set_crlf($crlf = "\n")
560 {
561 if ($crlf != "\n" AND $crlf != "\r\n" AND $crlf != "\r")
562 {
563 $this->crlf = "\n";
564 return;
565 }
566
567 $this->crlf = $crlf;
Eric Barnes6113f542010-12-29 13:36:12 -0500568
Greg Akera769deb2010-11-10 15:47:29 -0600569 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000570 }
Barry Mienydd671972010-10-04 16:33:58 +0200571
Derek Allard2067d1a2008-11-13 22:59:24 +0000572 // --------------------------------------------------------------------
573
574 /**
575 * Set Message Boundary
576 *
577 * @access private
578 * @return void
579 */
580 function _set_boundaries()
581 {
582 $this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
583 $this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
584 }
Barry Mienydd671972010-10-04 16:33:58 +0200585
Derek Allard2067d1a2008-11-13 22:59:24 +0000586 // --------------------------------------------------------------------
587
588 /**
589 * Get the Message ID
590 *
591 * @access private
592 * @return string
593 */
594 function _get_message_id()
595 {
596 $from = $this->_headers['Return-Path'];
597 $from = str_replace(">", "", $from);
598 $from = str_replace("<", "", $from);
599
600 return "<".uniqid('').strstr($from, '@').">";
601 }
Barry Mienydd671972010-10-04 16:33:58 +0200602
Derek Allard2067d1a2008-11-13 22:59:24 +0000603 // --------------------------------------------------------------------
604
605 /**
606 * Get Mail Protocol
607 *
608 * @access private
609 * @param bool
610 * @return string
611 */
612 function _get_protocol($return = TRUE)
613 {
614 $this->protocol = strtolower($this->protocol);
615 $this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;
616
617 if ($return == TRUE)
618 {
619 return $this->protocol;
620 }
621 }
Barry Mienydd671972010-10-04 16:33:58 +0200622
Derek Allard2067d1a2008-11-13 22:59:24 +0000623 // --------------------------------------------------------------------
624
625 /**
626 * Get Mail Encoding
627 *
628 * @access private
629 * @param bool
630 * @return string
631 */
632 function _get_encoding($return = TRUE)
633 {
634 $this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;
635
636 foreach ($this->_base_charsets as $charset)
637 {
638 if (strncmp($charset, $this->charset, strlen($charset)) == 0)
639 {
640 $this->_encoding = '7bit';
641 }
642 }
643
644 if ($return == TRUE)
645 {
646 return $this->_encoding;
647 }
648 }
649
650 // --------------------------------------------------------------------
651
652 /**
653 * Get content type (text/html/attachment)
654 *
655 * @access private
656 * @return string
657 */
658 function _get_content_type()
659 {
660 if ($this->mailtype == 'html' && count($this->_attach_name) == 0)
661 {
662 return 'html';
663 }
664 elseif ($this->mailtype == 'html' && count($this->_attach_name) > 0)
665 {
666 return 'html-attach';
667 }
668 elseif ($this->mailtype == 'text' && count($this->_attach_name) > 0)
669 {
670 return 'plain-attach';
671 }
672 else
673 {
674 return 'plain';
675 }
676 }
Barry Mienydd671972010-10-04 16:33:58 +0200677
Derek Allard2067d1a2008-11-13 22:59:24 +0000678 // --------------------------------------------------------------------
679
680 /**
681 * Set RFC 822 Date
682 *
683 * @access private
684 * @return string
685 */
686 function _set_date()
687 {
688 $timezone = date("Z");
689 $operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+';
690 $timezone = abs($timezone);
691 $timezone = floor($timezone/3600) * 100 + ($timezone % 3600 ) / 60;
692
693 return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);
694 }
Barry Mienydd671972010-10-04 16:33:58 +0200695
Derek Allard2067d1a2008-11-13 22:59:24 +0000696 // --------------------------------------------------------------------
697
698 /**
699 * Mime message
700 *
701 * @access private
702 * @return string
703 */
704 function _get_mime_message()
705 {
706 return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
707 }
Barry Mienydd671972010-10-04 16:33:58 +0200708
Derek Allard2067d1a2008-11-13 22:59:24 +0000709 // --------------------------------------------------------------------
710
711 /**
712 * Validate Email Address
713 *
714 * @access public
715 * @param string
716 * @return bool
717 */
718 function validate_email($email)
719 {
720 if ( ! is_array($email))
721 {
722 $this->_set_error_message('email_must_be_array');
723 return FALSE;
724 }
725
726 foreach ($email as $val)
727 {
728 if ( ! $this->valid_email($val))
729 {
730 $this->_set_error_message('email_invalid_address', $val);
731 return FALSE;
732 }
733 }
734
735 return TRUE;
736 }
Barry Mienydd671972010-10-04 16:33:58 +0200737
Derek Allard2067d1a2008-11-13 22:59:24 +0000738 // --------------------------------------------------------------------
739
740 /**
741 * Email Validation
742 *
743 * @access public
744 * @param string
745 * @return bool
746 */
747 function valid_email($address)
748 {
749 return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE;
750 }
Barry Mienydd671972010-10-04 16:33:58 +0200751
Derek Allard2067d1a2008-11-13 22:59:24 +0000752 // --------------------------------------------------------------------
753
754 /**
755 * Clean Extended Email Address: Joe Smith <joe@smith.com>
756 *
757 * @access public
758 * @param string
759 * @return string
760 */
761 function clean_email($email)
762 {
763 if ( ! is_array($email))
764 {
765 if (preg_match('/\<(.*)\>/', $email, $match))
766 {
Barry Mienydd671972010-10-04 16:33:58 +0200767 return $match['1'];
Derek Allard2067d1a2008-11-13 22:59:24 +0000768 }
Barry Mienydd671972010-10-04 16:33:58 +0200769 else
Derek Allard2067d1a2008-11-13 22:59:24 +0000770 {
Barry Mienydd671972010-10-04 16:33:58 +0200771 return $email;
Derek Allard2067d1a2008-11-13 22:59:24 +0000772 }
773 }
774
775 $clean_email = array();
776
777 foreach ($email as $addy)
778 {
779 if (preg_match( '/\<(.*)\>/', $addy, $match))
780 {
Barry Mienydd671972010-10-04 16:33:58 +0200781 $clean_email[] = $match['1'];
Derek Allard2067d1a2008-11-13 22:59:24 +0000782 }
Barry Mienydd671972010-10-04 16:33:58 +0200783 else
Derek Allard2067d1a2008-11-13 22:59:24 +0000784 {
Barry Mienydd671972010-10-04 16:33:58 +0200785 $clean_email[] = $addy;
Derek Allard2067d1a2008-11-13 22:59:24 +0000786 }
787 }
788
789 return $clean_email;
790 }
Barry Mienydd671972010-10-04 16:33:58 +0200791
Derek Allard2067d1a2008-11-13 22:59:24 +0000792 // --------------------------------------------------------------------
793
794 /**
795 * Build alternative plain text message
796 *
797 * This function provides the raw message for use
798 * in plain-text headers of HTML-formatted emails.
799 * If the user hasn't specified his own alternative message
800 * it creates one by stripping the HTML
801 *
802 * @access private
803 * @return string
804 */
805 function _get_alt_message()
806 {
807 if ($this->alt_message != "")
808 {
809 return $this->word_wrap($this->alt_message, '76');
810 }
811
812 if (preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match))
813 {
814 $body = $match['1'];
815 }
816 else
817 {
818 $body = $this->_body;
819 }
820
821 $body = trim(strip_tags($body));
822 $body = preg_replace( '#<!--(.*)--\>#', "", $body);
823 $body = str_replace("\t", "", $body);
824
825 for ($i = 20; $i >= 3; $i--)
826 {
827 $n = "";
828
829 for ($x = 1; $x <= $i; $x ++)
830 {
Barry Mienydd671972010-10-04 16:33:58 +0200831 $n .= "\n";
Derek Allard2067d1a2008-11-13 22:59:24 +0000832 }
833
834 $body = str_replace($n, "\n\n", $body);
835 }
836
837 return $this->word_wrap($body, '76');
838 }
Barry Mienydd671972010-10-04 16:33:58 +0200839
Derek Allard2067d1a2008-11-13 22:59:24 +0000840 // --------------------------------------------------------------------
841
842 /**
843 * Word Wrap
844 *
845 * @access public
846 * @param string
847 * @param integer
848 * @return string
849 */
850 function word_wrap($str, $charlim = '')
851 {
852 // Se the character limit
853 if ($charlim == '')
854 {
855 $charlim = ($this->wrapchars == "") ? "76" : $this->wrapchars;
856 }
857
858 // Reduce multiple spaces
859 $str = preg_replace("| +|", " ", $str);
860
861 // Standardize newlines
862 if (strpos($str, "\r") !== FALSE)
863 {
864 $str = str_replace(array("\r\n", "\r"), "\n", $str);
865 }
866
867 // If the current word is surrounded by {unwrap} tags we'll
868 // strip the entire chunk and replace it with a marker.
869 $unwrap = array();
870 if (preg_match_all("|(\{unwrap\}.+?\{/unwrap\})|s", $str, $matches))
871 {
872 for ($i = 0; $i < count($matches['0']); $i++)
873 {
874 $unwrap[] = $matches['1'][$i];
875 $str = str_replace($matches['1'][$i], "{{unwrapped".$i."}}", $str);
876 }
877 }
878
879 // Use PHP's native function to do the initial wordwrap.
880 // We set the cut flag to FALSE so that any individual words that are
881 // too long get left alone. In the next step we'll deal with them.
882 $str = wordwrap($str, $charlim, "\n", FALSE);
883
884 // Split the string into individual lines of text and cycle through them
885 $output = "";
886 foreach (explode("\n", $str) as $line)
887 {
888 // Is the line within the allowed character count?
889 // If so we'll join it to the output and continue
890 if (strlen($line) <= $charlim)
891 {
892 $output .= $line.$this->newline;
893 continue;
894 }
895
896 $temp = '';
897 while((strlen($line)) > $charlim)
898 {
899 // If the over-length word is a URL we won't wrap it
900 if (preg_match("!\[url.+\]|://|wwww.!", $line))
901 {
902 break;
903 }
904
905 // Trim the word down
906 $temp .= substr($line, 0, $charlim-1);
907 $line = substr($line, $charlim-1);
908 }
909
910 // If $temp contains data it means we had to split up an over-length
911 // word into smaller chunks so we'll add it back to our current line
912 if ($temp != '')
913 {
914 $output .= $temp.$this->newline.$line;
915 }
916 else
917 {
918 $output .= $line;
919 }
920
921 $output .= $this->newline;
922 }
923
924 // Put our markers back
925 if (count($unwrap) > 0)
926 {
927 foreach ($unwrap as $key => $val)
928 {
929 $output = str_replace("{{unwrapped".$key."}}", $val, $output);
930 }
931 }
932
933 return $output;
934 }
Barry Mienydd671972010-10-04 16:33:58 +0200935
Derek Allard2067d1a2008-11-13 22:59:24 +0000936 // --------------------------------------------------------------------
937
938 /**
939 * Build final headers
940 *
941 * @access private
942 * @param string
943 * @return string
944 */
945 function _build_headers()
946 {
947 $this->_set_header('X-Sender', $this->clean_email($this->_headers['From']));
948 $this->_set_header('X-Mailer', $this->useragent);
949 $this->_set_header('X-Priority', $this->_priorities[$this->priority - 1]);
950 $this->_set_header('Message-ID', $this->_get_message_id());
951 $this->_set_header('Mime-Version', '1.0');
952 }
Barry Mienydd671972010-10-04 16:33:58 +0200953
Derek Allard2067d1a2008-11-13 22:59:24 +0000954 // --------------------------------------------------------------------
955
956 /**
957 * Write Headers as a string
958 *
959 * @access private
960 * @return void
961 */
962 function _write_headers()
963 {
964 if ($this->protocol == 'mail')
965 {
966 $this->_subject = $this->_headers['Subject'];
967 unset($this->_headers['Subject']);
968 }
969
970 reset($this->_headers);
971 $this->_header_str = "";
972
973 foreach($this->_headers as $key => $val)
974 {
975 $val = trim($val);
976
977 if ($val != "")
978 {
979 $this->_header_str .= $key.": ".$val.$this->newline;
980 }
981 }
982
983 if ($this->_get_protocol() == 'mail')
984 {
Derek Jones1d890882009-02-10 20:32:14 +0000985 $this->_header_str = rtrim($this->_header_str);
Derek Allard2067d1a2008-11-13 22:59:24 +0000986 }
987 }
Barry Mienydd671972010-10-04 16:33:58 +0200988
Derek Allard2067d1a2008-11-13 22:59:24 +0000989 // --------------------------------------------------------------------
990
991 /**
992 * Build Final Body and attachments
993 *
994 * @access private
995 * @return void
996 */
997 function _build_message()
998 {
999 if ($this->wordwrap === TRUE AND $this->mailtype != 'html')
1000 {
1001 $this->_body = $this->word_wrap($this->_body);
1002 }
1003
1004 $this->_set_boundaries();
1005 $this->_write_headers();
1006
1007 $hdr = ($this->_get_protocol() == 'mail') ? $this->newline : '';
Brandon Jones485d7412010-11-09 16:38:17 -05001008 $body = '';
Derek Allard2067d1a2008-11-13 22:59:24 +00001009
1010 switch ($this->_get_content_type())
1011 {
1012 case 'plain' :
1013
1014 $hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1015 $hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding();
1016
1017 if ($this->_get_protocol() == 'mail')
1018 {
1019 $this->_header_str .= $hdr;
1020 $this->_finalbody = $this->_body;
Derek Allard2067d1a2008-11-13 22:59:24 +00001021 }
Brandon Jones485d7412010-11-09 16:38:17 -05001022 else
1023 {
1024 $this->_finalbody = $hdr . $this->newline . $this->newline . $this->_body;
1025 }
Eric Barnes6113f542010-12-29 13:36:12 -05001026
Derek Allard2067d1a2008-11-13 22:59:24 +00001027 return;
1028
1029 break;
1030 case 'html' :
1031
1032 if ($this->send_multipart === FALSE)
1033 {
1034 $hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1035 $hdr .= "Content-Transfer-Encoding: quoted-printable";
1036 }
1037 else
1038 {
Derek Jonesa45e7612009-02-10 18:33:01 +00001039 $hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline . $this->newline;
Derek Allard2067d1a2008-11-13 22:59:24 +00001040
Brandon Jones485d7412010-11-09 16:38:17 -05001041 $body .= $this->_get_mime_message() . $this->newline . $this->newline;
1042 $body .= "--" . $this->_alt_boundary . $this->newline;
Derek Allard2067d1a2008-11-13 22:59:24 +00001043
Brandon Jones485d7412010-11-09 16:38:17 -05001044 $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1045 $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
1046 $body .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
1047
1048 $body .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1049 $body .= "Content-Transfer-Encoding: quoted-printable" . $this->newline . $this->newline;
Derek Allard2067d1a2008-11-13 22:59:24 +00001050 }
Eric Barnes6113f542010-12-29 13:36:12 -05001051
Brandon Jones485d7412010-11-09 16:38:17 -05001052 $this->_finalbody = $body . $this->_prep_quoted_printable($this->_body) . $this->newline . $this->newline;
Eric Barnes6113f542010-12-29 13:36:12 -05001053
1054
Derek Allard2067d1a2008-11-13 22:59:24 +00001055 if ($this->_get_protocol() == 'mail')
1056 {
1057 $this->_header_str .= $hdr;
Brandon Jones485d7412010-11-09 16:38:17 -05001058 }
1059 else
1060 {
1061 $this->_finalbody = $hdr . $this->_finalbody;
Derek Allard2067d1a2008-11-13 22:59:24 +00001062 }
1063
Derek Allard2067d1a2008-11-13 22:59:24 +00001064
1065 if ($this->send_multipart !== FALSE)
1066 {
Brandon Jones485d7412010-11-09 16:38:17 -05001067 $this->_finalbody .= "--" . $this->_alt_boundary . "--";
Derek Allard2067d1a2008-11-13 22:59:24 +00001068 }
1069
Derek Allard2067d1a2008-11-13 22:59:24 +00001070 return;
1071
1072 break;
1073 case 'plain-attach' :
1074
Derek Jonesa45e7612009-02-10 18:33:01 +00001075 $hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline . $this->newline;
Derek Allard2067d1a2008-11-13 22:59:24 +00001076
1077 if ($this->_get_protocol() == 'mail')
1078 {
1079 $this->_header_str .= $hdr;
Eric Barnes6113f542010-12-29 13:36:12 -05001080 }
1081
Brandon Jones485d7412010-11-09 16:38:17 -05001082 $body .= $this->_get_mime_message() . $this->newline . $this->newline;
1083 $body .= "--" . $this->_atc_boundary . $this->newline;
Derek Allard2067d1a2008-11-13 22:59:24 +00001084
Brandon Jones485d7412010-11-09 16:38:17 -05001085 $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1086 $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
Derek Allard2067d1a2008-11-13 22:59:24 +00001087
Brandon Jones485d7412010-11-09 16:38:17 -05001088 $body .= $this->_body . $this->newline . $this->newline;
Derek Allard2067d1a2008-11-13 22:59:24 +00001089
1090 break;
1091 case 'html-attach' :
1092
Derek Jonesa45e7612009-02-10 18:33:01 +00001093 $hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline . $this->newline;
Eric Barnes6113f542010-12-29 13:36:12 -05001094
Derek Allard2067d1a2008-11-13 22:59:24 +00001095 if ($this->_get_protocol() == 'mail')
1096 {
1097 $this->_header_str .= $hdr;
Derek Allard2067d1a2008-11-13 22:59:24 +00001098 }
1099
Brandon Jones485d7412010-11-09 16:38:17 -05001100 $body .= $this->_get_mime_message() . $this->newline . $this->newline;
1101 $body .= "--" . $this->_atc_boundary . $this->newline;
1102
1103 $body .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline .$this->newline;
1104 $body .= "--" . $this->_alt_boundary . $this->newline;
1105
1106 $body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1107 $body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
1108 $body .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
1109
1110 $body .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1111 $body .= "Content-Transfer-Encoding: quoted-printable" . $this->newline . $this->newline;
1112
1113 $body .= $this->_prep_quoted_printable($this->_body) . $this->newline . $this->newline;
1114 $body .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;
Derek Allard2067d1a2008-11-13 22:59:24 +00001115
1116 break;
1117 }
1118
1119 $attachment = array();
1120
1121 $z = 0;
1122
1123 for ($i=0; $i < count($this->_attach_name); $i++)
1124 {
1125 $filename = $this->_attach_name[$i];
1126 $basename = basename($filename);
1127 $ctype = $this->_attach_type[$i];
1128
1129 if ( ! file_exists($filename))
1130 {
1131 $this->_set_error_message('email_attachment_missing', $filename);
1132 return FALSE;
1133 }
1134
1135 $h = "--".$this->_atc_boundary.$this->newline;
1136 $h .= "Content-type: ".$ctype."; ";
1137 $h .= "name=\"".$basename."\"".$this->newline;
1138 $h .= "Content-Disposition: ".$this->_attach_disp[$i].";".$this->newline;
1139 $h .= "Content-Transfer-Encoding: base64".$this->newline;
1140
1141 $attachment[$z++] = $h;
1142 $file = filesize($filename) +1;
1143
1144 if ( ! $fp = fopen($filename, FOPEN_READ))
1145 {
1146 $this->_set_error_message('email_attachment_unreadable', $filename);
1147 return FALSE;
1148 }
1149
1150 $attachment[$z++] = chunk_split(base64_encode(fread($fp, $file)));
1151 fclose($fp);
1152 }
1153
Brandon Jones485d7412010-11-09 16:38:17 -05001154 $body .= implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";
Eric Barnes6113f542010-12-29 13:36:12 -05001155
Brandon Jones485d7412010-11-09 16:38:17 -05001156
Derek Allard2067d1a2008-11-13 22:59:24 +00001157 if ($this->_get_protocol() == 'mail')
1158 {
Brandon Jones485d7412010-11-09 16:38:17 -05001159 $this->_finalbody = $body;
Derek Allard2067d1a2008-11-13 22:59:24 +00001160 }
Brandon Jones485d7412010-11-09 16:38:17 -05001161 else
1162 {
1163 $this->_finalbody = $hdr . $body;
1164 }
Eric Barnes6113f542010-12-29 13:36:12 -05001165
Derek Allard2067d1a2008-11-13 22:59:24 +00001166 return;
1167 }
Barry Mienydd671972010-10-04 16:33:58 +02001168
Derek Allard2067d1a2008-11-13 22:59:24 +00001169 // --------------------------------------------------------------------
1170
1171 /**
1172 * Prep Quoted Printable
1173 *
1174 * Prepares string for Quoted-Printable Content-Transfer-Encoding
1175 * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
1176 *
1177 * @access private
1178 * @param string
1179 * @param integer
1180 * @return string
1181 */
1182 function _prep_quoted_printable($str, $charlim = '')
1183 {
1184 // Set the character limit
1185 // Don't allow over 76, as that will make servers and MUAs barf
1186 // all over quoted-printable data
1187 if ($charlim == '' OR $charlim > '76')
1188 {
1189 $charlim = '76';
1190 }
1191
1192 // Reduce multiple spaces
1193 $str = preg_replace("| +|", " ", $str);
1194
1195 // kill nulls
1196 $str = preg_replace('/\x00+/', '', $str);
1197
1198 // Standardize newlines
1199 if (strpos($str, "\r") !== FALSE)
1200 {
1201 $str = str_replace(array("\r\n", "\r"), "\n", $str);
1202 }
1203
1204 // We are intentionally wrapping so mail servers will encode characters
1205 // properly and MUAs will behave, so {unwrap} must go!
1206 $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
1207
1208 // Break into an array of lines
1209 $lines = explode("\n", $str);
1210
1211 $escape = '=';
1212 $output = '';
1213
1214 foreach ($lines as $line)
1215 {
1216 $length = strlen($line);
1217 $temp = '';
1218
1219 // Loop through each character in the line to add soft-wrap
1220 // characters at the end of a line " =\r\n" and add the newly
1221 // processed line(s) to the output (see comment on $crlf class property)
1222 for ($i = 0; $i < $length; $i++)
1223 {
1224 // Grab the next character
1225 $char = substr($line, $i, 1);
1226 $ascii = ord($char);
1227
1228 // Convert spaces and tabs but only if it's the end of the line
1229 if ($i == ($length - 1))
1230 {
1231 $char = ($ascii == '32' OR $ascii == '9') ? $escape.sprintf('%02s', dechex($ascii)) : $char;
1232 }
1233
1234 // encode = signs
1235 if ($ascii == '61')
1236 {
1237 $char = $escape.strtoupper(sprintf('%02s', dechex($ascii))); // =3D
1238 }
1239
1240 // If we're at the character limit, add the line to the output,
1241 // reset our temp variable, and keep on chuggin'
1242 if ((strlen($temp) + strlen($char)) >= $charlim)
1243 {
1244 $output .= $temp.$escape.$this->crlf;
1245 $temp = '';
1246 }
1247
1248 // Add the character to our temporary line
1249 $temp .= $char;
1250 }
1251
1252 // Add our completed line to the output
1253 $output .= $temp.$this->crlf;
1254 }
1255
1256 // get rid of extra CRLF tacked onto the end
1257 $output = substr($output, 0, strlen($this->crlf) * -1);
1258
1259 return $output;
1260 }
1261
1262 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +02001263
Derek Allard2067d1a2008-11-13 22:59:24 +00001264 /**
1265 * Prep Q Encoding
1266 *
1267 * Performs "Q Encoding" on a string for use in email headers. It's related
1268 * but not identical to quoted-printable, so it has its own method
1269 *
1270 * @access public
1271 * @param str
1272 * @param bool // set to TRUE for processing From: headers
1273 * @return str
1274 */
1275 function _prep_q_encoding($str, $from = FALSE)
1276 {
1277 $str = str_replace(array("\r", "\n"), array('', ''), $str);
1278
1279 // Line length must not exceed 76 characters, so we adjust for
1280 // a space, 7 extra characters =??Q??=, and the charset that we will add to each line
1281 $limit = 75 - 7 - strlen($this->charset);
1282
1283 // these special characters must be converted too
1284 $convert = array('_', '=', '?');
1285
1286 if ($from === TRUE)
1287 {
1288 $convert[] = ',';
1289 $convert[] = ';';
1290 }
1291
1292 $output = '';
1293 $temp = '';
1294
1295 for ($i = 0, $length = strlen($str); $i < $length; $i++)
1296 {
1297 // Grab the next character
1298 $char = substr($str, $i, 1);
1299 $ascii = ord($char);
1300
1301 // convert ALL non-printable ASCII characters and our specials
1302 if ($ascii < 32 OR $ascii > 126 OR in_array($char, $convert))
1303 {
1304 $char = '='.dechex($ascii);
1305 }
1306
1307 // handle regular spaces a bit more compactly than =20
1308 if ($ascii == 32)
1309 {
1310 $char = '_';
1311 }
1312
1313 // If we're at the character limit, add the line to the output,
1314 // reset our temp variable, and keep on chuggin'
1315 if ((strlen($temp) + strlen($char)) >= $limit)
1316 {
1317 $output .= $temp.$this->crlf;
1318 $temp = '';
1319 }
1320
1321 // Add the character to our temporary line
1322 $temp .= $char;
1323 }
1324
1325 $str = $output.$temp;
1326
1327 // wrap each line with the shebang, charset, and transfer encoding
1328 // the preceding space on successive lines is required for header "folding"
1329 $str = trim(preg_replace('/^(.*)$/m', ' =?'.$this->charset.'?Q?$1?=', $str));
1330
1331 return $str;
1332 }
1333
1334 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +02001335
Derek Allard2067d1a2008-11-13 22:59:24 +00001336 /**
1337 * Send Email
1338 *
1339 * @access public
1340 * @return bool
1341 */
1342 function send()
1343 {
1344 if ($this->_replyto_flag == FALSE)
1345 {
1346 $this->reply_to($this->_headers['From']);
1347 }
1348
1349 if (( ! isset($this->_recipients) AND ! isset($this->_headers['To'])) AND
1350 ( ! isset($this->_bcc_array) AND ! isset($this->_headers['Bcc'])) AND
1351 ( ! isset($this->_headers['Cc'])))
1352 {
1353 $this->_set_error_message('email_no_recipients');
1354 return FALSE;
1355 }
1356
1357 $this->_build_headers();
1358
1359 if ($this->bcc_batch_mode AND count($this->_bcc_array) > 0)
1360 {
1361 if (count($this->_bcc_array) > $this->bcc_batch_size)
1362 return $this->batch_bcc_send();
1363 }
1364
1365 $this->_build_message();
1366
1367 if ( ! $this->_spool_email())
1368 {
1369 return FALSE;
1370 }
1371 else
1372 {
1373 return TRUE;
1374 }
1375 }
Barry Mienydd671972010-10-04 16:33:58 +02001376
Derek Allard2067d1a2008-11-13 22:59:24 +00001377 // --------------------------------------------------------------------
1378
1379 /**
1380 * Batch Bcc Send. Sends groups of BCCs in batches
1381 *
1382 * @access public
1383 * @return bool
1384 */
1385 function batch_bcc_send()
1386 {
1387 $float = $this->bcc_batch_size -1;
1388
1389 $set = "";
1390
1391 $chunk = array();
1392
1393 for ($i = 0; $i < count($this->_bcc_array); $i++)
1394 {
1395 if (isset($this->_bcc_array[$i]))
1396 {
1397 $set .= ", ".$this->_bcc_array[$i];
1398 }
1399
1400 if ($i == $float)
1401 {
1402 $chunk[] = substr($set, 1);
1403 $float = $float + $this->bcc_batch_size;
1404 $set = "";
1405 }
1406
1407 if ($i == count($this->_bcc_array)-1)
1408 {
1409 $chunk[] = substr($set, 1);
1410 }
1411 }
1412
1413 for ($i = 0; $i < count($chunk); $i++)
1414 {
1415 unset($this->_headers['Bcc']);
1416 unset($bcc);
1417
1418 $bcc = $this->_str_to_array($chunk[$i]);
1419 $bcc = $this->clean_email($bcc);
1420
1421 if ($this->protocol != 'smtp')
1422 {
1423 $this->_set_header('Bcc', implode(", ", $bcc));
1424 }
1425 else
1426 {
1427 $this->_bcc_array = $bcc;
1428 }
1429
1430 $this->_build_message();
1431 $this->_spool_email();
1432 }
1433 }
Barry Mienydd671972010-10-04 16:33:58 +02001434
Derek Allard2067d1a2008-11-13 22:59:24 +00001435 // --------------------------------------------------------------------
1436
1437 /**
1438 * Unwrap special elements
1439 *
1440 * @access private
1441 * @return void
1442 */
1443 function _unwrap_specials()
1444 {
1445 $this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_remove_nl_callback'), $this->_finalbody);
1446 }
Barry Mienydd671972010-10-04 16:33:58 +02001447
Derek Allard2067d1a2008-11-13 22:59:24 +00001448 // --------------------------------------------------------------------
1449
1450 /**
1451 * Strip line-breaks via callback
1452 *
1453 * @access private
1454 * @return string
1455 */
1456 function _remove_nl_callback($matches)
1457 {
1458 if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE)
1459 {
1460 $matches[1] = str_replace(array("\r\n", "\r", "\n"), '', $matches[1]);
1461 }
1462
1463 return $matches[1];
1464 }
Barry Mienydd671972010-10-04 16:33:58 +02001465
Derek Allard2067d1a2008-11-13 22:59:24 +00001466 // --------------------------------------------------------------------
1467
1468 /**
1469 * Spool mail to the mail server
1470 *
1471 * @access private
1472 * @return bool
1473 */
1474 function _spool_email()
1475 {
1476 $this->_unwrap_specials();
1477
1478 switch ($this->_get_protocol())
1479 {
1480 case 'mail' :
1481
1482 if ( ! $this->_send_with_mail())
1483 {
1484 $this->_set_error_message('email_send_failure_phpmail');
1485 return FALSE;
1486 }
1487 break;
1488 case 'sendmail' :
1489
1490 if ( ! $this->_send_with_sendmail())
1491 {
1492 $this->_set_error_message('email_send_failure_sendmail');
1493 return FALSE;
1494 }
1495 break;
1496 case 'smtp' :
1497
1498 if ( ! $this->_send_with_smtp())
1499 {
1500 $this->_set_error_message('email_send_failure_smtp');
1501 return FALSE;
1502 }
1503 break;
1504
1505 }
1506
1507 $this->_set_error_message('email_sent', $this->_get_protocol());
1508 return TRUE;
1509 }
Barry Mienydd671972010-10-04 16:33:58 +02001510
Derek Allard2067d1a2008-11-13 22:59:24 +00001511 // --------------------------------------------------------------------
1512
1513 /**
1514 * Send using mail()
1515 *
1516 * @access private
1517 * @return bool
1518 */
1519 function _send_with_mail()
1520 {
1521 if ($this->_safe_mode == TRUE)
1522 {
1523 if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str))
1524 {
1525 return FALSE;
1526 }
1527 else
1528 {
1529 return TRUE;
1530 }
1531 }
1532 else
1533 {
1534 // most documentation of sendmail using the "-f" flag lacks a space after it, however
1535 // we've encountered servers that seem to require it to be in place.
Eric Barnes6113f542010-12-29 13:36:12 -05001536
Derek Allard2067d1a2008-11-13 22:59:24 +00001537 if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$this->clean_email($this->_headers['From'])))
1538 {
1539 return FALSE;
1540 }
1541 else
1542 {
1543 return TRUE;
1544 }
1545 }
1546 }
Barry Mienydd671972010-10-04 16:33:58 +02001547
Derek Allard2067d1a2008-11-13 22:59:24 +00001548 // --------------------------------------------------------------------
1549
1550 /**
1551 * Send using Sendmail
1552 *
1553 * @access private
1554 * @return bool
1555 */
1556 function _send_with_sendmail()
1557 {
1558 $fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w');
1559
Derek Jones4cefaa42009-04-29 19:13:56 +00001560 if ($fp === FALSE OR $fp === NULL)
1561 {
1562 // server probably has popen disabled, so nothing we can do to get a verbose error.
1563 return FALSE;
1564 }
Derek Jones71141ce2010-03-02 16:41:20 -06001565
Derek Jonesc630bcf2008-11-17 21:09:45 +00001566 fputs($fp, $this->_header_str);
1567 fputs($fp, $this->_finalbody);
1568
Barry Mienydd671972010-10-04 16:33:58 +02001569 $status = pclose($fp);
Eric Barnes6113f542010-12-29 13:36:12 -05001570
Derek Jonesc630bcf2008-11-17 21:09:45 +00001571 if (version_compare(PHP_VERSION, '4.2.3') == -1)
1572 {
1573 $status = $status >> 8 & 0xFF;
Barry Mienydd671972010-10-04 16:33:58 +02001574 }
Derek Jones71141ce2010-03-02 16:41:20 -06001575
Derek Jones604873f2008-11-18 15:57:24 +00001576 if ($status != 0)
Derek Allard2067d1a2008-11-13 22:59:24 +00001577 {
Derek Jones604873f2008-11-18 15:57:24 +00001578 $this->_set_error_message('email_exit_status', $status);
Derek Allard2067d1a2008-11-13 22:59:24 +00001579 $this->_set_error_message('email_no_socket');
1580 return FALSE;
1581 }
1582
Derek Allard2067d1a2008-11-13 22:59:24 +00001583 return TRUE;
1584 }
Barry Mienydd671972010-10-04 16:33:58 +02001585
Derek Allard2067d1a2008-11-13 22:59:24 +00001586 // --------------------------------------------------------------------
1587
1588 /**
1589 * Send using SMTP
1590 *
1591 * @access private
1592 * @return bool
1593 */
1594 function _send_with_smtp()
1595 {
1596 if ($this->smtp_host == '')
1597 {
1598 $this->_set_error_message('email_no_hostname');
1599 return FALSE;
1600 }
1601
1602 $this->_smtp_connect();
1603 $this->_smtp_authenticate();
1604
1605 $this->_send_command('from', $this->clean_email($this->_headers['From']));
1606
1607 foreach($this->_recipients as $val)
1608 {
1609 $this->_send_command('to', $val);
1610 }
1611
1612 if (count($this->_cc_array) > 0)
1613 {
1614 foreach($this->_cc_array as $val)
1615 {
1616 if ($val != "")
1617 {
1618 $this->_send_command('to', $val);
1619 }
1620 }
1621 }
1622
1623 if (count($this->_bcc_array) > 0)
1624 {
1625 foreach($this->_bcc_array as $val)
1626 {
1627 if ($val != "")
1628 {
1629 $this->_send_command('to', $val);
1630 }
1631 }
1632 }
1633
1634 $this->_send_command('data');
1635
1636 // perform dot transformation on any lines that begin with a dot
1637 $this->_send_data($this->_header_str . preg_replace('/^\./m', '..$1', $this->_finalbody));
1638
1639 $this->_send_data('.');
1640
1641 $reply = $this->_get_smtp_data();
1642
1643 $this->_set_error_message($reply);
1644
1645 if (strncmp($reply, '250', 3) != 0)
1646 {
1647 $this->_set_error_message('email_smtp_error', $reply);
1648 return FALSE;
1649 }
1650
1651 $this->_send_command('quit');
1652 return TRUE;
1653 }
Barry Mienydd671972010-10-04 16:33:58 +02001654
Derek Allard2067d1a2008-11-13 22:59:24 +00001655 // --------------------------------------------------------------------
1656
1657 /**
1658 * SMTP Connect
1659 *
1660 * @access private
1661 * @param string
1662 * @return string
1663 */
1664 function _smtp_connect()
1665 {
1666 $this->_smtp_connect = fsockopen($this->smtp_host,
1667 $this->smtp_port,
1668 $errno,
1669 $errstr,
1670 $this->smtp_timeout);
1671
1672 if( ! is_resource($this->_smtp_connect))
1673 {
1674 $this->_set_error_message('email_smtp_error', $errno." ".$errstr);
1675 return FALSE;
1676 }
1677
1678 $this->_set_error_message($this->_get_smtp_data());
1679 return $this->_send_command('hello');
1680 }
Barry Mienydd671972010-10-04 16:33:58 +02001681
Derek Allard2067d1a2008-11-13 22:59:24 +00001682 // --------------------------------------------------------------------
1683
1684 /**
1685 * Send SMTP command
1686 *
1687 * @access private
1688 * @param string
1689 * @param string
1690 * @return string
1691 */
1692 function _send_command($cmd, $data = '')
1693 {
1694 switch ($cmd)
1695 {
1696 case 'hello' :
1697
1698 if ($this->_smtp_auth OR $this->_get_encoding() == '8bit')
1699 $this->_send_data('EHLO '.$this->_get_hostname());
1700 else
1701 $this->_send_data('HELO '.$this->_get_hostname());
1702
1703 $resp = 250;
1704 break;
1705 case 'from' :
1706
1707 $this->_send_data('MAIL FROM:<'.$data.'>');
1708
1709 $resp = 250;
1710 break;
1711 case 'to' :
1712
1713 $this->_send_data('RCPT TO:<'.$data.'>');
1714
1715 $resp = 250;
1716 break;
1717 case 'data' :
1718
1719 $this->_send_data('DATA');
1720
1721 $resp = 354;
1722 break;
1723 case 'quit' :
1724
1725 $this->_send_data('QUIT');
1726
1727 $resp = 221;
1728 break;
1729 }
1730
1731 $reply = $this->_get_smtp_data();
1732
1733 $this->_debug_msg[] = "<pre>".$cmd.": ".$reply."</pre>";
1734
1735 if (substr($reply, 0, 3) != $resp)
1736 {
1737 $this->_set_error_message('email_smtp_error', $reply);
1738 return FALSE;
1739 }
1740
1741 if ($cmd == 'quit')
1742 {
1743 fclose($this->_smtp_connect);
1744 }
1745
1746 return TRUE;
1747 }
Barry Mienydd671972010-10-04 16:33:58 +02001748
Derek Allard2067d1a2008-11-13 22:59:24 +00001749 // --------------------------------------------------------------------
1750
1751 /**
1752 * SMTP Authenticate
1753 *
1754 * @access private
1755 * @return bool
1756 */
1757 function _smtp_authenticate()
1758 {
1759 if ( ! $this->_smtp_auth)
1760 {
1761 return TRUE;
1762 }
1763
1764 if ($this->smtp_user == "" AND $this->smtp_pass == "")
1765 {
1766 $this->_set_error_message('email_no_smtp_unpw');
1767 return FALSE;
1768 }
1769
1770 $this->_send_data('AUTH LOGIN');
1771
1772 $reply = $this->_get_smtp_data();
1773
1774 if (strncmp($reply, '334', 3) != 0)
1775 {
1776 $this->_set_error_message('email_failed_smtp_login', $reply);
1777 return FALSE;
1778 }
1779
1780 $this->_send_data(base64_encode($this->smtp_user));
1781
1782 $reply = $this->_get_smtp_data();
1783
1784 if (strncmp($reply, '334', 3) != 0)
1785 {
1786 $this->_set_error_message('email_smtp_auth_un', $reply);
1787 return FALSE;
1788 }
1789
1790 $this->_send_data(base64_encode($this->smtp_pass));
1791
1792 $reply = $this->_get_smtp_data();
1793
1794 if (strncmp($reply, '235', 3) != 0)
1795 {
1796 $this->_set_error_message('email_smtp_auth_pw', $reply);
1797 return FALSE;
1798 }
1799
1800 return TRUE;
1801 }
Barry Mienydd671972010-10-04 16:33:58 +02001802
Derek Allard2067d1a2008-11-13 22:59:24 +00001803 // --------------------------------------------------------------------
1804
1805 /**
1806 * Send SMTP data
1807 *
1808 * @access private
1809 * @return bool
1810 */
1811 function _send_data($data)
1812 {
1813 if ( ! fwrite($this->_smtp_connect, $data . $this->newline))
1814 {
1815 $this->_set_error_message('email_smtp_data_failure', $data);
1816 return FALSE;
1817 }
1818 else
1819 {
1820 return TRUE;
1821 }
1822 }
Barry Mienydd671972010-10-04 16:33:58 +02001823
Derek Allard2067d1a2008-11-13 22:59:24 +00001824 // --------------------------------------------------------------------
1825
1826 /**
1827 * Get SMTP data
1828 *
1829 * @access private
1830 * @return string
1831 */
1832 function _get_smtp_data()
1833 {
1834 $data = "";
1835
1836 while ($str = fgets($this->_smtp_connect, 512))
1837 {
1838 $data .= $str;
1839
1840 if (substr($str, 3, 1) == " ")
1841 {
1842 break;
1843 }
1844 }
1845
1846 return $data;
1847 }
Barry Mienydd671972010-10-04 16:33:58 +02001848
Derek Allard2067d1a2008-11-13 22:59:24 +00001849 // --------------------------------------------------------------------
1850
1851 /**
1852 * Get Hostname
1853 *
1854 * @access private
1855 * @return string
1856 */
1857 function _get_hostname()
1858 {
1859 return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
1860 }
Barry Mienydd671972010-10-04 16:33:58 +02001861
Derek Allard2067d1a2008-11-13 22:59:24 +00001862 // --------------------------------------------------------------------
1863
1864 /**
1865 * Get IP
1866 *
1867 * @access private
1868 * @return string
1869 */
1870 function _get_ip()
1871 {
1872 if ($this->_IP !== FALSE)
1873 {
1874 return $this->_IP;
1875 }
1876
1877 $cip = (isset($_SERVER['HTTP_CLIENT_IP']) AND $_SERVER['HTTP_CLIENT_IP'] != "") ? $_SERVER['HTTP_CLIENT_IP'] : FALSE;
1878 $rip = (isset($_SERVER['REMOTE_ADDR']) AND $_SERVER['REMOTE_ADDR'] != "") ? $_SERVER['REMOTE_ADDR'] : FALSE;
1879 $fip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND $_SERVER['HTTP_X_FORWARDED_FOR'] != "") ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE;
1880
Barry Mienydd671972010-10-04 16:33:58 +02001881 if ($cip && $rip) $this->_IP = $cip;
Derek Allard2067d1a2008-11-13 22:59:24 +00001882 elseif ($rip) $this->_IP = $rip;
1883 elseif ($cip) $this->_IP = $cip;
1884 elseif ($fip) $this->_IP = $fip;
1885
Robin Sowell76b369e2010-03-19 11:15:28 -04001886 if (strpos($this->_IP, ',') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +00001887 {
1888 $x = explode(',', $this->_IP);
1889 $this->_IP = end($x);
1890 }
1891
1892 if ( ! preg_match( "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $this->_IP))
1893 {
1894 $this->_IP = '0.0.0.0';
1895 }
1896
1897 unset($cip);
1898 unset($rip);
1899 unset($fip);
1900
1901 return $this->_IP;
1902 }
Barry Mienydd671972010-10-04 16:33:58 +02001903
Derek Allard2067d1a2008-11-13 22:59:24 +00001904 // --------------------------------------------------------------------
1905
1906 /**
1907 * Get Debug Message
1908 *
1909 * @access public
1910 * @return string
1911 */
1912 function print_debugger()
1913 {
1914 $msg = '';
1915
1916 if (count($this->_debug_msg) > 0)
1917 {
1918 foreach ($this->_debug_msg as $val)
1919 {
1920 $msg .= $val;
1921 }
1922 }
1923
1924 $msg .= "<pre>".$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';
1925 return $msg;
1926 }
Barry Mienydd671972010-10-04 16:33:58 +02001927
Derek Allard2067d1a2008-11-13 22:59:24 +00001928 // --------------------------------------------------------------------
1929
1930 /**
1931 * Set Message
1932 *
1933 * @access private
1934 * @param string
1935 * @return string
1936 */
1937 function _set_error_message($msg, $val = '')
1938 {
1939 $CI =& get_instance();
1940 $CI->lang->load('email');
1941
1942 if (FALSE === ($line = $CI->lang->line($msg)))
1943 {
1944 $this->_debug_msg[] = str_replace('%s', $val, $msg)."<br />";
1945 }
1946 else
1947 {
1948 $this->_debug_msg[] = str_replace('%s', $val, $line)."<br />";
1949 }
1950 }
Barry Mienydd671972010-10-04 16:33:58 +02001951
Derek Allard2067d1a2008-11-13 22:59:24 +00001952 // --------------------------------------------------------------------
1953
1954 /**
1955 * Mime Types
1956 *
1957 * @access private
1958 * @param string
1959 * @return string
1960 */
1961 function _mime_types($ext = "")
1962 {
1963 $mimes = array( 'hqx' => 'application/mac-binhex40',
1964 'cpt' => 'application/mac-compactpro',
1965 'doc' => 'application/msword',
1966 'bin' => 'application/macbinary',
1967 'dms' => 'application/octet-stream',
1968 'lha' => 'application/octet-stream',
1969 'lzh' => 'application/octet-stream',
1970 'exe' => 'application/octet-stream',
1971 'class' => 'application/octet-stream',
1972 'psd' => 'application/octet-stream',
1973 'so' => 'application/octet-stream',
1974 'sea' => 'application/octet-stream',
1975 'dll' => 'application/octet-stream',
1976 'oda' => 'application/oda',
1977 'pdf' => 'application/pdf',
1978 'ai' => 'application/postscript',
1979 'eps' => 'application/postscript',
1980 'ps' => 'application/postscript',
1981 'smi' => 'application/smil',
1982 'smil' => 'application/smil',
1983 'mif' => 'application/vnd.mif',
1984 'xls' => 'application/vnd.ms-excel',
1985 'ppt' => 'application/vnd.ms-powerpoint',
1986 'wbxml' => 'application/vnd.wap.wbxml',
1987 'wmlc' => 'application/vnd.wap.wmlc',
1988 'dcr' => 'application/x-director',
1989 'dir' => 'application/x-director',
1990 'dxr' => 'application/x-director',
1991 'dvi' => 'application/x-dvi',
1992 'gtar' => 'application/x-gtar',
1993 'php' => 'application/x-httpd-php',
1994 'php4' => 'application/x-httpd-php',
1995 'php3' => 'application/x-httpd-php',
1996 'phtml' => 'application/x-httpd-php',
1997 'phps' => 'application/x-httpd-php-source',
1998 'js' => 'application/x-javascript',
1999 'swf' => 'application/x-shockwave-flash',
2000 'sit' => 'application/x-stuffit',
2001 'tar' => 'application/x-tar',
2002 'tgz' => 'application/x-tar',
2003 'xhtml' => 'application/xhtml+xml',
2004 'xht' => 'application/xhtml+xml',
2005 'zip' => 'application/zip',
2006 'mid' => 'audio/midi',
2007 'midi' => 'audio/midi',
2008 'mpga' => 'audio/mpeg',
2009 'mp2' => 'audio/mpeg',
2010 'mp3' => 'audio/mpeg',
2011 'aif' => 'audio/x-aiff',
2012 'aiff' => 'audio/x-aiff',
2013 'aifc' => 'audio/x-aiff',
2014 'ram' => 'audio/x-pn-realaudio',
2015 'rm' => 'audio/x-pn-realaudio',
2016 'rpm' => 'audio/x-pn-realaudio-plugin',
2017 'ra' => 'audio/x-realaudio',
2018 'rv' => 'video/vnd.rn-realvideo',
2019 'wav' => 'audio/x-wav',
2020 'bmp' => 'image/bmp',
2021 'gif' => 'image/gif',
2022 'jpeg' => 'image/jpeg',
2023 'jpg' => 'image/jpeg',
2024 'jpe' => 'image/jpeg',
2025 'png' => 'image/png',
2026 'tiff' => 'image/tiff',
2027 'tif' => 'image/tiff',
2028 'css' => 'text/css',
2029 'html' => 'text/html',
2030 'htm' => 'text/html',
2031 'shtml' => 'text/html',
2032 'txt' => 'text/plain',
2033 'text' => 'text/plain',
2034 'log' => 'text/plain',
2035 'rtx' => 'text/richtext',
2036 'rtf' => 'text/rtf',
2037 'xml' => 'text/xml',
2038 'xsl' => 'text/xml',
2039 'mpeg' => 'video/mpeg',
2040 'mpg' => 'video/mpeg',
2041 'mpe' => 'video/mpeg',
2042 'qt' => 'video/quicktime',
2043 'mov' => 'video/quicktime',
2044 'avi' => 'video/x-msvideo',
2045 'movie' => 'video/x-sgi-movie',
2046 'doc' => 'application/msword',
2047 'word' => 'application/msword',
2048 'xl' => 'application/excel',
2049 'eml' => 'message/rfc822'
2050 );
2051
2052 return ( ! isset($mimes[strtolower($ext)])) ? "application/x-unknown-content-type" : $mimes[strtolower($ext)];
2053 }
2054
2055}
2056// END CI_Email class
2057
2058/* End of file Email.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +00002059/* Location: ./system/libraries/Email.php */