Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | espaco | 1 | <?php |
| 2 | /* |
||
| 3 | * jQuery File Upload Plugin PHP Class 8.1.0 |
||
| 4 | * https://github.com/blueimp/jQuery-File-Upload |
||
| 5 | * |
||
| 6 | * Copyright 2010, Sebastian Tschan |
||
| 7 | * https://blueimp.net |
||
| 8 | * |
||
| 9 | * Licensed under the MIT license: |
||
| 10 | * http://www.opensource.org/licenses/MIT |
||
| 11 | */ |
||
| 12 | |||
| 13 | class UploadHandler |
||
| 14 | { |
||
| 15 | |||
| 16 | protected $options; |
||
| 17 | |||
| 18 | // PHP File Upload error message codes: |
||
| 19 | // http://php.net/manual/en/features.file-upload.errors.php |
||
| 20 | protected $error_messages = array( |
||
| 21 | 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini', |
||
| 22 | 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form', |
||
| 23 | 3 => 'The uploaded file was only partially uploaded', |
||
| 24 | 4 => 'No file was uploaded', |
||
| 25 | 6 => 'Missing a temporary folder', |
||
| 26 | 7 => 'Failed to write file to disk', |
||
| 27 | 8 => 'A PHP extension stopped the file upload', |
||
| 28 | 'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini', |
||
| 29 | 'max_file_size' => 'File is too big', |
||
| 30 | 'min_file_size' => 'File is too small', |
||
| 31 | 'accept_file_types' => 'Filetype not allowed', |
||
| 32 | 'max_number_of_files' => 'Maximum number of files exceeded', |
||
| 33 | 'max_width' => 'Image exceeds maximum width', |
||
| 34 | 'min_width' => 'Image requires a minimum width', |
||
| 35 | 'max_height' => 'Image exceeds maximum height', |
||
| 36 | 'min_height' => 'Image requires a minimum height', |
||
| 37 | 'abort' => 'File upload aborted', |
||
| 38 | 'image_resize' => 'Failed to resize image' |
||
| 39 | ); |
||
| 40 | |||
| 41 | protected $image_objects = array(); |
||
| 42 | |||
| 43 | function __construct($options = null, $initialize = true, $error_messages = null) { |
||
| 44 | $this->options = array( |
||
| 45 | 'script_url' => $this->get_full_url().'/', |
||
| 46 | 'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/files/', |
||
| 47 | 'upload_url' => $this->get_full_url().'/files/', |
||
| 48 | 'user_dirs' => false, |
||
| 49 | 'mkdir_mode' => 0755, |
||
| 50 | 'param_name' => 'files', |
||
| 51 | // Set the following option to 'POST', if your server does not support |
||
| 52 | // DELETE requests. This is a parameter sent to the client: |
||
| 53 | 'delete_type' => 'DELETE', |
||
| 54 | 'access_control_allow_origin' => '*', |
||
| 55 | 'access_control_allow_credentials' => false, |
||
| 56 | 'access_control_allow_methods' => array( |
||
| 57 | 'OPTIONS', |
||
| 58 | 'HEAD', |
||
| 59 | 'GET', |
||
| 60 | 'POST', |
||
| 61 | 'PUT', |
||
| 62 | 'PATCH', |
||
| 63 | 'DELETE' |
||
| 64 | ), |
||
| 65 | 'access_control_allow_headers' => array( |
||
| 66 | 'Content-Type', |
||
| 67 | 'Content-Range', |
||
| 68 | 'Content-Disposition' |
||
| 69 | ), |
||
| 70 | // Enable to provide file downloads via GET requests to the PHP script: |
||
| 71 | // 1. Set to 1 to download files via readfile method through PHP |
||
| 72 | // 2. Set to 2 to send a X-Sendfile header for lighttpd/Apache |
||
| 73 | // 3. Set to 3 to send a X-Accel-Redirect header for nginx |
||
| 74 | // If set to 2 or 3, adjust the upload_url option to the base path of |
||
| 75 | // the redirect parameter, e.g. '/files/'. |
||
| 76 | 'download_via_php' => false, |
||
| 77 | // Read files in chunks to avoid memory limits when download_via_php |
||
| 78 | // is enabled, set to 0 to disable chunked reading of files: |
||
| 79 | 'readfile_chunk_size' => 10 * 1024 * 1024, // 10 MiB |
||
| 80 | // Defines which files can be displayed inline when downloaded: |
||
| 81 | 'inline_file_types' => '/\.(gif|jpe?g|png)$/i', |
||
| 82 | // Defines which files (based on their names) are accepted for upload: |
||
| 83 | 'accept_file_types' => '/.+$/i', |
||
| 84 | // The php.ini settings upload_max_filesize and post_max_size |
||
| 85 | // take precedence over the following max_file_size setting: |
||
| 86 | 'max_file_size' => null, |
||
| 87 | 'min_file_size' => 1, |
||
| 88 | // The maximum number of files for the upload directory: |
||
| 89 | 'max_number_of_files' => null, |
||
| 90 | // Defines which files are handled as image files: |
||
| 91 | 'image_file_types' => '/\.(gif|jpe?g|png)$/i', |
||
| 92 | // Use exif_imagetype on all files to correct file extensions: |
||
| 93 | 'correct_image_extensions' => false, |
||
| 94 | // Image resolution restrictions: |
||
| 95 | 'max_width' => null, |
||
| 96 | 'max_height' => null, |
||
| 97 | 'min_width' => 1, |
||
| 98 | 'min_height' => 1, |
||
| 99 | // Set the following option to false to enable resumable uploads: |
||
| 100 | 'discard_aborted_uploads' => true, |
||
| 101 | // Set to 0 to use the GD library to scale and orient images, |
||
| 102 | // set to 1 to use imagick (if installed, falls back to GD), |
||
| 103 | // set to 2 to use the ImageMagick convert binary directly: |
||
| 104 | 'image_library' => 1, |
||
| 105 | // Uncomment the following to define an array of resource limits |
||
| 106 | // for imagick: |
||
| 107 | /* |
||
| 108 | 'imagick_resource_limits' => array( |
||
| 109 | imagick::RESOURCETYPE_MAP => 32, |
||
| 110 | imagick::RESOURCETYPE_MEMORY => 32 |
||
| 111 | ), |
||
| 112 | */ |
||
| 113 | // Command or path for to the ImageMagick convert binary: |
||
| 114 | 'convert_bin' => 'convert', |
||
| 115 | // Uncomment the following to add parameters in front of each |
||
| 116 | // ImageMagick convert call (the limit constraints seem only |
||
| 117 | // to have an effect if put in front): |
||
| 118 | /* |
||
| 119 | 'convert_params' => '-limit memory 32MiB -limit map 32MiB', |
||
| 120 | */ |
||
| 121 | // Command or path for to the ImageMagick identify binary: |
||
| 122 | 'identify_bin' => 'identify', |
||
| 123 | 'image_versions' => array( |
||
| 124 | // The empty image version key defines options for the original image: |
||
| 125 | '' => array( |
||
| 126 | // Automatically rotate images based on EXIF meta data: |
||
| 127 | 'auto_orient' => true |
||
| 128 | ), |
||
| 129 | // Uncomment the following to create medium sized images: |
||
| 130 | /* |
||
| 131 | 'medium' => array( |
||
| 132 | 'max_width' => 800, |
||
| 133 | 'max_height' => 600 |
||
| 134 | ), |
||
| 135 | */ |
||
| 136 | 'thumbnail' => array( |
||
| 137 | // Uncomment the following to use a defined directory for the thumbnails |
||
| 138 | // instead of a subdirectory based on the version identifier. |
||
| 139 | // Make sure that this directory doesn't allow execution of files if you |
||
| 140 | // don't pose any restrictions on the type of uploaded files, e.g. by |
||
| 141 | // copying the .htaccess file from the files directory for Apache: |
||
| 142 | //'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/thumb/', |
||
| 143 | //'upload_url' => $this->get_full_url().'/thumb/', |
||
| 144 | // Uncomment the following to force the max |
||
| 145 | // dimensions and e.g. create square thumbnails: |
||
| 146 | //'crop' => true, |
||
| 147 | 'max_width' => 80, |
||
| 148 | 'max_height' => 80 |
||
| 149 | ) |
||
| 150 | ) |
||
| 151 | ); |
||
| 152 | if ($options) { |
||
| 153 | $this->options = $options + $this->options; |
||
| 154 | } |
||
| 155 | if ($error_messages) { |
||
| 156 | $this->error_messages = $error_messages + $this->error_messages; |
||
| 157 | } |
||
| 158 | if ($initialize) { |
||
| 159 | $this->initialize(); |
||
| 160 | } |
||
| 161 | } |
||
| 162 | |||
| 163 | protected function initialize() { |
||
| 164 | switch ($this->get_server_var('REQUEST_METHOD')) { |
||
| 165 | case 'OPTIONS': |
||
| 166 | case 'HEAD': |
||
| 167 | $this->head(); |
||
| 168 | break; |
||
| 169 | case 'GET': |
||
| 170 | $this->get(); |
||
| 171 | break; |
||
| 172 | case 'PATCH': |
||
| 173 | case 'PUT': |
||
| 174 | case 'POST': |
||
| 175 | $this->post(); |
||
| 176 | break; |
||
| 177 | case 'DELETE': |
||
| 178 | $this->delete(); |
||
| 179 | break; |
||
| 180 | default: |
||
| 181 | $this->header('HTTP/1.1 405 Method Not Allowed'); |
||
| 182 | } |
||
| 183 | } |
||
| 184 | |||
| 185 | protected function get_full_url() { |
||
| 186 | $https = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') === 0 || |
||
| 187 | !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && |
||
| 188 | strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0; |
||
| 189 | return |
||
| 190 | ($https ? 'https://' : 'http://'). |
||
| 191 | (!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : ''). |
||
| 192 | (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME']. |
||
| 193 | ($https && $_SERVER['SERVER_PORT'] === 443 || |
||
| 194 | $_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))). |
||
| 195 | substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/')); |
||
| 196 | } |
||
| 197 | |||
| 198 | protected function get_user_id() { |
||
| 199 | @session_start(); |
||
| 200 | return session_id(); |
||
| 201 | } |
||
| 202 | |||
| 203 | protected function get_user_path() { |
||
| 204 | if ($this->options['user_dirs']) { |
||
| 205 | return $this->get_user_id().'/'; |
||
| 206 | } |
||
| 207 | return ''; |
||
| 208 | } |
||
| 209 | |||
| 210 | protected function get_upload_path($file_name = null, $version = null) { |
||
| 211 | $file_name = $file_name ? $file_name : ''; |
||
| 212 | if (empty($version)) { |
||
| 213 | $version_path = ''; |
||
| 214 | } else { |
||
| 215 | $version_dir = @$this->options['image_versions'][$version]['upload_dir']; |
||
| 216 | if ($version_dir) { |
||
| 217 | return $version_dir.$this->get_user_path().$file_name; |
||
| 218 | } |
||
| 219 | $version_path = $version.'/'; |
||
| 220 | } |
||
| 221 | return $this->options['upload_dir'].$this->get_user_path() |
||
| 222 | .$version_path.$file_name; |
||
| 223 | } |
||
| 224 | |||
| 225 | protected function get_query_separator($url) { |
||
| 226 | return strpos($url, '?') === false ? '?' : '&'; |
||
| 227 | } |
||
| 228 | |||
| 229 | protected function get_download_url($file_name, $version = null, $direct = false) { |
||
| 230 | if (!$direct && $this->options['download_via_php']) { |
||
| 231 | $url = $this->options['script_url'] |
||
| 232 | .$this->get_query_separator($this->options['script_url']) |
||
| 233 | .$this->get_singular_param_name() |
||
| 234 | .'='.rawurlencode($file_name); |
||
| 235 | if ($version) { |
||
| 236 | $url .= '&version='.rawurlencode($version); |
||
| 237 | } |
||
| 238 | return $url.'&download=1'; |
||
| 239 | } |
||
| 240 | if (empty($version)) { |
||
| 241 | $version_path = ''; |
||
| 242 | } else { |
||
| 243 | $version_url = @$this->options['image_versions'][$version]['upload_url']; |
||
| 244 | if ($version_url) { |
||
| 245 | return $version_url.$this->get_user_path().rawurlencode($file_name); |
||
| 246 | } |
||
| 247 | $version_path = rawurlencode($version).'/'; |
||
| 248 | } |
||
| 249 | return $this->options['upload_url'].$this->get_user_path() |
||
| 250 | .$version_path.rawurlencode($file_name); |
||
| 251 | } |
||
| 252 | |||
| 253 | protected function set_additional_file_properties($file) { |
||
| 254 | $file->deleteUrl = $this->options['script_url'] |
||
| 255 | .$this->get_query_separator($this->options['script_url']) |
||
| 256 | .$this->get_singular_param_name() |
||
| 257 | .'='.rawurlencode($file->name); |
||
| 258 | $file->deleteType = $this->options['delete_type']; |
||
| 259 | if ($file->deleteType !== 'DELETE') { |
||
| 260 | $file->deleteUrl .= '&_method=DELETE'; |
||
| 261 | } |
||
| 262 | if ($this->options['access_control_allow_credentials']) { |
||
| 263 | $file->deleteWithCredentials = true; |
||
| 264 | } |
||
| 265 | } |
||
| 266 | |||
| 267 | // Fix for overflowing signed 32 bit integers, |
||
| 268 | // works for sizes up to 2^32-1 bytes (4 GiB - 1): |
||
| 269 | protected function fix_integer_overflow($size) { |
||
| 270 | if ($size < 0) { |
||
| 271 | $size += 2.0 * (PHP_INT_MAX + 1); |
||
| 272 | } |
||
| 273 | return $size; |
||
| 274 | } |
||
| 275 | |||
| 276 | protected function get_file_size($file_path, $clear_stat_cache = false) { |
||
| 277 | if ($clear_stat_cache) { |
||
| 278 | if (version_compare(PHP_VERSION, '5.3.0') >= 0) { |
||
| 279 | clearstatcache(true, $file_path); |
||
| 280 | } else { |
||
| 281 | clearstatcache(); |
||
| 282 | } |
||
| 283 | } |
||
| 284 | return $this->fix_integer_overflow(filesize($file_path)); |
||
| 285 | } |
||
| 286 | |||
| 287 | protected function is_valid_file_object($file_name) { |
||
| 288 | $file_path = $this->get_upload_path($file_name); |
||
| 289 | if (is_file($file_path) && $file_name[0] !== '.') { |
||
| 290 | return true; |
||
| 291 | } |
||
| 292 | return false; |
||
| 293 | } |
||
| 294 | |||
| 295 | protected function get_file_object($file_name) { |
||
| 296 | if ($this->is_valid_file_object($file_name)) { |
||
| 297 | $file = new \stdClass(); |
||
| 298 | $file->name = $file_name; |
||
| 299 | $file->size = $this->get_file_size( |
||
| 300 | $this->get_upload_path($file_name) |
||
| 301 | ); |
||
| 302 | $file->url = $this->get_download_url($file->name); |
||
| 303 | foreach($this->options['image_versions'] as $version => $options) { |
||
| 304 | if (!empty($version)) { |
||
| 305 | if (is_file($this->get_upload_path($file_name, $version))) { |
||
| 306 | $file->{$version.'Url'} = $this->get_download_url( |
||
| 307 | $file->name, |
||
| 308 | $version |
||
| 309 | ); |
||
| 310 | } |
||
| 311 | } |
||
| 312 | } |
||
| 313 | $this->set_additional_file_properties($file); |
||
| 314 | return $file; |
||
| 315 | } |
||
| 316 | return null; |
||
| 317 | } |
||
| 318 | |||
| 319 | protected function get_file_objects($iteration_method = 'get_file_object') { |
||
| 320 | $upload_dir = $this->get_upload_path(); |
||
| 321 | if (!is_dir($upload_dir)) { |
||
| 322 | return array(); |
||
| 323 | } |
||
| 324 | return array_values(array_filter(array_map( |
||
| 325 | array($this, $iteration_method), |
||
| 326 | scandir($upload_dir) |
||
| 327 | ))); |
||
| 328 | } |
||
| 329 | |||
| 330 | protected function count_file_objects() { |
||
| 331 | return count($this->get_file_objects('is_valid_file_object')); |
||
| 332 | } |
||
| 333 | |||
| 334 | protected function get_error_message($error) { |
||
| 335 | return array_key_exists($error, $this->error_messages) ? |
||
| 336 | $this->error_messages[$error] : $error; |
||
| 337 | } |
||
| 338 | |||
| 339 | function get_config_bytes($val) { |
||
| 340 | $val = trim($val); |
||
| 341 | $last = strtolower($val[strlen($val)-1]); |
||
| 342 | switch($last) { |
||
| 343 | case 'g': |
||
| 344 | $val *= 1024; |
||
| 345 | case 'm': |
||
| 346 | $val *= 1024; |
||
| 347 | case 'k': |
||
| 348 | $val *= 1024; |
||
| 349 | } |
||
| 350 | return $this->fix_integer_overflow($val); |
||
| 351 | } |
||
| 352 | |||
| 353 | protected function validate($uploaded_file, $file, $error, $index) { |
||
| 354 | if ($error) { |
||
| 355 | $file->error = $this->get_error_message($error); |
||
| 356 | return false; |
||
| 357 | } |
||
| 358 | $content_length = $this->fix_integer_overflow(intval( |
||
| 359 | $this->get_server_var('CONTENT_LENGTH') |
||
| 360 | )); |
||
| 361 | $post_max_size = $this->get_config_bytes(ini_get('post_max_size')); |
||
| 362 | if ($post_max_size && ($content_length > $post_max_size)) { |
||
| 363 | $file->error = $this->get_error_message('post_max_size'); |
||
| 364 | return false; |
||
| 365 | } |
||
| 366 | if (!preg_match($this->options['accept_file_types'], $file->name)) { |
||
| 367 | $file->error = $this->get_error_message('accept_file_types'); |
||
| 368 | return false; |
||
| 369 | } |
||
| 370 | if ($uploaded_file && is_uploaded_file($uploaded_file)) { |
||
| 371 | $file_size = $this->get_file_size($uploaded_file); |
||
| 372 | } else { |
||
| 373 | $file_size = $content_length; |
||
| 374 | } |
||
| 375 | if ($this->options['max_file_size'] && ( |
||
| 376 | $file_size > $this->options['max_file_size'] || |
||
| 377 | $file->size > $this->options['max_file_size']) |
||
| 378 | ) { |
||
| 379 | $file->error = $this->get_error_message('max_file_size'); |
||
| 380 | return false; |
||
| 381 | } |
||
| 382 | if ($this->options['min_file_size'] && |
||
| 383 | $file_size < $this->options['min_file_size']) { |
||
| 384 | $file->error = $this->get_error_message('min_file_size'); |
||
| 385 | return false; |
||
| 386 | } |
||
| 387 | if (is_int($this->options['max_number_of_files']) && |
||
| 388 | ($this->count_file_objects() >= $this->options['max_number_of_files']) && |
||
| 389 | // Ignore additional chunks of existing files: |
||
| 390 | !is_file($this->get_upload_path($file->name))) { |
||
| 391 | $file->error = $this->get_error_message('max_number_of_files'); |
||
| 392 | return false; |
||
| 393 | } |
||
| 394 | $max_width = @$this->options['max_width']; |
||
| 395 | $max_height = @$this->options['max_height']; |
||
| 396 | $min_width = @$this->options['min_width']; |
||
| 397 | $min_height = @$this->options['min_height']; |
||
| 398 | if (($max_width || $max_height || $min_width || $min_height) |
||
| 399 | && preg_match($this->options['image_file_types'], $file->name)) { |
||
| 400 | list($img_width, $img_height) = $this->get_image_size($uploaded_file); |
||
| 401 | } |
||
| 402 | if (!empty($img_width)) { |
||
| 403 | if ($max_width && $img_width > $max_width) { |
||
| 404 | $file->error = $this->get_error_message('max_width'); |
||
| 405 | return false; |
||
| 406 | } |
||
| 407 | if ($max_height && $img_height > $max_height) { |
||
| 408 | $file->error = $this->get_error_message('max_height'); |
||
| 409 | return false; |
||
| 410 | } |
||
| 411 | if ($min_width && $img_width < $min_width) { |
||
| 412 | $file->error = $this->get_error_message('min_width'); |
||
| 413 | return false; |
||
| 414 | } |
||
| 415 | if ($min_height && $img_height < $min_height) { |
||
| 416 | $file->error = $this->get_error_message('min_height'); |
||
| 417 | return false; |
||
| 418 | } |
||
| 419 | } |
||
| 420 | return true; |
||
| 421 | } |
||
| 422 | |||
| 423 | protected function upcount_name_callback($matches) { |
||
| 424 | $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1; |
||
| 425 | $ext = isset($matches[2]) ? $matches[2] : ''; |
||
| 426 | return ' ('.$index.')'.$ext; |
||
| 427 | } |
||
| 428 | |||
| 429 | protected function upcount_name($name) { |
||
| 430 | return preg_replace_callback( |
||
| 431 | '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/', |
||
| 432 | array($this, 'upcount_name_callback'), |
||
| 433 | $name, |
||
| 434 | 1 |
||
| 435 | ); |
||
| 436 | } |
||
| 437 | |||
| 438 | protected function get_unique_filename($file_path, $name, $size, $type, $error, |
||
| 439 | $index, $content_range) { |
||
| 440 | while(is_dir($this->get_upload_path($name))) { |
||
| 441 | $name = $this->upcount_name($name); |
||
| 442 | } |
||
| 443 | // Keep an existing filename if this is part of a chunked upload: |
||
| 444 | $uploaded_bytes = $this->fix_integer_overflow(intval($content_range[1])); |
||
| 445 | while(is_file($this->get_upload_path($name))) { |
||
| 446 | if ($uploaded_bytes === $this->get_file_size( |
||
| 447 | $this->get_upload_path($name))) { |
||
| 448 | break; |
||
| 449 | } |
||
| 450 | $name = $this->upcount_name($name); |
||
| 451 | } |
||
| 452 | return $name; |
||
| 453 | } |
||
| 454 | |||
| 455 | protected function fix_file_extension($file_path, $name, $size, $type, $error, |
||
| 456 | $index, $content_range) { |
||
| 457 | // Add missing file extension for known image types: |
||
| 458 | if (strpos($name, '.') === false && |
||
| 459 | preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) { |
||
| 460 | $name .= '.'.$matches[1]; |
||
| 461 | } |
||
| 462 | if ($this->options['correct_image_extensions'] && |
||
| 463 | function_exists('exif_imagetype')) { |
||
| 464 | switch(@exif_imagetype($file_path)){ |
||
| 465 | case IMAGETYPE_JPEG: |
||
| 466 | $extensions = array('jpg', 'jpeg'); |
||
| 467 | break; |
||
| 468 | case IMAGETYPE_PNG: |
||
| 469 | $extensions = array('png'); |
||
| 470 | break; |
||
| 471 | case IMAGETYPE_GIF: |
||
| 472 | $extensions = array('gif'); |
||
| 473 | break; |
||
| 474 | } |
||
| 475 | // Adjust incorrect image file extensions: |
||
| 476 | if (!empty($extensions)) { |
||
| 477 | $parts = explode('.', $name); |
||
| 478 | $extIndex = count($parts) - 1; |
||
| 479 | $ext = strtolower(@$parts[$extIndex]); |
||
| 480 | if (!in_array($ext, $extensions)) { |
||
| 481 | $parts[$extIndex] = $extensions[0]; |
||
| 482 | $name = implode('.', $parts); |
||
| 483 | } |
||
| 484 | } |
||
| 485 | } |
||
| 486 | return $name; |
||
| 487 | } |
||
| 488 | |||
| 489 | protected function trim_file_name($file_path, $name, $size, $type, $error, |
||
| 490 | $index, $content_range) { |
||
| 491 | // Remove path information and dots around the filename, to prevent uploading |
||
| 492 | // into different directories or replacing hidden system files. |
||
| 493 | // Also remove control characters and spaces (\x00..\x20) around the filename: |
||
| 494 | $name = trim(basename(stripslashes($name)), ".\x00..\x20"); |
||
| 495 | // Use a timestamp for empty filenames: |
||
| 496 | if (!$name) { |
||
| 497 | $name = str_replace('.', '-', microtime(true)); |
||
| 498 | } |
||
| 499 | return $name; |
||
| 500 | } |
||
| 501 | |||
| 502 | protected function get_file_name($file_path, $name, $size, $type, $error, |
||
| 503 | $index, $content_range) { |
||
| 504 | $name = $this->trim_file_name($file_path, $name, $size, $type, $error, |
||
| 505 | $index, $content_range); |
||
| 506 | return $this->get_unique_filename( |
||
| 507 | $file_path, |
||
| 508 | $this->fix_file_extension($file_path, $name, $size, $type, $error, |
||
| 509 | $index, $content_range), |
||
| 510 | $size, |
||
| 511 | $type, |
||
| 512 | $error, |
||
| 513 | $index, |
||
| 514 | $content_range |
||
| 515 | ); |
||
| 516 | } |
||
| 517 | |||
| 518 | protected function handle_form_data($file, $index) { |
||
| 519 | // Handle form data, e.g. $_REQUEST['description'][$index] |
||
| 520 | } |
||
| 521 | |||
| 522 | protected function get_scaled_image_file_paths($file_name, $version) { |
||
| 523 | $file_path = $this->get_upload_path($file_name); |
||
| 524 | if (!empty($version)) { |
||
| 525 | $version_dir = $this->get_upload_path(null, $version); |
||
| 526 | if (!is_dir($version_dir)) { |
||
| 527 | mkdir($version_dir, $this->options['mkdir_mode'], true); |
||
| 528 | } |
||
| 529 | $new_file_path = $version_dir.'/'.$file_name; |
||
| 530 | } else { |
||
| 531 | $new_file_path = $file_path; |
||
| 532 | } |
||
| 533 | return array($file_path, $new_file_path); |
||
| 534 | } |
||
| 535 | |||
| 536 | protected function gd_get_image_object($file_path, $func, $no_cache = false) { |
||
| 537 | if (empty($this->image_objects[$file_path]) || $no_cache) { |
||
| 538 | $this->gd_destroy_image_object($file_path); |
||
| 539 | $this->image_objects[$file_path] = $func($file_path); |
||
| 540 | } |
||
| 541 | return $this->image_objects[$file_path]; |
||
| 542 | } |
||
| 543 | |||
| 544 | protected function gd_set_image_object($file_path, $image) { |
||
| 545 | $this->gd_destroy_image_object($file_path); |
||
| 546 | $this->image_objects[$file_path] = $image; |
||
| 547 | } |
||
| 548 | |||
| 549 | protected function gd_destroy_image_object($file_path) { |
||
| 550 | $image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null ; |
||
| 551 | return $image && imagedestroy($image); |
||
| 552 | } |
||
| 553 | |||
| 554 | protected function gd_imageflip($image, $mode) { |
||
| 555 | if (function_exists('imageflip')) { |
||
| 556 | return imageflip($image, $mode); |
||
| 557 | } |
||
| 558 | $new_width = $src_width = imagesx($image); |
||
| 559 | $new_height = $src_height = imagesy($image); |
||
| 560 | $new_img = imagecreatetruecolor($new_width, $new_height); |
||
| 561 | $src_x = 0; |
||
| 562 | $src_y = 0; |
||
| 563 | switch ($mode) { |
||
| 564 | case '1': // flip on the horizontal axis |
||
| 565 | $src_y = $new_height - 1; |
||
| 566 | $src_height = -$new_height; |
||
| 567 | break; |
||
| 568 | case '2': // flip on the vertical axis |
||
| 569 | $src_x = $new_width - 1; |
||
| 570 | $src_width = -$new_width; |
||
| 571 | break; |
||
| 572 | case '3': // flip on both axes |
||
| 573 | $src_y = $new_height - 1; |
||
| 574 | $src_height = -$new_height; |
||
| 575 | $src_x = $new_width - 1; |
||
| 576 | $src_width = -$new_width; |
||
| 577 | break; |
||
| 578 | default: |
||
| 579 | return $image; |
||
| 580 | } |
||
| 581 | imagecopyresampled( |
||
| 582 | $new_img, |
||
| 583 | $image, |
||
| 584 | 0, |
||
| 585 | 0, |
||
| 586 | $src_x, |
||
| 587 | $src_y, |
||
| 588 | $new_width, |
||
| 589 | $new_height, |
||
| 590 | $src_width, |
||
| 591 | $src_height |
||
| 592 | ); |
||
| 593 | return $new_img; |
||
| 594 | } |
||
| 595 | |||
| 596 | protected function gd_orient_image($file_path, $src_img) { |
||
| 597 | if (!function_exists('exif_read_data')) { |
||
| 598 | return false; |
||
| 599 | } |
||
| 600 | $exif = @exif_read_data($file_path); |
||
| 601 | if ($exif === false) { |
||
| 602 | return false; |
||
| 603 | } |
||
| 604 | $orientation = intval(@$exif['Orientation']); |
||
| 605 | if ($orientation < 2 || $orientation > 8) { |
||
| 606 | return false; |
||
| 607 | } |
||
| 608 | switch ($orientation) { |
||
| 609 | case 2: |
||
| 610 | $new_img = $this->gd_imageflip( |
||
| 611 | $src_img, |
||
| 612 | defined('IMG_FLIP_VERTICAL') ? IMG_FLIP_VERTICAL : 2 |
||
| 613 | ); |
||
| 614 | break; |
||
| 615 | case 3: |
||
| 616 | $new_img = imagerotate($src_img, 180, 0); |
||
| 617 | break; |
||
| 618 | case 4: |
||
| 619 | $new_img = $this->gd_imageflip( |
||
| 620 | $src_img, |
||
| 621 | defined('IMG_FLIP_HORIZONTAL') ? IMG_FLIP_HORIZONTAL : 1 |
||
| 622 | ); |
||
| 623 | break; |
||
| 624 | case 5: |
||
| 625 | $tmp_img = $this->gd_imageflip( |
||
| 626 | $src_img, |
||
| 627 | defined('IMG_FLIP_HORIZONTAL') ? IMG_FLIP_HORIZONTAL : 1 |
||
| 628 | ); |
||
| 629 | $new_img = imagerotate($tmp_img, 270, 0); |
||
| 630 | imagedestroy($tmp_img); |
||
| 631 | break; |
||
| 632 | case 6: |
||
| 633 | $new_img = imagerotate($src_img, 270, 0); |
||
| 634 | break; |
||
| 635 | case 7: |
||
| 636 | $tmp_img = $this->gd_imageflip( |
||
| 637 | $src_img, |
||
| 638 | defined('IMG_FLIP_VERTICAL') ? IMG_FLIP_VERTICAL : 2 |
||
| 639 | ); |
||
| 640 | $new_img = imagerotate($tmp_img, 270, 0); |
||
| 641 | imagedestroy($tmp_img); |
||
| 642 | break; |
||
| 643 | case 8: |
||
| 644 | $new_img = imagerotate($src_img, 90, 0); |
||
| 645 | break; |
||
| 646 | default: |
||
| 647 | return false; |
||
| 648 | } |
||
| 649 | $this->gd_set_image_object($file_path, $new_img); |
||
| 650 | return true; |
||
| 651 | } |
||
| 652 | |||
| 653 | protected function gd_create_scaled_image($file_name, $version, $options) { |
||
| 654 | if (!function_exists('imagecreatetruecolor')) { |
||
| 655 | error_log('Function not found: imagecreatetruecolor'); |
||
| 656 | return false; |
||
| 657 | } |
||
| 658 | list($file_path, $new_file_path) = |
||
| 659 | $this->get_scaled_image_file_paths($file_name, $version); |
||
| 660 | $type = strtolower(substr(strrchr($file_name, '.'), 1)); |
||
| 661 | switch ($type) { |
||
| 662 | case 'jpg': |
||
| 663 | case 'jpeg': |
||
| 664 | $src_func = 'imagecreatefromjpeg'; |
||
| 665 | $write_func = 'imagejpeg'; |
||
| 666 | $image_quality = isset($options['jpeg_quality']) ? |
||
| 667 | $options['jpeg_quality'] : 75; |
||
| 668 | break; |
||
| 669 | case 'gif': |
||
| 670 | $src_func = 'imagecreatefromgif'; |
||
| 671 | $write_func = 'imagegif'; |
||
| 672 | $image_quality = null; |
||
| 673 | break; |
||
| 674 | case 'png': |
||
| 675 | $src_func = 'imagecreatefrompng'; |
||
| 676 | $write_func = 'imagepng'; |
||
| 677 | $image_quality = isset($options['png_quality']) ? |
||
| 678 | $options['png_quality'] : 9; |
||
| 679 | break; |
||
| 680 | default: |
||
| 681 | return false; |
||
| 682 | } |
||
| 683 | $src_img = $this->gd_get_image_object( |
||
| 684 | $file_path, |
||
| 685 | $src_func, |
||
| 686 | !empty($options['no_cache']) |
||
| 687 | ); |
||
| 688 | $image_oriented = false; |
||
| 689 | if (!empty($options['auto_orient']) && $this->gd_orient_image( |
||
| 690 | $file_path, |
||
| 691 | $src_img |
||
| 692 | )) { |
||
| 693 | $image_oriented = true; |
||
| 694 | $src_img = $this->gd_get_image_object( |
||
| 695 | $file_path, |
||
| 696 | $src_func |
||
| 697 | ); |
||
| 698 | } |
||
| 699 | $max_width = $img_width = imagesx($src_img); |
||
| 700 | $max_height = $img_height = imagesy($src_img); |
||
| 701 | if (!empty($options['max_width'])) { |
||
| 702 | $max_width = $options['max_width']; |
||
| 703 | } |
||
| 704 | if (!empty($options['max_height'])) { |
||
| 705 | $max_height = $options['max_height']; |
||
| 706 | } |
||
| 707 | $scale = min( |
||
| 708 | $max_width / $img_width, |
||
| 709 | $max_height / $img_height |
||
| 710 | ); |
||
| 711 | if ($scale >= 1) { |
||
| 712 | if ($image_oriented) { |
||
| 713 | return $write_func($src_img, $new_file_path, $image_quality); |
||
| 714 | } |
||
| 715 | if ($file_path !== $new_file_path) { |
||
| 716 | return copy($file_path, $new_file_path); |
||
| 717 | } |
||
| 718 | return true; |
||
| 719 | } |
||
| 720 | if (empty($options['crop'])) { |
||
| 721 | $new_width = $img_width * $scale; |
||
| 722 | $new_height = $img_height * $scale; |
||
| 723 | $dst_x = 0; |
||
| 724 | $dst_y = 0; |
||
| 725 | $new_img = imagecreatetruecolor($new_width, $new_height); |
||
| 726 | } else { |
||
| 727 | if (($img_width / $img_height) >= ($max_width / $max_height)) { |
||
| 728 | $new_width = $img_width / ($img_height / $max_height); |
||
| 729 | $new_height = $max_height; |
||
| 730 | } else { |
||
| 731 | $new_width = $max_width; |
||
| 732 | $new_height = $img_height / ($img_width / $max_width); |
||
| 733 | } |
||
| 734 | $dst_x = 0 - ($new_width - $max_width) / 2; |
||
| 735 | $dst_y = 0 - ($new_height - $max_height) / 2; |
||
| 736 | $new_img = imagecreatetruecolor($max_width, $max_height); |
||
| 737 | } |
||
| 738 | // Handle transparency in GIF and PNG images: |
||
| 739 | switch ($type) { |
||
| 740 | case 'gif': |
||
| 741 | case 'png': |
||
| 742 | imagecolortransparent($new_img, imagecolorallocate($new_img, 0, 0, 0)); |
||
| 743 | case 'png': |
||
| 744 | imagealphablending($new_img, false); |
||
| 745 | imagesavealpha($new_img, true); |
||
| 746 | break; |
||
| 747 | } |
||
| 748 | $success = imagecopyresampled( |
||
| 749 | $new_img, |
||
| 750 | $src_img, |
||
| 751 | $dst_x, |
||
| 752 | $dst_y, |
||
| 753 | 0, |
||
| 754 | 0, |
||
| 755 | $new_width, |
||
| 756 | $new_height, |
||
| 757 | $img_width, |
||
| 758 | $img_height |
||
| 759 | ) && $write_func($new_img, $new_file_path, $image_quality); |
||
| 760 | $this->gd_set_image_object($file_path, $new_img); |
||
| 761 | return $success; |
||
| 762 | } |
||
| 763 | |||
| 764 | protected function imagick_get_image_object($file_path, $no_cache = false) { |
||
| 765 | if (empty($this->image_objects[$file_path]) || $no_cache) { |
||
| 766 | $this->imagick_destroy_image_object($file_path); |
||
| 767 | $image = new \Imagick(); |
||
| 768 | if (!empty($this->options['imagick_resource_limits'])) { |
||
| 769 | foreach ($this->options['imagick_resource_limits'] as $type => $limit) { |
||
| 770 | $image->setResourceLimit($type, $limit); |
||
| 771 | } |
||
| 772 | } |
||
| 773 | $image->readImage($file_path); |
||
| 774 | $this->image_objects[$file_path] = $image; |
||
| 775 | } |
||
| 776 | return $this->image_objects[$file_path]; |
||
| 777 | } |
||
| 778 | |||
| 779 | protected function imagick_set_image_object($file_path, $image) { |
||
| 780 | $this->imagick_destroy_image_object($file_path); |
||
| 781 | $this->image_objects[$file_path] = $image; |
||
| 782 | } |
||
| 783 | |||
| 784 | protected function imagick_destroy_image_object($file_path) { |
||
| 785 | $image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null ; |
||
| 786 | return $image && $image->destroy(); |
||
| 787 | } |
||
| 788 | |||
| 789 | protected function imagick_orient_image($image) { |
||
| 790 | $orientation = $image->getImageOrientation(); |
||
| 791 | $background = new \ImagickPixel('none'); |
||
| 792 | switch ($orientation) { |
||
| 793 | case \imagick::ORIENTATION_TOPRIGHT: // 2 |
||
| 794 | $image->flopImage(); // horizontal flop around y-axis |
||
| 795 | break; |
||
| 796 | case \imagick::ORIENTATION_BOTTOMRIGHT: // 3 |
||
| 797 | $image->rotateImage($background, 180); |
||
| 798 | break; |
||
| 799 | case \imagick::ORIENTATION_BOTTOMLEFT: // 4 |
||
| 800 | $image->flipImage(); // vertical flip around x-axis |
||
| 801 | break; |
||
| 802 | case \imagick::ORIENTATION_LEFTTOP: // 5 |
||
| 803 | $image->flopImage(); // horizontal flop around y-axis |
||
| 804 | $image->rotateImage($background, 270); |
||
| 805 | break; |
||
| 806 | case \imagick::ORIENTATION_RIGHTTOP: // 6 |
||
| 807 | $image->rotateImage($background, 90); |
||
| 808 | break; |
||
| 809 | case \imagick::ORIENTATION_RIGHTBOTTOM: // 7 |
||
| 810 | $image->flipImage(); // vertical flip around x-axis |
||
| 811 | $image->rotateImage($background, 270); |
||
| 812 | break; |
||
| 813 | case \imagick::ORIENTATION_LEFTBOTTOM: // 8 |
||
| 814 | $image->rotateImage($background, 270); |
||
| 815 | break; |
||
| 816 | default: |
||
| 817 | return false; |
||
| 818 | } |
||
| 819 | $image->setImageOrientation(\imagick::ORIENTATION_TOPLEFT); // 1 |
||
| 820 | return true; |
||
| 821 | } |
||
| 822 | |||
| 823 | protected function imagick_create_scaled_image($file_name, $version, $options) { |
||
| 824 | list($file_path, $new_file_path) = |
||
| 825 | $this->get_scaled_image_file_paths($file_name, $version); |
||
| 826 | $image = $this->imagick_get_image_object( |
||
| 827 | $file_path, |
||
| 828 | !empty($options['no_cache']) |
||
| 829 | ); |
||
| 830 | if ($image->getImageFormat() === 'GIF') { |
||
| 831 | // Handle animated GIFs: |
||
| 832 | $images = $image->coalesceImages(); |
||
| 833 | foreach ($images as $frame) { |
||
| 834 | $image = $frame; |
||
| 835 | $this->imagick_set_image_object($file_name, $image); |
||
| 836 | break; |
||
| 837 | } |
||
| 838 | } |
||
| 839 | $image_oriented = false; |
||
| 840 | if (!empty($options['auto_orient'])) { |
||
| 841 | $image_oriented = $this->imagick_orient_image($image); |
||
| 842 | } |
||
| 843 | $new_width = $max_width = $img_width = $image->getImageWidth(); |
||
| 844 | $new_height = $max_height = $img_height = $image->getImageHeight(); |
||
| 845 | if (!empty($options['max_width'])) { |
||
| 846 | $new_width = $max_width = $options['max_width']; |
||
| 847 | } |
||
| 848 | if (!empty($options['max_height'])) { |
||
| 849 | $new_height = $max_height = $options['max_height']; |
||
| 850 | } |
||
| 851 | if (!($image_oriented || $max_width < $img_width || $max_height < $img_height)) { |
||
| 852 | if ($file_path !== $new_file_path) { |
||
| 853 | return copy($file_path, $new_file_path); |
||
| 854 | } |
||
| 855 | return true; |
||
| 856 | } |
||
| 857 | $crop = !empty($options['crop']); |
||
| 858 | if ($crop) { |
||
| 859 | $x = 0; |
||
| 860 | $y = 0; |
||
| 861 | if (($img_width / $img_height) >= ($max_width / $max_height)) { |
||
| 862 | $new_width = 0; // Enables proportional scaling based on max_height |
||
| 863 | $x = ($img_width / ($img_height / $max_height) - $max_width) / 2; |
||
| 864 | } else { |
||
| 865 | $new_height = 0; // Enables proportional scaling based on max_width |
||
| 866 | $y = ($img_height / ($img_width / $max_width) - $max_height) / 2; |
||
| 867 | } |
||
| 868 | } |
||
| 869 | $success = $image->resizeImage( |
||
| 870 | $new_width, |
||
| 871 | $new_height, |
||
| 872 | isset($options['filter']) ? $options['filter'] : \imagick::FILTER_LANCZOS, |
||
| 873 | isset($options['blur']) ? $options['blur'] : 1, |
||
| 874 | $new_width && $new_height // fit image into constraints if not to be cropped |
||
| 875 | ); |
||
| 876 | if ($success && $crop) { |
||
| 877 | $success = $image->cropImage( |
||
| 878 | $max_width, |
||
| 879 | $max_height, |
||
| 880 | $x, |
||
| 881 | $y |
||
| 882 | ); |
||
| 883 | if ($success) { |
||
| 884 | $success = $image->setImagePage($max_width, $max_height, 0, 0); |
||
| 885 | } |
||
| 886 | } |
||
| 887 | $type = strtolower(substr(strrchr($file_name, '.'), 1)); |
||
| 888 | switch ($type) { |
||
| 889 | case 'jpg': |
||
| 890 | case 'jpeg': |
||
| 891 | if (!empty($options['jpeg_quality'])) { |
||
| 892 | $image->setImageCompression(\imagick::COMPRESSION_JPEG); |
||
| 893 | $image->setImageCompressionQuality($options['jpeg_quality']); |
||
| 894 | } |
||
| 895 | break; |
||
| 896 | } |
||
| 897 | if (!empty($options['strip'])) { |
||
| 898 | $image->stripImage(); |
||
| 899 | } |
||
| 900 | return $success && $image->writeImage($new_file_path); |
||
| 901 | } |
||
| 902 | |||
| 903 | protected function imagemagick_create_scaled_image($file_name, $version, $options) { |
||
| 904 | list($file_path, $new_file_path) = |
||
| 905 | $this->get_scaled_image_file_paths($file_name, $version); |
||
| 906 | $resize = @$options['max_width'] |
||
| 907 | .(empty($options['max_height']) ? '' : 'X'.$options['max_height']); |
||
| 908 | if (!$resize && empty($options['auto_orient'])) { |
||
| 909 | if ($file_path !== $new_file_path) { |
||
| 910 | return copy($file_path, $new_file_path); |
||
| 911 | } |
||
| 912 | return true; |
||
| 913 | } |
||
| 914 | $cmd = $this->options['convert_bin']; |
||
| 915 | if (!empty($this->options['convert_params'])) { |
||
| 916 | $cmd .= ' '.$this->options['convert_params']; |
||
| 917 | } |
||
| 918 | $cmd .= ' '.escapeshellarg($file_path); |
||
| 919 | if (!empty($options['auto_orient'])) { |
||
| 920 | $cmd .= ' -auto-orient'; |
||
| 921 | } |
||
| 922 | if ($resize) { |
||
| 923 | // Handle animated GIFs: |
||
| 924 | $cmd .= ' -coalesce'; |
||
| 925 | if (empty($options['crop'])) { |
||
| 926 | $cmd .= ' -resize '.escapeshellarg($resize.'>'); |
||
| 927 | } else { |
||
| 928 | $cmd .= ' -resize '.escapeshellarg($resize.'^'); |
||
| 929 | $cmd .= ' -gravity center'; |
||
| 930 | $cmd .= ' -crop '.escapeshellarg($resize.'+0+0'); |
||
| 931 | } |
||
| 932 | // Make sure the page dimensions are correct (fixes offsets of animated GIFs): |
||
| 933 | $cmd .= ' +repage'; |
||
| 934 | } |
||
| 935 | if (!empty($options['convert_params'])) { |
||
| 936 | $cmd .= ' '.$options['convert_params']; |
||
| 937 | } |
||
| 938 | $cmd .= ' '.escapeshellarg($new_file_path); |
||
| 939 | exec($cmd, $output, $error); |
||
| 940 | if ($error) { |
||
| 941 | error_log(implode('\n', $output)); |
||
| 942 | return false; |
||
| 943 | } |
||
| 944 | return true; |
||
| 945 | } |
||
| 946 | |||
| 947 | protected function get_image_size($file_path) { |
||
| 948 | if ($this->options['image_library']) { |
||
| 949 | if (extension_loaded('imagick')) { |
||
| 950 | $image = new \Imagick(); |
||
| 951 | try { |
||
| 952 | if (@$image->pingImage($file_path)) { |
||
| 953 | $dimensions = array($image->getImageWidth(), $image->getImageHeight()); |
||
| 954 | $image->destroy(); |
||
| 955 | return $dimensions; |
||
| 956 | } |
||
| 957 | return false; |
||
| 958 | } catch (Exception $e) { |
||
| 959 | error_log($e->getMessage()); |
||
| 960 | } |
||
| 961 | } |
||
| 962 | if ($this->options['image_library'] === 2) { |
||
| 963 | $cmd = $this->options['identify_bin']; |
||
| 964 | $cmd .= ' -ping '.escapeshellarg($file_path); |
||
| 965 | exec($cmd, $output, $error); |
||
| 966 | if (!$error && !empty($output)) { |
||
| 967 | // image.jpg JPEG 1920x1080 1920x1080+0+0 8-bit sRGB 465KB 0.000u 0:00.000 |
||
| 968 | $infos = preg_split('/\s+/', $output[0]); |
||
| 969 | $dimensions = preg_split('/x/', $infos[2]); |
||
| 970 | return $dimensions; |
||
| 971 | } |
||
| 972 | return false; |
||
| 973 | } |
||
| 974 | } |
||
| 975 | if (!function_exists('getimagesize')) { |
||
| 976 | error_log('Function not found: getimagesize'); |
||
| 977 | return false; |
||
| 978 | } |
||
| 979 | return @getimagesize($file_path); |
||
| 980 | } |
||
| 981 | |||
| 982 | protected function create_scaled_image($file_name, $version, $options) { |
||
| 983 | if ($this->options['image_library'] === 2) { |
||
| 984 | return $this->imagemagick_create_scaled_image($file_name, $version, $options); |
||
| 985 | } |
||
| 986 | if ($this->options['image_library'] && extension_loaded('imagick')) { |
||
| 987 | return $this->imagick_create_scaled_image($file_name, $version, $options); |
||
| 988 | } |
||
| 989 | return $this->gd_create_scaled_image($file_name, $version, $options); |
||
| 990 | } |
||
| 991 | |||
| 992 | protected function destroy_image_object($file_path) { |
||
| 993 | if ($this->options['image_library'] && extension_loaded('imagick')) { |
||
| 994 | return $this->imagick_destroy_image_object($file_path); |
||
| 995 | } |
||
| 996 | } |
||
| 997 | |||
| 998 | protected function is_valid_image_file($file_path) { |
||
| 999 | if (!preg_match($this->options['image_file_types'], $file_path)) { |
||
| 1000 | return false; |
||
| 1001 | } |
||
| 1002 | if (function_exists('exif_imagetype')) { |
||
| 1003 | return @exif_imagetype($file_path); |
||
| 1004 | } |
||
| 1005 | $image_info = $this->get_image_size($file_path); |
||
| 1006 | return $image_info && $image_info[0] && $image_info[1]; |
||
| 1007 | } |
||
| 1008 | |||
| 1009 | protected function handle_image_file($file_path, $file) { |
||
| 1010 | $failed_versions = array(); |
||
| 1011 | foreach($this->options['image_versions'] as $version => $options) { |
||
| 1012 | if ($this->create_scaled_image($file->name, $version, $options)) { |
||
| 1013 | if (!empty($version)) { |
||
| 1014 | $file->{$version.'Url'} = $this->get_download_url( |
||
| 1015 | $file->name, |
||
| 1016 | $version |
||
| 1017 | ); |
||
| 1018 | } else { |
||
| 1019 | $file->size = $this->get_file_size($file_path, true); |
||
| 1020 | } |
||
| 1021 | } else { |
||
| 1022 | $failed_versions[] = $version ? $version : 'original'; |
||
| 1023 | } |
||
| 1024 | } |
||
| 1025 | if (count($failed_versions)) { |
||
| 1026 | $file->error = $this->get_error_message('image_resize') |
||
| 1027 | .' ('.implode($failed_versions,', ').')'; |
||
| 1028 | } |
||
| 1029 | // Free memory: |
||
| 1030 | $this->destroy_image_object($file_path); |
||
| 1031 | } |
||
| 1032 | |||
| 1033 | protected function handle_file_upload($uploaded_file, $name, $size, $type, $error, |
||
| 1034 | $index = null, $content_range = null) { |
||
| 1035 | $file = new \stdClass(); |
||
| 1036 | $file->name = $this->get_file_name($uploaded_file, $name, $size, $type, $error, |
||
| 1037 | $index, $content_range); |
||
| 1038 | $file->size = $this->fix_integer_overflow(intval($size)); |
||
| 1039 | $file->type = $type; |
||
| 1040 | if ($this->validate($uploaded_file, $file, $error, $index)) { |
||
| 1041 | $this->handle_form_data($file, $index); |
||
| 1042 | $upload_dir = $this->get_upload_path(); |
||
| 1043 | if (!is_dir($upload_dir)) { |
||
| 1044 | mkdir($upload_dir, $this->options['mkdir_mode'], true); |
||
| 1045 | } |
||
| 1046 | $file_path = $this->get_upload_path($file->name); |
||
| 1047 | $append_file = $content_range && is_file($file_path) && |
||
| 1048 | $file->size > $this->get_file_size($file_path); |
||
| 1049 | if ($uploaded_file && is_uploaded_file($uploaded_file)) { |
||
| 1050 | // multipart/formdata uploads (POST method uploads) |
||
| 1051 | if ($append_file) { |
||
| 1052 | file_put_contents( |
||
| 1053 | $file_path, |
||
| 1054 | fopen($uploaded_file, 'r'), |
||
| 1055 | FILE_APPEND |
||
| 1056 | ); |
||
| 1057 | } else { |
||
| 1058 | move_uploaded_file($uploaded_file, $file_path); |
||
| 1059 | } |
||
| 1060 | } else { |
||
| 1061 | // Non-multipart uploads (PUT method support) |
||
| 1062 | file_put_contents( |
||
| 1063 | $file_path, |
||
| 1064 | fopen('php://input', 'r'), |
||
| 1065 | $append_file ? FILE_APPEND : 0 |
||
| 1066 | ); |
||
| 1067 | } |
||
| 1068 | $file_size = $this->get_file_size($file_path, $append_file); |
||
| 1069 | if ($file_size === $file->size) { |
||
| 1070 | $file->url = $this->get_download_url($file->name); |
||
| 1071 | if ($this->is_valid_image_file($file_path)) { |
||
| 1072 | $this->handle_image_file($file_path, $file); |
||
| 1073 | } |
||
| 1074 | } else { |
||
| 1075 | $file->size = $file_size; |
||
| 1076 | if (!$content_range && $this->options['discard_aborted_uploads']) { |
||
| 1077 | unlink($file_path); |
||
| 1078 | $file->error = $this->get_error_message('abort'); |
||
| 1079 | } |
||
| 1080 | } |
||
| 1081 | $this->set_additional_file_properties($file); |
||
| 1082 | } |
||
| 1083 | return $file; |
||
| 1084 | } |
||
| 1085 | |||
| 1086 | protected function readfile($file_path) { |
||
| 1087 | $file_size = $this->get_file_size($file_path); |
||
| 1088 | $chunk_size = $this->options['readfile_chunk_size']; |
||
| 1089 | if ($chunk_size && $file_size > $chunk_size) { |
||
| 1090 | $handle = fopen($file_path, 'rb'); |
||
| 1091 | while (!feof($handle)) { |
||
| 1092 | echo fread($handle, $chunk_size); |
||
| 1093 | @ob_flush(); |
||
| 1094 | @flush(); |
||
| 1095 | } |
||
| 1096 | fclose($handle); |
||
| 1097 | return $file_size; |
||
| 1098 | } |
||
| 1099 | return readfile($file_path); |
||
| 1100 | } |
||
| 1101 | |||
| 1102 | protected function body($str) { |
||
| 1103 | echo $str; |
||
| 1104 | } |
||
| 1105 | |||
| 1106 | protected function header($str) { |
||
| 1107 | header($str); |
||
| 1108 | } |
||
| 1109 | |||
| 1110 | protected function get_server_var($id) { |
||
| 1111 | return isset($_SERVER[$id]) ? $_SERVER[$id] : ''; |
||
| 1112 | } |
||
| 1113 | |||
| 1114 | protected function generate_response($content, $print_response = true) { |
||
| 1115 | if ($print_response) { |
||
| 1116 | $json = json_encode($content); |
||
| 1117 | $redirect = isset($_REQUEST['redirect']) ? |
||
| 1118 | stripslashes($_REQUEST['redirect']) : null; |
||
| 1119 | if ($redirect) { |
||
| 1120 | $this->header('Location: '.sprintf($redirect, rawurlencode($json))); |
||
| 1121 | return; |
||
| 1122 | } |
||
| 1123 | $this->head(); |
||
| 1124 | if ($this->get_server_var('HTTP_CONTENT_RANGE')) { |
||
| 1125 | $files = isset($content[$this->options['param_name']]) ? |
||
| 1126 | $content[$this->options['param_name']] : null; |
||
| 1127 | if ($files && is_array($files) && is_object($files[0]) && $files[0]->size) { |
||
| 1128 | $this->header('Range: 0-'.( |
||
| 1129 | $this->fix_integer_overflow(intval($files[0]->size)) - 1 |
||
| 1130 | )); |
||
| 1131 | } |
||
| 1132 | } |
||
| 1133 | $this->body($json); |
||
| 1134 | } |
||
| 1135 | return $content; |
||
| 1136 | } |
||
| 1137 | |||
| 1138 | protected function get_version_param() { |
||
| 1139 | return isset($_GET['version']) ? basename(stripslashes($_GET['version'])) : null; |
||
| 1140 | } |
||
| 1141 | |||
| 1142 | protected function get_singular_param_name() { |
||
| 1143 | return substr($this->options['param_name'], 0, -1); |
||
| 1144 | } |
||
| 1145 | |||
| 1146 | protected function get_file_name_param() { |
||
| 1147 | $name = $this->get_singular_param_name(); |
||
| 1148 | return isset($_REQUEST[$name]) ? basename(stripslashes($_REQUEST[$name])) : null; |
||
| 1149 | } |
||
| 1150 | |||
| 1151 | protected function get_file_names_params() { |
||
| 1152 | $params = isset($_REQUEST[$this->options['param_name']]) ? |
||
| 1153 | $_REQUEST[$this->options['param_name']] : array(); |
||
| 1154 | foreach ($params as $key => $value) { |
||
| 1155 | $params[$key] = basename(stripslashes($value)); |
||
| 1156 | } |
||
| 1157 | return $params; |
||
| 1158 | } |
||
| 1159 | |||
| 1160 | protected function get_file_type($file_path) { |
||
| 1161 | switch (strtolower(pathinfo($file_path, PATHINFO_EXTENSION))) { |
||
| 1162 | case 'jpeg': |
||
| 1163 | case 'jpg': |
||
| 1164 | return 'image/jpeg'; |
||
| 1165 | case 'png': |
||
| 1166 | return 'image/png'; |
||
| 1167 | case 'gif': |
||
| 1168 | return 'image/gif'; |
||
| 1169 | default: |
||
| 1170 | return ''; |
||
| 1171 | } |
||
| 1172 | } |
||
| 1173 | |||
| 1174 | protected function download() { |
||
| 1175 | switch ($this->options['download_via_php']) { |
||
| 1176 | case 1: |
||
| 1177 | $redirect_header = null; |
||
| 1178 | break; |
||
| 1179 | case 2: |
||
| 1180 | $redirect_header = 'X-Sendfile'; |
||
| 1181 | break; |
||
| 1182 | case 3: |
||
| 1183 | $redirect_header = 'X-Accel-Redirect'; |
||
| 1184 | break; |
||
| 1185 | default: |
||
| 1186 | return $this->header('HTTP/1.1 403 Forbidden'); |
||
| 1187 | } |
||
| 1188 | $file_name = $this->get_file_name_param(); |
||
| 1189 | if (!$this->is_valid_file_object($file_name)) { |
||
| 1190 | return $this->header('HTTP/1.1 404 Not Found'); |
||
| 1191 | } |
||
| 1192 | if ($redirect_header) { |
||
| 1193 | return $this->header( |
||
| 1194 | $redirect_header.': '.$this->get_download_url( |
||
| 1195 | $file_name, |
||
| 1196 | $this->get_version_param(), |
||
| 1197 | true |
||
| 1198 | ) |
||
| 1199 | ); |
||
| 1200 | } |
||
| 1201 | $file_path = $this->get_upload_path($file_name, $this->get_version_param()); |
||
| 1202 | // Prevent browsers from MIME-sniffing the content-type: |
||
| 1203 | $this->header('X-Content-Type-Options: nosniff'); |
||
| 1204 | if (!preg_match($this->options['inline_file_types'], $file_name)) { |
||
| 1205 | $this->header('Content-Type: application/octet-stream'); |
||
| 1206 | $this->header('Content-Disposition: attachment; filename="'.$file_name.'"'); |
||
| 1207 | } else { |
||
| 1208 | $this->header('Content-Type: '.$this->get_file_type($file_path)); |
||
| 1209 | $this->header('Content-Disposition: inline; filename="'.$file_name.'"'); |
||
| 1210 | } |
||
| 1211 | $this->header('Content-Length: '.$this->get_file_size($file_path)); |
||
| 1212 | $this->header('Last-Modified: '.gmdate('D, d M Y H:i:s T', filemtime($file_path))); |
||
| 1213 | $this->readfile($file_path); |
||
| 1214 | } |
||
| 1215 | |||
| 1216 | protected function send_content_type_header() { |
||
| 1217 | $this->header('Vary: Accept'); |
||
| 1218 | if (strpos($this->get_server_var('HTTP_ACCEPT'), 'application/json') !== false) { |
||
| 1219 | $this->header('Content-type: application/json'); |
||
| 1220 | } else { |
||
| 1221 | $this->header('Content-type: text/plain'); |
||
| 1222 | } |
||
| 1223 | } |
||
| 1224 | |||
| 1225 | protected function send_access_control_headers() { |
||
| 1226 | $this->header('Access-Control-Allow-Origin: '.$this->options['access_control_allow_origin']); |
||
| 1227 | $this->header('Access-Control-Allow-Credentials: ' |
||
| 1228 | .($this->options['access_control_allow_credentials'] ? 'true' : 'false')); |
||
| 1229 | $this->header('Access-Control-Allow-Methods: ' |
||
| 1230 | .implode(', ', $this->options['access_control_allow_methods'])); |
||
| 1231 | $this->header('Access-Control-Allow-Headers: ' |
||
| 1232 | .implode(', ', $this->options['access_control_allow_headers'])); |
||
| 1233 | } |
||
| 1234 | |||
| 1235 | public function head() { |
||
| 1236 | $this->header('Pragma: no-cache'); |
||
| 1237 | $this->header('Cache-Control: no-store, no-cache, must-revalidate'); |
||
| 1238 | $this->header('Content-Disposition: inline; filename="files.json"'); |
||
| 1239 | // Prevent Internet Explorer from MIME-sniffing the content-type: |
||
| 1240 | $this->header('X-Content-Type-Options: nosniff'); |
||
| 1241 | if ($this->options['access_control_allow_origin']) { |
||
| 1242 | $this->send_access_control_headers(); |
||
| 1243 | } |
||
| 1244 | $this->send_content_type_header(); |
||
| 1245 | } |
||
| 1246 | |||
| 1247 | public function get($print_response = true) { |
||
| 1248 | if ($print_response && isset($_GET['download'])) { |
||
| 1249 | return $this->download(); |
||
| 1250 | } |
||
| 1251 | $file_name = $this->get_file_name_param(); |
||
| 1252 | if ($file_name) { |
||
| 1253 | $response = array( |
||
| 1254 | $this->get_singular_param_name() => $this->get_file_object($file_name) |
||
| 1255 | ); |
||
| 1256 | } else { |
||
| 1257 | $response = array( |
||
| 1258 | $this->options['param_name'] => $this->get_file_objects() |
||
| 1259 | ); |
||
| 1260 | } |
||
| 1261 | return $this->generate_response($response, $print_response); |
||
| 1262 | } |
||
| 1263 | |||
| 1264 | public function post($print_response = true) { |
||
| 1265 | if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') { |
||
| 1266 | return $this->delete($print_response); |
||
| 1267 | } |
||
| 1268 | $upload = isset($_FILES[$this->options['param_name']]) ? |
||
| 1269 | $_FILES[$this->options['param_name']] : null; |
||
| 1270 | // Parse the Content-Disposition header, if available: |
||
| 1271 | $file_name = $this->get_server_var('HTTP_CONTENT_DISPOSITION') ? |
||
| 1272 | rawurldecode(preg_replace( |
||
| 1273 | '/(^[^"]+")|("$)/', |
||
| 1274 | '', |
||
| 1275 | $this->get_server_var('HTTP_CONTENT_DISPOSITION') |
||
| 1276 | )) : null; |
||
| 1277 | // Parse the Content-Range header, which has the following form: |
||
| 1278 | // Content-Range: bytes 0-524287/2000000 |
||
| 1279 | $content_range = $this->get_server_var('HTTP_CONTENT_RANGE') ? |
||
| 1280 | preg_split('/[^0-9]+/', $this->get_server_var('HTTP_CONTENT_RANGE')) : null; |
||
| 1281 | $size = $content_range ? $content_range[3] : null; |
||
| 1282 | $files = array(); |
||
| 1283 | if ($upload && is_array($upload['tmp_name'])) { |
||
| 1284 | // param_name is an array identifier like "files[]", |
||
| 1285 | // $_FILES is a multi-dimensional array: |
||
| 1286 | foreach ($upload['tmp_name'] as $index => $value) { |
||
| 1287 | $files[] = $this->handle_file_upload( |
||
| 1288 | $upload['tmp_name'][$index], |
||
| 1289 | $file_name ? $file_name : $upload['name'][$index], |
||
| 1290 | $size ? $size : $upload['size'][$index], |
||
| 1291 | $upload['type'][$index], |
||
| 1292 | $upload['error'][$index], |
||
| 1293 | $index, |
||
| 1294 | $content_range |
||
| 1295 | ); |
||
| 1296 | } |
||
| 1297 | } else { |
||
| 1298 | // param_name is a single object identifier like "file", |
||
| 1299 | // $_FILES is a one-dimensional array: |
||
| 1300 | $files[] = $this->handle_file_upload( |
||
| 1301 | isset($upload['tmp_name']) ? $upload['tmp_name'] : null, |
||
| 1302 | $file_name ? $file_name : (isset($upload['name']) ? |
||
| 1303 | $upload['name'] : null), |
||
| 1304 | $size ? $size : (isset($upload['size']) ? |
||
| 1305 | $upload['size'] : $this->get_server_var('CONTENT_LENGTH')), |
||
| 1306 | isset($upload['type']) ? |
||
| 1307 | $upload['type'] : $this->get_server_var('CONTENT_TYPE'), |
||
| 1308 | isset($upload['error']) ? $upload['error'] : null, |
||
| 1309 | null, |
||
| 1310 | $content_range |
||
| 1311 | ); |
||
| 1312 | } |
||
| 1313 | return $this->generate_response( |
||
| 1314 | array($this->options['param_name'] => $files), |
||
| 1315 | $print_response |
||
| 1316 | ); |
||
| 1317 | } |
||
| 1318 | |||
| 1319 | public function delete($print_response = true) { |
||
| 1320 | $file_names = $this->get_file_names_params(); |
||
| 1321 | if (empty($file_names)) { |
||
| 1322 | $file_names = array($this->get_file_name_param()); |
||
| 1323 | } |
||
| 1324 | $response = array(); |
||
| 1325 | foreach($file_names as $file_name) { |
||
| 1326 | $file_path = $this->get_upload_path($file_name); |
||
| 1327 | $success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path); |
||
| 1328 | if ($success) { |
||
| 1329 | foreach($this->options['image_versions'] as $version => $options) { |
||
| 1330 | if (!empty($version)) { |
||
| 1331 | $file = $this->get_upload_path($file_name, $version); |
||
| 1332 | if (is_file($file)) { |
||
| 1333 | unlink($file); |
||
| 1334 | } |
||
| 1335 | } |
||
| 1336 | } |
||
| 1337 | } |
||
| 1338 | $response[$file_name] = $success; |
||
| 1339 | } |
||
| 1340 | return $this->generate_response($response, $print_response); |
||
| 1341 | } |
||
| 1342 | |||
| 1343 | } |