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