/lenasys/trunk

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