From 647509e201096cc06c9541ddf736d1a6e1c0df9c Mon Sep 17 00:00:00 2001 From: xevidos Date: Mon, 1 Jul 2019 09:23:51 -0400 Subject: [PATCH 1/3] Added check to save positions so no POST action is made if there is nothing to save --- components/active/init.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/components/active/init.js b/components/active/init.js index 1c5a035..68d9439 100755 --- a/components/active/init.js +++ b/components/active/init.js @@ -1013,14 +1013,17 @@ uploadPositions: function() { - $.ajax( { - type: 'POST', - url: codiad.active.controller + '?action=save_positions', - data: { - positions: ( JSON.stringify( codiad.active.positions ) ) - }, - success: function( data ) {}, - }); + if( Object.keys( codiad.active.positions ).length > 0 ) { + + $.ajax( { + type: 'POST', + url: codiad.active.controller + '?action=save_positions', + data: { + positions: ( JSON.stringify( codiad.active.positions ) ) + }, + success: function( data ) {}, + }); + } }, savePosition: function() { From 400618c0bbccdef513f0e87480384a058d3232b4 Mon Sep 17 00:00:00 2001 From: xevidos Date: Mon, 15 Jul 2019 18:43:32 -0400 Subject: [PATCH 2/3] Added to todo list, Reformatted filemanager dialog --- README.md | 6 + components/filemanager/dialog.php | 278 ++++++++++++++++-------------- 2 files changed, 151 insertions(+), 133 deletions(-) diff --git a/README.md b/README.md index 8bbd19e..e7469ef 100755 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ Current Tasks: Task List: * Add ability to login with LDAP +* Add archive management +* Add bookmark files * Add custom market * \- Add in new admin interface ( Check admin-portal branch for progress ) - Group Management @@ -46,13 +48,17 @@ Task List: - Project Management - System Settings - User Management +* Add different code linters * Add Drag and Drop natively to filemanager * Add folder / filestructure upload ability * Add if file could not be saved 5 times close the open file * Add multi level users. ( Projects for only certain groups, Permission levels ) * Add mobile compatibility +* Add move files * Add permissions module ( more in depth permissions such as read/write, delete, etc ) +* Add print code * Add support for more database systems ( MSSQL, Oracle, SQLite, Filesystem storage, etc ) +* Add terminal support ( optional per permission level ) * Add in auto save timer that saves after the user stops typing instead of after every change * Clean up update script * Fix broken themes diff --git a/components/filemanager/dialog.php b/components/filemanager/dialog.php index 030a0ee..4d9d8d2 100755 --- a/components/filemanager/dialog.php +++ b/components/filemanager/dialog.php @@ -18,141 +18,153 @@ checkSession();
- - - - - - - - - - - - - - - - -
- - - - - -
- - - - -


">

- - - -


-
+ + + + + +
+			
+		
+ + + + + + + + + + + + + + +
+ + +    + + +
+ + "> +
+

+		
+ + + Date: Tue, 16 Jul 2019 16:02:35 -0400 Subject: [PATCH 3/3] Initial rewrite of filemanager to allow for easier future customizations --- components/filemanager/class.filemanager.php | 1051 ++++++++---------- components/filemanager/controller.php | 295 +++-- components/filemanager/dialog.php | 44 +- components/filemanager/init.js | 12 +- 4 files changed, 724 insertions(+), 678 deletions(-) diff --git a/components/filemanager/class.filemanager.php b/components/filemanager/class.filemanager.php index 8946de7..ab5c386 100755 --- a/components/filemanager/class.filemanager.php +++ b/components/filemanager/class.filemanager.php @@ -15,29 +15,6 @@ class Filemanager extends Common { // PROPERTIES ////////////////////////////////////////////////////////////////// - public $root = ""; - public $project = ""; - public $rel_path = ""; - public $path = ""; - public $patch = ""; - public $type = ""; - public $new_name = ""; - public $content = ""; - public $destination = ""; - public $upload = ""; - public $controller = ""; - public $upload_json = ""; - public $search_string = ""; - - public $search_file_type = ""; - public $query = ""; - public $foptions = ""; - - // JSEND Return Contents - public $status = ""; - public $data = ""; - public $message = ""; - ////////////////////////////////////////////////////////////////// // METHODS ////////////////////////////////////////////////////////////////// @@ -48,214 +25,221 @@ class Filemanager extends Common { // Construct ////////////////////////////////////////////////////////////////// - public function __construct( $get, $post, $files ) { - $this->rel_path = Filemanager::cleanPath($get['path']); + public function __construct() {} + + ////////////////////////////////////////////////////////////////// + // Clean a path + ////////////////////////////////////////////////////////////////// + + public static function cleanPath( $path ) { - if ( $this->rel_path != "/ ") { + // Prevent going out of the workspace + while ( strpos( $path, '../' ) !== false ) { - $this->rel_path .= "/"; + $path = str_replace( '../', '', $path ); } - if ( ! empty( $get['query'] ) ) { - $this->query = $get['query']; - } - if ( ! empty($get['options'] ) ) { - $this->foptions = $get['options']; - } - $this->root = $get['root']; - if ( $this->isAbsPath( $get['path'] ) ) { + + if( self::isAbsPath( $path ) ) { - $this->path = Filemanager::cleanPath($get['path']); + $full_path = $path; } else { - $this->root .= '/'; - $this->path = $this->root . Filemanager::cleanPath( $get['path'] ); - } - // Search - if ( ! empty( $post['search_string'] ) ) { - $this->search_string = ($post['search_string']); - } - if ( ! empty( $post['search_file_type'] ) ) { - $this->search_file_type = ($post['search_file_type']); - } - // Create - if ( ! empty($get['type'] ) ) { - - $this->type = $get['type']; - } - // Modify\Create - if ( ! empty( $get['new_name'] ) ) { - - $this->new_name = $get['new_name']; + $full_path = WORKSPACE . "/" . $path; } - foreach ( array( 'content', 'mtime', 'patch' ) as $key ) { + /** + * If a file with an invalid character exists and the user is + * trying to rename or delete it, allow the actual file name. + */ + + $invalid_characters = preg_match( '/[^A-Za-z0-9\-\._@\/\ ]/', $path ); + + if( $invalid_characters && ! ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) { - if ( ! empty( $post[$key] ) ) { + exit( formatJSEND( "error", "Error, the filename contains invalid characters, please either rename or delete it." ) ); + } elseif( $invalid_characters && ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) { + } else { + + $path = preg_replace( '/[^A-Za-z0-9\-\._\/\ ]/', '', $path ); + } + return $path; + } + + ////////////////////////////////////////////////////////////////// + // CREATE (Creates a new file or directory) + ////////////////////////////////////////////////////////////////// + + public function create( $path, $type, $content = "" ) { + + $response = array( + "status" => "none", + "message" => null, + ); + $path = self::formatPath( $path ); + + // Create file + if( $type == "file" ) { + + if ( ! file_exists( $path ) ) { - if ( get_magic_quotes_gpc() ) { + if ( $file = fopen( $path, 'w' ) ) { - $this->$key = stripslashes( $post[$key] ); + // Write content + if ( $content ) { + + fwrite( $file, $content ); + } + fclose( $file ); + + $response["status"] = "success"; + $response["mtime"] = filemtime( $path ); } else { - $this->$key = $post[$key]; + $response["status"] = "error"; + $response["message"] = "Cannot Create File"; } - } - } - // Duplicate - if ( ! empty( $get['destination'] ) ) { - - $get['destination'] = Filemanager::cleanPath( $get['destination'] ); - if ( $this->isAbsPath( $get['path'] ) ) { - - $i = 1; - $this->destination = $get['destination']; - - do { - - if( is_dir( $this->destination ) ) { - - $this->destination = $get['destination'] . " $i"; - } elseif( is_file( $this->destination ) ) { - - $path_parts = pathinfo( $this->destination ); - - if( isset( $path_parts["extension"] ) ) { - - $this->destination = str_replace( ".{$path_parts["extension"]}", " {$i}.{$path_parts["extension"]}", $get['destination'] ); - } else { - - $this->destination = $get['destination'] . " $i"; - } - } - - $i++; - } while( ( is_file( $this->destination ) || is_dir( $this->destination ) ) ); - } else { - $i = 1; - $this->destination = $this->root . $get['destination']; - do { - - if( is_dir( $this->destination ) ) { - - $this->destination = $this->root . $get['destination'] . " $i"; - } elseif( is_file( $this->destination ) ) { - - $path_parts = pathinfo( $this->destination ); - - if( isset( $path_parts["extension"] ) ) { - - $this->destination = str_replace( ".{$path_parts["extension"]}", " {$i}.{$path_parts["extension"]}", $this->root . $get['destination'] ); - } else { - - $this->destination = $this->root . $get['destination'] . " $i"; - } - } - - $i++; - } while( ( is_file( $this->destination ) || is_dir( $this->destination ) ) ); + $response["status"] = "error"; + $response["message"] = "File Already Exists"; + } + } elseif( $type == "directory" ) { + + if ( ! is_dir( $path ) ) { + + mkdir( $path ); + $response["status"] = "success"; + } else { + + $response["status"] = "error"; + $response["message"] = "Directory Already Exists"; } } + return $response; } ////////////////////////////////////////////////////////////////// - // INDEX (Returns list of files and directories) + // DELETE (Deletes a file or directory (+contents or only contents)) ////////////////////////////////////////////////////////////////// - public function index() { - - if ( file_exists( $this->path ) ) { + public function delete( $path, $follow, $keep_parent = false ) { + + $response = array( + "status" => "none", + "message" => null, + ); + + if( ! Common::checkPath( $path ) ) { - $index = array(); - if ( is_dir( $this->path ) && $handle = opendir( $this->path ) ) { - - while (false !== ( $object = readdir( $handle ) ) ) { - - if ($object != "." && $object != ".." && $object != $this->controller) { - if ( is_dir( $this->path.'/'.$object ) ) { - - $type = "directory"; - $size = count( glob( $this->path . '/' . $object . '/*' ) ); - } else { - - $type = "file"; - $size = @filesize( $this->path.'/' . $object ); - } - $index[] = array( - "name"=>$this->rel_path . $object, - "type"=>$type, - "size"=>$size - ); - } - } + $response["status"] = "error"; + $response["message"] = "No access."; + } else { - $folders = array(); - $files = array(); - foreach ($index as $item => $data) { - - if ( $data['type'] == 'directory' ) { - - $folders[] = array( "name"=>htmlentities( $data['name'], ENT_QUOTES ), "type"=>$data['type'], "size"=>$data['size'] ); - } - if ( $data['type'] == 'file' ) { - - $files[] = array( "name"=>htmlentities( $data['name'], ENT_QUOTES ), "type"=>$data['type'], "size"=>$data['size'] ); - } - } + $path = self::formatPath( $path ); + if ( file_exists( $path ) ) { - function sorter($a, $b, $key = 'name') { - - return strnatcmp( $a[$key], $b[$key] ); - } - - usort( $folders, "sorter" ); - usort( $files, "sorter" ); - - $output = array_merge( $folders, $files ); - - $this->status = "success"; - $this->data = '"index":' . json_encode( $output ); + self::recursive_delete( $path, $follow, $keep_parent ); + $response["status"] = "success"; } else { - $this->status = "error"; - $this->message = "Not A Directory"; + $response["status"] = "error"; + $response["message"] = "Path Does Not Exist "; + } + } + return $response; + } + + ////////////////////////////////////////////////////////////////// + // DUPLICATE (Creates a duplicate of the object - (cut/copy/paste) + ////////////////////////////////////////////////////////////////// + + public function duplicate( $source, $destination ) { + + $response = array( + "status" => "none", + "message" => null, + ); + + $source = self::formatPath( $source ); + $destination = self::formatPath( $destination ); + $new_destination = $destination; + $path_parts = pathinfo( $destination ); + $i = 1; + + do { + + if( is_dir( $new_destination ) ) { + + $new_destination = rtrim( $destination, "/" ) . " $i/"; + } elseif( is_file( $new_destination ) ) { + + if( isset( $path_parts["extension"] ) ) { + + $new_destination = str_replace( ".{$path_parts["extension"]}", " {$i}.{$path_parts["extension"]}", $destination ); + } else { + + $new_destination = $destination . " $i"; + } + } + $i++; + } while( ( is_file( $new_destination ) || is_dir( $new_destination ) ) ); + + if( file_exists( $source ) ) { + + if( is_file( $source ) ) { + + copy( $source, $new_destination ); + $response["status"] = "success"; + } else { + + self::recursive_copy( $source, $new_destination ); + $response["status"] = "success"; } } else { - $this->status = "error"; - $this->message = "Path Does Not Exist"; + + $response["status"] = "error"; + $response["message"] = "Invalid Source"; } - - $this->respond(); + return $response; } - public function find() { - + public function find( $path, $query, $options = array() ) { + + $response = array( + "status" => "none", + "message" => null, + ); + $current_path = getcwd(); + $path = self::formatPath( $path ); + if ( ! function_exists( 'shell_exec' ) ) { - $this->status = "error"; - $this->message = "Shell_exec() Command Not Enabled."; + $response["status"] = "error"; + $response["message"] = "Shell_exec() Command Not Enabled."; } else { - chdir( $this->path ); - $input = str_replace( '"', '', $this->query ); + chdir( $path ); + $input = str_replace( '"', '', $query ); $cmd = 'find -L '; $strategy = ''; - if ( $this->foptions && $this->foptions['strategy'] ) { + if ( ! empty( $options ) && isset( $options["strategy"] ) ) { - $strategy = $this->foptions['strategy']; + $strategy = $options["strategy"]; } + switch ( $strategy ) { case 'substring': - $cmd = "$cmd -iname ".escapeshellarg( '*' . $input . '*' ); + + $cmd = "$cmd -iname " . escapeshellarg( '*' . $input . '*' ); break; case 'regexp': - $cmd = "$cmd -regex ".escapeshellarg( $input ); + + $cmd = "$cmd -regex " . escapeshellarg( $input ); break; case 'left_prefix': default: - $cmd = "$cmd -iname ".escapeshellarg( $input . '*'); + $cmd = "$cmd -iname " . escapeshellarg( $input . '*'); break; } $cmd = "$cmd -printf \"%h/%f %y\n\""; @@ -278,7 +262,7 @@ class Filemanager extends Common { } if ( strlen( $fname ) != 0 ) { - $fname = $this->rel_path . substr( $fname, 2 ); + $fname = $path . substr( $fname, 2 ); $f = array( 'path' => $fname, 'type' => $ftype ); array_push( $output_arr, $f ); } @@ -286,73 +270,244 @@ class Filemanager extends Common { if ( count( $output_arr ) == 0 ) { - $this->status = "error"; - $this->message = "No Results Returned"; + $response["status"] = "error"; + $response["message"] = "No Results Returned"; } else { - $this->status = "success"; - $this->data = '"index":' . json_encode( $output_arr ); + $response["status"] = "success"; + $response["index"] = $output_arr; } } - $this->respond(); + return $response; + } + + public static function formatPath( $path ) { + + if( self::isAbsPath( $path ) ) { + + $path = self::cleanPath( $path ); + } else { + + $path = WORKSPACE . "/" . self::cleanPath( $path ); + } + + if( is_dir( $path ) ) { + + $path = rtrim( $path, '/' ) . '/'; + } + return( $path ); } ////////////////////////////////////////////////////////////////// - // SEARCH + // INDEX (Returns list of files and directories) ////////////////////////////////////////////////////////////////// - public function search() { + public function index( $path ) { - if ( ! function_exists( 'shell_exec' ) ) { + $response = array( + "status" => "none", + "message" => null, + ); + $relative_path = rtrim( self::cleanPath( $path ), '/' ) . '/'; + $path = self::formatPath( $path ); + + if( file_exists( $path ) ) { - $this->status = "error"; - $this->message = "Shell_exec() Command Not Enabled."; - } else { + $index = array(); + + if( is_dir( $path ) && $handle = opendir( $path ) ) { - if ( $_GET['type'] == 1 ) { - - $this->path = WORKSPACE; - } - $return = array(); - $input = str_replace( '"', '', $this->search_string ); - $cmd = 'find -L ' . escapeshellarg( $this->path ) . ' -iregex '.escapeshellarg( '.*' . $this->search_file_type ) . ' -type f | xargs grep -i -I -n -R -H ' . escapeshellarg( $input ) . ''; - $output = shell_exec( $cmd ); - $output_arr = explode( "\n", $output ); - foreach ( $output_arr as $line ) { - - $data = explode( ":", $line ); - $da = array(); - if ( count( $data ) > 2 ) { + while( false !== ( $object = readdir( $handle ) ) ) { - $da['line'] = $data[1]; - $da['file'] = str_replace( $this->path, '', $data[0] ); - $da['result'] = str_replace( $this->root, '', $data[0] ); - $da['string'] = str_replace( $data[0] . ":" . $data[1] . ':', '', $line ); - $return[] = $da; + if( $object != "." && $object != ".." && $object != $this->controller ) { + + if ( is_dir( $path . '/' . $object ) ) { + + $type = "directory"; + $size = count( glob( $path . '/' . $object . '/*' ) ); + } else { + + $type = "file"; + $size = @filesize( $path . '/' . $object ); + } + $index[] = array( + + "name" => $relative_path . $object, + "type" => $type, + "size" => $size + ); + } + } + + $folders = array(); + $files = array(); + foreach( $index as $item => $data ) { + + if ( $data['type'] == 'directory' ) { + + $folders[] = array( "name" => htmlentities( $data['name'], ENT_QUOTES ), "type" => $data['type'], "size" => $data['size'] ); + } + if ( $data['type'] == 'file' ) { + + $files[] = array( "name" => htmlentities( $data['name'], ENT_QUOTES ), "type" => $data['type'], "size" => $data['size'] ); + } } - } - if ( count( $return ) == 0 ) { - $this->status = "error"; - $this->message = "No Results Returned"; + function sorter( $a, $b, $key = 'name' ) { + + return strnatcmp( $a[$key], $b[$key] ); + } + + usort( $folders, "sorter" ); + usort( $files, "sorter" ); + + $output = array_merge( $folders, $files ); + + $response["status"] = "success"; + $response["data"] = array( "index" => $output ); } else { - $this->status = "success"; - $this->data = '"index":' . json_encode( $return ); + $response["status"] = "error"; + $response["message"] = "Not A Directory"; } + } else { + + $response["status"] = "error"; + $response["message"] = "Path Does Not Exist"; } - $this->respond(); + return $response; + } + + ////////////////////////////////////////////////////////////////// + // MODIFY (Modifies a file name/contents or directory name) + ////////////////////////////////////////////////////////////////// + + public function modify( $path, $content, $patch=false, $mtime=0 ) { + + // Change content + $response = array( + "status" => "none", + "message" => null, + ); + $path = self::formatPath( $path ); + + if( $content == ' ' ) { + + $content = ''; // Blank out file + } + + if( $patch && ! $mtime ) { + + $response["status"] = "error"; + $response["message"] = "mtime parameter not found"; + return $response; + } + + if( is_file( $path ) ) { + + $serverMTime = filemtime( $path ); + $fileContents = file_get_contents( $path ); + + if( $patch && $mtime != $serverMTime ) { + + $response["status"] = "error"; + $response["message"] = "Client is out of sync"; + //DEBUG : file_put_contents($this->path.".conflict", "SERVER MTIME :".$serverMTime.", CLIENT MTIME :".$this->mtime); + return $response; + } elseif( strlen( trim( $patch ) ) == 0 && ! $content ) { + + // Do nothing if the patch is empty and there is no content + $response["status"] = "success"; + $response["data"] = array( + "mtime" => $serverMTime + ); + return $response; + } + + if( $file = fopen( $path, 'w' ) ) { + + if( $patch ) { + + $dmp = new diff_match_patch(); + $p = $dmp->patch_apply( $dmp->patch_fromText( $patch ), $fileContents ); + $content = $p[0]; + //DEBUG : file_put_contents($this->path.".orig",$fileContents ); + //DEBUG : file_put_contents($this->path.".patch", $this->patch); + } + + if( fwrite( $file, $content ) === false ) { + + $response["status"] = "error"; + $response["message"] = "could not write to file"; + } else { + + // Unless stat cache is cleared the pre-cached mtime will be + // returned instead of new modification time after editing + // the file. + clearstatcache(); + $response["status"] = "success"; + $response["data"] = array( + "mtime" => filemtime( $path ) + ); + } + fclose( $file ); + } else { + + $response["status"] = "error"; + $response["message"] = "Cannot Write to File"; + } + + } else { + + $response["status"] = "error"; + $response["message"] = "Not A File"; + } + return $response; + } + + public function move( $path, $new_path ) { + + $response = array( + "status" => "none", + ); + $path = self::formatPath( $path ); + $new_path = self::formatPath( $new_path ); + + if ( ! file_exists( $new_path ) ) { + + if( rename( $path, $new_path ) ) { + + $response["status"] = "success"; + } else { + + $response["status"] = "error"; + $response["message"] = "Could Not Rename"; + } + } else { + + $response["status"] = "error"; + $response["message"] = "Path Already Exists"; + } + return $response; } ////////////////////////////////////////////////////////////////// // OPEN (Returns the contents of a file) ////////////////////////////////////////////////////////////////// - public function open() { + public function open( $path ) { - if ( is_file( $this->path ) ) { + $response = array( + "status" => "none", + "message" => null, + ); + + $relative_path = self::cleanPath( $path ); + $path = self::formatPath( $path ); + + if ( is_file( $path ) ) { + + $output = file_get_contents( $path ); - $output = file_get_contents($this->path); - if ( extension_loaded( 'mbstring' ) ) { if ( ! mb_check_encoding( $output, 'UTF-8' ) ) { @@ -367,423 +522,185 @@ class Filemanager extends Common { } } - $this->status = "success"; - $this->data = '"content":' . json_encode( $output ); - $mtime = filemtime( $this->path ); - $this->data .= ', "mtime":'.$mtime; + $response["status"] = "success"; + $response["data"] = array( + "content" => $output, + "mtime" => filemtime( $path ), + ); } else { - $this->status = "error"; - $this->message = "Not A File :" . $this->path; + $response["status"] = "error"; + $response["message"] = "Error, {$path} is not a file."; } - - $this->respond(); + return $response; } + + ////////////////////////////////////////////////////////////////// + // OPEN IN BROWSER (Return URL) + ////////////////////////////////////////////////////////////////// + + public function openinbrowser( $path ) { - ////////////////////////////////////////////////////////////////// - // OPEN IN BROWSER (Return URL) - ////////////////////////////////////////////////////////////////// - - public function openinbrowser() { - $protocol = ( ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] != 'off' ) || $_SERVER['SERVER_PORT'] == 443 ) ? "https://" : "http://"; $domainName = $_SERVER['HTTP_HOST']; - $url = $protocol . WSURL . '/' . $this->rel_path; - $this->status = "success"; - $this->data = '"url":' . json_encode( rtrim( $url, "/" ) ); - $this->respond(); + $url = $protocol . WSURL . '/' . self::cleanPath( $path ); + $response = array( + "status" => "success", + "data" => rtrim( $url, "/" ), + ); + return $response; } - ////////////////////////////////////////////////////////////////// - // CREATE (Creates a new file or directory) - ////////////////////////////////////////////////////////////////// - - public function create() { - - // Create file - if ( $this->type == "file" ) { - - if ( ! file_exists( $this->path ) ) { - - if ( $file = fopen( $this->path, 'w' ) ) { - - // Write content - if ( $this->content ) { - - fwrite( $file, $this->content ); - } - $this->data = '"mtime":' . filemtime( $this->path ); - fclose( $file ); - $this->status = "success"; - } else { - - $this->status = "error"; - $this->message = "Cannot Create File"; - } - } else { - - $this->status = "error"; - $this->message = "File Already Exists"; - } - } + static function recursive_copy( $source, $destination ) { - // Create directory - if ( $this->type == "directory" ) { - - if ( ! is_dir( $this->path ) ) { - - mkdir( $this->path ); - $this->status = "success"; - } else { - - $this->status = "error"; - $this->message = "Directory Already Exists"; - } - } + $dir = opendir( $source ); + @mkdir( $source ); - $this->respond(); - } - - ////////////////////////////////////////////////////////////////// - // DELETE (Deletes a file or directory (+contents or only contents)) - ////////////////////////////////////////////////////////////////// - - public function delete( $keep_parent = false ) { - - if( Common::checkPath( $path ) ) { + if( is_dir( $source ) ) { + + @mkdir( $destination ); + } else { - $this->status = "error"; - $this->message = "No access."; - $this->respond(); return; } - function rrmdir( $path, $follow, $keep_parent = false ) { + while ( false !== ( $file = readdir( $dir ) ) ) { - if ( is_file( $path ) ) { - - unlink( $path ); - } else { - - $files = array_diff( scandir( $path ), array( '.', '..' ) ); - foreach ( $files as $file ) { - - if ( is_link( $path . "/" . $file ) ) { - - if ( $follow ) { - - rrmdir( $path . "/" . $file, $follow, false); - } - unlink( $path . "/" . $file ); - } elseif ( is_dir( $path . "/" . $file ) ) { - - rrmdir( $path . "/" . $file, $follow, false ); - } else { - - unlink( $path . "/" . $file ); - } - } - if( $keep_parent === false ) { - - rmdir( $path ); - return; - } else { + if ( ( $file != '.' ) && ( $file != '..' ) ) { - return; + if ( is_dir( $source . '/' . $file ) ) { + + self::recurse_copy( $source . '/' . $file, $destination . '/' . $file ); + } else { + + copy( $source . '/' . $file, $destination . '/' . $file ); } } } - - if ( file_exists( $this->path ) ) { - - if ( isset( $_GET['follow'] ) ) { - - rrmdir( $this->path, true, $keep_parent ); - } else { - - rrmdir( $this->path, false, $keep_parent ); - } - - $this->status = "success"; - } else { - - $this->status = "error"; - $this->message = "Path Does Not Exist "; - } - $this->respond(); + closedir( $dir ); } - - ////////////////////////////////////////////////////////////////// - // MODIFY (Modifies a file name/contents or directory name) - ////////////////////////////////////////////////////////////////// - - public function modify() { + static function recursive_delete( $path, $follow, $keep_parent = false ) { - // Change name - if ( $this->new_name ) { + if ( is_file( $path ) ) { - $explode = explode( '/', $this->path ); - array_pop( $explode ); - $new_path = implode( "/", $explode ) . "/" . $this->new_name; - $new_path = $this->cleanPath( $new_path ); - - if ( ! file_exists( $new_path ) ) { - - if ( rename( $this->path, $new_path ) ) { - - //unlink($this->path); - $this->status = "success"; - } else { - - $this->status = "error"; - $this->message = "Could Not Rename"; - } - } else { - - $this->status = "error"; - $this->message = "Path Already Exists"; - } + unlink( $path ); } else { + + $files = array_diff( scandir( $path ), array( '.', '..' ) ); + foreach ( $files as $file ) { - // Change content - if ( $this->content || $this->patch ) { + if ( is_link( $path . "/" . $file ) ) { - if ( $this->content == ' ' ) { - - $this->content = ''; // Blank out file - } - if ( $this->patch && ! $this->mtime ) { - - $this->status = "error"; - $this->message = "mtime parameter not found"; - $this->respond(); - return; - } - if ( is_file( $this->path ) ) { - - $serverMTime = filemtime( $this->path ); - $fileContents = file_get_contents( $this->path ); - - if ( $this->patch && $this->mtime != $serverMTime ) { + if ( $follow ) { - $this->status = "error"; - $this->message = "Client is out of sync"; - //DEBUG : file_put_contents($this->path.".conflict", "SERVER MTIME :".$serverMTime.", CLIENT MTIME :".$this->mtime); - $this->respond(); - return; - } elseif ( strlen( trim( $this->patch ) ) == 0 && ! $this->content ) { - - // Do nothing if the patch is empty and there is no content - $this->status = "success"; - $this->data = '"mtime":' . $serverMTime; - $this->respond(); - return; - } - - if ( $file = fopen( $this->path, 'w' ) ) { - - if ( $this->patch ) { - - $dmp = new diff_match_patch(); - $p = $dmp->patch_apply( $dmp->patch_fromText( $this->patch ), $fileContents ); - $this->content = $p[0]; - //DEBUG : file_put_contents($this->path.".orig",$fileContents ); - //DEBUG : file_put_contents($this->path.".patch", $this->patch); - } - - if ( fwrite( $file, $this->content ) === false ) { - - $this->status = "error"; - $this->message = "could not write to file"; - } else { - - // Unless stat cache is cleared the pre-cached mtime will be - // returned instead of new modification time after editing - // the file. - clearstatcache(); - $this->data = '"mtime":'.filemtime( $this->path ); - $this->status = "success"; - } - - fclose( $file ); - } else { - - $this->status = "error"; - $this->message = "Cannot Write to File"; + self::recursive_delete( $path . "/" . $file, $follow, false); } + unlink( $path . "/" . $file ); + } elseif ( is_dir( $path . "/" . $file ) ) { + self::recursive_delete( $path . "/" . $file, $follow, false ); } else { - $this->status = "error"; - $this->message = "Not A File"; + unlink( $path . "/" . $file ); } + } + if( $keep_parent === false ) { + + rmdir( $path ); + return; } else { - $file = fopen( $this->path, 'w' ); - fclose( $file ); - $this->data = '"mtime":' . filemtime ($this->path); - $this->status = "success"; + return; } } - - $this->respond(); } ////////////////////////////////////////////////////////////////// - // DUPLICATE (Creates a duplicate of the object - (cut/copy/paste) + // SEARCH ////////////////////////////////////////////////////////////////// - public function duplicate() { - - if ( ! file_exists( $this->path ) ) { - - $this->status = "error"; - $this->message = "Invalid Source"; - } + public function search( $path, $query, $options ) { - function recurse_copy( $src, $dst ) { + $response = array( + "status" => "none", + "message" => null, + ); + if ( ! function_exists( 'shell_exec' ) ) { - $dir = opendir( $src ); - @mkdir( $dst ); - while ( false !== ( $file = readdir( $dir ) ) ) { + $response["status"] = "error"; + $response["message"] = "Shell_exec() Command Not Enabled."; + } else { + + $return = array(); + $input = str_replace( '"', '', $query ); + $cmd = 'find -L ' . escapeshellarg( $path ) . ' -iregex '.escapeshellarg( '.*' . $options["filetype"] ) . ' -type f | xargs grep -i -I -n -R -H ' . escapeshellarg( $input ) . ''; + $output = shell_exec( $cmd ); + $output_arr = explode( "\n", $output ); + foreach ( $output_arr as $line ) { - if (( $file != '.' ) && ( $file != '..' )) { + $data = explode( ":", $line ); + $da = array(); + if ( count( $data ) > 2 ) { - if ( is_dir( $src . '/' . $file ) ) { - - recurse_copy( $src . '/' . $file, $dst . '/' . $file ); - } else { - - copy( $src . '/' . $file, $dst . '/' . $file ); - } + $da['line'] = $data[1]; + $da['file'] = str_replace( $path, '', $data[0] ); + $da['result'] = str_replace( $path, '', $data[0] ); + $da['string'] = str_replace( $data[0] . ":" . $data[1] . ':', '', $line ); + $return[] = $da; } } - closedir($dir); - } - - if ( $this->status != "error" ) { - - if ( is_file( $this->path ) ) { + if ( count( $return ) == 0 ) { - copy( $this->path, $this->destination ); - $this->status = "success"; + $response["status"] = "error"; + $response["message"] = "No Results Returned"; } else { - recurse_copy( $this->path, $this->destination ); - if ( ! $this->response ) { - - $this->status = "success"; - } + $response["status"] = "success"; + $response["index"] = $return; } } - $this->respond(); + return $response; } ////////////////////////////////////////////////////////////////// // UPLOAD (Handles uploads to the specified directory) ////////////////////////////////////////////////////////////////// - public function upload() { - + public function upload( $path ) { + // Check that the path is a directory - if ( is_file( $this->path ) ) { + $response = array( + "status" => "none", + "message" => "", + "files" => array(), + ); + if ( is_file( $path ) ) { - $this->status = "error"; - $this->message = "Path Not A Directory"; + $response["status"] = "error"; + $response["message"] = "Path Not A Directory"; } else { - // Handle upload - $info = array(); foreach( $_FILES['upload']['name'] as $key => $value ) { if ( ! empty( $value ) ) { $filename = $value; - $add = $this->path."/$filename"; - if ( @move_uploaded_file( $_FILES['upload']['tmp_name'][$key], $add ) ) { + $filepath = $path . "/$filename"; + if ( @move_uploaded_file( $_FILES['upload']['tmp_name'][$key], $filepath ) ) { $info[] = array( - "name"=>$value, - "size"=>filesize($add), - "url"=>$add, - "thumbnail_url"=>$add, - "delete_url"=>$add, - "delete_type"=>'DELETE' + "name" => $filename, + "size" => filesize( $filepath ), + "url" => $filepath, + "thumbnail_url" => $filepath, + "delete_url" => $filepath, + "delete_type" => 'DELETE' ); } } } - $this->upload_json = json_encode( $info ); } - $this->respond(); - } - - ////////////////////////////////////////////////////////////////// - // RESPOND (Outputs data in JSON [JSEND] format) - ////////////////////////////////////////////////////////////////// - - public function respond() { - - // Success /////////////////////////////////////////////// - if ( $this->status=="success" ) { - - if ( $this->data ) { - - $json = '{"status":"success","data":{' . $this->data . '}}'; - } else { - $json = '{"status":"success","data":null}'; - } - - // Upload JSON /////////////////////////////////////////// - } elseif ( $this->upload_json != '' ) { - - $json = $this->upload_json; - // Error ///////////////////////////////////////////////// - } else { - - $json = '{"status":"error","message":"'.$this->message.'"}'; - } - - // Output //////////////////////////////////////////////// - echo( $json ); - } - - ////////////////////////////////////////////////////////////////// - // Clean a path - ////////////////////////////////////////////////////////////////// - - public static function cleanPath( $path ) { - - // Prevent going out of the workspace - while ( strpos( $path, '../' ) !== false ) { - - $path = str_replace( '../', '', $path ); - } - - if( Filemanager::isAbsPath( $path ) ) { - - $full_path = $path; - } else { - - $full_path = WORKSPACE . "/" . $path; - } - - /** - * If a file with an invalid character exists and the user is - * trying to rename or delete it, allow the actual file name. - */ - - $invalid_characters = preg_match( '/[^A-Za-z0-9\-\._@\/\ ]/', $path ); - - if( $invalid_characters && ! ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) { - - exit( '{"status":"error","message":"Error, the filename contains invalid characters, please either rename or delete it."}' ); - } elseif( $invalid_characters && ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) { - } else { - - $path = preg_replace( '/[^A-Za-z0-9\-\._\/\ ]/', '', $path ); - } - return $path; + return $response; } } diff --git a/components/filemanager/controller.php b/components/filemanager/controller.php index 4822eb3..22956ba 100755 --- a/components/filemanager/controller.php +++ b/components/filemanager/controller.php @@ -1,95 +1,230 @@ "none", +); if (!empty($_GET['action'])) { - $action = $_GET['action']; + + $action = $_GET['action']; } else { - exit('{"status":"error","data":{"error":"No Action Specified"}}'); + + $response["status"] = "error"; + $response["data"] = array( + "error" => "No action specified" + ); + exit( json_encode( $response ) ); } - ////////////////////////////////////////////////////////////////// - // Ensure Project Has Been Loaded - ////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// +// Ensure Project Has Been Loaded +////////////////////////////////////////////////////////////////// -if (!isset($_SESSION['project'])) { - $_GET['action']='get_current'; - $_GET['no_return']='true'; - require_once('../project/controller.php'); -} - - ////////////////////////////////////////////////////////////////// - // Security Check - ////////////////////////////////////////////////////////////////// - -if (!checkPath($_GET['path'])) { - die('{"status":"error","message":"Invalid Path"}'); +if ( ! isset( $_SESSION['project'] ) ) { + + $_GET['action'] = 'get_current'; + $_GET['no_return'] = 'true'; + require_once('../project/controller.php'); } - ////////////////////////////////////////////////////////////////// - // Define Root - ////////////////////////////////////////////////////////////////// - - $_GET['root'] = WORKSPACE; - - ////////////////////////////////////////////////////////////////// - // Handle Action - ////////////////////////////////////////////////////////////////// - - $Filemanager = new Filemanager($_GET, $_POST, $_FILES); - $Filemanager->project = @$_SESSION['project']['path']; - -switch ($action) { - case 'index': - $Filemanager->index(); - break; - case 'search': - $Filemanager->search(); - break; - case 'find': - $Filemanager->find(); - break; - case 'open': - $Filemanager->open(); - break; - case 'open_in_browser': - $Filemanager->openinbrowser(); - break; - case 'create': - $Filemanager->create(); - break; - case 'delete': - $Filemanager->delete(); - break; - case 'deleteInner': - $Filemanager->delete( true ); - break; - case 'modify': - $Filemanager->modify(); - break; - case 'duplicate': - $Filemanager->duplicate(); - break; - case 'upload': - $Filemanager->upload(); - break; - default: - exit('{"status":"fail","data":{"error":"Unknown Action"}}'); +if( isset( $_GET["path"] ) ) { + + $path = $_GET["path"]; +} else { + + $response["status"] = "error"; + $response["message"] = "Missing path."; + exit( json_encode( $response ) ); } + +////////////////////////////////////////////////////////////////// +// Security Check +////////////////////////////////////////////////////////////////// + +if ( ! checkPath( $path ) ) { + + $response["status"] = "error"; + $response["message"] = "Invalid Path"; + exit( json_encode( $response ) ); +} + +if( isset( $_GET["destination"] ) ) { + + $destination = $_GET["destination"]; + + if ( ! checkPath( $destination ) ) { + + $response["status"] = "error"; + $response["message"] = "Invalid destination"; + exit( json_encode( $response ) ); + } +} + +////////////////////////////////////////////////////////////////// +// Handle Action +////////////////////////////////////////////////////////////////// + +$Filemanager = new Filemanager(); + +switch( $action ) { + + case 'create': + + if( isset( $_GET["type"] ) ) { + + $type = $_GET["type"]; + $response = $Filemanager->create( $path, $type ); + } else { + + $response["status"] = "error"; + $response["message"] = "No filetype set"; + } + break; + + case 'delete': + + $response = $Filemanager->delete( $path, true ); + break; + + case 'deleteInner': + + $response = $Filemanager->delete( $path, true, true ); + break; + + case 'duplicate': + + $response = $Filemanager->duplicate( $path, $destination ); + break; + + case 'find': + + if( ! isset( $_GET["query"] ) ) { + + $response["status"] = "error"; + $response["message"] = "Missing search query."; + } else { + + $query = $_GET["query"]; + if( isset( $_GET["options"] ) ) { + + $options = $_GET["options"]; + } + + $response = $Filemanager->find( $path, $query, @$options ); + } + break; + + case 'index': + + $response = $Filemanager->index( $path ); + break; + + case 'modify': + + if( isset( $_POST["content"] ) || isset( $_POST["patch"] ) ) { + + $content = isset( $_POST["content"] ) ? $_POST["content"] : ""; + $patch = isset( $_POST["patch"] ) ? $_POST["patch"] : false; + $mtime = isset( $_POST["mtime"] ) ? $_POST["mtime"] : 0; + + if( get_magic_quotes_gpc() ){ + + $content = stripslashes( $content ); + $patch = stripslashes( $patch ); + $mtime = stripslashes( $mtime ); + } + + $response = $Filemanager->modify( $path, $content, $mtime ); + } else { + + $response["status"] = "error"; + $response["message"] = "Missing modification content"; + } + break; + + case 'move': + + if( isset( $destination ) ) { + + $response = $Filemanager->move( $path, $destination ); + } else { + + $response["status"] = "error"; + $response["message"] = "Missing destination"; + } + break; + + case 'open': + + $response = $Filemanager->open( $path ); + break; + + case 'open_in_browser': + + $response = $Filemanager->openinbrowser( $path ); + break; + + case 'rename': + + if( isset( $destination ) ) { + + $response = $Filemanager->move( $path, $destination ); + } else { + + $response["status"] = "error"; + $response["message"] = "Missing destination"; + } + break; + + case 'search': + + if( isset( $_GET["query"] ) ) { + + $query = $_GET["query"]; + if( isset( $_GET["options"] ) ) { + + $options = $_GET["options"]; + } + + $response = $Filemanager->search( $path, $query ); + } else { + + $response["status"] = "error"; + $response["message"] = "Missing search query."; + } + break; + + case 'upload': + + $response = $Filemanager->upload( $path ); + break; + + default: + + $response["status"] = "error"; + $response["data"] = array( + "error" => "Unknown action" + ); + break; +} + +exit( json_encode( $response ) ); \ No newline at end of file diff --git a/components/filemanager/dialog.php b/components/filemanager/dialog.php index 4d9d8d2..a6fe507 100755 --- a/components/filemanager/dialog.php +++ b/components/filemanager/dialog.php @@ -14,9 +14,7 @@ require_once('class.filemanager.php'); checkSession(); -?> - - -


">

- - - -


- -

- +
+

Error, unknown file type.

+
+ -
 			
-
+?>
diff --git a/components/filemanager/init.js b/components/filemanager/init.js
index ef867a6..782cc64 100755
--- a/components/filemanager/init.js
+++ b/components/filemanager/init.js
@@ -60,8 +60,6 @@
 				this.nodeListener();
 			this.auto_reload = ( await codiad.settings.get_option( "codiad.filemanager.autoReloadPreview" ) == "true" );
 			
-			console.log( this.auto_reload );
-			
 			amplify.subscribe( 'settings.save', async function() {
 				
 				let option = ( await codiad.settings.get_option( "codiad.filemanager.autoReloadPreview" ) == "true" );
@@ -717,7 +715,6 @@
 			} else if( path == this.clipboard ) {
 				codiad.message.error( i18n( 'Cannot Paste Directory Into Itself' ) );
 			} else {
-				let project = codiad.project.getCurrent();
 				var shortName = _this.getShortName( _this.clipboard );
 				if( $( '#file-manager a[data-path="' + path + '/' + shortName + '"]' )
 				.length ) { // Confirm overwrite?
@@ -731,15 +728,13 @@
 						var duplicate = false;
 						if( $( '#modal-content form select[name="or_action"]' ).val() == 1 ) {
 							duplicate = true;
-							console.log( 'Dup!' );
+							//console.log( 'Dup!' );
 						}
 						_this.processPasteNode( path, duplicate );
 					});
 				} else { // No conflicts; proceed...
 					_this.processPasteNode( path, false );
 				}
-				
-				codiad.filemanager.rescan( project );
 			}
 		},
 		
@@ -762,6 +757,7 @@
 							shortName: shortName,
 							duplicate: duplicate
 						});
+						codiad.filemanager.rescan( path );
 					}
 				});
 		},
@@ -794,9 +790,9 @@
 				}
 				var newPath = temp.join( '/' ) + '/' + newName;
 				$.get( _this.controller, {
-					action: 'modify',
+					action: 'rename',
 					path: path,
-					new_name: newName
+					destination: newPath
 				}, function( data ) {
 					var renameResponse = codiad.jsend.parse( data );
 					let renamedMessage = "";