Created
March 8, 2013 04:55
-
-
Save DimitriGilbert/5114297 to your computer and use it in GitHub Desktop.
correction of upload.php fuel core class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Part of the Fuel framework. | |
| * | |
| * @package Fuel | |
| * @version 1.0 | |
| * @author Fuel Development Team | |
| * @license MIT License | |
| * @copyright 2010 - 2012 Fuel Development Team | |
| * @link http://fuelphp.com | |
| */ | |
| namespace Fuel\Core; | |
| /** | |
| * Upload Class | |
| * | |
| * @package Fuel | |
| * @category Core | |
| * @author Harro "WanWizard" Verton | |
| * @link http://docs.fuelphp.com/classes/upload.html | |
| */ | |
| class Upload { | |
| /* --------------------------------------------------------------------------- | |
| * ERROR CODE CONSTANTS | |
| * --------------------------------------------------------------------------- */ | |
| // duplicate the PHP standard error codes for consistency | |
| const UPLOAD_ERR_OK = UPLOAD_ERR_OK; | |
| const UPLOAD_ERR_INI_SIZE = UPLOAD_ERR_INI_SIZE; | |
| const UPLOAD_ERR_FORM_SIZE = UPLOAD_ERR_FORM_SIZE; | |
| const UPLOAD_ERR_PARTIAL = UPLOAD_ERR_PARTIAL; | |
| const UPLOAD_ERR_NO_FILE = UPLOAD_ERR_NO_FILE; | |
| const UPLOAD_ERR_NO_TMP_DIR = UPLOAD_ERR_NO_TMP_DIR; | |
| const UPLOAD_ERR_CANT_WRITE = UPLOAD_ERR_CANT_WRITE; | |
| const UPLOAD_ERR_EXTENSION = UPLOAD_ERR_EXTENSION; | |
| // and add our own error codes | |
| const UPLOAD_ERR_MAX_SIZE = 101; | |
| const UPLOAD_ERR_EXT_BLACKLISTED = 102; | |
| const UPLOAD_ERR_EXT_NOT_WHITELISTED = 103; | |
| const UPLOAD_ERR_TYPE_BLACKLISTED = 104; | |
| const UPLOAD_ERR_TYPE_NOT_WHITELISTED = 105; | |
| const UPLOAD_ERR_MIME_BLACKLISTED = 106; | |
| const UPLOAD_ERR_MIME_NOT_WHITELISTED = 107; | |
| const UPLOAD_ERR_MAX_FILENAME_LENGTH = 108; | |
| const UPLOAD_ERR_MOVE_FAILED = 109; | |
| const UPLOAD_ERR_DUPLICATE_FILE = 110; | |
| const UPLOAD_ERR_MKDIR_FAILED = 111; | |
| const UPLOAD_ERR_FTP_FAILED = 112; | |
| /* --------------------------------------------------------------------------- | |
| * STATIC PROPERTIES | |
| * --------------------------------------------------------------------------- */ | |
| /** | |
| * @var array default configuration values | |
| */ | |
| protected static $_defaults = array( | |
| 'auto_process' => false, | |
| // validation settings | |
| 'max_size' => 0, | |
| 'max_length' => 0, | |
| 'ext_whitelist' => array(), | |
| 'ext_blacklist' => array(), | |
| 'type_whitelist' => array(), | |
| 'type_blacklist' => array(), | |
| 'mime_whitelist' => array(), | |
| 'mime_blacklist' => array(), | |
| // save settings | |
| 'path' => '', | |
| 'prefix' => '', | |
| 'suffix' => '', | |
| 'extension' => '', | |
| 'create_path' => true, | |
| 'path_chmod' => 0777, | |
| 'file_chmod' => 0666, | |
| 'auto_rename' => true, | |
| 'overwrite' => false, | |
| 'randomize' => false, | |
| 'normalize' => false, | |
| 'normalize_separator' => '_', | |
| 'change_case' => false, | |
| 'ftp_mode' => 'auto', | |
| 'ftp_permissions' => null | |
| ); | |
| /** | |
| * @var array defined callbacks | |
| */ | |
| protected static $callbacks = array( | |
| 'validate' => null, | |
| 'before' => null, | |
| 'after' => null | |
| ); | |
| /** | |
| * @var array configuration of this instance | |
| */ | |
| protected static $config = array(); | |
| /** | |
| * @var array normalized $_FILES array | |
| */ | |
| protected static $files = array(); | |
| /** | |
| * @var bool indicator of valid uploads | |
| */ | |
| protected static $valid = false; | |
| /** | |
| * @var object Ftp object | |
| */ | |
| protected static $with_ftp = false; | |
| /* --------------------------------------------------------------------------- | |
| * STATIC METHODS | |
| * --------------------------------------------------------------------------- */ | |
| /** | |
| * class initialisation, load the config and process $_FILES if needed | |
| * | |
| * @return void | |
| */ | |
| public static function _init() { | |
| // get the config for this upload | |
| \Config::load('upload', true); | |
| // get the language file for this upload | |
| \Lang::load('upload', true); | |
| // make sure we have defaults for those not defined | |
| static::$config = array_merge(static::$_defaults, \Config::get('upload', array())); | |
| static::$config['auto_process'] and static::process(); | |
| } | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Check if we have valid files | |
| * | |
| * @return bool true if static:$files contains uploaded files that are valid | |
| */ | |
| public static function is_valid() { | |
| return static::$valid; | |
| } | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Get the list of validated files | |
| * | |
| * @return array list of uploaded files that are validated | |
| */ | |
| public static function get_files($index = null) { | |
| // if no parameters were passed, return all files successfully uploaded | |
| if (func_num_args() == 0) { | |
| return array_filter(static::$files, function($file) { | |
| return $file['error'] === false; | |
| }); | |
| } | |
| // if an index number was given, return that file only (if that was successful) | |
| elseif (isset(static::$files[$index]) and static::$files[$index]['error'] === false) { | |
| return static::$files[$index]; | |
| } | |
| // if an field name was given, return that file only (if that was successful) | |
| else { | |
| foreach (static::$files as $file) { | |
| if ($file['field'] == $index and $file['error'] === false) { | |
| return $file; | |
| } | |
| } | |
| throw new \FuelException('No valid uploaded file exists with index "' . $index . '"'); | |
| } | |
| } | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Get the list of non-validated files | |
| * | |
| * @return array list of uploaded files that failed to validate | |
| */ | |
| public static function get_errors($index = null) { | |
| // if no parameters were passed, return all files in error | |
| if (func_num_args() == 0) { | |
| return array_filter(static::$files, function($file) { | |
| return $file['error'] === true; | |
| }); | |
| } | |
| // if an index number was given, return that file only (if it is in error) | |
| elseif (isset(static::$files[$index]) and static::$files[$index]['error'] === true) { | |
| return static::$files[$index]; | |
| } | |
| // if an field name was given, return that file only (if it is in error) | |
| else { | |
| foreach (static::$files as $file) { | |
| if ($file['field'] == $index and $file['error'] === true) { | |
| return $file; | |
| } | |
| } | |
| throw new \FuelException('No invalid uploaded file exists with index "' . $index . '"'); | |
| } | |
| } | |
| // -------------------------------------------------------------------- | |
| /** | |
| * Register | |
| * | |
| * Registers a Callback for a given event | |
| * | |
| * @access public | |
| * @param string The name of the event | |
| * @param mixed callback information | |
| * @return void | |
| */ | |
| public static function register() { | |
| // get any arguments passed | |
| $callback = func_get_args(); | |
| // if the arguments are valid, register the callback | |
| if (isset($callback[0]) and is_string($callback[0]) and isset($callback[1]) and is_callable($callback[1])) { | |
| // make sure we have an entry for this callback | |
| if (array_key_exists($callback[0], static::$callbacks)) { | |
| static::$callbacks[array_shift($callback)] = $callback; | |
| // report success | |
| return true; | |
| } | |
| } | |
| // can't register the callback | |
| return false; | |
| } | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Normalize the $_FILES array and store the result in $files | |
| * | |
| * @return void | |
| */ | |
| public static function process($config = array()) { | |
| // process runtime config | |
| if (is_array($config)) { | |
| static::$config = array_merge(static::$config, $config); | |
| } | |
| // processed files array's | |
| static::$files = array(); | |
| $files = array(); | |
| // any files uploaded? | |
| if (!empty($_FILES)) { | |
| // normalize the multidimensional fields in the $_FILES array | |
| foreach ($_FILES as $name => $value) { | |
| if (is_array($value['name'])) { | |
| foreach ($value as $field => $content) { | |
| foreach (\Arr::flatten($content) as $element => $data) { | |
| $_FILES[$name . ':' . $element][$field] = $data; | |
| } | |
| } | |
| unset($_FILES[$name]); | |
| } | |
| } | |
| // normalize the $_FILES array | |
| foreach ($_FILES as $name => $value) { | |
| // store the file data | |
| $file = array('field' => $name, 'file' => $value['tmp_name']); | |
| if ($value['error']) { | |
| $file['error'] = true; | |
| $file['errors'][] = array('error' => $value['error']); | |
| } else { | |
| $file['error'] = false; | |
| $file['errors'] = array(); | |
| } | |
| unset($value['tmp_name']); | |
| $files[] = array_merge($value, $file); | |
| } | |
| // verify and augment the files data | |
| foreach ($files as $key => $value) { | |
| // add some filename details (pathinfo can't be trusted with utf-8 filenames!) | |
| $files[$key]['extension'] = ltrim(strrchr(ltrim($files[$key]['name'], '.'), '.'), '.'); | |
| if (empty($files[$key]['extension'])) { | |
| $files[$key]['filename'] = $files[$key]['name']; | |
| } else { | |
| $files[$key]['filename'] = substr($files[$key]['name'], 0, strlen($files[$key]['name']) - (strlen($files[$key]['extension']) + 1)); | |
| } | |
| // does this upload exceed the maximum size? | |
| if (!empty(static::$config['max_size']) and $files[$key]['size'] > static::$config['max_size']) { | |
| $files[$key]['error'] = true; | |
| $files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_MAX_SIZE); | |
| } | |
| // add mimetype information | |
| if (!$files[$key]['error']) { | |
| $handle = finfo_open(FILEINFO_MIME_TYPE); | |
| $files[$key]['mimetype'] = finfo_file($handle, $value['file']); | |
| finfo_close($handle); | |
| if ($files[$key]['mimetype'] == 'application/octet-stream' and $files[$key]['type'] != $files[$key]['mimetype']) { | |
| $files[$key]['mimetype'] = $files[$key]['type']; | |
| } | |
| // make sure it contains something valid | |
| if (empty($files[$key]['mimetype'])) { | |
| $files[$key]['mimetype'] = 'application/octet-stream'; | |
| } | |
| } | |
| // check the file extension black- and whitelists | |
| if (!$files[$key]['error']) { | |
| if (in_array(strtolower($files[$key]['extension']), (array) static::$config['ext_blacklist'])) { | |
| $files[$key]['error'] = true; | |
| $files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_EXT_BLACKLISTED); | |
| } elseif (!empty(static::$config['ext_whitelist']) and !in_array(strtolower($files[$key]['extension']), (array) static::$config['ext_whitelist'])) { | |
| $files[$key]['error'] = true; | |
| $files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_EXT_NOT_WHITELISTED); | |
| } | |
| } | |
| // check the file type black- and whitelists | |
| if (!$files[$key]['error']) { | |
| // split the mimetype info so we can run some tests | |
| preg_match('|^(.*)/(.*)|', $files[$key]['mimetype'], $mimeinfo); | |
| if (is_array($mimeinfo) and count($mimeinfo) >= 2) { | |
| if (in_array($mimeinfo[1], (array) static::$config['type_blacklist'])) { | |
| $files[$key]['error'] = true; | |
| $files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_TYPE_BLACKLISTED); | |
| } | |
| if (!empty(static::$config['type_whitelist']) and !in_array($mimeinfo[1], (array) static::$config['type_whitelist'])) { | |
| $files[$key]['error'] = true; | |
| $files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_TYPE_NOT_WHITELISTED); | |
| } | |
| } else { | |
| $files[$key]['error'] = true; | |
| } | |
| } | |
| // check the file mimetype black- and whitelists | |
| if (!$files[$key]['error']) { | |
| if (in_array($files[$key]['mimetype'], (array) static::$config['mime_blacklist'])) { | |
| $files[$key]['error'] = true; | |
| $files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_MIME_BLACKLISTED); | |
| } elseif (!empty(static::$config['mime_whitelist']) and !in_array($files[$key]['mimetype'], (array) static::$config['mime_whitelist'])) { | |
| $files[$key]['error'] = true; | |
| $files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_MIME_NOT_WHITELISTED); | |
| } | |
| } | |
| // store the normalized and validated result | |
| static::$files[$key] = $files[$key]; | |
| // validation callback defined? | |
| if (array_key_exists('validate', static::$callbacks) and !is_null(static::$callbacks['validate'])) { | |
| // get the callback method | |
| $callback = static::$callbacks['validate'][0]; | |
| // call the callback | |
| if (is_callable($callback)) { | |
| $result = call_user_func_array($callback, array(&static::$files[$key])); | |
| if (is_numeric($result) and $result) { | |
| static::$files[$key]['error'] = true; | |
| static::$files[$key]['errors'][] = array('error' => $result); | |
| } | |
| } | |
| } | |
| // and add the message texts | |
| foreach (static::$files[$key]['errors'] as $e => $error) { | |
| empty(static::$files[$key]['errors'][$e]['message']) and static::$files[$key]['errors'][$e]['message'] = \Lang::get('upload.error_' . $error['error']); | |
| } | |
| } | |
| } | |
| // determine the validate status of at least one uploaded file | |
| $valid = false; | |
| foreach (static::$files as $key => $value) { | |
| if ($value['error'] === false) { | |
| $valid = true; | |
| break; | |
| } | |
| } | |
| static::$valid = $valid; | |
| } | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Upload files with Ftp | |
| * | |
| * @param string|array The name of the config group to use, or a configuration array. | |
| * @param bool Automatically connect to this server. | |
| */ | |
| public static function with_ftp($config = 'default', $connect = true) { | |
| static::$with_ftp = \Ftp::forge($config, $connect); | |
| } | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * save uploaded file(s) | |
| * | |
| * @param mixed if int, $files element to move. if array, list of elements to move, if none, move all elements | |
| * @param string path to move to | |
| * @return void | |
| */ | |
| public static function save() { | |
| // path to save the files to | |
| $path = static::$config['path']; | |
| // files to save | |
| $files = array(); | |
| // check for parameters | |
| if (func_num_args()) { | |
| foreach (func_get_args() as $param) { | |
| // string => new path to save to | |
| if (is_string($param)) { | |
| $path = $param; | |
| } | |
| // array => list of $files indexes to save | |
| elseif (is_array($param)) { | |
| $files = array(); | |
| foreach ($param as $key) { | |
| if (isset(static::$files[(int) $key])) { | |
| $files[(int) $key] = static::$files[(int) $key]; | |
| } | |
| } | |
| } | |
| // integer => files index to save | |
| elseif (is_numeric($param)) { | |
| if (isset(static::$files[$param])) { | |
| $files[$param] = static::$files[$param]; | |
| } | |
| } | |
| } | |
| } else { | |
| // save all files | |
| $files = static::$files; | |
| } | |
| // anything to save? | |
| if (empty($files)) { | |
| throw new \FuelException('No uploaded files are selected.'); | |
| } | |
| // supplied new name and not auto renaming? | |
| if (array_key_exists('new_name', static::$config) and !static::$config['auto_rename'] and count($files) > 1) { | |
| throw new \FuelException('Can\'t rename multiple files without auto renaming.'); | |
| } | |
| // make sure we have a valid path | |
| $path = rtrim($path, DS) . DS; | |
| if (!is_dir($path) and (bool) static::$config['create_path']) { | |
| $oldumask = umask(0); | |
| @mkdir($path, static::$config['path_chmod'], true); | |
| umask($oldumask); | |
| } | |
| if (!is_dir($path)) { | |
| throw new \FuelException('Can\'t move the uploaded file. Destination path specified does not exist.'); | |
| } | |
| // save the old umask | |
| $oldumask = umask(0); | |
| // now that we have a path, let's save the files | |
| foreach ($files as $key => $file) { | |
| // skip all files in error | |
| if ($file['error'] === true) { | |
| continue; | |
| } | |
| // do we need to generate a random filename? | |
| if ((bool) static::$config['randomize']) { | |
| $filename = md5(serialize($file)); | |
| } else { | |
| $filename = $file['filename']; | |
| if ((bool) static::$config['normalize']) { | |
| $filename = \Inflector::friendly_title($filename, static::$config['normalize_separator']); | |
| } | |
| } | |
| array_key_exists('new_name', static::$config) and $filename = (string) static::$config['new_name']; | |
| // array with the final filename | |
| $save_as = array( | |
| static::$config['prefix'], | |
| $filename, | |
| static::$config['suffix'], | |
| '', | |
| '.', | |
| empty(static::$config['extension']) ? $file['extension'] : static::$config['extension'] | |
| ); | |
| // remove the dot if no extension is present | |
| if (empty($save_as[5])) { | |
| $save_as[4] = ''; | |
| } | |
| // need to modify case? | |
| switch (static::$config['change_case']) { | |
| case 'upper': | |
| $save_as = array_map(function($var) { | |
| return strtoupper($var); | |
| }, $save_as); | |
| break; | |
| case 'lower': | |
| $save_as = array_map(function($var) { | |
| return strtolower($var); | |
| }, $save_as); | |
| break; | |
| default: | |
| break; | |
| } | |
| // check if the file already exists | |
| if (file_exists($path . implode('', $save_as))) { | |
| if ((bool) static::$config['auto_rename']) { | |
| $counter = 0; | |
| do { | |
| $save_as[3] = '_' . ++$counter; | |
| } while (file_exists($path . implode('', $save_as))); | |
| } else { | |
| if (!(bool) static::$config['overwrite']) { | |
| static::$files[$key]['error'] = true; | |
| static::$files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_DUPLICATE_FILE); | |
| continue; | |
| } | |
| } | |
| } | |
| // no need to store it as an array anymore | |
| $save_as = implode('', $save_as); | |
| // does the filename exceed the maximum length? | |
| if (!empty(static::$config['max_length']) and strlen($save_as) > static::$config['max_length']) { | |
| static::$files[$key]['error'] = true; | |
| static::$files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_MAX_FILENAME_LENGTH); | |
| continue; | |
| } | |
| // if no error was detected, move the file | |
| if (!static::$files[$key]['error']) { | |
| // save the additional information | |
| static::$files[$key]['saved_to'] = $path; | |
| static::$files[$key]['saved_as'] = $save_as; | |
| // before callback defined? | |
| if (array_key_exists('before', static::$callbacks) and !is_null(static::$callbacks['before'])) { | |
| // get the callback method | |
| $callback = static::$callbacks['before'][0]; | |
| // call the callback | |
| if (is_callable($callback)) { | |
| $result = call_user_func_array($callback, array(&static::$files[$key])); | |
| if (is_numeric($result)) { | |
| static::$files[$key]['error'] = true; | |
| static::$files[$key]['errors'][] = array('error' => $result); | |
| } | |
| } | |
| // recheck the saved_to path, it might have been altered | |
| if (!is_dir(static::$files[$key]['saved_to']) and (bool) static::$config['create_path']) { | |
| @mkdir(static::$files[$key]['saved_to'], static::$config['path_chmod'], true); | |
| } | |
| if (!is_dir(static::$files[$key]['saved_to'])) { | |
| static::$files[$key]['error'] = true; | |
| static::$files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_MKDIR_FAILED); | |
| continue; | |
| } | |
| } | |
| // move the uploaded file | |
| if (!static::$files[$key]['error']) { | |
| // check if file should be uploaded with ftp | |
| if (static::$with_ftp) { | |
| $uploaded = static::$with_ftp->upload($file['file'], static::$config['path'] . static::$files[$key]['saved_as'], static::$config['ftp_mode'], static::$config['ftp_permissions']); | |
| if (!$uploaded) { | |
| static::$files[$key]['error'] = true; | |
| static::$files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_FTP_FAILED); | |
| } | |
| } else { | |
| if (!@move_uploaded_file($file['file'], static::$files[$key]['saved_to'] . static::$files[$key]['saved_as'])) { | |
| static::$files[$key]['error'] = true; | |
| static::$files[$key]['errors'][] = array('error' => static::UPLOAD_ERR_MOVE_FAILED); | |
| } else { | |
| @chmod(static::$files[$key]['saved_to'] . static::$files[$key]['saved_as'], static::$config['file_chmod']); | |
| } | |
| // after callback defined? | |
| if (array_key_exists('after', static::$callbacks) and !is_null(static::$callbacks['after'])) { | |
| // get the callback method | |
| $callback = static::$callbacks['after'][0]; | |
| // call the callback | |
| if (is_callable($callback)) { | |
| $result = call_user_func_array($callback, array(&static::$files[$key])); | |
| if (is_numeric($result)) { | |
| static::$files[$key]['error'] = true; | |
| static::$files[$key]['errors'][] = array('error' => $result); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // and add the message texts | |
| foreach (static::$files[$key]['errors'] as $e => $error) { | |
| empty(static::$files[$key]['errors'][$e]['message']) and static::$files[$key]['errors'][$e]['message'] = \Lang::get('upload.error_' . $error['error']); | |
| } | |
| // reset the umask | |
| umask($oldumask); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment