Changeset 1302


Ignore:
Timestamp:
04/01/11 14:24:09 (8 years ago)
Author:
gogo
Message:

Updates to http://www.github.com/sleemanj/mootools-filemanager from GerHoblett?

http://www.github.com/GerHoblett/

Changes to said updates by gogo (sleemanj @ github)

Modifications to MootoolsFileManager? to work with those updates, some courtesy of GerHoblett?, some sleemanj

GerHoblett? provided a large diff which accomplished the goal in a quite different way. It has merit, however I have opted for a less-affecting path in so far as Xinha's "way" is concerned, namely, not splitting the config for a single plugin into several calls to backend config passing functions which seemed a little cumbersome.

Instead I take the option of using POST to send backend data around, at the minor expense of an extra round trip when displaying thumbnails (for each one). This could be reduced by checking for thumbnail existence and returning the thumbnail name directly in "onView" rather than the backend request to generate said thumbnail.

Still to do, is to make the preview pane thumbnail also work.


Location:
branches/MootoolsFileManager-Update/plugins/MootoolsFileManager
Files:
43 added
7 deleted
107 edited

Legend:

Unmodified
Added
Removed
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/MootoolsFileManager.ImageManager.js

    r1300 r1302  
    6565  { 
    6666    this.ImageManagerWidget = new FileManager({ 
    67       url:            this.editor.config.MootoolsFileManager.backend+'__function=image-manager&', 
     67      url:            this.editor.config.MootoolsFileManager.backend, 
    6868      assetBasePath:  Xinha.getPluginDir('MootoolsFileManager')+'/mootools-filemanager/Assets', 
    6969      language:       _editor_lang, 
    7070      selectable:     true, 
     71                                               
    7172      upload:         this.phpcfg.allow_images_upload, 
    7273      destroy:        this.phpcfg.allow_images_delete, 
    73       createFolders:  this.phpcfg.allow_images_upload,             
    74       uploadAuthData: this.editor.config.MootoolsFileManager.backend_data, 
    75       onComplete:     function(path, file) { self.ImageManagerReturn(path,file); }, 
     74      createFolders:  this.phpcfg.allow_images_create_dir, 
     75      rename:         this.phpcfg.allow_images_rename, 
     76      move_or_copy:   this.phpcfg.allow_images_rename, 
     77      download:       this.phpcfg.allow_images_download, 
     78                                                                              
     79      propagateData:  Object.merge({'__function': 'image-manager'}, this.editor.config.MootoolsFileManager.backend_data), 
     80      propagateType:  'POST', 
     81                                               
     82      onComplete:     function(path, file, mgr) { self.ImageManagerReturn(path,file); }, 
    7683      onHide:         function() { if(this.swf && this.swf.box) this.swf.box.style.display = 'none'; }, 
    77       onShow:         function() {         
    78         if(this.swf && this.swf.box) this.swf.box.style.display = '';  
    79       }, 
     84      onShow:         function() { if(this.swf && this.swf.box) this.swf.box.style.display = '';     }, 
    8085      onDetails:      function(details)  
    8186                      {                                                  
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/MootoolsFileManager.css

    r1300 r1302  
    22.filemanager-info-area { width:50%; float:left; clear:left; } 
    33.filemanager-preview-area { width:50%; float:left; clear:right; } 
    4 div.filemanager-info-area dt, div.filemanager-preview-area dt { width:80px; margin-left:5px; } 
     4div.filemanager-info-area dt, div.filemanager-preview-area dt { width:8%;/*80px;*/ margin-left:5px; } 
    55div.filemanager-info-area dd, div.filemanager-preview-area dd { width:auto; } 
    66 
    77div.filemanager-preview-area dt { float:left; } 
    88div.filemanager-preview-area h2 { display:none; } 
    9 div.filemanager-preview-area img.preview { margin-left:5px; margin-right:5px; margin-top:0px; float:right; clear:right; max-width:140px; } 
     9div.filemanager-preview-area img.preview { margin-left:5px; margin-right:5px; margin-top:0px; float:right; clear:right; /*max-width:140px;*/ } 
    1010div.filemanager-preview-area dl { float:left; clear:left; width:auto; } 
    1111div.filemanager-preview div.object { margin-left:15%; margin-right:15%; margin-top:0px; float:right; clear:right; } 
     
    5252 
    5353div.filemanager-uploader-area { clear:both; } 
     54div.filemanager-preview p.tech_info { 
     55  clear: both; 
     56} 
     57 
     58div.filemanager-preview p.err_info { 
     59  clear: both; 
     60} 
     61 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/MootoolsFileManager.js

    r1300 r1302  
    4141{ 
    4242  MootoolsFileManager.AssetLoader 
    43     .loadScript('mootools-filemanager/Demos/mootools-core-1.3.1.js', 'MootoolsFileManager') 
    44     .loadScript('mootools-filemanager/Demos/mootools-more-1.3.1.1.js', 'MootoolsFileManager'); 
     43    .loadScript('mootools-filemanager/Demos/mootools-core.js', 'MootoolsFileManager') 
     44    .loadScript('mootools-filemanager/Demos/mootools-more.js', 'MootoolsFileManager'); 
    4545} 
    4646 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/backend.php

    r1300 r1302  
    9898 
    9999  case 'image-manager': 
    100     include('mootools-filemanager/Assets/Connector/FileManager.php'); 
    101  
    102     $browser = new FileManager(array( 
    103       'directory'     => $IMConfig['images_dir'], 
    104       'baseURL'       => $IMConfig['images_url'], 
    105       'thumbnailPath' => $IMConfig['thumbs_dir'], 
     100    // include('mootools-filemanager/Assets/Connector/FileManager.php'); 
     101    include('XinhaFileManager.php'); 
     102     
     103    $browser = new XinhaFileManager(array( 
     104      'images_dir'    => $IMConfig['images_dir'], 
     105      'images_url'    => $IMConfig['images_url'], 
     106       
     107      'thumbs_dir'    => $IMConfig['thumbs_dir'], 
     108      'thumbs_url'    => $IMConfig['thumbs_url'], 
    106109       
    107110      'assetBasePath' => $IMConfig['base_url'] .'/mootools-filemanager/Assets', 
    108111       
    109112      'upload'        => $IMConfig['allow_images_upload'], 
    110       'create'        => $IMConfig['allow_images_upload'], 
    111       'maxUploadSize' => size_to_bytes($IMConfig['max_images_upload_size']), 
     113      'destroy'       => $IMConfig['allow_images_delete'],       
     114      'create'        => $IMConfig['allow_images_create_dir'], 
     115      'move'          => $IMConfig['allow_images_move'], 
     116      'download'      => $IMConfig['allow_images_download'], 
    112117       
     118       
     119      'maxUploadSize' => size_to_bytes($IMConfig['max_images_upload_size']),       
    113120      'suggestedMaxImageDimension' => $IMConfig['suggested_images_image_dimension'], 
    114              
    115       'destroy'       => $IMConfig['allow_images_delete'], 
    116       'filter'        => 'image/', 
    117              
     121                   
     122      'filter'        => 'image/',             
    118123    )); 
    119124 
     
    125130 
    126131    $browser = new FileManager(array( 
    127       'directory'     => $IMConfig['files_dir'], 
    128       'baseURL'       => $IMConfig['files_url'], 
    129       'thumbnailPath' => $IMConfig['thumbs_dir'], 
     132      'files_dir'     => $IMConfig['files_dir'], 
     133      'files_url'     => $IMConfig['files_url'], 
     134       
     135      'thumbs_dir'    => $IMConfig['thumbs_dir'], 
     136      'thumbs_url'    => $IMConfig['thumbs_dir'], 
    130137       
    131138      'assetBasePath' => $IMConfig['base_url'] .'/mootools-filemanager/Assets', 
    132139       
    133140      'upload'        => $IMConfig['allow_files_upload'], 
    134       'create'        => $IMConfig['allow_files_upload'], 
    135       'maxUploadSize' => size_to_bytes($IMConfig['max_files_upload_size']), 
     141      'destroy'       => $IMConfig['allow_files_delete'], 
     142      'create'        => $IMConfig['allow_files_create_dir'], 
     143      'move'          => $IMConfig['allow_files_move'], 
     144      'download'      => $IMConfig['allow_files_download'], 
    136145       
     146       
     147      'maxUploadSize' => size_to_bytes($IMConfig['max_files_upload_size']),       
    137148      'suggestedMaxImageDimension' => $IMConfig['suggested_files_image_dimension'], 
    138149             
    139       'destroy'       => $IMConfig['allow_files_delete'], 
    140150     // 'filter'        => $IMConfig['files_filter'], 
    141151             
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/config.php

    r1300 r1302  
    8989  $IMConfig['images_url'] = FALSE; // No trailing slash 
    9090   
    91   $IMConfig['thumbs_dir'] = NULL;  // Will default to images_url/.thumbs or files_url/.thumbs 
    92  
     91  $IMConfig['thumbs_dir'] = NULL;  // Will default to images_dir/.thumbs or files_dir/.thumbs 
     92  $IMConfig['thumbs_url'] = NULL;  // Will default to images_url/.thumbs or files_url/.thumbs 
    9393  /** 
    9494    == Turning On Uploads == 
     
    127127// ------------------------------------------------------------------------- 
    128128 
     129/** Expanded Permissions */ 
     130 
     131$IMConfig['allow_images_create_dir']  = NULL;  // Defaults to allow_images_upload 
     132$IMConfig['allow_images_move']        = false;  
     133$IMConfig['allow_images_download']    = false;  
     134   
     135$IMConfig['allow_files_create_dir']  = NULL; // Defaults to allow_files_upload 
     136$IMConfig['allow_files_move']        = false;  
     137$IMConfig['allow_files_download']    = false;  
     138   
     139   
    129140/** 
    130141 
     
    197208  $IMConfig['thumbs_dir'] = (isset($IMConfig['images_dir']) ? $IMConfig['images_dir'] : $IMConfig['files_dir']) . '/.thumbs'; 
    198209} 
     210 
     211if(!isset($IMConfig['thumbs_url'])) 
     212{ 
     213  $IMConfig['thumbs_url'] = (isset($IMConfig['images_url']) ? $IMConfig['images_url'] : $IMConfig['files_url']) . '/.thumbs'; 
     214} 
     215 
     216if(!isset($IMConfig['allow_images_create_dir']))  
     217{ 
     218  $IMConfig['allow_images_create_dir'] = $IMConfig['allow_images_upload']; 
     219} 
     220 
     221if(!isset($IMConfig['allow_files_create_dir']))  
     222{ 
     223  $IMConfig['allow_files_create_dir'] = $IMConfig['allow_files_upload']; 
     224} 
     225 
     226 
     227 
    199228?> 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/.git/logs/HEAD

    r1300 r1302  
    110000000000000000000000000000000000000000 9a926918afdcddb1a7555ef685e7acb6523a7192 James Sleeman <boffin@mortimer.(none)> 1301357331 +1300       clone: from git://github.com/sleemanj/mootools-filemanager.git 
     29a926918afdcddb1a7555ef685e7acb6523a7192 4032882436175bde9c335b9ad75f81a208c7c98e James Sleeman <james@gogo.co.nz> 1301648133 +1300     pull : Fast-forward 
     34032882436175bde9c335b9ad75f81a208c7c98e 794418a4483a3bc4dcc6fd4d631dafb328c518c3 James Sleeman <james@gogo.co.nz> 1301666698 +1300     commit: Initial ability to *optionally* POST the propagateData instead of GET'ing it, thumbnails are supported in the list view but not yet in the preview/details, probably need to have onDetails actually generate the thumb(s), this would be fine since if you're calling onDetails one assumes you will be rather shortly hitting up the image anyway. 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/.git/logs/refs/heads/master

    r1300 r1302  
    110000000000000000000000000000000000000000 9a926918afdcddb1a7555ef685e7acb6523a7192 James Sleeman <boffin@mortimer.(none)> 1301357331 +1300       clone: from git://github.com/sleemanj/mootools-filemanager.git 
     29a926918afdcddb1a7555ef685e7acb6523a7192 4032882436175bde9c335b9ad75f81a208c7c98e James Sleeman <james@gogo.co.nz> 1301648133 +1300     pull : Fast-forward 
     34032882436175bde9c335b9ad75f81a208c7c98e 794418a4483a3bc4dcc6fd4d631dafb328c518c3 James Sleeman <james@gogo.co.nz> 1301666698 +1300     commit: Initial ability to *optionally* POST the propagateData instead of GET'ing it, thumbnails are supported in the list view but not yet in the preview/details, probably need to have onDetails actually generate the thumb(s), this would be fine since if you're calling onDetails one assumes you will be rather shortly hitting up the image anyway. 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/.git/refs/heads/master

    r1300 r1302  
    1 9a926918afdcddb1a7555ef685e7acb6523a7192 
     1794418a4483a3bc4dcc6fd4d631dafb328c518c3 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/.gitignore

    r1300 r1302  
    55!Assets/Thumbs/index.html 
    66Demos/TinyMCE 
     7Demos/tiny_mce_*.js 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/Assets/Connector/Assets/getid3/getid3.lib.php

    r1300 r1302  
    900900 
    901901                        ob_start(); 
    902                         if ($converted_string = iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { 
     902                        if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { 
    903903                                ob_end_clean(); 
    904904                                switch ($out_charset) { 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/Assets/Connector/Assets/getid3/getid3.php

    r1300 r1302  
    11881188                                foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) { 
    11891189                                        foreach ($valuearray as $key => $value) { 
    1190                                                 $value = (is_string($value) ? trim($value) : $value); 
     1190                                                // $value = (is_string($value) ? trim($value) : $value);     // [i_a] http://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1136 
    11911191                                                if (!empty($value) > 0) { 
    11921192                                                        $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed! 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/Assets/Connector/Assets/getid3/module.archive.rar.php

    r1300 r1302  
    1818{ 
    1919 
    20         var $option_use_rar_extension = false; 
     20        var $option_use_rar_extension = true; 
    2121 
    2222        function getid3_rar(&$fd, &$ThisFileInfo) { 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/Assets/Connector/Assets/getid3/module.tag.xmp.php

    r1300 r1302  
    152152 
    153153                                // Read the segment data with length indicated by the previously read size 
    154                                 $segdata = fread($filehnd, $decodedsize['size'] - 2); 
     154                                if ($decodedsize['size'] - 2 > 0)       // [i_a] bugfix 
     155                                { 
     156                                        $segdata = fread($filehnd, $decodedsize['size'] - 2); 
     157                                } 
     158                                else 
     159                                { 
     160                                        $segdata = null; 
     161                                } 
    155162 
    156163                                // Store the segment information in the output array 
     
    334341                                                { 
    335342                                                        // If Lang Alt (language alternatives) then ensure we take the default language 
    336                                                         if ($xml_elem['attributes']['xml:lang'] != 'x-default') 
     343                                                        if (empty($xml_elem['attributes']['xml:lang']) || $xml_elem['attributes']['xml:lang'] != 'x-default')  // [i_a] crash fix 
    337344                                                        { 
    338345                                                                break; 
    339346                                                        } 
    340347                                                } 
    341                                                 if ($current_property != '') 
     348                                                if ($current_property != '' && isset($xml_elem['value']))  // [i_a] 
    342349                                                { 
    343350                                                        $xmp_array[$current_property][$container_index] = $xml_elem['value']; 
  • branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/Assets/Connector/FileManager.php

    r1300 r1302  
    33 * Script: FileManager.php 
    44 *   MooTools FileManager - Backend for the FileManager Script 
    5  *  
     5 * 
    66 * Authors: 
    77 *  - Christoph Pojer (http://cpojer.net) (author) 
    88 *  - James Ehly (http://www.devtrench.com) 
    99 *  - Fabian Vogelsteller (http://frozeman.de) 
    10  *  
     10 *  - Ger Hobbelt (http://hebbut.net) 
     11 *  - James Sleeman (http://code.gogo.co.nz) 
     12 * 
    1113 * License: 
    1214 *   MIT-style license. 
    13  *  
     15 * 
    1416 * Copyright: 
    15  *   Copyright (c) 2009 [Christoph Pojer](http://cpojer.net) 
    16  *  
     17 *   Copyright (c) 2009-2011 [Christoph Pojer](http://cpojer.net) 
     18 *   Backend: FileManager & FMgr4Alias Copyright (c) 2011 [Ger Hobbelt](http://hobbelt.com) 
     19 * 
    1720 * Dependencies: 
    18  *   - Upload.php 
     21 *   - Tooling.php 
    1922 *   - Image.class.php 
    2023 *   - getId3 Library 
    21  *  
     24 * 
    2225 * Options: 
    23  *   - directory: (string) The base directory to be used for the FileManager 
    24  *   - assetBasePath: (string, optional) The path to all images and swf files used by the filemanager 
    25  *   - thumbnailPath: (string) The path where the thumbnails of the pictures will be saved 
    26  *   - mimeTypesPath: (string, optional) The path to the MimeTypes.ini file. 
     26 *   - directory: (string) The URI base directory to be used for the FileManager ('URI path' i.e. an absolute path here would be rooted at DocumentRoot: '/' == DocumentRoot) 
     27 *   - assetBasePath: (string, optional) The URI path to all images and swf files used by the filemanager 
     28 *   - thumbnailPath: (string) The URI path where the thumbnails of the pictures will be saved 
     29 *   - mimeTypesPath: (string, optional) The filesystem path to the MimeTypes.ini file. May exist in a place outside the DocumentRoot tree. 
    2730 *   - dateFormat: (string, defaults to *j M Y - H:i*) The format in which dates should be displayed 
    2831 *   - maxUploadSize: (integer, defaults to *20280000* bytes) The maximum file size for upload in bytes 
    29  *   - maxImageSize: (integer, default is 1024) The maximum number of pixels in both height and width an image can have, if the user enables "resize on upload" 
    30  *   - upload: (boolean, defaults to *true*) allow uploads, this is also set in the FileManager.js (this here is only for security protection when uploads should be deactivated) 
    31  *   - destroy: (boolean, defaults to *true*) allow files to get deleted, this is also set in the FileManager.js (this here is only for security protection when file/directory delete operations should be deactivated) 
    32  *   - create: (boolean, defaults to *true*) allow creating new subdirectories, this is also set in the FileManager.js (this here is only for security protection when dir creates should be deactivated) 
    33  *   - move: (boolean, defaults to *true*) allow file and directory move/rename and copy, this is also set in the FileManager.js (this here is only for security protection when rename/move/copy should be deactivated) 
    34  *   - download: (boolean, defaults to *true*) allow downloads, this is also set in the FileManager.js (this here is only for security protection when downloads should be deactivated) 
     32 *   - maxImageDimension: (array, defaults to *array('width' => 1024, 'height' => 768)*) The maximum number of pixels in height and width an image can have, if the user enables "resize on upload". 
     33 *   - upload: (boolean, defaults to *false*) allow uploads, this is also set in the FileManager.js (this here is only for security protection when uploads should be deactivated) 
     34 *   - destroy: (boolean, defaults to *false*) allow files to get deleted, this is also set in the FileManager.js (this here is only for security protection when file/directory delete operations should be deactivated) 
     35 *   - create: (boolean, defaults to *false*) allow creating new subdirectories, this is also set in the FileManager.js (this here is only for security protection when dir creates should be deactivated) 
     36 *   - move: (boolean, defaults to *false*) allow file and directory move/rename and copy, this is also set in the FileManager.js (this here is only for security protection when rename/move/copy should be deactivated) 
     37 *   - download: (boolean, defaults to *false*) allow downloads, this is also set in the FileManager.js (this here is only for security protection when downloads should be deactivated) 
    3538 *   - allowExtChange: (boolean, defaults to *false*) allow the file extension to be changed when performing a rename operation. 
    3639 *   - safe: (boolean, defaults to *true*) If true, disallows 'exe', 'dll', 'php', 'php3', 'php4', 'php5', 'phps' and saves them as 'txt' instead. 
    3740 *   - chmod: (integer, default is 0777) the permissions set to the uploaded files and created thumbnails (must have a leading "0", e.g. 0777) 
     41 *   - filter: (string, defaults to *null*) If not empty, this is a list of allowed mimetypes (overruled by the GET request 'filter' parameter: single requests can thus overrule the common setup in the constructor for this option) 
     42 *   - ViewIsAuthorized_cb (function/reference, default is *null*) authentication + authorization callback which can be used to determine whether the given directory may be viewed. 
     43 *     The parameter $action = 'view'. 
     44 *   - DetailIsAuthorized_cb (function/reference, default is *null*) authentication + authorization callback which can be used to determine whether the given file may be inspected (and the details listed). 
     45 *     The parameter $action = 'detail'. 
     46 *   - ThumbnailIsAuthorized_cb (function/reference, default is *null*) authentication + authorization callback which can be used to determine whether a thumbnail of the given file may be shown. 
     47 *     The parameter $action = 'thumbnail'. 
    3848 *   - UploadIsAuthorized_cb (function/reference, default is *null*) authentication + authorization callback which can be used to determine whether the given file may be uploaded. 
    3949 *     The parameter $action = 'upload'. 
     
    4757 *     Note that currently support for copying subdirectories is missing. 
    4858 *     The parameter $action = 'move'. 
    49  *  
     59 *   - URIpropagateData (array, default is *null*) the data elements which will be passed along as part of the generated request URIs, i.e. the thumbnail request URIs. Use this to pass custom data elements to the 
     60 *     handler which delivers the thumbnails to the front-end. 
     61 * 
     62 * Obsoleted options: 
     63 *   - maxImageSize: (integer, default is 1024) The maximum number of pixels in both height and width an image can have, if the user enables "resize on upload". (This option is obsoleted by the 'suggestedMaxImageDimension' option.) 
     64 * 
     65 * 
     66 * About the action permissions (upload|destroy|create|move|download): 
     67 * 
     68 *     All the option "permissions" are set to FALSE by default. Developers should always SPECIFICALLY enable a permission to have that permission, for two reasons: 
     69 * 
     70 *     1. Developers forget to disable permissions, they don't forget to enable them (because things don't work!) 
     71 * 
     72 *     2. Having open permissions by default leaves potential for security vulnerabilities where those open permissions are exploited. 
     73 * 
     74 * 
    5075 * For all authorization hooks (callback functions) the following applies: 
    51  *  
     76 * 
    5277 *     The callback should return TRUE for yes (permission granted), FALSE for no (permission denied). 
    5378 *     Parameters sent to the callback are: 
     
    5580 *     where $fileinfo is an array containing info about the file being uploaded, $action is a (string) identifying the current operation, $this is a reference to this FileManager instance. 
    5681 *     $action was included as a redundant parameter to each callback as a simple means to allow users to hook a single callback function to all the authorization hooks, without the need to create a wrapper function for each. 
    57  *  
    58  *     For more info about the hook parameter $fileinfo contents and a basic implementation, see Demos/manager.php and Demos/selectImage.php 
    59  *  
     82 * 
     83 *     For more info about the hook parameter $fileinfo contents and a basic implementation, see further below (section 'Hooks: Detailed Interface Specification') and the examples in 
     84 *     Demos/FM-common.php, Demos/manager.php and Demos/selectImage.php 
     85 * 
     86 * 
    6087 * Notes on relative paths and safety / security: 
    61  *  
     88 * 
    6289 *   If any option is specifying a relative path, e.g. '../Assets' or 'Media/Stuff/', this is assumed to be relative to the request URI path, 
    6390 *   i.e. dirname($_SERVER['SCRIPT_NAME']). 
    64  *  
     91 * 
    6592 *   Requests may post/submit relative paths as arguments to their FileManager events/actions in $_GET/$_POST, and those relative paths will be 
    6693 *   regarded as relative to the request URI handling script path, i.e. dirname($_SERVER['SCRIPT_NAME']) to make the most 
    6794 *   sense from bother server and client coding perspective. 
    68  *  
    69  *  
     95 * 
     96 * 
    7097 *   We also assume that any of the paths may be specified from the outside, so each path is processed and filtered to prevent malicious intent 
    7198 *   from succeeding. (An example of such would be an attacker posting his own 'destroy' event request requesting the destruction of 
    7299 *   '../../../../../../../../../etc/passwd' for example. In more complex rigs, the attack may be assisted through attacks at these options' paths, 
    73100 *   so these are subjected to the same scrutiny in here.) 
    74  *  
     101 * 
    75102 *   All paths, absolute or relative, as passed to the event handlers (see the onXXX methods of this class) are ENFORCED TO ABIDE THE RULE 
    76  *   'every path resides within the BASEDIR rooted tree' without exception. 
    77  *   When paths apparently don't, they are forcibly coerced into adherence to this rule. Because we can do without exceptions to important rules. ;-) 
    78  *  
     103 *   'every path resides within the options['directory'] a.k.a. BASEDIR rooted tree' without exception. 
     104 *   Because we can do without exceptions to important rules. ;-) 
     105 * 
     106 *   When paths apparently don't, they are coerced into adherence to this rule; when this fails, an exception is thrown internally and an error 
     107 *   will be reported and the action temrinated. 
     108 * 
     109 *  'LEGAL URL paths': 
     110 * 
     111 *   Paths which adhere to the aforementioned rule are so-called LEGAL URL paths; their 'root' equals BASEDIR. 
     112 * 
    79113 *   BASEDIR equals the path pointed at by the options['directory'] setting. It is therefore imperative that you ensure this value is 
    80114 *   correctly set up; worst case, this setting will equal DocumentRoot. 
    81115 *   In other words: you'll never be able to reach any file or directory outside this site's DocumentRoot directory tree, ever. 
    82  *  
    83  *  
     116 * 
     117 * 
     118 *  Path transformations: 
     119 * 
     120 *   To allow arbitrary directory/path mapping algorithms to be applied (e.g. when implementing Alias support such as available in the 
     121 *   derived class FileManagerWithAliasSupport), all paths are, on every change/edit, transformed from their LEGAL URL representation to 
     122 *   their 'absolute URI path' (which is suitable to be used in links and references in HTML output) and 'absolute physical filesystem path' 
     123 *   equivalents. 
     124 *   By enforcing such a unidirectional transformation we implicitly support non-reversible and hard-to-reverse path aliasing mechanisms, 
     125 *   e.g. complex regex+context based path manipulations in the server. 
     126 * 
     127 * 
    84128 *   When you need your paths to be restricted to the bounds of the options['directory'] tree (which is a subtree of the DocumentRoot based 
    85  *   tree), you may wish to use the CheckFile(), getPath() and getDir() methods instead of getRealPath() and getRealDir(), as the latter 
    86  *   restrict targets to within the DocumentRoot tree only. 
    87  *  
    88  *   getPath() and getRealPath() both deliver absolute paths relative to DocumentRoot, hence suitable for use in URIs and feeding to client side 
    89  *   scripts, while getRealDir() and getDir() both return absolute paths in the server filesystem perspective, i.e. the latter are suitable for 
    90  *   server side script based file operation functions. 
     129 *   tree), you may wish to use the 'legal' class of path transformation member functions: 
     130 * 
     131 *   - legal2abs_url_path() 
     132 *   - rel2abs_legal_url_path() 
     133 *   - legal_url_path2file_path() 
     134 * 
     135 *   When you have a 'absolute URI path' or a path relative in URI space (implicitly relative to dirname($_SERVER['SCRIPT_NAME']) ), you can 
     136 *   transform such a path to either a guaranteed-absolute URI space path or a filesystem path: 
     137 * 
     138 *   - rel2abs_url_path() 
     139 *   - url_path2file_path() 
     140 * 
     141 *   Any other path transformations are ILLEGAL and DANGEROUS. The only other possibly legal transformation is from absolute URI path to 
     142 *   BASEDIR-based LEGAL URL path, as the URI path space is assumed to be linear and contiguous. However, this operation is HIGHLY discouraged 
     143 *   as it is a very strong indicator of other faulty logic, so we do NOT offer a method for this. 
     144 * 
     145 * 
     146 * Hooks: Detailed Interface Specification: 
     147 * 
     148 *   All 'authorization' callback hooks share a common interface specification (function parameter set). This is by design, so one callback 
     149 *   function can be used to process any and all of these events: 
     150 * 
     151 *   Function prototype: 
     152 * 
     153 *       function CallbackFunction($mgr, $action, &$info) 
     154 * 
     155 *   where 
     156 * 
     157 *       $msg:      (object) reference to the current FileManager class instance. Can be used to invoke public FileManager methods inside 
     158 *                  the callback. 
     159 * 
     160 *       $action:   (string) identifies the event being processed. Can be one of these: 
     161 * 
     162 *                  'create'          create new directory 
     163 *                  'move'            move or copy a file or directory 
     164 *                  'destroy'         delete a file or directory 
     165 *                  'upload'          upload a single file (when performing a bulk upload, each file will be uploaded individually) 
     166 *                  'download'        download a file 
     167 *                  'view'            show a directory listing (in either 'list' or 'thumb' mode) 
     168 *                  'detail'          show detailed information about the file and, whn possible, provide a link to a (largish) thumbnail 
     169 *                  'thumbnail'       send the thumbnail to the client (done this way to allow JiT thumbnail creation) 
     170 * 
     171 *       $info      (array) carries all the details. Some of which can even be manipulated if your callbac is more than just an 
     172 *                  authentication / authorization checker. ;-) 
     173 *                  For more detail, see the next major section. 
     174 * 
     175 *   The callback should return a boolean, where TRUE means the session/client is authorized to execute the action, while FALSE 
     176 *   will cause the backend to report an authentication error and abort the action. 
     177 * 
     178 *  Exceptions throwing from the callback: 
     179 * 
     180 *   Note that you may choose to throw exceptions from inside the callback; those will be caught and transformed to proper error reports. 
     181 * 
     182 *   You may either throw any exceptions based on either the FileManagerException or Exception classes. When you format the exception 
     183 *   message as "XYZ:data", where 'XYZ' is a alphanumeric-only word, this will be transformed to a i18n-support string, where 
     184 *   'backend.XYZ' must map to a translation string (e.g. 'backend.nofile', see also the Language/Language.XX.js files) and the optional 
     185 *   'data' tail will be appended to the translated message. 
     186 * 
     187 * 
     188 * $info: the details: 
     189 * 
     190 *   Here is the list of $info members per $action event code: 
     191 * 
     192 *   'upload': 
     193 * 
     194 *           $info[] contains: 
     195 * 
     196 *               'legal_url'             (string) LEGAL URI path to the directory where the file is being uploaded. You may invoke 
     197 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     198 *                                       to obtain the physical filesystem path (also available in the 'dir' $info entry, by the way!), or 
     199 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     200 *                                       to obtain the absolute URI path for the given directory. 
     201 * 
     202 *               'dir'                   (string) physical filesystem path to the directory where the file is being uploaded. 
     203 * 
     204 *               'raw_filename'          (string) the raw, unprocessed filename of the file being being uploaded, as specified by the client. 
     205 * 
     206 *                                       WARNING: 'raw_filename' may contain anything illegal, such as directory paths instead of just a filename, 
     207 *                                                filesystem-illegal characters and what-not. Use 'name'+'extension' instead if you want to know 
     208 *                                                where the upload will end up. 
     209 * 
     210 *               'name'                  (string) the filename, sans extension, of the file being uploaded; this filename is ensured 
     211 *                                       to be both filesystem-legal, unique and not yet existing in the given directory. 
     212 * 
     213 *               'extension'             (string) the filename extension of the file being uploaded; this extension is ensured 
     214 *                                       to be filesystem-legal. 
     215 * 
     216 *                                       Note that the file name extension has already been cleaned, including 'safe' mode processing, 
     217 *                                       i.e. any uploaded binary executable will have been assigned the extension '.txt' already, when 
     218 *                                       FileManager's options['safe'] is enabled. 
     219 * 
     220 *               'tmp_filepath'          (string) filesystem path pointing at the temporary storage location of the uploaded file: you can 
     221 *                                       access the file data available here to optionally validate the uploaded content. 
     222 * 
     223 *               'mime'                  (string) the mime type as sniffed from the file 
     224 * 
     225 *               'mime_filter'           (optional, string) mime filter as specified by the client: a comma-separated string containing 
     226 *                                       full or partial mime types, where a 'partial' mime types is the part of a mime type before 
     227 *                                       and including the slash, e.g. 'image/' 
     228 * 
     229 *               'mime_filters'          (optional, array of strings) the set of allowed mime types, derived from the 'mime_filter' setting. 
     230 * 
     231 *               'size'                  (integer) number of bytes of the uploaded file 
     232 * 
     233 *               'maxsize'               (integer) the configured maximum number of bytes for any single upload 
     234 * 
     235 *               'overwrite'             (boolean) FALSE: the uploaded file will not overwrite any existing file, it will fail instead. 
     236 * 
     237 *                                       Set to TRUE (and adjust the 'name' and 'extension' entries as you desire) when you wish to overwrite 
     238 *                                       an existing file. 
     239 * 
     240 *               'chmod'                 (integer) UNIX access rights (default: 0666) for the file-to-be-created (RW for user,group,world). 
     241 * 
     242 *                                       Note that the eXecutable bits have already been stripped before the callback was invoked. 
     243 * 
     244 * 
     245 *         Note that this request originates from a Macromedia Flash client: hence you'll need to use the 
     246 *         $_POST[session_name()] value to manually set the PHP session_id() before you start your your session 
     247 *         again. 
     248 * 
     249 *         The frontend-specified options.propagateData items will be available as $_GET[] items. 
     250 * 
     251 *         The frontend-specified options.uploadAuthData items will be available as $_POST[] items. 
     252 * 
     253 * 
     254 *  'download': 
     255 * 
     256 *           $info[] contains: 
     257 * 
     258 *               'legal_url'             (string) LEGAL URI path to the file to be downloaded. You may invoke 
     259 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     260 *                                       to obtain the physical filesystem path (also available in the 'file' $info entry, by the way!), or 
     261 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     262 *                                       to obtain the absolute URI path for the given file. 
     263 * 
     264 *               'file'                  (string) physical filesystem path to the file being downloaded. 
     265 * 
     266 *               'mime'                  (string) the mime type as sniffed from the file 
     267 * 
     268 *               'mime_filter'           (optional, string) mime filter as specified by the client: a comma-separated string containing 
     269 *                                       full or partial mime types, where a 'partial' mime types is the part of a mime type before 
     270 *                                       and including the slash, e.g. 'image/' 
     271 * 
     272 *               'mime_filters'          (optional, array of strings) the set of allowed mime types, derived from the 'mime_filter' setting. 
     273 * 
     274 *         The frontend-specified options.propagateData items will be available as $_GET[] items. 
     275 * 
     276 * 
     277 *  'create': // create directory 
     278 * 
     279 *           $info[] contains: 
     280 * 
     281 *               'legal_url'             (string) LEGAL URI path to the parent directory of the directory being created. You may invoke 
     282 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     283 *                                       to obtain the physical filesystem path (also available in the 'dir' $info entry, by the way!), or 
     284 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     285 *                                       to obtain the absolute URI path for this parent directory. 
     286 * 
     287 *               'dir'                   (string) physical filesystem path to the parent directory of the directory being created. 
     288 * 
     289 *               'raw_name'              (string) the name of the directory to be created, as specified by the client (unfiltered!) 
     290 * 
     291 *               'uniq_name'             (string) the name of the directory to be created, filtered and ensured to be both unique and 
     292 *                                       not-yet-existing in the filesystem. 
     293 * 
     294 *               'newdir'                (string) the filesystem absolute path to the directory to be created; identical to: 
     295 *                                           $newdir = $mgr->legal_url_path2file_path($legal_url . $uniq_name); 
     296 *                                       Note the above: all paths are transformed from URI space to physical disk every time a change occurs; 
     297 *                                       this allows us to map even not-existing 'directories' to possibly disparate filesystem locations. 
     298 * 
     299 *               'chmod'                 (integer) UNIX access rights (default: 0777) for the directory-to-be-created (RWX for user,group,world) 
     300 * 
     301 *         The frontend-specified options.propagateData items will be available as $_GET[] items. 
     302 * 
     303 * 
     304 *  'destroy': 
     305 * 
     306 *           $info[] contains: 
     307 * 
     308 *               'legal_url'             (string) LEGAL URI path to the file/directory to be deleted. You may invoke 
     309 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     310 *                                       to obtain the physical filesystem path (also available in the 'file' $info entry, by the way!), or 
     311 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     312 *                                       to obtain the absolute URI path for the given file/directory. 
     313 * 
     314 *               'file'                  (string) physical filesystem path to the file/directory being deleted. 
     315 * 
     316 *               'mime'                  (string) the mime type as sniffed from the file / directory (directories are mime type: 'text/directory') 
     317 * 
     318 *               'mime_filter'           (optional, string) mime filter as specified by the client: a comma-separated string containing 
     319 *                                       full or partial mime types, where a 'partial' mime types is the part of a mime type before 
     320 *                                       and including the slash, e.g. 'image/' 
     321 * 
     322 *               'mime_filters'          (optional, array of strings) the set of allowed mime types, derived from the 'mime_filter' setting. 
     323 * 
     324 *                                       Note that the 'mime_filters', if any, are applied to the 'delete' operation in a special way: only 
     325 *                                       files matching one of the mime types in this list will be deleted; anything else will remain intact. 
     326 *                                       This can be used to selectively clean a directory tree. 
     327 * 
     328 *                                       The design idea behind this approach is that you are only allowed what you can see ('view'), so 
     329 *                                       all 'view' restrictions should equally to the 'delete' operation. 
     330 * 
     331 *         The frontend-specified options.propagateData items will be available as $_GET[] items. 
     332 * 
     333 * 
     334 *  'move':  // move or copy! 
     335 * 
     336 *           $info[] contains: 
     337 * 
     338 *               'legal_url'             (string) LEGAL URI path to the source parent directory of the file/directory being moved/copied. You may invoke 
     339 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     340 *                                       to obtain the physical filesystem path (also available in the 'dir' $info entry, by the way!), or 
     341 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     342 *                                       to obtain the absolute URI path for the given directory. 
     343 * 
     344 *               'dir'                   (string) physical filesystem path to the source parent directory of the file/directory being moved/copied. 
     345 * 
     346 *               'path'                  (string) physical filesystem path to the file/directory being moved/copied itself; this is the full source path. 
     347 * 
     348 *               'name'                  (string) the name itself of the file/directory being moved/copied; this is the source name. 
     349 * 
     350 *               'legal_newurl'          (string) LEGAL URI path to the target parent directory of the file/directory being moved/copied. You may invoke 
     351 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     352 *                                       to obtain the physical filesystem path (also available in the 'dir' $info entry, by the way!), or 
     353 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     354 *                                       to obtain the absolute URI path for the given directory. 
     355 * 
     356 *               'newdir'                (string) physical filesystem path to the target parent directory of the file/directory being moved/copied; 
     357 *                                       this is the full path of the directory where the file/directory will be moved/copied to. (filesystem absolute) 
     358 * 
     359 *               'newpath'               (string) physical filesystem path to the target file/directory being moved/copied itself; this is the full destination path, 
     360 *                                       i.e. the full path of where the file/directory should be renamed/moved to. (filesystem absolute) 
     361 * 
     362 *               'newname'               (string) the target name itself of the file/directory being moved/copied; this is the destination name. 
     363 * 
     364 *                                       This filename is ensured to be both filesystem-legal, unique and not yet existing in the given target directory. 
     365 * 
     366 *               'rename'                (boolean) TRUE when a file/directory RENAME operation is requested (name change, staying within the same 
     367 *                                       parent directory). FALSE otherwise. 
     368 * 
     369 *               'is_dir'                (boolean) TRUE when the subject is a directory itself, FALSE when it is a regular file. 
     370 * 
     371 *               'function'              (string) PHP call which will perform the operation. ('rename' or 'copy') 
     372 * 
     373 *         The frontend-specified options.propagateData items will be available as $_GET[] items. 
     374 * 
     375 * 
     376 *  'view': 
     377 * 
     378 *           $info[] contains: 
     379 * 
     380 *               'legal_url'             (string) LEGAL URI path to the directory being viewed/scanned. You may invoke 
     381 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     382 *                                       to obtain the physical filesystem path (also available in the 'dir' $info entry, by the way!), or 
     383 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     384 *                                       to obtain the absolute URI path for the scanned directory. 
     385 * 
     386 *               'dir'                   (string) physical filesystem path to the directory being viewed/scanned. 
     387 * 
     388 *               'files'                 (array of strings) array of files and directories (including '..' entry at the top when this is a 
     389 *                                       subdirectory of the FM-managed tree): only names, not full paths. 
     390 * 
     391 *               'mime_filter'           (optional, string) mime filter as specified by the client: a comma-separated string containing 
     392 *                                       full or partial mime types, where a 'partial' mime types is the part of a mime type before 
     393 *                                       and including the slash, e.g. 'image/' 
     394 * 
     395 *               'mime_filters'          (optional, array of strings) the set of allowed mime types, derived from the 'mime_filter' setting. 
     396 * 
     397 *               'guess_mime'            (boolean) TRUE when the mime type for each file in this directory will be determined using filename 
     398 *                                       extension sniffing only; FALSE means the mime type will be determined using content sniffing, which 
     399 *                                       is slower. 
     400 * 
     401 *               'list_type'             (string) the type of view requested: 'list' or 'thumb'. 
     402 * 
     403 *               'file_preselect'        (optional, string) filename of a file in this directory which should be located and selected. 
     404 *                                       When found, the backend will provide an index number pointing at the corresponding JSON files[] 
     405 *                                       entry to assist the front-end in jumping to that particular item in the view. 
     406 * 
     407 *               'preliminary_json'      (array) the JSON data collected so far; when ['status']==1, then we're performing a regular view 
     408 *                                       operation (possibly as the second half of a copy/move/delete operation), when the ['status']==0, 
     409 *                                       we are performing a view operation as the second part of another otherwise failed action, e.g. a 
     410 *                                       failed 'create directory'. 
     411 * 
     412 *         The frontend-specified options.propagateData items will be available as $_GET[] items. 
     413 * 
     414 * 
     415 *  'detail': 
     416 * 
     417 *           $info[] contains: 
     418 * 
     419 *               'legal_url'             (string) LEGAL URI path to the file/directory being inspected. You may invoke 
     420 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     421 *                                       to obtain the physical filesystem path (also available in the 'file' $info entry, by the way!), or 
     422 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     423 *                                       to obtain the absolute URI path for the given file. 
     424 * 
     425 *               'file'                  (string) physical filesystem path to the file being inspected. 
     426 * 
     427 *               'filename'              (string) the filename of the file being inspected. (Identical to 'basename($legal_url)') 
     428 * 
     429 *               'mime'                  (string) the mime type as sniffed from the file 
     430 * 
     431 *               'mime_filter'           (optional, string) mime filter as specified by the client: a comma-separated string containing 
     432 *                                       full or partial mime types, where a 'partial' mime types is the part of a mime type before 
     433 *                                       and including the slash, e.g. 'image/' 
     434 * 
     435 *               'mime_filters'          (optional, array of strings) the set of allowed mime types, derived from the 'mime_filter' setting. 
     436 * 
     437 *         The frontend-specified options.propagateData items will be available as $_GET[] items. 
     438 * 
     439 * 
     440 *  'thumbnail': 
     441 * 
     442 *           $info[] contains: 
     443 * 
     444 *               'legal_url'             (string) LEGAL URI path to the file/directory being thumbnailed. You may invoke 
     445 *                                           $dir = $mgr->legal_url_path2file_path($legal_url); 
     446 *                                       to obtain the physical filesystem path (also available in the 'file' $info entry, by the way!), or 
     447 *                                           $url = $mgr->legal2abs_url_path($legal_url); 
     448 *                                       to obtain the absolute URI path for the given file. 
     449 * 
     450 *               'file'                  (string) physical filesystem path to the file being inspected. 
     451 * 
     452 *               'filename'              (string) the filename of the file being inspected. (Identical to 'basename($legal_url)') 
     453 * 
     454 *               'mime'                  (string) the mime type as sniffed from the file 
     455 * 
     456 *               'mime_filter'           (optional, string) mime filter as specified by the client: a comma-separated string containing 
     457 *                                       full or partial mime types, where a 'partial' mime types is the part of a mime type before 
     458 *                                       and including the slash, e.g. 'image/' 
     459 * 
     460 *               'mime_filters'          (optional, array of strings) the set of allowed mime types, derived from the 'mime_filter' setting. 
     461 *The parameter $action = 'thumbnail'. 
     462 *   - UploadIsAuthorized_cb (function/reference, default is *null*) authentication + authorization callback which can be used to determine whether the given file may be uploaded. 
     463 *               'requested_size'        (integer) the size (maximum width and height) in pixels of the thumbnail to be produced. 
     464 * 
     465 *         The frontend-specified options.propagateData items will be available as $_GET[] items. 
     466 * 
     467 * 
     468 * 
     469 * Developer Notes: 
     470 * 
     471 * - member functions which have a commented out 'static' keyword have it removed by design: it makes for easier overloading through 
     472 *   inheritance that way and meanwhile there's no pressing need to have those (public) member functions acccessible from the outside world 
     473 *   without having an instance of the FileManager class itself round at the same time. 
    91474 */ 
    92475 
     
    94477if (version_compare(PHP_VERSION, '5.2.0') < 0) 
    95478{ 
    96     // die horribly: server does not match our requirements! 
    97     header('HTTP/1.0 500 FileManager requires PHP 5.2.0 or later', true, 500); // Internal server error 
    98     throw Exception('FileManager requires PHP 5.2.0 or later');   // this exception will most probably not be caught; that's our intent! 
     479        // die horribly: server does not match our requirements! 
     480        header('HTTP/1.0 500 FileManager requires PHP 5.2.0 or later', true, 500); // Internal server error 
     481        throw Exception('FileManager requires PHP 5.2.0 or later');   // this exception will most probably not be caught; that's our intent! 
    99482} 
    100483 
    101484if (function_exists('UploadIsAuthenticated')) 
    102485{ 
    103     // die horribly: user has not upgraded his callback hook(s)! 
    104     header('HTTP/1.0 500 FileManager callback has not been upgraded!', true, 500); // Internal server error 
    105     throw Exception('FileManager callback has not been upgraded!');   // this exception will most probably not be caught; that's our intent! 
     486        // die horribly: user has not upgraded his callback hook(s)! 
     487        header('HTTP/1.0 500 FileManager callback has not been upgraded!', true, 500); // Internal server error 
     488        throw Exception('FileManager callback has not been upgraded!');   // this exception will most probably not be caught; that's our intent! 
    106489} 
    107490 
    108491//------------------------------------------------------------------------------------------------------------- 
    109492 
    110  
    111 if (!defined('MTFM_PATH')) 
    112 { 
    113     $base = str_replace('\\','/',dirname(__FILE__)); 
    114     define('MTFM_PATH', $base); 
    115 } 
    116  
    117 require_once(MTFM_PATH . '/Upload.php'); 
    118 require_once(MTFM_PATH . '/Image.class.php'); 
     493if (!defined('DEVELOPMENT')) define('DEVELOPMENT', 0);   // make sure this #define is always known to us 
     494 
     495 
     496 
     497require_once(str_replace('\\', '/', dirname(__FILE__)) . '/Tooling.php'); 
     498require_once(str_replace('\\', '/', dirname(__FILE__)) . '/Image.class.php'); 
     499require_once(str_replace('\\', '/', dirname(__FILE__)) . '/Assets/getid3/getid3.php'); 
     500 
     501 
     502 
     503// the jpeg quality for the largest thumbnails (smaller ones are automatically done at increasingly higher quality) 
     504define('MTFM_THUMBNAIL_JPEG_QUALITY', 75); 
     505 
     506// the number of directory levels in the thumbnail cache; set to 2 when you expect to handle huge image collections. 
     507// 
     508// Note that each directory level distributes the files evenly across 256 directories; hence, you may set this 
     509// level count to 2 when you expect to handle more than 32K images in total -- as each image will have two thumbnails: 
     510// a 48px small one and a 250px large one. 
     511define('MTFM_NUMBER_OF_DIRLEVELS_FOR_CACHE', 1); 
     512 
    119513 
    120514class FileManager 
    121515{ 
    122   protected $path = null; 
    123   protected $basedir = null;                    // absolute path equivalent, filesystem-wise, for options['directory'] 
    124   protected $options; 
    125   protected $post; 
    126   protected $get; 
    127  
    128   public function __construct($options) 
    129   { 
    130     $this->options = array_merge(array( 
    131       /* 
    132        * Note that all default paths as listed below are transformed to DocumentRoot-based paths 
    133        * through the getRealPath() invocations further below: 
    134        */ 
    135       'directory' => MTFM_PATH . '/Files/', 
    136       'assetBasePath' => MTFM_PATH . '/../../Assets/', 
    137       'thumbnailPath' => MTFM_PATH . '/../../Assets/Thumbs/',  // written like this so we're completely clear on where the default thumbnails directory will be 
    138       'mimeTypesPath' => MTFM_PATH . '/MimeTypes.ini', 
    139       'dateFormat' => 'j M Y - H:i', 
    140       'maxUploadSize' => 2600 * 2600 * 3, 
    141       'maxImageSize' => 999999, // Xinha: We have separate X/Y dimension in 'suggestedMaxImageDimension', don't want this 
    142       'upload' => false, 
    143       'destroy' => false, 
    144       'create' => false, 
    145       'move' => false, 
    146       'download' => false, 
    147       /* ^^^ this last one is easily circumnavigated if it's about images: when you can view 'em, you can 'download' them anyway. 
    148        *     However, for other mime types which are not previewable / viewable 'in their full bluntal nugity' ;-) , this will 
    149        *     be a strong deterent. 
    150        * 
    151        *     Think Springer Verlag and PDFs, for instance. You can have 'em, but only /after/ you've ... 
    152        */ 
    153       'allowExtChange' => false, 
    154       'safe' => true, 
    155       'chmod' => 0777, 
    156       'UploadIsAuthorized_cb' => null, 
    157       'DownloadIsAuthorized_cb' => null, 
    158       'CreateIsAuthorized_cb' => null, 
    159       'DestroyIsAuthorized_cb' => null, 
    160       'MoveIsAuthorized_cb' => null, 
    161        
    162      // Xinha: Allow to specify the "Resize Large Images" tolerance level. 
    163      'suggestedMaxImageDimension' => array('width' => 1024, 'height' => 768), 
    164     ), (is_array($options) ? $options : array())); 
    165  
    166     $this->options['thumbnailPath'] = FileManagerUtility::getRealPath($this->options['thumbnailPath'], $this->options['chmod'], true); // create path if nonexistent 
    167     $this->options['assetBasePath'] = FileManagerUtility::getRealPath($this->options['assetBasePath']); 
    168     $this->options['mimeTypesPath'] = FileManagerUtility::getRealDir($this->options['mimeTypesPath'], 0, false, false); // filespec, not a dirspec! 
    169     $this->options['directory'] = FileManagerUtility::getRealPath($this->options['directory']); 
    170     $this->basedir = FileManagerUtility::getSiteRoot() . $this->options['directory']; 
    171  
    172     header('Expires: Fri, 01 Jan 1990 00:00:00 GMT'); 
    173     header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); 
    174  
    175     $this->get = $_GET; 
    176     $this->post = $_POST; 
    177   } 
    178  
    179   public function fireEvent($event) 
    180   { 
    181     $event = $event ? 'on' . ucfirst($event) : null; 
    182     if (!$event || !method_exists($this, $event)) $event = 'onView'; 
    183  
    184     $this->{$event}(); 
    185   } 
    186  
    187   /** 
    188    * @return array the FileManager options and settings. 
    189    */ 
    190   public function getSettings() 
    191   { 
    192     return array_merge(array( 
    193         'basedir' => $this->basedir 
    194     ), $this->options); 
    195   } 
    196  
    197   private function _onView($dir, $json, $mime_filter, $list_type) 
    198   { 
    199     $files = ($files = glob($dir . '*')) ? $files : array(); 
    200  
    201     $root = FileManagerUtility::getSiteRoot(); 
    202  
    203     if ($dir != $this->basedir) array_unshift($files, $dir . '..'); 
    204     natcasesort($files); 
    205     foreach ($files as $file) 
    206     { 
    207       $file = self::normalize($file); 
    208       $url = str_replace($root,'',$file); 
    209  
    210       $mime = $this->getMimeType($file); 
    211       if ($mime_filter && $mime != 'text/directory' && !FileManagerUtility::startsWith($mime, $mime_filter)) 
    212         continue; 
    213  
    214       /* 
    215        * each image we inspect may throw an exception due to a out of memory warning 
    216        * (which is far better than without those: a silent fatal abort!) 
    217        * 
    218        * However, now that we do have a way to check most memory failures occurring in here (due to large images 
    219        * and too little available RAM) we /still/ want a directory view; we just want to skip/ignore/mark those 
    220        * overly large ones. 
    221        */ 
    222       $thumb = false; 
    223       try 
     516        protected $options; 
     517 
     518        public function __construct($options) 
     519        { 
     520                $this->options = array_merge(array( 
     521                        /* 
     522                         * Note that all default paths as listed below are transformed to DocumentRoot-based paths 
     523                         * through the getRealPath() invocations further below: 
     524                         */ 
     525                        'directory' => null,                                       // MUST be in the DocumentRoot tree 
     526                        'assetBasePath' => null,                                   // may sit outside options['directory'] but MUST be in the DocumentRoot tree 
     527                        'thumbnailPath' => null,                                   // may sit outside options['directory'] but MUST be in the DocumentRoot tree 
     528                        'mimeTypesPath' => str_replace('\\', '/', dirname(__FILE__)) . '/MimeTypes.ini',   // an absolute filesystem path anywhere; when relative, it will be assumed to be against SERVER['SCRIPT_NAME'] 
     529                        'dateFormat' => 'j M Y - H:i', 
     530                        'maxUploadSize' => 2600 * 2600 * 3, 
     531                        // 'maxImageSize' => 99999,                                 // obsoleted, replaced by 'suggestedMaxImageDimension' 
     532                        // Xinha: Allow to specify the "Resize Large Images" tolerance level. 
     533                        'maxImageDimension' => array('width' => 1024, 'height' => 768), 
     534                        'upload' => false, 
     535                        'destroy' => false, 
     536                        'create' => false, 
     537                        'move' => false, 
     538                        'download' => false, 
     539                        /* ^^^ this last one is easily circumnavigated if it's about images: when you can view 'em, you can 'download' them anyway. 
     540                         *     However, for other mime types which are not previewable / viewable 'in their full bluntal nugity' ;-) , this will 
     541                         *     be a strong deterent. 
     542                         * 
     543                         *     Think Springer Verlag and PDFs, for instance. You can have 'em, but only /after/ you've ... 
     544                         */ 
     545                        'allowExtChange' => false, 
     546                        'safe' => true, 
     547                        'filter' => null, 
     548                        'chmod' => 0777, 
     549                        'ViewIsAuthorized_cb' => null, 
     550                        'DetailIsAuthorized_cb' => null, 
     551                        'ThumbnailIsAuthorized_cb' => null, 
     552                        'UploadIsAuthorized_cb' => null, 
     553                        'DownloadIsAuthorized_cb' => null, 
     554                        'CreateIsAuthorized_cb' => null, 
     555                        'DestroyIsAuthorized_cb' => null, 
     556                        'MoveIsAuthorized_cb' => null, 
     557                        'URIpropagateData' => null 
     558                ), (is_array($options) ? $options : array())); 
     559 
     560                // transform the obsoleted/deprecated options: 
     561                if (!empty($this->options['maxImageSize']) && $this->options['maxImageSize'] != 1024 && $this->options['maxImageDimension']['width'] == 1024 && $this->options['maxImageDimension']['height'] == 768) 
     562                { 
     563                        $this->options['maxImageDimension'] = array('width' => $this->options['maxImageSize'], 'height' => $this->options['maxImageSize']); 
     564                } 
     565 
     566                // only calculate the guestimated defaults when they are indeed required: 
     567                if ($this->options['directory'] == null || $this->options['assetBasePath'] == null || $this->options['thumbnailPath'] == null) 
     568                { 
     569                        $assumed_root = @realpath($_SERVER['DOCUMENT_ROOT']); 
     570                        $assumed_root = str_replace('\\', '/', $assumed_root); 
     571                        if (FileManagerUtility::endsWith($assumed_root, '/')) 
     572                        { 
     573                                $assumed_root = substr($assumed_root, 0, -1); 
     574                        } 
     575                        $my_path = @realpath(dirname(__FILE__)); 
     576                        $my_path = str_replace('\\', '/', $my_path); 
     577                        if (!FileManagerUtility::endsWith($my_path, '/')) 
     578                        { 
     579                                $my_path .= '/'; 
     580                        } 
     581                        $my_assumed_url_path = str_replace($assumed_root, '', $my_path); 
     582 
     583                        // we throw an Exception here because when these do not apply, the user should have specified all three these entries! 
     584                        if (empty($assumed_root) || empty($my_path) || !FileManagerUtility::startsWith($my_path, $assumed_root)) 
     585                                throw new FileManagerException('nofile'); 
     586 
     587                        if ($this->options['directory'] == null) 
     588                        { 
     589                                $this->options['directory'] = $my_assumed_url_path . '../../Demos/Files/'; 
     590                        } 
     591                        if ($this->options['assetBasePath'] == null) 
     592                        { 
     593                                $this->options['assetBasePath'] = $my_assumed_url_path . '../../Demos/Files/../../Assets/'; 
     594                        } 
     595                        if ($this->options['thumbnailPath'] == null) 
     596                        { 
     597                                $this->options['thumbnailPath'] = $my_assumed_url_path . '../../Demos/Files/../../Assets/Thumbs/'; 
     598                        } 
     599                } 
     600 
     601                /* 
     602                 * make sure we start with a very predictable and LEGAL options['directory'] setting, so that the checks applied to the 
     603                 * (possibly) user specified value for this bugger acvtually can check out okay AS LONG AS IT'S INSIDE the DocumentRoot-based 
     604                 * directory tree: 
     605                 */ 
     606                $new_root = $this->options['directory']; 
     607                $this->options['directory'] = '/';      // use DocumentRoot temporarily as THE root for this optional transform 
     608                $this->options['directory'] = self::enforceTrailingSlash($this->rel2abs_url_path($new_root)); 
     609 
     610                // now that the correct options['directory'] has been set up, go and check/clean the other paths in the options[]: 
     611 
     612                $this->options['thumbnailPath'] = self::enforceTrailingSlash($this->rel2abs_url_path($this->options['thumbnailPath'])); 
     613                $this->options['assetBasePath'] = self::enforceTrailingSlash($this->rel2abs_url_path($this->options['assetBasePath'])); 
     614 
     615                $this->options['mimeTypesPath'] = @realpath($this->options['mimeTypesPath']); 
     616                if (empty($this->options['mimeTypesPath'])) 
     617                        throw new FileManagerException('nofile'); 
     618                $this->options['mimeTypesPath'] = str_replace('\\', '/', $this->options['mimeTypesPath']); 
     619 
     620                if (!headers_sent()) 
     621                { 
     622                        header('Expires: Fri, 01 Jan 1990 00:00:00 GMT'); 
     623                        header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); 
     624                } 
     625        } 
     626 
     627        /** 
     628         * @return array the FileManager options and settings. 
     629         */ 
     630        public function getSettings() 
     631        { 
     632                return array_merge(array( 
     633                                'basedir' => $this->url_path2file_path($this->options['directory']) 
     634                ), $this->options); 
     635        } 
     636 
     637 
     638 
     639 
     640        /** 
     641         * Central entry point for any client side request. 
     642         */ 
     643        public function fireEvent($event = null) 
     644        { 
     645                $event = !empty($event) ? 'on' . ucfirst($event) : null; 
     646                if (!$event || !method_exists($this, $event)) $event = 'onView'; 
     647 
     648                $this->{$event}(); 
     649        } 
     650 
     651 
     652 
     653 
     654 
     655 
     656        /** 
     657         * Generalized 'view' handler, which produces a directory listing. 
     658         * 
     659         * Return the directory listing in a nested array, suitable for JSON encoding. 
     660         */ 
     661        protected function _onView($legal_url, $json, $mime_filter, $list_type, $file_preselect_arg = null, $filemask = '*') 
     662        { 
     663                $dir = $this->legal_url_path2file_path($legal_url); 
     664                if (!is_dir($dir)) 
     665                { 
     666                        throw new FileManagerException('nofile'); 
     667                } 
     668                $files = $this->scandir($dir, $filemask); 
     669 
     670                if ($files === false) 
     671                        throw new FileManagerException('nofile'); 
     672 
     673                /* 
     674                 * To ensure '..' ends up at the very top of the view, no matter what the other entries in $files[] are made of, 
     675                 * we pop the last element off the array, check whether it's the double-dot, and if so, keep it out while we 
     676                 * let the sort run. 
     677                 */ 
     678                $doubledot = array_pop($files); 
     679                if ($doubledot !== null && $doubledot !== '..') 
     680                { 
     681                        $files[] = $doubledot; 
     682                        $doubledot = null; 
     683                } 
     684                natcasesort($files); 
     685                if ($doubledot !== null) 
     686                { 
     687                        array_unshift($files, $doubledot); 
     688                } 
     689 
     690                $mime_filters = $this->getAllowedMimeTypes($mime_filter); 
     691 
     692                // remove the imageinfo() call overhead per file for very large directories; just guess at the mimetye from the filename alone. 
     693                // The real mimetype will show up in the 'details' view anyway! This is only for the 'filter' function: 
     694                $just_guess_mime = (count($files) > 100); 
     695 
     696                $fileinfo = array( 
     697                                'legal_url' => $legal_url, 
     698                                'dir' => $dir, 
     699                                'files' => $files, 
     700                                'mime_filter' => $mime_filter, 
     701                                'mime_filters' => $mime_filters, 
     702                                'guess_mime' => $just_guess_mime, 
     703                                'list_type' => $list_type, 
     704                                'file_preselect' => $file_preselect_arg, 
     705                                'preliminary_json' => $json 
     706                        ); 
     707 
     708                if (!empty($this->options['ViewIsAuthorized_cb']) && function_exists($this->options['ViewIsAuthorized_cb']) && !$this->options['ViewIsAuthorized_cb']($this, 'view', $fileinfo)) 
     709                        throw new FileManagerException('authorized'); 
     710 
     711                $legal_url = $fileinfo['legal_url']; 
     712                $dir = $fileinfo['dir']; 
     713                $files = $fileinfo['files']; 
     714                $mime_filter = $fileinfo['mime_filter']; 
     715                $mime_filters = $fileinfo['mime_filters']; 
     716                $just_guess_mime = $fileinfo['guess_mime']; 
     717                $list_type = $fileinfo['list_type']; 
     718                $file_preselect_arg = $fileinfo['file_preselect']; 
     719                $json = $fileinfo['preliminary_json']; 
     720 
     721                $file_preselect_index = -1; 
     722                $idx = array(0, 0); 
     723 
     724                foreach ($files as $filename) 
     725                { 
     726                        $url = $legal_url . $filename; 
     727                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     728                        $file = $this->legal_url_path2file_path($url); 
     729 
     730                        $isdir = (is_file($file) ? 0 : 1); 
     731                        if (!$isdir) 
     732                        { 
     733                                $mime = $this->getMimeType($file, $just_guess_mime); 
     734                                if (is_file($file)) 
     735                                { 
     736                                        if (!$this->IsAllowedMimeType($mime, $mime_filters)) 
     737                                                continue; 
     738                                } 
     739                                else 
     740                                { 
     741                                        continue; 
     742                                } 
     743                                $iconspec = $filename; 
     744 
     745                                if ($filename == $file_preselect_arg) 
     746                                { 
     747                                        $file_preselect_index = $idx[0]; 
     748                                } 
     749                        } 
     750                        else if (is_dir($file)) 
     751                        { 
     752                                $mime = 'text/directory'; 
     753                                $iconspec = ($filename == '..' ? 'is.dir_up' : 'is.dir'); 
     754                        } 
     755                        else 
     756                        { 
     757                                // simply do NOT list anything that we cannot cope with. 
     758                                // That includes clearly inaccessible files (and paths) with non-ASCII characters: 
     759                                // PHP5 and below are a real mess when it comes to handling Unicode filesystems 
     760                                // (see the php.net site too: readdir / glob / etc. user comments and the official 
     761                                // notice that PHP will support filesystem UTF-8/Unicode only when PHP6 is released. 
     762                                // 
     763                                // Big, fat bummer! 
     764                                continue; 
     765                        } 
     766 
     767                        if (FileManagerUtility::startsWith($mime, 'image/')) 
     768                        { 
     769                                /* 
     770                                 * offload the thumbnailing process to another event ('event=thumbnail') to be fired by the client 
     771                                 * when it's time to render the thumbnail: the offloading helps us tremendously in coping with large 
     772                                 * directories: 
     773                                 * WE simply assume the thumbnail will be there, so we don't even need to check for its existence 
     774                                 * (which saves us one more file_exists() per item at the very least). And when it doesn't, that's 
     775                                 * for the event=thumbnail handler to worry about (creating the thumbnail on demand or serving 
     776                                 * a generic icon image instead). 
     777                                 */ 
     778                                $thumb48 = $this->mkEventHandlerURL(array( 
     779                                                'event' => 'thumbnail', 
     780                                                // directory and filename of the ORIGINAL image should follow next: 
     781                                                'directory' => $legal_url, 
     782                                                'file' => $filename, 
     783                                                'size' => 48,          // thumbnail suitable for 'view/type=thumb' list views 
     784                                                'filter' => $mime_filter 
     785                                        )); 
     786                                $thumb250 = $this->mkEventHandlerURL(array( 
     787                                                'event' => 'thumbnail', 
     788                                                // directory and filename of the ORIGINAL image should follow next: 
     789                                                'directory' => $legal_url, 
     790                                                'file' => $filename, 
     791                                                'size' => 250,         // thumbnail suitable for 'view/type=thumb' list views 
     792                                                'filter' => $mime_filter 
     793                                        )); 
     794                        } 
     795                        else 
     796                        { 
     797                                $thumb48 = FileManagerUtility::rawurlencode_path($this->getIcon($iconspec, false)); 
     798                                $thumb250 = $thumb48; 
     799                        } 
     800                        $icon = FileManagerUtility::rawurlencode_path($this->getIcon($iconspec, true)); 
     801 
     802                        if ($list_type == 'thumb') 
     803                        { 
     804                                $thumb = $thumb48; 
     805                        } 
     806                        else 
     807                        { 
     808                                $thumb = $icon; 
     809                        } 
     810 
     811                        $out[$isdir][] = array( 
     812                                        'path' => FileManagerUtility::rawurlencode_path($url), 
     813                                        'name' => preg_replace('/[^ -~]/', '?', $filename),       // HACK/TWEAK: PHP5 and below are completely b0rked when it comes to international filenames   :-( 
     814                                        'date' => date($this->options['dateFormat'], @filemtime($file)), 
     815                                        'mime' => $mime, 
     816                                        'thumbnail' => $thumb, 
     817                                        'thumbnail48' => $thumb48, 
     818                                        'thumbnail250' => $thumb250, 
     819                                        'icon' => $icon, 
     820                                        'size' => @filesize($file) 
     821                                ); 
     822                        $idx[$isdir]++; 
     823 
     824                        if (0) 
     825                        { 
     826                                // help PHP when 'doing' large image directories: reset the timeout for each thumbnail / entry we produce: 
     827                                //   http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time 
     828                                set_time_limit(max(30, ini_get('max_execution_time'))); 
     829                        } 
     830                } 
     831 
     832                $thumb48 = FileManagerUtility::rawurlencode_path($this->getIcon('is.dir', false)); 
     833                $icon = FileManagerUtility::rawurlencode_path($this->getIcon('is.dir', true)); 
     834                if ($list_type == 'thumb') 
     835                { 
     836                        $thumb = $thumb48; 
     837                } 
     838                else 
     839                { 
     840                        $thumb = $icon; 
     841                } 
     842                return array( 
     843                        'dirs' => (!empty($out[0]) ? $out[0] : array()), 
     844                        'files' => (!empty($out[1]) ? $out[1] : array()), 
     845                        'json' => array_merge((is_array($json) ? $json : array()), array( 
     846                                'root' => substr($this->options['directory'], 1), 
     847                                'path' => $legal_url,                                  // is relative to options['directory'] 
     848                                'dir' => array( 
     849                                        'path' => FileManagerUtility::rawurlencode_path($legal_url), 
     850                                        'name' => pathinfo($legal_url, PATHINFO_BASENAME), 
     851                                        'date' => date($this->options['dateFormat'], @filemtime($dir)), 
     852                                        'mime' => 'text/directory', 
     853                                        'thumbnail' => $thumb, 
     854                                        'thumbnail48' => $thumb48, 
     855                                        'thumbnail250' => $thumb48, 
     856                                        'icon' => $icon 
     857                                ), 
     858                                'preselect_index' => $file_preselect_index, 
     859                                'preselect_name' => ($file_preselect_index >= 0 ? $file_preselect_arg : null) 
     860                        )) 
     861                ); 
     862        } 
     863 
     864        /** 
     865         * Process the 'view' event (default event fired by fireEvent() method) 
     866         * 
     867         * Returns a JSON encoded directory view list. 
     868         * 
     869         * Expected parameters: 
     870         * 
     871         * $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
     872         * 
     873         * $_POST['file_preselect']     optional filename or path: 
     874         *                         when a filename, this is the filename of a file in this directory  
     875         *                         which should be located and selected. When found, the backend will  
     876         *                         provide an index number pointing at the corresponding JSON files[] 
     877         *                         entry to assist the front-end in jumping to that particular item  
     878         *                         in the view. 
     879         * 
     880         *                         when a path, it is either an absolute or a relative path: 
     881         *                         either is assumed to be a URI URI path, i.e. rooted at 
     882         *                           DocumentRoot. 
     883         *                         The path will be transformed to a LEGAL URI path and 
     884         *                         will OVERRIDE the $_POST['directory'] path. 
     885         *                         Otherwise, this mode acts as when only a filename was specified here. 
     886         *                         This mode is useful to help a frontend to quickly jump to a file 
     887         *                         pointed at by a URI. 
     888         * 
     889         *                         N.B.: This also the only entry which accepts absolute URI paths and  
     890         *                               transforms them to LEGAL URI paths. 
     891         * 
     892         *                         When the specified path is illegal, i.e. does not reside inside the 
     893         *                         options['directory']-rooted LEGAL URI subtree, it will be discarded 
     894         *                         entirely (as all file paths, whether they are absolute or relative, 
     895         *                         must end up inside the options['directory']-rooted subtree to be 
     896         *                         considered manageable files) and the process will continue as if  
     897         *                         the $_POST['file_preselect'] entry had not been set. 
     898         * 
     899         * $_POST['filter']        optional mimetype filter string, amy be the part up to and 
     900         *                         including the slash '/' or the full mimetype. Only files 
     901         *                         matching this (set of) mimetypes will be listed. 
     902         *                         Examples: 'image/' or 'application/zip' 
     903         * 
     904         * $_POST['type']          'thumb' will produce a list view including thumbnail and other 
     905         *                         information with each listed file; other values will produce 
     906         *                         a basic list view (similar to Windows Explorer 'list' view). 
     907         * 
     908         * Errors will produce a JSON encoded error report, including at least two fields: 
     909         * 
     910         * status                  0 for error; nonzero for success 
     911         * 
     912         * error                   error message 
     913         * 
     914         * Next to these, the JSON encoded output will, with high probability, include a 
     915         * list view of the parent or 'basedir' as a fast and easy fallback mechanism for client side 
     916         * viewing code. However, severe and repetitive errors may not produce this 
     917         * 'fallback view list' so proper client code should check the 'status' field in the 
     918         * JSON output. 
     919         */ 
     920        protected function onView() 
     921        { 
     922                // try to produce the view; if it b0rks, retry with the parent, until we've arrived at the basedir: 
     923                // then we fail more severely. 
     924 
     925                $emsg = null; 
     926                $jserr = array( 
     927                                'status' => 1 
     928                        ); 
     929 
     930                $mime_filter = $this->getPOSTparam('filter', $this->options['filter']); 
     931                $list_type = ($this->getPOSTparam('type') != 'thumb' ? 'list' : 'thumb'); 
     932 
     933                try 
     934                { 
     935                        $dir_arg = $this->getPOSTparam('directory'); 
     936                        $legal_url = $this->rel2abs_legal_url_path($dir_arg); 
     937                        $legal_url = self::enforceTrailingSlash($legal_url); 
     938 
     939                        $file_preselect_arg = $this->getPOSTparam('file_preselect'); 
     940                        try 
     941                        { 
     942                                if (!empty($file_preselect_arg)) 
     943                                { 
     944                                        // check if this a path instead of just a basename, then convert to legal_url and split across filename and directory. 
     945                                        if (strpos($file_preselect_arg, '/') !== false) 
     946                                        { 
     947                                                // this will also convert a relative path to an absolute path before transforming it to a LEGAL URI path: 
     948                                                $legal_presel = $this->abs2legal_url_path($file_preselect_arg); 
     949                                                 
     950                                                $prseli = pathinfo($legal_presel); 
     951                                                $file_preselect_arg = $prseli['basename']; 
     952                                                // override the directory! 
     953                                                $legal_url = $prseli['dirname']; 
     954                                                $legal_url = self::enforceTrailingSlash($legal_url); 
     955                                        } 
     956                                        else 
     957                                        { 
     958                                                $file_preselect_arg = pathinfo($file_preselect_arg, PATHINFO_BASENAME); 
     959                                        } 
     960                                } 
     961                        } 
     962                        catch(FileManagerException $e) 
     963                        { 
     964                                // discard the preselect input entirely: 
     965                                $file_preselect_arg = null; 
     966                        } 
     967                } 
     968                catch(FileManagerException $e) 
     969                { 
     970                        $emsg = $e->getMessage(); 
     971                        $legal_url = '/'; 
     972                        $file_preselect_arg = null; 
     973                } 
     974                catch(Exception $e) 
     975                { 
     976                        // catching other severe failures; since this can be anything it may not be a translation keyword in the message... 
     977                        $emsg = $e->getMessage(); 
     978                        $legal_url = '/'; 
     979                        $file_preselect_arg = null; 
     980                } 
     981 
     982                // loop until we drop below the bottomdir; meanwhile getDir() above guarantees that $dir is a subdir of bottomdir, hence dir >= bottomdir. 
     983                do 
     984                { 
     985                        try 
     986                        { 
     987                                $rv = $this->_onView($legal_url, $jserr, $mime_filter, $list_type, $file_preselect_arg); 
     988 
     989                                if (!headers_sent()) header('Content-Type: application/json'); 
     990 
     991                                echo json_encode(array_merge($rv['json'], array('files' => array_merge(array(), $rv['dirs'], $rv['files'])))); 
     992                                return; 
     993                        } 
     994                        catch(FileManagerException $e) 
     995                        { 
     996                                if ($emsg === null) 
     997                                        $emsg = $e->getMessage(); 
     998                        } 
     999                        catch(Exception $e) 
     1000                        { 
     1001                                // catching other severe failures; since this can be anything it may not be a translation keyword in the message... 
     1002                                if ($emsg === null) 
     1003                                        $emsg = $e->getMessage(); 
     1004                        } 
     1005 
     1006                        // step down to the parent dir and retry: 
     1007                        $legal_url = self::getParentDir($legal_url); 
     1008                        $file_preselect_arg = null; 
     1009 
     1010                        $jserr['status']++; 
     1011 
     1012                } while ($legal_url !== false); 
     1013 
     1014                $this->modify_json4exception($jserr, $emsg . ' : path :: ' . $legal_url); 
     1015 
     1016                if (!headers_sent()) header('Content-Type: application/json'); 
     1017 
     1018                // when we fail here, it's pretty darn bad and nothing to it. 
     1019                // just push the error JSON as go. 
     1020                echo json_encode($jserr); 
     1021        } 
     1022 
     1023        /** 
     1024         * Process the 'detail' event 
     1025         * 
     1026         * Returns a JSON encoded HTML chunk describing the specified file (metadata such 
     1027         * as size, format and possibly a thumbnail image as well) 
     1028         * 
     1029         * Expected parameters: 
     1030         * 
     1031         * $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
     1032         * 
     1033         * $_POST['file']          filename (including extension, of course) of the file to 
     1034         *                         be detailed. 
     1035         * 
     1036         * $_POST['filter']        optional mimetype filter string, amy be the part up to and 
     1037         *                         including the slash '/' or the full mimetype. Only files 
     1038         *                         matching this (set of) mimetypes will be listed. 
     1039         *                         Examples: 'image/' or 'application/zip' 
     1040         * 
     1041         * Errors will produce a JSON encoded error report, including at least two fields: 
     1042         * 
     1043         * status                  0 for error; nonzero for success 
     1044         * 
     1045         * error                   error message 
     1046         */ 
     1047        protected function onDetail() 
     1048        { 
     1049                $emsg = null; 
     1050                $jserr = array( 
     1051                                'status' => 1 
     1052                        ); 
     1053 
     1054                try 
     1055                { 
     1056                        $file_arg = $this->getPOSTparam('file'); 
     1057                        if (empty($file_arg)) 
     1058                                throw new FileManagerException('nofile'); 
     1059 
     1060                        $dir_arg = $this->getPOSTparam('directory'); 
     1061                        $legal_url = $this->rel2abs_legal_url_path($dir_arg); 
     1062                        $legal_url = self::enforceTrailingSlash($legal_url); 
     1063 
     1064                        $filename = pathinfo($file_arg, PATHINFO_BASENAME); 
     1065                        $legal_url .= $filename; 
     1066                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     1067                        $file = $this->legal_url_path2file_path($legal_url); 
     1068 
     1069                        if (!is_readable($file)) 
     1070                                throw new FileManagerException('nofile'); 
     1071 
     1072                        $mime_filter = $this->getPOSTparam('filter', $this->options['filter']); 
     1073                        $mime_filters = $this->getAllowedMimeTypes($mime_filter); 
     1074                        $mime = $this->getMimeType($file); 
     1075                        if (is_file($file)) 
     1076                        { 
     1077                                if (!$this->IsAllowedMimeType($mime, $mime_filters)) 
     1078                                        throw new FileManagerException('extension'); 
     1079                        } 
     1080                        else if (!is_dir($file)) 
     1081                        { 
     1082                                throw new FileManagerException('nofile'); 
     1083                        } 
     1084 
     1085                        $fileinfo = array( 
     1086                                        'legal_url' => $legal_url, 
     1087                                        'file' => $file, 
     1088                                        'filename' => $filename, 
     1089                                        'mime' => $mime, 
     1090                                        'mime_filter' => $mime_filter, 
     1091                                        'mime_filters' => $mime_filters 
     1092                                ); 
     1093 
     1094                        if (!empty($this->options['DetailIsAuthorized_cb']) && function_exists($this->options['DetailIsAuthorized_cb']) && !$this->options['DetailIsAuthorized_cb']($this, 'detail', $fileinfo)) 
     1095                                throw new FileManagerException('authorized'); 
     1096 
     1097                        $legal_url = $fileinfo['legal_url']; 
     1098                        $file = $fileinfo['file']; 
     1099                        $filename = $fileinfo['filename']; 
     1100                        $mime = $fileinfo['mime']; 
     1101                        $mime_filter = $fileinfo['mime_filter']; 
     1102                        $mime_filters = $fileinfo['mime_filters']; 
     1103 
     1104                        $jserr = $this->extractDetailInfo($jserr, $legal_url, $file, $mime, $mime_filter); 
     1105 
     1106                        if (!headers_sent()) header('Content-Type: application/json'); 
     1107 
     1108                        echo json_encode($jserr); 
     1109                        return; 
     1110                } 
     1111                catch(FileManagerException $e) 
     1112                { 
     1113                        $emsg = $e->getMessage(); 
     1114                } 
     1115                catch(Exception $e) 
     1116                { 
     1117                        // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
     1118                        $emsg = $e->getMessage(); 
     1119                } 
     1120 
     1121                $this->modify_json4exception($jserr, $emsg); 
     1122 
     1123                if (!headers_sent()) header('Content-Type: application/json'); 
     1124 
     1125                // when we fail here, it's pretty darn bad and nothing to it. 
     1126                // just push the error JSON as go. 
     1127                echo json_encode($jserr); 
     1128        } 
     1129 
     1130        /** 
     1131         * Process the 'thumbnail' event 
     1132         * 
     1133         * Returns either the binary content of the requested thumbnail or the binary content of a replacement image. 
     1134         * 
     1135         * Technical info: this function is assumed to be fired from a <img src="..."> URI or similar and must produce 
     1136         * the content of an image. 
     1137         * It is used in conjection with the 'view/list=thumb' view mode of the FM client: the 'view' list, as 
     1138         * produced by us, contains specially crafted URLs pointing back at us (the 'event=thumbnail' URLs) to 
     1139         * enable FM to cope much better with large image collections by having the entire thumbnail checking 
     1140         * and creation process offloaded to this Just-in-Time subevent. 
     1141         * 
     1142         * By not loading the 'view' event with the thumbnail precreation/checking effort, it can respond 
     1143         * much faster or at least not timeout in the backend for larger image sets in any directory. 
     1144         * ('view' simply assumes the thumbnail will be there, hence reducing its own workload with at least 
     1145         * 1 file_exists() plus worst-case one GD imageinfo + imageresample + extras per image in the 'view' list!) 
     1146         * 
     1147         * Expected parameters: 
     1148         * 
     1149         * $_GET['directory']      path relative to basedir a.k.a. options['directory'] root 
     1150         * 
     1151         * $_GET['file']           filename (including extension, of course) of the file to 
     1152         *                         be thumbnailed. 
     1153         * 
     1154         * $_GET['size']           the requested thumbnail maximum width / height (the bounding box is square). 
     1155         *                         Must be one of our 'authorized' sizes: 48, 250. 
     1156         * 
     1157         * $_GET['filter']         optional mimetype filter string, amy be the part up to and 
     1158         *                         including the slash '/' or the full mimetype. Only files 
     1159         *                         matching this (set of) mimetypes will be listed. 
     1160         *                         Examples: 'image/' or 'application/zip' 
     1161         * 
     1162         * $_GET['asJson']        return some JSON {status: 1, thumbnail: 'path/to/thumbnail.png' } 
     1163         * 
     1164         * Errors will produce a JSON encoded error report, including at least two fields: 
     1165         * 
     1166         * status                  0 for error; nonzero for success 
     1167         * 
     1168         * error                   error message 
     1169         * 
     1170         * Next to these, the JSON encoded output will, with high probability, include a 
     1171         * list view of the parent or 'basedir' as a fast and easy fallback mechanism for client side 
     1172         * viewing code. However, severe and repetitive errors may not produce this 
     1173         * 'fallback view list' so proper client code should check the 'status' field in the 
     1174         * JSON output. 
     1175         */ 
     1176        protected function onThumbnail() 
     1177        { 
     1178                // try to produce the view; if it b0rks, retry with the parent, until we've arrived at the basedir: 
     1179                // then we fail more severely. 
     1180 
     1181                $emsg = null; 
     1182                $img_filepath = null; 
     1183                $reqd_size = 48; 
     1184                $filename = null; 
     1185 
     1186                try 
     1187                { 
     1188                        $reqd_size = intval($this->getGETparam('size')); 
     1189                        if (empty($reqd_size)) 
     1190                                throw new FileManagerException('disabled'); 
     1191                        // and when not requesting one of our 'authorized' thumbnail sizes, you're gonna burn as well! 
     1192                        if (!in_array($reqd_size, array(16, 48, 250))) 
     1193                                throw new FileManagerException('disabled'); 
     1194 
     1195                        $file_arg = $this->getGETparam('file'); 
     1196                        if (empty($file_arg)) 
     1197                                throw new FileManagerException('nofile'); 
     1198 
     1199                        $dir_arg = $this->getGETparam('directory'); 
     1200                        $legal_url = $this->rel2abs_legal_url_path($dir_arg); 
     1201                        $legal_url = self::enforceTrailingSlash($legal_url); 
     1202 
     1203                        $filename = pathinfo($file_arg, PATHINFO_BASENAME); 
     1204                        $legal_url .= $filename; 
     1205                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     1206                        $file = $this->legal_url_path2file_path($legal_url); 
     1207 
     1208                        if (!is_readable($file)) 
     1209                                throw new FileManagerException('nofile'); 
     1210 
     1211                        $mime_filter = $this->getGETparam('filter', $this->options['filter']); 
     1212                        $mime_filters = $this->getAllowedMimeTypes($mime_filter); 
     1213                        $mime = $this->getMimeType($file); 
     1214                        if (is_file($file)) 
     1215                        { 
     1216                                if (!$this->IsAllowedMimeType($mime, $mime_filters)) 
     1217                                        throw new FileManagerException('extension'); 
     1218                        } 
     1219                        else 
     1220                        { 
     1221                                throw new FileManagerException('nofile'); 
     1222                        } 
     1223 
     1224                        $fileinfo = array( 
     1225                                        'legal_url' => $legal_url, 
     1226                                        'file' => $file, 
     1227                                        'filename' => $filename, 
     1228                                        'mime' => $mime, 
     1229                                        'mime_filter' => $mime_filter, 
     1230                                        'mime_filters' => $mime_filters, 
     1231                                        'requested_size' => $reqd_size 
     1232                                ); 
     1233 
     1234                        if (!empty($this->options['ThumbnailIsAuthorized_cb']) && function_exists($this->options['ThumbnailIsAuthorized_cb']) && !$this->options['ThumbnailIsAuthorized_cb']($this, 'thumbnail', $fileinfo)) 
     1235                                throw new FileManagerException('authorized'); 
     1236 
     1237                        $legal_url = $fileinfo['legal_url']; 
     1238                        $file = $fileinfo['file']; 
     1239                        $filename = $fileinfo['filename']; 
     1240                        $mime = $fileinfo['mime']; 
     1241                        $mime_filter = $fileinfo['mime_filter']; 
     1242                        $mime_filters = $fileinfo['mime_filters']; 
     1243                        $reqd_size = $fileinfo['requested_size']; 
     1244 
     1245                        /* 
     1246                         * each image we inspect may throw an exception due to a out of memory warning 
     1247                         * (which is far better than without those: a silent fatal abort!) 
     1248                         * 
     1249                         * However, now that we do have a way to check most memory failures occurring in here (due to large images 
     1250                         * and too little available RAM) we /still/ want to see that happen: for broken and overlarge images, we 
     1251                         * produce some alternative graphics instead! 
     1252                         */ 
     1253                        $thumb_path = null; 
     1254                        if (FileManagerUtility::startsWith($mime, 'image/')) 
     1255                        { 
     1256                                // access the image and create a thumbnail image; this can fail dramatically 
     1257                                $thumb_path = $this->getThumb($legal_url, $file, $reqd_size, $reqd_size); 
     1258                        } 
     1259 
     1260                        $img_filepath = (!empty($thumb_path) ? $thumb_path : $this->getIcon($filename, $reqd_size <= 16)); 
     1261                } 
     1262                catch(FileManagerException $e) 
     1263                { 
     1264                        $emsg = $e->getMessage(); 
     1265                } 
     1266                catch(Exception $e) 
     1267                { 
     1268                        // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
     1269                        $emsg = $e->getMessage(); 
     1270                } 
     1271 
     1272                // now go and serve the content of the thumbnail / icon image file (which we still need to determine /exactly/): 
     1273                try 
     1274                { 
     1275                        if (empty($img_filepath)) 
     1276                        { 
     1277                                $img_filepath = $this->getIconForError($emsg, $filename, $reqd_size <= 16); 
     1278                        } 
     1279 
     1280      if($this->getGETParam('asJson', 0)) 
    2241281      { 
    225         // access the image and create a thumbnail image; this can fail dramatically 
    226         if(strpos($mime,'image') !== false) 
    227           $thumb = $this->getThumb($file); 
     1282        $Response = array('status' => 1, 'thumbnail' => $img_filepath); 
     1283        if(@$emsg) 
     1284        { 
     1285          $Response['status'] = 0; 
     1286          $Response['error']  = $emsg; 
     1287        }         
     1288         
     1289        if (!headers_sent()) header('Content-Type: application/json'); 
     1290        echo json_encode($Response); 
     1291        return; 
    2281292      } 
    229       catch (Exception $e) 
    230       { 
    231          // do nothing, except mark image as 'not suitable for thumbnailing' 
    232       } 
    233  
    234       $icon = ($list_type == 'thumb' && $thumb) 
    235         ? $this->options['thumbnailPath'] . $thumb 
    236         : $this->getIcon($file, $list_type != 'thumb'); // TODO: add extra icons for those bad format and superlarge images with make us b0rk? 
    237  
    238       // list files, except the thumbnail folder itself or any file in it: 
    239       if(!FileManagerUtility::startswith($url, substr($this->options['thumbnailPath'],0,-1))) 
    240       { 
    241         $out[is_dir($file) ? 0 : 1][] = array( 
    242           'path' => FileManagerUtility::rawurlencode_path($url), 
    243           'name' => pathinfo($file, PATHINFO_BASENAME), 
    244           'date' => date($this->options['dateFormat'], @filemtime($file)), 
    245           'mime' => $mime, 
    246           'thumbnail' => FileManagerUtility::rawurlencode_path($icon), 
    247           'icon' => FileManagerUtility::rawurlencode_path($this->getIcon($file,true)), 
    248           'size' => @filesize($file) 
    249         ); 
    250       } 
    251     } 
    252     return array_merge((is_array($json) ? $json : array()), array( 
    253         //'assetBasePath' => $this->options['assetBasePath'], 
    254         //'thumbnailPath' => $this->options['thumbnailPath'], 
    255         //'ia_directory' => $this->options['directory'], 
    256         //'ia_dir' => $dir, 
    257         //'ia_root' => $root, 
    258         //'ia_basedir' => $this->basedir, 
    259         'root' => substr($this->options['directory'], 1), 
    260         'path' => str_replace($this->basedir,'',$dir),               // is relative to 'root' 
    261         'dir' => array( 
    262             'name' => pathinfo($dir, PATHINFO_BASENAME), 
    263             'date' => date($this->options['dateFormat'], @filemtime($dir)), 
    264             'mime' => 'text/directory', 
    265             'thumbnail' => $this->getIcon($dir), 
    266             'icon' => $this->getIcon($dir,true) 
    267           ), 
    268       'files' => array_merge(!empty($out[0]) ? $out[0] : array(), !empty($out[1]) ? $out[1] : array()) 
    269     )); 
    270   } 
    271  
    272   /** 
    273    * Process the 'view' event (default event fired by fireEvent() method) 
    274    * 
    275    * Returns a JSON encoded directory view list. 
    276    * 
    277    * Expected parameters: 
    278    * 
    279    * $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
    280    * 
    281    * $_POST['filter']        optional mimetype filter string, amy be the part up to and 
    282    *                         including the slash '/' or the full mimetype. Only files 
    283    *                         matching this (set of) mimetypes will be listed. 
    284    *                         Examples: 'image/' or 'application/zip' 
    285    * 
    286    * $_POST['type']          'thumb' will produce a list view including thumbnail and other 
    287    *                         information with each listed file; other values will produce 
    288    *                         a basic list view (similar to Windows Explorer 'list' view). 
    289    * 
    290    * Errors will produce a JSON encoded error report, including at least two fields: 
    291    * 
    292    * status                  0 for error; nonzero for success 
    293    * 
    294    * error                   error message 
    295    * 
    296    * Next to these, the JSON encoded output will, with high probability, include a 
    297    * list view of the parent or 'basedir' as a fast and easy fallback mechanism for client side 
    298    * viewing code. However, severe and repetitive errors may not produce this 
    299    * 'fallback view list' so proper client code should check the 'status' field in the 
    300    * JSON output. 
    301    */ 
    302   protected function onView() 
    303   { 
    304     // try to produce the view; if it b0rks, retry with the parent, until we've arrived at the basedir: 
    305     // then we fail more severely. 
    306  
    307     $mime_filter = null; 
    308     $list_type = null; 
    309     $emsg = null; 
    310     $jserr = array( 
    311             'status' => 1 
    312         ); 
    313     $bottomdir = $this->basedir; 
    314  
    315     try 
    316     { 
    317         $mime_filter = ((isset($_POST['filter']) && !empty($_POST['filter'])) ? $_POST['filter'].'/' : null); 
    318         $list_type = ((isset($_POST['type']) && $_POST['type'] == 'list') ? 'list' : 'thumb'); 
    319  
    320         $dir = $this->getDir(!empty($this->post['directory']) ? $this->post['directory'] : null); 
    321     } 
    322     catch(FileManagerException $e) 
    323     { 
    324         $emsg = $e->getMessage(); 
    325         $dir = $this->basedir; 
    326     } 
    327     catch(Exception $e) 
    328     { 
    329         // catching other severe failures; since this can be anything it may not be a translation keyword in the message... 
    330         $emsg = $e->getMessage(); 
    331         $dir = $this->basedir; 
    332     } 
    333  
    334     // loop until we drop below the bottomdir; meanwhile getDir() above guarantees that $dir is a subdir of bottomdir, hence dir >= bottomdir. 
    335     do 
    336     { 
    337         try 
    338         { 
    339             $rv = $this->_onView($dir, $jserr, $mime_filter, $list_type); 
    340             echo json_encode($rv); 
    341             return; 
    342         } 
    343         catch(FileManagerException $e) 
    344         { 
    345             $emsg = $e->getMessage(); 
    346         } 
    347         catch(Exception $e) 
    348         { 
    349             // catching other severe failures; since this can be anything it may not be a translation keyword in the message... 
    350             $emsg = $e->getMessage(); 
    351         } 
    352  
    353         // only set up the new json error report array when this is the first exception we got: 
    354         if ($jserr['status']) 
    355         { 
    356             // check the error message and see if it is a translation code word (with or without parameters) or just a generic error report string 
    357             $e = explode(':', $emsg, 2); 
    358             if (preg_match('/[^A-Za-z0-9_-]/', $e[0])) 
    359             { 
    360                 // generic message. ouch. 
    361                 $jserr = array( 
    362                         'status' => 0, 
    363                         'error' => $emsg 
    364                     ); 
    365             } 
    366             else 
    367             { 
    368                 $jserr = array( 
    369                         'status' => 0, 
    370                         'error' => '${backend.' . $e[0] . '}' . (isset($e[1]) ? $e[1] : '') 
    371                     ); 
    372             } 
    373         } 
    374  
    375         // step down to the parent dir and retry: 
    376         $dir = dirname($dir); 
    377         if (!FileManagerUtility::endsWith($dir, '/')) $dir .= '/'; 
    378  
    379     } while (strcmp($dir, $bottomdir) >= 0); 
    380  
    381     // when we fail here, it's pretty darn bad and nothing to it. 
    382     // just push the error JSON as go. 
    383     echo json_encode($jserr); 
    384   } 
    385  
    386   /** 
    387    * Process the 'detail' event 
    388    * 
    389    * Returns a JSON encoded HTML chunk describing the specified file (metadata such 
    390    * as size, format and possibly a thumbnail image as well) 
    391    * 
    392    * Expected parameters: 
    393    * 
    394    * $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
    395    * 
    396    * $_POST['file']          filename (including extension, of course) of the file to 
    397    *                         be detailed. 
    398    * 
    399    * Errors will produce a JSON encoded error report, including at least two fields: 
    400    * 
    401    * status                  0 for error; nonzero for success 
    402    * 
    403    * error                   error message 
    404    */ 
    405   protected function onDetail() 
    406   { 
    407   try 
    408   { 
    409     if (empty($this->post['file'])) 
    410         throw new FileManagerException('nofile'); 
    411  
    412     $url = $this->getPath(!empty($this->post['directory']) ? $this->post['directory'] : null); 
    413     $dir = FileManagerUtility::getSiteRoot() . $url; 
    414     $file = pathinfo($this->post['file'], PATHINFO_BASENAME); 
    415  
    416     $dir .= $file; 
    417     $url .= $file; 
    418  
    419     if (!$this->checkFile($dir)) 
    420         throw new FileManagerException('nofile'); 
    421  
    422     // spare the '/' dir separators from URL encoding: 
    423     $encoded_url = FileManagerUtility::rawurlencode_path($url); 
    424  
    425     $mime = $this->getMimeType($dir); 
    426     $content = null; 
    427     
    428     // Xinha: We want to get some more information about what has been selected in a way 
    429     // we can use it.  Effectively what gets put in here will be passed into the 
    430     // 'onDetails' event handler of your FileManager object (if any). 
    431     $extra_return_detail = array 
    432       ( 
    433         'url'  => $url, 
    434         'mime' => $mime 
    435       ); 
    436     
    437     // image 
    438     if (FileManagerUtility::startsWith($mime, 'image/')) 
    439     { 
    440       // generates a random number to put on the end of the image, to prevent caching 
    441       $randomImage = '?'.md5(uniqid(rand(),1)); 
    442       $size = @getimagesize($dir); 
    443       // check for badly formatted image files (corruption); we'll handle the overly large ones next 
    444       if (!$size) 
    445         throw new FileManagerException('corrupt_img:' . $url); 
    446     
    447         // Xinha: Return some information about the image which can be access  
    448         // from the onDetails event handler in FileManager 
    449        $extra_return_detail['width']  = $size[0]; 
    450        $extra_return_detail['height'] = $size[1];        
    451          
    452       $thumbfile = $this->options['thumbnailPath'] . $this->getThumb($dir); 
    453       $content = '<dl> 
    454           <dt>${width}</dt><dd>' . $size[0] . 'px</dd> 
    455           <dt>${height}</dt><dd>' . $size[1] . 'px</dd> 
    456         </dl> 
    457         <h2>${preview}</h2> 
    458         '; 
    459       try 
    460       { 
    461           $tnc = '<a href="'.$encoded_url.'" data-milkbox="preview" title="'.htmlentities($file, ENT_QUOTES, 'UTF-8').'"><img src="' . FileManagerUtility::rawurlencode_path($thumbfile) . $randomImage . '" class="preview" alt="preview" /></a>'; 
    462       } 
    463       catch (Exception $e) 
    464       { 
    465           $tnc = '<a href="'.$encoded_url.'" data-milkbox="preview" title="'.htmlentities($file, ENT_QUOTES, 'UTF-8').'"><img src="' . FileManagerUtility::rawurlencode_path($this->getIcon($dir)).$randomImage . '" class="preview" alt="preview" /></a>'; 
    466       } 
    467       $content .= $tnc; 
    468     // text preview 
    469     } 
    470     elseif (FileManagerUtility::startsWith($mime, 'text/') || $mime == 'application/x-javascript') 
    471     { 
    472       $filecontent = file_get_contents($dir, false, null, 0); 
    473       if (!FileManagerUtility::isBinary($filecontent)) 
    474       { 
    475         $content = '<div class="textpreview"><pre>' . str_replace(array('$', "\t"), array('&#36;', '&nbsp;&nbsp;'), htmlentities($filecontent,ENT_QUOTES,'UTF-8')) . '</pre></div>'; 
    476       } 
    477       // else: fall back to 'no preview available' 
    478     // zip 
    479     } 
    480     elseif ($mime == 'application/zip') 
    481     { 
    482       require_once(MTFM_PATH . '/Assets/getid3/getid3.php'); 
    483  
    484       $out = array(array(), array()); 
    485       $getid3 = new getID3(); 
    486       $getid3->Analyze($dir); 
    487       foreach ($getid3->info['zip']['files'] as $name => $size) 
    488       { 
    489         $isdir = is_array($size) ? true : false; 
    490         $out[($isdir) ? 0 : 1][$name] = '<li><a><img src="'.FileManagerUtility::rawurlencode_path($this->getIcon($dir,true)).'" alt="" /> ' . $name . '</a></li>'; 
    491       } 
    492       natcasesort($out[0]); 
    493       natcasesort($out[1]); 
    494       $content = '<ul>' . implode(array_merge($out[0], $out[1])) . '</ul>'; 
    495     // swf 
    496     } 
    497     elseif ($mime == 'application/x-shockwave-flash') 
    498     { 
    499       require_once(MTFM_PATH . '/Assets/getid3/getid3.php'); 
    500       $getid3 = new getID3(); 
    501       $getid3->Analyze($dir); 
    502  
    503       $content = '<dl> 
    504           <dt>${width}</dt><dd>' . $getid3->info['swf']['header']['frame_width']/10 . 'px</dd> 
    505           <dt>${height}</dt><dd>' . $getid3->info['swf']['header']['frame_height']/10 . 'px</dd> 
    506           <dt>${length}</dt><dd>' . round(($getid3->info['swf']['header']['length']/$getid3->info['swf']['header']['frame_count'])) . 's</dd> 
    507         </dl> 
    508         <h2>${preview}</h2> 
    509         <div class="object"> 
    510           <object type="application/x-shockwave-flash" data="'.FileManagerUtility::rawurlencode_path($url).'" width="500" height="400"> 
    511             <param name="scale" value="noscale" /> 
    512             <param name="movie" value="'.FileManagerUtility::rawurlencode_path($url).'" /> 
    513           </object> 
    514         </div>'; 
    515     // audio 
    516     } 
    517     elseif (FileManagerUtility::startsWith($mime, 'audio/')) 
    518     { 
    519       require_once(MTFM_PATH . '/Assets/getid3/getid3.php'); 
    520       $getid3 = new getID3(); 
    521       $getid3->Analyze($dir); 
    522       getid3_lib::CopyTagsToComments($getid3->info); 
    523  
    524       $dewplayer = FileManagerUtility::rawurlencode_path($this->options['assetBasePath'] . 'dewplayer.swf'); 
    525       $content = '<dl> 
    526           <dt>${title}</dt><dd>' . $getid3->info['comments']['title'][0] . '</dd> 
    527           <dt>${artist}</dt><dd>' . $getid3->info['comments']['artist'][0] . '</dd> 
    528           <dt>${album}</dt><dd>' . $getid3->info['comments']['album'][0] . '</dd> 
    529           <dt>${length}</dt><dd>' . $getid3->info['playtime_string'] . '</dd> 
    530           <dt>${bitrate}</dt><dd>' . round($getid3->info['bitrate']/1000) . 'kbps</dd> 
    531         </dl> 
    532         <h2>${preview}</h2> 
    533         <div class="object"> 
    534           <object type="application/x-shockwave-flash" data="' . $dewplayer . '" width="200" height="20" id="dewplayer" name="dewplayer"> 
    535             <param name="wmode" value="transparent" /> 
    536             <param name="movie" value="' . $dewplayer . '" /> 
    537             <param name="flashvars" value="mp3=' . FileManagerUtility::rawurlencode_path($url) . '&amp;volume=50&amp;showtime=1" /> 
    538           </object> 
    539         </div>'; 
    540     } 
    541     // else: fall back to 'no preview available' 
    542  
    543     echo json_encode(array_merge(array( 
    544       'status' => 1, 
    545       'content' => $content ? $content : '<div class="margin"> 
    546         ${nopreview} 
    547       </div>'                 //<br/><button value="' . $url . '">${download}</button> 
    548     ), $extra_return_detail)); 
    549     } 
    550     catch(FileManagerException $e) 
    551     { 
    552         $emsg = explode(':', $e->getMessage(), 2); 
    553         echo json_encode(array( 
    554                 'status' => 0, 
    555                 'content' => '<div class="margin"> 
    556                   ${nopreview} 
    557                   <div class="failure_notice"> 
    558                     <h3>${error}</h3> 
    559                     <p>mem usage: ' . number_format(memory_get_usage() / 1E6, 2) . ' MB : ' . number_format(memory_get_peak_usage() / 1E6, 2) . ' MB</p> 
    560                     <p>${backend.' . $emsg[0] . '}' . (isset($emsg[1]) ? $emsg[1] : '') . '</p> 
    561                   </div> 
    562                 </div>'       // <br/><button value="' . $url . '">${download}</button> 
    563             )); 
    564     } 
    565     catch(Exception $e) 
    566     { 
    567         // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
    568         echo json_encode(array( 
    569                 'status' => 0, 
    570                 'content' => '<div class="margin"> 
    571                   ${nopreview} 
    572                   <div class="failure_notice"> 
    573                     <h3>${error}</h3> 
    574                     <p>mem usage: ' . number_format(memory_get_usage() / 1E6, 2) . ' MB : ' . number_format(memory_get_peak_usage() / 1E6, 2) . ' MB</p> 
    575                     <p>' . $e->getMessage() . '</p> 
    576                   </div> 
    577                 </div>'       // <br/><button value="' . $url . '">${download}</button> 
    578             )); 
    579     } 
    580   } 
    581  
    582   /** 
    583    * Process the 'destroy' event 
    584    * 
    585    * Delete the specified file or directory and return a JSON encoded status of success 
    586    * or failure. 
    587    * 
    588    * Note that when images are deleted, so are their thumbnails. 
    589    * 
    590    * Expected parameters: 
    591    * 
    592    * $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
    593    * 
    594    * $_POST['file']          filename (including extension, of course) of the file to 
    595    *                         be detailed. 
    596    * 
    597    * Errors will produce a JSON encoded error report, including at least two fields: 
    598    * 
    599    * status                  0 for error; nonzero for success 
    600    * 
    601    * error                   error message 
    602    */ 
    603   protected function onDestroy() 
    604   { 
    605     try 
    606     { 
    607         if (!$this->options['destroy']) 
    608             throw new FileManagerException('disabled'); 
    609         if (empty($this->post['file'])) 
    610             throw new FileManagerException('nofile'); 
    611  
    612         $dir = $this->getDir(!empty($this->post['directory']) ? $this->post['directory'] : null); 
    613         $file = pathinfo($this->post['file'], PATHINFO_BASENAME); 
    614  
    615         $fileinfo = array( 
    616             'dir' => $dir, 
    617             'file' => $file 
    618         ); 
    619  
    620         if (!$this->checkFile($dir . $file)) 
    621             throw new FileManagerException('nofile'); 
    622  
    623         if (!empty($this->options['DestroyIsAuthorized_cb']) && function_exists($this->options['DestroyIsAuthorized_cb']) && !$this->options['DestroyIsAuthorized_cb']($this, 'destroy', $fileinfo)) 
    624             throw new FileManagerException('authorized'); 
    625  
    626         if (!$this->unlink($dir . $file)) 
    627             throw new FileManagerException('unlink_failed:' . $dir . $file); 
    628  
    629         echo json_encode(array( 
    630           'status' => 1, 
    631           'content' => 'destroyed' 
    632         )); 
    633     } 
    634     catch(FileManagerException $e) 
    635     { 
    636         $emsg = explode(':', $e->getMessage(), 2); 
    637         echo json_encode(array( 
    638                 'status' => 0, 
    639                 'error' => '${backend.' . $emsg[0] . '}' . (isset($emsg[1]) ? $emsg[1] : '') 
    640             )); 
    641     } 
    642     catch(Exception $e) 
    643     { 
    644         // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
    645         echo json_encode(array( 
    646                 'status' => 0, 
    647                 'error' => $e->getMessage() 
    648             )); 
    649     } 
    650   } 
    651  
    652   /** 
    653    * Process the 'create' event 
    654    * 
    655    * Create the specified subdirectory and give it the configured permissions 
    656    * (options['chmod'], default 0777) and return a JSON encoded status of success 
    657    * or failure. 
    658    * 
    659    * Expected parameters: 
    660    * 
    661    * $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
    662    * 
    663    * $_POST['file']          name of the subdirectory to be created 
    664    * 
    665    * Extra input parameters considered while producing the JSON encoded directory view. 
    666    * This may not seem relevant for an empty directory, but these parameters are also 
    667    * considered when providing the fallback directory view in case an error occurred 
    668    * and then the listed directory (either the parent or the basedir itself) may very 
    669    * likely not be empty! 
    670    * 
    671    * $_POST['filter']        optional mimetype filter string, amy be the part up to and 
    672    *                         including the slash '/' or the full mimetype. Only files 
    673    *                         matching this (set of) mimetypes will be listed. 
    674    *                         Examples: 'image/' or 'application/zip' 
    675    * 
    676    * $_POST['type']          'thumb' will produce a list view including thumbnail and other 
    677    *                         information with each listed file; other values will produce 
    678    *                         a basic list view (similar to Windows Explorer 'list' view). 
    679    * 
    680    * Errors will produce a JSON encoded error report, including at least two fields: 
    681    * 
    682    * status                  0 for error; nonzero for success 
    683    * 
    684    * error                   error message 
    685    */ 
    686   protected function onCreate() 
    687   { 
    688     try 
    689     { 
    690         $mime_filter = ((isset($_POST['filter']) && !empty($_POST['filter'])) ? $_POST['filter'].'/' : null); 
    691         $list_type = ((isset($_POST['type']) && $_POST['type'] == 'list') ? 'list' : 'thumb'); 
    692  
    693         if (!$this->options['create']) 
    694             throw new FileManagerException('disabled'); 
    695         if (empty($this->post['file'])) 
    696             throw new FileManagerException('nofile'); 
    697  
    698         $dir = $this->getDir(!empty($this->post['directory']) ? $this->post['directory'] : null); 
    699         $file = $this->getName(array('filename' => $this->post['file']), $dir);  // a directory has no 'extension'! 
    700         if (!$file) 
    701             throw new FileManagerException('nofile'); 
    702  
    703         $fileinfo = array( 
    704             'dir' => $dir, 
    705             'file' => $file, 
    706             'chmod' => $this->options['chmod'] 
    707         ); 
    708         if (!empty($this->options['CreateIsAuthorized_cb']) && function_exists($this->options['CreateIsAuthorized_cb']) && !$this->options['CreateIsAuthorized_cb']($this, 'create', $fileinfo)) 
    709             throw new FileManagerException('authorized'); 
    710  
    711         if (!@mkdir($file, $fileinfo['chmod'])) 
    712             throw new FileManagerException('mkdir_failed:' . $file); 
    713  
    714         // success, now show the new directory as a list view: 
    715         $jsok = array( 
    716                 'status' => 1 
    717             ); 
    718         $rv = $this->_onView($file . '/', $jsok, $mime_filter, $list_type); 
    719         echo json_encode($rv); 
    720     } 
    721     catch(FileManagerException $e) 
    722     { 
    723         $emsg = explode(':', $e->getMessage(), 2); 
    724         $jserr = array( 
    725                 'status' => 0, 
    726                 'error' => '${backend.' . $emsg[0] . '}' . (isset($emsg[1]) ? $emsg[1] : '') 
    727             ); 
    728         // and fall back to showing the PARENT directory 
    729         try 
    730         { 
    731             $rv = $this->_onView($dir, $jserr, $mime_filter, $list_type); 
    732             echo json_encode($rv); 
    733         } 
    734         catch (Exception $e) 
    735         { 
    736             // and fall back to showing the BASEDIR directory 
    737             try 
    738             { 
    739                 $dir = $this->getDir(); 
    740                 $rv = $this->_onView($dir, $jserr, $mime_filter, $list_type); 
    741                 echo json_encode($rv); 
    742             } 
    743             catch (Exception $e) 
    744             { 
    745                 // when we fail here, it's pretty darn bad and nothing to it. 
    746                 // just push the error JSON as go. 
    747                 echo json_encode($jserr); 
    748             } 
    749         } 
    750     } 
    751     catch(Exception $e) 
    752     { 
    753         // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
    754         $jserr = array( 
    755                 'status' => 0, 
    756                 'error' => $e->getMessage() 
    757             ); 
    758         // and fall back to showing the PARENT directory 
    759         try 
    760         { 
    761             $rv = $this->_onView($dir, $jserr, $mime_filter, $list_type); 
    762             echo json_encode($rv); 
    763         } 
    764         catch (Exception $e) 
    765         { 
    766             // and fall back to showing the BASEDIR directory 
    767             try 
    768             { 
    769                 $dir = $this->getDir(); 
    770                 $rv = $this->_onView($dir, $jserr, $mime_filter, $list_type); 
    771                 echo json_encode($rv); 
    772             } 
    773             catch (Exception $e) 
    774             { 
    775                 // when we fail here, it's pretty darn bad and nothing to it. 
    776                 // just push the error JSON as go. 
    777                 echo json_encode($jserr); 
    778             } 
    779         } 
    780     } 
    781   } 
    782  
    783   /** 
    784    * Process the 'download' event 
    785    * 
    786    * Send the file content of the specified file for download by the client. 
    787    * Only files residing within the directory tree rooted by the 
    788    * 'basedir' (options['directory']) will be allowed to be downloaded. 
    789    * 
    790    * Expected parameters: 
    791    * 
    792    * $_GET['file']          filepath of the file to be downloaded 
    793    * 
    794    * On errors a HTTP 403 error response will be sent instead. 
    795    */ 
    796   protected function onDownload() 
    797   { 
    798     try 
    799     { 
    800         if (!$this->options['download']) 
    801             throw new FileManagerException('disabled'); 
    802         if (empty($_GET['file'])) 
    803             throw new FileManagerException('nofile'); 
    804         // no need to check explicitly for '../' and './' here as getDir() will take care of it all! 
    805  
    806         // change the path to fit your websites document structure 
    807         $path = $this->getDir($_GET['file'], 0, false, false); 
    808         if (!is_file($path)) 
    809             throw new FileManagerException('nofile'); 
    810  
    811         $fileinfo = array( 
    812             'file' => $path 
    813         ); 
    814         if (!empty($this->options['DownloadIsAuthorized_cb']) && function_exists($this->options['DownloadIsAuthorized_cb']) && !$this->options['DownloadIsAuthorized_cb']($this, 'download', $fileinfo)) 
    815             throw new FileManagerException('authorized'); 
    816  
    817         if ($fd = fopen($path, "r")) 
    818         { 
    819             $fsize = filesize($path); 
    820             $path_parts = pathinfo($path); 
    821             $ext = strtolower($path_parts["extension"]); 
    822             switch ($ext) 
    823             { 
    824             case "pdf": 
    825                 header('Content-type: application/pdf'); 
    826                 header('Content-Disposition: attachment; filename="' . $path_parts["basename"] . '"'); // use 'attachment' to force a download 
    827                 break; 
    828  
    829              // add here more headers for diff. extensions 
    830  
    831             default; 
    832                 header('Content-type: application/octet-stream'); 
    833                 header('Content-Disposition: filename="' . $path_parts["basename"] . '"'); 
    834             } 
    835             header("Content-length: $fsize"); 
    836             header("Cache-control: private"); //use this to open files directly 
    837  
    838             fpassthru($fd); 
    839             fclose($fd); 
    840         } 
    841     } 
    842     catch(FileManagerException $e) 
    843     { 
    844         // we don't care whether it's a 404, a 403 or something else entirely: we feed 'em a 403 and that's final! 
    845         if (function_exists('send_response_status_header')) 
    846         { 
    847             send_response_status_header(403); 
    848             echo $e->getMessage(); 
    849         } 
    850         else 
    851         { 
    852             // no smarties detection whether we're running on fcgi or bare iron, we assume the latter: 
    853             header('HTTP/1.0 403 Forbidden', true, 403); 
    854             echo $e->getMessage(); 
    855         } 
    856     } 
    857     catch(Exception $e) 
    858     { 
    859         // we don't care whether it's a 404, a 403 or something else entirely: we feed 'em a 403 and that's final! 
    860         if (function_exists('send_response_status_header')) 
    861         { 
    862             send_response_status_header(403); 
    863             echo $e->getMessage(); 
    864         } 
    865         else 
    866         { 
    867             // no smarties detection whether we're running on fcgi or bare iron, we assume the latter: 
    868             header('HTTP/1.0 403 Forbidden', true, 403); 
    869             echo $e->getMessage(); 
    870         } 
    871     } 
    872   } 
    873  
    874   /** 
    875    * Process the 'upload' event 
    876    * 
    877    * Process and store the uploaded file in the designated location. 
    878    * Images will be resized when possible and applicable. A thumbnail image will also 
    879    * be preproduced when possible. 
    880    * Return a JSON encoded status of success or failure. 
    881    * 
    882    * Expected parameters: 
    883    * 
    884    * $_GET['directory']     path relative to basedir a.k.a. options['directory'] root 
    885    * 
    886    * $_FILES[]              the metadata for the uploaded file 
    887    * 
    888    * Errors will produce a JSON encoded error report, including at least two fields: 
    889    * 
    890    * status                  0 for error; nonzero for success 
    891    * 
    892    * error                   error message 
    893    */ 
    894   protected function onUpload() 
    895   { 
    896     try 
    897     { 
    898       if (!$this->options['upload']) 
    899         throw new FileManagerException('disabled'); 
    900       if (!Upload::exists('Filedata')) 
    901         throw new FileManagerException('nofile'); 
    902  
    903       $dir = $this->getDir(!empty($this->get['directory']) ? $this->get['directory'] : null); 
    904       $file = $this->getName($_FILES['Filedata']['name'], $dir); 
    905       if (!$file) 
    906         throw new FileManagerException('nofile'); 
    907       $fi = pathinfo($file); 
    908       if (!$fi['filename']) 
    909         throw new FileManagerException('nofile'); 
    910  
    911       /* 
    912       Security: 
    913  
    914       Upload::move() processes the unfiltered version of $_FILES[]['name'], at least to get the extension, 
    915       unless we ALWAYS override the filename and extension in the options array below. That's why we 
    916       calculate the extension at all times here. 
    917       */ 
    918       if (!is_string($fi['extension']) || strlen($fi['extension']) == 0) // can't use 'empty()' as "0" is a valid extension itself. 
    919       { 
    920         //enforce a mandatory extension, even when there isn't one (due to filtering or original input producing none) 
    921         $fi['extension'] = 'txt'; 
    922       } 
    923       else if ($this->options['safe'] && in_array(strtolower($fi['extension']), array('exe', 'dll', 'com', 'php', 'php3', 'php4', 'php5', 'phps'))) 
    924       { 
    925         $fi['extension'] = 'txt'; 
    926       } 
    927  
    928       $fileinfo = array( 
    929         'dir' => $dir, 
    930         'name' => $fi['filename'], 
    931         'extension' => $fi['extension'], 
    932         'size' => $_FILES['Filedata']['size'], 
    933         'maxsize' => $this->options['maxUploadSize'], 
    934         'mimes' => $this->getAllowedMimeTypes(), 
    935         'ext2mime_map' => $this->getMimeTypeDefinitions(), 
    936         'chmod' => $this->options['chmod'] & 0666   // security: never make those files 'executable'! 
    937       ); 
    938       if (!empty($this->options['UploadIsAuthorized_cb']) && function_exists($this->options['UploadIsAuthorized_cb']) && !$this->options['UploadIsAuthorized_cb']($this, 'upload', $fileinfo)) 
    939         throw new FileManagerException('authorized'); 
    940  
    941       $file = Upload::move('Filedata', $dir, $fileinfo); 
    942       $file = self::normalize($file); 
    943  
    944       /* 
    945        * NOTE: you /can/ (and should be able to, IMHO) upload 'overly large' image files to your site, but the thumbnailing process step 
    946        *       happening here will fail; we have memory usage estimators in place to make the fatal crash a non-silent one, i,e, one 
    947        *       where we still have a very high probability of NOT fatally crashing the PHP iunterpreter but catching a suitable exception 
    948        *       instead. 
    949        *       Having uploaded such huge images, a developer/somebody can always go in later and up the memory limit if the site admins 
    950        *       feel it is deserved. Until then, no thumbnails of such images (though you /should/ be able to milkbox-view the real thing!) 
    951        */ 
    952       if (FileManagerUtility::startsWith($this->getMimeType($file), 'image/') && !empty($this->get['resize'])) 
    953       { 
    954         $img = new Image($file); 
    955         $size = $img->getSize(); 
    956         // Xinha: We have separate width/height max dimensions, if these fail, the fallback of the 
    957         //   maxImageSize is used, which we set to some very high number by default for back compat 
    958         if ($size['width'] > $this->options['suggestedMaxImageDimension']['width'])  
    959           $img->resize( $this->options['suggestedMaxImageDimension']['width'])->save(); 
    960         elseif ($size['height'] > $this->options['suggestedMaxImageDimension']['height'])  
    961           $img->resize(null, $this->options['suggestedMaxImageDimension']['height'])->save();         
    962         else 
    963         // Image::resize() takes care to maintain the proper aspect ratio, so this is easy: 
    964         if ($size['width'] > $this->options['maxImageSize'] || $size['height'] > $this->options['maxImageSize']) 
    965           $img->resize($this->options['maxImageSize'], $this->options['maxImageSize'])->save(); 
    966         unset($img); 
    967       } 
    968  
    969       echo json_encode(array( 
    970         'status' => 1, 
    971         'name' => pathinfo($file, PATHINFO_BASENAME) 
    972       )); 
    973     } 
    974     catch(UploadException $e) 
    975     { 
    976       echo json_encode(array( 
    977         'status' => 0, 
    978         'error' => class_exists('ValidatorException') ? strip_tags($e->getMessage()) : '${backend.' . $e->getMessage() . '}' // This is for Styx :) 
    979       )); 
    980     } 
    981     catch(FileManagerException $e) 
    982     { 
    983         $emsg = explode(':', $e->getMessage(), 2); 
    984         echo json_encode(array( 
    985                 'status' => 0, 
    986                 'error' => '${backend.' . $emsg[0] . '}' . (isset($emsg[1]) ? $emsg[1] : '') 
    987             )); 
    988     } 
    989     catch(Exception $e) 
    990     { 
    991       // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
    992       echo json_encode(array( 
    993         'status' => 0, 
    994         'error' => $e->getMessage() 
    995       )); 
    996     } 
    997   } 
    998  
    999   /** 
    1000    * Process the 'move' event (with is used by both move/copy and rename client side actions) 
    1001    * 
    1002    * Copy or move/rename a given file or directory and return a JSON encoded status of success 
    1003    * or failure. 
    1004    * 
    1005    * Expected parameters: 
    1006    * 
    1007    * $_POST['copy']            nonzero value means copy, zero or nil for move/rename 
    1008    * 
    1009    * Source filespec: 
    1010    * 
    1011    *   $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
    1012    * 
    1013    *   $_POST['file']          original name of the file/subdirectory to be renamed/copied 
    1014    * 
    1015    * Destination filespec: 
    1016    * 
    1017    *   $_POST['newDirectory']  path relative to basedir a.k.a. options['directory'] root; 
    1018    *                           target directory where the file must be moved / copied 
    1019    * 
    1020    *   $_POST['name']          target name of the file/subdirectory to be renamed 
    1021    * 
    1022    * Errors will produce a JSON encoded error report, including at least two fields: 
    1023    * 
    1024    * status                    0 for error; nonzero for success 
    1025    * 
    1026    * error                     error message 
    1027    */ 
    1028   protected function onMove() 
    1029   { 
    1030     try 
    1031     { 
    1032         if (!$this->options['move']) 
    1033             throw new FileManagerException('disabled'); 
    1034         if (empty($this->post['file'])) 
    1035             throw new FileManagerException('nofile'); 
    1036  
    1037         $rename = empty($this->post['newDirectory']) && !empty($this->post['name']); 
    1038         $is_copy = (!empty($this->post['copy'])  && $this->post['copy']); 
    1039         $dir = $this->getDir(!empty($this->post['directory']) ? $this->post['directory'] : null); 
    1040         $file = pathinfo($this->post['file'], PATHINFO_BASENAME); 
    1041  
    1042         $is_dir = is_dir($dir . $file); 
    1043  
    1044         // note: we do not support copying entire directories, though directory rename/move is okay 
    1045         if (!$this->checkFile($dir . $file) || ($is_copy && $is_dir)) 
    1046             throw new FileManagerException('nofile'); 
    1047  
    1048         if($rename) 
    1049         { 
    1050             $fn = 'rename'; 
    1051             $newdir = null; 
    1052             if ($is_dir) 
    1053                 $newname = $this->getName(array('filename' => $this->post['name']), $dir);  // a directory has no 'extension' 
    1054             else 
    1055                 $newname = $this->getName($this->post['name'], $dir); 
    1056  
    1057             // when the new name seems to have a different extension, make sure the extension doesn't change after all: 
    1058             // Note: - if it's only 'case' we're changing here, then exchange the extension instead of appending it. 
    1059             //       - directories do not have extensions 
    1060             $extOld = pathinfo($file, PATHINFO_EXTENSION); 
    1061             $extNew = pathinfo($newname, PATHINFO_EXTENSION); 
    1062             if ((!$this->options['allowExtChange'] || (!$is_dir && empty($extNew))) && !empty($extOld) && strtolower($extOld) != strtolower($extNew)) 
    1063             { 
    1064                 $newname .= '.' . $extOld; 
    1065             } 
    1066         } 
    1067         else 
    1068         { 
    1069             $fn = ($is_copy ? 'copy' : 'rename' /* 'move' */); 
    1070             $newdir = $this->getDir(!empty($this->post['newDirectory']) ? $this->post['newDirectory'] : null); 
    1071             $newname = $this->getName($file, $newdir); 
    1072         } 
    1073  
    1074         if (!$newname) 
    1075             throw new FileManagerException('nonewfile'); 
    1076  
    1077         $fileinfo = array( 
    1078             'dir' => $dir, 
    1079             'file' => $file, 
    1080             'newdir' => $newdir, 
    1081             'newname' => $newname, 
    1082             'rename' => $rename, 
    1083             'is_dir' => $is_dir, 
    1084             'function' => $fn 
    1085         ); 
    1086  
    1087         if (!empty($this->options['MoveIsAuthorized_cb']) && function_exists($this->options['MoveIsAuthorized_cb']) && !$this->options['MoveIsAuthorized_cb']($this, 'move', $fileinfo)) 
    1088             throw new FileManagerException('authorized'); 
    1089  
    1090         if($rename) 
    1091         { 
    1092             // try to remove the thumbnail related to the original file; don't mind if it doesn't exist 
    1093             if(!$is_dir) 
    1094             { 
    1095                 if (!$this->deleteThumb($dir . $file)) 
    1096                     throw new FileManagerException('delete_thumbnail_failed'); 
    1097             } 
    1098         } 
    1099  
    1100         if (!@$fn($dir . $file, $newname)) 
    1101             throw new FileManagerException($fn . '_failed:' . $dir . $file . ':' . $newname); 
    1102  
    1103         echo json_encode(array( 
    1104             'status' => 1, 
    1105             'name' => pathinfo($newname, PATHINFO_BASENAME) 
    1106         )); 
    1107     } 
    1108     catch(FileManagerException $e) 
    1109     { 
    1110         $emsg = explode(':', $e->getMessage(), 2); 
    1111         echo json_encode(array( 
    1112                 'status' => 0, 
    1113                 'error' => '${backend.' . $emsg[0] . '}' . (isset($emsg[1]) ? $emsg[1] : '') 
    1114             )); 
    1115     } 
    1116     catch(Exception $e) 
    1117     { 
    1118         // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
    1119         echo json_encode(array( 
    1120                 'status' => 0, 
    1121                 'error' => $e->getMessage() 
    1122             )); 
    1123     } 
    1124   } 
    1125  
    1126  
    1127  
    1128   /** 
    1129    * Delete a file or directory, inclusing subdirectories and files. 
    1130    * 
    1131    * Return TRUE on success, FALSE when an error occurred. 
    1132    * 
    1133    * Note that the routine will try to percevere and keep deleting other subdirectories 
    1134    * and files, even when an error occurred for one or more of the subitems: this is 
    1135    * a best effort policy. 
    1136    */ 
    1137   protected function unlink($file) 
    1138   { 
    1139     if (!$file || !FileManagerUtility::startsWith($file, $this->basedir)) 
    1140         return false; 
    1141  
    1142     $rv = true; 
    1143     if(is_dir($file)) 
    1144     { 
    1145       $files = glob($file . '/*'); 
    1146       if (is_array($files)) 
    1147         foreach ($files as $f) 
    1148         { 
    1149           $rv &= $this->unlink($f); 
    1150           $rv &= $this->deleteThumb($f); 
    1151         } 
    1152  
    1153       $rv &= @rmdir($file); 
    1154     } 
    1155     else 
    1156     { 
    1157       if (file_exists($file)) 
    1158       { 
    1159         $rv &= @unlink($file); 
    1160         $rv &= $this->deleteThumb($file); 
    1161       } 
    1162     } 
    1163     return $rv; 
    1164   } 
    1165  
    1166   /** 
    1167    * Make a cleaned-up, unique filename 
    1168    * 
    1169    * Return the file (dir + name + ext), or a unique, yet non-existing, variant thereof, where the filename 
    1170    * is appended with a '_' and a number, e.g. '_1', when the file itself already exists in the given 
    1171    * directory. The directory part of the returned value equals $dir. 
    1172    * 
    1173    * Return NULL when $file is empty or when the specified directory does not reside within the 
    1174    * directory tree rooted by options['directory'] 
    1175    * 
    1176    * Note that the given filename will be converted to a legal filename, containing a filesystem-legal 
    1177    * subset of ASCII characters only, before being used and returned by this function. 
    1178    * 
    1179    * @param mixed $fileinfo     either a string containing a filename+ext or an array as produced by pathinfo(). 
    1180    * @daram string $dir         path pointing at where the given file may exist. 
    1181    * 
    1182    * @return a filepath consisting of $dir and the cleaned up and possibly sequenced filename and file extension 
    1183    *         as provided by $fileinfo. 
    1184    */ 
    1185   protected function getName($fileinfo, $dir) 
    1186   { 
    1187     if (!FileManagerUtility::endsWith($dir, '/')) $dir .= '/'; 
    1188  
    1189     if (is_string($fileinfo)) 
    1190     { 
    1191         $fileinfo = pathinfo($fileinfo); 
    1192     } 
    1193  
    1194     if (!is_array($fileinfo) || !$fileinfo['filename'] || !FileManagerUtility::startsWith($dir, $this->basedir)) return null; 
    1195  
    1196  
    1197     /* 
    1198      * since 'pagetitle()' is used to produce a unique, non-existing filename, we can forego the dirscan 
    1199      * and simply check whether the constructed filename/path exists or not and bump the suffix number 
    1200      * by 1 until it does not, thus quickly producing a unique filename. 
    1201      * 
    1202      * This is faster than using a dirscan to collect a set of existing filenames and feeding them as 
    1203      * an option array to pagetitle(), particularly for large directories. 
    1204      */ 
    1205     $filename = FileManagerUtility::pagetitle($fileinfo['filename'], null, '-_., []()~!@+' /* . '#&' */, '-_,~@+#&'); 
    1206     if (!$filename) 
    1207         return null; 
    1208  
    1209     // also clean up the extension: only allow alphanumerics in there! 
    1210     $ext = FileManagerUtility::pagetitle(!empty($fileinfo['extension']) ? $fileinfo['extension'] : null); 
    1211     $ext = (!empty($ext) ? '.' . $ext : null); 
    1212     // make sure the generated filename is SAFE: 
    1213     $file = $dir . $filename . $ext; 
    1214     if (file_exists($file)) 
    1215     { 
    1216         /* 
    1217          * make a unique name. Do this by postfixing the filename with '_X' where X is a sequential number. 
    1218          * 
    1219          * Note that when the input name is already such a 'sequenced' name, the sequence number is 
    1220          * extracted from it and sequencing continues from there, hence input 'file_5' would, if it already 
    1221          * existed, thus be bumped up to become 'file_6' and so on, until a filename is found which 
    1222          * does not yet exist in the designated directory. 
    1223          */ 
    1224         $i = 1; 
    1225         if (preg_match('/^(.*)_([1-9][0-9]*)$/', $filename, $matches)) 
    1226         { 
    1227             $i = intval($matches[2]); 
    1228             if ('P'.$i != 'P'.$matches[2] || $i > 100000) 
    1229             { 
    1230                 // very large number: not a sequence number! 
    1231                 $i = 1; 
    1232             } 
    1233             else 
    1234             { 
    1235                 $filename = $matches[1]; 
    1236             } 
    1237         } 
    1238         do 
    1239         { 
    1240             $file = $dir . $filename . ($i ? '_' . $i : '') . $ext; 
    1241             $i++; 
    1242         } while (file_exists($file)); 
    1243     } 
    1244  
    1245     // $file is now guaranteed to NOT exist 
    1246     return $file; 
    1247   } 
    1248  
    1249   protected function getIcon($file, $smallIcon = false) 
    1250   { 
    1251     if (FileManagerUtility::endsWith($file, '/..')) $ext = 'dir_up'; 
    1252     elseif (is_dir($file)) $ext = 'dir'; 
    1253     else $ext = pathinfo($file, PATHINFO_EXTENSION); 
    1254  
    1255     $largeDir = ($smallIcon === false ? 'Large/' : ''); 
    1256     $path = (is_file(FileManagerUtility::getSiteRoot() . $this->options['assetBasePath'] . 'Images/Icons/' .$largeDir.$ext.'.png')) 
    1257       ? $this->options['assetBasePath'] . 'Images/Icons/'.$largeDir.$ext.'.png' 
    1258       : $this->options['assetBasePath'] . 'Images/Icons/'.$largeDir.'default.png'; 
    1259  
    1260     return $path; 
    1261   } 
    1262  
    1263   protected function getThumb($file) 
    1264   { 
    1265     $thumb = $this->generateThumbName($file); 
    1266     $thumbPath = FileManagerUtility::getSiteRoot() . $this->options['thumbnailPath'] . $thumb; 
    1267     if (is_file($thumbPath)) 
    1268       return $thumb; 
    1269     elseif(is_file(FileManagerUtility::getSiteRoot() . $this->options['thumbnailPath'] . basename($file))) 
    1270       return basename($file); 
    1271     else 
    1272       return $this->generateThumb($file,$thumbPath); 
    1273   } 
    1274  
    1275   protected function generateThumbName($file) 
    1276   { 
    1277     return 'thumb_'.md5($file).'_'.str_replace('.','_',basename($file)).'.png'; 
    1278   } 
    1279  
    1280   protected function generateThumb($file,$thumbPath) 
    1281   { 
    1282     $img = new Image($file); 
    1283     $img->resize(250,250,true,false)->process('png',$thumbPath); // TODO: save as lossy / lower-Q jpeg to reduce filesize? 
    1284     unset($img); 
    1285     return basename($thumbPath); 
    1286   } 
    1287  
    1288   protected function deleteThumb($file) 
    1289   { 
    1290     $thumb = $this->generateThumbName($file); 
    1291     $thumbPath = FileManagerUtility::getSiteRoot() . $this->options['thumbnailPath'] . $thumb; 
    1292     if(is_file($thumbPath)) 
    1293       return @unlink($thumbPath); 
    1294     return true;   // when thumbnail does not exist, say it is succesfully removed: all that counts is it doesn't exist anymore when we're done here. 
    1295   } 
    1296  
    1297   public function getMimeType($file) 
    1298   { 
    1299     return is_dir($file) ? 'text/directory' : Upload::mime($file, null, $this->getMimeTypeDefinitions()); 
    1300   } 
    1301  
    1302   /** 
    1303    * Produce the absolute path equivalent, filesystem-wise, of the given $dir directory. 
    1304    * 
    1305    * The directory is enforced to sit within the directory tree rooted by options['directory'] 
    1306    * 
    1307    * When the directory does not exist or does not match this restricting criterium, the 
    1308    * basedir path (abs path eqv. to options['directory']) is returned instead. 
    1309    * 
    1310    * In short: getDir() will guarantee the returned path equals the options['directory'] path or 
    1311    *           a subdirectory thereof. The returned path is an absolute path in the filesystem. 
    1312    */ 
    1313   protected function getDir($dir = null, $chmod = 0777, $mkdir_if_notexist = false, $with_trailing_slash = true) 
    1314   { 
    1315     $dir = str_replace('\\','/', $dir); 
    1316     $basedir = $this->basedir; 
    1317     $root = FileManagerUtility::getSiteRoot(); 
    1318     $dir = (!FileManagerUtility::startsWith($dir, '/') ? $basedir : $root) . $dir; 
    1319     $dir = FileManagerUtility::getRealDir($dir, $chmod, $mkdir_if_notexist, $with_trailing_slash); 
    1320     return $this->checkFile($mkdir_if_notexist ? dirname($dir) : $dir) ? $dir : $this->basedir; 
    1321   } 
    1322  
    1323   /** 
    1324    * Identical to getDir() apart from the fact that this method returns a DocumentRoot based abolute one. 
    1325    * 
    1326    * This function assumes the specified path is located within the options['directory'] a.k.a. 
    1327    * 'basedir' based directory tree. 
    1328    */ 
    1329   protected function getPath($dir = null, $chmod = 0777, $mkdir_if_notexist = false, $with_trailing_slash = true) 
    1330   { 
    1331     $path = $this->getDir($dir, $chmod, $mkdir_if_notexist, $with_trailing_slash); 
    1332     $root = FileManagerUtility::getSiteRoot(); 
    1333     $path = str_replace($root,'',$path); 
    1334  
    1335     return $path; 
    1336   } 
    1337  
    1338   /** 
    1339    * Determine whether the specified file or directory is not nil, 
    1340    * exists within the directory tree rooted by options['directory'] and 
    1341    * matches the permitted mimetypes restriction (optional $mime_filter) 
    1342    * 
    1343    * @return TRUE when all criteria are met, FALSE otherwise. 
    1344    */ 
    1345   protected function checkFile($file, $mime_filter = null) 
    1346   { 
    1347     $mimes = $this->getAllowedMimeTypes($mime_filter); 
    1348  
    1349     $hasFilter = ($mime_filter && count($mimes)); 
    1350     if ($hasFilter) array_push($mimes, 'text/directory'); 
    1351     return !empty($file) && FileManagerUtility::startsWith($file, $this->basedir) && file_exists($file) && (!$hasFilter || in_array($this->getMimeType($file), $mimes)); 
    1352   } 
    1353  
    1354   /** 
    1355    * Normalize a path by converting all slashes '/' and/or backslashes '\' and any mix thereof in the 
    1356    * specified path to UNIX/MAC/Win compatible single forward slashes '/'. 
    1357    */ 
    1358   protected static function normalize($file) 
    1359   { 
    1360     return preg_replace('/(\\\|\/)+/', '/', $file); 
    1361   } 
    1362  
    1363   public function getAllowedMimeTypes($mime_filter = null) 
    1364   { 
    1365     $mimeTypes = array(); 
    1366  
    1367     if (!$mime_filter) return null; 
    1368     if (!FileManagerUtility::endsWith($mime_filter, '/')) return array($mime_filter); 
    1369  
    1370     $mimes = $this->getMimeTypeDefinitions(); 
    1371  
    1372     foreach ($mimes as $mime) 
    1373       if (FileManagerUtility::startsWith($mime, $mime_filter)) 
    1374         $mimeTypes[] = $mime; 
    1375  
    1376     return $mimeTypes; 
    1377   } 
    1378  
    1379   public function getMimeTypeDefinitions() 
    1380   { 
    1381     static $mimes; 
    1382  
    1383     if (!$mimes) $mimes = parse_ini_file($this->options['mimeTypesPath']); 
    1384     if (!$mimes) $mimes = array(); // prevent faulty mimetype ini file from b0rking other code sections. 
    1385     return $mimes; 
    1386   } 
     1293 
     1294                        $file = $this->url_path2file_path($img_filepath); 
     1295                        $mime = $this->getMimeType($file); 
     1296                        $fd = fopen($file, 'rb'); 
     1297                        if (!$fd) 
     1298                        { 
     1299                                // when the icon / thumbnail cannot be opened for whatever reason, fall back to the default error image: 
     1300                                $file = $this->url_path2file_path($this->getIcon('is.default-error', $reqd_size <= 16)); 
     1301                                $mime = $this->getMimeType($file); 
     1302                                $fd = fopen($file, 'rb'); 
     1303                                if (!$fd) 
     1304                                        throw new Exception('panic'); 
     1305                        } 
     1306                        $fsize = filesize($file); 
     1307                        if (!empty($mime)) 
     1308                        { 
     1309                                header('Content-Type: ' . $mime); 
     1310                        } 
     1311                        header('Content-Length: ' . $fsize); 
     1312 
     1313                        header("Cache-Control: private"); //use this to open files directly 
     1314 
     1315                        fpassthru($fd); 
     1316                        fclose($fd); 
     1317                        exit(); 
     1318                } 
     1319                catch(Exception $e) 
     1320                { 
     1321                        send_response_status_header(500); 
     1322                        echo 'Cannot produce thumbnail: ' . $emsg . ' :: ' . $img_filepath; 
     1323                } 
     1324        } 
     1325 
     1326 
     1327        /** 
     1328         * Process the 'destroy' event 
     1329         * 
     1330         * Delete the specified file or directory and return a JSON encoded status of success 
     1331         * or failure. 
     1332         * 
     1333         * Note that when images are deleted, so are their thumbnails. 
     1334         * 
     1335         * Expected parameters: 
     1336         * 
     1337         * $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
     1338         * 
     1339         * $_POST['file']          filename (including extension, of course) of the file to 
     1340         *                         be detailed. 
     1341         * 
     1342         * $_POST['filter']        optional mimetype filter string, amy be the part up to and 
     1343         *                         including the slash '/' or the full mimetype. Only files 
     1344         *                         matching this (set of) mimetypes will be listed. 
     1345         *                         Examples: 'image/' or 'application/zip' 
     1346         * 
     1347         * Errors will produce a JSON encoded error report, including at least two fields: 
     1348         * 
     1349         * status                  0 for error; nonzero for success 
     1350         * 
     1351         * error                   error message 
     1352         */ 
     1353        protected function onDestroy() 
     1354        { 
     1355                $emsg = null; 
     1356                $jserr = array( 
     1357                                'status' => 1 
     1358                        ); 
     1359 
     1360                try 
     1361                { 
     1362                        if (!$this->options['destroy']) 
     1363                                throw new FileManagerException('disabled'); 
     1364 
     1365                        $file_arg = $this->getPOSTparam('file'); 
     1366                        if (empty($file_arg)) 
     1367                                throw new FileManagerException('nofile'); 
     1368 
     1369                        $dir_arg = $this->getPOSTparam('directory'); 
     1370                        $legal_url = $this->rel2abs_legal_url_path($dir_arg); 
     1371                        $legal_url = self::enforceTrailingSlash($legal_url); 
     1372 
     1373                        $filename = pathinfo($file_arg, PATHINFO_BASENAME); 
     1374                        $legal_url .= $filename; 
     1375                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     1376                        $file = $this->legal_url_path2file_path($legal_url); 
     1377 
     1378                        if (!file_exists($file)) 
     1379                                throw new FileManagerException('nofile'); 
     1380 
     1381                        $mime_filter = $this->getPOSTparam('filter', $this->options['filter']); 
     1382                        $mime = $this->getMimeType($file); 
     1383                        $mime_filters = $this->getAllowedMimeTypes($mime_filter); 
     1384                        if (is_file($file)) 
     1385                        { 
     1386                                if (!$this->IsAllowedMimeType($mime, $mime_filters)) 
     1387                                        throw new FileManagerException('extension'); 
     1388                        } 
     1389                        else if (!is_dir($file)) 
     1390                        { 
     1391                                throw new FileManagerException('nofile'); 
     1392                        } 
     1393 
     1394                        $fileinfo = array( 
     1395                                        'legal_url' => $legal_url, 
     1396                                        'file' => $file, 
     1397                                        'mime' => $mime, 
     1398                                        'mime_filter' => $mime_filter, 
     1399                                        'mime_filters' => $mime_filters 
     1400                                ); 
     1401 
     1402                        if (!empty($this->options['DestroyIsAuthorized_cb']) && function_exists($this->options['DestroyIsAuthorized_cb']) && !$this->options['DestroyIsAuthorized_cb']($this, 'destroy', $fileinfo)) 
     1403                                throw new FileManagerException('authorized'); 
     1404 
     1405                        $legal_url = $fileinfo['legal_url']; 
     1406                        $file = $fileinfo['file']; 
     1407                        $mime = $fileinfo['mime']; 
     1408                        $mime_filter = $fileinfo['mime_filter']; 
     1409                        $mime_filters = $fileinfo['mime_filters']; 
     1410 
     1411                        if (!$this->unlink($legal_url, $mime_filters)) 
     1412                                throw new FileManagerException('unlink_failed:' . $legal_url); 
     1413 
     1414                        if (!headers_sent()) header('Content-Type: application/json'); 
     1415 
     1416                        echo json_encode(array( 
     1417                                        'status' => 1, 
     1418                                        'content' => 'destroyed' 
     1419                                )); 
     1420                        return; 
     1421                } 
     1422                catch(FileManagerException $e) 
     1423                { 
     1424                        $emsg = $e->getMessage(); 
     1425                } 
     1426                catch(Exception $e) 
     1427                { 
     1428                        // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
     1429                        $emsg = $e->getMessage(); 
     1430                } 
     1431 
     1432                $this->modify_json4exception($jserr, $emsg); 
     1433 
     1434                if (!headers_sent()) header('Content-Type: application/json'); 
     1435 
     1436                // when we fail here, it's pretty darn bad and nothing to it. 
     1437                // just push the error JSON as go. 
     1438                echo json_encode($jserr); 
     1439        } 
     1440 
     1441        /** 
     1442         * Process the 'create' event 
     1443         * 
     1444         * Create the specified subdirectory and give it the configured permissions 
     1445         * (options['chmod'], default 0777) and return a JSON encoded status of success 
     1446         * or failure. 
     1447         * 
     1448         * Expected parameters: 
     1449         * 
     1450         * $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
     1451         * 
     1452         * $_POST['file']          name of the subdirectory to be created 
     1453         * 
     1454         * Extra input parameters considered while producing the JSON encoded directory view. 
     1455         * These may not seem relevant for an empty directory, but these parameters are also 
     1456         * considered when providing the fallback directory view in case an error occurred 
     1457         * and then the listed directory (either the parent or the basedir itself) may very 
     1458         * likely not be empty! 
     1459         * 
     1460         * $_POST['filter']        optional mimetype filter string, amy be the part up to and 
     1461         *                         including the slash '/' or the full mimetype. Only files 
     1462         *                         matching this (set of) mimetypes will be listed. 
     1463         *                         Examples: 'image/' or 'application/zip' 
     1464         * 
     1465         * $_POST['type']          'thumb' will produce a list view including thumbnail and other 
     1466         *                         information with each listed file; other values will produce 
     1467         *                         a basic list view (similar to Windows Explorer 'list' view). 
     1468         * 
     1469         * Errors will produce a JSON encoded error report, including at least two fields: 
     1470         * 
     1471         * status                  0 for error; nonzero for success 
     1472         * 
     1473         * error                   error message 
     1474         */ 
     1475        protected function onCreate() 
     1476        { 
     1477                $emsg = null; 
     1478                $jserr = array( 
     1479                                'status' => 1 
     1480                        ); 
     1481 
     1482                $mime_filter = $this->getPOSTparam('filter', $this->options['filter']); 
     1483                $list_type = ($this->getPOSTparam('type') != 'thumb' ? 'list' : 'thumb'); 
     1484 
     1485                $legal_url = null; 
     1486 
     1487                try 
     1488                { 
     1489                        $dir_arg = $this->getPOSTparam('directory'); 
     1490                        $legal_url = $this->rel2abs_legal_url_path($dir_arg); 
     1491                        $legal_url = self::enforceTrailingSlash($legal_url); 
     1492 
     1493                        if (!$this->options['create']) 
     1494                                throw new FileManagerException('disabled'); 
     1495 
     1496                        $file_arg = $this->getPOSTparam('file'); 
     1497                        if (empty($file_arg)) 
     1498                                throw new FileManagerException('nofile'); 
     1499 
     1500                        $filename = pathinfo($file_arg, PATHINFO_BASENAME); 
     1501                        //$legal_url .= $filename; 
     1502                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     1503                        $dir = $this->legal_url_path2file_path($legal_url); 
     1504 
     1505                        if (!is_dir($dir)) 
     1506                                throw new FileManagerException('nofile'); 
     1507 
     1508                        $file = $this->getUniqueName(array('filename' => $filename), $dir);  // a directory has no 'extension'! 
     1509                        if (!$file) 
     1510                                throw new FileManagerException('nofile'); 
     1511                        $newdir = $this->legal_url_path2file_path($legal_url . $file); 
     1512 
     1513                        $fileinfo = array( 
     1514                                        'legal_url' => $legal_url, 
     1515                                        'dir' => $dir, 
     1516                                        'raw_name' => $filename, 
     1517                                        'uniq_name' => $file, 
     1518                                        'newdir' => $newdir, 
     1519                                        'chmod' => $this->options['chmod'] 
     1520                                ); 
     1521                        if (!empty($this->options['CreateIsAuthorized_cb']) && function_exists($this->options['CreateIsAuthorized_cb']) && !$this->options['CreateIsAuthorized_cb']($this, 'create', $fileinfo)) 
     1522                                throw new FileManagerException('authorized'); 
     1523 
     1524                        $legal_url = $fileinfo['legal_url']; 
     1525                        $dir = $fileinfo['dir']; 
     1526                        $filename = $fileinfo['raw_name']; 
     1527                        $file = $fileinfo['uniq_name']; 
     1528                        $newdir = $fileinfo['newdir']; 
     1529 
     1530                        if (!@mkdir($newdir, $fileinfo['chmod'], true)) 
     1531                                throw new FileManagerException('mkdir_failed:' . $this->legal2abs_url_path($legal_url) . $file); 
     1532 
     1533                        if (!headers_sent()) header('Content-Type: application/json'); 
     1534 
     1535                        // success, now show the new directory as a list view: 
     1536                        $rv = $this->_onView($legal_url . $file . '/', $jserr, $mime_filter, $list_type); 
     1537                        echo json_encode(array_merge($rv['json'], array('files' => array_merge(array(), $rv['dirs'], $rv['files'])))); 
     1538                        return; 
     1539                } 
     1540                catch(FileManagerException $e) 
     1541                { 
     1542                        $emsg = $e->getMessage(); 
     1543 
     1544                        $jserr['status'] = 0; 
     1545 
     1546                        // and fall back to showing the PARENT directory 
     1547                        try 
     1548                        { 
     1549                                $rv = $this->_onView($legal_url, $jserr, $mime_filter, $list_type); 
     1550                                $jserr = array_merge($rv['json'], array('files' => array_merge(array(), $rv['dirs'], $rv['files']))); 
     1551                        } 
     1552                        catch (Exception $e) 
     1553                        { 
     1554                                // and fall back to showing the BASEDIR directory 
     1555                                try 
     1556                                { 
     1557                                        $legal_url = $this->options['directory']; 
     1558                                        $rv = $this->_onView($legal_url, $jserr, $mime_filter, $list_type); 
     1559                                        $jserr = array_merge($rv['json'], array('files' => array_merge(array(), $rv['dirs'], $rv['files']))); 
     1560                                } 
     1561                                catch (Exception $e) 
     1562                                { 
     1563                                        // when we fail here, it's pretty darn bad and nothing to it. 
     1564                                        // just push the error JSON as go. 
     1565                                } 
     1566                        } 
     1567                } 
     1568                catch(Exception $e) 
     1569                { 
     1570                        // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
     1571                        $emsg = $e->getMessage(); 
     1572 
     1573                        $jserr['status'] = 0; 
     1574 
     1575                        // and fall back to showing the PARENT directory 
     1576                        try 
     1577                        { 
     1578                                $rv = $this->_onView($legal_url, $jserr, $mime_filter, $list_type); 
     1579                                $jserr = array_merge($rv['json'], array('files' => array_merge(array(), $rv['dirs'], $rv['files']))); 
     1580                        } 
     1581                        catch (Exception $e) 
     1582                        { 
     1583                                // and fall back to showing the BASEDIR directory 
     1584                                try 
     1585                                { 
     1586                                        $legal_url = $this->options['directory']; 
     1587                                        $rv = $this->_onView($legal_url, $jserr, $mime_filter, $list_type); 
     1588                                        $jserr = array_merge($rv['json'], array('files' => array_merge(array(), $rv['dirs'], $rv['files']))); 
     1589                                } 
     1590                                catch (Exception $e) 
     1591                                { 
     1592                                        // when we fail here, it's pretty darn bad and nothing to it. 
     1593                                        // just push the error JSON as go. 
     1594                                } 
     1595                        } 
     1596                } 
     1597 
     1598                $this->modify_json4exception($jserr, $emsg); 
     1599 
     1600                if (!headers_sent()) header('Content-Type: application/json'); 
     1601 
     1602                // when we fail here, it's pretty darn bad and nothing to it. 
     1603                // just push the error JSON as go. 
     1604                echo json_encode($jserr); 
     1605        } 
     1606 
     1607        /** 
     1608         * Process the 'download' event 
     1609         * 
     1610         * Send the file content of the specified file for download by the client. 
     1611         * Only files residing within the directory tree rooted by the 
     1612         * 'basedir' (options['directory']) will be allowed to be downloaded. 
     1613         * 
     1614         * Expected parameters: 
     1615         * 
     1616         * $_GET['file']          filepath of the file to be downloaded 
     1617         * 
     1618         * $_GET['filter']        optional mimetype filter string, amy be the part up to and 
     1619         *                        including the slash '/' or the full mimetype. Only files 
     1620         *                        matching this (set of) mimetypes will be listed. 
     1621         *                        Examples: 'image/' or 'application/zip' 
     1622         * 
     1623         * On errors a HTTP 403 error response will be sent instead. 
     1624         */ 
     1625        protected function onDownload() 
     1626        { 
     1627                try 
     1628                { 
     1629                        if (!$this->options['download']) 
     1630                                throw new FileManagerException('disabled'); 
     1631 
     1632                        $file_arg = $this->getGETparam('file'); 
     1633                        if (empty($file_arg)) 
     1634                                throw new FileManagerException('nofile'); 
     1635 
     1636                        $legal_url = $this->rel2abs_legal_url_path($file_arg); 
     1637                        //$legal_url = self::enforceTrailingSlash($legal_url); 
     1638 
     1639                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     1640                        $file = $this->legal_url_path2file_path($legal_url); 
     1641 
     1642                        if (!is_readable($file)) 
     1643                                throw new FileManagerException('nofile'); 
     1644 
     1645                        $mime_filter = $this->getGETparam('filter', $this->options['filter']); 
     1646                        $mime = $this->getMimeType($file); 
     1647                        $mime_filters = $this->getAllowedMimeTypes($mime_filter); 
     1648                        if (is_file($file)) 
     1649                        { 
     1650                                if (!$this->IsAllowedMimeType($mime, $mime_filters)) 
     1651                                        throw new FileManagerException('extension'); 
     1652                        } 
     1653                        else 
     1654                        { 
     1655                                throw new FileManagerException('nofile'); 
     1656                        } 
     1657 
     1658                        $fileinfo = array( 
     1659                                        'legal_url' => $legal_url, 
     1660                                        'file' => $file, 
     1661                                        'mime' => $mime, 
     1662                                        'mime_filter' => $mime_filter, 
     1663                                        'mime_filters' => $mime_filters 
     1664                                ); 
     1665                        if (!empty($this->options['DownloadIsAuthorized_cb']) && function_exists($this->options['DownloadIsAuthorized_cb']) && !$this->options['DownloadIsAuthorized_cb']($this, 'download', $fileinfo)) 
     1666                                throw new FileManagerException('authorized'); 
     1667 
     1668                        $legal_url = $fileinfo['legal_url']; 
     1669                        $file = $fileinfo['file']; 
     1670                        $mime = $fileinfo['mime']; 
     1671                        $mime_filter = $fileinfo['mime_filter']; 
     1672                        $mime_filters = $fileinfo['mime_filters']; 
     1673 
     1674                        if ($fd = fopen($file, 'rb')) 
     1675                        { 
     1676                                $fsize = filesize($file); 
     1677                                $path_parts = pathinfo($legal_url); 
     1678                                $ext = strtolower($path_parts["extension"]); 
     1679                                switch ($ext) 
     1680                                { 
     1681                                case "pdf": 
     1682                                        header('Content-Type: application/pdf'); 
     1683                                        header('Content-Disposition: attachment; filename="' . $path_parts["basename"] . '"'); // use 'attachment' to force a download 
     1684                                        break; 
     1685 
     1686                                // add here more headers for diff. extensions 
     1687 
     1688                                default; 
     1689                                        header('Content-Type: application/octet-stream'); 
     1690                                        header('Content-Disposition: filename="' . $path_parts["basename"] . '"'); 
     1691                                        break; 
     1692                                } 
     1693                                header("Content-length: $fsize"); 
     1694                                header("Cache-control: private"); //use this to open files directly 
     1695 
     1696                                fpassthru($fd); 
     1697                                fclose($fd); 
     1698                        } 
     1699                } 
     1700                catch(FileManagerException $e) 
     1701                { 
     1702                        // we don't care whether it's a 404, a 403 or something else entirely: we feed 'em a 403 and that's final! 
     1703                        send_response_status_header(403); 
     1704                        echo $e->getMessage(); 
     1705                } 
     1706                catch(Exception $e) 
     1707                { 
     1708                        // we don't care whether it's a 404, a 403 or something else entirely: we feed 'em a 403 and that's final! 
     1709                        send_response_status_header(403); 
     1710                        echo $e->getMessage(); 
     1711                } 
     1712        } 
     1713 
     1714        /** 
     1715         * Process the 'upload' event 
     1716         * 
     1717         * Process and store the uploaded file in the designated location. 
     1718         * Images will be resized when possible and applicable. A thumbnail image will also 
     1719         * be preproduced when possible. 
     1720         * Return a JSON encoded status of success or failure. 
     1721         * 
     1722         * Expected parameters: 
     1723         * 
     1724         * $_GET['directory']     path relative to basedir a.k.a. options['directory'] root 
     1725         * 
     1726         * $_GET['resize']        nonzero value indicates any uploaded image should be resized to the configured options['maxImageDimension'] width and height whenever possible 
     1727         * 
     1728         * $_GET['filter']        optional mimetype filter string, amy be the part up to and 
     1729         *                        including the slash '/' or the full mimetype. Only files 
     1730         *                        matching this (set of) mimetypes will be listed. 
     1731         *                        Examples: 'image/' or 'application/zip' 
     1732         * 
     1733         * $_FILES[]              the metadata for the uploaded file 
     1734         * 
     1735         * Errors will produce a JSON encoded error report, including at least two fields: 
     1736         * 
     1737         * status                  0 for error; nonzero for success 
     1738         * 
     1739         * error                   error message 
     1740         */ 
     1741        protected function onUpload() 
     1742        { 
     1743                $emsg = null; 
     1744                $jserr = array( 
     1745                                'status' => 1 
     1746                        ); 
     1747 
     1748                try 
     1749                { 
     1750                        if (!$this->options['upload']) 
     1751                                throw new FileManagerException('disabled'); 
     1752 
     1753                        if (!isset($_FILES) || empty($_FILES['Filedata']) || empty($_FILES['Filedata']['name']) || empty($_FILES['Filedata']['size'])) 
     1754                                throw new FileManagerException('nofile'); 
     1755 
     1756                        $file_arg = $_FILES['Filedata']['name']; 
     1757                        if (empty($file_arg)) 
     1758                                throw new FileManagerException('nofile'); 
     1759 
     1760                        $dir_arg = $this->getPOSTparam('directory'); 
     1761                        $legal_url = $this->rel2abs_legal_url_path($dir_arg); 
     1762                        $legal_url = self::enforceTrailingSlash($legal_url); 
     1763                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     1764                        $dir = $this->legal_url_path2file_path($legal_url); 
     1765 
     1766                        $filename = $this->getUniqueName($file_arg, $dir); 
     1767                        if (!$filename) 
     1768                                throw new FileManagerException('nofile'); 
     1769                        $fi = pathinfo($filename); 
     1770 
     1771 
     1772                        $mime_filter = $this->getGETparam('filter', $this->options['filter']); 
     1773                        $tmppath = $_FILES['Filedata']['tmp_name']; 
     1774                        $mime = $this->getMimeType($tmppath); 
     1775                        $mime_filters = $this->getAllowedMimeTypes($mime_filter); 
     1776                        if (!$this->IsAllowedMimeType($mime, $mime_filters)) 
     1777                                throw new FileManagerException('extension'); 
     1778 
     1779                        /* 
     1780                        Security: 
     1781 
     1782                        Upload::move() processes the unfiltered version of $_FILES[]['name'], at least to get the extension, 
     1783                        unless we ALWAYS override the filename and extension in the options array below. That's why we 
     1784                        calculate the extension at all times here. 
     1785                        */ 
     1786                        if (!is_string($fi['extension']) || strlen($fi['extension']) == 0) // can't use 'empty()' as "0" is a valid extension itself. 
     1787                        { 
     1788                                //enforce a mandatory extension, even when there isn't one (due to filtering or original input producing none) 
     1789                                $fi['extension'] = 'txt'; 
     1790                        } 
     1791                        else if ($this->options['safe'] && in_array(strtolower($fi['extension']), array('exe', 'dll', 'com', 'php', 'php3', 'php4', 'php5', 'phps'))) 
     1792                        { 
     1793                                $fi['extension'] = 'txt'; 
     1794                        } 
     1795 
     1796                        $fileinfo = array( 
     1797                                'legal_url' => $legal_url, 
     1798                                'dir' => $dir, 
     1799                                'raw_filename' => $file_arg, 
     1800                                'name' => $fi['filename'], 
     1801                                'extension' => $fi['extension'], 
     1802                                'mime' => $mime, 
     1803                                'mime_filter' => $mime_filter, 
     1804                                'mime_filters' => $mime_filters, 
     1805                                'tmp_filepath' => $tmppath, 
     1806                                'size' => $_FILES['Filedata']['size'], 
     1807                                'maxsize' => $this->options['maxUploadSize'], 
     1808                                'overwrite' => false, 
     1809                                'chmod' => $this->options['chmod'] & 0666   // security: never make those files 'executable'! 
     1810                        ); 
     1811                        if (!empty($this->options['UploadIsAuthorized_cb']) && function_exists($this->options['UploadIsAuthorized_cb']) && !$this->options['UploadIsAuthorized_cb']($this, 'upload', $fileinfo)) 
     1812                                throw new FileManagerException('authorized'); 
     1813 
     1814                        $legal_url = $fileinfo['legal_url']; 
     1815                        $dir = $fileinfo['dir']; 
     1816                        $file_arg = $fileinfo['raw_filename']; 
     1817                        $filename = $fileinfo['name'] . (!empty($fileinfo['extension']) ? '.' . $fileinfo['extension'] : ''); 
     1818                        $mime = $fileinfo['mime']; 
     1819                        $mime_filter = $fileinfo['mime_filter']; 
     1820                        $mime_filters = $fileinfo['mime_filters']; 
     1821                        //$tmppath = $fileinfo['tmp_filepath']; 
     1822 
     1823                        if($fileinfo['maxsize'] && $fileinfo['size'] > $fileinfo['maxsize']) 
     1824                                throw new FileManagerException('size'); 
     1825 
     1826                        if(!$fileinfo['extension']) 
     1827                                throw new FileManagerException('extension'); 
     1828 
     1829                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     1830                        $file = $this->legal_url_path2file_path($legal_url . $filename); 
     1831 
     1832                        if(!$fileinfo['overwrite'] && file_exists($file)) 
     1833                                throw new FileManagerException('exists'); 
     1834 
     1835                        if(!move_uploaded_file($_FILES['Filedata']['tmp_name'], $file)) 
     1836                                throw new FileManagerException(strtolower($_FILES['Filedata']['error'] <= 2 ? 'size' : ($_FILES['Filedata']['error'] == 3 ? 'partial' : 'path'))); 
     1837 
     1838                        @chmod($file, $fileinfo['chmod']); 
     1839 
     1840 
     1841                        /* 
     1842                         * NOTE: you /can/ (and should be able to, IMHO) upload 'overly large' image files to your site, but the resizing process step 
     1843                         *       happening here will fail; we have memory usage estimators in place to make the fatal crash a non-silent one, i,e, one 
     1844                         *       where we still have a very high probability of NOT fatally crashing the PHP iunterpreter but catching a suitable exception 
     1845                         *       instead. 
     1846                         *       Having uploaded such huge images, a developer/somebody can always go in later and up the memory limit if the site admins 
     1847                         *       feel it is deserved. Until then, no thumbnails of such images (though you /should/ be able to milkbox-view the real thing!) 
     1848                         */ 
     1849                        if (FileManagerUtility::startsWith($mime, 'image/') && $this->getGETparam('resize', 0)) 
     1850                        { 
     1851                                $img = new Image($file); 
     1852                                $size = $img->getSize(); 
     1853                                // Image::resize() takes care to maintain the proper aspect ratio, so this is easy 
     1854                                // (default quality is 100% for JPEG so we get the cleanest resized images here) 
     1855                                $img->resize($this->options['maxImageDimension']['width'], $this->options['maxImageDimension']['height'])->save(); 
     1856                                unset($img); 
     1857                        } 
     1858 
     1859                        if (!headers_sent()) header('Content-Type: application/json'); 
     1860 
     1861                        echo json_encode(array( 
     1862                                        'status' => 1, 
     1863                                        'name' => pathinfo($file, PATHINFO_BASENAME) 
     1864                                )); 
     1865                        return; 
     1866                } 
     1867                catch(FileManagerException $e) 
     1868                { 
     1869                        $emsg = $e->getMessage(); 
     1870                } 
     1871                catch(Exception $e) 
     1872                { 
     1873                        // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
     1874                        $emsg = $e->getMessage(); 
     1875                } 
     1876 
     1877                $this->modify_json4exception($jserr, $emsg); 
     1878 
     1879                if (!headers_sent()) header('Content-Type: application/json'); 
     1880 
     1881                // when we fail here, it's pretty darn bad and nothing to it. 
     1882                // just push the error JSON as go. 
     1883                echo json_encode($jserr); 
     1884        } 
     1885 
     1886        /** 
     1887         * Process the 'move' event (with is used by both move/copy and rename client side actions) 
     1888         * 
     1889         * Copy or move/rename a given file or directory and return a JSON encoded status of success 
     1890         * or failure. 
     1891         * 
     1892         * Expected parameters: 
     1893         * 
     1894         * $_POST['copy']            nonzero value means copy, zero or nil for move/rename 
     1895         * 
     1896         * Source filespec: 
     1897         * 
     1898         *   $_POST['directory']     path relative to basedir a.k.a. options['directory'] root 
     1899         * 
     1900         *   $_POST['file']          original name of the file/subdirectory to be renamed/copied 
     1901         * 
     1902         * Destination filespec: 
     1903         * 
     1904         *   $_POST['newDirectory']  path relative to basedir a.k.a. options['directory'] root; 
     1905         *                           target directory where the file must be moved / copied 
     1906         * 
     1907         *   $_POST['name']          target name of the file/subdirectory to be renamed 
     1908         * 
     1909         * Errors will produce a JSON encoded error report, including at least two fields: 
     1910         * 
     1911         * status                    0 for error; nonzero for success 
     1912         * 
     1913         * error                     error message 
     1914         */ 
     1915        protected function onMove() 
     1916        { 
     1917                $emsg = null; 
     1918                $jserr = array( 
     1919                                'status' => 1 
     1920                        ); 
     1921 
     1922                try 
     1923                { 
     1924                        if (!$this->options['move']) 
     1925                                throw new FileManagerException('disabled'); 
     1926 
     1927                        $file_arg = $this->getPOSTparam('file'); 
     1928                        if (empty($file_arg)) 
     1929                                throw new FileManagerException('nofile'); 
     1930 
     1931                        $dir_arg = $this->getPOSTparam('directory'); 
     1932                        $legal_url = $this->rel2abs_legal_url_path($dir_arg); 
     1933                        $legal_url = self::enforceTrailingSlash($legal_url); 
     1934 
     1935                        $filename = pathinfo($file_arg, PATHINFO_BASENAME); 
     1936                        //$legal_url .= $filename; 
     1937                        // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     1938                        $dir = $this->legal_url_path2file_path($legal_url); 
     1939                        $path = $this->legal_url_path2file_path($legal_url . $filename); 
     1940 
     1941                        if (!file_exists($path)) 
     1942                                throw new FileManagerException('nofile'); 
     1943 
     1944                        $is_dir = is_dir($path); 
     1945 
     1946                        $newdir_arg = $this->getPOSTparam('newDirectory'); 
     1947                        $newname_arg = $this->getPOSTparam('name'); 
     1948                        $rename = (empty($newdir_arg) && !empty($newname_arg)); 
     1949 
     1950                        $is_copy = !!$this->getPOSTparam('copy'); 
     1951 
     1952 
     1953                        // note: we do not support copying entire directories, though directory rename/move is okay 
     1954                        if ($is_copy && $is_dir) 
     1955                                throw new FileManagerException('disabled'); 
     1956 
     1957                        if($rename) 
     1958                        { 
     1959                                $fn = 'rename'; 
     1960                                $legal_newurl = $legal_url; 
     1961                                $newdir = $dir; 
     1962 
     1963                                $newname = pathinfo($newname_arg, PATHINFO_BASENAME); 
     1964                                if ($is_dir) 
     1965                                        $newname = $this->getUniqueName(array('filename' => $newname), $newdir);  // a directory has no 'extension' 
     1966                                else 
     1967                                        $newname = $this->getUniqueName($newname, $newdir); 
     1968 
     1969                                if (!$newname) 
     1970                                        throw new FileManagerException('nonewfile'); 
     1971 
     1972                                // when the new name seems to have a different extension, make sure the extension doesn't change after all: 
     1973                                // Note: - if it's only 'case' we're changing here, then exchange the extension instead of appending it. 
     1974                                //       - directories do not have extensions 
     1975                                $extOld = pathinfo($filename, PATHINFO_EXTENSION); 
     1976                                $extNew = pathinfo($newname, PATHINFO_EXTENSION); 
     1977                                if ((!$this->options['allowExtChange'] || (!$is_dir && empty($extNew))) && !empty($extOld) && strtolower($extOld) != strtolower($extNew)) 
     1978                                { 
     1979                                        $newname .= '.' . $extOld; 
     1980                                } 
     1981                        } 
     1982                        else 
     1983                        { 
     1984                                $fn = ($is_copy ? 'copy' : 'rename' /* 'move' */); 
     1985                                $legal_newurl = $this->rel2abs_legal_url_path($newdir_arg); 
     1986                                $legal_newurl = self::enforceTrailingSlash($legal_newurl); 
     1987                                $newdir = $this->legal_url_path2file_path($legal_newurl); 
     1988 
     1989                                if ($is_dir) 
     1990                                        $newname = $this->getUniqueName(array('filename' => $filename), $newdir);  // a directory has no 'extension' 
     1991                                else 
     1992                                        $newname = $this->getUniqueName($filename, $newdir); 
     1993 
     1994                                if (!$newname) 
     1995                                        throw new FileManagerException('nonewfile'); 
     1996                        } 
     1997 
     1998                        $newpath = $this->legal_url_path2file_path($legal_newurl . $newname); 
     1999 
     2000 
     2001                        $fileinfo = array( 
     2002                                        'legal_url' => $legal_url, 
     2003                                        'dir' => $dir, 
     2004                                        'path' => $path, 
     2005                                        'name' => $filename, 
     2006                                        'legal_newurl' => $legal_newurl, 
     2007                                        'newdir' => $newdir, 
     2008                                        'newpath' => $newpath, 
     2009                                        'newname' => $newname, 
     2010                                        'rename' => $rename, 
     2011                                        'is_dir' => $is_dir, 
     2012                                        'function' => $fn 
     2013                                ); 
     2014 
     2015                        if (!empty($this->options['MoveIsAuthorized_cb']) && function_exists($this->options['MoveIsAuthorized_cb']) && !$this->options['MoveIsAuthorized_cb']($this, 'move', $fileinfo)) 
     2016                                throw new FileManagerException('authorized'); 
     2017 
     2018                        $legal_url = $fileinfo['legal_url']; 
     2019                        $dir = $fileinfo['dir']; 
     2020                        $path = $fileinfo['path']; 
     2021                        $filename = $fileinfo['name']; 
     2022                        $legal_newurl = $fileinfo['legal_newurl']; 
     2023                        $newdir = $fileinfo['newdir']; 
     2024                        $newpath = $fileinfo['newpath']; 
     2025                        $newname = $fileinfo['newname']; 
     2026                        $rename = $fileinfo['rename']; 
     2027                        $is_dir = $fileinfo['is_dir']; 
     2028                        $fn = $fileinfo['function']; 
     2029 
     2030                        if($rename) 
     2031                        { 
     2032                                // try to remove the thumbnail related to the original file; don't mind if it doesn't exist 
     2033                                if(!$is_dir) 
     2034                                { 
     2035                                        if (!$this->deleteThumb($legal_url . $filename)) 
     2036                                                throw new FileManagerException('delete_thumbnail_failed'); 
     2037                                } 
     2038                        } 
     2039 
     2040                        if (!@$fn($path, $newpath)) 
     2041                                throw new FileManagerException($fn . '_failed:' . $legal_newurl . ':' . $newname); 
     2042 
     2043                        if (!headers_sent()) header('Content-Type: application/json'); 
     2044 
     2045                        echo json_encode(array( 
     2046                                'status' => 1, 
     2047                                'name' => $newname 
     2048                        )); 
     2049                        return; 
     2050                } 
     2051                catch(FileManagerException $e) 
     2052                { 
     2053                        $emsg = $e->getMessage(); 
     2054                } 
     2055                catch(Exception $e) 
     2056                { 
     2057                        // catching other severe failures; since this can be anything and should only happen in the direst of circumstances, we don't bother translating 
     2058                        $emsg = $e->getMessage(); 
     2059                } 
     2060 
     2061                $this->modify_json4exception($jserr, $emsg); 
     2062 
     2063                if (!headers_sent()) header('Content-Type: application/json'); 
     2064 
     2065                // when we fail here, it's pretty darn bad and nothing to it. 
     2066                // just push the error JSON as go. 
     2067                echo json_encode($jserr); 
     2068        } 
     2069 
     2070 
     2071 
     2072 
     2073 
     2074 
     2075 
     2076        /** 
     2077         * Convert a given file spec into a URL pointing at our JiT thumbnail creation/delivery event handler. 
     2078         * 
     2079         * The spec must be an array with these elements: 
     2080         *   'event':       'thumbnail' 
     2081         *   'directory':   URI path to directory of the ORIGINAL file 
     2082         *   'file':        filename of the ORIGINAL file 
     2083         *   'size':        requested thumbnail size (e.g. 48) 
     2084         *   'filter':      optional mime_filter as originally specified by the client 
     2085         *   'type':        'thumb' or 'list': the current type of directory view at the client 
     2086         * 
     2087         * Return the URL string. 
     2088         */ 
     2089        public function mkEventHandlerURL($spec) 
     2090        { 
     2091                // first determine how the client can reach us; assume that's the same URI as he went to right now. 
     2092                $our_handler_url = $_SERVER['SCRIPT_NAME']; 
     2093 
     2094    // Disabled the addition of propagateData here, this is done only in the client 
     2095                if (0 &&  is_array($this->options['URIpropagateData'])) 
     2096                { 
     2097                        // the items in 'spec' always win over any entries in 'URIpropagateData': 
     2098                        $spec = array_merge(array(), $this->options['URIpropagateData'], $spec); 
     2099                } 
     2100 
     2101                // next, construct the query part of the URI: 
     2102                $qstr = http_build_query_ex($spec, null, '&', null, PHP_QUERY_RFC3986); 
     2103 
     2104                return $our_handler_url . '?' . $qstr; 
     2105        } 
     2106 
     2107 
     2108 
     2109        /** 
     2110         * Produce a HTML snippet detailing the given file in the JSON 'content' element; place additional info 
     2111         * in the JSON elements 'thumbnail', 'thumbnail48', 'thumbnail250', 'width', 'height', ... 
     2112         * 
     2113         * Return an augmented JSON array. 
     2114         * 
     2115         * Throw an exception on error. 
     2116         */ 
     2117        public function extractDetailInfo($json, $legal_url, $file, $mime, $mime_filter) 
     2118        { 
     2119                $url = $this->legal2abs_url_path($legal_url); 
     2120                $filename = pathinfo($url, PATHINFO_BASENAME); 
     2121 
     2122                $isdir = !is_file($file); 
     2123                if (!$isdir) 
     2124                { 
     2125                        $iconspec = $filename; 
     2126                } 
     2127                else if (is_dir($file)) 
     2128                { 
     2129                        $mime = 'text/directory'; 
     2130                        $iconspec = ($filename == '..' ? 'is.dir_up' : 'is.dir'); 
     2131                } 
     2132                else 
     2133                { 
     2134                        // simply do NOT list anything that we cannot cope with. 
     2135                        // That includes clearly inaccessible files (and paths) with non-ASCII characters: 
     2136                        // PHP5 and below are a real mess when it comes to handling Unicode filesystems 
     2137                        // (see the php.net site too: readdir / glob / etc. user comments and the official 
     2138                        // notice that PHP will support filesystem UTF-8/Unicode only when PHP6 is released. 
     2139                        // 
     2140                        // Big, fat bummer! 
     2141                        throw new FileManagerException('nofile'); 
     2142                } 
     2143 
     2144                if (FileManagerUtility::startsWith($mime, 'image/')) 
     2145                { 
     2146                        /* 
     2147                         * offload the thumbnailing process to another event ('event=thumbnail') to be fired by the client 
     2148                         * when it's time to render the thumbnail: the offloading helps us tremendously in coping with large 
     2149                         * directories: 
     2150                         * WE simply assume the thumbnail will be there, so we don't even need to check for its existence 
     2151                         * (which saves us one more file_exists() per item at the very least). And when it doesn't, that's 
     2152                         * for the event=thumbnail handler to worry about (creating the thumbnail on demand or serving 
     2153                         * a generic icon image instead). 
     2154                         */ 
     2155                        $thumb48 = $this->mkEventHandlerURL(array( 
     2156                                        'event' => 'thumbnail', 
     2157                                        // directory and filename of the ORIGINAL image should follow next: 
     2158                                        'directory' => pathinfo($legal_url, PATHINFO_DIRNAME), 
     2159                                        'file' => pathinfo($legal_url, PATHINFO_BASENAME), 
     2160                                        'size' => 48,          // thumbnail suitable for 'view/type=thumb' list views 
     2161                                        'filter' => $mime_filter 
     2162                                )); 
     2163                        $thumb250 = $this->mkEventHandlerURL(array( 
     2164                                        'event' => 'thumbnail', 
     2165                                        // directory and filename of the ORIGINAL image should follow next: 
     2166                                        'directory' => pathinfo($legal_url, PATHINFO_DIRNAME), 
     2167                                        'file' => pathinfo($legal_url, PATHINFO_BASENAME), 
     2168                                        'size' => 250,         // thumbnail suitable for 'view/type=thumb' list views 
     2169                                        'filter' => $mime_filter 
     2170                                )); 
     2171                } 
     2172                else 
     2173                { 
     2174                        $thumb48 = $this->getIcon($iconspec, false); 
     2175                        $thumb250 = $thumb48; 
     2176                } 
     2177                $icon = $this->getIcon($iconspec, true); 
     2178 
     2179                $json = array_merge(array( 
     2180                                //'status' => 1, 
     2181                                //'mimetype' => $mime, 
     2182                                'content' => self::compressHTML('<div class="margin"> 
     2183                                        ${nopreview} 
     2184                                </div>') 
     2185                        ), 
     2186                        (is_array($json) ? $json : array()), 
     2187                        array( 
     2188                                'path' => FileManagerUtility::rawurlencode_path($url), 
     2189                                'name' => preg_replace('/[^ -~]/', '?', $filename),       // HACK/TWEAK: PHP5 and below are completely b0rked when it comes to international filenames   :-( 
     2190                                'date' => date($this->options['dateFormat'], @filemtime($file)), 
     2191                                'mime' => $mime, 
     2192                                //'thumbnail' => $thumb, 
     2193                                'thumbnail48' => $thumb48, 
     2194                                'thumbnail250' => $thumb250, 
     2195                                'icon' => FileManagerUtility::rawurlencode_path($icon), 
     2196                                'size' => @filesize($file) 
     2197                        )); 
     2198 
     2199 
     2200                // getID3 is slower as it *copies* the image to the temp dir before processing: see GetDataImageSize(). 
     2201                // This is done as getID3 can also analyze *embedded* images, for which this approach is required. 
     2202                $getid3 = new getID3(); 
     2203                $getid3->encoding = 'UTF-8'; 
     2204                $getid3->Analyze($file); 
     2205 
     2206                $content = null; 
     2207 
     2208                if (FileManagerUtility::startsWith($mime, 'image/')) 
     2209                { 
     2210                        // generates a random number to put on the end of the image, to prevent caching 
     2211                        //$randomImage = '?'.md5(uniqid(rand(),1)); 
     2212 
     2213                        //$size = @getimagesize($file); 
     2214                        //// check for badly formatted image files (corruption); we'll handle the overly large ones next 
     2215                        //if (!$size) 
     2216                        //  throw new FileManagerException('corrupt_img:' . $url); 
     2217 
     2218                        $sw_make = $this->getID3infoItem($getid3, null, 'jpg', 'exif', 'IFD0', 'Software'); 
     2219                        $time_make = $this->getID3infoItem($getid3, null, 'jpg', 'exif', 'IFD0', 'DateTime'); 
     2220 
     2221                        $width = $this->getID3infoItem($getid3, 0, 'video', 'resolution_x'); 
     2222                        $height = $this->getID3infoItem($getid3, 0, 'video', 'resolution_y'); 
     2223                        $json['width'] = $width; 
     2224                        $json['height'] = $height; 
     2225 
     2226                        $content = '<dl> 
     2227                                        <dt>${width}</dt><dd>' . $width . 'px</dd> 
     2228                                        <dt>${height}</dt><dd>' . $height . 'px</dd> 
     2229                                </dl>'; 
     2230                        if (!empty($sw_make) || !empty($time_make)) 
     2231                        { 
     2232                                $content .= '<p>Made with ' . (empty($sw_make) ? '???' : $sw_make) . ' @ ' . (empty($time_make) ? '???' : $time_make) . '</p>'; 
     2233                        } 
     2234                        $content .= ' 
     2235                                <h2>${preview}</h2> 
     2236                                '; 
     2237 
     2238                        $emsg = null; 
     2239                        try 
     2240                        { 
     2241                                $thumbfile = $this->getThumb($legal_url, $file, 250, 250); 
     2242                                /* 
     2243                                 * the thumbnail may be produced now, but we want to stay in control when the thumbnail is 
     2244                                 * fetched by the client, so we force them to travel through this backend. 
     2245                                 */ 
     2246                                $enc_thumbfile = $thumb250; 
     2247                        } 
     2248                        catch (Exception $e) 
     2249                        { 
     2250                                $emsg = $e->getMessage(); 
     2251                                $thumbfile = $this->getIconForError($emsg, $legal_url, false); 
     2252                                $enc_thumbfile = FileManagerUtility::rawurlencode_path($thumbfile); 
     2253 
     2254                                $json['thumbnail48'] = $thumbfile; 
     2255                                $json['thumbnail250'] = $thumbfile; 
     2256                        } 
     2257 
     2258                        // get the size of the thumbnail/icon: the <img> is styled with width and height to ensure the background 'loader' image is shown correctly: 
     2259                        $tnpath = $this->url_path2file_path($thumbfile); 
     2260                        $tninf = @getimagesize($tnpath); 
     2261 
     2262                        $json['tn_width'] = $tninf[0]; 
     2263                        $json['tn_height'] = $tninf[1]; 
     2264 
     2265                        $content .= '<a href="' . FileManagerUtility::rawurlencode_path($url) . '" data-milkbox="single" title="' . htmlentities($filename, ENT_QUOTES, 'UTF-8') . '"> 
     2266                                                   <img src="' . $enc_thumbfile . '" class="preview" alt="preview" style="width: ' . $tninf[0] . 'px; height: ' . $tninf[1] . 'px;" /> 
     2267                                                 </a>'; 
     2268                        if (!empty($emsg)) 
     2269                        { 
     2270                                // use the abilities of modify_json4exception() to munge/format the exception message: 
     2271                                $jsa = array(); 
     2272                                $this->modify_json4exception($jsa, $emsg); 
     2273                                $content .= "\n" . '<p class="err_info">' . $jsa['error'] . '</p>'; 
     2274                        } 
     2275                        if (!empty($emsg) && strpos($emsg, 'img_will_not_fit') !== false) 
     2276                        { 
     2277                                $earr = explode(':', $e->getMessage(), 2); 
     2278                                $content .= "\n" . '<p class="tech_info">Estimated minimum memory requirements to create thumbnails for this image: ' . $earr[1] . '</p>'; 
     2279                        } 
     2280 
     2281                        if (DEVELOPMENT) 
     2282                        { 
     2283                                $finfo = Image::guestimateRequiredMemorySpace($file); 
     2284                                if (!empty($finfo['usage_guestimate']) && !empty($finfo['usage_min_advised'])) 
     2285                                { 
     2286                                        $content .= "\n" . '<p class="tech_info">memory used: ' . number_format(memory_get_peak_usage() / 1E6, 1) . ' MB / estimated: ' . number_format($finfo['usage_guestimate'] / 1E6, 1) . ' MB / suggested: ' . number_format($finfo['usage_min_advised'] / 1E6, 1) . ' MB</p>'; 
     2287                                } 
     2288                        } 
     2289 
     2290                        $exif_data = $this->getID3infoItem($getid3, null, 'jpg', 'exif'); 
     2291                        try 
     2292                        { 
     2293                                if (!empty($exif_data)) 
     2294                                { 
     2295                                        /* 
     2296                                         * before dumping the EXIF data array (which may carry binary content and MAY CRASH the json_encode()r >:-(( 
     2297                                         * we filter it to prevent such crashes and oddly looking (diagnostic) presentation of values. 
     2298                                         */ 
     2299                                        self::clean_EXIF_results($exif_data); 
     2300                                        ob_start(); 
     2301                                                var_dump($exif_data); 
     2302                                        $dump = ob_get_clean(); 
     2303                                        $content .= $dump; 
     2304                                } 
     2305                        } 
     2306                        catch (Exception $e) 
     2307                        { 
     2308                                // use the abilities of modify_json4exception() to munge/format the exception message: 
     2309                                $jsa = array('error' => ''); 
     2310                                $this->modify_json4exception($jsa, $e->getMessage()); 
     2311                                $content .= "\n" . '<p class="err_info">' . $jsa['error'] . '</p>'; 
     2312                        } 
     2313                } 
     2314                elseif (FileManagerUtility::startsWith($mime, 'text/') || $mime == 'application/x-javascript') 
     2315                { 
     2316                        // text preview: 
     2317                        $filecontent = @file_get_contents($file, false, null, 0); 
     2318                        if ($filecontent === false) 
     2319                                throw new FileManagerException('nofile'); 
     2320 
     2321                        if (!FileManagerUtility::isBinary($filecontent)) 
     2322                        { 
     2323                                $content = '<div class="textpreview"><pre>' . str_replace(array('$', "\t"), array('&#36;', '&nbsp;&nbsp;'), htmlentities($filecontent, ENT_NOQUOTES, 'UTF-8')) . '</pre></div>'; 
     2324                        } 
     2325                        // else: fall back to 'no preview available' 
     2326                } 
     2327                elseif ($mime == 'application/zip') 
     2328                { 
     2329                        $out = array(array(), array()); 
     2330                        $info = $this->getID3infoItem($getid3, null, 'zip', 'files'); 
     2331                        if (is_array($info)) 
     2332                        { 
     2333                                foreach ($info as $name => $size) 
     2334                                { 
     2335                                        $isdir = is_array($size) ? true : false; 
     2336                                        $out[($isdir) ? 0 : 1][$name] = '<li><a><img src="' . FileManagerUtility::rawurlencode_path($this->getIcon($name, true)) . '" alt="" /> ' . $name . '</a></li>'; 
     2337                                } 
     2338                                natcasesort($out[0]); 
     2339                                natcasesort($out[1]); 
     2340                                $content = '<ul>' . implode(array_merge($out[0], $out[1])) . '</ul>'; 
     2341                        } 
     2342                } 
     2343                elseif ($mime == 'application/x-shockwave-flash') 
     2344                { 
     2345                        $info = $this->getID3infoItem($getid3, null, 'swf', 'header'); 
     2346                        if (is_array($info)) 
     2347                        { 
     2348                                // Note: preview data= urls were formatted like this in CCMS: 
     2349                                // $this->options['assetBasePath'] . 'dewplayer.swf?mp3=' . rawurlencode($url) . '&volume=30' 
     2350 
     2351                                $width = $this->getID3infoItem($getid3, 0, 'swf', 'header', 'frame_width') / 10; 
     2352                                $height = $this->getID3infoItem($getid3, 0, 'swf', 'header', 'frame_height') / 10; 
     2353                                $json['width'] = $width; 
     2354                                $json['height'] = $height; 
     2355 
     2356                                $content = '<dl> 
     2357                                                <dt>${width}</dt><dd>' . $width . 'px</dd> 
     2358                                                <dt>${height}</dt><dd>' . $height . 'px</dd> 
     2359                                                <dt>${length}</dt><dd>' . round($this->getID3infoItem($getid3, 0, 'swf', 'header', 'length') / $this->getID3infoItem($getid3, 25, 'swf', 'header', 'frame_count')) . 's</dd> 
     2360                                        </dl> 
     2361                                        <h2>${preview}</h2> 
     2362                                        <div class="object"> 
     2363                                                <object type="application/x-shockwave-flash" data="'.FileManagerUtility::rawurlencode_path($url).'" width="500" height="400"> 
     2364                                                        <param name="scale" value="noscale" /> 
     2365                                                        <param name="movie" value="'.FileManagerUtility::rawurlencode_path($url).'" /> 
     2366                                                </object> 
     2367                                        </div>'; 
     2368                        } 
     2369                } 
     2370                elseif (FileManagerUtility::startsWith($mime, 'audio/')) 
     2371                { 
     2372                        getid3_lib::CopyTagsToComments($getid3->info); 
     2373 
     2374                        $dewplayer = FileManagerUtility::rawurlencode_path($this->options['assetBasePath'] . 'dewplayer.swf'); 
     2375 
     2376                        $content = '<dl> 
     2377                                        <dt>${title}</dt><dd>' . $this->getID3infoItem($getid3, '???', 'comments', 'title', 0) . '</dd> 
     2378                                        <dt>${artist}</dt><dd>' . $this->getID3infoItem($getid3, '???', 'comments', 'artist', 0) . '</dd> 
     2379                                        <dt>${album}</dt><dd>' . $this->getID3infoItem($getid3, '???', 'comments', 'album', 0) . '</dd> 
     2380                                        <dt>${length}</dt><dd>' . $this->getID3infoItem($getid3, '???', 'playtime_string') . '</dd> 
     2381                                        <dt>${bitrate}</dt><dd>' . round($this->getID3infoItem($getid3, 0, 'bitrate') / 1000) . 'kbps</dd> 
     2382                                </dl> 
     2383                                <h2>${preview}</h2> 
     2384                                <div class="object"> 
     2385                                        <object type="application/x-shockwave-flash" data="' . $dewplayer . '" width="200" height="20" id="dewplayer" name="dewplayer"> 
     2386                                                <param name="wmode" value="transparent" /> 
     2387                                                <param name="movie" value="' . $dewplayer . '" /> 
     2388                                                <param name="flashvars" value="mp3=' . FileManagerUtility::rawurlencode_path($url) . '&amp;volume=50&amp;showtime=1" /> 
     2389                                        </object> 
     2390                                </div>'; 
     2391                } 
     2392                elseif (FileManagerUtility::startsWith($mime, 'video/')) 
     2393                { 
     2394                        $dewplayer = FileManagerUtility::rawurlencode_path($this->options['assetBasePath'] . 'dewplayer.swf'); 
     2395 
     2396                        $a_fmt = $this->getID3infoItem($getid3, '???', 'audio', 'dataformat'); 
     2397                        $a_samplerate = $this->getID3infoItem($getid3, 0, 'audio', 'sample_rate') / 1000; 
     2398                        $a_bitrate = round($this->getID3infoItem($getid3, 0, 'audio', 'bitrate') / 1000); 
     2399                        $a_bitrate_mode = $this->getID3infoItem($getid3, '???', 'audio', 'bitrate_mode'); 
     2400                        $a_channels = $this->getID3infoItem($getid3, 0, 'audio', 'channels'); 
     2401                        $a_codec = $this->getID3infoItem($getid3, '', 'audio', 'codec'); 
     2402                        $a_streams = $this->getID3infoItem($getid3, '???', 'audio', 'streams'); 
     2403                        $a_streamcount = (is_array($a_streams) ? count($a_streams) : 0); 
     2404 
     2405                        $v_fmt = $this->getID3infoItem($getid3, '???', 'video', 'dataformat'); 
     2406                        $v_bitrate = round($this->getID3infoItem($getid3, 0, 'video', 'bitrate') / 1000); 
     2407                        $v_bitrate_mode = $this->getID3infoItem($getid3, '???', 'video', 'bitrate_mode'); 
     2408                        $v_framerate = $this->getID3infoItem($getid3, '???', 'video', 'frame_rate'); 
     2409                        $v_width = $this->getID3infoItem($getid3, '???', 'video', 'resolution_x'); 
     2410                        $v_height = $this->getID3infoItem($getid3, '???', 'video', 'resolution_y'); 
     2411                        $v_par = $this->getID3infoItem($getid3, 1.0, 'video', 'pixel_aspect_ratio'); 
     2412                        $v_codec = $this->getID3infoItem($getid3, '', 'video', 'codec'); 
     2413 
     2414                        $g_bitrate = round($this->getID3infoItem($getid3, 0, 'bitrate') / 1000); 
     2415                        $g_playtime_str = $this->getID3infoItem($getid3, '???', 'playtime_string'); 
     2416 
     2417                        $content = '<dl> 
     2418                                        <dt>Audio</dt><dd>' . $a_fmt . (!empty($a_codec) ? ' (' . $a_codec . ')' : '') . 
     2419                                                                                (!empty($a_channels) ? ($a_channels === 1 ? ' (mono)' : $a_channels === 2 ? ' (stereo)' : ' (' . $a_channels . ' channels)') : '') . 
     2420                                                                                ': ' . $a_samplerate . ' kHz @ ' . $a_bitrate . ' kbps (' . strtoupper($a_bitrate_mode) . ')' . 
     2421                                                                                ($a_streamcount > 1 ? ' (' . $a_streamcount . ' streams)' : '') . 
     2422                                                                '</dd> 
     2423                                        <dt>Video</dt><dd>' . $v_fmt . (!empty($v_codec) ? ' (' . $v_codec . ')' : '') .  ': ' . $v_framerate . ' fps @ ' . $v_bitrate . ' kbps (' . strtoupper($v_bitrate_mode) . ')' . 
     2424                                                                                ($v_par != 1.0 ? ', PAR: ' . $v_par : '') . 
     2425                                                                '</dd> 
     2426                                        <dt>${width}</dt><dd>' . $v_width . 'px</dd> 
     2427                                        <dt>${height}</dt><dd>' . $v_height . 'px</dd> 
     2428                                        <dt>${length}</dt><dd>' . $g_playtime_str . '</dd> 
     2429                                        <dt>${bitrate}</dt><dd>' . $g_bitrate . 'kbps</dd> 
     2430                                </dl> 
     2431                                <h2>${preview}</h2> 
     2432                                <div class="object"> 
     2433                                        <object type="application/x-shockwave-flash" data="' . $dewplayer . '" width="200" height="20" id="dewplayer" name="dewplayer"> 
     2434                                                <param name="wmode" value="transparent" /> 
     2435                                                <param name="movie" value="' . $dewplayer . '" /> 
     2436                                                <param name="flashvars" value="mp3=' . FileManagerUtility::rawurlencode_path($url) . '&amp;volume=50&amp;showtime=1" /> 
     2437                                        </object> 
     2438                                </div>'; 
     2439                } 
     2440                else 
     2441                { 
     2442                        // else: fall back to 'no preview available' 
     2443                        if (!empty($getid3->info) && empty($getid3->info['error'])) 
     2444                        { 
     2445                                try 
     2446                                { 
     2447                                        ob_start(); 
     2448                                                var_dump($getid3->info); 
     2449                                        $dump = ob_get_clean(); 
     2450                                        // $dump may dump object IDs and other binary stuff, which will completely b0rk json_encode: make it palatable: 
     2451 
     2452                                        // strip the NULs out: 
     2453                                        $dump = str_replace('&#0;', '?', $dump); 
     2454                                        //$dump = html_entity_decode(strip_tags($dump), ENT_QUOTES, 'UTF-8'); 
     2455                                        //@file_put_contents('getid3.raw.log', $dump); 
     2456                                        // since the regex matcher leaves NUL bytes alone, we do those above in undecoded form; the rest is treated here 
     2457                                        $dump = preg_replace("/[^ -~\n\r\t]/", '?', $dump); // remove everything outside ASCII range; some of the high byte values seem to crash json_encode()! 
     2458                                        // and reduce long sequences of unknown charcodes: 
     2459                                        $dump = preg_replace('/\?{8,}/', '???????', $dump); 
     2460                                        //$dump = html_entity_encode(strip_tags($dump), ENT_NOQUOTES, 'UTF-8'); 
     2461 
     2462                                        $content = '<div class="margin"> 
     2463                                                                <h2>${preview}</h2> 
     2464                                                                <pre>' . "\n" . $dump . "\n" . '</pre></div>'; 
     2465                                        //@file_put_contents('getid3.log', $dump); 
     2466                                } 
     2467                                catch(Exception $e) 
     2468                                { 
     2469                                        // ignore 
     2470                                        $content = '<div class="margin"> 
     2471                                                                ${nopreview} 
     2472                                                                <p class="err_info">' . $e->getMessage() . '</p> 
     2473                                                        </div>'; 
     2474                                } 
     2475                        } 
     2476                        else 
     2477                        { 
     2478                                $content = '<div class="margin"> 
     2479                                                        ${nopreview} 
     2480                                                        <p class="err_info">' . implode(', ', $getid3->info['error']) . '</p> 
     2481                                                </div>'; 
     2482                        } 
     2483                } 
     2484 
     2485                if (!empty($content)) 
     2486                { 
     2487                        $json['content'] = self::compressHTML($content); 
     2488                } 
     2489 
     2490                return $json; 
     2491        } 
     2492 
     2493        /** 
     2494         * Traverse the getID3 info[] array tree and fetch the item pointed at by the variable number of indices specified 
     2495         * as additional parameters to this function. 
     2496         * 
     2497         * Return the default value when the indicated element does not exist in the info[] set; otherwise return the located item. 
     2498         * 
     2499         * The purpose of this method is to act as a safe go-in-between for the fileManager to collect arbitrary getID3 data and 
     2500         * not get a PHP error when some item in there does not exist. 
     2501         */ 
     2502        public /* static */ function getID3infoItem($getid3_obj, $default_value /* , ... */ ) 
     2503        { 
     2504                $rv = false; 
     2505                $o = $getid3_obj->info; 
     2506                $argc = func_num_args(); 
     2507 
     2508                for ($i = 2; $i < $argc; $i++) 
     2509                { 
     2510                        if (!is_array($o)) 
     2511                        { 
     2512                                return $default_value; 
     2513                        } 
     2514 
     2515                        $index = func_get_arg($i); 
     2516                        if (array_key_exists($index, $o)) 
     2517                        { 
     2518                                $o = $o[$index]; 
     2519                        } 
     2520                        else 
     2521                        { 
     2522                                return $default_value; 
     2523                        } 
     2524                } 
     2525                return $o; 
     2526        } 
     2527 
     2528        // helper function for clean_EXIF_results() as PHP < 5.3 lacks lambda functions 
     2529        protected static function __clean_EXIF_results(&$value, $key) 
     2530        { 
     2531                if (is_string($value)) 
     2532                { 
     2533                        if (FileManagerUtility::isBinary($value)) 
     2534                        { 
     2535                                $value = '(binary data... length = ' . strlen($value) . ')'; 
     2536                        } 
     2537                } 
     2538        } 
     2539 
     2540        protected static function clean_EXIF_results(&$arr) 
     2541        { 
     2542                // see http://nl2.php.net/manual/en/function.array-walk-recursive.php#81835 
     2543                // --> we don't mind about it because we're not worried about the references occurring in here, now or later. 
     2544                // Indeed, that does assume we (as in 'we' being this particular function!) know about how the 
     2545                // data we process will be used. Risky, but fine with me. Hence the 'protected'. 
     2546                array_walk_recursive($arr, 'self::__clean_EXIF_results'); 
     2547        } 
     2548 
     2549        /** 
     2550         * Delete a file or directory, inclusing subdirectories and files. 
     2551         * 
     2552         * Return TRUE on success, FALSE when an error occurred. 
     2553         * 
     2554         * Note that the routine will try to percevere and keep deleting other subdirectories 
     2555         * and files, even when an error occurred for one or more of the subitems: this is 
     2556         * a best effort policy. 
     2557         */ 
     2558        protected function unlink($legal_url, $mime_filters) 
     2559        { 
     2560                $rv = true; 
     2561 
     2562                // must transform here so alias/etc. expansions inside legal_url_path2file_path() get a chance: 
     2563                $file = $this->legal_url_path2file_path($legal_url); 
     2564 
     2565                if(is_dir($file)) 
     2566                { 
     2567                        $dir = self::enforceTrailingSlash($file); 
     2568                        $url = self::enforceTrailingSlash($legal_url); 
     2569                        $files = $this->scandir($dir); 
     2570                        foreach ($files as $f) 
     2571                        { 
     2572                                if(in_array($f, array('.','..'))) 
     2573                                        continue; 
     2574 
     2575                                $rv2 = $this->unlink($url . $f, $mime_filters); 
     2576                                if ($rv2) 
     2577                                        $rv &= $this->deleteThumb($url . $f); 
     2578                                else 
     2579                                        $rv = false; 
     2580                        } 
     2581 
     2582                        $rv &= @rmdir($file); 
     2583                } 
     2584                else if (file_exists($file)) 
     2585                { 
     2586                        if (is_file($file)) 
     2587                        { 
     2588                                $mime = $this->getMimeType($file); 
     2589                                if (!$this->IsAllowedMimeType($mime, $mime_filters)) 
     2590                                        return false; 
     2591                        } 
     2592 
     2593                        $rv2 = @unlink($file); 
     2594                        if ($rv2) 
     2595                                $rv &= $this->deleteThumb($legal_url); 
     2596                        else 
     2597                                $rv = false; 
     2598                } 
     2599                return $rv; 
     2600        } 
     2601 
     2602        /** 
     2603         * glob() wrapper: accepts the same options as Tooling.php::safe_glob() 
     2604         * 
     2605         * However, this method will also ensure the '..' directory entry is only returned, 
     2606         * even while asked for, when the parent directory can be legally traversed by the FileManager. 
     2607         * 
     2608         * Always return an array (possibly empty) 
     2609         * 
     2610         * IMPORTANT: this function GUARANTEES that, when present at all, the double-dot '..' 
     2611         *            entry is the very last entry in the array. 
     2612         *            This guarantee is important as onView() et al depend on it. 
     2613         */ 
     2614        protected function scandir($dir, $filemask = '*', $see_thumbnail_dir = false) 
     2615        { 
     2616                // list files, except the thumbnail folder itself or any file in it: 
     2617                $dir = self::enforceTrailingSlash($dir); 
     2618 
     2619                $just_below_thumbnail_dir = false; 
     2620                if (!$see_thumbnail_dir) 
     2621                { 
     2622                        $tnpath = $this->url_path2file_path($this->options['thumbnailPath']); 
     2623                        if (FileManagerUtility::startswith($dir, $tnpath)) 
     2624                                return false; 
     2625 
     2626                        $tnparent = $this->url_path2file_path(self::getParentDir($this->options['thumbnailPath'])); 
     2627                        $just_below_thumbnail_dir = ($dir == $tnparent); 
     2628 
     2629                        $tndir = basename(substr($this->options['thumbnailPath'], 0, -1)); 
     2630                } 
     2631 
     2632                $at_basedir = ($this->url_path2file_path($this->options['directory']) == $dir); 
     2633 
     2634 
     2635                $files = safe_glob($dir . $filemask, GLOB_NODOTS | GLOB_NOSORT); 
     2636 
     2637 
     2638                if ($just_below_thumbnail_dir) 
     2639                { 
     2640                        $f = array(); 
     2641                        foreach($files as $file) 
     2642                        { 
     2643                                if ($file !== $tndir) 
     2644                                        $f[] = $file; 
     2645                        } 
     2646                        unset($files); 
     2647                        $files = $f; 
     2648                } 
     2649 
     2650                if (!$at_basedir) 
     2651                { 
     2652                        $files[] = '..'; 
     2653                } 
     2654 
     2655                return $files; 
     2656        } 
     2657 
     2658        /** 
     2659         * Make a cleaned-up, unique filename 
     2660         * 
     2661         * Return the file (dir + name + ext), or a unique, yet non-existing, variant thereof, where the filename 
     2662         * is appended with a '_' and a number, e.g. '_1', when the file itself already exists in the given 
     2663         * directory. The directory part of the returned value equals $dir. 
     2664         * 
     2665         * Return NULL when $file is empty or when the specified directory does not reside within the 
     2666         * directory tree rooted by options['directory'] 
     2667         * 
     2668         * Note that the given filename will be converted to a legal filename, containing a filesystem-legal 
     2669         * subset of ASCII characters only, before being used and returned by this function. 
     2670         * 
     2671         * @param mixed $fileinfo     either a string containing a filename+ext or an array as produced by pathinfo(). 
     2672         * @daram string $dir         path pointing at where the given file may exist. 
     2673         * 
     2674         * @return a filepath consisting of $dir and the cleaned up and possibly sequenced filename and file extension 
     2675         *         as provided by $fileinfo. 
     2676         */ 
     2677        protected function getUniqueName($fileinfo, $dir) 
     2678        { 
     2679                $dir = self::enforceTrailingSlash($dir); 
     2680 
     2681                if (is_string($fileinfo)) 
     2682                { 
     2683                        $fileinfo = pathinfo($fileinfo); 
     2684                } 
     2685 
     2686                if (!is_array($fileinfo) || !$fileinfo['filename']) return null; 
     2687 
     2688 
     2689                /* 
     2690                 * since 'pagetitle()' is used to produce a unique, non-existing filename, we can forego the dirscan 
     2691                 * and simply check whether the constructed filename/path exists or not and bump the suffix number 
     2692                 * by 1 until it does not, thus quickly producing a unique filename. 
     2693                 * 
     2694                 * This is faster than using a dirscan to collect a set of existing filenames and feeding them as 
     2695                 * an option array to pagetitle(), particularly for large directories. 
     2696                 */ 
     2697                $filename = FileManagerUtility::pagetitle($fileinfo['filename'], null, '-_., []()~!@+' /* . '#&' */, '-_,~@+#&'); 
     2698                if (!$filename) 
     2699                        return null; 
     2700 
     2701                // also clean up the extension: only allow alphanumerics in there! 
     2702                $ext = FileManagerUtility::pagetitle(!empty($fileinfo['extension']) ? $fileinfo['extension'] : null); 
     2703                $ext = (!empty($ext) ? '.' . $ext : null); 
     2704                // make sure the generated filename is SAFE: 
     2705                $fname = $filename . $ext; 
     2706                $file = $dir . $fname; 
     2707                if (file_exists($file)) 
     2708                { 
     2709                        /* 
     2710                         * make a unique name. Do this by postfixing the filename with '_X' where X is a sequential number. 
     2711                         * 
     2712                         * Note that when the input name is already such a 'sequenced' name, the sequence number is 
     2713                         * extracted from it and sequencing continues from there, hence input 'file_5' would, if it already 
     2714                         * existed, thus be bumped up to become 'file_6' and so on, until a filename is found which 
     2715                         * does not yet exist in the designated directory. 
     2716                         */ 
     2717                        $i = 1; 
     2718                        if (preg_match('/^(.*)_([1-9][0-9]*)$/', $filename, $matches)) 
     2719                        { 
     2720                                $i = intval($matches[2]); 
     2721                                if ('P'.$i != 'P'.$matches[2] || $i > 100000) 
     2722                                { 
     2723                                        // very large number: not a sequence number! 
     2724                                        $i = 1; 
     2725                                } 
     2726                                else 
     2727                                { 
     2728                                        $filename = $matches[1]; 
     2729                                } 
     2730                        } 
     2731                        do 
     2732                        { 
     2733                                $fname = $filename . ($i ? '_' . $i : '') . $ext; 
     2734                                $file = $dir . $fname; 
     2735                                $i++; 
     2736                        } while (file_exists($file)); 
     2737                } 
     2738 
     2739                // $fname is now guaranteed to NOT exist in the given directory 
     2740                return $fname; 
     2741        } 
     2742 
     2743        /** 
     2744         * Returns the URI path to the apropriate icon image for the given file / directory. 
     2745         * 
     2746         * NOTES: 
     2747         * 
     2748         * 1) any $path with an 'extension' of '.dir' is assumed to be a directory. 
     2749         * 
     2750         * 2) This method specifically does NOT check whether the given path exists or not: it just looks at 
     2751         *    the filename extension passed to it, that's all. 
     2752         * 
     2753         * Note #2 is important as this enables this function to also serve as icon fetcher for ZIP content viewer, etc.: 
     2754         * after all, those files do not exist physically on disk themselves! 
     2755         */ 
     2756        public function getIcon($file, $smallIcon) 
     2757        { 
     2758                $ext = pathinfo($file, PATHINFO_EXTENSION); 
     2759 
     2760                $largeDir = (!$smallIcon ? 'Large/' : ''); 
     2761                $url_path = $this->options['assetBasePath'] . 'Images/Icons/' .$largeDir.$ext.'.png'; 
     2762                $path = (is_file($this->url_path2file_path($url_path))) 
     2763                        ? $url_path 
     2764                        : $this->options['assetBasePath'] . 'Images/Icons/'.$largeDir.'default.png'; 
     2765 
     2766                return $path; 
     2767        } 
     2768 
     2769        /** 
     2770         * Return the path to the thumbnail of the specified image, the thumbnail having its 
     2771         * width and height limited to $width pixels. 
     2772         * 
     2773         * When the thumbnail image does not exist yet, it will created on the fly. 
     2774         * 
     2775         * @param string $legal_url    the LEGAL URL path to the original image. Is used solely 
     2776         *                             to generate a suitable thumbnail filename. 
     2777         * 
     2778         * @param string $path         filesystem path to the original image. Is used to derive 
     2779         *                             the thumbnail content from. 
     2780         * 
     2781         * @param integer $width       the maximum number of pixels for width of the 
     2782         *                             thumbnail. 
     2783         * 
     2784         * @param integer $height      the maximum number of pixels for height of the 
     2785         *                             thumbnail. 
     2786         */ 
     2787        public function getThumb($legal_url, $path, $width, $height) 
     2788        { 
     2789                $thumb = $this->generateThumbName($legal_url, $width); 
     2790                $thumbPath = $this->url_path2file_path($this->options['thumbnailPath'] . $thumb); 
     2791                if (!is_file($thumbPath)) 
     2792                { 
     2793                        if (!file_exists(dirname($thumbPath))) 
     2794                        { 
     2795                                @mkdir(dirname($thumbPath), $this->options['chmod'], true); 
     2796                        } 
     2797                        $img = new Image($path); 
     2798                        // generally save as lossy / lower-Q jpeg to reduce filesize, unless orig is PNG/GIF, higher quality for smaller thumbnails: 
     2799                        $img->resize($width,$height)->save($thumbPath, min(98, max(MTFM_THUMBNAIL_JPEG_QUALITY, MTFM_THUMBNAIL_JPEG_QUALITY + 0.15 * (250 - min($width, $height)))), true); 
     2800                        unset($img); 
     2801                } 
     2802                return $this->options['thumbnailPath'] . $thumb; 
     2803        } 
     2804 
     2805        /** 
     2806         * Assitant function which produces the best possible icon image path for the given error/exception message. 
     2807         */ 
     2808        public function getIconForError($emsg, $original_filename, $small_icon) 
     2809        { 
     2810                if (empty($emsg)) 
     2811                { 
     2812                        // just go and pick the extension-related icon for this one; nothing is wrong today, it seems. 
     2813                        $thumb_path = (!empty($original_filename) ? $original_filename : 'is.default-missing'); 
     2814                } 
     2815                else 
     2816                { 
     2817                        $thumb_path = 'is.default-error'; 
     2818 
     2819                        if (strpos($emsg, 'img_will_not_fit') !== false) 
     2820                        { 
     2821                                $thumb_path = 'is.oversized_img'; 
     2822                        } 
     2823                        else if (strpos($emsg, 'nofile') !== false) 
     2824                        { 
     2825                                $thumb_path = 'is.default-missing'; 
     2826                        } 
     2827                        else if (strpos($emsg, 'unsupported_imgfmt') !== false) 
     2828                        { 
     2829                                // just go and pick the extension-related icon for this one; nothing seriously wrong here. 
     2830                                $thumb_path = (!empty($original_filename) ? $original_filename : $thumb_path); 
     2831                        } 
     2832                        else if (strpos($emsg, 'image') !== false) 
     2833                        { 
     2834                                $thumb_path = 'badly.broken_img'; 
     2835                        } 
     2836                } 
     2837 
     2838                $img_filepath = $this->getIcon($thumb_path, $small_icon); 
     2839 
     2840                return $img_filepath; 
     2841        } 
     2842 
     2843        /** 
     2844         * Make sure the generated thumbpath is unique for each file. To prevent 
     2845         * reduced performance for large file sets: all thumbnails derived from any files in the entire 
     2846         * FileManager-managed directory tree, rooted by options['directory'], can become a huge collection, 
     2847         * so we distribute them across a directory tree, which is created on demand. 
     2848         * 
     2849         * The thumbnails directory tree is determined by the MD5 of the full path to the image, 
     2850         * using the first two characters of the MD5, making for a span of 256. 
     2851         * 
     2852         * Note: when you expect to manage a really HUGE file collection from FM, you may dial up the 
     2853         *       $number_of_dir_levels to 2 here. 
     2854         */ 
     2855        protected function generateThumbName($legal_url, $width = 250, $number_of_dir_levels = MTFM_NUMBER_OF_DIRLEVELS_FOR_CACHE) 
     2856        { 
     2857                $fi = pathinfo($legal_url); 
     2858                $ext = strtolower(!empty($fi['extension']) ? $fi['extension'] : ''); 
     2859                switch ($ext) 
     2860                { 
     2861                case 'gif': 
     2862                case 'png': 
     2863                case 'jpg': 
     2864                case 'jpeg': 
     2865                        break; 
     2866 
     2867                default: 
     2868                        // default to PNG, as it'll handle transparancy and full color both: 
     2869                        $ext = 'png'; 
     2870                        break; 
     2871                } 
     2872 
     2873                // as the Thumbnail is generated, but NOT guaranteed from a safe filepath (FM may be visiting unsafe 
     2874                // image files when they exist in a preloaded directory tree!) we do the full safe-filename transform 
     2875                // on the name itself. 
     2876                // The MD5 is taken from the untrammeled original, though: 
     2877                $dircode = md5($legal_url); 
     2878 
     2879                $rv = ''; 
     2880                for ($i = 0; $i < $number_of_dir_levels; $i++) 
     2881                { 
     2882                        $rv .= substr($dircode, 0, 2) . '/'; 
     2883                        $dircode = substr($dircode, 2); 
     2884                } 
     2885 
     2886                $fn = '_' . $fi['filename']; 
     2887                $fn = substr($dircode, 0, 4) . preg_replace('/[^A-Za-z0-9]+/', '_', $fn); 
     2888                $fn = substr($fn . $dircode, 0, 38); 
     2889                $ext = preg_replace('/[^A-Za-z0-9_]+/', '_', $ext); 
     2890 
     2891                $rv .= $fn . '-' . $width . '.' . $ext; 
     2892                return $rv; 
     2893        } 
     2894 
     2895        protected function deleteThumb($legal_url) 
     2896        { 
     2897                // generate a thumbnail name with embedded wildcard for the size parameter: 
     2898                $thumb = $this->generateThumbName($legal_url, '*'); 
     2899                $tfi = pathinfo($thumb); 
     2900                $thumbnail_subdir = $tfi['dirname']; 
     2901                $thumbPath = $this->url_path2file_path($this->options['thumbnailPath'] . $thumbnail_subdir); 
     2902                $thumbPath = self::enforceTrailingSlash($thumbPath); 
     2903 
     2904                // remove thumbnails (any size) and any other related cached files (TODO: future version should cache getID3 metadata as well -- and delete it here!) 
     2905                $files = $this->scandir($thumbPath, $tfi['filename'] . '.*', true); 
     2906 
     2907                $rv = true; 
     2908                if (is_array($files)) 
     2909                { 
     2910                        foreach($files as $filename) 
     2911                        { 
     2912                                if(in_array($filename, array('.','..'))) 
     2913                                        continue; 
     2914 
     2915                                $file = $thumbPath . $filename; 
     2916                                if(is_file($file)) 
     2917                                        $rv &= @unlink($file); 
     2918                        } 
     2919                } 
     2920 
     2921                // as the thumbnail subdirectory may now be entirely empty, try to remove it as well, 
     2922                // but do NOT yack when we don't succeed: there may be other thumbnails, etc. in there still! 
     2923 
     2924                while ($thumbnail_subdir > '/') 
     2925                { 
     2926                        // try to NOT delete the thumbnails base directory itself; we MAY not be able to recreate it later on demand! 
     2927                        $thumbPath = $this->url_path2file_path($this->options['thumbnailPath'] . $thumbnail_subdir); 
     2928                        @rmdir($thumbPath); 
     2929 
     2930                        $thumbnail_subdir = self::getParentDir($thumbnail_subdir); 
     2931                } 
     2932 
     2933                return $rv;   // when thumbnail does not exist, say it is succesfully removed: all that counts is it doesn't exist anymore when we're done here. 
     2934        } 
     2935 
     2936 
     2937 
     2938 
     2939 
     2940 
     2941 
     2942 
     2943 
     2944 
     2945        /** 
     2946         * Safe replacement of dirname(); does not care whether the input has a trailing slash or not. 
     2947         * 
     2948         * Return FALSE when the path is attempting to get the parent of '/' 
     2949         */ 
     2950        public static function getParentDir($path) 
     2951        { 
     2952                /* 
     2953                 * on Windows, you get: 
     2954                 * 
     2955                 * dirname("/") = "\" 
     2956                 * dirname("y/") = "." 
     2957                 * dirname("/x") = "\" 
     2958                 * 
     2959                 * so we'd rather not use dirname()   :-( 
     2960                 */ 
     2961                if (!is_string($path)) 
     2962                        return false; 
     2963                $path = rtrim($path, '/'); 
     2964                // empty directory or a path with only 1 character in it cannot be a parent+child: that would be 2 at the very least when it's '/a': parent is root '/' then: 
     2965                if (strlen($path) <= 1) 
     2966                        return false; 
     2967 
     2968                $p2 = strrpos($path, '/' /* , -1 */ );  // -1 as extra offset is not good enough? Nope. At least not for my Win32 PHP 5.3.1. Yeah, sounds like a PHP bug to me. So we rtrim() now... 
     2969                if ($p2 === false) 
     2970                { 
     2971                        return false; // tampering! 
     2972                } 
     2973                $prev = substr($path, 0, $p2 + 1); 
     2974                return $prev; 
     2975        } 
     2976 
     2977        /** 
     2978         * Return the URI absolute path to the directory pointed at by the current URI request. 
     2979         * For example, if the request was 'http://site.org/dir1/dir2/script', then this method will 
     2980         * return '/dir1/dir2/'. 
     2981         * 
     2982         * Note that the path is returned WITH a trailing slash '/'. 
     2983         */ 
     2984        public /* static */ function getRequestPath() 
     2985        { 
     2986                // see also: http://php.about.com/od/learnphp/qt/_SERVER_PHP.htm 
     2987                $path = self::getParentDir(str_replace('\\', '/', $_SERVER['SCRIPT_NAME'])); 
     2988                $path = self::enforceTrailingSlash($path); 
     2989 
     2990                return $path; 
     2991        } 
     2992 
     2993        /** 
     2994         * Normalize an absolute path by converting all slashes '/' and/or backslashes '\' and any mix thereof in the 
     2995         * specified path to UNIX/MAC/Win compatible single forward slashes '/'. 
     2996         * 
     2997         * Also roll up any ./ and ../ directory elements in there. 
     2998         * 
     2999         * Throw an exception when the operation failed to produce a legal path. 
     3000         */ 
     3001        public /* static */ function normalize($path) 
     3002        { 
     3003                $path = preg_replace('/(\\\|\/)+/', '/', $path); 
     3004 
     3005                /* 
     3006                 * fold '../' directory parts to prevent malicious paths such as 'a/../../../../../../../../../etc/' 
     3007                 * from succeeding 
     3008                 * 
     3009                 * to prevent screwups in the folding code, we FIRST clean out the './' directories, to prevent 
     3010                 * 'a/./.././.././.././.././.././.././.././.././../etc/' from succeeding: 
     3011                 */ 
     3012                $path = preg_replace('#/(\./)+#', '/', $path); 
     3013 
     3014                // now temporarily strip off the leading part up to the colon to prevent entries like '../d:/dir' to succeed when the site root is 'c:/', for example: 
     3015                $lead = ''; 
     3016                // the leading part may NOT contain any directory separators, as it's for drive letters only. 
     3017                // So we must check in order to prevent malice like /../../../../../../../c:/dir from making it through. 
     3018                if (preg_match('#^([A-Za-z]:)?/(.*)$#', $path, $matches)) 
     3019                { 
     3020                        $lead = $matches[1]; 
     3021