mirror of
https://github.com/gtbu/Typesetter-5.3-p8.git
synced 2024-11-21 21:59:12 +01:00
525 lines
10 KiB
PHP
525 lines
10 KiB
PHP
<?php
|
|
namespace gp\tool;
|
|
|
|
defined('is_running') or die('Not an entry point...');
|
|
|
|
|
|
gp_defined('FS_CHMOD_DIR',0755);
|
|
gp_defined('FS_CHMOD_FILE',0644); // 0666 causes errors on some systems
|
|
|
|
|
|
class FileSystem{
|
|
|
|
public $conn_id = false;
|
|
public $connect_vars = array();
|
|
public $temp_file = null;
|
|
public $method = 'gp_filesystem_direct';
|
|
public $connect_msg = '';
|
|
|
|
|
|
|
|
/**
|
|
* Determine which class is needed to write to $context
|
|
*
|
|
* @param array|string $context
|
|
*/
|
|
public static function init($context){
|
|
|
|
if( is_array($context) ){
|
|
$method = self::get_filesystem_method_list($context);
|
|
}else{
|
|
$method = self::get_filesystem_method($context);
|
|
}
|
|
return self::set_method($method);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a filesystem object based on the $method
|
|
* @param string $method
|
|
*/
|
|
public static function set_method($method){
|
|
|
|
switch($method){
|
|
case 'gp_filesystem_direct':
|
|
return new FileSystem();
|
|
|
|
|
|
case 'gp_filesystem_ftp':
|
|
return new FileSystemFtp();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine which class is needed to write to $context
|
|
*
|
|
* @param string $context
|
|
*/
|
|
public static function get_filesystem_method($context){
|
|
|
|
while( !file_exists($context) ){
|
|
$context = \gp\tool::DirName($context);
|
|
}
|
|
|
|
if( gp_is_writable($context) ){
|
|
return 'gp_filesystem_direct';
|
|
}
|
|
|
|
if( function_exists('ftp_connect') ){
|
|
return 'gp_filesystem_ftp';
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* check files and folders in $context according to instructions
|
|
*
|
|
* @since 2.0b1
|
|
*
|
|
* @param array $context array of paths (as keys) and instructions (as values), possible values are (file,dir)
|
|
*/
|
|
public static function get_filesystem_method_list($context = array()){
|
|
|
|
foreach($context as $file => $instruction){
|
|
switch($instruction){
|
|
case 'dir':
|
|
$writable = self::writable($file);
|
|
break;
|
|
case 'file':
|
|
$writable = gp_is_writable($file);
|
|
break;
|
|
}
|
|
|
|
if( $writable ){
|
|
continue;
|
|
}
|
|
|
|
if( function_exists('ftp_connect') ){
|
|
return 'gp_filesystem_ftp';
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
return 'gp_filesystem_direct';
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine if the file/directory/link is writable
|
|
*
|
|
*/
|
|
public static function writable($file){
|
|
|
|
if( is_link($file) ){
|
|
return self::writable_link($file);
|
|
}
|
|
|
|
if( is_dir($file) ){
|
|
return self::writable_dir($file);
|
|
}
|
|
|
|
return gp_is_writable($file);
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine if the directory and all it's contents are writable
|
|
*
|
|
*/
|
|
public static function writable_dir($dir){
|
|
|
|
if( !gp_is_writable($dir) ){
|
|
return false;
|
|
}
|
|
|
|
$dh = @opendir($dir);
|
|
if( !$dh ){
|
|
return false;
|
|
}
|
|
|
|
|
|
$dir = str_replace('\\','/',$dir);
|
|
$dir = rtrim($dir,'/');
|
|
|
|
while( ($file = readdir($dh)) !== false){
|
|
|
|
if( $file === '.' || $file === '..' ){
|
|
continue;
|
|
}
|
|
|
|
$full_path = $dir.'/'.$file;
|
|
|
|
if( !self::writable($full_path) ){
|
|
msg('not writable: '.$full_path);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the minimum filesystem_method for $link
|
|
* if the target of the symbolic link doesnt exist then is_writable($file) will return false
|
|
*
|
|
* @param string $link
|
|
*/
|
|
public static function writable_link($link){
|
|
|
|
$temp = self::TempFile( $link );
|
|
if( @rename( $link, $temp ) && @rename( $temp, $link ) ){
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function connectForm(){
|
|
return true;
|
|
}
|
|
public function CompleteForm(){
|
|
return true;
|
|
}
|
|
|
|
public function ConnectOrPrompt($action=''){
|
|
return true;
|
|
}
|
|
public function connect(){
|
|
return true;
|
|
}
|
|
|
|
public function connect_handler($args){
|
|
return true;
|
|
}
|
|
|
|
public function get_base_dir(){
|
|
global $dataDir;
|
|
return $dataDir;
|
|
}
|
|
|
|
public function mkdir($dir){
|
|
if( !mkdir($dir,FS_CHMOD_DIR) ){
|
|
return false;
|
|
}
|
|
chmod($dir,FS_CHMOD_DIR);
|
|
return true;
|
|
}
|
|
|
|
public function unlink($path){
|
|
return unlink($path);
|
|
}
|
|
|
|
public function is_dir($path){
|
|
return is_dir($path);
|
|
}
|
|
|
|
|
|
/**
|
|
* Delete folders
|
|
*
|
|
*/
|
|
public function CleanUpFolders($folders, &$not_deleted){
|
|
|
|
$filesystem_base = $this->get_base_dir();
|
|
$not_deleted = array();
|
|
|
|
foreach($folders as $old_folder){
|
|
|
|
if( ( strpos($old_folder,'../') !== false ) || ( strpos($old_folder,'./') !== false ) ){
|
|
continue;
|
|
}
|
|
|
|
$old_folder = '/'.ltrim($old_folder,'/');
|
|
$old_folder_full = $filesystem_base.$old_folder;
|
|
|
|
if( !$this->file_exists($old_folder_full) ){
|
|
continue;
|
|
}
|
|
|
|
if( !$this->rmdir_all($old_folder_full) ){
|
|
$not_deleted[] = htmlspecialchars($old_folder);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove a file, symlink or directory
|
|
* @param string $path
|
|
*/
|
|
public function rmdir_all($path){
|
|
|
|
if( empty($path) ) return false;
|
|
|
|
if( is_link($path) ){
|
|
return $this->unlink($path);
|
|
}
|
|
|
|
if( !$this->is_dir($path) ){
|
|
return $this->unlink($path);
|
|
}
|
|
|
|
$success = $this->rmdir_dir($path);
|
|
|
|
if( !$success ){
|
|
return false;
|
|
}
|
|
return rmdir($path);
|
|
}
|
|
|
|
|
|
/**
|
|
* Remnove a directory and all it's contents
|
|
*
|
|
*/
|
|
public function rmdir_dir($dir){
|
|
|
|
$success = true;
|
|
$list = $this->dirlist($dir);
|
|
|
|
if( !is_array($list) ){
|
|
return true;
|
|
}
|
|
|
|
foreach($list as $file){
|
|
$full_path = $dir.'/'.$file;
|
|
if( $this->is_dir($full_path) ){
|
|
if( !$this->rmdir_all($full_path) ){
|
|
$success = false;
|
|
}
|
|
}elseif( !$this->unlink($full_path) ){
|
|
$success = false;
|
|
}
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a list of files and folders in $dir
|
|
*
|
|
* @param string $dir
|
|
*
|
|
*/
|
|
public function dirlist($dir ){
|
|
|
|
$dh = @opendir($dir);
|
|
if( !$dh ){
|
|
return false;
|
|
}
|
|
|
|
$list = array();
|
|
while( ($file = readdir($dh)) !== false){
|
|
if( $file == '.' || $file == '..' ){
|
|
continue;
|
|
}
|
|
$list[$file] = $file;
|
|
}
|
|
closedir($dh);
|
|
return $list;
|
|
}
|
|
|
|
public function rename($old_name,$new_name){
|
|
return rename($old_name,$new_name);
|
|
}
|
|
|
|
public function RelRename($old_rel, $new_rel){
|
|
$fs_root = $this->get_base_dir();
|
|
$old = $fs_root.$old_rel;
|
|
$new = $fs_root.$new_rel;
|
|
return $this->rename($old,$new);
|
|
}
|
|
|
|
public function put_contents($file, $contents, $type = '' ){
|
|
if( !\gp\tool\Files::Save($file,$contents) ){
|
|
return false;
|
|
}
|
|
//the core does not need to be world writable
|
|
@chmod($file,FS_CHMOD_FILE);
|
|
return true;
|
|
}
|
|
|
|
public function get_connect_vars($args){
|
|
$result = array();
|
|
if( is_array($args) ){
|
|
foreach($args as $key => $value ){
|
|
if( array_key_exists($key,$this->connect_vars) ){
|
|
$result[$key] = $value;
|
|
}
|
|
}
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
public function destruct(){
|
|
if( is_null($this->temp_file) ){
|
|
return;
|
|
}
|
|
if( file_exists($this->temp_file) ){
|
|
unlink($this->temp_file);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create <input> elements for all the values in $array
|
|
*
|
|
* @param array $array
|
|
* @param string $name
|
|
*/
|
|
public function ArrayToForm($array,$name=null){
|
|
|
|
foreach($array as $key => $value){
|
|
|
|
if( !is_null($name) ){
|
|
$full_name = $name.'['.$key.']';
|
|
}else{
|
|
$full_name = $key;
|
|
}
|
|
|
|
if( is_array($value) ){
|
|
$this->ArrayToForm($value,$full_name);
|
|
continue;
|
|
}
|
|
echo '<input type="hidden" name="'.htmlspecialchars($full_name).'" value="'.htmlspecialchars($value).'" />';
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Append a random string to the end of $relative_from to get the path of a non-existant file
|
|
*
|
|
* @param string $relative_from The relative path of a file
|
|
* @return string The path of a non-existant file in the same directory as $relative_from
|
|
*
|
|
*/
|
|
public static function TempFile( $relative_from, $extension = '' ){
|
|
global $dataDir;
|
|
static $rand_index;
|
|
|
|
if( !empty($extension) ){
|
|
$extension = '.'.ltrim($extension,'.');
|
|
}
|
|
|
|
clearstatcache();
|
|
|
|
if( is_null($rand_index) ){
|
|
$rand_index = rand(1000,9000000);
|
|
}
|
|
|
|
$i = 0;
|
|
do{
|
|
$new_relative = $relative_from.'-'.$rand_index.$extension;
|
|
$full_path = $dataDir.$new_relative;
|
|
$i++;
|
|
|
|
}while( file_exists($full_path) && $i < 100 && $rand_index++ );
|
|
|
|
return $new_relative;
|
|
}
|
|
|
|
|
|
/**
|
|
* Replace the directories in $replace_dirs
|
|
*
|
|
* @param array $replace_dirs The relative paths of directories to be replaced
|
|
* @return mixed true if successful, error string otherwise
|
|
*
|
|
*/
|
|
public function ReplaceDirs( $replace_dirs, &$clean_dirs ){
|
|
global $langmessage, $dataDir;
|
|
|
|
|
|
$fs_root = $this->get_base_dir();
|
|
$trash_dirs = array();
|
|
$completed = true;
|
|
$message = '';
|
|
foreach( $replace_dirs as $to_rel => $from_rel ){
|
|
|
|
|
|
$to_rel = trim($to_rel,'/');
|
|
$from_rel = trim($from_rel,'/');
|
|
|
|
|
|
$completed = false;
|
|
$to_full = $fs_root.'/'.$to_rel;
|
|
$from_full = $fs_root.'/'.$from_rel;
|
|
$trash_rel = self::TempFile( $to_rel.'-old' );
|
|
$trash_full = $fs_root.'/'.$trash_rel;
|
|
|
|
if( !$this->file_exists($from_full) ){
|
|
$message = $langmessage['dir_replace_failed'].' (Exist Check Failed - '.$this->method.' - '.htmlspecialchars($from_full).')';
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
//rename the original to the trash if it exists
|
|
if( $this->file_exists($to_full) ){
|
|
if( !$this->rename( $to_full, $trash_full ) ){
|
|
$message = $langmessage['dir_replace_failed'].' (Rename of existing directory failed - '.$this->method.')';
|
|
break;
|
|
}
|
|
$trash_dirs[$to_rel] = $trash_rel;
|
|
}
|
|
|
|
|
|
//if we've gotten this far, it's very unlikely this rename would fail
|
|
if( !$this->rename( $from_full, $to_full ) ){
|
|
$message = $langmessage['dir_replace_failed'].' (Rename of new directory failed - '.$this->method.')';
|
|
break;
|
|
}
|
|
|
|
//break;
|
|
$completed = true;
|
|
}
|
|
|
|
|
|
if( $completed ){
|
|
$clean_dirs = $trash_dirs;
|
|
return true;
|
|
}
|
|
|
|
//if it's not all completed, undo the changes that were completed
|
|
foreach( $trash_dirs as $to_rel => $trash_rel ){
|
|
|
|
$to_full = $fs_root.'/'.$to_rel;
|
|
$from_full = $fs_root.'/'.$replace_dirs[$to_rel];
|
|
$trash_full = $fs_root.'/'.$trash_rel;
|
|
|
|
$this->rename( $to_full, $from_full );
|
|
$this->rename( $trash_full, $to_full );
|
|
}
|
|
|
|
$clean_dirs = $replace_dirs;
|
|
|
|
return $message;
|
|
}
|
|
|
|
public function file_exists($file){
|
|
clearstatcache(true, $file);
|
|
return file_exists($file);
|
|
}
|
|
|
|
|
|
/**
|
|
* Determines if the string provided contains binary characters.
|
|
*
|
|
* @since 2.7
|
|
*
|
|
* @param string $text String to test against
|
|
* @return bool true if string is binary, false otherwise
|
|
*/
|
|
public function is_binary( $text ) {
|
|
return (bool) preg_match('|[^\x20-\x7E]|', $text);
|
|
}
|
|
|
|
}
|