Blame | Last modification | View Log | RSS feed
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');/*** CodeIgniter** An open source application development framework for PHP 4.3.2 or newer** @package CodeIgniter* @author ExpressionEngine Dev Team* @copyright Copyright (c) 2008, EllisLab, Inc.* @license http://codeigniter.com/user_guide/license.html* @link http://codeigniter.com* @since Version 1.0* @filesource*/// ------------------------------------------------------------------------/*** File Uploading Class** @package CodeIgniter* @subpackage Libraries* @category Uploads* @author ExpressionEngine Dev Team* @link http://codeigniter.com/user_guide/libraries/file_uploading.html*/class CI_Upload {var $max_size = 0;var $max_width = 0;var $max_height = 0;var $max_filename = 0;var $allowed_types = "";var $file_temp = "";var $file_name = "";var $orig_name = "";var $file_type = "";var $file_size = "";var $file_ext = "";var $upload_path = "";var $overwrite = FALSE;var $encrypt_name = FALSE;var $is_image = FALSE;var $image_width = '';var $image_height = '';var $image_type = '';var $image_size_str = '';var $error_msg = array();var $mimes = array();var $remove_spaces = TRUE;var $xss_clean = FALSE;var $temp_prefix = "temp_file_";/*** Constructor** @access public*/function CI_Upload($props = array()){if (count($props) > 0){$this->initialize($props);}log_message('debug', "Upload Class Initialized");}// --------------------------------------------------------------------/*** Initialize preferences** @access public* @param array* @return void*/function initialize($config = array()){$defaults = array('max_size' => 0,'max_width' => 0,'max_height' => 0,'max_filename' => 0,'allowed_types' => "",'file_temp' => "",'file_name' => "",'orig_name' => "",'file_type' => "",'file_size' => "",'file_ext' => "",'upload_path' => "",'overwrite' => FALSE,'encrypt_name' => FALSE,'is_image' => FALSE,'image_width' => '','image_height' => '','image_type' => '','image_size_str' => '','error_msg' => array(),'mimes' => array(),'remove_spaces' => TRUE,'xss_clean' => FALSE,'temp_prefix' => "temp_file_");foreach ($defaults as $key => $val){if (isset($config[$key])){$method = 'set_'.$key;if (method_exists($this, $method)){$this->$method($config[$key]);}else{$this->$key = $config[$key];}}else{$this->$key = $val;}}}// --------------------------------------------------------------------/*** Perform the file upload** @access public* @return bool*/function do_upload($field = 'userfile'){// Is $_FILES[$field] set? If not, no reason to continue.if ( ! isset($_FILES[$field])){$this->set_error('upload_no_file_selected');return FALSE;}// Is the upload path valid?if ( ! $this->validate_upload_path()){// errors will already be set by validate_upload_path() so just return FALSEreturn FALSE;}// Was the file able to be uploaded? If not, determine the reason why.if ( ! is_uploaded_file($_FILES[$field]['tmp_name'])){$error = ( ! isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error'];switch($error){case 1: // UPLOAD_ERR_INI_SIZE$this->set_error('upload_file_exceeds_limit');break;case 2: // UPLOAD_ERR_FORM_SIZE$this->set_error('upload_file_exceeds_form_limit');break;case 3: // UPLOAD_ERR_PARTIAL$this->set_error('upload_file_partial');break;case 4: // UPLOAD_ERR_NO_FILE$this->set_error('upload_no_file_selected');break;case 6: // UPLOAD_ERR_NO_TMP_DIR$this->set_error('upload_no_temp_directory');break;case 7: // UPLOAD_ERR_CANT_WRITE$this->set_error('upload_unable_to_write_file');break;case 8: // UPLOAD_ERR_EXTENSION$this->set_error('upload_stopped_by_extension');break;default : $this->set_error('upload_no_file_selected');break;}return FALSE;}// Set the uploaded data as class variables$this->file_temp = $_FILES[$field]['tmp_name'];$this->file_name = $this->_prep_filename($_FILES[$field]['name']);$this->file_size = $_FILES[$field]['size'];$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']);$this->file_type = strtolower($this->file_type);$this->file_ext = $this->get_extension($_FILES[$field]['name']);// Convert the file size to kilobytesif ($this->file_size > 0){$this->file_size = round($this->file_size/1024, 2);}// Is the file type allowed to be uploaded?if ( ! $this->is_allowed_filetype()){$this->set_error('upload_invalid_filetype');return FALSE;}// Is the file size within the allowed maximum?if ( ! $this->is_allowed_filesize()){$this->set_error('upload_invalid_filesize');return FALSE;}// Are the image dimensions within the allowed size?// Note: This can fail if the server has an open_basdir restriction.if ( ! $this->is_allowed_dimensions()){$this->set_error('upload_invalid_dimensions');return FALSE;}// Sanitize the file name for security$this->file_name = $this->clean_file_name($this->file_name);// Truncate the file name if it's too longif ($this->max_filename > 0){$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);}// Remove white spaces in the nameif ($this->remove_spaces == TRUE){$this->file_name = preg_replace("/\s+/", "_", $this->file_name);}/** Validate the file name* This function appends an number onto the end of* the file if one with the same name already exists.* If it returns false there was a problem.*/$this->orig_name = $this->file_name;if ($this->overwrite == FALSE){$this->file_name = $this->set_filename($this->upload_path, $this->file_name);if ($this->file_name === FALSE){return FALSE;}}/** Move the file to the final destination* To deal with different server configurations* we'll attempt to use copy() first. If that fails* we'll use move_uploaded_file(). One of the two should* reliably work in most environments*/if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name)){if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name)){$this->set_error('upload_destination_error');return FALSE;}}/** Run the file through the XSS hacking filter* This helps prevent malicious code from being* embedded within a file. Scripts can easily* be disguised as images or other file types.*/if ($this->xss_clean == TRUE){$this->do_xss_clean();}/** Set the finalized image dimensions* This sets the image width/height (assuming the* file was an image). We use this information* in the "data" function.*/$this->set_image_properties($this->upload_path.$this->file_name);return TRUE;}// --------------------------------------------------------------------/*** Finalized Data Array** Returns an associative array containing all of the information* related to the upload, allowing the developer easy access in one array.** @access public* @return array*/function data(){return array ('file_name' => $this->file_name,'file_type' => $this->file_type,'file_path' => $this->upload_path,'full_path' => $this->upload_path.$this->file_name,'raw_name' => str_replace($this->file_ext, '', $this->file_name),'orig_name' => $this->orig_name,'file_ext' => $this->file_ext,'file_size' => $this->file_size,'is_image' => $this->is_image(),'image_width' => $this->image_width,'image_height' => $this->image_height,'image_type' => $this->image_type,'image_size_str' => $this->image_size_str,);}// --------------------------------------------------------------------/*** Set Upload Path** @access public* @param string* @return void*/function set_upload_path($path){// Make sure it has a trailing slash$this->upload_path = rtrim($path, '/').'/';}// --------------------------------------------------------------------/*** Set the file name** This function takes a filename/path as input and looks for the* existence of a file with the same name. If found, it will append a* number to the end of the filename to avoid overwriting a pre-existing file.** @access public* @param string* @param string* @return string*/function set_filename($path, $filename){if ($this->encrypt_name == TRUE){mt_srand();$filename = md5(uniqid(mt_rand())).$this->file_ext;}if ( ! file_exists($path.$filename)){return $filename;}$filename = str_replace($this->file_ext, '', $filename);$new_filename = '';for ($i = 1; $i < 100; $i++){if ( ! file_exists($path.$filename.$i.$this->file_ext)){$new_filename = $filename.$i.$this->file_ext;break;}}if ($new_filename == ''){$this->set_error('upload_bad_filename');return FALSE;}else{return $new_filename;}}// --------------------------------------------------------------------/*** Set Maximum File Size** @access public* @param integer* @return void*/function set_max_filesize($n){$this->max_size = ((int) $n < 0) ? 0: (int) $n;}// --------------------------------------------------------------------/*** Set Maximum File Name Length** @access public* @param integer* @return void*/function set_max_filename($n){$this->max_filename = ((int) $n < 0) ? 0: (int) $n;}// --------------------------------------------------------------------/*** Set Maximum Image Width** @access public* @param integer* @return void*/function set_max_width($n){$this->max_width = ((int) $n < 0) ? 0: (int) $n;}// --------------------------------------------------------------------/*** Set Maximum Image Height** @access public* @param integer* @return void*/function set_max_height($n){$this->max_height = ((int) $n < 0) ? 0: (int) $n;}// --------------------------------------------------------------------/*** Set Allowed File Types** @access public* @param string* @return void*/function set_allowed_types($types){$this->allowed_types = explode('|', $types);}// --------------------------------------------------------------------/*** Set Image Properties** Uses GD to determine the width/height/type of image** @access public* @param string* @return void*/function set_image_properties($path = ''){if ( ! $this->is_image()){return;}if (function_exists('getimagesize')){if (FALSE !== ($D = @getimagesize($path))){$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');$this->image_width = $D['0'];$this->image_height = $D['1'];$this->image_type = ( ! isset($types[$D['2']])) ? 'unknown' : $types[$D['2']];$this->image_size_str = $D['3']; // string containing height and width}}}// --------------------------------------------------------------------/*** Set XSS Clean** Enables the XSS flag so that the file that was uploaded* will be run through the XSS filter.** @access public* @param bool* @return void*/function set_xss_clean($flag = FALSE){$this->xss_clean = ($flag == TRUE) ? TRUE : FALSE;}// --------------------------------------------------------------------/*** Validate the image** @access public* @return bool*/function is_image(){// IE will sometimes return odd mime-types during upload, so here we just standardize all// jpegs or pngs to the same file type.$png_mimes = array('image/x-png');$jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');if (in_array($this->file_type, $png_mimes)){$this->file_type = 'image/png';}if (in_array($this->file_type, $jpeg_mimes)){$this->file_type = 'image/jpeg';}$img_mimes = array('image/gif','image/jpeg','image/png',);return (in_array($this->file_type, $img_mimes, TRUE)) ? TRUE : FALSE;}// --------------------------------------------------------------------/*** Verify that the filetype is allowed** @access public* @return bool*/function is_allowed_filetype(){if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types)){$this->set_error('upload_no_file_types');return FALSE;}foreach ($this->allowed_types as $val){$mime = $this->mimes_types(strtolower($val));if (is_array($mime)){if (in_array($this->file_type, $mime, TRUE)){return TRUE;}}else{if ($mime == $this->file_type){return TRUE;}}}return FALSE;}// --------------------------------------------------------------------/*** Verify that the file is within the allowed size** @access public* @return bool*/function is_allowed_filesize(){if ($this->max_size != 0 AND $this->file_size > $this->max_size){return FALSE;}else{return TRUE;}}// --------------------------------------------------------------------/*** Verify that the image is within the allowed width/height** @access public* @return bool*/function is_allowed_dimensions(){if ( ! $this->is_image()){return TRUE;}if (function_exists('getimagesize')){$D = @getimagesize($this->file_temp);if ($this->max_width > 0 AND $D['0'] > $this->max_width){return FALSE;}if ($this->max_height > 0 AND $D['1'] > $this->max_height){return FALSE;}return TRUE;}return TRUE;}// --------------------------------------------------------------------/*** Validate Upload Path** Verifies that it is a valid upload path with proper permissions.*** @access public* @return bool*/function validate_upload_path(){if ($this->upload_path == ''){$this->set_error('upload_no_filepath');return FALSE;}if (function_exists('realpath') AND @realpath($this->upload_path) !== FALSE){$this->upload_path = str_replace("\\", "/", realpath($this->upload_path));}if ( ! @is_dir($this->upload_path)){$this->set_error('upload_no_filepath');return FALSE;}if ( ! is_really_writable($this->upload_path)){$this->set_error('upload_not_writable');return FALSE;}$this->upload_path = preg_replace("/(.+?)\/*$/", "\\1/", $this->upload_path);return TRUE;}// --------------------------------------------------------------------/*** Extract the file extension** @access public* @param string* @return string*/function get_extension($filename){$x = explode('.', $filename);return '.'.end($x);}// --------------------------------------------------------------------/*** Clean the file name for security** @access public* @param string* @return string*/function clean_file_name($filename){$bad = array("<!--","-->","'","<",">",'"','&','$','=',';','?','/',"%20","%22","%3c", // <"%253c", // <"%3e", // >"%0e", // >"%28", // ("%29", // )"%2528", // ("%26", // &"%24", // $"%3f", // ?"%3b", // ;"%3d" // =);$filename = str_replace($bad, '', $filename);return stripslashes($filename);}// --------------------------------------------------------------------/*** Limit the File Name Length** @access public* @param string* @return string*/function limit_filename_length($filename, $length){if (strlen($filename) < $length){return $filename;}$ext = '';if (strpos($filename, '.') !== FALSE){$parts = explode('.', $filename);$ext = '.'.array_pop($parts);$filename = implode('.', $parts);}return substr($filename, 0, ($length - strlen($ext))).$ext;}// --------------------------------------------------------------------/*** Runs the file through the XSS clean function** This prevents people from embedding malicious code in their files.* I'm not sure that it won't negatively affect certain files in unexpected ways,* but so far I haven't found that it causes trouble.** @access public* @return void*/function do_xss_clean(){$file = $this->upload_path.$this->file_name;if (filesize($file) == 0){return FALSE;}if (($data = @file_get_contents($file)) === FALSE){return FALSE;}if ( ! $fp = @fopen($file, FOPEN_READ_WRITE)){return FALSE;}$CI =& get_instance();$data = $CI->input->xss_clean($data);flock($fp, LOCK_EX);fwrite($fp, $data);flock($fp, LOCK_UN);fclose($fp);}// --------------------------------------------------------------------/*** Set an error message** @access public* @param string* @return void*/function set_error($msg){$CI =& get_instance();$CI->lang->load('upload');if (is_array($msg)){foreach ($msg as $val){$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);$this->error_msg[] = $msg;log_message('error', $msg);}}else{$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);$this->error_msg[] = $msg;log_message('error', $msg);}}// --------------------------------------------------------------------/*** Display the error message** @access public* @param string* @param string* @return string*/function display_errors($open = '<p>', $close = '</p>'){$str = '';foreach ($this->error_msg as $val){$str .= $open.$val.$close;}return $str;}// --------------------------------------------------------------------/*** List of Mime Types** This is a list of mime types. We use it to validate* the "allowed types" set by the developer** @access public* @param string* @return string*/function mimes_types($mime){global $mimes;if (count($this->mimes) == 0){if (@require_once(APPPATH.'config/mimes'.EXT)){$this->mimes = $mimes;unset($mimes);}}return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime];}// --------------------------------------------------------------------/*** Prep Filename** Prevents possible script execution from Apache's handling of files multiple extensions* http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext** @access private* @param string* @return string*/function _prep_filename($filename){if (strpos($filename, '.') === FALSE){return $filename;}$parts = explode('.', $filename);$ext = array_pop($parts);$filename = array_shift($parts);foreach ($parts as $part){if ($this->mimes_types(strtolower($part)) === FALSE){$filename .= '.'.$part.'_';}else{$filename .= '.'.$part;}}$filename .= '.'.$ext;return $filename;}// --------------------------------------------------------------------}// END Upload Class/* End of file Upload.php *//* Location: ./system/libraries/Upload.php */