From adeccb6683b77c3d27109e84fa4e2094471ab94a Mon Sep 17 00:00:00 2001 From: Marco Dickert Date: Tue, 21 Jul 2020 17:58:44 +0200 Subject: [PATCH 01/11] Sane coding style for PHP part. Signed-off-by: Marco Dickert --- compiler.php | 76 +- src/assets.cdn.css | 8 + src/assets.cdn.js | 15 + src/assets.cdn.part | 32 - src/assets.css | 8 + src/assets.js | 16 + src/assets.part | 31 - src/ifm.js | 291 +++++--- src/main.php | 1533 +++++++++++++++++++------------------- src/templates/login.html | 6 +- 10 files changed, 1024 insertions(+), 992 deletions(-) create mode 100644 src/assets.cdn.css create mode 100644 src/assets.cdn.js delete mode 100644 src/assets.cdn.part create mode 100644 src/assets.css create mode 100644 src/assets.js delete mode 100644 src/assets.part diff --git a/compiler.php b/compiler.php index 25d1888..e1a600f 100755 --- a/compiler.php +++ b/compiler.php @@ -9,21 +9,21 @@ chdir(realpath(dirname(__FILE__))); // output files and common attrs -define( "IFM_VERSION", "v2.6.2" ); -define( "IFM_RELEASE_DIR", "dist/"); -define( "IFM_STANDALONE", "ifm.php" ); -define( "IFM_STANDALONE_GZ", "ifm.min.php" ); -define( "IFM_LIB", "libifm.php" ); +define("IFM_VERSION", "v2.6.2"); +define("IFM_RELEASE_DIR", "dist/"); +define("IFM_STANDALONE", "ifm.php"); +define("IFM_STANDALONE_GZ", "ifm.min.php"); +define("IFM_LIB", "libifm.php"); // php source files -$IFM_SRC_PHP = array( +$IFM_SRC_PHP = [ 0 => "src/main.php", 1 => "src/ifmarchive.php", 2 => "src/htpasswd.php" -); +]; // get options -$options = getopt(null, array("language::", "languages::", "lang::", "cdn")); +$options = getopt(null, ["language::", "languages::", "lang::", "cdn"]); // build CDN version? if (isset($options['cdn'])) @@ -36,6 +36,7 @@ $langs = []; foreach ($options as $key => $value) if (substr($key, 0, 4) == "lang") $langs = array_merge($langs, explode(",", $value)); + $langs = array_unique($langs); $vars['default_lang'] = ($langs[0] == "all") ? "en" : $langs[0]; // ensure english is available, as it gets merged with the other languages @@ -61,68 +62,65 @@ foreach ($langs as $l) print "WARNING: Language file src/i18n/".$l.".json not found.\n"; // Concat PHP Files -$compiled = array( "run(); -', FILE_APPEND ); +', FILE_APPEND); // build compressed ifm file_put_contents( IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_STANDALONE_GZ, '' - . gzencode( file_get_contents( IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') .IFM_STANDALONE, false, null, 5 ) ) + . gzencode(file_get_contents(IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_STANDALONE, false, null, 5)) ); // build lib -file_put_contents( IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_LIB, $compiled ); +file_put_contents(IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : '') . IFM_LIB, $compiled); diff --git a/src/assets.cdn.css b/src/assets.cdn.css new file mode 100644 index 0000000..104faf8 --- /dev/null +++ b/src/assets.cdn.css @@ -0,0 +1,8 @@ + + + diff --git a/src/assets.cdn.js b/src/assets.cdn.js new file mode 100644 index 0000000..bf4c71a --- /dev/null +++ b/src/assets.cdn.js @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/assets.cdn.part b/src/assets.cdn.part deleted file mode 100644 index 636d29c..0000000 --- a/src/assets.cdn.part +++ /dev/null @@ -1,32 +0,0 @@ - public function getCSS() { - print ' - - - - - - - '; - } - - public function getJS() { - print ' - - - - - - - - - - - '; - echo <<<'f00bar' - -f00bar; - } \ No newline at end of file diff --git a/src/assets.css b/src/assets.css new file mode 100644 index 0000000..b8e4b3d --- /dev/null +++ b/src/assets.css @@ -0,0 +1,8 @@ + diff --git a/src/assets.js b/src/assets.js new file mode 100644 index 0000000..e18ee72 --- /dev/null +++ b/src/assets.js @@ -0,0 +1,16 @@ + diff --git a/src/assets.part b/src/assets.part deleted file mode 100644 index 45281ed..0000000 --- a/src/assets.part +++ /dev/null @@ -1,31 +0,0 @@ - public function getCSS() { - print ' - - - - - - - '; - } - - public function getJS() { - echo <<<'f00bar' - -f00bar; - } diff --git a/src/ifm.js b/src/ifm.js index 7edf868..d03b5f2 100644 --- a/src/ifm.js +++ b/src/ifm.js @@ -289,132 +289,133 @@ function IFM(params) { }); if( self.config.contextmenu && !!( self.config.edit || self.config.extract || self.config.rename || self.config.copymove || self.config.download || self.config.delete ) ) { - // create the context menu, this also uses jquery, AFAIK - var contextMenu = new BootstrapMenu( '.clickable-row', { - fetchElementData: function( row ) { - var data = {}; - data.selected = - Array.prototype.slice.call( document.getElementsByClassName( 'selectedItem' ) ) - .map( function(e){ return self.fileCache.find( x => x.guid == e.children[0].children[0].id ); } ); - data.clicked = self.fileCache.find( x => x.guid == row[0].children[0].children[0].id ); - return data; - }, - actionsGroups:[ - ['edit', 'extract', 'rename', 'copylink'], - ['copymove', 'download', 'createarchive', 'delete'] - ], - actions: { - edit: { - name: self.i18n.edit, - onClick: function( data ) { - self.editFile( data.clicked.name ); - }, - iconClass: "icon icon-pencil", - isShown: function( data ) { - return !!( self.config.edit && data.clicked.eaction == "edit" && !data.selected.length ); - } + // initialize the context menu, this also uses jquery, AFAIK + if (!self.contextMenu) + self.contextMenu = new BootstrapMenu( '.clickable-row', { + fetchElementData: function( row ) { + var data = {}; + data.selected = + Array.prototype.slice.call( document.getElementsByClassName( 'selectedItem' ) ) + .map( function(e){ return self.fileCache.find( x => x.guid == e.children[0].children[0].id ); } ); + data.clicked = self.fileCache.find( x => x.guid == row[0].children[0].children[0].id ); + return data; }, - extract: { - name: self.i18n.extract, - onClick: function( data ) { - self.showExtractFileDialog( data.clicked.name ); - }, - iconClass: "icon icon-archive", - isShown: function( data ) { - return !!( self.config.extract && data.clicked.eaction == "extract" && !data.selected.length ); - } - }, - rename: { - name: self.i18n.rename, - onClick: function( data ) { - self.showRenameFileDialog( data.clicked.name ); - }, - iconClass: "icon icon-terminal", - isShown: function( data ) { return !!( self.config.rename && !data.selected.length && data.clicked.name != ".." ); } - }, - copylink: { - name: self.i18n.copylink, - onClick: function( data ) { - if( data.clicked.link.toLowerCase().substr(0,4) == "http" ) - self.copyToClipboard( data.clicked.link ); - else { - var pathname = window.location.pathname.replace( /^\/*/g, '' ).split( '/' ); - pathname.pop(); - var link = self.pathCombine( window.location.origin, data.clicked.link ) - if( pathname.length > 0 ) - link = self.pathCombine( window.location.origin, pathname.join( '/' ), data.clicked.link ) - self.copyToClipboard( link ); + actionsGroups:[ + ['edit', 'extract', 'rename', 'copylink'], + ['copymove', 'download', 'createarchive', 'delete'] + ], + actions: { + edit: { + name: self.i18n.edit, + onClick: function( data ) { + self.editFile( data.clicked.name ); + }, + iconClass: "icon icon-pencil", + isShown: function( data ) { + return !!( self.config.edit && data.clicked.eaction == "edit" && !data.selected.length ); } }, - iconClass: "icon icon-link-ext", - isShown: function( data ) { return !!( !data.selected.length && data.clicked.name != ".." ); } - }, - copymove: { - name: function( data ) { - if( data.selected.length > 0 ) - return self.i18n.copy+'/'+self.i18n.move+' '+data.selected.length+''; - else - return self.i18n.copy+'/'+self.i18n.move; + extract: { + name: self.i18n.extract, + onClick: function( data ) { + self.showExtractFileDialog( data.clicked.name ); + }, + iconClass: "icon icon-archive", + isShown: function( data ) { + return !!( self.config.extract && data.clicked.eaction == "extract" && !data.selected.length ); + } }, - onClick: function( data ) { - if( data.selected.length > 0 ) - self.showCopyMoveDialog( data.selected ); - else - self.showCopyMoveDialog( data.clicked ); + rename: { + name: self.i18n.rename, + onClick: function( data ) { + self.showRenameFileDialog( data.clicked.name ); + }, + iconClass: "icon icon-terminal", + isShown: function( data ) { return !!( self.config.rename && !data.selected.length && data.clicked.name != ".." ); } }, - iconClass: "icon icon-folder-empty", - isShown: function( data ) { return !!( self.config.copymove && data.clicked.name != ".." ); } - }, - download: { - name: function( data ) { - if( data.selected.length > 0 ) - return self.i18n.download+' '+data.selected.length+''; - else - return self.i18n.download; + copylink: { + name: self.i18n.copylink, + onClick: function( data ) { + if( data.clicked.link.toLowerCase().substr(0,4) == "http" ) + self.copyToClipboard( data.clicked.link ); + else { + var pathname = window.location.pathname.replace( /^\/*/g, '' ).split( '/' ); + pathname.pop(); + var link = self.pathCombine( window.location.origin, data.clicked.link ) + if( pathname.length > 0 ) + link = self.pathCombine( window.location.origin, pathname.join( '/' ), data.clicked.link ) + self.copyToClipboard( link ); + } + }, + iconClass: "icon icon-link-ext", + isShown: function( data ) { return !!( !data.selected.length && data.clicked.name != ".." ); } }, - onClick: function( data ) { - if( data.selected.length > 0 ) - self.showMessage( "At the moment it is not possible to download a set of files." ); - else - document.forms["d_"+data.clicked.guid].submit(); + copymove: { + name: function( data ) { + if( data.selected.length > 0 ) + return self.i18n.copy+'/'+self.i18n.move+' '+data.selected.length+''; + else + return self.i18n.copy+'/'+self.i18n.move; + }, + onClick: function( data ) { + if( data.selected.length > 0 ) + self.showCopyMoveDialog( data.selected ); + else + self.showCopyMoveDialog( data.clicked ); + }, + iconClass: "icon icon-folder-empty", + isShown: function( data ) { return !!( self.config.copymove && data.clicked.name != ".." ); } }, - iconClass: "icon icon-download", - isShown: function() { return !!self.config.download; } - }, - createarchive: { - name: function( data ) { - if( data.selected.length > 0 ) - return self.i18n.create_archive+' '+data.selected.length+''; - else - return self.i18n.create_archive; + download: { + name: function( data ) { + if( data.selected.length > 0 ) + return self.i18n.download+' '+data.selected.length+''; + else + return self.i18n.download; + }, + onClick: function( data ) { + if( data.selected.length > 0 ) + self.showMessage( "At the moment it is not possible to download a set of files." ); + else + document.forms["d_"+data.clicked.guid].submit(); + }, + iconClass: "icon icon-download", + isShown: function() { return !!self.config.download; } }, - onClick: function( data ) { - if( data.selected.length > 0 ) - self.showCreateArchiveDialog( data.selected ); - else - self.showCreateArchiveDialog( data.clicked ); + createarchive: { + name: function( data ) { + if( data.selected.length > 0 ) + return self.i18n.create_archive+' '+data.selected.length+''; + else + return self.i18n.create_archive; + }, + onClick: function( data ) { + if( data.selected.length > 0 ) + self.showCreateArchiveDialog( data.selected ); + else + self.showCreateArchiveDialog( data.clicked ); + }, + iconClass: "icon icon-archive", + isShown: function( data ) { return !!( self.config.createarchive && data.clicked.name != ".." ); } }, - iconClass: "icon icon-archive", - isShown: function( data ) { return !!( self.config.createarchive && data.clicked.name != ".." ); } - }, - 'delete': { - name: function( data ) { - if( data.selected.length > 0 ) - return self.i18n.delete+' '+data.selected.length+''; - else - return self.i18n.delete; - }, - onClick: function( data ) { - if( data.selected.length > 0 ) - self.showDeleteDialog( data.selected ); - else - self.showDeleteDialog( data.clicked ); - }, - iconClass: "icon icon-trash", - isShown: function( data ) { return !!( self.config.delete && data.clicked.name != ".." ); } + 'delete': { + name: function( data ) { + if( data.selected.length > 0 ) + return self.i18n.delete+' '+data.selected.length+''; + else + return self.i18n.delete; + }, + onClick: function( data ) { + if( data.selected.length > 0 ) + self.showDeleteDialog( data.selected ); + else + self.showDeleteDialog( data.clicked ); + }, + iconClass: "icon icon-trash", + isShown: function( data ) { return !!( self.config.delete && data.clicked.name != ".." ); } + } } - } - }); + }); } }; @@ -1533,9 +1534,9 @@ function IFM(params) { * * @param string m - message text */ - this.log = function( m ) { - if( self.config.debug ) { - console.log( "IFM (debug): " + m ); + this.log = function(m) { + if (self.config.debug) { + console.log("IFM (debug): " + m); } }; @@ -1834,8 +1835,8 @@ function IFM(params) { dataType: "json", success: function(d) { self.i18n = d; - self.log( "I18N loaded" ); - self.initApplication(); + self.log("I18N loaded"); + self.initCheckAuth(); }, error: function() { throw new Error( self.i18n.load_text_error ); @@ -1843,6 +1844,50 @@ function IFM(params) { }); }; + this.initCheckAuth = function() { + $.ajax({ + url: self.api, + type: "POST", + data: { + api: "checkAuth" + }, + dataType: "json", + success: function(d) { + if (d.status == "ERROR") { + self.showModal(Mustache.render(self.templates.login, {i18n: self.i18n}), {large: true}); + + var form = document.forms.loginForm; + form.addEventListener('click', function(e) { + if (e.target.id == "buttonLogin") { + $.ajax({ + url: self.api, + type: "POST", + data: { + api: "checkAuth", + inputLogin: form.elements[0].value, + inputPassword: form.elements[1].value + }, + dataType: "json", + success: function(e) { + self.hideModal(); + self.initApplication(); + }, + error: function(e) { + self.showMessage("Authentication failed", "e"); + } + }); + } + }); + } else { + self.initApplication(); + } + }, + error: function(resp) { + throw new Error("Not authenticated"); + } + }); + }; + this.initApplication = function() { self.rootElement.innerHTML = Mustache.render( self.templates.app, @@ -1994,8 +2039,8 @@ function IFM(params) { } }; - this.init = function( id ) { - self.rootElement = document.getElementById( id ); + this.init = function(id) { + self.rootElement = document.getElementById(id); this.initLoadConfig(); }; } diff --git a/src/main.php b/src/main.php index 36d4148..a620bb3 100644 --- a/src/main.php +++ b/src/main.php @@ -10,19 +10,28 @@ * main */ -error_reporting( E_ALL ); -ini_set( 'display_errors', 'OFF' ); +error_reporting(E_ALL); +ini_set('display_errors', 0); + +class IFMException extends Exception { + public $forUser = false; + public function __construct($message, $forUser = false, $code = 0, Exception $previous = null) { + $this->forUser = $forUser; + parent::__construct($message, $code, $previous); + } +} class IFM { - private $defaultconfig = array( + private $defaultconfig = [ // general config "auth" => 0, "auth_source" => 'inline;admin:$2y$10$0Bnm5L4wKFHRxJgNq.oZv.v7yXhkJZQvinJYR2p6X1zPvzyDRUVRC', + "auth_ignore_basic" => 0, "root_dir" => "", "root_public_url" => "", "tmp_dir" => "", "timezone" => "", - "forbiddenChars" => array(), + "forbiddenChars" => [], "dateLocale" => "en-US", "language" => "@@@vars:default_lang@@@", "selfoverwrite" => 0, @@ -58,73 +67,202 @@ class IFM { "showrefresh" => 1, "forceproxy" => 0, "confirmoverwrite" => 1 - ); + ]; - private $config = array(); - private $templates = array(); - private $i18n = array(); + private $config = []; + private $templates = []; + private $i18n = []; public $mode = "standalone"; - public function __construct( $config=array() ) { - + public function __construct($config=[]) { // load the default config $this->config = $this->defaultconfig; // load config from environment variables - $this->config['auth'] = getenv('IFM_AUTH') !== false ? intval( getenv('IFM_AUTH') ) : $this->config['auth'] ; - $this->config['auth_source'] = getenv('IFM_AUTH_SOURCE') !== false ? getenv('IFM_AUTH_SOURCE') : $this->config['auth_source'] ; - $this->config['root_dir'] = getenv('IFM_ROOT_DIR') !== false ? getenv('IFM_ROOT_DIR') : $this->config['root_dir'] ; - $this->config['root_public_url'] = getenv('IFM_ROOT_PUBLIC_URL') !== false ? getenv('IFM_ROOT_PUBLIC_URL') : $this->config['root_public_url'] ; - $this->config['tmp_dir'] = getenv('IFM_TMP_DIR') !== false ? getenv('IFM_TMP_DIR') : $this->config['tmp_dir'] ; - $this->config['timezone'] = getenv('IFM_TIMEZONE') !== false ? getenv('IFM_TIMEZONE') : $this->config['timezone'] ; - $this->config['dateLocale'] = getenv('IFM_DATELOCALE') !== false ? getenv('IFM_DATELOCALE') : $this->config['dateLocale'] ; - $this->config['forbiddenChars'] = getenv('IFM_FORBIDDENCHARS') !== false ? str_split( getenv('IFM_FORBIDDENCHARS') ) : $this->config['forbiddenChars'] ; - $this->config['language'] = getenv('IFM_LANGUAGE') !== false ? getenv('IFM_LANGUAGE') : $this->config['language'] ; - $this->config['selfoverwrite'] = getenv('IFM_SELFOVERWRITE') !== false ? getenv('IFM_SELFOVERWRITE') : $this->config['selfoverwrite'] ; - $this->config['ajaxrequest'] = getenv('IFM_API_AJAXREQUEST') !== false ? intval( getenv('IFM_API_AJAXREQUEST') ) : $this->config['ajaxrequest'] ; - $this->config['chmod'] = getenv('IFM_API_CHMOD') !== false ? intval( getenv('IFM_API_CHMOD') ) : $this->config['chmod'] ; - $this->config['copymove'] = getenv('IFM_API_COPYMOVE') !== false ? intval( getenv('IFM_API_COPYMOVE') ) : $this->config['copymove'] ; - $this->config['createdir'] = getenv('IFM_API_CREATEDIR') !== false ? intval( getenv('IFM_API_CREATEDIR') ) : $this->config['createdir'] ; - $this->config['createfile'] = getenv('IFM_API_CREATEFILE') !== false ? intval( getenv('IFM_API_CREATEFILE') ) : $this->config['createfile'] ; - $this->config['edit'] = getenv('IFM_API_EDIT') !== false ? intval( getenv('IFM_API_EDIT') ) : $this->config['edit'] ; - $this->config['delete'] = getenv('IFM_API_DELETE') !== false ? intval( getenv('IFM_API_DELETE') ) : $this->config['delete'] ; - $this->config['download'] = getenv('IFM_API_DOWNLOAD') !== false ? intval( getenv('IFM_API_DOWNLOAD') ) : $this->config['download'] ; - $this->config['extract'] = getenv('IFM_API_EXTRACT') !== false ? intval( getenv('IFM_API_EXTRACT') ) : $this->config['extract'] ; - $this->config['upload'] = getenv('IFM_API_UPLOAD') !== false ? intval( getenv('IFM_API_UPLOAD') ) : $this->config['upload'] ; - $this->config['remoteupload'] = getenv('IFM_API_REMOTEUPLOAD') !== false ? intval( getenv('IFM_API_REMOTEUPLOAD') ) : $this->config['remoteupload'] ; - $this->config['rename'] = getenv('IFM_API_RENAME') !== false ? intval( getenv('IFM_API_RENAME') ) : $this->config['rename'] ; - $this->config['zipnload'] = getenv('IFM_API_ZIPNLOAD') !== false ? intval( getenv('IFM_API_ZIPNLOAD') ) : $this->config['zipnload'] ; - $this->config['createarchive'] = getenv('IFM_API_CREATEARCHIVE') !== false ? intval( getenv('IFM_API_CREATEARCHIVE') ) : $this->config['createarchive'] ; - $this->config['showlastmodified'] = getenv('IFM_GUI_SHOWLASTMODIFIED') !== false ? intval( getenv('IFM_GUI_SHOWLASTMODIFIED') ) : $this->config['showlastmodified'] ; - $this->config['showfilesize'] = getenv('IFM_GUI_SHOWFILESIZE') !== false ? intval( getenv('IFM_GUI_SHOWFILESIZE') ) : $this->config['showfilesize'] ; - $this->config['showowner'] = getenv('IFM_GUI_SHOWOWNER') !== false ? intval( getenv('IFM_GUI_SHOWOWNER') ) : $this->config['showowner'] ; - $this->config['showgroup'] = getenv('IFM_GUI_SHOWGROUP') !== false ? intval( getenv('IFM_GUI_SHOWGROUP') ) : $this->config['showgroup'] ; - $this->config['showpermissions'] = getenv('IFM_GUI_SHOWPERMISSIONS') !== false ? intval( getenv('IFM_GUI_SHOWPERMISSIONS') ) : $this->config['showpermissions'] ; - $this->config['showhtdocs'] = getenv('IFM_GUI_SHOWHTDOCS') !== false ? intval( getenv('IFM_GUI_SHOWHTDOCS') ) : $this->config['showhtdocs'] ; - $this->config['showhiddenfiles'] = getenv('IFM_GUI_SHOWHIDDENFILES') !== false ? intval( getenv('IFM_GUI_SHOWHIDDENFILES') ) : $this->config['showhiddenfiles'] ; - $this->config['showpath'] = getenv('IFM_GUI_SHOWPATH') !== false ? intval( getenv('IFM_GUI_SHOWPATH') ) : $this->config['showpath'] ; - $this->config['contextmenu'] = getenv('IFM_GUI_CONTEXTMENU') !== false ? intval( getenv('IFM_GUI_CONTEXTMENU') ) : $this->config['contextmenu'] ; - $this->config['search'] = getenv('IFM_API_SEARCH') !== false ? intval( getenv('IFM_API_SEARCH') ) : $this->config['search'] ; - $this->config['showrefresh'] = getenv('IFM_GUI_REFRESH') !== false ? intval( getenv('IFM_GUI_REFRESH') ) : $this->config['showrefresh'] ; - $this->config['forceproxy'] = getenv('IFM_GUI_FORCEPROXY') !== false ? intval( getenv('IFM_GUI_FORCEPROXY') ) : $this->config['forceproxy'] ; - $this->config['confirmoverwrite'] = getenv('IFM_GUI_CONFIRMOVERWRITE') !== false ? intval( getenv('IFM_GUI_CONFIRMOVERWRITE') ) : $this->config['confirmoverwrite'] ; - - // optional settings - if( getenv('IFM_SESSION_LIFETIME') !== false ) - $this->config['session_lifetime'] = getenv('IFM_SESSION_LIFETIME'); - if( getenv('IFM_FORCE_SESSION_LIFETIME') !== false ) - $this->config['session_lifetime'] = getenv('IFM_FORCE_SESSION_LIFETIME'); + foreach (array_keys($this->config) as $key) { + if (($value = getenv('IFM_' . strtoupper($key))) !== false) { + if (is_numeric($value)) + $value = intval($value); + $this->config[$key] = $value; + } + } // load config from passed array - $this->config = array_merge( $this->config, $config ); + $this->config = array_merge($this->config, $config); - // get list of ace includes - $this->config['ace_includes'] = <<<'f00bar' -@@@vars:ace_includes@@@ + + + if ($this->config['timezone']) + date_default_timezone_set($this->config['timezone']); + } + + /** + * This function contains the client-side application + */ + public function getApplication() { + $this->getHTMLHeader(); + print '
'; + $this->getJS(); + print ''; + $this->getHTMLFooter(); + } + + public function getInlineApplication() { + $this->getCSS(); + print '
'; + $this->getJS(); + } + + public function getCSS() { + echo <<<'f00bar' + @@@ASSETS_CSS@@@ f00bar; + } + public function getJS() { + echo <<<'f00bar' + @@@ASSETS_JS@@@ +f00bar; + } + + public function getHTMLHeader() { + print ' + + + IFM - improved file manager + + + '; + $this->getCSS(); + print ''; + } + + public function getHTMLFooter() { + print ''; + } + + /** + * main functions + */ + + private function dispatch() { + // APIs which do not need authentication + switch ($_REQUEST['api']) { + case "checkAuth": + try { + if ($this->checkAuth()) + $this->jsonResponse(["status" => "OK", "message" => "Authenticated"]); + } catch (Exception $e) { + $this->jsonResponse(["status" => "ERROR", "message" => "Not authenticated"]); + } + return; + case "getConfig": + $this->getConfig(); + return; + case "getTemplates": + $this->getTemplates(); + return; + case "getI18N": + $this->getI18N($_REQUEST); + return; + case "logout": + unset($_SESSION); + session_destroy(); + header("Location: " . strtok($_SERVER["REQUEST_URI"], '?')); + return; + } + + // check authentication + if (!$this->checkAuth()) + throw new IFMException("Not authenticated", true); + + // api requests which work without a valid working directory + switch ($_REQUEST['api']) { + case "getRealpath": + if (isset($_REQUEST["dir"]) && $_REQUEST["dir"] != "") + $this->jsonResponse(array("realpath" => $this->getValidDir($_REQUEST["dir"]))); + else + $this->jsonResponse(array("realpath" => "")); + return; + case "getFiles": + if (isset($_REQUEST["dir"]) && $this->isPathValid($_REQUEST["dir"])) + $this->getFiles($_REQUEST["dir"]); + else + $this->getFiles(""); + return; + case "getFolders": + $this->getFolders($_REQUEST); + return; + } + + // checking working directory + if (!isset($_REQUEST["dir"]) || !$this->isPathValid($_REQUEST["dir"])) + throw new IFMException("Invalid working directory", true); + + $this->chDirIfNecessary($_REQUEST['dir']); + switch ($_REQUEST["api"]) { + case "createDir": $this->createDir($_REQUEST); return; + case "saveFile": $this->saveFile($_REQUEST); return; + case "getContent": $this->getContent($_REQUEST); return; + case "delete": $this->deleteFiles($_REQUEST); return; + case "rename": $this->renameFile($_REQUEST); return; + case "download": $this->downloadFile($_REQUEST); return; + case "extract": $this->extractFile($_REQUEST); return; + case "upload": $this->uploadFile($_REQUEST); return; + case "copyMove": $this->copyMove($_REQUEST); return; + case "changePermissions": $this->changePermissions($_REQUEST); return; + case "zipnload": $this->zipnload($_REQUEST); return; + case "remoteUpload": $this->remoteUpload($_REQUEST); return; + case "searchItems": $this->searchItems($_REQUEST); return; + case "getFolderTree": $this->getFolderTree($_REQUEST); return; + case "createArchive": $this->createArchive($_REQUEST); return; + case "proxy": $this->downloadFile($_REQUEST, false); return; + default: + throw new IFMException("Invalid api action given", true); + return; + } + } + + public function run($mode="standalone") { + try { + if (!is_dir(realpath($this->config['root_dir'])) || !is_readable(realpath($this->config['root_dir']))) + throw new IFMException("Cannot access root_dir.", false); + + chdir(realpath($this->config['root_dir'])); + + $this->mode = $mode; + if (isset($_REQUEST['api']) || $mode == "api") + $this->dispatch(); + elseif ($mode == "standalone") + $this->getApplication(); + else + $this->getInlineApplication(); + } catch (IFMException $e) { + $this->jsonResponse(["status" => "ERROR", "message" => $e->getMessage()]); + } catch (Exception $e) { + $this->jsonResponse(["status" => "ERROR", "message" => $e->getMessage()]); + } + } + + /** + * api functions + */ + + private function getI18N($lang="en") { + $i18n = []; + @@@vars:languageincludes@@@ + $this->i18n = $i18n; + + if (in_array($lang, array_keys($i18n))) + // Merge english with the language in case of missing keys + $this->jsonResponse(array_merge($i18n['en'], $i18n[$lang])); + else + $this->jsonResponse($i18n['en']); + } + + private function getTemplates() { // templates - $templates = array(); + $templates = []; $templates['app'] = <<<'f00bar' @@@file:src/templates/app.html@@@ f00bar; @@ -182,171 +320,44 @@ f00bar; $templates['uploadconfirmoverwrite'] = <<<'f00bar' @@@file:src/templates/modal.uploadconfirmoverwrite.html@@@ f00bar; - $this->templates = $templates; - - $i18n = array(); - @@@vars:languageincludes@@@ - $this->i18n = $i18n; - - if( in_array( $this->config['language'], array_keys( $this->i18n ) ) ) - // Merge english with the language in case of missing keys - $this->l = array_merge($this->i18n['en'], $this->i18n[$this->config['language']]); - else - $this->l = $this->i18n['en']; - - if ($this->config['timezone']) - date_default_timezone_set($this->config['timezone']); + $this->jsonResponse($templates); } - /** - * This function contains the client-side application - */ - public function getApplication() { - $this->getHTMLHeader(); - print '
'; - $this->getJS(); - print ''; - $this->getHTMLFooter(); - } + private function getFiles($dir) { + $this->chDirIfNecessary($dir); - public function getInlineApplication() { - $this->getCSS(); - print '
'; - $this->getJS(); - } + unset($files); unset($dirs); $files = []; $dirs = []; -IFM_ASSETS - - public function getHTMLHeader() { - print ' - - - IFM - improved file manager - - - '; - $this->getCSS(); - print ''; - } - - public function getHTMLFooter() { - print ''; - } - - /* - main functions - */ - - private function handleRequest() { - if( $_REQUEST["api"] == "getRealpath" ) { - if( isset( $_REQUEST["dir"] ) && $_REQUEST["dir"] != "" ) - $this->jsonResponse( array( "realpath" => $this->getValidDir( $_REQUEST["dir"] ) ) ); - else - $this->jsonResponse( array( "realpath" => "" ) ); - } - elseif( $_REQUEST["api"] == "getFiles" ) { - if( isset( $_REQUEST["dir"] ) && $this->isPathValid( $_REQUEST["dir"] ) ) - $this->getFiles( $_REQUEST["dir"] ); - else - $this->getFiles( "" ); - } - elseif( $_REQUEST["api"] == "getConfig" ) { - $this->getConfig(); - } - elseif( $_REQUEST["api"] == "getFolders" ) { - $this->getFolders( $_REQUEST ); - } elseif( $_REQUEST["api"] == "getTemplates" ) { - $this->jsonResponse( $this->templates ); - } elseif( $_REQUEST["api"] == "getI18N" ) { - $this->jsonResponse( $this->l ); - } elseif( $_REQUEST["api"] == "logout" ) { - unset( $_SESSION['ifmauth'] ); - session_destroy(); - header( "Location: " . strtok( $_SERVER["REQUEST_URI"], '?' ) ); - exit( 0 ); - } else { - if( isset( $_REQUEST["dir"] ) && $this->isPathValid( $_REQUEST["dir"] ) ) { - $this->chDirIfNecessary( $_REQUEST['dir'] ); - switch( $_REQUEST["api"] ) { - case "createDir": $this->createDir( $_REQUEST["dir"], $_REQUEST["dirname"] ); break; - case "saveFile": $this->saveFile( $_REQUEST ); break; - case "getContent": $this->getContent( $_REQUEST ); break; - case "delete": $this->deleteFiles( $_REQUEST ); break; - case "rename": $this->renameFile( $_REQUEST ); break; - case "download": $this->downloadFile( $_REQUEST ); break; - case "extract": $this->extractFile( $_REQUEST ); break; - case "upload": $this->uploadFile( $_REQUEST ); break; - case "copyMove": $this->copyMove( $_REQUEST ); break; - case "changePermissions": $this->changePermissions( $_REQUEST ); break; - case "zipnload": $this->zipnload( $_REQUEST); break; - case "remoteUpload": $this->remoteUpload( $_REQUEST ); break; - case "searchItems": $this->searchItems( $_REQUEST ); break; - case "getFolderTree": $this->getFolderTree( $_REQUEST ); break; - case "createArchive": $this->createArchive( $_REQUEST ); break; - case "proxy": $this->downloadFile( $_REQUEST, false ); break; - default: - $this->jsonResponse( array( "status" => "ERROR", "message" => "Invalid api action given" ) ); - break; - } - } else { - print $this->jsonResponse( array( "status" => "ERROR", "message" => "Invalid working directory" ) ); - } - } - exit( 0 ); - } - - public function run( $mode="standalone" ) { - if ( $this->checkAuth() ) { - // go to our root_dir - if( ! is_dir( realpath( $this->config['root_dir'] ) ) || ! is_readable( realpath( $this->config['root_dir'] ) ) ) - die( "Cannot access root_dir."); - else - chdir( realpath( $this->config['root_dir'] ) ); - $this->mode = $mode; - if( isset( $_REQUEST['api'] ) || $mode == "api" ) { - $this->handleRequest(); - } elseif( $mode == "standalone" ) { - $this->getApplication(); - } else { - $this->getInlineApplication(); - } - } - } - - /* - api functions - */ - - - private function getFiles( $dir ) { - $this->chDirIfNecessary( $dir ); - - unset( $files ); unset( $dirs ); $files = array(); $dirs = array(); - - if( $handle = opendir( "." ) ) { - while( false !== ( $result = readdir( $handle ) ) ) { - if( $result == basename( $_SERVER['SCRIPT_NAME'] ) && $this->getScriptRoot() == getcwd() ) { } - elseif( ( $result == ".htaccess" || $result==".htpasswd" ) && $this->config['showhtdocs'] != 1 ) {} - elseif( $result == "." ) {} - elseif( $result != ".." && substr( $result, 0, 1 ) == "." && $this->config['showhiddenfiles'] != 1 ) {} + if ($handle = opendir(".")) { + while (false !== ($result = readdir($handle))) { + if ($result == basename($_SERVER['SCRIPT_NAME']) && $this->getScriptRoot() == getcwd()) + continue; + elseif (($result == ".htaccess" || $result==".htpasswd") && $this->config['showhtdocs'] != 1) + continue; + elseif ($result == ".") + continue; + elseif ($result != ".." && substr($result, 0, 1) == "." && $this->config['showhiddenfiles'] != 1) + continue; else { - $item = $this->getItemInformation( $result ); - if( $item['type'] == "dir" ) $dirs[] = $item; - else $files[] = $item; + $item = $this->getItemInformation($result); + if ($item['type'] == "dir") + $dirs[] = $item; + else + $files[] = $item; } } - closedir( $handle ); + closedir($handle); } - usort( $dirs, array( $this, "sortByName" ) ); - usort( $files, array( $this, "sortByName" ) ); + usort($dirs, [$this, "sortByName"]); + usort($files, [$this, "sortByName"]); - $this->jsonResponse( array_merge( $dirs, $files ) ); + $this->jsonResponse(array_merge($dirs, $files)); } - private function getItemInformation( $name ) { + private function getItemInformation($name) { $item = array(); $item["name"] = $name; - if( is_dir( $name ) ) { + if (is_dir($name)) { $item["type"] = "dir"; if( $name == ".." ) $item["icon"] = "icon icon-up-open"; @@ -354,45 +365,49 @@ IFM_ASSETS $item["icon"] = "icon icon-folder-empty"; } else { $item["type"] = "file"; - if( in_array( substr( $name, -7 ), array( ".tar.gz", ".tar.xz" ) ) ) - $type = substr( $name, -6 ); + if (in_array(substr($name, -7), array( ".tar.gz", ".tar.xz" ))) + $type = substr($name, -6); elseif( substr( $name, -8 ) == ".tar.bz2" ) $type = "tar.bz2"; else $type = substr( strrchr( $name, "." ), 1 ); $item["icon"] = $this->getTypeIcon( $type ); $item["ext"] = strtolower($type); - if( !$this->config['disable_mime_detection'] ) - $item["mime_type"] = mime_content_type( $name ); + if (!$this->config['disable_mime_detection']) + $item["mime_type"] = mime_content_type($name); } - if( $this->config['showlastmodified'] == 1 ) { $item["lastmodified"] = filemtime( $name ); } - if( $this->config['showfilesize'] == 1 ) { - if( $item['type'] == "dir" ) { + if ($this->config['showlastmodified'] == 1) + $item["lastmodified"] = filemtime($name); + if ($this->config['showfilesize'] == 1) { + if ($item['type'] == "dir") { $item['size_raw'] = 0; $item['size'] = ""; } else { - $item["size_raw"] = filesize( $name ); - if( $item["size_raw"] > 1073741824 ) $item["size"] = round( ( $item["size_raw"]/1073741824 ), 2 ) . " GB"; - elseif($item["size_raw"]>1048576)$item["size"] = round( ( $item["size_raw"]/1048576 ), 2 ) . " MB"; - elseif($item["size_raw"]>1024)$item["size"] = round( ( $item["size_raw"]/1024 ), 2 ) . " KB"; + $item["size_raw"] = filesize($name); + if ($item["size_raw"] > 1073741824) $item["size"] = round(($item["size_raw"]/1073741824 ), 2) . " GB"; + elseif($item["size_raw"]>1048576)$item["size"] = round(($item["size_raw"]/1048576), 2) . " MB"; + elseif($item["size_raw"]>1024)$item["size"] = round(($item["size_raw"]/1024), 2) . " KB"; else $item["size"] = $item["size_raw"] . " Byte"; } } - if( $this->config['showpermissions'] > 0 ) { - if( $this->config['showpermissions'] == 1 ) $item["fileperms"] = substr( decoct( fileperms( $name ) ), -3 ); - elseif( $this->config['showpermissions'] == 2 ) $item["fileperms"] = $this->filePermsDecode( fileperms( $name ) ); - if( $item["fileperms"] == "" ) $item["fileperms"] = " "; - $item["filepermmode"] = ( $this->config['showpermissions'] == 1 ) ? "short" : "long"; + if ($this->config['showpermissions'] > 0) { + if ($this->config['showpermissions'] == 1) + $item["fileperms"] = substr(decoct(fileperms($name)), -3); + elseif ($this->config['showpermissions'] == 2) + $item["fileperms"] = $this->filePermsDecode(fileperms($name)); + if ($item["fileperms"] == "") + $item["fileperms"] = " "; + $item["filepermmode"] = ($this->config['showpermissions'] == 1) ? "short" : "long"; } - if( $this->config['showowner'] == 1 ) { - if ( function_exists( "posix_getpwuid" ) && fileowner($name) !== false ) { - $ownerarr = posix_getpwuid( fileowner( $name ) ); + if ($this->config['showowner'] == 1) { + if (function_exists("posix_getpwuid") && fileowner($name) !== false) { + $ownerarr = posix_getpwuid(fileowner($name)); $item["owner"] = $ownerarr['name']; } else $item["owner"] = false; } - if( $this->config['showgroup'] == 1 ) { - if( function_exists( "posix_getgrgid" ) && filegroup( $name ) !== false ) { - $grouparr = posix_getgrgid( filegroup( $name ) ); + if ($this->config['showgroup'] == 1) { + if (function_exists("posix_getgrgid") && filegroup($name) !== false) { + $grouparr = posix_getgrgid(filegroup($name)); $item["group"] = $grouparr['name']; } else $item["group"] = false; } @@ -404,527 +419,533 @@ IFM_ASSETS $ret['inline'] = ( $this->mode == "inline" ) ? true : false; $ret['isDocroot'] = ($this->getRootDir() == $this->getScriptRoot()); - foreach (array("auth_source", "root_dir") as $field) { + foreach (["auth_source", "root_dir"] as $field) unset($ret[$field]); - } + $this->jsonResponse($ret); } private function getFolders( $d ) { - if( ! isset( $d['dir'] ) ) + if (!isset($d['dir'])) $d['dir'] = $this->getRootDir(); - if( ! $this->isPathValid( $d['dir'] ) ) + + if (!$this->isPathValid($d['dir'])) echo "[]"; else { - $ret = array(); - foreach( glob( $this->pathCombine( $d['dir'], "*" ), GLOB_ONLYDIR ) as $dir ) { - array_push( $ret, array( - "text" => htmlspecialchars( basename( $dir ) ), + $ret = []; + foreach (glob($this->pathCombine($d['dir'], "*"), GLOB_ONLYDIR) as $dir) { + array_push( $ret, [ + "text" => htmlspecialchars(basename($dir)), "lazyLoad" => true, - "dataAttr" => array( "path" => $dir ) - )); + "dataAttr" => ["path" => $dir] + ]); } sort( $ret ); if( $this->getScriptRoot() == realpath( $d['dir'] ) ) $ret = array_merge( - array( - 0 => array( + [ + 0 => [ "text" => "/ [root]", - "dataAttr" => array( "path" => $this->getRootDir() ) - ) - ), + "dataAttr" => ["path" => $this->getRootDir()] + ] + ], $ret ); - $this->jsonResponse( $ret ); + $this->jsonResponse($ret); } } - private function searchItems( $d ) { - if( $this->config['search'] != 1 ) { - $this->jsonResponse( array( "status" => "ERROR", "message" => $this->l['nopermissions'] ) ); + private function searchItems($d) { + if ($this->config['search'] != 1) { + $this->jsonResponse(["status" => "ERROR", "message" => $this->l['nopermissions']]); return; } - if( strpos( $d['pattern'], '/' ) !== false ) { - $this->jsonResponse( array( "status" => "ERROR", "message" => $this->l['pattern_error_slashes'] ) ); - exit( 1 ); + if (strpos($d['pattern'], '/') !== false) { + $this->jsonResponse(["status" => "ERROR", "message" => $this->l['pattern_error_slashes']]); + exit(1); } + try { - $results = $this->searchItemsRecursive( $d['pattern'] ); - $this->jsonResponse( $results ); - } catch( Exception $e ) { - $this->jsonResponse( array( "status" => "ERROR", "message" => $this->l['error'] . " " . $e->getMessage() ) ); + $results = $this->searchItemsRecursive($d['pattern']); + $this->jsonResponse($results); + } catch (Exception $e) { + $this->jsonResponse(["status" => "ERROR", "message" => $this->l['error'] . " " . $e->getMessage()]); } } private function searchItemsRecursive( $pattern, $dir="" ) { - $items = array(); - $dir = $dir ? $dir : '.'; - foreach( glob( $this->pathCombine( $dir, $pattern ) ) as $result ) { - array_push( $items, $this->getItemInformation( $result ) ); - } - foreach( glob( $this->pathCombine( $dir, '*') , GLOB_ONLYDIR ) as $subdir ) { - $items = array_merge( $items, $this->searchItemsRecursive( $pattern, $subdir ) ); - } + $items = []; + $dir = $dir ?? '.'; + + foreach (glob($this->pathCombine($dir, $pattern)) as $result) + array_push($items, $this->getItemInformation( $result ) ); + + foreach (glob($this->pathCombine($dir, '*'), GLOB_ONLYDIR) as $subdir) + $items = array_merge($items, $this->searchItemsRecursive($pattern, $subdir)); + return $items; } private function getFolderTree( $d ) { $this->jsonResponse( array_merge( - array( - 0 => array( + [ + 0 => [ "text" => "/ [root]", - "nodes" => array(), - "dataAttributes" => array( "path" => $this->getRootDir() ) - ) - ), - $this->getFolderTreeRecursive( $d['dir'] ) + "nodes" => [], + "dataAttributes" => ["path" => $this->getRootDir()] + ] + ], + $this->getFolderTreeRecursive($d['dir']) ) ); } - private function getFolderTreeRecursive( $start_dir ) { - $ret = array(); - $start_dir = realpath( $start_dir ); - if( $handle = opendir( $start_dir ) ) { - while (false !== ( $result = readdir( $handle ) ) ) { - if( is_dir( $this->pathCombine( $start_dir, $result ) ) && $result != "." && $result != ".." ) { - array_push( - $ret, - array( - "text" => htmlspecialchars( $result ), - "dataAttributes" => array( - "path" => $this->pathCombine( $start_dir, $result ) - ), - "nodes" => $this->getFolderTreeRecursive( $this->pathCombine( $start_dir, $result ) ) - ) - ); + private function getFolderTreeRecursive($start_dir) { + $ret = []; + $start_dir = realpath($start_dir); + if ($handle = opendir($start_dir)) { + while (false !== ($result = readdir($handle))) { + if (is_dir($this->pathCombine($start_dir, $result)) && $result != "." && $result != ".." ) { + array_push($ret, [ + "text" => htmlspecialchars($result), + "dataAttributes" => ["path" => $this->pathCombine($start_dir, $result)], + "nodes" => $this->getFolderTreeRecursive($this->pathCombine($start_dir, $result)) + ]); } } } - sort( $ret ); + sort($ret); return $ret; } - private function copyMove( $d ) { - if( $this->config['copymove'] != 1 ) { + private function copyMove($d) { + if ($this->config['copymove'] != 1) { $this->jsonResponse( array( "status" => "ERROR", "message" => $this->l['nopermissions'] ) ); - exit( 1 ); + exit(1); } - if( ! isset( $d['destination'] ) || ! $this->isPathValid( realpath( $d['destination'] ) ) ) { - $this->jsonResponse( array( "status" => "ERROR", "message" => $this->l['invalid_dir'] ) ); - exit( 1 ); + + if (!isset($d['destination']) || !$this->isPathValid(realpath($d['destination']))) { + $this->jsonResponse(["status" => "ERROR", "message" => $this->l['invalid_dir']]); + exit(1); } - if( ! is_array( $d['filenames'] ) ) { - $this->jsonResponse( array( "status" => "ERROR", "message" => $this->l['invalid_params'] ) ); - exit( 1 ); + + if (!is_array($d['filenames'])) { + $this->jsonResponse(["status" => "ERROR", "message" => $this->l['invalid_params']]); + exit(1); } - if( ! in_array( $d['action'], array( 'copy', 'move' ) ) ) { - $this->jsonResponse( array( "status" => "ERROR", "message" => $this->l['invalid_action'] ) ); - exit( 1 ); + + if (!in_array($d['action'], ['copy', 'move'])) { + $this->jsonResponse(["status" => "ERROR", "message" => $this->l['invalid_action']]); + exit(1); } - $err = array(); $errFlag = -1; // -1 -> all errors; 0 -> at least some errors; 1 -> no errors - foreach( $d['filenames'] as $file ) { - if( ! file_exists( $file ) || $file == ".." || ! $this->isFilenameValid( $file ) ) { - array_push( $err, $file ); + + $err = []; $errFlag = -1; // -1 -> all errors; 0 -> at least some errors; 1 -> no errors + foreach ($d['filenames'] as $file) { + if (!file_exists($file) || $file == ".." || !$this->isFilenameValid($file)) { + array_push($err, $file); } - if( $d['action'] == "copy" ) { - if( $this->xcopy( $file, $d['destination'] ) ) + if ($d['action'] == "copy") { + if ($this->xcopy($file, $d['destination'])) $errFlag = 0; else - array_push( $err, $file ); - } elseif( $d['action'] == "move" ) { - if( rename( $file, $this->pathCombine( $d['destination'], basename( $file ) ) ) ) + array_push($err, $file); + } elseif ($d['action'] == "move") { + if (rename($file, $this->pathCombine($d['destination'], basename($file)))) $errFlag = 0; else - array_push( $err, $file ); + array_push($err, $file); } } - $action = ( $d['action'] == "copy" ? "copied" : "moved" ); - if( empty( $err ) ) { - $this->jsonResponse( array( "status" => "OK", "message" => ( $d['action'] == "copy" ? $this->l['copy_success'] : $this->l['move_success'] ), "errflag" => "1" ) ); - } - else { - $errmsg = ( $d['action'] == "copy" ? $this->l['copy_error'] : $this->l['move_error'] ) . "