/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
 * File Uploading Class
20
 *
21
 * @package		CodeIgniter
22
 * @subpackage	Libraries
23
 * @category	Uploads
24
 * @author		ExpressionEngine Dev Team
25
 * @link		http://codeigniter.com/user_guide/libraries/file_uploading.html
26
 */
27
class CI_Upload {
28
29
	public $max_size				= 0;
30
	public $max_width				= 0;
31
	public $max_height				= 0;
32
	public $max_filename			= 0;
33
	public $allowed_types			= "";
34
	public $file_temp				= "";
35
	public $file_name				= "";
36
	public $orig_name				= "";
37
	public $file_type				= "";
38
	public $file_size				= "";
39
	public $file_ext				= "";
40
	public $upload_path				= "";
41
	public $overwrite				= FALSE;
42
	public $encrypt_name			= FALSE;
43
	public $is_image				= FALSE;
44
	public $image_width				= '';
45
	public $image_height			= '';
46
	public $image_type				= '';
47
	public $image_size_str			= '';
48
	public $error_msg				= array();
49
	public $mimes					= array();
50
	public $remove_spaces			= TRUE;
51
	public $xss_clean				= FALSE;
52
	public $temp_prefix				= "temp_file_";
53
	public $client_name				= '';
54
55
	protected $_file_name_override	= '';
56
57
	/**
58
	 * Constructor
59
	 *
60
	 * @access	public
61
	 */
62
	public function __construct($props = array())
63
	{
64
		if (count($props) > 0)
65
		{
66
			$this->initialize($props);
67
		}
68
69
		log_message('debug', "Upload Class Initialized");
70
	}
71
72
	// --------------------------------------------------------------------
73
74
	/**
75
	 * Initialize preferences
76
	 *
77
	 * @param	array
78
	 * @return	void
79
	 */
80
	public function initialize($config = array())
81
	{
82
		$defaults = array(
83
							'max_size'			=> 0,
84
							'max_width'			=> 0,
85
							'max_height'		=> 0,
86
							'max_filename'		=> 0,
87
							'allowed_types'		=> "",
88
							'file_temp'			=> "",
89
							'file_name'			=> "",
90
							'orig_name'			=> "",
91
							'file_type'			=> "",
92
							'file_size'			=> "",
93
							'file_ext'			=> "",
94
							'upload_path'		=> "",
95
							'overwrite'			=> FALSE,
96
							'encrypt_name'		=> FALSE,
97
							'is_image'			=> FALSE,
98
							'image_width'		=> '',
99
							'image_height'		=> '',
100
							'image_type'		=> '',
101
							'image_size_str'	=> '',
102
							'error_msg'			=> array(),
103
							'mimes'				=> array(),
104
							'remove_spaces'		=> TRUE,
105
							'xss_clean'			=> FALSE,
106
							'temp_prefix'		=> "temp_file_",
107
							'client_name'		=> ''
108
						);
109
110
111
		foreach ($defaults as $key => $val)
112
		{
113
			if (isset($config[$key]))
114
			{
115
				$method = 'set_'.$key;
116
				if (method_exists($this, $method))
117
				{
118
					$this->$method($config[$key]);
119
				}
120
				else
121
				{
122
					$this->$key = $config[$key];
123
				}
124
			}
125
			else
126
			{
127
				$this->$key = $val;
128
			}
129
		}
130
131
		// if a file_name was provided in the config, use it instead of the user input
132
		// supplied file name for all uploads until initialized again
133
		$this->_file_name_override = $this->file_name;
134
	}
135
136
	// --------------------------------------------------------------------
137
138
	/**
139
	 * Perform the file upload
140
	 *
141
	 * @return	bool
142
	 */
143
	public function do_upload($field = 'userfile')
144
	{
145
146
	// Is $_FILES[$field] set? If not, no reason to continue.
147
		if ( ! isset($_FILES[$field]))
148
		{
149
			$this->set_error('upload_no_file_selected');
150
			return FALSE;
151
		}
152
153
		// Is the upload path valid?
154
		if ( ! $this->validate_upload_path())
155
		{
156
			// errors will already be set by validate_upload_path() so just return FALSE
157
			return FALSE;
158
		}
159
160
		// Was the file able to be uploaded? If not, determine the reason why.
161
		if ( ! is_uploaded_file($_FILES[$field]['tmp_name']))
162
		{
163
			$error = ( ! isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error'];
164
165
			switch($error)
166
			{
167
				case 1:	// UPLOAD_ERR_INI_SIZE
168
					$this->set_error('upload_file_exceeds_limit');
169
					break;
170
				case 2: // UPLOAD_ERR_FORM_SIZE
171
					$this->set_error('upload_file_exceeds_form_limit');
172
					break;
173
				case 3: // UPLOAD_ERR_PARTIAL
174
					$this->set_error('upload_file_partial');
175
					break;
176
				case 4: // UPLOAD_ERR_NO_FILE
177
					$this->set_error('upload_no_file_selected');
178
					break;
179
				case 6: // UPLOAD_ERR_NO_TMP_DIR
180
					$this->set_error('upload_no_temp_directory');
181
					break;
182
				case 7: // UPLOAD_ERR_CANT_WRITE
183
					$this->set_error('upload_unable_to_write_file');
184
					break;
185
				case 8: // UPLOAD_ERR_EXTENSION
186
					$this->set_error('upload_stopped_by_extension');
187
					break;
188
				default :   $this->set_error('upload_no_file_selected');
189
					break;
190
			}
191
192
			return FALSE;
193
		}
194
195
196
		// Set the uploaded data as class variables
197
		$this->file_temp = $_FILES[$field]['tmp_name'];
198
		$this->file_size = $_FILES[$field]['size'];
199
		$this->_file_mime_type($_FILES[$field]);
200
		$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $this->file_type);
201
		$this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
202
		$this->file_name = $this->_prep_filename($_FILES[$field]['name']);
203
		$this->file_ext	 = $this->get_extension($this->file_name);
204
		$this->client_name = $this->file_name;
205
206
		// Is the file type allowed to be uploaded?
207
		if ( ! $this->is_allowed_filetype())
208
		{
209
			$this->set_error('upload_invalid_filetype');
210
			return FALSE;
211
		}
212
213
		// if we're overriding, let's now make sure the new name and type is allowed
214
		if ($this->_file_name_override != '')
215
		{
216
			$this->file_name = $this->_prep_filename($this->_file_name_override);
217
218
			// If no extension was provided in the file_name config item, use the uploaded one
219
			if (strpos($this->_file_name_override, '.') === FALSE)
220
			{
221
				$this->file_name .= $this->file_ext;
222
			}
223
224
			// An extension was provided, lets have it!
225
			else
226
			{
227
				$this->file_ext	 = $this->get_extension($this->_file_name_override);
228
			}
229
230
			if ( ! $this->is_allowed_filetype(TRUE))
231
			{
232
				$this->set_error('upload_invalid_filetype');
233
				return FALSE;
234
			}
235
		}
236
237
		// Convert the file size to kilobytes
238
		if ($this->file_size > 0)
239
		{
240
			$this->file_size = round($this->file_size/1024, 2);
241
		}
242
243
		// Is the file size within the allowed maximum?
244
		if ( ! $this->is_allowed_filesize())
245
		{
246
			$this->set_error('upload_invalid_filesize');
247
			return FALSE;
248
		}
249
250
		// Are the image dimensions within the allowed size?
251
		// Note: This can fail if the server has an open_basdir restriction.
252
		if ( ! $this->is_allowed_dimensions())
253
		{
254
			$this->set_error('upload_invalid_dimensions');
255
			return FALSE;
256
		}
257
258
		// Sanitize the file name for security
259
		$this->file_name = $this->clean_file_name($this->file_name);
260
261
		// Truncate the file name if it's too long
262
		if ($this->max_filename > 0)
263
		{
264
			$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
265
		}
266
267
		// Remove white spaces in the name
268
		if ($this->remove_spaces == TRUE)
269
		{
270
			$this->file_name = preg_replace("/\s+/", "_", $this->file_name);
271
		}
272
273
		/*
274
		 * Validate the file name
275
		 * This function appends an number onto the end of
276
		 * the file if one with the same name already exists.
277
		 * If it returns false there was a problem.
278
		 */
279
		$this->orig_name = $this->file_name;
280
281
		if ($this->overwrite == FALSE)
282
		{
283
			$this->file_name = $this->set_filename($this->upload_path, $this->file_name);
284
285
			if ($this->file_name === FALSE)
286
			{
287
				return FALSE;
288
			}
289
		}
290
291
		/*
292
		 * Run the file through the XSS hacking filter
293
		 * This helps prevent malicious code from being
294
		 * embedded within a file.  Scripts can easily
295
		 * be disguised as images or other file types.
296
		 */
297
		if ($this->xss_clean)
298
		{
299
			if ($this->do_xss_clean() === FALSE)
300
			{
301
				$this->set_error('upload_unable_to_write_file');
302
				return FALSE;
303
			}
304
		}
305
306
		/*
307
		 * Move the file to the final destination
308
		 * To deal with different server configurations
309
		 * we'll attempt to use copy() first.  If that fails
310
		 * we'll use move_uploaded_file().  One of the two should
311
		 * reliably work in most environments
312
		 */
313
		if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
314
		{
315
			if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
316
			{
317
				$this->set_error('upload_destination_error');
318
				return FALSE;
319
			}
320
		}
321
322
		/*
323
		 * Set the finalized image dimensions
324
		 * This sets the image width/height (assuming the
325
		 * file was an image).  We use this information
326
		 * in the "data" function.
327
		 */
328
		$this->set_image_properties($this->upload_path.$this->file_name);
329
330
		return TRUE;
331
	}
332
333
	// --------------------------------------------------------------------
334
335
	/**
336
	 * Finalized Data Array
337
	 *
338
	 * Returns an associative array containing all of the information
339
	 * related to the upload, allowing the developer easy access in one array.
340
	 *
341
	 * @return	array
342
	 */
343
	public function data()
344
	{
345
		return array (
346
						'file_name'			=> $this->file_name,
347
						'file_type'			=> $this->file_type,
348
						'file_path'			=> $this->upload_path,
349
						'full_path'			=> $this->upload_path.$this->file_name,
350
						'raw_name'			=> str_replace($this->file_ext, '', $this->file_name),
351
						'orig_name'			=> $this->orig_name,
352
						'client_name'		=> $this->client_name,
353
						'file_ext'			=> $this->file_ext,
354
						'file_size'			=> $this->file_size,
355
						'is_image'			=> $this->is_image(),
356
						'image_width'		=> $this->image_width,
357
						'image_height'		=> $this->image_height,
358
						'image_type'		=> $this->image_type,
359
						'image_size_str'	=> $this->image_size_str,
360
					);
361
	}
362
363
	// --------------------------------------------------------------------
364
365
	/**
366
	 * Set Upload Path
367
	 *
368
	 * @param	string
369
	 * @return	void
370
	 */
371
	public function set_upload_path($path)
372
	{
373
		// Make sure it has a trailing slash
374
		$this->upload_path = rtrim($path, '/').'/';
375
	}
376
377
	// --------------------------------------------------------------------
378
379
	/**
380
	 * Set the file name
381
	 *
382
	 * This function takes a filename/path as input and looks for the
383
	 * existence of a file with the same name. If found, it will append a
384
	 * number to the end of the filename to avoid overwriting a pre-existing file.
385
	 *
386
	 * @param	string
387
	 * @param	string
388
	 * @return	string
389
	 */
390
	public function set_filename($path, $filename)
391
	{
392
		if ($this->encrypt_name == TRUE)
393
		{
394
			mt_srand();
395
			$filename = md5(uniqid(mt_rand())).$this->file_ext;
396
		}
397
398
		if ( ! file_exists($path.$filename))
399
		{
400
			return $filename;
401
		}
402
403
		$filename = str_replace($this->file_ext, '', $filename);
404
405
		$new_filename = '';
406
		for ($i = 1; $i < 100; $i++)
407
		{
408
			if ( ! file_exists($path.$filename.$i.$this->file_ext))
409
			{
410
				$new_filename = $filename.$i.$this->file_ext;
411
				break;
412
			}
413
		}
414
415
		if ($new_filename == '')
416
		{
417
			$this->set_error('upload_bad_filename');
418
			return FALSE;
419
		}
420
		else
421
		{
422
			return $new_filename;
423
		}
424
	}
425
426
	// --------------------------------------------------------------------
427
428
	/**
429
	 * Set Maximum File Size
430
	 *
431
	 * @param	integer
432
	 * @return	void
433
	 */
434
	public function set_max_filesize($n)
435
	{
436
		$this->max_size = ((int) $n < 0) ? 0: (int) $n;
437
	}
438
439
	// --------------------------------------------------------------------
440
441
	/**
442
	 * Set Maximum File Name Length
443
	 *
444
	 * @param	integer
445
	 * @return	void
446
	 */
447
	public function set_max_filename($n)
448
	{
449
		$this->max_filename = ((int) $n < 0) ? 0: (int) $n;
450
	}
451
452
	// --------------------------------------------------------------------
453
454
	/**
455
	 * Set Maximum Image Width
456
	 *
457
	 * @param	integer
458
	 * @return	void
459
	 */
460
	public function set_max_width($n)
461
	{
462
		$this->max_width = ((int) $n < 0) ? 0: (int) $n;
463
	}
464
465
	// --------------------------------------------------------------------
466
467
	/**
468
	 * Set Maximum Image Height
469
	 *
470
	 * @param	integer
471
	 * @return	void
472
	 */
473
	public function set_max_height($n)
474
	{
475
		$this->max_height = ((int) $n < 0) ? 0: (int) $n;
476
	}
477
478
	// --------------------------------------------------------------------
479
480
	/**
481
	 * Set Allowed File Types
482
	 *
483
	 * @param	string
484
	 * @return	void
485
	 */
486
	public function set_allowed_types($types)
487
	{
488
		if ( ! is_array($types) && $types == '*')
489
		{
490
			$this->allowed_types = '*';
491
			return;
492
		}
493
		$this->allowed_types = explode('|', $types);
494
	}
495
496
	// --------------------------------------------------------------------
497
498
	/**
499
	 * Set Image Properties
500
	 *
501
	 * Uses GD to determine the width/height/type of image
502
	 *
503
	 * @param	string
504
	 * @return	void
505
	 */
506
	public function set_image_properties($path = '')
507
	{
508
		if ( ! $this->is_image())
509
		{
510
			return;
511
		}
512
513
		if (function_exists('getimagesize'))
514
		{
515
			if (FALSE !== ($D = @getimagesize($path)))
516
			{
517
				$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
518
519
				$this->image_width		= $D['0'];
520
				$this->image_height		= $D['1'];
521
				$this->image_type		= ( ! isset($types[$D['2']])) ? 'unknown' : $types[$D['2']];
522
				$this->image_size_str	= $D['3'];  // string containing height and width
523
			}
524
		}
525
	}
526
527
	// --------------------------------------------------------------------
528
529
	/**
530
	 * Set XSS Clean
531
	 *
532
	 * Enables the XSS flag so that the file that was uploaded
533
	 * will be run through the XSS filter.
534
	 *
535
	 * @param	bool
536
	 * @return	void
537
	 */
538
	public function set_xss_clean($flag = FALSE)
539
	{
540
		$this->xss_clean = ($flag == TRUE) ? TRUE : FALSE;
541
	}
542
543
	// --------------------------------------------------------------------
544
545
	/**
546
	 * Validate the image
547
	 *
548
	 * @return	bool
549
	 */
550
	public function is_image()
551
	{
552
		// IE will sometimes return odd mime-types during upload, so here we just standardize all
553
		// jpegs or pngs to the same file type.
554
555
		$png_mimes  = array('image/x-png');
556
		$jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');
557
558
		if (in_array($this->file_type, $png_mimes))
559
		{
560
			$this->file_type = 'image/png';
561
		}
562
563
		if (in_array($this->file_type, $jpeg_mimes))
564
		{
565
			$this->file_type = 'image/jpeg';
566
		}
567
568
		$img_mimes = array(
569
							'image/gif',
570
							'image/jpeg',
571
							'image/png',
572
						);
573
574
		return (in_array($this->file_type, $img_mimes, TRUE)) ? TRUE : FALSE;
575
	}
576
577
	// --------------------------------------------------------------------
578
579
	/**
580
	 * Verify that the filetype is allowed
581
	 *
582
	 * @return	bool
583
	 */
584
	public function is_allowed_filetype($ignore_mime = FALSE)
585
	{
586
		if ($this->allowed_types == '*')
587
		{
588
			return TRUE;
589
		}
590
591
		if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))
592
		{
593
			$this->set_error('upload_no_file_types');
594
			return FALSE;
595
		}
596
597
		$ext = strtolower(ltrim($this->file_ext, '.'));
598
599
		if ( ! in_array($ext, $this->allowed_types))
600
		{
601
			return FALSE;
602
		}
603
604
		// Images get some additional checks
605
		$image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');
606
607
		if (in_array($ext, $image_types))
608
		{
609
			if (getimagesize($this->file_temp) === FALSE)
610
			{
611
				return FALSE;
612
			}
613
		}
614
615
		if ($ignore_mime === TRUE)
616
		{
617
			return TRUE;
618
		}
619
620
		$mime = $this->mimes_types($ext);
621
622
		if (is_array($mime))
623
		{
624
			if (in_array($this->file_type, $mime, TRUE))
625
			{
626
				return TRUE;
627
			}
628
		}
629
		elseif ($mime == $this->file_type)
630
		{
631
				return TRUE;
632
		}
633
634
		return FALSE;
635
	}
636
637
	// --------------------------------------------------------------------
638
639
	/**
640
	 * Verify that the file is within the allowed size
641
	 *
642
	 * @return	bool
643
	 */
644
	public function is_allowed_filesize()
645
	{
646
		if ($this->max_size != 0  AND  $this->file_size > $this->max_size)
647
		{
648
			return FALSE;
649
		}
650
		else
651
		{
652
			return TRUE;
653
		}
654
	}
655
656
	// --------------------------------------------------------------------
657
658
	/**
659
	 * Verify that the image is within the allowed width/height
660
	 *
661
	 * @return	bool
662
	 */
663
	public function is_allowed_dimensions()
664
	{
665
		if ( ! $this->is_image())
666
		{
667
			return TRUE;
668
		}
669
670
		if (function_exists('getimagesize'))
671
		{
672
			$D = @getimagesize($this->file_temp);
673
674
			if ($this->max_width > 0 AND $D['0'] > $this->max_width)
675
			{
676
				return FALSE;
677
			}
678
679
			if ($this->max_height > 0 AND $D['1'] > $this->max_height)
680
			{
681
				return FALSE;
682
			}
683
684
			return TRUE;
685
		}
686
687
		return TRUE;
688
	}
689
690
	// --------------------------------------------------------------------
691
692
	/**
693
	 * Validate Upload Path
694
	 *
695
	 * Verifies that it is a valid upload path with proper permissions.
696
	 *
697
	 *
698
	 * @return	bool
699
	 */
700
	public function validate_upload_path()
701
	{
702
		if ($this->upload_path == '')
703
		{
704
			$this->set_error('upload_no_filepath');
705
			return FALSE;
706
		}
707
708
		if (function_exists('realpath') AND @realpath($this->upload_path) !== FALSE)
709
		{
710
			$this->upload_path = str_replace("\\", "/", realpath($this->upload_path));
711
		}
712
713
		if ( ! @is_dir($this->upload_path))
714
		{
715
			$this->set_error('upload_no_filepath');
716
			return FALSE;
717
		}
718
719
		if ( ! is_really_writable($this->upload_path))
720
		{
721
			$this->set_error('upload_not_writable');
722
			return FALSE;
723
		}
724
725
		$this->upload_path = preg_replace("/(.+?)\/*$/", "\\1/",  $this->upload_path);
726
		return TRUE;
727
	}
728
729
	// --------------------------------------------------------------------
730
731
	/**
732
	 * Extract the file extension
733
	 *
734
	 * @param	string
735
	 * @return	string
736
	 */
737
	public function get_extension($filename)
738
	{
739
		$x = explode('.', $filename);
740
		return '.'.end($x);
741
	}
742
743
	// --------------------------------------------------------------------
744
745
	/**
746
	 * Clean the file name for security
747
	 *
748
	 * @param	string
749
	 * @return	string
750
	 */
751
	public function clean_file_name($filename)
752
	{
753
		$bad = array(
754
						"<!--",
755
						"-->",
756
						"'",
757
						"<",
758
						">",
759
						'"',
760
						'&',
761
						'$',
762
						'=',
763
						';',
764
						'?',
765
						'/',
766
						"%20",
767
						"%22",
768
						"%3c",		// <
769
						"%253c",	// <
770
						"%3e",		// >
771
						"%0e",		// >
772
						"%28",		// (
773
						"%29",		// )
774
						"%2528",	// (
775
						"%26",		// &
776
						"%24",		// $
777
						"%3f",		// ?
778
						"%3b",		// ;
779
						"%3d"		// =
780
					);
781
782
		$filename = str_replace($bad, '', $filename);
783
784
		return stripslashes($filename);
785
	}
786
787
	// --------------------------------------------------------------------
788
789
	/**
790
	 * Limit the File Name Length
791
	 *
792
	 * @param	string
793
	 * @return	string
794
	 */
795
	public function limit_filename_length($filename, $length)
796
	{
797
		if (strlen($filename) < $length)
798
		{
799
			return $filename;
800
		}
801
802
		$ext = '';
803
		if (strpos($filename, '.') !== FALSE)
804
		{
805
			$parts		= explode('.', $filename);
806
			$ext		= '.'.array_pop($parts);
807
			$filename	= implode('.', $parts);
808
		}
809
810
		return substr($filename, 0, ($length - strlen($ext))).$ext;
811
	}
812
813
	// --------------------------------------------------------------------
814
815
	/**
816
	 * Runs the file through the XSS clean function
817
	 *
818
	 * This prevents people from embedding malicious code in their files.
819
	 * I'm not sure that it won't negatively affect certain files in unexpected ways,
820
	 * but so far I haven't found that it causes trouble.
821
	 *
822
	 * @return	void
823
	 */
824
	public function do_xss_clean()
825
	{
826
		$file = $this->file_temp;
827
828
		if (filesize($file) == 0)
829
		{
830
			return FALSE;
831
		}
832
833
		if (function_exists('memory_get_usage') && memory_get_usage() && ini_get('memory_limit') != '')
834
		{
835
			$current = ini_get('memory_limit') * 1024 * 1024;
836
837
			// There was a bug/behavioural change in PHP 5.2, where numbers over one million get output
838
			// into scientific notation.  number_format() ensures this number is an integer
839
			// http://bugs.php.net/bug.php?id=43053
840
841
			$new_memory = number_format(ceil(filesize($file) + $current), 0, '.', '');
842
843
			ini_set('memory_limit', $new_memory); // When an integer is used, the value is measured in bytes. - PHP.net
844
		}
845
846
		// If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but
847
		// IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone
848
		// using IE who looks at the image.  It does this by inspecting the first 255 bytes of an image.  To get around this
849
		// CI will itself look at the first 255 bytes of an image to determine its relative safety.  This can save a lot of
850
		// processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an
851
		// attempted XSS attack.
852
853
		if (function_exists('getimagesize') && @getimagesize($file) !== FALSE)
854
		{
855
			if (($file = @fopen($file, 'rb')) === FALSE) // "b" to force binary
856
			{
857
				return FALSE; // Couldn't open the file, return FALSE
858
			}
859
860
			$opening_bytes = fread($file, 256);
861
			fclose($file);
862
863
			// These are known to throw IE into mime-type detection chaos
864
			// <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title
865
			// title is basically just in SVG, but we filter it anyhow
866
867
			if ( ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes))
868
			{
869
				return TRUE; // its an image, no "triggers" detected in the first 256 bytes, we're good
870
			}
871
			else
872
			{
873
				return FALSE;
874
			}
875
		}
876
877
		if (($data = @file_get_contents($file)) === FALSE)
878
		{
879
			return FALSE;
880
		}
881
882
		$CI =& get_instance();
883
		return $CI->security->xss_clean($data, TRUE);
884
	}
885
886
	// --------------------------------------------------------------------
887
888
	/**
889
	 * Set an error message
890
	 *
891
	 * @param	string
892
	 * @return	void
893
	 */
894
	public function set_error($msg)
895
	{
896
		$CI =& get_instance();
897
		$CI->lang->load('upload');
898
899
		if (is_array($msg))
900
		{
901
			foreach ($msg as $val)
902
			{
903
				$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
904
				$this->error_msg[] = $msg;
905
				log_message('error', $msg);
906
			}
907
		}
908
		else
909
		{
910
			$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
911
			$this->error_msg[] = $msg;
912
			log_message('error', $msg);
913
		}
914
	}
915
916
	// --------------------------------------------------------------------
917
918
	/**
919
	 * Display the error message
920
	 *
921
	 * @param	string
922
	 * @param	string
923
	 * @return	string
924
	 */
925
	public function display_errors($open = '<p>', $close = '</p>')
926
	{
927
		$str = '';
928
		foreach ($this->error_msg as $val)
929
		{
930
			$str .= $open.$val.$close;
931
		}
932
933
		return $str;
934
	}
935
936
	// --------------------------------------------------------------------
937
938
	/**
939
	 * List of Mime Types
940
	 *
941
	 * This is a list of mime types.  We use it to validate
942
	 * the "allowed types" set by the developer
943
	 *
944
	 * @param	string
945
	 * @return	string
946
	 */
947
	public function mimes_types($mime)
948
	{
949
		global $mimes;
950
951
		if (count($this->mimes) == 0)
952
		{
953
			if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
954
			{
955
				include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
956
			}
957
			elseif (is_file(APPPATH.'config/mimes.php'))
958
			{
959
				include(APPPATH.'config//mimes.php');
960
			}
961
			else
962
			{
963
				return FALSE;
964
			}
965
966
			$this->mimes = $mimes;
967
			unset($mimes);
968
		}
969
970
		return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime];
971
	}
972
973
	// --------------------------------------------------------------------
974
975
	/**
976
	 * Prep Filename
977
	 *
978
	 * Prevents possible script execution from Apache's handling of files multiple extensions
979
	 * http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
980
	 *
981
	 * @param	string
982
	 * @return	string
983
	 */
984
	protected function _prep_filename($filename)
985
	{
986
		if (strpos($filename, '.') === FALSE OR $this->allowed_types == '*')
987
		{
988
			return $filename;
989
		}
990
991
		$parts		= explode('.', $filename);
992
		$ext		= array_pop($parts);
993
		$filename	= array_shift($parts);
994
995
		foreach ($parts as $part)
996
		{
997
			if ( ! in_array(strtolower($part), $this->allowed_types) OR $this->mimes_types(strtolower($part)) === FALSE)
998
			{
999
				$filename .= '.'.$part.'_';
1000
			}
1001
			else
1002
			{
1003
				$filename .= '.'.$part;
1004
			}
1005
		}
1006
1007
		$filename .= '.'.$ext;
1008
1009
		return $filename;
1010
	}
1011
1012
	// --------------------------------------------------------------------
1013
1014
	/**
1015
	 * File MIME type
1016
	 *
1017
	 * Detects the (actual) MIME type of the uploaded file, if possible.
1018
	 * The input array is expected to be $_FILES[$field]
1019
	 *
1020
	 * @param	array
1021
	 * @return	void
1022
	 */
1023
	protected function _file_mime_type($file)
1024
	{
1025
		// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
1026
		$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
1027
1028
		/* Fileinfo extension - most reliable method
1029
		 *
1030
		 * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
1031
		 * more convenient FILEINFO_MIME_TYPE flag doesn't exist.
1032
		 */
1033
		if (function_exists('finfo_file'))
1034
		{
1035
			$finfo = finfo_open(FILEINFO_MIME);
1036
			if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
1037
			{
1038
				$mime = @finfo_file($finfo, $file['tmp_name']);
1039
				finfo_close($finfo);
1040
1041
				/* According to the comments section of the PHP manual page,
1042
				 * it is possible that this function returns an empty string
1043
				 * for some files (e.g. if they don't exist in the magic MIME database)
1044
				 */
1045
				if (is_string($mime) && preg_match($regexp, $mime, $matches))
1046
				{
1047
					$this->file_type = $matches[1];
1048
					return;
1049
				}
1050
			}
1051
		}
1052
1053
		/* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
1054
		 * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
1055
		 * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
1056
		 * than mime_content_type() as well, hence the attempts to try calling the command line with
1057
		 * three different functions.
1058
		 *
1059
		 * Notes:
1060
		 *	- the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
1061
		 *	- many system admins would disable the exec(), shell_exec(), popen() and similar functions
1062
		 *	  due to security concerns, hence the function_exists() checks
1063
		 */
1064
		if (DIRECTORY_SEPARATOR !== '\\')
1065
		{
1066
			$cmd = 'file --brief --mime ' . escapeshellarg($file['tmp_name']) . ' 2>&1';
1067
1068
			if (function_exists('exec'))
1069
			{
1070
				/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
1071
				 * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
1072
				 * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
1073
				 * value, which is only put to allow us to get the return status code.
1074
				 */
1075
				$mime = @exec($cmd, $mime, $return_status);
1076
				if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))
1077
				{
1078
					$this->file_type = $matches[1];
1079
					return;
1080
				}
1081
			}
1082
1083
			if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec'))
1084
			{
1085
				$mime = @shell_exec($cmd);
1086
				if (strlen($mime) > 0)
1087
				{
1088
					$mime = explode("\n", trim($mime));
1089
					if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
1090
					{
1091
						$this->file_type = $matches[1];
1092
						return;
1093
					}
1094
				}
1095
			}
1096
1097
			if (function_exists('popen'))
1098
			{
1099
				$proc = @popen($cmd, 'r');
1100
				if (is_resource($proc))
1101
				{
1102
					$mime = @fread($proc, 512);
1103
					@pclose($proc);
1104
					if ($mime !== FALSE)
1105
					{
1106
						$mime = explode("\n", trim($mime));
1107
						if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
1108
						{
1109
							$this->file_type = $matches[1];
1110
							return;
1111
						}
1112
					}
1113
				}
1114
			}
1115
		}
1116
1117
		// Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
1118
		if (function_exists('mime_content_type'))
1119
		{
1120
			$this->file_type = @mime_content_type($file['tmp_name']);
1121
			if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
1122
			{
1123
				return;
1124
			}
1125
		}
1126
1127
		$this->file_type = $file['type'];
1128
	}
1129
1130
	// --------------------------------------------------------------------
1131
1132
}
1133
// END Upload Class
1134
1135
/* End of file Upload.php */
1136
/* Location: ./system/libraries/Upload.php */