blob: 97343a1ffca99be9c82fb06cc7db02ca34edeb64 [file] [log] [blame]
Derek Allard2067d1a2008-11-13 22:59:24 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
Greg Aker741de1c2010-11-10 14:52:57 -06005 * An open source application development framework for PHP 5.1.6 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
Greg Aker0711dc82011-01-05 10:49:40 -06009 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
Derek Allard2067d1a2008-11-13 22:59:24 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
12 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * 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 */
27class CI_Upload {
Barry Mienydd671972010-10-04 16:33:58 +020028
Greg Aker58fdee82010-11-10 15:07:09 -060029 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 = '';
Barry Mienydd671972010-10-04 16:33:58 +020054
Greg Aker58fdee82010-11-10 15:07:09 -060055 protected $_file_name_override = '';
Barry Mienydd671972010-10-04 16:33:58 +020056
Derek Allard2067d1a2008-11-13 22:59:24 +000057 /**
58 * Constructor
59 *
60 * @access public
61 */
Greg Aker58fdee82010-11-10 15:07:09 -060062 public function __construct($props = array())
Derek Allard2067d1a2008-11-13 22:59:24 +000063 {
64 if (count($props) > 0)
65 {
66 $this->initialize($props);
67 }
Barry Mienydd671972010-10-04 16:33:58 +020068
Derek Allard2067d1a2008-11-13 22:59:24 +000069 log_message('debug', "Upload Class Initialized");
70 }
Barry Mienydd671972010-10-04 16:33:58 +020071
Derek Allard2067d1a2008-11-13 22:59:24 +000072 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +020073
Derek Allard2067d1a2008-11-13 22:59:24 +000074 /**
75 * Initialize preferences
76 *
Derek Allard2067d1a2008-11-13 22:59:24 +000077 * @param array
78 * @return void
Barry Mienydd671972010-10-04 16:33:58 +020079 */
Greg Aker58fdee82010-11-10 15:07:09 -060080 public function initialize($config = array())
Derek Allard2067d1a2008-11-13 22:59:24 +000081 {
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,
Derek Jonese9d723f2010-07-12 10:10:59 -0500106 'temp_prefix' => "temp_file_",
107 'client_name' => ''
Barry Mienydd671972010-10-04 16:33:58 +0200108 );
109
110
Derek Allard2067d1a2008-11-13 22:59:24 +0000111 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];
Barry Mienydd671972010-10-04 16:33:58 +0200123 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000124 }
125 else
126 {
127 $this->$key = $val;
128 }
129 }
Barry Mienydd671972010-10-04 16:33:58 +0200130
Derek Jonese9d723f2010-07-12 10:10:59 -0500131 // 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;
Derek Allard2067d1a2008-11-13 22:59:24 +0000134 }
Barry Mienydd671972010-10-04 16:33:58 +0200135
Derek Allard2067d1a2008-11-13 22:59:24 +0000136 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200137
Derek Allard2067d1a2008-11-13 22:59:24 +0000138 /**
139 * Perform the file upload
140 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000141 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200142 */
Greg Aker58fdee82010-11-10 15:07:09 -0600143 public function do_upload($field = 'userfile')
Derek Allard2067d1a2008-11-13 22:59:24 +0000144 {
Robin Sowell0ecc0622011-01-30 16:47:43 -0500145
146 // Is $_FILES[$field] set? If not, no reason to continue.
Derek Allard2067d1a2008-11-13 22:59:24 +0000147 if ( ! isset($_FILES[$field]))
148 {
149 $this->set_error('upload_no_file_selected');
150 return FALSE;
151 }
Barry Mienydd671972010-10-04 16:33:58 +0200152
Derek Allard2067d1a2008-11-13 22:59:24 +0000153 // 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
Barry Mienydd671972010-10-04 16:33:58 +0200174 $this->set_error('upload_file_partial');
Derek Allard2067d1a2008-11-13 22:59:24 +0000175 break;
176 case 4: // UPLOAD_ERR_NO_FILE
Barry Mienydd671972010-10-04 16:33:58 +0200177 $this->set_error('upload_no_file_selected');
Derek Allard2067d1a2008-11-13 22:59:24 +0000178 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
Derek Jonese9d723f2010-07-12 10:10:59 -0500195
Derek Allard2067d1a2008-11-13 22:59:24 +0000196 // Set the uploaded data as class variables
Barry Mienydd671972010-10-04 16:33:58 +0200197 $this->file_temp = $_FILES[$field]['tmp_name'];
198 $this->file_size = $_FILES[$field]['size'];
Derek Allard2067d1a2008-11-13 22:59:24 +0000199 $this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']);
Derek Jones616fb022010-04-22 16:52:18 -0500200 $this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
Derek Jonese9d723f2010-07-12 10:10:59 -0500201 $this->file_name = $this->_prep_filename($_FILES[$field]['name']);
202 $this->file_ext = $this->get_extension($this->file_name);
203 $this->client_name = $this->file_name;
Barry Mienydd671972010-10-04 16:33:58 +0200204
Derek Allard2067d1a2008-11-13 22:59:24 +0000205 // Is the file type allowed to be uploaded?
206 if ( ! $this->is_allowed_filetype())
207 {
208 $this->set_error('upload_invalid_filetype');
209 return FALSE;
210 }
211
Derek Jonese9d723f2010-07-12 10:10:59 -0500212 // if we're overriding, let's now make sure the new name and type is allowed
213 if ($this->_file_name_override != '')
214 {
215 $this->file_name = $this->_prep_filename($this->_file_name_override);
216 $this->file_ext = $this->get_extension($this->file_name);
217
218 if ( ! $this->is_allowed_filetype(TRUE))
219 {
220 $this->set_error('upload_invalid_filetype');
Barry Mienydd671972010-10-04 16:33:58 +0200221 return FALSE;
Derek Jonese9d723f2010-07-12 10:10:59 -0500222 }
223 }
Barry Mienydd671972010-10-04 16:33:58 +0200224
Derek Jonese9d723f2010-07-12 10:10:59 -0500225 // Convert the file size to kilobytes
226 if ($this->file_size > 0)
227 {
228 $this->file_size = round($this->file_size/1024, 2);
229 }
230
Derek Allard2067d1a2008-11-13 22:59:24 +0000231 // Is the file size within the allowed maximum?
232 if ( ! $this->is_allowed_filesize())
233 {
234 $this->set_error('upload_invalid_filesize');
235 return FALSE;
236 }
237
238 // Are the image dimensions within the allowed size?
239 // Note: This can fail if the server has an open_basdir restriction.
240 if ( ! $this->is_allowed_dimensions())
241 {
242 $this->set_error('upload_invalid_dimensions');
243 return FALSE;
244 }
245
246 // Sanitize the file name for security
247 $this->file_name = $this->clean_file_name($this->file_name);
Barry Mienydd671972010-10-04 16:33:58 +0200248
Derek Allard2067d1a2008-11-13 22:59:24 +0000249 // Truncate the file name if it's too long
250 if ($this->max_filename > 0)
251 {
252 $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
253 }
254
255 // Remove white spaces in the name
256 if ($this->remove_spaces == TRUE)
257 {
258 $this->file_name = preg_replace("/\s+/", "_", $this->file_name);
259 }
260
261 /*
262 * Validate the file name
263 * This function appends an number onto the end of
264 * the file if one with the same name already exists.
265 * If it returns false there was a problem.
266 */
267 $this->orig_name = $this->file_name;
268
269 if ($this->overwrite == FALSE)
270 {
271 $this->file_name = $this->set_filename($this->upload_path, $this->file_name);
Barry Mienydd671972010-10-04 16:33:58 +0200272
Derek Allard2067d1a2008-11-13 22:59:24 +0000273 if ($this->file_name === FALSE)
274 {
275 return FALSE;
276 }
277 }
278
279 /*
Derek Jonese9d723f2010-07-12 10:10:59 -0500280 * Run the file through the XSS hacking filter
281 * This helps prevent malicious code from being
282 * embedded within a file. Scripts can easily
283 * be disguised as images or other file types.
284 */
285 if ($this->xss_clean)
286 {
287 if ($this->do_xss_clean() === FALSE)
288 {
289 $this->set_error('upload_unable_to_write_file');
290 return FALSE;
291 }
292 }
293
294 /*
Derek Allard2067d1a2008-11-13 22:59:24 +0000295 * Move the file to the final destination
296 * To deal with different server configurations
297 * we'll attempt to use copy() first. If that fails
298 * we'll use move_uploaded_file(). One of the two should
299 * reliably work in most environments
300 */
301 if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
302 {
303 if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
304 {
Barry Mienydd671972010-10-04 16:33:58 +0200305 $this->set_error('upload_destination_error');
306 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000307 }
308 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000309
310 /*
311 * Set the finalized image dimensions
312 * This sets the image width/height (assuming the
313 * file was an image). We use this information
314 * in the "data" function.
315 */
316 $this->set_image_properties($this->upload_path.$this->file_name);
317
318 return TRUE;
319 }
Barry Mienydd671972010-10-04 16:33:58 +0200320
Derek Allard2067d1a2008-11-13 22:59:24 +0000321 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200322
Derek Allard2067d1a2008-11-13 22:59:24 +0000323 /**
324 * Finalized Data Array
Barry Mienydd671972010-10-04 16:33:58 +0200325 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000326 * Returns an associative array containing all of the information
327 * related to the upload, allowing the developer easy access in one array.
328 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000329 * @return array
Barry Mienydd671972010-10-04 16:33:58 +0200330 */
Greg Aker58fdee82010-11-10 15:07:09 -0600331 public function data()
Derek Allard2067d1a2008-11-13 22:59:24 +0000332 {
333 return array (
334 'file_name' => $this->file_name,
335 'file_type' => $this->file_type,
336 'file_path' => $this->upload_path,
337 'full_path' => $this->upload_path.$this->file_name,
338 'raw_name' => str_replace($this->file_ext, '', $this->file_name),
339 'orig_name' => $this->orig_name,
Derek Jonese9d723f2010-07-12 10:10:59 -0500340 'client_name' => $this->client_name,
Derek Allard2067d1a2008-11-13 22:59:24 +0000341 'file_ext' => $this->file_ext,
342 'file_size' => $this->file_size,
343 'is_image' => $this->is_image(),
344 'image_width' => $this->image_width,
345 'image_height' => $this->image_height,
346 'image_type' => $this->image_type,
347 'image_size_str' => $this->image_size_str,
348 );
349 }
Barry Mienydd671972010-10-04 16:33:58 +0200350
Derek Allard2067d1a2008-11-13 22:59:24 +0000351 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200352
Derek Allard2067d1a2008-11-13 22:59:24 +0000353 /**
354 * Set Upload Path
355 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000356 * @param string
357 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200358 */
Greg Aker58fdee82010-11-10 15:07:09 -0600359 public function set_upload_path($path)
Derek Allard2067d1a2008-11-13 22:59:24 +0000360 {
361 // Make sure it has a trailing slash
362 $this->upload_path = rtrim($path, '/').'/';
363 }
Barry Mienydd671972010-10-04 16:33:58 +0200364
Derek Allard2067d1a2008-11-13 22:59:24 +0000365 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200366
Derek Allard2067d1a2008-11-13 22:59:24 +0000367 /**
368 * Set the file name
369 *
370 * This function takes a filename/path as input and looks for the
371 * existence of a file with the same name. If found, it will append a
372 * number to the end of the filename to avoid overwriting a pre-existing file.
373 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000374 * @param string
375 * @param string
376 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200377 */
Greg Aker58fdee82010-11-10 15:07:09 -0600378 public function set_filename($path, $filename)
Derek Allard2067d1a2008-11-13 22:59:24 +0000379 {
380 if ($this->encrypt_name == TRUE)
Barry Mienydd671972010-10-04 16:33:58 +0200381 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000382 mt_srand();
Barry Mienydd671972010-10-04 16:33:58 +0200383 $filename = md5(uniqid(mt_rand())).$this->file_ext;
Derek Allard2067d1a2008-11-13 22:59:24 +0000384 }
Barry Mienydd671972010-10-04 16:33:58 +0200385
Derek Allard2067d1a2008-11-13 22:59:24 +0000386 if ( ! file_exists($path.$filename))
387 {
388 return $filename;
389 }
Barry Mienydd671972010-10-04 16:33:58 +0200390
Derek Allard2067d1a2008-11-13 22:59:24 +0000391 $filename = str_replace($this->file_ext, '', $filename);
Barry Mienydd671972010-10-04 16:33:58 +0200392
Derek Allard2067d1a2008-11-13 22:59:24 +0000393 $new_filename = '';
394 for ($i = 1; $i < 100; $i++)
Barry Mienydd671972010-10-04 16:33:58 +0200395 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000396 if ( ! file_exists($path.$filename.$i.$this->file_ext))
397 {
398 $new_filename = $filename.$i.$this->file_ext;
399 break;
400 }
401 }
402
403 if ($new_filename == '')
404 {
405 $this->set_error('upload_bad_filename');
406 return FALSE;
407 }
408 else
409 {
410 return $new_filename;
411 }
412 }
Barry Mienydd671972010-10-04 16:33:58 +0200413
Derek Allard2067d1a2008-11-13 22:59:24 +0000414 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200415
Derek Allard2067d1a2008-11-13 22:59:24 +0000416 /**
417 * Set Maximum File Size
418 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000419 * @param integer
420 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200421 */
Greg Aker58fdee82010-11-10 15:07:09 -0600422 public function set_max_filesize($n)
Derek Allard2067d1a2008-11-13 22:59:24 +0000423 {
424 $this->max_size = ((int) $n < 0) ? 0: (int) $n;
425 }
Barry Mienydd671972010-10-04 16:33:58 +0200426
Derek Allard2067d1a2008-11-13 22:59:24 +0000427 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200428
Derek Allard2067d1a2008-11-13 22:59:24 +0000429 /**
430 * Set Maximum File Name Length
431 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000432 * @param integer
433 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200434 */
Greg Aker58fdee82010-11-10 15:07:09 -0600435 public function set_max_filename($n)
Derek Allard2067d1a2008-11-13 22:59:24 +0000436 {
437 $this->max_filename = ((int) $n < 0) ? 0: (int) $n;
438 }
439
440 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200441
Derek Allard2067d1a2008-11-13 22:59:24 +0000442 /**
443 * Set Maximum Image Width
444 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000445 * @param integer
446 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200447 */
Greg Aker58fdee82010-11-10 15:07:09 -0600448 public function set_max_width($n)
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 {
450 $this->max_width = ((int) $n < 0) ? 0: (int) $n;
451 }
Barry Mienydd671972010-10-04 16:33:58 +0200452
Derek Allard2067d1a2008-11-13 22:59:24 +0000453 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200454
Derek Allard2067d1a2008-11-13 22:59:24 +0000455 /**
456 * Set Maximum Image Height
457 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000458 * @param integer
459 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200460 */
Greg Aker58fdee82010-11-10 15:07:09 -0600461 public function set_max_height($n)
Derek Allard2067d1a2008-11-13 22:59:24 +0000462 {
463 $this->max_height = ((int) $n < 0) ? 0: (int) $n;
464 }
Barry Mienydd671972010-10-04 16:33:58 +0200465
Derek Allard2067d1a2008-11-13 22:59:24 +0000466 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200467
Derek Allard2067d1a2008-11-13 22:59:24 +0000468 /**
469 * Set Allowed File Types
470 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000471 * @param string
472 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200473 */
Greg Aker58fdee82010-11-10 15:07:09 -0600474 public function set_allowed_types($types)
Derek Allard2067d1a2008-11-13 22:59:24 +0000475 {
Derek Jonese12f64e2010-03-02 22:55:08 -0600476 if ( ! is_array($types) && $types == '*')
477 {
478 $this->allowed_types = '*';
479 return;
480 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000481 $this->allowed_types = explode('|', $types);
482 }
Barry Mienydd671972010-10-04 16:33:58 +0200483
Derek Allard2067d1a2008-11-13 22:59:24 +0000484 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200485
Derek Allard2067d1a2008-11-13 22:59:24 +0000486 /**
487 * Set Image Properties
488 *
489 * Uses GD to determine the width/height/type of image
490 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000491 * @param string
492 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200493 */
Greg Aker58fdee82010-11-10 15:07:09 -0600494 public function set_image_properties($path = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000495 {
496 if ( ! $this->is_image())
497 {
498 return;
499 }
500
501 if (function_exists('getimagesize'))
502 {
503 if (FALSE !== ($D = @getimagesize($path)))
Barry Mienydd671972010-10-04 16:33:58 +0200504 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000505 $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
506
507 $this->image_width = $D['0'];
508 $this->image_height = $D['1'];
509 $this->image_type = ( ! isset($types[$D['2']])) ? 'unknown' : $types[$D['2']];
510 $this->image_size_str = $D['3']; // string containing height and width
511 }
512 }
513 }
Barry Mienydd671972010-10-04 16:33:58 +0200514
Derek Allard2067d1a2008-11-13 22:59:24 +0000515 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200516
Derek Allard2067d1a2008-11-13 22:59:24 +0000517 /**
518 * Set XSS Clean
519 *
520 * Enables the XSS flag so that the file that was uploaded
521 * will be run through the XSS filter.
522 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000523 * @param bool
524 * @return void
525 */
Greg Aker58fdee82010-11-10 15:07:09 -0600526 public function set_xss_clean($flag = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000527 {
528 $this->xss_clean = ($flag == TRUE) ? TRUE : FALSE;
529 }
Barry Mienydd671972010-10-04 16:33:58 +0200530
Derek Allard2067d1a2008-11-13 22:59:24 +0000531 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200532
Derek Allard2067d1a2008-11-13 22:59:24 +0000533 /**
534 * Validate the image
535 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000536 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200537 */
Greg Aker58fdee82010-11-10 15:07:09 -0600538 public function is_image()
Derek Allard2067d1a2008-11-13 22:59:24 +0000539 {
540 // IE will sometimes return odd mime-types during upload, so here we just standardize all
541 // jpegs or pngs to the same file type.
542
543 $png_mimes = array('image/x-png');
544 $jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');
Barry Mienydd671972010-10-04 16:33:58 +0200545
Derek Allard2067d1a2008-11-13 22:59:24 +0000546 if (in_array($this->file_type, $png_mimes))
547 {
548 $this->file_type = 'image/png';
549 }
Barry Mienydd671972010-10-04 16:33:58 +0200550
Derek Allard2067d1a2008-11-13 22:59:24 +0000551 if (in_array($this->file_type, $jpeg_mimes))
552 {
553 $this->file_type = 'image/jpeg';
554 }
555
556 $img_mimes = array(
557 'image/gif',
558 'image/jpeg',
559 'image/png',
Barry Mienydd671972010-10-04 16:33:58 +0200560 );
Derek Allard2067d1a2008-11-13 22:59:24 +0000561
562 return (in_array($this->file_type, $img_mimes, TRUE)) ? TRUE : FALSE;
563 }
Barry Mienydd671972010-10-04 16:33:58 +0200564
Derek Allard2067d1a2008-11-13 22:59:24 +0000565 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200566
Derek Allard2067d1a2008-11-13 22:59:24 +0000567 /**
568 * Verify that the filetype is allowed
569 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000570 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200571 */
Greg Aker58fdee82010-11-10 15:07:09 -0600572 public function is_allowed_filetype($ignore_mime = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000573 {
Derek Jonese12f64e2010-03-02 22:55:08 -0600574 if ($this->allowed_types == '*')
575 {
576 return TRUE;
577 }
Barry Mienydd671972010-10-04 16:33:58 +0200578
Derek Allard2067d1a2008-11-13 22:59:24 +0000579 if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))
580 {
581 $this->set_error('upload_no_file_types');
582 return FALSE;
583 }
Barry Mienydd671972010-10-04 16:33:58 +0200584
Derek Jonese9d723f2010-07-12 10:10:59 -0500585 $ext = strtolower(ltrim($this->file_ext, '.'));
Barry Mienydd671972010-10-04 16:33:58 +0200586
Derek Jonese9d723f2010-07-12 10:10:59 -0500587 if ( ! in_array($ext, $this->allowed_types))
Derek Allard2067d1a2008-11-13 22:59:24 +0000588 {
Derek Jonese9d723f2010-07-12 10:10:59 -0500589 return FALSE;
590 }
Derek Jonesafa282f2009-02-10 17:11:52 +0000591
Barry Mienydd671972010-10-04 16:33:58 +0200592 // Images get some additional checks
Derek Jonese9d723f2010-07-12 10:10:59 -0500593 $image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');
Barry Mienydd671972010-10-04 16:33:58 +0200594
Derek Jonese9d723f2010-07-12 10:10:59 -0500595 if (in_array($ext, $image_types))
596 {
597 if (getimagesize($this->file_temp) === FALSE)
Derek Jonesafa282f2009-02-10 17:11:52 +0000598 {
Derek Jonese9d723f2010-07-12 10:10:59 -0500599 return FALSE;
Barry Mienydd671972010-10-04 16:33:58 +0200600 }
Derek Jonese9d723f2010-07-12 10:10:59 -0500601 }
Barry Mienydd671972010-10-04 16:33:58 +0200602
Derek Jonese9d723f2010-07-12 10:10:59 -0500603 if ($ignore_mime === TRUE)
604 {
605 return TRUE;
606 }
Barry Mienydd671972010-10-04 16:33:58 +0200607
Derek Jonese9d723f2010-07-12 10:10:59 -0500608 $mime = $this->mimes_types($ext);
Barry Mienydd671972010-10-04 16:33:58 +0200609
Derek Jonese9d723f2010-07-12 10:10:59 -0500610 if (is_array($mime))
611 {
612 if (in_array($this->file_type, $mime, TRUE))
Derek Allard2067d1a2008-11-13 22:59:24 +0000613 {
Derek Jonese9d723f2010-07-12 10:10:59 -0500614 return TRUE;
Barry Mienydd671972010-10-04 16:33:58 +0200615 }
Derek Jonese9d723f2010-07-12 10:10:59 -0500616 }
617 elseif ($mime == $this->file_type)
618 {
619 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000620 }
Barry Mienydd671972010-10-04 16:33:58 +0200621
Derek Allard2067d1a2008-11-13 22:59:24 +0000622 return FALSE;
623 }
Barry Mienydd671972010-10-04 16:33:58 +0200624
Derek Allard2067d1a2008-11-13 22:59:24 +0000625 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200626
Derek Allard2067d1a2008-11-13 22:59:24 +0000627 /**
628 * Verify that the file is within the allowed size
629 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000630 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200631 */
Greg Aker58fdee82010-11-10 15:07:09 -0600632 public function is_allowed_filesize()
Derek Allard2067d1a2008-11-13 22:59:24 +0000633 {
634 if ($this->max_size != 0 AND $this->file_size > $this->max_size)
635 {
636 return FALSE;
637 }
638 else
639 {
640 return TRUE;
641 }
642 }
Barry Mienydd671972010-10-04 16:33:58 +0200643
Derek Allard2067d1a2008-11-13 22:59:24 +0000644 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200645
Derek Allard2067d1a2008-11-13 22:59:24 +0000646 /**
647 * Verify that the image is within the allowed width/height
648 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000649 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200650 */
Greg Aker58fdee82010-11-10 15:07:09 -0600651 public function is_allowed_dimensions()
Derek Allard2067d1a2008-11-13 22:59:24 +0000652 {
653 if ( ! $this->is_image())
654 {
655 return TRUE;
656 }
657
658 if (function_exists('getimagesize'))
659 {
660 $D = @getimagesize($this->file_temp);
661
662 if ($this->max_width > 0 AND $D['0'] > $this->max_width)
663 {
664 return FALSE;
665 }
666
667 if ($this->max_height > 0 AND $D['1'] > $this->max_height)
668 {
669 return FALSE;
670 }
671
672 return TRUE;
673 }
674
675 return TRUE;
676 }
Barry Mienydd671972010-10-04 16:33:58 +0200677
Derek Allard2067d1a2008-11-13 22:59:24 +0000678 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200679
Derek Allard2067d1a2008-11-13 22:59:24 +0000680 /**
681 * Validate Upload Path
682 *
683 * Verifies that it is a valid upload path with proper permissions.
684 *
685 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000686 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200687 */
Greg Aker58fdee82010-11-10 15:07:09 -0600688 public function validate_upload_path()
Derek Allard2067d1a2008-11-13 22:59:24 +0000689 {
690 if ($this->upload_path == '')
691 {
692 $this->set_error('upload_no_filepath');
693 return FALSE;
694 }
Barry Mienydd671972010-10-04 16:33:58 +0200695
Derek Allard2067d1a2008-11-13 22:59:24 +0000696 if (function_exists('realpath') AND @realpath($this->upload_path) !== FALSE)
697 {
698 $this->upload_path = str_replace("\\", "/", realpath($this->upload_path));
699 }
700
701 if ( ! @is_dir($this->upload_path))
702 {
703 $this->set_error('upload_no_filepath');
704 return FALSE;
705 }
706
707 if ( ! is_really_writable($this->upload_path))
708 {
709 $this->set_error('upload_not_writable');
710 return FALSE;
711 }
712
713 $this->upload_path = preg_replace("/(.+?)\/*$/", "\\1/", $this->upload_path);
714 return TRUE;
715 }
Barry Mienydd671972010-10-04 16:33:58 +0200716
Derek Allard2067d1a2008-11-13 22:59:24 +0000717 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200718
Derek Allard2067d1a2008-11-13 22:59:24 +0000719 /**
720 * Extract the file extension
721 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000722 * @param string
723 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200724 */
Greg Aker58fdee82010-11-10 15:07:09 -0600725 public function get_extension($filename)
Derek Allard2067d1a2008-11-13 22:59:24 +0000726 {
727 $x = explode('.', $filename);
728 return '.'.end($x);
Barry Mienydd671972010-10-04 16:33:58 +0200729 }
730
Derek Allard2067d1a2008-11-13 22:59:24 +0000731 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200732
Derek Allard2067d1a2008-11-13 22:59:24 +0000733 /**
734 * Clean the file name for security
735 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000736 * @param string
737 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200738 */
Greg Aker58fdee82010-11-10 15:07:09 -0600739 public function clean_file_name($filename)
Derek Allard2067d1a2008-11-13 22:59:24 +0000740 {
741 $bad = array(
742 "<!--",
743 "-->",
744 "'",
745 "<",
746 ">",
747 '"',
748 '&',
749 '$',
750 '=',
751 ';',
752 '?',
753 '/',
754 "%20",
755 "%22",
756 "%3c", // <
Barry Mienydd671972010-10-04 16:33:58 +0200757 "%253c", // <
758 "%3e", // >
759 "%0e", // >
760 "%28", // (
761 "%29", // )
762 "%2528", // (
763 "%26", // &
764 "%24", // $
765 "%3f", // ?
766 "%3b", // ;
Derek Allard2067d1a2008-11-13 22:59:24 +0000767 "%3d" // =
768 );
Barry Mienydd671972010-10-04 16:33:58 +0200769
Derek Allard2067d1a2008-11-13 22:59:24 +0000770 $filename = str_replace($bad, '', $filename);
771
772 return stripslashes($filename);
773 }
774
775 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200776
Derek Allard2067d1a2008-11-13 22:59:24 +0000777 /**
778 * Limit the File Name Length
779 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000780 * @param string
781 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200782 */
Greg Aker58fdee82010-11-10 15:07:09 -0600783 public function limit_filename_length($filename, $length)
Derek Allard2067d1a2008-11-13 22:59:24 +0000784 {
785 if (strlen($filename) < $length)
786 {
787 return $filename;
788 }
Barry Mienydd671972010-10-04 16:33:58 +0200789
Derek Allard2067d1a2008-11-13 22:59:24 +0000790 $ext = '';
791 if (strpos($filename, '.') !== FALSE)
792 {
793 $parts = explode('.', $filename);
794 $ext = '.'.array_pop($parts);
795 $filename = implode('.', $parts);
796 }
Barry Mienydd671972010-10-04 16:33:58 +0200797
Derek Allard2067d1a2008-11-13 22:59:24 +0000798 return substr($filename, 0, ($length - strlen($ext))).$ext;
799 }
800
801 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200802
Derek Allard2067d1a2008-11-13 22:59:24 +0000803 /**
804 * Runs the file through the XSS clean function
805 *
806 * This prevents people from embedding malicious code in their files.
807 * I'm not sure that it won't negatively affect certain files in unexpected ways,
808 * but so far I haven't found that it causes trouble.
809 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000810 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200811 */
Greg Aker58fdee82010-11-10 15:07:09 -0600812 public function do_xss_clean()
Barry Mienydd671972010-10-04 16:33:58 +0200813 {
Derek Jonese9d723f2010-07-12 10:10:59 -0500814 $file = $this->file_temp;
Barry Mienydd671972010-10-04 16:33:58 +0200815
Derek Allard2067d1a2008-11-13 22:59:24 +0000816 if (filesize($file) == 0)
817 {
818 return FALSE;
819 }
Barry Mienydd671972010-10-04 16:33:58 +0200820
Greg Akerf82e51c2010-04-14 19:33:50 -0500821 if (function_exists('memory_get_usage') && memory_get_usage() && ini_get('memory_limit') != '')
822 {
823 $current = ini_get('memory_limit') * 1024 * 1024;
Barry Mienydd671972010-10-04 16:33:58 +0200824
Greg Akerc78a2592010-06-09 11:45:32 -0500825 // There was a bug/behavioural change in PHP 5.2, where numbers over one million get output
826 // into scientific notation. number_format() ensures this number is an integer
827 // http://bugs.php.net/bug.php?id=43053
Barry Mienydd671972010-10-04 16:33:58 +0200828
Greg Akerc78a2592010-06-09 11:45:32 -0500829 $new_memory = number_format(ceil(filesize($file) + $current), 0, '.', '');
Barry Mienydd671972010-10-04 16:33:58 +0200830
Greg Akerc78a2592010-06-09 11:45:32 -0500831 ini_set('memory_limit', $new_memory); // When an integer is used, the value is measured in bytes. - PHP.net
Greg Akerf82e51c2010-04-14 19:33:50 -0500832 }
833
834 // If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but
835 // IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone
836 // using IE who looks at the image. It does this by inspecting the first 255 bytes of an image. To get around this
837 // CI will itself look at the first 255 bytes of an image to determine its relative safety. This can save a lot of
Barry Mienydd671972010-10-04 16:33:58 +0200838 // processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an
Greg Akerf82e51c2010-04-14 19:33:50 -0500839 // attempted XSS attack.
840
841 if (function_exists('getimagesize') && @getimagesize($file) !== FALSE)
842 {
Barry Mienydd671972010-10-04 16:33:58 +0200843 if (($file = @fopen($file, 'rb')) === FALSE) // "b" to force binary
844 {
Greg Akerf82e51c2010-04-14 19:33:50 -0500845 return FALSE; // Couldn't open the file, return FALSE
Barry Mienydd671972010-10-04 16:33:58 +0200846 }
Greg Akerf82e51c2010-04-14 19:33:50 -0500847
Barry Mienydd671972010-10-04 16:33:58 +0200848 $opening_bytes = fread($file, 256);
849 fclose($file);
Greg Akerf82e51c2010-04-14 19:33:50 -0500850
851 // These are known to throw IE into mime-type detection chaos
852 // <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title
853 // title is basically just in SVG, but we filter it anyhow
854
855 if ( ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes))
856 {
857 return TRUE; // its an image, no "triggers" detected in the first 256 bytes, we're good
858 }
859 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000860
861 if (($data = @file_get_contents($file)) === FALSE)
862 {
863 return FALSE;
864 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000865
Greg Akerf82e51c2010-04-14 19:33:50 -0500866 $CI =& get_instance();
Barry Mienydd671972010-10-04 16:33:58 +0200867
Derek Jones30841672010-04-26 09:09:21 -0500868 if ( ! isset($CI->security))
Derek Jones5640a712010-04-23 11:22:40 -0500869 {
Derek Jones247f0292010-04-26 09:10:21 -0500870 $CI->load->library('security');
Derek Jones5640a712010-04-23 11:22:40 -0500871 }
Barry Mienydd671972010-10-04 16:33:58 +0200872
Greg Akerf82e51c2010-04-14 19:33:50 -0500873 return $CI->security->xss_clean($data, TRUE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000874 }
Barry Mienydd671972010-10-04 16:33:58 +0200875
Derek Allard2067d1a2008-11-13 22:59:24 +0000876 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200877
Derek Allard2067d1a2008-11-13 22:59:24 +0000878 /**
879 * Set an error message
880 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000881 * @param string
882 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200883 */
Greg Aker58fdee82010-11-10 15:07:09 -0600884 public function set_error($msg)
Derek Allard2067d1a2008-11-13 22:59:24 +0000885 {
Barry Mienydd671972010-10-04 16:33:58 +0200886 $CI =& get_instance();
Derek Allard2067d1a2008-11-13 22:59:24 +0000887 $CI->lang->load('upload');
Barry Mienydd671972010-10-04 16:33:58 +0200888
Derek Allard2067d1a2008-11-13 22:59:24 +0000889 if (is_array($msg))
890 {
891 foreach ($msg as $val)
892 {
Barry Mienydd671972010-10-04 16:33:58 +0200893 $msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
Derek Allard2067d1a2008-11-13 22:59:24 +0000894 $this->error_msg[] = $msg;
895 log_message('error', $msg);
Barry Mienydd671972010-10-04 16:33:58 +0200896 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000897 }
898 else
899 {
900 $msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
901 $this->error_msg[] = $msg;
902 log_message('error', $msg);
903 }
904 }
Barry Mienydd671972010-10-04 16:33:58 +0200905
Derek Allard2067d1a2008-11-13 22:59:24 +0000906 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200907
Derek Allard2067d1a2008-11-13 22:59:24 +0000908 /**
909 * Display the error message
910 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000911 * @param string
912 * @param string
913 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200914 */
Greg Aker58fdee82010-11-10 15:07:09 -0600915 public function display_errors($open = '<p>', $close = '</p>')
Derek Allard2067d1a2008-11-13 22:59:24 +0000916 {
917 $str = '';
918 foreach ($this->error_msg as $val)
919 {
920 $str .= $open.$val.$close;
921 }
Barry Mienydd671972010-10-04 16:33:58 +0200922
Derek Allard2067d1a2008-11-13 22:59:24 +0000923 return $str;
924 }
Barry Mienydd671972010-10-04 16:33:58 +0200925
Derek Allard2067d1a2008-11-13 22:59:24 +0000926 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200927
Derek Allard2067d1a2008-11-13 22:59:24 +0000928 /**
929 * List of Mime Types
930 *
931 * This is a list of mime types. We use it to validate
932 * the "allowed types" set by the developer
933 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000934 * @param string
935 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200936 */
Greg Aker58fdee82010-11-10 15:07:09 -0600937 public function mimes_types($mime)
Derek Allard2067d1a2008-11-13 22:59:24 +0000938 {
939 global $mimes;
Barry Mienydd671972010-10-04 16:33:58 +0200940
Derek Allard2067d1a2008-11-13 22:59:24 +0000941 if (count($this->mimes) == 0)
942 {
943 if (@require_once(APPPATH.'config/mimes'.EXT))
944 {
945 $this->mimes = $mimes;
946 unset($mimes);
947 }
948 }
Barry Mienydd671972010-10-04 16:33:58 +0200949
Derek Allard2067d1a2008-11-13 22:59:24 +0000950 return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime];
951 }
952
953 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200954
Derek Allard2067d1a2008-11-13 22:59:24 +0000955 /**
956 * Prep Filename
957 *
958 * Prevents possible script execution from Apache's handling of files multiple extensions
959 * http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
960 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000961 * @param string
962 * @return string
963 */
Greg Aker58fdee82010-11-10 15:07:09 -0600964 protected function _prep_filename($filename)
Derek Allard2067d1a2008-11-13 22:59:24 +0000965 {
Greg Aker924000e2010-07-22 11:04:58 -0500966 if (strpos($filename, '.') === FALSE OR $this->allowed_types == '*')
Derek Allard2067d1a2008-11-13 22:59:24 +0000967 {
968 return $filename;
969 }
Derek Allard616dab82009-02-16 15:44:32 +0000970
Derek Allard2067d1a2008-11-13 22:59:24 +0000971 $parts = explode('.', $filename);
972 $ext = array_pop($parts);
973 $filename = array_shift($parts);
Derek Allard616dab82009-02-16 15:44:32 +0000974
Derek Allard2067d1a2008-11-13 22:59:24 +0000975 foreach ($parts as $part)
976 {
Derek Jonese9d723f2010-07-12 10:10:59 -0500977 if ( ! in_array(strtolower($part), $this->allowed_types) OR $this->mimes_types(strtolower($part)) === FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000978 {
979 $filename .= '.'.$part.'_';
980 }
981 else
982 {
983 $filename .= '.'.$part;
984 }
985 }
Derek Allardd70b0642009-02-16 13:51:42 +0000986
Derek Allard2067d1a2008-11-13 22:59:24 +0000987 $filename .= '.'.$ext;
Barry Mienydd671972010-10-04 16:33:58 +0200988
Derek Allard2067d1a2008-11-13 22:59:24 +0000989 return $filename;
990 }
991
992 // --------------------------------------------------------------------
993
994}
995// END Upload Class
996
997/* End of file Upload.php */
Greg Aker58fdee82010-11-10 15:07:09 -0600998/* Location: ./system/libraries/Upload.php */