initial commit
[namibia] / module / Utility / src / Utility / Upload / Image.php
1 <?php
2 namespace Utility\Upload;
3
4
5
6 /*
7  * jQuery File Upload Plugin PHP Class 5.18.3
8  * https://github.com/blueimp/jQuery-File-Upload
9  *
10  * Copyright 2010, Sebastian Tschan
11  * https://blueimp.net
12  *
13  * Licensed under the MIT license:
14  * http://www.opensource.org/licenses/MIT
15  */
16
17 class Image
18 {
19     protected $options;
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     );
40
41     function __construct($options = null, $initialize = true) {
42         $this->options = array(
43             'script_url' => $this->get_full_url().'/',
44             'upload_dir' => \Utility\Registry::getConfigParam('ImagePath'),
45             'upload_url' => \Utility\Registry::getConfigParam('ImageUrl'),
46             'user_dirs' => false,
47             'mkdir_mode' => 0755,
48             'param_name' => 'files',
49             // Set the following option to 'POST', if your server does not support
50             // DELETE requests. This is a parameter sent to the client:
51             'delete_type' => 'DELETE',
52             'access_control_allow_origin' => '*',
53             'access_control_allow_credentials' => false,
54             'access_control_allow_methods' => array(
55                 'OPTIONS',
56                 'HEAD',
57                 'GET',
58                 'POST',
59                 'PUT',
60                 'DELETE'
61             ),
62             'access_control_allow_headers' => array(
63                 'Content-Type',
64                 'Content-Range',
65                 'Content-Disposition',
66                 'Content-Description'
67             ),
68             // Enable to provide file downloads via GET requests to the PHP script:
69             'download_via_php' => false,
70             // Defines which files can be displayed inline when downloaded:
71             'inline_file_types' => '/\.(gif|jpe?g|png)$/i',
72             // Defines which files (based on their names) are accepted for upload:
73             'accept_file_types' => '/.+$/i',
74             // The php.ini settings upload_max_filesize and post_max_size
75             // take precedence over the following max_file_size setting:
76             'max_file_size' => null,
77             'min_file_size' => 1,
78             // The maximum number of files for the upload directory:
79             'max_number_of_files' => null,
80             // Image resolution restrictions:
81             'max_width'  => null,
82             'max_height' => null,
83             'min_width'  => 1,
84             'min_height' => 1,
85             // Set the following option to false to enable resumable uploads:
86             'discard_aborted_uploads' => true,
87             // Set to true to rotate images based on EXIF meta data, if available:
88             'orient_image'   => false,
89             'image_versions' => array(
90                 // Restrict the size of uploaded images:
91                 '' => array(
92                     'max_width'    => 1920,
93                     'max_height'   => 1200,
94                     'jpeg_quality' => 95
95                 ),
96                 // Uncomment the following to create medium sized images:
97                 'thumbnail' => array(
98                     'max_width'    => 300,
99                     'max_height'   => 200,
100                     'jpeg_quality' => 90
101                 )
102             )
103         );
104         if ($options) {
105             $this->options = array_merge($this->options, $options);
106         }
107         if ($initialize) {
108             $this->initialize();
109         }
110     }
111
112     /**
113      * Download external file
114      * @param string $sourceUri
115      * @param string $destinationPath
116      * @return boolean
117      */
118     public function externalDownload($sourceUri)
119     {
120         set_time_limit(0);
121         $destinationPath = '/tmp/' . sha1( 'ext_' . time() . rand(1, 50000) ) . '.ext';
122         $fp = fopen ($destinationPath, 'w+');
123
124
125         $ch = curl_init();
126         curl_setopt( $ch, CURLOPT_URL, str_replace(" ", "%20", $sourceUri) );
127
128
129         curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
130         curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
131         curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
132
133         /*
134          * Increase timeout to download big file
135          */
136         curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 180 );
137
138         /*
139          * Write data to local file
140          */
141         curl_setopt( $ch, CURLOPT_FILE, $fp );
142
143         curl_exec( $ch );
144         curl_close( $ch );
145         fclose( $fp );
146
147         $fileDownloaded = filesize($destinationPath) > 0 ? true : false;
148
149         if(filesize($destinationPath) > 0)
150         {
151                 $fileName = array_pop(explode('/', $sourceUri));
152
153                 $file = new \stdClass();
154
155                 $file->name = $fileName;
156                 $file->size = filesize($destinationPath);
157
158                 $finfo = finfo_open(FILEINFO_MIME_TYPE);
159                 $file->type = finfo_file($finfo, $destinationPath);
160                 finfo_close($finfo);
161
162
163                 $em = \Utility\Registry::getEntityManager();
164                 $oImage = new \Utility\Entity\Image();
165                 $oImage->filename = $file->name;
166                 $oImage->mimeType = $file->type;
167                 $em->persist($oImage);
168                 $em->flush();
169
170
171                 $file->id = $oImage->id;
172
173                 $file->name = $oImage->id . '.' . array_pop(explode('.', $file->name));
174                 $oImage->filename = $file->name;
175                 $em->flush();
176
177                 $moved = rename($destinationPath, $this->options['upload_dir'] . $oImage->filename);
178
179                 foreach($this->options['image_versions'] as $version => $options)
180                 {
181                         $this->create_scaled_image($file->name, $version, $options);
182                 }
183
184                 return $file;
185         }
186         else
187         {
188                 \Utility\Debug::errorLog('File not downloaded', $sourceUri);
189                 throw new \Exception('File not downloaded');
190         }
191
192     }
193
194     protected function initialize() {
195         switch ($_SERVER['REQUEST_METHOD']) {
196             case 'OPTIONS':
197             case 'HEAD':
198                 $this->head();
199                 break;
200             case 'GET':
201                 $this->get();
202                 break;
203             case 'POST':
204                 $this->post();
205                 break;
206             case 'DELETE':
207                 $this->delete();
208                 break;
209             default:
210                 header('HTTP/1.1 405 Method Not Allowed');
211         }
212     }
213
214     protected function get_full_url() {
215         $https = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
216         return
217             ($https ? 'https://' : 'http://').
218             (!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : '').
219             (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'].
220             ($https && $_SERVER['SERVER_PORT'] === 443 ||
221             $_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))).
222             substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/'));
223     }
224
225     protected function get_user_id() {
226         @session_start();
227         return session_id();
228     }
229
230     protected function get_user_path() {
231         if ($this->options['user_dirs']) {
232             return $this->get_user_id().'/';
233         }
234         return '';
235     }
236
237     protected function get_upload_path($file_name = null, $version = null) {
238         $file_name = $file_name ? $file_name : '';
239         $version_path = empty($version) ? '' : $version.'/';
240         return $this->options['upload_dir'].$this->get_user_path()
241             .$version_path.$file_name;
242     }
243
244     protected function get_download_url($file_name, $version = null) {
245         if ($this->options['download_via_php']) {
246             $url = $this->options['script_url'].'?file='.rawurlencode($file_name);
247             if ($version) {
248                 $url .= '&version='.rawurlencode($version);
249             }
250             return $url.'&download=1';
251         }
252         $version_path = empty($version) ? '' : rawurlencode($version).'/';
253         return $this->options['upload_url'].$this->get_user_path()
254             .$version_path.rawurlencode($file_name);
255     }
256
257     protected function set_file_delete_properties($file) {
258         $file->delete_url = $this->options['script_url']
259             .'?file='.rawurlencode($file->name);
260         $file->delete_type = $this->options['delete_type'];
261         if ($file->delete_type !== 'DELETE') {
262             $file->delete_url .= '&_method=DELETE';
263         }
264         if ($this->options['access_control_allow_credentials']) {
265             $file->delete_with_credentials = true;
266         }
267     }
268
269     // Fix for overflowing signed 32 bit integers,
270     // works for sizes up to 2^32-1 bytes (4 GiB - 1):
271     protected function fix_integer_overflow($size) {
272         if ($size < 0) {
273             $size += 2.0 * (PHP_INT_MAX + 1);
274         }
275         return $size;
276     }
277
278     protected function get_file_size($file_path, $clear_stat_cache = false) {
279         if ($clear_stat_cache) {
280             clearstatcache();
281         }
282         return $this->fix_integer_overflow(@filesize($file_path));
283
284     }
285
286     protected function is_valid_file_object($file_name) {
287         $file_path = $this->get_upload_path($file_name);
288         if (is_file($file_path) && $file_name[0] !== '.') {
289             return true;
290         }
291         return false;
292     }
293
294     protected function get_file_object($file_name) {
295         if ($this->is_valid_file_object($file_name)) {
296             $file = new \stdClass();
297             $file->name = $file_name;
298             $file->size = $this->get_file_size(
299                 $this->get_upload_path($file_name)
300             );
301             $file->url = $this->get_download_url($file->name);
302             foreach($this->options['image_versions'] as $version => $options) {
303                 if (!empty($version)) {
304                     if (is_file($this->get_upload_path($file_name, $version))) {
305                         $file->{$version.'_url'} = $this->get_download_url(
306                             $file->name,
307                             $version
308                         );
309                     }
310                 }
311             }
312             $this->set_file_delete_properties($file);
313             return $file;
314         }
315         return null;
316     }
317
318     protected function get_file_objects($iteration_method = 'get_file_object') {
319         $upload_dir = $this->get_upload_path();
320         if (!is_dir($upload_dir)) {
321             mkdir($upload_dir, $this->options['mkdir_mode']);
322         }
323         return array_values(array_filter(array_map(
324             array($this, $iteration_method),
325             scandir($upload_dir)
326         )));
327     }
328
329     protected function count_file_objects() {
330         return count($this->get_file_objects('is_valid_file_object'));
331     }
332
333     protected function create_scaled_image($file_name, $version, $options) {
334         $file_path = $this->get_upload_path($file_name);
335         if (!empty($version)) {
336             $version_dir = $this->get_upload_path(null, $version);
337             if (!is_dir($version_dir)) {
338                 mkdir($version_dir, $this->options['mkdir_mode']);
339             }
340             $new_file_path = $version_dir.'/'.$file_name;
341         } else {
342             $new_file_path = $file_path;
343         }
344         list($img_width, $img_height) = @getimagesize($file_path);
345         if (!$img_width || !$img_height) {
346             return false;
347         }
348         $scale = min(
349             $options['max_width'] / $img_width,
350             $options['max_height'] / $img_height
351         );
352         if ($scale >= 1) {
353             if ($file_path !== $new_file_path) {
354                 return copy($file_path, $new_file_path);
355             }
356             return true;
357         }
358         $new_width = $img_width * $scale;
359         $new_height = $img_height * $scale;
360         $new_img = @imagecreatetruecolor($new_width, $new_height);
361         switch (strtolower(substr(strrchr($file_name, '.'), 1))) {
362             case 'jpg':
363             case 'jpeg':
364                 $src_img = @imagecreatefromjpeg($file_path);
365                 $write_image = 'imagejpeg';
366                 $image_quality = isset($options['jpeg_quality']) ?
367                     $options['jpeg_quality'] : 75;
368                 break;
369             case 'gif':
370                 @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
371                 $src_img = @imagecreatefromgif($file_path);
372                 $write_image = 'imagegif';
373                 $image_quality = null;
374                 break;
375             case 'png':
376                 @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
377                 @imagealphablending($new_img, false);
378                 @imagesavealpha($new_img, true);
379                 $src_img = @imagecreatefrompng($file_path);
380                 $write_image = 'imagepng';
381                 $image_quality = isset($options['png_quality']) ?
382                     $options['png_quality'] : 9;
383                 break;
384             default:
385                 $src_img = null;
386         }
387         $success = $src_img && @imagecopyresampled(
388             $new_img,
389             $src_img,
390             0, 0, 0, 0,
391             $new_width,
392             $new_height,
393             $img_width,
394             $img_height
395         ) && $write_image($new_img, $new_file_path, $image_quality);
396         // Free up memory (imagedestroy does not delete files):
397         @imagedestroy($src_img);
398         @imagedestroy($new_img);
399         return $success;
400     }
401
402     protected function get_error_message($error) {
403         return array_key_exists($error, $this->error_messages) ?
404             $this->error_messages[$error] : $error;
405     }
406
407     function get_config_bytes($val) {
408         $val = trim($val);
409         $last = strtolower($val[strlen($val)-1]);
410         switch($last) {
411             case 'g':
412                 $val *= 1024;
413             case 'm':
414                 $val *= 1024;
415             case 'k':
416                 $val *= 1024;
417         }
418         return $this->fix_integer_overflow($val);
419     }
420
421     protected function validate($uploaded_file, $file, $error, $index) {
422         if ($error) {
423             $file->error = $this->get_error_message($error);
424             return false;
425         }
426         $content_length = $this->fix_integer_overflow(intval($_SERVER['CONTENT_LENGTH']));
427         if ($content_length > $this->get_config_bytes(ini_get('post_max_size'))) {
428             $file->error = $this->get_error_message('post_max_size');
429             return false;
430         }
431         if (!preg_match($this->options['accept_file_types'], $file->name)) {
432             $file->error = $this->get_error_message('accept_file_types');
433             return false;
434         }
435         if ($uploaded_file && is_uploaded_file($uploaded_file)) {
436             $file_size = $this->get_file_size($uploaded_file);
437         } else {
438             $file_size = $content_length;
439         }
440         if ($this->options['max_file_size'] && (
441                 $file_size > $this->options['max_file_size'] ||
442                 $file->size > $this->options['max_file_size'])
443             ) {
444             $file->error = $this->get_error_message('max_file_size');
445             return false;
446         }
447         if ($this->options['min_file_size'] &&
448             $file_size < $this->options['min_file_size']) {
449             $file->error = $this->get_error_message('min_file_size');
450             return false;
451         }
452         if (is_int($this->options['max_number_of_files']) && (
453                 $this->count_file_objects() >= $this->options['max_number_of_files'])
454             ) {
455             $file->error = $this->get_error_message('max_number_of_files');
456             return false;
457         }
458         list($img_width, $img_height) = @getimagesize($uploaded_file);
459         if (is_int($img_width)) {
460             if ($this->options['max_width'] && $img_width > $this->options['max_width']) {
461                 $file->error = $this->get_error_message('max_width');
462                 return false;
463             }
464             if ($this->options['max_height'] && $img_height > $this->options['max_height']) {
465                 $file->error = $this->get_error_message('max_height');
466                 return false;
467             }
468             if ($this->options['min_width'] && $img_width < $this->options['min_width']) {
469                 $file->error = $this->get_error_message('min_width');
470                 return false;
471             }
472             if ($this->options['min_height'] && $img_height < $this->options['min_height']) {
473                 $file->error = $this->get_error_message('min_height');
474                 return false;
475             }
476         }
477         return true;
478     }
479
480     protected function upcount_name_callback($matches) {
481         $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
482         $ext = isset($matches[2]) ? $matches[2] : '';
483         return ' ('.$index.')'.$ext;
484     }
485
486     protected function upcount_name($name) {
487         return preg_replace_callback(
488             '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
489             array($this, 'upcount_name_callback'),
490             $name,
491             1
492         );
493     }
494
495     protected function trim_file_name($name, $type, $index, $content_range) {
496         // Remove path information and dots around the filename, to prevent uploading
497         // into different directories or replacing hidden system files.
498         // Also remove control characters and spaces (\x00..\x20) around the filename:
499         $file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
500         // Add missing file extension for known image types:
501         if (strpos($file_name, '.') === false &&
502             preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) {
503             $file_name .= '.'.$matches[1];
504         }
505         while(is_dir($this->get_upload_path($file_name))) {
506             $file_name = $this->upcount_name($file_name);
507         }
508         $uploaded_bytes = $this->fix_integer_overflow(intval($content_range[1]));
509         while(is_file($this->get_upload_path($file_name))) {
510             if ($uploaded_bytes === $this->get_file_size(
511                     $this->get_upload_path($file_name))) {
512                 break;
513             }
514             $file_name = $this->upcount_name($file_name);
515         }
516         return $file_name;
517     }
518
519     protected function handle_form_data($file, $index) {
520         // Handle form data, e.g. $_REQUEST['description'][$index]
521     }
522
523     protected function orient_image($file_path) {
524           $exif = @exif_read_data($file_path);
525         if ($exif === false) {
526             return false;
527         }
528           $orientation = intval(@$exif['Orientation']);
529           if (!in_array($orientation, array(3, 6, 8))) {
530               return false;
531           }
532           $image = @imagecreatefromjpeg($file_path);
533           switch ($orientation) {
534               case 3:
535                   $image = @imagerotate($image, 180, 0);
536                   break;
537               case 6:
538                   $image = @imagerotate($image, 270, 0);
539                   break;
540               case 8:
541                   $image = @imagerotate($image, 90, 0);
542                   break;
543               default:
544                   return false;
545           }
546           $success = imagejpeg($image, $file_path);
547           // Free up memory (imagedestroy does not delete files):
548           @imagedestroy($image);
549           return $success;
550     }
551
552     protected function handle_file_upload($uploaded_file, $name, $size, $type, $error,
553             $index = null, $content_range = null) {
554         $file = new \stdClass();
555         $file->name = $this->trim_file_name($name, $type, $index, $content_range);
556         $file->size = $this->fix_integer_overflow(intval($size));
557         $file->type = $type;
558         $em = \Utility\Registry::getEntityManager();
559         $oImage = new \Utility\Entity\Image();
560         $oImage->filename = $file->name;
561         $oImage->mimeType = $type;
562         $em->persist($oImage);
563         $em->flush();
564         $parts = explode('.', $file->name);
565         $file->name = $oImage->id . '.' . array_pop($parts);
566                 $oImage->filename = $file->name;
567         $em->flush();
568         if ($this->validate($uploaded_file, $file, $error, $index)) {
569             $this->handle_form_data($file, $index);
570             $upload_dir = $this->get_upload_path();
571             if (!is_dir($upload_dir)) {
572                 mkdir($upload_dir, $this->options['mkdir_mode']);
573             }
574             $parts = explode('.', $file->name);
575             $file_path = $this->get_upload_path($file->name);
576             $append_file = $content_range && is_file($file_path) &&
577                 $file->size > $this->get_file_size($file_path);
578             if ($uploaded_file && is_uploaded_file($uploaded_file)) {
579                 // multipart/formdata uploads (POST method uploads)
580                 if (file_exists($file_path))
581                 {
582                         unlink($file_path);
583                 }
584                 if ($append_file) {
585                     file_put_contents(
586                         $file_path,
587                         fopen($uploaded_file, 'r'),
588                         FILE_APPEND
589                     );
590                 } else {
591                     move_uploaded_file($uploaded_file, $file_path);
592                 }
593             } else {
594                 // Non-multipart uploads (PUT method support)
595                 file_put_contents(
596                     $file_path,
597                     fopen('php://input', 'r'),
598                     $append_file ? FILE_APPEND : 0
599                 );
600             }
601             $file_size = $this->get_file_size($file_path, $append_file);
602             //if ($file_size === $file->size) {
603             if ($file->size) {
604                 if ($this->options['orient_image']) {
605                     $this->orient_image($file_path);
606                 }
607                 $file->url = $this->get_download_url($file->name);
608                 foreach($this->options['image_versions'] as $version => $options) {
609                     if ($this->create_scaled_image($file->name, $version, $options)) {
610                         if (!empty($version)) {
611                             $file->{$version.'_url'} = $this->get_download_url(
612                                 $file->name,
613                                 $version
614                             );
615                         } else {
616                             $file_size = $this->get_file_size($file_path, true);
617                         }
618                     }
619                 }
620                 $file->id = $oImage->id;
621                 $file->name = $oImage->filename;
622             } else if (!$content_range && $this->options['discard_aborted_uploads']) {
623                 unlink($file_path);
624                 $file->error = 'abort';
625             }
626             $file->size = $file_size;
627             $this->set_file_delete_properties($file);
628         }
629         return $file;
630     }
631
632     private function customResizeImage($imgString, $mimeType)
633                 {
634                         switch ($mimeType)
635                         {
636                                 case 'image/jpeg':
637                                 case 'image/pjpeg':
638                                         $fileExt = '.jpg';
639                                         break;
640                                 case 'image/gif':
641                                         $fileExt = '.gif';
642                                         break;
643                                 case 'image/png':
644                                         $fileExt = '.png';
645                                         break;
646                                 case 'image/bmp':
647                                 case 'image/x-windows-bmp':
648                                         $fileExt = '.bmp';
649                                         break;
650                                 default:
651                                         return false;
652                         }
653                         $tmpFile = APPLICATION_PATH . '/../public/files/'
654                         . mt_rand(10000000, 99999999) . $fileExt;
655                         $bytesWritten = file_put_contents($tmpFile, $imgString);
656                         list($img_width, $img_height) = getimagesize($tmpFile);
657                         unlink($tmpFile);
658                         if (!$img_width || !$img_height)
659                         {
660                                 return false;
661                         }
662                         $src_img = imagecreatefromstring($imgString);
663                         $scale = min(
664                                         300 / $img_width,
665                                         200 / $img_height
666                         );
667                         if ($scale >= 1)
668                         {
669                                 return $imgString;
670                         }
671                         $new_width = $img_width * $scale;
672                         $new_height = $img_height * $scale;
673                         $new_img = @imagecreatetruecolor($new_width, $new_height);
674                         switch ($mimeType)
675                         {
676                                 case 'image/jpeg':
677                                 case 'image/pjpeg':
678                                         $write_image = 'imagejpeg';
679                                         $image_quality = 95;
680                                         break;
681                                 case 'image/gif':
682                                         @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
683                                         $write_image = 'imagegif';
684                                         $image_quality = null;
685                                         break;
686                                 case 'image/png':
687                                         @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
688                                         @imagealphablending($new_img, false);
689                                         @imagesavealpha($new_img, true);
690                                         $write_image = 'imagepng';
691                                         $image_quality = 9;
692                                         break;
693                                 case 'image/bmp':
694                                 case 'image/x-windows-bmp':
695                                         $write_image = 'imagebmp';
696                                         $image_quality = null;
697                                         break;
698                                 default:
699                                         return false;
700                         }
701                         $success = @imagecopyresampled(
702                                         $new_img,
703                                         $src_img,
704                                         0, 0, 0, 0,
705                                         $new_width,
706                                         $new_height,
707                                         $img_width,
708                                         $img_height
709                         );
710                         if ($success)
711                         {
712                                 if ('imagebmp' == $write_image)
713                                 {
714                                         BMP::imagebmp($new_img, $tmpFile);
715                                 }
716                                 else
717                                 {
718                                         $write_image($new_img, $tmpFile, $image_quality);
719                                 }
720                                 @imagedestroy($src_img);
721                                 @imagedestroy($new_img);
722                                 $imgString = file_get_contents($tmpFile);
723                                 unlink($tmpFile);
724                                 return $imgString;
725                         }
726                         unlink($tmpFile);
727                         @imagedestroy($src_img);
728                         @imagedestroy($new_img);
729                         return false;
730                 }
731
732     protected function generate_response($content, $print_response = true) {
733         if ($print_response) {
734             $json = json_encode($content);
735             $redirect = isset($_REQUEST['redirect']) ?
736                 stripslashes($_REQUEST['redirect']) : null;
737             if ($redirect) {
738                 header('Location: '.sprintf($redirect, rawurlencode($json)));
739                 return;
740             }
741             $this->head();
742             if (isset($_SERVER['HTTP_CONTENT_RANGE']) && is_array($content) &&
743                     is_object($content[0]) && $content[0]->size) {
744                 header('Range: 0-'.($this->fix_integer_overflow(intval($content[0]->size)) - 1));
745             }
746             echo $json;
747         }
748         return $content;
749     }
750
751     protected function get_version_param() {
752         return isset($_GET['version']) ? basename(stripslashes($_GET['version'])) : null;
753     }
754
755     protected function get_file_name_param() {
756         return isset($_GET['file']) ? basename(stripslashes($_GET['file'])) : null;
757     }
758
759     protected function get_file_type($file_path) {
760         switch (strtolower(pathinfo($file_path, PATHINFO_EXTENSION))) {
761             case 'jpeg':
762             case 'jpg':
763                 return 'image/jpeg';
764             case 'png':
765                 return 'image/png';
766             case 'gif':
767                 return 'image/gif';
768             default:
769                 return '';
770         }
771     }
772
773     protected function download() {
774         if (!$this->options['download_via_php']) {
775             header('HTTP/1.1 403 Forbidden');
776             return;
777         }
778         $file_name = $this->get_file_name_param();
779         if ($this->is_valid_file_object($file_name)) {
780             $file_path = $this->get_upload_path($file_name, $this->get_version_param());
781             if (is_file($file_path)) {
782                 if (!preg_match($this->options['inline_file_types'], $file_name)) {
783                     header('Content-Description: File Transfer');
784                     header('Content-Type: application/octet-stream');
785                     header('Content-Disposition: attachment; filename="'.$file_name.'"');
786                     header('Content-Transfer-Encoding: binary');
787                 } else {
788                     // Prevent Internet Explorer from MIME-sniffing the content-type:
789                     header('X-Content-Type-Options: nosniff');
790                     header('Content-Type: '.$this->get_file_type($file_path));
791                     header('Content-Disposition: inline; filename="'.$file_name.'"');
792                 }
793                 header('Content-Length: '.$this->get_file_size($file_path));
794                 header('Last-Modified: '.gmdate('D, d M Y H:i:s T', filemtime($file_path)));
795                 readfile($file_path);
796             }
797         }
798     }
799
800     protected function send_content_type_header() {
801         header('Vary: Accept');
802         if (isset($_SERVER['HTTP_ACCEPT']) &&
803             (strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false)) {
804             header('Content-type: application/json');
805         } else {
806             header('Content-type: text/plain');
807         }
808     }
809
810     protected function send_access_control_headers() {
811         header('Access-Control-Allow-Origin: '.$this->options['access_control_allow_origin']);
812         header('Access-Control-Allow-Credentials: '
813             .($this->options['access_control_allow_credentials'] ? 'true' : 'false'));
814         header('Access-Control-Allow-Methods: '
815             .implode(', ', $this->options['access_control_allow_methods']));
816         header('Access-Control-Allow-Headers: '
817             .implode(', ', $this->options['access_control_allow_headers']));
818     }
819
820     public function head() {
821         header('Pragma: no-cache');
822         header('Cache-Control: no-store, no-cache, must-revalidate');
823         header('Content-Disposition: inline; filename="files.json"');
824         // Prevent Internet Explorer from MIME-sniffing the content-type:
825         header('X-Content-Type-Options: nosniff');
826         if ($this->options['access_control_allow_origin']) {
827            $this->send_access_control_headers();
828         }
829         $this->send_content_type_header();
830     }
831
832     public function get($print_response = true) {
833         if ($print_response && isset($_GET['download'])) {
834             return $this->download();
835         }
836         $file_name = $this->get_file_name_param();
837         if ($file_name) {
838             $info = $this->get_file_object($file_name);
839         } else {
840             $info = $this->get_file_objects();
841         }
842         return $this->generate_response($info, $print_response);
843     }
844
845     public function post($print_response = true) {
846         if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') {
847             return $this->delete($print_response);
848         }
849         $upload = isset($_FILES[$this->options['param_name']]) ?
850             $_FILES[$this->options['param_name']] : null;
851         // Parse the Content-Disposition header, if available:
852         $file_name = isset($_SERVER['HTTP_CONTENT_DISPOSITION']) ?
853             rawurldecode(preg_replace(
854                 '/(^[^"]+")|("$)/',
855                 '',
856                 $_SERVER['HTTP_CONTENT_DISPOSITION']
857             )) : null;
858         $file_type = isset($_SERVER['HTTP_CONTENT_DESCRIPTION']) ?
859             $_SERVER['HTTP_CONTENT_DESCRIPTION'] : null;
860         // Parse the Content-Range header, which has the following form:
861         // Content-Range: bytes 0-524287/2000000
862         $content_range = isset($_SERVER['HTTP_CONTENT_RANGE']) ?
863             preg_split('/[^0-9]+/', $_SERVER['HTTP_CONTENT_RANGE']) : null;
864         $size =  $content_range ? $content_range[3] : null;
865         $info = array();
866         if ($upload && is_array($upload['tmp_name'])) {
867             // param_name is an array identifier like "files[]",
868             // $_FILES is a multi-dimensional array:
869             foreach ($upload['tmp_name'] as $index => $value) {
870                 $info[] = $this->handle_file_upload(
871                     $upload['tmp_name'][$index],
872                     $file_name ? $file_name : $upload['name'][$index],
873                     $size ? $size : $upload['size'][$index],
874                     $file_type ? $file_type : $upload['type'][$index],
875                     $upload['error'][$index],
876                     $index,
877                     $content_range
878                 );
879             }
880         } else {
881             // param_name is a single object identifier like "file",
882             // $_FILES is a one-dimensional array:
883             $info[] = $this->handle_file_upload(
884                 isset($upload['tmp_name']) ? $upload['tmp_name'] : null,
885                 $file_name ? $file_name : (isset($upload['name']) ?
886                         $upload['name'] : null),
887                 $size ? $size : (isset($upload['size']) ?
888                         $upload['size'] : $_SERVER['CONTENT_LENGTH']),
889                 $file_type ? $file_type : (isset($upload['type']) ?
890                         $upload['type'] : $_SERVER['CONTENT_TYPE']),
891                 isset($upload['error']) ? $upload['error'] : null,
892                 null,
893                 $content_range
894             );
895         }
896         return $this->generate_response($info, $print_response);
897     }
898
899     public function delete($print_response = true) {
900         $file_name = $this->get_file_name_param();
901         $file_path = $this->get_upload_path($file_name);
902         $success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
903         if ($success) {
904             foreach($this->options['image_versions'] as $version => $options) {
905                 if (!empty($version)) {
906                     $file = $this->get_upload_path($file_name, $version);
907                     if (is_file($file)) {
908                         unlink($file);
909                     }
910                 }
911             }
912         }
913         return $this->generate_response($success, $print_response);
914     }
915
916 }
917
918
919
920
921
922
923
924
925
926 // Read 1,4,8,24,32bit BMP files
927 // Save 24bit BMP files
928
929 // Author: de77
930 // Licence: MIT
931 // Webpage: de77.com
932 // Article about this class: http://de77.com/php/read-and-write-bmp-in-php-imagecreatefrombmp-imagebmp
933 // First-version: 07.02.2010
934 // Version: 21.08.2010
935
936 class BMP
937 {
938         public static function imagebmp(&$img, $filename = false)
939         {
940                 $wid = imagesx($img);
941                 $hei = imagesy($img);
942                 $wid_pad = str_pad('', $wid % 4, "\0");
943
944                 $size = 54 + ($wid + $wid_pad) * $hei * 3; //fixed
945
946                 //prepare & save header
947                 $header['identifier']           = 'BM';
948                 $header['file_size']            = self::dword($size);
949                 $header['reserved']                     = self::dword(0);
950                 $header['bitmap_data']          = self::dword(54);
951                 $header['header_size']          = self::dword(40);
952                 $header['width']                        = self::dword($wid);
953                 $header['height']                       = self::dword($hei);
954                 $header['planes']                       = self::word(1);
955                 $header['bits_per_pixel']       = self::word(24);
956                 $header['compression']          = self::dword(0);
957                 $header['data_size']            = self::dword(0);
958                 $header['h_resolution']         = self::dword(0);
959                 $header['v_resolution']         = self::dword(0);
960                 $header['colors']                       = self::dword(0);
961                 $header['important_colors']     = self::dword(0);
962
963                 if ($filename)
964                 {
965                     $f = fopen($filename, "wb");
966                     foreach ($header AS $h)
967                     {
968                         fwrite($f, $h);
969                     }
970
971                         //save pixels
972                         for ($y=$hei-1; $y>=0; $y--)
973                         {
974                                 for ($x=0; $x<$wid; $x++)
975                                 {
976                                         $rgb = imagecolorat($img, $x, $y);
977                                         fwrite($f, byte3($rgb));
978                                 }
979                                 fwrite($f, $wid_pad);
980                         }
981                         fclose($f);
982                 }
983                 else
984                 {
985                     foreach ($header AS $h)
986                     {
987                         echo $h;
988                     }
989
990                         //save pixels
991                         for ($y=$hei-1; $y>=0; $y--)
992                         {
993                                 for ($x=0; $x<$wid; $x++)
994                                 {
995                                         $rgb = imagecolorat($img, $x, $y);
996                                         echo self::byte3($rgb);
997                                 }
998                                 echo $wid_pad;
999                         }
1000                 }
1001         }
1002
1003         public static function imagecreatefrombmp($filename)
1004         {
1005                 $f = fopen($filename, "rb");
1006
1007                 //read header
1008             $header = fread($f, 54);
1009             $header = unpack(   'c2identifier/Vfile_size/Vreserved/Vbitmap_data/Vheader_size/' .
1010                                                         'Vwidth/Vheight/vplanes/vbits_per_pixel/Vcompression/Vdata_size/'.
1011                                                         'Vh_resolution/Vv_resolution/Vcolors/Vimportant_colors', $header);
1012
1013             if ($header['identifier1'] != 66 or $header['identifier2'] != 77)
1014             {
1015                 die('Not a valid bmp file');
1016             }
1017
1018             if (!in_array($header['bits_per_pixel'], array(24, 32, 8, 4, 1)))
1019             {
1020                 die('Only 1, 4, 8, 24 and 32 bit BMP images are supported');
1021             }
1022
1023                 $bps = $header['bits_per_pixel']; //bits per pixel
1024             $wid2 = ceil(($bps/8 * $header['width']) / 4) * 4;
1025                 $colors = pow(2, $bps);
1026
1027             $wid = $header['width'];
1028             $hei = $header['height'];
1029
1030             $img = imagecreatetruecolor($header['width'], $header['height']);
1031
1032                 //read palette
1033                 if ($bps < 9)
1034                 {
1035                         for ($i=0; $i<$colors; $i++)
1036                         {
1037                                 $palette[] = self::undword(fread($f, 4));
1038                         }
1039                 }
1040                 else
1041                 {
1042                         if ($bps == 32)
1043                         {
1044                                 imagealphablending($img, false);
1045                                 imagesavealpha($img, true);
1046                         }
1047                         $palette = array();
1048                 }
1049
1050                 //read pixels
1051             for ($y=$hei-1; $y>=0; $y--)
1052             {
1053                         $row = fread($f, $wid2);
1054                         $pixels = self::str_split2($row, $bps, $palette);
1055                 for ($x=0; $x<$wid; $x++)
1056                 {
1057                         self::makepixel($img, $x, $y, $pixels[$x], $bps);
1058                 }
1059             }
1060                 fclose($f);
1061
1062                 return $img;
1063         }
1064
1065         private static function str_split2($row, $bps, $palette)
1066         {
1067                 switch ($bps)
1068                 {
1069                         case 32:
1070                         case 24:        return str_split($row, $bps/8);
1071                         case  8:        $out = array();
1072                                                 $count = strlen($row);
1073                                                 for ($i=0; $i<$count; $i++)
1074                                                 {
1075                                                         $out[] = $palette[      ord($row[$i])           ];
1076                                                 }
1077                                                 return $out;
1078                         case  4:        $out = array();
1079                                                 $count = strlen($row);
1080                                                 for ($i=0; $i<$count; $i++)
1081                                                 {
1082                                                         $roww = ord($row[$i]);
1083                                                         $out[] = $palette[      ($roww & 240) >> 4      ];
1084                                                         $out[] = $palette[      ($roww & 15)            ];
1085                                                 }
1086                                                 return $out;
1087                         case  1:        $out = array();
1088                                                 $count = strlen($row);
1089                                                 for ($i=0; $i<$count; $i++)
1090                                                 {
1091                                                         $roww = ord($row[$i]);
1092                                                         $out[] = $palette[      ($roww & 128) >> 7      ];
1093                                                         $out[] = $palette[      ($roww & 64) >> 6       ];
1094                                                         $out[] = $palette[      ($roww & 32) >> 5       ];
1095                                                         $out[] = $palette[      ($roww & 16) >> 4       ];
1096                                                         $out[] = $palette[      ($roww & 8) >> 3        ];
1097                                                         $out[] = $palette[      ($roww & 4) >> 2        ];
1098                                                         $out[] = $palette[      ($roww & 2) >> 1        ];
1099                                                         $out[] = $palette[      ($roww & 1)                     ];
1100                                                 }
1101                                                 return $out;
1102                 }
1103         }
1104
1105         private static function makepixel($img, $x, $y, $str, $bps)
1106         {
1107                 switch ($bps)
1108                 {
1109                         case 32 :       $a = ord($str[0]);
1110                                                 $b = ord($str[1]);
1111                                                 $c = ord($str[2]);
1112                                                 $d = 256 - ord($str[3]); //TODO: gives imperfect results
1113                                                 $pixel = $d*256*256*256 + $c*256*256 + $b*256 + $a;
1114                                                 imagesetpixel($img, $x, $y, $pixel);
1115                                                 break;
1116                         case 24 :       $a = ord($str[0]);
1117                                                 $b = ord($str[1]);
1118                                                 $c = ord($str[2]);
1119                                                 $pixel = $c*256*256 + $b*256 + $a;
1120                                                 imagesetpixel($img, $x, $y, $pixel);
1121                                                 break;
1122                         case 8 :
1123                         case 4 :
1124                         case 1 :        imagesetpixel($img, $x, $y, $str);
1125                                                 break;
1126                 }
1127         }
1128
1129         private static function byte3($n)
1130         {
1131                 return chr($n & 255) . chr(($n >> 8) & 255) . chr(($n >> 16) & 255);
1132         }
1133
1134         private static function undword($n)
1135         {
1136                 $r = unpack("V", $n);
1137                 return $r[1];
1138         }
1139
1140         private static function dword($n)
1141         {
1142                 return pack("V", $n);
1143         }
1144
1145         private static function word($n)
1146         {
1147                 return pack("v", $n);
1148         }
1149 }
1150
1151 function imagebmp(&$img, $filename = false)
1152 {
1153         return BMP::imagebmp($img, $filename);
1154 }
1155
1156 function imagecreatefrombmp($filename)
1157 {
1158         return BMP::imagecreatefrombmp($filename);
1159 }
1160