GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (423)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

system/libraries/Email.php (14 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 51 and the first side effect is on line 38.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2015, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	http://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
40
/**
41
 * CodeIgniter Email Class
42
 *
43
 * Permits email to be sent using Mail, Sendmail, or SMTP.
44
 *
45
 * @package		CodeIgniter
46
 * @subpackage	Libraries
47
 * @category	Libraries
48
 * @author		EllisLab Dev Team
49
 * @link		http://codeigniter.com/user_guide/libraries/email.html
50
 */
51
class CI_Email {
52
53
	/**
54
	 * Used as the User-Agent and X-Mailer headers' value.
55
	 *
56
	 * @var	string
57
	 */
58
	public $useragent	= 'CodeIgniter';
59
60
	/**
61
	 * Path to the Sendmail binary.
62
	 *
63
	 * @var	string
64
	 */
65
	public $mailpath	= '/usr/sbin/sendmail';	// Sendmail path
66
67
	/**
68
	 * Which method to use for sending e-mails.
69
	 *
70
	 * @var	string	'mail', 'sendmail' or 'smtp'
71
	 */
72
	public $protocol	= 'mail';		// mail/sendmail/smtp
73
74
	/**
75
	 * STMP Server host
76
	 *
77
	 * @var	string
78
	 */
79
	public $smtp_host	= '';
80
81
	/**
82
	 * SMTP Username
83
	 *
84
	 * @var	string
85
	 */
86
	public $smtp_user	= '';
87
88
	/**
89
	 * SMTP Password
90
	 *
91
	 * @var	string
92
	 */
93
	public $smtp_pass	= '';
94
95
	/**
96
	 * SMTP Server port
97
	 *
98
	 * @var	int
99
	 */
100
	public $smtp_port	= 25;
101
102
	/**
103
	 * SMTP connection timeout in seconds
104
	 *
105
	 * @var	int
106
	 */
107
	public $smtp_timeout	= 5;
108
109
	/**
110
	 * SMTP persistent connection
111
	 *
112
	 * @var	bool
113
	 */
114
	public $smtp_keepalive	= FALSE;
115
116
	/**
117
	 * SMTP Encryption
118
	 *
119
	 * @var	string	empty, 'tls' or 'ssl'
120
	 */
121
	public $smtp_crypto	= '';
122
123
	/**
124
	 * Whether to apply word-wrapping to the message body.
125
	 *
126
	 * @var	bool
127
	 */
128
	public $wordwrap	= TRUE;
129
130
	/**
131
	 * Number of characters to wrap at.
132
	 *
133
	 * @see	CI_Email::$wordwrap
134
	 * @var	int
135
	 */
136
	public $wrapchars	= 76;
137
138
	/**
139
	 * Message format.
140
	 *
141
	 * @var	string	'text' or 'html'
142
	 */
143
	public $mailtype	= 'text';
144
145
	/**
146
	 * Character set (default: utf-8)
147
	 *
148
	 * @var	string
149
	 */
150
	public $charset		= 'utf-8';
151
152
	/**
153
	 * Multipart message
154
	 *
155
	 * @var	string	'mixed' (in the body) or 'related' (separate)
156
	 */
157
	public $multipart	= 'mixed';		// "mixed" (in the body) or "related" (separate)
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
158
159
	/**
160
	 * Alternative message (for HTML messages only)
161
	 *
162
	 * @var	string
163
	 */
164
	public $alt_message	= '';
165
166
	/**
167
	 * Whether to validate e-mail addresses.
168
	 *
169
	 * @var	bool
170
	 */
171
	public $validate	= FALSE;
172
173
	/**
174
	 * X-Priority header value.
175
	 *
176
	 * @var	int	1-5
177
	 */
178
	public $priority	= 3;			// Default priority (1 - 5)
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
179
180
	/**
181
	 * Newline character sequence.
182
	 * Use "\r\n" to comply with RFC 822.
183
	 *
184
	 * @link	http://www.ietf.org/rfc/rfc822.txt
185
	 * @var	string	"\r\n" or "\n"
186
	 */
187
	public $newline		= "\n";			// Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
188
189
	/**
190
	 * CRLF character sequence
191
	 *
192
	 * RFC 2045 specifies that for 'quoted-printable' encoding,
193
	 * "\r\n" must be used. However, it appears that some servers
194
	 * (even on the receiving end) don't handle it properly and
195
	 * switching to "\n", while improper, is the only solution
196
	 * that seems to work for all environments.
197
	 *
198
	 * @link	http://www.ietf.org/rfc/rfc822.txt
199
	 * @var	string
200
	 */
201
	public $crlf		= "\n";
202
203
	/**
204
	 * Whether to use Delivery Status Notification.
205
	 *
206
	 * @var	bool
207
	 */
208
	public $dsn		= FALSE;
209
210
	/**
211
	 * Whether to send multipart alternatives.
212
	 * Yahoo! doesn't seem to like these.
213
	 *
214
	 * @var	bool
215
	 */
216
	public $send_multipart	= TRUE;
217
218
	/**
219
	 * Whether to send messages to BCC recipients in batches.
220
	 *
221
	 * @var	bool
222
	 */
223
	public $bcc_batch_mode	= FALSE;
224
225
	/**
226
	 * BCC Batch max number size.
227
	 *
228
	 * @see	CI_Email::$bcc_batch_mode
229
	 * @var	int
230
	 */
231
	public $bcc_batch_size	= 200;
232
233
	// --------------------------------------------------------------------
234
235
	/**
236
	 * Whether PHP is running in safe mode. Initialized by the class constructor.
237
	 *
238
	 * @var	bool
239
	 */
240
	protected $_safe_mode		= FALSE;
241
242
	/**
243
	 * Subject header
244
	 *
245
	 * @var	string
246
	 */
247
	protected $_subject		= '';
248
249
	/**
250
	 * Message body
251
	 *
252
	 * @var	string
253
	 */
254
	protected $_body		= '';
255
256
	/**
257
	 * Final message body to be sent.
258
	 *
259
	 * @var	string
260
	 */
261
	protected $_finalbody		= '';
262
263
	/**
264
	 * multipart/alternative boundary
265
	 *
266
	 * @var	string
267
	 */
268
	protected $_alt_boundary	= '';
269
270
	/**
271
	 * Attachment boundary
272
	 *
273
	 * @var	string
274
	 */
275
	protected $_atc_boundary	= '';
276
277
	/**
278
	 * Final headers to send
279
	 *
280
	 * @var	string
281
	 */
282
	protected $_header_str		= '';
283
284
	/**
285
	 * SMTP Connection socket placeholder
286
	 *
287
	 * @var	resource
288
	 */
289
	protected $_smtp_connect	= '';
290
291
	/**
292
	 * Mail encoding
293
	 *
294
	 * @var	string	'8bit' or '7bit'
295
	 */
296
	protected $_encoding		= '8bit';
297
298
	/**
299
	 * Whether to perform SMTP authentication
300
	 *
301
	 * @var	bool
302
	 */
303
	protected $_smtp_auth		= FALSE;
304
305
	/**
306
	 * Whether to send a Reply-To header
307
	 *
308
	 * @var	bool
309
	 */
310
	protected $_replyto_flag	= FALSE;
311
312
	/**
313
	 * Debug messages
314
	 *
315
	 * @see	CI_Email::print_debugger()
316
	 * @var	string
317
	 */
318
	protected $_debug_msg		= array();
319
320
	/**
321
	 * Recipients
322
	 *
323
	 * @var	string[]
324
	 */
325
	protected $_recipients		= array();
326
327
	/**
328
	 * CC Recipients
329
	 *
330
	 * @var	string[]
331
	 */
332
	protected $_cc_array		= array();
333
334
	/**
335
	 * BCC Recipients
336
	 *
337
	 * @var	string[]
338
	 */
339
	protected $_bcc_array		= array();
340
341
	/**
342
	 * Message headers
343
	 *
344
	 * @var	string[]
345
	 */
346
	protected $_headers		= array();
347
348
	/**
349
	 * Attachment data
350
	 *
351
	 * @var	array
352
	 */
353
	protected $_attachments		= array();
354
355
	/**
356
	 * Valid $protocol values
357
	 *
358
	 * @see	CI_Email::$protocol
359
	 * @var	string[]
360
	 */
361
	protected $_protocols		= array('mail', 'sendmail', 'smtp');
362
363
	/**
364
	 * Base charsets
365
	 *
366
	 * Character sets valid for 7-bit encoding,
367
	 * excluding language suffix.
368
	 *
369
	 * @var	string[]
370
	 */
371
	protected $_base_charsets	= array('us-ascii', 'iso-2022-');
372
373
	/**
374
	 * Bit depths
375
	 *
376
	 * Valid mail encodings
377
	 *
378
	 * @see	CI_Email::$_encoding
379
	 * @var	string[]
380
	 */
381
	protected $_bit_depths		= array('7bit', '8bit');
382
383
	/**
384
	 * $priority translations
385
	 *
386
	 * Actual values to send with the X-Priority header
387
	 *
388
	 * @var	string[]
389
	 */
390
	protected $_priorities = array(
391
		1 => '1 (Highest)',
392
		2 => '2 (High)',
393
		3 => '3 (Normal)',
394
		4 => '4 (Low)',
395
		5 => '5 (Lowest)'
396
	);
397
398
	// --------------------------------------------------------------------
399
400
	/**
401
	 * Constructor - Sets Email Preferences
402
	 *
403
	 * The constructor can be passed an array of config values
404
	 *
405
	 * @param	array	$config = array()
406
	 * @return	void
407
	 */
408
	public function __construct(array $config = array())
409
	{
410
		$this->charset = config_item('charset');
411
412
		if (count($config) > 0)
413
		{
414
			$this->initialize($config);
415
		}
416
		else
417
		{
418
			$this->_smtp_auth = ! ($this->smtp_user === '' && $this->smtp_pass === '');
419
		}
420
421
		$this->_safe_mode = ( ! is_php('5.4') && ini_get('safe_mode'));
422
		$this->charset = strtoupper($this->charset);
423
424
		log_message('info', 'Email Class Initialized');
425
	}
426
427
	// --------------------------------------------------------------------
428
429
	/**
430
	 * Destructor - Releases Resources
431
	 *
432
	 * @return	void
433
	 */
434
	public function __destruct()
435
	{
436
		if (is_resource($this->_smtp_connect))
437
		{
438
			$this->_send_command('quit');
439
		}
440
	}
441
442
	// --------------------------------------------------------------------
443
444
	/**
445
	 * Initialize preferences
446
	 *
447
	 * @param	array
448
	 * @return	CI_Email
449
	 */
450
	public function initialize($config = array())
451
	{
452
		foreach ($config as $key => $val)
453
		{
454
			if (isset($this->$key))
455
			{
456
				$method = 'set_'.$key;
457
458
				if (method_exists($this, $method))
459
				{
460
					$this->$method($val);
461
				}
462
				else
463
				{
464
					$this->$key = $val;
465
				}
466
			}
467
		}
468
		$this->clear();
469
470
		$this->_smtp_auth = ! ($this->smtp_user === '' && $this->smtp_pass === '');
471
472
		return $this;
473
	}
474
475
	// --------------------------------------------------------------------
476
477
	/**
478
	 * Initialize the Email Data
479
	 *
480
	 * @param	bool
481
	 * @return	CI_Email
482
	 */
483
	public function clear($clear_attachments = FALSE)
484
	{
485
		$this->_subject		= '';
486
		$this->_body		= '';
487
		$this->_finalbody	= '';
488
		$this->_header_str	= '';
489
		$this->_replyto_flag	= FALSE;
490
		$this->_recipients	= array();
491
		$this->_cc_array	= array();
492
		$this->_bcc_array	= array();
493
		$this->_headers		= array();
494
		$this->_debug_msg	= array();
495
496
		$this->set_header('User-Agent', $this->useragent);
497
		$this->set_header('Date', $this->_set_date());
498
499
		if ($clear_attachments !== FALSE)
500
		{
501
			$this->_attachments = array();
502
		}
503
504
		return $this;
505
	}
506
507
	// --------------------------------------------------------------------
508
509
	/**
510
	 * Set FROM
511
	 *
512
	 * @param	string	$from
513
	 * @param	string	$name
514
	 * @param	string	$return_path = NULL	Return-Path
515
	 * @return	CI_Email
516
	 */
517
	public function from($from, $name = '', $return_path = NULL)
518
	{
519
		if (preg_match('/\<(.*)\>/', $from, $match))
520
		{
521
			$from = $match[1];
522
		}
523
524
		if ($this->validate)
525
		{
526
			$this->validate_email($this->_str_to_array($from));
527
			if ($return_path)
528
			{
529
				$this->validate_email($this->_str_to_array($return_path));
530
			}
531
		}
532
533
		// prepare the display name
534
		if ($name !== '')
535
		{
536
			// only use Q encoding if there are characters that would require it
537
			if ( ! preg_match('/[\200-\377]/', $name))
538
			{
539
				// add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes
540
				$name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"';
541
			}
542
			else
543
			{
544
				$name = $this->_prep_q_encoding($name);
545
			}
546
		}
547
548
		$this->set_header('From', $name.' <'.$from.'>');
549
550
		isset($return_path) OR $return_path = $from;
551
		$this->set_header('Return-Path', '<'.$return_path.'>');
552
553
		return $this;
554
	}
555
556
	// --------------------------------------------------------------------
557
558
	/**
559
	 * Set Reply-to
560
	 *
561
	 * @param	string
562
	 * @param	string
563
	 * @return	CI_Email
564
	 */
565
	public function reply_to($replyto, $name = '')
566
	{
567
		if (preg_match('/\<(.*)\>/', $replyto, $match))
568
		{
569
			$replyto = $match[1];
570
		}
571
572
		if ($this->validate)
573
		{
574
			$this->validate_email($this->_str_to_array($replyto));
575
		}
576
577
		if ($name === '')
578
		{
579
			$name = $replyto;
580
		}
581
582
		if (strpos($name, '"') !== 0)
583
		{
584
			$name = '"'.$name.'"';
585
		}
586
587
		$this->set_header('Reply-To', $name.' <'.$replyto.'>');
588
		$this->_replyto_flag = TRUE;
589
590
		return $this;
591
	}
592
593
	// --------------------------------------------------------------------
594
595
	/**
596
	 * Set Recipients
597
	 *
598
	 * @param	string
599
	 * @return	CI_Email
600
	 */
601 View Code Duplication
	public function to($to)
602
	{
603
		$to = $this->_str_to_array($to);
604
		$to = $this->clean_email($to);
605
606
		if ($this->validate)
607
		{
608
			$this->validate_email($to);
609
		}
610
611
		if ($this->_get_protocol() !== 'mail')
612
		{
613
			$this->set_header('To', implode(', ', $to));
614
		}
615
616
		$this->_recipients = $to;
617
618
		return $this;
619
	}
620
621
	// --------------------------------------------------------------------
622
623
	/**
624
	 * Set CC
625
	 *
626
	 * @param	string
627
	 * @return	CI_Email
628
	 */
629 View Code Duplication
	public function cc($cc)
630
	{
631
		$cc = $this->clean_email($this->_str_to_array($cc));
632
633
		if ($this->validate)
634
		{
635
			$this->validate_email($cc);
636
		}
637
638
		$this->set_header('Cc', implode(', ', $cc));
639
640
		if ($this->_get_protocol() === 'smtp')
641
		{
642
			$this->_cc_array = $cc;
643
		}
644
645
		return $this;
646
	}
647
648
	// --------------------------------------------------------------------
649
650
	/**
651
	 * Set BCC
652
	 *
653
	 * @param	string
654
	 * @param	string
655
	 * @return	CI_Email
656
	 */
657
	public function bcc($bcc, $limit = '')
658
	{
659
		if ($limit !== '' && is_numeric($limit))
660
		{
661
			$this->bcc_batch_mode = TRUE;
662
			$this->bcc_batch_size = $limit;
663
		}
664
665
		$bcc = $this->clean_email($this->_str_to_array($bcc));
666
667
		if ($this->validate)
668
		{
669
			$this->validate_email($bcc);
670
		}
671
672
		if ($this->_get_protocol() === 'smtp' OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
673
		{
674
			$this->_bcc_array = $bcc;
675
		}
676
		else
677
		{
678
			$this->set_header('Bcc', implode(', ', $bcc));
679
		}
680
681
		return $this;
682
	}
683
684
	// --------------------------------------------------------------------
685
686
	/**
687
	 * Set Email Subject
688
	 *
689
	 * @param	string
690
	 * @return	CI_Email
691
	 */
692
	public function subject($subject)
693
	{
694
		$subject = $this->_prep_q_encoding($subject);
695
		$this->set_header('Subject', $subject);
696
		return $this;
697
	}
698
699
	// --------------------------------------------------------------------
700
701
	/**
702
	 * Set Body
703
	 *
704
	 * @param	string
705
	 * @return	CI_Email
706
	 */
707
	public function message($body)
708
	{
709
		$this->_body = rtrim(str_replace("\r", '', $body));
710
711
		/* strip slashes only if magic quotes is ON
712
		   if we do it with magic quotes OFF, it strips real, user-inputted chars.
713
714
		   NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
715
			 it will probably not exist in future versions at all.
716
		*/
717
		if ( ! is_php('5.4') && get_magic_quotes_gpc())
718
		{
719
			$this->_body = stripslashes($this->_body);
720
		}
721
722
		return $this;
723
	}
724
725
	// --------------------------------------------------------------------
726
727
	/**
728
	 * Assign file attachments
729
	 *
730
	 * @param	string	$file	Can be local path, URL or buffered content
731
	 * @param	string	$disposition = 'attachment'
732
	 * @param	string	$newname = NULL
733
	 * @param	string	$mime = ''
734
	 * @return	CI_Email
735
	 */
736
	public function attach($file, $disposition = '', $newname = NULL, $mime = '')
737
	{
738
		if ($mime === '')
739
		{
740
			if (strpos($file, '://') === FALSE && ! file_exists($file))
741
			{
742
				$this->_set_error_message('lang:email_attachment_missing', $file);
743
				return FALSE;
744
			}
745
746
			if ( ! $fp = @fopen($file, 'rb'))
747
			{
748
				$this->_set_error_message('lang:email_attachment_unreadable', $file);
749
				return FALSE;
750
			}
751
752
			$file_content = stream_get_contents($fp);
753
			$mime = $this->_mime_types(pathinfo($file, PATHINFO_EXTENSION));
754
			fclose($fp);
755
		}
756
		else
757
		{
758
			$file_content =& $file; // buffered file
759
		}
760
761
		$this->_attachments[] = array(
762
			'name'		=> array($file, $newname),
763
			'disposition'	=> empty($disposition) ? 'attachment' : $disposition,  // Can also be 'inline'  Not sure if it matters
764
			'type'		=> $mime,
765
			'content'	=> chunk_split(base64_encode($file_content))
766
		);
767
768
		return $this;
769
	}
770
771
	// --------------------------------------------------------------------
772
773
	/**
774
	 * Set and return attachment Content-ID
775
	 *
776
	 * Useful for attached inline pictures
777
	 *
778
	 * @param	string	$filename
779
	 * @return	string
780
	 */
781
	public function attachment_cid($filename)
782
	{
783
		if ($this->multipart !== 'related')
784
		{
785
			$this->multipart = 'related'; // Thunderbird need this for inline images
786
		}
787
788
		for ($i = 0, $c = count($this->_attachments); $i < $c; $i++)
789
		{
790
			if ($this->_attachments[$i]['name'][0] === $filename)
791
			{
792
				$this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]).'@');
793
				return $this->_attachments[$i]['cid'];
794
			}
795
		}
796
797
		return FALSE;
798
	}
799
800
	// --------------------------------------------------------------------
801
802
	/**
803
	 * Add a Header Item
804
	 *
805
	 * @param	string
806
	 * @param	string
807
	 * @return	CI_Email
808
	 */
809
	public function set_header($header, $value)
810
	{
811
		$this->_headers[$header] = str_replace(array("\n", "\r"), '', $value);
812
		return $this;
813
	}
814
815
	// --------------------------------------------------------------------
816
817
	/**
818
	 * Convert a String to an Array
819
	 *
820
	 * @param	string
821
	 * @return	array
822
	 */
823
	protected function _str_to_array($email)
824
	{
825
		if ( ! is_array($email))
826
		{
827
			return (strpos($email, ',') !== FALSE)
828
				? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY)
829
				: (array) trim($email);
830
		}
831
832
		return $email;
833
	}
834
835
	// --------------------------------------------------------------------
836
837
	/**
838
	 * Set Multipart Value
839
	 *
840
	 * @param	string
841
	 * @return	CI_Email
842
	 */
843
	public function set_alt_message($str)
844
	{
845
		$this->alt_message = (string) $str;
846
		return $this;
847
	}
848
849
	// --------------------------------------------------------------------
850
851
	/**
852
	 * Set Mailtype
853
	 *
854
	 * @param	string
855
	 * @return	CI_Email
856
	 */
857
	public function set_mailtype($type = 'text')
858
	{
859
		$this->mailtype = ($type === 'html') ? 'html' : 'text';
860
		return $this;
861
	}
862
863
	// --------------------------------------------------------------------
864
865
	/**
866
	 * Set Wordwrap
867
	 *
868
	 * @param	bool
869
	 * @return	CI_Email
870
	 */
871
	public function set_wordwrap($wordwrap = TRUE)
872
	{
873
		$this->wordwrap = (bool) $wordwrap;
874
		return $this;
875
	}
876
877
	// --------------------------------------------------------------------
878
879
	/**
880
	 * Set Protocol
881
	 *
882
	 * @param	string
883
	 * @return	CI_Email
884
	 */
885
	public function set_protocol($protocol = 'mail')
886
	{
887
		$this->protocol = in_array($protocol, $this->_protocols, TRUE) ? strtolower($protocol) : 'mail';
888
		return $this;
889
	}
890
891
	// --------------------------------------------------------------------
892
893
	/**
894
	 * Set Priority
895
	 *
896
	 * @param	int
897
	 * @return	CI_Email
898
	 */
899
	public function set_priority($n = 3)
900
	{
901
		$this->priority = preg_match('/^[1-5]$/', $n) ? (int) $n : 3;
902
		return $this;
903
	}
904
905
	// --------------------------------------------------------------------
906
907
	/**
908
	 * Set Newline Character
909
	 *
910
	 * @param	string
911
	 * @return	CI_Email
912
	 */
913
	public function set_newline($newline = "\n")
914
	{
915
		$this->newline = in_array($newline, array("\n", "\r\n", "\r")) ? $newline : "\n";
916
		return $this;
917
	}
918
919
	// --------------------------------------------------------------------
920
921
	/**
922
	 * Set CRLF
923
	 *
924
	 * @param	string
925
	 * @return	CI_Email
926
	 */
927
	public function set_crlf($crlf = "\n")
928
	{
929
		$this->crlf = ($crlf !== "\n" && $crlf !== "\r\n" && $crlf !== "\r") ? "\n" : $crlf;
930
		return $this;
931
	}
932
933
	// --------------------------------------------------------------------
934
935
	/**
936
	 * Set Message Boundary
937
	 *
938
	 * @return	void
939
	 */
940
	protected function _set_boundaries()
941
	{
942
		$this->_alt_boundary = 'B_ALT_'.uniqid(''); // multipart/alternative
943
		$this->_atc_boundary = 'B_ATC_'.uniqid(''); // attachment boundary
944
	}
945
946
	// --------------------------------------------------------------------
947
948
	/**
949
	 * Get the Message ID
950
	 *
951
	 * @return	string
952
	 */
953
	protected function _get_message_id()
954
	{
955
		$from = str_replace(array('>', '<'), '', $this->_headers['Return-Path']);
956
		return '<'.uniqid('').strstr($from, '@').'>';
957
	}
958
959
	// --------------------------------------------------------------------
960
961
	/**
962
	 * Get Mail Protocol
963
	 *
964
	 * @param	bool
965
	 * @return	mixed
966
	 */
967
	protected function _get_protocol($return = TRUE)
968
	{
969
		$this->protocol = strtolower($this->protocol);
970
		in_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail';
971
972
		if ($return === TRUE)
973
		{
974
			return $this->protocol;
975
		}
976
	}
977
978
	// --------------------------------------------------------------------
979
980
	/**
981
	 * Get Mail Encoding
982
	 *
983
	 * @param	bool
984
	 * @return	string
985
	 */
986
	protected function _get_encoding($return = TRUE)
987
	{
988
		in_array($this->_encoding, $this->_bit_depths) OR $this->_encoding = '8bit';
989
990
		foreach ($this->_base_charsets as $charset)
991
		{
992
			if (strpos($charset, $this->charset) === 0)
993
			{
994
				$this->_encoding = '7bit';
995
			}
996
		}
997
998
		if ($return === TRUE)
999
		{
1000
			return $this->_encoding;
1001
		}
1002
	}
1003
1004
	// --------------------------------------------------------------------
1005
1006
	/**
1007
	 * Get content type (text/html/attachment)
1008
	 *
1009
	 * @return	string
1010
	 */
1011
	protected function _get_content_type()
1012
	{
1013
		if ($this->mailtype === 'html')
1014
		{
1015
			return (count($this->_attachments) === 0) ? 'html' : 'html-attach';
1016
		}
1017
		elseif	($this->mailtype === 'text' && count($this->_attachments) > 0)
1018
		{
1019
			return 'plain-attach';
1020
		}
1021
		else
1022
		{
1023
			return 'plain';
1024
		}
1025
	}
1026
1027
	// --------------------------------------------------------------------
1028
1029
	/**
1030
	 * Set RFC 822 Date
1031
	 *
1032
	 * @return	string
1033
	 */
1034
	protected function _set_date()
1035
	{
1036
		$timezone = date('Z');
1037
		$operator = ($timezone[0] === '-') ? '-' : '+';
1038
		$timezone = abs($timezone);
1039
		$timezone = floor($timezone/3600) * 100 + ($timezone % 3600) / 60;
1040
1041
		return sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone);
1042
	}
1043
1044
	// --------------------------------------------------------------------
1045
1046
	/**
1047
	 * Mime message
1048
	 *
1049
	 * @return	string
1050
	 */
1051
	protected function _get_mime_message()
1052
	{
1053
		return 'This is a multi-part message in MIME format.'.$this->newline.'Your email application may not support this format.';
1054
	}
1055
1056
	// --------------------------------------------------------------------
1057
1058
	/**
1059
	 * Validate Email Address
1060
	 *
1061
	 * @param	string
1062
	 * @return	bool
1063
	 */
1064
	public function validate_email($email)
1065
	{
1066
		if ( ! is_array($email))
1067
		{
1068
			$this->_set_error_message('lang:email_must_be_array');
1069
			return FALSE;
1070
		}
1071
1072
		foreach ($email as $val)
1073
		{
1074
			if ( ! $this->valid_email($val))
1075
			{
1076
				$this->_set_error_message('lang:email_invalid_address', $val);
1077
				return FALSE;
1078
			}
1079
		}
1080
1081
		return TRUE;
1082
	}
1083
1084
	// --------------------------------------------------------------------
1085
1086
	/**
1087
	 * Email Validation
1088
	 *
1089
	 * @param	string
1090
	 * @return	bool
1091
	 */
1092 View Code Duplication
	public function valid_email($email)
1093
	{
1094
		if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@'))
1095
		{
1096
			$email = substr($email, 0, ++$atpos).idn_to_ascii(substr($email, $atpos));
1097
		}
1098
1099
		return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
1100
	}
1101
1102
	// --------------------------------------------------------------------
1103
1104
	/**
1105
	 * Clean Extended Email Address: Joe Smith <[email protected]>
1106
	 *
1107
	 * @param	string
1108
	 * @return	string
1109
	 */
1110
	public function clean_email($email)
1111
	{
1112
		if ( ! is_array($email))
1113
		{
1114
			return preg_match('/\<(.*)\>/', $email, $match) ? $match[1] : $email;
1115
		}
1116
1117
		$clean_email = array();
1118
1119
		foreach ($email as $addy)
1120
		{
1121
			$clean_email[] = preg_match('/\<(.*)\>/', $addy, $match) ? $match[1] : $addy;
1122
		}
1123
1124
		return $clean_email;
1125
	}
1126
1127
	// --------------------------------------------------------------------
1128
1129
	/**
1130
	 * Build alternative plain text message
1131
	 *
1132
	 * Provides the raw message for use in plain-text headers of
1133
	 * HTML-formatted emails.
1134
	 * If the user hasn't specified his own alternative message
1135
	 * it creates one by stripping the HTML
1136
	 *
1137
	 * @return	string
1138
	 */
1139
	protected function _get_alt_message()
1140
	{
1141
		if ( ! empty($this->alt_message))
1142
		{
1143
			return ($this->wordwrap)
1144
				? $this->word_wrap($this->alt_message, 76)
1145
				: $this->alt_message;
1146
		}
1147
1148
		$body = preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match) ? $match[1] : $this->_body;
1149
		$body = str_replace("\t", '', preg_replace('#<!--(.*)--\>#', '', trim(strip_tags($body))));
1150
1151
		for ($i = 20; $i >= 3; $i--)
1152
		{
1153
			$body = str_replace(str_repeat("\n", $i), "\n\n", $body);
1154
		}
1155
1156
		// Reduce multiple spaces
1157
		$body = preg_replace('| +|', ' ', $body);
1158
1159
		return ($this->wordwrap)
1160
			? $this->word_wrap($body, 76)
1161
			: $body;
1162
	}
1163
1164
	// --------------------------------------------------------------------
1165
1166
	/**
1167
	 * Word Wrap
1168
	 *
1169
	 * @param	string
1170
	 * @param	int	line-length limit
1171
	 * @return	string
1172
	 */
1173
	public function word_wrap($str, $charlim = NULL)
1174
	{
1175
		// Set the character limit, if not already present
1176
		if (empty($charlim))
1177
		{
1178
			$charlim = empty($this->wrapchars) ? 76 : $this->wrapchars;
1179
		}
1180
1181
		// Standardize newlines
1182 View Code Duplication
		if (strpos($str, "\r") !== FALSE)
1183
		{
1184
			$str = str_replace(array("\r\n", "\r"), "\n", $str);
1185
		}
1186
1187
		// Reduce multiple spaces at end of line
1188
		$str = preg_replace('| +\n|', "\n", $str);
1189
1190
		// If the current word is surrounded by {unwrap} tags we'll
1191
		// strip the entire chunk and replace it with a marker.
1192
		$unwrap = array();
1193 View Code Duplication
		if (preg_match_all('|\{unwrap\}(.+?)\{/unwrap\}|s', $str, $matches))
1194
		{
1195
			for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
1196
			{
1197
				$unwrap[] = $matches[1][$i];
1198
				$str = str_replace($matches[0][$i], '{{unwrapped'.$i.'}}', $str);
1199
			}
1200
		}
1201
1202
		// Use PHP's native function to do the initial wordwrap.
1203
		// We set the cut flag to FALSE so that any individual words that are
1204
		// too long get left alone. In the next step we'll deal with them.
1205
		$str = wordwrap($str, $charlim, "\n", FALSE);
1206
1207
		// Split the string into individual lines of text and cycle through them
1208
		$output = '';
1209
		foreach (explode("\n", $str) as $line)
1210
		{
1211
			// Is the line within the allowed character count?
1212
			// If so we'll join it to the output and continue
1213
			if (mb_strlen($line) <= $charlim)
1214
			{
1215
				$output .= $line.$this->newline;
1216
				continue;
1217
			}
1218
1219
			$temp = '';
1220 View Code Duplication
			do
1221
			{
1222
				// If the over-length word is a URL we won't wrap it
1223
				if (preg_match('!\[url.+\]|://|www\.!', $line))
1224
				{
1225
					break;
1226
				}
1227
1228
				// Trim the word down
1229
				$temp .= mb_substr($line, 0, $charlim - 1);
1230
				$line = mb_substr($line, $charlim - 1);
1231
			}
1232
			while (mb_strlen($line) > $charlim);
1233
1234
			// If $temp contains data it means we had to split up an over-length
1235
			// word into smaller chunks so we'll add it back to our current line
1236
			if ($temp !== '')
1237
			{
1238
				$output .= $temp.$this->newline;
1239
			}
1240
1241
			$output .= $line.$this->newline;
1242
		}
1243
1244
		// Put our markers back
1245 View Code Duplication
		if (count($unwrap) > 0)
1246
		{
1247
			foreach ($unwrap as $key => $val)
1248
			{
1249
				$output = str_replace('{{unwrapped'.$key.'}}', $val, $output);
1250
			}
1251
		}
1252
1253
		return $output;
1254
	}
1255
1256
	// --------------------------------------------------------------------
1257
1258
	/**
1259
	 * Build final headers
1260
	 *
1261
	 * @return	string
1262
	 */
1263
	protected function _build_headers()
1264
	{
1265
		$this->set_header('X-Sender', $this->clean_email($this->_headers['From']));
1266
		$this->set_header('X-Mailer', $this->useragent);
1267
		$this->set_header('X-Priority', $this->_priorities[$this->priority]);
1268
		$this->set_header('Message-ID', $this->_get_message_id());
1269
		$this->set_header('Mime-Version', '1.0');
1270
	}
1271
1272
	// --------------------------------------------------------------------
1273
1274
	/**
1275
	 * Write Headers as a string
1276
	 *
1277
	 * @return	void
1278
	 */
1279
	protected function _write_headers()
1280
	{
1281
		if ($this->protocol === 'mail')
1282
		{
1283
			if (isset($this->_headers['Subject']))
1284
			{
1285
				$this->_subject = $this->_headers['Subject'];
1286
				unset($this->_headers['Subject']);
1287
			}
1288
		}
1289
1290
		reset($this->_headers);
1291
		$this->_header_str = '';
1292
1293
		foreach ($this->_headers as $key => $val)
1294
		{
1295
			$val = trim($val);
1296
1297
			if ($val !== '')
1298
			{
1299
				$this->_header_str .= $key.': '.$val.$this->newline;
1300
			}
1301
		}
1302
1303
		if ($this->_get_protocol() === 'mail')
1304
		{
1305
			$this->_header_str = rtrim($this->_header_str);
1306
		}
1307
	}
1308
1309
	// --------------------------------------------------------------------
1310
1311
	/**
1312
	 * Build Final Body and attachments
1313
	 *
1314
	 * @return	bool
1315
	 */
1316
	protected function _build_message()
1317
	{
1318
		if ($this->wordwrap === TRUE && $this->mailtype !== 'html')
1319
		{
1320
			$this->_body = $this->word_wrap($this->_body);
1321
		}
1322
1323
		$this->_set_boundaries();
1324
		$this->_write_headers();
1325
1326
		$hdr = ($this->_get_protocol() === 'mail') ? $this->newline : '';
1327
		$body = '';
1328
1329
		switch ($this->_get_content_type())
1330
		{
1331
			case 'plain' :
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1332
1333
				$hdr .= 'Content-Type: text/plain; charset='.$this->charset.$this->newline
1334
					.'Content-Transfer-Encoding: '.$this->_get_encoding();
1335
1336 View Code Duplication
				if ($this->_get_protocol() === 'mail')
1337
				{
1338
					$this->_header_str .= $hdr;
1339
					$this->_finalbody = $this->_body;
1340
				}
1341
				else
1342
				{
1343
					$this->_finalbody = $hdr.$this->newline.$this->newline.$this->_body;
1344
				}
1345
1346
				return;
1347
1348
			case 'html' :
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1349
1350
				if ($this->send_multipart === FALSE)
1351
				{
1352
					$hdr .= 'Content-Type: text/html; charset='.$this->charset.$this->newline
1353
						.'Content-Transfer-Encoding: quoted-printable';
1354
				}
1355
				else
1356
				{
1357
					$hdr .= 'Content-Type: multipart/alternative; boundary="'.$this->_alt_boundary.'"';
1358
1359
					$body .= $this->_get_mime_message().$this->newline.$this->newline
1360
						.'--'.$this->_alt_boundary.$this->newline
1361
1362
						.'Content-Type: text/plain; charset='.$this->charset.$this->newline
1363
						.'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline
1364
						.$this->_get_alt_message().$this->newline.$this->newline.'--'.$this->_alt_boundary.$this->newline
1365
1366
						.'Content-Type: text/html; charset='.$this->charset.$this->newline
1367
						.'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline;
1368
				}
1369
1370
				$this->_finalbody = $body.$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline;
1371
1372 View Code Duplication
				if ($this->_get_protocol() === 'mail')
1373
				{
1374
					$this->_header_str .= $hdr;
1375
				}
1376
				else
1377
				{
1378
					$this->_finalbody = $hdr.$this->newline.$this->newline.$this->_finalbody;
1379
				}
1380
1381
				if ($this->send_multipart !== FALSE)
1382
				{
1383
					$this->_finalbody .= '--'.$this->_alt_boundary.'--';
1384
				}
1385
1386
				return;
1387
1388
			case 'plain-attach' :
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1389
1390
				$hdr .= 'Content-Type: multipart/'.$this->multipart.'; boundary="'.$this->_atc_boundary.'"';
1391
1392
				if ($this->_get_protocol() === 'mail')
1393
				{
1394
					$this->_header_str .= $hdr;
1395
				}
1396
1397
				$body .= $this->_get_mime_message().$this->newline
1398
					.$this->newline
1399
					.'--'.$this->_atc_boundary.$this->newline
1400
					.'Content-Type: text/plain; charset='.$this->charset.$this->newline
1401
					.'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline
1402
					.$this->newline
1403
					.$this->_body.$this->newline.$this->newline;
1404
1405
			break;
1406
			case 'html-attach' :
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1407
1408
				$hdr .= 'Content-Type: multipart/'.$this->multipart.'; boundary="'.$this->_atc_boundary.'"';
1409
1410
				if ($this->_get_protocol() === 'mail')
1411
				{
1412
					$this->_header_str .= $hdr;
1413
				}
1414
1415
				$body .= $this->_get_mime_message().$this->newline.$this->newline
1416
					.'--'.$this->_atc_boundary.$this->newline
1417
1418
					.'Content-Type: multipart/alternative; boundary="'.$this->_alt_boundary.'"'.$this->newline.$this->newline
1419
					.'--'.$this->_alt_boundary.$this->newline
1420
1421
					.'Content-Type: text/plain; charset='.$this->charset.$this->newline
1422
					.'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline
1423
					.$this->_get_alt_message().$this->newline.$this->newline.'--'.$this->_alt_boundary.$this->newline
1424
1425
					.'Content-Type: text/html; charset='.$this->charset.$this->newline
1426
					.'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline
1427
1428
					.$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline
1429
					.'--'.$this->_alt_boundary.'--'.$this->newline.$this->newline;
1430
1431
			break;
1432
		}
1433
1434
		$attachment = array();
1435
		for ($i = 0, $c = count($this->_attachments), $z = 0; $i < $c; $i++)
1436
		{
1437
			$filename = $this->_attachments[$i]['name'][0];
1438
			$basename = ($this->_attachments[$i]['name'][1] === NULL)
1439
				? basename($filename) : $this->_attachments[$i]['name'][1];
1440
1441
			$attachment[$z++] = '--'.$this->_atc_boundary.$this->newline
1442
				.'Content-type: '.$this->_attachments[$i]['type'].'; '
1443
				.'name="'.$basename.'"'.$this->newline
1444
				.'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline
1445
				.'Content-Transfer-Encoding: base64'.$this->newline
1446
				.(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline);
1447
1448
			$attachment[$z++] = $this->_attachments[$i]['content'];
1449
		}
1450
1451
		$body .= implode($this->newline, $attachment).$this->newline.'--'.$this->_atc_boundary.'--';
1452
		$this->_finalbody = ($this->_get_protocol() === 'mail')
1453
			? $body
1454
			: $hdr.$this->newline.$this->newline.$body;
1455
1456
		return TRUE;
1457
	}
1458
1459
	// --------------------------------------------------------------------
1460
1461
	/**
1462
	 * Prep Quoted Printable
1463
	 *
1464
	 * Prepares string for Quoted-Printable Content-Transfer-Encoding
1465
	 * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
1466
	 *
1467
	 * @param	string
1468
	 * @return	string
1469
	 */
1470
	protected function _prep_quoted_printable($str)
1471
	{
1472
		// ASCII code numbers for "safe" characters that can always be
1473
		// used literally, without encoding, as described in RFC 2049.
1474
		// http://www.ietf.org/rfc/rfc2049.txt
1475
		static $ascii_safe_chars = array(
1476
			// ' (  )   +   ,   -   .   /   :   =   ?
1477
			39, 40, 41, 43, 44, 45, 46, 47, 58, 61, 63,
1478
			// numbers
1479
			48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
1480
			// upper-case letters
1481
			65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
1482
			// lower-case letters
1483
			97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122
1484
		);
1485
1486
		// We are intentionally wrapping so mail servers will encode characters
1487
		// properly and MUAs will behave, so {unwrap} must go!
1488
		$str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
1489
1490
		// RFC 2045 specifies CRLF as "\r\n".
1491
		// However, many developers choose to override that and violate
1492
		// the RFC rules due to (apparently) a bug in MS Exchange,
1493
		// which only works with "\n".
1494
		if ($this->crlf === "\r\n")
1495
		{
1496
			if (is_php('5.3'))
1497
			{
1498
				return quoted_printable_encode($str);
1499
			}
1500
			elseif (function_exists('imap_8bit'))
1501
			{
1502
				return imap_8bit($str);
1503
			}
1504
		}
1505
1506
		// Reduce multiple spaces & remove nulls
1507
		$str = preg_replace(array('| +|', '/\x00+/'), array(' ', ''), $str);
1508
1509
		// Standardize newlines
1510 View Code Duplication
		if (strpos($str, "\r") !== FALSE)
1511
		{
1512
			$str = str_replace(array("\r\n", "\r"), "\n", $str);
1513
		}
1514
1515
		$escape = '=';
1516
		$output = '';
1517
1518
		foreach (explode("\n", $str) as $line)
1519
		{
1520
			$length = strlen($line);
1521
			$temp = '';
1522
1523
			// Loop through each character in the line to add soft-wrap
1524
			// characters at the end of a line " =\r\n" and add the newly
1525
			// processed line(s) to the output (see comment on $crlf class property)
1526
			for ($i = 0; $i < $length; $i++)
1527
			{
1528
				// Grab the next character
1529
				$char = $line[$i];
1530
				$ascii = ord($char);
1531
1532
				// Convert spaces and tabs but only if it's the end of the line
1533
				if ($ascii === 32 OR $ascii === 9)
1534
				{
1535
					if ($i === ($length - 1))
1536
					{
1537
						$char = $escape.sprintf('%02s', dechex($ascii));
1538
					}
1539
				}
1540
				// DO NOT move this below the $ascii_safe_chars line!
1541
				//
1542
				// = (equals) signs are allowed by RFC2049, but must be encoded
1543
				// as they are the encoding delimiter!
1544
				elseif ($ascii === 61)
1545
				{
1546
					$char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));  // =3D
1547
				}
1548
				elseif ( ! in_array($ascii, $ascii_safe_chars, TRUE))
1549
				{
1550
					$char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));
1551
				}
1552
1553
				// If we're at the character limit, add the line to the output,
1554
				// reset our temp variable, and keep on chuggin'
1555
				if ((strlen($temp) + strlen($char)) >= 76)
1556
				{
1557
					$output .= $temp.$escape.$this->crlf;
1558
					$temp = '';
1559
				}
1560
1561
				// Add the character to our temporary line
1562
				$temp .= $char;
1563
			}
1564
1565
			// Add our completed line to the output
1566
			$output .= $temp.$this->crlf;
1567
		}
1568
1569
		// get rid of extra CRLF tacked onto the end
1570
		return substr($output, 0, strlen($this->crlf) * -1);
1571
	}
1572
1573
	// --------------------------------------------------------------------
1574
1575
	/**
1576
	 * Prep Q Encoding
1577
	 *
1578
	 * Performs "Q Encoding" on a string for use in email headers.
1579
	 * It's related but not identical to quoted-printable, so it has its
1580
	 * own method.
1581
	 *
1582
	 * @param	string
1583
	 * @return	string
1584
	 */
1585
	protected function _prep_q_encoding($str)
1586
	{
1587
		$str = str_replace(array("\r", "\n"), '', $str);
1588
1589
		if ($this->charset === 'UTF-8')
1590
		{
1591
			// Note: We used to have mb_encode_mimeheader() as the first choice
1592
			//       here, but it turned out to be buggy and unreliable. DO NOT
1593
			//       re-add it! -- Narf
1594
			if (ICONV_ENABLED === TRUE)
1595
			{
1596
				$output = @iconv_mime_encode('', $str,
1597
					array(
1598
						'scheme' => 'Q',
1599
						'line-length' => 76,
1600
						'input-charset' => $this->charset,
1601
						'output-charset' => $this->charset,
1602
						'line-break-chars' => $this->crlf
1603
					)
1604
				);
1605
1606
				// There are reports that iconv_mime_encode() might fail and return FALSE
1607
				if ($output !== FALSE)
1608
				{
1609
					// iconv_mime_encode() will always put a header field name.
1610
					// We've passed it an empty one, but it still prepends our
1611
					// encoded string with ': ', so we need to strip it.
1612
					return substr($output, 2);
1613
				}
1614
1615
				$chars = iconv_strlen($str, 'UTF-8');
1616
			}
1617
			elseif (MB_ENABLED === TRUE)
1618
			{
1619
				$chars = mb_strlen($str, 'UTF-8');
1620
			}
1621
		}
1622
1623
		// We might already have this set for UTF-8
1624
		isset($chars) OR $chars = strlen($str);
1625
1626
		$output = '=?'.$this->charset.'?Q?';
1627
		for ($i = 0, $length = strlen($output); $i < $chars; $i++)
1628
		{
1629
			$chr = ($this->charset === 'UTF-8' && ICONV_ENABLED === TRUE)
1630
				? '='.implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2))
1631
				: '='.strtoupper(bin2hex($str[$i]));
1632
1633
			// RFC 2045 sets a limit of 76 characters per line.
1634
			// We'll append ?= to the end of each line though.
1635
			if ($length + ($l = strlen($chr)) > 74)
1636
			{
1637
				$output .= '?='.$this->crlf // EOL
1638
					.' =?'.$this->charset.'?Q?'.$chr; // New line
1639
				$length = 6 + strlen($this->charset) + $l; // Reset the length for the new line
1640
			}
1641
			else
1642
			{
1643
				$output .= $chr;
1644
				$length += $l;
1645
			}
1646
		}
1647
1648
		// End the header
1649
		return $output.'?=';
1650
	}
1651
1652
	// --------------------------------------------------------------------
1653
1654
	/**
1655
	 * Send Email
1656
	 *
1657
	 * @param	bool	$auto_clear = TRUE
1658
	 * @return	bool
1659
	 */
1660
	public function send($auto_clear = TRUE)
1661
	{
1662
		if ( ! isset($this->_headers['From']))
1663
		{
1664
			$this->_set_error_message('lang:email_no_from');
1665
			return FALSE;
1666
		}
1667
1668
		if ($this->_replyto_flag === FALSE)
1669
		{
1670
			$this->reply_to($this->_headers['From']);
1671
		}
1672
1673
		if ( ! isset($this->_recipients) && ! isset($this->_headers['To'])
1674
			&& ! isset($this->_bcc_array) && ! isset($this->_headers['Bcc'])
1675
			&& ! isset($this->_headers['Cc']))
1676
		{
1677
			$this->_set_error_message('lang:email_no_recipients');
1678
			return FALSE;
1679
		}
1680
1681
		$this->_build_headers();
1682
1683
		if ($this->bcc_batch_mode && count($this->_bcc_array) > $this->bcc_batch_size)
1684
		{
1685
			$result = $this->batch_bcc_send();
1686
1687
			if ($result && $auto_clear)
1688
			{
1689
				$this->clear();
1690
			}
1691
1692
			return $result;
1693
		}
1694
1695
		if ($this->_build_message() === FALSE)
1696
		{
1697
			return FALSE;
1698
		}
1699
1700
		$result = $this->_spool_email();
1701
1702
		if ($result && $auto_clear)
1703
		{
1704
			$this->clear();
1705
		}
1706
1707
		return $result;
1708
	}
1709
1710
	// --------------------------------------------------------------------
1711
1712
	/**
1713
	 * Batch Bcc Send. Sends groups of BCCs in batches
1714
	 *
1715
	 * @return	void
1716
	 */
1717
	public function batch_bcc_send()
1718
	{
1719
		$float = $this->bcc_batch_size - 1;
1720
		$set = '';
1721
		$chunk = array();
1722
1723
		for ($i = 0, $c = count($this->_bcc_array); $i < $c; $i++)
1724
		{
1725
			if (isset($this->_bcc_array[$i]))
1726
			{
1727
				$set .= ', '.$this->_bcc_array[$i];
1728
			}
1729
1730
			if ($i === $float)
1731
			{
1732
				$chunk[] = substr($set, 1);
1733
				$float += $this->bcc_batch_size;
1734
				$set = '';
1735
			}
1736
1737
			if ($i === $c-1)
1738
			{
1739
				$chunk[] = substr($set, 1);
1740
			}
1741
		}
1742
1743
		for ($i = 0, $c = count($chunk); $i < $c; $i++)
1744
		{
1745
			unset($this->_headers['Bcc']);
1746
1747
			$bcc = $this->clean_email($this->_str_to_array($chunk[$i]));
1748
1749
			if ($this->protocol !== 'smtp')
1750
			{
1751
				$this->set_header('Bcc', implode(', ', $bcc));
1752
			}
1753
			else
1754
			{
1755
				$this->_bcc_array = $bcc;
1756
			}
1757
1758
			if ($this->_build_message() === FALSE)
1759
			{
1760
				return FALSE;
1761
			}
1762
1763
			$this->_spool_email();
1764
		}
1765
	}
1766
1767
	// --------------------------------------------------------------------
1768
1769
	/**
1770
	 * Unwrap special elements
1771
	 *
1772
	 * @return	void
1773
	 */
1774
	protected function _unwrap_specials()
1775
	{
1776
		$this->_finalbody = preg_replace_callback('/\{unwrap\}(.*?)\{\/unwrap\}/si', array($this, '_remove_nl_callback'), $this->_finalbody);
1777
	}
1778
1779
	// --------------------------------------------------------------------
1780
1781
	/**
1782
	 * Strip line-breaks via callback
1783
	 *
1784
	 * @param	string	$matches
1785
	 * @return	string
1786
	 */
1787
	protected function _remove_nl_callback($matches)
1788
	{
1789
		if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE)
1790
		{
1791
			$matches[1] = str_replace(array("\r\n", "\r", "\n"), '', $matches[1]);
1792
		}
1793
1794
		return $matches[1];
1795
	}
1796
1797
	// --------------------------------------------------------------------
1798
1799
	/**
1800
	 * Spool mail to the mail server
1801
	 *
1802
	 * @return	bool
1803
	 */
1804
	protected function _spool_email()
1805
	{
1806
		$this->_unwrap_specials();
1807
1808
		$method = '_send_with_'.$this->_get_protocol();
1809
		if ( ! $this->$method())
1810
		{
1811
			$this->_set_error_message('lang:email_send_failure_'.($this->_get_protocol() === 'mail' ? 'phpmail' : $this->_get_protocol()));
1812
			return FALSE;
1813
		}
1814
1815
		$this->_set_error_message('lang:email_sent', $this->_get_protocol());
1816
		return TRUE;
1817
	}
1818
1819
	// --------------------------------------------------------------------
1820
1821
	/**
1822
	 * Send using mail()
1823
	 *
1824
	 * @return	bool
1825
	 */
1826
	protected function _send_with_mail()
1827
	{
1828
		if (is_array($this->_recipients))
1829
		{
1830
			$this->_recipients = implode(', ', $this->_recipients);
1831
		}
1832
1833
		if ($this->_safe_mode === TRUE)
1834
		{
1835
			return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str);
1836
		}
1837
		else
1838
		{
1839
			// most documentation of sendmail using the "-f" flag lacks a space after it, however
1840
			// we've encountered servers that seem to require it to be in place.
1841
			return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$this->clean_email($this->_headers['Return-Path']));
1842
		}
1843
	}
1844
1845
	// --------------------------------------------------------------------
1846
1847
	/**
1848
	 * Send using Sendmail
1849
	 *
1850
	 * @return	bool
1851
	 */
1852
	protected function _send_with_sendmail()
1853
	{
1854
		// is popen() enabled?
1855
		if ( ! function_usable('popen')
1856
			OR FALSE === ($fp = @popen(
1857
						$this->mailpath.' -oi -f '.$this->clean_email($this->_headers['From']).' -t'
1858
						, 'w'))
1859
		) // server probably has popen disabled, so nothing we can do to get a verbose error.
1860
		{
1861
			return FALSE;
1862
		}
1863
1864
		fputs($fp, $this->_header_str);
1865
		fputs($fp, $this->_finalbody);
1866
1867
		$status = pclose($fp);
1868
1869
		if ($status !== 0)
1870
		{
1871
			$this->_set_error_message('lang:email_exit_status', $status);
1872
			$this->_set_error_message('lang:email_no_socket');
1873
			return FALSE;
1874
		}
1875
1876
		return TRUE;
1877
	}
1878
1879
	// --------------------------------------------------------------------
1880
1881
	/**
1882
	 * Send using SMTP
1883
	 *
1884
	 * @return	bool
1885
	 */
1886
	protected function _send_with_smtp()
1887
	{
1888
		if ($this->smtp_host === '')
1889
		{
1890
			$this->_set_error_message('lang:email_no_hostname');
1891
			return FALSE;
1892
		}
1893
1894
		if ( ! $this->_smtp_connect() OR ! $this->_smtp_authenticate())
1895
		{
1896
			return FALSE;
1897
		}
1898
1899
		if ( ! $this->_send_command('from', $this->clean_email($this->_headers['From'])))
1900
		{
1901
			return FALSE;
1902
		}
1903
1904
		foreach ($this->_recipients as $val)
1905
		{
1906
			if ( ! $this->_send_command('to', $val))
1907
			{
1908
				return FALSE;
1909
			}
1910
		}
1911
1912 View Code Duplication
		if (count($this->_cc_array) > 0)
1913
		{
1914
			foreach ($this->_cc_array as $val)
1915
			{
1916
				if ($val !== '' && ! $this->_send_command('to', $val))
1917
				{
1918
					return FALSE;
1919
				}
1920
			}
1921
		}
1922
1923 View Code Duplication
		if (count($this->_bcc_array) > 0)
1924
		{
1925
			foreach ($this->_bcc_array as $val)
1926
			{
1927
				if ($val !== '' && ! $this->_send_command('to', $val))
1928
				{
1929
					return FALSE;
1930
				}
1931
			}
1932
		}
1933
1934
		if ( ! $this->_send_command('data'))
1935
		{
1936
			return FALSE;
1937
		}
1938
1939
		// perform dot transformation on any lines that begin with a dot
1940
		$this->_send_data($this->_header_str.preg_replace('/^\./m', '..$1', $this->_finalbody));
1941
1942
		$this->_send_data('.');
1943
1944
		$reply = $this->_get_smtp_data();
1945
1946
		$this->_set_error_message($reply);
1947
1948
		if (strpos($reply, '250') !== 0)
1949
		{
1950
			$this->_set_error_message('lang:email_smtp_error', $reply);
1951
			return FALSE;
1952
		}
1953
1954
		if ($this->smtp_keepalive)
1955
		{
1956
			$this->_send_command('reset');
1957
		}
1958
		else
1959
		{
1960
			$this->_send_command('quit');
1961
		}
1962
1963
		return TRUE;
1964
	}
1965
1966
	// --------------------------------------------------------------------
1967
1968
	/**
1969
	 * SMTP Connect
1970
	 *
1971
	 * @return	string
1972
	 */
1973
	protected function _smtp_connect()
1974
	{
1975
		if (is_resource($this->_smtp_connect))
1976
		{
1977
			return TRUE;
1978
		}
1979
1980
		$ssl = ($this->smtp_crypto === 'ssl') ? 'ssl://' : '';
1981
1982
		$this->_smtp_connect = fsockopen($ssl.$this->smtp_host,
1983
							$this->smtp_port,
1984
							$errno,
1985
							$errstr,
1986
							$this->smtp_timeout);
1987
1988
		if ( ! is_resource($this->_smtp_connect))
1989
		{
1990
			$this->_set_error_message('lang:email_smtp_error', $errno.' '.$errstr);
1991
			return FALSE;
1992
		}
1993
1994
		stream_set_timeout($this->_smtp_connect, $this->smtp_timeout);
1995
		$this->_set_error_message($this->_get_smtp_data());
1996
1997
		if ($this->smtp_crypto === 'tls')
1998
		{
1999
			$this->_send_command('hello');
2000
			$this->_send_command('starttls');
2001
2002
			$crypto = stream_socket_enable_crypto($this->_smtp_connect, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT);
2003
2004
			if ($crypto !== TRUE)
2005
			{
2006
				$this->_set_error_message('lang:email_smtp_error', $this->_get_smtp_data());
2007
				return FALSE;
2008
			}
2009
		}
2010
2011
		return $this->_send_command('hello');
2012
	}
2013
2014
	// --------------------------------------------------------------------
2015
2016
	/**
2017
	 * Send SMTP command
2018
	 *
2019
	 * @param	string
2020
	 * @param	string
2021
	 * @return	string
2022
	 */
2023
	protected function _send_command($cmd, $data = '')
2024
	{
2025
		switch ($cmd)
2026
		{
2027
			case 'hello' :
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2028
2029
						if ($this->_smtp_auth OR $this->_get_encoding() === '8bit')
2030
						{
2031
							$this->_send_data('EHLO '.$this->_get_hostname());
2032
						}
2033
						else
2034
						{
2035
							$this->_send_data('HELO '.$this->_get_hostname());
2036
						}
2037
2038
						$resp = 250;
2039
			break;
2040
			case 'starttls'	:
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2041
2042
						$this->_send_data('STARTTLS');
2043
						$resp = 220;
2044
			break;
2045
			case 'from' :
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2046
2047
						$this->_send_data('MAIL FROM:<'.$data.'>');
2048
						$resp = 250;
2049
			break;
2050
			case 'to' :
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2051
2052
						if ($this->dsn)
2053
						{
2054
							$this->_send_data('RCPT TO:<'.$data.'> NOTIFY=SUCCESS,DELAY,FAILURE ORCPT=rfc822;'.$data);
2055
						}
2056
						else
2057
						{
2058
							$this->_send_data('RCPT TO:<'.$data.'>');
2059
						}
2060
2061
						$resp = 250;
2062
			break;
2063
			case 'data'	:
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2064
2065
						$this->_send_data('DATA');
2066
						$resp = 354;
2067
			break;
2068
			case 'reset':
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2069
2070
						$this->_send_data('RSET');
2071
						$resp = 250;
2072
			break;
2073
			case 'quit'	:
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2074
2075
						$this->_send_data('QUIT');
2076
						$resp = 221;
2077
			break;
2078
		}
2079
2080
		$reply = $this->_get_smtp_data();
2081
2082
		$this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>';
2083
2084
		if ((int) substr($reply, 0, 3) !== $resp)
2085
		{
2086
			$this->_set_error_message('lang:email_smtp_error', $reply);
2087
			return FALSE;
2088
		}
2089
2090
		if ($cmd === 'quit')
2091
		{
2092
			fclose($this->_smtp_connect);
2093
		}
2094
2095
		return TRUE;
2096
	}
2097
2098
	// --------------------------------------------------------------------
2099
2100
	/**
2101
	 * SMTP Authenticate
2102
	 *
2103
	 * @return	bool
2104
	 */
2105
	protected function _smtp_authenticate()
2106
	{
2107
		if ( ! $this->_smtp_auth)
2108
		{
2109
			return TRUE;
2110
		}
2111
2112
		if ($this->smtp_user === '' && $this->smtp_pass === '')
2113
		{
2114
			$this->_set_error_message('lang:email_no_smtp_unpw');
2115
			return FALSE;
2116
		}
2117
2118
		$this->_send_data('AUTH LOGIN');
2119
2120
		$reply = $this->_get_smtp_data();
2121
2122
		if (strpos($reply, '503') === 0)	// Already authenticated
2123
		{
2124
			return TRUE;
2125
		}
2126
		elseif (strpos($reply, '334') !== 0)
2127
		{
2128
			$this->_set_error_message('lang:email_failed_smtp_login', $reply);
2129
			return FALSE;
2130
		}
2131
2132
		$this->_send_data(base64_encode($this->smtp_user));
2133
2134
		$reply = $this->_get_smtp_data();
2135
2136
		if (strpos($reply, '334') !== 0)
2137
		{
2138
			$this->_set_error_message('lang:email_smtp_auth_un', $reply);
2139
			return FALSE;
2140
		}
2141
2142
		$this->_send_data(base64_encode($this->smtp_pass));
2143
2144
		$reply = $this->_get_smtp_data();
2145
2146
		if (strpos($reply, '235') !== 0)
2147
		{
2148
			$this->_set_error_message('lang:email_smtp_auth_pw', $reply);
2149
			return FALSE;
2150
		}
2151
2152
		return TRUE;
2153
	}
2154
2155
	// --------------------------------------------------------------------
2156
2157
	/**
2158
	 * Send SMTP data
2159
	 *
2160
	 * @param	string	$data
2161
	 * @return	bool
2162
	 */
2163
	protected function _send_data($data)
2164
	{
2165
		$data .= $this->newline;
2166 View Code Duplication
		for ($written = $timestamp = 0, $length = strlen($data); $written < $length; $written += $result)
2167
		{
2168
			if (($result = fwrite($this->_smtp_connect, substr($data, $written))) === FALSE)
2169
			{
2170
				break;
2171
			}
2172
			// See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951
2173
			elseif ($result === 0)
2174
			{
2175
				if ($timestamp === 0)
2176
				{
2177
					$timestamp = time();
2178
				}
2179
				elseif ($timestamp < (time() - $this->smtp_timeout))
2180
				{
2181
					$result = FALSE;
2182
					break;
2183
				}
2184
2185
				usleep(250000);
2186
				continue;
2187
			}
2188
			else
2189
			{
2190
				$timestamp = 0;
2191
			}
2192
		}
2193
2194
		if ($result === FALSE)
2195
		{
2196
			$this->_set_error_message('lang:email_smtp_data_failure', $data);
2197
			return FALSE;
2198
		}
2199
2200
		return TRUE;
2201
	}
2202
2203
	// --------------------------------------------------------------------
2204
2205
	/**
2206
	 * Get SMTP data
2207
	 *
2208
	 * @return	string
2209
	 */
2210
	protected function _get_smtp_data()
2211
	{
2212
		$data = '';
2213
2214
		while ($str = fgets($this->_smtp_connect, 512))
2215
		{
2216
			$data .= $str;
2217
2218
			if ($str[3] === ' ')
2219
			{
2220
				break;
2221
			}
2222
		}
2223
2224
		return $data;
2225
	}
2226
2227
	// --------------------------------------------------------------------
2228
2229
	/**
2230
	 * Get Hostname
2231
	 *
2232
	 * There are only two legal types of hostname - either a fully
2233
	 * qualified domain name (eg: "mail.example.com") or an IP literal
2234
	 * (eg: "[1.2.3.4]").
2235
	 *
2236
	 * @link	https://tools.ietf.org/html/rfc5321#section-2.3.5
2237
	 * @link	http://cbl.abuseat.org/namingproblems.html
2238
	 * @return	string
2239
	 */
2240
	protected function _get_hostname()
2241
	{
2242
		if (isset($_SERVER['SERVER_NAME']))
2243
		{
2244
			return $_SERVER['SERVER_NAME'];
2245
		}
2246
2247
		return isset($_SERVER['SERVER_ADDR']) ? '['.$_SERVER['SERVER_ADDR'].']' : '[127.0.0.1]';
2248
	}
2249
2250
	// --------------------------------------------------------------------
2251
2252
	/**
2253
	 * Get Debug Message
2254
	 *
2255
	 * @param	array	$include	List of raw data chunks to include in the output
2256
	 *					Valid options are: 'headers', 'subject', 'body'
2257
	 * @return	string
2258
	 */
2259
	public function print_debugger($include = array('headers', 'subject', 'body'))
2260
	{
2261
		$msg = '';
2262
2263
		if (count($this->_debug_msg) > 0)
2264
		{
2265
			foreach ($this->_debug_msg as $val)
2266
			{
2267
				$msg .= $val;
2268
			}
2269
		}
2270
2271
		// Determine which parts of our raw data needs to be printed
2272
		$raw_data = '';
2273
		is_array($include) OR $include = array($include);
2274
2275
		if (in_array('headers', $include, TRUE))
2276
		{
2277
			$raw_data = htmlspecialchars($this->_header_str)."\n";
2278
		}
2279
2280
		if (in_array('subject', $include, TRUE))
2281
		{
2282
			$raw_data .= htmlspecialchars($this->_subject)."\n";
2283
		}
2284
2285
		if (in_array('body', $include, TRUE))
2286
		{
2287
			$raw_data .= htmlspecialchars($this->_finalbody);
2288
		}
2289
2290
		return $msg.($raw_data === '' ? '' : '<pre>'.$raw_data.'</pre>');
2291
	}
2292
2293
	// --------------------------------------------------------------------
2294
2295
	/**
2296
	 * Set Message
2297
	 *
2298
	 * @param	string	$msg
2299
	 * @param	string	$val = ''
2300
	 * @return	void
2301
	 */
2302
	protected function _set_error_message($msg, $val = '')
2303
	{
2304
		$CI =& get_instance();
2305
		$CI->lang->load('email');
2306
2307
		if (sscanf($msg, 'lang:%s', $line) !== 1 OR FALSE === ($line = $CI->lang->line($line)))
2308
		{
2309
			$this->_debug_msg[] = str_replace('%s', $val, $msg).'<br />';
2310
		}
2311
		else
2312
		{
2313
			$this->_debug_msg[] = str_replace('%s', $val, $line).'<br />';
2314
		}
2315
	}
2316
2317
	// --------------------------------------------------------------------
2318
2319
	/**
2320
	 * Mime Types
2321
	 *
2322
	 * @param	string
2323
	 * @return	string
2324
	 */
2325
	protected function _mime_types($ext = '')
2326
	{
2327
		$ext = strtolower($ext);
2328
2329
		$mimes =& get_mimes();
2330
2331 View Code Duplication
		if (isset($mimes[$ext]))
2332
		{
2333
			return is_array($mimes[$ext])
2334
				? current($mimes[$ext])
2335
				: $mimes[$ext];
2336
		}
2337
2338
		return 'application/x-unknown-content-type';
2339
	}
2340
2341
}
2342