source: trunk/plugins/ImageManager/Classes/ImageEditor.php @ 1143

Last change on this file since 1143 was 1143, checked in by gogo, 10 years ago

Security patch - see ticket:1363

  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
File size: 13.1 KB
Line 
1<?php
2/**
3 * Image Editor. Editing tools, crop, rotate, scale and save.
4 * @author $Author:ray $
5 * @version $Id:ImageEditor.php 938 2008-01-22 20:13:47Z ray $
6 * @package ImageManager
7 */
8
9require_once('../ImageManager/Classes/Transform.php');
10
11/**
12 * Handles the basic image editing capbabilities.
13 * @author $Author:ray $
14 * @version $Id:ImageEditor.php 938 2008-01-22 20:13:47Z ray $
15 * @package ImageManager
16 * @subpackage Editor
17 */
18class ImageEditor
19{
20        /**
21         * ImageManager instance.
22         */
23        var $manager;
24
25        /**
26         * user based on IP address
27         */
28        var $_uid;
29
30        /**
31         * tmp file storage time.
32         */
33        var $lapse_time =900; //15 mins
34
35        var $filesaved = 0;
36
37        /**
38         * Create a new ImageEditor instance. Editing requires a
39         * tmp file, which is saved in the current directory where the
40         * image is edited. The tmp file is assigned by md5 hash of the
41         * user IP address. This hashed is used as an ID for cleaning up
42         * the tmp files. In addition, any tmp files older than the
43         * the specified period will be deleted.
44         * @param ImageManager $manager the image manager, we need this
45         * for some file and path handling functions.
46         */
47        function ImageEditor($manager)
48        {
49                $this->manager = $manager;
50                $this->_uid = md5($_SERVER['REMOTE_ADDR']);
51        }
52       
53        /**
54         * Did we save a file?
55         * @return int 1 if the file was saved sucessfully,
56         * 0 no save operation, -1 file save error.
57         */
58        function isFileSaved()
59        {
60                Return $this->filesaved;
61        }
62
63        /**
64         * Process the image, if not action, just display the image.
65         * @return array with image information, empty array if not an image.
66         * <code>array('src'=>'url of the image', 'dimensions'=>'width="xx" height="yy"',
67         * 'file'=>'image file, relative', 'fullpath'=>'full path to the image');</code>
68         */
69        function processImage()
70        {
71                if(isset($_GET['img']))
72                        $relative = rawurldecode($_GET['img']);
73                else
74                        Return array();
75               
76                //$relative = '/Series2004NoteFront.jpg';
77
78                $imgURL = $this->manager->getFileURL($relative);
79                $fullpath = $this->manager->getFullPath($relative);
80               
81                $imgInfo = @getImageSize($fullpath);
82                if(!is_array($imgInfo))
83                        Return array();
84
85                $action = $this->getAction();
86
87                if(!is_null($action))
88                {
89                        $image = $this->processAction($action, $relative, $fullpath);
90                }
91                else
92                {
93                        $image['src'] = $imgURL;
94                        $image['dimensions'] = $imgInfo[3];
95                        $image['file'] = $relative;
96                        $image['fullpath'] = $fullpath;
97                        $image['filesize'] = @filesize($fullpath);
98                }
99
100                Return $image;
101        }
102
103        /**
104         * Process the actions, crop, scale(resize), rotate, flip, and save.
105         * When ever an action is performed, the result is save into a
106         * temporary image file, see createUnique on the filename specs.
107         * It does not return the saved file, alway returning the tmp file.
108         * @param string $action, should be 'crop', 'scale', 'rotate','flip', or 'save'
109         * @param string $relative the relative image filename
110         * @param string $fullpath the fullpath to the image file
111         * @return array with image information
112         * <code>array('src'=>'url of the image', 'dimensions'=>'width="xx" height="yy"',
113         * 'file'=>'image file, relative', 'fullpath'=>'full path to the image');</code>
114         */
115        function processAction($action, $relative, $fullpath)
116        {
117                $params = '';
118               
119                if(isset($_GET['params']))
120                        $params = $_GET['params'];
121
122                $values =  explode(',',$params);
123                $saveFile = $this->getSaveFileName($values[0]);
124
125                $img = Image_Transform::factory(IMAGE_CLASS);
126                $img->load($fullpath);
127               
128                if ( is_callable( array($img,'paletteToTrueColorWithTransparency')) && !imageistruecolor($img->imageHandle))
129                {
130                        $img->paletteToTrueColorWithTransparency();
131                }
132                switch ($action)
133                {
134                        case 'crop':
135                                $img->crop(intval($values[0]),intval($values[1]),
136                                                        intval($values[2]),intval($values[3]));
137                        break;
138                        case 'scale':
139                                $img->resize(intval($values[0]),intval($values[1]));
140                                break;
141                        case 'rotate':
142                                $img->rotate(floatval($values[0]));
143                                break;
144                        case 'flip':
145                                if ($values[0] == 'hoz')
146                                        $img->flip(true);
147                                else if($values[0] == 'ver')
148                                        $img->flip(false);
149                                break;
150                        case 'save':
151                                if(!is_null($saveFile))
152                                {
153                                        $quality = intval($values[1]);
154                            if($quality <0) $quality = 85;
155                                        $newSaveFile = $this->makeRelative($relative, $saveFile);
156                                        $newSaveFile = $this->getUniqueFilename($newSaveFile);
157                                       
158                                        //get unique filename just returns the filename, so
159                                        //we need to make the relative path once more.
160                                        $newSaveFile = $this->makeRelative($relative, $newSaveFile);
161                                $image['saveFile'] = $newSaveFile;
162                                        $newSaveFullpath = $this->manager->getFullPath($newSaveFile);
163                                        if ( $values[0] == 'gif' && is_callable(array($img, 'preserveTransparencyForPalette')))
164                                        {
165                                                $img->preserveTransparencyForPalette();
166                                        }
167                                        $img->save($newSaveFullpath, $values[0], $quality);
168                                        if(is_file($newSaveFullpath))
169                                                $this->filesaved = 1;
170                                        else
171                                                $this->filesaved = -1;
172                                }
173                                break;
174                                case 'preview':
175                                        $quality = intval($values[1]);
176                                       
177                                       
178                                        $image['file'] = $relative;
179                                        $image['fullpath'] = $fullpath;
180                                       
181                                        //create the tmp image file
182                                        $filename = $this->createUnique($fullpath);
183                                        $newRelative = $this->makeRelative($relative, $filename);
184                                        $newFullpath = $this->manager->getFullPath($newRelative);
185                                        $newURL = $this->manager->getFileURL($newRelative);
186                                       
187                                       
188                                        if ( $values[0] == 'gif' && is_callable(array($img, 'preserveTransparencyForPalette')))
189                                        {
190                                                $img->preserveTransparencyForPalette();
191                                        }
192                                        $img->save($newFullpath, $values[0] );
193                                        $img->free();
194                       
195                                        //get the image information
196                                        $imgInfo = @getimagesize($newFullpath);
197                               
198                                        $image['src'] = $newURL;
199                                $image['width'] = $imgInfo[0];
200                                        $image['height'] = $imgInfo[1];
201                                        $image['dimensions'] = $imgInfo[3];
202                                        $image['file'] = $relative;
203                                        $image['fullpath'] = $fullpath;
204                                        $image['filesize'] = @filesize($newFullpath);
205
206                                        Return $image;
207       
208                                break;
209                }
210               
211                //create the tmp image file
212                $filename = $this->createUnique($fullpath);
213                $newRelative = $this->makeRelative($relative, $filename);
214                $newFullpath = $this->manager->getFullPath($newRelative);
215                $newURL = $this->manager->getFileURL($newRelative);
216
217                //save the file.
218                $img->save($newFullpath, 'png' );
219                $img->free();
220
221                //get the image information
222                $imgInfo = @getimagesize($newFullpath);
223
224                $image['src'] = $newURL;
225        $image['width'] = $imgInfo[0];
226                $image['height'] = $imgInfo[1];
227                $image['dimensions'] = $imgInfo[3];
228                $image['file'] = $newRelative;
229                $image['fullpath'] = $newFullpath;
230                $image['filesize'] = @filesize($newFullpath);
231                $image['type'] = image_type_to_mime_type($imgInfo[2]);
232               
233                Return $image;
234       
235        }
236
237        /**
238         * Get the file name base on the save name
239         * and the save type.
240         * @param string $type image type, 'jpeg', 'png', or 'gif'
241         * @return string the filename according to save type
242         */
243        function getSaveFileName($type)
244        {
245                if(!isset($_GET['file']))
246                        Return null;
247
248                $filename = Files::escape(rawurldecode($_GET['file']));
249                $index = strrpos($filename,'.');
250                $base = substr($filename,0,$index);
251                $ext = strtolower(substr($filename,$index+1,strlen($filename)));
252
253                if($type == 'jpeg' && !($ext=='jpeg' || $ext=='jpg'))
254                {
255                        Return $base.'.jpeg';
256                }
257                if($type=='png' && $ext != 'png')
258                        Return $base.'.png';
259                if($type=='gif' && $ext != 'gif')
260                        Return $base.'.gif';
261
262    // Ensure type is in acceptable image types
263    $valid_extensions = $this->manager->config['allowed_image_extensions'];   
264    if(!in_array($ext, $valid_extensions))
265                {
266      return $base . ".".strtolower($type ? $type : 'jpg');
267                }
268   
269                Return $filename;
270        }
271
272        /**
273         * Get the default save file name, used by editor.php.
274         * @return string a suggestive filename, this should be unique
275         */
276        function getDefaultSaveFile()
277        {
278                if(isset($_GET['img']))
279                        $relative = rawurldecode($_GET['img']);
280                else
281                        Return null;
282
283                Return $this->getUniqueFilename($relative);
284        }
285
286        /**
287         * Get a unique filename. If the file exists, the filename
288         * base is appended with an increasing integer.
289         * @param string $relative the relative filename to the base_dir
290         * @return string a unique filename in the current path
291         */
292        function getUniqueFilename($relative)
293        {
294                $fullpath = $this->manager->getFullPath($relative);
295               
296                $pathinfo = pathinfo($fullpath);
297
298                $path = Files::fixPath($pathinfo['dirname']);
299                $file = Files::escape($pathinfo['basename']);
300               
301                $filename = $file;
302
303                $dotIndex = strrpos($file, '.');
304                $ext = '';
305
306                if(is_int($dotIndex))
307                {
308                        $ext = substr($file, $dotIndex);
309                        $base = substr($file, 0, $dotIndex);
310                }
311
312                $counter = 0;
313                while(is_file($path.$filename))
314                {
315                        $counter++;
316                        $filename = $base.'_'.$counter.$ext;
317                }
318               
319                Return $filename;
320               
321        }
322
323        /**
324         * Specifiy the original relative path, a new filename
325         * and return the new filename with relative path.
326         * i.e. $pathA (-filename) + $file
327         * @param string $pathA the relative file
328         * @param string $file the new filename
329         * @return string relative path with the new filename
330         */
331        function makeRelative($pathA, $file)
332        {
333                $index = strrpos($pathA,'/');
334                if(!is_int($index))
335                        Return $file;
336
337                $path = substr($pathA, 0, $index);
338                Return Files::fixPath($path).$file;
339        }
340
341        /**
342         * Get the action GET parameter
343         * @return string action parameter
344         */
345        function getAction()
346        {
347                $action = null;
348                if(isset($_GET['action']))
349                        $action = $_GET['action'];
350                Return $action;
351        }
352
353        /**
354         * Generate a unique string based on md5(microtime()).
355         * Well not so uniqe, as it is limited to 6 characters
356         * @return string unique string.
357         */
358    function uniqueStr()
359    {
360      return substr(md5(microtime()),0,6);
361    }
362
363        /**
364         * Create unique tmp image file name.
365         * The filename is based on the tmp file prefix
366         * specified in config.inc.php plus
367         * the UID (basically a md5 of the remote IP)
368         * and some random 6 character string.
369         * This function also calls to clean up the tmp files.
370         * @param string $file the fullpath to a file
371         * @return string a unique filename for that path
372         * NOTE: it only returns the filename, path no included.
373         */
374        function createUnique($file)
375        {
376                $pathinfo = pathinfo($file);
377                $path = Files::fixPath($pathinfo['dirname']);
378                $imgType = $this->getImageType($file);
379
380                $unique_str = $this->manager->getTmpPrefix().$this->_uid.'_'.$this->uniqueStr().".".$imgType;
381
382           //make sure the the unique temp file does not exists
383        while (file_exists($path.$unique_str))
384        {
385            $unique_str = $this->manager->getTmpPrefix().$this->_uid.'_'.$this->uniqueStr().".".$imgType;
386        }
387
388                $this->cleanUp($path,$pathinfo['basename']);
389
390                Return $unique_str;
391        }
392
393        /**
394         * Delete any tmp image files.
395         * @param string $path the full path
396         * where the clean should take place.
397         */
398        function cleanUp($path,$file)
399        {
400                $path = Files::fixPath($path);
401
402                if(!is_dir($path))
403                        Return false;
404
405                $d = @dir($path);
406               
407                $tmp = $this->manager->getTmpPrefix();
408                $tmpLen = strlen($tmp);
409
410                $prefix = $tmp.$this->_uid;
411                $len = strlen($prefix);
412
413                while (false !== ($entry = $d->read()))
414                {
415                        //echo $entry."<br>";
416                        if(is_file($path.$entry) && $this->manager->isTmpFile($entry))
417                        {
418                                if(substr($entry,0,$len)==$prefix && $entry != $file)
419                                        Files::delFile($path.$entry);
420                                else if(substr($entry,0,$tmpLen)==$tmp && $entry != $file)
421                                {
422                                        if(filemtime($path.$entry)+$this->lapse_time < time())
423                                                Files::delFile($path.$entry);
424                                }
425                        }
426                }
427                $d->close();
428        }
429
430        /**
431         * Get the image type base on an image file.
432         * @param string $file the full path to the image file.
433         * @return string of either 'gif', 'jpeg', 'png' or 'bmp'
434         * otherwise it will return null.
435         */
436        function getImageType($file)
437        {
438                $imageInfo = @getImageSize($file);
439
440                if(!is_array($imageInfo))
441                        Return null;
442
443                switch($imageInfo[2])
444                {
445                        case 1:
446                                Return 'gif';
447                        case 2:
448                                Return 'jpeg';
449                        case 3:
450                                Return 'png';
451                        case 6:
452                                Return 'bmp';
453                }
454
455                Return null;
456        }
457
458        /**
459         * Check if the specified image can be edit by GD
460         * mainly to check that GD can read and save GIFs
461         * @return int 0 if it is not a GIF file, 1 is GIF is editable, -1 if not editable.
462         */
463        function isGDEditable()
464        {
465                if(isset($_GET['img']))
466                        $relative = rawurldecode($_GET['img']);
467                else
468                        Return 0;
469                if(IMAGE_CLASS != 'GD')
470                        Return 0;
471
472                $fullpath = $this->manager->getFullPath($relative);
473 
474                $type = $this->getImageType($fullpath);
475                if($type != 'gif')
476                        Return 0;
477
478                if(function_exists('ImageCreateFrom'.$type)
479                        && function_exists('image'.$type))
480                        Return 1;
481                else
482                        Return -1;
483        }
484
485        /**
486         * Check if GIF can be edit by GD.
487         * @return int 0 if it is not using the GD library, 1 is GIF is editable, -1 if not editable.
488         */
489        function isGDGIFAble()
490        {
491                if(IMAGE_CLASS != 'GD')
492                        Return 0;
493
494                if(function_exists('ImageCreateFromGif')
495                        && function_exists('imagegif'))
496                        Return 1;
497                else
498                        Return -1;
499        }
500}
501
502?>
Note: See TracBrowser for help on using the repository browser.