Plugin Directory

Changeset 3479889


Ignore:
Timestamp:
03/11/2026 09:54:42 AM (3 weeks ago)
Author:
flexcubed
Message:

Fixed a vulnerability issue confined to uploads the plugin folder 11.2.0

Location:
pitchprint
Files:
10 edited
1 copied

Legend:

Unmodified
Added
Removed
  • pitchprint/tags/11.2.0/CHANGELOG.txt

    r3391719 r3479889  
     1== 11.2.0 =
     2Fixed a vulnerability issue confined to uploads the plugin folder
     3
    14== 11.1.1 =
    25Minor fix to skip clearing customization if the call is an ajax with `ppc-simulate-cart`
  • pitchprint/tags/11.2.0/pitchprint.php

    r3404073 r3479889  
    66*   Description:            A beautiful web based print customization app for your online store. Integrates with WooCommerce.
    77*   Author:                 PitchPrint, Inc.
    8 *   Version:                11.1.2
     8*   Version:                11.2.0
    99*   Author URI:             https://pitchprint.com
    1010*   Requires at least:      3.8
     
    4747             *  @var string
    4848            */
    49             public $version = '11.1.2';
     49            public $version = '11.2.0';
    5050
    5151            /**
  • pitchprint/tags/11.2.0/readme.txt

    r3404073 r3479889  
    44Requires at least: 3.8
    55Tested up to: 6.8
    6 Stable tag: 11.1.2
     6Stable tag: 11.2.0
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    172172
    173173== Changelog ==
     174
     175== 11.2.0 =
     176Fixed a vulnerability issue confined to uploads the plugin folder
    174177
    175178== 11.1.2 =
  • pitchprint/tags/11.2.0/uploader/UploadHandler.php

    r3286188 r3479889  
    11<?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.
    115 */
    126
    137error_reporting(E_ERROR | E_PARSE);
    148
    15 class UploadHandler
    16 {
     9class PitchPrintUploader {
    1710
    18     protected $options;
     11    private $options;
    1912
    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;
    16115    }
    16216
    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')));
    18222        }
     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);
    18336    }
    18437
    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;
    19492    }
    19593
    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));
    45299        }
    453100        return $name;
    454101    }
    455102
    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;
    495108    }
    496109
    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)) {
    993112            return false;
    994113        }
    995114        if (function_exists('exif_imagetype')) {
    996             return @exif_imagetype($file_path);
     115            return (bool) @exif_imagetype($file_path);
    997116        }
    998         $image_info = $this->get_image_size($file_path);
    999         return $image_info && $image_info[0] && $image_info[1];
     117        $info = @getimagesize($file_path);
     118        return $info && $info[0] && $info[1];
    1000119    }
    1001120
    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);
    1016154            }
    1017155        }
    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;
    1021165        }
    1022         // Free memory:
    1023         $this->destroy_image_object($file_path);
     166
     167        imagedestroy($src);
     168        imagedestroy($dst);
     169        return $ok;
    1024170    }
    1025171
    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';
    1077183    }
    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)) - 1
    1123                     ));
    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                     true
    1191                 )
    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/2000000
    1272         $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_range
    1291                 );
    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_range
    1307             );
    1308         }
    1309         return $this->generate_response(
    1310             array($this->options['param_name'] => $files),
    1311             $print_response
    1312         );
    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 
    1339184}
  • pitchprint/tags/11.2.0/uploader/index.php

    r3286188 r3479889  
    11<?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.
    115 */
    126
    13 error_reporting(E_ALL | E_STRICT);
     7error_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;
     16foreach ($wp_load_paths as $path) {
     17    if (file_exists($path)) {
     18        require_once($path);
     19        $wp_loaded = true;
     20        break;
     21    }
     22}
     23
     24if (!$wp_loaded) {
     25    http_response_code(500);
     26    exit;
     27}
     28
     29// Only allow POST
     30if ($_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
    1437require('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
     49header('Content-Type: application/json');
     50header('X-Content-Type-Options: nosniff');
     51echo json_encode($handler->handle());
  • pitchprint/trunk/CHANGELOG.txt

    r3391719 r3479889  
     1== 11.2.0 =
     2Fixed a vulnerability issue confined to uploads the plugin folder
     3
    14== 11.1.1 =
    25Minor fix to skip clearing customization if the call is an ajax with `ppc-simulate-cart`
  • pitchprint/trunk/pitchprint.php

    r3404073 r3479889  
    66*   Description:            A beautiful web based print customization app for your online store. Integrates with WooCommerce.
    77*   Author:                 PitchPrint, Inc.
    8 *   Version:                11.1.2
     8*   Version:                11.2.0
    99*   Author URI:             https://pitchprint.com
    1010*   Requires at least:      3.8
     
    4747             *  @var string
    4848            */
    49             public $version = '11.1.2';
     49            public $version = '11.2.0';
    5050
    5151            /**
  • pitchprint/trunk/readme.txt

    r3404073 r3479889  
    44Requires at least: 3.8
    55Tested up to: 6.8
    6 Stable tag: 11.1.2
     6Stable tag: 11.2.0
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    172172
    173173== Changelog ==
     174
     175== 11.2.0 =
     176Fixed a vulnerability issue confined to uploads the plugin folder
    174177
    175178== 11.1.2 =
  • pitchprint/trunk/uploader/UploadHandler.php

    r3286188 r3479889  
    11<?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.
    115 */
    126
    137error_reporting(E_ERROR | E_PARSE);
    148
    15 class UploadHandler
    16 {
     9class PitchPrintUploader {
    1710
    18     protected $options;
     11    private $options;
    1912
    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;
    16115    }
    16216
    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')));
    18222        }
     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);
    18336    }
    18437
    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;
    19492    }
    19593
    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));
    45299        }
    453100        return $name;
    454101    }
    455102
    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;
    495108    }
    496109
    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)) {
    993112            return false;
    994113        }
    995114        if (function_exists('exif_imagetype')) {
    996             return @exif_imagetype($file_path);
     115            return (bool) @exif_imagetype($file_path);
    997116        }
    998         $image_info = $this->get_image_size($file_path);
    999         return $image_info && $image_info[0] && $image_info[1];
     117        $info = @getimagesize($file_path);
     118        return $info && $info[0] && $info[1];
    1000119    }
    1001120
    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);
    1016154            }
    1017155        }
    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;
    1021165        }
    1022         // Free memory:
    1023         $this->destroy_image_object($file_path);
     166
     167        imagedestroy($src);
     168        imagedestroy($dst);
     169        return $ok;
    1024170    }
    1025171
    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';
    1077183    }
    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)) - 1
    1123                     ));
    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                     true
    1191                 )
    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/2000000
    1272         $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_range
    1291                 );
    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_range
    1307             );
    1308         }
    1309         return $this->generate_response(
    1310             array($this->options['param_name'] => $files),
    1311             $print_response
    1312         );
    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 
    1339184}
  • pitchprint/trunk/uploader/index.php

    r3286188 r3479889  
    11<?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.
    115 */
    126
    13 error_reporting(E_ALL | E_STRICT);
     7error_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;
     16foreach ($wp_load_paths as $path) {
     17    if (file_exists($path)) {
     18        require_once($path);
     19        $wp_loaded = true;
     20        break;
     21    }
     22}
     23
     24if (!$wp_loaded) {
     25    http_response_code(500);
     26    exit;
     27}
     28
     29// Only allow POST
     30if ($_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
    1437require('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
     49header('Content-Type: application/json');
     50header('X-Content-Type-Options: nosniff');
     51echo json_encode($handler->handle());
Note: See TracChangeset for help on using the changeset viewer.