From 3cdeee6b7bd118756ccf9e40d71e851b7abce0a2 Mon Sep 17 00:00:00 2001 From: Marco Dickert Date: Sat, 4 Mar 2017 18:44:31 +0100 Subject: [PATCH 1/3] use isPathValid function everywhere instead of manual checking --- ifm.php | 11 ++++------- src/main.php | 11 ++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/ifm.php b/ifm.php index af3c93c..0d73941 100644 --- a/ifm.php +++ b/ifm.php @@ -1832,10 +1832,10 @@ ifm.init(); } private function getValidDir($dir) { - if( $this->getScriptRoot() != substr( realpath( $dir ), 0, strlen( $this->getScriptRoot() ) ) ) { + if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { return ""; } else { - return ( file_exists( realpath( $dir ) ) ) ? substr( realpath( $dir ), strlen( $this->getScriptRoot() ) + 1 ) : ""; + return $dir; } } @@ -1861,7 +1861,6 @@ ifm.init(); } private function getScriptRoot() { - //return realpath( substr( $_SERVER["SCRIPT_FILENAME"], 0, strrpos( $_SERVER["SCRIPT_FILENAME"], "/" ) ) ); return dirname( $_SERVER["SCRIPT_FILENAME"] ); } @@ -1887,12 +1886,10 @@ ifm.init(); } private function getRealpath($dir) { - if( $this->getScriptRoot() != substr( realpath( $_POST["dir"] ), 0, strlen( $this->getScriptRoot() ) ) ) { + if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { echo json_encode( array( "realpath" => "" ) ); } else { - $rp = substr( realpath( $_POST["dir"] ), strlen( $this->getScriptRoot() ) + 1 ); - if( $rp == false ) $rp = ""; - echo json_encode( array( "realpath" => $rp ) ); + echo json_encode( array( "realpath" => $dir ) ); } } private function rec_rmdir( $path ) { diff --git a/src/main.php b/src/main.php index 648fd85..c66e524 100644 --- a/src/main.php +++ b/src/main.php @@ -671,10 +671,10 @@ class IFM { } private function getValidDir($dir) { - if( $this->getScriptRoot() != substr( realpath( $dir ), 0, strlen( $this->getScriptRoot() ) ) ) { + if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { return ""; } else { - return ( file_exists( realpath( $dir ) ) ) ? substr( realpath( $dir ), strlen( $this->getScriptRoot() ) + 1 ) : ""; + return $dir; } } @@ -700,7 +700,6 @@ class IFM { } private function getScriptRoot() { - //return realpath( substr( $_SERVER["SCRIPT_FILENAME"], 0, strrpos( $_SERVER["SCRIPT_FILENAME"], "/" ) ) ); return dirname( $_SERVER["SCRIPT_FILENAME"] ); } @@ -726,12 +725,10 @@ class IFM { } private function getRealpath($dir) { - if( $this->getScriptRoot() != substr( realpath( $_POST["dir"] ), 0, strlen( $this->getScriptRoot() ) ) ) { + if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { echo json_encode( array( "realpath" => "" ) ); } else { - $rp = substr( realpath( $_POST["dir"] ), strlen( $this->getScriptRoot() ) + 1 ); - if( $rp == false ) $rp = ""; - echo json_encode( array( "realpath" => $rp ) ); + echo json_encode( array( "realpath" => $dir ) ); } } private function rec_rmdir( $path ) { From 83fa66031c075b2f3e000c7249fd5b2db1502ba0 Mon Sep 17 00:00:00 2001 From: Marco Dickert Date: Mon, 13 Mar 2017 15:52:45 +0100 Subject: [PATCH 2/3] added root_dir option, fixed some things around that topic --- ifm.php | 88 +++++++++++++++++++++++++++++++++++--------------- src/config.php | 30 +++++++++++++++-- src/main.php | 58 +++++++++++++++++++-------------- 3 files changed, 124 insertions(+), 52 deletions(-) diff --git a/ifm.php b/ifm.php index 0d73941..2ecfe34 100644 --- a/ifm.php +++ b/ifm.php @@ -34,14 +34,40 @@ class IFMConfig { const showfilesize = 1; // show filesize? const showowner = 1; // show file owner? const showgroup = 1; // show file group? - const showpath = 0; // show real path of directory (not only root)? const showpermissions = 2; // show permissions 0 -> not; 1 -> octal, 2 -> human readable const showhtdocs = 1; // show .htaccess and .htpasswd const showhiddenfiles = 1; // show files beginning with a dot (e.g. ".bashrc") + const showpath = 0; // show absolute path - // general config + /* + authentication + + This provides a super simple authentication functionality. At the moment only one user can be + configured. The credential information can be either set inline or read from a file. The + password has to be a hash generated by PHPs password_hash function. The default credentials are + admin:admin. + + If you specify a file it should only contain one line, with the credentials in the following + format: + : + + examples: + const auth_source = 'inline;admin:$2y$10$0Bnm5L4wKFHRxJgNq.oZv.v7yXhkJZQvinJYR2p6X1zPvzyDRUVRC'; + const auth_source = 'file;/path/to/file'; + */ const auth = 0; const auth_source = 'inline;admin:$2y$10$0Bnm5L4wKFHRxJgNq.oZv.v7yXhkJZQvinJYR2p6X1zPvzyDRUVRC'; + + /* + root_dir - set a custom root directory instead of the script location + + This option is highly experimental and should only be set if you definitely know what you do. + Settings this option may cause black holes or other unwanted things. Use with special care. + + default setting: + const root_dir = ""; + */ + const root_dir = ""; const defaulttimezone = "Europe/Berlin"; // set default timezone // development tools @@ -350,7 +376,7 @@ div.footer div.panel-body { padding: 5px !important; }
Content of '; - if( IFMConfig::showpath == 1 ) print $this->getScriptRoot().'/'; else print '/'; + print ( IFMConfig::showpath == 1 ) ? realpath( IFMConfig::root_dir ) : "/"; print '
@@ -1287,9 +1313,9 @@ ifm.init(); private function handleRequest() { if($_REQUEST["api"] == "getRealpath") { if( isset( $_REQUEST["dir"] ) && $_REQUEST["dir"] != "" ) - $this->getRealpath( $_REQUEST["dir"] ); + echo json_encode( array( "realpath" => $this->getValidDir( $_REQUEST["dir"] ) ) ); else - echo json_encode(array("realpath"=>"")); + echo json_encode( array( "realpath" => "" ) ); } elseif( $_REQUEST["api"] == "getFiles" ) { if( isset( $_REQUEST["dir"] ) && $this->isPathValid( $_REQUEST["dir"] ) ) @@ -1321,6 +1347,11 @@ ifm.init(); public function run() { if ( $this->checkAuth() ) { + // go to our root_dir + if( ! is_dir( realpath( IFMConfig::root_dir ) ) || ! is_readable( realpath( IFMConfig::root_dir ) ) ) + die( "Cannot access root_dir."); + else + chdir( IFMConfig::root_dir ); if ( ! isset($_REQUEST['api']) ) { $this->getApplication(); } else { @@ -1831,14 +1862,6 @@ ifm.init(); '; } - private function getValidDir($dir) { - if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { - return ""; - } else { - return $dir; - } - } - private function filePermsDecode( $perms ) { $oct = str_split( strrev( decoct( $perms ) ), 1 ); $masks = array( '---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx' ); @@ -1851,13 +1874,33 @@ ifm.init(); ); } - private function isPathValid($p) { - if( $p == "" ) { - return true; - } elseif( str_replace( "\\", "/", $this->getScriptRoot() ) == str_replace( "\\", "/", substr( realpath( dirname( $p ) ), 0, strlen( $this->getScriptRoot() ) ) ) ) { - return true; + private function getValidDir( $dir ) { + if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { + return ""; + } else { + $rpDir = realpath( $dir ); + $rpConfig = realpath( IFMConfig::root_dir ); + if( $rpConfig == "/" ) + return $rpDir; + elseif( $rpDir == $rpConfig ) + return ""; + else + return substr( $rpDir, strlen( $rpConfig ) + 1 ); } - return false; + } + + private function isPathValid( $dir ) { + $rpDir = realpath( $dir ); + $rpConfig = realpath( IFMConfig::root_dir ); + if( ! is_string( $rpDir ) || ! is_string( $rpConfig ) ) // can happen if open_basedir is in effect + return false; + elseif( $rpDir == $rpConfig ) + return true; + elseif( 0 === strpos( $rpDir, $rpConfig ) ) { + return true; + } + else + return false; } private function getScriptRoot() { @@ -1885,13 +1928,6 @@ ifm.init(); } } - private function getRealpath($dir) { - if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { - echo json_encode( array( "realpath" => "" ) ); - } else { - echo json_encode( array( "realpath" => $dir ) ); - } - } private function rec_rmdir( $path ) { if( !is_dir( $path ) ) { return -1; diff --git a/src/config.php b/src/config.php index 7ef1022..8fc1a34 100644 --- a/src/config.php +++ b/src/config.php @@ -34,14 +34,40 @@ class IFMConfig { const showfilesize = 1; // show filesize? const showowner = 1; // show file owner? const showgroup = 1; // show file group? - const showpath = 0; // show real path of directory (not only root)? const showpermissions = 2; // show permissions 0 -> not; 1 -> octal, 2 -> human readable const showhtdocs = 1; // show .htaccess and .htpasswd const showhiddenfiles = 1; // show files beginning with a dot (e.g. ".bashrc") + const showpath = 0; // show absolute path - // general config + /* + authentication + + This provides a super simple authentication functionality. At the moment only one user can be + configured. The credential information can be either set inline or read from a file. The + password has to be a hash generated by PHPs password_hash function. The default credentials are + admin:admin. + + If you specify a file it should only contain one line, with the credentials in the following + format: + : + + examples: + const auth_source = 'inline;admin:$2y$10$0Bnm5L4wKFHRxJgNq.oZv.v7yXhkJZQvinJYR2p6X1zPvzyDRUVRC'; + const auth_source = 'file;/path/to/file'; + */ const auth = 0; const auth_source = 'inline;admin:$2y$10$0Bnm5L4wKFHRxJgNq.oZv.v7yXhkJZQvinJYR2p6X1zPvzyDRUVRC'; + + /* + root_dir - set a custom root directory instead of the script location + + This option is highly experimental and should only be set if you definitely know what you do. + Settings this option may cause black holes or other unwanted things. Use with special care. + + default setting: + const root_dir = ""; + */ + const root_dir = ""; const defaulttimezone = "Europe/Berlin"; // set default timezone // development tools diff --git a/src/main.php b/src/main.php index c66e524..3ad1221 100644 --- a/src/main.php +++ b/src/main.php @@ -54,7 +54,7 @@ class IFM {
Content of '; - if( IFMConfig::showpath == 1 ) print $this->getScriptRoot().'/'; else print '/'; + print ( IFMConfig::showpath == 1 ) ? realpath( IFMConfig::root_dir ) : "/"; print '
@@ -126,9 +126,9 @@ class IFM { private function handleRequest() { if($_REQUEST["api"] == "getRealpath") { if( isset( $_REQUEST["dir"] ) && $_REQUEST["dir"] != "" ) - $this->getRealpath( $_REQUEST["dir"] ); + echo json_encode( array( "realpath" => $this->getValidDir( $_REQUEST["dir"] ) ) ); else - echo json_encode(array("realpath"=>"")); + echo json_encode( array( "realpath" => "" ) ); } elseif( $_REQUEST["api"] == "getFiles" ) { if( isset( $_REQUEST["dir"] ) && $this->isPathValid( $_REQUEST["dir"] ) ) @@ -160,6 +160,11 @@ class IFM { public function run() { if ( $this->checkAuth() ) { + // go to our root_dir + if( ! is_dir( realpath( IFMConfig::root_dir ) ) || ! is_readable( realpath( IFMConfig::root_dir ) ) ) + die( "Cannot access root_dir."); + else + chdir( IFMConfig::root_dir ); if ( ! isset($_REQUEST['api']) ) { $this->getApplication(); } else { @@ -670,14 +675,6 @@ class IFM { '; } - private function getValidDir($dir) { - if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { - return ""; - } else { - return $dir; - } - } - private function filePermsDecode( $perms ) { $oct = str_split( strrev( decoct( $perms ) ), 1 ); $masks = array( '---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx' ); @@ -690,13 +687,33 @@ class IFM { ); } - private function isPathValid($p) { - if( $p == "" ) { - return true; - } elseif( str_replace( "\\", "/", $this->getScriptRoot() ) == str_replace( "\\", "/", substr( realpath( dirname( $p ) ), 0, strlen( $this->getScriptRoot() ) ) ) ) { - return true; + private function getValidDir( $dir ) { + if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { + return ""; + } else { + $rpDir = realpath( $dir ); + $rpConfig = realpath( IFMConfig::root_dir ); + if( $rpConfig == "/" ) + return $rpDir; + elseif( $rpDir == $rpConfig ) + return ""; + else + return substr( $rpDir, strlen( $rpConfig ) + 1 ); } - return false; + } + + private function isPathValid( $dir ) { + $rpDir = realpath( $dir ); + $rpConfig = realpath( IFMConfig::root_dir ); + if( ! is_string( $rpDir ) || ! is_string( $rpConfig ) ) // can happen if open_basedir is in effect + return false; + elseif( $rpDir == $rpConfig ) + return true; + elseif( 0 === strpos( $rpDir, $rpConfig ) ) { + return true; + } + else + return false; } private function getScriptRoot() { @@ -724,13 +741,6 @@ class IFM { } } - private function getRealpath($dir) { - if( ! $this->isPathValid( $dir ) || ! is_dir( $dir ) ) { - echo json_encode( array( "realpath" => "" ) ); - } else { - echo json_encode( array( "realpath" => $dir ) ); - } - } private function rec_rmdir( $path ) { if( !is_dir( $path ) ) { return -1; From 53f91120f50579cc9c239720b75396359f6f0bb6 Mon Sep 17 00:00:00 2001 From: Marco Dickert Date: Wed, 15 Mar 2017 09:54:08 +0100 Subject: [PATCH 3/3] fixed wrong links when root_dir != DocumentRoot If the root_dir is != the DocumentRoot then files are not linked directly, but downloaded instead. This prevents broken links. Trying to guess the DocumentRoot is not really an option, because due to aliases, rewrites and fallbacks it is nearly impossible to guess the correct DocumentRoot reliably. This is related to issue #26 --- ifm.php | 13 +++++++++---- src/ifm.js | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/ifm.php b/ifm.php index 2ecfe34..35adfb4 100644 --- a/ifm.php +++ b/ifm.php @@ -466,6 +466,7 @@ function IFM() { this.IFM_SCFN = ""; this.config = jQuery.parseJSON(''); // serialize the PHP config array, so we can use it in JS too + this.isDocroot = ; this.editor = null; // global ace editor this.fileChanged = false; // flag for check if file was changed already this.currentDir = ""; // this is the global variable for the current directory; it is used for AJAX requests @@ -523,16 +524,20 @@ function IFM() { else if( self.config.edit == 1 && data[i].name.toLowerCase().substr(-4) != ".zip" ) newRow += ' data-eaction="edit"'; newRow += '>"'; + if( self.isDocroot ) { + newRow += ' href="'+self.pathCombine(ifm.currentDir,data[i].name)+'"'; + if( data[i].icon.indexOf( 'file-image' ) !== -1 ) + newRow += ' data-toggle="tooltip" title=" ' + ( data[i].name == '..' ? '[ up ]' : data[i].name ) + ''; if( ( data[i].type != "dir" && self.config.download == 1 ) || ( data[i].type == "dir" && self.config.zipnload == 1 ) ) { - var guid = self.generateGuid(); newRow += '
'; newRow += ''; newRow += ''; diff --git a/src/ifm.js b/src/ifm.js index 109e908..c0a830e 100644 --- a/src/ifm.js +++ b/src/ifm.js @@ -5,6 +5,7 @@ function IFM() { this.IFM_SCFN = ""; this.config = jQuery.parseJSON(''); // serialize the PHP config array, so we can use it in JS too + this.isDocroot = ; this.editor = null; // global ace editor this.fileChanged = false; // flag for check if file was changed already this.currentDir = ""; // this is the global variable for the current directory; it is used for AJAX requests @@ -62,16 +63,20 @@ function IFM() { else if( self.config.edit == 1 && data[i].name.toLowerCase().substr(-4) != ".zip" ) newRow += ' data-eaction="edit"'; newRow += '>"'; + if( self.isDocroot ) { + newRow += ' href="'+self.pathCombine(ifm.currentDir,data[i].name)+'"'; + if( data[i].icon.indexOf( 'file-image' ) !== -1 ) + newRow += ' data-toggle="tooltip" title=" ' + ( data[i].name == '..' ? '[ up ]' : data[i].name ) + ''; if( ( data[i].type != "dir" && self.config.download == 1 ) || ( data[i].type == "dir" && self.config.zipnload == 1 ) ) { - var guid = self.generateGuid(); newRow += ''; newRow += ''; newRow += '';