2018-07-13 18:39:55 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
2019-09-23 03:35:26 +02:00
|
|
|
* Copyright (c) Codiad & Kent Safranski (codiad.com), Telaaedifex distributed
|
2018-07-13 18:39:55 +02:00
|
|
|
* as-is and without warranty under the MIT License. See
|
|
|
|
* [root]/license.txt for more. This information must remain intact.
|
|
|
|
*/
|
|
|
|
|
2019-12-22 08:57:43 +01:00
|
|
|
require_once( '../../lib/diff_match_patch.php' );
|
|
|
|
require_once( '../../common.php' );
|
|
|
|
require_once( __DIR__ . '/class.archive.php' );
|
2018-07-13 18:39:55 +02:00
|
|
|
|
2018-09-21 03:44:10 +02:00
|
|
|
class Filemanager extends Common {
|
|
|
|
|
2019-12-11 17:23:33 +01:00
|
|
|
const PATH_REGEX = '/[^\w\-\._@]/';
|
|
|
|
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// PROPERTIES
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// METHODS
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// -----------------------------||----------------------------- //
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// Construct
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
public function __construct() {}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// Clean a path
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
public static function cleanPath( $path ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
// Prevent going out of the workspace
|
|
|
|
while ( strpos( $path, '../' ) !== false ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$path = str_replace( '../', '', $path );
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
if( self::isAbsPath( $path ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$full_path = $path;
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$full_path = WORKSPACE . "/" . $path;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* If a file with an invalid character exists and the user is
|
|
|
|
* trying to rename or delete it, allow the actual file name.
|
|
|
|
*/
|
|
|
|
|
2019-10-25 04:27:51 +02:00
|
|
|
$invalid_characters = preg_match( '/[^A-Za-z0-9\-\._@\/\(\) ]/', $path );
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
if( $invalid_characters && ! ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
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 {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-10-25 04:27:51 +02:00
|
|
|
$path = preg_replace( '/[^A-Za-z0-9\-\._@\/\(\) ]/', '', $path );
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $path;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// CREATE (Creates a new file or directory)
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
public function create( $path, $type, $content = "" ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => null,
|
|
|
|
);
|
|
|
|
$path = self::formatPath( $path );
|
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
if( Permissions::has_create( $path ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
// Create file
|
|
|
|
if( $type == "file" ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
if ( ! file_exists( $path ) ) {
|
2019-07-16 22:02:35 +02:00
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
if ( $file = fopen( $path, 'w' ) ) {
|
2019-07-16 22:02:35 +02:00
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
// Write content
|
|
|
|
if ( $content ) {
|
|
|
|
|
|
|
|
fwrite( $file, $content );
|
|
|
|
}
|
|
|
|
fclose( $file );
|
|
|
|
|
|
|
|
$response["status"] = "success";
|
|
|
|
$response["mtime"] = filemtime( $path );
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Cannot Create File";
|
2019-07-16 22:02:35 +02:00
|
|
|
}
|
2019-10-25 18:46:23 +02:00
|
|
|
} else {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "File Already Exists";
|
|
|
|
}
|
|
|
|
} elseif( $type == "directory" ) {
|
|
|
|
|
|
|
|
if ( ! is_dir( $path ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
mkdir( $path );
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "success";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
2019-10-25 18:46:23 +02:00
|
|
|
$response["message"] = "Directory Already Exists";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
2019-10-25 18:46:23 +02:00
|
|
|
} else {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "You do not have permission to create files in this project";
|
2019-07-16 22:02:35 +02:00
|
|
|
}
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// DELETE (Deletes a file or directory (+contents or only contents))
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
public function delete( $path, $follow, $keep_parent = false ) {
|
|
|
|
|
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => null,
|
|
|
|
);
|
|
|
|
|
|
|
|
if( ! Common::checkPath( $path ) ) {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
2019-10-28 04:34:33 +01:00
|
|
|
$response["message"] = "You do not have access to delete this file";
|
2019-07-16 22:02:35 +02:00
|
|
|
} else {
|
|
|
|
|
|
|
|
$path = self::formatPath( $path );
|
|
|
|
if ( file_exists( $path ) ) {
|
2019-02-06 05:04:14 +01:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
self::recursive_delete( $path, $follow, $keep_parent );
|
|
|
|
$response["status"] = "success";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Path Does Not Exist ";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2019-07-16 22:02:35 +02:00
|
|
|
// DUPLICATE (Creates a duplicate of the object - (cut/copy/paste)
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
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 {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if( is_dir( $new_destination ) ) {
|
|
|
|
|
|
|
|
$new_destination = rtrim( $destination, "/" ) . " $i/";
|
|
|
|
} elseif( is_file( $new_destination ) ) {
|
|
|
|
|
|
|
|
if( isset( $path_parts["extension"] ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$new_destination = str_replace( ".{$path_parts["extension"]}", " {$i}.{$path_parts["extension"]}", $destination );
|
|
|
|
} else {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$new_destination = $destination . " $i";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
}
|
|
|
|
$i++;
|
|
|
|
} while( ( is_file( $new_destination ) || is_dir( $new_destination ) ) );
|
|
|
|
|
|
|
|
if( file_exists( $source ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if( is_file( $source ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
copy( $source, $new_destination );
|
|
|
|
$response["status"] = "success";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
self::recursive_copy( $source, $new_destination );
|
|
|
|
$response["status"] = "success";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
} else {
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Invalid Source";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
public function find( $path, $query, $options = array() ) {
|
|
|
|
|
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => null,
|
|
|
|
);
|
|
|
|
$current_path = getcwd();
|
|
|
|
$path = self::formatPath( $path );
|
|
|
|
|
2018-09-21 03:44:10 +02:00
|
|
|
if ( ! function_exists( 'shell_exec' ) ) {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Shell_exec() Command Not Enabled.";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
chdir( $path );
|
|
|
|
$input = str_replace( '"', '', $query );
|
2018-09-21 03:44:10 +02:00
|
|
|
$cmd = 'find -L ';
|
|
|
|
$strategy = '';
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( ! empty( $options ) && isset( $options["strategy"] ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$strategy = $options["strategy"];
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
|
2018-09-21 03:44:10 +02:00
|
|
|
switch ( $strategy ) {
|
|
|
|
|
|
|
|
case 'substring':
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
$cmd = "$cmd -iname " . escapeshellarg( '*' . $input . '*' );
|
2018-09-21 03:44:10 +02:00
|
|
|
break;
|
|
|
|
case 'regexp':
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
$cmd = "$cmd -regex " . escapeshellarg( $input );
|
2018-09-21 03:44:10 +02:00
|
|
|
break;
|
|
|
|
case 'left_prefix':
|
|
|
|
default:
|
2019-07-16 22:02:35 +02:00
|
|
|
$cmd = "$cmd -iname " . escapeshellarg( $input . '*');
|
2018-09-21 03:44:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
$cmd = "$cmd -printf \"%h/%f %y\n\"";
|
|
|
|
$output = shell_exec( $cmd );
|
|
|
|
$file_arr = explode( "\n", $output );
|
|
|
|
$output_arr = array();
|
|
|
|
|
|
|
|
error_reporting( 0 );
|
|
|
|
|
|
|
|
foreach ( $file_arr as $i => $fentry ) {
|
|
|
|
|
|
|
|
$farr = explode( " ", $fentry );
|
|
|
|
$fname = trim( $farr[0] );
|
|
|
|
if ( $farr[1] == 'f' ) {
|
|
|
|
|
|
|
|
$ftype = 'file';
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$ftype = 'directory';
|
|
|
|
}
|
|
|
|
if ( strlen( $fname ) != 0 ) {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$fname = $path . substr( $fname, 2 );
|
2018-09-21 03:44:10 +02:00
|
|
|
$f = array( 'path' => $fname, 'type' => $ftype );
|
|
|
|
array_push( $output_arr, $f );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( count( $output_arr ) == 0 ) {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "No Results Returned";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "success";
|
|
|
|
$response["index"] = $output_arr;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
public static function formatPath( $path ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if( self::isAbsPath( $path ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$path = self::cleanPath( $path );
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
$path = WORKSPACE . "/" . self::cleanPath( $path );
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
if( is_dir( $path ) ) {
|
|
|
|
|
|
|
|
$path = rtrim( $path, '/' ) . '/';
|
|
|
|
}
|
|
|
|
return( $path );
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2019-07-16 22:02:35 +02:00
|
|
|
// INDEX (Returns list of files and directories)
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
public function index( $path ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => null,
|
|
|
|
);
|
|
|
|
$relative_path = rtrim( self::cleanPath( $path ), '/' ) . '/';
|
|
|
|
$path = self::formatPath( $path );
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if( file_exists( $path ) ) {
|
|
|
|
|
|
|
|
$index = array();
|
|
|
|
|
2019-10-25 04:27:51 +02:00
|
|
|
if( is_dir( $path ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-10-25 04:27:51 +02:00
|
|
|
$files = $this->index_path( $path );
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
$response["status"] = "success";
|
2019-10-25 04:27:51 +02:00
|
|
|
$response["data"] = array( "index" => $files );
|
2019-07-16 22:02:35 +02:00
|
|
|
} else {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Not A Directory";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Path Does Not Exist";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
2019-10-25 04:27:51 +02:00
|
|
|
function index_path( $path ) {
|
|
|
|
|
|
|
|
$paths = array();
|
|
|
|
|
|
|
|
if( is_dir( $path ) && $handle = opendir( $path ) ) {
|
|
|
|
|
|
|
|
while( false !== ( $f = readdir( $handle ) ) ) {
|
|
|
|
|
|
|
|
if ( "$f" != '.' && "$f" != '..' ) {
|
|
|
|
|
|
|
|
$p = "$path" . DIRECTORY_SEPARATOR . "$f";
|
|
|
|
$p = str_replace( "//", "/", $p );
|
|
|
|
$rp = realpath( $p );
|
|
|
|
$path_info = pathinfo( $p );
|
|
|
|
|
|
|
|
if( is_dir( $p ) ) {
|
|
|
|
|
2019-11-07 04:42:33 +01:00
|
|
|
$children = $this->is_empty( $p ) ? null : array();
|
|
|
|
|
2019-10-25 04:27:51 +02:00
|
|
|
$paths[] = array(
|
|
|
|
|
|
|
|
"basename" => $path_info["basename"],
|
2019-11-07 04:42:33 +01:00
|
|
|
"children" => $children,
|
2019-10-25 04:27:51 +02:00
|
|
|
"dirname" => str_replace( WORKSPACE . "/", "", $p ),
|
|
|
|
"extension" => null,
|
|
|
|
"filename" => $path_info["filename"],
|
|
|
|
"full_dirname" => $path_info["dirname"],
|
|
|
|
"full_path" => $p,
|
|
|
|
"path" => str_replace( WORKSPACE . "/", "", $p ),
|
2019-10-25 18:46:23 +02:00
|
|
|
"type" => "directory",
|
2019-10-25 04:27:51 +02:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$paths[] = array(
|
|
|
|
"basename" => $path_info["basename"],
|
|
|
|
"dirname" => str_replace( WORKSPACE . "/", "", $p ),
|
|
|
|
"extension" => isset( $path_info["extension"] ) ? $path_info["extension"] : null,
|
|
|
|
"filename" => $path_info["filename"],
|
|
|
|
"path" => str_replace( WORKSPACE . "/", "", $p ),
|
2019-10-25 18:46:23 +02:00
|
|
|
"type" => "file",
|
2019-10-25 04:27:51 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir( $handle );
|
2019-10-25 18:46:23 +02:00
|
|
|
usort( $paths, array( $this, "sorter" ) );
|
2019-10-25 04:27:51 +02:00
|
|
|
return $paths;
|
|
|
|
}
|
|
|
|
|
2019-11-07 04:42:33 +01:00
|
|
|
function is_empty( $dir ) {
|
|
|
|
|
|
|
|
$pass = true;
|
|
|
|
|
|
|
|
if( is_dir( $dir ) ) {
|
|
|
|
|
|
|
|
$handle = opendir( $dir );
|
|
|
|
while( false !== ( $entry = readdir( $handle ) ) ) {
|
|
|
|
|
|
|
|
if( $entry != "." && $entry != ".." ) {
|
|
|
|
|
|
|
|
$pass = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir( $handle );
|
|
|
|
}
|
|
|
|
return $pass;
|
|
|
|
}
|
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
function sorter( $a, $b ) {
|
|
|
|
|
|
|
|
$basename = strnatcmp( $a["basename"], $b["basename"] );
|
|
|
|
$type = strnatcmp( $a["type"], $b["type"] );
|
|
|
|
$result = 0;
|
|
|
|
|
|
|
|
if( $type == 0 ) {
|
|
|
|
|
|
|
|
$result = $basename;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$result = $type;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
2019-07-16 22:02:35 +02:00
|
|
|
// MODIFY (Modifies a file name/contents or directory name)
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2020-01-22 16:14:01 +01:00
|
|
|
public function modify( $path, $content, $patch = false, $mtime = 0 ) {
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
// Change content
|
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => null,
|
|
|
|
);
|
|
|
|
$path = self::formatPath( $path );
|
|
|
|
|
|
|
|
if( $content == ' ' ) {
|
|
|
|
|
|
|
|
$content = ''; // Blank out file
|
|
|
|
}
|
|
|
|
|
2019-10-25 18:46:23 +02:00
|
|
|
if( ! Permissions::has_write( $path ) ) {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "You do not have access to write to this file.";
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if( $patch && ! $mtime ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
2019-10-16 16:20:09 +02:00
|
|
|
$response["message"] = "invalid mtime parameter not found";
|
|
|
|
$response["mtime"] = $mtime;
|
2019-07-16 22:02:35 +02:00
|
|
|
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 ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
// 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 ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$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";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
// 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 )
|
|
|
|
);
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
fclose( $file );
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Cannot Write to File";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Not A File";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function move( $path, $new_path ) {
|
|
|
|
|
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
);
|
|
|
|
$path = self::formatPath( $path );
|
|
|
|
$new_path = self::formatPath( $new_path );
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( ! file_exists( $new_path ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if( rename( $path, $new_path ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "success";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Could Not Rename";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
} else {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Path Already Exists";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2019-07-16 22:02:35 +02:00
|
|
|
// OPEN (Returns the contents of a file)
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
public function open( $path ) {
|
2018-10-11 16:17:41 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => null,
|
|
|
|
);
|
2018-10-11 16:17:41 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$relative_path = self::cleanPath( $path );
|
|
|
|
$path = self::formatPath( $path );
|
|
|
|
|
|
|
|
if ( is_file( $path ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$output = file_get_contents( $path );
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( extension_loaded( 'mbstring' ) ) {
|
|
|
|
|
|
|
|
if ( ! mb_check_encoding( $output, 'UTF-8' ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( mb_check_encoding( $output, 'ISO-8859-1' ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$output = utf8_encode( $output );
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$output = mb_convert_encoding( $content, 'UTF-8' );
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "success";
|
|
|
|
$response["data"] = array(
|
|
|
|
"content" => $output,
|
|
|
|
"mtime" => filemtime( $path ),
|
2019-10-25 18:46:23 +02:00
|
|
|
"read_only" => ( ! Permissions::has_write( $path ) ),
|
2019-07-16 22:02:35 +02:00
|
|
|
);
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Error, {$path} is not a file.";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2019-07-16 22:02:35 +02:00
|
|
|
// OPEN IN BROWSER (Return URL)
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
public function openinbrowser( $path ) {
|
|
|
|
|
|
|
|
$protocol = ( ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] != 'off' ) || $_SERVER['SERVER_PORT'] == 443 ) ? "https://" : "http://";
|
|
|
|
$domainName = $_SERVER['HTTP_HOST'];
|
|
|
|
$url = $protocol . WSURL . '/' . self::cleanPath( $path );
|
|
|
|
$response = array(
|
|
|
|
"status" => "success",
|
|
|
|
"data" => rtrim( $url, "/" ),
|
|
|
|
);
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
static function recursive_copy( $source, $destination ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$dir = opendir( $source );
|
|
|
|
@mkdir( $source );
|
|
|
|
|
|
|
|
if( is_dir( $source ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
@mkdir( $destination );
|
|
|
|
} else {
|
2019-03-03 17:38:22 +01:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ( false !== ( $file = readdir( $dir ) ) ) {
|
|
|
|
|
|
|
|
if ( ( $file != '.' ) && ( $file != '..' ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( is_dir( $source . '/' . $file ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
self::recurse_copy( $source . '/' . $file, $destination . '/' . $file );
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
copy( $source . '/' . $file, $destination . '/' . $file );
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
}
|
|
|
|
closedir( $dir );
|
|
|
|
}
|
|
|
|
|
|
|
|
static function recursive_delete( $path, $follow, $keep_parent = false ) {
|
|
|
|
|
|
|
|
if ( is_file( $path ) ) {
|
|
|
|
|
|
|
|
unlink( $path );
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
$files = array_diff( scandir( $path ), array( '.', '..' ) );
|
|
|
|
foreach ( $files as $file ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( is_link( $path . "/" . $file ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( $follow ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
self::recursive_delete( $path . "/" . $file, $follow, false);
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
unlink( $path . "/" . $file );
|
|
|
|
} elseif ( is_dir( $path . "/" . $file ) ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
self::recursive_delete( $path . "/" . $file, $follow, false );
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
unlink( $path . "/" . $file );
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
}
|
|
|
|
if( $keep_parent === false ) {
|
|
|
|
|
|
|
|
rmdir( $path );
|
|
|
|
return;
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
return;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
2019-07-16 22:02:35 +02:00
|
|
|
// SEARCH
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
public function search( $path, $query, $options ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => null,
|
|
|
|
);
|
2019-10-24 14:56:24 +02:00
|
|
|
|
|
|
|
if( ! common::isAbsPath( $path ) ) {
|
|
|
|
|
|
|
|
$path = WORKSPACE . "/$path";
|
|
|
|
}
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( ! function_exists( 'shell_exec' ) ) {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "Shell_exec() Command Not Enabled.";
|
|
|
|
} else {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$return = array();
|
|
|
|
$input = str_replace( '"', '', $query );
|
2019-10-24 23:56:23 +02:00
|
|
|
$cmd = 'find -L ' . escapeshellarg( $path ) . ' -iregex ' . escapeshellarg( '.*' . $options["filetype"] ) . ' -type f -print0 | xargs -0 grep -i -I -n -R -H ' . escapeshellarg( $input ) . '';
|
2019-07-16 22:02:35 +02:00
|
|
|
$output = shell_exec( $cmd );
|
|
|
|
$output_arr = explode( "\n", $output );
|
|
|
|
foreach ( $output_arr as $line ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$data = explode( ":", $line );
|
|
|
|
$da = array();
|
|
|
|
if ( count( $data ) > 2 ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$da['line'] = $data[1];
|
|
|
|
$da['file'] = str_replace( $path, '', $data[0] );
|
2019-10-24 23:56:23 +02:00
|
|
|
$da['result'] = str_replace( WORKSPACE . '/', '', $data[0] );
|
2019-07-16 22:02:35 +02:00
|
|
|
$da['string'] = str_replace( $data[0] . ":" . $data[1] . ':', '', $line );
|
|
|
|
$return[] = $da;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
if ( count( $return ) == 0 ) {
|
2018-09-21 03:44:10 +02:00
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "No Results Returned";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-07-16 22:02:35 +02:00
|
|
|
$response["status"] = "success";
|
2019-10-24 14:56:24 +02:00
|
|
|
$response["data"] = array();
|
|
|
|
$response["data"]["index"] = $return;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
|
|
|
|
2020-01-22 16:14:01 +01:00
|
|
|
public function stitch( $path ) {
|
|
|
|
|
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => "",
|
|
|
|
);
|
|
|
|
|
|
|
|
if( ! Permissions::has_write( $path ) ) {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "You do not have access to write to this file.";
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ! common::isAbsPath( $path ) ) {
|
|
|
|
|
|
|
|
$path = WORKSPACE . "/$path";
|
|
|
|
}
|
|
|
|
|
|
|
|
$path = $_POST["path"];
|
|
|
|
$tmp = DATA . "tmp/$path/";
|
|
|
|
$dir = dirname( $path );
|
|
|
|
$name = basename( $path );
|
|
|
|
$files = scandir( $tmp );
|
|
|
|
|
|
|
|
if( ! is_dir( $dir ) ) {
|
|
|
|
|
|
|
|
mkdir( $dir, 0755, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach( $files as $id => $file ) {
|
|
|
|
|
|
|
|
if( $file !== "." && $file !== ".." ) {
|
|
|
|
|
|
|
|
$data = file_get_contents( $cache_path . $file );
|
|
|
|
$handle = fopen( $path, "a" );
|
|
|
|
$status = fwrite( $handle, $data );
|
|
|
|
fclose( $handle );
|
|
|
|
unlink( $cache_path . $file );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$tmp_array = explode( "/", $path );
|
|
|
|
$remove = array();
|
|
|
|
|
|
|
|
while( count( $tmp_array ) > 0 ) {
|
|
|
|
|
|
|
|
$remove[] = DATA . "tmp/" . implode( "/", $tmp_array );
|
|
|
|
array_pop( $tmp_array );
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach( $tmp_array as $id => $i ) {
|
|
|
|
|
|
|
|
rmdir( $i );
|
|
|
|
}
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
2018-09-21 03:44:10 +02:00
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// UPLOAD (Handles uploads to the specified directory)
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-11-24 04:56:19 +01:00
|
|
|
public function upload( $path, $blob ) {
|
2019-07-16 22:02:35 +02:00
|
|
|
|
2020-01-22 16:14:01 +01:00
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => "",
|
|
|
|
);
|
|
|
|
|
2018-09-21 03:44:10 +02:00
|
|
|
// Check that the path is a directory
|
2019-11-24 04:56:19 +01:00
|
|
|
if( ! Permissions::has_write( $path ) ) {
|
|
|
|
|
|
|
|
$response["status"] = "error";
|
|
|
|
$response["message"] = "You do not have access to write to this file.";
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ! common::isAbsPath( $path ) ) {
|
|
|
|
|
|
|
|
$path = WORKSPACE . "/$path";
|
|
|
|
}
|
|
|
|
|
2020-01-22 16:14:01 +01:00
|
|
|
$dirname = dirname( $path );
|
|
|
|
$name = basename( $path );
|
|
|
|
|
|
|
|
$blob = @file_get_contents( $_POST["data"] );
|
|
|
|
$path = $_POST["path"];
|
|
|
|
$index = $_POST["index"];
|
2019-07-16 22:02:35 +02:00
|
|
|
$response = array(
|
|
|
|
"status" => "none",
|
|
|
|
"message" => "",
|
|
|
|
);
|
2020-01-22 16:14:01 +01:00
|
|
|
$tmp = DATA . "tmp/";
|
2019-11-24 04:56:19 +01:00
|
|
|
|
2020-01-22 16:14:01 +01:00
|
|
|
if( ! is_dir( $tmp . $path ) ) {
|
2019-11-24 04:56:19 +01:00
|
|
|
|
2020-01-22 16:14:01 +01:00
|
|
|
mkdir( $tmp . $path, 0755, true );
|
2019-11-24 04:56:19 +01:00
|
|
|
}
|
|
|
|
|
2020-01-22 16:14:01 +01:00
|
|
|
$handle = fopen( "$tmp$path/$index", "a" );
|
2019-12-12 14:22:43 +01:00
|
|
|
$status = fwrite( $handle, $blob );
|
|
|
|
fclose( $handle );
|
|
|
|
|
2019-11-24 04:56:19 +01:00
|
|
|
if( $status === false ) {
|
2019-07-16 22:02:35 +02:00
|
|
|
|
|
|
|
$response["status"] = "error";
|
2019-11-24 04:56:19 +01:00
|
|
|
$response["message"] = "File could not be written to.";
|
2018-09-21 03:44:10 +02:00
|
|
|
} else {
|
|
|
|
|
2019-11-24 04:56:19 +01:00
|
|
|
$response["status"] = "success";
|
|
|
|
$response["path"] = $path;
|
|
|
|
$response["bytes"] = $status;
|
|
|
|
$response["message"] = "$status bytes written to file.";
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-07-16 22:02:35 +02:00
|
|
|
return $response;
|
2018-09-21 03:44:10 +02:00
|
|
|
}
|
2019-03-03 21:04:50 +01:00
|
|
|
}
|