PageRenderTime 38ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/library/UploadHandler.php

https://bitbucket.org/bggsk-admin/go-yug
PHP | 1508 lines | 1178 code | 152 blank | 178 comment | 198 complexity | 57d510fe60c6a4b213b4bf8e5ea5eb3a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file