root / trunk / plugins / ImageManager / Classes / ImageManager.php @ 1143

Revision 1143, 17.4 kB (checked in by gogo, 4 years ago)

Security patch - see ticket:1363

  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
Line 
1<?php
2/**
3 * ImageManager, list images, directories, and thumbnails.
4 * @author $Author:ray $
5 * @version $Id:ImageManager.php 709 2007-01-30 23:22:04Z ray $
6 * @package ImageManager
7 */
8
9require_once('../ImageManager/Classes/Files.php');
10
11// uncomment to turn on debugging
12
13// _ddtOn();
14
15/**
16 * ImageManager Class.
17 * @author $Author:ray $
18 * @version $Id:ImageManager.php 709 2007-01-30 23:22:04Z ray $
19 */
20class ImageManager
21{
22    /**
23     * Configuration array.
24     */
25    var $config;
26
27    /**
28     * Array of directory information.
29     */
30    var $dirs;
31
32    /**
33     * Constructor. Create a new Image Manager instance.
34     * @param array $config configuration array, see config.inc.php
35     */
36    function ImageManager($config)
37    {
38        $this->config = $config;
39    }
40
41    /**
42     * Get the images base directory.
43     * @return string base dir, see config.inc.php
44     */
45    function getImagesDir()
46    {
47        Return $this->config['images_dir'];
48    }
49
50    /**
51     * Get the images base URL.
52     * @return string base url, see config.inc.php
53     */
54    function getImagesURL()
55    {
56        Return $this->config['images_url'];
57    }
58
59    function isValidBase()
60    {
61        return is_dir($this->getImagesDir());
62    }
63
64    /**
65     * Get the tmp file prefix.
66     * @return string tmp file prefix.
67     */
68    function getTmpPrefix()
69    {
70        Return $this->config['tmp_prefix'];
71    }
72
73    /**
74     * Get the sub directories in the base dir.
75     * Each array element contain
76     * the relative path (relative to the base dir) as key and the
77     * full path as value.
78     * @return array of sub directries
79     * <code>array('path name' => 'full directory path', ...)</code>
80     */
81    function getDirs()
82    {
83        if(is_null($this->dirs))
84        {
85            $dirs = $this->_dirs($this->getImagesDir(),'/');
86            ksort($dirs);
87            $this->dirs = $dirs;
88        }
89        return $this->dirs;
90    }
91
92    /**
93     * Recursively travese the directories to get a list
94     * of accessable directories.
95     * @param string $base the full path to the current directory
96     * @param string $path the relative path name
97     * @return array of accessiable sub-directories
98     * <code>array('path name' => 'full directory path', ...)</code>
99     */
100    function _dirs($base, $path)
101    {
102        $base = Files::fixPath($base);
103        $dirs = array();
104
105        if($this->isValidBase() == false)
106            return $dirs;
107
108        $d = @dir($base);
109       
110        while (false !== ($entry = $d->read()))
111        {
112            //If it is a directory, and it doesn't start with
113            // a dot, and if is it not the thumbnail directory
114            if(is_dir($base.$entry)
115                && substr($entry,0,1) != '.'
116                && $this->isThumbDir($entry) == false)
117            {
118                $relative = Files::fixPath($path.$entry);
119                $fullpath = Files::fixPath($base.$entry);
120                $dirs[$relative] = $fullpath;
121                $dirs = array_merge($dirs, $this->_dirs($fullpath, $relative));
122            }
123        }
124        $d->close();
125
126        Return $dirs;
127    }
128
129    /**
130     * Get all the files and directories of a relative path.
131     * @param string $path relative path to be base path.
132     * @return array of file and path information.
133     * <code>array(0=>array('relative'=>'fullpath',...), 1=>array('filename'=>fileinfo array(),...)</code>
134     * fileinfo array: <code>array('url'=>'full url',
135     *                       'relative'=>'relative to base',
136     *                        'fullpath'=>'full file path',
137     *                        'image'=>imageInfo array() false if not image,
138     *                        'stat' => filestat)</code>
139     */
140    function getFiles($path)
141    {
142        $files = array();
143        $dirs = array();
144
145        if($this->isValidBase() == false)
146            return array($files,$dirs);
147
148        $path = Files::fixPath($path);
149        $base = Files::fixPath($this->getImagesDir());
150        $fullpath = Files::makePath($base,$path);
151
152
153        $d = @dir($fullpath);
154       
155        while (false !== ($entry = $d->read()))
156        {
157            //not a dot file or directory
158            if(substr($entry,0,1) != '.')
159            {
160                if(is_dir($fullpath.$entry)
161                    && $this->isThumbDir($entry) == false)
162                {
163                    $relative = Files::fixPath($path.$entry);
164                    $full = Files::fixPath($fullpath.$entry);
165                    $count = $this->countFiles($full);
166                    $dirs[$relative] = array('fullpath'=>$full,'entry'=>$entry,'count'=>$count);
167                }
168                else if(is_file($fullpath.$entry) && $this->isThumb($entry)==false && $this->isTmpFile($entry) == false)
169                {
170                    $img = $this->getImageInfo($fullpath.$entry);
171
172                    if(!(!is_array($img)&&$this->config['validate_images']))
173                    {
174                        $file['url'] = Files::makePath($this->config['base_url'],$path).$entry;
175                        $file['relative'] = $path.$entry;
176                        $file['fullpath'] = $fullpath.$entry;
177                        $file['image'] = $img;
178                        $file['stat'] = stat($fullpath.$entry);
179                        $files[$entry] = $file;
180                    }
181                }
182            }
183        }
184        $d->close();
185        ksort($dirs);
186        ksort($files);
187       
188        Return array($dirs, $files);
189    }   
190
191    /**
192     * Count the number of files and directories in a given folder
193     * minus the thumbnail folders and thumbnails.
194     */
195    function countFiles($path)
196    {
197        $total = 0;
198
199        if(is_dir($path))
200        {
201            $d = @dir($path);
202
203            while (false !== ($entry = $d->read()))
204            {
205                //echo $entry."<br>";
206                if(substr($entry,0,1) != '.'
207                    && $this->isThumbDir($entry) == false
208                    && $this->isTmpFile($entry) == false
209                    && $this->isThumb($entry) == false)
210                {
211                    $total++;
212                }
213            }
214            $d->close();
215        }
216        return $total;
217    }
218
219    /**
220     * Get image size information.
221     * @param string $file the image file
222     * @return array of getImageSize information,
223     *  false if the file is not an image.
224     */
225    function getImageInfo($file)
226    {
227        Return @getImageSize($file);
228    }
229
230    /**
231     * Check if the file contains the thumbnail prefix.
232     * @param string $file filename to be checked
233     * @return true if the file contains the thumbnail prefix, false otherwise.
234     */
235    function isThumb($file)
236    {
237        $len = strlen($this->config['thumbnail_prefix']);
238        if(substr($file,0,$len)==$this->config['thumbnail_prefix'])
239            Return true;
240        else
241            Return false;
242    }
243
244    /**
245     * Check if the given directory is a thumbnail directory.
246     * @param string $entry directory name
247     * @return true if it is a thumbnail directory, false otherwise
248     */
249    function isThumbDir($entry)
250    {
251        if($this->config['thumbnail_dir'] == false
252            || strlen(trim($this->config['thumbnail_dir'])) == 0)
253            Return false;       
254        else
255            Return ($entry == $this->config['thumbnail_dir']);
256    }
257
258    /**
259     * Check if the given file is a tmp file.
260     * @param string $file file name
261     * @return boolean true if it is a tmp file, false otherwise
262     */
263    function isTmpFile($file)
264    {
265        $len = strlen($this->config['tmp_prefix']);
266        if(substr($file,0,$len)==$this->config['tmp_prefix'])
267            Return true;
268        else
269            Return false;         
270    }
271
272    /**
273     * For a given image file, get the respective thumbnail filename
274     * no file existence check is done.
275     * @param string $fullpathfile the full path to the image file
276     * @return string of the thumbnail file
277     */
278    function getThumbName($fullpathfile)
279    {
280        $path_parts = pathinfo($fullpathfile);
281       
282        $thumbnail = $this->config['thumbnail_prefix'].$path_parts['basename'];
283
284        if( strlen(trim($this->config['thumbnail_dir'])) == 0 || $this->config['safe_mode'] == true)
285        {
286            Return Files::makeFile($path_parts['dirname'],$thumbnail);
287        }
288        else
289        {
290                $path = Files::makePath($path_parts['dirname'],$this->config['thumbnail_dir']);
291                if(!is_dir($path))
292                    Files::createFolder($path);
293                Return Files::makeFile($path,$thumbnail);
294        }
295    }
296   
297    /**
298     * Similar to getThumbName, but returns the URL, base on the
299     * given base_url in config.inc.php
300     * @param string $relative the relative image file name,
301     * relative to the base_dir path
302     * @return string the url of the thumbnail
303     */
304    function getThumbURL($relative)
305    {
306
307        _ddt( __FILE__, __LINE__, "getThumbURL(): relative is '$relative'" );
308
309        $path_parts = pathinfo($relative);
310        $thumbnail = $this->config['thumbnail_prefix'].$path_parts['basename'];
311        if($path_parts['dirname']=='\\') $path_parts['dirname']='/';
312
313        if($this->config['safe_mode'] == true
314            || strlen(trim($this->config['thumbnail_dir'])) == 0)
315        {
316            Return Files::makeFile($this->getImagesURL(),$thumbnail);
317        }
318        else
319        {
320            if(strlen(trim($this->config['thumbnail_dir'])) > 0)
321            {
322                $path = Files::makePath($path_parts['dirname'],$this->config['thumbnail_dir']);
323                $url_path = Files::makePath($this->getImagesURL(), $path);
324
325                _ddt( __FILE__, __LINE__, "getThumbURL(): url_path is '$url_path'" );
326
327                Return Files::makeFile($url_path,$thumbnail);
328            }
329            else //should this ever happen?
330            {
331                //error_log('ImageManager: Error in creating thumbnail url');
332            }
333
334        }
335    }
336
337
338    /**
339     * For a given image file, get the respective resized filename
340     * no file existence check is done.
341     * @param string $fullpathfile the full path to the image file
342     * @param integer $width the intended width
343     * @param integer $height the intended height
344     * @param boolean $mkDir whether to attempt to make the resized_dir if it doesn't exist
345     * @return string of the resized filename
346     */
347    function getResizedName($fullpathfile, $width, $height, $mkDir = TRUE)
348    {
349        $path_parts = pathinfo($fullpathfile);
350
351        $thumbnail = $this->config['resized_prefix']."_{$width}x{$height}_{$path_parts['basename']}";
352
353        if( strlen(trim($this->config['resized_dir'])) == 0 || $this->config['safe_mode'] == true )
354        {
355            Return Files::makeFile($path_parts['dirname'],$thumbnail);
356        }
357        else
358        {
359            $path = Files::makePath($path_parts['dirname'],$this->config['resized_dir']);
360            if($mkDir && !is_dir($path))
361                Files::createFolder($path);
362            Return Files::makeFile($path,$thumbnail);
363        }
364    }
365
366    /**
367     * Check if the given path is part of the subdirectories
368     * under the base_dir.
369     * @param string $path the relative path to be checked
370     * @return boolean true if the path exists, false otherwise
371     */
372    function validRelativePath($path)
373    {
374        $dirs = $this->getDirs();
375        if($path == '/')
376            Return true;
377        //check the path given in the url against the
378        //list of paths in the system.
379        for($i = 0; $i < count($dirs); $i++)
380        {
381            $key = key($dirs);
382            //we found the path
383            if($key == $path)
384                Return true;
385       
386            next($dirs);
387        }       
388        Return false;
389    }
390
391    /**
392     * Process uploaded files, assumes the file is in
393     * $_FILES['upload'] and $_POST['dir'] is set.
394     * The dir must be relative to the base_dir and exists.
395     * If 'validate_images' is set to true, only file with
396     * image dimensions will be accepted.
397     * @return null
398     */
399    function processUploads()
400    {
401        if($this->isValidBase() == false)
402            return;
403
404        $relative = null;
405
406        if(isset($_POST['dir']))
407            $relative = rawurldecode($_POST['dir']);
408        else
409            return;
410
411        //check for the file, and must have valid relative path
412        if(isset($_FILES['upload']) && $this->validRelativePath($relative))
413        {
414            $this->_processFiles($relative, $_FILES['upload']);
415        }
416    }
417
418    /**
419     * Process upload files. The file must be an
420     * uploaded file. If 'validate_images' is set to
421     * true, only images will be processed. Any duplicate
422     * file will be renamed. See Files::copyFile for details
423     * on renaming.
424     * @param string $relative the relative path where the file
425     * should be copied to.
426     * @param array $file the uploaded file from $_FILES
427     * @return boolean true if the file was processed successfully,
428     * false otherwise
429     */
430    function _processFiles($relative, $file)
431    {
432       
433        if($file['error']!=0)
434        {
435            Return false;
436        }
437
438        if(!is_file($file['tmp_name']))
439        {
440            Return false;
441        }
442
443        if(!is_uploaded_file($file['tmp_name']))
444        {
445            Files::delFile($file['tmp_name']);
446            Return false;
447        }
448       
449
450        if($this->config['validate_images'] == true)
451        {
452            $imgInfo = @getImageSize($file['tmp_name']);
453            if(!is_array($imgInfo))
454            {
455                Files::delFile($file['tmp_name']);
456                Return false;
457            }
458        }
459   
460    $valid_extensions = $this->config['allowed_image_extensions'];
461    $afruext = strtolower(substr(strrchr($file['name'], "."), 1));
462    if(!in_array($afruext, $valid_extensions))
463        {
464            Files::delFile($file['tmp_name']);
465            Return 'Cannot upload $extension='.$afruext.'$ Files. Permission denied.';
466        }
467
468        //now copy the file
469        $path = Files::makePath($this->getImagesDir(),$relative);
470        $result = Files::copyFile($file['tmp_name'], $path, $file['name']);
471
472        //no copy error
473        if(!is_int($result))
474        {
475            Files::delFile($file['tmp_name']);
476            Return true;
477        }
478
479        //delete tmp files.
480        Files::delFile($file['tmp_name']);
481        Return false;
482    }
483
484    /**
485     * Get the URL of the relative file.
486     * basically appends the relative file to the
487     * base_url given in config.inc.php
488     * @param string $relative a file the relative to the base_dir
489     * @return string the URL of the relative file.
490     */
491    function getFileURL($relative)
492    {
493        Return Files::makeFile($this->getImagesURL(),$relative);
494    }
495
496    /**
497     * Get the fullpath to a relative file.
498     * @param string $relative the relative file.
499     * @return string the full path, .ie. the base_dir + relative.
500     */
501    function getFullPath($relative)
502    {
503        Return Files::makeFile($this->getImagesDir(),$relative);;
504    }
505
506    /**
507     * Get the default thumbnail.
508     * @return string default thumbnail, empty string if
509     * the thumbnail doesn't exist.
510     */
511    function getDefaultThumb()
512    {
513
514        // FIXME: hack
515
516        Return $this->config['default_thumbnail'];
517
518        if(is_file($this->config['default_thumbnail']))
519            {
520            Return $this->config['default_thumbnail'];
521            }
522        else
523            Return '';
524    }
525
526
527    /**
528     * Get the thumbnail url to be displayed.
529     * If the thumbnail exists, and it is up-to-date
530     * the thumbnail url will be returns. If the
531     * file is not an image, a default image will be returned.
532     * If it is an image file, and no thumbnail exists or
533     * the thumbnail is out-of-date (i.e. the thumbnail
534     * modified time is less than the original file)
535     * then a thumbs.php?img=filename.jpg is returned.
536     * The thumbs.php url will generate a new thumbnail
537     * on the fly. If the image is less than the dimensions
538     * of the thumbnails, the image will be display instead.
539     * @param string $relative the relative image file.
540     * @return string the url of the thumbnail, be it
541     * actually thumbnail or a script to generate the
542     * thumbnail on the fly.
543     */
544    function getThumbnail($relative)
545    {
546
547        global $IMConfig;
548
549        _ddt( __FILE__, __LINE__, "getThumbnail(): top with '$relative'" );
550
551        $fullpath = Files::makeFile($this->getImagesDir(),$relative);
552
553        //not a file???
554        if(!is_file($fullpath))
555            Return $this->getDefaultThumb();
556
557        $imgInfo = @getImageSize($fullpath);
558       
559        //not an image
560        if(!is_array($imgInfo))
561            Return $this->getDefaultThumb();
562
563        //the original image is smaller than thumbnails,
564        //so just return the url to the original image.
565        if ($imgInfo[0] <= $this->config['thumbnail_width']
566         && $imgInfo[1] <= $this->config['thumbnail_height'])
567            Return $this->getFileURL($relative);
568
569        $thumbnail = $this->getThumbName($fullpath);
570       
571        //check for thumbnails, if exists and
572        // it is up-to-date, return the thumbnail url
573        if(is_file($thumbnail))
574        {
575            if(filemtime($thumbnail) >= filemtime($fullpath))
576                {
577                _ddt( __FILE__, __LINE__, "getThumbnail(): returning url '" . $this->getThumbURL($relative) . "'" );
578
579                Return $this->getThumbURL($relative);
580                }
581        }
582
583        //well, no thumbnail was found, so ask the thumbs.php
584        //to generate the thumbnail on the fly.
585        Return $IMConfig['backend_url'] . '__function=thumbs&img='.rawurlencode($relative);
586    }
587
588    /**
589     * Delete and specified files.
590     * @return boolean true if delete, false otherwise
591     */
592    function deleteFiles()
593    {
594        if(isset($_GET['delf']))
595            $this->_delFile(rawurldecode($_GET['delf']));
596    }
597
598    /**
599     * Delete and specified directories.
600     * @return boolean true if delete, false otherwise
601     */
602    function deleteDirs()
603    {
604         if(isset($_GET['deld']))
605            return $this->_delDir(rawurldecode($_GET['deld']));       
606         else
607             Return false;
608    }
609
610    /**
611     * Delete the relative file, and any thumbnails.
612     * @param string $relative the relative file.
613     * @return boolean true if deleted, false otherwise.
614     */
615    function _delFile($relative)
616    {
617        $fullpath = Files::makeFile($this->getImagesDir(),$relative);
618       
619        //check that the file is an image
620        if($this->config['validate_images'] == true)
621        {
622            if(!is_array($this->getImageInfo($fullpath)))
623                return false; //hmmm not an Image!!???
624        }
625
626        $thumbnail = $this->getThumbName($fullpath);
627
628        if(Files::delFile($fullpath))
629            Return Files::delFile($thumbnail);
630        else
631            Return false;
632    }
633
634    /**
635     * Delete directories recursively.
636     * @param string $relative the relative path to be deleted.
637     * @return boolean true if deleted, false otherwise.
638     */
639    function _delDir($relative)
640    {
641        $fullpath = Files::makePath($this->getImagesDir(),$relative);
642        if($this->countFiles($fullpath) <= 0)
643            return Files::delFolder($fullpath,true); //delete recursively.
644        else
645            Return false;
646    }
647
648    /**
649     * Create new directories.
650     * If in safe_mode, nothing happens.
651     * @return boolean true if created, false otherwise.
652     */
653    function processNewDir()
654    {
655        if($this->config['safe_mode'] == true)
656            Return false;
657
658        if(isset($_GET['newDir']) && isset($_GET['dir']))
659        {
660            $newDir = rawurldecode($_GET['newDir']);
661            $dir = rawurldecode($_GET['dir']);
662            $path = Files::makePath($this->getImagesDir(),$dir);
663            $fullpath = Files::makePath($path, Files::escape($newDir));
664            if(is_dir($fullpath))
665                Return false;
666
667            Return Files::createFolder($fullpath);
668        }
669    }
670}
671
672?>
Note: See TracBrowser for help on using the browser.