diff --git a/php/autoload.php b/php/autoload.php index d74b8e841..720a4fd7c 100644 --- a/php/autoload.php +++ b/php/autoload.php @@ -2,53 +2,54 @@ define('ELFINDER_PHP_ROOT_PATH', dirname(__FILE__)); -function elFinderAutoloader($name) { - $map = array( - 'elFinder' => 'elFinder.class.php', - 'elFinderConnector' => 'elFinderConnector.class.php', - 'elFinderEditor' => 'editors/editor.php', - 'elFinderLibGdBmp' => 'libs/GdBmp.php', - 'elFinderPlugin' => 'elFinderPlugin.php', - 'elFinderPluginAutoResize' => 'plugins/AutoResize/plugin.php', - 'elFinderPluginAutoRotate' => 'plugins/AutoRotate/plugin.php', - 'elFinderPluginNormalizer' => 'plugins/Normalizer/plugin.php', - 'elFinderPluginSanitizer' => 'plugins/Sanitizer/plugin.php', - 'elFinderPluginWatermark' => 'plugins/Watermark/plugin.php', - 'elFinderSession' => 'elFinderSession.php', - 'elFinderSessionInterface' => 'elFinderSessionInterface.php', - 'elFinderVolumeDriver' => 'elFinderVolumeDriver.class.php', - 'elFinderVolumeDropbox2' => 'elFinderVolumeDropbox2.class.php', - 'elFinderVolumeFTP' => 'elFinderVolumeFTP.class.php', - 'elFinderVolumeFlysystemGoogleDriveCache' => 'elFinderFlysystemGoogleDriveNetmount.php', - 'elFinderVolumeFlysystemGoogleDriveNetmount' => 'elFinderFlysystemGoogleDriveNetmount.php', - 'elFinderVolumeGoogleDrive' => 'elFinderVolumeGoogleDrive.class.php', - 'elFinderVolumeGroup' => 'elFinderVolumeGroup.class.php', - 'elFinderVolumeLocalFileSystem' => 'elFinderVolumeLocalFileSystem.class.php', - 'elFinderVolumeMySQL' => 'elFinderVolumeMySQL.class.php', - 'elFinderVolumeTrash' => 'elFinderVolumeTrash.class.php', - ); - if (isset($map[$name])) { - return include_once(ELFINDER_PHP_ROOT_PATH . '/' . $map[$name]); - } - $prefix = substr($name, 0, 14); - if (substr($prefix, 0, 8) === 'elFinder') { - if ($prefix === 'elFinderVolume') { - $file = ELFINDER_PHP_ROOT_PATH . '/' . $name . '.class.php'; - return (is_file($file) && include_once($file)); - } else if ($prefix === 'elFinderPlugin') { - $file = ELFINDER_PHP_ROOT_PATH . '/plugins/' . substr($name, 14) . '/plugin.php'; - return (is_file($file) && include_once($file)); - } else if ($prefix === 'elFinderEditor') { - $file = ELFINDER_PHP_ROOT_PATH . '/editors/' . substr($name, 14) . '/editor.php'; - return (is_file($file) && include_once($file)); - } - } - return false; +function elFinderAutoloader($name) +{ + $map = array( + 'elFinder' => 'elFinder.class.php', + 'elFinderConnector' => 'elFinderConnector.class.php', + 'elFinderEditor' => 'editors/editor.php', + 'elFinderLibGdBmp' => 'libs/GdBmp.php', + 'elFinderPlugin' => 'elFinderPlugin.php', + 'elFinderPluginAutoResize' => 'plugins/AutoResize/plugin.php', + 'elFinderPluginAutoRotate' => 'plugins/AutoRotate/plugin.php', + 'elFinderPluginNormalizer' => 'plugins/Normalizer/plugin.php', + 'elFinderPluginSanitizer' => 'plugins/Sanitizer/plugin.php', + 'elFinderPluginWatermark' => 'plugins/Watermark/plugin.php', + 'elFinderSession' => 'elFinderSession.php', + 'elFinderSessionInterface' => 'elFinderSessionInterface.php', + 'elFinderVolumeDriver' => 'elFinderVolumeDriver.class.php', + 'elFinderVolumeDropbox2' => 'elFinderVolumeDropbox2.class.php', + 'elFinderVolumeFTP' => 'elFinderVolumeFTP.class.php', + 'elFinderVolumeFlysystemGoogleDriveCache' => 'elFinderFlysystemGoogleDriveNetmount.php', + 'elFinderVolumeFlysystemGoogleDriveNetmount' => 'elFinderFlysystemGoogleDriveNetmount.php', + 'elFinderVolumeGoogleDrive' => 'elFinderVolumeGoogleDrive.class.php', + 'elFinderVolumeGroup' => 'elFinderVolumeGroup.class.php', + 'elFinderVolumeLocalFileSystem' => 'elFinderVolumeLocalFileSystem.class.php', + 'elFinderVolumeMySQL' => 'elFinderVolumeMySQL.class.php', + 'elFinderVolumeTrash' => 'elFinderVolumeTrash.class.php', + ); + if (isset($map[$name])) { + return include_once(ELFINDER_PHP_ROOT_PATH . '/' . $map[$name]); + } + $prefix = substr($name, 0, 14); + if (substr($prefix, 0, 8) === 'elFinder') { + if ($prefix === 'elFinderVolume') { + $file = ELFINDER_PHP_ROOT_PATH . '/' . $name . '.class.php'; + return (is_file($file) && include_once($file)); + } else if ($prefix === 'elFinderPlugin') { + $file = ELFINDER_PHP_ROOT_PATH . '/plugins/' . substr($name, 14) . '/plugin.php'; + return (is_file($file) && include_once($file)); + } else if ($prefix === 'elFinderEditor') { + $file = ELFINDER_PHP_ROOT_PATH . '/editors/' . substr($name, 14) . '/editor.php'; + return (is_file($file) && include_once($file)); + } + } + return false; } if (version_compare(PHP_VERSION, '5.3', '<')) { - spl_autoload_register('elFinderAutoloader'); + spl_autoload_register('elFinderAutoloader'); } else { - spl_autoload_register('elFinderAutoloader', true, true); + spl_autoload_register('elFinderAutoloader', true, true); } diff --git a/php/chars-test.php b/php/chars-test.php index 4bcc27bfe..2ce3d4637 100644 --- a/php/chars-test.php +++ b/php/chars-test.php @@ -7,36 +7,36 @@ echo 'белая собака'; echo strToLower('белая собака'); $orig = 'ёЁйЙØÅŻ'; -echo $orig.'
'; +echo $orig . '
'; $path = '../../../files/tmp/'; -if (!touch($path.$orig)) { - exit('unable to create file'); +if (!touch($path . $orig)) { + exit('unable to create file'); } $orig = str_replace('"', '', json_encode($orig)); -echo "original: ".$orig.'
'; +echo "original: " . $orig . '
'; $origParts = explode('\\', $orig); array_shift($origParts); // й ё Й Ё Ø Å $patterns = array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"); -$replace = array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5"); +$replace = array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5"); -foreach(scandir($path) as $f) { - if ($f != '.' && $f != '..' && substr($f, 0, 1) != '.') { - - // echo mb_detect_encoding($f); - - $name = str_replace('"', '', json_encode($f)); - echo "before replace: $name
"; - // $name = str_replace($patterns, $replace, $name); - // echo "after replace: $name
"; - break; - } +foreach (scandir($path) as $f) { + if ($f != '.' && $f != '..' && substr($f, 0, 1) != '.') { + + // echo mb_detect_encoding($f); + + $name = str_replace('"', '', json_encode($f)); + echo "before replace: $name
"; + // $name = str_replace($patterns, $replace, $name); + // echo "after replace: $name
"; + break; + } } @@ -47,10 +47,10 @@ array_shift($parts); $diff = array_diff($parts, $origParts); if (count($diff)) { - echo "Following symbols not found in original"; - echo '
';
-	print_r($diff);
-	echo '
'; + echo "Following symbols not found in original"; + echo '
';
+    print_r($diff);
+    echo '
'; } else { - echo "OK"; + echo "OK"; } \ No newline at end of file diff --git a/php/editors/OnlineConvert/editor.php b/php/editors/OnlineConvert/editor.php index e6ce9e73e..b4808c408 100644 --- a/php/editors/OnlineConvert/editor.php +++ b/php/editors/OnlineConvert/editor.php @@ -28,7 +28,7 @@ class elFinderEditorOnlineConvert extends elFinderEditor $string_method = ''; $options = array(); // Currently these converts are make error with API call. I don't know why. - $nonApi = array('android','blackberry','dpg','ipad','iphone','ipod','nintendo-3ds','nintendo-ds','ps3','psp','wii','xbox'); + $nonApi = array('android', 'blackberry', 'dpg', 'ipad', 'iphone', 'ipod', 'nintendo-3ds', 'nintendo-ds', 'ps3', 'psp', 'wii', 'xbox'); if (in_array($convert, $nonApi)) { return array('apires' => array()); } @@ -88,17 +88,17 @@ class elFinderEditorOnlineConvert extends elFinderEditor if ($ch) { $response = curl_exec($ch); $info = curl_getinfo($ch); - $error = curl_error($ch); + $error = curl_error($ch); curl_close($ch); - if (! empty($error)) { + if (!empty($error)) { $res = array('error' => $error); } else { $data = json_decode($response, true); if (isset($data['status']) && isset($data['status']['code']) && $data['status']['code'] === 'completed') { $session = $this->elfinder->getSession(); $urlContentSaveIds = $session->get('urlContentSaveIds', array()); - $urlContentSaveIds['OnlineConvert-'.$data['id']] = true; + $urlContentSaveIds['OnlineConvert-' . $data['id']] = true; $session->set('urlContentSaveIds', $urlContentSaveIds); } $res = array('apires' => $data); diff --git a/php/editors/ZipArchive/editor.php b/php/editors/ZipArchive/editor.php index cde61be24..bdaa4b718 100644 --- a/php/editors/ZipArchive/editor.php +++ b/php/editors/ZipArchive/editor.php @@ -5,8 +5,8 @@ class elFinderEditorZipArchive extends elFinderEditor public function enabled() { return (!defined('ELFINDER_DISABLE_ZIPEDITOR') || !ELFINDER_DISABLE_ZIPEDITOR) && - class_exists('Barryvdh\elFinderFlysystemDriver\Driver') && - class_exists('League\Flysystem\Filesystem') && - class_exists('League\Flysystem\ZipArchive\ZipArchiveAdapter'); + class_exists('Barryvdh\elFinderFlysystemDriver\Driver') && + class_exists('League\Flysystem\Filesystem') && + class_exists('League\Flysystem\ZipArchive\ZipArchiveAdapter'); } } \ No newline at end of file diff --git a/php/editors/ZohoOffice/editor.php b/php/editors/ZohoOffice/editor.php index 80b96d9cb..2dec97757 100644 --- a/php/editors/ZohoOffice/editor.php +++ b/php/editors/ZohoOffice/editor.php @@ -50,7 +50,7 @@ class elFinderEditorZohoOffice extends elFinderEditor $cookie = $this->elfinder->getFetchCookieFile(); $save = false; $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, elFinder::getConnectorUrl().'?cmd=editor&name=ZohoOffice&method=chk&args[target]='.rawurlencode($hash).$cdata); + curl_setopt($ch, CURLOPT_URL, elFinder::getConnectorUrl() . '?cmd=editor&name=ZohoOffice&method=chk&args[target]=' . rawurlencode($hash) . $cdata); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); if ($cookie) { curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie); @@ -77,7 +77,7 @@ class elFinderEditorZohoOffice extends elFinderEditor $cfile->setPostFilename($file['name']); $cfile->setMimeType($file['mime']); } else { - $cfile = '@'.$srcFile; + $cfile = '@' . $srcFile; } } } @@ -101,7 +101,7 @@ class elFinderEditorZohoOffice extends elFinderEditor 'lang' => $lang ); if ($save) { - $data['saveurl'] = elFinder::getConnectorUrl().'?cmd=editor&name=ZohoOffice&method=save'.$cdata; + $data['saveurl'] = elFinder::getConnectorUrl() . '?cmd=editor&name=ZohoOffice&method=save' . $cdata; } if ($cfile) { $data['content'] = $cfile; @@ -146,7 +146,7 @@ class elFinderEditorZohoOffice extends elFinderEditor public function save() { - if (isset($_POST) && ! empty($_POST['id'])) { + if (isset($_POST) && !empty($_POST['id'])) { $hash = $_POST['id']; if ($volume = $this->elfinder->getVolume($hash)) { $content = file_get_contents($_FILES['content']['tmp_name']); diff --git a/php/editors/editor.php b/php/editors/editor.php index a0da7991c..f2d41c247 100644 --- a/php/editors/editor.php +++ b/php/editors/editor.php @@ -1,4 +1,5 @@ args value of the key * - * @param string $key target key - * @param string $empty empty value + * @param string $key target key + * @param string $empty empty value * * @return mixed */ public function argValue($key, $empty = '') { - return isset($this->args[$key])? $this->args[$key] : $empty; + return isset($this->args[$key]) ? $this->args[$key] : $empty; } } diff --git a/php/elFinder.class.php b/php/elFinder.class.php index d9fd46401..ee501dc11 100644 --- a/php/elFinder.class.php +++ b/php/elFinder.class.php @@ -5,4600 +5,4767 @@ * Core class. * * @package elfinder - * @author Dmitry (dio) Levashov - * @author Troex Nevelin - * @author Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Troex Nevelin + * @author Alexey Sukhotin **/ -class elFinder { - - /** - * API version number - * - * @var float - **/ - protected static $ApiVersion = 2.1; - - /** - * API version number - * - * @deprecated - * @var string - **/ - protected $version; - - /** - * API revision that this connector supports all functions - * - * @var integer - */ - protected static $ApiRevision = 44; - - /** - * Storages (root dirs) - * - * @var array - **/ - protected $volumes = array(); - - /** - * elFinder instance - * - * @var object - */ - public static $instance = null; - - /** - * Current request args - * - * @var array - */ - public static $currentArgs = array(); - - /** - * Network mount drivers - * - * @var array - */ - public static $netDrivers = array(); - - /** - * elFinder global locale - * - * @var string - */ - public static $locale = ''; - - /** - * elFinderVolumeDriver default mime.type file path - * - * @var string - */ - public static $defaultMimefile = ''; - - /** - * A file save destination path when a temporary content URL is required - * on a network volume or the like - * It can be overwritten by volume route setting - * - * @var string - */ - public static $tmpLinkPath = ''; - - /** - * A file save destination URL when a temporary content URL is required - * on a network volume or the like - * It can be overwritten by volume route setting - * - * @var string - */ - public static $tmpLinkUrl = ''; - - /** - * Temporary content URL lifetime (seconds) - * - * @var integer - */ - public static $tmpLinkLifeTime = 3600; - - /** - * MIME type list handled as a text file - * - * @var array - */ - public static $textMimes = array( - 'application/dash+xml', - 'application/docbook+xml', - 'application/javascript', - 'application/json', - 'application/plt', - 'application/sat', - 'application/sql', - 'application/step', - 'application/vnd.hp-hpgl', - 'application/x-awk', - 'application/x-config', - 'application/x-csh', - 'application/x-empty', - 'application/x-mpegurl', - 'application/x-perl', - 'application/x-php', - 'application/x-web-config', - 'application/xhtml+xml', - 'application/xml', - 'audio/x-mp3-playlist', - 'image/cgm', - 'image/svg+xml', - 'image/vnd.dxf', - 'model/iges' - ); - - /** - * Maximum memory size to be extended during GD processing - * (0: not expanded, -1: unlimited or memory size notation) - * - * @var integer|string - */ - public static $memoryLimitGD = 0; - - /** - * Path of current request flag file for abort check - * - * @var string - */ - protected static $abortCheckFile = null; - - /** - * elFinder session wrapper object - * - * @var elFinderSessionInterface - */ - protected $session; - - /** - * elFinder global sessionCacheKey - * - * @deprecated - * @var string - */ - public static $sessionCacheKey = ''; - - /** - * Is session closed - * - * @deprecated - * @var bool - */ - private static $sessionClosed = false; - - /** - * elFinder base64encodeSessionData - * elFinder save session data as `UTF-8` - * If the session storage mechanism of the system does not allow `UTF-8` - * And it must be `true` option 'base64encodeSessionData' of elFinder - * - * WARNING: When enabling this option, if saving the data passed from the user directly to the session variable, - * it make vulnerable to the object injection attack, so use it carefully. - * see https://github.com/Studio-42/elFinder/issues/2345 - * - * @var bool - */ - protected static $base64encodeSessionData = false; - - /** - * elFinder common tempraly path - * - * @var string - * @default "./.tmp" or sys_get_temp_dir() - **/ - protected static $commonTempPath = ''; - - /** - * Connection flag files path that connection check of current request - * - * @var string - * @default value of $commonTempPath - */ - protected static $connectionFlagsPath = ''; - - /** - * Additional volume root options for network mounting volume - * - * @var array - */ - protected $optionsNetVolumes = array(); - - /** - * Session key of net mount volumes - * - * @deprecated - * @var string - */ - protected $netVolumesSessionKey = ''; - - /** - * Mounted volumes count - * Required to create unique volume id - * - * @var int - **/ - public static $volumesCnt = 1; - - /** - * Default root (storage) - * - * @var elFinderVolumeDriver - **/ - protected $default = null; - - /** - * Commands and required arguments list - * - * @var array - **/ - protected $commands = array( - 'open' => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false, 'compare' => false), - 'ls' => array('target' => true, 'mimes' => false, 'intersect' => false), - 'tree' => array('target' => true), - 'parents' => array('target' => true, 'until' => false), - 'tmb' => array('targets' => true), - 'file' => array('target' => true, 'download' => false, 'cpath' => false, 'onetime' => false), - 'zipdl' => array('targets' => true, 'download' => false), - 'size' => array('targets' => true), - 'mkdir' => array('target' => true, 'name' => false, 'dirs' => false), - 'mkfile' => array('target' => true, 'name' => true, 'mimes' => false), - 'rm' => array('targets' => true), - 'rename' => array('target' => true, 'name' => true, 'mimes' => false, 'targets' => false, 'q' => false), - 'duplicate' => array('targets' => true, 'suffix' => false), - 'paste' => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false, 'renames' => false, 'hashes' => false, 'suffix' => false), - 'upload' => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false, 'upload' => false, 'name' => false, 'upload_path' => false, 'chunk' => false, 'cid' => false, 'node' => false, 'renames' => false, 'hashes' => false, 'suffix' => false, 'mtime' => false, 'overwrite' => false, 'contentSaveId' => false), - 'get' => array('target' => true, 'conv' => false), - 'put' => array('target' => true, 'content' => '', 'mimes' => false, 'encoding' => false), - 'archive' => array('targets' => true, 'type' => true, 'mimes' => false, 'name' => false), - 'extract' => array('target' => true, 'mimes' => false, 'makedir' => false), - 'search' => array('q' => true, 'mimes' => false, 'target' => false, 'type' => false), - 'info' => array('targets' => true, 'compare' => false), - 'dim' => array('target' => true, 'substitute' => false), - 'resize' => array('target' => true, 'width' => false, 'height' => false, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false, 'quality' => false, 'bg' => false), - 'netmount' => array('protocol' => true, 'host' => true, 'path' => false, 'port' => false, 'user' => false, 'pass' => false, 'alias' => false, 'options' => false), - 'url' => array('target' => true, 'options' => false), - 'callback' => array('node' => true, 'json' => false, 'bind' => false, 'done' => false), - 'chmod' => array('targets' => true, 'mode' => true), - 'subdirs' => array('targets' => true), - 'abort' => array('id' => true), - 'editor' => array('name' => true, 'method' => true, 'args' => false) - ); - - /** - * Plugins instance - * - * @var array - **/ - protected $plugins = array(); - - /** - * Commands listeners - * - * @var array - **/ - protected $listeners = array(); - - /** - * script work time for debug - * - * @var string - **/ - protected $time = 0; - /** - * Is elFinder init correctly? - * - * @var bool - **/ - protected $loaded = false; - /** - * Send debug to client? - * - * @var string - **/ - protected $debug = false; - - /** - * Call `session_write_close()` before exec command? - * - * @var bool - */ - protected $sessionCloseEarlier = true; - - /** - * SESSION use commands @see __construct() - * - * @var array - */ - protected $sessionUseCmds = array(); - - /** - * session expires timeout - * - * @var int - **/ - protected $timeout = 0; - - /** - * Temp dir path for Upload - * - * @var string - */ - protected $uploadTempPath = ''; - - /** - * Max allowed archive files size (0 - no limit) - * - * @var integer - */ - protected $maxArcFilesSize = 0; - - /** - * undocumented class variable - * - * @var string - **/ - protected $uploadDebug = ''; - - /** - * Max allowed numbar of targets (0 - no limit) - * - * @var integer - */ - public $maxTargets = 1000; - - /** - * Errors from PHP - * - * @var array - **/ - public static $phpErrors = array(); - - /** - * Errors from not mounted volumes - * - * @var array - **/ - public $mountErrors = array(); - - - /** - * Archivers cache - * - * @var array - */ - public static $archivers = array(); - - /** - * URL for callback output window for CORS - * redirect to this URL when callback output - * - * @var string URL - */ - protected $callbackWindowURL = ''; - - /** - * hash of items to unlock on command completion - * - * @var array hashes - */ - protected $autoUnlocks = array(); - - /** - * Item locking expiration (seconds) - * Default: 3600 secs - * - * @var integer - */ - protected $itemLockExpire = 3600; - - /** - * Additional request querys - * - * @var array|null - */ - protected $customData = null; - - /** - * Ids to remove of session var "urlContentSaveIds" for contents uploading by URL - * - * @var array - */ - protected $removeContentSaveIds = array(); - - /** - * Flag of throw Error on exec() - * - * @var boolean - */ - protected $throwErrorOnExec = false; - - // Errors messages - const ERROR_UNKNOWN = 'errUnknown'; - const ERROR_UNKNOWN_CMD = 'errUnknownCmd'; - const ERROR_CONF = 'errConf'; - const ERROR_CONF_NO_JSON = 'errJSON'; - const ERROR_CONF_NO_VOL = 'errNoVolumes'; - const ERROR_INV_PARAMS = 'errCmdParams'; - const ERROR_OPEN = 'errOpen'; - const ERROR_DIR_NOT_FOUND = 'errFolderNotFound'; - const ERROR_FILE_NOT_FOUND = 'errFileNotFound'; // 'File not found.' - const ERROR_TRGDIR_NOT_FOUND = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.' - const ERROR_NOT_DIR = 'errNotFolder'; - const ERROR_NOT_FILE = 'errNotFile'; - const ERROR_PERM_DENIED = 'errPerm'; - const ERROR_LOCKED = 'errLocked'; // '"$1" is locked and can not be renamed, moved or removed.' - const ERROR_EXISTS = 'errExists'; // 'File named "$1" already exists.' - const ERROR_INVALID_NAME = 'errInvName'; // 'Invalid file name.' - const ERROR_INVALID_DIRNAME = 'errInvDirname'; // 'Invalid folder name.' - const ERROR_MKDIR = 'errMkdir'; - const ERROR_MKFILE = 'errMkfile'; - const ERROR_RENAME = 'errRename'; - const ERROR_COPY = 'errCopy'; - const ERROR_MOVE = 'errMove'; - const ERROR_COPY_FROM = 'errCopyFrom'; - const ERROR_COPY_TO = 'errCopyTo'; - const ERROR_COPY_ITSELF = 'errCopyInItself'; - const ERROR_REPLACE = 'errReplace'; // 'Unable to replace "$1".' - const ERROR_RM = 'errRm'; // 'Unable to remove "$1".' - const ERROR_RM_SRC = 'errRmSrc'; // 'Unable remove source file(s)' - const ERROR_MKOUTLINK = 'errMkOutLink'; // 'Unable to create a link to outside the volume root.' - const ERROR_UPLOAD = 'errUpload'; // 'Upload error.' - const ERROR_UPLOAD_FILE = 'errUploadFile'; // 'Unable to upload "$1".' - const ERROR_UPLOAD_NO_FILES = 'errUploadNoFiles'; // 'No files found for upload.' - const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize'; // 'Data exceeds the maximum allowed size.' - const ERROR_UPLOAD_FILE_SIZE = 'errUploadFileSize'; // 'File exceeds maximum allowed size.' - const ERROR_UPLOAD_FILE_MIME = 'errUploadMime'; // 'File type not allowed.' - const ERROR_UPLOAD_TRANSFER = 'errUploadTransfer'; // '"$1" transfer error.' - const ERROR_UPLOAD_TEMP = 'errUploadTemp'; // 'Unable to make temporary file for upload.' - const ERROR_ACCESS_DENIED = 'errAccess'; - const ERROR_NOT_REPLACE = 'errNotReplace'; // Object "$1" already exists at this location and can not be replaced with object of another type. - const ERROR_SAVE = 'errSave'; - const ERROR_EXTRACT = 'errExtract'; - const ERROR_ARCHIVE = 'errArchive'; - const ERROR_NOT_ARCHIVE = 'errNoArchive'; - const ERROR_ARCHIVE_TYPE = 'errArcType'; - const ERROR_ARC_SYMLINKS = 'errArcSymlinks'; - const ERROR_ARC_MAXSIZE = 'errArcMaxSize'; - const ERROR_RESIZE = 'errResize'; - const ERROR_RESIZESIZE = 'errResizeSize'; - const ERROR_UNSUPPORT_TYPE = 'errUsupportType'; - const ERROR_CONV_UTF8 = 'errConvUTF8'; - const ERROR_NOT_UTF8_CONTENT = 'errNotUTF8Content'; - const ERROR_NETMOUNT = 'errNetMount'; - const ERROR_NETUNMOUNT = 'errNetUnMount'; - const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver'; - const ERROR_NETMOUNT_FAILED = 'errNetMountFailed'; - const ERROR_SESSION_EXPIRES = 'errSessionExpires'; - const ERROR_CREATING_TEMP_DIR = 'errCreatingTempDir'; - const ERROR_FTP_DOWNLOAD_FILE = 'errFtpDownloadFile'; - const ERROR_FTP_UPLOAD_FILE = 'errFtpUploadFile'; - const ERROR_FTP_MKDIR = 'errFtpMkdir'; - const ERROR_ARCHIVE_EXEC = 'errArchiveExec'; - const ERROR_EXTRACT_EXEC = 'errExtractExec'; - const ERROR_SEARCH_TIMEOUT = 'errSearchTimeout'; // 'Timed out while searching "$1". Search result is partial.' - const ERROR_REAUTH_REQUIRE = 'errReauthRequire'; // 'Re-authorization is required.' - const ERROR_MAX_TARGTES = 'errMaxTargets'; // 'Max number of selectable items is $1.' - - /** - * Constructor - * - * @param array elFinder and roots configurations - * @author Dmitry (dio) Levashov - */ - public function __construct($opts) { - // set default_charset - if (version_compare(PHP_VERSION, '5.6', '>=')) { - if (($_val = ini_get('iconv.internal_encoding')) && strtoupper($_val) !== 'UTF-8') { - ini_set('iconv.internal_encoding', ''); - } - if (($_val = ini_get('mbstring.internal_encoding')) && strtoupper($_val) !== 'UTF-8') { - ini_set('mbstring.internal_encoding', ''); - } - if (($_val = ini_get('internal_encoding')) && strtoupper($_val) !== 'UTF-8') { - ini_set('internal_encoding', ''); - } - } else { - if (function_exists('iconv_set_encoding') && strtoupper(iconv_get_encoding('internal_encoding')) !== 'UTF-8') { - iconv_set_encoding('internal_encoding', 'UTF-8'); - } - if (function_exists('mb_internal_encoding') && strtoupper(mb_internal_encoding()) !== 'UTF-8') { - mb_internal_encoding('UTF-8'); - } - } - ini_set('default_charset', 'UTF-8'); - - // define accept constant of server commands path - ! defined('ELFINDER_TAR_PATH') && define('ELFINDER_TAR_PATH', 'tar'); - ! defined('ELFINDER_GZIP_PATH') && define('ELFINDER_GZIP_PATH', 'gzip'); - ! defined('ELFINDER_BZIP2_PATH') && define('ELFINDER_BZIP2_PATH', 'bzip2'); - ! defined('ELFINDER_XZ_PATH') && define('ELFINDER_XZ_PATH', 'xz'); - ! defined('ELFINDER_ZIP_PATH') && define('ELFINDER_ZIP_PATH', 'zip'); - ! defined('ELFINDER_UNZIP_PATH') && define('ELFINDER_UNZIP_PATH', 'unzip'); - ! defined('ELFINDER_RAR_PATH') && define('ELFINDER_RAR_PATH', 'rar'); - ! defined('ELFINDER_UNRAR_PATH') && define('ELFINDER_UNRAR_PATH', 'unrar'); - ! defined('ELFINDER_7Z_PATH') && define('ELFINDER_7Z_PATH', (substr(PHP_OS, 0, 3) === 'WIN')? '7z' : '7za'); - ! defined('ELFINDER_CONVERT_PATH') && define('ELFINDER_CONVERT_PATH', 'convert'); - ! defined('ELFINDER_IDENTIFY_PATH') && define('ELFINDER_IDENTIFY_PATH', 'identify'); - ! defined('ELFINDER_EXIFTRAN_PATH') && define('ELFINDER_EXIFTRAN_PATH', 'exiftran'); - ! defined('ELFINDER_JPEGTRAN_PATH') && define('ELFINDER_JPEGTRAN_PATH', 'jpegtran'); - ! defined('ELFINDER_FFMPEG_PATH') && define('ELFINDER_FFMPEG_PATH', 'ffmpeg'); - - ! defined('ELFINDER_DISABLE_ZIPEDITOR') && define('ELFINDER_DISABLE_ZIPEDITOR', false); - - // enable(true)/disable(false) handling postscript on ImageMagick - // Should be `false` as long as there is a Ghostscript vulnerability - // see https://artifex.com/news/ghostscript-security-resolved/ - ! defined('ELFINDER_IMAGEMAGICK_PS') && define('ELFINDER_IMAGEMAGICK_PS', false); - - // for backward compat - $this->version = (string)self::$ApiVersion; - - // set error handler of WARNING, NOTICE - $errLevel = E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_STRICT | E_RECOVERABLE_ERROR; - if (defined('E_DEPRECATED')) { - $errLevel |= E_DEPRECATED | E_USER_DEPRECATED; - } - set_error_handler('elFinder::phpErrorHandler', $errLevel); - - // Associative array of files to delete at the end of script: ['temp file path' => true] - $GLOBALS['elFinderTempFiles'] = array(); - // regist Shutdown function - register_shutdown_function(array('elFinder', 'onShutdown')); - - // convert PATH_INFO to GET query - if (! empty($_SERVER['PATH_INFO'])) { - $_ps = explode('/', trim($_SERVER['PATH_INFO'], '/')); - if (! isset($_GET['cmd'])) { - $_cmd = $_ps[0]; - if (isset($this->commands[$_cmd])) { - $_GET['cmd'] = $_cmd; - $_i = 1; - foreach(array_keys($this->commands[$_cmd]) as $_k) { - if (isset($_ps[$_i])) { - if (! isset($_GET[$_k])) { - $_GET[$_k] = $_ps[$_i]; - } - } else { - break; - } - } - } - } - } - - // set elFinder instance - elFinder::$instance = $this; - - // setup debug mode - $this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false); - if ($this->debug) { - error_reporting(defined('ELFINDER_DEBUG_ERRORLEVEL')? ELFINDER_DEBUG_ERRORLEVEL : -1); - ini_set('display_errors', '1'); - // clear output buffer and stop output filters - while(ob_get_level() && ob_end_clean()){} - } - - if (! interface_exists('elFinderSessionInterface')) { - include_once dirname(__FILE__).'/elFinderSessionInterface.php'; - } - - // session handler - if (!empty($opts['session']) && $opts['session'] instanceof elFinderSessionInterface) { - $this->session = $opts['session']; - } else { - $sessionOpts = array( - 'base64encode' => !empty($opts['base64encodeSessionData']), - 'keys' => array( - 'default' => !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches', - 'netvolume' => !empty($opts['netVolumesSessionKey'])? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes' - ) - ); - if (! class_exists('elFinderSession')) { - include_once dirname(__FILE__) . '/elFinderSession.php'; - } - $this->session = new elFinderSession($sessionOpts); - } - // try session start | restart - $this->session->start(); - - // 'netmount' added to handle requests synchronously on unmount - $sessionUseCmds = array('netmount'); - if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) { - $sessionUseCmds = array_merge($sessionUseCmds, $opts['sessionUseCmds']); - } - - // set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart" - if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) { - self::$volumesCnt = $volumesCntStart; - } - - $this->time = $this->utime(); - $this->sessionCloseEarlier = isset($opts['sessionCloseEarlier'])? (bool)$opts['sessionCloseEarlier'] : true; - $this->sessionUseCmds = array_flip($sessionUseCmds); - $this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0); - $this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : ''); - $this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : ''); - $this->maxTargets = (isset($opts['maxTargets']) ? intval($opts['maxTargets']) : $this->maxTargets); - elFinder::$commonTempPath = (isset($opts['commonTempPath']) ? realpath($opts['commonTempPath']) : dirname(__FILE__) . '/.tmp'); - if (!is_writable(elFinder::$commonTempPath)) { - elFinder::$commonTempPath = sys_get_temp_dir(); - if (!is_writable(elFinder::$commonTempPath)) { - elFinder::$commonTempPath = ''; - } - } - if (isset($opts['connectionFlagsPath']) && is_writable($opts['connectionFlagsPath'] = realpath($opts['connectionFlagsPath']))) { - elFinder::$connectionFlagsPath = $opts['connectionFlagsPath']; - } else { - elFinder::$connectionFlagsPath = elFinder::$commonTempPath; - } - - if (! empty($opts['tmpLinkPath'])) { - elFinder::$tmpLinkPath = realpath($opts['tmpLinkPath']); - } - if (! empty($opts['tmpLinkUrl'])) { - elFinder::$tmpLinkUrl = $opts['tmpLinkUrl']; - } - if (! empty($opts['tmpLinkLifeTime'])) { - elFinder::$tmpLinkLifeTime = $opts['tmpLinkLifeTime']; - } - if (! empty($opts['textMimes']) && is_array($opts['textMimes'])) { - elfinder::$textMimes = $opts['textMimes']; - } - $this->maxArcFilesSize = isset($opts['maxArcFilesSize'])? intval($opts['maxArcFilesSize']) : 0; - $this->optionsNetVolumes = (isset($opts['optionsNetVolumes']) && is_array($opts['optionsNetVolumes']))? $opts['optionsNetVolumes'] : array(); - if (isset($opts['itemLockExpire'])) { - $this->itemLockExpire = intval($opts['itemLockExpire']); - } - - // deprecated settings - $this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey'])? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes'; - self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches'; - - // check session cache - $_optsMD5 = md5(json_encode($opts['roots'])); - if ($this->session->get('_optsMD5') !== $_optsMD5) { - $this->session->set('_optsMD5', $_optsMD5); - } - - // setlocale and global locale regists to elFinder::locale - self::$locale = !empty($opts['locale']) ? $opts['locale'] : (substr(PHP_OS, 0, 3) === 'WIN'? 'C' : 'en_US.UTF-8'); - if (false === setlocale(LC_ALL, self::$locale)) { - self::$locale = setlocale(LC_ALL, '0'); - } - - // set defaultMimefile - elFinder::$defaultMimefile = isset($opts['defaultMimefile'])? $opts['defaultMimefile'] : ''; - - // set memoryLimitGD - elFinder::$memoryLimitGD = isset($opts['memoryLimitGD'])? $opts['memoryLimitGD'] : 0; - - // set flag of throwErrorOnExec - // `true` need `try{}` block for `$connector->run();` - $this->throwErrorOnExec = !empty($opts['throwErrorOnExec']); - - // set archivers - elFinder::$archivers = isset($opts['archivers']) && is_array($opts['archivers'])? $opts['archivers'] : array(); - - // bind events listeners - if (!empty($opts['bind']) && is_array($opts['bind'])) { - $_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET; - $_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : ''; - foreach ($opts['bind'] as $cmd => $handlers) { - $doRegist = (strpos($cmd, '*') !== false); - if (! $doRegist) { - $doRegist = ($_reqCmd && in_array($_reqCmd, array_map('self::getCmdOfBind', explode(' ', $cmd)))); - } - if ($doRegist) { - // for backward compatibility - if (! is_array($handlers)) { - $handlers = array($handlers); - } else { - if (count($handlers) === 2 && is_callable($handlers)) { - $handlers = array($handlers); - } - } - foreach($handlers as $handler) { - if ($handler) { - if (is_string($handler) && strpos($handler, '.')) { - list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, ''); - if (strcasecmp($_domain, 'plugin') === 0) { - if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name])? $opts['plugin'][$_name] : array()) - and method_exists($plugin, $_method)) { - $this->bind($cmd, array($plugin, $_method)); - } - } - } else { - $this->bind($cmd, $handler); - } - } - } - } - } - } - - if (!isset($opts['roots']) || !is_array($opts['roots'])) { - $opts['roots'] = array(); - } - - // try to enable elFinderVolumeFlysystemZipArchiveNetmount to zip editing - if (empty(elFinder::$netDrivers['ziparchive'])) { - elFinder::$netDrivers['ziparchive'] = 'FlysystemZipArchiveNetmount'; - } - - // check for net volumes stored in session - $netVolumes = $this->getNetVolumes(); - foreach ($netVolumes as $key => $root) { - if (! isset($root['id'])) { - // given fixed unique id - if (! $root['id'] = $this->getNetVolumeUniqueId($netVolumes)) { - $this->mountErrors[] = 'Netmount Driver "'.$root['driver'].'" : Could\'t given volume id.'; - continue; - } - } - $opts['roots'][$key] = $root; - } - - // "mount" volumes - foreach ($opts['roots'] as $i => $o) { - $class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : ''); - - if (class_exists($class)) { - /* @var elFinderVolumeDriver $volume */ - $volume = new $class(); - - try { - if ($this->maxArcFilesSize && (empty($o['maxArcFilesSize']) || $this->maxArcFilesSize < $o['maxArcFilesSize'])) { - $o['maxArcFilesSize'] = $this->maxArcFilesSize; - } - // pass session handler - $volume->setSession($this->session); - if ($volume->mount($o)) { - // unique volume id (ends on "_") - used as prefix to files hash - $id = $volume->id(); - - $this->volumes[$id] = $volume; - if ((!$this->default || $volume->root() !== $volume->defaultPath()) && $volume->isReadable()) { - $this->default = $this->volumes[$id]; - } - } else { - $this->removeNetVolume($i, $volume); - $this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error()); - } - } catch (Exception $e) { - $this->removeNetVolume($i, $volume); - $this->mountErrors[] = 'Driver "'.$class.'" : '.$e->getMessage(); - } - } else { - $this->removeNetVolume($i, null); - $this->mountErrors[] = 'Driver "'.$class.'" does not exist'; - } - } - - // if at least one readable volume - ii desu >_< - $this->loaded = !empty($this->default); - - // restore error handler for now - restore_error_handler(); - } - - /** - * Return elFinder session wrapper instance - * - * @return elFinderSessionInterface - **/ - public function getSession() { - return $this->session; - } - - /** - * Return true if fm init correctly - * - * @return bool - * @author Dmitry (dio) Levashov - **/ - public function loaded() { - return $this->loaded; - } - - /** - * Return version (api) number - * - * @return string - * @author Dmitry (dio) Levashov - **/ - public function version() { - return self::$ApiVersion; - } - - /** - * Return revision (api) number - * - * @return string - * @author Naoki Sawada - **/ - public function revision() { - return self::$ApiRevision; - } - - /** - * Add handler to elFinder command - * - * @param string command name - * @param string|array callback name or array(object, method) - * @return elFinder - * @author Dmitry (dio) Levashov - **/ - public function bind($cmd, $handler) { - $allCmds = array_keys($this->commands); - $cmds = array(); - foreach(explode(' ', $cmd) as $_cmd) { - if ($_cmd !== '') { - if ($all = strpos($_cmd, '*') !== false) { - list(, $sub) = array_pad(explode('.', $_cmd), 2, ''); - if ($sub) { - $sub = str_replace('\'', '\\\'', $sub); - $subs = array_fill(0, count($allCmds), $sub); - $cmds = array_merge($cmds, array_map(array('elFinder', 'addSubToBindName'), $allCmds, $subs)); - } else { - $cmds = array_merge($cmds, $allCmds); - } - } else { - $cmds[] = $_cmd; - } - } - } - $cmds = array_unique($cmds); - - foreach ($cmds as $cmd) { - if (!isset($this->listeners[$cmd])) { - $this->listeners[$cmd] = array(); - } - - if (is_callable($handler)) { - $this->listeners[$cmd][] = $handler; - } - } - - return $this; - } - - /** - * Remove event (command exec) handler - * - * @param string command name - * @param string|array callback name or array(object, method) - * @return elFinder - * @author Dmitry (dio) Levashov - **/ - public function unbind($cmd, $handler) { - if (!empty($this->listeners[$cmd])) { - foreach ($this->listeners[$cmd] as $i => $h) { - if ($h === $handler) { - unset($this->listeners[$cmd][$i]); - return $this; - } - } - } - return $this; - } - - /** - * Return true if command exists - * - * @param string command name - * @return bool - * @author Dmitry (dio) Levashov - **/ - public function commandExists($cmd) { - return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd); - } - - /** - * Return root - file's owner (public func of volume()) - * - * @param string file hash - * @return elFinderVolumeDriver - * @author Naoki Sawada - */ - public function getVolume($hash) { - return $this->volume($hash); - } - - /** - * Return command required arguments info - * - * @param string command name - * @return array - * @author Dmitry (dio) Levashov - **/ - public function commandArgsList($cmd) { - if ($this->commandExists($cmd)) { - $list = $this->commands[$cmd]; - $list['reqid'] = false; - } else { - $list = array(); - } - return $list; - } - - private function session_expires() { - - if (! $last = $this->session->get(':LAST_ACTIVITY')) { - $this->session->set(':LAST_ACTIVITY', time()); - return false; - } - - if ( ($this->timeout > 0) && (time() - $last > $this->timeout) ) { - return true; - } - - $this->session->set(':LAST_ACTIVITY', time()); - return false; - } - - /** - * Exec command and return result - * - * @param string $cmd command name - * @param array $args command arguments - * @return array - * @throws elFinderAbortException|Exception - * @author Dmitry (dio) Levashov - **/ - public function exec($cmd, $args) { - // set error handler of WARNING, NOTICE - set_error_handler('elFinder::phpErrorHandler', E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE); - - // set current request args - self::$currentArgs = $args; - - if (!$this->loaded) { - return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL)); - } - - if ($this->session_expires()) { - return array('error' => $this->error(self::ERROR_SESSION_EXPIRES)); - } - - if (!$this->commandExists($cmd)) { - return array('error' => $this->error(self::ERROR_UNKNOWN_CMD)); - } - - // check request id - $args['reqid'] = preg_replace('[^0-9a-fA-F]', '', ! empty($args['reqid'])? $args['reqid'] : (! empty($_SERVER['HTTP_X_ELFINDERREQID'])? $_SERVER['HTTP_X_ELFINDERREQID'] : '')); - - // to abort this request - if ($cmd === 'abort') { - $this->abort($args); - return array('error' => 0); - } - - // make flag file and set self::$abortCheckFile - if ($args['reqid']) { - $this->abort(array('makeFile' => $args['reqid'])); - } - - if (!empty($args['mimes']) && is_array($args['mimes'])) { - foreach ($this->volumes as $id => $v) { - $this->volumes[$id]->setMimesFilter($args['mimes']); - } - } - - // regist shutdown function as fallback - register_shutdown_function(array($this, 'itemAutoUnlock')); - - // detect destination dirHash and volume - $dstVolume = false; - $dst = ! empty($args['target'])? $args['target'] : (! empty($args['dst'])? $args['dst'] : ''); - if ($dst) { - $dstVolume = $this->volume($dst); - } else if (isset($args['targets']) && is_array($args['targets']) && isset($args['targets'][0])) { - $dst = $args['targets'][0]; - $dstVolume = $this->volume($dst); - if ($dstVolume && ($_stat = $dstVolume->file($dst)) && ! empty($_stat['phash'])) { - $dst = $_stat['phash']; - } else { - $dst = ''; - } - } else if ($cmd === 'open') { - // for initial open without args `target` - $dstVolume = $this->default; - $dst = $dstVolume->defaultPath(); - } - - $result = null; - - // call pre handlers for this command - $args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd])? false : $this->sessionCloseEarlier; - if (!empty($this->listeners[$cmd.'.pre'])) { - foreach ($this->listeners[$cmd.'.pre'] as $handler) { - $_res = call_user_func_array($handler, array($cmd, &$args, $this, $dstVolume)); - if (is_array($_res)) { - if (! empty($_res['preventexec'])) { - $result = array('error' => true); - if ($cmd === 'upload' && ! empty($args['node'])) { - $result['callback'] = array( - 'node' => $args['node'], - 'bind' => $cmd - ); - } - if (! empty($_res['results']) && is_array($_res['results'])) { - $result = array_merge($result, $_res['results']); - } - break; - } - } - } - } - - // unlock session data for multiple access - if ($this->sessionCloseEarlier && $args['sessionCloseEarlier']) { - $this->session->close(); - // deprecated property - elFinder::$sessionClosed = true; - } - - if (substr(PHP_OS,0,3) === 'WIN') { - // set time out - elFinder::extendTimeLimit(300); - } - - if (! is_array($result)) { - try { - $result = $this->$cmd($args); - } catch (elFinderAbortException $e) { - throw $e; - } catch (Exception $e) { - $result = array( - 'error' => htmlspecialchars($e->getMessage()), - 'sync' => true - ); - if ($this->throwErrorOnExec) { - throw $e; - } - } - } - - // check change dstDir - $changeDst = false; - if ($dst && $dstVolume && (! empty($result['added']) || ! empty($result['removed']))) { - $changeDst = true; - } - - foreach ($this->volumes as $volume) { - $removed = $volume->removed(); - if (! empty($removed)) { - if (! isset($result['removed'])) { - $result['removed'] = array(); - } - $result['removed'] = array_merge($result['removed'], $removed); - if (! $changeDst && $dst && $dstVolume && $volume === $dstVolume) { - $changeDst = true; - } - } - $added = $volume->added(); - if (! empty($added)) { - if (! isset($result['added'])) { - $result['added'] = array(); - } - $result['added'] = array_merge($result['added'], $added); - if (! $changeDst && $dst && $dstVolume && $volume === $dstVolume) { - $changeDst = true; - } - } - $volume->resetResultStat(); - } - - // dstDir is changed - if ($changeDst) { - if ($dstDir = $dstVolume->dir($dst)) { - if (! isset($result['changed'])) { - $result['changed'] = array(); - } - $result['changed'][] = $dstDir; - } - } - - // call handlers for this command - if (!empty($this->listeners[$cmd])) { - foreach ($this->listeners[$cmd] as $handler) { - if (call_user_func_array($handler,array($cmd, &$result, $args, $this, $dstVolume))) { - // handler return true to force sync client after command completed - $result['sync'] = true; - } - } - } - - // replace removed files info with removed files hashes - if (!empty($result['removed'])) { - $removed = array(); - foreach ($result['removed'] as $file) { - $removed[] = $file['hash']; - } - $result['removed'] = array_unique($removed); - } - // remove hidden files and filter files by mimetypes - if (!empty($result['added'])) { - $result['added'] = $this->filter($result['added']); - } - // remove hidden files and filter files by mimetypes - if (!empty($result['changed'])) { - $result['changed'] = $this->filter($result['changed']); - } - - if ($this->debug || !empty($args['debug'])) { - $result['debug'] = array( - 'connector' => 'php', - 'phpver' => PHP_VERSION, - 'time' => $this->utime() - $this->time, - 'memory' => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'), - 'upload' => $this->uploadDebug, - 'volumes' => array(), - 'mountErrors' => $this->mountErrors, - 'phpErrors' => elFinder::$phpErrors - ); - elFinder::$phpErrors = array(); - - foreach ($this->volumes as $id => $volume) { - $result['debug']['volumes'][] = $volume->debug(); - } - } - - // remove sesstion var 'urlContentSaveIds' - if ($this->removeContentSaveIds) { - $urlContentSaveIds = $this->session->get('urlContentSaveIds', array()); - foreach(array_keys($this->removeContentSaveIds) as $contentSaveId) { - if (isset($urlContentSaveIds[$contentSaveId])) { - unset($urlContentSaveIds[$contentSaveId]); - } - } - if ($urlContentSaveIds) { - $this->session->set('urlContentSaveIds', $urlContentSaveIds); - } else { - $this->session->remove('urlContentSaveIds'); - } - } - - foreach ($this->volumes as $volume) { - $volume->saveSessionCache(); - $volume->umount(); - } - - // unlock locked items - $this->itemAutoUnlock(); - - // custom data - if ($this->customData !== null) { - $result['customData'] = $this->customData? json_encode($this->customData) : ''; - } - - if (!empty($result['callback'])) { - $result['callback']['json'] = json_encode($result); - $this->callback($result['callback']); - return array(); - } else { - return $result; - } - } - - /** - * Return file real path - * - * @param string $hash file hash - * @return string - * @author Dmitry (dio) Levashov - **/ - public function realpath($hash) { - if (($volume = $this->volume($hash)) == false) { - return false; - } - return $volume->realpath($hash); - } - - /** - * Sets custom data(s). - * - * @param string|array $key The key or data array - * @param mixed $val The value - * - * @return self ( elFinder instance ) - */ - public function setCustomData($key, $val = null) { - if (is_array($key)) { - foreach($key as $k => $v) { - $this->customData[$k] = $v; - } - } else { - $this->customData[$key] = $val; - } - return $this; - } - - /** - * Removes a custom data. - * - * @param string $key The key - * - * @return self ( elFinder instance ) - */ - public function removeCustomData($key) { - $this->customData[$key] = null; - return $this; - } - - /** - * Update sesstion value of a NetVolume option - * - * @param string $netKey - * @param string $optionKey - * @param mixed $val - */ - public function updateNetVolumeOption($netKey, $optionKey, $val) { - $netVolumes = $this->getNetVolumes(); - if (is_string($netKey) && isset($netVolumes[$netKey]) && is_string($optionKey)) { - $netVolumes[$netKey][$optionKey] = $val; - } - } - - /** - * remove of session var "urlContentSaveIds" - * - * @param string $id - */ - public function removeUrlContentSaveId($id) { - $this->removeContentSaveIds[$id] = true; - } - - /** - * Return network volumes config. - * - * @return array - * @author Dmitry (dio) Levashov - */ - protected function getNetVolumes() { - if ($data = $this->session->get('netvolume', array())) { - return $data; - } - return array(); - } - - /** - * Save network volumes config. - * - * @param array $volumes volumes config - * @return void - * @author Dmitry (dio) Levashov - */ - protected function saveNetVolumes($volumes) { - $this->session->set('netvolume', $volumes); - } - - /** - * Remove netmount volume - * - * @param string $key netvolume key - * @param object $volume volume driver instance - * @return bool - */ - protected function removeNetVolume($key, $volume) { - $netVolumes = $this->getNetVolumes(); - $res = true; - if (is_object($volume) && method_exists($volume, 'netunmount')) { - $res = $volume->netunmount($netVolumes, $key); - $volume->clearSessionCache(); - } - if ($res) { - if (is_string($key) && isset($netVolumes[$key])) { - unset($netVolumes[$key]); - $this->saveNetVolumes($netVolumes); - return true; - } - } - return false; - } - - /** - * Get plugin instance & set to $this->plugins - * - * @param string $name Plugin name (dirctory name) - * @param array $opts Plugin options (optional) - * @return object | bool Plugin object instance Or false - * @author Naoki Sawada - */ - protected function getPluginInstance($name, $opts = array()) { - $key = strtolower($name); - if (! isset($this->plugins[$key])) { - $class = 'elFinderPlugin' . $name; - // to try auto load - if (! class_exists($class)) { - $p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php'; - if (is_file($p_file)) { - include_once $p_file; - } - } - if (class_exists($class, false)) { - $this->plugins[$key] = new $class($opts); - } else { - $this->plugins[$key] = false; - } - } - return $this->plugins[$key]; - } - - /***************************************************************************/ - /* commands */ - /***************************************************************************/ - - /** - * Normalize error messages - * - * @return array - * @author Dmitry (dio) Levashov - **/ - public function error() { - $errors = array(); - - foreach (func_get_args() as $msg) { - if (is_array($msg)) { - $errors = array_merge($errors, $msg); - } else { - $errors[] = $msg; - } - } - - return count($errors) ? $errors : array(self::ERROR_UNKNOWN); - } - - /** - * @param $args - * @return array - * @throws elFinderAbortException - */ - protected function netmount($args) { - $options = array(); - $protocol = $args['protocol']; - $toast = ''; - - if ($protocol === 'netunmount') { - if (! empty($args['user']) && $volume = $this->volume($args['user'])) { - if ($this->removeNetVolume($args['host'], $volume)) { - return array('removed' => array(array('hash' => $volume->root()))); - } - } - return array('sync' => true, 'error' => $this->error(self::ERROR_NETUNMOUNT)); - } - - $driver = isset(self::$netDrivers[$protocol]) ? self::$netDrivers[$protocol] : ''; - $class = 'elFinderVolume'.$driver; - - if (!class_exists($class)) { - return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER)); - } - - if (!$args['path']) { - $args['path'] = '/'; - } - - foreach ($args as $k => $v) { - if ($k != 'options' && $k != 'protocol' && $v) { - $options[$k] = $v; - } - } - - if (is_array($args['options'])) { - foreach ($args['options'] as $key => $value) { - $options[$key] = $value; - } - } - - /* @var elFinderVolumeDriver $volume */ - $volume = new $class(); - - // pass session handler - $volume->setSession($this->session); - - if (is_callable(array($volume, 'netmountPrepare'))) { - $options = $volume->netmountPrepare($options); - if (isset($options['exit'])) { - if ($options['exit'] === 'callback') { - $this->callback($options['out']); - } - return $options; - } - if (!empty($options['toast'])) { - $toast = $options['toast']; - unset($options['toast']); - } - } - - $netVolumes = $this->getNetVolumes(); - - if (! isset($options['id'])) { - // given fixed unique id - if (! $options['id'] = $this->getNetVolumeUniqueId($netVolumes)) { - return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], 'Could\'t given volume id.')); - } - } - - // load additional volume root options - if (! empty($this->optionsNetVolumes['*'])) { - $options = array_merge($this->optionsNetVolumes['*'], $options); - } - if (! empty($this->optionsNetVolumes[$protocol])) { - $options = array_merge($this->optionsNetVolumes[$protocol], $options); - } - - if (! $key = $volume->netMountKey) { - $key = md5($protocol . '-' . serialize($options)); - } - $options['netkey'] = $key; - - if (!isset($netVolumes[$key]) && $volume->mount($options)) { - // call post-process function of netmount - if (is_callable(array($volume, 'postNetmount'))) { - $volume->postNetmount($options); - } - $options['driver'] = $driver; - $netVolumes[$key] = $options; - $this->saveNetVolumes($netVolumes); - $rootstat = $volume->file($volume->root()); - $res = array('added' => array($rootstat)); - if ($toast) { - $res['toast'] = $toast; - } - return $res; - } else { - $this->removeNetVolume(null, $volume); - return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error()))); - } - } - - /** - * "Open" directory - * Return array with following elements - * - cwd - opened dir info - * - files - opened dir content [and dirs tree if $args[tree]] - * - api - api version (if $args[init]) - * - uplMaxSize - if $args[init] - * - error - on failed - * - * @param array command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function open($args) { - $target = $args['target']; - $init = !empty($args['init']); - $tree = !empty($args['tree']); - $volume = $this->volume($target); - $cwd = $volume ? $volume->dir($target) : false; - $hash = $init ? 'default folder' : '#'.$target; - $compare = ''; - - // on init request we can get invalid dir hash - - // dir which can not be opened now, but remembered by client, - // so open default dir - if ((!$cwd || !$cwd['read']) && $init) { - $volume = $this->default; - $target = $volume->defaultPath(); - $cwd = $volume->dir($target); - } - - if (!$cwd) { - return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND)); - } - if (!$cwd['read']) { - return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED)); - } - - $files = array(); - - // get current working directory files list - if (($ls = $volume->scandir($cwd['hash'])) === false) { - return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error())); - } - - if (isset($cwd['dirs']) && $cwd['dirs'] != 1) { - $cwd = $volume->dir($target); - } - - // get other volume root - if ($tree) { - foreach ($this->volumes as $id => $v) { - $files[] = $v->file($v->root()); - } - } - - // long polling mode - if ($args['compare']) { - $sleep = max(1, (int)$volume->getOption('lsPlSleep')); - $standby = (int)$volume->getOption('plStandby'); - if ($standby > 0 && $sleep > $standby) { - $standby = $sleep; - } - $limit = max(0, floor($standby / $sleep)) + 1; - do { - elFinder::extendTimeLimit(30 + $sleep); - $_mtime = 0; - foreach($ls as $_f) { - $_mtime = max($_mtime, $_f['ts']); - } - $compare = strval(count($ls)).':'.strval($_mtime); - if ($compare !== $args['compare']) { - break; - } - if (--$limit) { - sleep($sleep); - $volume->clearstatcache(); - if (($ls = $volume->scandir($cwd['hash'])) === false) { - break; - } - } - } while($limit); - if ($ls === false) { - return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error())); - } - } - - if ($ls) { - if ($files) { - $files = array_merge($files, $ls); - } else { - $files = $ls; - } - } - - $result = array( - 'cwd' => $cwd, - 'options' => $volume->options($cwd['hash']), - 'files' => $files - ); - - if ($compare) { - $result['cwd']['compare'] = $compare; - } - - if (!empty($args['init'])) { - $result['api'] = sprintf('%.1F%03d', self::$ApiVersion, self::$ApiRevision); - $result['uplMaxSize'] = ini_get('upload_max_filesize'); - $result['uplMaxFile'] = ini_get('max_file_uploads'); - $result['netDrivers'] = array_keys(self::$netDrivers); - $result['maxTargets'] = $this->maxTargets; - if ($volume) { - $result['cwd']['root'] = $volume->root(); - } - if (elfinder::$textMimes) { - $result['textMimes'] = elfinder::$textMimes; - } - } - - return $result; - } - - /** - * Return dir files names list - * - * @param array command arguments - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function ls($args) { - $target = $args['target']; - $intersect = isset($args['intersect'])? $args['intersect'] : array(); - - if (($volume = $this->volume($target)) == false - || ($list = $volume->ls($target, $intersect)) === false) { - return array('error' => $this->error(self::ERROR_OPEN, '#'.$target)); - } - return array('list' => $list); - } - - /** - * Return subdirs for required directory - * - * @param array command arguments - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function tree($args) { - $target = $args['target']; - - if (($volume = $this->volume($target)) == false - || ($tree = $volume->tree($target)) == false) { - return array('error' => $this->error(self::ERROR_OPEN, '#'.$target)); - } - - return array('tree' => $tree); - } - - /** - * Return parents dir for required directory - * - * @param array command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function parents($args) { - $target = $args['target']; - $until = $args['until']; - - if (($volume = $this->volume($target)) == false - || ($tree = $volume->parents($target, false, $until)) == false) { - return array('error' => $this->error(self::ERROR_OPEN, '#'.$target)); - } - - return array('tree' => $tree); - } - - /** - * Return new created thumbnails list - * - * @param array command arguments - * @return array - * @throws ImagickException - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function tmb($args) { - - $result = array('images' => array()); - $targets = $args['targets']; - - foreach ($targets as $target) { - elFinder::checkAborted(); - - if (($volume = $this->volume($target)) != false - && (($tmb = $volume->tmb($target)) != false)) { - $result['images'][$target] = $tmb; - } - } - return $result; - } - - /** - * Download files/folders as an archive file - * - * 1st: Return srrsy contains download archive file info - * 2nd: Return array contains opened file pointer, root itself and required headers - * - * @param array command arguments - * @return array - * @throws Exception - * @author Naoki Sawada - */ - protected function zipdl($args) { - $targets = $args['targets']; - $download = !empty($args['download']); - $h404 = 'HTTP/1.x 404 Not Found'; - - if (!$download) { - //1st: Return array contains download archive file info - $error = array(self::ERROR_ARCHIVE); - if (($volume = $this->volume($targets[0])) !== false) { - if ($dlres = $volume->zipdl($targets)) { - $path = $dlres['path']; - register_shutdown_function(array('elFinder', 'rmFileInDisconnected'), $path); - if (count($targets) === 1) { - $name = basename($volume->path($targets[0])); - } else { - $name = $dlres['prefix'].'_Files'; - } - $name .= '.'.$dlres['ext']; - $uniqid = uniqid(); - $this->session->set('zipdl' . $uniqid, basename($path)); - $result = array( - 'zipdl' => array( - 'file' => $uniqid, - 'name' => $name, - 'mime' => $dlres['mime'] - ) - ); - return $result; - } - $error = array_merge($error, $volume->error()); - } - return array('error' => $error); - } else { - // 2nd: Return array contains opened file session key, root itself and required headers - if (count($targets) !== 4 || ($volume = $this->volume($targets[0])) == false || !($file = $this->session->get('zipdl' . $targets[1]))) { - return array('error' => 'File not found', 'header' => $h404, 'raw' => true); - } - $this->session->remove('zipdl' . $targets[1]); - if ($volume->commandDisabled('zipdl')) { - return array('error' => 'File not found', 'header' => $h404, 'raw' => true); - } - $path = $volume->getTempPath() . DIRECTORY_SEPARATOR . basename($file); - if (!is_readable($path) || !is_writable($path)) { - return array('error' => 'File not found', 'header' => $h404, 'raw' => true); - } - // register auto delete on shutdown - $GLOBALS['elFinderTempFiles'][$path] = true; - // for HTTP headers - $name = $targets[2]; - $mime = $targets[3]; - - $filenameEncoded = rawurlencode($name); - if (strpos($filenameEncoded, '%') === false) { // ASCII only - $filename = 'filename="'.$name.'"'; - } else { - $ua = $_SERVER['HTTP_USER_AGENT']; - if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987) - $filename = 'filename="'.$filenameEncoded.'"'; - } elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6 - $filename = 'filename="'.str_replace('"', '', $name).'"'; - } else { // RFC 6266 (RFC 2231/RFC 5987) - $filename = 'filename*=UTF-8\'\''.$filenameEncoded; - } - } - - $fp = fopen($path, 'rb'); - $file = fstat($fp); - $result = array( - 'pointer' => $fp, - 'header' => array( - 'Content-Type: '.$mime, - 'Content-Disposition: attachment; '.$filename, - 'Content-Transfer-Encoding: binary', - 'Content-Length: '.$file['size'], - 'Accept-Ranges: none', - 'Connection: close' - ) - ); - return $result; - } - } - - /** - * Required to output file in browser when volume URL is not set - * Return array contains opened file pointer, root itself and required headers - * - * @param array command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function file($args) { - $target = $args['target']; - $download = !empty($args['download']); - $onetime = !empty($args['onetime']); - //$h304 = 'HTTP/1.1 304 Not Modified'; - //$h403 = 'HTTP/1.0 403 Access Denied'; - $h404 = 'HTTP/1.0 404 Not Found'; - $a404 = array('error' => 'File not found', 'header' => $h404, 'raw' => true); - - if ($onetime) { - $volume = null; - $tmpdir = elFinder::$commonTempPath; - if (!$tmpdir || !is_file($tmpf = $tmpdir . DIRECTORY_SEPARATOR . 'ELF' . $target)) { - return $a404; - } - $GLOBALS['elFinderTempFiles'][$tmpf] = true; - if ($file = json_decode(file_get_contents($tmpf), true)) { - $src = base64_decode($file['file']); - if (!is_file($src) || !($fp = fopen($src, 'rb'))) { - return $a404; - } - if (strpos($src, $tmpdir) === 0) { - $GLOBALS['elFinderTempFiles'][$src] = true; - } - unset($file['file']); - $file['read'] = true; - $file['size'] = filesize($src); - } else { - return $a404; - } - } else { - if (($volume = $this->volume($target)) == false) { - return $a404; - } - - if (($file = $volume->file($target)) == false) { - return $a404; - } - - if (!$file['read']) { - return $a404; - } - - if (($fp = $volume->open($target)) == false) { - return $a404; - } - } - - // check aborted by user - elFinder::checkAborted(); - - // allow change MIME type by 'file.pre' callback functions - $mime = isset($args['mime'])? $args['mime'] : $file['mime']; - if ($download || $onetime) { - $disp = 'attachment'; - } else { - $dispInlineRegex = $volume->getOption('dispInlineRegex'); - $inlineRegex = false; - if ($dispInlineRegex) { - $inlineRegex = '#' . str_replace('#', '\\#', $dispInlineRegex) . '#'; - try { - preg_match($inlineRegex, ''); - } catch(Exception $e) { - $inlineRegex = false; - } - } - if (!$inlineRegex) { - $inlineRegex = '#^(?:(?:image|text)|application/x-shockwave-flash$)#'; - } - $disp = preg_match($inlineRegex, $mime)? 'inline' : 'attachment'; - } - - $filenameEncoded = rawurlencode($file['name']); - if (strpos($filenameEncoded, '%') === false) { // ASCII only - $filename = 'filename="'.$file['name'].'"'; - } else { - $ua = $_SERVER['HTTP_USER_AGENT']; - if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987) - $filename = 'filename="'.$filenameEncoded.'"'; - } elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6 - $filename = 'filename="'.str_replace('"', '', $file['name']).'"'; - } else { // RFC 6266 (RFC 2231/RFC 5987) - $filename = 'filename*=UTF-8\'\''.$filenameEncoded; - } - } - - if ($args['cpath'] && $args['reqid']) { - setcookie('elfdl' . $args['reqid'], '1', 0, $args['cpath']); - } - - $result = array( - 'volume' => $volume, - 'pointer' => $fp, - 'info' => $file, - 'header' => array( - 'Content-Type: '.$mime, - 'Content-Disposition: '.$disp.'; '.$filename, - 'Content-Transfer-Encoding: binary', - 'Content-Length: '.$file['size'], - 'Last-Modified: '.gmdate('D, d M Y H:i:s T', $file['ts']), - 'Connection: close' - ) - ); - - if (!$onetime) { - // add cache control headers - if ($cacheHeaders = $volume->getOption('cacheHeaders')) { - $result['header'] = array_merge($result['header'], $cacheHeaders); - } - - // check 'xsendfile' - $xsendfile = $volume->getOption('xsendfile'); - $path = null; - if ($xsendfile) { - $info = stream_get_meta_data($fp); - if ($path = empty($info['uri'])? null : $info['uri']) { - $basePath = rtrim($volume->getOption('xsendfilePath'), DIRECTORY_SEPARATOR); - if ($basePath) { - $root = rtrim($volume->getRootPath(), DIRECTORY_SEPARATOR); - if (strpos($path, $root) === 0) { - $path = $basePath . substr($path, strlen($root)); - } else { - $path = null; - } - } - } - } - if ($path) { - $result['header'][] = $xsendfile . ': ' . $path; - $result['info']['xsendfile'] = $xsendfile; - } - } - - // add "Content-Location" if file has url data - if (isset($file['url']) && $file['url'] && $file['url'] != 1) { - $result['header'][] = 'Content-Location: '.$file['url']; - } - return $result; - } - - /** - * Count total files size - * - * @param array command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function size($args) { - $size = 0; - $files = 0; - $dirs = 0; - $itemCount = true; - $sizes = array(); - - foreach ($args['targets'] as $target) { - elFinder::checkAborted(); - if (($volume = $this->volume($target)) == false - || ($file = $volume->file($target)) == false - || !$file['read']) { - return array('error' => $this->error(self::ERROR_OPEN, '#'.$target)); - } - - $volRes = $volume->size($target); - if (is_array($volRes)) { - $sizeInfo = array('size' => 0, 'fileCnt' => 0, 'dirCnt' => 0); - if (! empty($volRes['size'])) { - $sizeInfo['size'] = $volRes['size']; - $size += $volRes['size']; - } - if (! empty($volRes['files'])) { - $sizeInfo['fileCnt'] = $volRes['files']; - } - if (! empty($volRes['dirs'])) { - $sizeInfo['dirCnt'] = $volRes['dirs']; - } - if ($itemCount) { - $files += $sizeInfo['fileCnt']; - $dirs += $sizeInfo['dirCnt']; - } - $sizes[$target] = $sizeInfo; - } else if (is_numeric($volRes)) { - $size += $volRes; - $files = $dirs = 'unknown'; - $itemCount = false; - } - } - return array('size' => $size, 'fileCnt' => $files, 'dirCnt' => $dirs, 'sizes' => $sizes); - } - - /** - * Create directory - * - * @param array command arguments - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function mkdir($args) { - $target = $args['target']; - $name = $args['name']; - $dirs = $args['dirs']; - if ($name === '' && !$dirs) { - return array('error' => $this->error(self::ERROR_INV_PARAMS, 'mkdir')); - } - - if (($volume = $this->volume($target)) == false) { - return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)); - } - if ($dirs) { - sort($dirs); - $reset = null; - $mkdirs = array(); - foreach($dirs as $dir) { - $tgt =& $mkdirs; - $_names = explode('/', trim($dir, '/')); - foreach($_names as $_key => $_name) { - if (! isset($tgt[$_name])) { - $tgt[$_name] = array(); - } - $tgt =& $tgt[$_name]; - } - $tgt =& $reset; - } - $res = $this->ensureDirsRecursively($volume, $target, $mkdirs); - $ret = array( - 'added' => $res['stats'], - 'hashes' => $res['hashes'] - ); - if ($res['error']) { - $ret['warning'] = $this->error(self::ERROR_MKDIR, $res['error'][0], $volume->error()); - } - return $ret; - } else { - return ($dir = $volume->mkdir($target, $name)) == false - ? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error())) - : array('added' => array($dir)); - } - } - - /** - * Create empty file - * - * @param array command arguments - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function mkfile($args) { - $target = $args['target']; - $name = $args['name']; - - if (($volume = $this->volume($target)) == false) { - return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)); - } - - return ($file = $volume->mkfile($target, $args['name'])) == false - ? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error())) - : array('added' => array($file)); - } - - /** - * Rename file, Accept multiple items >= API 2.1031 - * - * @param array $args - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Naoki Sawada - */ - protected function rename($args) { - $target = $args['target']; - $name = $args['name']; - $query = (strpos($args['q'], '*') !== false)? $args['q'] : ''; - $targets = $args['targets']; - $rms = array(); - $notfounds = array(); - $locked = array(); - $errs = array(); - $files = array(); - $removed = array(); - $res = array(); - $type = 'normal'; - - if (!($volume = $this->volume($target))) { - return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND)); - } - - if ($targets) { - array_unshift($targets, $target); - foreach($targets as $h) { - if ($rm = $volume->file($h)) { - if ($this->itemLocked($h)) { - $locked[] = $rm['name']; - } else { - $rm['realpath'] = $volume->realpath($h); - $rms[] = $rm; - } - } else { - $notfounds[] = '#'.$h; - } - } - if (!$rms) { - $res['error'] = array(); - if ($notfounds) { - $res['error'] = array(self::ERROR_RENAME, join(', ', $notfounds), self::ERROR_FILE_NOT_FOUND); - } - if ($locked) { - array_push($res['error'], self::ERROR_LOCKED, join(', ',$locked)); - } - return $res; - } - - $res['warning'] = array(); - if ($notfounds) { - array_push($res['warning'], self::ERROR_RENAME, join(', ', $notfounds), self::ERROR_FILE_NOT_FOUND); - } - if ($locked) { - array_push($res['warning'], self::ERROR_LOCKED, join(', ',$locked)); - } - - if ($query) { - // batch rename - $splits = elFinder::splitFileExtention($query); - if ($splits[1] && $splits[0] === '*') { - $type = 'extention'; - $name = $splits[1]; - } else if (strlen($splits[0]) > 1) { - if (substr($splits[0], -1) === '*') { - $type = 'prefix'; - $name = substr($splits[0], 0 ,strlen($splits[0]) - 1); - } else if (substr($splits[0], 0, 1) === '*') { - $type = 'suffix'; - $name = substr($splits[0], 1); - } - } - if ($type !== 'normal') { - if (!empty($this->listeners['rename.pre'])) { - $_args = array('name' => $name); - foreach ($this->listeners['rename.pre'] as $handler) { - $_res = call_user_func_array($handler, array('rename', &$_args, $this, $volume)); - if (! empty($_res['preventexec'])) { - break; - } - } - $name = $_args['name']; - } - } - } - foreach($rms as $rm) { - if ($type === 'normal') { - $rname = $volume->uniqueName($volume->realpath($rm['phash']), $name, '', false); - } else { - $rname = $name; - if ($type === 'extention') { - $splits = elFinder::splitFileExtention($rm['name']); - $rname = $splits[0] . '.' . $name; - } else if ($type === 'prefix') { - $rname = $name . $rm['name']; - } else if ($type === 'suffix') { - $splits = elFinder::splitFileExtention($rm['name']); - $rname = $splits[0]. $name . ($splits[1]? ('.' . $splits[1]) : ''); - } - $rname = $volume->uniqueName($volume->realpath($rm['phash']), $rname, '', true); - } - if ($file = $volume->rename($rm['hash'], $rname)) { - $files[] = $file; - $removed[] = $rm; - } else { - $errs[] = $rm['name']; - } - } - - if (!$files) { - $res['error'] = $this->error(self::ERROR_RENAME, join(', ', $errs), $volume->error()); - if (!$res['warning']) { - unset($res['warning']); - } - return $res; - } - if ($errs) { - array_push($res['warning'], self::ERROR_RENAME, join(', ', $errs), $volume->error()); - } - if (!$res['warning']) { - unset($res['warning']); - } - $res['added'] = $files; - $res['removed'] = $removed; - return $res; - } else { - if (!($rm = $volume->file($target))) { - return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND)); - } - if ($this->itemLocked($target)) { - return array('error' => $this->error(self::ERROR_LOCKED, $rm['name'])); - } - $rm['realpath'] = $volume->realpath($target); - - return ($file = $volume->rename($target, $name)) == false - ? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error())) - : array('added' => array($file), 'removed' => array($rm)); - } - } - - /** - * Duplicate file - create copy with "copy %d" suffix - * - * @param array $args command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function duplicate($args) { - $targets = is_array($args['targets']) ? $args['targets'] : array(); - $result = array(); - $suffix = empty($args['suffix']) ? 'copy' : $args['suffix']; - - $this->itemLock($targets); - - foreach ($targets as $target) { - elFinder::checkAborted(); - - if (($volume = $this->volume($target)) == false - || ($src = $volume->file($target)) == false) { - $result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND); - break; - } - - if (($file = $volume->duplicate($target, $suffix)) == false) { - $result['warning'] = $this->error($volume->error()); - break; - } - } - - return $result; - } - - /** - * Remove dirs/files - * - * @param array command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function rm($args) { - $targets = is_array($args['targets']) ? $args['targets'] : array(); - $result = array('removed' => array()); - - foreach ($targets as $target) { - elFinder::checkAborted(); - - if (($volume = $this->volume($target)) == false) { - $result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND); - break; - } - - if ($this->itemLocked($target)) { - $rm = $volume->file($target); - $result['warning'] = $this->error(self::ERROR_LOCKED, $rm['name']); - break; - } - - if (!$volume->rm($target)) { - $result['warning'] = $this->error($volume->error()); - break; - } - } - - return $result; - } - - /** - * Return has subdirs - * - * @param array command arguments - * @return array - * @author Dmitry Naoki Sawada - **/ - protected function subdirs($args) { - - $result = array('subdirs' => array()); - $targets = $args['targets']; - - foreach ($targets as $target) { - if (($volume = $this->volume($target)) !== false) { - $result['subdirs'][$target] = $volume->subdirs($target)? 1 : 0; - } - } - return $result; - } - - /** - * Gateway for custom contents editor - * - * @param array $args command arguments - * @return array - * @author Naoki Sawada - */ - protected function editor($args = array()) { - /* @var elFinderEditor $editor */ - $name = $args['name']; - if (is_array($name)) { - $res = array(); - foreach($name as $c) { - $class = 'elFinderEditor' . $c; - if (class_exists($class)) { - $editor = new $class($this, $args['args']); - $res[$c] = $editor->enabled(); - } else { - $res[$c] = 0; - } - } - return $res; - } else { - $class = 'elFinderEditor' . $name; - $method = ''; - if (class_exists($class)) { - $editor = new $class($this, $args['args']); - $method = $args['method']; - if ($editor->isAllowedMethod($method) && method_exists($editor, $method)) { - return $editor->$method(); - } - } - return array('error', $this->error(self::ERROR_UNKNOWN_CMD, 'editor.'.$name.'.'.$method)); - } - } - - /** - * Abort current request and make flag file to running check - * - * @param array $args - * @return void - */ - protected function abort($args = array()) { - if (! elFinder::$connectionFlagsPath || $_SERVER['REQUEST_METHOD'] === 'HEAD') { - return; - } - $flagFile = elFinder::$connectionFlagsPath . DIRECTORY_SEPARATOR . 'elfreq%s'; - if (! empty($args['makeFile'])) { - self::$abortCheckFile = sprintf($flagFile, $args['makeFile']); - touch(self::$abortCheckFile); - $GLOBALS['elFinderTempFiles'][self::$abortCheckFile] = true; - return; - } - - $file = ! empty($args['id'])? sprintf($flagFile, $args['id']) : self::$abortCheckFile; - $file && is_file($file) && unlink($file); - } - - /** - * Get remote contents - * - * @param string $url target url - * @param int $timeout timeout (sec) - * @param int $redirect_max redirect max count - * @param string $ua - * @param resource $fp - * @return string, resource or bool(false) - * @retval string contents - * @retval resource conttents - * @rettval false error - * @author Naoki Sawada - **/ - protected function get_remote_contents( &$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null ) { - $method = (function_exists('curl_exec') && !ini_get('safe_mode') && !ini_get('open_basedir'))? 'curl_get_contents' : 'fsock_get_contents'; - return $this->$method( $url, $timeout, $redirect_max, $ua, $fp ); - } - - /** - * Get remote contents with cURL - * - * @param string $url target url - * @param int $timeout timeout (sec) - * @param int $redirect_max redirect max count - * @param string $ua - * @param resource $outfp - * @return string, resource or bool(false) - * @retval string contents - * @retval resource conttents - * @retval false error - * @author Naoki Sawada - **/ - protected function curl_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ){ - $ch = curl_init(); - curl_setopt( $ch, CURLOPT_URL, $url ); - curl_setopt( $ch, CURLOPT_HEADER, false ); - if ($outfp) { - curl_setopt( $ch, CURLOPT_FILE, $outfp ); - } else { - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); - curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true ); - } - curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 1 ); - curl_setopt( $ch, CURLOPT_LOW_SPEED_TIME, $timeout ); - curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); - curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1); - curl_setopt( $ch, CURLOPT_MAXREDIRS, $redirect_max); - curl_setopt( $ch, CURLOPT_USERAGENT, $ua); - $result = curl_exec( $ch ); - $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); - curl_close( $ch ); - return $outfp? $outfp : $result; - } - - /** - * Get remote contents with fsockopen() - * - * @param string $url url - * @param int $timeout timeout (sec) - * @param int $redirect_max redirect max count - * @param string $ua - * @param resource $outfp - * @return string, resource or bool(false) - * @retval string contents - * @retval resource conttents - * @retval false error - * @throws elFinderAbortException - * @author Naoki Sawada - */ - protected function fsock_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ) { - $connect_timeout = 3; - $connect_try = 3; - $method = 'GET'; - $readsize = 4096; - $ssl = ''; - - $getSize = null; - $headers = ''; - - $arr = parse_url($url); - if (!$arr){ - // Bad request - return false; - } - if ($arr['scheme'] === 'https') { - $ssl = 'ssl://'; - } - - // query - $arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : ''; - // port - $port = isset($arr['port']) ? $arr['port'] : ''; - $arr['port'] = $port? $port : ($ssl? 443 : 80); - - $url_base = $arr['scheme'].'://'.$arr['host'].($port? (':'.$port) : ''); - $url_path = isset($arr['path']) ? $arr['path'] : '/'; - $uri = $url_path.$arr['query']; - - $query = $method.' '.$uri." HTTP/1.0\r\n"; - $query .= "Host: ".$arr['host']."\r\n"; - $query .= "Accept: */*\r\n"; - $query .= "Connection: close\r\n"; - if (!empty($ua)) $query .= "User-Agent: ".$ua."\r\n"; - if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n"; - - $query .= $headers; - - $query .= "\r\n"; - - $fp = $connect_try_count = 0; - while( !$fp && $connect_try_count < $connect_try ) { - - $errno = 0; - $errstr = ""; - $fp = fsockopen( - $ssl.$arr['host'], - $arr['port'], - $errno,$errstr,$connect_timeout); - if ($fp) break; - $connect_try_count++; - if (connection_aborted()) { - throw new elFinderAbortException(); - } - sleep(1); // wait 1sec - } - - $fwrite = 0; - for ($written = 0; $written < strlen($query); $written += $fwrite) { - $fwrite = fwrite($fp, substr($query, $written)); - if (!$fwrite) { - break; - } - } - - if ($timeout) { - socket_set_timeout($fp, $timeout); - } - - $_response = ''; - $header = ''; - while($_response !== "\r\n"){ - $_response = fgets($fp, $readsize); - $header .= $_response; - }; - - $rccd = array_pad(explode(' ',$header,2), 2, ''); // array('HTTP/1.1','200') - $rc = (int)$rccd[1]; - - $ret = false; - // Redirect - switch ($rc) { - case 307: // Temporary Redirect - case 303: // See Other - case 302: // Moved Temporarily - case 301: // Moved Permanently - $matches = array(); - if (preg_match('/^Location: (.+?)(#.+)?$/im',$header,$matches) && --$redirect_max > 0) { - $_url = $url; - $url = trim($matches[1]); - if (!preg_match('/^https?:\//',$url)) { // no scheme - if ($url{0} != '/') { // Relative path - // to Absolute path - $url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url; - } - // add sheme,host - $url = $url_base.$url; - } - if ($_url === $url) { - sleep(1); - } - fclose($fp); - return $this->fsock_get_contents( $url, $timeout, $redirect_max, $ua, $outfp ); - } - break; - case 200: - $ret = true; - } - if (! $ret) { - fclose($fp); - return false; - } - - $body = ''; - if (!$outfp) { - $outfp = fopen('php://temp', 'rwb'); - $body = true; - } - while(fwrite($outfp, fread($fp, $readsize))) { - if ($timeout) { - $_status = socket_get_status($fp); - if ($_status['timed_out']) { - fclose($outfp); - fclose($fp); - return false; // Request Time-out - } - } - } - if ($body) { - rewind($outfp); - $body = stream_get_contents($outfp); - fclose($outfp); - $outfp = null; - } - - fclose($fp); - - return $outfp? $outfp : $body; // Data - } - - /** - * Parse Data URI scheme - * - * @param string $str - * @param array $extTable - * @param array $args - * @return array - * @author Naoki Sawada - */ - protected function parse_data_scheme($str, $extTable, $args = null) { - $data = $name = $mime = ''; - // Scheme 'data://' require `allow_url_fopen` and `allow_url_include` - if ($fp = fopen('data://'.substr($str, 5), 'rb')) { - if ($data = stream_get_contents($fp)) { - $meta = stream_get_meta_data($fp); - $mime = $meta['mediatype']; - } - fclose($fp); - } else if (preg_match('~^data:(.+?/.+?)?(?:;charset=.+?)?;base64,~', substr($str, 0, 128), $m)) { - $data = base64_decode(substr($str, strlen($m[0]))); - if ($m[1]) { - $mime = $m[1]; - } - } - if ($data) { - $ext = ($mime && isset($extTable[$mime]))? '.' . $extTable[$mime] : ''; - // Set name if name eq 'image.png' and $args has 'name' array, e.g. clipboard data - if (is_array($args['name']) && isset($args['name'][0])) { - $name = $args['name'][0]; - if ($ext) { - $name = preg_replace('/\.[^.]*$/', '', $name); - } - } else { - $name = substr(md5($data), 0, 8); - } - $name .= $ext; - } else { - $data = $name = ''; - } - return array($data, $name); - } - - /** - * Detect file MIME Type by local path - * - * @param string $path Local path - * @return string file MIME Type - * @author Naoki Sawada - */ - protected function detectMimeType($path) { - static $type, $finfo; - if (!$type) { - if (class_exists('finfo', false)) { - $tmpFileInfo = explode(';', finfo_file(finfo_open(FILEINFO_MIME), __FILE__)); - } else { - $tmpFileInfo = false; - } - $regexp = '/text\/x\-(php|c\+\+)/'; - if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) { - $type = 'finfo'; - $finfo = finfo_open(FILEINFO_MIME); - } elseif (function_exists('mime_content_type') - && preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) { - $type = 'mime_content_type'; - } elseif (function_exists('getimagesize')) { - $type = 'getimagesize'; - } else { - $type = 'none'; - } - } - - $mime = ''; - if ($type === 'finfo') { - $mime = finfo_file($finfo, $path); - } elseif ($type === 'mime_content_type') { - $mime = mime_content_type($path); - } elseif ($type === 'getimagesize') { - if ($img = getimagesize($path)) { - $mime = $img['mime']; - } - } - - if ($mime) { - $mime = explode(';', $mime); - $mime = trim($mime[0]); - - if (in_array($mime, array('application/x-empty', 'inode/x-empty'))) { - // finfo return this mime for empty files - $mime = 'text/plain'; - } elseif ($mime == 'application/x-zip') { - // http://elrte.org/redmine/issues/163 - $mime = 'application/zip'; - } - } - - return $mime? $mime : 'unknown'; - } - - /** - * Detect file type extension by local path - * - * @param object $volume elFinderVolumeDriver instance - * @param string $path Local path - * @param string $name Filename to save - * @return string file type extension with dot - * @author Naoki Sawada - */ - protected function detectFileExtension($volume, $path, $name) { - $mime = $this->detectMimeType($path); - if ($mime === 'unknown') { - $mime = 'application/octet-stream'; - } - $ext = $volume->getExtentionByMime($volume->mimeTypeNormalize($mime, $name)); - return $ext? ('.' . $ext) : ''; - } - - /** - * Get temporary directory path - * - * @param string $volumeTempPath - * @return string - * @author Naoki Sawada - */ - private function getTempDir($volumeTempPath = null) { - $testDirs = array(); - if ($this->uploadTempPath) { - $testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR); - } - if ($volumeTempPath) { - $testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR); - } - if (elFinder::$commonTempPath) { - $testDirs[] = elFinder::$commonTempPath; - } - $tempDir = ''; - foreach($testDirs as $testDir) { - if (!$testDir || !is_dir($testDir)) continue; - if (is_writable($testDir)) { - $tempDir = $testDir; - $gc = time() - 3600; - foreach(glob($tempDir . DIRECTORY_SEPARATOR .'ELF*') as $cf) { - if (filemtime($cf) < $gc) { - unlink($cf); - } - } - break; - } - } - return $tempDir; - } - - /** - * chmod - * - * @param array command arguments - * @return array - * @throws elFinderAbortException - * @author David Bartle - */ - protected function chmod($args) { - $targets = $args['targets']; - $mode = intval((string)$args['mode'], 8); - - if (!is_array($targets)) { - $targets = array($targets); - } - - $result = array(); - - if (($volume = $this->volume($targets[0])) == false) { - $result['error'] = $this->error(self::ERROR_CONF_NO_VOL); - return $result; - } - - $this->itemLock($targets); - - $files = array(); - $errors = array(); - foreach($targets as $target) { - elFinder::checkAborted(); - - $file = $volume->chmod($target, $mode); - if ($file) { - $files = array_merge($files, is_array($file)? $file : array($file)); - } else { - $errors = array_merge($errors, $volume->error()); - } - } - - if ($files) { - $result['changed'] = $files; - if ($errors) { - $result['warning'] = $this->error($errors); - } - } else { - $result['error'] = $this->error($errors); - } - - return $result; - } - - /** - * Check chunked upload files - * - * @param string $tmpname uploaded temporary file path - * @param string $chunk uploaded chunk file name - * @param string $cid uploaded chunked file id - * @param string $tempDir temporary dirctroy path - * @param null $volume - * @return array|null - * @throws elFinderAbortException - * @author Naoki Sawada - */ - private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null) { - /* @var elFinderVolumeDriver $volume */ - if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) { - $fname = $m[1]; - $encname = md5($cid . '_' . $fname); - $base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname; - $clast = intval($m[3]); - if (is_null($tmpname)) { - ignore_user_abort(true); - // chunked file upload fail - foreach(glob($base . '*') as $cf) { - unlink($cf); - } - ignore_user_abort(false); - return null; - } - - $range = isset($_POST['range'])? trim($_POST['range']) : ''; - if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) { - $start = $ranges[1]; - $len = $ranges[2]; - $size = $ranges[3]; - $tmp = $base . '.part'; - $csize = filesize($tmpname); - - $tmpExists = is_file($tmp); - if (!$tmpExists) { - // check upload max size - $uploadMaxSize = $volume? $volume->getUploadMaxSize() : 0; - if ($uploadMaxSize > 0 && $size > $uploadMaxSize) { - return array(self::ERROR_UPLOAD_FILE_SIZE, false); - } - // make temp file - $ok = false; - if ($fp = fopen($tmp, 'wb')) { - flock($fp, LOCK_EX); - $ok = ftruncate($fp, $size); - flock($fp, LOCK_UN); - fclose($fp); - touch($base); - } - if (!$ok) { - unlink($tmp); - return array(self::ERROR_UPLOAD_TEMP, false); - } - } else { - // wait until makeing temp file (for anothor session) - $cnt = 1200; // Time limit 120 sec - while(!is_file($base) && --$cnt) { - usleep(100000); // wait 100ms - } - if (!$cnt) { - return array(self::ERROR_UPLOAD_TEMP, false); - } - } - - // check size info - if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) { - return array(self::ERROR_UPLOAD_TEMP, false); - } - - // write chunk data - $src = fopen($tmpname, 'rb'); - $fp = fopen($tmp, 'cb'); - fseek($fp, $start); - $writelen = stream_copy_to_stream($src, $fp, $len); - fclose($fp); - fclose($src); - - try { - // to check connection is aborted - elFinder::checkAborted(); - } catch (elFinderAbortException $e) { - unlink($tmpname); - is_file($tmp) && unlink($tmp); - is_file($base) && unlink($base); - throw $e; - } - - if ($writelen != $len) { - return array(self::ERROR_UPLOAD_TEMP, false); - } - - // write counts - file_put_contents($base, "\0", FILE_APPEND | LOCK_EX); - - if (filesize($base) >= $clast + 1) { - // Completion - unlink($base); - return array($tmp, $fname); - } - } else { - // old way - $part = $base . $m[2]; - if (move_uploaded_file($tmpname, $part)) { - chmod($part, 0600); - if ($clast < count(glob($base . '*'))) { - $parts = array(); - for ($i = 0; $i <= $clast; $i++) { - $name = $base . '.' . $i . '_' . $clast; - if (is_readable($name)) { - $parts[] = $name; - } else { - $parts = null; - break; - } - } - if ($parts) { - if (!is_file($base)) { - touch($base); - if ($resfile = tempnam($tempDir, 'ELF')) { - $target = fopen($resfile, 'wb'); - foreach($parts as $f) { - $fp = fopen($f, 'rb'); - while (!feof($fp)) { - fwrite($target, fread($fp, 8192)); - } - fclose($fp); - unlink($f); - } - fclose($target); - unlink($base); - return array($resfile, $fname); - } - unlink($base); - } - } - } - } - } - } - return array('', ''); - } - - /** - * Save uploaded files - * - * @param array - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function upload($args) { - $ngReg = '/[\/\\?*:|"<>]/'; - $target = $args['target']; - $volume = $this->volume($target); - $files = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array(); - $header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8'); - $result = array_merge(array('added' => array()), $header); - $paths = $args['upload_path']? $args['upload_path'] : array(); - $chunk = $args['chunk']? $args['chunk'] : ''; - $cid = $args['cid']? (int)$args['cid'] : ''; - $mtimes = $args['mtime']? $args['mtime'] : array(); - $tmpfname = ''; - - if (!$volume) { - return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)), $header); - } - - // check $chunk - if (strpos($chunk, '/') !== false || strpos($chunk, '\\') !== false) { - return array('error' => $this->error(self::ERROR_UPLOAD)); - } - - if ($args['overwrite'] !== '') { - $volume->setUploadOverwrite($args['overwrite']); - } - - $renames = $hashes = array(); - $suffix = '~'; - if ($args['renames'] && is_array($args['renames'])) { - $renames = array_flip($args['renames']); - if (is_string($args['suffix']) && ! preg_match($ngReg, $args['suffix'])) { - $suffix = $args['suffix']; - } - } - if ($args['hashes'] && is_array($args['hashes'])) { - $hashes = array_flip($args['hashes']); - } - - $this->itemLock($target); - - // file extentions table by MIME - $extTable = array_flip(array_unique($volume->getMimeTable())); - - if (empty($files)) { - if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) { - $names = array(); - foreach($args['upload'] as $i => $url) { - // check chunked file upload commit - if ($chunk) { - if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') { - $this->checkChunkedFile(null, $chunk, $cid, $tempDir); - if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) { - $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TEMP); - } - return $result; - } else { - $tmpfname = $tempDir . '/' . $chunk; - $files['tmp_name'][$i] = $tmpfname; - $files['name'][$i] = $url; - $files['error'][$i] = 0; - $GLOBALS['elFinderTempFiles'][$tmpfname] = true; - break; - } - } - - $tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url.microtime(true)); - $GLOBALS['elFinderTempFiles'][$tmpfname] = true; - - $_name = ''; - // check is data: - if (substr($url, 0, 5) === 'data:') { - list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable, $args); - } else { - $fp = fopen($tmpfname, 'wb'); - $data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp); - // to check connection is aborted - elFinder::checkAborted(); - $_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url)); - // Check `Content-Disposition` response header - if ($data && ($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) { - if (preg_match('/filename\*=(?:([a-zA-Z0-9_-]+?)\'\')"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) { - $_name = rawurldecode($m[2]); - if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) { - $_name = mb_convert_encoding($_name, 'UTF-8', $m[1]); - } - } else if (preg_match('/filename="?([ a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) { - $_name = rawurldecode($m[1]); - } - } - } - if ($data) { - if (isset($args['name'][$i])) { - $_name = $args['name'][$i]; - } - if ($_name) { - $_ext = ''; - if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) { - $_ext = $_match[1]; - } - if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) { - $GLOBALS['elFinderTempFiles'][$tmpfname] = true; - $_name = preg_replace($ngReg, '_', $_name); - list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, ''); - if ($_b === '') { - if ($_ext) { - rename($tmpfname, $tmpfname . $_ext); - $tmpfname = $tmpfname . $_ext; - } - $_b = $this->detectFileExtension($volume, $tmpfname, $_name); - $_name = $_a.$_b; - } else { - $_b = '.'.$_b; - } - if (isset($names[$_name])) { - $_name = $_a.'_'.$names[$_name]++.$_b; - } else { - $names[$_name] = 1; - } - $files['tmp_name'][$i] = $tmpfname; - $files['name'][$i] = $_name; - $files['error'][$i] = 0; - // set to auto rename - $volume->setUploadOverwrite(false); - } else { - unlink($tmpfname); - } - } - } - } - } - if (empty($files)) { - return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header); - } - } - - $addedDirs = array(); - $errors = array(); - foreach ($files['name'] as $i => $name) { - if (($error = $files['error'][$i]) > 0) { - $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER, $error); - $this->uploadDebug = 'Upload error code: '.$error; - break; - } - - $tmpname = $files['tmp_name'][$i]; - $thash = ($paths && isset($paths[$i]))? $paths[$i] : $target; - $mtime = isset($mtimes[$i])? $mtimes[$i] : 0; - if ($name === 'blob') { - if ($chunk) { - if ($tempDir = $this->getTempDir($volume->getTempPath())) { - list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume); - if ($tmpname) { - if ($name === false) { - preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m); - $result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname); - $result['_chunkfailure'] = true; - $this->uploadDebug = 'Upload error: ' . $tmpname; - } else if ($name) { - $result['_chunkmerged'] = basename($tmpname); - $result['_name'] = $name; - $result['_mtime'] = $mtime; - } - } - } else { - $result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TEMP); - $this->uploadDebug = 'Upload error: unable open tmp file'; - } - return $result; - } else { - // for form clipboard with Google Chrome or Opera - $name = 'image.png'; - } - } - - // Set name if name eq 'image.png' and $args has 'name' array, e.g. clipboard data - if (strtolower(substr($name, 0, 5)) === 'image' && is_array($args['name']) && isset($args['name'][$i])) { - $type = $files['type'][$i]; - $name = $args['name'][$i]; - $ext = isset($extTable[$type])? '.' . $extTable[$type] : ''; - if ($ext) { - $name = preg_replace('/\.[^.]*$/', '', $name); - } - $name .= $ext; - } - - // do hook function 'upload.presave' - if (! empty($this->listeners['upload.presave'])) { - foreach($this->listeners['upload.presave'] as $handler) { - call_user_func_array($handler, array(&$thash, &$name, $tmpname, $this, $volume)); - } - } - - clearstatcache(); - if ($mtime && is_file($tmpname)) { - // for keep timestamp option in the LocalFileSystem volume - touch($tmpname, $mtime); - } - - if (!is_file($tmpname) || ($fp = fopen($tmpname, 'rb')) === false) { - $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TEMP); - $this->uploadDebug = 'Upload error: unable open tmp file'; - if (! is_uploaded_file($tmpname)) { - if (unlink($tmpname) && $tmpfname) unset($GLOBALS['elFinderTempFiles'][$tmpfname]); - continue; - } - break; - } - $rnres = array(); - if ($thash !== '' && $thash !== $target) { - if ($dir = $volume->dir($thash)) { - $_target = $thash; - if (! isset($addedDirs[$thash])) { - $addedDirs[$thash] = true; - $result['added'][] = $dir; - // to support multi-level directory creation - $_phash = isset($dir['phash'])? $dir['phash'] : null; - while($_phash && ! isset($addedDirs[$_phash]) && $_phash !== $target) { - if ($_dir = $volume->dir($_phash)) { - $addedDirs[$_phash] = true; - $result['added'][] = $_dir; - $_phash = isset($_dir['phash'])? $_dir['phash'] : null; - } else { - break; - } - } - } - } else { - $result['error'] = $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, 'hash@'.$thash); - break; - } - } else { - $_target = $target; - // file rename for backup - if (isset($renames[$name])) { - $dir = $volume->realpath($_target); - if (isset($hashes[$name])) { - $hash = $hashes[$name]; - } else { - $hash = $volume->getHash($dir, $name); - } - $rnres = $this->rename(array('target' => $hash, 'name' => $volume->uniqueName($dir, $name, $suffix, true, 0))); - if (!empty($rnres['error'])) { - $result['warning'] = $rnres['error']; - if (!is_array($rnres['error'])) { - $errors = array_push($errors, $rnres['error']); - } else { - $errors = array_merge($errors, $rnres['error']); - } - continue; - } - } - } - if (! $_target || ($file = $volume->upload($fp, $_target, $name, $tmpname, ($_target === $target)? $hashes : array())) === false) { - $errors = array_merge($errors, $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error())); - fclose($fp); - if (! is_uploaded_file($tmpname) && unlink($tmpname)) { - unset($GLOBALS['elFinderTempFiles'][$tmpname]); - } - continue; - } - - is_resource($fp) && fclose($fp); - if (! is_uploaded_file($tmpname)){ - clearstatcache(); - if (!is_file($tmpname) || unlink($tmpname)) { - unset($GLOBALS['elFinderTempFiles'][$tmpname]); - } - } - $result['added'][] = $file; - if ($rnres) { - $result = array_merge_recursive($result, $rnres); - } - } - - if ($errors) { - $result['warning'] = $errors; - } - - if ($GLOBALS['elFinderTempFiles']) { - foreach(array_keys($GLOBALS['elFinderTempFiles']) as $_temp) { - is_file($_temp) && unlink($_temp); - } - } - $result['removed'] = $volume->removed(); - - if (!empty($args['node'])) { - $result['callback'] = array( - 'node' => $args['node'], - 'bind' => 'upload' - ); - } - return $result; - } - - /** - * Copy/move files into new destination - * - * @param array command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function paste($args) { - $dst = $args['dst']; - $targets = is_array($args['targets']) ? $args['targets'] : array(); - $cut = !empty($args['cut']); - $error = $cut ? self::ERROR_MOVE : self::ERROR_COPY; - $result = array('changed' => array(), 'added' => array(), 'removed' => array(), 'warning' => array()); - - if (($dstVolume = $this->volume($dst)) == false) { - return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst)); - } - - $this->itemLock($dst); - - $hashes = $renames = array(); - $suffix = '~'; - if (!empty($args['renames'])) { - $renames = array_flip($args['renames']); - if (is_string($args['suffix']) && ! preg_match('/[\/\\?*:|"<>]/', $args['suffix'])) { - $suffix = $args['suffix']; - } - } - if (!empty($args['hashes'])) { - $hashes = array_flip($args['hashes']); - } - - foreach ($targets as $target) { - elFinder::checkAborted(); - - if (($srcVolume = $this->volume($target)) == false) { - $result['warning'] = array_merge($result['warning'], $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND)); - continue; - } - - $rnres = array(); - if ($renames) { - $file = $srcVolume->file($target); - if (isset($renames[$file['name']])) { - $dir = $dstVolume->realpath($dst); - if (isset($hashes[$file['name']])) { - $hash = $hashes[$file['name']]; - } else { - $hash = $dstVolume->getHash($dir, $file['name']); - } - $rnres = $this->rename(array('target' => $hash, 'name' => $dstVolume->uniqueName($dir, $file['name'], $suffix, true, 0))); - if (!empty($rnres['error'])) { - $result['warning'] = array_merge($result['warning'], $rnres['error']); - continue; - } - } - } - - if ($cut && $this->itemLocked($target)) { - $rm = $srcVolume->file($target); - $result['warning'] = array_merge($result['warning'], $this->error(self::ERROR_LOCKED, $rm['name'])); - continue; - } - - if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut, $hashes)) == false) { - $result['warning'] = array_merge($result['warning'], $this->error($dstVolume->error())); - continue; - } - - if ($error = $dstVolume->error()) { - $result['warning'] = array_merge($result['warning'], $this->error($error)); - } - - $dirChange = ! empty($file['dirChange']); - unset($file['dirChange']); - if ($dirChange) { - $result['changed'][] = $file; - } else { - $result['added'][] = $file; - } - if ($rnres) { - $result = array_merge_recursive($result, $rnres); - } - } - if (count($result['warning']) < 1) { - unset($result['warning']); - } else { - $result['sync'] = true; - } - - return $result; - } - - /** - * Return file content - * - * @param array $args command arguments - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function get($args) { - $target = $args['target']; - $volume = $this->volume($target); - $enc = false; - - if (!$volume || ($file = $volume->file($target)) == false) { - return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND)); - } - - if (($content = $volume->getContents($target)) === false) { - return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error())); - } - - $mime = isset($file['mime'])? $file['mime'] : ''; - if ($mime && (strtolower(substr($mime, 0, 4)) === 'text' || in_array(strtolower($mime), self::$textMimes))) { - $enc = ''; - if ($content !== '') { - if (! $args['conv'] || $args['conv'] == '1') { - // detect encoding - if (function_exists('mb_detect_encoding')) { - if ($enc = mb_detect_encoding($content , mb_detect_order(), true)) { - $encu = strtoupper($enc); - if ($encu === 'UTF-8' || $encu === 'ASCII') { - $enc = ''; - } - } else { - $enc = 'unknown'; - } - } else if (! preg_match('//u', $content)) { - $enc = 'unknown'; - } - if ($enc === 'unknown') { - $enc = $volume->getOption('encoding'); - if (! $enc || strtoupper($enc) === 'UTF-8') { - $enc = 'unknown'; - } - } - if ($enc && $enc !== 'unknown') { - $utf8 = iconv($enc, 'UTF-8', $content); - if ($utf8 === false && function_exists('mb_convert_encoding')) { - $utf8 = mb_convert_encoding($content, 'UTF-8', $enc); - if (mb_convert_encoding($utf8, $enc, 'UTF-8') !== $content) { - $enc = 'unknown'; - } - } else { - if ($utf8 === false || iconv('UTF-8', $enc, $utf8) !== $content) { - $enc = 'unknown'; - } - } - if ($enc !== 'unknown') { - $content = $utf8; - } - } - if ($enc) { - if ($args['conv'] == '1') { - $args['conv'] = ''; - if ($enc === 'unknown') { - $content = false; - } - } else if ($enc === 'unknown') { - return array('doconv' => $enc); - } - } - if ($args['conv'] == '1') { - $args['conv'] = ''; - } - } - if ($args['conv']) { - $enc = $args['conv']; - if (strtoupper($enc) !== 'UTF-8') { - $_content = $content; - $content = iconv($enc, 'UTF-8', $content); - if ($content === false && function_exists('mb_convert_encoding')) { - $content = mb_convert_encoding($_content, 'UTF-8', $enc); - } - } else { - $enc = ''; - } - } - } - } else { - $content = 'data:' . ($mime? $mime : 'application/octet-stream') . ';base64,'.base64_encode($content); - } - - if ($enc !== false) { - $json = false; - if ($content !== false) { - $json = json_encode($content); - } - if ($content === false || $json === false || strlen($json) < strlen($content)) { - return array('error' => $this->error(self::ERROR_CONV_UTF8, self::ERROR_NOT_UTF8_CONTENT, $volume->path($target))); - } - } - - $res = array( - 'header' => array( - 'Content-Type: application/json' - ), - 'content' => $content - ); - - // add cache control headers - if ($cacheHeaders = $volume->getOption('cacheHeaders')) { - $res['header'] = array_merge($res['header'], $cacheHeaders); - } - - if ($enc) { - $res['encoding'] = $enc; - } - return $res; - } - - /** - * Save content into text file - * - * @param $args - * @return array - * @author Dmitry (dio) Levashov - */ - protected function put($args) { - $target = $args['target']; - $encoding = isset($args['encoding'])? $args['encoding'] : ''; - - if (($volume = $this->volume($target)) == false - || ($file = $volume->file($target)) == false) { - return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND)); - } - - $this->itemLock($target); - - if ($encoding === 'scheme') { - if (preg_match('~^https?://~i', $args['content'])) { - /** @var resource $fp */ - $fp = $this->get_remote_contents($args['content'], 30, 5, 'Mozilla/5.0', $volume->tmpfile()); - if (! $fp) { - return array('error' => self::ERROR_SAVE, $args['content'], self::ERROR_FILE_NOT_FOUND); - } - $fmeta = stream_get_meta_data($fp); - $mime = $this->detectMimeType($fmeta['uri']); - if ($mime === 'unknown') { - $mime = 'application/octet-stream'; - } - $mime = $volume->mimeTypeNormalize($mime, $file['name']); - $args['content'] = 'data:'.$mime.';base64,'.base64_encode(file_get_contents($fmeta['uri'])); - } - $encoding = ''; - $args['content'] = "\0" . $args['content']; - } else if ($encoding === 'hash') { - $_hash = $args['content']; - if ($_src = $this->getVolume($_hash)) { - if ($_file = $_src->file($_hash)) { - if ($_data = $_src->getContents($_hash)) { - $args['content'] = 'data:'.$file['mime'].';base64,'.base64_encode($_data); - } - } - } - $encoding = ''; - $args['content'] = "\0" . $args['content']; - } - if ($encoding) { - $content = iconv('UTF-8', $encoding, $args['content']); - if ($content === false && function_exists('mb_detect_encoding')) { - $content = mb_convert_encoding($args['content'], $encoding, 'UTF-8'); - } - if ($content !== false) { - $args['content'] = $content; - } - } - if (($file = $volume->putContents($target, $args['content'])) == false) { - return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error())); - } - - return array('changed' => array($file)); - } - - /** - * Extract files from archive - * - * @param array $args command arguments - * @return array - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - **/ - protected function extract($args) { - $target = $args['target']; - $makedir = isset($args['makedir'])? (bool)$args['makedir'] : null; - - if (($volume = $this->volume($target)) == false - || ($file = $volume->file($target)) == false) { - return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND)); - } - - $res = array(); - if ($file = $volume->extract($target, $makedir)) { - $res['added'] = isset($file['read'])? array($file) : $file; - if ($err = $volume->error()) { - $res['warning'] = $err; - } - } else { - $res['error'] = $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()); - } - return $res; - } - - /** - * Create archive - * - * @param array $args command arguments - * @return array - * @throws Exception - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - */ - protected function archive($args) { - $targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array(); - $name = isset($args['name'])? $args['name'] : ''; - - if (($volume = $this->volume($targets[0])) == false) { - return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND); - } - - foreach($targets as $target) { - $this->itemLock($target); - } - - return ($file = $volume->archive($targets, $args['type'], $name)) - ? array('added' => array($file)) - : array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error())); - } - - /** - * Search files - * - * @param array $args command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry Levashov - */ - protected function search($args) { - $q = trim($args['q']); - $mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array(); - $target = !empty($args['target'])? $args['target'] : null; - $type = !empty($args['type'])? $args['type'] : null; - $result = array(); - $errors = array(); - - if ($target) { - if ($volume = $this->volume($target)) { - $result = $volume->search($q, $mimes, $target, $type); - $errors = array_merge($errors, $volume->error()); - } - } else { - foreach ($this->volumes as $volume) { - $result = array_merge($result, $volume->search($q, $mimes, null, $type)); - $errors = array_merge($errors, $volume->error()); - } - } - - $result = array('files' => $result); - if ($errors) { - $result['warning'] = $errors; - } - return $result; - } - - /** - * Return file info (used by client "places" ui) - * - * @param array $args command arguments - * @return array - * @throws elFinderAbortException - * @author Dmitry Levashov - */ - protected function info($args) { - $files = array(); - $compare = null; - // long polling mode - if ($args['compare'] && count($args['targets']) === 1) { - $compare = intval($args['compare']); - $hash = $args['targets'][0]; - if ($volume = $this->volume($hash)) { - $standby = (int)$volume->getOption('plStandby'); - $_compare = false; - if (($syncCheckFunc = $volume->getOption('syncCheckFunc')) && is_callable($syncCheckFunc)) { - $_compare = call_user_func_array($syncCheckFunc, array($volume->realpath($hash), $standby, $compare, $volume, $this)); - } - if ($_compare !== false) { - $compare = $_compare; - } else { - $sleep = max(1, (int)$volume->getOption('tsPlSleep')); - $limit = max(1, $standby / $sleep) + 1; - do { - elFinder::extendTimeLimit(30 + $sleep); - $volume->clearstatcache(); - if (($info = $volume->file($hash)) != false) { - if ($info['ts'] != $compare) { - $compare = $info['ts']; - break; - } - } else { - $compare = 0; - break; - } - if (--$limit) { - sleep($sleep); - } - } while($limit); - } - } - } else { - foreach ($args['targets'] as $hash) { - elFinder::checkAborted(); - if (($volume = $this->volume($hash)) != false - && ($info = $volume->file($hash)) != false) { - $info['path'] = $volume->path($hash); - $files[] = $info; - } - } - } - - $result = array('files' => $files); - if (!is_null($compare)) { - $result['compare'] = strval($compare); - } - return $result; - } - - /** - * Return image dimensions - * - * @param array $args command arguments - * @return array - * @throws ImagickException - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function dim($args) { - $res = array(); - $target = $args['target']; - - if (($volume = $this->volume($target)) != false) { - if ($dim = $volume->dimensions($target, $args)) { - if (is_array($dim) && isset($dim['dim'])) { - $res = $dim; - } else { - $res = array('dim' => $dim); - if ($subImgLink = $volume->getSubstituteImgLink($target, explode('x', $dim))) { - $res['url'] = $subImgLink; - } - } - } - } - - return $res; - } - - /** - * Resize image - * - * @param array command arguments - * @return array - * @throws ImagickException - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin - */ - protected function resize($args) { - $target = $args['target']; - $width = (int)$args['width']; - $height = (int)$args['height']; - $x = (int)$args['x']; - $y = (int)$args['y']; - $mode = $args['mode']; - $bg = $args['bg']; - $degree = (int)$args['degree']; - $quality= (int)$args['quality']; - - if (($volume = $this->volume($target)) == false - || ($file = $volume->file($target)) == false) { - return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND)); - } - - if ($mode !== 'rotate' && ($width < 1 || $height < 1)) { - return array('error' => $this->error(self::ERROR_RESIZESIZE)); - } - return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree, $quality)) - ? (!empty($file['losslessRotate'])? $file : array('changed' => array($file))) - : array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error())); - } - - /** - * Return content URL - * - * @param array $args command arguments - * @return array - * @author Naoki Sawada - **/ - protected function url($args) { - $target = $args['target']; - $options = isset($args['options'])? $args['options'] : array(); - if (($volume = $this->volume($target)) != false) { - if (! $volume->commandDisabled('url')) { - $url = $volume->getContentUrl($target, $options); - return $url ? array('url' => $url) : array(); - } - } - return array(); - } - - /** - * Output callback result with JavaScript that control elFinder - * or HTTP redirect to callbackWindowURL - * - * @param array command arguments - * @throws elFinderAbortException - * @author Naoki Sawada - */ - protected function callback($args) { - $checkReg = '/[^a-zA-Z0-9;._-]/'; - $node = (isset($args['node']) && !preg_match($checkReg, $args['node']))? $args['node'] : ''; - $json = (isset($args['json']) && json_decode($args['json']))? $args['json'] : '{}'; - $bind = (isset($args['bind']) && !preg_match($checkReg, $args['bind']))? $args['bind'] : ''; - $done = (!empty($args['done'])); - - while( ob_get_level() ) { - if (! ob_end_clean()) { - break; - } - } - - if ($done || ! $this->callbackWindowURL) { - $script = ''; - if ($node) { - if ($bind) { - $trigger = 'elf.trigger(\''.$bind.'\', data);'; - $triggerdone = 'elf.trigger(\''.$bind.'done\');'; - $triggerfail = 'elf.trigger(\''.$bind.'fail\', data);'; - } else { - $trigger = $triggerdone = $triggerfail = ''; - } - $script .= ' +class elFinder +{ + + /** + * API version number + * + * @var float + **/ + protected static $ApiVersion = 2.1; + + /** + * API version number + * + * @deprecated + * @var string + **/ + protected $version; + + /** + * API revision that this connector supports all functions + * + * @var integer + */ + protected static $ApiRevision = 44; + + /** + * Storages (root dirs) + * + * @var array + **/ + protected $volumes = array(); + + /** + * elFinder instance + * + * @var object + */ + public static $instance = null; + + /** + * Current request args + * + * @var array + */ + public static $currentArgs = array(); + + /** + * Network mount drivers + * + * @var array + */ + public static $netDrivers = array(); + + /** + * elFinder global locale + * + * @var string + */ + public static $locale = ''; + + /** + * elFinderVolumeDriver default mime.type file path + * + * @var string + */ + public static $defaultMimefile = ''; + + /** + * A file save destination path when a temporary content URL is required + * on a network volume or the like + * It can be overwritten by volume route setting + * + * @var string + */ + public static $tmpLinkPath = ''; + + /** + * A file save destination URL when a temporary content URL is required + * on a network volume or the like + * It can be overwritten by volume route setting + * + * @var string + */ + public static $tmpLinkUrl = ''; + + /** + * Temporary content URL lifetime (seconds) + * + * @var integer + */ + public static $tmpLinkLifeTime = 3600; + + /** + * MIME type list handled as a text file + * + * @var array + */ + public static $textMimes = array( + 'application/dash+xml', + 'application/docbook+xml', + 'application/javascript', + 'application/json', + 'application/plt', + 'application/sat', + 'application/sql', + 'application/step', + 'application/vnd.hp-hpgl', + 'application/x-awk', + 'application/x-config', + 'application/x-csh', + 'application/x-empty', + 'application/x-mpegurl', + 'application/x-perl', + 'application/x-php', + 'application/x-web-config', + 'application/xhtml+xml', + 'application/xml', + 'audio/x-mp3-playlist', + 'image/cgm', + 'image/svg+xml', + 'image/vnd.dxf', + 'model/iges' + ); + + /** + * Maximum memory size to be extended during GD processing + * (0: not expanded, -1: unlimited or memory size notation) + * + * @var integer|string + */ + public static $memoryLimitGD = 0; + + /** + * Path of current request flag file for abort check + * + * @var string + */ + protected static $abortCheckFile = null; + + /** + * elFinder session wrapper object + * + * @var elFinderSessionInterface + */ + protected $session; + + /** + * elFinder global sessionCacheKey + * + * @deprecated + * @var string + */ + public static $sessionCacheKey = ''; + + /** + * Is session closed + * + * @deprecated + * @var bool + */ + private static $sessionClosed = false; + + /** + * elFinder base64encodeSessionData + * elFinder save session data as `UTF-8` + * If the session storage mechanism of the system does not allow `UTF-8` + * And it must be `true` option 'base64encodeSessionData' of elFinder + * WARNING: When enabling this option, if saving the data passed from the user directly to the session variable, + * it make vulnerable to the object injection attack, so use it carefully. + * see https://github.com/Studio-42/elFinder/issues/2345 + * + * @var bool + */ + protected static $base64encodeSessionData = false; + + /** + * elFinder common tempraly path + * + * @var string + * @default "./.tmp" or sys_get_temp_dir() + **/ + protected static $commonTempPath = ''; + + /** + * Connection flag files path that connection check of current request + * + * @var string + * @default value of $commonTempPath + */ + protected static $connectionFlagsPath = ''; + + /** + * Additional volume root options for network mounting volume + * + * @var array + */ + protected $optionsNetVolumes = array(); + + /** + * Session key of net mount volumes + * + * @deprecated + * @var string + */ + protected $netVolumesSessionKey = ''; + + /** + * Mounted volumes count + * Required to create unique volume id + * + * @var int + **/ + public static $volumesCnt = 1; + + /** + * Default root (storage) + * + * @var elFinderVolumeDriver + **/ + protected $default = null; + + /** + * Commands and required arguments list + * + * @var array + **/ + protected $commands = array( + 'open' => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false, 'compare' => false), + 'ls' => array('target' => true, 'mimes' => false, 'intersect' => false), + 'tree' => array('target' => true), + 'parents' => array('target' => true, 'until' => false), + 'tmb' => array('targets' => true), + 'file' => array('target' => true, 'download' => false, 'cpath' => false, 'onetime' => false), + 'zipdl' => array('targets' => true, 'download' => false), + 'size' => array('targets' => true), + 'mkdir' => array('target' => true, 'name' => false, 'dirs' => false), + 'mkfile' => array('target' => true, 'name' => true, 'mimes' => false), + 'rm' => array('targets' => true), + 'rename' => array('target' => true, 'name' => true, 'mimes' => false, 'targets' => false, 'q' => false), + 'duplicate' => array('targets' => true, 'suffix' => false), + 'paste' => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false, 'renames' => false, 'hashes' => false, 'suffix' => false), + 'upload' => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false, 'upload' => false, 'name' => false, 'upload_path' => false, 'chunk' => false, 'cid' => false, 'node' => false, 'renames' => false, 'hashes' => false, 'suffix' => false, 'mtime' => false, 'overwrite' => false, 'contentSaveId' => false), + 'get' => array('target' => true, 'conv' => false), + 'put' => array('target' => true, 'content' => '', 'mimes' => false, 'encoding' => false), + 'archive' => array('targets' => true, 'type' => true, 'mimes' => false, 'name' => false), + 'extract' => array('target' => true, 'mimes' => false, 'makedir' => false), + 'search' => array('q' => true, 'mimes' => false, 'target' => false, 'type' => false), + 'info' => array('targets' => true, 'compare' => false), + 'dim' => array('target' => true, 'substitute' => false), + 'resize' => array('target' => true, 'width' => false, 'height' => false, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false, 'quality' => false, 'bg' => false), + 'netmount' => array('protocol' => true, 'host' => true, 'path' => false, 'port' => false, 'user' => false, 'pass' => false, 'alias' => false, 'options' => false), + 'url' => array('target' => true, 'options' => false), + 'callback' => array('node' => true, 'json' => false, 'bind' => false, 'done' => false), + 'chmod' => array('targets' => true, 'mode' => true), + 'subdirs' => array('targets' => true), + 'abort' => array('id' => true), + 'editor' => array('name' => true, 'method' => true, 'args' => false) + ); + + /** + * Plugins instance + * + * @var array + **/ + protected $plugins = array(); + + /** + * Commands listeners + * + * @var array + **/ + protected $listeners = array(); + + /** + * script work time for debug + * + * @var string + **/ + protected $time = 0; + /** + * Is elFinder init correctly? + * + * @var bool + **/ + protected $loaded = false; + /** + * Send debug to client? + * + * @var string + **/ + protected $debug = false; + + /** + * Call `session_write_close()` before exec command? + * + * @var bool + */ + protected $sessionCloseEarlier = true; + + /** + * SESSION use commands @see __construct() + * + * @var array + */ + protected $sessionUseCmds = array(); + + /** + * session expires timeout + * + * @var int + **/ + protected $timeout = 0; + + /** + * Temp dir path for Upload + * + * @var string + */ + protected $uploadTempPath = ''; + + /** + * Max allowed archive files size (0 - no limit) + * + * @var integer + */ + protected $maxArcFilesSize = 0; + + /** + * undocumented class variable + * + * @var string + **/ + protected $uploadDebug = ''; + + /** + * Max allowed numbar of targets (0 - no limit) + * + * @var integer + */ + public $maxTargets = 1000; + + /** + * Errors from PHP + * + * @var array + **/ + public static $phpErrors = array(); + + /** + * Errors from not mounted volumes + * + * @var array + **/ + public $mountErrors = array(); + + + /** + * Archivers cache + * + * @var array + */ + public static $archivers = array(); + + /** + * URL for callback output window for CORS + * redirect to this URL when callback output + * + * @var string URL + */ + protected $callbackWindowURL = ''; + + /** + * hash of items to unlock on command completion + * + * @var array hashes + */ + protected $autoUnlocks = array(); + + /** + * Item locking expiration (seconds) + * Default: 3600 secs + * + * @var integer + */ + protected $itemLockExpire = 3600; + + /** + * Additional request querys + * + * @var array|null + */ + protected $customData = null; + + /** + * Ids to remove of session var "urlContentSaveIds" for contents uploading by URL + * + * @var array + */ + protected $removeContentSaveIds = array(); + + /** + * Flag of throw Error on exec() + * + * @var boolean + */ + protected $throwErrorOnExec = false; + + // Errors messages + const ERROR_UNKNOWN = 'errUnknown'; + const ERROR_UNKNOWN_CMD = 'errUnknownCmd'; + const ERROR_CONF = 'errConf'; + const ERROR_CONF_NO_JSON = 'errJSON'; + const ERROR_CONF_NO_VOL = 'errNoVolumes'; + const ERROR_INV_PARAMS = 'errCmdParams'; + const ERROR_OPEN = 'errOpen'; + const ERROR_DIR_NOT_FOUND = 'errFolderNotFound'; + const ERROR_FILE_NOT_FOUND = 'errFileNotFound'; // 'File not found.' + const ERROR_TRGDIR_NOT_FOUND = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.' + const ERROR_NOT_DIR = 'errNotFolder'; + const ERROR_NOT_FILE = 'errNotFile'; + const ERROR_PERM_DENIED = 'errPerm'; + const ERROR_LOCKED = 'errLocked'; // '"$1" is locked and can not be renamed, moved or removed.' + const ERROR_EXISTS = 'errExists'; // 'File named "$1" already exists.' + const ERROR_INVALID_NAME = 'errInvName'; // 'Invalid file name.' + const ERROR_INVALID_DIRNAME = 'errInvDirname'; // 'Invalid folder name.' + const ERROR_MKDIR = 'errMkdir'; + const ERROR_MKFILE = 'errMkfile'; + const ERROR_RENAME = 'errRename'; + const ERROR_COPY = 'errCopy'; + const ERROR_MOVE = 'errMove'; + const ERROR_COPY_FROM = 'errCopyFrom'; + const ERROR_COPY_TO = 'errCopyTo'; + const ERROR_COPY_ITSELF = 'errCopyInItself'; + const ERROR_REPLACE = 'errReplace'; // 'Unable to replace "$1".' + const ERROR_RM = 'errRm'; // 'Unable to remove "$1".' + const ERROR_RM_SRC = 'errRmSrc'; // 'Unable remove source file(s)' + const ERROR_MKOUTLINK = 'errMkOutLink'; // 'Unable to create a link to outside the volume root.' + const ERROR_UPLOAD = 'errUpload'; // 'Upload error.' + const ERROR_UPLOAD_FILE = 'errUploadFile'; // 'Unable to upload "$1".' + const ERROR_UPLOAD_NO_FILES = 'errUploadNoFiles'; // 'No files found for upload.' + const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize'; // 'Data exceeds the maximum allowed size.' + const ERROR_UPLOAD_FILE_SIZE = 'errUploadFileSize'; // 'File exceeds maximum allowed size.' + const ERROR_UPLOAD_FILE_MIME = 'errUploadMime'; // 'File type not allowed.' + const ERROR_UPLOAD_TRANSFER = 'errUploadTransfer'; // '"$1" transfer error.' + const ERROR_UPLOAD_TEMP = 'errUploadTemp'; // 'Unable to make temporary file for upload.' + const ERROR_ACCESS_DENIED = 'errAccess'; + const ERROR_NOT_REPLACE = 'errNotReplace'; // Object "$1" already exists at this location and can not be replaced with object of another type. + const ERROR_SAVE = 'errSave'; + const ERROR_EXTRACT = 'errExtract'; + const ERROR_ARCHIVE = 'errArchive'; + const ERROR_NOT_ARCHIVE = 'errNoArchive'; + const ERROR_ARCHIVE_TYPE = 'errArcType'; + const ERROR_ARC_SYMLINKS = 'errArcSymlinks'; + const ERROR_ARC_MAXSIZE = 'errArcMaxSize'; + const ERROR_RESIZE = 'errResize'; + const ERROR_RESIZESIZE = 'errResizeSize'; + const ERROR_UNSUPPORT_TYPE = 'errUsupportType'; + const ERROR_CONV_UTF8 = 'errConvUTF8'; + const ERROR_NOT_UTF8_CONTENT = 'errNotUTF8Content'; + const ERROR_NETMOUNT = 'errNetMount'; + const ERROR_NETUNMOUNT = 'errNetUnMount'; + const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver'; + const ERROR_NETMOUNT_FAILED = 'errNetMountFailed'; + const ERROR_SESSION_EXPIRES = 'errSessionExpires'; + const ERROR_CREATING_TEMP_DIR = 'errCreatingTempDir'; + const ERROR_FTP_DOWNLOAD_FILE = 'errFtpDownloadFile'; + const ERROR_FTP_UPLOAD_FILE = 'errFtpUploadFile'; + const ERROR_FTP_MKDIR = 'errFtpMkdir'; + const ERROR_ARCHIVE_EXEC = 'errArchiveExec'; + const ERROR_EXTRACT_EXEC = 'errExtractExec'; + const ERROR_SEARCH_TIMEOUT = 'errSearchTimeout'; // 'Timed out while searching "$1". Search result is partial.' + const ERROR_REAUTH_REQUIRE = 'errReauthRequire'; // 'Re-authorization is required.' + const ERROR_MAX_TARGTES = 'errMaxTargets'; // 'Max number of selectable items is $1.' + + /** + * Constructor + * + * @param array elFinder and roots configurations + * + * @author Dmitry (dio) Levashov + */ + public function __construct($opts) + { + // set default_charset + if (version_compare(PHP_VERSION, '5.6', '>=')) { + if (($_val = ini_get('iconv.internal_encoding')) && strtoupper($_val) !== 'UTF-8') { + ini_set('iconv.internal_encoding', ''); + } + if (($_val = ini_get('mbstring.internal_encoding')) && strtoupper($_val) !== 'UTF-8') { + ini_set('mbstring.internal_encoding', ''); + } + if (($_val = ini_get('internal_encoding')) && strtoupper($_val) !== 'UTF-8') { + ini_set('internal_encoding', ''); + } + } else { + if (function_exists('iconv_set_encoding') && strtoupper(iconv_get_encoding('internal_encoding')) !== 'UTF-8') { + iconv_set_encoding('internal_encoding', 'UTF-8'); + } + if (function_exists('mb_internal_encoding') && strtoupper(mb_internal_encoding()) !== 'UTF-8') { + mb_internal_encoding('UTF-8'); + } + } + ini_set('default_charset', 'UTF-8'); + + // define accept constant of server commands path + !defined('ELFINDER_TAR_PATH') && define('ELFINDER_TAR_PATH', 'tar'); + !defined('ELFINDER_GZIP_PATH') && define('ELFINDER_GZIP_PATH', 'gzip'); + !defined('ELFINDER_BZIP2_PATH') && define('ELFINDER_BZIP2_PATH', 'bzip2'); + !defined('ELFINDER_XZ_PATH') && define('ELFINDER_XZ_PATH', 'xz'); + !defined('ELFINDER_ZIP_PATH') && define('ELFINDER_ZIP_PATH', 'zip'); + !defined('ELFINDER_UNZIP_PATH') && define('ELFINDER_UNZIP_PATH', 'unzip'); + !defined('ELFINDER_RAR_PATH') && define('ELFINDER_RAR_PATH', 'rar'); + !defined('ELFINDER_UNRAR_PATH') && define('ELFINDER_UNRAR_PATH', 'unrar'); + !defined('ELFINDER_7Z_PATH') && define('ELFINDER_7Z_PATH', (substr(PHP_OS, 0, 3) === 'WIN') ? '7z' : '7za'); + !defined('ELFINDER_CONVERT_PATH') && define('ELFINDER_CONVERT_PATH', 'convert'); + !defined('ELFINDER_IDENTIFY_PATH') && define('ELFINDER_IDENTIFY_PATH', 'identify'); + !defined('ELFINDER_EXIFTRAN_PATH') && define('ELFINDER_EXIFTRAN_PATH', 'exiftran'); + !defined('ELFINDER_JPEGTRAN_PATH') && define('ELFINDER_JPEGTRAN_PATH', 'jpegtran'); + !defined('ELFINDER_FFMPEG_PATH') && define('ELFINDER_FFMPEG_PATH', 'ffmpeg'); + + !defined('ELFINDER_DISABLE_ZIPEDITOR') && define('ELFINDER_DISABLE_ZIPEDITOR', false); + + // enable(true)/disable(false) handling postscript on ImageMagick + // Should be `false` as long as there is a Ghostscript vulnerability + // see https://artifex.com/news/ghostscript-security-resolved/ + !defined('ELFINDER_IMAGEMAGICK_PS') && define('ELFINDER_IMAGEMAGICK_PS', false); + + // for backward compat + $this->version = (string)self::$ApiVersion; + + // set error handler of WARNING, NOTICE + $errLevel = E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_STRICT | E_RECOVERABLE_ERROR; + if (defined('E_DEPRECATED')) { + $errLevel |= E_DEPRECATED | E_USER_DEPRECATED; + } + set_error_handler('elFinder::phpErrorHandler', $errLevel); + + // Associative array of files to delete at the end of script: ['temp file path' => true] + $GLOBALS['elFinderTempFiles'] = array(); + // regist Shutdown function + register_shutdown_function(array('elFinder', 'onShutdown')); + + // convert PATH_INFO to GET query + if (!empty($_SERVER['PATH_INFO'])) { + $_ps = explode('/', trim($_SERVER['PATH_INFO'], '/')); + if (!isset($_GET['cmd'])) { + $_cmd = $_ps[0]; + if (isset($this->commands[$_cmd])) { + $_GET['cmd'] = $_cmd; + $_i = 1; + foreach (array_keys($this->commands[$_cmd]) as $_k) { + if (isset($_ps[$_i])) { + if (!isset($_GET[$_k])) { + $_GET[$_k] = $_ps[$_i]; + } + } else { + break; + } + } + } + } + } + + // set elFinder instance + elFinder::$instance = $this; + + // setup debug mode + $this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false); + if ($this->debug) { + error_reporting(defined('ELFINDER_DEBUG_ERRORLEVEL') ? ELFINDER_DEBUG_ERRORLEVEL : -1); + ini_set('display_errors', '1'); + // clear output buffer and stop output filters + while (ob_get_level() && ob_end_clean()) { + } + } + + if (!interface_exists('elFinderSessionInterface')) { + include_once dirname(__FILE__) . '/elFinderSessionInterface.php'; + } + + // session handler + if (!empty($opts['session']) && $opts['session'] instanceof elFinderSessionInterface) { + $this->session = $opts['session']; + } else { + $sessionOpts = array( + 'base64encode' => !empty($opts['base64encodeSessionData']), + 'keys' => array( + 'default' => !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches', + 'netvolume' => !empty($opts['netVolumesSessionKey']) ? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes' + ) + ); + if (!class_exists('elFinderSession')) { + include_once dirname(__FILE__) . '/elFinderSession.php'; + } + $this->session = new elFinderSession($sessionOpts); + } + // try session start | restart + $this->session->start(); + + // 'netmount' added to handle requests synchronously on unmount + $sessionUseCmds = array('netmount'); + if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) { + $sessionUseCmds = array_merge($sessionUseCmds, $opts['sessionUseCmds']); + } + + // set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart" + if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) { + self::$volumesCnt = $volumesCntStart; + } + + $this->time = $this->utime(); + $this->sessionCloseEarlier = isset($opts['sessionCloseEarlier']) ? (bool)$opts['sessionCloseEarlier'] : true; + $this->sessionUseCmds = array_flip($sessionUseCmds); + $this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0); + $this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : ''); + $this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : ''); + $this->maxTargets = (isset($opts['maxTargets']) ? intval($opts['maxTargets']) : $this->maxTargets); + elFinder::$commonTempPath = (isset($opts['commonTempPath']) ? realpath($opts['commonTempPath']) : dirname(__FILE__) . '/.tmp'); + if (!is_writable(elFinder::$commonTempPath)) { + elFinder::$commonTempPath = sys_get_temp_dir(); + if (!is_writable(elFinder::$commonTempPath)) { + elFinder::$commonTempPath = ''; + } + } + if (isset($opts['connectionFlagsPath']) && is_writable($opts['connectionFlagsPath'] = realpath($opts['connectionFlagsPath']))) { + elFinder::$connectionFlagsPath = $opts['connectionFlagsPath']; + } else { + elFinder::$connectionFlagsPath = elFinder::$commonTempPath; + } + + if (!empty($opts['tmpLinkPath'])) { + elFinder::$tmpLinkPath = realpath($opts['tmpLinkPath']); + } + if (!empty($opts['tmpLinkUrl'])) { + elFinder::$tmpLinkUrl = $opts['tmpLinkUrl']; + } + if (!empty($opts['tmpLinkLifeTime'])) { + elFinder::$tmpLinkLifeTime = $opts['tmpLinkLifeTime']; + } + if (!empty($opts['textMimes']) && is_array($opts['textMimes'])) { + elfinder::$textMimes = $opts['textMimes']; + } + $this->maxArcFilesSize = isset($opts['maxArcFilesSize']) ? intval($opts['maxArcFilesSize']) : 0; + $this->optionsNetVolumes = (isset($opts['optionsNetVolumes']) && is_array($opts['optionsNetVolumes'])) ? $opts['optionsNetVolumes'] : array(); + if (isset($opts['itemLockExpire'])) { + $this->itemLockExpire = intval($opts['itemLockExpire']); + } + + // deprecated settings + $this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey']) ? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes'; + self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches'; + + // check session cache + $_optsMD5 = md5(json_encode($opts['roots'])); + if ($this->session->get('_optsMD5') !== $_optsMD5) { + $this->session->set('_optsMD5', $_optsMD5); + } + + // setlocale and global locale regists to elFinder::locale + self::$locale = !empty($opts['locale']) ? $opts['locale'] : (substr(PHP_OS, 0, 3) === 'WIN' ? 'C' : 'en_US.UTF-8'); + if (false === setlocale(LC_ALL, self::$locale)) { + self::$locale = setlocale(LC_ALL, '0'); + } + + // set defaultMimefile + elFinder::$defaultMimefile = isset($opts['defaultMimefile']) ? $opts['defaultMimefile'] : ''; + + // set memoryLimitGD + elFinder::$memoryLimitGD = isset($opts['memoryLimitGD']) ? $opts['memoryLimitGD'] : 0; + + // set flag of throwErrorOnExec + // `true` need `try{}` block for `$connector->run();` + $this->throwErrorOnExec = !empty($opts['throwErrorOnExec']); + + // set archivers + elFinder::$archivers = isset($opts['archivers']) && is_array($opts['archivers']) ? $opts['archivers'] : array(); + + // bind events listeners + if (!empty($opts['bind']) && is_array($opts['bind'])) { + $_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET; + $_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : ''; + foreach ($opts['bind'] as $cmd => $handlers) { + $doRegist = (strpos($cmd, '*') !== false); + if (!$doRegist) { + $doRegist = ($_reqCmd && in_array($_reqCmd, array_map('self::getCmdOfBind', explode(' ', $cmd)))); + } + if ($doRegist) { + // for backward compatibility + if (!is_array($handlers)) { + $handlers = array($handlers); + } else { + if (count($handlers) === 2 && is_callable($handlers)) { + $handlers = array($handlers); + } + } + foreach ($handlers as $handler) { + if ($handler) { + if (is_string($handler) && strpos($handler, '.')) { + list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, ''); + if (strcasecmp($_domain, 'plugin') === 0) { + if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name]) ? $opts['plugin'][$_name] : array()) + and method_exists($plugin, $_method)) { + $this->bind($cmd, array($plugin, $_method)); + } + } + } else { + $this->bind($cmd, $handler); + } + } + } + } + } + } + + if (!isset($opts['roots']) || !is_array($opts['roots'])) { + $opts['roots'] = array(); + } + + // try to enable elFinderVolumeFlysystemZipArchiveNetmount to zip editing + if (empty(elFinder::$netDrivers['ziparchive'])) { + elFinder::$netDrivers['ziparchive'] = 'FlysystemZipArchiveNetmount'; + } + + // check for net volumes stored in session + $netVolumes = $this->getNetVolumes(); + foreach ($netVolumes as $key => $root) { + if (!isset($root['id'])) { + // given fixed unique id + if (!$root['id'] = $this->getNetVolumeUniqueId($netVolumes)) { + $this->mountErrors[] = 'Netmount Driver "' . $root['driver'] . '" : Could\'t given volume id.'; + continue; + } + } + $opts['roots'][$key] = $root; + } + + // "mount" volumes + foreach ($opts['roots'] as $i => $o) { + $class = 'elFinderVolume' . (isset($o['driver']) ? $o['driver'] : ''); + + if (class_exists($class)) { + /* @var elFinderVolumeDriver $volume */ + $volume = new $class(); + + try { + if ($this->maxArcFilesSize && (empty($o['maxArcFilesSize']) || $this->maxArcFilesSize < $o['maxArcFilesSize'])) { + $o['maxArcFilesSize'] = $this->maxArcFilesSize; + } + // pass session handler + $volume->setSession($this->session); + if ($volume->mount($o)) { + // unique volume id (ends on "_") - used as prefix to files hash + $id = $volume->id(); + + $this->volumes[$id] = $volume; + if ((!$this->default || $volume->root() !== $volume->defaultPath()) && $volume->isReadable()) { + $this->default = $this->volumes[$id]; + } + } else { + $this->removeNetVolume($i, $volume); + $this->mountErrors[] = 'Driver "' . $class . '" : ' . implode(' ', $volume->error()); + } + } catch (Exception $e) { + $this->removeNetVolume($i, $volume); + $this->mountErrors[] = 'Driver "' . $class . '" : ' . $e->getMessage(); + } + } else { + $this->removeNetVolume($i, null); + $this->mountErrors[] = 'Driver "' . $class . '" does not exist'; + } + } + + // if at least one readable volume - ii desu >_< + $this->loaded = !empty($this->default); + + // restore error handler for now + restore_error_handler(); + } + + /** + * Return elFinder session wrapper instance + * + * @return elFinderSessionInterface + **/ + public function getSession() + { + return $this->session; + } + + /** + * Return true if fm init correctly + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + public function loaded() + { + return $this->loaded; + } + + /** + * Return version (api) number + * + * @return string + * @author Dmitry (dio) Levashov + **/ + public function version() + { + return self::$ApiVersion; + } + + /** + * Return revision (api) number + * + * @return string + * @author Naoki Sawada + **/ + public function revision() + { + return self::$ApiRevision; + } + + /** + * Add handler to elFinder command + * + * @param string command name + * @param string|array callback name or array(object, method) + * + * @return elFinder + * @author Dmitry (dio) Levashov + **/ + public function bind($cmd, $handler) + { + $allCmds = array_keys($this->commands); + $cmds = array(); + foreach (explode(' ', $cmd) as $_cmd) { + if ($_cmd !== '') { + if ($all = strpos($_cmd, '*') !== false) { + list(, $sub) = array_pad(explode('.', $_cmd), 2, ''); + if ($sub) { + $sub = str_replace('\'', '\\\'', $sub); + $subs = array_fill(0, count($allCmds), $sub); + $cmds = array_merge($cmds, array_map(array('elFinder', 'addSubToBindName'), $allCmds, $subs)); + } else { + $cmds = array_merge($cmds, $allCmds); + } + } else { + $cmds[] = $_cmd; + } + } + } + $cmds = array_unique($cmds); + + foreach ($cmds as $cmd) { + if (!isset($this->listeners[$cmd])) { + $this->listeners[$cmd] = array(); + } + + if (is_callable($handler)) { + $this->listeners[$cmd][] = $handler; + } + } + + return $this; + } + + /** + * Remove event (command exec) handler + * + * @param string command name + * @param string|array callback name or array(object, method) + * + * @return elFinder + * @author Dmitry (dio) Levashov + **/ + public function unbind($cmd, $handler) + { + if (!empty($this->listeners[$cmd])) { + foreach ($this->listeners[$cmd] as $i => $h) { + if ($h === $handler) { + unset($this->listeners[$cmd][$i]); + return $this; + } + } + } + return $this; + } + + /** + * Return true if command exists + * + * @param string command name + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + public function commandExists($cmd) + { + return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd); + } + + /** + * Return root - file's owner (public func of volume()) + * + * @param string file hash + * + * @return elFinderVolumeDriver + * @author Naoki Sawada + */ + public function getVolume($hash) + { + return $this->volume($hash); + } + + /** + * Return command required arguments info + * + * @param string command name + * + * @return array + * @author Dmitry (dio) Levashov + **/ + public function commandArgsList($cmd) + { + if ($this->commandExists($cmd)) { + $list = $this->commands[$cmd]; + $list['reqid'] = false; + } else { + $list = array(); + } + return $list; + } + + private function session_expires() + { + + if (!$last = $this->session->get(':LAST_ACTIVITY')) { + $this->session->set(':LAST_ACTIVITY', time()); + return false; + } + + if (($this->timeout > 0) && (time() - $last > $this->timeout)) { + return true; + } + + $this->session->set(':LAST_ACTIVITY', time()); + return false; + } + + /** + * Exec command and return result + * + * @param string $cmd command name + * @param array $args command arguments + * + * @return array + * @throws elFinderAbortException|Exception + * @author Dmitry (dio) Levashov + **/ + public function exec($cmd, $args) + { + // set error handler of WARNING, NOTICE + set_error_handler('elFinder::phpErrorHandler', E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE); + + // set current request args + self::$currentArgs = $args; + + if (!$this->loaded) { + return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL)); + } + + if ($this->session_expires()) { + return array('error' => $this->error(self::ERROR_SESSION_EXPIRES)); + } + + if (!$this->commandExists($cmd)) { + return array('error' => $this->error(self::ERROR_UNKNOWN_CMD)); + } + + // check request id + $args['reqid'] = preg_replace('[^0-9a-fA-F]', '', !empty($args['reqid']) ? $args['reqid'] : (!empty($_SERVER['HTTP_X_ELFINDERREQID']) ? $_SERVER['HTTP_X_ELFINDERREQID'] : '')); + + // to abort this request + if ($cmd === 'abort') { + $this->abort($args); + return array('error' => 0); + } + + // make flag file and set self::$abortCheckFile + if ($args['reqid']) { + $this->abort(array('makeFile' => $args['reqid'])); + } + + if (!empty($args['mimes']) && is_array($args['mimes'])) { + foreach ($this->volumes as $id => $v) { + $this->volumes[$id]->setMimesFilter($args['mimes']); + } + } + + // regist shutdown function as fallback + register_shutdown_function(array($this, 'itemAutoUnlock')); + + // detect destination dirHash and volume + $dstVolume = false; + $dst = !empty($args['target']) ? $args['target'] : (!empty($args['dst']) ? $args['dst'] : ''); + if ($dst) { + $dstVolume = $this->volume($dst); + } else if (isset($args['targets']) && is_array($args['targets']) && isset($args['targets'][0])) { + $dst = $args['targets'][0]; + $dstVolume = $this->volume($dst); + if ($dstVolume && ($_stat = $dstVolume->file($dst)) && !empty($_stat['phash'])) { + $dst = $_stat['phash']; + } else { + $dst = ''; + } + } else if ($cmd === 'open') { + // for initial open without args `target` + $dstVolume = $this->default; + $dst = $dstVolume->defaultPath(); + } + + $result = null; + + // call pre handlers for this command + $args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd]) ? false : $this->sessionCloseEarlier; + if (!empty($this->listeners[$cmd . '.pre'])) { + foreach ($this->listeners[$cmd . '.pre'] as $handler) { + $_res = call_user_func_array($handler, array($cmd, &$args, $this, $dstVolume)); + if (is_array($_res)) { + if (!empty($_res['preventexec'])) { + $result = array('error' => true); + if ($cmd === 'upload' && !empty($args['node'])) { + $result['callback'] = array( + 'node' => $args['node'], + 'bind' => $cmd + ); + } + if (!empty($_res['results']) && is_array($_res['results'])) { + $result = array_merge($result, $_res['results']); + } + break; + } + } + } + } + + // unlock session data for multiple access + if ($this->sessionCloseEarlier && $args['sessionCloseEarlier']) { + $this->session->close(); + // deprecated property + elFinder::$sessionClosed = true; + } + + if (substr(PHP_OS, 0, 3) === 'WIN') { + // set time out + elFinder::extendTimeLimit(300); + } + + if (!is_array($result)) { + try { + $result = $this->$cmd($args); + } catch (elFinderAbortException $e) { + throw $e; + } catch (Exception $e) { + $result = array( + 'error' => htmlspecialchars($e->getMessage()), + 'sync' => true + ); + if ($this->throwErrorOnExec) { + throw $e; + } + } + } + + // check change dstDir + $changeDst = false; + if ($dst && $dstVolume && (!empty($result['added']) || !empty($result['removed']))) { + $changeDst = true; + } + + foreach ($this->volumes as $volume) { + $removed = $volume->removed(); + if (!empty($removed)) { + if (!isset($result['removed'])) { + $result['removed'] = array(); + } + $result['removed'] = array_merge($result['removed'], $removed); + if (!$changeDst && $dst && $dstVolume && $volume === $dstVolume) { + $changeDst = true; + } + } + $added = $volume->added(); + if (!empty($added)) { + if (!isset($result['added'])) { + $result['added'] = array(); + } + $result['added'] = array_merge($result['added'], $added); + if (!$changeDst && $dst && $dstVolume && $volume === $dstVolume) { + $changeDst = true; + } + } + $volume->resetResultStat(); + } + + // dstDir is changed + if ($changeDst) { + if ($dstDir = $dstVolume->dir($dst)) { + if (!isset($result['changed'])) { + $result['changed'] = array(); + } + $result['changed'][] = $dstDir; + } + } + + // call handlers for this command + if (!empty($this->listeners[$cmd])) { + foreach ($this->listeners[$cmd] as $handler) { + if (call_user_func_array($handler, array($cmd, &$result, $args, $this, $dstVolume))) { + // handler return true to force sync client after command completed + $result['sync'] = true; + } + } + } + + // replace removed files info with removed files hashes + if (!empty($result['removed'])) { + $removed = array(); + foreach ($result['removed'] as $file) { + $removed[] = $file['hash']; + } + $result['removed'] = array_unique($removed); + } + // remove hidden files and filter files by mimetypes + if (!empty($result['added'])) { + $result['added'] = $this->filter($result['added']); + } + // remove hidden files and filter files by mimetypes + if (!empty($result['changed'])) { + $result['changed'] = $this->filter($result['changed']); + } + + if ($this->debug || !empty($args['debug'])) { + $result['debug'] = array( + 'connector' => 'php', + 'phpver' => PHP_VERSION, + 'time' => $this->utime() - $this->time, + 'memory' => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage() / 1024) . 'Kb / ' : '') . ceil(memory_get_usage() / 1024) . 'Kb / ' . ini_get('memory_limit'), + 'upload' => $this->uploadDebug, + 'volumes' => array(), + 'mountErrors' => $this->mountErrors, + 'phpErrors' => elFinder::$phpErrors + ); + elFinder::$phpErrors = array(); + + foreach ($this->volumes as $id => $volume) { + $result['debug']['volumes'][] = $volume->debug(); + } + } + + // remove sesstion var 'urlContentSaveIds' + if ($this->removeContentSaveIds) { + $urlContentSaveIds = $this->session->get('urlContentSaveIds', array()); + foreach (array_keys($this->removeContentSaveIds) as $contentSaveId) { + if (isset($urlContentSaveIds[$contentSaveId])) { + unset($urlContentSaveIds[$contentSaveId]); + } + } + if ($urlContentSaveIds) { + $this->session->set('urlContentSaveIds', $urlContentSaveIds); + } else { + $this->session->remove('urlContentSaveIds'); + } + } + + foreach ($this->volumes as $volume) { + $volume->saveSessionCache(); + $volume->umount(); + } + + // unlock locked items + $this->itemAutoUnlock(); + + // custom data + if ($this->customData !== null) { + $result['customData'] = $this->customData ? json_encode($this->customData) : ''; + } + + if (!empty($result['callback'])) { + $result['callback']['json'] = json_encode($result); + $this->callback($result['callback']); + return array(); + } else { + return $result; + } + } + + /** + * Return file real path + * + * @param string $hash file hash + * + * @return string + * @author Dmitry (dio) Levashov + **/ + public function realpath($hash) + { + if (($volume = $this->volume($hash)) == false) { + return false; + } + return $volume->realpath($hash); + } + + /** + * Sets custom data(s). + * + * @param string|array $key The key or data array + * @param mixed $val The value + * + * @return self ( elFinder instance ) + */ + public function setCustomData($key, $val = null) + { + if (is_array($key)) { + foreach ($key as $k => $v) { + $this->customData[$k] = $v; + } + } else { + $this->customData[$key] = $val; + } + return $this; + } + + /** + * Removes a custom data. + * + * @param string $key The key + * + * @return self ( elFinder instance ) + */ + public function removeCustomData($key) + { + $this->customData[$key] = null; + return $this; + } + + /** + * Update sesstion value of a NetVolume option + * + * @param string $netKey + * @param string $optionKey + * @param mixed $val + */ + public function updateNetVolumeOption($netKey, $optionKey, $val) + { + $netVolumes = $this->getNetVolumes(); + if (is_string($netKey) && isset($netVolumes[$netKey]) && is_string($optionKey)) { + $netVolumes[$netKey][$optionKey] = $val; + } + } + + /** + * remove of session var "urlContentSaveIds" + * + * @param string $id + */ + public function removeUrlContentSaveId($id) + { + $this->removeContentSaveIds[$id] = true; + } + + /** + * Return network volumes config. + * + * @return array + * @author Dmitry (dio) Levashov + */ + protected function getNetVolumes() + { + if ($data = $this->session->get('netvolume', array())) { + return $data; + } + return array(); + } + + /** + * Save network volumes config. + * + * @param array $volumes volumes config + * + * @return void + * @author Dmitry (dio) Levashov + */ + protected function saveNetVolumes($volumes) + { + $this->session->set('netvolume', $volumes); + } + + /** + * Remove netmount volume + * + * @param string $key netvolume key + * @param object $volume volume driver instance + * + * @return bool + */ + protected function removeNetVolume($key, $volume) + { + $netVolumes = $this->getNetVolumes(); + $res = true; + if (is_object($volume) && method_exists($volume, 'netunmount')) { + $res = $volume->netunmount($netVolumes, $key); + $volume->clearSessionCache(); + } + if ($res) { + if (is_string($key) && isset($netVolumes[$key])) { + unset($netVolumes[$key]); + $this->saveNetVolumes($netVolumes); + return true; + } + } + return false; + } + + /** + * Get plugin instance & set to $this->plugins + * + * @param string $name Plugin name (dirctory name) + * @param array $opts Plugin options (optional) + * + * @return object | bool Plugin object instance Or false + * @author Naoki Sawada + */ + protected function getPluginInstance($name, $opts = array()) + { + $key = strtolower($name); + if (!isset($this->plugins[$key])) { + $class = 'elFinderPlugin' . $name; + // to try auto load + if (!class_exists($class)) { + $p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php'; + if (is_file($p_file)) { + include_once $p_file; + } + } + if (class_exists($class, false)) { + $this->plugins[$key] = new $class($opts); + } else { + $this->plugins[$key] = false; + } + } + return $this->plugins[$key]; + } + + /***************************************************************************/ + /* commands */ + /***************************************************************************/ + + /** + * Normalize error messages + * + * @return array + * @author Dmitry (dio) Levashov + **/ + public function error() + { + $errors = array(); + + foreach (func_get_args() as $msg) { + if (is_array($msg)) { + $errors = array_merge($errors, $msg); + } else { + $errors[] = $msg; + } + } + + return count($errors) ? $errors : array(self::ERROR_UNKNOWN); + } + + /** + * @param $args + * + * @return array + * @throws elFinderAbortException + */ + protected function netmount($args) + { + $options = array(); + $protocol = $args['protocol']; + $toast = ''; + + if ($protocol === 'netunmount') { + if (!empty($args['user']) && $volume = $this->volume($args['user'])) { + if ($this->removeNetVolume($args['host'], $volume)) { + return array('removed' => array(array('hash' => $volume->root()))); + } + } + return array('sync' => true, 'error' => $this->error(self::ERROR_NETUNMOUNT)); + } + + $driver = isset(self::$netDrivers[$protocol]) ? self::$netDrivers[$protocol] : ''; + $class = 'elFinderVolume' . $driver; + + if (!class_exists($class)) { + return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER)); + } + + if (!$args['path']) { + $args['path'] = '/'; + } + + foreach ($args as $k => $v) { + if ($k != 'options' && $k != 'protocol' && $v) { + $options[$k] = $v; + } + } + + if (is_array($args['options'])) { + foreach ($args['options'] as $key => $value) { + $options[$key] = $value; + } + } + + /* @var elFinderVolumeDriver $volume */ + $volume = new $class(); + + // pass session handler + $volume->setSession($this->session); + + if (is_callable(array($volume, 'netmountPrepare'))) { + $options = $volume->netmountPrepare($options); + if (isset($options['exit'])) { + if ($options['exit'] === 'callback') { + $this->callback($options['out']); + } + return $options; + } + if (!empty($options['toast'])) { + $toast = $options['toast']; + unset($options['toast']); + } + } + + $netVolumes = $this->getNetVolumes(); + + if (!isset($options['id'])) { + // given fixed unique id + if (!$options['id'] = $this->getNetVolumeUniqueId($netVolumes)) { + return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], 'Could\'t given volume id.')); + } + } + + // load additional volume root options + if (!empty($this->optionsNetVolumes['*'])) { + $options = array_merge($this->optionsNetVolumes['*'], $options); + } + if (!empty($this->optionsNetVolumes[$protocol])) { + $options = array_merge($this->optionsNetVolumes[$protocol], $options); + } + + if (!$key = $volume->netMountKey) { + $key = md5($protocol . '-' . serialize($options)); + } + $options['netkey'] = $key; + + if (!isset($netVolumes[$key]) && $volume->mount($options)) { + // call post-process function of netmount + if (is_callable(array($volume, 'postNetmount'))) { + $volume->postNetmount($options); + } + $options['driver'] = $driver; + $netVolumes[$key] = $options; + $this->saveNetVolumes($netVolumes); + $rootstat = $volume->file($volume->root()); + $res = array('added' => array($rootstat)); + if ($toast) { + $res['toast'] = $toast; + } + return $res; + } else { + $this->removeNetVolume(null, $volume); + return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error()))); + } + } + + /** + * "Open" directory + * Return array with following elements + * - cwd - opened dir info + * - files - opened dir content [and dirs tree if $args[tree]] + * - api - api version (if $args[init]) + * - uplMaxSize - if $args[init] + * - error - on failed + * + * @param array command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function open($args) + { + $target = $args['target']; + $init = !empty($args['init']); + $tree = !empty($args['tree']); + $volume = $this->volume($target); + $cwd = $volume ? $volume->dir($target) : false; + $hash = $init ? 'default folder' : '#' . $target; + $compare = ''; + + // on init request we can get invalid dir hash - + // dir which can not be opened now, but remembered by client, + // so open default dir + if ((!$cwd || !$cwd['read']) && $init) { + $volume = $this->default; + $target = $volume->defaultPath(); + $cwd = $volume->dir($target); + } + + if (!$cwd) { + return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND)); + } + if (!$cwd['read']) { + return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED)); + } + + $files = array(); + + // get current working directory files list + if (($ls = $volume->scandir($cwd['hash'])) === false) { + return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error())); + } + + if (isset($cwd['dirs']) && $cwd['dirs'] != 1) { + $cwd = $volume->dir($target); + } + + // get other volume root + if ($tree) { + foreach ($this->volumes as $id => $v) { + $files[] = $v->file($v->root()); + } + } + + // long polling mode + if ($args['compare']) { + $sleep = max(1, (int)$volume->getOption('lsPlSleep')); + $standby = (int)$volume->getOption('plStandby'); + if ($standby > 0 && $sleep > $standby) { + $standby = $sleep; + } + $limit = max(0, floor($standby / $sleep)) + 1; + do { + elFinder::extendTimeLimit(30 + $sleep); + $_mtime = 0; + foreach ($ls as $_f) { + $_mtime = max($_mtime, $_f['ts']); + } + $compare = strval(count($ls)) . ':' . strval($_mtime); + if ($compare !== $args['compare']) { + break; + } + if (--$limit) { + sleep($sleep); + $volume->clearstatcache(); + if (($ls = $volume->scandir($cwd['hash'])) === false) { + break; + } + } + } while ($limit); + if ($ls === false) { + return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error())); + } + } + + if ($ls) { + if ($files) { + $files = array_merge($files, $ls); + } else { + $files = $ls; + } + } + + $result = array( + 'cwd' => $cwd, + 'options' => $volume->options($cwd['hash']), + 'files' => $files + ); + + if ($compare) { + $result['cwd']['compare'] = $compare; + } + + if (!empty($args['init'])) { + $result['api'] = sprintf('%.1F%03d', self::$ApiVersion, self::$ApiRevision); + $result['uplMaxSize'] = ini_get('upload_max_filesize'); + $result['uplMaxFile'] = ini_get('max_file_uploads'); + $result['netDrivers'] = array_keys(self::$netDrivers); + $result['maxTargets'] = $this->maxTargets; + if ($volume) { + $result['cwd']['root'] = $volume->root(); + } + if (elfinder::$textMimes) { + $result['textMimes'] = elfinder::$textMimes; + } + } + + return $result; + } + + /** + * Return dir files names list + * + * @param array command arguments + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function ls($args) + { + $target = $args['target']; + $intersect = isset($args['intersect']) ? $args['intersect'] : array(); + + if (($volume = $this->volume($target)) == false + || ($list = $volume->ls($target, $intersect)) === false) { + return array('error' => $this->error(self::ERROR_OPEN, '#' . $target)); + } + return array('list' => $list); + } + + /** + * Return subdirs for required directory + * + * @param array command arguments + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function tree($args) + { + $target = $args['target']; + + if (($volume = $this->volume($target)) == false + || ($tree = $volume->tree($target)) == false) { + return array('error' => $this->error(self::ERROR_OPEN, '#' . $target)); + } + + return array('tree' => $tree); + } + + /** + * Return parents dir for required directory + * + * @param array command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function parents($args) + { + $target = $args['target']; + $until = $args['until']; + + if (($volume = $this->volume($target)) == false + || ($tree = $volume->parents($target, false, $until)) == false) { + return array('error' => $this->error(self::ERROR_OPEN, '#' . $target)); + } + + return array('tree' => $tree); + } + + /** + * Return new created thumbnails list + * + * @param array command arguments + * + * @return array + * @throws ImagickException + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function tmb($args) + { + + $result = array('images' => array()); + $targets = $args['targets']; + + foreach ($targets as $target) { + elFinder::checkAborted(); + + if (($volume = $this->volume($target)) != false + && (($tmb = $volume->tmb($target)) != false)) { + $result['images'][$target] = $tmb; + } + } + return $result; + } + + /** + * Download files/folders as an archive file + * 1st: Return srrsy contains download archive file info + * 2nd: Return array contains opened file pointer, root itself and required headers + * + * @param array command arguments + * + * @return array + * @throws Exception + * @author Naoki Sawada + */ + protected function zipdl($args) + { + $targets = $args['targets']; + $download = !empty($args['download']); + $h404 = 'HTTP/1.x 404 Not Found'; + + if (!$download) { + //1st: Return array contains download archive file info + $error = array(self::ERROR_ARCHIVE); + if (($volume = $this->volume($targets[0])) !== false) { + if ($dlres = $volume->zipdl($targets)) { + $path = $dlres['path']; + register_shutdown_function(array('elFinder', 'rmFileInDisconnected'), $path); + if (count($targets) === 1) { + $name = basename($volume->path($targets[0])); + } else { + $name = $dlres['prefix'] . '_Files'; + } + $name .= '.' . $dlres['ext']; + $uniqid = uniqid(); + $this->session->set('zipdl' . $uniqid, basename($path)); + $result = array( + 'zipdl' => array( + 'file' => $uniqid, + 'name' => $name, + 'mime' => $dlres['mime'] + ) + ); + return $result; + } + $error = array_merge($error, $volume->error()); + } + return array('error' => $error); + } else { + // 2nd: Return array contains opened file session key, root itself and required headers + if (count($targets) !== 4 || ($volume = $this->volume($targets[0])) == false || !($file = $this->session->get('zipdl' . $targets[1]))) { + return array('error' => 'File not found', 'header' => $h404, 'raw' => true); + } + $this->session->remove('zipdl' . $targets[1]); + if ($volume->commandDisabled('zipdl')) { + return array('error' => 'File not found', 'header' => $h404, 'raw' => true); + } + $path = $volume->getTempPath() . DIRECTORY_SEPARATOR . basename($file); + if (!is_readable($path) || !is_writable($path)) { + return array('error' => 'File not found', 'header' => $h404, 'raw' => true); + } + // register auto delete on shutdown + $GLOBALS['elFinderTempFiles'][$path] = true; + // for HTTP headers + $name = $targets[2]; + $mime = $targets[3]; + + $filenameEncoded = rawurlencode($name); + if (strpos($filenameEncoded, '%') === false) { // ASCII only + $filename = 'filename="' . $name . '"'; + } else { + $ua = $_SERVER['HTTP_USER_AGENT']; + if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987) + $filename = 'filename="' . $filenameEncoded . '"'; + } elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6 + $filename = 'filename="' . str_replace('"', '', $name) . '"'; + } else { // RFC 6266 (RFC 2231/RFC 5987) + $filename = 'filename*=UTF-8\'\'' . $filenameEncoded; + } + } + + $fp = fopen($path, 'rb'); + $file = fstat($fp); + $result = array( + 'pointer' => $fp, + 'header' => array( + 'Content-Type: ' . $mime, + 'Content-Disposition: attachment; ' . $filename, + 'Content-Transfer-Encoding: binary', + 'Content-Length: ' . $file['size'], + 'Accept-Ranges: none', + 'Connection: close' + ) + ); + return $result; + } + } + + /** + * Required to output file in browser when volume URL is not set + * Return array contains opened file pointer, root itself and required headers + * + * @param array command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function file($args) + { + $target = $args['target']; + $download = !empty($args['download']); + $onetime = !empty($args['onetime']); + //$h304 = 'HTTP/1.1 304 Not Modified'; + //$h403 = 'HTTP/1.0 403 Access Denied'; + $h404 = 'HTTP/1.0 404 Not Found'; + $a404 = array('error' => 'File not found', 'header' => $h404, 'raw' => true); + + if ($onetime) { + $volume = null; + $tmpdir = elFinder::$commonTempPath; + if (!$tmpdir || !is_file($tmpf = $tmpdir . DIRECTORY_SEPARATOR . 'ELF' . $target)) { + return $a404; + } + $GLOBALS['elFinderTempFiles'][$tmpf] = true; + if ($file = json_decode(file_get_contents($tmpf), true)) { + $src = base64_decode($file['file']); + if (!is_file($src) || !($fp = fopen($src, 'rb'))) { + return $a404; + } + if (strpos($src, $tmpdir) === 0) { + $GLOBALS['elFinderTempFiles'][$src] = true; + } + unset($file['file']); + $file['read'] = true; + $file['size'] = filesize($src); + } else { + return $a404; + } + } else { + if (($volume = $this->volume($target)) == false) { + return $a404; + } + + if (($file = $volume->file($target)) == false) { + return $a404; + } + + if (!$file['read']) { + return $a404; + } + + if (($fp = $volume->open($target)) == false) { + return $a404; + } + } + + // check aborted by user + elFinder::checkAborted(); + + // allow change MIME type by 'file.pre' callback functions + $mime = isset($args['mime']) ? $args['mime'] : $file['mime']; + if ($download || $onetime) { + $disp = 'attachment'; + } else { + $dispInlineRegex = $volume->getOption('dispInlineRegex'); + $inlineRegex = false; + if ($dispInlineRegex) { + $inlineRegex = '#' . str_replace('#', '\\#', $dispInlineRegex) . '#'; + try { + preg_match($inlineRegex, ''); + } catch (Exception $e) { + $inlineRegex = false; + } + } + if (!$inlineRegex) { + $inlineRegex = '#^(?:(?:image|text)|application/x-shockwave-flash$)#'; + } + $disp = preg_match($inlineRegex, $mime) ? 'inline' : 'attachment'; + } + + $filenameEncoded = rawurlencode($file['name']); + if (strpos($filenameEncoded, '%') === false) { // ASCII only + $filename = 'filename="' . $file['name'] . '"'; + } else { + $ua = $_SERVER['HTTP_USER_AGENT']; + if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987) + $filename = 'filename="' . $filenameEncoded . '"'; + } elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6 + $filename = 'filename="' . str_replace('"', '', $file['name']) . '"'; + } else { // RFC 6266 (RFC 2231/RFC 5987) + $filename = 'filename*=UTF-8\'\'' . $filenameEncoded; + } + } + + if ($args['cpath'] && $args['reqid']) { + setcookie('elfdl' . $args['reqid'], '1', 0, $args['cpath']); + } + + $result = array( + 'volume' => $volume, + 'pointer' => $fp, + 'info' => $file, + 'header' => array( + 'Content-Type: ' . $mime, + 'Content-Disposition: ' . $disp . '; ' . $filename, + 'Content-Transfer-Encoding: binary', + 'Content-Length: ' . $file['size'], + 'Last-Modified: ' . gmdate('D, d M Y H:i:s T', $file['ts']), + 'Connection: close' + ) + ); + + if (!$onetime) { + // add cache control headers + if ($cacheHeaders = $volume->getOption('cacheHeaders')) { + $result['header'] = array_merge($result['header'], $cacheHeaders); + } + + // check 'xsendfile' + $xsendfile = $volume->getOption('xsendfile'); + $path = null; + if ($xsendfile) { + $info = stream_get_meta_data($fp); + if ($path = empty($info['uri']) ? null : $info['uri']) { + $basePath = rtrim($volume->getOption('xsendfilePath'), DIRECTORY_SEPARATOR); + if ($basePath) { + $root = rtrim($volume->getRootPath(), DIRECTORY_SEPARATOR); + if (strpos($path, $root) === 0) { + $path = $basePath . substr($path, strlen($root)); + } else { + $path = null; + } + } + } + } + if ($path) { + $result['header'][] = $xsendfile . ': ' . $path; + $result['info']['xsendfile'] = $xsendfile; + } + } + + // add "Content-Location" if file has url data + if (isset($file['url']) && $file['url'] && $file['url'] != 1) { + $result['header'][] = 'Content-Location: ' . $file['url']; + } + return $result; + } + + /** + * Count total files size + * + * @param array command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function size($args) + { + $size = 0; + $files = 0; + $dirs = 0; + $itemCount = true; + $sizes = array(); + + foreach ($args['targets'] as $target) { + elFinder::checkAborted(); + if (($volume = $this->volume($target)) == false + || ($file = $volume->file($target)) == false + || !$file['read']) { + return array('error' => $this->error(self::ERROR_OPEN, '#' . $target)); + } + + $volRes = $volume->size($target); + if (is_array($volRes)) { + $sizeInfo = array('size' => 0, 'fileCnt' => 0, 'dirCnt' => 0); + if (!empty($volRes['size'])) { + $sizeInfo['size'] = $volRes['size']; + $size += $volRes['size']; + } + if (!empty($volRes['files'])) { + $sizeInfo['fileCnt'] = $volRes['files']; + } + if (!empty($volRes['dirs'])) { + $sizeInfo['dirCnt'] = $volRes['dirs']; + } + if ($itemCount) { + $files += $sizeInfo['fileCnt']; + $dirs += $sizeInfo['dirCnt']; + } + $sizes[$target] = $sizeInfo; + } else if (is_numeric($volRes)) { + $size += $volRes; + $files = $dirs = 'unknown'; + $itemCount = false; + } + } + return array('size' => $size, 'fileCnt' => $files, 'dirCnt' => $dirs, 'sizes' => $sizes); + } + + /** + * Create directory + * + * @param array command arguments + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function mkdir($args) + { + $target = $args['target']; + $name = $args['name']; + $dirs = $args['dirs']; + if ($name === '' && !$dirs) { + return array('error' => $this->error(self::ERROR_INV_PARAMS, 'mkdir')); + } + + if (($volume = $this->volume($target)) == false) { + return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#' . $target)); + } + if ($dirs) { + sort($dirs); + $reset = null; + $mkdirs = array(); + foreach ($dirs as $dir) { + $tgt =& $mkdirs; + $_names = explode('/', trim($dir, '/')); + foreach ($_names as $_key => $_name) { + if (!isset($tgt[$_name])) { + $tgt[$_name] = array(); + } + $tgt =& $tgt[$_name]; + } + $tgt =& $reset; + } + $res = $this->ensureDirsRecursively($volume, $target, $mkdirs); + $ret = array( + 'added' => $res['stats'], + 'hashes' => $res['hashes'] + ); + if ($res['error']) { + $ret['warning'] = $this->error(self::ERROR_MKDIR, $res['error'][0], $volume->error()); + } + return $ret; + } else { + return ($dir = $volume->mkdir($target, $name)) == false + ? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error())) + : array('added' => array($dir)); + } + } + + /** + * Create empty file + * + * @param array command arguments + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function mkfile($args) + { + $target = $args['target']; + $name = $args['name']; + + if (($volume = $this->volume($target)) == false) { + return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#' . $target)); + } + + return ($file = $volume->mkfile($target, $args['name'])) == false + ? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error())) + : array('added' => array($file)); + } + + /** + * Rename file, Accept multiple items >= API 2.1031 + * + * @param array $args + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Naoki Sawada + */ + protected function rename($args) + { + $target = $args['target']; + $name = $args['name']; + $query = (strpos($args['q'], '*') !== false) ? $args['q'] : ''; + $targets = $args['targets']; + $rms = array(); + $notfounds = array(); + $locked = array(); + $errs = array(); + $files = array(); + $removed = array(); + $res = array(); + $type = 'normal'; + + if (!($volume = $this->volume($target))) { + return array('error' => $this->error(self::ERROR_RENAME, '#' . $target, self::ERROR_FILE_NOT_FOUND)); + } + + if ($targets) { + array_unshift($targets, $target); + foreach ($targets as $h) { + if ($rm = $volume->file($h)) { + if ($this->itemLocked($h)) { + $locked[] = $rm['name']; + } else { + $rm['realpath'] = $volume->realpath($h); + $rms[] = $rm; + } + } else { + $notfounds[] = '#' . $h; + } + } + if (!$rms) { + $res['error'] = array(); + if ($notfounds) { + $res['error'] = array(self::ERROR_RENAME, join(', ', $notfounds), self::ERROR_FILE_NOT_FOUND); + } + if ($locked) { + array_push($res['error'], self::ERROR_LOCKED, join(', ', $locked)); + } + return $res; + } + + $res['warning'] = array(); + if ($notfounds) { + array_push($res['warning'], self::ERROR_RENAME, join(', ', $notfounds), self::ERROR_FILE_NOT_FOUND); + } + if ($locked) { + array_push($res['warning'], self::ERROR_LOCKED, join(', ', $locked)); + } + + if ($query) { + // batch rename + $splits = elFinder::splitFileExtention($query); + if ($splits[1] && $splits[0] === '*') { + $type = 'extention'; + $name = $splits[1]; + } else if (strlen($splits[0]) > 1) { + if (substr($splits[0], -1) === '*') { + $type = 'prefix'; + $name = substr($splits[0], 0, strlen($splits[0]) - 1); + } else if (substr($splits[0], 0, 1) === '*') { + $type = 'suffix'; + $name = substr($splits[0], 1); + } + } + if ($type !== 'normal') { + if (!empty($this->listeners['rename.pre'])) { + $_args = array('name' => $name); + foreach ($this->listeners['rename.pre'] as $handler) { + $_res = call_user_func_array($handler, array('rename', &$_args, $this, $volume)); + if (!empty($_res['preventexec'])) { + break; + } + } + $name = $_args['name']; + } + } + } + foreach ($rms as $rm) { + if ($type === 'normal') { + $rname = $volume->uniqueName($volume->realpath($rm['phash']), $name, '', false); + } else { + $rname = $name; + if ($type === 'extention') { + $splits = elFinder::splitFileExtention($rm['name']); + $rname = $splits[0] . '.' . $name; + } else if ($type === 'prefix') { + $rname = $name . $rm['name']; + } else if ($type === 'suffix') { + $splits = elFinder::splitFileExtention($rm['name']); + $rname = $splits[0] . $name . ($splits[1] ? ('.' . $splits[1]) : ''); + } + $rname = $volume->uniqueName($volume->realpath($rm['phash']), $rname, '', true); + } + if ($file = $volume->rename($rm['hash'], $rname)) { + $files[] = $file; + $removed[] = $rm; + } else { + $errs[] = $rm['name']; + } + } + + if (!$files) { + $res['error'] = $this->error(self::ERROR_RENAME, join(', ', $errs), $volume->error()); + if (!$res['warning']) { + unset($res['warning']); + } + return $res; + } + if ($errs) { + array_push($res['warning'], self::ERROR_RENAME, join(', ', $errs), $volume->error()); + } + if (!$res['warning']) { + unset($res['warning']); + } + $res['added'] = $files; + $res['removed'] = $removed; + return $res; + } else { + if (!($rm = $volume->file($target))) { + return array('error' => $this->error(self::ERROR_RENAME, '#' . $target, self::ERROR_FILE_NOT_FOUND)); + } + if ($this->itemLocked($target)) { + return array('error' => $this->error(self::ERROR_LOCKED, $rm['name'])); + } + $rm['realpath'] = $volume->realpath($target); + + return ($file = $volume->rename($target, $name)) == false + ? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error())) + : array('added' => array($file), 'removed' => array($rm)); + } + } + + /** + * Duplicate file - create copy with "copy %d" suffix + * + * @param array $args command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function duplicate($args) + { + $targets = is_array($args['targets']) ? $args['targets'] : array(); + $result = array(); + $suffix = empty($args['suffix']) ? 'copy' : $args['suffix']; + + $this->itemLock($targets); + + foreach ($targets as $target) { + elFinder::checkAborted(); + + if (($volume = $this->volume($target)) == false + || ($src = $volume->file($target)) == false) { + $result['warning'] = $this->error(self::ERROR_COPY, '#' . $target, self::ERROR_FILE_NOT_FOUND); + break; + } + + if (($file = $volume->duplicate($target, $suffix)) == false) { + $result['warning'] = $this->error($volume->error()); + break; + } + } + + return $result; + } + + /** + * Remove dirs/files + * + * @param array command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function rm($args) + { + $targets = is_array($args['targets']) ? $args['targets'] : array(); + $result = array('removed' => array()); + + foreach ($targets as $target) { + elFinder::checkAborted(); + + if (($volume = $this->volume($target)) == false) { + $result['warning'] = $this->error(self::ERROR_RM, '#' . $target, self::ERROR_FILE_NOT_FOUND); + break; + } + + if ($this->itemLocked($target)) { + $rm = $volume->file($target); + $result['warning'] = $this->error(self::ERROR_LOCKED, $rm['name']); + break; + } + + if (!$volume->rm($target)) { + $result['warning'] = $this->error($volume->error()); + break; + } + } + + return $result; + } + + /** + * Return has subdirs + * + * @param array command arguments + * + * @return array + * @author Dmitry Naoki Sawada + **/ + protected function subdirs($args) + { + + $result = array('subdirs' => array()); + $targets = $args['targets']; + + foreach ($targets as $target) { + if (($volume = $this->volume($target)) !== false) { + $result['subdirs'][$target] = $volume->subdirs($target) ? 1 : 0; + } + } + return $result; + } + + /** + * Gateway for custom contents editor + * + * @param array $args command arguments + * + * @return array + * @author Naoki Sawada + */ + protected function editor($args = array()) + { + /* @var elFinderEditor $editor */ + $name = $args['name']; + if (is_array($name)) { + $res = array(); + foreach ($name as $c) { + $class = 'elFinderEditor' . $c; + if (class_exists($class)) { + $editor = new $class($this, $args['args']); + $res[$c] = $editor->enabled(); + } else { + $res[$c] = 0; + } + } + return $res; + } else { + $class = 'elFinderEditor' . $name; + $method = ''; + if (class_exists($class)) { + $editor = new $class($this, $args['args']); + $method = $args['method']; + if ($editor->isAllowedMethod($method) && method_exists($editor, $method)) { + return $editor->$method(); + } + } + return array('error', $this->error(self::ERROR_UNKNOWN_CMD, 'editor.' . $name . '.' . $method)); + } + } + + /** + * Abort current request and make flag file to running check + * + * @param array $args + * + * @return void + */ + protected function abort($args = array()) + { + if (!elFinder::$connectionFlagsPath || $_SERVER['REQUEST_METHOD'] === 'HEAD') { + return; + } + $flagFile = elFinder::$connectionFlagsPath . DIRECTORY_SEPARATOR . 'elfreq%s'; + if (!empty($args['makeFile'])) { + self::$abortCheckFile = sprintf($flagFile, $args['makeFile']); + touch(self::$abortCheckFile); + $GLOBALS['elFinderTempFiles'][self::$abortCheckFile] = true; + return; + } + + $file = !empty($args['id']) ? sprintf($flagFile, $args['id']) : self::$abortCheckFile; + $file && is_file($file) && unlink($file); + } + + /** + * Get remote contents + * + * @param string $url target url + * @param int $timeout timeout (sec) + * @param int $redirect_max redirect max count + * @param string $ua + * @param resource $fp + * + * @return string, resource or bool(false) + * @retval string contents + * @retval resource conttents + * @rettval false error + * @author Naoki Sawada + **/ + protected function get_remote_contents(&$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null) + { + $method = (function_exists('curl_exec') && !ini_get('safe_mode') && !ini_get('open_basedir')) ? 'curl_get_contents' : 'fsock_get_contents'; + return $this->$method($url, $timeout, $redirect_max, $ua, $fp); + } + + /** + * Get remote contents with cURL + * + * @param string $url target url + * @param int $timeout timeout (sec) + * @param int $redirect_max redirect max count + * @param string $ua + * @param resource $outfp + * + * @return string, resource or bool(false) + * @retval string contents + * @retval resource conttents + * @retval false error + * @author Naoki Sawada + **/ + protected function curl_get_contents(&$url, $timeout, $redirect_max, $ua, $outfp) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, false); + if ($outfp) { + curl_setopt($ch, CURLOPT_FILE, $outfp); + } else { + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); + } + curl_setopt($ch, CURLOPT_LOW_SPEED_LIMIT, 1); + curl_setopt($ch, CURLOPT_LOW_SPEED_TIME, $timeout); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_MAXREDIRS, $redirect_max); + curl_setopt($ch, CURLOPT_USERAGENT, $ua); + $result = curl_exec($ch); + $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); + curl_close($ch); + return $outfp ? $outfp : $result; + } + + /** + * Get remote contents with fsockopen() + * + * @param string $url url + * @param int $timeout timeout (sec) + * @param int $redirect_max redirect max count + * @param string $ua + * @param resource $outfp + * + * @return string, resource or bool(false) + * @retval string contents + * @retval resource conttents + * @retval false error + * @throws elFinderAbortException + * @author Naoki Sawada + */ + protected function fsock_get_contents(&$url, $timeout, $redirect_max, $ua, $outfp) + { + $connect_timeout = 3; + $connect_try = 3; + $method = 'GET'; + $readsize = 4096; + $ssl = ''; + + $getSize = null; + $headers = ''; + + $arr = parse_url($url); + if (!$arr) { + // Bad request + return false; + } + if ($arr['scheme'] === 'https') { + $ssl = 'ssl://'; + } + + // query + $arr['query'] = isset($arr['query']) ? '?' . $arr['query'] : ''; + // port + $port = isset($arr['port']) ? $arr['port'] : ''; + $arr['port'] = $port ? $port : ($ssl ? 443 : 80); + + $url_base = $arr['scheme'] . '://' . $arr['host'] . ($port ? (':' . $port) : ''); + $url_path = isset($arr['path']) ? $arr['path'] : '/'; + $uri = $url_path . $arr['query']; + + $query = $method . ' ' . $uri . " HTTP/1.0\r\n"; + $query .= "Host: " . $arr['host'] . "\r\n"; + $query .= "Accept: */*\r\n"; + $query .= "Connection: close\r\n"; + if (!empty($ua)) $query .= "User-Agent: " . $ua . "\r\n"; + if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n"; + + $query .= $headers; + + $query .= "\r\n"; + + $fp = $connect_try_count = 0; + while (!$fp && $connect_try_count < $connect_try) { + + $errno = 0; + $errstr = ""; + $fp = fsockopen( + $ssl . $arr['host'], + $arr['port'], + $errno, $errstr, $connect_timeout); + if ($fp) break; + $connect_try_count++; + if (connection_aborted()) { + throw new elFinderAbortException(); + } + sleep(1); // wait 1sec + } + + $fwrite = 0; + for ($written = 0; $written < strlen($query); $written += $fwrite) { + $fwrite = fwrite($fp, substr($query, $written)); + if (!$fwrite) { + break; + } + } + + if ($timeout) { + socket_set_timeout($fp, $timeout); + } + + $_response = ''; + $header = ''; + while ($_response !== "\r\n") { + $_response = fgets($fp, $readsize); + $header .= $_response; + }; + + $rccd = array_pad(explode(' ', $header, 2), 2, ''); // array('HTTP/1.1','200') + $rc = (int)$rccd[1]; + + $ret = false; + // Redirect + switch ($rc) { + case 307: // Temporary Redirect + case 303: // See Other + case 302: // Moved Temporarily + case 301: // Moved Permanently + $matches = array(); + if (preg_match('/^Location: (.+?)(#.+)?$/im', $header, $matches) && --$redirect_max > 0) { + $_url = $url; + $url = trim($matches[1]); + if (!preg_match('/^https?:\//', $url)) { // no scheme + if ($url{0} != '/') { // Relative path + // to Absolute path + $url = substr($url_path, 0, strrpos($url_path, '/')) . '/' . $url; + } + // add sheme,host + $url = $url_base . $url; + } + if ($_url === $url) { + sleep(1); + } + fclose($fp); + return $this->fsock_get_contents($url, $timeout, $redirect_max, $ua, $outfp); + } + break; + case 200: + $ret = true; + } + if (!$ret) { + fclose($fp); + return false; + } + + $body = ''; + if (!$outfp) { + $outfp = fopen('php://temp', 'rwb'); + $body = true; + } + while (fwrite($outfp, fread($fp, $readsize))) { + if ($timeout) { + $_status = socket_get_status($fp); + if ($_status['timed_out']) { + fclose($outfp); + fclose($fp); + return false; // Request Time-out + } + } + } + if ($body) { + rewind($outfp); + $body = stream_get_contents($outfp); + fclose($outfp); + $outfp = null; + } + + fclose($fp); + + return $outfp ? $outfp : $body; // Data + } + + /** + * Parse Data URI scheme + * + * @param string $str + * @param array $extTable + * @param array $args + * + * @return array + * @author Naoki Sawada + */ + protected function parse_data_scheme($str, $extTable, $args = null) + { + $data = $name = $mime = ''; + // Scheme 'data://' require `allow_url_fopen` and `allow_url_include` + if ($fp = fopen('data://' . substr($str, 5), 'rb')) { + if ($data = stream_get_contents($fp)) { + $meta = stream_get_meta_data($fp); + $mime = $meta['mediatype']; + } + fclose($fp); + } else if (preg_match('~^data:(.+?/.+?)?(?:;charset=.+?)?;base64,~', substr($str, 0, 128), $m)) { + $data = base64_decode(substr($str, strlen($m[0]))); + if ($m[1]) { + $mime = $m[1]; + } + } + if ($data) { + $ext = ($mime && isset($extTable[$mime])) ? '.' . $extTable[$mime] : ''; + // Set name if name eq 'image.png' and $args has 'name' array, e.g. clipboard data + if (is_array($args['name']) && isset($args['name'][0])) { + $name = $args['name'][0]; + if ($ext) { + $name = preg_replace('/\.[^.]*$/', '', $name); + } + } else { + $name = substr(md5($data), 0, 8); + } + $name .= $ext; + } else { + $data = $name = ''; + } + return array($data, $name); + } + + /** + * Detect file MIME Type by local path + * + * @param string $path Local path + * + * @return string file MIME Type + * @author Naoki Sawada + */ + protected function detectMimeType($path) + { + static $type, $finfo; + if (!$type) { + if (class_exists('finfo', false)) { + $tmpFileInfo = explode(';', finfo_file(finfo_open(FILEINFO_MIME), __FILE__)); + } else { + $tmpFileInfo = false; + } + $regexp = '/text\/x\-(php|c\+\+)/'; + if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) { + $type = 'finfo'; + $finfo = finfo_open(FILEINFO_MIME); + } elseif (function_exists('mime_content_type') + && preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) { + $type = 'mime_content_type'; + } elseif (function_exists('getimagesize')) { + $type = 'getimagesize'; + } else { + $type = 'none'; + } + } + + $mime = ''; + if ($type === 'finfo') { + $mime = finfo_file($finfo, $path); + } elseif ($type === 'mime_content_type') { + $mime = mime_content_type($path); + } elseif ($type === 'getimagesize') { + if ($img = getimagesize($path)) { + $mime = $img['mime']; + } + } + + if ($mime) { + $mime = explode(';', $mime); + $mime = trim($mime[0]); + + if (in_array($mime, array('application/x-empty', 'inode/x-empty'))) { + // finfo return this mime for empty files + $mime = 'text/plain'; + } elseif ($mime == 'application/x-zip') { + // http://elrte.org/redmine/issues/163 + $mime = 'application/zip'; + } + } + + return $mime ? $mime : 'unknown'; + } + + /** + * Detect file type extension by local path + * + * @param object $volume elFinderVolumeDriver instance + * @param string $path Local path + * @param string $name Filename to save + * + * @return string file type extension with dot + * @author Naoki Sawada + */ + protected function detectFileExtension($volume, $path, $name) + { + $mime = $this->detectMimeType($path); + if ($mime === 'unknown') { + $mime = 'application/octet-stream'; + } + $ext = $volume->getExtentionByMime($volume->mimeTypeNormalize($mime, $name)); + return $ext ? ('.' . $ext) : ''; + } + + /** + * Get temporary directory path + * + * @param string $volumeTempPath + * + * @return string + * @author Naoki Sawada + */ + private function getTempDir($volumeTempPath = null) + { + $testDirs = array(); + if ($this->uploadTempPath) { + $testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR); + } + if ($volumeTempPath) { + $testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR); + } + if (elFinder::$commonTempPath) { + $testDirs[] = elFinder::$commonTempPath; + } + $tempDir = ''; + foreach ($testDirs as $testDir) { + if (!$testDir || !is_dir($testDir)) continue; + if (is_writable($testDir)) { + $tempDir = $testDir; + $gc = time() - 3600; + foreach (glob($tempDir . DIRECTORY_SEPARATOR . 'ELF*') as $cf) { + if (filemtime($cf) < $gc) { + unlink($cf); + } + } + break; + } + } + return $tempDir; + } + + /** + * chmod + * + * @param array command arguments + * + * @return array + * @throws elFinderAbortException + * @author David Bartle + */ + protected function chmod($args) + { + $targets = $args['targets']; + $mode = intval((string)$args['mode'], 8); + + if (!is_array($targets)) { + $targets = array($targets); + } + + $result = array(); + + if (($volume = $this->volume($targets[0])) == false) { + $result['error'] = $this->error(self::ERROR_CONF_NO_VOL); + return $result; + } + + $this->itemLock($targets); + + $files = array(); + $errors = array(); + foreach ($targets as $target) { + elFinder::checkAborted(); + + $file = $volume->chmod($target, $mode); + if ($file) { + $files = array_merge($files, is_array($file) ? $file : array($file)); + } else { + $errors = array_merge($errors, $volume->error()); + } + } + + if ($files) { + $result['changed'] = $files; + if ($errors) { + $result['warning'] = $this->error($errors); + } + } else { + $result['error'] = $this->error($errors); + } + + return $result; + } + + /** + * Check chunked upload files + * + * @param string $tmpname uploaded temporary file path + * @param string $chunk uploaded chunk file name + * @param string $cid uploaded chunked file id + * @param string $tempDir temporary dirctroy path + * @param null $volume + * + * @return array|null + * @throws elFinderAbortException + * @author Naoki Sawada + */ + private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null) + { + /* @var elFinderVolumeDriver $volume */ + if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) { + $fname = $m[1]; + $encname = md5($cid . '_' . $fname); + $base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname; + $clast = intval($m[3]); + if (is_null($tmpname)) { + ignore_user_abort(true); + // chunked file upload fail + foreach (glob($base . '*') as $cf) { + unlink($cf); + } + ignore_user_abort(false); + return null; + } + + $range = isset($_POST['range']) ? trim($_POST['range']) : ''; + if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) { + $start = $ranges[1]; + $len = $ranges[2]; + $size = $ranges[3]; + $tmp = $base . '.part'; + $csize = filesize($tmpname); + + $tmpExists = is_file($tmp); + if (!$tmpExists) { + // check upload max size + $uploadMaxSize = $volume ? $volume->getUploadMaxSize() : 0; + if ($uploadMaxSize > 0 && $size > $uploadMaxSize) { + return array(self::ERROR_UPLOAD_FILE_SIZE, false); + } + // make temp file + $ok = false; + if ($fp = fopen($tmp, 'wb')) { + flock($fp, LOCK_EX); + $ok = ftruncate($fp, $size); + flock($fp, LOCK_UN); + fclose($fp); + touch($base); + } + if (!$ok) { + unlink($tmp); + return array(self::ERROR_UPLOAD_TEMP, false); + } + } else { + // wait until makeing temp file (for anothor session) + $cnt = 1200; // Time limit 120 sec + while (!is_file($base) && --$cnt) { + usleep(100000); // wait 100ms + } + if (!$cnt) { + return array(self::ERROR_UPLOAD_TEMP, false); + } + } + + // check size info + if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) { + return array(self::ERROR_UPLOAD_TEMP, false); + } + + // write chunk data + $src = fopen($tmpname, 'rb'); + $fp = fopen($tmp, 'cb'); + fseek($fp, $start); + $writelen = stream_copy_to_stream($src, $fp, $len); + fclose($fp); + fclose($src); + + try { + // to check connection is aborted + elFinder::checkAborted(); + } catch (elFinderAbortException $e) { + unlink($tmpname); + is_file($tmp) && unlink($tmp); + is_file($base) && unlink($base); + throw $e; + } + + if ($writelen != $len) { + return array(self::ERROR_UPLOAD_TEMP, false); + } + + // write counts + file_put_contents($base, "\0", FILE_APPEND | LOCK_EX); + + if (filesize($base) >= $clast + 1) { + // Completion + unlink($base); + return array($tmp, $fname); + } + } else { + // old way + $part = $base . $m[2]; + if (move_uploaded_file($tmpname, $part)) { + chmod($part, 0600); + if ($clast < count(glob($base . '*'))) { + $parts = array(); + for ($i = 0; $i <= $clast; $i++) { + $name = $base . '.' . $i . '_' . $clast; + if (is_readable($name)) { + $parts[] = $name; + } else { + $parts = null; + break; + } + } + if ($parts) { + if (!is_file($base)) { + touch($base); + if ($resfile = tempnam($tempDir, 'ELF')) { + $target = fopen($resfile, 'wb'); + foreach ($parts as $f) { + $fp = fopen($f, 'rb'); + while (!feof($fp)) { + fwrite($target, fread($fp, 8192)); + } + fclose($fp); + unlink($f); + } + fclose($target); + unlink($base); + return array($resfile, $fname); + } + unlink($base); + } + } + } + } + } + } + return array('', ''); + } + + /** + * Save uploaded files + * + * @param array + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function upload($args) + { + $ngReg = '/[\/\\?*:|"<>]/'; + $target = $args['target']; + $volume = $this->volume($target); + $files = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array(); + $header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8'); + $result = array_merge(array('added' => array()), $header); + $paths = $args['upload_path'] ? $args['upload_path'] : array(); + $chunk = $args['chunk'] ? $args['chunk'] : ''; + $cid = $args['cid'] ? (int)$args['cid'] : ''; + $mtimes = $args['mtime'] ? $args['mtime'] : array(); + $tmpfname = ''; + + if (!$volume) { + return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#' . $target)), $header); + } + + // check $chunk + if (strpos($chunk, '/') !== false || strpos($chunk, '\\') !== false) { + return array('error' => $this->error(self::ERROR_UPLOAD)); + } + + if ($args['overwrite'] !== '') { + $volume->setUploadOverwrite($args['overwrite']); + } + + $renames = $hashes = array(); + $suffix = '~'; + if ($args['renames'] && is_array($args['renames'])) { + $renames = array_flip($args['renames']); + if (is_string($args['suffix']) && !preg_match($ngReg, $args['suffix'])) { + $suffix = $args['suffix']; + } + } + if ($args['hashes'] && is_array($args['hashes'])) { + $hashes = array_flip($args['hashes']); + } + + $this->itemLock($target); + + // file extentions table by MIME + $extTable = array_flip(array_unique($volume->getMimeTable())); + + if (empty($files)) { + if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) { + $names = array(); + foreach ($args['upload'] as $i => $url) { + // check chunked file upload commit + if ($chunk) { + if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') { + $this->checkChunkedFile(null, $chunk, $cid, $tempDir); + if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) { + $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TEMP); + } + return $result; + } else { + $tmpfname = $tempDir . '/' . $chunk; + $files['tmp_name'][$i] = $tmpfname; + $files['name'][$i] = $url; + $files['error'][$i] = 0; + $GLOBALS['elFinderTempFiles'][$tmpfname] = true; + break; + } + } + + $tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url . microtime(true)); + $GLOBALS['elFinderTempFiles'][$tmpfname] = true; + + $_name = ''; + // check is data: + if (substr($url, 0, 5) === 'data:') { + list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable, $args); + } else { + $fp = fopen($tmpfname, 'wb'); + $data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp); + // to check connection is aborted + elFinder::checkAborted(); + $_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url)); + // Check `Content-Disposition` response header + if ($data && ($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) { + if (preg_match('/filename\*=(?:([a-zA-Z0-9_-]+?)\'\')"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) { + $_name = rawurldecode($m[2]); + if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) { + $_name = mb_convert_encoding($_name, 'UTF-8', $m[1]); + } + } else if (preg_match('/filename="?([ a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) { + $_name = rawurldecode($m[1]); + } + } + } + if ($data) { + if (isset($args['name'][$i])) { + $_name = $args['name'][$i]; + } + if ($_name) { + $_ext = ''; + if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) { + $_ext = $_match[1]; + } + if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) { + $GLOBALS['elFinderTempFiles'][$tmpfname] = true; + $_name = preg_replace($ngReg, '_', $_name); + list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, ''); + if ($_b === '') { + if ($_ext) { + rename($tmpfname, $tmpfname . $_ext); + $tmpfname = $tmpfname . $_ext; + } + $_b = $this->detectFileExtension($volume, $tmpfname, $_name); + $_name = $_a . $_b; + } else { + $_b = '.' . $_b; + } + if (isset($names[$_name])) { + $_name = $_a . '_' . $names[$_name]++ . $_b; + } else { + $names[$_name] = 1; + } + $files['tmp_name'][$i] = $tmpfname; + $files['name'][$i] = $_name; + $files['error'][$i] = 0; + // set to auto rename + $volume->setUploadOverwrite(false); + } else { + unlink($tmpfname); + } + } + } + } + } + if (empty($files)) { + return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header); + } + } + + $addedDirs = array(); + $errors = array(); + foreach ($files['name'] as $i => $name) { + if (($error = $files['error'][$i]) > 0) { + $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER, $error); + $this->uploadDebug = 'Upload error code: ' . $error; + break; + } + + $tmpname = $files['tmp_name'][$i]; + $thash = ($paths && isset($paths[$i])) ? $paths[$i] : $target; + $mtime = isset($mtimes[$i]) ? $mtimes[$i] : 0; + if ($name === 'blob') { + if ($chunk) { + if ($tempDir = $this->getTempDir($volume->getTempPath())) { + list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume); + if ($tmpname) { + if ($name === false) { + preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m); + $result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname); + $result['_chunkfailure'] = true; + $this->uploadDebug = 'Upload error: ' . $tmpname; + } else if ($name) { + $result['_chunkmerged'] = basename($tmpname); + $result['_name'] = $name; + $result['_mtime'] = $mtime; + } + } + } else { + $result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TEMP); + $this->uploadDebug = 'Upload error: unable open tmp file'; + } + return $result; + } else { + // for form clipboard with Google Chrome or Opera + $name = 'image.png'; + } + } + + // Set name if name eq 'image.png' and $args has 'name' array, e.g. clipboard data + if (strtolower(substr($name, 0, 5)) === 'image' && is_array($args['name']) && isset($args['name'][$i])) { + $type = $files['type'][$i]; + $name = $args['name'][$i]; + $ext = isset($extTable[$type]) ? '.' . $extTable[$type] : ''; + if ($ext) { + $name = preg_replace('/\.[^.]*$/', '', $name); + } + $name .= $ext; + } + + // do hook function 'upload.presave' + if (!empty($this->listeners['upload.presave'])) { + foreach ($this->listeners['upload.presave'] as $handler) { + call_user_func_array($handler, array(&$thash, &$name, $tmpname, $this, $volume)); + } + } + + clearstatcache(); + if ($mtime && is_file($tmpname)) { + // for keep timestamp option in the LocalFileSystem volume + touch($tmpname, $mtime); + } + + if (!is_file($tmpname) || ($fp = fopen($tmpname, 'rb')) === false) { + $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TEMP); + $this->uploadDebug = 'Upload error: unable open tmp file'; + if (!is_uploaded_file($tmpname)) { + if (unlink($tmpname) && $tmpfname) unset($GLOBALS['elFinderTempFiles'][$tmpfname]); + continue; + } + break; + } + $rnres = array(); + if ($thash !== '' && $thash !== $target) { + if ($dir = $volume->dir($thash)) { + $_target = $thash; + if (!isset($addedDirs[$thash])) { + $addedDirs[$thash] = true; + $result['added'][] = $dir; + // to support multi-level directory creation + $_phash = isset($dir['phash']) ? $dir['phash'] : null; + while ($_phash && !isset($addedDirs[$_phash]) && $_phash !== $target) { + if ($_dir = $volume->dir($_phash)) { + $addedDirs[$_phash] = true; + $result['added'][] = $_dir; + $_phash = isset($_dir['phash']) ? $_dir['phash'] : null; + } else { + break; + } + } + } + } else { + $result['error'] = $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, 'hash@' . $thash); + break; + } + } else { + $_target = $target; + // file rename for backup + if (isset($renames[$name])) { + $dir = $volume->realpath($_target); + if (isset($hashes[$name])) { + $hash = $hashes[$name]; + } else { + $hash = $volume->getHash($dir, $name); + } + $rnres = $this->rename(array('target' => $hash, 'name' => $volume->uniqueName($dir, $name, $suffix, true, 0))); + if (!empty($rnres['error'])) { + $result['warning'] = $rnres['error']; + if (!is_array($rnres['error'])) { + $errors = array_push($errors, $rnres['error']); + } else { + $errors = array_merge($errors, $rnres['error']); + } + continue; + } + } + } + if (!$_target || ($file = $volume->upload($fp, $_target, $name, $tmpname, ($_target === $target) ? $hashes : array())) === false) { + $errors = array_merge($errors, $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error())); + fclose($fp); + if (!is_uploaded_file($tmpname) && unlink($tmpname)) { + unset($GLOBALS['elFinderTempFiles'][$tmpname]); + } + continue; + } + + is_resource($fp) && fclose($fp); + if (!is_uploaded_file($tmpname)) { + clearstatcache(); + if (!is_file($tmpname) || unlink($tmpname)) { + unset($GLOBALS['elFinderTempFiles'][$tmpname]); + } + } + $result['added'][] = $file; + if ($rnres) { + $result = array_merge_recursive($result, $rnres); + } + } + + if ($errors) { + $result['warning'] = $errors; + } + + if ($GLOBALS['elFinderTempFiles']) { + foreach (array_keys($GLOBALS['elFinderTempFiles']) as $_temp) { + is_file($_temp) && unlink($_temp); + } + } + $result['removed'] = $volume->removed(); + + if (!empty($args['node'])) { + $result['callback'] = array( + 'node' => $args['node'], + 'bind' => 'upload' + ); + } + return $result; + } + + /** + * Copy/move files into new destination + * + * @param array command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function paste($args) + { + $dst = $args['dst']; + $targets = is_array($args['targets']) ? $args['targets'] : array(); + $cut = !empty($args['cut']); + $error = $cut ? self::ERROR_MOVE : self::ERROR_COPY; + $result = array('changed' => array(), 'added' => array(), 'removed' => array(), 'warning' => array()); + + if (($dstVolume = $this->volume($dst)) == false) { + return array('error' => $this->error($error, '#' . $targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#' . $dst)); + } + + $this->itemLock($dst); + + $hashes = $renames = array(); + $suffix = '~'; + if (!empty($args['renames'])) { + $renames = array_flip($args['renames']); + if (is_string($args['suffix']) && !preg_match('/[\/\\?*:|"<>]/', $args['suffix'])) { + $suffix = $args['suffix']; + } + } + if (!empty($args['hashes'])) { + $hashes = array_flip($args['hashes']); + } + + foreach ($targets as $target) { + elFinder::checkAborted(); + + if (($srcVolume = $this->volume($target)) == false) { + $result['warning'] = array_merge($result['warning'], $this->error($error, '#' . $target, self::ERROR_FILE_NOT_FOUND)); + continue; + } + + $rnres = array(); + if ($renames) { + $file = $srcVolume->file($target); + if (isset($renames[$file['name']])) { + $dir = $dstVolume->realpath($dst); + if (isset($hashes[$file['name']])) { + $hash = $hashes[$file['name']]; + } else { + $hash = $dstVolume->getHash($dir, $file['name']); + } + $rnres = $this->rename(array('target' => $hash, 'name' => $dstVolume->uniqueName($dir, $file['name'], $suffix, true, 0))); + if (!empty($rnres['error'])) { + $result['warning'] = array_merge($result['warning'], $rnres['error']); + continue; + } + } + } + + if ($cut && $this->itemLocked($target)) { + $rm = $srcVolume->file($target); + $result['warning'] = array_merge($result['warning'], $this->error(self::ERROR_LOCKED, $rm['name'])); + continue; + } + + if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut, $hashes)) == false) { + $result['warning'] = array_merge($result['warning'], $this->error($dstVolume->error())); + continue; + } + + if ($error = $dstVolume->error()) { + $result['warning'] = array_merge($result['warning'], $this->error($error)); + } + + $dirChange = !empty($file['dirChange']); + unset($file['dirChange']); + if ($dirChange) { + $result['changed'][] = $file; + } else { + $result['added'][] = $file; + } + if ($rnres) { + $result = array_merge_recursive($result, $rnres); + } + } + if (count($result['warning']) < 1) { + unset($result['warning']); + } else { + $result['sync'] = true; + } + + return $result; + } + + /** + * Return file content + * + * @param array $args command arguments + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function get($args) + { + $target = $args['target']; + $volume = $this->volume($target); + $enc = false; + + if (!$volume || ($file = $volume->file($target)) == false) { + return array('error' => $this->error(self::ERROR_OPEN, '#' . $target, self::ERROR_FILE_NOT_FOUND)); + } + + if (($content = $volume->getContents($target)) === false) { + return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error())); + } + + $mime = isset($file['mime']) ? $file['mime'] : ''; + if ($mime && (strtolower(substr($mime, 0, 4)) === 'text' || in_array(strtolower($mime), self::$textMimes))) { + $enc = ''; + if ($content !== '') { + if (!$args['conv'] || $args['conv'] == '1') { + // detect encoding + if (function_exists('mb_detect_encoding')) { + if ($enc = mb_detect_encoding($content, mb_detect_order(), true)) { + $encu = strtoupper($enc); + if ($encu === 'UTF-8' || $encu === 'ASCII') { + $enc = ''; + } + } else { + $enc = 'unknown'; + } + } else if (!preg_match('//u', $content)) { + $enc = 'unknown'; + } + if ($enc === 'unknown') { + $enc = $volume->getOption('encoding'); + if (!$enc || strtoupper($enc) === 'UTF-8') { + $enc = 'unknown'; + } + } + if ($enc && $enc !== 'unknown') { + $utf8 = iconv($enc, 'UTF-8', $content); + if ($utf8 === false && function_exists('mb_convert_encoding')) { + $utf8 = mb_convert_encoding($content, 'UTF-8', $enc); + if (mb_convert_encoding($utf8, $enc, 'UTF-8') !== $content) { + $enc = 'unknown'; + } + } else { + if ($utf8 === false || iconv('UTF-8', $enc, $utf8) !== $content) { + $enc = 'unknown'; + } + } + if ($enc !== 'unknown') { + $content = $utf8; + } + } + if ($enc) { + if ($args['conv'] == '1') { + $args['conv'] = ''; + if ($enc === 'unknown') { + $content = false; + } + } else if ($enc === 'unknown') { + return array('doconv' => $enc); + } + } + if ($args['conv'] == '1') { + $args['conv'] = ''; + } + } + if ($args['conv']) { + $enc = $args['conv']; + if (strtoupper($enc) !== 'UTF-8') { + $_content = $content; + $content = iconv($enc, 'UTF-8', $content); + if ($content === false && function_exists('mb_convert_encoding')) { + $content = mb_convert_encoding($_content, 'UTF-8', $enc); + } + } else { + $enc = ''; + } + } + } + } else { + $content = 'data:' . ($mime ? $mime : 'application/octet-stream') . ';base64,' . base64_encode($content); + } + + if ($enc !== false) { + $json = false; + if ($content !== false) { + $json = json_encode($content); + } + if ($content === false || $json === false || strlen($json) < strlen($content)) { + return array('error' => $this->error(self::ERROR_CONV_UTF8, self::ERROR_NOT_UTF8_CONTENT, $volume->path($target))); + } + } + + $res = array( + 'header' => array( + 'Content-Type: application/json' + ), + 'content' => $content + ); + + // add cache control headers + if ($cacheHeaders = $volume->getOption('cacheHeaders')) { + $res['header'] = array_merge($res['header'], $cacheHeaders); + } + + if ($enc) { + $res['encoding'] = $enc; + } + return $res; + } + + /** + * Save content into text file + * + * @param $args + * + * @return array + * @author Dmitry (dio) Levashov + */ + protected function put($args) + { + $target = $args['target']; + $encoding = isset($args['encoding']) ? $args['encoding'] : ''; + + if (($volume = $this->volume($target)) == false + || ($file = $volume->file($target)) == false) { + return array('error' => $this->error(self::ERROR_SAVE, '#' . $target, self::ERROR_FILE_NOT_FOUND)); + } + + $this->itemLock($target); + + if ($encoding === 'scheme') { + if (preg_match('~^https?://~i', $args['content'])) { + /** @var resource $fp */ + $fp = $this->get_remote_contents($args['content'], 30, 5, 'Mozilla/5.0', $volume->tmpfile()); + if (!$fp) { + return array('error' => self::ERROR_SAVE, $args['content'], self::ERROR_FILE_NOT_FOUND); + } + $fmeta = stream_get_meta_data($fp); + $mime = $this->detectMimeType($fmeta['uri']); + if ($mime === 'unknown') { + $mime = 'application/octet-stream'; + } + $mime = $volume->mimeTypeNormalize($mime, $file['name']); + $args['content'] = 'data:' . $mime . ';base64,' . base64_encode(file_get_contents($fmeta['uri'])); + } + $encoding = ''; + $args['content'] = "\0" . $args['content']; + } else if ($encoding === 'hash') { + $_hash = $args['content']; + if ($_src = $this->getVolume($_hash)) { + if ($_file = $_src->file($_hash)) { + if ($_data = $_src->getContents($_hash)) { + $args['content'] = 'data:' . $file['mime'] . ';base64,' . base64_encode($_data); + } + } + } + $encoding = ''; + $args['content'] = "\0" . $args['content']; + } + if ($encoding) { + $content = iconv('UTF-8', $encoding, $args['content']); + if ($content === false && function_exists('mb_detect_encoding')) { + $content = mb_convert_encoding($args['content'], $encoding, 'UTF-8'); + } + if ($content !== false) { + $args['content'] = $content; + } + } + if (($file = $volume->putContents($target, $args['content'])) == false) { + return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error())); + } + + return array('changed' => array($file)); + } + + /** + * Extract files from archive + * + * @param array $args command arguments + * + * @return array + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + **/ + protected function extract($args) + { + $target = $args['target']; + $makedir = isset($args['makedir']) ? (bool)$args['makedir'] : null; + + if (($volume = $this->volume($target)) == false + || ($file = $volume->file($target)) == false) { + return array('error' => $this->error(self::ERROR_EXTRACT, '#' . $target, self::ERROR_FILE_NOT_FOUND)); + } + + $res = array(); + if ($file = $volume->extract($target, $makedir)) { + $res['added'] = isset($file['read']) ? array($file) : $file; + if ($err = $volume->error()) { + $res['warning'] = $err; + } + } else { + $res['error'] = $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()); + } + return $res; + } + + /** + * Create archive + * + * @param array $args command arguments + * + * @return array + * @throws Exception + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + */ + protected function archive($args) + { + $targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array(); + $name = isset($args['name']) ? $args['name'] : ''; + + if (($volume = $this->volume($targets[0])) == false) { + return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND); + } + + foreach ($targets as $target) { + $this->itemLock($target); + } + + return ($file = $volume->archive($targets, $args['type'], $name)) + ? array('added' => array($file)) + : array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error())); + } + + /** + * Search files + * + * @param array $args command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry Levashov + */ + protected function search($args) + { + $q = trim($args['q']); + $mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array(); + $target = !empty($args['target']) ? $args['target'] : null; + $type = !empty($args['type']) ? $args['type'] : null; + $result = array(); + $errors = array(); + + if ($target) { + if ($volume = $this->volume($target)) { + $result = $volume->search($q, $mimes, $target, $type); + $errors = array_merge($errors, $volume->error()); + } + } else { + foreach ($this->volumes as $volume) { + $result = array_merge($result, $volume->search($q, $mimes, null, $type)); + $errors = array_merge($errors, $volume->error()); + } + } + + $result = array('files' => $result); + if ($errors) { + $result['warning'] = $errors; + } + return $result; + } + + /** + * Return file info (used by client "places" ui) + * + * @param array $args command arguments + * + * @return array + * @throws elFinderAbortException + * @author Dmitry Levashov + */ + protected function info($args) + { + $files = array(); + $compare = null; + // long polling mode + if ($args['compare'] && count($args['targets']) === 1) { + $compare = intval($args['compare']); + $hash = $args['targets'][0]; + if ($volume = $this->volume($hash)) { + $standby = (int)$volume->getOption('plStandby'); + $_compare = false; + if (($syncCheckFunc = $volume->getOption('syncCheckFunc')) && is_callable($syncCheckFunc)) { + $_compare = call_user_func_array($syncCheckFunc, array($volume->realpath($hash), $standby, $compare, $volume, $this)); + } + if ($_compare !== false) { + $compare = $_compare; + } else { + $sleep = max(1, (int)$volume->getOption('tsPlSleep')); + $limit = max(1, $standby / $sleep) + 1; + do { + elFinder::extendTimeLimit(30 + $sleep); + $volume->clearstatcache(); + if (($info = $volume->file($hash)) != false) { + if ($info['ts'] != $compare) { + $compare = $info['ts']; + break; + } + } else { + $compare = 0; + break; + } + if (--$limit) { + sleep($sleep); + } + } while ($limit); + } + } + } else { + foreach ($args['targets'] as $hash) { + elFinder::checkAborted(); + if (($volume = $this->volume($hash)) != false + && ($info = $volume->file($hash)) != false) { + $info['path'] = $volume->path($hash); + $files[] = $info; + } + } + } + + $result = array('files' => $files); + if (!is_null($compare)) { + $result['compare'] = strval($compare); + } + return $result; + } + + /** + * Return image dimensions + * + * @param array $args command arguments + * + * @return array + * @throws ImagickException + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function dim($args) + { + $res = array(); + $target = $args['target']; + + if (($volume = $this->volume($target)) != false) { + if ($dim = $volume->dimensions($target, $args)) { + if (is_array($dim) && isset($dim['dim'])) { + $res = $dim; + } else { + $res = array('dim' => $dim); + if ($subImgLink = $volume->getSubstituteImgLink($target, explode('x', $dim))) { + $res['url'] = $subImgLink; + } + } + } + } + + return $res; + } + + /** + * Resize image + * + * @param array command arguments + * + * @return array + * @throws ImagickException + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + */ + protected function resize($args) + { + $target = $args['target']; + $width = (int)$args['width']; + $height = (int)$args['height']; + $x = (int)$args['x']; + $y = (int)$args['y']; + $mode = $args['mode']; + $bg = $args['bg']; + $degree = (int)$args['degree']; + $quality = (int)$args['quality']; + + if (($volume = $this->volume($target)) == false + || ($file = $volume->file($target)) == false) { + return array('error' => $this->error(self::ERROR_RESIZE, '#' . $target, self::ERROR_FILE_NOT_FOUND)); + } + + if ($mode !== 'rotate' && ($width < 1 || $height < 1)) { + return array('error' => $this->error(self::ERROR_RESIZESIZE)); + } + return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree, $quality)) + ? (!empty($file['losslessRotate']) ? $file : array('changed' => array($file))) + : array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error())); + } + + /** + * Return content URL + * + * @param array $args command arguments + * + * @return array + * @author Naoki Sawada + **/ + protected function url($args) + { + $target = $args['target']; + $options = isset($args['options']) ? $args['options'] : array(); + if (($volume = $this->volume($target)) != false) { + if (!$volume->commandDisabled('url')) { + $url = $volume->getContentUrl($target, $options); + return $url ? array('url' => $url) : array(); + } + } + return array(); + } + + /** + * Output callback result with JavaScript that control elFinder + * or HTTP redirect to callbackWindowURL + * + * @param array command arguments + * + * @throws elFinderAbortException + * @author Naoki Sawada + */ + protected function callback($args) + { + $checkReg = '/[^a-zA-Z0-9;._-]/'; + $node = (isset($args['node']) && !preg_match($checkReg, $args['node'])) ? $args['node'] : ''; + $json = (isset($args['json']) && json_decode($args['json'])) ? $args['json'] : '{}'; + $bind = (isset($args['bind']) && !preg_match($checkReg, $args['bind'])) ? $args['bind'] : ''; + $done = (!empty($args['done'])); + + while (ob_get_level()) { + if (!ob_end_clean()) { + break; + } + } + + if ($done || !$this->callbackWindowURL) { + $script = ''; + if ($node) { + if ($bind) { + $trigger = 'elf.trigger(\'' . $bind . '\', data);'; + $triggerdone = 'elf.trigger(\'' . $bind . 'done\');'; + $triggerfail = 'elf.trigger(\'' . $bind . 'fail\', data);'; + } else { + $trigger = $triggerdone = $triggerfail = ''; + } + $script .= ' var w = window.opener || window.parent || window; try { - var elf = w.document.getElementById(\''.$node.'\').elfinder; + var elf = w.document.getElementById(\'' . $node . '\').elfinder; if (elf) { - var data = '.$json.'; + var data = ' . $json . '; if (data.error) { - '.$triggerfail.' + ' . $triggerfail . ' elf.error(data.error); } else { data.warning && elf.error(data.warning); data.removed && data.removed.length && elf.remove(data); data.added && data.added.length && elf.add(data); data.changed && data.changed.length && elf.change(data); - '.$trigger.' - '.$triggerdone.' + ' . $trigger . ' + ' . $triggerdone . ' data.sync && elf.sync(); } } } catch(e) { // for CORS - w.postMessage && w.postMessage(JSON.stringify({bind:\''.$bind.'\',data:'.$json.'}), \'*\'); + w.postMessage && w.postMessage(JSON.stringify({bind:\'' . $bind . '\',data:' . $json . '}), \'*\'); }'; - } - $script .= 'window.close();'; - - $out = 'Close this window'; - - header('Content-Type: text/html; charset=utf-8'); - header('Content-Length: '.strlen($out)); - header('Cache-Control: private'); - header('Pragma: no-cache'); - - echo $out; - - } else { - $url = $this->callbackWindowURL; - $url .= ((strpos($url, '?') === false)? '?' : '&') - . '&node=' . rawurlencode($node) - . (($json !== '{}')? ('&json=' . rawurlencode($json)) : '') - . ($bind? ('&bind=' . rawurlencode($bind)) : '') - . '&done=1'; - - header('Location: ' . $url); - - } - throw new elFinderAbortException(); - } + } + $script .= 'window.close();'; - /** - * PHP error handler, catch error types only E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE - * - * @param int $errno - * @param string $errstr - * @param string $errfile - * @param int $errline - * @return boolean - */ - public static function phpErrorHandler($errno, $errstr, $errfile, $errline) { - static $base = null; + $out = 'Close this window'; - $proc = false; + header('Content-Type: text/html; charset=utf-8'); + header('Content-Length: ' . strlen($out)); + header('Cache-Control: private'); + header('Pragma: no-cache'); - if (is_null($base)) { - $base = dirname(__FILE__) . DIRECTORY_SEPARATOR; - } - - if (! (error_reporting() & $errno)) { - return $proc; - } - - $errfile = str_replace($base, '', $errfile); - - switch ($errno) { - case E_WARNING: - case E_USER_WARNING: - elFinder::$phpErrors[] = "WARNING: $errstr in $errfile line $errline."; - $proc = true; - break; - - case E_NOTICE: - case E_USER_NOTICE: - elFinder::$phpErrors[] = "NOTICE: $errstr in $errfile line $errline."; - $proc = true; - break; - - case E_STRICT: - elFinder::$phpErrors[] = "STRICT: $errstr in $errfile line $errline."; - $proc = true; - break; - - case E_RECOVERABLE_ERROR: - elFinder::$phpErrors[] = "RECOVERABLE_ERROR: $errstr in $errfile line $errline."; - $proc = true; - break; - } - - if (defined('E_DEPRECATED')) { - switch ($errno) { - case E_DEPRECATED: - case E_USER_DEPRECATED: - elFinder::$phpErrors[] = "DEPRECATED: $errstr in $errfile line $errline."; - $proc = true; - break; - } - } - - return $proc; - } + echo $out; - /***************************************************************************/ - /* utils */ - /***************************************************************************/ - - /** - * Return root - file's owner - * - * @param string file hash - * @return elFinderVolumeDriver|boolean (false) - * @author Dmitry (dio) Levashov - **/ - protected function volume($hash) { - foreach ($this->volumes as $id => $v) { - if (strpos(''.$hash, $id) === 0) { - return $this->volumes[$id]; - } - } - return false; - } - - /** - * Return files info array - * - * @param array $data one file info or files info - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function toArray($data) { - return isset($data['hash']) || !is_array($data) ? array($data) : $data; - } - - /** - * Return fils hashes list - * - * @param array $files files info - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function hashes($files) { - $ret = array(); - foreach ($files as $file) { - $ret[] = $file['hash']; - } - return $ret; - } - - /** - * Remove from files list hidden files and files with required mime types - * - * @param array $files files info - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function filter($files) { - $exists = array(); - foreach ($files as $i => $file) { - if (isset($exists[$file['hash']]) || !empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) { - unset($files[$i]); - } - $exists[$file['hash']] = true; - } - return array_values($files); - } - - protected function utime() { - $time = explode(" ", microtime()); - return (double)$time[1] + (double)$time[0]; - } - - /** - * Return Network mount volume unique ID - * - * @param array $netVolumes Saved netvolumes array - * @param string $prefix Id prefix - * @return string|false - * @author Naoki Sawada - **/ - protected function getNetVolumeUniqueId($netVolumes = null, $prefix = 'nm') { - if (is_null($netVolumes)) { - $netVolumes = $this->getNetVolumes(); - } - $ids = array(); - foreach($netVolumes as $vOps) { - if (isset($vOps['id']) && strpos($vOps['id'], $prefix) === 0) { - $ids[$vOps['id']] = true; - } - } - if (! $ids) { - $id = $prefix.'1'; - } else { - $i = 0; - while(isset($ids[$prefix.++$i]) && $i < 10000); - $id = $prefix.$i; - if (isset($ids[$id])) { - $id = false; - } - } - return $id; - } - - /** - * Is item locked? - * - * @param string $hash - * @return boolean - */ - protected function itemLocked($hash) { - if (! elFinder::$commonTempPath) { - return false; - } - $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . $hash . '.lock'; - if (file_exists($lock)) { - if (filemtime($lock) + $this->itemLockExpire < time()) { - unlink($lock); - return false; - } - return true; - } - - return false; - } - - /** - * Do lock target item - * - * @param array|string $hashes - * @param boolean $autoUnlock - * @return void - */ - protected function itemLock($hashes, $autoUnlock = true) { - if (! elFinder::$commonTempPath) { - return; - } - if (! is_array($hashes)) { - $hashes = array($hashes); - } - foreach($hashes as $hash) { - $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . $hash . '.lock'; - if ($this->itemLocked($hash)) { - $cnt = file_get_contents($lock) + 1; - } else { - $cnt = 1; - } - if (file_put_contents($lock, $cnt, LOCK_EX)) { - if ($autoUnlock) { - $this->autoUnlocks[] = $hash; - } - } - } - } - - /** - * Do unlock target item - * - * @param string $hash - * @return boolean - */ - protected function itemUnlock($hash) { - if (! $this->itemLocked($hash)) { - return true; - } - $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . $hash . '.lock'; - $cnt = file_get_contents($lock); - if (--$cnt < 1) { - unlink($lock); - return true; - } else { - file_put_contents($lock, $cnt, LOCK_EX); - return false; - } - } - - /** - * unlock locked items on command completion - * - * @return void - */ - public function itemAutoUnlock() { - if ($this->autoUnlocks) { - foreach($this->autoUnlocks as $hash) { - $this->itemUnlock($hash); - } - $this->autoUnlocks = array(); - } - } - - /** - * Ensure directories recursively - * - * @param object $volume Volume object - * @param string $target Target hash - * @param array $dirs Array of directory tree to ensure - * @param string $path Relative path form target hash - * @return array|false array('stats' => array([stat of maked directory]), 'hashes' => array('[path]' => '[hash]'), 'makes' => array([New directory hashes]), 'error' => array([Error name])) - * @author Naoki Sawada - **/ - protected function ensureDirsRecursively($volume, $target, $dirs, $path = '') { - $res = array('stats' => array(), 'hashes' => array(), 'makes' => array(), 'error' => array()); - foreach($dirs as $name => $sub) { - $name = (string)$name; - $dir = $newDir = null; - if ((($parent = $volume->realpath($target)) && ($dir = $volume->dir($volume->getHash($parent, $name)))) || ($newDir = $volume->mkdir($target, $name))) { - $_path = $path . '/' . $name; - if ($newDir) { - $res['makes'][] = $newDir['hash']; - $dir = $newDir; - } - $res['stats'][] = $dir; - $res['hashes'][$_path] = $dir['hash']; - if (count($sub)) { - $res = array_merge_recursive($res, $this->ensureDirsRecursively($volume, $dir['hash'], $sub, $_path)); - } - } else { - $res['error'][] = $name; - } - } - return $res; - } - - /***************************************************************************/ - /* static utils */ - /***************************************************************************/ - - /** - * Return full version of API that this connector supports all functions - * - * @return string - */ - public static function getApiFullVersion() { - return (string)self::$ApiVersion . '.' . (string)self::$ApiRevision; - } - - /** - * Return Is Animation Gif - * - * @param string $path server local path of target image - * @return bool - */ - public static function isAnimationGif($path) { - list(, , $type) = getimagesize($path); - switch ($type) { - case IMAGETYPE_GIF: - break; - default: - return false; - } - - $imgcnt = 0; - $fp = fopen($path, 'rb'); - fread($fp, 4); - $c = fread($fp,1); - if (ord($c) != 0x39) { // GIF89a - return false; - } - - while (!feof($fp)) { - do { - $c = fread($fp, 1); - } while(ord($c) != 0x21 && !feof($fp)); - - if (feof($fp)) { - break; - } - - $c2 = fread($fp,2); - if (bin2hex($c2) == "f904") { - $imgcnt++; - if ($imgcnt === 2) { - break; - } - } - - if (feof($fp)) { - break; - } - } - - if ($imgcnt > 1) { - return true; - } else { - return false; - } - } + } else { + $url = $this->callbackWindowURL; + $url .= ((strpos($url, '?') === false) ? '?' : '&') + . '&node=' . rawurlencode($node) + . (($json !== '{}') ? ('&json=' . rawurlencode($json)) : '') + . ($bind ? ('&bind=' . rawurlencode($bind)) : '') + . '&done=1'; - /** - * Return Is Animation Png - * - * @param string $path server local path of target image - * @return bool - */ - public static function isAnimationPng($path) { - list(, , $type) = getimagesize($path); - switch ($type) { - case IMAGETYPE_PNG: - break; - default: - return false; - } - - $fp = fopen($path, 'rb'); - $img_bytes = fread($fp, 1024); - fclose($fp); - if ($img_bytes) { - if (strpos(substr($img_bytes, 0, strpos($img_bytes, 'IDAT')), 'acTL') !== false) { - return true; - } - } - return false; - } + header('Location: ' . $url); - /** - * Return Is seekable stream resource - * - * @param resource $resource - * @return bool - */ - public static function isSeekableStream($resource) { - $metadata = stream_get_meta_data($resource); - return $metadata['seekable']; - } + } + throw new elFinderAbortException(); + } - /** - * Rewind stream resource - * - * @param resource $resource - * @return void - */ - public static function rewind($resource) { - self::isSeekableStream($resource) && rewind($resource); - } + /** + * PHP error handler, catch error types only E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * + * @return boolean + */ + public static function phpErrorHandler($errno, $errstr, $errfile, $errline) + { + static $base = null; - /** - * serialize and base64_encode of session data (If needed) - * - * @deprecated - * @param mixed $var target variable - * @author Naoki Sawada - * @return mixed|string - */ - public static function sessionDataEncode($var) { - if (self::$base64encodeSessionData) { - $var = base64_encode(serialize($var)); - } - return $var; - } + $proc = false; - /** - * base64_decode and unserialize of session data (If needed) - * - * @deprecated - * @param mixed $var target variable - * @param bool $checkIs data type for check (array|string|object|int) - * @author Naoki Sawada - * @return bool|mixed - */ - public static function sessionDataDecode(&$var, $checkIs = null) { - if (self::$base64encodeSessionData) { - $data = unserialize(base64_decode($var)); - } else { - $data = $var; - } - $chk = true; - if ($checkIs) { - switch ($checkIs) { - case 'array': - $chk = is_array($data); - break; - case 'string': - $chk = is_string($data); - break; - case 'object': - $chk = is_object($data); - break; - case 'int': - $chk = is_int($data); - break; - } - } - if (!$chk) { - unset($var); - return false; - } - return $data; - } - - /** - * Call session_write_close() if session is restarted - * - * @deprecated - * @return void - */ - public static function sessionWrite() { - if (session_id()) { - session_write_close(); - } - } + if (is_null($base)) { + $base = dirname(__FILE__) . DIRECTORY_SEPARATOR; + } - /** - * Return elFinder static variable - * - * @param $key - * @return mixed|null - */ - public static function getStaticVar($key) { - return isset(elFinder::$$key)? elFinder::$$key : null; - } + if (!(error_reporting() & $errno)) { + return $proc; + } - /** - * Extend PHP execution time limit and also check connection is aborted - * - * @param Int $time - * @return void - * @throws elFinderAbortException - */ - public static function extendTimeLimit($time = null) { - static $defLimit = null; - if (! self::aborted()) { - if (is_null($defLimit)) { - $defLimit = ini_get('max_execution_time'); - } - if ($defLimit != 0) { - $time = is_null($time)? $defLimit : max($defLimit, $time); - set_time_limit($time); - } - } else { - throw new elFinderAbortException(); - } - } + $errfile = str_replace($base, '', $errfile); - /** - * Check connection is aborted - * Script stop immediately if connection aborted - * - * @return void - * @throws elFinderAbortException - */ - public static function checkAborted() { - elFinder::extendTimeLimit(); - } - - /** - * Return bytes from php.ini value - * - * @param string $iniName - * @param string $val - * @return number - */ - public static function getIniBytes($iniName = '', $val = '') { - if ($iniName !== '') { - $val = ini_get($iniName); - if ($val === false) { - return 0; - } - } - $val = trim($val, "bB \t\n\r\0\x0B"); - $last = strtolower($val[strlen($val) - 1]); - $val = (int)$val; - switch($last) { - case 't': - $val *= 1024; - case 'g': - $val *= 1024; - case 'm': - $val *= 1024; - case 'k': - $val *= 1024; - } - return $val; - } + switch ($errno) { + case E_WARNING: + case E_USER_WARNING: + elFinder::$phpErrors[] = "WARNING: $errstr in $errfile line $errline."; + $proc = true; + break; - /** - * Get script url. - * - * @return string full URL - * - * @author Naoki Sawada - */ - public static function getConnectorUrl() { - $https = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off'); - $url = ($https ? 'https://' : 'http://') - .$_SERVER['SERVER_NAME'] // host - .(((! $https && $_SERVER['SERVER_PORT'] == 80) || ($https && $_SERVER['SERVER_PORT'] == 443)) ? '' : (':' . $_SERVER['SERVER_PORT'])) // port - .$_SERVER['REQUEST_URI']; // path & query - list($url) = explode('?', $url); - - return $url; - } + case E_NOTICE: + case E_USER_NOTICE: + elFinder::$phpErrors[] = "NOTICE: $errstr in $errfile line $errline."; + $proc = true; + break; - /** - * Get stream resource pointer by URL - * - * @param array $data array('target'=>'URL', 'headers' => array()) - * @param int $redirectLimit - * @return resource|boolean - * - * @author Naoki Sawada - */ - public static function getStreamByUrl($data, $redirectLimit = 5) { - if (isset($data['target'])) { - $data = array( - 'cnt' => 0, - 'url' => $data['target'], - 'headers' => isset($data['headers'])? $data['headers'] : array(), - 'cookies' => array(), - ); - } - if ($data['cnt'] > $redirectLimit) { - return false; - } - $dlurl = $data['url']; - $data['url'] = ''; - $headers = $data['headers']; - - if ($dlurl) { - $url = parse_url($dlurl); - $ports = array( - 'http' => '80', - 'https' => '443', - 'ftp' => '21' - ); - $url['scheme'] = strtolower($url['scheme']); - if (! isset($url['port']) && isset($ports[$url['scheme']])) { - $url['port'] = $ports[$url['scheme']]; - } - if (! isset($url['port'])) { - return false; - } - $cookies = array(); - if ($data['cookies']) { - foreach ($data['cookies'] as $d => $c) { - if (strpos($url['host'], $d) !== false) { - $cookies[] = $c; - } - } - } + case E_STRICT: + elFinder::$phpErrors[] = "STRICT: $errstr in $errfile line $errline."; + $proc = true; + break; - $transport = ($url['scheme'] === 'https')? 'tls' : 'tcp'; - $query = isset($url['query']) ? '?'.$url['query'] : ''; - $stream = stream_socket_client($transport.'://'.$url['host'].':'.$url['port']); - stream_set_timeout($stream, 300); - fputs($stream, "GET {$url['path']}{$query} HTTP/1.1\r\n"); - fputs($stream, "Host: {$url['host']}\r\n"); - foreach($headers as $header) { - fputs($stream, trim($header, "\r\n")."\r\n"); - } - fputs($stream, "Connection: Close\r\n"); - if ($cookies) { - fputs($stream, 'Cookie: '.implode('; ', $cookies)."\r\n"); - } - fputs($stream, "\r\n"); - while (($res = trim(fgets($stream))) !== '') { - // find redirect - if (preg_match('/^Location: (.+)$/', $res, $m)) { - $data['url'] = $m[1]; - } - // fetch cookie - if (strpos($res, 'Set-Cookie:') === 0) { - $domain = $url['host']; - if (preg_match('/^Set-Cookie:(.+)(?:domain=\s*([^ ;]+))?/i', $res, $c1)) { - if (!empty($c1[2])) { - $domain = trim($c1[2]); - } - if (preg_match('/([^ ]+=[^;]+)/', $c1[1], $c2)) { - $data['cookies'][$domain] = $c2[1]; - } - } - } - } - if ($data['url']) { - ++$data['cnt']; - fclose($stream); + case E_RECOVERABLE_ERROR: + elFinder::$phpErrors[] = "RECOVERABLE_ERROR: $errstr in $errfile line $errline."; + $proc = true; + break; + } - return self::getStreamByUrl($data, $redirectLimit); - } + if (defined('E_DEPRECATED')) { + switch ($errno) { + case E_DEPRECATED: + case E_USER_DEPRECATED: + elFinder::$phpErrors[] = "DEPRECATED: $errstr in $errfile line $errline."; + $proc = true; + break; + } + } - return $stream; - } - - return false; - } - - /** - * Gets the fetch cookie file for curl. - * - * @return string The fetch cookie file. - */ - public function getFetchCookieFile() { - $file = ''; - if ($tmpDir = $this->getTempDir()) { - $file = $tmpDir . '/.elFinderAnonymousCookie'; - } - return $file; - } + return $proc; + } - /** - * Call curl_exec() with supported redirect on `safe_mode` or `open_basedir` - * - * @param resource $curl - * @param array $options - * @param array $headers - * - * @throws \Exception - * - * @return mixed - * - * @author Naoki Sawada - */ - public static function curlExec($curl, $options = array(), $headers = array()) { - $followLocation = (!ini_get('safe_mode') && !ini_get('open_basedir')); - if ($followLocation) { - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - } - - if ($options) { - curl_setopt_array($curl, $options); - } - - if ($headers) { - curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); - } - - $result = curl_exec($curl); - - if (! $followLocation && $redirect = curl_getinfo($curl, CURLINFO_REDIRECT_URL)) { - if ($stream = self::getStreamByUrl(array('target' => $redirect, 'headers' => $headers))) { - $result = stream_get_contents($stream); - } - } - - if ($result === false) { - if (curl_errno($curl)) { - throw new \Exception('curl_exec() failed: '.curl_error($curl)); - } else { - throw new \Exception('curl_exec(): empty response'); - } - } - - curl_close($curl); - - return $result; - } - - /** - * Return bool that current request was aborted by client side - * - * @return boolean - */ - public static function aborted() { - if ($file = self::$abortCheckFile) { - (version_compare(PHP_VERSION, '5.3.0') >= 0) ? clearstatcache(true, $file) : clearstatcache(); - if (! is_file($file)) { - // GC (expire 12h) - list($ptn) = explode('elfreq', $file); - self::GlobGC($ptn . 'elfreq*', 43200); - return true; - } - } - return false; - } - - /** - * Return array ["name without extention", "extention"] by filename - * - * @param string $name - * @return array - */ - public static function splitFileExtention($name) { - if (preg_match('/^(.+?)?\.((?:tar\.(?:gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(?:gz|bz2)|[a-z0-9]{1,10})$/i', $name, $m)) { - return array((string)$m[1], $m[2]); - } else { - return array($name, ''); - } - } - - /** - * Gets the memory size by imageinfo. - * - * @param array $imgInfo array that result of getimagesize() - * - * @return integer The memory size by imageinfo. - */ - public static function getMemorySizeByImageInfo($imgInfo) { - $width = $imgInfo[0]; - $height = $imgInfo[1]; - $bits = isset($imgInfo['bits'])? $imgInfo['bits'] : 24; - $channels = isset($imgInfo['channels'])? $imgInfo['channels'] : 3; - return round(($width * $height * $bits * $channels / 8 + Pow(2, 16)) * 1.65); - } + /***************************************************************************/ + /* utils */ + /***************************************************************************/ - /** - * Auto expand memory for GD processing - * - * @param array $imgInfos The image infos - */ - public static function expandMemoryForGD($imgInfos) { - if (elFinder::$memoryLimitGD != 0 && $imgInfos && is_array($imgInfos)) { - if (!is_array($imgInfos[0])) { - $imgInfos = array($imgInfos); - } - $limit = self::getIniBytes('', elFinder::$memoryLimitGD); - $memLimit = self::getIniBytes('memory_limit'); - $needs = 0; - foreach($imgInfos as $info) { - $needs += self::getMemorySizeByImageInfo($info); - } - $needs += memory_get_usage(); - if ($needs > $memLimit && ($limit == -1 || $limit > $needs)) { - ini_set('memory_limit', $needs); - } - } - } + /** + * Return root - file's owner + * + * @param string file hash + * + * @return elFinderVolumeDriver|boolean (false) + * @author Dmitry (dio) Levashov + **/ + protected function volume($hash) + { + foreach ($this->volumes as $id => $v) { + if (strpos('' . $hash, $id) === 0) { + return $this->volumes[$id]; + } + } + return false; + } + + /** + * Return files info array + * + * @param array $data one file info or files info + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function toArray($data) + { + return isset($data['hash']) || !is_array($data) ? array($data) : $data; + } + + /** + * Return fils hashes list + * + * @param array $files files info + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function hashes($files) + { + $ret = array(); + foreach ($files as $file) { + $ret[] = $file['hash']; + } + return $ret; + } + + /** + * Remove from files list hidden files and files with required mime types + * + * @param array $files files info + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function filter($files) + { + $exists = array(); + foreach ($files as $i => $file) { + if (isset($exists[$file['hash']]) || !empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) { + unset($files[$i]); + } + $exists[$file['hash']] = true; + } + return array_values($files); + } + + protected function utime() + { + $time = explode(" ", microtime()); + return (double)$time[1] + (double)$time[0]; + } + + /** + * Return Network mount volume unique ID + * + * @param array $netVolumes Saved netvolumes array + * @param string $prefix Id prefix + * + * @return string|false + * @author Naoki Sawada + **/ + protected function getNetVolumeUniqueId($netVolumes = null, $prefix = 'nm') + { + if (is_null($netVolumes)) { + $netVolumes = $this->getNetVolumes(); + } + $ids = array(); + foreach ($netVolumes as $vOps) { + if (isset($vOps['id']) && strpos($vOps['id'], $prefix) === 0) { + $ids[$vOps['id']] = true; + } + } + if (!$ids) { + $id = $prefix . '1'; + } else { + $i = 0; + while (isset($ids[$prefix . ++$i]) && $i < 10000) ; + $id = $prefix . $i; + if (isset($ids[$id])) { + $id = false; + } + } + return $id; + } + + /** + * Is item locked? + * + * @param string $hash + * + * @return boolean + */ + protected function itemLocked($hash) + { + if (!elFinder::$commonTempPath) { + return false; + } + $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . $hash . '.lock'; + if (file_exists($lock)) { + if (filemtime($lock) + $this->itemLockExpire < time()) { + unlink($lock); + return false; + } + return true; + } + + return false; + } + + /** + * Do lock target item + * + * @param array|string $hashes + * @param boolean $autoUnlock + * + * @return void + */ + protected function itemLock($hashes, $autoUnlock = true) + { + if (!elFinder::$commonTempPath) { + return; + } + if (!is_array($hashes)) { + $hashes = array($hashes); + } + foreach ($hashes as $hash) { + $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . $hash . '.lock'; + if ($this->itemLocked($hash)) { + $cnt = file_get_contents($lock) + 1; + } else { + $cnt = 1; + } + if (file_put_contents($lock, $cnt, LOCK_EX)) { + if ($autoUnlock) { + $this->autoUnlocks[] = $hash; + } + } + } + } + + /** + * Do unlock target item + * + * @param string $hash + * + * @return boolean + */ + protected function itemUnlock($hash) + { + if (!$this->itemLocked($hash)) { + return true; + } + $lock = elFinder::$commonTempPath . DIRECTORY_SEPARATOR . $hash . '.lock'; + $cnt = file_get_contents($lock); + if (--$cnt < 1) { + unlink($lock); + return true; + } else { + file_put_contents($lock, $cnt, LOCK_EX); + return false; + } + } + + /** + * unlock locked items on command completion + * + * @return void + */ + public function itemAutoUnlock() + { + if ($this->autoUnlocks) { + foreach ($this->autoUnlocks as $hash) { + $this->itemUnlock($hash); + } + $this->autoUnlocks = array(); + } + } + + /** + * Ensure directories recursively + * + * @param object $volume Volume object + * @param string $target Target hash + * @param array $dirs Array of directory tree to ensure + * @param string $path Relative path form target hash + * + * @return array|false array('stats' => array([stat of maked directory]), 'hashes' => array('[path]' => '[hash]'), 'makes' => array([New directory hashes]), 'error' => array([Error name])) + * @author Naoki Sawada + **/ + protected function ensureDirsRecursively($volume, $target, $dirs, $path = '') + { + $res = array('stats' => array(), 'hashes' => array(), 'makes' => array(), 'error' => array()); + foreach ($dirs as $name => $sub) { + $name = (string)$name; + $dir = $newDir = null; + if ((($parent = $volume->realpath($target)) && ($dir = $volume->dir($volume->getHash($parent, $name)))) || ($newDir = $volume->mkdir($target, $name))) { + $_path = $path . '/' . $name; + if ($newDir) { + $res['makes'][] = $newDir['hash']; + $dir = $newDir; + } + $res['stats'][] = $dir; + $res['hashes'][$_path] = $dir['hash']; + if (count($sub)) { + $res = array_merge_recursive($res, $this->ensureDirsRecursively($volume, $dir['hash'], $sub, $_path)); + } + } else { + $res['error'][] = $name; + } + } + return $res; + } + + /***************************************************************************/ + /* static utils */ + /***************************************************************************/ + + /** + * Return full version of API that this connector supports all functions + * + * @return string + */ + public static function getApiFullVersion() + { + return (string)self::$ApiVersion . '.' . (string)self::$ApiRevision; + } + + /** + * Return Is Animation Gif + * + * @param string $path server local path of target image + * + * @return bool + */ + public static function isAnimationGif($path) + { + list(, , $type) = getimagesize($path); + switch ($type) { + case IMAGETYPE_GIF: + break; + default: + return false; + } + + $imgcnt = 0; + $fp = fopen($path, 'rb'); + fread($fp, 4); + $c = fread($fp, 1); + if (ord($c) != 0x39) { // GIF89a + return false; + } + + while (!feof($fp)) { + do { + $c = fread($fp, 1); + } while (ord($c) != 0x21 && !feof($fp)); + + if (feof($fp)) { + break; + } + + $c2 = fread($fp, 2); + if (bin2hex($c2) == "f904") { + $imgcnt++; + if ($imgcnt === 2) { + break; + } + } + + if (feof($fp)) { + break; + } + } + + if ($imgcnt > 1) { + return true; + } else { + return false; + } + } + + /** + * Return Is Animation Png + * + * @param string $path server local path of target image + * + * @return bool + */ + public static function isAnimationPng($path) + { + list(, , $type) = getimagesize($path); + switch ($type) { + case IMAGETYPE_PNG: + break; + default: + return false; + } + + $fp = fopen($path, 'rb'); + $img_bytes = fread($fp, 1024); + fclose($fp); + if ($img_bytes) { + if (strpos(substr($img_bytes, 0, strpos($img_bytes, 'IDAT')), 'acTL') !== false) { + return true; + } + } + return false; + } + + /** + * Return Is seekable stream resource + * + * @param resource $resource + * + * @return bool + */ + public static function isSeekableStream($resource) + { + $metadata = stream_get_meta_data($resource); + return $metadata['seekable']; + } + + /** + * Rewind stream resource + * + * @param resource $resource + * + * @return void + */ + public static function rewind($resource) + { + self::isSeekableStream($resource) && rewind($resource); + } + + /** + * serialize and base64_encode of session data (If needed) + * + * @deprecated + * + * @param mixed $var target variable + * + * @author Naoki Sawada + * @return mixed|string + */ + public static function sessionDataEncode($var) + { + if (self::$base64encodeSessionData) { + $var = base64_encode(serialize($var)); + } + return $var; + } + + /** + * base64_decode and unserialize of session data (If needed) + * + * @deprecated + * + * @param mixed $var target variable + * @param bool $checkIs data type for check (array|string|object|int) + * + * @author Naoki Sawada + * @return bool|mixed + */ + public static function sessionDataDecode(&$var, $checkIs = null) + { + if (self::$base64encodeSessionData) { + $data = unserialize(base64_decode($var)); + } else { + $data = $var; + } + $chk = true; + if ($checkIs) { + switch ($checkIs) { + case 'array': + $chk = is_array($data); + break; + case 'string': + $chk = is_string($data); + break; + case 'object': + $chk = is_object($data); + break; + case 'int': + $chk = is_int($data); + break; + } + } + if (!$chk) { + unset($var); + return false; + } + return $data; + } + + /** + * Call session_write_close() if session is restarted + * + * @deprecated + * @return void + */ + public static function sessionWrite() + { + if (session_id()) { + session_write_close(); + } + } + + /** + * Return elFinder static variable + * + * @param $key + * + * @return mixed|null + */ + public static function getStaticVar($key) + { + return isset(elFinder::$$key) ? elFinder::$$key : null; + } + + /** + * Extend PHP execution time limit and also check connection is aborted + * + * @param Int $time + * + * @return void + * @throws elFinderAbortException + */ + public static function extendTimeLimit($time = null) + { + static $defLimit = null; + if (!self::aborted()) { + if (is_null($defLimit)) { + $defLimit = ini_get('max_execution_time'); + } + if ($defLimit != 0) { + $time = is_null($time) ? $defLimit : max($defLimit, $time); + set_time_limit($time); + } + } else { + throw new elFinderAbortException(); + } + } + + /** + * Check connection is aborted + * Script stop immediately if connection aborted + * + * @return void + * @throws elFinderAbortException + */ + public static function checkAborted() + { + elFinder::extendTimeLimit(); + } + + /** + * Return bytes from php.ini value + * + * @param string $iniName + * @param string $val + * + * @return number + */ + public static function getIniBytes($iniName = '', $val = '') + { + if ($iniName !== '') { + $val = ini_get($iniName); + if ($val === false) { + return 0; + } + } + $val = trim($val, "bB \t\n\r\0\x0B"); + $last = strtolower($val[strlen($val) - 1]); + $val = (int)$val; + switch ($last) { + case 't': + $val *= 1024; + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + return $val; + } + + /** + * Get script url. + * + * @return string full URL + * @author Naoki Sawada + */ + public static function getConnectorUrl() + { + $https = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off'); + $url = ($https ? 'https://' : 'http://') + . $_SERVER['SERVER_NAME'] // host + . (((!$https && $_SERVER['SERVER_PORT'] == 80) || ($https && $_SERVER['SERVER_PORT'] == 443)) ? '' : (':' . $_SERVER['SERVER_PORT'])) // port + . $_SERVER['REQUEST_URI']; // path & query + list($url) = explode('?', $url); + + return $url; + } + + /** + * Get stream resource pointer by URL + * + * @param array $data array('target'=>'URL', 'headers' => array()) + * @param int $redirectLimit + * + * @return resource|boolean + * @author Naoki Sawada + */ + public static function getStreamByUrl($data, $redirectLimit = 5) + { + if (isset($data['target'])) { + $data = array( + 'cnt' => 0, + 'url' => $data['target'], + 'headers' => isset($data['headers']) ? $data['headers'] : array(), + 'cookies' => array(), + ); + } + if ($data['cnt'] > $redirectLimit) { + return false; + } + $dlurl = $data['url']; + $data['url'] = ''; + $headers = $data['headers']; + + if ($dlurl) { + $url = parse_url($dlurl); + $ports = array( + 'http' => '80', + 'https' => '443', + 'ftp' => '21' + ); + $url['scheme'] = strtolower($url['scheme']); + if (!isset($url['port']) && isset($ports[$url['scheme']])) { + $url['port'] = $ports[$url['scheme']]; + } + if (!isset($url['port'])) { + return false; + } + $cookies = array(); + if ($data['cookies']) { + foreach ($data['cookies'] as $d => $c) { + if (strpos($url['host'], $d) !== false) { + $cookies[] = $c; + } + } + } + + $transport = ($url['scheme'] === 'https') ? 'tls' : 'tcp'; + $query = isset($url['query']) ? '?' . $url['query'] : ''; + $stream = stream_socket_client($transport . '://' . $url['host'] . ':' . $url['port']); + stream_set_timeout($stream, 300); + fputs($stream, "GET {$url['path']}{$query} HTTP/1.1\r\n"); + fputs($stream, "Host: {$url['host']}\r\n"); + foreach ($headers as $header) { + fputs($stream, trim($header, "\r\n") . "\r\n"); + } + fputs($stream, "Connection: Close\r\n"); + if ($cookies) { + fputs($stream, 'Cookie: ' . implode('; ', $cookies) . "\r\n"); + } + fputs($stream, "\r\n"); + while (($res = trim(fgets($stream))) !== '') { + // find redirect + if (preg_match('/^Location: (.+)$/', $res, $m)) { + $data['url'] = $m[1]; + } + // fetch cookie + if (strpos($res, 'Set-Cookie:') === 0) { + $domain = $url['host']; + if (preg_match('/^Set-Cookie:(.+)(?:domain=\s*([^ ;]+))?/i', $res, $c1)) { + if (!empty($c1[2])) { + $domain = trim($c1[2]); + } + if (preg_match('/([^ ]+=[^;]+)/', $c1[1], $c2)) { + $data['cookies'][$domain] = $c2[1]; + } + } + } + } + if ($data['url']) { + ++$data['cnt']; + fclose($stream); + + return self::getStreamByUrl($data, $redirectLimit); + } + + return $stream; + } + + return false; + } + + /** + * Gets the fetch cookie file for curl. + * + * @return string The fetch cookie file. + */ + public function getFetchCookieFile() + { + $file = ''; + if ($tmpDir = $this->getTempDir()) { + $file = $tmpDir . '/.elFinderAnonymousCookie'; + } + return $file; + } + + /** + * Call curl_exec() with supported redirect on `safe_mode` or `open_basedir` + * + * @param resource $curl + * @param array $options + * @param array $headers + * + * @throws \Exception + * @return mixed + * @author Naoki Sawada + */ + public static function curlExec($curl, $options = array(), $headers = array()) + { + $followLocation = (!ini_get('safe_mode') && !ini_get('open_basedir')); + if ($followLocation) { + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + } + + if ($options) { + curl_setopt_array($curl, $options); + } + + if ($headers) { + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + } + + $result = curl_exec($curl); + + if (!$followLocation && $redirect = curl_getinfo($curl, CURLINFO_REDIRECT_URL)) { + if ($stream = self::getStreamByUrl(array('target' => $redirect, 'headers' => $headers))) { + $result = stream_get_contents($stream); + } + } + + if ($result === false) { + if (curl_errno($curl)) { + throw new \Exception('curl_exec() failed: ' . curl_error($curl)); + } else { + throw new \Exception('curl_exec(): empty response'); + } + } + + curl_close($curl); + + return $result; + } + + /** + * Return bool that current request was aborted by client side + * + * @return boolean + */ + public static function aborted() + { + if ($file = self::$abortCheckFile) { + (version_compare(PHP_VERSION, '5.3.0') >= 0) ? clearstatcache(true, $file) : clearstatcache(); + if (!is_file($file)) { + // GC (expire 12h) + list($ptn) = explode('elfreq', $file); + self::GlobGC($ptn . 'elfreq*', 43200); + return true; + } + } + return false; + } + + /** + * Return array ["name without extention", "extention"] by filename + * + * @param string $name + * + * @return array + */ + public static function splitFileExtention($name) + { + if (preg_match('/^(.+?)?\.((?:tar\.(?:gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(?:gz|bz2)|[a-z0-9]{1,10})$/i', $name, $m)) { + return array((string)$m[1], $m[2]); + } else { + return array($name, ''); + } + } + + /** + * Gets the memory size by imageinfo. + * + * @param array $imgInfo array that result of getimagesize() + * + * @return integer The memory size by imageinfo. + */ + public static function getMemorySizeByImageInfo($imgInfo) + { + $width = $imgInfo[0]; + $height = $imgInfo[1]; + $bits = isset($imgInfo['bits']) ? $imgInfo['bits'] : 24; + $channels = isset($imgInfo['channels']) ? $imgInfo['channels'] : 3; + return round(($width * $height * $bits * $channels / 8 + Pow(2, 16)) * 1.65); + } + + /** + * Auto expand memory for GD processing + * + * @param array $imgInfos The image infos + */ + public static function expandMemoryForGD($imgInfos) + { + if (elFinder::$memoryLimitGD != 0 && $imgInfos && is_array($imgInfos)) { + if (!is_array($imgInfos[0])) { + $imgInfos = array($imgInfos); + } + $limit = self::getIniBytes('', elFinder::$memoryLimitGD); + $memLimit = self::getIniBytes('memory_limit'); + $needs = 0; + foreach ($imgInfos as $info) { + $needs += self::getMemorySizeByImageInfo($info); + } + $needs += memory_get_usage(); + if ($needs > $memLimit && ($limit == -1 || $limit > $needs)) { + ini_set('memory_limit', $needs); + } + } + } + + /***************************************************************************/ + /* callbacks */ + /***************************************************************************/ + + /** + * Get command name of binded "commandName.subName" + * + * @param string $cmd + * + * @return string + */ + protected static function getCmdOfBind($cmd) + { + list($ret) = explode('.', $cmd); + return trim($ret); + } + + /** + * Add subName to commandName + * + * @param string $cmd + * @param string $sub + * + * @return string + */ + protected static function addSubToBindName($cmd, $sub) + { + return $cmd . '.' . trim($sub); + } + + /** + * Remove a file if connection is disconnected + * + * @param string $file + */ + public static function rmFileInDisconnected($file) + { + (connection_aborted() || connection_status() !== CONNECTION_NORMAL) && is_file($file) && unlink($file); + } + + /** + * Call back function on shutdown + * - delete files in $GLOBALS['elFinderTempFiles'] + */ + public static function onShutdown() + { + self::$abortCheckFile = null; + if (!empty($GLOBALS['elFinderTempFiles'])) { + foreach (array_keys($GLOBALS['elFinderTempFiles']) as $f) { + is_file($f) && unlink($f); + } + } + } + + /** + * Garbage collection with glob + * + * @param string $pattern + * @param integer $time + */ + public static function GlobGC($pattern, $time) + { + $now = time(); + foreach (glob($pattern) as $file) { + (filemtime($file) < ($now - $time)) && unlink($file); + } + } - /***************************************************************************/ - /* callbacks */ - /***************************************************************************/ - - /** - * Get command name of binded "commandName.subName" - * - * @param string $cmd - * @return string - */ - protected static function getCmdOfBind($cmd) { - list($ret) = explode('.', $cmd); - return trim($ret); - } - - /** - * Add subName to commandName - * - * @param string $cmd - * @param string $sub - * @return string - */ - protected static function addSubToBindName($cmd, $sub) { - return $cmd . '.' . trim($sub); - } - - /** - * Remove a file if connection is disconnected - * - * @param string $file - */ - public static function rmFileInDisconnected($file) { - (connection_aborted() || connection_status() !== CONNECTION_NORMAL) && is_file($file) && unlink($file); - } - - /** - * Call back function on shutdown - * - delete files in $GLOBALS['elFinderTempFiles'] - * - */ - public static function onShutdown() { - self::$abortCheckFile = null; - if (! empty($GLOBALS['elFinderTempFiles'])) { - foreach(array_keys($GLOBALS['elFinderTempFiles']) as $f){ - is_file($f) && unlink($f); - } - } - } - - /** - * Garbage collection with glob - * - * @param string $pattern - * @param integer $time - */ - public static function GlobGC($pattern, $time) { - $now = time(); - foreach(glob($pattern) as $file) { - (filemtime($file) < ($now - $time)) && unlink($file); - } - } - } // END class /** * Custom exception class for aborting request - * */ -class elFinderAbortException extends Exception {} +class elFinderAbortException extends Exception +{ +} diff --git a/php/elFinderConnector.class.php b/php/elFinderConnector.class.php index 8cdbfe67d..0eb64b25c 100644 --- a/php/elFinderConnector.class.php +++ b/php/elFinderConnector.class.php @@ -5,351 +5,364 @@ * * @author Dmitry (dio) Levashov **/ -class elFinderConnector { - /** - * elFinder instance - * - * @var elFinder - **/ - protected $elFinder; - - /** - * Options - * - * @var array - **/ - protected $options = array(); - - /** - * Must be use output($data) $data['header'] - * - * @var string - * @deprecated - **/ - protected $header = ''; +class elFinderConnector +{ + /** + * elFinder instance + * + * @var elFinder + **/ + protected $elFinder; - /** - * HTTP request method - * - * @var string - */ - protected $reqMethod = ''; - - /** - * Content type of output JSON - * - * @var string - */ - protected static $contentType = 'Content-Type: application/json; charset=utf-8'; - - /** - * Constructor - * - * @param $elFinder - * @param bool $debug - * @author Dmitry (dio) Levashov - */ - public function __construct($elFinder, $debug=false) { - - $this->elFinder = $elFinder; - $this->reqMethod = strtoupper($_SERVER["REQUEST_METHOD"]); - if ($debug) { - self::$contentType = 'Content-Type: text/plain; charset=utf-8'; - } - } - - /** - * Execute elFinder command and output result - * - * @return void - * @throws Exception - * @author Dmitry (dio) Levashov - */ - public function run() { - $isPost = $this->reqMethod === 'POST'; - $src = $isPost ? array_merge($_GET, $_POST) : $_GET; - $maxInputVars = (! $src || isset($src['targets']))? ini_get('max_input_vars') : null; - if ((! $src || $maxInputVars) && $rawPostData = file_get_contents('php://input')) { - // for max_input_vars and supports IE XDomainRequest() - $parts = explode('&', $rawPostData); - if (! $src || $maxInputVars < count($parts)) { - $src = array(); - foreach($parts as $part) { - list($key, $value) = array_pad(explode('=', $part), 2, ''); - $key = rawurldecode($key); - if (preg_match('/^(.+?)\[([^\[\]]*)\]$/', $key, $m)) { - $key = $m[1]; - $idx = $m[2]; - if (!isset($src[$key])) { - $src[$key] = array(); - } - if ($idx) { - $src[$key][$idx] = rawurldecode($value); - } else { - $src[$key][] = rawurldecode($value); - } - } else { - $src[$key] = rawurldecode($value); - } - } - $_POST = $this->input_filter($src); - $_REQUEST = $this->input_filter(array_merge_recursive($src, $_REQUEST)); - } - } - - if (isset($src['targets']) && $this->elFinder->maxTargets && count($src['targets']) > $this->elFinder->maxTargets) { - $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_MAX_TARGTES))); - } - - $cmd = isset($src['cmd']) ? $src['cmd'] : ''; - $args = array(); - - if (!function_exists('json_encode')) { - $error = $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_JSON); - $this->output(array('error' => '{"error":["'.implode('","', $error).'"]}', 'raw' => true)); - } - - if (!$this->elFinder->loaded()) { - $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_VOL), 'debug' => $this->elFinder->mountErrors)); - } - - // telepat_mode: on - if (!$cmd && $isPost) { - $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UPLOAD, elFinder::ERROR_UPLOAD_TOTAL_SIZE), 'header' => 'Content-Type: text/html')); - } - // telepat_mode: off - - if (!$this->elFinder->commandExists($cmd)) { - $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD))); - } - - // collect required arguments to exec command - $hasFiles = false; - foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) { - if ($name === 'FILES') { - if (isset($_FILES)) { - $hasFiles = true; - } elseif ($req) { - $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd))); - } - } else { - $arg = isset($src[$name])? $src[$name] : ''; - - if (!is_array($arg) && $req !== '') { - $arg = trim($arg); - } - if ($req && $arg === '') { - $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd))); - } - $args[$name] = $arg; - } - } - - $args['debug'] = isset($src['debug']) ? !!$src['debug'] : false; - - $args = $this->input_filter($args); - if ($hasFiles) { - $args['FILES'] = $_FILES; - } - - try { - $this->output($this->elFinder->exec($cmd, $args)); - } catch (elFinderAbortException $e) { - // connection aborted - // unlock session data for multiple access - $this->elFinder->getSession()->close(); - // HTTP response code - header('HTTP/1.0 204 No Content'); - // clear output buffer - while(ob_get_level() && ob_end_clean()){} - exit(); - } - } - - /** - * Output json - * - * @param array data to output - * @return void - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function output(array $data) { - // unlock session data for multiple access - $this->elFinder->getSession()->close(); - // client disconnect should abort - ignore_user_abort(false); - - if ($this->header) { - self::sendHeader($this->header); - } - - if (isset($data['pointer'])) { - // set time limit to 0 - elFinder::extendTimeLimit(0); - - // send optional header - if (!empty($data['header'])) { - self::sendHeader($data['header']); - } - - // clear output buffer - while(ob_get_level() && ob_end_clean()){} - - $toEnd = true; - $fp = $data['pointer']; - $sendData = !($this->reqMethod === 'HEAD' || !empty($data['info']['xsendfile'])); - $psize = null; - if (($this->reqMethod === 'GET' || !$sendData) - && elFinder::isSeekableStream($fp) - && (array_search('Accept-Ranges: none', headers_list()) === false)) { - header('Accept-Ranges: bytes'); - if (!empty($_SERVER['HTTP_RANGE'])) { - $size = $data['info']['size']; - $end = $size - 1; - if (preg_match('/bytes=(\d*)-(\d*)(,?)/i', $_SERVER['HTTP_RANGE'], $matches)) { - if (empty($matches[3])) { - if (empty($matches[1]) && $matches[1] !== '0') { - $start = $size - $matches[2]; - } else { - $start = intval($matches[1]); - if (!empty($matches[2])) { - $end = intval($matches[2]); - if ($end >= $size) { - $end = $size - 1; - } - $toEnd = ($end == ($size - 1)); - } - } - $psize = $end - $start + 1; - - header('HTTP/1.1 206 Partial Content'); - header('Content-Length: ' . $psize); - header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size); - - // Apache mod_xsendfile dose not support range request - if (isset($data['info']['xsendfile']) && strtolower($data['info']['xsendfile']) === 'x-sendfile') { - if (function_exists('header_remove')) { - header_remove($data['info']['xsendfile']); - } else { - header($data['info']['xsendfile'] . ':'); - } - unset($data['info']['xsendfile']); - if ($this->reqMethod !== 'HEAD') { - $sendData = true; - } - } - - $sendData && fseek($fp, $start); - } - } - } - if ($sendData && is_null($psize)){ - elFinder::rewind($fp); - } - } else { - header('Accept-Ranges: none'); - if (isset($data['info']) && ! $data['info']['size']) { - if (function_exists('header_remove')) { - header_remove('Content-Length'); - } else { - header('Content-Length:'); - } - } - } + /** + * Options + * + * @var array + **/ + protected $options = array(); - if ($sendData) { - if ($toEnd) { - // PHP < 5.6 has a bug of fpassthru - // see https://bugs.php.net/bug.php?id=66736 - if (version_compare(PHP_VERSION, '5.6', '<')) { - file_put_contents('php://output', $fp); - } else { - fpassthru($fp); - } - } else { - $out = fopen('php://output', 'wb'); - stream_copy_to_stream($fp, $out, $psize); - fclose($out); - } - } - - if (!empty($data['volume'])) { - $data['volume']->close($data['pointer'], $data['info']['hash']); - } - exit(); - } else { - self::outputJson($data); - exit(0); - } - } - - /** - * Remove null & stripslashes applies on "magic_quotes_gpc" - * - * @param mixed $args - * @return mixed - * @author Naoki Sawada - */ - protected function input_filter($args) { - static $magic_quotes_gpc = NULL; - - if ($magic_quotes_gpc === NULL) - $magic_quotes_gpc = (version_compare(PHP_VERSION, '5.4', '<') && get_magic_quotes_gpc()); - - if (is_array($args)) { - return array_map(array(& $this, 'input_filter'), $args); - } - $res = str_replace("\0", '', $args); - $magic_quotes_gpc && ($res = stripslashes($res)); - return $res; - } - - /** - * Send HTTP header - * - * @param string|array $header optional header - */ - protected static function sendHeader($header = null) { - if ($header) { - if (is_array($header)) { - foreach ($header as $h) { - header($h); - } - } else { - header($header); - } - } - } - - /** - * Output JSON - * - * @param array $data - */ - public static function outputJson($data) { - // send header - $header = isset($data['header']) ? $data['header'] : self::$contentType; - self::sendHeader($header); - - unset($data['header']); - - if (!empty($data['raw']) && isset($data['error'])) { - $out = $data['error']; - } else { - if (isset($data['debug']) && isset($data['debug']['phpErrors'])) { - $data['debug']['phpErrors'] = array_merge($data['debug']['phpErrors'], elFinder::$phpErrors); - } - $out = json_encode($data); - } - - // clear output buffer - while(ob_get_level() && ob_end_clean()){} - - header('Content-Length: ' . strlen($out)); - - echo $out; - - flush(); - } + /** + * Must be use output($data) $data['header'] + * + * @var string + * @deprecated + **/ + protected $header = ''; + + /** + * HTTP request method + * + * @var string + */ + protected $reqMethod = ''; + + /** + * Content type of output JSON + * + * @var string + */ + protected static $contentType = 'Content-Type: application/json; charset=utf-8'; + + /** + * Constructor + * + * @param $elFinder + * @param bool $debug + * + * @author Dmitry (dio) Levashov + */ + public function __construct($elFinder, $debug = false) + { + + $this->elFinder = $elFinder; + $this->reqMethod = strtoupper($_SERVER["REQUEST_METHOD"]); + if ($debug) { + self::$contentType = 'Content-Type: text/plain; charset=utf-8'; + } + } + + /** + * Execute elFinder command and output result + * + * @return void + * @throws Exception + * @author Dmitry (dio) Levashov + */ + public function run() + { + $isPost = $this->reqMethod === 'POST'; + $src = $isPost ? array_merge($_GET, $_POST) : $_GET; + $maxInputVars = (!$src || isset($src['targets'])) ? ini_get('max_input_vars') : null; + if ((!$src || $maxInputVars) && $rawPostData = file_get_contents('php://input')) { + // for max_input_vars and supports IE XDomainRequest() + $parts = explode('&', $rawPostData); + if (!$src || $maxInputVars < count($parts)) { + $src = array(); + foreach ($parts as $part) { + list($key, $value) = array_pad(explode('=', $part), 2, ''); + $key = rawurldecode($key); + if (preg_match('/^(.+?)\[([^\[\]]*)\]$/', $key, $m)) { + $key = $m[1]; + $idx = $m[2]; + if (!isset($src[$key])) { + $src[$key] = array(); + } + if ($idx) { + $src[$key][$idx] = rawurldecode($value); + } else { + $src[$key][] = rawurldecode($value); + } + } else { + $src[$key] = rawurldecode($value); + } + } + $_POST = $this->input_filter($src); + $_REQUEST = $this->input_filter(array_merge_recursive($src, $_REQUEST)); + } + } + + if (isset($src['targets']) && $this->elFinder->maxTargets && count($src['targets']) > $this->elFinder->maxTargets) { + $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_MAX_TARGTES))); + } + + $cmd = isset($src['cmd']) ? $src['cmd'] : ''; + $args = array(); + + if (!function_exists('json_encode')) { + $error = $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_JSON); + $this->output(array('error' => '{"error":["' . implode('","', $error) . '"]}', 'raw' => true)); + } + + if (!$this->elFinder->loaded()) { + $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_VOL), 'debug' => $this->elFinder->mountErrors)); + } + + // telepat_mode: on + if (!$cmd && $isPost) { + $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UPLOAD, elFinder::ERROR_UPLOAD_TOTAL_SIZE), 'header' => 'Content-Type: text/html')); + } + // telepat_mode: off + + if (!$this->elFinder->commandExists($cmd)) { + $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD))); + } + + // collect required arguments to exec command + $hasFiles = false; + foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) { + if ($name === 'FILES') { + if (isset($_FILES)) { + $hasFiles = true; + } elseif ($req) { + $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd))); + } + } else { + $arg = isset($src[$name]) ? $src[$name] : ''; + + if (!is_array($arg) && $req !== '') { + $arg = trim($arg); + } + if ($req && $arg === '') { + $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd))); + } + $args[$name] = $arg; + } + } + + $args['debug'] = isset($src['debug']) ? !!$src['debug'] : false; + + $args = $this->input_filter($args); + if ($hasFiles) { + $args['FILES'] = $_FILES; + } + + try { + $this->output($this->elFinder->exec($cmd, $args)); + } catch (elFinderAbortException $e) { + // connection aborted + // unlock session data for multiple access + $this->elFinder->getSession()->close(); + // HTTP response code + header('HTTP/1.0 204 No Content'); + // clear output buffer + while (ob_get_level() && ob_end_clean()) { + } + exit(); + } + } + + /** + * Output json + * + * @param array data to output + * + * @return void + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function output(array $data) + { + // unlock session data for multiple access + $this->elFinder->getSession()->close(); + // client disconnect should abort + ignore_user_abort(false); + + if ($this->header) { + self::sendHeader($this->header); + } + + if (isset($data['pointer'])) { + // set time limit to 0 + elFinder::extendTimeLimit(0); + + // send optional header + if (!empty($data['header'])) { + self::sendHeader($data['header']); + } + + // clear output buffer + while (ob_get_level() && ob_end_clean()) { + } + + $toEnd = true; + $fp = $data['pointer']; + $sendData = !($this->reqMethod === 'HEAD' || !empty($data['info']['xsendfile'])); + $psize = null; + if (($this->reqMethod === 'GET' || !$sendData) + && elFinder::isSeekableStream($fp) + && (array_search('Accept-Ranges: none', headers_list()) === false)) { + header('Accept-Ranges: bytes'); + if (!empty($_SERVER['HTTP_RANGE'])) { + $size = $data['info']['size']; + $end = $size - 1; + if (preg_match('/bytes=(\d*)-(\d*)(,?)/i', $_SERVER['HTTP_RANGE'], $matches)) { + if (empty($matches[3])) { + if (empty($matches[1]) && $matches[1] !== '0') { + $start = $size - $matches[2]; + } else { + $start = intval($matches[1]); + if (!empty($matches[2])) { + $end = intval($matches[2]); + if ($end >= $size) { + $end = $size - 1; + } + $toEnd = ($end == ($size - 1)); + } + } + $psize = $end - $start + 1; + + header('HTTP/1.1 206 Partial Content'); + header('Content-Length: ' . $psize); + header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size); + + // Apache mod_xsendfile dose not support range request + if (isset($data['info']['xsendfile']) && strtolower($data['info']['xsendfile']) === 'x-sendfile') { + if (function_exists('header_remove')) { + header_remove($data['info']['xsendfile']); + } else { + header($data['info']['xsendfile'] . ':'); + } + unset($data['info']['xsendfile']); + if ($this->reqMethod !== 'HEAD') { + $sendData = true; + } + } + + $sendData && fseek($fp, $start); + } + } + } + if ($sendData && is_null($psize)) { + elFinder::rewind($fp); + } + } else { + header('Accept-Ranges: none'); + if (isset($data['info']) && !$data['info']['size']) { + if (function_exists('header_remove')) { + header_remove('Content-Length'); + } else { + header('Content-Length:'); + } + } + } + + if ($sendData) { + if ($toEnd) { + // PHP < 5.6 has a bug of fpassthru + // see https://bugs.php.net/bug.php?id=66736 + if (version_compare(PHP_VERSION, '5.6', '<')) { + file_put_contents('php://output', $fp); + } else { + fpassthru($fp); + } + } else { + $out = fopen('php://output', 'wb'); + stream_copy_to_stream($fp, $out, $psize); + fclose($out); + } + } + + if (!empty($data['volume'])) { + $data['volume']->close($data['pointer'], $data['info']['hash']); + } + exit(); + } else { + self::outputJson($data); + exit(0); + } + } + + /** + * Remove null & stripslashes applies on "magic_quotes_gpc" + * + * @param mixed $args + * + * @return mixed + * @author Naoki Sawada + */ + protected function input_filter($args) + { + static $magic_quotes_gpc = NULL; + + if ($magic_quotes_gpc === NULL) + $magic_quotes_gpc = (version_compare(PHP_VERSION, '5.4', '<') && get_magic_quotes_gpc()); + + if (is_array($args)) { + return array_map(array(& $this, 'input_filter'), $args); + } + $res = str_replace("\0", '', $args); + $magic_quotes_gpc && ($res = stripslashes($res)); + return $res; + } + + /** + * Send HTTP header + * + * @param string|array $header optional header + */ + protected static function sendHeader($header = null) + { + if ($header) { + if (is_array($header)) { + foreach ($header as $h) { + header($h); + } + } else { + header($header); + } + } + } + + /** + * Output JSON + * + * @param array $data + */ + public static function outputJson($data) + { + // send header + $header = isset($data['header']) ? $data['header'] : self::$contentType; + self::sendHeader($header); + + unset($data['header']); + + if (!empty($data['raw']) && isset($data['error'])) { + $out = $data['error']; + } else { + if (isset($data['debug']) && isset($data['debug']['phpErrors'])) { + $data['debug']['phpErrors'] = array_merge($data['debug']['phpErrors'], elFinder::$phpErrors); + } + $out = json_encode($data); + } + + // clear output buffer + while (ob_get_level() && ob_end_clean()) { + } + + header('Content-Length: ' . strlen($out)); + + echo $out; + + flush(); + } }// END class diff --git a/php/elFinderFlysystemGoogleDriveNetmount.php b/php/elFinderFlysystemGoogleDriveNetmount.php index f0f6de0a8..2035b4e5c 100644 --- a/php/elFinderFlysystemGoogleDriveNetmount.php +++ b/php/elFinderFlysystemGoogleDriveNetmount.php @@ -11,7 +11,7 @@ use Hypweb\elFinderFlysystemDriverExt\Driver as ExtDriver; elFinder::$netDrivers['googledrive'] = 'FlysystemGoogleDriveNetmount'; -if (! class_exists('elFinderVolumeFlysystemGoogleDriveCache', false)) { +if (!class_exists('elFinderVolumeFlysystemGoogleDriveCache', false)) { class elFinderVolumeFlysystemGoogleDriveCache extends ACache { use Hasdir; @@ -25,14 +25,14 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver public function __construct() { parent::__construct(); - + $opts = array( 'acceptedName' => '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#', 'rootCssClass' => 'elfinder-navbar-root-googledrive', - 'gdAlias' => '%s@GDrive', - 'gdCacheDir' => __DIR__ . '/.tmp', - 'gdCachePrefix' => 'gd-', - 'gdCacheExpire' => 600 + 'gdAlias' => '%s@GDrive', + 'gdCacheDir' => __DIR__ . '/.tmp', + 'gdCachePrefix' => 'gd-', + 'gdCacheExpire' => 600 ); $this->options = array_merge($this->options, $opts); @@ -64,6 +64,7 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver * Call from elFinder::netmout() before volume->mount() * * @param $options + * * @return Array * @author Naoki Sawada */ @@ -75,11 +76,11 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver if (empty($options['client_secret']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET')) { $options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET; } - - if (! isset($options['pass'])) { + + if (!isset($options['pass'])) { $options['pass'] = ''; } - + try { $client = new \Google_Client(); $client->setClientId($options['client_id']); @@ -93,8 +94,8 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver } $options = array_merge($this->session->get('GoogleDriveAuthParams', []), $options); - - if (! isset($options['access_token'])) { + + if (!isset($options['access_token'])) { $options['access_token'] = $this->session->get('GoogleDriveTokens', []); $this->session->remove('GoogleDriveTokens'); } @@ -129,22 +130,22 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver if (empty($options['url'])) { $options['url'] = elFinder::getConnectorUrl(); } - - $callback = $options['url'] - . '?cmd=netmount&protocol=googledrive&host=1'; + + $callback = $options['url'] + . '?cmd=netmount&protocol=googledrive&host=1'; $client->setRedirectUri($callback); - if (! $aToken && empty($_GET['code'])) { - $client->setScopes([ Google_Service_Drive::DRIVE ]); - if (! empty($options['offline'])) { + if (!$aToken && empty($_GET['code'])) { + $client->setScopes([Google_Service_Drive::DRIVE]); + if (!empty($options['offline'])) { $client->setApprovalPrompt('force'); $client->setAccessType('offline'); } $url = $client->createAuthUrl(); - - $html = ''; + + $html = ''; $html .= ''; if (empty($options['pass']) && $options['host'] !== '1') { $options['pass'] = 'return'; @@ -153,13 +154,13 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver } else { $out = array( 'node' => $options['id'], - 'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "'.str_replace($html, '"', '\\"').'", "error" : "'.elFinder::ERROR_ACCESS_DENIED.'"}', + 'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "' . str_replace($html, '"', '\\"') . '", "error" : "' . elFinder::ERROR_ACCESS_DENIED . '"}', 'bind' => 'netmount' ); return array('exit' => 'callback', 'out' => $out); } } else { - if (! empty($_GET['code'])) { + if (!empty($_GET['code'])) { $aToken = $client->fetchAccessTokenWithAuthCode($_GET['code']); $options['access_token'] = $aToken; $this->session->set('GoogleDriveTokens', $aToken)->set('GoogleDriveAuthParams', $options); @@ -171,7 +172,7 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver return array('exit' => 'callback', 'out' => $out); } $folders = []; - foreach($service->files->listFiles([ + foreach ($service->files->listFiles([ 'pageSize' => 1000, 'q' => 'trashed = false and mimeType = "application/vnd.google-apps.folder"' ]) as $f) { @@ -180,11 +181,11 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver natcasesort($folders); $folders = ['root' => $rootObj->getName()] + $folders; $folders = json_encode($folders); - $json = '{"protocol": "googledrive", "mode": "done", "folders": '.$folders.'}'; + $json = '{"protocol": "googledrive", "mode": "done", "folders": ' . $folders . '}'; $options['pass'] = 'return'; $html = 'Google.com'; $html .= ''; $this->session->set('GoogleDriveAuthParams', $options); return array('exit' => true, 'body' => $html); @@ -193,20 +194,20 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver } catch (Exception $e) { $this->session->remove('GoogleDriveAuthParams')->remove('GoogleDriveTokens'); if (empty($options['pass'])) { - return array('exit' => true, 'body' => '{msg:'.elFinder::ERROR_ACCESS_DENIED.'}'.' '.$e->getMessage()); + return array('exit' => true, 'body' => '{msg:' . elFinder::ERROR_ACCESS_DENIED . '}' . ' ' . $e->getMessage()); } else { return array('exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]); } } - - if (! $aToken) { + + if (!$aToken) { return array('exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE); } - + if ($options['path'] === '/') { $options['path'] = 'root'; } - + try { $file = $service->files->get($options['path']); $options['alias'] = sprintf($this->options['gdAlias'], $file->getName()); @@ -221,7 +222,7 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver return array('exit' => true, 'error' => $e->getMessage()); } - foreach(['host', 'user', 'pass', 'id', 'offline'] as $key) { + foreach (['host', 'user', 'pass', 'id', 'offline'] as $key) { unset($options[$key]); } @@ -234,17 +235,18 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver * * @param $netVolumes * @param $key + * * @return bool * @internal param array $options */ public function netunmount($netVolumes, $key) { - $cache = $this->options['gdCacheDir'] . DIRECTORY_SEPARATOR . $this->options['gdCachePrefix'].$this->netMountKey; + $cache = $this->options['gdCacheDir'] . DIRECTORY_SEPARATOR . $this->options['gdCachePrefix'] . $this->netMountKey; if (file_exists($cache) && is_writeable($cache)) { unlink($cache); } if ($tmbs = glob($this->tmbPath . DIRECTORY_SEPARATOR . $this->netMountKey . '*')) { - foreach($tmbs as $file) { + foreach ($tmbs as $file) { unlink($file); } } @@ -257,6 +259,7 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver * false - otherwise * * @param array $opts + * * @return bool * @author Naoki Sawada */ @@ -264,7 +267,7 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver { $creds = null; if (isset($opts['access_token'])) { - $this->netMountKey = md5(join('-', array('googledrive', $opts['path'], (isset($opts['access_token']['refresh_token'])? $opts['access_token']['refresh_token'] : $opts['access_token']['access_token'])))); + $this->netMountKey = md5(join('-', array('googledrive', $opts['path'], (isset($opts['access_token']['refresh_token']) ? $opts['access_token']['refresh_token'] : $opts['access_token']['access_token'])))); } $client = new \Google_Client(); @@ -289,13 +292,13 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver if (!isset($opts['path']) || $opts['path'] === '') { $opts['path'] = 'root'; } - - $googleDrive = new GoogleDriveAdapter($service, $opts['path'], [ 'useHasDir' => true ]); + + $googleDrive = new GoogleDriveAdapter($service, $opts['path'], ['useHasDir' => true]); $opts['fscache'] = null; if ($this->options['gdCacheDir'] && is_writeable($this->options['gdCacheDir'])) { if ($this->options['gdCacheExpire']) { - $opts['fscache'] = new elFinderVolumeFlysystemGoogleDriveCache(new Local($this->options['gdCacheDir']), $this->options['gdCachePrefix'].$this->netMountKey, $this->options['gdCacheExpire']); + $opts['fscache'] = new elFinderVolumeFlysystemGoogleDriveCache(new Local($this->options['gdCacheDir']), $this->options['gdCachePrefix'] . $this->netMountKey, $this->options['gdCacheExpire']); } } if ($opts['fscache']) { @@ -308,10 +311,10 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver $opts['filesystem'] = $filesystem; $opts['separator'] = '/'; $opts['checkSubfolders'] = true; - if (! isset($opts['alias'])) { + if (!isset($opts['alias'])) { $opts['alias'] = 'GoogleDrive'; } - + if ($res = parent::mount($opts)) { // update access_token of session data if ($creds) { @@ -327,8 +330,9 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver /** * @inheritdoc */ - protected function tmbname($stat) { - return $this->netMountKey.substr(substr($stat['hash'], strlen($this->id)), -38).$stat['ts'].'.png'; - } + protected function tmbname($stat) + { + return $this->netMountKey . substr(substr($stat['hash'], strlen($this->id)), -38) . $stat['ts'] . '.png'; + } } diff --git a/php/elFinderPlugin.php b/php/elFinderPlugin.php index 3eb1888ad..2bc85c7ee 100644 --- a/php/elFinderPlugin.php +++ b/php/elFinderPlugin.php @@ -1,107 +1,113 @@ opts; - if (is_object($volume)) { - $volOpts = $volume->getOptionsPlugin($name); - if (is_array($volOpts)) { - $opts = array_merge($opts, $volOpts); - } - } - return $opts; - } - - /** - * Is enabled with options - * - * @param array $opts - * @param elFinder $elfinder - * @return boolean - */ - protected function iaEnabled($opts, $elfinder = null) { - if (! $opts['enable']) { - return false; - } - - // check post var 'contentSaveId' to disable this plugin - if ($elfinder && !empty($opts['disableWithContentSaveId'])) { - $session = $elfinder->getSession(); - $urlContentSaveIds = $session->get('urlContentSaveIds', array()); - if (!empty(elFinder::$currentArgs['contentSaveId']) && ($contentSaveId = elFinder::$currentArgs['contentSaveId'])) { - if (!empty($urlContentSaveIds[$contentSaveId])) { - $elfinder->removeUrlContentSaveId($contentSaveId); - return false; - } - } - } +class elFinderPlugin +{ - if (isset($opts['onDropWith']) && !is_null($opts['onDropWith'])) { - // plugin disabled by default, enabled only if given key is pressed - if (isset($_REQUEST['dropWith']) && $_REQUEST['dropWith']) { - $onDropWith = $opts['onDropWith']; - $action = (int)$_REQUEST['dropWith']; - if (!is_array($onDropWith)) { - $onDropWith = array($onDropWith); - } - foreach($onDropWith as $key) { - $key = (int)$key; - if (($action & $key) === $key) { - return true; - } - } - } - return false; - } - - if (isset($opts['offDropWith']) && ! is_null($opts['offDropWith']) && isset($_REQUEST['dropWith'])) { - // plugin enabled by default, disabled only if given key is pressed - $offDropWith = $opts['offDropWith']; - $action = (int)$_REQUEST['dropWith']; - if (! is_array($offDropWith)) { - $offDropWith = array($offDropWith); - } - $res = true; - foreach($offDropWith as $key) { - $key = (int)$key; - if ($key === 0) { - if ($action === 0) { - $res = false; - break; - } - } else { - if (($action & $key) === $key) { - $res = false; - break; - } - } - } - if (! $res) { - return false; - } - } - - return true; - } + /** + * This plugin's options + * + * @var array + */ + protected $opts = array(); + + /** + * Get current volume's options + * + * @param object $volume + * + * @return array options + */ + protected function getCurrentOpts($volume) + { + $name = substr(get_class($this), 14); // remove "elFinderPlugin" + $opts = $this->opts; + if (is_object($volume)) { + $volOpts = $volume->getOptionsPlugin($name); + if (is_array($volOpts)) { + $opts = array_merge($opts, $volOpts); + } + } + return $opts; + } + + /** + * Is enabled with options + * + * @param array $opts + * @param elFinder $elfinder + * + * @return boolean + */ + protected function iaEnabled($opts, $elfinder = null) + { + if (!$opts['enable']) { + return false; + } + + // check post var 'contentSaveId' to disable this plugin + if ($elfinder && !empty($opts['disableWithContentSaveId'])) { + $session = $elfinder->getSession(); + $urlContentSaveIds = $session->get('urlContentSaveIds', array()); + if (!empty(elFinder::$currentArgs['contentSaveId']) && ($contentSaveId = elFinder::$currentArgs['contentSaveId'])) { + if (!empty($urlContentSaveIds[$contentSaveId])) { + $elfinder->removeUrlContentSaveId($contentSaveId); + return false; + } + } + } + + if (isset($opts['onDropWith']) && !is_null($opts['onDropWith'])) { + // plugin disabled by default, enabled only if given key is pressed + if (isset($_REQUEST['dropWith']) && $_REQUEST['dropWith']) { + $onDropWith = $opts['onDropWith']; + $action = (int)$_REQUEST['dropWith']; + if (!is_array($onDropWith)) { + $onDropWith = array($onDropWith); + } + foreach ($onDropWith as $key) { + $key = (int)$key; + if (($action & $key) === $key) { + return true; + } + } + } + return false; + } + + if (isset($opts['offDropWith']) && !is_null($opts['offDropWith']) && isset($_REQUEST['dropWith'])) { + // plugin enabled by default, disabled only if given key is pressed + $offDropWith = $opts['offDropWith']; + $action = (int)$_REQUEST['dropWith']; + if (!is_array($offDropWith)) { + $offDropWith = array($offDropWith); + } + $res = true; + foreach ($offDropWith as $key) { + $key = (int)$key; + if ($key === 0) { + if ($action === 0) { + $res = false; + break; + } + } else { + if (($action & $key) === $key) { + $res = false; + break; + } + } + } + if (!$res) { + return false; + } + } + + return true; + } } diff --git a/php/elFinderSession.php b/php/elFinderSession.php index d6575984c..932194ade 100644 --- a/php/elFinderSession.php +++ b/php/elFinderSession.php @@ -5,241 +5,243 @@ * Session Wrapper Class. * * @package elfinder - * @author Naoki Sawada + * @author Naoki Sawada **/ class elFinderSession implements elFinderSessionInterface { - protected $started = false; + protected $started = false; + + protected $cookiePay = false; + + protected $keys = array(); + + protected $base64encode = false; + + protected $opts = array( + 'base64encode' => false, + 'keys' => array( + 'default' => 'elFinderCaches', + 'netvolume' => 'elFinderNetVolumes' + ) + ); + + public function __construct($opts) + { + $this->opts = array_merge($this->opts, $opts); + $this->base64encode = !empty($this->opts['base64encode']); + $this->keys = $this->opts['keys']; + + return $this; + } - protected $cookiePay = false; - - protected $keys = array(); - - protected $base64encode = false; - - protected $opts = array( - 'base64encode' => false, - 'keys' => array( - 'default' => 'elFinderCaches', - 'netvolume' => 'elFinderNetVolumes' - ) - ); - - public function __construct($opts) - { - $this->opts = array_merge($this->opts, $opts); - $this->base64encode = !empty($this->opts['base64encode']); - $this->keys = $this->opts['keys']; - - return $this; - } - /** * {@inheritdoc} */ - public function start() - { - if (version_compare(PHP_VERSION, '5.4.0', '>=')) { - if (session_status() !== PHP_SESSION_ACTIVE) { - session_start(); - } - } else { - set_error_handler(array($this, 'session_start_error'), E_NOTICE); - session_start(); - restore_error_handler(); - } - $this->started = session_id()? true : false; - - return $this; - } - + public function start() + { + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + if (session_status() !== PHP_SESSION_ACTIVE) { + session_start(); + } + } else { + set_error_handler(array($this, 'session_start_error'), E_NOTICE); + session_start(); + restore_error_handler(); + } + $this->started = session_id() ? true : false; + + return $this; + } + /** * {@inheritdoc} */ - public function close() - { - if ($this->started) { - if (!$this->cookiePay) { - $cParm = session_get_cookie_params(); - ini_set('session.use_cookies', 0); - setcookie(session_name(), session_id(), 0, $cParm['path'], $cParm['domain'], $cParm['secure'], $cParm['httponly']); - $this->cookiePay = true; - } - session_write_close(); - } - $this->started = false; - - return $this; - } - + public function close() + { + if ($this->started) { + if (!$this->cookiePay) { + $cParm = session_get_cookie_params(); + ini_set('session.use_cookies', 0); + setcookie(session_name(), session_id(), 0, $cParm['path'], $cParm['domain'], $cParm['secure'], $cParm['httponly']); + $this->cookiePay = true; + } + session_write_close(); + } + $this->started = false; + + return $this; + } + /** * {@inheritdoc} */ - public function get($key, $empty = null) - { - $closed = false; - if (! $this->started) { - $closed = true; - $this->start(); - } - - $data = null; - - if ($this->started) { - $session =& $this->getSessionRef($key); - $data = $session; - if ($data && $this->base64encode) { - $data = $this->decodeData($data); - } - } - - $checkFn = null; - if (! is_null($empty)) { - if (is_string($empty)) { - $checkFn = 'is_string'; - } elseif (is_array($empty)) { - $checkFn = 'is_array'; - } elseif (is_object($empty)) { - $checkFn = 'is_object'; - } elseif (is_float($empty)) { - $checkFn = 'is_float'; - } elseif (is_int($empty)) { - $checkFn = 'is_int'; - } - } - - if (is_null($data) || ($checkFn && ! $checkFn($data))) { - $session = $data = $empty; - } - - if ($closed) { - $this->close(); - } + public function get($key, $empty = null) + { + $closed = false; + if (!$this->started) { + $closed = true; + $this->start(); + } + + $data = null; + + if ($this->started) { + $session =& $this->getSessionRef($key); + $data = $session; + if ($data && $this->base64encode) { + $data = $this->decodeData($data); + } + } + + $checkFn = null; + if (!is_null($empty)) { + if (is_string($empty)) { + $checkFn = 'is_string'; + } elseif (is_array($empty)) { + $checkFn = 'is_array'; + } elseif (is_object($empty)) { + $checkFn = 'is_object'; + } elseif (is_float($empty)) { + $checkFn = 'is_float'; + } elseif (is_int($empty)) { + $checkFn = 'is_int'; + } + } + + if (is_null($data) || ($checkFn && !$checkFn($data))) { + $session = $data = $empty; + } + + if ($closed) { + $this->close(); + } + + return $data; + } - return $data; - } - /** * {@inheritdoc} */ - public function set($key, $data) - { - $closed = false; - if (! $this->started) { - $closed = true; - $this->start(); - } - $session =& $this->getSessionRef($key); - if ($this->base64encode) { - $data = $this->encodeData($data); - } - $session = $data; - - if ($closed) { - $this->close(); - } - - return $this; - } - + public function set($key, $data) + { + $closed = false; + if (!$this->started) { + $closed = true; + $this->start(); + } + $session =& $this->getSessionRef($key); + if ($this->base64encode) { + $data = $this->encodeData($data); + } + $session = $data; + + if ($closed) { + $this->close(); + } + + return $this; + } + /** * {@inheritdoc} */ - public function remove($key) - { - $closed = false; - if (! $this->started) { - $closed = true; - $this->start(); - } + public function remove($key) + { + $closed = false; + if (!$this->started) { + $closed = true; + $this->start(); + } - list($cat, $name) = array_pad(explode('.', $key, 2), 2, null); - if (is_null($name)) { - if (! isset($this->keys[$cat])) { - $name = $cat; - $cat = 'default'; - } - } - if (isset($this->keys[$cat])) { - $cat = $this->keys[$cat]; - } else { - $name = $cat . '.' . $name; - $cat = $this->keys['default']; - } - if (is_null($name)) { - unset($_SESSION[$cat]); - } else { - if (isset($_SESSION[$cat]) && is_array($_SESSION[$cat])) { - unset($_SESSION[$cat][$name]); - } - } + list($cat, $name) = array_pad(explode('.', $key, 2), 2, null); + if (is_null($name)) { + if (!isset($this->keys[$cat])) { + $name = $cat; + $cat = 'default'; + } + } + if (isset($this->keys[$cat])) { + $cat = $this->keys[$cat]; + } else { + $name = $cat . '.' . $name; + $cat = $this->keys['default']; + } + if (is_null($name)) { + unset($_SESSION[$cat]); + } else { + if (isset($_SESSION[$cat]) && is_array($_SESSION[$cat])) { + unset($_SESSION[$cat][$name]); + } + } - if ($closed) { - $this->close(); - } - - return $this; - } - - protected function & getSessionRef($key) - { - $session = null; - if ($this->started) { - list($cat, $name) = array_pad(explode('.', $key, 2), 2, null); - if (is_null($name)) { - if (! isset($this->keys[$cat])) { - $name = $cat; - $cat = 'default'; - } - } - if (isset($this->keys[$cat])) { - $cat = $this->keys[$cat]; - } else { - $name = $cat . '.' . $name; - $cat = $this->keys['default']; - } - if (is_null($name)) { - if (! isset($_SESSION[$cat])) { - $_SESSION[$cat] = null; - } - $session =& $_SESSION[$cat]; - } else { - if (! isset($_SESSION[$cat]) || ! is_array($_SESSION[$cat])) { - $_SESSION[$cat] = array(); - } - if (! isset($_SESSION[$cat][$name])) { - $_SESSION[$cat][$name] = null; - } - $session =& $_SESSION[$cat][$name]; - } - } - return $session; - } - - protected function encodeData($data) - { - if ($this->base64encode) { - $data = base64_encode(serialize($data)); - } - return $data; - } - - protected function decodeData($data) - { - if ($this->base64encode) { - if (is_string($data)) { - if (($data = base64_decode($data)) !== false) { - $data = unserialize($data); - } else { - $data = null; - } - } else { - $data = null; - } - } - return $data; - } + if ($closed) { + $this->close(); + } - protected function session_start_error($errno , $errstr) {} + return $this; + } + + protected function & getSessionRef($key) + { + $session = null; + if ($this->started) { + list($cat, $name) = array_pad(explode('.', $key, 2), 2, null); + if (is_null($name)) { + if (!isset($this->keys[$cat])) { + $name = $cat; + $cat = 'default'; + } + } + if (isset($this->keys[$cat])) { + $cat = $this->keys[$cat]; + } else { + $name = $cat . '.' . $name; + $cat = $this->keys['default']; + } + if (is_null($name)) { + if (!isset($_SESSION[$cat])) { + $_SESSION[$cat] = null; + } + $session =& $_SESSION[$cat]; + } else { + if (!isset($_SESSION[$cat]) || !is_array($_SESSION[$cat])) { + $_SESSION[$cat] = array(); + } + if (!isset($_SESSION[$cat][$name])) { + $_SESSION[$cat][$name] = null; + } + $session =& $_SESSION[$cat][$name]; + } + } + return $session; + } + + protected function encodeData($data) + { + if ($this->base64encode) { + $data = base64_encode(serialize($data)); + } + return $data; + } + + protected function decodeData($data) + { + if ($this->base64encode) { + if (is_string($data)) { + if (($data = base64_decode($data)) !== false) { + $data = unserialize($data); + } else { + $data = null; + } + } else { + $data = null; + } + } + return $data; + } + + protected function session_start_error($errno, $errstr) + { + } } diff --git a/php/elFinderSessionInterface.php b/php/elFinderSessionInterface.php index 936848c7a..ff90332a6 100644 --- a/php/elFinderSessionInterface.php +++ b/php/elFinderSessionInterface.php @@ -5,55 +5,53 @@ * Session Wrapper Interface. * * @package elfinder - * @author Naoki Sawada + * @author Naoki Sawada **/ interface elFinderSessionInterface { - /** - * Session start - * - * @return self - **/ - public function start(); - - /** - * Session write & close - * - * @return self - **/ - public function close(); - - /** - * Get session data - * - * This method must be equipped with an automatic start / close. - * - * @param string $key Target key - * @param mixed $empty Return value of if session target key does not exist - * - * @return mixed - **/ - public function get($key, $empty = ''); - - /** - * Set session data - * - * This method must be equipped with an automatic start / close. - * - * @param string $key Target key - * @param mixed $data Value - * - * @return self - **/ - public function set($key, $data); - - /** - * Get session data - * - * @param string $key Target key - * - * @return self - **/ - public function remove($key); + /** + * Session start + * + * @return self + **/ + public function start(); + + /** + * Session write & close + * + * @return self + **/ + public function close(); + + /** + * Get session data + * This method must be equipped with an automatic start / close. + * + * @param string $key Target key + * @param mixed $empty Return value of if session target key does not exist + * + * @return mixed + **/ + public function get($key, $empty = ''); + + /** + * Set session data + * This method must be equipped with an automatic start / close. + * + * @param string $key Target key + * @param mixed $data Value + * + * @return self + **/ + public function set($key, $data); + + /** + * Get session data + * + * @param string $key Target key + * + * @return self + **/ + public function remove($key); } diff --git a/php/elFinderVolumeBox.class.php b/php/elFinderVolumeBox.class.php index ab896f7be..b0130a0c6 100644 --- a/php/elFinderVolumeBox.class.php +++ b/php/elFinderVolumeBox.class.php @@ -129,7 +129,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver $paths = explode('/', trim($path, '/')); $id = array_pop($paths); if ($paths) { - $parent = '/'.implode('/', $paths); + $parent = '/' . implode('/', $paths); $pid = array_pop($paths); } else { $pid = '0'; @@ -144,9 +144,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver * Obtains a new access token from OAuth. This token is valid for one hour. * * @param string $clientSecret The Box client secret - * @param string $code The code returned by Box after + * @param string $code The code returned by Box after * successful log in - * @param string $redirectUri Must be the same as the redirect URI passed + * @param string $redirectUri Must be the same as the redirect URI passed * to LoginUrl * * @return bool|object @@ -189,11 +189,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver CURLOPT_URL => $url, )); - $decoded = $this->_bd_curlExec($curl, true, array('Content-Length: '.strlen($fields))); + $decoded = $this->_bd_curlExec($curl, true, array('Content-Length: ' . strlen($fields))); - return (object) array( - 'expires' => time() + $decoded->expires_in - 30, - 'data' => $decoded, + return (object)array( + 'expires' => time() + $decoded->expires_in - 30, + 'data' => $decoded, ); } @@ -227,16 +227,16 @@ class elFinderVolumeBox extends elFinderVolumeDriver $curl = curl_init(); curl_setopt_array($curl, array( - // General options. - CURLOPT_RETURNTRANSFER => true, - CURLOPT_POST => true, // i am sending post data - CURLOPT_POSTFIELDS => 'client_id='.urlencode($this->options['client_id']) - .'&client_secret='.urlencode($this->options['client_secret']) - .'&grant_type=refresh_token' - .'&refresh_token='.urlencode($token->data->refresh_token), + // General options. + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, // i am sending post data + CURLOPT_POSTFIELDS => 'client_id=' . urlencode($this->options['client_id']) + . '&client_secret=' . urlencode($this->options['client_secret']) + . '&grant_type=refresh_token' + . '&refresh_token=' . urlencode($token->data->refresh_token), - CURLOPT_URL => $url, - )); + CURLOPT_URL => $url, + )); $decoded = $this->_bd_curlExec($curl); @@ -244,10 +244,10 @@ class elFinderVolumeBox extends elFinderVolumeDriver throw new \Exception(elFinder::ERROR_REAUTH_REQUIRE); } - $token = (object) array( - 'expires' => time() + $decoded->expires_in - 30, - 'data' => $decoded, - ); + $token = (object)array( + 'expires' => time() + $decoded->expires_in - 30, + 'data' => $decoded, + ); $this->session->set('BoxTokens', $token); $this->options['accessToken'] = json_encode($token); @@ -285,8 +285,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver /** * Creates a base cURL object which is compatible with the Box.com API. * - * @param $url + * @param $url * @param bool $contents + * * @return boolean|array * @throws Exception */ @@ -309,9 +310,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver if (!$single && $total > ($offset + $cnt)) { $offset = $offset + $cnt; if (strpos($url, 'offset=') === false) { - $url .= '&offset='.$offset; + $url .= '&offset=' . $offset; } else { - $url = preg_replace('/^(.+?offset=)\d+(.*)$/', '${1}'.$offset.'$2', $url); + $url = preg_replace('/^(.+?offset=)\d+(.*)$/', '${1}' . $offset . '$2', $url); } $more = $this->_bd_fetch($url); if (is_array($more)) { @@ -338,13 +339,12 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param array $headers * * @throws \Exception - * * @return mixed */ protected function _bd_curlExec($curl, $decodeOrParent = true, $headers = array()) { $headers = array_merge(array( - 'Authorization: Bearer '.$this->token->data->access_token, + 'Authorization: Bearer ' . $this->token->data->access_token, ), $headers); $result = elFinder::curlExec($curl, array(), $headers); @@ -358,7 +358,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver if (!empty($decoded->error_code)) { $errmsg = $decoded->error_code; if (!empty($decoded->message)) { - $errmsg .= ': '.$decoded->message; + $errmsg .= ': ' . $decoded->message; } throw new \Exception($errmsg); } @@ -388,9 +388,10 @@ class elFinderVolumeBox extends elFinderVolumeDriver /** * Drive query and fetchAll. * - * @param $itemId + * @param $itemId * @param bool $fetch_self * @param bool $recursive + * * @return bool|object * @throws Exception */ @@ -403,12 +404,12 @@ class elFinderVolumeBox extends elFinderVolumeDriver } if ($fetch_self) { - $path = '/folders/'.$itemId.'?fields='.self::FETCHFIELDS; + $path = '/folders/' . $itemId . '?fields=' . self::FETCHFIELDS; } else { - $path = '/folders/'.$itemId.'/items?limit=1000&fields='.self::FETCHFIELDS; + $path = '/folders/' . $itemId . '/items?limit=1000&fields=' . self::FETCHFIELDS; } - $url = self::API_URL.$path; + $url = self::API_URL . $path; if ($recursive) { foreach ($this->_bd_fetch($url) as $file) { @@ -422,8 +423,8 @@ class elFinderVolumeBox extends elFinderVolumeDriver } else { $result = $this->_bd_fetch($url); if ($fetch_self && !$result) { - $path = '/files/'.$itemId.'?fields='.self::FETCHFIELDS; - $url = self::API_URL.$path; + $path = '/files/' . $itemId . '?fields=' . self::FETCHFIELDS; + $url = self::API_URL . $path; $result = $this->_bd_fetch($url); } } @@ -461,7 +462,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param object $raw line from ftp_rawlist() output * * @return array - * * @author Dmitry Levashov **/ protected function _bd_parseRaw($raw) @@ -479,7 +479,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver $stat['size'] = 0; $stat['dirs'] = -1; } else { - $stat['size'] = (int) $raw->size; + $stat['size'] = (int)$raw->size; if (!empty($raw->shared_link->url) && $raw->shared_link->access == 'open') { if ($url = $this->getSharedWebContentLink($raw)) { $stat['url'] = $url; @@ -505,7 +505,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver list(, $itemId) = $this->_bd_splitPath($path); try { - $url = self::API_URL.'/files/'.$itemId.'/thumbnail.png?min_height=' . $this->tmbSize . '&min_width=' . $this->tmbSize; + $url = self::API_URL . '/files/' . $itemId . '/thumbnail.png?min_height=' . $this->tmbSize . '&min_width=' . $this->tmbSize; $contents = $this->_bd_fetch($url, true); return $contents; @@ -527,20 +527,20 @@ class elFinderVolumeBox extends elFinderVolumeDriver list(, $itemId) = $this->_bd_splitPath($path); if ($type == 'folders') { - $url = self::API_URL.'/'.$type.'/'.$itemId.'?recursive=true'; + $url = self::API_URL . '/' . $type . '/' . $itemId . '?recursive=true'; } else { - $url = self::API_URL.'/'.$type.'/'.$itemId; + $url = self::API_URL . '/' . $type . '/' . $itemId; } $curl = $this->_bd_prepareCurl(array( - CURLOPT_URL => $url, - CURLOPT_CUSTOMREQUEST => 'DELETE', + CURLOPT_URL => $url, + CURLOPT_CUSTOMREQUEST => 'DELETE', )); //unlink or delete File or Folder in the Parent $this->_bd_curlExec($curl); } catch (Exception $e) { - return $this->setError('Box error: '.$e->getMessage()); + return $this->setError('Box error: ' . $e->getMessage()); } return true; @@ -555,7 +555,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * Call from elFinder::netmout() before volume->mount(). * * @return array - * * @author Naoki Sawada * @author Raja Sharma updating for Box **/ @@ -590,28 +589,28 @@ class elFinderVolumeBox extends elFinderVolumeDriver try { // Obtain the token using the code received by the Box.com API $this->session->set('BoxTokens', - $this->_bd_obtainAccessToken($options['client_id'], $options['client_secret'], $_GET['code'])); + $this->_bd_obtainAccessToken($options['client_id'], $options['client_secret'], $_GET['code'])); $out = array( - 'node' => $options['id'], - 'json' => '{"protocol": "box", "mode": "done", "reset": 1}', - 'bind' => 'netmount', + 'node' => $options['id'], + 'json' => '{"protocol": "box", "mode": "done", "reset": 1}', + 'bind' => 'netmount', ); return array('exit' => 'callback', 'out' => $out); } catch (Exception $e) { $out = array( - 'node' => $options['id'], - 'json' => json_encode(array('error' => $e->getMessage())), + 'node' => $options['id'], + 'json' => json_encode(array('error' => $e->getMessage())), ); return array('exit' => 'callback', 'out' => $out); } } elseif (!empty($_GET['error'])) { $out = array( - 'node' => $options['id'], - 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED)), + 'node' => $options['id'], + 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED)), ); return array('exit' => 'callback', 'out' => $out); @@ -646,28 +645,28 @@ class elFinderVolumeBox extends elFinderVolumeDriver $this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET; foreach ($this->ARGS as $k => $v) { if (!in_array($k, $innerKeys)) { - $cdata .= '&'.$k.'='.rawurlencode($v); + $cdata .= '&' . $k . '=' . rawurlencode($v); } } if (empty($options['url'])) { $options['url'] = elFinder::getConnectorUrl(); } $callback = $options['url'] - .'?cmd=netmount&protocol=box&host=box.com&user=init&pass=return&node='.$options['id'].$cdata; + . '?cmd=netmount&protocol=box&host=box.com&user=init&pass=return&node=' . $options['id'] . $cdata; try { - $this->session->set('BoxTokens', (object) array('token' => null)); + $this->session->set('BoxTokens', (object)array('token' => null)); - $url = self::AUTH_URL.'?'.http_build_query(array('response_type' => 'code', 'client_id' => $options['client_id'], 'redirect_uri' => elFinder::getConnectorUrl().'?cmd=netmount&protocol=box&host=1')); + $url = self::AUTH_URL . '?' . http_build_query(array('response_type' => 'code', 'client_id' => $options['client_id'], 'redirect_uri' => elFinder::getConnectorUrl() . '?cmd=netmount&protocol=box&host=1')); - $url .= '&oauth_callback='.rawurlencode($callback); + $url .= '&oauth_callback=' . rawurlencode($callback); } catch (Exception $e) { return array('exit' => true, 'body' => '{msg:errAccess}'); } - $html = ''; + $html = ''; $html .= ''; return array('exit' => true, 'body' => $html); @@ -677,7 +676,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver if ($result) { foreach ($result as $res) { if ($res->type == 'folder') { - $folders[$res->id.' '] = $res->name; + $folders[$res->id . ' '] = $res->name; } } natcasesort($folders); @@ -690,11 +689,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver $folders = ['root' => 'My Box'] + $folders; $folders = json_encode($folders); - $expires = empty($this->token->data->refresh_token) ? (int) $this->token->expires : 0; - $json = '{"protocol": "box", "mode": "done", "folders": '.$folders.', "expires": '.$expires.'}'; + $expires = empty($this->token->data->refresh_token) ? (int)$this->token->expires : 0; + $json = '{"protocol": "box", "mode": "done", "folders": ' . $folders . ', "expires": ' . $expires . '}'; $html = 'Box.com'; $html .= ''; return array('exit' => true, 'body' => $html); @@ -724,11 +723,12 @@ class elFinderVolumeBox extends elFinderVolumeDriver * * @param $netVolumes * @param $key + * * @return bool */ public function netunmount($netVolumes, $key) { - if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/').DIRECTORY_SEPARATOR.$this->tmbPrefix.'*.png')) { + if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->tmbPrefix . '*.png')) { foreach ($tmbs as $file) { unlink($file); } @@ -745,10 +745,10 @@ class elFinderVolumeBox extends elFinderVolumeDriver public function debug() { $res = parent::debug(); - if (! empty($this->options['accessToken'])) { + if (!empty($this->options['accessToken'])) { $res['accessToken'] = $this->options['accessToken']; } - + return $res; } @@ -761,7 +761,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn. * * @return bool - * * @throws Exception * @author Dmitry (dio) Levashov * @author Cem (DiscoFever) @@ -802,12 +801,12 @@ class elFinderVolumeBox extends elFinderVolumeDriver if (empty($this->options['alias'])) { list(, $itemId) = $this->_bd_splitPath($this->options['path']); $this->options['alias'] = ($this->options['path'] === '/') ? $this->options['root'] : - $this->_bd_query($itemId, $fetch_self = true)->name.'@Box.com'; + $this->_bd_query($itemId, $fetch_self = true)->name . '@Box.com'; } $this->rootName = $this->options['alias']; - $this->tmbPrefix = 'box'.base_convert($this->netMountKey, 10, 32); + $this->tmbPrefix = 'box' . base_convert($this->netMountKey, 10, 32); if (!empty($this->options['tmpPath'])) { if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) { @@ -835,7 +834,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * Configure after successfull mount. * * @author Dmitry (dio) Levashov - * * @throws elFinderAbortException */ protected function configure() @@ -880,16 +878,14 @@ class elFinderVolumeBox extends elFinderVolumeDriver // > Note: If an item is added to Box then it becomes accessible through the search endpoint after ten minutes. /*** - $url = self::API_URL.'/search?limit=1&offset=0&content_types=name&ancestor_folder_ids='.rawurlencode($pid) - .'&query='.rawurlencode('"'.$name.'"') - .'fields='.self::FETCHFIELDS; - - $raw = $this->_bd_fetch($url); - - if (is_array($raw) && count($raw)) { - return $this->_bd_parseRaw($raw); - } - ***/ + * $url = self::API_URL.'/search?limit=1&offset=0&content_types=name&ancestor_folder_ids='.rawurlencode($pid) + * .'&query='.rawurlencode('"'.$name.'"') + * .'fields='.self::FETCHFIELDS; + * $raw = $this->_bd_fetch($url); + * if (is_array($raw) && count($raw)) { + * return $this->_bd_parseRaw($raw); + * } + ***/ $phash = $this->encode($parent); @@ -960,7 +956,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $name new file name (optionaly) * * @return string|false - * * @author Dmitry (dio) Levashov * @author Naoki Sawada **/ @@ -977,11 +972,10 @@ class elFinderVolumeBox extends elFinderVolumeDriver /** * Remove file/ recursive remove dir. * - * @param string $path file path - * @param bool $force try to remove even if file locked + * @param string $path file path + * @param bool $force try to remove even if file locked * * @return bool - * * @throws elFinderAbortException * @author Dmitry (dio) Levashov * @author Naoki Sawada @@ -1020,9 +1014,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver * Create thumnbnail and return it's URL on success. * * @param string $path file path - * @param $stat - * @return string|false + * @param $stat * + * @return string|false * @throws ImagickException * @throws elFinderAbortException * @author Dmitry (dio) Levashov @@ -1035,7 +1029,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver } $name = $this->tmbname($stat); - $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; + $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; // copy image into tmbPath so some drivers does not store files on local fs if (!$data = $this->_bd_getThumbnail($path)) { @@ -1095,12 +1089,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param array $stat file stat * * @return string - * * @author Dmitry (dio) Levashov **/ protected function tmbname($stat) { - return $this->tmbPrefix.$stat['rev'].$stat['ts'].'.png'; + return $this->tmbPrefix . $stat['rev'] . $stat['ts'] . '.png'; } /** @@ -1109,7 +1102,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param object $raw data * * @return string - * * @author Naoki Sawada **/ protected function getSharedWebContentLink($raw) @@ -1119,19 +1111,19 @@ class elFinderVolumeBox extends elFinderVolumeDriver if ($raw->shared_link->url && ($fType == 'image' || $fType == 'video' || $fType == 'audio')) { if ($fExtension == 'jpg' && $fType == 'image') { - $url = 'https://app.box.com/representation/file_version_'.$raw->file_version->id.'/image_2048_'.$fExtension.'/1.'.$fExtension.'?shared_name='.basename($raw->shared_link->url); + $url = 'https://app.box.com/representation/file_version_' . $raw->file_version->id . '/image_2048_' . $fExtension . '/1.' . $fExtension . '?shared_name=' . basename($raw->shared_link->url); return $url; } elseif ($fExtension !== 'jpg' && $fType == 'image') { - $url = 'https://app.box.com/representation/file_version_'.$raw->file_version->id.'/image_2048/1.'.$fExtension.'?shared_name='.basename($raw->shared_link->url); + $url = 'https://app.box.com/representation/file_version_' . $raw->file_version->id . '/image_2048/1.' . $fExtension . '?shared_name=' . basename($raw->shared_link->url); return $url; } elseif ($fType == 'video') { - $url = 'https://app.box.com/representation/file_version_'.$raw->file_version->id.'/video_480.'.$fExtension.'?shared_name='.basename($raw->shared_link->url); + $url = 'https://app.box.com/representation/file_version_' . $raw->file_version->id . '/video_480.' . $fExtension . '?shared_name=' . basename($raw->shared_link->url); return $url; } elseif ($fType == 'audio') { - $url = 'https://app.box.com/index.php?rm=preview_stream&&file_version_'.$raw->file_version->id.'/audio/mpeg:'.$raw->name.'&shared_name='.basename($raw->shared_link->url); + $url = 'https://app.box.com/index.php?rm=preview_stream&&file_version_' . $raw->file_version->id . '/audio/mpeg:' . $raw->name . '&shared_name=' . basename($raw->shared_link->url); return $url; } @@ -1145,17 +1137,16 @@ class elFinderVolumeBox extends elFinderVolumeDriver /** * Return content URL. * - * @param string $hash file hash - * @param array $options options + * @param string $hash file hash + * @param array $options options * * @return string - * * @throws Exception * @author Naoki Sawada */ public function getContentUrl($hash, $options = array()) { - if (! empty($options['onetime']) && $this->options['onetimeUrl']) { + if (!empty($options['onetime']) && $this->options['onetimeUrl']) { return parent::getContentUrl($hash, $options); } if (!empty($options['temporary'])) { @@ -1171,7 +1162,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver list(, $itemId) = $this->_bd_splitPath($path); $params['shared_link']['access'] = 'open'; //open|company|collaborators - $url = self::API_URL.'/files/'.$itemId; + $url = self::API_URL . '/files/' . $itemId; $curl = $this->_bd_prepareCurl(array( CURLOPT_URL => $url, @@ -1179,9 +1170,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver CURLOPT_POSTFIELDS => json_encode($params), )); $res = $this->_bd_curlExec($curl, true, array( - // The data is sent as JSON as per Box documentation. - 'Content-Type: application/json', - )); + // The data is sent as JSON as per Box documentation. + 'Content-Type: application/json', + )); if ($url = $this->getSharedWebContentLink($res)) { return $url; @@ -1199,7 +1190,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _dirname($path) @@ -1215,7 +1205,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _basename($path) @@ -1232,7 +1221,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $name * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _joinPath($dir, $name) @@ -1241,7 +1229,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver $dir = ''; } - return $this->_normpath($dir.'/'.$name); + return $this->_normpath($dir . '/' . $name); } /** @@ -1250,7 +1238,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path path * * @return string - * * @author Troex Nevelin **/ protected function _normpath($path) @@ -1258,7 +1245,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver if (DIRECTORY_SEPARATOR !== '/') { $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); } - $path = '/'.ltrim($path, '/'); + $path = '/' . ltrim($path, '/'); return $path; } @@ -1269,7 +1256,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _relpath($path) @@ -1283,7 +1269,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _abspath($path) @@ -1297,12 +1282,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _path($path) { - return $this->rootName.$this->_normpath(substr($path, strlen($this->root))); + return $this->rootName . $this->_normpath(substr($path, strlen($this->root))); } /** @@ -1312,12 +1296,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $parent parent path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _inpath($path, $parent) { - return $path == $parent || strpos($path, $parent.'/') === 0; + return $path == $parent || strpos($path, $parent . '/') === 0; } /***************** file stat ********************/ @@ -1333,13 +1316,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver * - (bool) hidden is object hidden. optionally * - (string) alias for symlinks - link target path relative to root path. optionally * - (string) target for symlinks - link target path. optionally. - * * If file does not exists - returns empty array or false. * * @param string $path file path * * @return array|false - * * @throws Exception * @author Dmitry (dio) Levashov */ @@ -1358,7 +1339,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path dir path * * @return bool - * * @throws Exception * @author Dmitry (dio) Levashov */ @@ -1366,9 +1346,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver { list(, $itemId) = $this->_bd_splitPath($path); - $path = '/folders/'.$itemId.'/items?limit=1&offset=0&fields='.self::FETCHFIELDS; + $path = '/folders/' . $itemId . '/items?limit=1&offset=0&fields=' . self::FETCHFIELDS; - $url = self::API_URL.$path; + $url = self::API_URL . $path; if ($res = $this->_bd_fetch($url)) { if ($res[0]->type == 'folder') { @@ -1387,7 +1367,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $mime file mime type * * @return string - * * @throws ImagickException * @throws elFinderAbortException * @author Dmitry (dio) Levashov @@ -1403,10 +1382,10 @@ class elFinderVolumeBox extends elFinderVolumeDriver if ($size = @getimagesize($work)) { $cache['width'] = $size[0]; $cache['height'] = $size[1]; - $ret = array('dim' => $size[0].'x'.$size[1]); + $ret = array('dim' => $size[0] . 'x' . $size[1]); $srcfp = fopen($work, 'rb'); if ($subImgLink = $this->getSubstituteImgLink(elFinder::$currentArgs['target'], $size, $srcfp)) { - $ret['url'] = $subImgLink; + $ret['url'] = $subImgLink; } } } @@ -1423,7 +1402,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path dir path * * @return array - * * @throws Exception * @author Dmitry (dio) Levashov * @author Cem (DiscoFever) @@ -1440,8 +1418,8 @@ class elFinderVolumeBox extends elFinderVolumeDriver * * @param string $path file path * @param string $mode - * @return resource|false * + * @return resource|false * @author Dmitry (dio) Levashov */ protected function _fopen($path, $mode = 'rb') @@ -1449,8 +1427,8 @@ class elFinderVolumeBox extends elFinderVolumeDriver if ($mode === 'rb' || $mode === 'r') { list(, $itemId) = $this->_bd_splitPath($path); $data = array( - 'target' => self::API_URL.'/files/'.$itemId.'/content', - 'headers' => array('Authorization: Bearer '.$this->token->data->access_token), + 'target' => self::API_URL . '/files/' . $itemId . '/content', + 'headers' => array('Authorization: Bearer ' . $this->token->data->access_token), ); return elFinder::getStreamByUrl($data); @@ -1463,10 +1441,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver * Close opened file. * * @param resource $fp file pointer + * @param string $path * - * @param string $path * @return void - * * @author Dmitry (dio) Levashov */ protected function _fclose($fp, $path = '') @@ -1486,7 +1463,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $name new directory name * * @return string|bool - * * @author Dmitry (dio) Levashov **/ protected function _mkdir($path, $name) @@ -1495,7 +1471,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver list(, $parentId) = $this->_bd_splitPath($path); $params = array('name' => $name, 'parent' => array('id' => $parentId)); - $url = self::API_URL.'/folders'; + $url = self::API_URL . '/folders'; $curl = $this->_bd_prepareCurl(array( CURLOPT_URL => $url, @@ -1508,7 +1484,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver return $this->_joinPath($path, $folder->id); } catch (Exception $e) { - return $this->setError('Box error: '.$e->getMessage()); + return $this->setError('Box error: ' . $e->getMessage()); } } @@ -1519,7 +1495,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $name new file name * * @return string|bool - * * @author Dmitry (dio) Levashov **/ protected function _mkfile($path, $name) @@ -1534,7 +1509,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path symlink path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _symlink($target, $path, $name) @@ -1550,7 +1524,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $name new file name * * @return string|false - * * @author Dmitry (dio) Levashov **/ protected function _copy($source, $targetDir, $name) @@ -1563,10 +1536,10 @@ class elFinderVolumeBox extends elFinderVolumeDriver $srcItem = $this->_bd_getRawItem($source); $properties = array('name' => $name, 'parent' => array('id' => $parentId)); - $data = (object) $properties; + $data = (object)$properties; $type = ($srcItem->type === 'folder') ? 'folders' : 'files'; - $url = self::API_URL.'/'.$type.'/'.$srcId.'/copy'; + $url = self::API_URL . '/' . $type . '/' . $srcId . '/copy'; $curl = $this->_bd_prepareCurl(array( CURLOPT_URL => $url, @@ -1587,7 +1560,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver return false; } catch (Exception $e) { - return $this->setError('Box error: '.$e->getMessage()); + return $this->setError('Box error: ' . $e->getMessage()); } } @@ -1600,7 +1573,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $name file name * * @return string|bool - * * @author Dmitry (dio) Levashov **/ protected function _move($source, $targetDir, $name) @@ -1617,8 +1589,8 @@ class elFinderVolumeBox extends elFinderVolumeDriver $properties = array('name' => $name, 'parent' => array('id' => $parentId)); $type = ($srcItem->type === 'folder') ? 'folders' : 'files'; - $url = self::API_URL.'/'.$type.'/'.$itemId; - $data = (object) $properties; + $url = self::API_URL . '/' . $type . '/' . $itemId; + $data = (object)$properties; $curl = $this->_bd_prepareCurl(array( CURLOPT_URL => $url, @@ -1627,9 +1599,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver )); $result = $this->_bd_curlExec($curl, $targetDir, array( - // The data is sent as JSON as per Box documentation. - 'Content-Type: application/json', - )); + // The data is sent as JSON as per Box documentation. + 'Content-Type: application/json', + )); if ($result && isset($result->id)) { return $this->_joinPath($targetDir, $result->id); @@ -1637,7 +1609,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver return false; } catch (Exception $e) { - return $this->setError('Box error: '.$e->getMessage()); + return $this->setError('Box error: ' . $e->getMessage()); } } @@ -1647,7 +1619,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path file path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _unlink($path) @@ -1661,7 +1632,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path dir path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _rmdir($path) @@ -1679,7 +1649,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param array $stat file stat (required by some virtual fs) * * @return bool|string - * * @author Dmitry (dio) Levashov **/ protected function _save($fp, $path, $name, $stat) @@ -1715,30 +1684,30 @@ class elFinderVolumeBox extends elFinderVolumeDriver if ($itemId === '') { //upload or create new file in destination target $properties = array('name' => $name, 'parent' => array('id' => $parentId)); - $url = self::UPLOAD_URL.'/files/content'; + $url = self::UPLOAD_URL . '/files/content'; } else { //update existing file in destination target $properties = array('name' => $name); - $url = self::UPLOAD_URL.'/files/'.$itemId.'/content'; + $url = self::UPLOAD_URL . '/files/' . $itemId . '/content'; } if (class_exists('CURLFile')) { $cfile = new CURLFile($tmpFilePath); } else { - $cfile = '@'.$tmpFilePath; + $cfile = '@' . $tmpFilePath; } $params = array('attributes' => json_encode($properties), 'file' => $cfile); $curl = $this->_bd_prepareCurl(array( - CURLOPT_URL => $url, - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => $params, - )); + CURLOPT_URL => $url, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $params, + )); $file = $this->_bd_curlExec($curl, $parent); return $this->_joinPath($parent, $file->entries[0]->id); } catch (Exception $e) { - return $this->setError('Box error: '.$e->getMessage()); + return $this->setError('Box error: ' . $e->getMessage()); } } @@ -1748,18 +1717,17 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $path file path * * @return string|false - * * @author Dmitry (dio) Levashov **/ protected function _getContents($path) { try { list(, $itemId) = $this->_bd_splitPath($path); - $url = self::API_URL.'/files/'.$itemId.'/content'; + $url = self::API_URL . '/files/' . $itemId . '/content'; $contents = $this->_bd_fetch($url, true); } catch (Exception $e) { - return $this->setError('Box error: '.$e->getMessage()); + return $this->setError('Box error: ' . $e->getMessage()); } return $contents; @@ -1772,7 +1740,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param string $content new file content * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _filePutContents($path, $content) @@ -1781,7 +1748,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver if ($local = $this->getTempFile($path)) { if (file_put_contents($local, $content, LOCK_EX) !== false - && ($fp = fopen($local, 'rb'))) { + && ($fp = fopen($local, 'rb'))) { clearstatcache(); $res = $this->_save($fp, $path, '', array()); fclose($fp); @@ -1818,7 +1785,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param array $arc archiver command and arguments (same as in $this->archivers) * * @return true - * * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin **/ @@ -1836,7 +1802,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver * @param array $arc archiver options * * @return string|bool - * * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin **/ diff --git a/php/elFinderVolumeDriver.class.php b/php/elFinderVolumeDriver.class.php index 88c1f6246..0247d405c 100644 --- a/php/elFinderVolumeDriver.class.php +++ b/php/elFinderVolumeDriver.class.php @@ -1,7095 +1,7410 @@ array(), - 'extract' => array() - ); - - /** - * Server character encoding - * - * @var string or null - **/ - protected $encoding = null; - - /** - * How many subdirs levels return for tree - * - * @var int - **/ - protected $treeDeep = 1; - - /** - * Errors from last failed action - * - * @var array - **/ - protected $error = array(); - - /** - * Today 24:00 timestamp - * - * @var int - **/ - protected $today = 0; - - /** - * Yesterday 24:00 timestamp - * - * @var int - **/ - protected $yesterday = 0; - - /** - * Force make dirctory on extract - * - * @var int - **/ - protected $extractToNewdir = 'auto'; - - /** - * Object configuration - * - * @var array - **/ - protected $options = array( - // Driver ID (Prefix of volume ID), Normally, the value specified for each volume driver is used. - 'driverId' => '', - // Id (Suffix of volume ID), Normally, the number incremented according to the specified number of volumes is used. - 'id' => '', - // revision id of root directory that uses for caching control of root stat - 'rootRev' => '', - // driver type it uses volume root's CSS class name. e.g. 'group' -> Adds 'elfinder-group' to CSS class name. - 'type' => '', - // root directory path - 'path' => '', - // Folder hash value on elFinder to be the parent of this volume - 'phash' => '', - // Folder hash value on elFinder to trash bin of this volume, it require 'copyJoin' to true - 'trashHash' => '', - // open this path on initial request instead of root path - 'startPath' => '', - // how many subdirs levels return per request - 'treeDeep' => 1, - // root url, not set to disable sending URL to client (replacement for old "fileURL" option) - 'URL' => '', - // enable onetime URL to a file - (true, false or callable (A function that return onetime URL)) - 'onetimeUrl' => false, - // directory link url to own manager url with folder hash (`true`, `false`, `'hide'`(No show) or default `'auto'`: URL is empty then `true` else `false`) - 'dirUrlOwn' => 'auto', - // directory separator. required by client to show paths correctly - 'separator' => DIRECTORY_SEPARATOR, - // Use '/' as directory separator when the path hash encode/decode on the Windows server too - 'winHashFix' => false, - // Server character encoding (default is '': UTF-8) - 'encoding' => '', - // for convert character encoding (default is '': Not change locale) - 'locale' => '', - // URL of volume icon image - 'icon' => '', - // CSS Class of volume root in tree - 'rootCssClass' => '', - // Items to disable session caching - 'noSessionCache' => array(), - // enable i18n folder name that convert name to elFinderInstance.messages['folder_'+name] - 'i18nFolderName' => false, - // Search timeout (sec) - 'searchTimeout' => 30, - // Search exclusion directory regex pattern (require demiliter e.g. '#/path/to/exclude_directory#i') - 'searchExDirReg' => '', - // library to crypt/uncrypt files names (not implemented) - 'cryptLib' => '', - // how to detect files mimetypes. (auto/internal/finfo/mime_content_type) - 'mimeDetect' => 'auto', - // mime.types file path (for mimeDetect==internal) - 'mimefile' => '', - // Static extension/MIME of general server side scripts to security issues - 'staticMineMap' => array( - 'php:*' => 'text/x-php', - 'pht:*' => 'text/x-php', - 'php3:*' => 'text/x-php', - 'php4:*' => 'text/x-php', - 'php5:*' => 'text/x-php', - 'php7:*' => 'text/x-php', - 'phtml:*' => 'text/x-php', - 'cgi:*' => 'text/x-httpd-cgi', - 'pl:*' => 'text/x-perl', - 'asp:*' => 'text/x-asap', - 'aspx:*' => 'text/x-asap', - 'py:*' => 'text/x-python', - 'rb:*' => 'text/x-ruby', - 'jsp:*' => 'text/x-jsp' - ), - // mime type normalize map : Array '[ext]:[detected mime type]' => '[normalized mime]' - 'mimeMap' => array( - 'md:application/x-genesis-rom' => 'text/x-markdown', - 'md:text/plain' => 'text/x-markdown', - 'markdown:text/plain' => 'text/x-markdown', - 'css:text/x-asm' => 'text/css', - 'css:text/plain' => 'text/css', - 'csv:text/plain' => 'text/csv', - 'json:text/plain' => 'application/json', - 'sql:text/plain' => 'text/x-sql', - 'rtf:text/rtf' => 'application/rtf', - 'rtfd:text/rtfd' => 'application/rtfd', - 'ico:image/vnd.microsoft.icon' => 'image/x-icon', - 'svg:text/plain' => 'image/svg+xml', - 'pxd:application/octet-stream' => 'image/x-pixlr-data', - 'dng:image/tiff' => 'image/x-adobe-dng', - 'sketch:application/zip' => 'image/x-sketch', - 'sketch:application/octet-stream' => 'image/x-sketch', - 'xcf:application/octet-stream' => 'image/x-xcf', - 'amr:application/octet-stream' => 'audio/amr', - 'm4a:video/mp4' => 'audio/mp4', - 'oga:application/ogg' => 'audio/ogg', - 'ogv:application/ogg' => 'video/ogg', - 'zip:application/x-zip' => 'application/zip', - 'm3u8:text/plain' => 'application/x-mpegURL', - 'mpd:text/plain' => 'application/dash+xml', - 'mpd:application/xml' => 'application/dash+xml', - '*:application/x-dosexec' => 'application/x-executable', - 'doc:application/vnd.ms-office'=> 'application/msword', - 'xls:application/vnd.ms-office'=> 'application/vnd.ms-excel', - 'ppt:application/vnd.ms-office'=> 'application/vnd.ms-powerpoint', - 'yml:text/plain' => 'text/x-yaml', - 'ai:application/pdf' => 'application/postscript', - 'cgm:text/plain' => 'image/cgm', - 'dxf:text/plain' => 'image/vnd.dxf', - 'hpgl:text/plain' => 'application/vnd.hp-hpgl', - 'igs:text/plain' => 'model/iges', - 'iges:text/plain' => 'model/iges', - 'plt:application/octet-stream' => 'application/plt', - 'plt:text/plain' => 'application/plt', - 'sat:text/plain' => 'application/sat', - 'step:text/plain' => 'application/step', - 'stp:text/plain' => 'application/step' - ), - // An option to add MimeMap to the `mimeMap` option - // Array '[ext]:[detected mime type]' => '[normalized mime]' - 'additionalMimeMap' => array(), - // MIME-Type of filetype detected as unknown - 'mimeTypeUnknown' => 'application/octet-stream', - // MIME regex of send HTTP header "Content-Disposition: inline" or allow preview in quicklook - // '.' is allow inline of all of MIME types - // '$^' is not allow inline of all of MIME types - 'dispInlineRegex' => '^(?:(?:video|audio)|image/(?!.+\+xml)|application/(?:ogg|x-mpegURL|dash\+xml)|(?:text/plain|application/pdf)$)', - // temporary content URL's base path - 'tmpLinkPath' => '', - // temporary content URL's base URL - 'tmpLinkUrl' => '', - // directory for thumbnails - 'tmbPath' => '.tmb', - // mode to create thumbnails dir - 'tmbPathMode' => 0777, - // thumbnails dir URL. Set it if store thumbnails outside root directory - 'tmbURL' => '', - // thumbnails size (px) - 'tmbSize' => 48, - // thumbnails crop (true - crop, false - scale image to fit thumbnail size) - 'tmbCrop' => true, - // thumbnails background color (hex #rrggbb or 'transparent') - 'tmbBgColor' => 'transparent', - // image rotate fallback background color (hex #rrggbb) - 'bgColorFb' => '#ffffff', - // image manipulations library - 'imgLib' => 'auto', - // Fallback self image to thumbnail (nothing imgLib) - 'tmbFbSelf' => true, - // Video to Image converters ['TYPE or MIME' => ['func' => function($file){ /* Converts $file to Image */ return true; }, 'maxlen' => (int)TransferLength]] - 'imgConverter' => array(), - // Max length of transfer to image converter - 'tmbVideoConvLen' => 10000000, - // Captre point seccond - 'tmbVideoConvSec' => 6, - // Resource path of fallback icon images defailt: php/resouces - 'resourcePath' => '', - // Jpeg image saveing quality - 'jpgQuality' => 100, - // Save as progressive JPEG on image editing - 'jpgProgressive' => true, - // enable to get substitute image with command `dim` - 'substituteImg' => true, - // on paste file - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext - 'copyOverwrite' => true, - // if true - join new and old directories content on paste - 'copyJoin' => true, - // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext - 'uploadOverwrite' => true, - // mimetypes allowed to upload - 'uploadAllow' => array(), - // mimetypes not allowed to upload - 'uploadDeny' => array(), - // order to process uploadAllow and uploadDeny options - 'uploadOrder' => array('deny', 'allow'), - // maximum upload file size. NOTE - this is size for every uploaded files - 'uploadMaxSize' => 0, - // maximum number of chunked upload connection. `-1` to disable chunked upload - 'uploadMaxConn' => 3, - // maximum get file size. NOTE - Maximum value is 50% of PHP memory_limit - 'getMaxSize' => 0, - // files dates format - 'dateFormat' => 'j M Y H:i', - // files time format - 'timeFormat' => 'H:i', - // if true - every folder will be check for children folders, -1 - every folder will be check asynchronously, false - all folders will be marked as having subfolders - 'checkSubfolders' => true, // true, false or -1 - // allow to copy from this volume to other ones? - 'copyFrom' => true, - // allow to copy from other volumes to this one? - 'copyTo' => true, - // cmd duplicate suffix format e.g. '_%s_' to without spaces - 'duplicateSuffix' => ' %s ', - // unique name numbar format e.g. '(%d)' to (1), (2)... - 'uniqueNumFormat' => '%d', - // list of commands disabled on this root - 'disabled' => array(), - // enable file owner, group & mode info, `false` to inactivate "chmod" command. - 'statOwner' => false, - // allow exec chmod of read-only files - 'allowChmodReadOnly' => false, - // regexp or function name to validate new file name - 'acceptedName' => '/^[^\.].*/', // Notice: overwritten it in some volume drivers contractor - // regexp or function name to validate new directory name - 'acceptedDirname' => '', // used `acceptedName` if empty value - // function/class method to control files permissions - 'accessControl' => null, - // some data required by access control - 'accessControlData' => null, - // default permissions. - 'defaults' => array( - 'read' => true, - 'write' => true, - 'locked' => false, - 'hidden' => false - ), - // files attributes - 'attributes' => array(), - // max allowed archive files size (0 - no limit) - 'maxArcFilesSize' => 0, - // Allowed archive's mimetypes to create. Leave empty for all available types. - 'archiveMimes' => array(), - // Manual config for archivers. See example below. Leave empty for auto detect - 'archivers' => array(), - // Use Archive function for remote volume - 'useRemoteArchive' => false, - // plugin settings - 'plugin' => array(), - // Is support parent directory time stamp update on add|remove|rename item - // Default `null` is auto detection that is LocalFileSystem, FTP or Dropbox are `true` - 'syncChkAsTs' => null, - // Long pooling sync checker function for syncChkAsTs is true - // Calls with args (TARGET DIRCTORY PATH, STAND-BY(sec), OLD TIMESTAMP, VOLUME DRIVER INSTANCE, ELFINDER INSTANCE) - // This function must return the following values. Changed: New Timestamp or Same: Old Timestamp or Error: false - // Default `null` is try use elFinderVolumeLocalFileSystem::localFileSystemInotify() on LocalFileSystem driver - // another driver use elFinder stat() checker - 'syncCheckFunc'=> null, - // Long polling sync stand-by time (sec) - 'plStandby' => 30, - // Sleep time (sec) for elFinder stat() checker (syncChkAsTs is true) - 'tsPlSleep' => 10, - // Sleep time (sec) for elFinder ls() checker (syncChkAsTs is false) - 'lsPlSleep' => 30, - // Client side sync interval minimum (ms) - // Default `null` is auto set to ('tsPlSleep' or 'lsPlSleep') * 1000 - // `0` to disable auto sync - 'syncMinMs' => null, - // required to fix bug on macos - // However, we recommend to use the Normalizer plugin instead this option - 'utf8fix' => false, - // й ё Й Ё Ø Å - 'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"), - 'utf8replace' => array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5"), - // cache control HTTP headers for commands `file` and `get` - 'cacheHeaders' => array( - 'Cache-Control: max-age=3600', - 'Expires:', - 'Pragma:' - ), - // Header to use to accelerate sending local files to clients (e.g. 'X-Sendfile', 'X-Accel-Redirect') - 'xsendfile' => '', - // Root path to xsendfile target. Probably, this is required for 'X-Accel-Redirect' on Nginx. - 'xsendfilePath'=> '' - ); - - /** - * Defaults permissions - * - * @var array - **/ - protected $defaults = array( - 'read' => true, - 'write' => true, - 'locked' => false, - 'hidden' => false - ); - - /** - * Access control function/class - * - * @var mixed - **/ - protected $attributes = array(); - - /** - * Access control function/class - * - * @var mixed - **/ - protected $access = null; - - /** - * Mime types allowed to upload - * - * @var array - **/ - protected $uploadAllow = array(); - - /** - * Mime types denied to upload - * - * @var array - **/ - protected $uploadDeny = array(); - - /** - * Order to validate uploadAllow and uploadDeny - * - * @var array - **/ - protected $uploadOrder = array(); - - /** - * Maximum allowed upload file size. - * Set as number or string with unit - "10M", "500K", "1G" - * - * @var int|string - **/ - protected $uploadMaxSize = 0; - - /** - * Run time setting of overwrite items on upload - * - * @var string - */ - protected $uploadOverwrite = true; - - /** - * Maximum allowed get file size. - * Set as number or string with unit - "10M", "500K", "1G" - * - * @var int|string - **/ - protected $getMaxSize = -1; - - /** - * Mimetype detect method - * - * @var string - **/ - protected $mimeDetect = 'auto'; - - /** - * Flag - mimetypes from externail file was loaded - * - * @var bool - **/ - private static $mimetypesLoaded = false; - - /** - * Finfo resource for mimeDetect == 'finfo' - * - * @var resource - **/ - protected $finfo = null; - - /** - * List of disabled client's commands - * - * @var array - **/ - protected $disabled = array(); - - /** - * overwrite extensions/mimetypes to mime.types - * - * @var array - **/ - protected static $mimetypes = array( - // applications - 'exe' => 'application/x-executable', - 'jar' => 'application/x-jar', - // archives - 'gz' => 'application/x-gzip', - 'tgz' => 'application/x-gzip', - 'tbz' => 'application/x-bzip2', - 'rar' => 'application/x-rar', - // texts - 'php' => 'text/x-php', - 'js' => 'text/javascript', - 'rtfd' => 'application/rtfd', - 'py' => 'text/x-python', - 'rb' => 'text/x-ruby', - 'sh' => 'text/x-shellscript', - 'pl' => 'text/x-perl', - 'xml' => 'text/xml', - 'c' => 'text/x-csrc', - 'h' => 'text/x-chdr', - 'cpp' => 'text/x-c++src', - 'hh' => 'text/x-c++hdr', - 'md' => 'text/x-markdown', - 'markdown' => 'text/x-markdown', - 'yml' => 'text/x-yaml', - // images - 'bmp' => 'image/x-ms-bmp', - 'tga' => 'image/x-targa', - 'xbm' => 'image/xbm', - 'pxm' => 'image/pxm', - //audio - 'wav' => 'audio/wav', - // video - 'dv' => 'video/x-dv', - 'wm' => 'video/x-ms-wmv', - 'ogm' => 'video/ogg', - 'm2ts' => 'video/MP2T', - 'mts' => 'video/MP2T', - 'ts' => 'video/MP2T', - 'm3u8' => 'application/x-mpegURL', - 'mpd' => 'application/dash+xml' - ); - - /** - * Directory separator - required by client - * - * @var string - **/ - protected $separator = DIRECTORY_SEPARATOR; - - /** - * Directory separator for decode/encode hash - * - * @var string - **/ - protected $separatorForHash = ''; - - /** - * System Root path (Unix like: '/', Windows: '\', 'C:\' or 'D:\'...) - * - * @var string - **/ - protected $systemRoot = DIRECTORY_SEPARATOR; - - /** - * Mimetypes allowed to display - * - * @var array - **/ - protected $onlyMimes = array(); - - /** - * Store files moved or overwrited files info - * - * @var array - **/ - protected $removed = array(); - - /** - * Store files added files info - * - * @var array - **/ - protected $added = array(); - - /** - * Cache storage - * - * @var array - **/ - protected $cache = array(); - - /** - * Cache by folders - * - * @var array - **/ - protected $dirsCache = array(); - - /** - * You should use `$this->sessionCache['subdirs']` instead - * - * @var array - * @deprecated - */ - protected $subdirsCache = array(); - - /** - * This volume session cache - * - * @var array - */ - protected $sessionCache; - - /** - * Session caching item list - * - * @var array - */ - protected $sessionCaching = array('rootstat' => true, 'subdirs' => true); - - /** - * elFinder session wrapper object - * - * @var elFinderSessionInterface - */ - protected $session; - - /** - * Search start time - * - * @var int - */ - protected $searchStart; - - /** - * Current query word on doSearch - * - * @var array - **/ - protected $doSearchCurrentQuery = array(); - - /** - * Is root modified (for clear root stat cache) - * - * @var bool - */ - protected $rootModified = false; - - /** - * Is disable of command `url` - * - * @var string - */ - protected $disabledGetUrl = false; - - /** - * Accepted filename validator - * - * @var string | callable - */ - protected $nameValidator; - - /** - * Accepted dirname validator - * - * @var string | callable - */ - protected $dirnameValidator; - - /*********************************************************************/ - /* INITIALIZATION */ - /*********************************************************************/ - - /** - * Prepare driver before mount volume. - * Return true if volume is ready. - * - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function init() { - return true; - } - - /** - * Configure after successfull mount. - * By default set thumbnails path and image manipulation library. - * - * @return void - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function configure() { - // set thumbnails path - $path = $this->options['tmbPath']; - if ($path) { - if (!file_exists($path)) { - if (mkdir($path)) { - chmod($path, $this->options['tmbPathMode']); - } else { - $path = ''; - } - } - - if (is_dir($path) && is_readable($path)) { - $this->tmbPath = $path; - $this->tmbPathWritable = is_writable($path); - } - } - // set resouce path - if (! is_dir($this->options['resourcePath'])) { - $this->options['resourcePath'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'resources'; - } - - // set image manipulation library - $type = preg_match('/^(imagick|gd|convert|auto)$/i', $this->options['imgLib']) - ? strtolower($this->options['imgLib']) - : 'auto'; - - if (($type === 'imagick' || $type === 'auto') && extension_loaded('imagick')) { - $this->imgLib = 'imagick'; - } else if (($type === 'gd' || $type === 'auto') && function_exists('gd_info')) { - $this->imgLib = 'gd'; - } else { - $convertCache = 'imgLibConvert'; - if (($convertCmd = $this->session->get($convertCache, false)) !== false) { - $this->imgLib = $convertCmd; - } else { - $this->imgLib = ($this->procExec(ELFINDER_CONVERT_PATH . ' -version') === 0)? 'convert' : ''; - $this->session->set($convertCache, $this->imgLib); - } - } - if ($type !== 'auto' && $this->imgLib === '') { - // fallback - $this->imgLib = extension_loaded('imagick')? 'imagick' : (function_exists('gd_info')? 'gd' : ''); - } - - // check video to img converter - if (! empty($this->options['imgConverter']) && is_array($this->options['imgConverter'])) { - foreach($this->options['imgConverter'] as $_type => $_converter) { - if (isset($_converter['func'])) { - $this->imgConverter[strtolower($_type)] = $_converter; - } - } - } - if (! isset($this->imgConverter['video'])) { - $videoLibCache = 'videoLib'; - if (($videoLibCmd = $this->session->get($videoLibCache, false)) === false) { - $videoLibCmd = ($this->procExec(ELFINDER_FFMPEG_PATH . ' -version') === 0)? 'ffmpeg' : ''; - $this->session->set($videoLibCache, $videoLibCmd); - } - if ($videoLibCmd) { - $this->imgConverter['video'] = array( - 'func' => array($this, $videoLibCmd . 'ToImg'), - 'maxlen' => $this->options['tmbVideoConvLen'] - ); - } - } - - // check archivers - if (empty($this->archivers['create'])) { - $this->disabled[] ='archive'; - } - if (empty($this->archivers['extract'])) { - $this->disabled[] ='extract'; - } - $_arc = $this->getArchivers(); - if (empty($_arc['create'])) { - $this->disabled[] ='zipdl'; - } - - // check 'statOwner' for command `chmod` - if (empty($this->options['statOwner'])) { - $this->disabled[] ='chmod'; - } - - // check 'mimeMap' - if (!is_array($this->options['mimeMap'])) { - $this->options['mimeMap'] = array(); - } - if (is_array($this->options['staticMineMap']) && $this->options['staticMineMap']) { - $this->options['mimeMap'] = array_merge($this->options['mimeMap'], $this->options['staticMineMap']); - } - if (is_array($this->options['additionalMimeMap']) && $this->options['additionalMimeMap']) { - $this->options['mimeMap'] = array_merge($this->options['mimeMap'], $this->options['additionalMimeMap']); - } - - // check 'url' in disabled commands - if (in_array('url', $this->disabled)) { - $this->disabledGetUrl = true; - } - - // set run time setting uploadOverwrite - $this->uploadOverwrite = $this->options['uploadOverwrite']; - } - - /** - * @deprecated - */ - protected function sessionRestart() { - $this->sessionCache = $this->session->start()->get($this->id, array()); - return true; - } - - /*********************************************************************/ - /* PUBLIC API */ - /*********************************************************************/ - - /** - * Return driver id. Used as a part of volume id. - * - * @return string - * @author Dmitry (dio) Levashov - **/ - public function driverId() { - return $this->driverId; - } - - /** - * Return volume id - * - * @return string - * @author Dmitry (dio) Levashov - **/ - public function id() { - return $this->id; - } - - /** - * Assign elFinder session wrapper object - * - * @param $session elFinderSessionInterface - */ - public function setSession($session) { - $this->session = $session; - } - - /** - * Get elFinder sesson wrapper object - * - * @return object The session object - */ - public function getSession() { - return $this->session; - } - - /** - * Save session cache data - * Calls this function before umount this volume on elFinder::exec() - * - * @return void - */ - public function saveSessionCache() { - $this->session->set($this->id, $this->sessionCache); - } - - /** - * Return debug info for client - * - * @return array - * @author Dmitry (dio) Levashov - **/ - public function debug() { - return array( - 'id' => $this->id(), - 'name' => strtolower(substr(get_class($this), strlen('elfinderdriver'))), - 'mimeDetect' => $this->mimeDetect, - 'imgLib' => $this->imgLib - ); - } - - /** - * chmod a file or folder - * - * @param string $hash file or folder hash to chmod - * @param string $mode octal string representing new permissions - * @return array|false - * @author David Bartle - **/ - public function chmod($hash, $mode) { - if ($this->commandDisabled('chmod')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if (!($file = $this->file($hash))) { - return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); - } - - if (!$this->options['allowChmodReadOnly']) { - if (!$this->attr($this->decode($hash), 'write', null, ($file['mime'] === 'directory'))) { - return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']); - } - } - - $path = $this->decode($hash); - $write = $file['write']; - - if ($this->convEncOut(!$this->_chmod($this->convEncIn($path), $mode))) { - return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']); - } - - $this->clearstatcache(); - if ($path == $this->root) { - $this->rootModified = true; - } - - if ($file = $this->stat($path)) { - $files = array($file); - if ($file['mime'] === 'directory' && $write !== $file['write']) { - foreach ($this->getScandir($path) as $stat) { - if ($this->mimeAccepted($stat['mime'])) { - $files[] = $stat; - } - } - } - return $files; - } else { - return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); - } - } - - /** - * stat a file or folder for elFinder cmd exec - * - * @param string $hash file or folder hash to chmod - * @return array - * @author Naoki Sawada - **/ - public function fstat($hash) { - $path = $this->decode($hash); - return $this->stat($path); - } - - /** - * Clear PHP stat cache & all of inner stat caches - * - */ - public function clearstatcache() { - clearstatcache(); - $this->clearcache(); - } - - /** - * Clear inner stat caches for target hash - * - * @param string $hash - */ - public function clearcaches($hash = null) { - if ($hash === null) { - $this->clearcache(); - } else { - $path = $this->decode($hash); - unset($this->cache[$path], $this->dirsCache[$path]); - } - } - - /** - * "Mount" volume. - * Return true if volume available for read or write, - * false - otherwise - * - * @param array $opts - * @return bool - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin - */ - public function mount(array $opts) { - $this->options = array_merge($this->options, $opts); - - if (!isset($this->options['path']) || $this->options['path'] === '') { - return $this->setError('Path undefined.'); - } - - if (! $this->session) { - return $this->setError('Session wrapper dose not set. Need to `$volume->setSession(elFinderSessionInterface);` before mount.'); - } - if (! ($this->session instanceof elFinderSessionInterface)) { - return $this->setError('Session wrapper instance must be "elFinderSessionInterface".'); - } - - // set driverId - if (!empty($this->options['driverId'])) { - $this->driverId = $this->options['driverId']; - } - - $this->id = $this->driverId.(!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++).'_'; - $this->root = $this->normpathCE($this->options['path']); - $this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR; - if (! empty($this->options['winHashFix'])) { - $this->separatorForHash = ($this->separator !== '/') ? '/' : ''; - } - $this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator; - - // set ARGS - $this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET; - - $argInit = !empty($this->ARGS['init']); - - // session cache - if ($argInit) { - $this->session->set($this->id, array()); - } - $this->sessionCache = $this->session->get($this->id, array()); - - // default file attribute - $this->defaults = array( - 'read' => isset($this->options['defaults']['read']) ? !!$this->options['defaults']['read'] : true, - 'write' => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true, - 'locked' => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false, - 'hidden' => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false - ); - - // root attributes - $this->attributes[] = array( - 'pattern' => '~^'.preg_quote($this->separator).'$~', - 'locked' => true, - 'hidden' => false - ); - // set files attributes - if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) { - - foreach ($this->options['attributes'] as $a) { - // attributes must contain pattern and at least one rule - if (!empty($a['pattern']) || count($a) > 1) { - $this->attributes[] = $a; - } - } - } - - if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) { - $this->access = $this->options['accessControl']; - } - - $this->today = mktime(0,0,0, date('m'), date('d'), date('Y')); - $this->yesterday = $this->today-86400; - - if (!$this->init()) { - return false; - } - - // set server encoding - if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') { - $this->encoding = $this->options['encoding']; - } else { - $this->encoding = null; - } - - // check some options is arrays - $this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow']) - ? $this->options['uploadAllow'] - : array(); - - $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny']) - ? $this->options['uploadDeny'] - : array(); - - $this->options['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap'])) - ? $this->options['uiCmdMap'] - : array(); - - if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x - $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow'); - $this->uploadOrder = array(trim($parts[0]), trim($parts[1])); - } else { // telephat_mode off - $this->uploadOrder = ! empty($this->options['uploadOrder'])? $this->options['uploadOrder'] : array('deny', 'allow'); - } - - if (!empty($this->options['uploadMaxSize'])) { - $this->uploadMaxSize = elFinder::getIniBytes('', $this->options['uploadMaxSize']); - } - // Set maximum to PHP_INT_MAX - if (!defined('PHP_INT_MAX')) { - define('PHP_INT_MAX', 2147483647); - } - if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) { - $this->uploadMaxSize = PHP_INT_MAX; - } - - // Set to get maximum size to 50% of memory_limit - $memLimit = elFinder::getIniBytes('memory_limit') / 2; - if ($memLimit > 0) { - $this->getMaxSize = empty($this->options['getMaxSize'])? $memLimit : min($memLimit, elFinder::getIniBytes('', $this->options['getMaxSize'])); - } else { - $this->getMaxSize = -1; - } - - $this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled']) - ? array_values(array_diff($this->options['disabled'], array('open'))) // 'open' is required - : array(); - - $this->cryptLib = $this->options['cryptLib']; - $this->mimeDetect = $this->options['mimeDetect']; - - // find available mimetype detect method - $type = strtolower($this->options['mimeDetect']); - $type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto'; - $regexp = '/text\/x\-(php|c\+\+)/'; - - if (($type == 'finfo' || $type == 'auto') - && class_exists('finfo', false)) { - $tmpFileInfo = explode(';', finfo_file(finfo_open(FILEINFO_MIME), __FILE__)); - } else { - $tmpFileInfo = false; - } - - $type = 'internal'; - if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) { - $type = 'finfo'; - $this->finfo = finfo_open(FILEINFO_MIME); - } elseif (($type == 'mime_content_type' || $type == 'auto') && function_exists('mime_content_type')) { - $_mimetypes = explode(';', mime_content_type(__FILE__)); - if (preg_match($regexp, array_shift($_mimetypes))) { - $type = 'mime_content_type'; - } - } - $this->mimeDetect = $type; - - // load mimes from external file for mimeDetect == 'internal' - // based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163 - // file must be in file directory or in parent one - if ($this->mimeDetect === 'internal' && !elFinderVolumeDriver::$mimetypesLoaded) { - elFinderVolumeDriver::loadMimeTypes(!empty($this->options['mimefile'])? $this->options['mimefile'] : ''); - } - - $this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias']; - - // This get's triggered if $this->root == '/' and alias is empty. - // Maybe modify _basename instead? - if ($this->rootName === '') $this->rootName = $this->separator; - - $root = $this->stat($this->root); - - if (!$root) { - return $this->setError('Root folder does not exist.'); - } - if (!$root['read'] && !$root['write']) { - return $this->setError('Root folder has not read and write permissions.'); - } - - if ($root['read']) { - if ($argInit) { - // check startPath - path to open by default instead of root - $startPath = $this->options['startPath']? $this->normpathCE($this->options['startPath']) : ''; - if ($startPath) { - $start = $this->stat($startPath); - if (!empty($start) - && $start['mime'] == 'directory' - && $start['read'] - && empty($start['hidden']) - && $this->inpathCE($startPath, $this->root)) { - $this->startPath = $startPath; - if (substr($this->startPath, -1, 1) == $this->options['separator']) { - $this->startPath = substr($this->startPath, 0, -1); - } - } - } - } - } else { - $this->options['URL'] = ''; - $this->options['tmbURL'] = ''; - $this->options['tmbPath'] = ''; - // read only volume - array_unshift($this->attributes, array( - 'pattern' => '/.*/', - 'read' => false - )); - } - $this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1; - $this->tmbSize = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48; - $this->URL = $this->options['URL']; - if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) { - $this->URL .= '/'; - } - - $dirUrlOwn = strtolower($this->options['dirUrlOwn']); - if ($dirUrlOwn === 'auto') { - $this->options['dirUrlOwn'] = $this->URL? false : true; - } else if ($dirUrlOwn === 'hide') { - $this->options['dirUrlOwn'] = 'hide'; - } else { - $this->options['dirUrlOwn'] = (bool)$this->options['dirUrlOwn']; - } - - $this->tmbURL = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : ''; - if ($this->tmbURL && $this->tmbURL !== 'self' && preg_match("|[^/?&=]$|", $this->tmbURL)) { - $this->tmbURL .= '/'; - } - - $this->nameValidator = !empty($this->options['acceptedName']) && (is_string($this->options['acceptedName']) || is_callable($this->options['acceptedName'])) - ? $this->options['acceptedName'] - : ''; - - $this->dirnameValidator = !empty($this->options['acceptedDirname']) && (is_callable($this->options['acceptedDirname']) || (is_string($this->options['acceptedDirname']) && preg_match($this->options['acceptedDirname'], '') !== false)) - ? $this->options['acceptedDirname'] - : $this->nameValidator; - - $this->_checkArchivers(); - - // enabling archivers['create'] with options['useRemoteArchive'] - if ($this->options['useRemoteArchive'] && empty($this->archivers['create']) && $this->getTempPath()) { - $_archivers = $this->getArchivers(); - $this->archivers['create'] = $_archivers['create']; - } - - // manual control archive types to create - if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) { - foreach ($this->archivers['create'] as $mime => $v) { - if (!in_array($mime, $this->options['archiveMimes'])) { - unset($this->archivers['create'][$mime]); - } - } - } - - // manualy add archivers - if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) { - foreach ($this->options['archivers']['create'] as $mime => $conf) { - if (strpos($mime, 'application/') === 0 - && !empty($conf['cmd']) - && isset($conf['argc']) - && !empty($conf['ext']) - && !isset($this->archivers['create'][$mime])) { - $this->archivers['create'][$mime] = $conf; - } - } - } - - if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) { - foreach ($this->options['archivers']['extract'] as $mime => $conf) { - if (strpos($mime, 'application/') === 0 - && !empty($conf['cmd']) - && isset($conf['argc']) - && !empty($conf['ext']) - && !isset($this->archivers['extract'][$mime])) { - $this->archivers['extract'][$mime] = $conf; - } - } - } - - if (! empty($this->options['noSessionCache']) && is_array($this->options['noSessionCache'])) { - foreach($this->options['noSessionCache'] as $_key) { - $this->sessionCaching[$_key] = false; - unset($this->sessionCache[$_key]); - } - } - if ($this->sessionCaching['subdirs']) { - if (! isset($this->sessionCache['subdirs'])) { - $this->sessionCache['subdirs'] = array(); - } - } - - - $this->configure(); - - // Normalize disabled (array_merge`for type array of JSON) - $this->disabled = array_values(array_unique($this->disabled)); - - // fix sync interval - if ($this->options['syncMinMs'] !== 0) { - $this->options['syncMinMs'] = max($this->options[$this->options['syncChkAsTs']? 'tsPlSleep' : 'lsPlSleep'] * 1000, intval($this->options['syncMinMs'])); - } - - // ` copyJoin` is required for the trash function - if ($this->options['trashHash'] && empty($this->options['copyJoin'])) { - $this->options['trashHash'] = ''; - } - - // set tmpLinkPath - if (elFinder::$tmpLinkPath && !$this->options['tmpLinkPath']) { - if (is_writeable(elFinder::$tmpLinkPath)) { - $this->options['tmpLinkPath'] = elFinder::$tmpLinkPath; - } else { - elFinder::$tmpLinkPath = ''; - } - } - if ($this->options['tmpLinkPath'] && is_writable($this->options['tmpLinkPath'])) { - $this->tmpLinkPath = realpath($this->options['tmpLinkPath']); - } else if (! $this->tmpLinkPath && $this->tmbURL && $this->tmbPath) { - $this->tmpLinkPath = $this->tmbPath; - $this->options['tmpLinkUrl'] = $this->tmbURL; - } else if (!$this->options['URL'] && is_writable('../files/.tmb')) { - $this->tmpLinkPath = realpath('../files/.tmb'); - $this->options['tmpLinkUrl'] = ''; - if (! elFinder::$tmpLinkPath) { - elFinder::$tmpLinkPath = $this->tmpLinkPath; - elFinder::$tmpLinkUrl = ''; - } - } - - // set tmpLinkUrl - if (elFinder::$tmpLinkUrl && !$this->options['tmpLinkUrl']) { - $this->options['tmpLinkUrl'] = elFinder::$tmpLinkUrl; - } - if ($this->options['tmpLinkUrl']) { - $this->tmpLinkUrl = $this->options['tmpLinkUrl']; - } - if ($this->tmpLinkPath && !$this->tmpLinkUrl) { - $cur = realpath('./'); - $i = 0; - while($cur !== $this->systemRoot && strpos($this->tmpLinkPath, $cur) !== 0) { - $i++; - $cur = dirname($cur); - } - list($req) = explode('?', $_SERVER['REQUEST_URI']); - $reqs = explode('/', dirname($req)); - $uri = join('/', array_slice($reqs, 0, count($reqs) - 1)).substr($this->tmpLinkPath, strlen($cur)); - $https = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off'); - $this->tmpLinkUrl = ($https ? 'https://' : 'http://') - .$_SERVER['SERVER_NAME'] // host - .(((! $https && $_SERVER['SERVER_PORT'] == 80) || ($https && $_SERVER['SERVER_PORT'] == 443)) ? '' : (':' . $_SERVER['SERVER_PORT'])) // port - .$uri; - if (! elFinder::$tmpLinkUrl) { - elFinder::$tmpLinkUrl = $this->tmpLinkUrl; - } - } - - // remove last '/' - if ($this->tmpLinkPath) { - $this->tmpLinkPath = rtrim($this->tmpLinkPath, '/'); - } - if ($this->tmpLinkUrl) { - $this->tmpLinkUrl = rtrim($this->tmpLinkUrl, '/'); - } - - // to update options cache - $this->updateCache($this->root, $root); - - return $this->mounted = true; - } - - /** - * Some "unmount" stuffs - may be required by virtual fs - * - * @return void - * @author Dmitry (dio) Levashov - **/ - public function umount() { - } - - /** - * Remove session cache of this volume - * - */ - public function clearSessionCache() { - $this->sessionCache = array(); - } - - /** - * Return error message from last failed action - * - * @return array - * @author Dmitry (dio) Levashov - **/ - public function error() { - return $this->error; - } - - /** - * Return is uploadable that given file name - * - * @param string $name file name - * @param bool $allowUnknown - * @return bool - * @author Naoki Sawada - **/ - public function isUploadableByName($name, $allowUnknown = false) { - $mimeByName = $this->mimetype($name, true); - return (($allowUnknown && $mimeByName === 'unknown') || $this->allowPutMime($mimeByName)); - } - - /** - * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes) - * - * @return array - * @author Naoki Sawada - */ - public function getMimeTable() { - // load mime.types - if (! elFinderVolumeDriver::$mimetypesLoaded) { - elFinderVolumeDriver::loadMimeTypes(); - } - return elFinderVolumeDriver::$mimetypes; - } - - /** - * Return file extention detected by MIME type - * - * @param string $mime MIME type - * @param string $suffix Additional suffix - * @return string - * @author Naoki Sawada - */ - public function getExtentionByMime($mime, $suffix = '') { - static $extTable = null; - - if (is_null($extTable)) { - $extTable = array_flip(array_unique($this->getMimeTable())); - foreach($this->options['mimeMap'] as $pair => $_mime) { - list($ext) = explode(':', $pair); - if ($ext !== '*' && ! isset($extTable[$_mime])) { - $extTable[$_mime] = $ext; - } - } - } - - if ($mime && isset($extTable[$mime])) { - return $suffix? ($extTable[$mime] . $suffix) : $extTable[$mime]; - } - return ''; - } - - /** - * Set mimetypes allowed to display to client - * - * @param array $mimes - * @return void - * @author Dmitry (dio) Levashov - **/ - public function setMimesFilter($mimes) { - if (is_array($mimes)) { - $this->onlyMimes = $mimes; - } - } - - /** - * Return root folder hash - * - * @return string - * @author Dmitry (dio) Levashov - **/ - public function root() { - return $this->encode($this->root); - } - - /** - * Return root path - * - * @return string - * @author Naoki Sawada - **/ - public function getRootPath() { - return $this->root; - } - - /** - * Return target path hash - * - * @param string $path - * @param string $name - * @author Naoki Sawada - * @return string - */ - public function getHash($path, $name = '') { - if ($name !== '') { - $path = $this->joinPathCE($path, $name); - } - return $this->encode($path); - } - - /** - * Return decoded path of target hash - * This method do not check the stat of target - * Use method `realpath()` to do check of the stat of target - * - * @param string $hash - * @author Naoki Sawada - * @return string - */ - public function getPath($hash) { - return $this->decode($hash); - } - - /** - * Return root or startPath hash - * - * @return string - * @author Dmitry (dio) Levashov - **/ - public function defaultPath() { - return $this->encode($this->startPath ? $this->startPath : $this->root); - } - - /** - * Return volume options required by client: - * - * @param $hash - * @return array - * @author Dmitry (dio) Levashov - */ - public function options($hash) { - $create = $createext = array(); - if (isset($this->archivers['create']) && is_array($this->archivers['create'])) { - foreach($this->archivers['create'] as $m => $v) { - $create[] = $m; - $createext[$m] = $v['ext']; - } - } - $opts = array( - 'path' => $hash? $this->path($hash) : '', - 'url' => $this->URL, - 'tmbUrl' => (! $this->imgLib && $this->options['tmbFbSelf'])? 'self' : $this->tmbURL, - 'disabled' => $this->disabled, - 'separator' => $this->separator, - 'copyOverwrite' => intval($this->options['copyOverwrite']), - 'uploadOverwrite' => intval($this->options['uploadOverwrite']), - 'uploadMaxSize' => intval($this->uploadMaxSize), - 'uploadMaxConn' => intval($this->options['uploadMaxConn']), - 'uploadMime' => array( - 'firstOrder' => isset($this->uploadOrder[0])? $this->uploadOrder[0] : 'deny', - 'allow' => $this->uploadAllow, - 'deny' => $this->uploadDeny - ), - 'dispInlineRegex' => $this->options['dispInlineRegex'], - 'jpgQuality' => intval($this->options['jpgQuality']), - 'archivers' => array( - 'create' => $create, - 'extract' => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(), - 'createext' => $createext - ), - 'uiCmdMap' => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap']))? $this->options['uiCmdMap'] : array(), - 'syncChkAsTs' => intval($this->options['syncChkAsTs']), - 'syncMinMs' => intval($this->options['syncMinMs']), - 'i18nFolderName' => intval($this->options['i18nFolderName']), - 'tmbCrop' => intval($this->options['tmbCrop']), - 'substituteImg' => (bool)$this->options['substituteImg'], - 'onetimeUrl' => (bool)$this->options['onetimeUrl'], - ); - if (! empty($this->options['trashHash'])) { - $opts['trashHash'] = $this->options['trashHash']; - } - if ($hash === null) { - // call from getRootStatExtra() - if (! empty($this->options['icon'])) { - $opts['icon'] = $this->options['icon']; - } - if (! empty($this->options['rootCssClass'])) { - $opts['csscls'] = $this->options['rootCssClass']; - } - if (isset($this->options['netkey'])) { - $opts['netkey'] = $this->options['netkey']; - } - } - return $opts; - } - - /** - * Get option value of this volume - * - * @param string $name target option name - * @return NULL|mixed target option value - * @author Naoki Sawada - */ - public function getOption($name) { - return isset($this->options[$name])? $this->options[$name] : null; - } - - /** - * Get plugin values of this options - * - * @param string $name Plugin name - * @return NULL|array Plugin values - * @author Naoki Sawada - */ - public function getOptionsPlugin($name = '') { - if ($name) { - return isset($this->options['plugin'][$name])? $this->options['plugin'][$name] : array(); - } else { - return $this->options['plugin']; - } - } - - /** - * Return true if command disabled in options - * - * @param string $cmd command name - * @return bool - * @author Dmitry (dio) Levashov - **/ - public function commandDisabled($cmd) { - return in_array($cmd, $this->disabled); - } - - /** - * Return true if mime is required mimes list - * - * @param string $mime mime type to check - * @param array $mimes allowed mime types list or not set to use client mimes list - * @param bool|null $empty what to return on empty list - * @return bool|null - * @author Dmitry (dio) Levashov - * @author Troex Nevelin - **/ - public function mimeAccepted($mime, $mimes = null, $empty = true) { - $mimes = is_array($mimes) ? $mimes : $this->onlyMimes; - if (empty($mimes)) { - return $empty; - } - return $mime == 'directory' - || in_array('all', $mimes) - || in_array('All', $mimes) - || in_array($mime, $mimes) - || in_array(substr($mime, 0, strpos($mime, '/')), $mimes); - } - - /** - * Return true if voume is readable. - * - * @return bool - * @author Dmitry (dio) Levashov - **/ - public function isReadable() { - $stat = $this->stat($this->root); - return $stat['read']; - } - - /** - * Return true if copy from this volume allowed - * - * @return bool - * @author Dmitry (dio) Levashov - **/ - public function copyFromAllowed() { - return !!$this->options['copyFrom']; - } - - /** - * Return file path related to root with convert encoging - * - * @param string $hash file hash - * @return string - * @author Dmitry (dio) Levashov - **/ - public function path($hash) { - return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash)))); - } - - /** - * Return file real path if file exists - * - * @param string $hash file hash - * @return string | false - * @author Dmitry (dio) Levashov - **/ - public function realpath($hash) { - $path = $this->decode($hash); - return $this->stat($path) ? $path : false; - } - - /** - * Return list of moved/overwrited files - * - * @return array - * @author Dmitry (dio) Levashov - **/ - public function removed() { - if ($this->removed) { - $unsetSubdir = isset($this->sessionCache['subdirs'])? true : false; - foreach($this->removed as $item) { - if ($item['mime'] === 'directory') { - $path = $this->decode($item['hash']); - if ($unsetSubdir) { - unset($this->sessionCache['subdirs'][$path]); - } - if ($item['phash'] !== '') { - $parent = $this->decode($item['phash']); - unset($this->cache[$parent]); - if ($this->root === $parent) { - $this->sessionCache['rootstat'] = array(); - } - if ($unsetSubdir) { - unset($this->sessionCache['subdirs'][$parent]); - } - } - } - } - $this->removed = array_values($this->removed); - } - return $this->removed; - } - - /** - * Return list of added files - * - * @deprecated - * @return array - * @author Naoki Sawada - **/ - public function added() { - return $this->added; - } - - /** - * Clean removed files list - * - * @return void - * @author Dmitry (dio) Levashov - **/ - public function resetRemoved() { - $this->resetResultStat(); - } - - /** - * Clean added/removed files list - * - * @return void - **/ - public function resetResultStat() { - $this->removed = array(); - $this->added = array(); - } - - /** - * Return file/dir hash or first founded child hash with required attr == $val - * - * @param string $hash file hash - * @param string $attr attribute name - * @param bool $val attribute value - * @return string|false - * @author Dmitry (dio) Levashov - **/ - public function closest($hash, $attr, $val) { - return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false; - } - - /** - * Return file info or false on error - * - * @param string $hash file hash - * @return array|false - * @internal param bool $realpath add realpath field to file info - * @author Dmitry (dio) Levashov - */ - public function file($hash) { - $file = $this->stat($this->decode($hash)); - - return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND); - } - - /** - * Return folder info - * - * @param string $hash folder hash - * @param bool $resolveLink - * @return array|false - * @internal param bool $hidden return hidden file info - * @author Dmitry (dio) Levashov - */ - public function dir($hash, $resolveLink=false) { - if (($dir = $this->file($hash)) == false) { - return $this->setError(elFinder::ERROR_DIR_NOT_FOUND); - } - - if ($resolveLink && !empty($dir['thash'])) { - $dir = $this->file($dir['thash']); - } - - return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) - ? $dir - : $this->setError(elFinder::ERROR_NOT_DIR); - } - - /** - * Return directory content or false on error - * - * @param string $hash file hash - * @return array|false - * @author Dmitry (dio) Levashov - **/ - public function scandir($hash) { - if (($dir = $this->dir($hash)) == false) { - return false; - } - - $path = $this->decode($hash); - if ($res = $dir['read'] - ? $this->getScandir($path) - : $this->setError(elFinder::ERROR_PERM_DENIED)) { - - $dirs = null; - if ($this->sessionCaching['subdirs'] && isset($this->sessionCache['subdirs'][$path])) { - $dirs = $this->sessionCache['subdirs'][$path]; - } - if ($dirs !== null || (isset($dir['dirs']) && $dir['dirs'] != 1)) { - $_dir = $dir; - if ($dirs || $this->subdirs($hash)) { - $dir['dirs'] = 1; - } else { - unset($dir['dirs']); - } - if ($dir !== $_dir) { - $this->updateCache($path, $dir); - } - } - } - - return $res; - } - - /** - * Return dir files names list - * - * @param string $hash file hash - * @param null $intersect - * @return array|false - * @author Dmitry (dio) Levashov - */ - public function ls($hash, $intersect = null) { - if (($dir = $this->dir($hash)) == false || !$dir['read']) { - return false; - } - - $list = array(); - $path = $this->decode($hash); - - $check = array(); - if ($intersect) { - $check = array_flip($intersect); - } - - foreach ($this->getScandir($path) as $stat) { - if (empty($stat['hidden']) && (!$check || isset($check[$stat['name']])) && $this->mimeAccepted($stat['mime'])) { - $list[$stat['hash']] = $stat['name']; - } - } - - return $list; - } - - /** - * Return subfolders for required folder or false on error - * - * @param string $hash folder hash or empty string to get tree from root folder - * @param int $deep subdir deep - * @param string $exclude dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders - * @return array|false - * @author Dmitry (dio) Levashov - **/ - public function tree($hash='', $deep=0, $exclude='') { - $path = $hash ? $this->decode($hash) : $this->root; - - if (($dir = $this->stat($path)) == false || $dir['mime'] != 'directory') { - return false; - } - - $dirs = $this->gettree($path, $deep > 0 ? $deep -1 : $this->treeDeep-1, $exclude ? $this->decode($exclude) : null); - array_unshift($dirs, $dir); - return $dirs; - } - - /** - * Return part of dirs tree from required dir up to root dir - * - * @param string $hash directory hash - * @param bool|null $lineal only lineal parents - * @return array|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - public function parents($hash, $lineal = false) { - if (($current = $this->dir($hash)) == false) { - return false; - } - - $args = func_get_args(); - // checks 3rd param `$until` (elFinder >= 2.1.24) - $until = ''; - if (isset($args[2])) { - $until = $args[2]; - } - - $path = $this->decode($hash); - $tree = array(); - - while ($path && $path != $this->root) { - elFinder::checkAborted(); - $path = $this->dirnameCE($path); - if (!($stat = $this->stat($path)) || !empty($stat['hidden']) || !$stat['read']) { - return false; - } - - array_unshift($tree, $stat); - if (!$lineal) { - foreach ($this->gettree($path, 0) as $dir) { - elFinder::checkAborted(); - if (!isset($tree[$dir['hash']])) { - $tree[$dir['hash']] = $dir; - } - } - } - - if ($until && $until === $this->encode($path)) { - break; - } - } - - return $tree ? array_values($tree) : array($current); - } - - /** - * Create thumbnail for required file and return its name of false on failed - * - * @param $hash - * @return false|string - * @throws ImagickException - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - public function tmb($hash) { - $path = $this->decode($hash); - $stat = $this->stat($path); - - if (isset($stat['tmb'])) { - $res = $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb']; - if (! $res) { - list($type) = explode('/', $stat['mime']); - $fallback = $this->options['resourcePath'] . DIRECTORY_SEPARATOR . strtolower($type) . '.png'; - if (is_file($fallback)) { - $res = $this->tmbname($stat); - if (! copy($fallback, $this->tmbPath . DIRECTORY_SEPARATOR . $res)) { - $res = false; - } - } - } - return $res; - } - return false; - } - - /** - * Return file size / total directory size - * - * @param string file hash - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - public function size($hash) { - return $this->countSize($this->decode($hash)); - } - - /** - * Open file for reading and return file pointer - * - * @param string file hash - * @return Resource|false - * @author Dmitry (dio) Levashov - **/ - public function open($hash) { - if (($file = $this->file($hash)) == false - || $file['mime'] == 'directory') { - return false; - } - - return $this->fopenCE($this->decode($hash), 'rb'); - } - - /** - * Close file pointer - * - * @param Resource $fp file pointer - * @param string $hash file hash - * @return void - * @author Dmitry (dio) Levashov - **/ - public function close($fp, $hash) { - $this->fcloseCE($fp, $this->decode($hash)); - } - - /** - * Create directory and return dir info - * - * @param string $dsthash destination directory hash - * @param string $name directory name - * @return array|false - * @author Dmitry (dio) Levashov - **/ - public function mkdir($dsthash, $name) { - if ($this->commandDisabled('mkdir')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if (!$this->nameAccepted($name, true)) { - return $this->setError(elFinder::ERROR_INVALID_DIRNAME); - } - - if (($dir = $this->dir($dsthash)) == false) { - return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dsthash); - } - - $path = $this->decode($dsthash); - - if (!$dir['write'] || !$this->allowCreate($path, $name, true)) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - $dst = $this->joinPathCE($path, $name); - $stat = $this->isNameExists($dst); - if (!empty($stat)) { - return $this->setError(elFinder::ERROR_EXISTS, $name); - } - $this->clearcache(); - - $mkpath = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name))); - if ($mkpath) { - $this->clearstatcache(); - $this->updateSubdirsCache($path, true); - $this->updateSubdirsCache($mkpath, false); - } - - return $mkpath? $this->stat($mkpath) : false; - } - - /** - * Create empty file and return its info - * - * @param string $dst destination directory - * @param string $name file name - * @return array|false - * @author Dmitry (dio) Levashov - **/ - public function mkfile($dst, $name) { - if ($this->commandDisabled('mkfile')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if (!$this->nameAccepted($name, false)) { - return $this->setError(elFinder::ERROR_INVALID_NAME); - } - - $mimeByName = $this->mimetype($name, true); - if ($mimeByName && !$this->allowPutMime($mimeByName)) { - return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name); - } - - if (($dir = $this->dir($dst)) == false) { - return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); - } - - $path = $this->decode($dst); - - if (!$dir['write'] || !$this->allowCreate($path, $name, false)) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if ($this->isNameExists($this->joinPathCE($path, $name))) { - return $this->setError(elFinder::ERROR_EXISTS, $name); - } - - $this->clearcache(); - $res = false; - if ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) { - $this->clearstatcache(); - $res = $this->stat($path); - } - return $res; - } - - /** - * Rename file and return file info - * - * @param string $hash file hash - * @param string $name new file name - * @return array|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - public function rename($hash, $name) { - if ($this->commandDisabled('rename')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if (!($file = $this->file($hash))) { - return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); - } - - if ($name === $file['name']) { - return $file; - } - - if (!empty($file['locked'])) { - return $this->setError(elFinder::ERROR_LOCKED, $file['name']); - } - - $isDir = ($file['mime'] === 'directory'); - - if (!$this->nameAccepted($name, $isDir)) { - return $this->setError($isDir? elFinder::ERROR_INVALID_DIRNAME : elFinder::ERROR_INVALID_NAME); - } - - if (! $isDir) { - $mimeByName = $this->mimetype($name, true); - if ($mimeByName && !$this->allowPutMime($mimeByName)) { - return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name); - } - } - - $path = $this->decode($hash); - $dir = $this->dirnameCE($path); - $stat = $this->isNameExists($this->joinPathCE($dir, $name)); - if ($stat) { - return $this->setError(elFinder::ERROR_EXISTS, $name); - } - - if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - $this->rmTmb($file); // remove old name tmbs, we cannot do this after dir move - - - if ($path = $this->convEncOut($this->_move($this->convEncIn($path), $this->convEncIn($dir), $this->convEncIn($name)))) { - $this->clearcache(); - return $this->stat($path); - } - return false; - } - - /** - * Create file copy with suffix "copy number" and return its info - * - * @param string $hash file hash - * @param string $suffix suffix to add to file name - * @return array|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - public function duplicate($hash, $suffix='copy') { - if ($this->commandDisabled('duplicate')) { - return $this->setError(elFinder::ERROR_COPY, '#'.$hash, elFinder::ERROR_PERM_DENIED); - } - - if (($file = $this->file($hash)) == false) { - return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND); - } - - $path = $this->decode($hash); - $dir = $this->dirnameCE($path); - $name = $this->uniqueName($dir, $file['name'], sprintf($this->options['duplicateSuffix'], $suffix)); - - if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - return ($path = $this->copy($path, $dir, $name)) == false - ? false - : $this->stat($path); - } - - /** - * Save uploaded file. - * On success return array with new file stat and with removed file hash (if existed file was replaced) - * - * @param Resource $fp file pointer - * @param string $dst destination folder hash - * @param $name - * @param string $tmpname file tmp name - required to detect mime type - * @param array $hashes exists files hash array with filename as key - * @return array|false - * @throws elFinderAbortException - * @internal param string $src file name - * @author Dmitry (dio) Levashov - */ - public function upload($fp, $dst, $name, $tmpname, $hashes = array()) { - if ($this->commandDisabled('upload')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if (($dir = $this->dir($dst)) == false) { - return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); - } - - if (empty($dir['write'])) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if (!$this->nameAccepted($name, false)) { - return $this->setError(elFinder::ERROR_INVALID_NAME); - } - - $mimeByName = ''; - if ($this->mimeDetect === 'internal') { - $mime = $this->mimetype($tmpname, $name); - } else { - $mime = $this->mimetype($tmpname, $name); - $mimeByName = $this->mimetype($name, true); - if ($mime === 'unknown') { - $mime = $mimeByName; - } - } - - if (!$this->allowPutMime($mime) || ($mimeByName && !$this->allowPutMime($mimeByName))) { - return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, '(' . $mime . ')'); - } - - $tmpsize = (int)sprintf('%u', filesize($tmpname)); - if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) { - return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE); - } - - $dstpath = $this->decode($dst); - if (isset($hashes[$name])) { - $test = $this->decode($hashes[$name]); - $file = $this->stat($test); - } else { - $test = $this->joinPathCE($dstpath, $name); - $file = $this->isNameExists($test); - } - - $this->clearcache(); - - if ($file && $file['name'] === $name) { // file exists and check filename for item ID based filesystem - if ($this->uploadOverwrite) { - if (!$file['write']) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } elseif ($file['mime'] == 'directory') { - return $this->setError(elFinder::ERROR_NOT_REPLACE, $name); - } - $this->remove($test); - } else { - $name = $this->uniqueName($dstpath, $name, '-', false); - } - } - - $stat = array( - 'mime' => $mime, - 'width' => 0, - 'height' => 0, - 'size' => $tmpsize); - - // $w = $h = 0; - if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) { - $stat['width'] = $s[0]; - $stat['height'] = $s[1]; - } - // $this->clearcache(); - if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) { - return false; - } - - $stat = $this->stat($path); - // Try get URL - if (empty($stat['url']) && ($url = $this->getContentUrl($stat['hash']))) { - $stat['url'] = $url; - } - - return $stat; - } - - /** - * Paste files - * - * @param Object $volume source volume - * @param $src - * @param string $dst destination dir hash - * @param bool $rmSrc remove source after copy? - * @param array $hashes - * @return array|false - * @throws elFinderAbortException - * @internal param string $source file hash - * @author Dmitry (dio) Levashov - */ - public function paste($volume, $src, $dst, $rmSrc = false, $hashes = array()) { - $err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY; - - if ($this->commandDisabled('paste')) { - return $this->setError($err, '#'.$src, elFinder::ERROR_PERM_DENIED); - } - - if (($file = $volume->file($src, $rmSrc)) == false) { - return $this->setError($err, '#'.$src, elFinder::ERROR_FILE_NOT_FOUND); - } - - $name = $file['name']; - $errpath = $volume->path($file['hash']); - - if (($dir = $this->dir($dst)) == false) { - return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#'.$dst); - } - - if (!$dir['write'] || !$file['read']) { - return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); - } - - $destination = $this->decode($dst); - - if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) { - return $rmSrc - ? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test)) - : $this->setError($err, $errpath, empty($file['thash'])? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK); - } - - if (isset($hashes[$name])) { - $test = $this->decode($hashes[$name]); - $stat = $this->stat($test); - } else { - $test = $this->joinPathCE($destination, $name); - $stat = $this->isNameExists($test); - } - $this->clearcache(); - $dstDirExists = false; - if ($stat && $stat['name'] === $name) { // file exists and check filename for item ID based filesystem - if ($this->options['copyOverwrite']) { - // do not replace file with dir or dir with file - if (!$this->isSameType($file['mime'], $stat['mime'])) { - return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash'])); - } - // existed file is not writable - if (empty($stat['write'])) { - return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); - } - if ($this->options['copyJoin']) { - if (!empty($stat['locked'])) { - return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash'])); - } - } else { - // existed file locked or has locked child - if (($locked = $this->closestByAttr($test, 'locked', true))) { - $stat = $this->stat($locked); - return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash'])); - } - } - // target is entity file of alias - if ($volume === $this && ((isset($file['target']) && $test == $file['target']) || $test == $this->decode($src))) { - return $this->setError(elFinder::ERROR_REPLACE, $errpath); - } - // remove existed file - if (! $this->options['copyJoin'] || $stat['mime'] !== 'directory') { - if (! $this->remove($test)) { - return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash'])); - } - } else if ($stat['mime'] === 'directory') { - $dstDirExists = true; - } - } else { - $name = $this->uniqueName($destination, $name, ' ', false); - } - } - - // copy/move inside current volume - if ($volume === $this) { // changing == operand to === fixes issue #1285 - Paul Canning 24/03/2016 - $source = $this->decode($src); - // do not copy into itself - if ($this->inpathCE($destination, $source)) { - return $this->setError(elFinder::ERROR_COPY_ITSELF, $errpath); - } - $rmDir = false; - if ($rmSrc) { - if ($dstDirExists) { - $rmDir = true; - $method = 'copy'; - } else { - $method = 'move'; - } - } else { - $method = 'copy'; - } - $this->clearcache(); - if ($res = ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false) { - if ($rmDir) { - $this->remove($source); - } - } else { - return false; - } - } else { - // copy/move from another volume - if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) { - return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED); - } - - $this->error = array(); - if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) { - return false; - } - - if ($rmSrc && !$this->error()) { - if (!$volume->rm($src)) { - if ($volume->file($src)) { - $this->addError(elFinder::ERROR_RM_SRC); - } else { - $this->removed[] = $file; - } - } - } - $res = $this->stat($path); - } - return $res; - } - - /** - * Return path info array to archive of target items - * - * @param array $hashes - * @return array|false - * @throws Exception - * @author Naoki Sawada - */ - public function zipdl($hashes) { - if ($this->commandDisabled('zipdl')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - $archivers = $this->getArchivers(); - $cmd = null; - if (!$archivers || empty($archivers['create'])) { - return false; - } - $archivers = $archivers['create']; - if (!$archivers) { - return false; - } - foreach(array('zip', 'tgz') as $ext) { - $mime = $this->mimetype('file.'.$ext, true); - if (isset($archivers[$mime])) { - $cmd = $archivers[$mime]; - break; - } - } - if (!$cmd) { - $cmd = array_shift($archivers); - if (!empty($ext)) { - $mime = $this->mimetype('file.'.$ext, true); - } - } - $file = $mime = ''; - $ext = $cmd['ext']; - $res = false; - $mixed = false; - $hashes = array_values($hashes); - $dirname = dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[0]))); - $cnt = count($hashes); - if ($cnt > 1) { - for($i = 1; $i < $cnt; $i++) { - if ($dirname !== dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[$i])))) { - $mixed = true; - break; - } - } - } - if ($mixed || $this->root == $this->dirnameCE($this->decode($hashes[0]))) { - $prefix = $this->rootName; - } else { - $prefix = basename($dirname); - } - if ($dir = $this->getItemsInHand($hashes)) { - $tmppre = (substr(PHP_OS, 0, 3) === 'WIN')? 'zdl' : 'elfzdl'; - $pdir = dirname($dir); - // garbage collection (expire 2h) - register_shutdown_function(array('elFinder', 'GlobGC'), $pdir.DIRECTORY_SEPARATOR.$tmppre.'*', 7200); - $files = self::localScandir($dir); - if ($files && ($arc = tempnam($dir, $tmppre))) { - unlink($arc); - $arc = $arc.'.'.$ext; - $name = basename($arc); - if ($arc = $this->makeArchive($dir, $files, $name, $cmd)) { - $file = tempnam($pdir, $tmppre); - unlink($file); - $res = rename($arc, $file); - $this->rmdirRecursive($dir); - } - } - } - return $res? array('path' => $file, 'ext' => $ext, 'mime' => $mime, 'prefix' => $prefix) : false; - } - - /** - * Return file contents - * - * @param string $hash file hash - * @return string|false - * @author Dmitry (dio) Levashov - **/ - public function getContents($hash) { - $file = $this->file($hash); - - if (!$file) { - return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); - } - - if ($file['mime'] == 'directory') { - return $this->setError(elFinder::ERROR_NOT_FILE); - } - - if (!$file['read']) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if ($this->getMaxSize > 0 && $file['size'] > $this->getMaxSize) { - return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE); - } - - return $file['size']? $this->_getContents($this->convEncIn($this->decode($hash), true)) : ''; - } - - /** - * Put content in text file and return file info. - * - * @param string $hash file hash - * @param string $content new file content - * @return array|false - * @author Dmitry (dio) Levashov - **/ - public function putContents($hash, $content) { - if ($this->commandDisabled('edit')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - $path = $this->decode($hash); - - if (!($file = $this->file($hash))) { - return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); - } - - if (!$file['write']) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - // check data cheme - if (preg_match('~^\0data:(.+?/.+?);base64,~', $content, $m)) { - $dMime =$m[1]; - if ($file['size'] > 0 && $dMime !== $file['mime']) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - $content = base64_decode(substr($content, strlen($m[0]))); - } - - // check MIME - $name = $this->basenameCE($path); - $mime = ''; - $mimeByName = $this->mimetype($name, true); - if ($this->mimeDetect !== 'internal') { - if ($tp = $this->tmpfile()) { - fwrite($tp, $content); - $info = stream_get_meta_data($tp); - $filepath = $info['uri']; - $mime = $this->mimetype($filepath, $name); - fclose($tp); - } - } - if (!$this->allowPutMime($mimeByName) || ($mime && !$this->allowPutMime($mime))) { - return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME); - } - - $this->clearcache(); - $res = false; - if ($this->convEncOut($this->_filePutContents($this->convEncIn($path), $content))) { - $this->clearstatcache(); - $res = $this->stat($path); - } - return $res; - } - - /** - * Extract files from archive - * - * @param string $hash archive hash - * @param null $makedir - * @return array|bool - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - */ - public function extract($hash, $makedir = null) { - if ($this->commandDisabled('extract')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if (($file = $this->file($hash)) == false) { - return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); - } - - $archiver = isset($this->archivers['extract'][$file['mime']]) - ? $this->archivers['extract'][$file['mime']] - : array(); - - if (!$archiver) { - return $this->setError(elFinder::ERROR_NOT_ARCHIVE); - } - - $path = $this->decode($hash); - $parent = $this->stat($this->dirnameCE($path)); - - if (!$file['read'] || !$parent['write']) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - $this->clearcache(); - $this->extractToNewdir = is_null($makedir)? 'auto' : (bool)$makedir; - - if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) { - if (is_array($path)) { - foreach ($path as $_k => $_p) { - $path[$_k] = $this->stat($_p); - } - } else { - $path = $this->stat($path); - } - return $path; - } else { - return false; - } - } - - /** - * Add files to archive - * - * @param $hashes - * @param $mime - * @param string $name - * @return array|bool - * @throws Exception - */ - public function archive($hashes, $mime, $name = '') { - if ($this->commandDisabled('archive')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if ($name !== '' && !$this->nameAccepted($name, false)) { - return $this->setError(elFinder::ERROR_INVALID_NAME); - } - - $archiver = isset($this->archivers['create'][$mime]) - ? $this->archivers['create'][$mime] - : array(); - - if (!$archiver) { - return $this->setError(elFinder::ERROR_ARCHIVE_TYPE); - } - - $files = array(); - $useRemoteArchive = !empty($this->options['useRemoteArchive']); - - $dir = ''; - foreach ($hashes as $hash) { - if (($file = $this->file($hash)) == false) { - return $this->setError(elFinder::ERROR_FILE_NOT_FOUND, '#'.$hash); - } - if (!$file['read']) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - $path = $this->decode($hash); - if ($dir === '') { - $dir = $this->dirnameCE($path); - $stat = $this->stat($dir); - if (!$stat['write']) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - } - - $files[] = $useRemoteArchive? $hash : $this->basenameCE($path); - } - - if ($name === '') { - $name = count($files) == 1 ? $files[0] : 'Archive'; - } else { - $name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name)); - } - $name .='.' . $archiver['ext']; - $name = $this->uniqueName($dir, $name, ''); - $this->clearcache(); - if ($useRemoteArchive) { - return ($path = $this->remoteArchive($files, $name, $archiver)) ? $this->stat($path) : false; - } else { - return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false; - } - } - - /** - * Create an archive from remote items - * - * @param array $hashes files hashes list - * @param string $name archive name - * @param array $arc archiver options - * @return string|boolean path of created archive - * @throws Exception - */ - protected function remoteArchive($hashes, $name, $arc) { - $resPath = false; - $file0 = $this->file($hashes[0]); - if ($file0 && ($dir = $this->getItemsInHand($hashes))) { - $files = self::localScandir($dir); - if ($files) { - if ($arc = $this->makeArchive($dir, $files, $name, $arc)) { - if ($fp = fopen($arc, 'rb')) { - $path = $this->decode($file0['phash']); - $stat = array(); - $resPath = $this->saveCE($fp, $path, $name, $stat); - fclose($fp); - } - } - } - $this->rmdirRecursive($dir); - } - return $resPath; - } - - /** - * Resize image - * - * @param string $hash image file - * @param int $width new width - * @param int $height new height - * @param int $x X start poistion for crop - * @param int $y Y start poistion for crop - * @param string $mode action how to mainpulate image - * @param string $bg background color - * @param int $degree rotete degree - * @param int $jpgQuality JEPG quality (1-100) - * @return array|false - * @throws ImagickException - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin - * @author nao-pon - * @author Troex Nevelin - * - */ - public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = null) { - if ($this->commandDisabled('resize')) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if ($mode === 'rotate' && $degree == 0) { - return array('losslessRotate' => ($this->procExec(ELFINDER_EXIFTRAN_PATH . ' -h') === 0 || $this->procExec(ELFINDER_JPEGTRAN_PATH . ' -version') === 0)); - } - - if (($file = $this->file($hash)) == false) { - return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); - } - - if (!$file['write'] || !$file['read']) { - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - $path = $this->decode($hash); - - $work_path = $this->getWorkFile($this->encoding? $this->convEncIn($path, true) : $path); - - if (!$work_path || !is_writable($work_path)) { - if ($work_path && $path !== $work_path && is_file($work_path)) { - unlink($work_path); - } - return $this->setError(elFinder::ERROR_PERM_DENIED); - } - - if ($this->imgLib !== 'imagick' && $this->imgLib !== 'convert') { - if (elFinder::isAnimationGif($work_path)) { - return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE); - } - } - - if (elFinder::isAnimationPng($work_path)) { - return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE); - } - - switch($mode) { - - case 'propresize': - $result = $this->imgResize($work_path, $width, $height, true, true, null, $jpgQuality); - break; - - case 'crop': - $result = $this->imgCrop($work_path, $width, $height, $x, $y, null, $jpgQuality); - break; - - case 'fitsquare': - $result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality); - break; - - case 'rotate': - $result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['bgColorFb']), null, $jpgQuality); - break; - - default: - $result = $this->imgResize($work_path, $width, $height, false, true, null, $jpgQuality); - break; - } - - $ret = false; - if ($result) { - $this->rmTmb($file); - $this->clearstatcache(); - $fstat = stat($work_path); - $imgsize = getimagesize($work_path); - if ($path !== $work_path) { - $file['size'] = $fstat['size']; - $file['ts'] = $fstat['mtime']; - if ($imgsize) { - $file['width'] = $imgsize[0]; - $file['height'] = $imgsize[1]; - } - if ($fp = fopen($work_path, 'rb')) { - $ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $file); - fclose($fp); - } - } else { - $ret = true; - } - if ($ret) { - $this->clearcache(); - $ret = $this->stat($path); - if ($imgsize) { - $ret['width'] = $imgsize[0]; - $ret['height'] = $imgsize[1]; - } - } - } - if ($path !== $work_path) { - is_file($work_path) && unlink($work_path); - } - - return $ret; - } - - /** - * Remove file/dir - * - * @param string $hash file hash - * @return bool - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - public function rm($hash) { - return $this->commandDisabled('rm') - ? $this->setError(elFinder::ERROR_PERM_DENIED) - : $this->remove($this->decode($hash)); - } - - /** - * Search files - * - * @param string $q search string - * @param array $mimes - * @param null $hash - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - public function search($q, $mimes, $hash = null) { - $res = array(); - $matchMethod = null; - $args = func_get_args(); - if (!empty($args[3])) { - $matchMethod = 'searchMatch' . $args[3]; - if (! is_callable(array($this, $matchMethod))) { - return array(); - } - } - - $dir = null; - if ($hash) { - $dir = $this->decode($hash); - $stat = $this->stat($dir); - if (!$stat || $stat['mime'] !== 'directory' || !$stat['read']) { - $q = ''; - } - } - if ($mimes && $this->onlyMimes) { - $mimes = array_intersect($mimes, $this->onlyMimes); - if (!$mimes) { - $q = ''; - } - } - $this->searchStart = time(); - - $qs = preg_split('/"([^"]+)"| +/', $q, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE); - $query = $excludes = array(); - foreach($qs as $_q) { - $_q = trim($_q); - if ($_q !== '') { - if ($_q[0] === '-') { - if (isset($_q[1])) { - $excludes[] = substr($_q, 1); - } - } else { - $query[] = $_q; - } - } - } - if (! $query) { - $q = ''; - } else { - $q = join(' ', $query); - $this->doSearchCurrentQuery = array( - 'q' => $q, - 'excludes' => $excludes, - 'matchMethod' => $matchMethod - ); - } - - if ($q === '' || $this->commandDisabled('search')) { - return $res; - } - - // valided regex $this->options['searchExDirReg'] - if ($this->options['searchExDirReg']) { - if (false === preg_match($this->options['searchExDirReg'], '')) { - $this->options['searchExDirReg'] = ''; - } - } - - // check the leaf root too - if (!$mimes && (is_null($dir) || $dir == $this->root)) { - $rootStat = $this->stat($this->root); - if (!empty($rootStat['phash'])) { - if ($this->stripos($rootStat['name'], $q) !== false) { - $res = array($rootStat); - } - } - } - - return array_merge($res, $this->doSearch(is_null($dir)? $this->root : $dir, $q, $mimes)); - } - - /** - * Return image dimensions - * - * @param string $hash file hash - * @return array|string - * @author Dmitry (dio) Levashov - **/ - public function dimensions($hash) { - if (($file = $this->file($hash)) == false) { - return false; - } - // Throw additional parameters for some drivers - if (func_num_args() > 1) { - $args = func_get_arg(1); - } else { - $args = array(); - } - return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime'], $args)); - } - - /** - * Return has subdirs - * - * @param string $hash file hash - * @return bool - * @author Naoki Sawada - **/ - public function subdirs($hash) { - return (bool) $this->subdirsCE($this->decode($hash)); - } - - /** - * Return content URL (for netmout volume driver) - * If file.url == 1 requests from JavaScript client with XHR - * - * @param string $hash file hash - * @param array $options options array - * @return boolean|string - * @author Naoki Sawada - */ - public function getContentUrl($hash, $options = array()) { - if (($file = $this->file($hash)) === false) { - return false; - } - if (! empty($options['onetime']) && $this->options['onetimeUrl']) { - if (is_callable($this->options['onetimeUrl'])) { - return call_user_func_array($this->options['onetimeUrl'], array($file, $options, $this)); - } else { - $ret = false; - if ($tmpdir = elFinder::getStaticVar('commonTempPath')) { - if ($source = $this->open($hash)) { - if ($_dat = tempnam($tmpdir, 'ELF')) { - $token = md5($_dat . session_id()); - $dat = $tmpdir . DIRECTORY_SEPARATOR . 'ELF' . $token; - if (rename($_dat, $dat)) { - $info = stream_get_meta_data($source); - if (!empty($info['uri'])) { - $tmp = $info['uri']; - } else { - $tmp = tempnam($tmpdir, 'ELF'); - if ($dest = fopen($tmp, 'wb')) { - if (!stream_copy_to_stream($source, $dest)) { - $tmp = false; - } - fclose($dest); - } - } - $this->close($source, $hash); - if ($tmp) { - $info = array( - 'file' => base64_encode($tmp), - 'name' => $file['name'], - 'mime' => $file['mime'], - 'ts' => $file['ts'] - ); - if (file_put_contents($dat, json_encode($info))) { - $ret = elFinder::getConnectorUrl() . '?cmd=file&onetime=1&target=' . $token; - - } - } - if (!$ret) { - unlink($dat); - } - } else { - unlink($_dat); - } - } - } - } - return $ret; - } - } - if (empty($file['url']) && $this->URL) { - $path = str_replace($this->separator, '/', substr($this->decode($hash), strlen(rtrim($this->root, '/'.$this->separator)) + 1)); - if ($this->encoding) { - $path = $this->convEncIn($path, true); - } - $path = str_replace('%2F', '/', rawurlencode($path)); - return $this->URL . $path; - } else { - $ret = false; - if ($file['url'] && $file['url'] != 1) { - return $file['url']; - } else if (!empty($options['temporary']) && ($tempInfo = $this->getTempLinkInfo('temp_' . md5($hash.session_id())))) { - if ($source = $this->open($hash)) { - if ($dest = fopen($tempInfo['path'], 'wb')) { - if (stream_copy_to_stream($source, $dest)) { - $ret = $tempInfo['url'] . '?' . rawurlencode($file['name']); - } - fclose($dest); - } - $this->close($source, $hash); - } - } - return $ret; - } - } - - /** - * Get temporary contents link infomation - * - * @param string $name - * @return boolean|array - * @author Naoki Sawada - */ - public function getTempLinkInfo($name = null) { - if ($this->tmpLinkPath) { - if (! $name) { - $name = 'temp_' . md5($_SERVER['REMOTE_ADDR'] . (string)microtime(true)); - } else if (substr($name, 0, 5) !== 'temp_') { - $name = 'temp_' . $name; - } - register_shutdown_function(array('elFinder', 'GlobGC'), $this->tmpLinkPath . DIRECTORY_SEPARATOR . 'temp_*', elFinder::$tmpLinkLifeTime); - return array( - 'path' => $path = $this->tmpLinkPath . DIRECTORY_SEPARATOR . $name, - 'url' => $this->tmpLinkUrl . '/' . $name - ); - } - return false; - } - - /** - * Get URL of substitute image by request args `substitute` or 4th argument $maxSize - * - * @param string $target Target hash - * @param array $srcSize Size info array [width, height] - * @param resource $srcfp Source file file pointer - * @param integer $maxSize Maximum pixel of substitute image - * @return boolean - * @throws ImagickException - * @throws elFinderAbortException - */ - public function getSubstituteImgLink($target, $srcSize, $srcfp = null, $maxSize = null) { - $url = false; - if (! $maxSize) { - $args = elFinder::$currentArgs; - if (! empty($args['substitute'])) { - $maxSize = $args['substitute']; - } - } - if ($maxSize && $srcSize[0] && $srcSize[1]) { - if ($this->getOption('substituteImg')) { - $maxSize = intval($maxSize); - $zoom = min(($maxSize/$srcSize[0]),($maxSize/$srcSize[1])); - if ($zoom < 1) { - $width = round($srcSize[0] * $zoom); - $height = round($srcSize[1] * $zoom); - $jpgQuality = 50; - $preserveExif = false; - $unenlarge = true; - $checkAnimated = true; - if (! $srcfp) { - elFinder::checkAborted(); - $srcfp = $this->open($target); - } - if ($srcfp && ($tempLink = $this->getTempLinkInfo())) { - elFinder::checkAborted(); - $dest = fopen($tempLink['path'], 'wb'); - if ($dest && stream_copy_to_stream($srcfp, $dest)) { - fclose($dest); - if ($this->imageUtil('resize', $tempLink['path'], compact('width', 'height', 'jpgQuality', 'preserveExif', 'unenlarge', 'checkAnimated'))) { - $url = $tempLink['url']; - // set expire to 1 min left - touch($tempLink['path'], time() - elFinder::$tmpLinkLifeTime + 60); - } else { - unlink($tempLink['path']); - } - } - $this->close($srcfp, $target); - } - } - } - } - - return $url; - } - - /** - * Return temp path - * - * @return string - * @author Naoki Sawada - */ - public function getTempPath() { - $tempPath = null; - if (isset($this->tmpPath) && $this->tmpPath && is_writable($this->tmpPath)) { - $tempPath = $this->tmpPath; - } else if (isset($this->tmp) && $this->tmp && is_writable($this->tmp)) { - $tempPath = $this->tmp; - } else if (elFinder::getStaticVar('commonTempPath') && is_writable(elFinder::getStaticVar('commonTempPath'))) { - $tempPath = elFinder::getStaticVar('commonTempPath'); - } else if (function_exists('sys_get_temp_dir')) { - $tempPath = sys_get_temp_dir(); - } else if ($this->tmbPathWritable) { - $tempPath = $this->tmbPath; - } - if ($tempPath && DIRECTORY_SEPARATOR !== '/') { - $tempPath = str_replace('/', DIRECTORY_SEPARATOR, $tempPath); - } - return $tempPath; - } - - /** - * (Make &) Get upload taget dirctory hash - * - * @param string $baseTargetHash - * @param string $path - * @param array $result - * @return boolean|string - * @author Naoki Sawada - */ - public function getUploadTaget($baseTargetHash, $path, & $result) { - $base = $this->decode($baseTargetHash); - $targetHash = $baseTargetHash; - $path = ltrim($path, $this->separator); - $dirs = explode($this->separator, $path); - array_pop($dirs); - foreach($dirs as $dir) { - $targetPath = $this->joinPathCE($base, $dir); - if (! $_realpath = $this->realpath($this->encode($targetPath))) { - if ($stat = $this->mkdir($targetHash, $dir)) { - $result['added'][] = $stat; - $targetHash = $stat['hash']; - $base = $this->decode($targetHash); - } else { - return false; - } - } else { - $targetHash = $this->encode($_realpath); - if ($this->dir($targetHash)) { - $base = $this->decode($targetHash); - } else { - return false; - } - } - } - return $targetHash; - } - - /** - * Return this uploadMaxSize value - * - * @return integer - * @author Naoki Sawada - */ - public function getUploadMaxSize() { - return $this->uploadMaxSize; - } - - public function setUploadOverwrite($var) { - $this->uploadOverwrite = (bool)$var; - } - - /** - * Image file utility - * - * @param string $mode 'resize', 'rotate', 'propresize', 'crop', 'fitsquare' - * @param string $src Image file local path - * @param array $options excute options - * @return bool - * @throws ImagickException - * @throws elFinderAbortException - * @author Naoki Sawada - */ - public function imageUtil($mode, $src, $options = array()) { - if (! isset($options['jpgQuality'])) { - $options['jpgQuality'] = intval($this->options['jpgQuality']); - } - if (! isset($options['bgcolor'])) { - $options['bgcolor'] = '#ffffff'; - } - if (! isset($options['bgColorFb'])) { - $options['bgColorFb'] = $this->options['bgColorFb']; - } - - // check 'width' ,'height' - if (in_array($mode, array('resize', 'propresize', 'crop', 'fitsquare'))) { - if (empty($options['width']) || empty($options['height'])) { - return false; - } - } - - if (! empty($options['checkAnimated'])) { - if ($this->imgLib !== 'imagick' && $this->imgLib !== 'convert') { - if (elFinder::isAnimationGif($src)) { - return false; - } - } - if (elFinder::isAnimationPng($src)) { - return false; - } - } - - switch($mode) { - case 'rotate': - if (empty($options['degree'])) { - return true; - } - return (bool)$this->imgRotate($src, $options['degree'], $options['bgColorFb'], null, $options['jpgQuality']); - - case 'resize': - return (bool)$this->imgResize($src, $options['width'], $options['height'], false, true, null, $options['jpgQuality'], $options); - - case 'propresize': - return (bool)$this->imgResize($src, $options['width'], $options['height'], true, true, null, $options['jpgQuality'], $options); - - case 'crop': - if (isset($options['x']) && isset($options['y'])) { - return (bool)$this->imgCrop($src, $options['width'], $options['height'], $options['x'], $options['y'], null, $options['jpgQuality']); - } - break; - - case 'fitsquare': - return (bool)$this->imgSquareFit($src, $options['width'], $options['height'], 'center', 'middle', $options['bgcolor'], null, $options['jpgQuality']); - - } - return false; - } - - /** - * Convert Video To Image by ffmpeg - * - * @param string $file video source file path - * @param array $stat file stat array - * @param object $self volume driver object - * @param int $ss start seconds - * @return bool - * @throws elFinderAbortException - * @author Naoki Sawada - */ - public function ffmpegToImg($file, $stat, $self, $ss = null) { - $name = basename($file); - $path = dirname($file); - $tmp = $path . DIRECTORY_SEPARATOR . md5($name); - // register auto delete on shutdown - $GLOBALS['elFinderTempFiles'][$tmp] = true; - if (rename($file, $tmp)) { - if ($ss === null) { - // specific start time by file name (xxx^[sec].[extention] - video^3.mp4) - if (preg_match('/\^(\d+(?:\.\d+)?)\.[^.]+$/', $stat['name'], $_m)) { - $ss = $_m[1]; - } else { - $ss = $this->options['tmbVideoConvSec']; - } - } - $cmd = sprintf(ELFINDER_FFMPEG_PATH . ' -i %s -ss 00:00:%.3f -vframes 1 -f image2 %s', escapeshellarg($tmp), $ss, escapeshellarg($file)); - $r = ($this->procExec($cmd) === 0); - clearstatcache(); - if ($r && $ss > 0 && ! file_exists($file)) { - // Retry by half of $ss - $ss = max(intval($ss / 2), 0); - rename($tmp, $file); - $r = $this->ffmpegToImg($file, $stat, $self, $ss); - } else { - unlink($tmp); - } - return $r; - } - return false; - } - - /** - * Creates a temporary file and return file pointer - * - * @return resource|boolean - */ - public function tmpfile() { - if ($tmp = $this->getTempFile()) { - return fopen($tmp, 'wb'); - } - return false; - } - - /** - * Save error message - * - * @param array error - * @return boolean false - * @author Naoki Sawada - **/ - protected function setError() { - $this->error = array(); - $this->addError(func_get_args()); - return false; - } - - /** - * Add error message - * - * @param array error - * @return false - * @author Dmitry(dio) Levashov - **/ - protected function addError() { - foreach (func_get_args() as $err) { - if (is_array($err)) { - $this->error = array_merge($this->error, $err); - } else { - $this->error[] = $err; - } - } - return false; - } - - /*********************************************************************/ - /* FS API */ - /*********************************************************************/ - - /***************** server encoding support *******************/ - - /** - * Return parent directory path (with convert encoding) - * - * @param string $path file path - * @return string - * @author Naoki Sawada - **/ - protected function dirnameCE($path) { - $dirname = (!$this->encoding)? $this->_dirname($path) : $this->convEncOut($this->_dirname($this->convEncIn($path))); - // check to infinite loop prevention - return ($dirname != $path)? $dirname : ''; - } - - /** - * Return file name (with convert encoding) - * - * @param string $path file path - * @return string - * @author Naoki Sawada - **/ - protected function basenameCE($path) { - return (!$this->encoding)? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path))); - } - - /** - * Join dir name and file name and return full path. (with convert encoding) - * Some drivers (db) use int as path - so we give to concat path to driver itself - * - * @param string $dir dir path - * @param string $name file name - * @return string - * @author Naoki Sawada - **/ - protected function joinPathCE($dir, $name) { - return (!$this->encoding)? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name))); - } - - /** - * Return normalized path (with convert encoding) - * - * @param string $path file path - * @return string - * @author Naoki Sawada - **/ - protected function normpathCE($path) { - return (!$this->encoding)? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path))); - } - - /** - * Return file path related to root dir (with convert encoding) - * - * @param string $path file path - * @return string - * @author Naoki Sawada - **/ - protected function relpathCE($path) { - return (!$this->encoding)? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path))); - } - - /** - * Convert path related to root dir into real path (with convert encoding) - * - * @param string $path rel file path - * @return string - * @author Naoki Sawada - **/ - protected function abspathCE($path) { - return (!$this->encoding)? $this->_abspath($path): $this->convEncOut($this->_abspath($this->convEncIn($path))); - } - - /** - * Return true if $path is children of $parent (with convert encoding) - * - * @param string $path path to check - * @param string $parent parent path - * @return bool - * @author Naoki Sawada - **/ - protected function inpathCE($path, $parent) { - return (!$this->encoding)? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent))); - } - - /** - * Open file and return file pointer (with convert encoding) - * - * @param string $path file path - * @param string $mode - * @return false|resource - * @internal param bool $write open file for writing - * @author Naoki Sawada - */ - protected function fopenCE($path, $mode='rb') { - return (!$this->encoding)? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode)); - } - - /** - * Close opened file (with convert encoding) - * - * @param resource $fp file pointer - * @param string $path file path - * @return bool - * @author Naoki Sawada - **/ - protected function fcloseCE($fp, $path='') { - return (!$this->encoding)? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path))); - } - - /** - * Create new file and write into it from file pointer. (with convert encoding) - * Return new file path or false on error. - * - * @param resource $fp file pointer - * @param string $dir target dir path - * @param string $name file name - * @param array $stat file stat (required by some virtual fs) - * @return bool|string - * @author Naoki Sawada - **/ - protected function saveCE($fp, $dir, $name, $stat) { - $res = (!$this->encoding)? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat))); - if ($res !== false) { - $this->clearstatcache(); - } - return $res; - } - - /** - * Return true if path is dir and has at least one childs directory (with convert encoding) - * - * @param string $path dir path - * @return bool - * @author Naoki Sawada - **/ - protected function subdirsCE($path) { - if ($this->sessionCaching['subdirs']) { - if (isset($this->sessionCache['subdirs'][$path]) && ! $this->isMyReload()) { - return $this->sessionCache['subdirs'][$path]; - } - } - $hasdir = (bool) ((!$this->encoding)? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)))); - $this->updateSubdirsCache($path, $hasdir); - return $hasdir; - } - - /** - * Return files list in directory (with convert encoding) - * - * @param string $path dir path - * @return array - * @author Naoki Sawada - **/ - protected function scandirCE($path) { - return (!$this->encoding)? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path))); - } - - /** - * Create symlink (with convert encoding) - * - * @param string $source file to link to - * @param string $targetDir folder to create link in - * @param string $name symlink name - * @return bool - * @author Naoki Sawada - **/ - protected function symlinkCE($source, $targetDir, $name) { - return (!$this->encoding)? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name))); - } - - /***************** paths *******************/ - - /** - * Encode path into hash - * - * @param string file path - * @return string - * @author Dmitry (dio) Levashov - * @author Troex Nevelin - **/ - protected function encode($path) { - if ($path !== '') { - - // cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root - $p = $this->relpathCE($path); - // if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt - if ($p === '') { - $p = $this->separator; - } - // change separator - if ($this->separatorForHash) { - $p = str_replace($this->separator, $this->separatorForHash, $p); - } - // TODO crypt path and return hash - $hash = $this->crypt($p); - // hash is used as id in HTML that means it must contain vaild chars - // make base64 html safe and append prefix in begining - $hash = strtr(base64_encode($hash), '+/=', '-_.'); - // remove dots '.' at the end, before it was '=' in base64 - $hash = rtrim($hash, '.'); - // append volume id to make hash unique - return $this->id.$hash; - } - //TODO: Add return statement here - } - - /** - * Decode path from hash - * - * @param string file hash - * @return string - * @author Dmitry (dio) Levashov - * @author Troex Nevelin - **/ - protected function decode($hash) { - if (strpos($hash, $this->id) === 0) { - // cut volume id after it was prepended in encode - $h = substr($hash, strlen($this->id)); - // replace HTML safe base64 to normal - $h = base64_decode(strtr($h, '-_.', '+/=')); - // TODO uncrypt hash and return path - $path = $this->uncrypt($h); - // change separator - if ($this->separatorForHash) { - $path = str_replace($this->separatorForHash, $this->separator, $path); - } - // append ROOT to path after it was cut in encode - return $this->abspathCE($path);//$this->root.($path === $this->separator ? '' : $this->separator.$path); - } - return ''; - } - - /** - * Return crypted path - * Not implemented - * - * @param string path - * @return mixed - * @author Dmitry (dio) Levashov - **/ - protected function crypt($path) { - return $path; - } - - /** - * Return uncrypted path - * Not implemented - * - * @param mixed hash - * @return mixed - * @author Dmitry (dio) Levashov - **/ - protected function uncrypt($hash) { - return $hash; - } - - /** - * Validate file name based on $this->options['acceptedName'] regexp or function - * - * @param string $name file name - * @param bool $isDir - * @return bool - * @author Dmitry (dio) Levashov - */ - protected function nameAccepted($name, $isDir = false) { - if (json_encode($name)===false) { - return false; - } - $nameValidator = $isDir? $this->dirnameValidator : $this->nameValidator; - if ($nameValidator) { - if (is_callable($nameValidator)) { - $res = call_user_func($nameValidator, $name); - return $res; - } - if (preg_match($nameValidator, '') !== false) { - return preg_match($nameValidator, $name); - } - } - return true; - } - - /** - * Return session rootstat cache key - * - * @return string - */ - protected function getRootstatCachekey() { - return md5($this->root.(isset($this->options['alias'])? $this->options['alias'] : '')); - } - - /** - * Return new unique name based on file name and suffix - * - * @param $dir - * @param $name - * @param string $suffix suffix append to name - * @param bool $checkNum - * @param int $start - * @return string - * @internal param string $path file path - * @author Dmitry (dio) Levashov - */ - public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) { - static $lasts = null; - - if ($lasts === null) { - $lasts = array(); - } - - $ext = ''; - - $splits = elFinder::splitFileExtention($name); - if ($splits[1]) { - $ext = '.'.$splits[1]; - $name = $splits[0]; - } - - if ($checkNum && preg_match('/('.preg_quote($suffix, '/').')(\d*)$/i', $name, $m)) { - $i = (int)$m[2]; - $name = substr($name, 0, strlen($name)-strlen($m[2])); - } else { - $i = $start; - $name .= $suffix; - } - $max = $i+100000; - - if (isset($lasts[$name])) { - $i = max($i, $lasts[$name]); - } - - while ($i <= $max) { - $n = $name.($i > 0 ? sprintf($this->options['uniqueNumFormat'], $i) : '').$ext; - - if (!$this->isNameExists($this->joinPathCE($dir, $n))) { - $this->clearcache(); - $lasts[$name] = ++$i; - return $n; - } - $i++; - } - return $name.md5($dir).$ext; - } - - /** - * Converts character encoding from UTF-8 to server's one - * - * @param mixed $var target string or array var - * @param bool $restoreLocale do retore global locale, default is false - * @param string $unknown replaces character for unknown - * @return mixed - * @author Naoki Sawada - */ - public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') { - return (!$this->encoding)? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown); - } - - /** - * Converts character encoding from server's one to UTF-8 - * - * @param mixed $var target string or array var - * @param bool $restoreLocale do retore global locale, default is true - * @param string $unknown replaces character for unknown - * @return mixed - * @author Naoki Sawada - */ - public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') { - return (!$this->encoding)? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown); - } - - /** - * Converts character encoding (base function) - * - * @param mixed $var target string or array var - * @param string $from from character encoding - * @param string $to to character encoding - * @param string $locale local locale - * @param $restoreLocale - * @param string $unknown replaces character for unknown - * @return mixed - */ - protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') { - if (strtoupper($from) !== strtoupper($to)) { - if ($locale) { - setlocale(LC_ALL, $locale); - } - if (is_array($var)) { - $_ret = array(); - foreach($var as $_k => $_v) { - $_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_'); - } - $var = $_ret; - } else { - $_var = false; - if (is_string($var)) { - $_var = $var; - if (false !== ($_var = iconv($from, $to.'//TRANSLIT', $_var))) { - $_var = str_replace('?', $unknown, $_var); - } - } - if ($_var !== false) { - $var = $_var; - } - } - if ($restoreLocale) { - setlocale(LC_ALL, elFinder::$locale); - } - } - return $var; - } - - /** - * Normalize MIME-Type by options['mimeMap'] - * - * @param string $type MIME-Type - * @param string $name Filename - * @param string $ext File extention without first dot (optional) - * - * @return string Normalized MIME-Type - */ - public function mimeTypeNormalize($type, $name, $ext = '') { - if ($ext === '') { - $ext = (false === $pos = strrpos($name, '.'))? '' : substr($name, $pos + 1); - } - $_checkKey = strtolower($ext . ':' . $type); - if ($type === '') { - $_keylen = strlen($_checkKey); - foreach($this->options['mimeMap'] as $_key => $_type) { - if (substr($_key, 0, $_keylen) === $_checkKey) { - $type = $_type; - break; - } - } - } else if (isset($this->options['mimeMap'][$_checkKey])) { - $type = $this->options['mimeMap'][$_checkKey]; - } else { - $_checkKey = strtolower($ext.':*'); - if (isset($this->options['mimeMap'][$_checkKey])) { - $type = $this->options['mimeMap'][$_checkKey]; - } else { - $_checkKey = strtolower('*:'.$type); - if (isset($this->options['mimeMap'][$_checkKey])) { - $type = $this->options['mimeMap'][$_checkKey]; - } - } - } - return $type; - } - - /*********************** util mainly for inheritance class *********************/ - - /** - * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called. - * When needing the unique file to a path, give $path to parameter. - * - * @param string $path for get unique file to a path - * @return string|false - * @author Naoki Sawada - */ - protected function getTempFile($path = '') { - static $cache = array(); - - $key = ''; - if ($path !== '') { - $key = $this->id . '#' . $path; - if (isset($cache[$key])) { - return $cache[$key]; - } - } - - if ($tmpdir = $this->getTempPath()) { - $name = tempnam($tmpdir, 'ELF'); - if ($key) { - $cache[$key] = $name; - } - // register auto delete on shutdown - $GLOBALS['elFinderTempFiles'][$name] = true; - return $name; - } - - return false; - } - - /** - * File path of local server side work file path - * - * @param string $path path need convert encoding to server encoding - * @return string - * @author Naoki Sawada - */ - protected function getWorkFile($path) { - if ($wfp = $this->tmpfile()) { - if ($fp = $this->_fopen($path)) { - while(!feof($fp)) { - fwrite($wfp, fread($fp, 8192)); - } - $info = stream_get_meta_data($wfp); - fclose($wfp); - if ($info && ! empty($info['uri'])) { - return $info['uri']; - } - } - } - return false; - } - - /** - * Get image size array with `dimensions` - * - * @param string $path path need convert encoding to server encoding - * @param string $mime file mime type - * @return array|false - * @throws ImagickException - * @throws elFinderAbortException - */ - public function getImageSize($path, $mime = '') { - $size = false; - if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') { - if ($work = $this->getWorkFile($path)) { - if ($size = getimagesize($work)) { - $size['dimensions'] = $size[0].'x'.$size[1]; - $srcfp = fopen($work, 'rb'); - $cArgs = elFinder::$currentArgs; - if (!empty($cArgs['target']) && $subImgLink = $this->getSubstituteImgLink($cArgs['target'], $size, $srcfp)) { - $size['url'] = $subImgLink; - } - } - } - is_file($work) && unlink($work); - } - return $size; - } - - /** - * Delete dirctory trees - * - * @param string $localpath path need convert encoding to server encoding - * @return boolean - * @throws elFinderAbortException - * @author Naoki Sawada - */ - protected function delTree($localpath) { - foreach ($this->_scandir($localpath) as $p) { - elFinder::checkAborted(); - $stat = $this->stat($this->convEncOut($p)); - $this->convEncIn(); - ($stat['mime'] === 'directory')? $this->delTree($p) : $this->_unlink($p); - } - $res = $this->_rmdir($localpath); - $res && $this->clearstatcache(); - return $res; - } - - /** - * Copy items to a new temporary directory on the local server - * - * @param array $hashes target hashes - * @param string $dir destination directory (for recurcive) - * @param string $canLink it can use link() (for recurcive) - * @return string|false saved path name - * @throws elFinderAbortException - * @author Naoki Sawada - */ - protected function getItemsInHand($hashes, $dir = null, $canLink = null) { - static $totalSize = 0; - if (is_null($dir)) { - $totalSize = 0; - if (! $tmpDir = $this->getTempPath()) { - return false; - } - $dir = tempnam($tmpDir, 'elf'); - if (!unlink($dir) || !mkdir($dir, 0700, true)) { - return false; - } - register_shutdown_function(array($this, 'rmdirRecursive'), $dir); - } - if (is_null($canLink)) { - $canLink = ($this instanceof elFinderVolumeLocalFileSystem); - } - elFinder::checkAborted(); - $res = true; - $files = array(); - foreach ($hashes as $hash) { - if (($file = $this->file($hash)) == false) { - continue; - } - if (!$file['read']) { - continue; - } - - $name = $file['name']; - // for call from search results - if (isset($files[$name])) { - $name = preg_replace('/^(.*?)(\..*)?$/', '$1_'.$files[$name]++.'$2', $name); - } else { - $files[$name] = 1; - } - $target = $dir.DIRECTORY_SEPARATOR.$name; - - if ($file['mime'] === 'directory') { - $chashes = array(); - $_files = $this->scandir($hash); - foreach($_files as $_file) { - if ($file['read']) { - $chashes[] = $_file['hash']; - } - } - if (($res = mkdir($target, 0700, true)) && $chashes) { - $res = $this->getItemsInHand($chashes, $target, $canLink); - } - if (!$res) { - break; - } - !empty($file['ts']) && touch($target, $file['ts']); - } else { - $path = $this->decode($hash); - if (! $canLink || ! ($canLink = link($path, $target))) { - if ($fp = $this->fopenCE($path)) { - if ($tfp = fopen($target, 'wb')) { - $totalSize += stream_copy_to_stream($fp, $tfp); - fclose($tfp); - } - !empty($file['ts']) && touch($target, $file['ts']); - $this->fcloseCE($fp, $path); - } - } else { - $totalSize += filesize($path); - } - if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $totalSize) { - $res = $this->setError(elFinder::ERROR_ARC_MAXSIZE); - } - } - } - return $res? $dir : false; - } - - /*********************** file stat *********************/ - - /** - * Check file attribute - * - * @param string $path file path - * @param string $name attribute name (read|write|locked|hidden) - * @param bool $val attribute value returned by file system - * @param bool $isDir path is directory (true: directory, false: file) - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function attr($path, $name, $val=null, $isDir=null) { - if (!isset($this->defaults[$name])) { - return false; - } - - $relpath = $this->relpathCE($path); - if ($this->separator !== '/') { - $relpath = str_replace($this->separator, '/', $relpath); - } - $relpath = '/' . $relpath; - - $perm = null; - - if ($this->access) { - $perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir, $relpath); - if ($perm !== null) { - return !!$perm; - } - } - - foreach($this->attributes as $attrs) { - if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $relpath)) { - $perm = $attrs[$name]; - break; - } - } - - return $perm === null ? (is_null($val)? $this->defaults[$name] : $val) : !!$perm; - } - - /** - * Return true if file with given name can be created in given folder. - * - * @param string $dir parent dir path - * @param string $name new file name - * @param null $isDir - * @return bool - * @author Dmitry (dio) Levashov - */ - protected function allowCreate($dir, $name, $isDir = null) { - return $this->attr($this->joinPathCE($dir, $name), 'write', true, $isDir); - } - - /** - * Return true if file MIME type can save with check uploadOrder config. - * - * @param string $mime - * @return boolean - */ - protected function allowPutMime($mime) { - // logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order - $allow = $this->mimeAccepted($mime, $this->uploadAllow, null); - $deny = $this->mimeAccepted($mime, $this->uploadDeny, null); - if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny' - $res = false; // default is deny - if (!$deny && ($allow === true)) { // match only allow - $res = true; - }// else (both match | no match | match only deny) { deny } - } else { // array('deny', 'allow'), default is to 'allow' - this is the default rule - $res = true; // default is allow - if (($deny === true) && !$allow) { // match only deny - $res = false; - } // else (both match | no match | match only allow) { allow } - } - return $res; - } - - /** - * Return fileinfo - * - * @param string $path file cache - * @return array|bool - * @author Dmitry (dio) Levashov - **/ - protected function stat($path) { - if ($path === false || is_null($path)) { - return false; - } - $is_root = ($path == $this->root); - if ($is_root) { - $rootKey = $this->getRootstatCachekey(); - if ($this->sessionCaching['rootstat'] && !isset($this->sessionCache['rootstat'])) { - $this->sessionCache['rootstat'] = array(); - } - if (! isset($this->cache[$path]) && ! $this->isMyReload()) { - // need $path as key for netmount/netunmount - if ($this->sessionCaching['rootstat'] && isset($this->sessionCache['rootstat'][$rootKey])) { - if ($ret = $this->sessionCache['rootstat'][$rootKey]) { - if ($this->options['rootRev'] === $ret['rootRev']) { - if (isset($this->options['phash'])) { - $ret['isroot'] = 1; - $ret['phash'] = $this->options['phash']; - } - return $ret; - } - } - } - } - } - $ret = isset($this->cache[$path]) - ? $this->cache[$path] - : $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path)))); - if ($is_root && $this->sessionCaching['rootstat']) { - if ($ret) { - $this->rootModified = false; - if (!empty($rootKey)) { - $this->sessionCache['rootstat'][$rootKey] = $ret; - } - if (isset($this->options['phash'])) { - $ret['isroot'] = 1; - $ret['phash'] = $this->options['phash']; - } - } else if (!empty($rootKey)) { - unset($this->sessionCache['rootstat'][$rootKey]); - } - } - return $ret; - } - - /** - * Get root stat extra key values - * - * @return array stat extras - * @author Naoki Sawada - */ - protected function getRootStatExtra() { - $stat = array(); - if ($this->rootName) { - $stat['name'] = $this->rootName; - } - $stat['rootRev'] = $this->options['rootRev']; - $stat['options'] = $this->options(null); - return $stat; - } - - /** - * Return fileinfo based on filename - * For item ID based path file system - * Please override if needed on each drivers - * - * @param string $path file cache - * @return array - */ - protected function isNameExists($path) { - return $this->stat($path); - } - - /** - * Put file stat in cache and return it - * - * @param string $path file path - * @param array $stat file stat - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function updateCache($path, $stat) { - if (empty($stat) || !is_array($stat)) { - return $this->cache[$path] = array(); - } - - $stat['hash'] = $this->encode($path); - - $root = $path == $this->root; - $parent = ''; - - if ($root) { - $stat = array_merge($stat, $this->getRootStatExtra()); - } else { - if (!isset($stat['name']) || $stat['name'] === '') { - $stat['name'] = $this->basenameCE($path); - } - if (empty($stat['phash'])) { - $parent = $this->dirnameCE($path); - $stat['phash'] = $this->encode($parent); - } else { - $parent = $this->decode($stat['phash']); - } - } - - // name check - if (!$jeName = json_encode($stat['name'])) { - return $this->cache[$path] = array(); - } - // fix name if required - if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) { - $stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName)); - } - - if (!isset($stat['size'])) { - $stat['size'] = 'unknown'; - } - - $mime = isset($stat['mime'])? $stat['mime'] : ''; - if ($isDir = ($mime === 'directory')) { - $stat['volumeid'] = $this->id; - } else { - if (empty($stat['mime']) || $stat['size'] == 0) { - $stat['mime'] = $this->mimetype($stat['name'], true, $stat['size'], $mime); - } else { - $stat['mime'] = $this->mimeTypeNormalize($stat['mime'], $stat['name']); - } - } - - $stat['read'] = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir)); - $stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir)); - if ($root) { - $stat['locked'] = 1; - if ($this->options['type'] !== '') { - $stat['type'] = $this->options['type']; - } - } else { - // lock when parent directory is not writable - if (!isset($stat['locked'])) { - $pstat = $this->stat($parent); - if (isset($pstat['write']) && !$pstat['write']) { - $stat['locked'] = true; - } - } - if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) { - $stat['locked'] = 1; - } else { - unset($stat['locked']); - } - } - - if ($root) { - unset($stat['hidden']); - } elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) - || !$this->mimeAccepted($stat['mime'])) { - $stat['hidden'] = 1; - } else { - unset($stat['hidden']); - } - - if ($stat['read'] && empty($stat['hidden'])) { - - if ($isDir) { - // caching parent's subdirs - if ($parent) { - $this->updateSubdirsCache($parent, true); - } - // for dir - check for subdirs - if ($this->options['checkSubfolders']) { - if (! isset($stat['dirs']) && intval($this->options['checkSubfolders']) === -1) { - $stat['dirs'] = -1; - } - if (isset($stat['dirs'])) { - if ($stat['dirs']) { - if ($stat['dirs'] == -1) { - $stat['dirs'] = ($this->sessionCaching['subdirs'] && isset($this->sessionCache['subdirs'][$path]))? (int)$this->sessionCache['subdirs'][$path] : -1; - } else { - $stat['dirs'] = 1; - } - } else { - unset($stat['dirs']); - } - } elseif (!empty($stat['alias']) && !empty($stat['target'])) { - $stat['dirs'] = isset($this->cache[$stat['target']]) - ? intval(isset($this->cache[$stat['target']]['dirs'])) - : $this->subdirsCE($stat['target']); - - } elseif ($this->subdirsCE($path)) { - $stat['dirs'] = 1; - } - } else { - $stat['dirs'] = 1; - } - if ($this->options['dirUrlOwn'] === true) { - // Set `null` to use the client option `commandsOptions.info.nullUrlDirLinkSelf = true` - $stat['url'] = null; - } else if ($this->options['dirUrlOwn'] === 'hide') { - // to hide link in info dialog of the elFinder client - $stat['url'] = ''; - } - } else { - // for files - check for thumbnails - $p = isset($stat['target']) ? $stat['target'] : $path; - if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) { - $tmb = $this->gettmb($p, $stat); - $stat['tmb'] = $tmb ? $tmb : 1; - } - - } - if (!isset($stat['url']) && $this->URL && $this->encoding) { - $_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1)); - $stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN')? $_path : $this->convEncIn($_path, true))); - } - } else { - if ($isDir) { - unset($stat['dirs']); - } - } - - if (!empty($stat['alias']) && !empty($stat['target'])) { - $stat['thash'] = $this->encode($stat['target']); - //$this->cache[$stat['target']] = $stat; - unset($stat['target']); - } - - $this->cache[$path] = $stat; - - if ($root && $this->sessionCaching['rootstat']) { - // to update session cache - $this->stat($path); - } - - return $stat; - } - - /** - * Get stat for folder content and put in cache - * - * @param string $path - * @return void - * @author Dmitry (dio) Levashov - **/ - protected function cacheDir($path) { - $this->dirsCache[$path] = array(); - $hasDir = false; - - foreach ($this->scandirCE($path) as $p) { - if (($stat = $this->stat($p)) && empty($stat['hidden'])) { - if (! $hasDir && $stat['mime'] === 'directory') { - $hasDir = true; - } - $this->dirsCache[$path][] = $p; - } - } - - $this->updateSubdirsCache($path, $hasDir); - } - - /** - * Clean cache - * - * @return void - * @author Dmitry (dio) Levashov - **/ - protected function clearcache() { - $this->cache = $this->dirsCache = array(); - } - - /** - * Return file mimetype - * - * @param string $path file path - * @param string|bool $name - * @param integer $size - * @param string $mime was notified from the volume driver - * @return string - * @author Dmitry (dio) Levashov - */ - protected function mimetype($path, $name = '', $size = null, $mime = null) { - $type = ''; - $nameCheck = false; - - if ($name === '') { - $name = $path; - } else if ($name === true) { - $name = $path; - $nameCheck = true; - } - $ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1); - if ($size === null) { - $size = file_exists($path)? filesize($path) : -1; - } - if (! $nameCheck && is_readable($path) && $size > 0) { - // detecting by contents - if ($this->mimeDetect === 'finfo') { - $type = finfo_file($this->finfo, $path); - } else if ($this->mimeDetect === 'mime_content_type') { - $type = mime_content_type($path); - } - if ($type) { - $type = explode(';', $type); - $type = trim($type[0]); - if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip|xml)$~', $type)) { - // load default MIME table file "mime.types" - if (!elFinderVolumeDriver::$mimetypesLoaded) { - elFinderVolumeDriver::loadMimeTypes(); - } - if (isset(elFinderVolumeDriver::$mimetypes[$ext])) { - $type = elFinderVolumeDriver::$mimetypes[$ext]; - } - } else if ($ext === 'js' && preg_match('~^text/~', $type)) { - $type = 'text/javascript'; - } - } - } - if (! $type) { - // detecting by filename - $type = elFinderVolumeDriver::mimetypeInternalDetect($name); - if ($type === 'unknown') { - if ($mime) { - $type = $mime; - } else { - $type = ($size == 0)? 'text/plain' : $this->options['mimeTypeUnknown']; - } - } - } - - // mime type normalization - $type = $this->mimeTypeNormalize($type, $name, $ext); - - return $type; - } - - /** - * Load file of mime.types - * - * @param string $mimeTypesFile The mime types file - */ - static protected function loadMimeTypes($mimeTypesFile = '') { - if (! elFinderVolumeDriver::$mimetypesLoaded) { - elFinderVolumeDriver::$mimetypesLoaded = true; - $file = false; - if (!empty($mimeTypesFile) && file_exists($mimeTypesFile)) { - $file = $mimeTypesFile; - } elseif (elFinder::$defaultMimefile && file_exists(elFinder::$defaultMimefile)) { - $file = elFinder::$defaultMimefile; - } elseif (file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types')) { - $file = dirname(__FILE__).DIRECTORY_SEPARATOR.'mime.types'; - } elseif (file_exists(dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types')) { - $file = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'mime.types'; - } - - if ($file && file_exists($file)) { - $mimecf = file($file); - - foreach ($mimecf as $line_num => $line) { - if (!preg_match('/^\s*#/', $line)) { - $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY); - for ($i = 1, $size = count($mime); $i < $size ; $i++) { - if (!isset(self::$mimetypes[$mime[$i]])) { - self::$mimetypes[$mime[$i]] = $mime[0]; - } - } - } - } - } - } - } - - /** - * Detect file mimetype using "internal" method or Loading mime.types with $path = '' - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - static protected function mimetypeInternalDetect($path = '') { - // load default MIME table file "mime.types" - if (!elFinderVolumeDriver::$mimetypesLoaded) { - elFinderVolumeDriver::loadMimeTypes(); - } - $ext = ''; - if ($path) { - $pinfo = pathinfo($path); - $ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : ''; - } - return ($ext && isset(elFinderVolumeDriver::$mimetypes[$ext])) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown'; - } - - /** - * Return file/total directory size infomation - * - * @param string $path file path - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function countSize($path) { - - elFinder::checkAborted(); - - $result = array('size' => 0, 'files' => 0, 'dirs' => 0); - $stat = $this->stat($path); - - if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) { - $result['size'] = 'unknown'; - return $result; - } - - if ($stat['mime'] !== 'directory') { - $result['size'] = intval($stat['size']); - $result['files'] = 1; - return $result; - } - - $result['dirs'] = 1; - $subdirs = $this->options['checkSubfolders']; - $this->options['checkSubfolders'] = true; - foreach ($this->getScandir($path) as $stat) { - if ($isDir = ($stat['mime'] === 'directory' && $stat['read'])) { - ++$result['dirs']; - } else { - ++$result['files']; - } - $res = $isDir - ? $this->countSize($this->decode($stat['hash'])) - : (isset($stat['size']) ? array('size' => intval($stat['size'])) : array()); - if (! empty($res['size']) && is_numeric($res['size'])) { - $result['size'] += $res['size']; - } - if (! empty($res['files']) && is_numeric($res['files'])) { - $result['files'] += $res['files']; - } - if (! empty($res['dirs']) && is_numeric($res['dirs'])) { - $result['dirs'] += $res['dirs']; - --$result['dirs']; - } - } - $this->options['checkSubfolders'] = $subdirs; - return $result; - } - - /** - * Return true if all mimes is directory or files - * - * @param string $mime1 mimetype - * @param string $mime2 mimetype - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function isSameType($mime1, $mime2) { - return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory'); - } - - /** - * If file has required attr == $val - return file path, - * If dir has child with has required attr == $val - return child path - * - * @param string $path file path - * @param string $attr attribute name - * @param bool $val attribute value - * @return string|false - * @author Dmitry (dio) Levashov - **/ - protected function closestByAttr($path, $attr, $val) { - $stat = $this->stat($path); - - if (empty($stat)) { - return false; - } - - $v = isset($stat[$attr]) ? $stat[$attr] : false; - - if ($v == $val) { - return $path; - } - - return $stat['mime'] == 'directory' - ? $this->childsByAttr($path, $attr, $val) - : false; - } - - /** - * Return first found children with required attr == $val - * - * @param string $path file path - * @param string $attr attribute name - * @param bool $val attribute value - * @return string|false - * @author Dmitry (dio) Levashov - **/ - protected function childsByAttr($path, $attr, $val) { - foreach ($this->scandirCE($path) as $p) { - if (($_p = $this->closestByAttr($p, $attr, $val)) != false) { - return $_p; - } - } - return false; - } - - protected function isMyReload($target = '', $ARGtarget = '') { - if ($this->rootModified || (! empty($this->ARGS['cmd']) && $this->ARGS['cmd'] === 'parents')) { - return true; - } - if (! empty($this->ARGS['reload'])) { - if ($ARGtarget === '') { - $ARGtarget = isset($this->ARGS['target'])? $this->ARGS['target'] - : ((isset($this->ARGS['targets']) && is_array($this->ARGS['targets']) && count($this->ARGS['targets']) === 1)? - $this->ARGS['targets'][0] : ''); - } - if ($ARGtarget !== '') { - $ARGtarget = strval($ARGtarget); - if ($target === '') { - return (strpos($ARGtarget, $this->id) === 0); - } else { - $target = strval($target); - return ($target === $ARGtarget); - } - } - } - return false; - } - - /** - * Update subdirs cache data - * - * @param string $path - * @param bool $subdirs - * - * @returnv void - */ - protected function updateSubdirsCache($path, $subdirs) { - if (isset($this->cache[$path])) { - if ($subdirs) { - $this->cache[$path]['dirs'] = 1; - } else { - unset($this->cache[$path]['dirs']); - } - } - if ($this->sessionCaching['subdirs']) { - $this->sessionCache['subdirs'][$path] = $subdirs; - } - if ($this->sessionCaching['rootstat'] && $path == $this->root) { - unset($this->sessionCache['rootstat'][$this->getRootstatCachekey()]); - } - } - - /***************** get content *******************/ - - /** - * Return required dir's files info. - * If onlyMimes is set - return only dirs and files of required mimes - * - * @param string $path dir path - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function getScandir($path) { - $files = array(); - - !isset($this->dirsCache[$path]) && $this->cacheDir($path); - - foreach ($this->dirsCache[$path] as $p) { - if (($stat = $this->stat($p)) && empty($stat['hidden'])) { - $files[] = $stat; - } - } - - return $files; - } - - - /** - * Return subdirs tree - * - * @param string $path parent dir path - * @param int $deep tree deep - * @param string $exclude - * @return array - * @author Dmitry (dio) Levashov - */ - protected function gettree($path, $deep, $exclude='') { - $dirs = array(); - - !isset($this->dirsCache[$path]) && $this->cacheDir($path); - - foreach ($this->dirsCache[$path] as $p) { - $stat = $this->stat($p); - - if ($stat && empty($stat['hidden']) && $p != $exclude && $stat['mime'] == 'directory') { - $dirs[] = $stat; - if ($deep > 0 && !empty($stat['dirs'])) { - $dirs = array_merge($dirs, $this->gettree($p, $deep-1)); - } - } - } - - return $dirs; - } - - /** - * Recursive files search - * - * @param string $path dir path - * @param string $q search string - * @param array $mimes - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function doSearch($path, $q, $mimes) { - $result = array(); - $matchMethod = empty($this->doSearchCurrentQuery['matchMethod'])? 'searchMatchName' : $this->doSearchCurrentQuery['matchMethod']; - $timeout = $this->options['searchTimeout']? $this->searchStart + $this->options['searchTimeout'] : 0; - if ($timeout && $timeout < time()) { - $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); - return $result; - } - - foreach($this->scandirCE($path) as $p) { - elFinder::extendTimeLimit($this->options['searchTimeout'] + 30); - - if ($timeout && ($this->error || $timeout < time())) { - !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); - break; - } - - - $stat = $this->stat($p); - - if (!$stat) { // invalid links - continue; - } - - if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) { - continue; - } - - $name = $stat['name']; - - if ($this->doSearchCurrentQuery['excludes']) { - foreach($this->doSearchCurrentQuery['excludes'] as $exclude) { - if ($this->stripos($name, $exclude) !== false) { - continue 2; - } - } - } - - if ((!$mimes || $stat['mime'] !== 'directory') && $this->$matchMethod($name, $q, $p) !== false) { - $stat['path'] = $this->path($stat['hash']); - if ($this->URL && !isset($stat['url'])) { - $path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1)); - if ($this->encoding) { - $path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true))); - } else { - $path = str_replace('%2F', '/', rawurlencode($path)); - } - $stat['url'] = $this->URL . $path; - } - - $result[] = $stat; - } - if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) { - if (! $this->options['searchExDirReg'] || ! preg_match($this->options['searchExDirReg'], $p)) { - $result = array_merge($result, $this->doSearch($p, $q, $mimes)); - } - } - } - - return $result; - } - - /********************** manuipulations ******************/ - - /** - * Copy file/recursive copy dir only in current volume. - * Return new file path or false. - * - * @param string $src source path - * @param string $dst destination dir path - * @param string $name new file name (optionaly) - * @return string|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function copy($src, $dst, $name) { - - elFinder::checkAborted(); - - $srcStat = $this->stat($src); - - if (!empty($srcStat['thash'])) { - $target = $this->decode($srcStat['thash']); - if (!$this->inpathCE($target, $this->root)) { - return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK); - } - $stat = $this->stat($target); - $this->clearcache(); - return $stat && $this->symlinkCE($target, $dst, $name) - ? $this->joinPathCE($dst, $name) - : $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); - } - - if ($srcStat['mime'] === 'directory') { - $testStat = $this->isNameExists($this->joinPathCE($dst, $name)); - $this->clearcache(); - - if (($testStat && $testStat['mime'] !== 'directory') || (! $testStat && ! $testStat = $this->mkdir($this->encode($dst), $name))) { - return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); - } - - $dst = $this->decode($testStat['hash']); - - foreach ($this->getScandir($src) as $stat) { - if (empty($stat['hidden'])) { - $name = $stat['name']; - $_src = $this->decode($stat['hash']); - if (! $this->copy($_src, $dst, $name)) { - $this->remove($dst, true); // fall back - return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src)); - } - } - } - - $this->added[] = $testStat; - - return $dst; - } - - if ($this->options['copyJoin']) { - $test = $this->joinPathCE($dst, $name); - if ($this->isNameExists($test)) { - $this->remove($test); - } - } - if ($res = $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) { - $path = is_string($res)? $res : $this->joinPathCE($dst, $name); - $this->clearstatcache(); - $this->added[] = $this->stat($path); - return $path; - } - - return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); - } - - /** - * Move file - * Return new file path or false. - * - * @param string $src source path - * @param string $dst destination dir path - * @param string $name new file name - * @return string|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function move($src, $dst, $name) { - $stat = $this->stat($src); - $stat['realpath'] = $src; - $this->rmTmb($stat); // can not do rmTmb() after _move() - $this->clearcache(); - - if ($res = $this->convEncOut($this->_move($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) { - $this->clearstatcache(); - $this->removed[] = $stat; - if ($stat['mime'] === 'directory') { - $this->updateSubdirsCache($dst, true); - } - return is_string($res)? $res : $this->joinPathCE($dst, $name); - } - - return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash'])); - } - - /** - * Copy file from another volume. - * Return new file path or false. - * - * @param Object $volume source volume - * @param string $src source file hash - * @param string $destination destination dir path - * @param string $name file name - * @return string|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function copyFrom($volume, $src, $destination, $name) { - - elFinder::checkAborted(); - - if (($source = $volume->file($src)) == false) { - return $this->addError(elFinder::ERROR_COPY, '#'.$src, $volume->error()); - } - - $srcIsDir = ($source['mime'] === 'directory'); - - $errpath = $volume->path($source['hash']); - - if (!$this->nameAccepted($source['name'], $srcIsDir)) { - return $this->addError(elFinder::ERROR_COPY, $errpath, $srcIsDir? elFinder::ERROR_INVALID_DIRNAME : elFinder::ERROR_INVALID_NAME); - } - - if (!$source['read']) { - return $this->addError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED); - } - - if ($srcIsDir) { - $test = $this->isNameExists($this->joinPathCE($destination, $name)); - $this->clearcache(); - - if (($test && $test['mime'] != 'directory') || (! $test && ! $test = $this->mkdir($this->encode($destination), $name))) { - return $this->addError(elFinder::ERROR_COPY, $errpath); - } - - //$path = $this->joinPathCE($destination, $name); - $path = $this->decode($test['hash']); - - foreach ($volume->scandir($src) as $entr) { - $this->copyFrom($volume, $entr['hash'], $path, $entr['name']); - } - - $this->added[] = $test; - } else { - // MIME check - $mimeByName = $this->mimetype($source['name'], true); - if ($source['mime'] === $mimeByName) { - $mimeByName = ''; - } - if (!$this->allowPutMime($source['mime']) || ($mimeByName && !$this->allowPutMime($mimeByName))) { - return $this->addError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath); - } - - if (strpos($source['mime'], 'image') === 0 && ($dim = $volume->dimensions($src))) { - if (is_array($dim)) { - $dim = isset($dim['dim'])? $dim['dim'] : null; - } - if ($dim) { - $s = explode('x', $dim); - $source['width'] = $s[0]; - $source['height'] = $s[1]; - } - } - - if (($fp = $volume->open($src)) == false - || ($path = $this->saveCE($fp, $destination, $name, $source)) == false) { - $fp && $volume->close($fp, $src); - return $this->addError(elFinder::ERROR_COPY, $errpath); - } - $volume->close($fp, $src); - - $this->added[] = $this->stat($path);; - } - - return $path; - } - - /** - * Remove file/ recursive remove dir - * - * @param string $path file path - * @param bool $force try to remove even if file locked - * @return bool - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function remove($path, $force = false) { - $stat = $this->stat($path); - - if (empty($stat)) { - return $this->setError(elFinder::ERROR_RM, $path, elFinder::ERROR_FILE_NOT_FOUND); - } - - $stat['realpath'] = $path; - $this->rmTmb($stat); - $this->clearcache(); - - if (!$force && !empty($stat['locked'])) { - return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash'])); - } - - if ($stat['mime'] == 'directory' && empty($stat['thash'])) { - $ret = $this->delTree($this->convEncIn($path)); - $this->convEncOut(); - if (!$ret) { - return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash'])); - } - } else { - if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) { - return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash'])); - } - $this->clearstatcache(); - } - - $this->removed[] = $stat; - return true; - } - - - /************************* thumbnails **************************/ - - /** - * Return thumbnail file name for required file - * - * @param array $stat file stat - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function tmbname($stat) { - $name = $stat['hash'] . (isset($stat['ts'])? $stat['ts'] : '') . '.png'; - if (strlen($name) > 255) { - $name = $this->id.md5($stat['hash']).$stat['ts'].'.png'; - } - return $name; - } - - /** - * Return thumnbnail name if exists - * - * @param string $path file path - * @param array $stat file stat - * @return string|false - * @author Dmitry (dio) Levashov - **/ - protected function gettmb($path, $stat) { - if ($this->tmbURL && $this->tmbPath) { - // file itself thumnbnail - if (strpos($path, $this->tmbPath) === 0) { - return basename($path); - } - - $name = $this->tmbname($stat); - if (file_exists($this->tmbPath.DIRECTORY_SEPARATOR.$name)) { - return $name; - } - } - return false; - } - - /** - * Return true if thumnbnail for required file can be created - * - * @param string $path thumnbnail path - * @param array $stat file stat - * @param bool $checkTmbPath - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - protected function canCreateTmb($path, $stat, $checkTmbPath = true) { - if ((! $checkTmbPath || $this->tmbPathWritable) - && (! $this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail - ) { - $mime = strtolower($stat['mime']); - list($type) = explode('/', $mime); - if (! empty($this->imgConverter)) { - if (isset($this->imgConverter[$mime])) { - return true; - } - if (isset($this->imgConverter[$type])) { - return true; - } - } - return $this->imgLib - && ( - ($type === 'image' && ($this->imgLib === 'gd' ? in_array($stat['mime'], array('image/jpeg', 'image/png', 'image/gif', 'image/x-ms-bmp')) : true)) - || - (ELFINDER_IMAGEMAGICK_PS && $this->imgLib !== 'gd' && in_array($stat['mime'], array('application/postscript', 'application/pdf'))) - ); - } - return false; - } - - /** - * Return true if required file can be resized. - * By default - the same as canCreateTmb - * - * @param string $path thumnbnail path - * @param array $stat file stat - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - protected function canResize($path, $stat) { - return $this->canCreateTmb($path, $stat, false); - } - - /** - * Create thumnbnail and return it's URL on success - * - * @param string $path file path - * @param $stat - * @return false|string - * @internal param string $mime file mime type - * @throws elFinderAbortException - * @throws ImagickException - * @author Dmitry (dio) Levashov - */ - protected function createTmb($path, $stat) { - if (!$stat || !$this->canCreateTmb($path, $stat)) { - return false; - } - - $name = $this->tmbname($stat); - $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; - - $maxlength = -1; - $imgConverter = null; - - // check imgConverter - $mime = strtolower($stat['mime']); - list($type) = explode('/', $mime); - if (isset($this->imgConverter[$mime])) { - $imgConverter = $this->imgConverter[$mime]['func']; - if (! empty($this->imgConverter[$mime]['maxlen'])) { - $maxlength = intval($this->imgConverter[$mime]['maxlen']); - } - } else if (isset($this->imgConverter[$type])) { - $imgConverter = $this->imgConverter[$type]['func']; - if (! empty($this->imgConverter[$type]['maxlen'])) { - $maxlength = intval($this->imgConverter[$type]['maxlen']); - } - } - if ($imgConverter && ! is_callable($imgConverter)) { - return false; - } - - // copy image into tmbPath so some drivers does not store files on local fs - if (($src = $this->fopenCE($path, 'rb')) == false) { - return false; - } - - if (($trg = fopen($tmb, 'wb')) == false) { - $this->fcloseCE($src, $path); - return false; - } - - stream_copy_to_stream($src, $trg, $maxlength); - - $this->fcloseCE($src, $path); - fclose($trg); - - // call imgConverter - if ($imgConverter) { - if (! call_user_func_array($imgConverter, array($tmb, $stat, $this))) { - file_exists($tmb) && unlink($tmb); - return false; - } - } - - $result = false; - - $tmbSize = $this->tmbSize; - - if ($this->imgLib === 'imagick') { - try { - $imagickTest = new imagick($tmb.'[0]'); - $imagickTest->clear(); - $imagickTest = true; - } catch (Exception $e) { - $imagickTest = false; - } - } - - if (($this->imgLib === 'imagick' && ! $imagickTest) || ($s = getimagesize($tmb)) === false) { - if ($this->imgLib === 'imagick') { - $bgcolor = $this->options['tmbBgColor']; - if ($bgcolor === 'transparent') { - $bgcolor = 'rgba(255, 255, 255, 0.0)'; - } - try { - $imagick = new imagick(); - $imagick->setBackgroundColor(new ImagickPixel($bgcolor)); - $imagick->readImage($this->getExtentionByMime($stat['mime'], ':') . $tmb . '[0]'); - try { - $imagick->trimImage(0); - } catch (Exception $e) {} - $imagick->setImageFormat('png'); - $imagick->writeImage($tmb); - $imagick->clear(); - if (($s = getimagesize($tmb)) !== false) { - $result = true; - } - } catch (Exception $e) {} - } else if ($this->imgLib === 'convert') { - $convParams = $this->imageMagickConvertPrepare($tmb, 'png', 100, array(), $stat['mime']); - $cmd = sprintf('%s -colorspace sRGB -trim %s %s', ELFINDER_CONVERT_PATH, $convParams['quotedPath'], $convParams['quotedDstPath']); - $result = false; - if ($this->procExec($cmd) === 0) { - if (($s = getimagesize($tmb)) !== false) { - $result = true; - } - } - } - if (! $result) { - // fallback imgLib to GD - if (function_exists('gd_info') && ($s = getimagesize($tmb))) { - $this->imgLib = 'gd'; - } else { - file_exists($tmb) && unlink($tmb); - return false; - } - } - } - - /* If image smaller or equal thumbnail size - just fitting to thumbnail square */ - if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { - $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' ); - } else { - - if ($this->options['tmbCrop']) { - - $result = $tmb; - /* Resize and crop if image bigger than thumbnail */ - if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { - $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); - } - - if ($result && ($s = getimagesize($tmb)) != false) { - $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize)/2) : 0; - $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize)/2) : 0; - $result = $this->imgCrop($result, $tmbSize, $tmbSize, $x, $y, 'png'); - } else { - $result = false; - } - - } else { - $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png'); - } - - if ($result) { - if ($s = getimagesize($tmb)) { - if ($s[0] !== $tmbSize || $s[1] !== $tmbSize) { - $result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' ); - } - } - } - } - - if (!$result) { - unlink($tmb); - return false; - } - - return $name; - } - - /** - * Resize image - * - * @param string $path image file - * @param int $width new width - * @param int $height new height - * @param bool $keepProportions crop image - * @param bool $resizeByBiggerSide resize image based on bigger side if true - * @param string $destformat image destination format - * @param int $jpgQuality JEPG quality (1-100) - * @param array $options Other extra options - * @return string|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin - */ - protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null, $jpgQuality = null, $options = array()) { - if (($s = getimagesize($path)) == false) { - return false; - } - - if (!$jpgQuality) { - $jpgQuality = $this->options['jpgQuality']; - } - - list($orig_w, $orig_h) = array($s[0], $s[1]); - list($size_w, $size_h) = array($width, $height); - - if (empty($options['unenlarge']) || $orig_w > $size_w || $orig_h > $size_h) { - if ($keepProportions == true) { - /* Resizing by biggest side */ - if ($resizeByBiggerSide) { - if ($orig_w > $orig_h) { - $size_h = round($orig_h * $width / $orig_w); - $size_w = $width; - } else { - $size_w = round($orig_w * $height / $orig_h); - $size_h = $height; - } - } else { - if ($orig_w > $orig_h) { - $size_w = round($orig_w * $height / $orig_h); - $size_h = $height; - } else { - $size_h = round($orig_h * $width / $orig_w); - $size_w = $width; - } - } - } - } else { - $size_w = $orig_w; - $size_h = $orig_h; - } - - elFinder::extendTimeLimit(300); - switch ($this->imgLib) { - case 'imagick': - - try { - $img = new imagick($path); - } catch (Exception $e) { - return false; - } - - // Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb - // resize bench: http://app-mgng.rhcloud.com/9 - // resize sample: http://www.dylanbeattie.net/magick/filters/result.html - $filter = ($destformat === 'png' /* createTmb */)? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS; - - $ani = ($img->getNumberImages() > 1); - if ($ani && is_null($destformat)) { - $img = $img->coalesceImages(); - do { - $img->resizeImage($size_w, $size_h, $filter, 1); - } while ($img->nextImage()); - $img->optimizeImageLayers(); - $result = $img->writeImages($path, true); - } else { - if ($ani) { - $img->setFirstIterator(); - } - if (strtoupper($img->getImageFormat()) === 'JPEG') { - $img->setImageCompression(imagick::COMPRESSION_JPEG); - $img->setImageCompressionQuality($jpgQuality); - if (isset($options['preserveExif']) && ! $options['preserveExif']) { - try { - $orientation = $img->getImageOrientation(); - } catch (ImagickException $e) { - $orientation = 0; - } - $img->stripImage(); - if ($orientation) { - $img->setImageOrientation($orientation); - } - } - if ($this->options['jpgProgressive']) { - $img->setInterlaceScheme(Imagick::INTERLACE_PLANE); - } - } - $img->resizeImage($size_w, $size_h, $filter, true); - if ($destformat) { - $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); - } else { - $result = $img->writeImage($path); - } - } - - $img->clear(); - - return $result ? $path : false; - - break; - - case 'convert': - extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); - $filter = ($destformat === 'png' /* createTmb */)? '-filter Box' : '-filter Lanczos'; - $strip = (isset($options['preserveExif']) && ! $options['preserveExif'])? ' -strip' : ''; - $cmd = sprintf('%s %s%s%s%s%s %s -geometry %dx%d! %s %s', ELFINDER_CONVERT_PATH, $quotedPath, $coalesce, $jpgQuality, $strip, $interlace, $filter, $size_w, $size_h, $deconstruct, $quotedDstPath); - - $result = false; - if ($this->procExec($cmd) === 0) { - $result = true; - } - return $result ? $path : false; - - break; - - case 'gd': - elFinder::expandMemoryForGD(array($s, array($size_w, $size_h))); - $img = $this->gdImageCreate($path,$s['mime']); - - if ($img && false != ($tmp = imagecreatetruecolor($size_w, $size_h))) { - - $bgNum = false; - if ($s[2] === IMAGETYPE_GIF && (! $destformat || $destformat === 'gif')) { - $bgIdx = imagecolortransparent($img); - if ($bgIdx !== -1) { - $c = imagecolorsforindex($img, $bgIdx); - $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']); - imagefill($tmp, 0, 0, $bgNum); - imagecolortransparent($tmp, $bgNum); - } - } - if ($bgNum === false) { - $this->gdImageBackground($tmp, 'transparent'); - } - - if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) { - return false; - } - - $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); - - imagedestroy($img); - imagedestroy($tmp); - - return $result ? $path : false; - - } - break; - } - - return false; - } - - /** - * Crop image - * - * @param string $path image file - * @param int $width crop width - * @param int $height crop height - * @param bool $x crop left offset - * @param bool $y crop top offset - * @param string $destformat image destination format - * @param int $jpgQuality JEPG quality (1-100) - * @return string|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin - */ - protected function imgCrop($path, $width, $height, $x, $y, $destformat = null, $jpgQuality = null) { - if (($s = getimagesize($path)) == false) { - return false; - } - - if (!$jpgQuality) { - $jpgQuality = $this->options['jpgQuality']; - } - - elFinder::extendTimeLimit(300); - switch ($this->imgLib) { - case 'imagick': - - try { - $img = new imagick($path); - } catch (Exception $e) { - return false; - } - - $ani = ($img->getNumberImages() > 1); - if ($ani && is_null($destformat)) { - $img = $img->coalesceImages(); - do { - $img->setImagePage($s[0], $s[1], 0, 0); - $img->cropImage($width, $height, $x, $y); - $img->setImagePage($width, $height, 0, 0); - } while ($img->nextImage()); - $img->optimizeImageLayers(); - $result = $img->writeImages($path, true); - } else { - if ($ani) { - $img->setFirstIterator(); - } - $img->setImagePage($s[0], $s[1], 0, 0); - $img->cropImage($width, $height, $x, $y); - $img->setImagePage($width, $height, 0, 0); - $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); - } - - $img->clear(); - - return $result ? $path : false; - - break; - - case 'convert': - extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); - $cmd = sprintf('%s %s%s%s%s -crop %dx%d+%d+%d%s %s', ELFINDER_CONVERT_PATH, $quotedPath, $coalesce, $jpgQuality, $interlace, $width, $height, $x, $y, $deconstruct, $quotedDstPath); - - $result = false; - if ($this->procExec($cmd) === 0) { - $result = true; - } - return $result ? $path : false; - - break; - - case 'gd': - elFinder::expandMemoryForGD(array($s, array($width, $height))); - $img = $this->gdImageCreate($path,$s['mime']); - - if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) { - - $bgNum = false; - if ($s[2] === IMAGETYPE_GIF && (! $destformat || $destformat === 'gif')) { - $bgIdx = imagecolortransparent($img); - if ($bgIdx !== -1) { - $c = imagecolorsforindex($img, $bgIdx); - $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']); - imagefill($tmp, 0, 0, $bgNum); - imagecolortransparent($tmp, $bgNum); - } - } - if ($bgNum === false) { - $this->gdImageBackground($tmp, 'transparent'); - } - - $size_w = $width; - $size_h = $height; - - if ($s[0] < $width || $s[1] < $height) { - $size_w = $s[0]; - $size_h = $s[1]; - } - - if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) { - return false; - } - - $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); - - imagedestroy($img); - imagedestroy($tmp); - - return $result ? $path : false; - - } - break; - } - - return false; - } - - /** - * Put image to square - * - * @param string $path image file - * @param int $width square width - * @param int $height square height - * @param int|string $align reserved - * @param int|string $valign reserved - * @param string $bgcolor square background color in #rrggbb format - * @param string $destformat image destination format - * @param int $jpgQuality JEPG quality (1-100) - * @return false|string - * @throws ImagickException - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin - */ - protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null, $jpgQuality = null) { - if (($s = getimagesize($path)) == false) { - return false; - } - - $result = false; - - /* Coordinates for image over square aligning */ - $y = ceil(abs($height - $s[1]) / 2); - $x = ceil(abs($width - $s[0]) / 2); - - if (!$jpgQuality) { - $jpgQuality = $this->options['jpgQuality']; - } - - elFinder::extendTimeLimit(300); - switch ($this->imgLib) { - case 'imagick': - try { - $img = new imagick($path); - } catch (Exception $e) { - return false; - } - - if ($bgcolor === 'transparent') { - $bgcolor = 'rgba(255, 255, 255, 0.0)'; - } - $ani = ($img->getNumberImages() > 1); - if ($ani && is_null($destformat)) { - $img1 = new Imagick(); - $img1->setFormat('gif'); - $img = $img->coalesceImages(); - do { - $gif = new Imagick(); - $gif->newImage($width, $height, new ImagickPixel($bgcolor)); - $gif->setImageColorspace($img->getImageColorspace()); - $gif->setImageFormat('gif'); - $gif->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y ); - $gif->setImageDelay($img->getImageDelay()); - $gif->setImageIterations($img->getImageIterations()); - $img1->addImage($gif); - $gif->clear(); - } while ($img->nextImage()); - $img1->optimizeImageLayers(); - $result = $img1->writeImages($path, true); - } else { - if ($ani) { - $img->setFirstIterator(); - } - $img1 = new Imagick(); - $img1->newImage($width, $height, new ImagickPixel($bgcolor)); - $img1->setImageColorspace($img->getImageColorspace()); - $img1->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y ); - $result = $this->imagickImage($img1, $path, $destformat, $jpgQuality); - } - - $img1->clear(); - $img->clear(); - return $result ? $path : false; - - break; - - case 'convert': - extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); - if ($bgcolor === 'transparent') { - $bgcolor = 'rgba(255, 255, 255, 0.0)'; - } - $cmd = sprintf('%s -size %dx%d "xc:%s" png:- | convert%s%s%s png:- %s -geometry +%d+%d -compose over -composite%s %s', ELFINDER_CONVERT_PATH, $width, $height, $bgcolor, $coalesce, $jpgQuality, $interlace, $quotedPath, $x, $y, $deconstruct, $quotedDstPath); - - $result = false; - if ($this->procExec($cmd) === 0) { - $result = true; - } - return $result ? $path : false; - - break; - - case 'gd': - elFinder::expandMemoryForGD(array($s, array($width, $height))); - $img = $this->gdImageCreate($path,$s['mime']); - - if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) { - - $this->gdImageBackground($tmp, $bgcolor); - if ($bgcolor === 'transparent' && ($destformat === 'png' || $s[2] === IMAGETYPE_PNG)) { - $bgNum = imagecolorallocatealpha($tmp, 255, 255, 255, 127); - imagefill($tmp, 0, 0, $bgNum); - } - - if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) { - return false; - } - - $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); - - imagedestroy($img); - imagedestroy($tmp); - - return $result ? $path : false; - } - break; - } - - return false; - } - - /** - * Rotate image - * - * @param string $path image file - * @param int $degree rotete degrees - * @param string $bgcolor square background color in #rrggbb format - * @param string $destformat image destination format - * @param int $jpgQuality JEPG quality (1-100) - * @return string|false - * @throws elFinderAbortException - * @author nao-pon - * @author Troex Nevelin - */ - protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null, $jpgQuality = null) { - if (($s = getimagesize($path)) == false || $degree % 360 === 0) { - return false; - } - - $result = false; - - // try lossless rotate - if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) { - $count = ($degree / 90) % 4; - $exiftran = array( - 1 => '-9', - 2 => '-1', - 3 => '-2' - ); - $jpegtran = array( - 1 => '90', - 2 => '180', - 3 => '270' - ); - $quotedPath = escapeshellarg($path); - $cmds = array(); - if ($this->procExec(ELFINDER_EXIFTRAN_PATH . ' -h') === 0) { - $cmds[] = ELFINDER_EXIFTRAN_PATH . ' -i '.$exiftran[$count].' '.$path; - } - if ($this->procExec(ELFINDER_JPEGTRAN_PATH . ' -version') === 0) { - $cmds[] = ELFINDER_JPEGTRAN_PATH . ' -rotate '.$jpegtran[$count].' -copy all -outfile '.$quotedPath.' '.$quotedPath; - } - foreach($cmds as $cmd) { - if ($this->procExec($cmd) === 0) { - $result = true; - break; - } - } - if ($result) { - return $path; - } - } - - if (!$jpgQuality) { - $jpgQuality = $this->options['jpgQuality']; - } - - elFinder::extendTimeLimit(300); - switch ($this->imgLib) { - case 'imagick': - try { - $img = new imagick($path); - } catch (Exception $e) { - return false; - } - - if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) { - $bgcolor = 'rgba(255, 255, 255, 0.0)'; - } - if ($img->getNumberImages() > 1) { - $img = $img->coalesceImages(); - do { - $img->rotateImage(new ImagickPixel($bgcolor), $degree); - } while ($img->nextImage()); - $img->optimizeImageLayers(); - $result = $img->writeImages($path, true); - } else { - $img->rotateImage(new ImagickPixel($bgcolor), $degree); - $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); - } - $img->clear(); - return $result ? $path : false; - - break; - - case 'convert': - extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); - if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) { - $bgcolor = 'rgba(255, 255, 255, 0.0)'; - } - $cmd = sprintf('%s %s%s%s%s -background "%s" -rotate %d%s %s', ELFINDER_CONVERT_PATH, $quotedPath, $coalesce, $jpgQuality, $interlace, $bgcolor, $degree, $deconstruct, $quotedDstPath); - - $result = false; - if ($this->procExec($cmd) === 0) { - $result = true; - } - return $result ? $path : false; - - break; - - case 'gd': - elFinder::expandMemoryForGD(array($s, $s)); - $img = $this->gdImageCreate($path,$s['mime']); - - $degree = 360 - $degree; - - $bgNum = -1; - $bgIdx = false; - if ($s[2] === IMAGETYPE_GIF) { - $bgIdx = imagecolortransparent($img); - if ($bgIdx !== -1) { - $c = imagecolorsforindex($img, $bgIdx); - $w = imagesx($img); - $h = imagesy($img); - $newImg = imagecreatetruecolor($w, $h); - imagepalettecopy($newImg, $img); - $bgNum = imagecolorallocate($newImg, $c['red'], $c['green'], $c['blue']); - imagefill($newImg, 0, 0, $bgNum); - imagecolortransparent($newImg, $bgNum); - imagecopy($newImg, $img, 0, 0, 0, 0, $w, $h); - imagedestroy($img); - $img = $newImg; - $newImg = null; - } - } else if ($s[2] === IMAGETYPE_PNG) { - $bgNum = imagecolorallocatealpha($img, 255, 255, 255, 127); - } - if ($bgNum === -1) { - list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); - $bgNum = imagecolorallocate($img, $r, $g, $b); - } - - $tmp = imageRotate($img, $degree, $bgNum); - if ($bgIdx !== -1) { - imagecolortransparent($tmp, $bgNum); - } - - $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); - - imageDestroy($img); - imageDestroy($tmp); - - return $result ? $path : false; - - break; - } - - return false; - } - - /** - * Execute shell command - * - * @param string $command command line - * @param string $output stdout strings - * @param int $return_var process exit code - * @param string $error_output stderr strings - * @return int exit code - * @throws elFinderAbortException - * @author Alexey Sukhotin - */ - protected function procExec($command , &$output = '', &$return_var = -1, &$error_output = '') { - - static $allowed = null; - - if ($allowed === null) { - if ($allowed = function_exists('proc_open')) { - if ($disabled = ini_get('disable_functions')) { - $funcs = array_map('trim', explode(',', $disabled)); - $allowed = ! in_array('proc_open', $funcs); - } - } - } - - if (! $allowed) { - $return_var = -1; - return $return_var; - } - - if (! $command) { - $return_var = 0; - return $return_var; - } - - $descriptorspec = array( - 0 => array("pipe", "r"), // stdin - 1 => array("pipe", "w"), // stdout - 2 => array("pipe", "w") // stderr - ); - - $process = proc_open($command, $descriptorspec, $pipes, null, null); - - if (is_resource($process)) { - stream_set_blocking($pipes[1], 0); - stream_set_blocking($pipes[2], 0); - - fclose($pipes[0]); - - $tmpout = ''; - $tmperr = ''; - while (feof($pipes[1]) === false || feof($pipes[2]) === false) { - elFinder::extendTimeLimit(); - $read = array($pipes[1], $pipes[2]); - $write = null; - $except = null; - $ret = stream_select($read, $write, $except, 1); - if ($ret === false) { - // error - break; - } else if ($ret === 0) { - // timeout - continue; - } else { - foreach ($read as $sock) { - if ($sock === $pipes[1]) { - $tmpout .= fread($sock, 4096); - } else if ($sock === $pipes[2]) { - $tmperr .= fread($sock, 4096); - } - } - } - } - - fclose($pipes[1]); - fclose($pipes[2]); - - $error_output = $tmpout; - $output = $tmperr; - $return_var = proc_close($process); - - } else { - $return_var = -1; - } - - return $return_var; - - } - - /** - * Remove thumbnail, also remove recursively if stat is directory - * - * @param array $stat file stat - * @return void - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Naoki Sawada - * @author Troex Nevelin - */ - protected function rmTmb($stat) { - if ($this->tmbPathWritable) { - if ($stat['mime'] === 'directory') { - foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) { - elFinder::extendTimeLimit(30); - $name = $this->basenameCE($p); - $name != '.' && $name != '..' && $this->rmTmb($this->stat($p)); - } - } else if (!empty($stat['tmb']) && $stat['tmb'] != "1") { - $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.rawurldecode($stat['tmb']); - file_exists($tmb) && unlink($tmb); - clearstatcache(); - } - } - } - - /** - * Create an gd image according to the specified mime type - * - * @param string $path image file - * @param string $mime - * @return resource|false GD image resource identifier - */ - protected function gdImageCreate($path,$mime){ - switch($mime){ - case 'image/jpeg': - return imagecreatefromjpeg($path); - - case 'image/png': - return imagecreatefrompng($path); - - case 'image/gif': - return imagecreatefromgif($path); - - case 'image/x-ms-bmp': - if (!function_exists('imagecreatefrombmp')) { - include_once dirname(__FILE__).'/libs/GdBmp.php'; - } - return imagecreatefrombmp($path); - - case 'image/xbm': - return imagecreatefromxbm($path); - - case 'image/xpm': - return imagecreatefromxpm($path); - } - return false; - } - - /** - * Output gd image to file - * - * @param resource $image gd image resource - * @param string $filename The path to save the file to. - * @param string $destformat The Image type to use for $filename - * @param string $mime The original image mime type - * @param int $jpgQuality JEPG quality (1-100) - * @return bool - */ - protected function gdImage($image, $filename, $destformat, $mime, $jpgQuality = null ){ - - if (! $jpgQuality) { - $jpgQuality = $this->options['jpgQuality']; - } - if ($destformat) { - switch ($destformat) { - case 'jpg': - $mime = 'image/jpeg'; - break; - case 'gif': - $mime = 'image/gif'; - break; - case 'png': - default: - $mime = 'image/png'; - break; - } - } - switch ($mime) { - case 'image/gif': - return imagegif($image, $filename); - case 'image/jpeg': - if ($this->options['jpgProgressive']) { - imageinterlace($image, true); - } - return imagejpeg($image, $filename, $jpgQuality); - case 'image/wbmp': - return imagewbmp($image, $filename); - case 'image/png': - default: - return imagepng($image, $filename); - } - } - - /** - * Output imagick image to file - * - * @param imagick $img imagick image resource - * @param string $filename The path to save the file to. - * @param string $destformat The Image type to use for $filename - * @param int $jpgQuality JEPG quality (1-100) - * @return bool - */ - protected function imagickImage($img, $filename, $destformat, $jpgQuality = null ){ - - if (!$jpgQuality) { - $jpgQuality = $this->options['jpgQuality']; - } - - try { - if ($destformat) { - if ($destformat === 'gif') { - $img->setImageFormat('gif'); - } else if ($destformat === 'png') { - $img->setImageFormat('png'); - } else if ($destformat === 'jpg') { - $img->setImageFormat('jpeg'); - } - } - if (strtoupper($img->getImageFormat()) === 'JPEG') { - $img->setImageCompression(imagick::COMPRESSION_JPEG); - $img->setImageCompressionQuality($jpgQuality); - if ($this->options['jpgProgressive']) { - $img->setInterlaceScheme(Imagick::INTERLACE_PLANE); - } - try { - $orientation = $img->getImageOrientation(); - } catch (ImagickException $e) { - $orientation = 0; - } - $img->stripImage(); - if ($orientation) { - $img->setImageOrientation($orientation); - } - } - $result = $img->writeImage($filename); - } catch (Exception $e) { - $result = false; - } - - return $result; - } - - /** - * Assign the proper background to a gd image - * - * @param resource $image gd image resource - * @param string $bgcolor background color in #rrggbb format - */ - protected function gdImageBackground($image, $bgcolor){ - - if ($bgcolor === 'transparent'){ - imagealphablending($image, false); - imagesavealpha($image, true); - } else { - list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); - $bgcolor1 = imagecolorallocate($image, $r, $g, $b); - imagefill($image, 0, 0, $bgcolor1); - } - } - - /** - * Prepare variables for exec convert of ImageMagick - * - * @param string $path - * @param string $destformat - * @param int $jpgQuality - * @param array $imageSize - * @param null $mime - * @return array - * @throws elFinderAbortException - */ - protected function imageMagickConvertPrepare($path, $destformat, $jpgQuality, $imageSize = null, $mime = null) { - if (is_null($imageSize)) { - $imageSize = getimagesize($path); - } - if (is_null($mime)) { - $mime = $this->mimetype($path); - } - $srcType = $this->getExtentionByMime($mime, ':'); - $ani = false; - if (preg_match('/^(?:gif|png|ico)/', $srcType)) { - $cmd = ELFINDER_IDENTIFY_PATH . ' ' . escapeshellarg($srcType . $path); - if ($this->procExec($cmd, $o) === 0) { - $ani = preg_split('/(?:\r\n|\n|\r)/', trim($o)); - if (count($ani) < 2) { - $ani = false; - } - } - } - $coalesce = $index = $interlace = ''; - $deconstruct = ' +repage'; - $index = '[0]'; - if ($ani) { - if (is_null($destformat)) { - $coalesce = ' -coalesce -repage 0x0'; - $deconstruct = ' +repage -deconstruct -layers optimize'; - } else if ($imageSize) { - if ($srcType === 'ico:') { - foreach($ani as $_i => $_info) { - if (preg_match('/ (\d+)x(\d+) /', $_info, $m)) { - if ($m[1] == $imageSize[0] && $m[2] == $imageSize[1]) { - $index = '[' . $_i . ']'; - break; - } - } - } - } - } - } - if ($imageSize && ($imageSize[2] === IMAGETYPE_JPEG || $imageSize[2] === IMAGETYPE_JPEG2000)) { - $jpgQuality = ' -quality ' . $jpgQuality; - if ($this->options['jpgProgressive']) { - $interlace = ' -interlace Plane'; - } - } else { - $jpgQuality = ''; - } - $quotedPath = escapeshellarg($srcType . $path . $index); - $quotedDstPath = escapeshellarg(($destformat? ($destformat . ':') : $srcType) . $path); - return compact('ani', 'index', 'coalesce', 'deconstruct', 'jpgQuality', 'quotedPath', 'quotedDstPath', 'interlace'); - } - - /*********************** misc *************************/ - - /** - * Return smart formatted date - * - * @param int $ts file timestamp - * @return string - * @author Dmitry (dio) Levashov - **/ - // protected function formatDate($ts) { - // if ($ts > $this->today) { - // return 'Today '.date($this->options['timeFormat'], $ts); - // } - // - // if ($ts > $this->yesterday) { - // return 'Yesterday '.date($this->options['timeFormat'], $ts); - // } - // - // return date($this->options['dateFormat'], $ts); - // } - - /** - * Find position of first occurrence of string in a string with multibyte support - * - * @param string $haystack The string being checked. - * @param string $needle The string to find in haystack. - * @param int $offset The search offset. If it is not specified, 0 is used. - * @return int|bool - * @author Alexey Sukhotin - **/ - protected function stripos($haystack , $needle , $offset = 0) { - if (function_exists('mb_stripos')) { - return mb_stripos($haystack , $needle , $offset, 'UTF-8'); - } else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) { - return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset); - } - return stripos($haystack , $needle , $offset); - } - - /** - * Default serach match method (name match) - * - * @param String $name Item name - * @param String $query Query word - * @param String $path Item path - * - * @return bool @return bool - */ - protected function searchMatchName($name , $query , $path) { - return $this->stripos($name , $query) !== false; - } - - /** - * Get server side available archivers - * - * @param bool $use_cache - * @return array - * @throws elFinderAbortException - */ - protected function getArchivers($use_cache = true) { - $sessionKey = 'archivers'; - if ($use_cache) { - if (isset($this->options['archivers']) && is_array($this->options['archivers']) && $this->options['archivers']) { - $cache = $this->options['archivers']; - } else { - $cache = elFinder::$archivers; - } - if ($cache) { - return $cache; - } else { - if ($cache = $this->session->get($sessionKey, array())) { - return elFinder::$archivers = $cache; - } - } - } - - $arcs = array( - 'create' => array(), - 'extract' => array() - ); - - if ($this->procExec('') === 0) { - - $this->procExec(ELFINDER_TAR_PATH . ' --version', $o, $ctar); - - if ($ctar == 0) { - $arcs['create']['application/x-tar'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-cf', 'ext' => 'tar'); - $arcs['extract']['application/x-tar'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-xf', 'ext' => 'tar', 'toSpec' => '-C '); - unset($o); - $this->procExec(ELFINDER_GZIP_PATH . ' --version', $o, $c); - if ($c == 0) { - $arcs['create']['application/x-gzip'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-czf', 'ext' => 'tgz'); - $arcs['extract']['application/x-gzip'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-xzf', 'ext' => 'tgz', 'toSpec' => '-C '); - } - unset($o); - $this->procExec(ELFINDER_BZIP2_PATH . ' --version', $o, $c); - if ($c == 0) { - $arcs['create']['application/x-bzip2'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-cjf', 'ext' => 'tbz'); - $arcs['extract']['application/x-bzip2'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-xjf', 'ext' => 'tbz', 'toSpec' => '-C '); - } - unset($o); - $this->procExec(ELFINDER_XZ_PATH . ' --version', $o, $c); - if ($c == 0) { - $arcs['create']['application/x-xz'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-cJf', 'ext' => 'xz'); - $arcs['extract']['application/x-xz'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-xJf', 'ext' => 'xz', 'toSpec' => '-C '); - } - } - unset($o); - $this->procExec(ELFINDER_ZIP_PATH . ' -h', $o, $c); - if ($c == 0) { - $arcs['create']['application/zip'] = array('cmd' => ELFINDER_ZIP_PATH, 'argc' => '-r9 -q', 'ext' => 'zip'); - } - unset($o); - $this->procExec(ELFINDER_UNZIP_PATH . ' --help', $o, $c); - if ($c == 0) { - $arcs['extract']['application/zip'] = array('cmd' => ELFINDER_UNZIP_PATH, 'argc' => '-q', 'ext' => 'zip', 'toSpec' => '-d '); - } - unset($o); - $this->procExec(ELFINDER_RAR_PATH . ' --version', $o, $c); - if ($c == 0 || $c == 7) { - $arcs['create']['application/x-rar'] = array('cmd' => ELFINDER_RAR_PATH, 'argc' => 'a -inul', 'ext' => 'rar'); - } - unset($o); - $this->procExec(ELFINDER_UNRAR_PATH, $o, $c); - if ($c==0 || $c == 7) { - $arcs['extract']['application/x-rar'] = array('cmd' => ELFINDER_UNRAR_PATH, 'argc' => 'x -y', 'ext' => 'rar', 'toSpec' => ''); - } - unset($o); - $this->procExec(ELFINDER_7Z_PATH, $o, $c); - if ($c == 0) { - $arcs['create']['application/x-7z-compressed'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a', 'ext' => '7z'); - $arcs['extract']['application/x-7z-compressed'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -y', 'ext' => '7z', 'toSpec' => '-o'); - - if (empty($arcs['create']['application/zip'])) { - $arcs['create']['application/zip'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a -tzip', 'ext' => 'zip'); - } - if (empty($arcs['extract']['application/zip'])) { - $arcs['extract']['application/zip'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -tzip -y', 'ext' => 'zip', 'toSpec' => '-o'); - } - if (empty($arcs['create']['application/x-tar'])) { - $arcs['create']['application/x-tar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a -ttar', 'ext' => 'tar'); - } - if (empty($arcs['extract']['application/x-tar'])) { - $arcs['extract']['application/x-tar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -ttar -y', 'ext' => 'tar', 'toSpec' => '-o'); - } - if (substr(PHP_OS, 0, 3) === 'WIN' && empty($arcs['extract']['application/x-rar'])) { - $arcs['extract']['application/x-rar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -trar -y', 'ext' => 'rar', 'toSpec' => '-o'); - } - } - - } - - // Use PHP ZipArchive Class - if (class_exists('ZipArchive', false)) { - if (empty($arcs['create']['application/zip'])) { - $arcs['create']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => array('self', 'zipArchiveZip'), 'ext' => 'zip'); - } - if (empty($arcs['extract']['application/zip'])) { - $arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => array('self', 'zipArchiveUnzip'), 'ext' => 'zip'); - } - } - - $this->session->set($sessionKey, $arcs); - return elFinder::$archivers = $arcs; - } - - /** - * Resolve relative / (Unix-like)absolute path - * - * @param string $path target path - * @param string $base base path - * @return string - */ - protected function getFullPath($path, $base) { - $separator = $this->separator; - $systemroot = $this->systemRoot; - $base = (string)$base; - - if ($base[0] === $separator && substr($base, 0, strlen($systemroot)) !== $systemroot) { - $base = $systemroot . substr($base, 1); - } - if ($base !== $systemroot) { - $base = rtrim($base, $separator); - } - - // 'Here' - if ($path === '' || $path === '.' . $separator) return $base; - - $sepquoted = preg_quote($separator, '#'); - - if (substr($path, 0, 3) === '..' . $separator) { - $path = $base . $separator . $path; - } - // normalize `/../` - $normreg = '#('.$sepquoted.')[^'.$sepquoted.']+'.$sepquoted.'\.\.'.$sepquoted.'#'; // '#(/)[^\/]+/\.\./#' - while(preg_match($normreg, $path)) { - $path = preg_replace($normreg, '$1', $path, 1); - } - if ($path !== $systemroot) { - $path = rtrim($path, $separator); - } - - // Absolute path - if ($path[0] === $separator || strpos($path, $systemroot) === 0) { - return $path; - } - - $preg_separator = '#' . $sepquoted . '#'; - - // Relative path from 'Here' - if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.') { - $arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY); - if ($arrn[0] !== '.') { - array_unshift($arrn, '.'); - } - $arrn[0] = rtrim($base, $separator); - return join($separator, $arrn); - } - - return $path; - } - - /** - * Remove directory recursive on local file system - * - * @param string $dir Target dirctory path - * @return boolean - * @throws elFinderAbortException - * @author Naoki Sawada - */ - public function rmdirRecursive($dir) { - return self::localRmdirRecursive($dir); - } - - /** - * Create archive and return its path - * - * @param string $dir target dir - * @param array $files files names list - * @param string $name archive name - * @param array $arc archiver options - * @return string|bool - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - * @author Naoki Sawada - */ - protected function makeArchive($dir, $files, $name, $arc) { - if ($arc['cmd'] === 'phpfunction') { - if (is_callable($arc['argc'])) { - call_user_func_array($arc['argc'], array($dir, $files, $name)); - } - } else { - $cwd = getcwd(); - if (chdir($dir)) { - foreach($files as $i => $file) { - $files[$i] = '.'.DIRECTORY_SEPARATOR.basename($file); - } - $files = array_map('escapeshellarg', $files); - - $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.implode(' ', $files); - $this->procExec($cmd, $o, $c); - chdir($cwd); - } else { - return false; - } - } - $path = $dir.DIRECTORY_SEPARATOR.$name; - return file_exists($path) ? $path : false; - } - - /** - * Unpack archive - * - * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) - * @param bool|string $mode bool: remove archive ( unlink($path) ) | string: extract to directory - * @return void - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin - * @author Naoki Sawada - */ - protected function unpackArchive($path, $arc, $mode = true) { - if (is_string($mode)) { - $dir = $mode; - $chdir = null; - $remove = false; - } else { - $dir = dirname($path); - $chdir = $dir; - $remove = $mode; - } - $dir = realpath($dir); - $path = realpath($path); - if ($arc['cmd'] === 'phpfunction') { - if (is_callable($arc['argc'])) { - call_user_func_array($arc['argc'], array($path, $dir)); - } - } else { - $cwd = getcwd(); - if (!$chdir || chdir($dir)) { - if ($chdir) { - $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($path)); - } else { - $cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($path).' '.$arc['toSpec'].escapeshellarg($dir); - } - $this->procExec($cmd, $o, $c); - $chdir && chdir($cwd); - } - } - $remove && unlink($path); - } - - /** - * Check and filter the extracted items - * - * @param string $path target local path - * @param array $checks types to check default: ['symlink', 'name', 'writable', 'mime'] - * @return array ['symlinks' => [], 'names' => [], 'writables' => [], 'mimes' => [], 'rmNames' => [], 'totalSize' => 0] - * @throws elFinderAbortException - * @throws Exception - * @author Naoki Sawada - */ - protected function checkExtractItems($path, $checks = null) { - if (is_null($checks) || ! is_array($checks)) { - $checks = array('symlink', 'name', 'writable', 'mime'); - } - $chkSymlink = in_array('symlink', $checks); - $chkName = in_array('name', $checks); - $chkWritable = in_array('writable', $checks); - $chkMime = in_array('mime', $checks); - - $res = array( - 'symlinks' => array(), - 'names' => array(), - 'writables' => array(), - 'mimes' => array(), - 'rmNames' => array(), - 'totalSize' => 0 - ); - - if (is_dir($path)) { - foreach (self::localScandir($path) as $name) { - $p = $path.DIRECTORY_SEPARATOR.$name; - if (!is_readable($p)) { - // Perhaps a symbolic link to open_basedir restricted location - self::localRmdirRecursive($p); - $res['symlinks'][] = $p; - $res['rmNames'][] = $name; - continue; - } - if ($chkSymlink && is_link($p)) { - self::localRmdirRecursive($p); - $res['symlinks'][] = $p; - $res['rmNames'][] = $name; - continue; - } - $isDir = is_dir($p); - if ($chkName && ! $this->nameAccepted($name, $isDir)) { - self::localRmdirRecursive($p); - $res['names'][] = $p; - $res['rmNames'][] = $name; - continue; - } - if ($chkWritable && ! $this->attr($p, 'write', null, $isDir)) { - self::localRmdirRecursive($p); - $res['writables'][] = $p; - $res['rmNames'][] = $name; - continue; - } - if ($chkMime && ($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && !$this->allowPutMime($mimeByName)) { - self::localRmdirRecursive($p); - $res['mimes'][] = $p; - $res['rmNames'][] = $name; - continue; - } - if ($isDir) { - $cRes = $this->checkExtractItems($p, $checks); - foreach($cRes as $k => $v) { - if (is_array($v)) { - $res[$k] = array_merge($res[$k], $cRes[$k]); - } else { - $res[$k] += $cRes[$k]; - } - } - } else { - $res['totalSize'] += (int)sprintf('%u', filesize($p)); - } - } - $res['rmNames'] = array_unique($res['rmNames']); - } else { - $name = basename($path); - if ($chkSymlink && is_link($path)) { - unlink($path); - $res['symlinks'][] = $path; - $res['rmNames'][] = basename($path); - } else if ($chkName && ! $this->nameAccepted($name, false)) { - unlink($path); - $res['names'][] = $path; - $res['rmNames'][] = $name; - } else if ($chkWritable && ! $this->attr($path, 'write', null, false)) { - unlink($path); - $res['writables'][] = $path; - $res['rmNames'][] = $name; - } else if ($chkMime && ($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && !$this->allowPutMime($mimeByName)) { - unlink($path); - $res['mimes'][] = $path; - $res['rmNames'][] = $name; - } else { - $res['totalSize'] += (int)sprintf('%u', filesize($path)); - } - } - - return $res; - } - - /** - * Return files of target directory that is dotfiles excludes. - * - * @param string $dir target directory path - * @return array - * @throws Exception - * @author Naoki Sawada - */ - protected static function localScandir($dir) { - // PHP function scandir() is not work well in specific environment. I dont know why. - // ref. https://github.com/Studio-42/elFinder/issues/1248 - $files = array(); - if ($dh = opendir($dir)) { - while (false !== ($file = readdir($dh))) { - if ($file !== '.' && $file !== '..') { - $files[] = $file; - } - } - closedir($dh); - } else { - throw new Exception('Can not open local directory.'); - } - return $files; - } - - /** - * Remove directory recursive on local file system - * - * @param string $dir Target dirctory path - * @return boolean - * @throws elFinderAbortException - * @author Naoki Sawada - */ - protected static function localRmdirRecursive($dir) { - // try system command - if (is_callable('exec')) { - $o = ''; - $r = 1; - if (substr(PHP_OS, 0, 3) === 'WIN') { - if (!is_link($dir) && is_dir($dir)) { - exec('rd /S /Q ' . escapeshellarg($dir), $o, $r); - } else { - exec('del /F /Q ' . escapeshellarg($dir), $o, $r); - } - } else { - exec('rm -rf ' . escapeshellarg($dir), $o, $r); - } - if ($r === 0) { - return true; - } - } - if (!is_link($dir) && is_dir($dir)) { - chmod($dir, 0777); - if ($handle = opendir($dir)) { - while (false !== ($file = readdir($handle))) { - if ($file === '.' || $file === '..') { - continue; - } - elFinder::extendTimeLimit(30); - $path = $dir . DIRECTORY_SEPARATOR . $file; - if (!is_link($dir) && is_dir($path)) { - self::localRmdirRecursive($path); - } else { - chmod($path, 0666); - unlink($path); - } - } - closedir($handle); - } - return rmdir($dir); - } else { - chmod($dir, 0666); - return unlink($dir); - } - } - - /** - * Move item recursive on local file system - * - * @param string $src - * @param string $target - * @param bool $overWrite - * @param bool $copyJoin - * @return boolean - * @throws elFinderAbortException - * @throws Exception - * @author Naoki Sawada - */ - protected static function localMoveRecursive($src, $target, $overWrite = true, $copyJoin = true) { - $res = false; - if (! file_exists($target)) { - return rename($src, $target); - } - if (! $copyJoin || ! is_dir($target)) { - if ($overWrite) { - if (is_dir($target)) { - $del = self::localRmdirRecursive($target); - } else { - $del = unlink($target); - } - if ($del) { - return rename($src, $target); - } - } - } else { - foreach(self::localScandir($src) as $item) { - $res |= self::localMoveRecursive($src.DIRECTORY_SEPARATOR.$item, $target.DIRECTORY_SEPARATOR.$item, $overWrite, $copyJoin); - } - } - return (bool)$res; - } - - /** - * Create Zip archive using PHP class ZipArchive - * - * @param string $dir target dir - * @param array $files files names list - * @param string|object $zipPath Zip archive name - * @return bool - * @author Naoki Sawada - */ - protected static function zipArchiveZip($dir, $files, $zipPath) { - try { - if ($start = is_string($zipPath)) { - $zip = new ZipArchive(); - if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) { - $zip = false; - } - } else { - $zip = $zipPath; - } - if ($zip) { - foreach($files as $file) { - $path = $dir . DIRECTORY_SEPARATOR . $file; - if (is_dir($path)) { - $zip->addEmptyDir($file); - $_files = array(); - if ($handle = opendir($path)) { - while (false !== ($entry = readdir($handle))) { - if ($entry !== "." && $entry !== "..") { - $_files[] = $file . DIRECTORY_SEPARATOR . $entry; - } - } - closedir($handle); - } - if ($_files) { - self::zipArchiveZip($dir, $_files, $zip); - } - } else { - $zip->addFile($path, $file); - } - } - $start && $zip->close(); - } - } catch (Exception $e) { - return false; - } - return true; - } - - /** - * Unpack Zip archive using PHP class ZipArchive - * - * @param string $zipPath Zip archive name - * @param string $toDir Extract to path - * @return bool - * @author Naoki Sawada - */ - protected static function zipArchiveUnzip($zipPath, $toDir) { - try { - $zip = new ZipArchive(); - if ($zip->open($zipPath) === true) { - $zip->extractTo($toDir); - $zip->close(); - } - } catch (Exception $e) { - return false; - } - return true; - } - - /** - * Recursive symlinks search - * - * @param string $path file/dir path - * @return bool - * @throws Exception - * @author Dmitry (dio) Levashov - */ - protected static function localFindSymlinks($path) { - if (is_link($path)) { - return true; - } - - if (is_dir($path)) { - foreach (self::localScandir($path) as $name) { - $p = $path.DIRECTORY_SEPARATOR.$name; - if (is_link($p)) { - return true; - } - if (is_dir($p) && self::localFindSymlinks($p)) { - return true; - } - } - } - - return false; - } - - /**==================================* abstract methods *====================================**/ - - /*********************** paths/urls *************************/ - - /** - * Return parent directory path - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _dirname($path); - - /** - * Return file name - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _basename($path); - - /** - * Join dir name and file name and return full path. - * Some drivers (db) use int as path - so we give to concat path to driver itself - * - * @param string $dir dir path - * @param string $name file name - * @return string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _joinPath($dir, $name); - - /** - * Return normalized path - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _normpath($path); - - /** - * Return file path related to root dir - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _relpath($path); - - /** - * Convert path related to root dir into real path - * - * @param string $path rel file path - * @return string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _abspath($path); - - /** - * Return fake path started from root dir. - * Required to show path on client side. - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _path($path); - - /** - * Return true if $path is children of $parent - * - * @param string $path path to check - * @param string $parent parent path - * @return bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _inpath($path, $parent); - - /** - * Return stat for given path. - * Stat contains following fields: - * - (int) size file size in b. required - * - (int) ts file modification time in unix time. required - * - (string) mime mimetype. required for folders, others - optionally - * - (bool) read read permissions. required - * - (bool) write write permissions. required - * - (bool) locked is object locked. optionally - * - (bool) hidden is object hidden. optionally - * - (string) alias for symlinks - link target path relative to root path. optionally - * - (string) target for symlinks - link target path. optionally - * - * If file does not exists - returns empty array or false. - * - * @param string $path file path - * @return array|false - * @author Dmitry (dio) Levashov - **/ - abstract protected function _stat($path); - - - /***************** file stat ********************/ - - - /** - * Return true if path is dir and has at least one childs directory - * - * @param string $path dir path - * @return bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _subdirs($path); - - /** - * Return object width and height - * Ususaly used for images, but can be realize for video etc... - * - * @param string $path file path - * @param string $mime file mime type - * @return string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _dimensions($path, $mime); - - /******************** file/dir content *********************/ - - /** - * Return files list in directory - * - * @param string $path dir path - * @return array - * @author Dmitry (dio) Levashov - **/ - abstract protected function _scandir($path); - - /** - * Open file and return file pointer - * - * @param string $path file path - * @param string $mode open mode - * @return resource|false - * @author Dmitry (dio) Levashov - **/ - abstract protected function _fopen($path, $mode="rb"); - - /** - * Close opened file - * - * @param resource $fp file pointer - * @param string $path file path - * @return bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _fclose($fp, $path=''); - - /******************** file/dir manipulations *************************/ - - /** - * Create dir and return created dir path or false on failed - * - * @param string $path parent dir path - * @param string $name new directory name - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _mkdir($path, $name); - - /** - * Create file and return it's path or false on failed - * - * @param string $path parent dir path - * @param string $name new file name - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _mkfile($path, $name); - - /** - * Create symlink - * - * @param string $source file to link to - * @param string $targetDir folder to create link in - * @param string $name symlink name - * @return bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _symlink($source, $targetDir, $name); - - /** - * Copy file into another file (only inside one volume) - * - * @param string $source source file path - * @param $targetDir - * @param string $name file name - * @return bool|string - * @internal param string $target target dir path - * @author Dmitry (dio) Levashov - */ - abstract protected function _copy($source, $targetDir, $name); - - /** - * Move file into another parent dir. - * Return new file path or false. - * - * @param string $source source file path - * @param $targetDir - * @param string $name file name - * @return bool|string - * @internal param string $target target dir path - * @author Dmitry (dio) Levashov - */ - abstract protected function _move($source, $targetDir, $name); - - /** - * Remove file - * - * @param string $path file path - * @return bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _unlink($path); - - /** - * Remove dir - * - * @param string $path dir path - * @return bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _rmdir($path); - - /** - * Create new file and write into it from file pointer. - * Return new file path or false on error. - * - * @param resource $fp file pointer - * @param string $dir target dir path - * @param string $name file name - * @param array $stat file stat (required by some virtual fs) - * @return bool|string - * @author Dmitry (dio) Levashov - **/ - abstract protected function _save($fp, $dir, $name, $stat); - - /** - * Get file contents - * - * @param string $path file path - * @return string|false - * @author Dmitry (dio) Levashov - **/ - abstract protected function _getContents($path); - - /** - * Write a string to a file - * - * @param string $path file path - * @param string $content new file content - * @return bool - * @author Dmitry (dio) Levashov - **/ - abstract protected function _filePutContents($path, $content); - - /** - * Extract files from archive - * - * @param string $path file path - * @param array $arc archiver options - * @return bool - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - **/ - abstract protected function _extract($path, $arc); - - /** - * Create archive and return its path - * - * @param string $dir target dir - * @param array $files files names list - * @param string $name archive name - * @param array $arc archiver options - * @return string|bool - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - **/ - abstract protected function _archive($dir, $files, $name, $arc); - - /** - * Detect available archivers - * - * @return void - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - **/ - abstract protected function _checkArchivers(); - - /** - * Change file mode (chmod) - * - * @param string $path file path - * @param string $mode octal string such as '0755' - * @return bool - * @author David Bartle, - **/ - abstract protected function _chmod($path, $mode); - - +abstract class elFinderVolumeDriver +{ + + /** + * Net mount key + * + * @var string + **/ + public $netMountKey = ''; + + /** + * Request args + * $_POST or $_GET values + * + * @var array + */ + protected $ARGS = array(); + + /** + * Driver id + * Must be started from letter and contains [a-z0-9] + * Used as part of volume id + * + * @var string + **/ + protected $driverId = 'a'; + + /** + * Volume id - used as prefix for files hashes + * + * @var string + **/ + protected $id = ''; + + /** + * Flag - volume "mounted" and available + * + * @var bool + **/ + protected $mounted = false; + + /** + * Root directory path + * + * @var string + **/ + protected $root = ''; + + /** + * Root basename | alias + * + * @var string + **/ + protected $rootName = ''; + + /** + * Default directory to open + * + * @var string + **/ + protected $startPath = ''; + + /** + * Base URL + * + * @var string + **/ + protected $URL = ''; + + /** + * Path to temporary directory + * + * @var string + */ + protected $tmp; + + /** + * A file save destination path when a temporary content URL is required + * on a network volume or the like + * If not specified, it tries to use "Connector Path/../files/.tmb". + * + * @var string + */ + protected $tmpLinkPath = ''; + + /** + * A file save destination URL when a temporary content URL is required + * on a network volume or the like + * If not specified, it tries to use "Connector URL/../files/.tmb". + * + * @var string + */ + protected $tmpLinkUrl = ''; + + /** + * Thumbnails dir path + * + * @var string + **/ + protected $tmbPath = ''; + + /** + * Is thumbnails dir writable + * + * @var bool + **/ + protected $tmbPathWritable = false; + + /** + * Thumbnails base URL + * + * @var string + **/ + protected $tmbURL = ''; + + /** + * Thumbnails size in px + * + * @var int + **/ + protected $tmbSize = 48; + + /** + * Image manipulation lib name + * auto|imagick|gd|convert + * + * @var string + **/ + protected $imgLib = 'auto'; + + /** + * Video to Image converter + * + * @var array + */ + protected $imgConverter = array(); + + /** + * Library to crypt files name + * + * @var string + **/ + protected $cryptLib = ''; + + /** + * Archivers config + * + * @var array + **/ + protected $archivers = array( + 'create' => array(), + 'extract' => array() + ); + + /** + * Server character encoding + * + * @var string or null + **/ + protected $encoding = null; + + /** + * How many subdirs levels return for tree + * + * @var int + **/ + protected $treeDeep = 1; + + /** + * Errors from last failed action + * + * @var array + **/ + protected $error = array(); + + /** + * Today 24:00 timestamp + * + * @var int + **/ + protected $today = 0; + + /** + * Yesterday 24:00 timestamp + * + * @var int + **/ + protected $yesterday = 0; + + /** + * Force make dirctory on extract + * + * @var int + **/ + protected $extractToNewdir = 'auto'; + + /** + * Object configuration + * + * @var array + **/ + protected $options = array( + // Driver ID (Prefix of volume ID), Normally, the value specified for each volume driver is used. + 'driverId' => '', + // Id (Suffix of volume ID), Normally, the number incremented according to the specified number of volumes is used. + 'id' => '', + // revision id of root directory that uses for caching control of root stat + 'rootRev' => '', + // driver type it uses volume root's CSS class name. e.g. 'group' -> Adds 'elfinder-group' to CSS class name. + 'type' => '', + // root directory path + 'path' => '', + // Folder hash value on elFinder to be the parent of this volume + 'phash' => '', + // Folder hash value on elFinder to trash bin of this volume, it require 'copyJoin' to true + 'trashHash' => '', + // open this path on initial request instead of root path + 'startPath' => '', + // how many subdirs levels return per request + 'treeDeep' => 1, + // root url, not set to disable sending URL to client (replacement for old "fileURL" option) + 'URL' => '', + // enable onetime URL to a file - (true, false or callable (A function that return onetime URL)) + 'onetimeUrl' => false, + // directory link url to own manager url with folder hash (`true`, `false`, `'hide'`(No show) or default `'auto'`: URL is empty then `true` else `false`) + 'dirUrlOwn' => 'auto', + // directory separator. required by client to show paths correctly + 'separator' => DIRECTORY_SEPARATOR, + // Use '/' as directory separator when the path hash encode/decode on the Windows server too + 'winHashFix' => false, + // Server character encoding (default is '': UTF-8) + 'encoding' => '', + // for convert character encoding (default is '': Not change locale) + 'locale' => '', + // URL of volume icon image + 'icon' => '', + // CSS Class of volume root in tree + 'rootCssClass' => '', + // Items to disable session caching + 'noSessionCache' => array(), + // enable i18n folder name that convert name to elFinderInstance.messages['folder_'+name] + 'i18nFolderName' => false, + // Search timeout (sec) + 'searchTimeout' => 30, + // Search exclusion directory regex pattern (require demiliter e.g. '#/path/to/exclude_directory#i') + 'searchExDirReg' => '', + // library to crypt/uncrypt files names (not implemented) + 'cryptLib' => '', + // how to detect files mimetypes. (auto/internal/finfo/mime_content_type) + 'mimeDetect' => 'auto', + // mime.types file path (for mimeDetect==internal) + 'mimefile' => '', + // Static extension/MIME of general server side scripts to security issues + 'staticMineMap' => array( + 'php:*' => 'text/x-php', + 'pht:*' => 'text/x-php', + 'php3:*' => 'text/x-php', + 'php4:*' => 'text/x-php', + 'php5:*' => 'text/x-php', + 'php7:*' => 'text/x-php', + 'phtml:*' => 'text/x-php', + 'cgi:*' => 'text/x-httpd-cgi', + 'pl:*' => 'text/x-perl', + 'asp:*' => 'text/x-asap', + 'aspx:*' => 'text/x-asap', + 'py:*' => 'text/x-python', + 'rb:*' => 'text/x-ruby', + 'jsp:*' => 'text/x-jsp' + ), + // mime type normalize map : Array '[ext]:[detected mime type]' => '[normalized mime]' + 'mimeMap' => array( + 'md:application/x-genesis-rom' => 'text/x-markdown', + 'md:text/plain' => 'text/x-markdown', + 'markdown:text/plain' => 'text/x-markdown', + 'css:text/x-asm' => 'text/css', + 'css:text/plain' => 'text/css', + 'csv:text/plain' => 'text/csv', + 'json:text/plain' => 'application/json', + 'sql:text/plain' => 'text/x-sql', + 'rtf:text/rtf' => 'application/rtf', + 'rtfd:text/rtfd' => 'application/rtfd', + 'ico:image/vnd.microsoft.icon' => 'image/x-icon', + 'svg:text/plain' => 'image/svg+xml', + 'pxd:application/octet-stream' => 'image/x-pixlr-data', + 'dng:image/tiff' => 'image/x-adobe-dng', + 'sketch:application/zip' => 'image/x-sketch', + 'sketch:application/octet-stream' => 'image/x-sketch', + 'xcf:application/octet-stream' => 'image/x-xcf', + 'amr:application/octet-stream' => 'audio/amr', + 'm4a:video/mp4' => 'audio/mp4', + 'oga:application/ogg' => 'audio/ogg', + 'ogv:application/ogg' => 'video/ogg', + 'zip:application/x-zip' => 'application/zip', + 'm3u8:text/plain' => 'application/x-mpegURL', + 'mpd:text/plain' => 'application/dash+xml', + 'mpd:application/xml' => 'application/dash+xml', + '*:application/x-dosexec' => 'application/x-executable', + 'doc:application/vnd.ms-office' => 'application/msword', + 'xls:application/vnd.ms-office' => 'application/vnd.ms-excel', + 'ppt:application/vnd.ms-office' => 'application/vnd.ms-powerpoint', + 'yml:text/plain' => 'text/x-yaml', + 'ai:application/pdf' => 'application/postscript', + 'cgm:text/plain' => 'image/cgm', + 'dxf:text/plain' => 'image/vnd.dxf', + 'hpgl:text/plain' => 'application/vnd.hp-hpgl', + 'igs:text/plain' => 'model/iges', + 'iges:text/plain' => 'model/iges', + 'plt:application/octet-stream' => 'application/plt', + 'plt:text/plain' => 'application/plt', + 'sat:text/plain' => 'application/sat', + 'step:text/plain' => 'application/step', + 'stp:text/plain' => 'application/step' + ), + // An option to add MimeMap to the `mimeMap` option + // Array '[ext]:[detected mime type]' => '[normalized mime]' + 'additionalMimeMap' => array(), + // MIME-Type of filetype detected as unknown + 'mimeTypeUnknown' => 'application/octet-stream', + // MIME regex of send HTTP header "Content-Disposition: inline" or allow preview in quicklook + // '.' is allow inline of all of MIME types + // '$^' is not allow inline of all of MIME types + 'dispInlineRegex' => '^(?:(?:video|audio)|image/(?!.+\+xml)|application/(?:ogg|x-mpegURL|dash\+xml)|(?:text/plain|application/pdf)$)', + // temporary content URL's base path + 'tmpLinkPath' => '', + // temporary content URL's base URL + 'tmpLinkUrl' => '', + // directory for thumbnails + 'tmbPath' => '.tmb', + // mode to create thumbnails dir + 'tmbPathMode' => 0777, + // thumbnails dir URL. Set it if store thumbnails outside root directory + 'tmbURL' => '', + // thumbnails size (px) + 'tmbSize' => 48, + // thumbnails crop (true - crop, false - scale image to fit thumbnail size) + 'tmbCrop' => true, + // thumbnails background color (hex #rrggbb or 'transparent') + 'tmbBgColor' => 'transparent', + // image rotate fallback background color (hex #rrggbb) + 'bgColorFb' => '#ffffff', + // image manipulations library + 'imgLib' => 'auto', + // Fallback self image to thumbnail (nothing imgLib) + 'tmbFbSelf' => true, + // Video to Image converters ['TYPE or MIME' => ['func' => function($file){ /* Converts $file to Image */ return true; }, 'maxlen' => (int)TransferLength]] + 'imgConverter' => array(), + // Max length of transfer to image converter + 'tmbVideoConvLen' => 10000000, + // Captre point seccond + 'tmbVideoConvSec' => 6, + // Resource path of fallback icon images defailt: php/resouces + 'resourcePath' => '', + // Jpeg image saveing quality + 'jpgQuality' => 100, + // Save as progressive JPEG on image editing + 'jpgProgressive' => true, + // enable to get substitute image with command `dim` + 'substituteImg' => true, + // on paste file - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext + 'copyOverwrite' => true, + // if true - join new and old directories content on paste + 'copyJoin' => true, + // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext + 'uploadOverwrite' => true, + // mimetypes allowed to upload + 'uploadAllow' => array(), + // mimetypes not allowed to upload + 'uploadDeny' => array(), + // order to process uploadAllow and uploadDeny options + 'uploadOrder' => array('deny', 'allow'), + // maximum upload file size. NOTE - this is size for every uploaded files + 'uploadMaxSize' => 0, + // maximum number of chunked upload connection. `-1` to disable chunked upload + 'uploadMaxConn' => 3, + // maximum get file size. NOTE - Maximum value is 50% of PHP memory_limit + 'getMaxSize' => 0, + // files dates format + 'dateFormat' => 'j M Y H:i', + // files time format + 'timeFormat' => 'H:i', + // if true - every folder will be check for children folders, -1 - every folder will be check asynchronously, false - all folders will be marked as having subfolders + 'checkSubfolders' => true, // true, false or -1 + // allow to copy from this volume to other ones? + 'copyFrom' => true, + // allow to copy from other volumes to this one? + 'copyTo' => true, + // cmd duplicate suffix format e.g. '_%s_' to without spaces + 'duplicateSuffix' => ' %s ', + // unique name numbar format e.g. '(%d)' to (1), (2)... + 'uniqueNumFormat' => '%d', + // list of commands disabled on this root + 'disabled' => array(), + // enable file owner, group & mode info, `false` to inactivate "chmod" command. + 'statOwner' => false, + // allow exec chmod of read-only files + 'allowChmodReadOnly' => false, + // regexp or function name to validate new file name + 'acceptedName' => '/^[^\.].*/', // Notice: overwritten it in some volume drivers contractor + // regexp or function name to validate new directory name + 'acceptedDirname' => '', // used `acceptedName` if empty value + // function/class method to control files permissions + 'accessControl' => null, + // some data required by access control + 'accessControlData' => null, + // default permissions. + 'defaults' => array( + 'read' => true, + 'write' => true, + 'locked' => false, + 'hidden' => false + ), + // files attributes + 'attributes' => array(), + // max allowed archive files size (0 - no limit) + 'maxArcFilesSize' => 0, + // Allowed archive's mimetypes to create. Leave empty for all available types. + 'archiveMimes' => array(), + // Manual config for archivers. See example below. Leave empty for auto detect + 'archivers' => array(), + // Use Archive function for remote volume + 'useRemoteArchive' => false, + // plugin settings + 'plugin' => array(), + // Is support parent directory time stamp update on add|remove|rename item + // Default `null` is auto detection that is LocalFileSystem, FTP or Dropbox are `true` + 'syncChkAsTs' => null, + // Long pooling sync checker function for syncChkAsTs is true + // Calls with args (TARGET DIRCTORY PATH, STAND-BY(sec), OLD TIMESTAMP, VOLUME DRIVER INSTANCE, ELFINDER INSTANCE) + // This function must return the following values. Changed: New Timestamp or Same: Old Timestamp or Error: false + // Default `null` is try use elFinderVolumeLocalFileSystem::localFileSystemInotify() on LocalFileSystem driver + // another driver use elFinder stat() checker + 'syncCheckFunc' => null, + // Long polling sync stand-by time (sec) + 'plStandby' => 30, + // Sleep time (sec) for elFinder stat() checker (syncChkAsTs is true) + 'tsPlSleep' => 10, + // Sleep time (sec) for elFinder ls() checker (syncChkAsTs is false) + 'lsPlSleep' => 30, + // Client side sync interval minimum (ms) + // Default `null` is auto set to ('tsPlSleep' or 'lsPlSleep') * 1000 + // `0` to disable auto sync + 'syncMinMs' => null, + // required to fix bug on macos + // However, we recommend to use the Normalizer plugin instead this option + 'utf8fix' => false, + // й ё Й Ё Ø Å + 'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"), + 'utf8replace' => array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5"), + // cache control HTTP headers for commands `file` and `get` + 'cacheHeaders' => array( + 'Cache-Control: max-age=3600', + 'Expires:', + 'Pragma:' + ), + // Header to use to accelerate sending local files to clients (e.g. 'X-Sendfile', 'X-Accel-Redirect') + 'xsendfile' => '', + // Root path to xsendfile target. Probably, this is required for 'X-Accel-Redirect' on Nginx. + 'xsendfilePath' => '' + ); + + /** + * Defaults permissions + * + * @var array + **/ + protected $defaults = array( + 'read' => true, + 'write' => true, + 'locked' => false, + 'hidden' => false + ); + + /** + * Access control function/class + * + * @var mixed + **/ + protected $attributes = array(); + + /** + * Access control function/class + * + * @var mixed + **/ + protected $access = null; + + /** + * Mime types allowed to upload + * + * @var array + **/ + protected $uploadAllow = array(); + + /** + * Mime types denied to upload + * + * @var array + **/ + protected $uploadDeny = array(); + + /** + * Order to validate uploadAllow and uploadDeny + * + * @var array + **/ + protected $uploadOrder = array(); + + /** + * Maximum allowed upload file size. + * Set as number or string with unit - "10M", "500K", "1G" + * + * @var int|string + **/ + protected $uploadMaxSize = 0; + + /** + * Run time setting of overwrite items on upload + * + * @var string + */ + protected $uploadOverwrite = true; + + /** + * Maximum allowed get file size. + * Set as number or string with unit - "10M", "500K", "1G" + * + * @var int|string + **/ + protected $getMaxSize = -1; + + /** + * Mimetype detect method + * + * @var string + **/ + protected $mimeDetect = 'auto'; + + /** + * Flag - mimetypes from externail file was loaded + * + * @var bool + **/ + private static $mimetypesLoaded = false; + + /** + * Finfo resource for mimeDetect == 'finfo' + * + * @var resource + **/ + protected $finfo = null; + + /** + * List of disabled client's commands + * + * @var array + **/ + protected $disabled = array(); + + /** + * overwrite extensions/mimetypes to mime.types + * + * @var array + **/ + protected static $mimetypes = array( + // applications + 'exe' => 'application/x-executable', + 'jar' => 'application/x-jar', + // archives + 'gz' => 'application/x-gzip', + 'tgz' => 'application/x-gzip', + 'tbz' => 'application/x-bzip2', + 'rar' => 'application/x-rar', + // texts + 'php' => 'text/x-php', + 'js' => 'text/javascript', + 'rtfd' => 'application/rtfd', + 'py' => 'text/x-python', + 'rb' => 'text/x-ruby', + 'sh' => 'text/x-shellscript', + 'pl' => 'text/x-perl', + 'xml' => 'text/xml', + 'c' => 'text/x-csrc', + 'h' => 'text/x-chdr', + 'cpp' => 'text/x-c++src', + 'hh' => 'text/x-c++hdr', + 'md' => 'text/x-markdown', + 'markdown' => 'text/x-markdown', + 'yml' => 'text/x-yaml', + // images + 'bmp' => 'image/x-ms-bmp', + 'tga' => 'image/x-targa', + 'xbm' => 'image/xbm', + 'pxm' => 'image/pxm', + //audio + 'wav' => 'audio/wav', + // video + 'dv' => 'video/x-dv', + 'wm' => 'video/x-ms-wmv', + 'ogm' => 'video/ogg', + 'm2ts' => 'video/MP2T', + 'mts' => 'video/MP2T', + 'ts' => 'video/MP2T', + 'm3u8' => 'application/x-mpegURL', + 'mpd' => 'application/dash+xml' + ); + + /** + * Directory separator - required by client + * + * @var string + **/ + protected $separator = DIRECTORY_SEPARATOR; + + /** + * Directory separator for decode/encode hash + * + * @var string + **/ + protected $separatorForHash = ''; + + /** + * System Root path (Unix like: '/', Windows: '\', 'C:\' or 'D:\'...) + * + * @var string + **/ + protected $systemRoot = DIRECTORY_SEPARATOR; + + /** + * Mimetypes allowed to display + * + * @var array + **/ + protected $onlyMimes = array(); + + /** + * Store files moved or overwrited files info + * + * @var array + **/ + protected $removed = array(); + + /** + * Store files added files info + * + * @var array + **/ + protected $added = array(); + + /** + * Cache storage + * + * @var array + **/ + protected $cache = array(); + + /** + * Cache by folders + * + * @var array + **/ + protected $dirsCache = array(); + + /** + * You should use `$this->sessionCache['subdirs']` instead + * + * @var array + * @deprecated + */ + protected $subdirsCache = array(); + + /** + * This volume session cache + * + * @var array + */ + protected $sessionCache; + + /** + * Session caching item list + * + * @var array + */ + protected $sessionCaching = array('rootstat' => true, 'subdirs' => true); + + /** + * elFinder session wrapper object + * + * @var elFinderSessionInterface + */ + protected $session; + + /** + * Search start time + * + * @var int + */ + protected $searchStart; + + /** + * Current query word on doSearch + * + * @var array + **/ + protected $doSearchCurrentQuery = array(); + + /** + * Is root modified (for clear root stat cache) + * + * @var bool + */ + protected $rootModified = false; + + /** + * Is disable of command `url` + * + * @var string + */ + protected $disabledGetUrl = false; + + /** + * Accepted filename validator + * + * @var string | callable + */ + protected $nameValidator; + + /** + * Accepted dirname validator + * + * @var string | callable + */ + protected $dirnameValidator; + + /*********************************************************************/ + /* INITIALIZATION */ + /*********************************************************************/ + + /** + * Prepare driver before mount volume. + * Return true if volume is ready. + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function init() + { + return true; + } + + /** + * Configure after successfull mount. + * By default set thumbnails path and image manipulation library. + * + * @return void + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function configure() + { + // set thumbnails path + $path = $this->options['tmbPath']; + if ($path) { + if (!file_exists($path)) { + if (mkdir($path)) { + chmod($path, $this->options['tmbPathMode']); + } else { + $path = ''; + } + } + + if (is_dir($path) && is_readable($path)) { + $this->tmbPath = $path; + $this->tmbPathWritable = is_writable($path); + } + } + // set resouce path + if (!is_dir($this->options['resourcePath'])) { + $this->options['resourcePath'] = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'resources'; + } + + // set image manipulation library + $type = preg_match('/^(imagick|gd|convert|auto)$/i', $this->options['imgLib']) + ? strtolower($this->options['imgLib']) + : 'auto'; + + if (($type === 'imagick' || $type === 'auto') && extension_loaded('imagick')) { + $this->imgLib = 'imagick'; + } else if (($type === 'gd' || $type === 'auto') && function_exists('gd_info')) { + $this->imgLib = 'gd'; + } else { + $convertCache = 'imgLibConvert'; + if (($convertCmd = $this->session->get($convertCache, false)) !== false) { + $this->imgLib = $convertCmd; + } else { + $this->imgLib = ($this->procExec(ELFINDER_CONVERT_PATH . ' -version') === 0) ? 'convert' : ''; + $this->session->set($convertCache, $this->imgLib); + } + } + if ($type !== 'auto' && $this->imgLib === '') { + // fallback + $this->imgLib = extension_loaded('imagick') ? 'imagick' : (function_exists('gd_info') ? 'gd' : ''); + } + + // check video to img converter + if (!empty($this->options['imgConverter']) && is_array($this->options['imgConverter'])) { + foreach ($this->options['imgConverter'] as $_type => $_converter) { + if (isset($_converter['func'])) { + $this->imgConverter[strtolower($_type)] = $_converter; + } + } + } + if (!isset($this->imgConverter['video'])) { + $videoLibCache = 'videoLib'; + if (($videoLibCmd = $this->session->get($videoLibCache, false)) === false) { + $videoLibCmd = ($this->procExec(ELFINDER_FFMPEG_PATH . ' -version') === 0) ? 'ffmpeg' : ''; + $this->session->set($videoLibCache, $videoLibCmd); + } + if ($videoLibCmd) { + $this->imgConverter['video'] = array( + 'func' => array($this, $videoLibCmd . 'ToImg'), + 'maxlen' => $this->options['tmbVideoConvLen'] + ); + } + } + + // check archivers + if (empty($this->archivers['create'])) { + $this->disabled[] = 'archive'; + } + if (empty($this->archivers['extract'])) { + $this->disabled[] = 'extract'; + } + $_arc = $this->getArchivers(); + if (empty($_arc['create'])) { + $this->disabled[] = 'zipdl'; + } + + // check 'statOwner' for command `chmod` + if (empty($this->options['statOwner'])) { + $this->disabled[] = 'chmod'; + } + + // check 'mimeMap' + if (!is_array($this->options['mimeMap'])) { + $this->options['mimeMap'] = array(); + } + if (is_array($this->options['staticMineMap']) && $this->options['staticMineMap']) { + $this->options['mimeMap'] = array_merge($this->options['mimeMap'], $this->options['staticMineMap']); + } + if (is_array($this->options['additionalMimeMap']) && $this->options['additionalMimeMap']) { + $this->options['mimeMap'] = array_merge($this->options['mimeMap'], $this->options['additionalMimeMap']); + } + + // check 'url' in disabled commands + if (in_array('url', $this->disabled)) { + $this->disabledGetUrl = true; + } + + // set run time setting uploadOverwrite + $this->uploadOverwrite = $this->options['uploadOverwrite']; + } + + /** + * @deprecated + */ + protected function sessionRestart() + { + $this->sessionCache = $this->session->start()->get($this->id, array()); + return true; + } + + /*********************************************************************/ + /* PUBLIC API */ + /*********************************************************************/ + + /** + * Return driver id. Used as a part of volume id. + * + * @return string + * @author Dmitry (dio) Levashov + **/ + public function driverId() + { + return $this->driverId; + } + + /** + * Return volume id + * + * @return string + * @author Dmitry (dio) Levashov + **/ + public function id() + { + return $this->id; + } + + /** + * Assign elFinder session wrapper object + * + * @param $session elFinderSessionInterface + */ + public function setSession($session) + { + $this->session = $session; + } + + /** + * Get elFinder sesson wrapper object + * + * @return object The session object + */ + public function getSession() + { + return $this->session; + } + + /** + * Save session cache data + * Calls this function before umount this volume on elFinder::exec() + * + * @return void + */ + public function saveSessionCache() + { + $this->session->set($this->id, $this->sessionCache); + } + + /** + * Return debug info for client + * + * @return array + * @author Dmitry (dio) Levashov + **/ + public function debug() + { + return array( + 'id' => $this->id(), + 'name' => strtolower(substr(get_class($this), strlen('elfinderdriver'))), + 'mimeDetect' => $this->mimeDetect, + 'imgLib' => $this->imgLib + ); + } + + /** + * chmod a file or folder + * + * @param string $hash file or folder hash to chmod + * @param string $mode octal string representing new permissions + * + * @return array|false + * @author David Bartle + **/ + public function chmod($hash, $mode) + { + if ($this->commandDisabled('chmod')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if (!($file = $this->file($hash))) { + return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); + } + + if (!$this->options['allowChmodReadOnly']) { + if (!$this->attr($this->decode($hash), 'write', null, ($file['mime'] === 'directory'))) { + return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']); + } + } + + $path = $this->decode($hash); + $write = $file['write']; + + if ($this->convEncOut(!$this->_chmod($this->convEncIn($path), $mode))) { + return $this->setError(elFinder::ERROR_PERM_DENIED, $file['name']); + } + + $this->clearstatcache(); + if ($path == $this->root) { + $this->rootModified = true; + } + + if ($file = $this->stat($path)) { + $files = array($file); + if ($file['mime'] === 'directory' && $write !== $file['write']) { + foreach ($this->getScandir($path) as $stat) { + if ($this->mimeAccepted($stat['mime'])) { + $files[] = $stat; + } + } + } + return $files; + } else { + return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); + } + } + + /** + * stat a file or folder for elFinder cmd exec + * + * @param string $hash file or folder hash to chmod + * + * @return array + * @author Naoki Sawada + **/ + public function fstat($hash) + { + $path = $this->decode($hash); + return $this->stat($path); + } + + /** + * Clear PHP stat cache & all of inner stat caches + */ + public function clearstatcache() + { + clearstatcache(); + $this->clearcache(); + } + + /** + * Clear inner stat caches for target hash + * + * @param string $hash + */ + public function clearcaches($hash = null) + { + if ($hash === null) { + $this->clearcache(); + } else { + $path = $this->decode($hash); + unset($this->cache[$path], $this->dirsCache[$path]); + } + } + + /** + * "Mount" volume. + * Return true if volume available for read or write, + * false - otherwise + * + * @param array $opts + * + * @return bool + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + */ + public function mount(array $opts) + { + $this->options = array_merge($this->options, $opts); + + if (!isset($this->options['path']) || $this->options['path'] === '') { + return $this->setError('Path undefined.'); + } + + if (!$this->session) { + return $this->setError('Session wrapper dose not set. Need to `$volume->setSession(elFinderSessionInterface);` before mount.'); + } + if (!($this->session instanceof elFinderSessionInterface)) { + return $this->setError('Session wrapper instance must be "elFinderSessionInterface".'); + } + + // set driverId + if (!empty($this->options['driverId'])) { + $this->driverId = $this->options['driverId']; + } + + $this->id = $this->driverId . (!empty($this->options['id']) ? $this->options['id'] : elFinder::$volumesCnt++) . '_'; + $this->root = $this->normpathCE($this->options['path']); + $this->separator = isset($this->options['separator']) ? $this->options['separator'] : DIRECTORY_SEPARATOR; + if (!empty($this->options['winHashFix'])) { + $this->separatorForHash = ($this->separator !== '/') ? '/' : ''; + } + $this->systemRoot = isset($this->options['systemRoot']) ? $this->options['systemRoot'] : $this->separator; + + // set ARGS + $this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET; + + $argInit = !empty($this->ARGS['init']); + + // session cache + if ($argInit) { + $this->session->set($this->id, array()); + } + $this->sessionCache = $this->session->get($this->id, array()); + + // default file attribute + $this->defaults = array( + 'read' => isset($this->options['defaults']['read']) ? !!$this->options['defaults']['read'] : true, + 'write' => isset($this->options['defaults']['write']) ? !!$this->options['defaults']['write'] : true, + 'locked' => isset($this->options['defaults']['locked']) ? !!$this->options['defaults']['locked'] : false, + 'hidden' => isset($this->options['defaults']['hidden']) ? !!$this->options['defaults']['hidden'] : false + ); + + // root attributes + $this->attributes[] = array( + 'pattern' => '~^' . preg_quote($this->separator) . '$~', + 'locked' => true, + 'hidden' => false + ); + // set files attributes + if (!empty($this->options['attributes']) && is_array($this->options['attributes'])) { + + foreach ($this->options['attributes'] as $a) { + // attributes must contain pattern and at least one rule + if (!empty($a['pattern']) || count($a) > 1) { + $this->attributes[] = $a; + } + } + } + + if (!empty($this->options['accessControl']) && is_callable($this->options['accessControl'])) { + $this->access = $this->options['accessControl']; + } + + $this->today = mktime(0, 0, 0, date('m'), date('d'), date('Y')); + $this->yesterday = $this->today - 86400; + + if (!$this->init()) { + return false; + } + + // set server encoding + if (!empty($this->options['encoding']) && strtoupper($this->options['encoding']) !== 'UTF-8') { + $this->encoding = $this->options['encoding']; + } else { + $this->encoding = null; + } + + // check some options is arrays + $this->uploadAllow = isset($this->options['uploadAllow']) && is_array($this->options['uploadAllow']) + ? $this->options['uploadAllow'] + : array(); + + $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny']) + ? $this->options['uploadDeny'] + : array(); + + $this->options['uiCmdMap'] = (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap'])) + ? $this->options['uiCmdMap'] + : array(); + + if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x + $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow'); + $this->uploadOrder = array(trim($parts[0]), trim($parts[1])); + } else { // telephat_mode off + $this->uploadOrder = !empty($this->options['uploadOrder']) ? $this->options['uploadOrder'] : array('deny', 'allow'); + } + + if (!empty($this->options['uploadMaxSize'])) { + $this->uploadMaxSize = elFinder::getIniBytes('', $this->options['uploadMaxSize']); + } + // Set maximum to PHP_INT_MAX + if (!defined('PHP_INT_MAX')) { + define('PHP_INT_MAX', 2147483647); + } + if ($this->uploadMaxSize < 1 || $this->uploadMaxSize > PHP_INT_MAX) { + $this->uploadMaxSize = PHP_INT_MAX; + } + + // Set to get maximum size to 50% of memory_limit + $memLimit = elFinder::getIniBytes('memory_limit') / 2; + if ($memLimit > 0) { + $this->getMaxSize = empty($this->options['getMaxSize']) ? $memLimit : min($memLimit, elFinder::getIniBytes('', $this->options['getMaxSize'])); + } else { + $this->getMaxSize = -1; + } + + $this->disabled = isset($this->options['disabled']) && is_array($this->options['disabled']) + ? array_values(array_diff($this->options['disabled'], array('open'))) // 'open' is required + : array(); + + $this->cryptLib = $this->options['cryptLib']; + $this->mimeDetect = $this->options['mimeDetect']; + + // find available mimetype detect method + $type = strtolower($this->options['mimeDetect']); + $type = preg_match('/^(finfo|mime_content_type|internal|auto)$/i', $type) ? $type : 'auto'; + $regexp = '/text\/x\-(php|c\+\+)/'; + + if (($type == 'finfo' || $type == 'auto') + && class_exists('finfo', false)) { + $tmpFileInfo = explode(';', finfo_file(finfo_open(FILEINFO_MIME), __FILE__)); + } else { + $tmpFileInfo = false; + } + + $type = 'internal'; + if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) { + $type = 'finfo'; + $this->finfo = finfo_open(FILEINFO_MIME); + } elseif (($type == 'mime_content_type' || $type == 'auto') && function_exists('mime_content_type')) { + $_mimetypes = explode(';', mime_content_type(__FILE__)); + if (preg_match($regexp, array_shift($_mimetypes))) { + $type = 'mime_content_type'; + } + } + $this->mimeDetect = $type; + + // load mimes from external file for mimeDetect == 'internal' + // based on Alexey Sukhotin idea and patch: http://elrte.org/redmine/issues/163 + // file must be in file directory or in parent one + if ($this->mimeDetect === 'internal' && !elFinderVolumeDriver::$mimetypesLoaded) { + elFinderVolumeDriver::loadMimeTypes(!empty($this->options['mimefile']) ? $this->options['mimefile'] : ''); + } + + $this->rootName = empty($this->options['alias']) ? $this->basenameCE($this->root) : $this->options['alias']; + + // This get's triggered if $this->root == '/' and alias is empty. + // Maybe modify _basename instead? + if ($this->rootName === '') $this->rootName = $this->separator; + + $root = $this->stat($this->root); + + if (!$root) { + return $this->setError('Root folder does not exist.'); + } + if (!$root['read'] && !$root['write']) { + return $this->setError('Root folder has not read and write permissions.'); + } + + if ($root['read']) { + if ($argInit) { + // check startPath - path to open by default instead of root + $startPath = $this->options['startPath'] ? $this->normpathCE($this->options['startPath']) : ''; + if ($startPath) { + $start = $this->stat($startPath); + if (!empty($start) + && $start['mime'] == 'directory' + && $start['read'] + && empty($start['hidden']) + && $this->inpathCE($startPath, $this->root)) { + $this->startPath = $startPath; + if (substr($this->startPath, -1, 1) == $this->options['separator']) { + $this->startPath = substr($this->startPath, 0, -1); + } + } + } + } + } else { + $this->options['URL'] = ''; + $this->options['tmbURL'] = ''; + $this->options['tmbPath'] = ''; + // read only volume + array_unshift($this->attributes, array( + 'pattern' => '/.*/', + 'read' => false + )); + } + $this->treeDeep = $this->options['treeDeep'] > 0 ? (int)$this->options['treeDeep'] : 1; + $this->tmbSize = $this->options['tmbSize'] > 0 ? (int)$this->options['tmbSize'] : 48; + $this->URL = $this->options['URL']; + if ($this->URL && preg_match("|[^/?&=]$|", $this->URL)) { + $this->URL .= '/'; + } + + $dirUrlOwn = strtolower($this->options['dirUrlOwn']); + if ($dirUrlOwn === 'auto') { + $this->options['dirUrlOwn'] = $this->URL ? false : true; + } else if ($dirUrlOwn === 'hide') { + $this->options['dirUrlOwn'] = 'hide'; + } else { + $this->options['dirUrlOwn'] = (bool)$this->options['dirUrlOwn']; + } + + $this->tmbURL = !empty($this->options['tmbURL']) ? $this->options['tmbURL'] : ''; + if ($this->tmbURL && $this->tmbURL !== 'self' && preg_match("|[^/?&=]$|", $this->tmbURL)) { + $this->tmbURL .= '/'; + } + + $this->nameValidator = !empty($this->options['acceptedName']) && (is_string($this->options['acceptedName']) || is_callable($this->options['acceptedName'])) + ? $this->options['acceptedName'] + : ''; + + $this->dirnameValidator = !empty($this->options['acceptedDirname']) && (is_callable($this->options['acceptedDirname']) || (is_string($this->options['acceptedDirname']) && preg_match($this->options['acceptedDirname'], '') !== false)) + ? $this->options['acceptedDirname'] + : $this->nameValidator; + + $this->_checkArchivers(); + + // enabling archivers['create'] with options['useRemoteArchive'] + if ($this->options['useRemoteArchive'] && empty($this->archivers['create']) && $this->getTempPath()) { + $_archivers = $this->getArchivers(); + $this->archivers['create'] = $_archivers['create']; + } + + // manual control archive types to create + if (!empty($this->options['archiveMimes']) && is_array($this->options['archiveMimes'])) { + foreach ($this->archivers['create'] as $mime => $v) { + if (!in_array($mime, $this->options['archiveMimes'])) { + unset($this->archivers['create'][$mime]); + } + } + } + + // manualy add archivers + if (!empty($this->options['archivers']['create']) && is_array($this->options['archivers']['create'])) { + foreach ($this->options['archivers']['create'] as $mime => $conf) { + if (strpos($mime, 'application/') === 0 + && !empty($conf['cmd']) + && isset($conf['argc']) + && !empty($conf['ext']) + && !isset($this->archivers['create'][$mime])) { + $this->archivers['create'][$mime] = $conf; + } + } + } + + if (!empty($this->options['archivers']['extract']) && is_array($this->options['archivers']['extract'])) { + foreach ($this->options['archivers']['extract'] as $mime => $conf) { + if (strpos($mime, 'application/') === 0 + && !empty($conf['cmd']) + && isset($conf['argc']) + && !empty($conf['ext']) + && !isset($this->archivers['extract'][$mime])) { + $this->archivers['extract'][$mime] = $conf; + } + } + } + + if (!empty($this->options['noSessionCache']) && is_array($this->options['noSessionCache'])) { + foreach ($this->options['noSessionCache'] as $_key) { + $this->sessionCaching[$_key] = false; + unset($this->sessionCache[$_key]); + } + } + if ($this->sessionCaching['subdirs']) { + if (!isset($this->sessionCache['subdirs'])) { + $this->sessionCache['subdirs'] = array(); + } + } + + + $this->configure(); + + // Normalize disabled (array_merge`for type array of JSON) + $this->disabled = array_values(array_unique($this->disabled)); + + // fix sync interval + if ($this->options['syncMinMs'] !== 0) { + $this->options['syncMinMs'] = max($this->options[$this->options['syncChkAsTs'] ? 'tsPlSleep' : 'lsPlSleep'] * 1000, intval($this->options['syncMinMs'])); + } + + // ` copyJoin` is required for the trash function + if ($this->options['trashHash'] && empty($this->options['copyJoin'])) { + $this->options['trashHash'] = ''; + } + + // set tmpLinkPath + if (elFinder::$tmpLinkPath && !$this->options['tmpLinkPath']) { + if (is_writeable(elFinder::$tmpLinkPath)) { + $this->options['tmpLinkPath'] = elFinder::$tmpLinkPath; + } else { + elFinder::$tmpLinkPath = ''; + } + } + if ($this->options['tmpLinkPath'] && is_writable($this->options['tmpLinkPath'])) { + $this->tmpLinkPath = realpath($this->options['tmpLinkPath']); + } else if (!$this->tmpLinkPath && $this->tmbURL && $this->tmbPath) { + $this->tmpLinkPath = $this->tmbPath; + $this->options['tmpLinkUrl'] = $this->tmbURL; + } else if (!$this->options['URL'] && is_writable('../files/.tmb')) { + $this->tmpLinkPath = realpath('../files/.tmb'); + $this->options['tmpLinkUrl'] = ''; + if (!elFinder::$tmpLinkPath) { + elFinder::$tmpLinkPath = $this->tmpLinkPath; + elFinder::$tmpLinkUrl = ''; + } + } + + // set tmpLinkUrl + if (elFinder::$tmpLinkUrl && !$this->options['tmpLinkUrl']) { + $this->options['tmpLinkUrl'] = elFinder::$tmpLinkUrl; + } + if ($this->options['tmpLinkUrl']) { + $this->tmpLinkUrl = $this->options['tmpLinkUrl']; + } + if ($this->tmpLinkPath && !$this->tmpLinkUrl) { + $cur = realpath('./'); + $i = 0; + while ($cur !== $this->systemRoot && strpos($this->tmpLinkPath, $cur) !== 0) { + $i++; + $cur = dirname($cur); + } + list($req) = explode('?', $_SERVER['REQUEST_URI']); + $reqs = explode('/', dirname($req)); + $uri = join('/', array_slice($reqs, 0, count($reqs) - 1)) . substr($this->tmpLinkPath, strlen($cur)); + $https = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off'); + $this->tmpLinkUrl = ($https ? 'https://' : 'http://') + . $_SERVER['SERVER_NAME'] // host + . (((!$https && $_SERVER['SERVER_PORT'] == 80) || ($https && $_SERVER['SERVER_PORT'] == 443)) ? '' : (':' . $_SERVER['SERVER_PORT'])) // port + . $uri; + if (!elFinder::$tmpLinkUrl) { + elFinder::$tmpLinkUrl = $this->tmpLinkUrl; + } + } + + // remove last '/' + if ($this->tmpLinkPath) { + $this->tmpLinkPath = rtrim($this->tmpLinkPath, '/'); + } + if ($this->tmpLinkUrl) { + $this->tmpLinkUrl = rtrim($this->tmpLinkUrl, '/'); + } + + // to update options cache + $this->updateCache($this->root, $root); + + return $this->mounted = true; + } + + /** + * Some "unmount" stuffs - may be required by virtual fs + * + * @return void + * @author Dmitry (dio) Levashov + **/ + public function umount() + { + } + + /** + * Remove session cache of this volume + */ + public function clearSessionCache() + { + $this->sessionCache = array(); + } + + /** + * Return error message from last failed action + * + * @return array + * @author Dmitry (dio) Levashov + **/ + public function error() + { + return $this->error; + } + + /** + * Return is uploadable that given file name + * + * @param string $name file name + * @param bool $allowUnknown + * + * @return bool + * @author Naoki Sawada + **/ + public function isUploadableByName($name, $allowUnknown = false) + { + $mimeByName = $this->mimetype($name, true); + return (($allowUnknown && $mimeByName === 'unknown') || $this->allowPutMime($mimeByName)); + } + + /** + * Return Extention/MIME Table (elFinderVolumeDriver::$mimetypes) + * + * @return array + * @author Naoki Sawada + */ + public function getMimeTable() + { + // load mime.types + if (!elFinderVolumeDriver::$mimetypesLoaded) { + elFinderVolumeDriver::loadMimeTypes(); + } + return elFinderVolumeDriver::$mimetypes; + } + + /** + * Return file extention detected by MIME type + * + * @param string $mime MIME type + * @param string $suffix Additional suffix + * + * @return string + * @author Naoki Sawada + */ + public function getExtentionByMime($mime, $suffix = '') + { + static $extTable = null; + + if (is_null($extTable)) { + $extTable = array_flip(array_unique($this->getMimeTable())); + foreach ($this->options['mimeMap'] as $pair => $_mime) { + list($ext) = explode(':', $pair); + if ($ext !== '*' && !isset($extTable[$_mime])) { + $extTable[$_mime] = $ext; + } + } + } + + if ($mime && isset($extTable[$mime])) { + return $suffix ? ($extTable[$mime] . $suffix) : $extTable[$mime]; + } + return ''; + } + + /** + * Set mimetypes allowed to display to client + * + * @param array $mimes + * + * @return void + * @author Dmitry (dio) Levashov + **/ + public function setMimesFilter($mimes) + { + if (is_array($mimes)) { + $this->onlyMimes = $mimes; + } + } + + /** + * Return root folder hash + * + * @return string + * @author Dmitry (dio) Levashov + **/ + public function root() + { + return $this->encode($this->root); + } + + /** + * Return root path + * + * @return string + * @author Naoki Sawada + **/ + public function getRootPath() + { + return $this->root; + } + + /** + * Return target path hash + * + * @param string $path + * @param string $name + * + * @author Naoki Sawada + * @return string + */ + public function getHash($path, $name = '') + { + if ($name !== '') { + $path = $this->joinPathCE($path, $name); + } + return $this->encode($path); + } + + /** + * Return decoded path of target hash + * This method do not check the stat of target + * Use method `realpath()` to do check of the stat of target + * + * @param string $hash + * + * @author Naoki Sawada + * @return string + */ + public function getPath($hash) + { + return $this->decode($hash); + } + + /** + * Return root or startPath hash + * + * @return string + * @author Dmitry (dio) Levashov + **/ + public function defaultPath() + { + return $this->encode($this->startPath ? $this->startPath : $this->root); + } + + /** + * Return volume options required by client: + * + * @param $hash + * + * @return array + * @author Dmitry (dio) Levashov + */ + public function options($hash) + { + $create = $createext = array(); + if (isset($this->archivers['create']) && is_array($this->archivers['create'])) { + foreach ($this->archivers['create'] as $m => $v) { + $create[] = $m; + $createext[$m] = $v['ext']; + } + } + $opts = array( + 'path' => $hash ? $this->path($hash) : '', + 'url' => $this->URL, + 'tmbUrl' => (!$this->imgLib && $this->options['tmbFbSelf']) ? 'self' : $this->tmbURL, + 'disabled' => $this->disabled, + 'separator' => $this->separator, + 'copyOverwrite' => intval($this->options['copyOverwrite']), + 'uploadOverwrite' => intval($this->options['uploadOverwrite']), + 'uploadMaxSize' => intval($this->uploadMaxSize), + 'uploadMaxConn' => intval($this->options['uploadMaxConn']), + 'uploadMime' => array( + 'firstOrder' => isset($this->uploadOrder[0]) ? $this->uploadOrder[0] : 'deny', + 'allow' => $this->uploadAllow, + 'deny' => $this->uploadDeny + ), + 'dispInlineRegex' => $this->options['dispInlineRegex'], + 'jpgQuality' => intval($this->options['jpgQuality']), + 'archivers' => array( + 'create' => $create, + 'extract' => isset($this->archivers['extract']) && is_array($this->archivers['extract']) ? array_keys($this->archivers['extract']) : array(), + 'createext' => $createext + ), + 'uiCmdMap' => (isset($this->options['uiCmdMap']) && is_array($this->options['uiCmdMap'])) ? $this->options['uiCmdMap'] : array(), + 'syncChkAsTs' => intval($this->options['syncChkAsTs']), + 'syncMinMs' => intval($this->options['syncMinMs']), + 'i18nFolderName' => intval($this->options['i18nFolderName']), + 'tmbCrop' => intval($this->options['tmbCrop']), + 'substituteImg' => (bool)$this->options['substituteImg'], + 'onetimeUrl' => (bool)$this->options['onetimeUrl'], + ); + if (!empty($this->options['trashHash'])) { + $opts['trashHash'] = $this->options['trashHash']; + } + if ($hash === null) { + // call from getRootStatExtra() + if (!empty($this->options['icon'])) { + $opts['icon'] = $this->options['icon']; + } + if (!empty($this->options['rootCssClass'])) { + $opts['csscls'] = $this->options['rootCssClass']; + } + if (isset($this->options['netkey'])) { + $opts['netkey'] = $this->options['netkey']; + } + } + return $opts; + } + + /** + * Get option value of this volume + * + * @param string $name target option name + * + * @return NULL|mixed target option value + * @author Naoki Sawada + */ + public function getOption($name) + { + return isset($this->options[$name]) ? $this->options[$name] : null; + } + + /** + * Get plugin values of this options + * + * @param string $name Plugin name + * + * @return NULL|array Plugin values + * @author Naoki Sawada + */ + public function getOptionsPlugin($name = '') + { + if ($name) { + return isset($this->options['plugin'][$name]) ? $this->options['plugin'][$name] : array(); + } else { + return $this->options['plugin']; + } + } + + /** + * Return true if command disabled in options + * + * @param string $cmd command name + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + public function commandDisabled($cmd) + { + return in_array($cmd, $this->disabled); + } + + /** + * Return true if mime is required mimes list + * + * @param string $mime mime type to check + * @param array $mimes allowed mime types list or not set to use client mimes list + * @param bool|null $empty what to return on empty list + * + * @return bool|null + * @author Dmitry (dio) Levashov + * @author Troex Nevelin + **/ + public function mimeAccepted($mime, $mimes = null, $empty = true) + { + $mimes = is_array($mimes) ? $mimes : $this->onlyMimes; + if (empty($mimes)) { + return $empty; + } + return $mime == 'directory' + || in_array('all', $mimes) + || in_array('All', $mimes) + || in_array($mime, $mimes) + || in_array(substr($mime, 0, strpos($mime, '/')), $mimes); + } + + /** + * Return true if voume is readable. + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + public function isReadable() + { + $stat = $this->stat($this->root); + return $stat['read']; + } + + /** + * Return true if copy from this volume allowed + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + public function copyFromAllowed() + { + return !!$this->options['copyFrom']; + } + + /** + * Return file path related to root with convert encoging + * + * @param string $hash file hash + * + * @return string + * @author Dmitry (dio) Levashov + **/ + public function path($hash) + { + return $this->convEncOut($this->_path($this->convEncIn($this->decode($hash)))); + } + + /** + * Return file real path if file exists + * + * @param string $hash file hash + * + * @return string | false + * @author Dmitry (dio) Levashov + **/ + public function realpath($hash) + { + $path = $this->decode($hash); + return $this->stat($path) ? $path : false; + } + + /** + * Return list of moved/overwrited files + * + * @return array + * @author Dmitry (dio) Levashov + **/ + public function removed() + { + if ($this->removed) { + $unsetSubdir = isset($this->sessionCache['subdirs']) ? true : false; + foreach ($this->removed as $item) { + if ($item['mime'] === 'directory') { + $path = $this->decode($item['hash']); + if ($unsetSubdir) { + unset($this->sessionCache['subdirs'][$path]); + } + if ($item['phash'] !== '') { + $parent = $this->decode($item['phash']); + unset($this->cache[$parent]); + if ($this->root === $parent) { + $this->sessionCache['rootstat'] = array(); + } + if ($unsetSubdir) { + unset($this->sessionCache['subdirs'][$parent]); + } + } + } + } + $this->removed = array_values($this->removed); + } + return $this->removed; + } + + /** + * Return list of added files + * + * @deprecated + * @return array + * @author Naoki Sawada + **/ + public function added() + { + return $this->added; + } + + /** + * Clean removed files list + * + * @return void + * @author Dmitry (dio) Levashov + **/ + public function resetRemoved() + { + $this->resetResultStat(); + } + + /** + * Clean added/removed files list + * + * @return void + **/ + public function resetResultStat() + { + $this->removed = array(); + $this->added = array(); + } + + /** + * Return file/dir hash or first founded child hash with required attr == $val + * + * @param string $hash file hash + * @param string $attr attribute name + * @param bool $val attribute value + * + * @return string|false + * @author Dmitry (dio) Levashov + **/ + public function closest($hash, $attr, $val) + { + return ($path = $this->closestByAttr($this->decode($hash), $attr, $val)) ? $this->encode($path) : false; + } + + /** + * Return file info or false on error + * + * @param string $hash file hash + * + * @return array|false + * @internal param bool $realpath add realpath field to file info + * @author Dmitry (dio) Levashov + */ + public function file($hash) + { + $file = $this->stat($this->decode($hash)); + + return ($file) ? $file : $this->setError(elFinder::ERROR_FILE_NOT_FOUND); + } + + /** + * Return folder info + * + * @param string $hash folder hash + * @param bool $resolveLink + * + * @return array|false + * @internal param bool $hidden return hidden file info + * @author Dmitry (dio) Levashov + */ + public function dir($hash, $resolveLink = false) + { + if (($dir = $this->file($hash)) == false) { + return $this->setError(elFinder::ERROR_DIR_NOT_FOUND); + } + + if ($resolveLink && !empty($dir['thash'])) { + $dir = $this->file($dir['thash']); + } + + return $dir && $dir['mime'] == 'directory' && empty($dir['hidden']) + ? $dir + : $this->setError(elFinder::ERROR_NOT_DIR); + } + + /** + * Return directory content or false on error + * + * @param string $hash file hash + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + public function scandir($hash) + { + if (($dir = $this->dir($hash)) == false) { + return false; + } + + $path = $this->decode($hash); + if ($res = $dir['read'] + ? $this->getScandir($path) + : $this->setError(elFinder::ERROR_PERM_DENIED)) { + + $dirs = null; + if ($this->sessionCaching['subdirs'] && isset($this->sessionCache['subdirs'][$path])) { + $dirs = $this->sessionCache['subdirs'][$path]; + } + if ($dirs !== null || (isset($dir['dirs']) && $dir['dirs'] != 1)) { + $_dir = $dir; + if ($dirs || $this->subdirs($hash)) { + $dir['dirs'] = 1; + } else { + unset($dir['dirs']); + } + if ($dir !== $_dir) { + $this->updateCache($path, $dir); + } + } + } + + return $res; + } + + /** + * Return dir files names list + * + * @param string $hash file hash + * @param null $intersect + * + * @return array|false + * @author Dmitry (dio) Levashov + */ + public function ls($hash, $intersect = null) + { + if (($dir = $this->dir($hash)) == false || !$dir['read']) { + return false; + } + + $list = array(); + $path = $this->decode($hash); + + $check = array(); + if ($intersect) { + $check = array_flip($intersect); + } + + foreach ($this->getScandir($path) as $stat) { + if (empty($stat['hidden']) && (!$check || isset($check[$stat['name']])) && $this->mimeAccepted($stat['mime'])) { + $list[$stat['hash']] = $stat['name']; + } + } + + return $list; + } + + /** + * Return subfolders for required folder or false on error + * + * @param string $hash folder hash or empty string to get tree from root folder + * @param int $deep subdir deep + * @param string $exclude dir hash which subfolders must be exluded from result, required to not get stat twice on cwd subfolders + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + public function tree($hash = '', $deep = 0, $exclude = '') + { + $path = $hash ? $this->decode($hash) : $this->root; + + if (($dir = $this->stat($path)) == false || $dir['mime'] != 'directory') { + return false; + } + + $dirs = $this->gettree($path, $deep > 0 ? $deep - 1 : $this->treeDeep - 1, $exclude ? $this->decode($exclude) : null); + array_unshift($dirs, $dir); + return $dirs; + } + + /** + * Return part of dirs tree from required dir up to root dir + * + * @param string $hash directory hash + * @param bool|null $lineal only lineal parents + * + * @return array|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + public function parents($hash, $lineal = false) + { + if (($current = $this->dir($hash)) == false) { + return false; + } + + $args = func_get_args(); + // checks 3rd param `$until` (elFinder >= 2.1.24) + $until = ''; + if (isset($args[2])) { + $until = $args[2]; + } + + $path = $this->decode($hash); + $tree = array(); + + while ($path && $path != $this->root) { + elFinder::checkAborted(); + $path = $this->dirnameCE($path); + if (!($stat = $this->stat($path)) || !empty($stat['hidden']) || !$stat['read']) { + return false; + } + + array_unshift($tree, $stat); + if (!$lineal) { + foreach ($this->gettree($path, 0) as $dir) { + elFinder::checkAborted(); + if (!isset($tree[$dir['hash']])) { + $tree[$dir['hash']] = $dir; + } + } + } + + if ($until && $until === $this->encode($path)) { + break; + } + } + + return $tree ? array_values($tree) : array($current); + } + + /** + * Create thumbnail for required file and return its name of false on failed + * + * @param $hash + * + * @return false|string + * @throws ImagickException + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + public function tmb($hash) + { + $path = $this->decode($hash); + $stat = $this->stat($path); + + if (isset($stat['tmb'])) { + $res = $stat['tmb'] == "1" ? $this->createTmb($path, $stat) : $stat['tmb']; + if (!$res) { + list($type) = explode('/', $stat['mime']); + $fallback = $this->options['resourcePath'] . DIRECTORY_SEPARATOR . strtolower($type) . '.png'; + if (is_file($fallback)) { + $res = $this->tmbname($stat); + if (!copy($fallback, $this->tmbPath . DIRECTORY_SEPARATOR . $res)) { + $res = false; + } + } + } + return $res; + } + return false; + } + + /** + * Return file size / total directory size + * + * @param string file hash + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + public function size($hash) + { + return $this->countSize($this->decode($hash)); + } + + /** + * Open file for reading and return file pointer + * + * @param string file hash + * + * @return Resource|false + * @author Dmitry (dio) Levashov + **/ + public function open($hash) + { + if (($file = $this->file($hash)) == false + || $file['mime'] == 'directory') { + return false; + } + + return $this->fopenCE($this->decode($hash), 'rb'); + } + + /** + * Close file pointer + * + * @param Resource $fp file pointer + * @param string $hash file hash + * + * @return void + * @author Dmitry (dio) Levashov + **/ + public function close($fp, $hash) + { + $this->fcloseCE($fp, $this->decode($hash)); + } + + /** + * Create directory and return dir info + * + * @param string $dsthash destination directory hash + * @param string $name directory name + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + public function mkdir($dsthash, $name) + { + if ($this->commandDisabled('mkdir')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if (!$this->nameAccepted($name, true)) { + return $this->setError(elFinder::ERROR_INVALID_DIRNAME); + } + + if (($dir = $this->dir($dsthash)) == false) { + return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#' . $dsthash); + } + + $path = $this->decode($dsthash); + + if (!$dir['write'] || !$this->allowCreate($path, $name, true)) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + $dst = $this->joinPathCE($path, $name); + $stat = $this->isNameExists($dst); + if (!empty($stat)) { + return $this->setError(elFinder::ERROR_EXISTS, $name); + } + $this->clearcache(); + + $mkpath = $this->convEncOut($this->_mkdir($this->convEncIn($path), $this->convEncIn($name))); + if ($mkpath) { + $this->clearstatcache(); + $this->updateSubdirsCache($path, true); + $this->updateSubdirsCache($mkpath, false); + } + + return $mkpath ? $this->stat($mkpath) : false; + } + + /** + * Create empty file and return its info + * + * @param string $dst destination directory + * @param string $name file name + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + public function mkfile($dst, $name) + { + if ($this->commandDisabled('mkfile')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if (!$this->nameAccepted($name, false)) { + return $this->setError(elFinder::ERROR_INVALID_NAME); + } + + $mimeByName = $this->mimetype($name, true); + if ($mimeByName && !$this->allowPutMime($mimeByName)) { + return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name); + } + + if (($dir = $this->dir($dst)) == false) { + return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#' . $dst); + } + + $path = $this->decode($dst); + + if (!$dir['write'] || !$this->allowCreate($path, $name, false)) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if ($this->isNameExists($this->joinPathCE($path, $name))) { + return $this->setError(elFinder::ERROR_EXISTS, $name); + } + + $this->clearcache(); + $res = false; + if ($path = $this->convEncOut($this->_mkfile($this->convEncIn($path), $this->convEncIn($name)))) { + $this->clearstatcache(); + $res = $this->stat($path); + } + return $res; + } + + /** + * Rename file and return file info + * + * @param string $hash file hash + * @param string $name new file name + * + * @return array|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + public function rename($hash, $name) + { + if ($this->commandDisabled('rename')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if (!($file = $this->file($hash))) { + return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); + } + + if ($name === $file['name']) { + return $file; + } + + if (!empty($file['locked'])) { + return $this->setError(elFinder::ERROR_LOCKED, $file['name']); + } + + $isDir = ($file['mime'] === 'directory'); + + if (!$this->nameAccepted($name, $isDir)) { + return $this->setError($isDir ? elFinder::ERROR_INVALID_DIRNAME : elFinder::ERROR_INVALID_NAME); + } + + if (!$isDir) { + $mimeByName = $this->mimetype($name, true); + if ($mimeByName && !$this->allowPutMime($mimeByName)) { + return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, $name); + } + } + + $path = $this->decode($hash); + $dir = $this->dirnameCE($path); + $stat = $this->isNameExists($this->joinPathCE($dir, $name)); + if ($stat) { + return $this->setError(elFinder::ERROR_EXISTS, $name); + } + + if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + $this->rmTmb($file); // remove old name tmbs, we cannot do this after dir move + + + if ($path = $this->convEncOut($this->_move($this->convEncIn($path), $this->convEncIn($dir), $this->convEncIn($name)))) { + $this->clearcache(); + return $this->stat($path); + } + return false; + } + + /** + * Create file copy with suffix "copy number" and return its info + * + * @param string $hash file hash + * @param string $suffix suffix to add to file name + * + * @return array|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + public function duplicate($hash, $suffix = 'copy') + { + if ($this->commandDisabled('duplicate')) { + return $this->setError(elFinder::ERROR_COPY, '#' . $hash, elFinder::ERROR_PERM_DENIED); + } + + if (($file = $this->file($hash)) == false) { + return $this->setError(elFinder::ERROR_COPY, elFinder::ERROR_FILE_NOT_FOUND); + } + + $path = $this->decode($hash); + $dir = $this->dirnameCE($path); + $name = $this->uniqueName($dir, $file['name'], sprintf($this->options['duplicateSuffix'], $suffix)); + + if (!$this->allowCreate($dir, $name, ($file['mime'] === 'directory'))) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + return ($path = $this->copy($path, $dir, $name)) == false + ? false + : $this->stat($path); + } + + /** + * Save uploaded file. + * On success return array with new file stat and with removed file hash (if existed file was replaced) + * + * @param Resource $fp file pointer + * @param string $dst destination folder hash + * @param $name + * @param string $tmpname file tmp name - required to detect mime type + * @param array $hashes exists files hash array with filename as key + * + * @return array|false + * @throws elFinderAbortException + * @internal param string $src file name + * @author Dmitry (dio) Levashov + */ + public function upload($fp, $dst, $name, $tmpname, $hashes = array()) + { + if ($this->commandDisabled('upload')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if (($dir = $this->dir($dst)) == false) { + return $this->setError(elFinder::ERROR_TRGDIR_NOT_FOUND, '#' . $dst); + } + + if (empty($dir['write'])) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if (!$this->nameAccepted($name, false)) { + return $this->setError(elFinder::ERROR_INVALID_NAME); + } + + $mimeByName = ''; + if ($this->mimeDetect === 'internal') { + $mime = $this->mimetype($tmpname, $name); + } else { + $mime = $this->mimetype($tmpname, $name); + $mimeByName = $this->mimetype($name, true); + if ($mime === 'unknown') { + $mime = $mimeByName; + } + } + + if (!$this->allowPutMime($mime) || ($mimeByName && !$this->allowPutMime($mimeByName))) { + return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME, '(' . $mime . ')'); + } + + $tmpsize = (int)sprintf('%u', filesize($tmpname)); + if ($this->uploadMaxSize > 0 && $tmpsize > $this->uploadMaxSize) { + return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE); + } + + $dstpath = $this->decode($dst); + if (isset($hashes[$name])) { + $test = $this->decode($hashes[$name]); + $file = $this->stat($test); + } else { + $test = $this->joinPathCE($dstpath, $name); + $file = $this->isNameExists($test); + } + + $this->clearcache(); + + if ($file && $file['name'] === $name) { // file exists and check filename for item ID based filesystem + if ($this->uploadOverwrite) { + if (!$file['write']) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } elseif ($file['mime'] == 'directory') { + return $this->setError(elFinder::ERROR_NOT_REPLACE, $name); + } + $this->remove($test); + } else { + $name = $this->uniqueName($dstpath, $name, '-', false); + } + } + + $stat = array( + 'mime' => $mime, + 'width' => 0, + 'height' => 0, + 'size' => $tmpsize); + + // $w = $h = 0; + if (strpos($mime, 'image') === 0 && ($s = getimagesize($tmpname))) { + $stat['width'] = $s[0]; + $stat['height'] = $s[1]; + } + // $this->clearcache(); + if (($path = $this->saveCE($fp, $dstpath, $name, $stat)) == false) { + return false; + } + + $stat = $this->stat($path); + // Try get URL + if (empty($stat['url']) && ($url = $this->getContentUrl($stat['hash']))) { + $stat['url'] = $url; + } + + return $stat; + } + + /** + * Paste files + * + * @param Object $volume source volume + * @param $src + * @param string $dst destination dir hash + * @param bool $rmSrc remove source after copy? + * @param array $hashes + * + * @return array|false + * @throws elFinderAbortException + * @internal param string $source file hash + * @author Dmitry (dio) Levashov + */ + public function paste($volume, $src, $dst, $rmSrc = false, $hashes = array()) + { + $err = $rmSrc ? elFinder::ERROR_MOVE : elFinder::ERROR_COPY; + + if ($this->commandDisabled('paste')) { + return $this->setError($err, '#' . $src, elFinder::ERROR_PERM_DENIED); + } + + if (($file = $volume->file($src, $rmSrc)) == false) { + return $this->setError($err, '#' . $src, elFinder::ERROR_FILE_NOT_FOUND); + } + + $name = $file['name']; + $errpath = $volume->path($file['hash']); + + if (($dir = $this->dir($dst)) == false) { + return $this->setError($err, $errpath, elFinder::ERROR_TRGDIR_NOT_FOUND, '#' . $dst); + } + + if (!$dir['write'] || !$file['read']) { + return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); + } + + $destination = $this->decode($dst); + + if (($test = $volume->closest($src, $rmSrc ? 'locked' : 'read', $rmSrc))) { + return $rmSrc + ? $this->setError($err, $errpath, elFinder::ERROR_LOCKED, $volume->path($test)) + : $this->setError($err, $errpath, empty($file['thash']) ? elFinder::ERROR_PERM_DENIED : elFinder::ERROR_MKOUTLINK); + } + + if (isset($hashes[$name])) { + $test = $this->decode($hashes[$name]); + $stat = $this->stat($test); + } else { + $test = $this->joinPathCE($destination, $name); + $stat = $this->isNameExists($test); + } + $this->clearcache(); + $dstDirExists = false; + if ($stat && $stat['name'] === $name) { // file exists and check filename for item ID based filesystem + if ($this->options['copyOverwrite']) { + // do not replace file with dir or dir with file + if (!$this->isSameType($file['mime'], $stat['mime'])) { + return $this->setError(elFinder::ERROR_NOT_REPLACE, $this->path($stat['hash'])); + } + // existed file is not writable + if (empty($stat['write'])) { + return $this->setError($err, $errpath, elFinder::ERROR_PERM_DENIED); + } + if ($this->options['copyJoin']) { + if (!empty($stat['locked'])) { + return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash'])); + } + } else { + // existed file locked or has locked child + if (($locked = $this->closestByAttr($test, 'locked', true))) { + $stat = $this->stat($locked); + return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash'])); + } + } + // target is entity file of alias + if ($volume === $this && ((isset($file['target']) && $test == $file['target']) || $test == $this->decode($src))) { + return $this->setError(elFinder::ERROR_REPLACE, $errpath); + } + // remove existed file + if (!$this->options['copyJoin'] || $stat['mime'] !== 'directory') { + if (!$this->remove($test)) { + return $this->setError(elFinder::ERROR_REPLACE, $this->path($stat['hash'])); + } + } else if ($stat['mime'] === 'directory') { + $dstDirExists = true; + } + } else { + $name = $this->uniqueName($destination, $name, ' ', false); + } + } + + // copy/move inside current volume + if ($volume === $this) { // changing == operand to === fixes issue #1285 - Paul Canning 24/03/2016 + $source = $this->decode($src); + // do not copy into itself + if ($this->inpathCE($destination, $source)) { + return $this->setError(elFinder::ERROR_COPY_ITSELF, $errpath); + } + $rmDir = false; + if ($rmSrc) { + if ($dstDirExists) { + $rmDir = true; + $method = 'copy'; + } else { + $method = 'move'; + } + } else { + $method = 'copy'; + } + $this->clearcache(); + if ($res = ($path = $this->$method($source, $destination, $name)) ? $this->stat($path) : false) { + if ($rmDir) { + $this->remove($source); + } + } else { + return false; + } + } else { + // copy/move from another volume + if (!$this->options['copyTo'] || !$volume->copyFromAllowed()) { + return $this->setError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED); + } + + $this->error = array(); + if (($path = $this->copyFrom($volume, $src, $destination, $name)) == false) { + return false; + } + + if ($rmSrc && !$this->error()) { + if (!$volume->rm($src)) { + if ($volume->file($src)) { + $this->addError(elFinder::ERROR_RM_SRC); + } else { + $this->removed[] = $file; + } + } + } + $res = $this->stat($path); + } + return $res; + } + + /** + * Return path info array to archive of target items + * + * @param array $hashes + * + * @return array|false + * @throws Exception + * @author Naoki Sawada + */ + public function zipdl($hashes) + { + if ($this->commandDisabled('zipdl')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + $archivers = $this->getArchivers(); + $cmd = null; + if (!$archivers || empty($archivers['create'])) { + return false; + } + $archivers = $archivers['create']; + if (!$archivers) { + return false; + } + foreach (array('zip', 'tgz') as $ext) { + $mime = $this->mimetype('file.' . $ext, true); + if (isset($archivers[$mime])) { + $cmd = $archivers[$mime]; + break; + } + } + if (!$cmd) { + $cmd = array_shift($archivers); + if (!empty($ext)) { + $mime = $this->mimetype('file.' . $ext, true); + } + } + $file = $mime = ''; + $ext = $cmd['ext']; + $res = false; + $mixed = false; + $hashes = array_values($hashes); + $dirname = dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[0]))); + $cnt = count($hashes); + if ($cnt > 1) { + for ($i = 1; $i < $cnt; $i++) { + if ($dirname !== dirname(str_replace($this->separator, DIRECTORY_SEPARATOR, $this->path($hashes[$i])))) { + $mixed = true; + break; + } + } + } + if ($mixed || $this->root == $this->dirnameCE($this->decode($hashes[0]))) { + $prefix = $this->rootName; + } else { + $prefix = basename($dirname); + } + if ($dir = $this->getItemsInHand($hashes)) { + $tmppre = (substr(PHP_OS, 0, 3) === 'WIN') ? 'zdl' : 'elfzdl'; + $pdir = dirname($dir); + // garbage collection (expire 2h) + register_shutdown_function(array('elFinder', 'GlobGC'), $pdir . DIRECTORY_SEPARATOR . $tmppre . '*', 7200); + $files = self::localScandir($dir); + if ($files && ($arc = tempnam($dir, $tmppre))) { + unlink($arc); + $arc = $arc . '.' . $ext; + $name = basename($arc); + if ($arc = $this->makeArchive($dir, $files, $name, $cmd)) { + $file = tempnam($pdir, $tmppre); + unlink($file); + $res = rename($arc, $file); + $this->rmdirRecursive($dir); + } + } + } + return $res ? array('path' => $file, 'ext' => $ext, 'mime' => $mime, 'prefix' => $prefix) : false; + } + + /** + * Return file contents + * + * @param string $hash file hash + * + * @return string|false + * @author Dmitry (dio) Levashov + **/ + public function getContents($hash) + { + $file = $this->file($hash); + + if (!$file) { + return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); + } + + if ($file['mime'] == 'directory') { + return $this->setError(elFinder::ERROR_NOT_FILE); + } + + if (!$file['read']) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if ($this->getMaxSize > 0 && $file['size'] > $this->getMaxSize) { + return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE); + } + + return $file['size'] ? $this->_getContents($this->convEncIn($this->decode($hash), true)) : ''; + } + + /** + * Put content in text file and return file info. + * + * @param string $hash file hash + * @param string $content new file content + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + public function putContents($hash, $content) + { + if ($this->commandDisabled('edit')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + $path = $this->decode($hash); + + if (!($file = $this->file($hash))) { + return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); + } + + if (!$file['write']) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + // check data cheme + if (preg_match('~^\0data:(.+?/.+?);base64,~', $content, $m)) { + $dMime = $m[1]; + if ($file['size'] > 0 && $dMime !== $file['mime']) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + $content = base64_decode(substr($content, strlen($m[0]))); + } + + // check MIME + $name = $this->basenameCE($path); + $mime = ''; + $mimeByName = $this->mimetype($name, true); + if ($this->mimeDetect !== 'internal') { + if ($tp = $this->tmpfile()) { + fwrite($tp, $content); + $info = stream_get_meta_data($tp); + $filepath = $info['uri']; + $mime = $this->mimetype($filepath, $name); + fclose($tp); + } + } + if (!$this->allowPutMime($mimeByName) || ($mime && !$this->allowPutMime($mime))) { + return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME); + } + + $this->clearcache(); + $res = false; + if ($this->convEncOut($this->_filePutContents($this->convEncIn($path), $content))) { + $this->clearstatcache(); + $res = $this->stat($path); + } + return $res; + } + + /** + * Extract files from archive + * + * @param string $hash archive hash + * @param null $makedir + * + * @return array|bool + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + */ + public function extract($hash, $makedir = null) + { + if ($this->commandDisabled('extract')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if (($file = $this->file($hash)) == false) { + return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); + } + + $archiver = isset($this->archivers['extract'][$file['mime']]) + ? $this->archivers['extract'][$file['mime']] + : array(); + + if (!$archiver) { + return $this->setError(elFinder::ERROR_NOT_ARCHIVE); + } + + $path = $this->decode($hash); + $parent = $this->stat($this->dirnameCE($path)); + + if (!$file['read'] || !$parent['write']) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + $this->clearcache(); + $this->extractToNewdir = is_null($makedir) ? 'auto' : (bool)$makedir; + + if ($path = $this->convEncOut($this->_extract($this->convEncIn($path), $archiver))) { + if (is_array($path)) { + foreach ($path as $_k => $_p) { + $path[$_k] = $this->stat($_p); + } + } else { + $path = $this->stat($path); + } + return $path; + } else { + return false; + } + } + + /** + * Add files to archive + * + * @param $hashes + * @param $mime + * @param string $name + * + * @return array|bool + * @throws Exception + */ + public function archive($hashes, $mime, $name = '') + { + if ($this->commandDisabled('archive')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if ($name !== '' && !$this->nameAccepted($name, false)) { + return $this->setError(elFinder::ERROR_INVALID_NAME); + } + + $archiver = isset($this->archivers['create'][$mime]) + ? $this->archivers['create'][$mime] + : array(); + + if (!$archiver) { + return $this->setError(elFinder::ERROR_ARCHIVE_TYPE); + } + + $files = array(); + $useRemoteArchive = !empty($this->options['useRemoteArchive']); + + $dir = ''; + foreach ($hashes as $hash) { + if (($file = $this->file($hash)) == false) { + return $this->setError(elFinder::ERROR_FILE_NOT_FOUND, '#' . $hash); + } + if (!$file['read']) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + $path = $this->decode($hash); + if ($dir === '') { + $dir = $this->dirnameCE($path); + $stat = $this->stat($dir); + if (!$stat['write']) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + } + + $files[] = $useRemoteArchive ? $hash : $this->basenameCE($path); + } + + if ($name === '') { + $name = count($files) == 1 ? $files[0] : 'Archive'; + } else { + $name = str_replace(array('/', '\\'), '_', preg_replace('/\.' . preg_quote($archiver['ext'], '/') . '$/i', '', $name)); + } + $name .= '.' . $archiver['ext']; + $name = $this->uniqueName($dir, $name, ''); + $this->clearcache(); + if ($useRemoteArchive) { + return ($path = $this->remoteArchive($files, $name, $archiver)) ? $this->stat($path) : false; + } else { + return ($path = $this->convEncOut($this->_archive($this->convEncIn($dir), $this->convEncIn($files), $this->convEncIn($name), $archiver))) ? $this->stat($path) : false; + } + } + + /** + * Create an archive from remote items + * + * @param array $hashes files hashes list + * @param string $name archive name + * @param array $arc archiver options + * + * @return string|boolean path of created archive + * @throws Exception + */ + protected function remoteArchive($hashes, $name, $arc) + { + $resPath = false; + $file0 = $this->file($hashes[0]); + if ($file0 && ($dir = $this->getItemsInHand($hashes))) { + $files = self::localScandir($dir); + if ($files) { + if ($arc = $this->makeArchive($dir, $files, $name, $arc)) { + if ($fp = fopen($arc, 'rb')) { + $path = $this->decode($file0['phash']); + $stat = array(); + $resPath = $this->saveCE($fp, $path, $name, $stat); + fclose($fp); + } + } + } + $this->rmdirRecursive($dir); + } + return $resPath; + } + + /** + * Resize image + * + * @param string $hash image file + * @param int $width new width + * @param int $height new height + * @param int $x X start poistion for crop + * @param int $y Y start poistion for crop + * @param string $mode action how to mainpulate image + * @param string $bg background color + * @param int $degree rotete degree + * @param int $jpgQuality JEPG quality (1-100) + * + * @return array|false + * @throws ImagickException + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + * @author nao-pon + * @author Troex Nevelin + */ + public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0, $jpgQuality = null) + { + if ($this->commandDisabled('resize')) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if ($mode === 'rotate' && $degree == 0) { + return array('losslessRotate' => ($this->procExec(ELFINDER_EXIFTRAN_PATH . ' -h') === 0 || $this->procExec(ELFINDER_JPEGTRAN_PATH . ' -version') === 0)); + } + + if (($file = $this->file($hash)) == false) { + return $this->setError(elFinder::ERROR_FILE_NOT_FOUND); + } + + if (!$file['write'] || !$file['read']) { + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + $path = $this->decode($hash); + + $work_path = $this->getWorkFile($this->encoding ? $this->convEncIn($path, true) : $path); + + if (!$work_path || !is_writable($work_path)) { + if ($work_path && $path !== $work_path && is_file($work_path)) { + unlink($work_path); + } + return $this->setError(elFinder::ERROR_PERM_DENIED); + } + + if ($this->imgLib !== 'imagick' && $this->imgLib !== 'convert') { + if (elFinder::isAnimationGif($work_path)) { + return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE); + } + } + + if (elFinder::isAnimationPng($work_path)) { + return $this->setError(elFinder::ERROR_UNSUPPORT_TYPE); + } + + switch ($mode) { + + case 'propresize': + $result = $this->imgResize($work_path, $width, $height, true, true, null, $jpgQuality); + break; + + case 'crop': + $result = $this->imgCrop($work_path, $width, $height, $x, $y, null, $jpgQuality); + break; + + case 'fitsquare': + $result = $this->imgSquareFit($work_path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor']), null, $jpgQuality); + break; + + case 'rotate': + $result = $this->imgRotate($work_path, $degree, ($bg ? $bg : $this->options['bgColorFb']), null, $jpgQuality); + break; + + default: + $result = $this->imgResize($work_path, $width, $height, false, true, null, $jpgQuality); + break; + } + + $ret = false; + if ($result) { + $this->rmTmb($file); + $this->clearstatcache(); + $fstat = stat($work_path); + $imgsize = getimagesize($work_path); + if ($path !== $work_path) { + $file['size'] = $fstat['size']; + $file['ts'] = $fstat['mtime']; + if ($imgsize) { + $file['width'] = $imgsize[0]; + $file['height'] = $imgsize[1]; + } + if ($fp = fopen($work_path, 'rb')) { + $ret = $this->saveCE($fp, $this->dirnameCE($path), $this->basenameCE($path), $file); + fclose($fp); + } + } else { + $ret = true; + } + if ($ret) { + $this->clearcache(); + $ret = $this->stat($path); + if ($imgsize) { + $ret['width'] = $imgsize[0]; + $ret['height'] = $imgsize[1]; + } + } + } + if ($path !== $work_path) { + is_file($work_path) && unlink($work_path); + } + + return $ret; + } + + /** + * Remove file/dir + * + * @param string $hash file hash + * + * @return bool + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + public function rm($hash) + { + return $this->commandDisabled('rm') + ? $this->setError(elFinder::ERROR_PERM_DENIED) + : $this->remove($this->decode($hash)); + } + + /** + * Search files + * + * @param string $q search string + * @param array $mimes + * @param null $hash + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + public function search($q, $mimes, $hash = null) + { + $res = array(); + $matchMethod = null; + $args = func_get_args(); + if (!empty($args[3])) { + $matchMethod = 'searchMatch' . $args[3]; + if (!is_callable(array($this, $matchMethod))) { + return array(); + } + } + + $dir = null; + if ($hash) { + $dir = $this->decode($hash); + $stat = $this->stat($dir); + if (!$stat || $stat['mime'] !== 'directory' || !$stat['read']) { + $q = ''; + } + } + if ($mimes && $this->onlyMimes) { + $mimes = array_intersect($mimes, $this->onlyMimes); + if (!$mimes) { + $q = ''; + } + } + $this->searchStart = time(); + + $qs = preg_split('/"([^"]+)"| +/', $q, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $query = $excludes = array(); + foreach ($qs as $_q) { + $_q = trim($_q); + if ($_q !== '') { + if ($_q[0] === '-') { + if (isset($_q[1])) { + $excludes[] = substr($_q, 1); + } + } else { + $query[] = $_q; + } + } + } + if (!$query) { + $q = ''; + } else { + $q = join(' ', $query); + $this->doSearchCurrentQuery = array( + 'q' => $q, + 'excludes' => $excludes, + 'matchMethod' => $matchMethod + ); + } + + if ($q === '' || $this->commandDisabled('search')) { + return $res; + } + + // valided regex $this->options['searchExDirReg'] + if ($this->options['searchExDirReg']) { + if (false === preg_match($this->options['searchExDirReg'], '')) { + $this->options['searchExDirReg'] = ''; + } + } + + // check the leaf root too + if (!$mimes && (is_null($dir) || $dir == $this->root)) { + $rootStat = $this->stat($this->root); + if (!empty($rootStat['phash'])) { + if ($this->stripos($rootStat['name'], $q) !== false) { + $res = array($rootStat); + } + } + } + + return array_merge($res, $this->doSearch(is_null($dir) ? $this->root : $dir, $q, $mimes)); + } + + /** + * Return image dimensions + * + * @param string $hash file hash + * + * @return array|string + * @author Dmitry (dio) Levashov + **/ + public function dimensions($hash) + { + if (($file = $this->file($hash)) == false) { + return false; + } + // Throw additional parameters for some drivers + if (func_num_args() > 1) { + $args = func_get_arg(1); + } else { + $args = array(); + } + return $this->convEncOut($this->_dimensions($this->convEncIn($this->decode($hash)), $file['mime'], $args)); + } + + /** + * Return has subdirs + * + * @param string $hash file hash + * + * @return bool + * @author Naoki Sawada + **/ + public function subdirs($hash) + { + return (bool)$this->subdirsCE($this->decode($hash)); + } + + /** + * Return content URL (for netmout volume driver) + * If file.url == 1 requests from JavaScript client with XHR + * + * @param string $hash file hash + * @param array $options options array + * + * @return boolean|string + * @author Naoki Sawada + */ + public function getContentUrl($hash, $options = array()) + { + if (($file = $this->file($hash)) === false) { + return false; + } + if (!empty($options['onetime']) && $this->options['onetimeUrl']) { + if (is_callable($this->options['onetimeUrl'])) { + return call_user_func_array($this->options['onetimeUrl'], array($file, $options, $this)); + } else { + $ret = false; + if ($tmpdir = elFinder::getStaticVar('commonTempPath')) { + if ($source = $this->open($hash)) { + if ($_dat = tempnam($tmpdir, 'ELF')) { + $token = md5($_dat . session_id()); + $dat = $tmpdir . DIRECTORY_SEPARATOR . 'ELF' . $token; + if (rename($_dat, $dat)) { + $info = stream_get_meta_data($source); + if (!empty($info['uri'])) { + $tmp = $info['uri']; + } else { + $tmp = tempnam($tmpdir, 'ELF'); + if ($dest = fopen($tmp, 'wb')) { + if (!stream_copy_to_stream($source, $dest)) { + $tmp = false; + } + fclose($dest); + } + } + $this->close($source, $hash); + if ($tmp) { + $info = array( + 'file' => base64_encode($tmp), + 'name' => $file['name'], + 'mime' => $file['mime'], + 'ts' => $file['ts'] + ); + if (file_put_contents($dat, json_encode($info))) { + $ret = elFinder::getConnectorUrl() . '?cmd=file&onetime=1&target=' . $token; + + } + } + if (!$ret) { + unlink($dat); + } + } else { + unlink($_dat); + } + } + } + } + return $ret; + } + } + if (empty($file['url']) && $this->URL) { + $path = str_replace($this->separator, '/', substr($this->decode($hash), strlen(rtrim($this->root, '/' . $this->separator)) + 1)); + if ($this->encoding) { + $path = $this->convEncIn($path, true); + } + $path = str_replace('%2F', '/', rawurlencode($path)); + return $this->URL . $path; + } else { + $ret = false; + if ($file['url'] && $file['url'] != 1) { + return $file['url']; + } else if (!empty($options['temporary']) && ($tempInfo = $this->getTempLinkInfo('temp_' . md5($hash . session_id())))) { + if ($source = $this->open($hash)) { + if ($dest = fopen($tempInfo['path'], 'wb')) { + if (stream_copy_to_stream($source, $dest)) { + $ret = $tempInfo['url'] . '?' . rawurlencode($file['name']); + } + fclose($dest); + } + $this->close($source, $hash); + } + } + return $ret; + } + } + + /** + * Get temporary contents link infomation + * + * @param string $name + * + * @return boolean|array + * @author Naoki Sawada + */ + public function getTempLinkInfo($name = null) + { + if ($this->tmpLinkPath) { + if (!$name) { + $name = 'temp_' . md5($_SERVER['REMOTE_ADDR'] . (string)microtime(true)); + } else if (substr($name, 0, 5) !== 'temp_') { + $name = 'temp_' . $name; + } + register_shutdown_function(array('elFinder', 'GlobGC'), $this->tmpLinkPath . DIRECTORY_SEPARATOR . 'temp_*', elFinder::$tmpLinkLifeTime); + return array( + 'path' => $path = $this->tmpLinkPath . DIRECTORY_SEPARATOR . $name, + 'url' => $this->tmpLinkUrl . '/' . $name + ); + } + return false; + } + + /** + * Get URL of substitute image by request args `substitute` or 4th argument $maxSize + * + * @param string $target Target hash + * @param array $srcSize Size info array [width, height] + * @param resource $srcfp Source file file pointer + * @param integer $maxSize Maximum pixel of substitute image + * + * @return boolean + * @throws ImagickException + * @throws elFinderAbortException + */ + public function getSubstituteImgLink($target, $srcSize, $srcfp = null, $maxSize = null) + { + $url = false; + if (!$maxSize) { + $args = elFinder::$currentArgs; + if (!empty($args['substitute'])) { + $maxSize = $args['substitute']; + } + } + if ($maxSize && $srcSize[0] && $srcSize[1]) { + if ($this->getOption('substituteImg')) { + $maxSize = intval($maxSize); + $zoom = min(($maxSize / $srcSize[0]), ($maxSize / $srcSize[1])); + if ($zoom < 1) { + $width = round($srcSize[0] * $zoom); + $height = round($srcSize[1] * $zoom); + $jpgQuality = 50; + $preserveExif = false; + $unenlarge = true; + $checkAnimated = true; + if (!$srcfp) { + elFinder::checkAborted(); + $srcfp = $this->open($target); + } + if ($srcfp && ($tempLink = $this->getTempLinkInfo())) { + elFinder::checkAborted(); + $dest = fopen($tempLink['path'], 'wb'); + if ($dest && stream_copy_to_stream($srcfp, $dest)) { + fclose($dest); + if ($this->imageUtil('resize', $tempLink['path'], compact('width', 'height', 'jpgQuality', 'preserveExif', 'unenlarge', 'checkAnimated'))) { + $url = $tempLink['url']; + // set expire to 1 min left + touch($tempLink['path'], time() - elFinder::$tmpLinkLifeTime + 60); + } else { + unlink($tempLink['path']); + } + } + $this->close($srcfp, $target); + } + } + } + } + + return $url; + } + + /** + * Return temp path + * + * @return string + * @author Naoki Sawada + */ + public function getTempPath() + { + $tempPath = null; + if (isset($this->tmpPath) && $this->tmpPath && is_writable($this->tmpPath)) { + $tempPath = $this->tmpPath; + } else if (isset($this->tmp) && $this->tmp && is_writable($this->tmp)) { + $tempPath = $this->tmp; + } else if (elFinder::getStaticVar('commonTempPath') && is_writable(elFinder::getStaticVar('commonTempPath'))) { + $tempPath = elFinder::getStaticVar('commonTempPath'); + } else if (function_exists('sys_get_temp_dir')) { + $tempPath = sys_get_temp_dir(); + } else if ($this->tmbPathWritable) { + $tempPath = $this->tmbPath; + } + if ($tempPath && DIRECTORY_SEPARATOR !== '/') { + $tempPath = str_replace('/', DIRECTORY_SEPARATOR, $tempPath); + } + return $tempPath; + } + + /** + * (Make &) Get upload taget dirctory hash + * + * @param string $baseTargetHash + * @param string $path + * @param array $result + * + * @return boolean|string + * @author Naoki Sawada + */ + public function getUploadTaget($baseTargetHash, $path, & $result) + { + $base = $this->decode($baseTargetHash); + $targetHash = $baseTargetHash; + $path = ltrim($path, $this->separator); + $dirs = explode($this->separator, $path); + array_pop($dirs); + foreach ($dirs as $dir) { + $targetPath = $this->joinPathCE($base, $dir); + if (!$_realpath = $this->realpath($this->encode($targetPath))) { + if ($stat = $this->mkdir($targetHash, $dir)) { + $result['added'][] = $stat; + $targetHash = $stat['hash']; + $base = $this->decode($targetHash); + } else { + return false; + } + } else { + $targetHash = $this->encode($_realpath); + if ($this->dir($targetHash)) { + $base = $this->decode($targetHash); + } else { + return false; + } + } + } + return $targetHash; + } + + /** + * Return this uploadMaxSize value + * + * @return integer + * @author Naoki Sawada + */ + public function getUploadMaxSize() + { + return $this->uploadMaxSize; + } + + public function setUploadOverwrite($var) + { + $this->uploadOverwrite = (bool)$var; + } + + /** + * Image file utility + * + * @param string $mode 'resize', 'rotate', 'propresize', 'crop', 'fitsquare' + * @param string $src Image file local path + * @param array $options excute options + * + * @return bool + * @throws ImagickException + * @throws elFinderAbortException + * @author Naoki Sawada + */ + public function imageUtil($mode, $src, $options = array()) + { + if (!isset($options['jpgQuality'])) { + $options['jpgQuality'] = intval($this->options['jpgQuality']); + } + if (!isset($options['bgcolor'])) { + $options['bgcolor'] = '#ffffff'; + } + if (!isset($options['bgColorFb'])) { + $options['bgColorFb'] = $this->options['bgColorFb']; + } + + // check 'width' ,'height' + if (in_array($mode, array('resize', 'propresize', 'crop', 'fitsquare'))) { + if (empty($options['width']) || empty($options['height'])) { + return false; + } + } + + if (!empty($options['checkAnimated'])) { + if ($this->imgLib !== 'imagick' && $this->imgLib !== 'convert') { + if (elFinder::isAnimationGif($src)) { + return false; + } + } + if (elFinder::isAnimationPng($src)) { + return false; + } + } + + switch ($mode) { + case 'rotate': + if (empty($options['degree'])) { + return true; + } + return (bool)$this->imgRotate($src, $options['degree'], $options['bgColorFb'], null, $options['jpgQuality']); + + case 'resize': + return (bool)$this->imgResize($src, $options['width'], $options['height'], false, true, null, $options['jpgQuality'], $options); + + case 'propresize': + return (bool)$this->imgResize($src, $options['width'], $options['height'], true, true, null, $options['jpgQuality'], $options); + + case 'crop': + if (isset($options['x']) && isset($options['y'])) { + return (bool)$this->imgCrop($src, $options['width'], $options['height'], $options['x'], $options['y'], null, $options['jpgQuality']); + } + break; + + case 'fitsquare': + return (bool)$this->imgSquareFit($src, $options['width'], $options['height'], 'center', 'middle', $options['bgcolor'], null, $options['jpgQuality']); + + } + return false; + } + + /** + * Convert Video To Image by ffmpeg + * + * @param string $file video source file path + * @param array $stat file stat array + * @param object $self volume driver object + * @param int $ss start seconds + * + * @return bool + * @throws elFinderAbortException + * @author Naoki Sawada + */ + public function ffmpegToImg($file, $stat, $self, $ss = null) + { + $name = basename($file); + $path = dirname($file); + $tmp = $path . DIRECTORY_SEPARATOR . md5($name); + // register auto delete on shutdown + $GLOBALS['elFinderTempFiles'][$tmp] = true; + if (rename($file, $tmp)) { + if ($ss === null) { + // specific start time by file name (xxx^[sec].[extention] - video^3.mp4) + if (preg_match('/\^(\d+(?:\.\d+)?)\.[^.]+$/', $stat['name'], $_m)) { + $ss = $_m[1]; + } else { + $ss = $this->options['tmbVideoConvSec']; + } + } + $cmd = sprintf(ELFINDER_FFMPEG_PATH . ' -i %s -ss 00:00:%.3f -vframes 1 -f image2 %s', escapeshellarg($tmp), $ss, escapeshellarg($file)); + $r = ($this->procExec($cmd) === 0); + clearstatcache(); + if ($r && $ss > 0 && !file_exists($file)) { + // Retry by half of $ss + $ss = max(intval($ss / 2), 0); + rename($tmp, $file); + $r = $this->ffmpegToImg($file, $stat, $self, $ss); + } else { + unlink($tmp); + } + return $r; + } + return false; + } + + /** + * Creates a temporary file and return file pointer + * + * @return resource|boolean + */ + public function tmpfile() + { + if ($tmp = $this->getTempFile()) { + return fopen($tmp, 'wb'); + } + return false; + } + + /** + * Save error message + * + * @param array error + * + * @return boolean false + * @author Naoki Sawada + **/ + protected function setError() + { + $this->error = array(); + $this->addError(func_get_args()); + return false; + } + + /** + * Add error message + * + * @param array error + * + * @return false + * @author Dmitry(dio) Levashov + **/ + protected function addError() + { + foreach (func_get_args() as $err) { + if (is_array($err)) { + $this->error = array_merge($this->error, $err); + } else { + $this->error[] = $err; + } + } + return false; + } + + /*********************************************************************/ + /* FS API */ + /*********************************************************************/ + + /***************** server encoding support *******************/ + + /** + * Return parent directory path (with convert encoding) + * + * @param string $path file path + * + * @return string + * @author Naoki Sawada + **/ + protected function dirnameCE($path) + { + $dirname = (!$this->encoding) ? $this->_dirname($path) : $this->convEncOut($this->_dirname($this->convEncIn($path))); + // check to infinite loop prevention + return ($dirname != $path) ? $dirname : ''; + } + + /** + * Return file name (with convert encoding) + * + * @param string $path file path + * + * @return string + * @author Naoki Sawada + **/ + protected function basenameCE($path) + { + return (!$this->encoding) ? $this->_basename($path) : $this->convEncOut($this->_basename($this->convEncIn($path))); + } + + /** + * Join dir name and file name and return full path. (with convert encoding) + * Some drivers (db) use int as path - so we give to concat path to driver itself + * + * @param string $dir dir path + * @param string $name file name + * + * @return string + * @author Naoki Sawada + **/ + protected function joinPathCE($dir, $name) + { + return (!$this->encoding) ? $this->_joinPath($dir, $name) : $this->convEncOut($this->_joinPath($this->convEncIn($dir), $this->convEncIn($name))); + } + + /** + * Return normalized path (with convert encoding) + * + * @param string $path file path + * + * @return string + * @author Naoki Sawada + **/ + protected function normpathCE($path) + { + return (!$this->encoding) ? $this->_normpath($path) : $this->convEncOut($this->_normpath($this->convEncIn($path))); + } + + /** + * Return file path related to root dir (with convert encoding) + * + * @param string $path file path + * + * @return string + * @author Naoki Sawada + **/ + protected function relpathCE($path) + { + return (!$this->encoding) ? $this->_relpath($path) : $this->convEncOut($this->_relpath($this->convEncIn($path))); + } + + /** + * Convert path related to root dir into real path (with convert encoding) + * + * @param string $path rel file path + * + * @return string + * @author Naoki Sawada + **/ + protected function abspathCE($path) + { + return (!$this->encoding) ? $this->_abspath($path) : $this->convEncOut($this->_abspath($this->convEncIn($path))); + } + + /** + * Return true if $path is children of $parent (with convert encoding) + * + * @param string $path path to check + * @param string $parent parent path + * + * @return bool + * @author Naoki Sawada + **/ + protected function inpathCE($path, $parent) + { + return (!$this->encoding) ? $this->_inpath($path, $parent) : $this->convEncOut($this->_inpath($this->convEncIn($path), $this->convEncIn($parent))); + } + + /** + * Open file and return file pointer (with convert encoding) + * + * @param string $path file path + * @param string $mode + * + * @return false|resource + * @internal param bool $write open file for writing + * @author Naoki Sawada + */ + protected function fopenCE($path, $mode = 'rb') + { + return (!$this->encoding) ? $this->_fopen($path, $mode) : $this->convEncOut($this->_fopen($this->convEncIn($path), $mode)); + } + + /** + * Close opened file (with convert encoding) + * + * @param resource $fp file pointer + * @param string $path file path + * + * @return bool + * @author Naoki Sawada + **/ + protected function fcloseCE($fp, $path = '') + { + return (!$this->encoding) ? $this->_fclose($fp, $path) : $this->convEncOut($this->_fclose($fp, $this->convEncIn($path))); + } + + /** + * Create new file and write into it from file pointer. (with convert encoding) + * Return new file path or false on error. + * + * @param resource $fp file pointer + * @param string $dir target dir path + * @param string $name file name + * @param array $stat file stat (required by some virtual fs) + * + * @return bool|string + * @author Naoki Sawada + **/ + protected function saveCE($fp, $dir, $name, $stat) + { + $res = (!$this->encoding) ? $this->_save($fp, $dir, $name, $stat) : $this->convEncOut($this->_save($fp, $this->convEncIn($dir), $this->convEncIn($name), $this->convEncIn($stat))); + if ($res !== false) { + $this->clearstatcache(); + } + return $res; + } + + /** + * Return true if path is dir and has at least one childs directory (with convert encoding) + * + * @param string $path dir path + * + * @return bool + * @author Naoki Sawada + **/ + protected function subdirsCE($path) + { + if ($this->sessionCaching['subdirs']) { + if (isset($this->sessionCache['subdirs'][$path]) && !$this->isMyReload()) { + return $this->sessionCache['subdirs'][$path]; + } + } + $hasdir = (bool)((!$this->encoding) ? $this->_subdirs($path) : $this->convEncOut($this->_subdirs($this->convEncIn($path)))); + $this->updateSubdirsCache($path, $hasdir); + return $hasdir; + } + + /** + * Return files list in directory (with convert encoding) + * + * @param string $path dir path + * + * @return array + * @author Naoki Sawada + **/ + protected function scandirCE($path) + { + return (!$this->encoding) ? $this->_scandir($path) : $this->convEncOut($this->_scandir($this->convEncIn($path))); + } + + /** + * Create symlink (with convert encoding) + * + * @param string $source file to link to + * @param string $targetDir folder to create link in + * @param string $name symlink name + * + * @return bool + * @author Naoki Sawada + **/ + protected function symlinkCE($source, $targetDir, $name) + { + return (!$this->encoding) ? $this->_symlink($source, $targetDir, $name) : $this->convEncOut($this->_symlink($this->convEncIn($source), $this->convEncIn($targetDir), $this->convEncIn($name))); + } + + /***************** paths *******************/ + + /** + * Encode path into hash + * + * @param string file path + * + * @return string + * @author Dmitry (dio) Levashov + * @author Troex Nevelin + **/ + protected function encode($path) + { + if ($path !== '') { + + // cut ROOT from $path for security reason, even if hacker decodes the path he will not know the root + $p = $this->relpathCE($path); + // if reqesting root dir $path will be empty, then assign '/' as we cannot leave it blank for crypt + if ($p === '') { + $p = $this->separator; + } + // change separator + if ($this->separatorForHash) { + $p = str_replace($this->separator, $this->separatorForHash, $p); + } + // TODO crypt path and return hash + $hash = $this->crypt($p); + // hash is used as id in HTML that means it must contain vaild chars + // make base64 html safe and append prefix in begining + $hash = strtr(base64_encode($hash), '+/=', '-_.'); + // remove dots '.' at the end, before it was '=' in base64 + $hash = rtrim($hash, '.'); + // append volume id to make hash unique + return $this->id . $hash; + } + //TODO: Add return statement here + } + + /** + * Decode path from hash + * + * @param string file hash + * + * @return string + * @author Dmitry (dio) Levashov + * @author Troex Nevelin + **/ + protected function decode($hash) + { + if (strpos($hash, $this->id) === 0) { + // cut volume id after it was prepended in encode + $h = substr($hash, strlen($this->id)); + // replace HTML safe base64 to normal + $h = base64_decode(strtr($h, '-_.', '+/=')); + // TODO uncrypt hash and return path + $path = $this->uncrypt($h); + // change separator + if ($this->separatorForHash) { + $path = str_replace($this->separatorForHash, $this->separator, $path); + } + // append ROOT to path after it was cut in encode + return $this->abspathCE($path);//$this->root.($path === $this->separator ? '' : $this->separator.$path); + } + return ''; + } + + /** + * Return crypted path + * Not implemented + * + * @param string path + * + * @return mixed + * @author Dmitry (dio) Levashov + **/ + protected function crypt($path) + { + return $path; + } + + /** + * Return uncrypted path + * Not implemented + * + * @param mixed hash + * + * @return mixed + * @author Dmitry (dio) Levashov + **/ + protected function uncrypt($hash) + { + return $hash; + } + + /** + * Validate file name based on $this->options['acceptedName'] regexp or function + * + * @param string $name file name + * @param bool $isDir + * + * @return bool + * @author Dmitry (dio) Levashov + */ + protected function nameAccepted($name, $isDir = false) + { + if (json_encode($name) === false) { + return false; + } + $nameValidator = $isDir ? $this->dirnameValidator : $this->nameValidator; + if ($nameValidator) { + if (is_callable($nameValidator)) { + $res = call_user_func($nameValidator, $name); + return $res; + } + if (preg_match($nameValidator, '') !== false) { + return preg_match($nameValidator, $name); + } + } + return true; + } + + /** + * Return session rootstat cache key + * + * @return string + */ + protected function getRootstatCachekey() + { + return md5($this->root . (isset($this->options['alias']) ? $this->options['alias'] : '')); + } + + /** + * Return new unique name based on file name and suffix + * + * @param $dir + * @param $name + * @param string $suffix suffix append to name + * @param bool $checkNum + * @param int $start + * + * @return string + * @internal param string $path file path + * @author Dmitry (dio) Levashov + */ + public function uniqueName($dir, $name, $suffix = ' copy', $checkNum = true, $start = 1) + { + static $lasts = null; + + if ($lasts === null) { + $lasts = array(); + } + + $ext = ''; + + $splits = elFinder::splitFileExtention($name); + if ($splits[1]) { + $ext = '.' . $splits[1]; + $name = $splits[0]; + } + + if ($checkNum && preg_match('/(' . preg_quote($suffix, '/') . ')(\d*)$/i', $name, $m)) { + $i = (int)$m[2]; + $name = substr($name, 0, strlen($name) - strlen($m[2])); + } else { + $i = $start; + $name .= $suffix; + } + $max = $i + 100000; + + if (isset($lasts[$name])) { + $i = max($i, $lasts[$name]); + } + + while ($i <= $max) { + $n = $name . ($i > 0 ? sprintf($this->options['uniqueNumFormat'], $i) : '') . $ext; + + if (!$this->isNameExists($this->joinPathCE($dir, $n))) { + $this->clearcache(); + $lasts[$name] = ++$i; + return $n; + } + $i++; + } + return $name . md5($dir) . $ext; + } + + /** + * Converts character encoding from UTF-8 to server's one + * + * @param mixed $var target string or array var + * @param bool $restoreLocale do retore global locale, default is false + * @param string $unknown replaces character for unknown + * + * @return mixed + * @author Naoki Sawada + */ + public function convEncIn($var = null, $restoreLocale = false, $unknown = '_') + { + return (!$this->encoding) ? $var : $this->convEnc($var, 'UTF-8', $this->encoding, $this->options['locale'], $restoreLocale, $unknown); + } + + /** + * Converts character encoding from server's one to UTF-8 + * + * @param mixed $var target string or array var + * @param bool $restoreLocale do retore global locale, default is true + * @param string $unknown replaces character for unknown + * + * @return mixed + * @author Naoki Sawada + */ + public function convEncOut($var = null, $restoreLocale = true, $unknown = '_') + { + return (!$this->encoding) ? $var : $this->convEnc($var, $this->encoding, 'UTF-8', $this->options['locale'], $restoreLocale, $unknown); + } + + /** + * Converts character encoding (base function) + * + * @param mixed $var target string or array var + * @param string $from from character encoding + * @param string $to to character encoding + * @param string $locale local locale + * @param $restoreLocale + * @param string $unknown replaces character for unknown + * + * @return mixed + */ + protected function convEnc($var, $from, $to, $locale, $restoreLocale, $unknown = '_') + { + if (strtoupper($from) !== strtoupper($to)) { + if ($locale) { + setlocale(LC_ALL, $locale); + } + if (is_array($var)) { + $_ret = array(); + foreach ($var as $_k => $_v) { + $_ret[$_k] = $this->convEnc($_v, $from, $to, '', false, $unknown = '_'); + } + $var = $_ret; + } else { + $_var = false; + if (is_string($var)) { + $_var = $var; + if (false !== ($_var = iconv($from, $to . '//TRANSLIT', $_var))) { + $_var = str_replace('?', $unknown, $_var); + } + } + if ($_var !== false) { + $var = $_var; + } + } + if ($restoreLocale) { + setlocale(LC_ALL, elFinder::$locale); + } + } + return $var; + } + + /** + * Normalize MIME-Type by options['mimeMap'] + * + * @param string $type MIME-Type + * @param string $name Filename + * @param string $ext File extention without first dot (optional) + * + * @return string Normalized MIME-Type + */ + public function mimeTypeNormalize($type, $name, $ext = '') + { + if ($ext === '') { + $ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1); + } + $_checkKey = strtolower($ext . ':' . $type); + if ($type === '') { + $_keylen = strlen($_checkKey); + foreach ($this->options['mimeMap'] as $_key => $_type) { + if (substr($_key, 0, $_keylen) === $_checkKey) { + $type = $_type; + break; + } + } + } else if (isset($this->options['mimeMap'][$_checkKey])) { + $type = $this->options['mimeMap'][$_checkKey]; + } else { + $_checkKey = strtolower($ext . ':*'); + if (isset($this->options['mimeMap'][$_checkKey])) { + $type = $this->options['mimeMap'][$_checkKey]; + } else { + $_checkKey = strtolower('*:' . $type); + if (isset($this->options['mimeMap'][$_checkKey])) { + $type = $this->options['mimeMap'][$_checkKey]; + } + } + } + return $type; + } + + /*********************** util mainly for inheritance class *********************/ + + /** + * Get temporary filename. Tempfile will be removed when after script execution finishes or exit() is called. + * When needing the unique file to a path, give $path to parameter. + * + * @param string $path for get unique file to a path + * + * @return string|false + * @author Naoki Sawada + */ + protected function getTempFile($path = '') + { + static $cache = array(); + + $key = ''; + if ($path !== '') { + $key = $this->id . '#' . $path; + if (isset($cache[$key])) { + return $cache[$key]; + } + } + + if ($tmpdir = $this->getTempPath()) { + $name = tempnam($tmpdir, 'ELF'); + if ($key) { + $cache[$key] = $name; + } + // register auto delete on shutdown + $GLOBALS['elFinderTempFiles'][$name] = true; + return $name; + } + + return false; + } + + /** + * File path of local server side work file path + * + * @param string $path path need convert encoding to server encoding + * + * @return string + * @author Naoki Sawada + */ + protected function getWorkFile($path) + { + if ($wfp = $this->tmpfile()) { + if ($fp = $this->_fopen($path)) { + while (!feof($fp)) { + fwrite($wfp, fread($fp, 8192)); + } + $info = stream_get_meta_data($wfp); + fclose($wfp); + if ($info && !empty($info['uri'])) { + return $info['uri']; + } + } + } + return false; + } + + /** + * Get image size array with `dimensions` + * + * @param string $path path need convert encoding to server encoding + * @param string $mime file mime type + * + * @return array|false + * @throws ImagickException + * @throws elFinderAbortException + */ + public function getImageSize($path, $mime = '') + { + $size = false; + if ($mime === '' || strtolower(substr($mime, 0, 5)) === 'image') { + if ($work = $this->getWorkFile($path)) { + if ($size = getimagesize($work)) { + $size['dimensions'] = $size[0] . 'x' . $size[1]; + $srcfp = fopen($work, 'rb'); + $cArgs = elFinder::$currentArgs; + if (!empty($cArgs['target']) && $subImgLink = $this->getSubstituteImgLink($cArgs['target'], $size, $srcfp)) { + $size['url'] = $subImgLink; + } + } + } + is_file($work) && unlink($work); + } + return $size; + } + + /** + * Delete dirctory trees + * + * @param string $localpath path need convert encoding to server encoding + * + * @return boolean + * @throws elFinderAbortException + * @author Naoki Sawada + */ + protected function delTree($localpath) + { + foreach ($this->_scandir($localpath) as $p) { + elFinder::checkAborted(); + $stat = $this->stat($this->convEncOut($p)); + $this->convEncIn(); + ($stat['mime'] === 'directory') ? $this->delTree($p) : $this->_unlink($p); + } + $res = $this->_rmdir($localpath); + $res && $this->clearstatcache(); + return $res; + } + + /** + * Copy items to a new temporary directory on the local server + * + * @param array $hashes target hashes + * @param string $dir destination directory (for recurcive) + * @param string $canLink it can use link() (for recurcive) + * + * @return string|false saved path name + * @throws elFinderAbortException + * @author Naoki Sawada + */ + protected function getItemsInHand($hashes, $dir = null, $canLink = null) + { + static $totalSize = 0; + if (is_null($dir)) { + $totalSize = 0; + if (!$tmpDir = $this->getTempPath()) { + return false; + } + $dir = tempnam($tmpDir, 'elf'); + if (!unlink($dir) || !mkdir($dir, 0700, true)) { + return false; + } + register_shutdown_function(array($this, 'rmdirRecursive'), $dir); + } + if (is_null($canLink)) { + $canLink = ($this instanceof elFinderVolumeLocalFileSystem); + } + elFinder::checkAborted(); + $res = true; + $files = array(); + foreach ($hashes as $hash) { + if (($file = $this->file($hash)) == false) { + continue; + } + if (!$file['read']) { + continue; + } + + $name = $file['name']; + // for call from search results + if (isset($files[$name])) { + $name = preg_replace('/^(.*?)(\..*)?$/', '$1_' . $files[$name]++ . '$2', $name); + } else { + $files[$name] = 1; + } + $target = $dir . DIRECTORY_SEPARATOR . $name; + + if ($file['mime'] === 'directory') { + $chashes = array(); + $_files = $this->scandir($hash); + foreach ($_files as $_file) { + if ($file['read']) { + $chashes[] = $_file['hash']; + } + } + if (($res = mkdir($target, 0700, true)) && $chashes) { + $res = $this->getItemsInHand($chashes, $target, $canLink); + } + if (!$res) { + break; + } + !empty($file['ts']) && touch($target, $file['ts']); + } else { + $path = $this->decode($hash); + if (!$canLink || !($canLink = link($path, $target))) { + if ($fp = $this->fopenCE($path)) { + if ($tfp = fopen($target, 'wb')) { + $totalSize += stream_copy_to_stream($fp, $tfp); + fclose($tfp); + } + !empty($file['ts']) && touch($target, $file['ts']); + $this->fcloseCE($fp, $path); + } + } else { + $totalSize += filesize($path); + } + if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $totalSize) { + $res = $this->setError(elFinder::ERROR_ARC_MAXSIZE); + } + } + } + return $res ? $dir : false; + } + + /*********************** file stat *********************/ + + /** + * Check file attribute + * + * @param string $path file path + * @param string $name attribute name (read|write|locked|hidden) + * @param bool $val attribute value returned by file system + * @param bool $isDir path is directory (true: directory, false: file) + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function attr($path, $name, $val = null, $isDir = null) + { + if (!isset($this->defaults[$name])) { + return false; + } + + $relpath = $this->relpathCE($path); + if ($this->separator !== '/') { + $relpath = str_replace($this->separator, '/', $relpath); + } + $relpath = '/' . $relpath; + + $perm = null; + + if ($this->access) { + $perm = call_user_func($this->access, $name, $path, $this->options['accessControlData'], $this, $isDir, $relpath); + if ($perm !== null) { + return !!$perm; + } + } + + foreach ($this->attributes as $attrs) { + if (isset($attrs[$name]) && isset($attrs['pattern']) && preg_match($attrs['pattern'], $relpath)) { + $perm = $attrs[$name]; + break; + } + } + + return $perm === null ? (is_null($val) ? $this->defaults[$name] : $val) : !!$perm; + } + + /** + * Return true if file with given name can be created in given folder. + * + * @param string $dir parent dir path + * @param string $name new file name + * @param null $isDir + * + * @return bool + * @author Dmitry (dio) Levashov + */ + protected function allowCreate($dir, $name, $isDir = null) + { + return $this->attr($this->joinPathCE($dir, $name), 'write', true, $isDir); + } + + /** + * Return true if file MIME type can save with check uploadOrder config. + * + * @param string $mime + * + * @return boolean + */ + protected function allowPutMime($mime) + { + // logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order + $allow = $this->mimeAccepted($mime, $this->uploadAllow, null); + $deny = $this->mimeAccepted($mime, $this->uploadDeny, null); + if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny' + $res = false; // default is deny + if (!$deny && ($allow === true)) { // match only allow + $res = true; + }// else (both match | no match | match only deny) { deny } + } else { // array('deny', 'allow'), default is to 'allow' - this is the default rule + $res = true; // default is allow + if (($deny === true) && !$allow) { // match only deny + $res = false; + } // else (both match | no match | match only allow) { allow } + } + return $res; + } + + /** + * Return fileinfo + * + * @param string $path file cache + * + * @return array|bool + * @author Dmitry (dio) Levashov + **/ + protected function stat($path) + { + if ($path === false || is_null($path)) { + return false; + } + $is_root = ($path == $this->root); + if ($is_root) { + $rootKey = $this->getRootstatCachekey(); + if ($this->sessionCaching['rootstat'] && !isset($this->sessionCache['rootstat'])) { + $this->sessionCache['rootstat'] = array(); + } + if (!isset($this->cache[$path]) && !$this->isMyReload()) { + // need $path as key for netmount/netunmount + if ($this->sessionCaching['rootstat'] && isset($this->sessionCache['rootstat'][$rootKey])) { + if ($ret = $this->sessionCache['rootstat'][$rootKey]) { + if ($this->options['rootRev'] === $ret['rootRev']) { + if (isset($this->options['phash'])) { + $ret['isroot'] = 1; + $ret['phash'] = $this->options['phash']; + } + return $ret; + } + } + } + } + } + $ret = isset($this->cache[$path]) + ? $this->cache[$path] + : $this->updateCache($path, $this->convEncOut($this->_stat($this->convEncIn($path)))); + if ($is_root && $this->sessionCaching['rootstat']) { + if ($ret) { + $this->rootModified = false; + if (!empty($rootKey)) { + $this->sessionCache['rootstat'][$rootKey] = $ret; + } + if (isset($this->options['phash'])) { + $ret['isroot'] = 1; + $ret['phash'] = $this->options['phash']; + } + } else if (!empty($rootKey)) { + unset($this->sessionCache['rootstat'][$rootKey]); + } + } + return $ret; + } + + /** + * Get root stat extra key values + * + * @return array stat extras + * @author Naoki Sawada + */ + protected function getRootStatExtra() + { + $stat = array(); + if ($this->rootName) { + $stat['name'] = $this->rootName; + } + $stat['rootRev'] = $this->options['rootRev']; + $stat['options'] = $this->options(null); + return $stat; + } + + /** + * Return fileinfo based on filename + * For item ID based path file system + * Please override if needed on each drivers + * + * @param string $path file cache + * + * @return array + */ + protected function isNameExists($path) + { + return $this->stat($path); + } + + /** + * Put file stat in cache and return it + * + * @param string $path file path + * @param array $stat file stat + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function updateCache($path, $stat) + { + if (empty($stat) || !is_array($stat)) { + return $this->cache[$path] = array(); + } + + $stat['hash'] = $this->encode($path); + + $root = $path == $this->root; + $parent = ''; + + if ($root) { + $stat = array_merge($stat, $this->getRootStatExtra()); + } else { + if (!isset($stat['name']) || $stat['name'] === '') { + $stat['name'] = $this->basenameCE($path); + } + if (empty($stat['phash'])) { + $parent = $this->dirnameCE($path); + $stat['phash'] = $this->encode($parent); + } else { + $parent = $this->decode($stat['phash']); + } + } + + // name check + if (!$jeName = json_encode($stat['name'])) { + return $this->cache[$path] = array(); + } + // fix name if required + if ($this->options['utf8fix'] && $this->options['utf8patterns'] && $this->options['utf8replace']) { + $stat['name'] = json_decode(str_replace($this->options['utf8patterns'], $this->options['utf8replace'], $jeName)); + } + + if (!isset($stat['size'])) { + $stat['size'] = 'unknown'; + } + + $mime = isset($stat['mime']) ? $stat['mime'] : ''; + if ($isDir = ($mime === 'directory')) { + $stat['volumeid'] = $this->id; + } else { + if (empty($stat['mime']) || $stat['size'] == 0) { + $stat['mime'] = $this->mimetype($stat['name'], true, $stat['size'], $mime); + } else { + $stat['mime'] = $this->mimeTypeNormalize($stat['mime'], $stat['name']); + } + } + + $stat['read'] = intval($this->attr($path, 'read', isset($stat['read']) ? !!$stat['read'] : null, $isDir)); + $stat['write'] = intval($this->attr($path, 'write', isset($stat['write']) ? !!$stat['write'] : null, $isDir)); + if ($root) { + $stat['locked'] = 1; + if ($this->options['type'] !== '') { + $stat['type'] = $this->options['type']; + } + } else { + // lock when parent directory is not writable + if (!isset($stat['locked'])) { + $pstat = $this->stat($parent); + if (isset($pstat['write']) && !$pstat['write']) { + $stat['locked'] = true; + } + } + if ($this->attr($path, 'locked', isset($stat['locked']) ? !!$stat['locked'] : null, $isDir)) { + $stat['locked'] = 1; + } else { + unset($stat['locked']); + } + } + + if ($root) { + unset($stat['hidden']); + } elseif ($this->attr($path, 'hidden', isset($stat['hidden']) ? !!$stat['hidden'] : null, $isDir) + || !$this->mimeAccepted($stat['mime'])) { + $stat['hidden'] = 1; + } else { + unset($stat['hidden']); + } + + if ($stat['read'] && empty($stat['hidden'])) { + + if ($isDir) { + // caching parent's subdirs + if ($parent) { + $this->updateSubdirsCache($parent, true); + } + // for dir - check for subdirs + if ($this->options['checkSubfolders']) { + if (!isset($stat['dirs']) && intval($this->options['checkSubfolders']) === -1) { + $stat['dirs'] = -1; + } + if (isset($stat['dirs'])) { + if ($stat['dirs']) { + if ($stat['dirs'] == -1) { + $stat['dirs'] = ($this->sessionCaching['subdirs'] && isset($this->sessionCache['subdirs'][$path])) ? (int)$this->sessionCache['subdirs'][$path] : -1; + } else { + $stat['dirs'] = 1; + } + } else { + unset($stat['dirs']); + } + } elseif (!empty($stat['alias']) && !empty($stat['target'])) { + $stat['dirs'] = isset($this->cache[$stat['target']]) + ? intval(isset($this->cache[$stat['target']]['dirs'])) + : $this->subdirsCE($stat['target']); + + } elseif ($this->subdirsCE($path)) { + $stat['dirs'] = 1; + } + } else { + $stat['dirs'] = 1; + } + if ($this->options['dirUrlOwn'] === true) { + // Set `null` to use the client option `commandsOptions.info.nullUrlDirLinkSelf = true` + $stat['url'] = null; + } else if ($this->options['dirUrlOwn'] === 'hide') { + // to hide link in info dialog of the elFinder client + $stat['url'] = ''; + } + } else { + // for files - check for thumbnails + $p = isset($stat['target']) ? $stat['target'] : $path; + if ($this->tmbURL && !isset($stat['tmb']) && $this->canCreateTmb($p, $stat)) { + $tmb = $this->gettmb($p, $stat); + $stat['tmb'] = $tmb ? $tmb : 1; + } + + } + if (!isset($stat['url']) && $this->URL && $this->encoding) { + $_path = str_replace($this->separator, '/', substr($path, strlen($this->root) + 1)); + $stat['url'] = rtrim($this->URL, '/') . '/' . str_replace('%2F', '/', rawurlencode((substr(PHP_OS, 0, 3) === 'WIN') ? $_path : $this->convEncIn($_path, true))); + } + } else { + if ($isDir) { + unset($stat['dirs']); + } + } + + if (!empty($stat['alias']) && !empty($stat['target'])) { + $stat['thash'] = $this->encode($stat['target']); + //$this->cache[$stat['target']] = $stat; + unset($stat['target']); + } + + $this->cache[$path] = $stat; + + if ($root && $this->sessionCaching['rootstat']) { + // to update session cache + $this->stat($path); + } + + return $stat; + } + + /** + * Get stat for folder content and put in cache + * + * @param string $path + * + * @return void + * @author Dmitry (dio) Levashov + **/ + protected function cacheDir($path) + { + $this->dirsCache[$path] = array(); + $hasDir = false; + + foreach ($this->scandirCE($path) as $p) { + if (($stat = $this->stat($p)) && empty($stat['hidden'])) { + if (!$hasDir && $stat['mime'] === 'directory') { + $hasDir = true; + } + $this->dirsCache[$path][] = $p; + } + } + + $this->updateSubdirsCache($path, $hasDir); + } + + /** + * Clean cache + * + * @return void + * @author Dmitry (dio) Levashov + **/ + protected function clearcache() + { + $this->cache = $this->dirsCache = array(); + } + + /** + * Return file mimetype + * + * @param string $path file path + * @param string|bool $name + * @param integer $size + * @param string $mime was notified from the volume driver + * + * @return string + * @author Dmitry (dio) Levashov + */ + protected function mimetype($path, $name = '', $size = null, $mime = null) + { + $type = ''; + $nameCheck = false; + + if ($name === '') { + $name = $path; + } else if ($name === true) { + $name = $path; + $nameCheck = true; + } + $ext = (false === $pos = strrpos($name, '.')) ? '' : substr($name, $pos + 1); + if ($size === null) { + $size = file_exists($path) ? filesize($path) : -1; + } + if (!$nameCheck && is_readable($path) && $size > 0) { + // detecting by contents + if ($this->mimeDetect === 'finfo') { + $type = finfo_file($this->finfo, $path); + } else if ($this->mimeDetect === 'mime_content_type') { + $type = mime_content_type($path); + } + if ($type) { + $type = explode(';', $type); + $type = trim($type[0]); + if ($ext && preg_match('~^application/(?:octet-stream|(?:x-)?zip|xml)$~', $type)) { + // load default MIME table file "mime.types" + if (!elFinderVolumeDriver::$mimetypesLoaded) { + elFinderVolumeDriver::loadMimeTypes(); + } + if (isset(elFinderVolumeDriver::$mimetypes[$ext])) { + $type = elFinderVolumeDriver::$mimetypes[$ext]; + } + } else if ($ext === 'js' && preg_match('~^text/~', $type)) { + $type = 'text/javascript'; + } + } + } + if (!$type) { + // detecting by filename + $type = elFinderVolumeDriver::mimetypeInternalDetect($name); + if ($type === 'unknown') { + if ($mime) { + $type = $mime; + } else { + $type = ($size == 0) ? 'text/plain' : $this->options['mimeTypeUnknown']; + } + } + } + + // mime type normalization + $type = $this->mimeTypeNormalize($type, $name, $ext); + + return $type; + } + + /** + * Load file of mime.types + * + * @param string $mimeTypesFile The mime types file + */ + static protected function loadMimeTypes($mimeTypesFile = '') + { + if (!elFinderVolumeDriver::$mimetypesLoaded) { + elFinderVolumeDriver::$mimetypesLoaded = true; + $file = false; + if (!empty($mimeTypesFile) && file_exists($mimeTypesFile)) { + $file = $mimeTypesFile; + } elseif (elFinder::$defaultMimefile && file_exists(elFinder::$defaultMimefile)) { + $file = elFinder::$defaultMimefile; + } elseif (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mime.types')) { + $file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mime.types'; + } elseif (file_exists(dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'mime.types')) { + $file = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'mime.types'; + } + + if ($file && file_exists($file)) { + $mimecf = file($file); + + foreach ($mimecf as $line_num => $line) { + if (!preg_match('/^\s*#/', $line)) { + $mime = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY); + for ($i = 1, $size = count($mime); $i < $size; $i++) { + if (!isset(self::$mimetypes[$mime[$i]])) { + self::$mimetypes[$mime[$i]] = $mime[0]; + } + } + } + } + } + } + } + + /** + * Detect file mimetype using "internal" method or Loading mime.types with $path = '' + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + static protected function mimetypeInternalDetect($path = '') + { + // load default MIME table file "mime.types" + if (!elFinderVolumeDriver::$mimetypesLoaded) { + elFinderVolumeDriver::loadMimeTypes(); + } + $ext = ''; + if ($path) { + $pinfo = pathinfo($path); + $ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : ''; + } + return ($ext && isset(elFinderVolumeDriver::$mimetypes[$ext])) ? elFinderVolumeDriver::$mimetypes[$ext] : 'unknown'; + } + + /** + * Return file/total directory size infomation + * + * @param string $path file path + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function countSize($path) + { + + elFinder::checkAborted(); + + $result = array('size' => 0, 'files' => 0, 'dirs' => 0); + $stat = $this->stat($path); + + if (empty($stat) || !$stat['read'] || !empty($stat['hidden'])) { + $result['size'] = 'unknown'; + return $result; + } + + if ($stat['mime'] !== 'directory') { + $result['size'] = intval($stat['size']); + $result['files'] = 1; + return $result; + } + + $result['dirs'] = 1; + $subdirs = $this->options['checkSubfolders']; + $this->options['checkSubfolders'] = true; + foreach ($this->getScandir($path) as $stat) { + if ($isDir = ($stat['mime'] === 'directory' && $stat['read'])) { + ++$result['dirs']; + } else { + ++$result['files']; + } + $res = $isDir + ? $this->countSize($this->decode($stat['hash'])) + : (isset($stat['size']) ? array('size' => intval($stat['size'])) : array()); + if (!empty($res['size']) && is_numeric($res['size'])) { + $result['size'] += $res['size']; + } + if (!empty($res['files']) && is_numeric($res['files'])) { + $result['files'] += $res['files']; + } + if (!empty($res['dirs']) && is_numeric($res['dirs'])) { + $result['dirs'] += $res['dirs']; + --$result['dirs']; + } + } + $this->options['checkSubfolders'] = $subdirs; + return $result; + } + + /** + * Return true if all mimes is directory or files + * + * @param string $mime1 mimetype + * @param string $mime2 mimetype + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function isSameType($mime1, $mime2) + { + return ($mime1 == 'directory' && $mime1 == $mime2) || ($mime1 != 'directory' && $mime2 != 'directory'); + } + + /** + * If file has required attr == $val - return file path, + * If dir has child with has required attr == $val - return child path + * + * @param string $path file path + * @param string $attr attribute name + * @param bool $val attribute value + * + * @return string|false + * @author Dmitry (dio) Levashov + **/ + protected function closestByAttr($path, $attr, $val) + { + $stat = $this->stat($path); + + if (empty($stat)) { + return false; + } + + $v = isset($stat[$attr]) ? $stat[$attr] : false; + + if ($v == $val) { + return $path; + } + + return $stat['mime'] == 'directory' + ? $this->childsByAttr($path, $attr, $val) + : false; + } + + /** + * Return first found children with required attr == $val + * + * @param string $path file path + * @param string $attr attribute name + * @param bool $val attribute value + * + * @return string|false + * @author Dmitry (dio) Levashov + **/ + protected function childsByAttr($path, $attr, $val) + { + foreach ($this->scandirCE($path) as $p) { + if (($_p = $this->closestByAttr($p, $attr, $val)) != false) { + return $_p; + } + } + return false; + } + + protected function isMyReload($target = '', $ARGtarget = '') + { + if ($this->rootModified || (!empty($this->ARGS['cmd']) && $this->ARGS['cmd'] === 'parents')) { + return true; + } + if (!empty($this->ARGS['reload'])) { + if ($ARGtarget === '') { + $ARGtarget = isset($this->ARGS['target']) ? $this->ARGS['target'] + : ((isset($this->ARGS['targets']) && is_array($this->ARGS['targets']) && count($this->ARGS['targets']) === 1) ? + $this->ARGS['targets'][0] : ''); + } + if ($ARGtarget !== '') { + $ARGtarget = strval($ARGtarget); + if ($target === '') { + return (strpos($ARGtarget, $this->id) === 0); + } else { + $target = strval($target); + return ($target === $ARGtarget); + } + } + } + return false; + } + + /** + * Update subdirs cache data + * + * @param string $path + * @param bool $subdirs + * + * @returnv void + */ + protected function updateSubdirsCache($path, $subdirs) + { + if (isset($this->cache[$path])) { + if ($subdirs) { + $this->cache[$path]['dirs'] = 1; + } else { + unset($this->cache[$path]['dirs']); + } + } + if ($this->sessionCaching['subdirs']) { + $this->sessionCache['subdirs'][$path] = $subdirs; + } + if ($this->sessionCaching['rootstat'] && $path == $this->root) { + unset($this->sessionCache['rootstat'][$this->getRootstatCachekey()]); + } + } + + /***************** get content *******************/ + + /** + * Return required dir's files info. + * If onlyMimes is set - return only dirs and files of required mimes + * + * @param string $path dir path + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function getScandir($path) + { + $files = array(); + + !isset($this->dirsCache[$path]) && $this->cacheDir($path); + + foreach ($this->dirsCache[$path] as $p) { + if (($stat = $this->stat($p)) && empty($stat['hidden'])) { + $files[] = $stat; + } + } + + return $files; + } + + + /** + * Return subdirs tree + * + * @param string $path parent dir path + * @param int $deep tree deep + * @param string $exclude + * + * @return array + * @author Dmitry (dio) Levashov + */ + protected function gettree($path, $deep, $exclude = '') + { + $dirs = array(); + + !isset($this->dirsCache[$path]) && $this->cacheDir($path); + + foreach ($this->dirsCache[$path] as $p) { + $stat = $this->stat($p); + + if ($stat && empty($stat['hidden']) && $p != $exclude && $stat['mime'] == 'directory') { + $dirs[] = $stat; + if ($deep > 0 && !empty($stat['dirs'])) { + $dirs = array_merge($dirs, $this->gettree($p, $deep - 1)); + } + } + } + + return $dirs; + } + + /** + * Recursive files search + * + * @param string $path dir path + * @param string $q search string + * @param array $mimes + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function doSearch($path, $q, $mimes) + { + $result = array(); + $matchMethod = empty($this->doSearchCurrentQuery['matchMethod']) ? 'searchMatchName' : $this->doSearchCurrentQuery['matchMethod']; + $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0; + if ($timeout && $timeout < time()) { + $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); + return $result; + } + + foreach ($this->scandirCE($path) as $p) { + elFinder::extendTimeLimit($this->options['searchTimeout'] + 30); + + if ($timeout && ($this->error || $timeout < time())) { + !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); + break; + } + + + $stat = $this->stat($p); + + if (!$stat) { // invalid links + continue; + } + + if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) { + continue; + } + + $name = $stat['name']; + + if ($this->doSearchCurrentQuery['excludes']) { + foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) { + if ($this->stripos($name, $exclude) !== false) { + continue 2; + } + } + } + + if ((!$mimes || $stat['mime'] !== 'directory') && $this->$matchMethod($name, $q, $p) !== false) { + $stat['path'] = $this->path($stat['hash']); + if ($this->URL && !isset($stat['url'])) { + $path = str_replace($this->separator, '/', substr($p, strlen($this->root) + 1)); + if ($this->encoding) { + $path = str_replace('%2F', '/', rawurlencode($this->convEncIn($path, true))); + } else { + $path = str_replace('%2F', '/', rawurlencode($path)); + } + $stat['url'] = $this->URL . $path; + } + + $result[] = $stat; + } + if ($stat['mime'] == 'directory' && $stat['read'] && !isset($stat['alias'])) { + if (!$this->options['searchExDirReg'] || !preg_match($this->options['searchExDirReg'], $p)) { + $result = array_merge($result, $this->doSearch($p, $q, $mimes)); + } + } + } + + return $result; + } + + /********************** manuipulations ******************/ + + /** + * Copy file/recursive copy dir only in current volume. + * Return new file path or false. + * + * @param string $src source path + * @param string $dst destination dir path + * @param string $name new file name (optionaly) + * + * @return string|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function copy($src, $dst, $name) + { + + elFinder::checkAborted(); + + $srcStat = $this->stat($src); + + if (!empty($srcStat['thash'])) { + $target = $this->decode($srcStat['thash']); + if (!$this->inpathCE($target, $this->root)) { + return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash']), elFinder::ERROR_MKOUTLINK); + } + $stat = $this->stat($target); + $this->clearcache(); + return $stat && $this->symlinkCE($target, $dst, $name) + ? $this->joinPathCE($dst, $name) + : $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); + } + + if ($srcStat['mime'] === 'directory') { + $testStat = $this->isNameExists($this->joinPathCE($dst, $name)); + $this->clearcache(); + + if (($testStat && $testStat['mime'] !== 'directory') || (!$testStat && !$testStat = $this->mkdir($this->encode($dst), $name))) { + return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); + } + + $dst = $this->decode($testStat['hash']); + + foreach ($this->getScandir($src) as $stat) { + if (empty($stat['hidden'])) { + $name = $stat['name']; + $_src = $this->decode($stat['hash']); + if (!$this->copy($_src, $dst, $name)) { + $this->remove($dst, true); // fall back + return $this->setError($this->error, elFinder::ERROR_COPY, $this->_path($src)); + } + } + } + + $this->added[] = $testStat; + + return $dst; + } + + if ($this->options['copyJoin']) { + $test = $this->joinPathCE($dst, $name); + if ($this->isNameExists($test)) { + $this->remove($test); + } + } + if ($res = $this->convEncOut($this->_copy($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) { + $path = is_string($res) ? $res : $this->joinPathCE($dst, $name); + $this->clearstatcache(); + $this->added[] = $this->stat($path); + return $path; + } + + return $this->setError(elFinder::ERROR_COPY, $this->path($srcStat['hash'])); + } + + /** + * Move file + * Return new file path or false. + * + * @param string $src source path + * @param string $dst destination dir path + * @param string $name new file name + * + * @return string|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function move($src, $dst, $name) + { + $stat = $this->stat($src); + $stat['realpath'] = $src; + $this->rmTmb($stat); // can not do rmTmb() after _move() + $this->clearcache(); + + if ($res = $this->convEncOut($this->_move($this->convEncIn($src), $this->convEncIn($dst), $this->convEncIn($name)))) { + $this->clearstatcache(); + $this->removed[] = $stat; + if ($stat['mime'] === 'directory') { + $this->updateSubdirsCache($dst, true); + } + return is_string($res) ? $res : $this->joinPathCE($dst, $name); + } + + return $this->setError(elFinder::ERROR_MOVE, $this->path($stat['hash'])); + } + + /** + * Copy file from another volume. + * Return new file path or false. + * + * @param Object $volume source volume + * @param string $src source file hash + * @param string $destination destination dir path + * @param string $name file name + * + * @return string|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function copyFrom($volume, $src, $destination, $name) + { + + elFinder::checkAborted(); + + if (($source = $volume->file($src)) == false) { + return $this->addError(elFinder::ERROR_COPY, '#' . $src, $volume->error()); + } + + $srcIsDir = ($source['mime'] === 'directory'); + + $errpath = $volume->path($source['hash']); + + if (!$this->nameAccepted($source['name'], $srcIsDir)) { + return $this->addError(elFinder::ERROR_COPY, $errpath, $srcIsDir ? elFinder::ERROR_INVALID_DIRNAME : elFinder::ERROR_INVALID_NAME); + } + + if (!$source['read']) { + return $this->addError(elFinder::ERROR_COPY, $errpath, elFinder::ERROR_PERM_DENIED); + } + + if ($srcIsDir) { + $test = $this->isNameExists($this->joinPathCE($destination, $name)); + $this->clearcache(); + + if (($test && $test['mime'] != 'directory') || (!$test && !$test = $this->mkdir($this->encode($destination), $name))) { + return $this->addError(elFinder::ERROR_COPY, $errpath); + } + + //$path = $this->joinPathCE($destination, $name); + $path = $this->decode($test['hash']); + + foreach ($volume->scandir($src) as $entr) { + $this->copyFrom($volume, $entr['hash'], $path, $entr['name']); + } + + $this->added[] = $test; + } else { + // MIME check + $mimeByName = $this->mimetype($source['name'], true); + if ($source['mime'] === $mimeByName) { + $mimeByName = ''; + } + if (!$this->allowPutMime($source['mime']) || ($mimeByName && !$this->allowPutMime($mimeByName))) { + return $this->addError(elFinder::ERROR_UPLOAD_FILE_MIME, $errpath); + } + + if (strpos($source['mime'], 'image') === 0 && ($dim = $volume->dimensions($src))) { + if (is_array($dim)) { + $dim = isset($dim['dim']) ? $dim['dim'] : null; + } + if ($dim) { + $s = explode('x', $dim); + $source['width'] = $s[0]; + $source['height'] = $s[1]; + } + } + + if (($fp = $volume->open($src)) == false + || ($path = $this->saveCE($fp, $destination, $name, $source)) == false) { + $fp && $volume->close($fp, $src); + return $this->addError(elFinder::ERROR_COPY, $errpath); + } + $volume->close($fp, $src); + + $this->added[] = $this->stat($path);; + } + + return $path; + } + + /** + * Remove file/ recursive remove dir + * + * @param string $path file path + * @param bool $force try to remove even if file locked + * + * @return bool + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function remove($path, $force = false) + { + $stat = $this->stat($path); + + if (empty($stat)) { + return $this->setError(elFinder::ERROR_RM, $path, elFinder::ERROR_FILE_NOT_FOUND); + } + + $stat['realpath'] = $path; + $this->rmTmb($stat); + $this->clearcache(); + + if (!$force && !empty($stat['locked'])) { + return $this->setError(elFinder::ERROR_LOCKED, $this->path($stat['hash'])); + } + + if ($stat['mime'] == 'directory' && empty($stat['thash'])) { + $ret = $this->delTree($this->convEncIn($path)); + $this->convEncOut(); + if (!$ret) { + return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash'])); + } + } else { + if ($this->convEncOut(!$this->_unlink($this->convEncIn($path)))) { + return $this->setError(elFinder::ERROR_RM, $this->path($stat['hash'])); + } + $this->clearstatcache(); + } + + $this->removed[] = $stat; + return true; + } + + + /************************* thumbnails **************************/ + + /** + * Return thumbnail file name for required file + * + * @param array $stat file stat + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function tmbname($stat) + { + $name = $stat['hash'] . (isset($stat['ts']) ? $stat['ts'] : '') . '.png'; + if (strlen($name) > 255) { + $name = $this->id . md5($stat['hash']) . $stat['ts'] . '.png'; + } + return $name; + } + + /** + * Return thumnbnail name if exists + * + * @param string $path file path + * @param array $stat file stat + * + * @return string|false + * @author Dmitry (dio) Levashov + **/ + protected function gettmb($path, $stat) + { + if ($this->tmbURL && $this->tmbPath) { + // file itself thumnbnail + if (strpos($path, $this->tmbPath) === 0) { + return basename($path); + } + + $name = $this->tmbname($stat); + if (file_exists($this->tmbPath . DIRECTORY_SEPARATOR . $name)) { + return $name; + } + } + return false; + } + + /** + * Return true if thumnbnail for required file can be created + * + * @param string $path thumnbnail path + * @param array $stat file stat + * @param bool $checkTmbPath + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + protected function canCreateTmb($path, $stat, $checkTmbPath = true) + { + if ((!$checkTmbPath || $this->tmbPathWritable) + && (!$this->tmbPath || strpos($path, $this->tmbPath) === false) // do not create thumnbnail for thumnbnail + ) { + $mime = strtolower($stat['mime']); + list($type) = explode('/', $mime); + if (!empty($this->imgConverter)) { + if (isset($this->imgConverter[$mime])) { + return true; + } + if (isset($this->imgConverter[$type])) { + return true; + } + } + return $this->imgLib + && ( + ($type === 'image' && ($this->imgLib === 'gd' ? in_array($stat['mime'], array('image/jpeg', 'image/png', 'image/gif', 'image/x-ms-bmp')) : true)) + || + (ELFINDER_IMAGEMAGICK_PS && $this->imgLib !== 'gd' && in_array($stat['mime'], array('application/postscript', 'application/pdf'))) + ); + } + return false; + } + + /** + * Return true if required file can be resized. + * By default - the same as canCreateTmb + * + * @param string $path thumnbnail path + * @param array $stat file stat + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + protected function canResize($path, $stat) + { + return $this->canCreateTmb($path, $stat, false); + } + + /** + * Create thumnbnail and return it's URL on success + * + * @param string $path file path + * @param $stat + * + * @return false|string + * @internal param string $mime file mime type + * @throws elFinderAbortException + * @throws ImagickException + * @author Dmitry (dio) Levashov + */ + protected function createTmb($path, $stat) + { + if (!$stat || !$this->canCreateTmb($path, $stat)) { + return false; + } + + $name = $this->tmbname($stat); + $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; + + $maxlength = -1; + $imgConverter = null; + + // check imgConverter + $mime = strtolower($stat['mime']); + list($type) = explode('/', $mime); + if (isset($this->imgConverter[$mime])) { + $imgConverter = $this->imgConverter[$mime]['func']; + if (!empty($this->imgConverter[$mime]['maxlen'])) { + $maxlength = intval($this->imgConverter[$mime]['maxlen']); + } + } else if (isset($this->imgConverter[$type])) { + $imgConverter = $this->imgConverter[$type]['func']; + if (!empty($this->imgConverter[$type]['maxlen'])) { + $maxlength = intval($this->imgConverter[$type]['maxlen']); + } + } + if ($imgConverter && !is_callable($imgConverter)) { + return false; + } + + // copy image into tmbPath so some drivers does not store files on local fs + if (($src = $this->fopenCE($path, 'rb')) == false) { + return false; + } + + if (($trg = fopen($tmb, 'wb')) == false) { + $this->fcloseCE($src, $path); + return false; + } + + stream_copy_to_stream($src, $trg, $maxlength); + + $this->fcloseCE($src, $path); + fclose($trg); + + // call imgConverter + if ($imgConverter) { + if (!call_user_func_array($imgConverter, array($tmb, $stat, $this))) { + file_exists($tmb) && unlink($tmb); + return false; + } + } + + $result = false; + + $tmbSize = $this->tmbSize; + + if ($this->imgLib === 'imagick') { + try { + $imagickTest = new imagick($tmb . '[0]'); + $imagickTest->clear(); + $imagickTest = true; + } catch (Exception $e) { + $imagickTest = false; + } + } + + if (($this->imgLib === 'imagick' && !$imagickTest) || ($s = getimagesize($tmb)) === false) { + if ($this->imgLib === 'imagick') { + $bgcolor = $this->options['tmbBgColor']; + if ($bgcolor === 'transparent') { + $bgcolor = 'rgba(255, 255, 255, 0.0)'; + } + try { + $imagick = new imagick(); + $imagick->setBackgroundColor(new ImagickPixel($bgcolor)); + $imagick->readImage($this->getExtentionByMime($stat['mime'], ':') . $tmb . '[0]'); + try { + $imagick->trimImage(0); + } catch (Exception $e) { + } + $imagick->setImageFormat('png'); + $imagick->writeImage($tmb); + $imagick->clear(); + if (($s = getimagesize($tmb)) !== false) { + $result = true; + } + } catch (Exception $e) { + } + } else if ($this->imgLib === 'convert') { + $convParams = $this->imageMagickConvertPrepare($tmb, 'png', 100, array(), $stat['mime']); + $cmd = sprintf('%s -colorspace sRGB -trim %s %s', ELFINDER_CONVERT_PATH, $convParams['quotedPath'], $convParams['quotedDstPath']); + $result = false; + if ($this->procExec($cmd) === 0) { + if (($s = getimagesize($tmb)) !== false) { + $result = true; + } + } + } + if (!$result) { + // fallback imgLib to GD + if (function_exists('gd_info') && ($s = getimagesize($tmb))) { + $this->imgLib = 'gd'; + } else { + file_exists($tmb) && unlink($tmb); + return false; + } + } + } + + /* If image smaller or equal thumbnail size - just fitting to thumbnail square */ + if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { + $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png'); + } else { + + if ($this->options['tmbCrop']) { + + $result = $tmb; + /* Resize and crop if image bigger than thumbnail */ + if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { + $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); + } + + if ($result && ($s = getimagesize($tmb)) != false) { + $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0; + $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0; + $result = $this->imgCrop($result, $tmbSize, $tmbSize, $x, $y, 'png'); + } else { + $result = false; + } + + } else { + $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png'); + } + + if ($result) { + if ($s = getimagesize($tmb)) { + if ($s[0] !== $tmbSize || $s[1] !== $tmbSize) { + $result = $this->imgSquareFit($result, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png'); + } + } + } + } + + if (!$result) { + unlink($tmb); + return false; + } + + return $name; + } + + /** + * Resize image + * + * @param string $path image file + * @param int $width new width + * @param int $height new height + * @param bool $keepProportions crop image + * @param bool $resizeByBiggerSide resize image based on bigger side if true + * @param string $destformat image destination format + * @param int $jpgQuality JEPG quality (1-100) + * @param array $options Other extra options + * + * @return string|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + */ + protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null, $jpgQuality = null, $options = array()) + { + if (($s = getimagesize($path)) == false) { + return false; + } + + if (!$jpgQuality) { + $jpgQuality = $this->options['jpgQuality']; + } + + list($orig_w, $orig_h) = array($s[0], $s[1]); + list($size_w, $size_h) = array($width, $height); + + if (empty($options['unenlarge']) || $orig_w > $size_w || $orig_h > $size_h) { + if ($keepProportions == true) { + /* Resizing by biggest side */ + if ($resizeByBiggerSide) { + if ($orig_w > $orig_h) { + $size_h = round($orig_h * $width / $orig_w); + $size_w = $width; + } else { + $size_w = round($orig_w * $height / $orig_h); + $size_h = $height; + } + } else { + if ($orig_w > $orig_h) { + $size_w = round($orig_w * $height / $orig_h); + $size_h = $height; + } else { + $size_h = round($orig_h * $width / $orig_w); + $size_w = $width; + } + } + } + } else { + $size_w = $orig_w; + $size_h = $orig_h; + } + + elFinder::extendTimeLimit(300); + switch ($this->imgLib) { + case 'imagick': + + try { + $img = new imagick($path); + } catch (Exception $e) { + return false; + } + + // Imagick::FILTER_BOX faster than FILTER_LANCZOS so use for createTmb + // resize bench: http://app-mgng.rhcloud.com/9 + // resize sample: http://www.dylanbeattie.net/magick/filters/result.html + $filter = ($destformat === 'png' /* createTmb */) ? Imagick::FILTER_BOX : Imagick::FILTER_LANCZOS; + + $ani = ($img->getNumberImages() > 1); + if ($ani && is_null($destformat)) { + $img = $img->coalesceImages(); + do { + $img->resizeImage($size_w, $size_h, $filter, 1); + } while ($img->nextImage()); + $img->optimizeImageLayers(); + $result = $img->writeImages($path, true); + } else { + if ($ani) { + $img->setFirstIterator(); + } + if (strtoupper($img->getImageFormat()) === 'JPEG') { + $img->setImageCompression(imagick::COMPRESSION_JPEG); + $img->setImageCompressionQuality($jpgQuality); + if (isset($options['preserveExif']) && !$options['preserveExif']) { + try { + $orientation = $img->getImageOrientation(); + } catch (ImagickException $e) { + $orientation = 0; + } + $img->stripImage(); + if ($orientation) { + $img->setImageOrientation($orientation); + } + } + if ($this->options['jpgProgressive']) { + $img->setInterlaceScheme(Imagick::INTERLACE_PLANE); + } + } + $img->resizeImage($size_w, $size_h, $filter, true); + if ($destformat) { + $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); + } else { + $result = $img->writeImage($path); + } + } + + $img->clear(); + + return $result ? $path : false; + + break; + + case 'convert': + extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); + $filter = ($destformat === 'png' /* createTmb */) ? '-filter Box' : '-filter Lanczos'; + $strip = (isset($options['preserveExif']) && !$options['preserveExif']) ? ' -strip' : ''; + $cmd = sprintf('%s %s%s%s%s%s %s -geometry %dx%d! %s %s', ELFINDER_CONVERT_PATH, $quotedPath, $coalesce, $jpgQuality, $strip, $interlace, $filter, $size_w, $size_h, $deconstruct, $quotedDstPath); + + $result = false; + if ($this->procExec($cmd) === 0) { + $result = true; + } + return $result ? $path : false; + + break; + + case 'gd': + elFinder::expandMemoryForGD(array($s, array($size_w, $size_h))); + $img = $this->gdImageCreate($path, $s['mime']); + + if ($img && false != ($tmp = imagecreatetruecolor($size_w, $size_h))) { + + $bgNum = false; + if ($s[2] === IMAGETYPE_GIF && (!$destformat || $destformat === 'gif')) { + $bgIdx = imagecolortransparent($img); + if ($bgIdx !== -1) { + $c = imagecolorsforindex($img, $bgIdx); + $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']); + imagefill($tmp, 0, 0, $bgNum); + imagecolortransparent($tmp, $bgNum); + } + } + if ($bgNum === false) { + $this->gdImageBackground($tmp, 'transparent'); + } + + if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, $size_w, $size_h, $s[0], $s[1])) { + return false; + } + + $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); + + imagedestroy($img); + imagedestroy($tmp); + + return $result ? $path : false; + + } + break; + } + + return false; + } + + /** + * Crop image + * + * @param string $path image file + * @param int $width crop width + * @param int $height crop height + * @param bool $x crop left offset + * @param bool $y crop top offset + * @param string $destformat image destination format + * @param int $jpgQuality JEPG quality (1-100) + * + * @return string|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + */ + protected function imgCrop($path, $width, $height, $x, $y, $destformat = null, $jpgQuality = null) + { + if (($s = getimagesize($path)) == false) { + return false; + } + + if (!$jpgQuality) { + $jpgQuality = $this->options['jpgQuality']; + } + + elFinder::extendTimeLimit(300); + switch ($this->imgLib) { + case 'imagick': + + try { + $img = new imagick($path); + } catch (Exception $e) { + return false; + } + + $ani = ($img->getNumberImages() > 1); + if ($ani && is_null($destformat)) { + $img = $img->coalesceImages(); + do { + $img->setImagePage($s[0], $s[1], 0, 0); + $img->cropImage($width, $height, $x, $y); + $img->setImagePage($width, $height, 0, 0); + } while ($img->nextImage()); + $img->optimizeImageLayers(); + $result = $img->writeImages($path, true); + } else { + if ($ani) { + $img->setFirstIterator(); + } + $img->setImagePage($s[0], $s[1], 0, 0); + $img->cropImage($width, $height, $x, $y); + $img->setImagePage($width, $height, 0, 0); + $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); + } + + $img->clear(); + + return $result ? $path : false; + + break; + + case 'convert': + extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); + $cmd = sprintf('%s %s%s%s%s -crop %dx%d+%d+%d%s %s', ELFINDER_CONVERT_PATH, $quotedPath, $coalesce, $jpgQuality, $interlace, $width, $height, $x, $y, $deconstruct, $quotedDstPath); + + $result = false; + if ($this->procExec($cmd) === 0) { + $result = true; + } + return $result ? $path : false; + + break; + + case 'gd': + elFinder::expandMemoryForGD(array($s, array($width, $height))); + $img = $this->gdImageCreate($path, $s['mime']); + + if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) { + + $bgNum = false; + if ($s[2] === IMAGETYPE_GIF && (!$destformat || $destformat === 'gif')) { + $bgIdx = imagecolortransparent($img); + if ($bgIdx !== -1) { + $c = imagecolorsforindex($img, $bgIdx); + $bgNum = imagecolorallocate($tmp, $c['red'], $c['green'], $c['blue']); + imagefill($tmp, 0, 0, $bgNum); + imagecolortransparent($tmp, $bgNum); + } + } + if ($bgNum === false) { + $this->gdImageBackground($tmp, 'transparent'); + } + + $size_w = $width; + $size_h = $height; + + if ($s[0] < $width || $s[1] < $height) { + $size_w = $s[0]; + $size_h = $s[1]; + } + + if (!imagecopy($tmp, $img, 0, 0, $x, $y, $size_w, $size_h)) { + return false; + } + + $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); + + imagedestroy($img); + imagedestroy($tmp); + + return $result ? $path : false; + + } + break; + } + + return false; + } + + /** + * Put image to square + * + * @param string $path image file + * @param int $width square width + * @param int $height square height + * @param int|string $align reserved + * @param int|string $valign reserved + * @param string $bgcolor square background color in #rrggbb format + * @param string $destformat image destination format + * @param int $jpgQuality JEPG quality (1-100) + * + * @return false|string + * @throws ImagickException + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + */ + protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null, $jpgQuality = null) + { + if (($s = getimagesize($path)) == false) { + return false; + } + + $result = false; + + /* Coordinates for image over square aligning */ + $y = ceil(abs($height - $s[1]) / 2); + $x = ceil(abs($width - $s[0]) / 2); + + if (!$jpgQuality) { + $jpgQuality = $this->options['jpgQuality']; + } + + elFinder::extendTimeLimit(300); + switch ($this->imgLib) { + case 'imagick': + try { + $img = new imagick($path); + } catch (Exception $e) { + return false; + } + + if ($bgcolor === 'transparent') { + $bgcolor = 'rgba(255, 255, 255, 0.0)'; + } + $ani = ($img->getNumberImages() > 1); + if ($ani && is_null($destformat)) { + $img1 = new Imagick(); + $img1->setFormat('gif'); + $img = $img->coalesceImages(); + do { + $gif = new Imagick(); + $gif->newImage($width, $height, new ImagickPixel($bgcolor)); + $gif->setImageColorspace($img->getImageColorspace()); + $gif->setImageFormat('gif'); + $gif->compositeImage($img, imagick::COMPOSITE_OVER, $x, $y); + $gif->setImageDelay($img->getImageDelay()); + $gif->setImageIterations($img->getImageIterations()); + $img1->addImage($gif); + $gif->clear(); + } while ($img->nextImage()); + $img1->optimizeImageLayers(); + $result = $img1->writeImages($path, true); + } else { + if ($ani) { + $img->setFirstIterator(); + } + $img1 = new Imagick(); + $img1->newImage($width, $height, new ImagickPixel($bgcolor)); + $img1->setImageColorspace($img->getImageColorspace()); + $img1->compositeImage($img, imagick::COMPOSITE_OVER, $x, $y); + $result = $this->imagickImage($img1, $path, $destformat, $jpgQuality); + } + + $img1->clear(); + $img->clear(); + return $result ? $path : false; + + break; + + case 'convert': + extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); + if ($bgcolor === 'transparent') { + $bgcolor = 'rgba(255, 255, 255, 0.0)'; + } + $cmd = sprintf('%s -size %dx%d "xc:%s" png:- | convert%s%s%s png:- %s -geometry +%d+%d -compose over -composite%s %s', ELFINDER_CONVERT_PATH, $width, $height, $bgcolor, $coalesce, $jpgQuality, $interlace, $quotedPath, $x, $y, $deconstruct, $quotedDstPath); + + $result = false; + if ($this->procExec($cmd) === 0) { + $result = true; + } + return $result ? $path : false; + + break; + + case 'gd': + elFinder::expandMemoryForGD(array($s, array($width, $height))); + $img = $this->gdImageCreate($path, $s['mime']); + + if ($img && false != ($tmp = imagecreatetruecolor($width, $height))) { + + $this->gdImageBackground($tmp, $bgcolor); + if ($bgcolor === 'transparent' && ($destformat === 'png' || $s[2] === IMAGETYPE_PNG)) { + $bgNum = imagecolorallocatealpha($tmp, 255, 255, 255, 127); + imagefill($tmp, 0, 0, $bgNum); + } + + if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) { + return false; + } + + $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); + + imagedestroy($img); + imagedestroy($tmp); + + return $result ? $path : false; + } + break; + } + + return false; + } + + /** + * Rotate image + * + * @param string $path image file + * @param int $degree rotete degrees + * @param string $bgcolor square background color in #rrggbb format + * @param string $destformat image destination format + * @param int $jpgQuality JEPG quality (1-100) + * + * @return string|false + * @throws elFinderAbortException + * @author nao-pon + * @author Troex Nevelin + */ + protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null, $jpgQuality = null) + { + if (($s = getimagesize($path)) == false || $degree % 360 === 0) { + return false; + } + + $result = false; + + // try lossless rotate + if ($degree % 90 === 0 && in_array($s[2], array(IMAGETYPE_JPEG, IMAGETYPE_JPEG2000))) { + $count = ($degree / 90) % 4; + $exiftran = array( + 1 => '-9', + 2 => '-1', + 3 => '-2' + ); + $jpegtran = array( + 1 => '90', + 2 => '180', + 3 => '270' + ); + $quotedPath = escapeshellarg($path); + $cmds = array(); + if ($this->procExec(ELFINDER_EXIFTRAN_PATH . ' -h') === 0) { + $cmds[] = ELFINDER_EXIFTRAN_PATH . ' -i ' . $exiftran[$count] . ' ' . $path; + } + if ($this->procExec(ELFINDER_JPEGTRAN_PATH . ' -version') === 0) { + $cmds[] = ELFINDER_JPEGTRAN_PATH . ' -rotate ' . $jpegtran[$count] . ' -copy all -outfile ' . $quotedPath . ' ' . $quotedPath; + } + foreach ($cmds as $cmd) { + if ($this->procExec($cmd) === 0) { + $result = true; + break; + } + } + if ($result) { + return $path; + } + } + + if (!$jpgQuality) { + $jpgQuality = $this->options['jpgQuality']; + } + + elFinder::extendTimeLimit(300); + switch ($this->imgLib) { + case 'imagick': + try { + $img = new imagick($path); + } catch (Exception $e) { + return false; + } + + if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) { + $bgcolor = 'rgba(255, 255, 255, 0.0)'; + } + if ($img->getNumberImages() > 1) { + $img = $img->coalesceImages(); + do { + $img->rotateImage(new ImagickPixel($bgcolor), $degree); + } while ($img->nextImage()); + $img->optimizeImageLayers(); + $result = $img->writeImages($path, true); + } else { + $img->rotateImage(new ImagickPixel($bgcolor), $degree); + $result = $this->imagickImage($img, $path, $destformat, $jpgQuality); + } + $img->clear(); + return $result ? $path : false; + + break; + + case 'convert': + extract($this->imageMagickConvertPrepare($path, $destformat, $jpgQuality, $s)); + if ($s[2] === IMAGETYPE_GIF || $s[2] === IMAGETYPE_PNG) { + $bgcolor = 'rgba(255, 255, 255, 0.0)'; + } + $cmd = sprintf('%s %s%s%s%s -background "%s" -rotate %d%s %s', ELFINDER_CONVERT_PATH, $quotedPath, $coalesce, $jpgQuality, $interlace, $bgcolor, $degree, $deconstruct, $quotedDstPath); + + $result = false; + if ($this->procExec($cmd) === 0) { + $result = true; + } + return $result ? $path : false; + + break; + + case 'gd': + elFinder::expandMemoryForGD(array($s, $s)); + $img = $this->gdImageCreate($path, $s['mime']); + + $degree = 360 - $degree; + + $bgNum = -1; + $bgIdx = false; + if ($s[2] === IMAGETYPE_GIF) { + $bgIdx = imagecolortransparent($img); + if ($bgIdx !== -1) { + $c = imagecolorsforindex($img, $bgIdx); + $w = imagesx($img); + $h = imagesy($img); + $newImg = imagecreatetruecolor($w, $h); + imagepalettecopy($newImg, $img); + $bgNum = imagecolorallocate($newImg, $c['red'], $c['green'], $c['blue']); + imagefill($newImg, 0, 0, $bgNum); + imagecolortransparent($newImg, $bgNum); + imagecopy($newImg, $img, 0, 0, 0, 0, $w, $h); + imagedestroy($img); + $img = $newImg; + $newImg = null; + } + } else if ($s[2] === IMAGETYPE_PNG) { + $bgNum = imagecolorallocatealpha($img, 255, 255, 255, 127); + } + if ($bgNum === -1) { + list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); + $bgNum = imagecolorallocate($img, $r, $g, $b); + } + + $tmp = imageRotate($img, $degree, $bgNum); + if ($bgIdx !== -1) { + imagecolortransparent($tmp, $bgNum); + } + + $result = $this->gdImage($tmp, $path, $destformat, $s['mime'], $jpgQuality); + + imageDestroy($img); + imageDestroy($tmp); + + return $result ? $path : false; + + break; + } + + return false; + } + + /** + * Execute shell command + * + * @param string $command command line + * @param string $output stdout strings + * @param int $return_var process exit code + * @param string $error_output stderr strings + * + * @return int exit code + * @throws elFinderAbortException + * @author Alexey Sukhotin + */ + protected function procExec($command, &$output = '', &$return_var = -1, &$error_output = '') + { + + static $allowed = null; + + if ($allowed === null) { + if ($allowed = function_exists('proc_open')) { + if ($disabled = ini_get('disable_functions')) { + $funcs = array_map('trim', explode(',', $disabled)); + $allowed = !in_array('proc_open', $funcs); + } + } + } + + if (!$allowed) { + $return_var = -1; + return $return_var; + } + + if (!$command) { + $return_var = 0; + return $return_var; + } + + $descriptorspec = array( + 0 => array("pipe", "r"), // stdin + 1 => array("pipe", "w"), // stdout + 2 => array("pipe", "w") // stderr + ); + + $process = proc_open($command, $descriptorspec, $pipes, null, null); + + if (is_resource($process)) { + stream_set_blocking($pipes[1], 0); + stream_set_blocking($pipes[2], 0); + + fclose($pipes[0]); + + $tmpout = ''; + $tmperr = ''; + while (feof($pipes[1]) === false || feof($pipes[2]) === false) { + elFinder::extendTimeLimit(); + $read = array($pipes[1], $pipes[2]); + $write = null; + $except = null; + $ret = stream_select($read, $write, $except, 1); + if ($ret === false) { + // error + break; + } else if ($ret === 0) { + // timeout + continue; + } else { + foreach ($read as $sock) { + if ($sock === $pipes[1]) { + $tmpout .= fread($sock, 4096); + } else if ($sock === $pipes[2]) { + $tmperr .= fread($sock, 4096); + } + } + } + } + + fclose($pipes[1]); + fclose($pipes[2]); + + $error_output = $tmpout; + $output = $tmperr; + $return_var = proc_close($process); + + } else { + $return_var = -1; + } + + return $return_var; + + } + + /** + * Remove thumbnail, also remove recursively if stat is directory + * + * @param array $stat file stat + * + * @return void + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Naoki Sawada + * @author Troex Nevelin + */ + protected function rmTmb($stat) + { + if ($this->tmbPathWritable) { + if ($stat['mime'] === 'directory') { + foreach ($this->scandirCE($this->decode($stat['hash'])) as $p) { + elFinder::extendTimeLimit(30); + $name = $this->basenameCE($p); + $name != '.' && $name != '..' && $this->rmTmb($this->stat($p)); + } + } else if (!empty($stat['tmb']) && $stat['tmb'] != "1") { + $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . rawurldecode($stat['tmb']); + file_exists($tmb) && unlink($tmb); + clearstatcache(); + } + } + } + + /** + * Create an gd image according to the specified mime type + * + * @param string $path image file + * @param string $mime + * + * @return resource|false GD image resource identifier + */ + protected function gdImageCreate($path, $mime) + { + switch ($mime) { + case 'image/jpeg': + return imagecreatefromjpeg($path); + + case 'image/png': + return imagecreatefrompng($path); + + case 'image/gif': + return imagecreatefromgif($path); + + case 'image/x-ms-bmp': + if (!function_exists('imagecreatefrombmp')) { + include_once dirname(__FILE__) . '/libs/GdBmp.php'; + } + return imagecreatefrombmp($path); + + case 'image/xbm': + return imagecreatefromxbm($path); + + case 'image/xpm': + return imagecreatefromxpm($path); + } + return false; + } + + /** + * Output gd image to file + * + * @param resource $image gd image resource + * @param string $filename The path to save the file to. + * @param string $destformat The Image type to use for $filename + * @param string $mime The original image mime type + * @param int $jpgQuality JEPG quality (1-100) + * + * @return bool + */ + protected function gdImage($image, $filename, $destformat, $mime, $jpgQuality = null) + { + + if (!$jpgQuality) { + $jpgQuality = $this->options['jpgQuality']; + } + if ($destformat) { + switch ($destformat) { + case 'jpg': + $mime = 'image/jpeg'; + break; + case 'gif': + $mime = 'image/gif'; + break; + case 'png': + default: + $mime = 'image/png'; + break; + } + } + switch ($mime) { + case 'image/gif': + return imagegif($image, $filename); + case 'image/jpeg': + if ($this->options['jpgProgressive']) { + imageinterlace($image, true); + } + return imagejpeg($image, $filename, $jpgQuality); + case 'image/wbmp': + return imagewbmp($image, $filename); + case 'image/png': + default: + return imagepng($image, $filename); + } + } + + /** + * Output imagick image to file + * + * @param imagick $img imagick image resource + * @param string $filename The path to save the file to. + * @param string $destformat The Image type to use for $filename + * @param int $jpgQuality JEPG quality (1-100) + * + * @return bool + */ + protected function imagickImage($img, $filename, $destformat, $jpgQuality = null) + { + + if (!$jpgQuality) { + $jpgQuality = $this->options['jpgQuality']; + } + + try { + if ($destformat) { + if ($destformat === 'gif') { + $img->setImageFormat('gif'); + } else if ($destformat === 'png') { + $img->setImageFormat('png'); + } else if ($destformat === 'jpg') { + $img->setImageFormat('jpeg'); + } + } + if (strtoupper($img->getImageFormat()) === 'JPEG') { + $img->setImageCompression(imagick::COMPRESSION_JPEG); + $img->setImageCompressionQuality($jpgQuality); + if ($this->options['jpgProgressive']) { + $img->setInterlaceScheme(Imagick::INTERLACE_PLANE); + } + try { + $orientation = $img->getImageOrientation(); + } catch (ImagickException $e) { + $orientation = 0; + } + $img->stripImage(); + if ($orientation) { + $img->setImageOrientation($orientation); + } + } + $result = $img->writeImage($filename); + } catch (Exception $e) { + $result = false; + } + + return $result; + } + + /** + * Assign the proper background to a gd image + * + * @param resource $image gd image resource + * @param string $bgcolor background color in #rrggbb format + */ + protected function gdImageBackground($image, $bgcolor) + { + + if ($bgcolor === 'transparent') { + imagealphablending($image, false); + imagesavealpha($image, true); + } else { + list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); + $bgcolor1 = imagecolorallocate($image, $r, $g, $b); + imagefill($image, 0, 0, $bgcolor1); + } + } + + /** + * Prepare variables for exec convert of ImageMagick + * + * @param string $path + * @param string $destformat + * @param int $jpgQuality + * @param array $imageSize + * @param null $mime + * + * @return array + * @throws elFinderAbortException + */ + protected function imageMagickConvertPrepare($path, $destformat, $jpgQuality, $imageSize = null, $mime = null) + { + if (is_null($imageSize)) { + $imageSize = getimagesize($path); + } + if (is_null($mime)) { + $mime = $this->mimetype($path); + } + $srcType = $this->getExtentionByMime($mime, ':'); + $ani = false; + if (preg_match('/^(?:gif|png|ico)/', $srcType)) { + $cmd = ELFINDER_IDENTIFY_PATH . ' ' . escapeshellarg($srcType . $path); + if ($this->procExec($cmd, $o) === 0) { + $ani = preg_split('/(?:\r\n|\n|\r)/', trim($o)); + if (count($ani) < 2) { + $ani = false; + } + } + } + $coalesce = $index = $interlace = ''; + $deconstruct = ' +repage'; + $index = '[0]'; + if ($ani) { + if (is_null($destformat)) { + $coalesce = ' -coalesce -repage 0x0'; + $deconstruct = ' +repage -deconstruct -layers optimize'; + } else if ($imageSize) { + if ($srcType === 'ico:') { + foreach ($ani as $_i => $_info) { + if (preg_match('/ (\d+)x(\d+) /', $_info, $m)) { + if ($m[1] == $imageSize[0] && $m[2] == $imageSize[1]) { + $index = '[' . $_i . ']'; + break; + } + } + } + } + } + } + if ($imageSize && ($imageSize[2] === IMAGETYPE_JPEG || $imageSize[2] === IMAGETYPE_JPEG2000)) { + $jpgQuality = ' -quality ' . $jpgQuality; + if ($this->options['jpgProgressive']) { + $interlace = ' -interlace Plane'; + } + } else { + $jpgQuality = ''; + } + $quotedPath = escapeshellarg($srcType . $path . $index); + $quotedDstPath = escapeshellarg(($destformat ? ($destformat . ':') : $srcType) . $path); + return compact('ani', 'index', 'coalesce', 'deconstruct', 'jpgQuality', 'quotedPath', 'quotedDstPath', 'interlace'); + } + + /*********************** misc *************************/ + + /** + * Return smart formatted date + * + * @param int $ts file timestamp + * + * @return string + * @author Dmitry (dio) Levashov + **/ + // protected function formatDate($ts) { + // if ($ts > $this->today) { + // return 'Today '.date($this->options['timeFormat'], $ts); + // } + // + // if ($ts > $this->yesterday) { + // return 'Yesterday '.date($this->options['timeFormat'], $ts); + // } + // + // return date($this->options['dateFormat'], $ts); + // } + + /** + * Find position of first occurrence of string in a string with multibyte support + * + * @param string $haystack The string being checked. + * @param string $needle The string to find in haystack. + * @param int $offset The search offset. If it is not specified, 0 is used. + * + * @return int|bool + * @author Alexey Sukhotin + **/ + protected function stripos($haystack, $needle, $offset = 0) + { + if (function_exists('mb_stripos')) { + return mb_stripos($haystack, $needle, $offset, 'UTF-8'); + } else if (function_exists('mb_strtolower') && function_exists('mb_strpos')) { + return mb_strpos(mb_strtolower($haystack, 'UTF-8'), mb_strtolower($needle, 'UTF-8'), $offset); + } + return stripos($haystack, $needle, $offset); + } + + /** + * Default serach match method (name match) + * + * @param String $name Item name + * @param String $query Query word + * @param String $path Item path + * + * @return bool @return bool + */ + protected function searchMatchName($name, $query, $path) + { + return $this->stripos($name, $query) !== false; + } + + /** + * Get server side available archivers + * + * @param bool $use_cache + * + * @return array + * @throws elFinderAbortException + */ + protected function getArchivers($use_cache = true) + { + $sessionKey = 'archivers'; + if ($use_cache) { + if (isset($this->options['archivers']) && is_array($this->options['archivers']) && $this->options['archivers']) { + $cache = $this->options['archivers']; + } else { + $cache = elFinder::$archivers; + } + if ($cache) { + return $cache; + } else { + if ($cache = $this->session->get($sessionKey, array())) { + return elFinder::$archivers = $cache; + } + } + } + + $arcs = array( + 'create' => array(), + 'extract' => array() + ); + + if ($this->procExec('') === 0) { + + $this->procExec(ELFINDER_TAR_PATH . ' --version', $o, $ctar); + + if ($ctar == 0) { + $arcs['create']['application/x-tar'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-cf', 'ext' => 'tar'); + $arcs['extract']['application/x-tar'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-xf', 'ext' => 'tar', 'toSpec' => '-C '); + unset($o); + $this->procExec(ELFINDER_GZIP_PATH . ' --version', $o, $c); + if ($c == 0) { + $arcs['create']['application/x-gzip'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-czf', 'ext' => 'tgz'); + $arcs['extract']['application/x-gzip'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-xzf', 'ext' => 'tgz', 'toSpec' => '-C '); + } + unset($o); + $this->procExec(ELFINDER_BZIP2_PATH . ' --version', $o, $c); + if ($c == 0) { + $arcs['create']['application/x-bzip2'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-cjf', 'ext' => 'tbz'); + $arcs['extract']['application/x-bzip2'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-xjf', 'ext' => 'tbz', 'toSpec' => '-C '); + } + unset($o); + $this->procExec(ELFINDER_XZ_PATH . ' --version', $o, $c); + if ($c == 0) { + $arcs['create']['application/x-xz'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-cJf', 'ext' => 'xz'); + $arcs['extract']['application/x-xz'] = array('cmd' => ELFINDER_TAR_PATH, 'argc' => '-xJf', 'ext' => 'xz', 'toSpec' => '-C '); + } + } + unset($o); + $this->procExec(ELFINDER_ZIP_PATH . ' -h', $o, $c); + if ($c == 0) { + $arcs['create']['application/zip'] = array('cmd' => ELFINDER_ZIP_PATH, 'argc' => '-r9 -q', 'ext' => 'zip'); + } + unset($o); + $this->procExec(ELFINDER_UNZIP_PATH . ' --help', $o, $c); + if ($c == 0) { + $arcs['extract']['application/zip'] = array('cmd' => ELFINDER_UNZIP_PATH, 'argc' => '-q', 'ext' => 'zip', 'toSpec' => '-d '); + } + unset($o); + $this->procExec(ELFINDER_RAR_PATH . ' --version', $o, $c); + if ($c == 0 || $c == 7) { + $arcs['create']['application/x-rar'] = array('cmd' => ELFINDER_RAR_PATH, 'argc' => 'a -inul', 'ext' => 'rar'); + } + unset($o); + $this->procExec(ELFINDER_UNRAR_PATH, $o, $c); + if ($c == 0 || $c == 7) { + $arcs['extract']['application/x-rar'] = array('cmd' => ELFINDER_UNRAR_PATH, 'argc' => 'x -y', 'ext' => 'rar', 'toSpec' => ''); + } + unset($o); + $this->procExec(ELFINDER_7Z_PATH, $o, $c); + if ($c == 0) { + $arcs['create']['application/x-7z-compressed'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a', 'ext' => '7z'); + $arcs['extract']['application/x-7z-compressed'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -y', 'ext' => '7z', 'toSpec' => '-o'); + + if (empty($arcs['create']['application/zip'])) { + $arcs['create']['application/zip'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a -tzip', 'ext' => 'zip'); + } + if (empty($arcs['extract']['application/zip'])) { + $arcs['extract']['application/zip'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -tzip -y', 'ext' => 'zip', 'toSpec' => '-o'); + } + if (empty($arcs['create']['application/x-tar'])) { + $arcs['create']['application/x-tar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'a -ttar', 'ext' => 'tar'); + } + if (empty($arcs['extract']['application/x-tar'])) { + $arcs['extract']['application/x-tar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -ttar -y', 'ext' => 'tar', 'toSpec' => '-o'); + } + if (substr(PHP_OS, 0, 3) === 'WIN' && empty($arcs['extract']['application/x-rar'])) { + $arcs['extract']['application/x-rar'] = array('cmd' => ELFINDER_7Z_PATH, 'argc' => 'x -trar -y', 'ext' => 'rar', 'toSpec' => '-o'); + } + } + + } + + // Use PHP ZipArchive Class + if (class_exists('ZipArchive', false)) { + if (empty($arcs['create']['application/zip'])) { + $arcs['create']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => array('self', 'zipArchiveZip'), 'ext' => 'zip'); + } + if (empty($arcs['extract']['application/zip'])) { + $arcs['extract']['application/zip'] = array('cmd' => 'phpfunction', 'argc' => array('self', 'zipArchiveUnzip'), 'ext' => 'zip'); + } + } + + $this->session->set($sessionKey, $arcs); + return elFinder::$archivers = $arcs; + } + + /** + * Resolve relative / (Unix-like)absolute path + * + * @param string $path target path + * @param string $base base path + * + * @return string + */ + protected function getFullPath($path, $base) + { + $separator = $this->separator; + $systemroot = $this->systemRoot; + $base = (string)$base; + + if ($base[0] === $separator && substr($base, 0, strlen($systemroot)) !== $systemroot) { + $base = $systemroot . substr($base, 1); + } + if ($base !== $systemroot) { + $base = rtrim($base, $separator); + } + + // 'Here' + if ($path === '' || $path === '.' . $separator) return $base; + + $sepquoted = preg_quote($separator, '#'); + + if (substr($path, 0, 3) === '..' . $separator) { + $path = $base . $separator . $path; + } + // normalize `/../` + $normreg = '#(' . $sepquoted . ')[^' . $sepquoted . ']+' . $sepquoted . '\.\.' . $sepquoted . '#'; // '#(/)[^\/]+/\.\./#' + while (preg_match($normreg, $path)) { + $path = preg_replace($normreg, '$1', $path, 1); + } + if ($path !== $systemroot) { + $path = rtrim($path, $separator); + } + + // Absolute path + if ($path[0] === $separator || strpos($path, $systemroot) === 0) { + return $path; + } + + $preg_separator = '#' . $sepquoted . '#'; + + // Relative path from 'Here' + if (substr($path, 0, 2) === '.' . $separator || $path[0] !== '.') { + $arrn = preg_split($preg_separator, $path, -1, PREG_SPLIT_NO_EMPTY); + if ($arrn[0] !== '.') { + array_unshift($arrn, '.'); + } + $arrn[0] = rtrim($base, $separator); + return join($separator, $arrn); + } + + return $path; + } + + /** + * Remove directory recursive on local file system + * + * @param string $dir Target dirctory path + * + * @return boolean + * @throws elFinderAbortException + * @author Naoki Sawada + */ + public function rmdirRecursive($dir) + { + return self::localRmdirRecursive($dir); + } + + /** + * Create archive and return its path + * + * @param string $dir target dir + * @param array $files files names list + * @param string $name archive name + * @param array $arc archiver options + * + * @return string|bool + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + * @author Naoki Sawada + */ + protected function makeArchive($dir, $files, $name, $arc) + { + if ($arc['cmd'] === 'phpfunction') { + if (is_callable($arc['argc'])) { + call_user_func_array($arc['argc'], array($dir, $files, $name)); + } + } else { + $cwd = getcwd(); + if (chdir($dir)) { + foreach ($files as $i => $file) { + $files[$i] = '.' . DIRECTORY_SEPARATOR . basename($file); + } + $files = array_map('escapeshellarg', $files); + + $cmd = $arc['cmd'] . ' ' . $arc['argc'] . ' ' . escapeshellarg($name) . ' ' . implode(' ', $files); + $this->procExec($cmd, $o, $c); + chdir($cwd); + } else { + return false; + } + } + $path = $dir . DIRECTORY_SEPARATOR . $name; + return file_exists($path) ? $path : false; + } + + /** + * Unpack archive + * + * @param string $path archive path + * @param array $arc archiver command and arguments (same as in $this->archivers) + * @param bool|string $mode bool: remove archive ( unlink($path) ) | string: extract to directory + * + * @return void + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + * @author Naoki Sawada + */ + protected function unpackArchive($path, $arc, $mode = true) + { + if (is_string($mode)) { + $dir = $mode; + $chdir = null; + $remove = false; + } else { + $dir = dirname($path); + $chdir = $dir; + $remove = $mode; + } + $dir = realpath($dir); + $path = realpath($path); + if ($arc['cmd'] === 'phpfunction') { + if (is_callable($arc['argc'])) { + call_user_func_array($arc['argc'], array($path, $dir)); + } + } else { + $cwd = getcwd(); + if (!$chdir || chdir($dir)) { + if ($chdir) { + $cmd = $arc['cmd'] . ' ' . $arc['argc'] . ' ' . escapeshellarg(basename($path)); + } else { + $cmd = $arc['cmd'] . ' ' . $arc['argc'] . ' ' . escapeshellarg($path) . ' ' . $arc['toSpec'] . escapeshellarg($dir); + } + $this->procExec($cmd, $o, $c); + $chdir && chdir($cwd); + } + } + $remove && unlink($path); + } + + /** + * Check and filter the extracted items + * + * @param string $path target local path + * @param array $checks types to check default: ['symlink', 'name', 'writable', 'mime'] + * + * @return array ['symlinks' => [], 'names' => [], 'writables' => [], 'mimes' => [], 'rmNames' => [], 'totalSize' => 0] + * @throws elFinderAbortException + * @throws Exception + * @author Naoki Sawada + */ + protected function checkExtractItems($path, $checks = null) + { + if (is_null($checks) || !is_array($checks)) { + $checks = array('symlink', 'name', 'writable', 'mime'); + } + $chkSymlink = in_array('symlink', $checks); + $chkName = in_array('name', $checks); + $chkWritable = in_array('writable', $checks); + $chkMime = in_array('mime', $checks); + + $res = array( + 'symlinks' => array(), + 'names' => array(), + 'writables' => array(), + 'mimes' => array(), + 'rmNames' => array(), + 'totalSize' => 0 + ); + + if (is_dir($path)) { + foreach (self::localScandir($path) as $name) { + $p = $path . DIRECTORY_SEPARATOR . $name; + if (!is_readable($p)) { + // Perhaps a symbolic link to open_basedir restricted location + self::localRmdirRecursive($p); + $res['symlinks'][] = $p; + $res['rmNames'][] = $name; + continue; + } + if ($chkSymlink && is_link($p)) { + self::localRmdirRecursive($p); + $res['symlinks'][] = $p; + $res['rmNames'][] = $name; + continue; + } + $isDir = is_dir($p); + if ($chkName && !$this->nameAccepted($name, $isDir)) { + self::localRmdirRecursive($p); + $res['names'][] = $p; + $res['rmNames'][] = $name; + continue; + } + if ($chkWritable && !$this->attr($p, 'write', null, $isDir)) { + self::localRmdirRecursive($p); + $res['writables'][] = $p; + $res['rmNames'][] = $name; + continue; + } + if ($chkMime && ($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && !$this->allowPutMime($mimeByName)) { + self::localRmdirRecursive($p); + $res['mimes'][] = $p; + $res['rmNames'][] = $name; + continue; + } + if ($isDir) { + $cRes = $this->checkExtractItems($p, $checks); + foreach ($cRes as $k => $v) { + if (is_array($v)) { + $res[$k] = array_merge($res[$k], $cRes[$k]); + } else { + $res[$k] += $cRes[$k]; + } + } + } else { + $res['totalSize'] += (int)sprintf('%u', filesize($p)); + } + } + $res['rmNames'] = array_unique($res['rmNames']); + } else { + $name = basename($path); + if ($chkSymlink && is_link($path)) { + unlink($path); + $res['symlinks'][] = $path; + $res['rmNames'][] = basename($path); + } else if ($chkName && !$this->nameAccepted($name, false)) { + unlink($path); + $res['names'][] = $path; + $res['rmNames'][] = $name; + } else if ($chkWritable && !$this->attr($path, 'write', null, false)) { + unlink($path); + $res['writables'][] = $path; + $res['rmNames'][] = $name; + } else if ($chkMime && ($mimeByName = elFinderVolumeDriver::mimetypeInternalDetect($name)) && !$this->allowPutMime($mimeByName)) { + unlink($path); + $res['mimes'][] = $path; + $res['rmNames'][] = $name; + } else { + $res['totalSize'] += (int)sprintf('%u', filesize($path)); + } + } + + return $res; + } + + /** + * Return files of target directory that is dotfiles excludes. + * + * @param string $dir target directory path + * + * @return array + * @throws Exception + * @author Naoki Sawada + */ + protected static function localScandir($dir) + { + // PHP function scandir() is not work well in specific environment. I dont know why. + // ref. https://github.com/Studio-42/elFinder/issues/1248 + $files = array(); + if ($dh = opendir($dir)) { + while (false !== ($file = readdir($dh))) { + if ($file !== '.' && $file !== '..') { + $files[] = $file; + } + } + closedir($dh); + } else { + throw new Exception('Can not open local directory.'); + } + return $files; + } + + /** + * Remove directory recursive on local file system + * + * @param string $dir Target dirctory path + * + * @return boolean + * @throws elFinderAbortException + * @author Naoki Sawada + */ + protected static function localRmdirRecursive($dir) + { + // try system command + if (is_callable('exec')) { + $o = ''; + $r = 1; + if (substr(PHP_OS, 0, 3) === 'WIN') { + if (!is_link($dir) && is_dir($dir)) { + exec('rd /S /Q ' . escapeshellarg($dir), $o, $r); + } else { + exec('del /F /Q ' . escapeshellarg($dir), $o, $r); + } + } else { + exec('rm -rf ' . escapeshellarg($dir), $o, $r); + } + if ($r === 0) { + return true; + } + } + if (!is_link($dir) && is_dir($dir)) { + chmod($dir, 0777); + if ($handle = opendir($dir)) { + while (false !== ($file = readdir($handle))) { + if ($file === '.' || $file === '..') { + continue; + } + elFinder::extendTimeLimit(30); + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (!is_link($dir) && is_dir($path)) { + self::localRmdirRecursive($path); + } else { + chmod($path, 0666); + unlink($path); + } + } + closedir($handle); + } + return rmdir($dir); + } else { + chmod($dir, 0666); + return unlink($dir); + } + } + + /** + * Move item recursive on local file system + * + * @param string $src + * @param string $target + * @param bool $overWrite + * @param bool $copyJoin + * + * @return boolean + * @throws elFinderAbortException + * @throws Exception + * @author Naoki Sawada + */ + protected static function localMoveRecursive($src, $target, $overWrite = true, $copyJoin = true) + { + $res = false; + if (!file_exists($target)) { + return rename($src, $target); + } + if (!$copyJoin || !is_dir($target)) { + if ($overWrite) { + if (is_dir($target)) { + $del = self::localRmdirRecursive($target); + } else { + $del = unlink($target); + } + if ($del) { + return rename($src, $target); + } + } + } else { + foreach (self::localScandir($src) as $item) { + $res |= self::localMoveRecursive($src . DIRECTORY_SEPARATOR . $item, $target . DIRECTORY_SEPARATOR . $item, $overWrite, $copyJoin); + } + } + return (bool)$res; + } + + /** + * Create Zip archive using PHP class ZipArchive + * + * @param string $dir target dir + * @param array $files files names list + * @param string|object $zipPath Zip archive name + * + * @return bool + * @author Naoki Sawada + */ + protected static function zipArchiveZip($dir, $files, $zipPath) + { + try { + if ($start = is_string($zipPath)) { + $zip = new ZipArchive(); + if ($zip->open($dir . DIRECTORY_SEPARATOR . $zipPath, ZipArchive::CREATE) !== true) { + $zip = false; + } + } else { + $zip = $zipPath; + } + if ($zip) { + foreach ($files as $file) { + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (is_dir($path)) { + $zip->addEmptyDir($file); + $_files = array(); + if ($handle = opendir($path)) { + while (false !== ($entry = readdir($handle))) { + if ($entry !== "." && $entry !== "..") { + $_files[] = $file . DIRECTORY_SEPARATOR . $entry; + } + } + closedir($handle); + } + if ($_files) { + self::zipArchiveZip($dir, $_files, $zip); + } + } else { + $zip->addFile($path, $file); + } + } + $start && $zip->close(); + } + } catch (Exception $e) { + return false; + } + return true; + } + + /** + * Unpack Zip archive using PHP class ZipArchive + * + * @param string $zipPath Zip archive name + * @param string $toDir Extract to path + * + * @return bool + * @author Naoki Sawada + */ + protected static function zipArchiveUnzip($zipPath, $toDir) + { + try { + $zip = new ZipArchive(); + if ($zip->open($zipPath) === true) { + $zip->extractTo($toDir); + $zip->close(); + } + } catch (Exception $e) { + return false; + } + return true; + } + + /** + * Recursive symlinks search + * + * @param string $path file/dir path + * + * @return bool + * @throws Exception + * @author Dmitry (dio) Levashov + */ + protected static function localFindSymlinks($path) + { + if (is_link($path)) { + return true; + } + + if (is_dir($path)) { + foreach (self::localScandir($path) as $name) { + $p = $path . DIRECTORY_SEPARATOR . $name; + if (is_link($p)) { + return true; + } + if (is_dir($p) && self::localFindSymlinks($p)) { + return true; + } + } + } + + return false; + } + + /**==================================* abstract methods *====================================**/ + + /*********************** paths/urls *************************/ + + /** + * Return parent directory path + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _dirname($path); + + /** + * Return file name + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _basename($path); + + /** + * Join dir name and file name and return full path. + * Some drivers (db) use int as path - so we give to concat path to driver itself + * + * @param string $dir dir path + * @param string $name file name + * + * @return string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _joinPath($dir, $name); + + /** + * Return normalized path + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _normpath($path); + + /** + * Return file path related to root dir + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _relpath($path); + + /** + * Convert path related to root dir into real path + * + * @param string $path rel file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _abspath($path); + + /** + * Return fake path started from root dir. + * Required to show path on client side. + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _path($path); + + /** + * Return true if $path is children of $parent + * + * @param string $path path to check + * @param string $parent parent path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _inpath($path, $parent); + + /** + * Return stat for given path. + * Stat contains following fields: + * - (int) size file size in b. required + * - (int) ts file modification time in unix time. required + * - (string) mime mimetype. required for folders, others - optionally + * - (bool) read read permissions. required + * - (bool) write write permissions. required + * - (bool) locked is object locked. optionally + * - (bool) hidden is object hidden. optionally + * - (string) alias for symlinks - link target path relative to root path. optionally + * - (string) target for symlinks - link target path. optionally + * If file does not exists - returns empty array or false. + * + * @param string $path file path + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + abstract protected function _stat($path); + + + /***************** file stat ********************/ + + + /** + * Return true if path is dir and has at least one childs directory + * + * @param string $path dir path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _subdirs($path); + + /** + * Return object width and height + * Ususaly used for images, but can be realize for video etc... + * + * @param string $path file path + * @param string $mime file mime type + * + * @return string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _dimensions($path, $mime); + + /******************** file/dir content *********************/ + + /** + * Return files list in directory + * + * @param string $path dir path + * + * @return array + * @author Dmitry (dio) Levashov + **/ + abstract protected function _scandir($path); + + /** + * Open file and return file pointer + * + * @param string $path file path + * @param string $mode open mode + * + * @return resource|false + * @author Dmitry (dio) Levashov + **/ + abstract protected function _fopen($path, $mode = "rb"); + + /** + * Close opened file + * + * @param resource $fp file pointer + * @param string $path file path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _fclose($fp, $path = ''); + + /******************** file/dir manipulations *************************/ + + /** + * Create dir and return created dir path or false on failed + * + * @param string $path parent dir path + * @param string $name new directory name + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _mkdir($path, $name); + + /** + * Create file and return it's path or false on failed + * + * @param string $path parent dir path + * @param string $name new file name + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _mkfile($path, $name); + + /** + * Create symlink + * + * @param string $source file to link to + * @param string $targetDir folder to create link in + * @param string $name symlink name + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _symlink($source, $targetDir, $name); + + /** + * Copy file into another file (only inside one volume) + * + * @param string $source source file path + * @param $targetDir + * @param string $name file name + * + * @return bool|string + * @internal param string $target target dir path + * @author Dmitry (dio) Levashov + */ + abstract protected function _copy($source, $targetDir, $name); + + /** + * Move file into another parent dir. + * Return new file path or false. + * + * @param string $source source file path + * @param $targetDir + * @param string $name file name + * + * @return bool|string + * @internal param string $target target dir path + * @author Dmitry (dio) Levashov + */ + abstract protected function _move($source, $targetDir, $name); + + /** + * Remove file + * + * @param string $path file path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _unlink($path); + + /** + * Remove dir + * + * @param string $path dir path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _rmdir($path); + + /** + * Create new file and write into it from file pointer. + * Return new file path or false on error. + * + * @param resource $fp file pointer + * @param string $dir target dir path + * @param string $name file name + * @param array $stat file stat (required by some virtual fs) + * + * @return bool|string + * @author Dmitry (dio) Levashov + **/ + abstract protected function _save($fp, $dir, $name, $stat); + + /** + * Get file contents + * + * @param string $path file path + * + * @return string|false + * @author Dmitry (dio) Levashov + **/ + abstract protected function _getContents($path); + + /** + * Write a string to a file + * + * @param string $path file path + * @param string $content new file content + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + abstract protected function _filePutContents($path, $content); + + /** + * Extract files from archive + * + * @param string $path file path + * @param array $arc archiver options + * + * @return bool + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + **/ + abstract protected function _extract($path, $arc); + + /** + * Create archive and return its path + * + * @param string $dir target dir + * @param array $files files names list + * @param string $name archive name + * @param array $arc archiver options + * + * @return string|bool + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + **/ + abstract protected function _archive($dir, $files, $name, $arc); + + /** + * Detect available archivers + * + * @return void + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + **/ + abstract protected function _checkArchivers(); + + /** + * Change file mode (chmod) + * + * @param string $path file path + * @param string $mode octal string such as '0755' + * + * @return bool + * @author David Bartle, + **/ + abstract protected function _chmod($path, $mode); + + } // END class diff --git a/php/elFinderVolumeDropbox2.class.php b/php/elFinderVolumeDropbox2.class.php index d6a571515..142a5fb2d 100644 --- a/php/elFinderVolumeDropbox2.class.php +++ b/php/elFinderVolumeDropbox2.class.php @@ -1,11 +1,11 @@ $oauth1_token, - 'oauth1_token_secret' => $oauth1_secret, + 'oauth1_token' => $oauth1_token, + 'oauth1_token_secret' => $oauth1_secret, ]; - $auth = base64_encode($app_key.':'.$app_secret); + $auth = base64_encode($app_key . ':' . $app_secret); $ch = curl_init('https://api.dropboxapi.com/2/auth/token/from_oauth1'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', - 'Authorization: Basic '.$auth, + 'Authorization: Basic ' . $auth, ]); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $result = curl_exec($ch); @@ -272,7 +271,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * Call from elFinder::netmout() before volume->mount(). * * @return array - * * @author Naoki Sawada **/ public function netmountPrepare($options) @@ -334,14 +332,14 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver } $callback = $options['url'] - .'?cmd=netmount&protocol=dropbox2&host=1'; + . '?cmd=netmount&protocol=dropbox2&host=1'; if (!$aToken && empty($_GET['code'])) { $url = $authHelper->getAuthUrl($callback); - $html = ''; + $html = ''; $html .= ''; if (empty($options['pass']) && $options['host'] !== '1') { $options['pass'] = 'return'; @@ -351,7 +349,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver } else { $out = [ 'node' => $options['id'], - 'json' => '{"protocol": "dropbox2", "mode": "makebtn", "body" : "'.str_replace($html, '"', '\\"').'", "error" : "'.elFinder::ERROR_ACCESS_DENIED.'"}', + 'json' => '{"protocol": "dropbox2", "mode": "makebtn", "body" : "' . str_replace($html, '"', '\\"') . '", "error" : "' . elFinder::ERROR_ACCESS_DENIED . '"}', 'bind' => 'netmount', ]; @@ -391,11 +389,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver $folders = ['/' => '/'] + $folders; $folders = json_encode($folders); - $json = '{"protocol": "dropbox2", "mode": "done", "folders": '.$folders.'}'; + $json = '{"protocol": "dropbox2", "mode": "done", "folders": ' . $folders . '}'; $options['pass'] = 'return'; $html = 'Dropbox.com'; $html .= ''; $this->session->set('Dropbox2AuthParams', $options); @@ -405,7 +403,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver } catch (DropboxClientException $e) { $this->session->remove('Dropbox2AuthParams')->remove('Dropbox2Tokens'); if (empty($options['pass'])) { - return ['exit' => true, 'body' => '{msg:'.elFinder::ERROR_ACCESS_DENIED.'}'.' '.$e->getMessage()]; + return ['exit' => true, 'body' => '{msg:' . elFinder::ERROR_ACCESS_DENIED . '}' . ' ' . $e->getMessage()]; } else { return ['exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]]; } @@ -448,7 +446,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver */ public function netunmount($netVolumes, $key) { - if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/').DIRECTORY_SEPARATOR.$this->driverId.'_'.$this->options['tokens']['uid'].'*.png')) { + if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->driverId . '_' . $this->options['tokens']['uid'] . '*.png')) { foreach ($tmbs as $file) { unlink($file); } @@ -466,7 +464,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * Connect to remote server and check if credentials are correct, if so, store the connection id in $this->service. * * @return bool - * * @author Naoki Sawada **/ protected function init() @@ -495,7 +492,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver try { // make net mount key for network mount $aToken = $this->options['access_token']; - $this->netMountKey = md5($aToken.'-'.$this->options['path']); + $this->netMountKey = md5($aToken . '-' . $this->options['path']); $errors = []; if (!$this->service) { @@ -503,7 +500,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver $this->service = new Dropbox($app); } } catch (DropboxClientException $e) { - $errors[] = 'Dropbox error: '.$e->getMessage(); + $errors[] = 'Dropbox error: ' . $e->getMessage(); } catch (Exception $e) { $errors[] = $e->getMessage(); } @@ -553,7 +550,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * Configure after successfull mount. * * @author Naoki Sawada - * * @throws elFinderAbortException */ protected function configure() @@ -585,10 +581,9 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * Cache dir contents. * * @param string $path dir path + * * @return - * * @author Naoki Sawada - * */ protected function cacheDir($path) { @@ -624,11 +619,10 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * Recursive files search. * * @param string $path dir path - * @param string $q search string - * @param array $mimes + * @param string $q search string + * @param array $mimes * * @return array - * * @throws elFinderAbortException * @author Naoki Sawada */ @@ -671,12 +665,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * Copy file/recursive copy dir only in current volume. * Return new file path or false. * - * @param string $src source path - * @param string $dst destination dir path + * @param string $src source path + * @param string $dst destination dir path * @param string $name new file name (optionaly) * * @return string|false - * * @throws elFinderAbortException * @author Naoki Sawada */ @@ -704,11 +697,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver /** * Remove file/ recursive remove dir. * - * @param string $path file path - * @param bool $force try to remove even if file locked - * @param bool $recursive - * @return bool + * @param string $path file path + * @param bool $force try to remove even if file locked + * @param bool $recursive * + * @return bool * @throws elFinderAbortException * @author Naoki Sawada */ @@ -746,11 +739,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * Create thumnbnail and return it's URL on success. * * @param string $path file path - * @param $stat + * @param $stat + * * @return string|false * @throws ImagickException * @throws elFinderAbortException - * * @author Naoki Sawada */ protected function createTmb($path, $stat) @@ -760,7 +753,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver } $name = $this->tmbname($stat); - $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; + $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; // copy image into tmbPath so some drivers does not store files on local fs if (!$data = $this->_db_getThumbnail($path)) { @@ -819,17 +812,16 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param array $stat file stat * * @return string - * * @author Naoki Sawada **/ protected function tmbname($stat) { - $name = $this->driverId.'_'; + $name = $this->driverId . '_'; if (isset($this->options['tokens']) && is_array($this->options['tokens'])) { $name .= $this->options['tokens']['uid']; } - return $name.md5($stat['iid']).$stat['ts'].'.png'; + return $name . md5($stat['iid']) . $stat['ts'] . '.png'; } /** @@ -840,12 +832,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param array $options options array * * @return bool|string - * * @author Naoki Sawada */ public function getContentUrl($hash, $options = []) { - if (! empty($options['onetime']) && $this->options['onetimeUrl']) { + if (!empty($options['onetime']) && $this->options['onetimeUrl']) { return parent::getContentUrl($hash, $options); } if (!empty($options['temporary'])) { @@ -864,8 +855,8 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver if ($res && !empty($res['links'])) { foreach ($res['links'] as $link) { if (isset($link['link_permissions']) - && isset($link['link_permissions']['requested_visibility']) - && $link['link_permissions']['requested_visibility']['.tag'] === $this->options['publishPermission']['requested_visibility']) { + && isset($link['link_permissions']['requested_visibility']) + && $link['link_permissions']['requested_visibility']['.tag'] === $this->options['publishPermission']['requested_visibility']) { $url = $link['url']; break; } @@ -884,7 +875,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver return $url; } } catch (DropboxClientException $e) { - return $this->setError('Dropbox error: '.$e->getMessage()); + return $this->setError('Dropbox error: ' . $e->getMessage()); } } @@ -915,7 +906,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Naoki Sawada **/ protected function _dirname($path) @@ -931,7 +921,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Naoki Sawada **/ protected function _basename($path) @@ -948,12 +937,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $name * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _joinPath($dir, $name) { - return rtrim($dir, '/').'/'.strtolower($name); + return rtrim($dir, '/') . '/' . strtolower($name); } /** @@ -962,12 +950,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path path * * @return string - * * @author Naoki Sawada **/ protected function _normpath($path) { - return '/'.ltrim($path, '/'); + return '/' . ltrim($path, '/'); } /** @@ -976,7 +963,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _relpath($path) @@ -994,7 +980,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Naoki Sawada **/ protected function _abspath($path) @@ -1012,7 +997,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Naoki Sawada **/ protected function _path($path) @@ -1029,12 +1013,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $parent parent path * * @return bool - * * @author Naoki Sawada **/ protected function _inpath($path, $parent) { - return $path == $parent || strpos($path, $parent.'/') === 0; + return $path == $parent || strpos($path, $parent . '/') === 0; } /***************** file stat ********************/ @@ -1050,13 +1033,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * - (bool) hidden is object hidden. optionally * - (string) alias for symlinks - link target path relative to root path. optionally * - (string) target for symlinks - link target path. optionally. - * * If file does not exists - returns empty array or false. * * @param string $path file path * * @return array|false - * * @author Dmitry (dio) Levashov **/ protected function _stat($path) @@ -1074,7 +1055,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path dir path * * @return bool - * * @author Naoki Sawada **/ protected function _subdirs($path) @@ -1093,7 +1073,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver } } } catch (DropboxClientException $e) { - $this->setError('Dropbox error: '.$e->getMessage()); + $this->setError('Dropbox error: ' . $e->getMessage()); } return $hasdir; @@ -1109,7 +1089,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @return string * @throws ImagickException * @throws elFinderAbortException - * * @author Naoki Sawada */ protected function _dimensions($path, $mime) @@ -1124,10 +1103,10 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver file_put_contents($tmp, $data); $size = getimagesize($tmp); if ($size) { - $ret = array('dim' => $size[0].'x'.$size[1]); + $ret = array('dim' => $size[0] . 'x' . $size[1]); $srcfp = fopen($tmp, 'rb'); if ($subImgLink = $this->getSubstituteImgLink(elFinder::$currentArgs['target'], $size, $srcfp)) { - $ret['url'] = $subImgLink; + $ret['url'] = $subImgLink; } } } @@ -1143,7 +1122,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path dir path * * @return array - * * @author Naoki Sawada **/ protected function _scandir($path) @@ -1160,7 +1138,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param bool $write open file for writing * * @return resource|false - * * @author Naoki Sawada **/ protected function _fopen($path, $mode = 'rb') @@ -1170,9 +1147,9 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver $access_token = $this->service->getAccessToken(); if ($access_token) { $data = array( - 'target' => $link->getLink(), - 'headers' => array('Authorization: Bearer '.$access_token), - ); + 'target' => $link->getLink(), + 'headers' => array('Authorization: Bearer ' . $access_token), + ); return elFinder::getStreamByUrl($data); } @@ -1188,7 +1165,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param resource $fp file pointer * * @return bool - * * @author Naoki Sawada **/ protected function _fclose($fp, $path = '') @@ -1205,7 +1181,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $name new directory name * * @return string|bool - * * @author Naoki Sawada **/ protected function _mkdir($path, $name) @@ -1213,7 +1188,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver try { return $this->service->createFolder($this->_db_joinName($path, $name))->getPathLower(); } catch (DropboxClientException $e) { - return $this->setError('Dropbox error: '.$e->getMessage()); + return $this->setError('Dropbox error: ' . $e->getMessage()); } } @@ -1224,7 +1199,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $name new file name * * @return string|bool - * * @author Naoki Sawada **/ protected function _mkfile($path, $name) @@ -1239,7 +1213,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path symlink path * * @return bool - * * @author Naoki Sawada **/ protected function _symlink($target, $path, $name) @@ -1255,7 +1228,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $name new file name * * @return bool - * * @author Naoki Sawada **/ protected function _copy($source, $targetDir, $name) @@ -1263,7 +1235,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver try { $this->service->copy($source, $this->_db_joinName($targetDir, $name))->getPathLower(); } catch (DropboxClientException $e) { - return $this->setError('Dropbox error: '.$e->getMessage()); + return $this->setError('Dropbox error: ' . $e->getMessage()); } return true; @@ -1278,7 +1250,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $name file name * * @return string|bool - * * @author Naoki Sawada **/ protected function _move($source, $targetDir, $name) @@ -1286,7 +1257,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver try { return $this->service->move($source, $this->_db_joinName($targetDir, $name))->getPathLower(); } catch (DropboxClientException $e) { - return $this->setError('Dropbox error: '.$e->getMessage()); + return $this->setError('Dropbox error: ' . $e->getMessage()); } } @@ -1296,7 +1267,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path file path * * @return bool - * * @author Naoki Sawada **/ protected function _unlink($path) @@ -1306,7 +1276,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver return true; } catch (DropboxClientException $e) { - return $this->setError('Dropbox error: '.$e->getMessage()); + return $this->setError('Dropbox error: ' . $e->getMessage()); } return true; @@ -1318,7 +1288,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path dir path * * @return bool - * * @author Naoki Sawada **/ protected function _rmdir($path) @@ -1336,7 +1305,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param array $stat file stat (required by some virtual fs) * * @return bool|string - * * @author Naoki Sawada **/ protected function _save($fp, $path, $name, $stat) @@ -1361,7 +1329,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver return $this->service->upload($dropboxFile, $fullpath, ['mode' => 'overwrite'])->getPathLower(); } catch (DropboxClientException $e) { - return $this->setError('Dropbox error: '.$e->getMessage()); + return $this->setError('Dropbox error: ' . $e->getMessage()); } } @@ -1371,7 +1339,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path file path * * @return string|false - * * @author Naoki Sawada **/ protected function _getContents($path) @@ -1382,7 +1349,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver $file = $this->service->download($path); $contents = $file->getContents(); } catch (Exception $e) { - return $this->setError('Dropbox error: '.$e->getMessage()); + return $this->setError('Dropbox error: ' . $e->getMessage()); } return $contents; @@ -1395,7 +1362,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $content new file content * * @return bool - * * @author Naoki Sawada **/ protected function _filePutContents($path, $content) @@ -1404,7 +1370,7 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver if ($local = $this->getTempFile($path)) { if (file_put_contents($local, $content, LOCK_EX) !== false - && ($fp = fopen($local, 'rb'))) { + && ($fp = fopen($local, 'rb'))) { clearstatcache(); $name = ''; $stat = $this->stat($path); @@ -1448,7 +1414,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param array $arc archiver command and arguments (same as in $this->archivers) * * @return true - * * @author Dmitry (dio) Levashov * @author Alexey Sukhotin **/ @@ -1464,7 +1429,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param string $path file/dir path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _findSymlinks($path) @@ -1479,7 +1443,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param array $arc archiver command and arguments (same as in $this->archivers) * * @return true - * * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin **/ @@ -1497,7 +1460,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver * @param array $arc archiver options * * @return string|bool - * * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin **/ diff --git a/php/elFinderVolumeFTP.class.php b/php/elFinderVolumeFTP.class.php index 39a17831a..6dcdb6d39 100644 --- a/php/elFinderVolumeFTP.class.php +++ b/php/elFinderVolumeFTP.class.php @@ -6,1697 +6,1786 @@ * @author Dmitry (dio) Levashov * @author Cem (discofever) **/ -class elFinderVolumeFTP extends elFinderVolumeDriver { - - /** - * Driver id - * Must be started from letter and contains [a-z0-9] - * Used as part of volume id - * - * @var string - **/ - protected $driverId = 'f'; - - /** - * FTP Connection Instance - * - * @var resource a FTP stream - **/ - protected $connect = null; - - /** - * Directory for tmp files - * If not set driver will try to use tmbDir as tmpDir - * - * @var string - **/ - protected $tmpPath = ''; - - /** - * Last FTP error message - * - * @var string - **/ - protected $ftpError = ''; - - /** - * FTP server output list as ftp on linux - * - * @var bool - **/ - protected $ftpOsUnix; - - /** - * FTP LIST command option - * - * @var string - */ - protected $ftpListOption = '-al'; - - - /** - * Is connected server Pure FTPd? - * - * @var bool - */ - protected $isPureFtpd = false; - - /** - * Is connected server with FTPS? - * - * @var bool - */ - protected $isFTPS = false; - - /** - * Tmp folder path - * - * @var string - **/ - protected $tmp = ''; - - /** - * FTP command `MLST` support - * - * @var bool - */ - private $MLSTsupprt = false; - - /** - * Calling cacheDir() target path with non-MLST - * - * @var string - */ - private $cacheDirTarget = ''; - - /** - * Constructor - * Extend options with required fields - * - * @author Dmitry (dio) Levashov - * @author Cem (DiscoFever) - */ - public function __construct() { - $opts = array( - 'host' => 'localhost', - 'user' => '', - 'pass' => '', - 'port' => 21, - 'mode' => 'passive', - 'ssl' => false, - 'path' => '/', - 'timeout' => 20, - 'owner' => true, - 'tmbPath' => '', - 'tmpPath' => '', - 'separator' => '/', - 'dirMode' => 0755, - 'fileMode' => 0644, - 'rootCssClass' => 'elfinder-navbar-root-ftp', - 'ftpListOption' => '-al', - ); - $this->options = array_merge($this->options, $opts); - $this->options['mimeDetect'] = 'internal'; - } - - /** - * Prepare - * Call from elFinder::netmout() before volume->mount() - * - * @param $options - * @return array volume root options - * @author Naoki Sawada - */ - public function netmountPrepare($options) { - if (!empty($_REQUEST['encoding']) && iconv('UTF-8', $_REQUEST['encoding'], '') !== false) { - $options['encoding'] = $_REQUEST['encoding']; - if (!empty($_REQUEST['locale']) && setlocale(LC_ALL, $_REQUEST['locale'])) { - setlocale(LC_ALL, elFinder::$locale); - $options['locale'] = $_REQUEST['locale']; - } - } - if (!empty($_REQUEST['FTPS'])) { - $options['ssl'] = true; - } - $options['statOwner'] = true; - $options['allowChmodReadOnly'] = true; - $options['acceptedName'] = '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#'; - return $options; - } - - /*********************************************************************/ - /* INIT AND CONFIGURE */ - /*********************************************************************/ - - /** - * Prepare FTP connection - * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn - * - * @return bool - * @author Dmitry (dio) Levashov - * @author Cem (DiscoFever) - **/ - protected function init() { - if (!$this->options['host'] - || !$this->options['port']) { - return $this->setError('Required options undefined.'); - } - - if (!$this->options['user']) { - $this->options['user'] = 'anonymous'; - $this->options['pass'] = ''; - } - if (!$this->options['path']) { - $this->options['path'] = '/'; - } - - // make ney mount key - $this->netMountKey = md5(join('-', array('ftp', $this->options['host'], $this->options['port'], $this->options['path'], $this->options['user']))); - - if (!function_exists('ftp_connect')) { - return $this->setError('FTP extension not loaded.'); - } - - // remove protocol from host - $scheme = parse_url($this->options['host'], PHP_URL_SCHEME); - - if ($scheme) { - $this->options['host'] = substr($this->options['host'], strlen($scheme)+3); - } - - // normalize root path - $this->root = $this->options['path'] = $this->_normpath($this->options['path']); - - if (empty($this->options['alias'])) { - $this->options['alias'] = $this->options['user'].'@'.$this->options['host']; - // $num = elFinder::$volumesCnt-1; - // $this->options['alias'] = $this->root == '/' || $this->root == '.' ? 'FTP folder '.$num : basename($this->root); - } - - $this->rootName = $this->options['alias']; - $this->options['separator'] = '/'; - - if (is_null($this->options['syncChkAsTs'])) { - $this->options['syncChkAsTs'] = true; - } - - if(isset($this->options['ftpListOption'])) { - $this->ftpListOption = $this->options['ftpListOption']; - } - - return $this->connect(); - - } - - - /** - * Configure after successfull mount. - * - * @return void - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function configure() { - parent::configure(); - - if (!empty($this->options['tmpPath'])) { - if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) { - $this->tmp = $this->options['tmpPath']; - } - } - if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) { - $this->tmp = $tmp; - } - - // fallback of $this->tmp - if (!$this->tmp && $this->tmbPathWritable) { - $this->tmp = $this->tmbPath; - } - - if (!$this->tmp) { - $this->disabled[] = 'mkfile'; - $this->disabled[] = 'paste'; - $this->disabled[] = 'duplicate'; - $this->disabled[] = 'upload'; - $this->disabled[] = 'edit'; - $this->disabled[] = 'archive'; - $this->disabled[] = 'extract'; - } - - // echo $this->tmp; - - } - - /** - * Connect to ftp server - * - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function connect() { - $withSSL = empty($this->options['ssl'])? '' : ' with SSL'; - if ($withSSL) { - if (!function_exists('ftp_ssl_connect') || !($this->connect = ftp_ssl_connect($this->options['host'], $this->options['port'], $this->options['timeout']))) { - return $this->setError('Unable to connect to FTP server '.$this->options['host'].$withSSL); - } - $this->isFTPS = true; - } else { - if (!($this->connect = ftp_connect($this->options['host'], $this->options['port'], $this->options['timeout']))) { - return $this->setError('Unable to connect to FTP server '.$this->options['host']); - } - } - if (!ftp_login($this->connect, $this->options['user'], $this->options['pass'])) { - $this->umount(); - return $this->setError('Unable to login into '.$this->options['host'].$withSSL); - } - - // try switch utf8 mode - if ($this->encoding) { - ftp_raw($this->connect, 'OPTS UTF8 OFF'); - } else { - ftp_raw($this->connect, 'OPTS UTF8 ON' ); - } - - $help = ftp_raw($this->connect, 'HELP'); - $this->isPureFtpd = stripos(implode(' ', $help), 'Pure-FTPd') !== false; - - if(! $this->isPureFtpd){ - // switch off extended passive mode - may be usefull for some servers - // this command, for pure-ftpd, doesn't work and takes a timeout in some pure-ftpd versions - ftp_raw($this->connect, 'epsv4 off' ); - } - // enter passive mode if required - $pasv = ($this->options['mode'] == 'passive'); - if (! ftp_pasv($this->connect, $pasv)) { - if ($pasv) { - $this->options['mode'] = 'active'; - } - } - - // enter root folder - if (! ftp_chdir($this->connect, $this->root) - || $this->root != ftp_pwd($this->connect)) { - $this->umount(); - return $this->setError('Unable to open root folder.'); - } - - // check for MLST support - $features = ftp_raw($this->connect, 'FEAT'); - if (!is_array($features)) { - $this->umount(); - return $this->setError('Server does not support command FEAT.'); - } - - foreach ($features as $feat) { - if (strpos(trim($feat), 'MLST') === 0) { - $this->MLSTsupprt = true; - break; - } - } - - return true; - } - - /** - * Call ftp_rawlist with option prefix - * - * @param string $path - * @return array - */ - protected function ftpRawList($path) { - if ($this->isPureFtpd) { - $path = str_replace(' ', '\ ', $path); - } - if ($this->ftpListOption) { - $path = $this->ftpListOption . ' ' . $path; - } - $res = ftp_rawlist($this->connect, $path); - if ($res === false) { - $res = array(); - } - return $res; - } - - /*********************************************************************/ - /* FS API */ - /*********************************************************************/ - - /** - * Close opened connection - * - * @return void - * @author Dmitry (dio) Levashov - **/ - public function umount() { - $this->connect && ftp_close($this->connect); - } - - - /** - * Parse line from ftp_rawlist() output and return file stat (array) - * - * @param string $raw line from ftp_rawlist() output - * @param $base - * @param bool $nameOnly - * @return array - * @author Dmitry Levashov - */ - protected function parseRaw($raw, $base, $nameOnly = false) { - static $now; - static $lastyear; - - if (! $now) { - $now = time(); - $lastyear = date('Y') - 1; - } - - $info = preg_split("/\s+/", $raw, 9); - $stat = array(); - - if (!isset($this->ftpOsUnix)) { - $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); - } - if (!$this->ftpOsUnix) { - $info = $this->normalizeRawWindows($raw); - } - - if (count($info) < 9 || $info[8] == '.' || $info[8] == '..') { - return false; - } - - $name = $info[8]; - - if (preg_match('|(.+)\-\>(.+)|', $name, $m)) { - $name = trim($m[1]); - // check recursive processing - if ($this->cacheDirTarget && $this->_joinPath($base, $name) !== $this->cacheDirTarget) { - return array(); - } - if (!$nameOnly) { - $target = trim($m[2]); - if (substr($target, 0, 1) !== $this->separator) { - $target = $this->getFullPath($target, $base); - } - $target = $this->_normpath($target); - $stat['name'] = $name; - $stat['target'] = $target; - return $stat; - } - } - - if ($nameOnly) { - return array('name' => $name); - } - - if (is_numeric($info[5]) && !$info[6] && !$info[7]) { - // by normalizeRawWindows() - $stat['ts'] = $info[5]; - } else { - $stat['ts'] = strtotime($info[5].' '.$info[6].' '.$info[7]); - if ($stat['ts'] && $stat['ts'] > $now && strpos($info[7], ':') !== false) { - $stat['ts'] = strtotime($info[5].' '.$info[6].' '.$lastyear.' '.$info[7]); - } - if (empty($stat['ts'])) { - $stat['ts'] = strtotime($info[6].' '.$info[5].' '.$info[7]); - if ($stat['ts'] && $stat['ts'] > $now && strpos($info[7], ':') !== false) { - $stat['ts'] = strtotime($info[6].' '.$info[5].' '.$lastyear.' '.$info[7]); - } - } - } - - if ($this->options['statOwner']) { - $stat['owner'] = $info[2]; - $stat['group'] = $info[3]; - $stat['perm'] = substr($info[0], 1); - // - // if not exists owner in LS ftp ==> isowner = true - // if is defined as option : 'owner' => true isowner = true - // - // if exist owner in LS ftp and 'owner' => False isowner = result of owner(file) == user(logged with ftp) - // - $stat['isowner'] = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true; - } - - $owner_computed = isset($stat['isowner'])? $stat['isowner'] : $this->options['owner'] ; - $perm = $this->parsePermissions($info[0], $owner_computed ); - $stat['name'] = $name; - $stat['mime'] = substr(strtolower($info[0]), 0, 1) == 'd' ? 'directory' : $this->mimetype($stat['name'], true); - $stat['size'] = $stat['mime'] == 'directory' ? 0 : $info[4]; - $stat['read'] = $perm['read']; - $stat['write'] = $perm['write']; - - return $stat; - } - - /** - * Normalize MS-DOS style FTP LIST Raw line - * - * @param string $raw line from FTP LIST (MS-DOS style) - * @return array - * @author Naoki Sawada - **/ - protected function normalizeRawWindows($raw) { - $info = array_pad(array(), 9, ''); - $item = preg_replace('#\s+#', ' ', trim($raw), 3); - list($date, $time, $size, $name) = explode(' ', $item, 4); - $format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i'; - $dateObj = DateTime::createFromFormat($format, $date.$time); - $info[5] = strtotime($dateObj->format('Y-m-d H:i')); - $info[8] = $name; - if ($size === '') { - $info[4] = 0; - $info[0] = 'drwxr-xr-x'; - } else { - $info[4] = (int)$size; - $info[0] = '-rw-r--r--'; - } - return $info; - } - - /** - * Parse permissions string. Return array(read => true/false, write => true/false) - * - * @param string $perm permissions string 'rwx' + 'rwx' + 'rwx' - * ^ ^ ^ - * | | +-> others - * | +---------> group - * +-----------------> owner - * The isowner parameter is computed by the caller. - * If the owner parameter in the options is true, the user is the actual owner of all objects even if che user used in the ftp Login - * is different from the file owner id. - * If the owner parameter is false to understand if the user is the file owner we compare the ftp user with the file owner id. - * @param Boolean $isowner. Tell if the current user is the owner of the object. - * @return string - * @author Dmitry (dio) Levashov - * @author Ugo Vierucci - */ - protected function parsePermissions($perm, $isowner = true) { - $res = array(); - $parts = array(); - for ($i = 0, $l = strlen($perm); $i < $l; $i++) { - $parts[] = substr($perm, $i, 1); - } - - $read = ($isowner && $parts[1] == 'r') || $parts[4] == 'r' || $parts[7] == 'r'; - - return array( - 'read' => $parts[0] == 'd' ? $read && (($isowner && $parts[3] == 'x') || $parts[6] == 'x' || $parts[9] == 'x') : $read, - 'write' => ($isowner && $parts[2] == 'w') || $parts[5] == 'w' || $parts[8] == 'w' - ); - } - - /** - * Cache dir contents - * - * @param string $path dir path - * @return void - * @author Dmitry Levashov - **/ - protected function cacheDir($path) { - $this->dirsCache[$path] = array(); - $hasDir = false; - - $list = array(); - $encPath = $this->convEncIn($path); - foreach ($this->ftpRawList($encPath) as $raw) { - if (($stat = $this->parseRaw($raw, $encPath))) { - $list[] = $stat; - } - } - $list = $this->convEncOut($list); - $prefix = ($path === $this->separator)? $this->separator : $path . $this->separator; - $targets = array(); - foreach($list as $stat) { - $p = $prefix . $stat['name']; - if (isset($stat['target'])) { - // stat later - $targets[$stat['name']] = $stat['target']; - } else { - $stat = $this->updateCache($p, $stat); - if (empty($stat['hidden'])) { - if (! $hasDir && $stat['mime'] === 'directory') { - $hasDir = true; - } - $this->dirsCache[$path][] = $p; - } - } - } - // stat link targets - foreach($targets as $name => $target) { - $stat = array(); - $stat['name'] = $name; - $p = $prefix . $name; - $cacheDirTarget = $this->cacheDirTarget; - $this->cacheDirTarget = $this->convEncIn($target, true); - if ($tstat = $this->stat($target)) { - $stat['size'] = $tstat['size']; - $stat['alias'] = $target; - $stat['thash'] = $tstat['hash']; - $stat['mime'] = $tstat['mime']; - $stat['read'] = $tstat['read']; - $stat['write'] = $tstat['write']; - - if (isset($tstat['ts'])) { $stat['ts'] = $tstat['ts']; } - if (isset($tstat['owner'])) { $stat['owner'] = $tstat['owner']; } - if (isset($tstat['group'])) { $stat['group'] = $tstat['group']; } - if (isset($tstat['perm'])) { $stat['perm'] = $tstat['perm']; } - if (isset($tstat['isowner'])) { $stat['isowner'] = $tstat['isowner']; } - } else { - - $stat['mime'] = 'symlink-broken'; - $stat['read'] = false; - $stat['write'] = false; - $stat['size'] = 0; - - } - $this->cacheDirTarget = $cacheDirTarget; - $stat = $this->updateCache($p, $stat); - if (empty($stat['hidden'])) { - if (! $hasDir && $stat['mime'] === 'directory') { - $hasDir = true; - } - $this->dirsCache[$path][] = $p; - } - } - - if (isset($this->sessionCache['subdirs'])) { - $this->sessionCache['subdirs'][$path] = $hasDir; - } - } - - /** - * Return ftp transfer mode for file - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function ftpMode($path) { - return strpos($this->mimetype($path), 'text/') === 0 ? FTP_ASCII : FTP_BINARY; - } - - /*********************** paths/urls *************************/ - - /** - * Return parent directory path - * - * @param string $path file path - * @return string - * @author Naoki Sawada - **/ - protected function _dirname($path) { - $parts = explode($this->separator, trim($path, $this->separator)); - array_pop($parts); - return $this->separator . join($this->separator, $parts); - } - - /** - * Return file name - * - * @param string $path file path - * @return string - * @author Naoki Sawada - **/ - protected function _basename($path) { - $parts = explode($this->separator, trim($path, $this->separator)); - return array_pop($parts); - } - - /** - * Join dir name and file name and retur full path - * - * @param string $dir - * @param string $name - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _joinPath($dir, $name) { - return rtrim($dir, $this->separator).$this->separator.$name; - } - - /** - * Return normalized path, this works the same as os.path.normpath() in Python - * - * @param string $path path - * @return string - * @author Troex Nevelin - **/ - protected function _normpath($path) { - if (empty($path)) { - $path = '.'; - } - // path must be start with / - $path = preg_replace('|^\.\/?|', $this->separator, $path); - $path = preg_replace('/^([^\/])/', "/$1", $path); - - if ($path[0] === $this->separator) { - $initial_slashes = true; - } else { - $initial_slashes = false; - } - - if (($initial_slashes) - && (strpos($path, '//') === 0) - && (strpos($path, '///') === false)) { - $initial_slashes = 2; - } - - $initial_slashes = (int) $initial_slashes; - - $comps = explode($this->separator, $path); - $new_comps = array(); - foreach ($comps as $comp) { - if (in_array($comp, array('', '.'))) { - continue; - } - - if (($comp != '..') - || (!$initial_slashes && !$new_comps) - || ($new_comps && (end($new_comps) == '..'))) { - array_push($new_comps, $comp); - } elseif ($new_comps) { - array_pop($new_comps); - } - } - $comps = $new_comps; - $path = implode($this->separator, $comps); - if ($initial_slashes) { - $path = str_repeat($this->separator, $initial_slashes) . $path; - } - - return $path ? $path : '.'; - } - - /** - * Return file path related to root dir - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _relpath($path) { - if ($path === $this->root) { - return ''; - } else { - if (strpos($path, $this->root) === 0) { - return ltrim(substr($path, strlen($this->root)), $this->separator); - } else { - // for link - return $path; - } - } - } - - /** - * Convert path related to root dir into real path - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _abspath($path) { - if ($path === $this->separator) { - return $this->root; - } else { - if ($path[0] === $this->separator) { - // for link - return $path; - } else { - return $this->_joinPath($this->root, $path); - } - } - } - - /** - * Return fake path started from root dir - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _path($path) { - return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path)); - } - - /** - * Return true if $path is children of $parent - * - * @param string $path path to check - * @param string $parent parent path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _inpath($path, $parent) { - return $path == $parent || strpos($path, rtrim($parent, $this->separator) . $this->separator) === 0; - } - - /***************** file stat ********************/ - /** - * Return stat for given path. - * Stat contains following fields: - * - (int) size file size in b. required - * - (int) ts file modification time in unix time. required - * - (string) mime mimetype. required for folders, others - optionally - * - (bool) read read permissions. required - * - (bool) write write permissions. required - * - (bool) locked is object locked. optionally - * - (bool) hidden is object hidden. optionally - * - (string) alias for symlinks - link target path relative to root path. optionally - * - (string) target for symlinks - link target path. optionally - * - * If file does not exists - returns empty array or false. - * - * @param string $path file path - * @return array|false - * @author Dmitry (dio) Levashov - **/ - protected function _stat($path) { - $outPath = $this->convEncOut($path); - if (isset($this->cache[$outPath])) { - return $this->convEncIn($this->cache[$outPath]); - } else { - $this->convEncIn(); - } - if (! $this->MLSTsupprt) { - if ($path === $this->root) { - $res = array( - 'name' => $this->root, - 'mime' => 'directory', - 'dirs' => $this->_subdirs($path) - ); - if ($this->isMyReload()) { - $ts = 0; - foreach ($this->ftpRawList($path) as $str) { - if (($stat = $this->parseRaw($str, $path))) { - if (! empty($stat['ts'])) { - $ts = max($ts, $stat['ts']); - } - } - } - if ($ts) { - $res['ts'] = $ts; - } - } - return $res; - } - // stat of system root - if ($path === $this->separator) { - $res = array( - 'name' => $this->separator, - 'mime' => 'directory', - 'dirs' => 1 - ); - $this->cache[$outPath] = $res; - return $res; - } - - $pPath = $this->_dirname($path); - if ($this->_inPath($pPath, $this->root)) { - $outPPpath = $this->convEncOut($pPath); - if (! isset($this->dirsCache[$outPPpath])) { - $parentSubdirs = null; - if (isset($this->sessionCache['subdirs']) && isset($this->sessionCache['subdirs'][$outPPpath])) { - $parentSubdirs = $this->sessionCache['subdirs'][$outPPpath ]; - } - $this->cacheDir($outPPpath); - if ($parentSubdirs) { - $this->sessionCache['subdirs'][$outPPpath] = $parentSubdirs; - } - } - } - - $stat = $this->convEncIn(isset($this->cache[$outPath])? $this->cache[$outPath] : array()); - if (! $this->mounted) { - // dispose incomplete cache made by calling `stat` by 'startPath' option - $this->cache = array(); - } - return $stat; - } - $raw = ftp_raw($this->connect, 'MLST ' . $path); - if (is_array($raw) && count($raw) > 1 && substr(trim($raw[0]), 0, 1) == 2) { - $parts = explode(';', trim($raw[1])); - array_pop($parts); - $parts = array_map('strtolower', $parts); - $stat = array(); - $mode = ''; - foreach ($parts as $part) { - - list($key, $val) = explode('=', $part, 2); - - switch ($key) { - case 'type': - if (strpos($val, 'dir') !== false) { - $stat['mime'] = 'directory'; - } else if (strpos($val, 'link') !== false) { - $stat['mime'] = 'symlink'; - break(2); - } else { - $stat['mime'] = $this->mimetype($path); - } - break; - - case 'size': - $stat['size'] = $val; - break; - - case 'modify': - $ts = mktime(intval(substr($val, 8, 2)), intval(substr($val, 10, 2)), intval(substr($val, 12, 2)), intval(substr($val, 4, 2)), intval(substr($val, 6, 2)), substr($val, 0, 4)); - $stat['ts'] = $ts; - break; - - case 'unix.mode': - $mode = strval($val); - break; - - case 'unix.uid': - $stat['owner'] = $val; - break; - - case 'unix.gid': - $stat['group'] = $val; - break; - - case 'perm': - $val = strtolower($val); - $stat['read'] = (int)preg_match('/e|l|r/', $val); - $stat['write'] = (int)preg_match('/w|m|c/', $val); - if (!preg_match('/f|d/', $val)) { - $stat['locked'] = 1; - } - break; - } - } - - if (empty($stat['mime'])) { - return array(); - } - - // do not use MLST to get stat of symlink - if ($stat['mime'] === 'symlink') { - $this->MLSTsupprt = false; - $res = $this->_stat($path); - $this->MLSTsupprt = true; - return $res; - } - - if ($stat['mime'] === 'directory') { - $stat['size'] = 0; - } - - if ($mode) { - $stat['perm'] = ''; - if ($mode[0] === '0') { - $mode = substr($mode, 1); - } - - $perm = array(); - for ($i = 0; $i <= 2; $i++) { - $perm[$i] = array(false, false, false); - $n = isset($mode[$i]) ? $mode[$i] : 0; - - if ($n - 4 >= 0) { - $perm[$i][0] = true; - $n = $n - 4; - $stat['perm'] .= 'r'; - } else { - $stat['perm'] .= '-'; - } - - if ($n - 2 >= 0) { - $perm[$i][1] = true; - $n = $n - 2; - $stat['perm'] .= 'w'; - } else { - $stat['perm'] .= '-'; - } - - if ($n - 1 == 0) { - $perm[$i][2] = true; - $stat['perm'] .= 'x'; - } else { - $stat['perm'] .= '-'; - } - } - - $stat['perm'] = trim($stat['perm']); - // - // if not exists owner in LS ftp ==> isowner = true - // if is defined as option : 'owner' => true isowner = true - // - // if exist owner in LS ftp and 'owner' => False isowner = result of owner(file) == user(logged with ftp) - - $owner_computed = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true; - - $read = ($owner_computed && $perm[0][0]) || $perm[1][0] || $perm[2][0]; - - $stat['read'] = $stat['mime'] == 'directory' ? $read && (($owner_computed && $perm[0][2]) || $perm[1][2] || $perm[2][2]) : $read; - $stat['write'] = ($owner_computed && $perm[0][1]) || $perm[1][1] || $perm[2][1]; - - if ($this->options['statOwner']) { - $stat['isowner'] = $owner_computed; - } else { - unset($stat['owner'], $stat['group'], $stat['perm']); - } - } - - return $stat; - - } - - return array(); - } - - /** - * Return true if path is dir and has at least one childs directory - * - * @param string $path dir path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _subdirs($path) { - - foreach ($this->ftpRawList($path) as $str) { - $info = preg_split('/\s+/', $str, 9); - if (!isset($this->ftpOsUnix)) { - $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); - } - if (!$this->ftpOsUnix) { - $info = $this->normalizeRawWindows($str); - } - $name = isset($info[8])? trim($info[8]) : ''; - if ($name && $name !== '.' && $name !== '..' && substr(strtolower($info[0]), 0, 1) === 'd') { - return true; - } - } - return false; - } - - /** - * Return object width and height - * Ususaly used for images, but can be realize for video etc... - * - * @param string $path file path - * @param string $mime file mime type - * @return string|false - * @throws ImagickException - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function _dimensions($path, $mime) { - $ret = false; - if ($imgsize = $this->getImageSize($path, $mime)) { - $ret = array('dim' => $imgsize['dimensions']); - if (!empty($imgsize['url'])) { - $ret['url'] = $imgsize['url']; - } - } - return $ret; - } - - /******************** file/dir content *********************/ - - /** - * Return files list in directory. - * - * @param string $path dir path - * @return array - * @author Dmitry (dio) Levashov - * @author Cem (DiscoFever) - **/ - protected function _scandir($path) { - $files = array(); - - foreach ($this->ftpRawList($path) as $str) { - if (($stat = $this->parseRaw($str, $path, true))) { - $files[] = $this->_joinPath($path, $stat['name']); - } - } - - return $files; - } - - /** - * Open file and return file pointer - * - * @param string $path file path - * @param string $mode - * @return false|resource - * @throws elFinderAbortException - * @internal param bool $write open file for writing - * @author Dmitry (dio) Levashov - */ - protected function _fopen($path, $mode='rb') { - // try ftp stream wrapper - if ($this->options['mode'] === 'passive' && ini_get('allow_url_fopen')) { - $url = ($this->isFTPS? 'ftps' : 'ftp').'://'.$this->options['user'].':'.$this->options['pass'].'@'.$this->options['host'].':'.$this->options['port'].$path; - if (strtolower($mode[0]) === 'w') { - $context = stream_context_create(array('ftp' => array('overwrite' => true))); - $fp = fopen($url, $mode, false, $context); - } else { - $fp = fopen($url, $mode); - } - if ($fp) { - return $fp; - } - } - - if ($this->tmp) { - $local = $this->getTempFile($path); - $fp = fopen($local, 'wb'); - $ret = ftp_nb_fget($this->connect, $fp, $path, FTP_BINARY); - while ($ret === FTP_MOREDATA) { - elFinder::extendTimeLimit(); - $ret = ftp_nb_continue($this->connect); - } - if ($ret === FTP_FINISHED) { - fclose($fp); - $fp = fopen($local, $mode); - return $fp; - } - fclose($fp); - is_file($local) && unlink($local); - } - - return false; - } - - /** - * Close opened file - * - * @param resource $fp file pointer - * @param string $path - * @return void - * @author Dmitry (dio) Levashov - */ - protected function _fclose($fp, $path='') { - is_resource($fp) && fclose($fp); - if ($path) { - unlink($this->getTempFile($path)); - } - } - - /******************** file/dir manipulations *************************/ - - /** - * Create dir and return created dir path or false on failed - * - * @param string $path parent dir path - * @param string $name new directory name - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - protected function _mkdir($path, $name) { - $path = $this->_joinPath($path, $name); - if (ftp_mkdir($this->connect, $path) === false) { - return false; - } - - $this->options['dirMode'] && ftp_chmod($this->connect, $this->options['dirMode'], $path); - return $path; - } - - /** - * Create file and return it's path or false on failed - * - * @param string $path parent dir path - * @param string $name new file name - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - protected function _mkfile($path, $name) { - if ($this->tmp) { - $path = $this->_joinPath($path, $name); - $local = $this->getTempFile(); - $res = touch($local) && ftp_put($this->connect, $path, $local, FTP_ASCII); - unlink($local); - return $res ? $path : false; - } - return false; - } - - /** - * Create symlink. FTP driver does not support symlinks. - * - * @param string $target link target - * @param string $path symlink path - * @param string $name - * @return bool - * @author Dmitry (dio) Levashov - */ - protected function _symlink($target, $path, $name) { - return false; - } - - /** - * Copy file into another file - * - * @param string $source source file path - * @param string $targetDir target directory path - * @param string $name new file name - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _copy($source, $targetDir, $name) { - $res = false; - - if ($this->tmp) { - $local = $this->getTempFile(); - $target = $this->_joinPath($targetDir, $name); - - if (ftp_get($this->connect, $local, $source, FTP_BINARY) - && ftp_put($this->connect, $target, $local, $this->ftpMode($target))) { - $res = $target; - } - unlink($local); - } - - return $res; - } - - /** - * Move file into another parent dir. - * Return new file path or false. - * - * @param string $source source file path - * @param $targetDir - * @param string $name file name - * @return bool|string - * @internal param string $target target dir path - * @author Dmitry (dio) Levashov - */ - protected function _move($source, $targetDir, $name) { - $target = $this->_joinPath($targetDir, $name); - return ftp_rename($this->connect, $source, $target) ? $target : false; - } - - /** - * Remove file - * - * @param string $path file path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _unlink($path) { - return ftp_delete($this->connect, $path); - } - - /** - * Remove dir - * - * @param string $path dir path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _rmdir($path) { - return ftp_rmdir($this->connect, $path); - } - - /** - * Create new file and write into it from file pointer. - * Return new file path or false on error. - * - * @param resource $fp file pointer - * @param string $dir target dir path - * @param string $name file name - * @param array $stat file stat (required by some virtual fs) - * @return bool|string - * @author Dmitry (dio) Levashov - **/ - protected function _save($fp, $dir, $name, $stat) { - $path = $this->_joinPath($dir, $name); - return ftp_fput($this->connect, $path, $fp, $this->ftpMode($path)) - ? $path - : false; - } - - /** - * Get file contents - * - * @param string $path file path - * @return string|false - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function _getContents($path) { - $contents = ''; - if (($fp = $this->_fopen($path))) { - while (!feof($fp)) { - $contents .= fread($fp, 8192); - } - $this->_fclose($fp, $path); - return $contents; - } - return false; - } - - /** - * Write a string to a file - * - * @param string $path file path - * @param string $content new file content - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _filePutContents($path, $content) { - $res = false; - - if ($this->tmp) { - $local = $this->getTempFile(); - - if (file_put_contents($local, $content, LOCK_EX) !== false - && ($fp = fopen($local, 'rb'))) { - $file = $this->stat($this->convEncOut($path, false)); - if (! empty($file['thash'])) { - $path = $this->decode($file['thash']); - } - clearstatcache(); - $res = ftp_fput($this->connect, $path, $fp, $this->ftpMode($path)); - fclose($fp); - } - file_exists($local) && unlink($local); - } - - return $res; - } - - /** - * Detect available archivers - * - * @return void - * @throws elFinderAbortException - */ - protected function _checkArchivers() { - $this->archivers = $this->getArchivers(); - return; - } - - /** - * chmod availability - * - * @param string $path - * @param string $mode - * @return bool - */ - protected function _chmod($path, $mode) { - $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o",$mode)); - return ftp_chmod($this->connect, $modeOct, $path); - } - - /** - * Extract files from archive - * - * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) - * @return true - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - */ - protected function _extract($path, $arc) - { - $dir = $this->tempDir(); - if (!$dir) { - return false; - } - - $basename = $this->_basename($path); - $localPath = $dir . DIRECTORY_SEPARATOR . $basename; - - if (!ftp_get($this->connect, $localPath, $path, FTP_BINARY)) { - //cleanup - $this->rmdirRecursive($dir); - return false; - } - - $this->unpackArchive($localPath, $arc); - - $this->archiveSize = 0; - - // find symlinks and check extracted items - $checkRes = $this->checkExtractItems($dir); - if ($checkRes['symlinks']) { - $this->rmdirRecursive($dir); - return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS))); - } - $this->archiveSize = $checkRes['totalSize']; - if ($checkRes['rmNames']) { - foreach($checkRes['rmNames'] as $name) { - $this->addError(elFinder::ERROR_SAVE, $name); - } - } - - $filesToProcess = self::listFilesInDirectory($dir, true); - - // no files - extract error ? - if (empty($filesToProcess)) { - $this->rmdirRecursive($dir); - return false; - } - - // check max files size - if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { - $this->rmdirRecursive($dir); - return $this->setError(elFinder::ERROR_ARC_MAXSIZE); - } - - $extractTo = $this->extractToNewdir; // 'auto', ture or false - - // archive contains one item - extract in archive dir - $name = ''; - $src = $dir . DIRECTORY_SEPARATOR . $filesToProcess[0]; - if (($extractTo === 'auto' || !$extractTo) && count($filesToProcess) === 1 && is_file($src)) { - $name = $filesToProcess[0]; - } else if ($extractTo === 'auto' || $extractTo) { - // for several files - create new directory - // create unique name for directory - $src = $dir; - $splits = elFinder::splitFileExtention(basename($path)); - $name = $splits[0]; - $test = $this->_joinPath(dirname($path), $name); - if ($this->stat($test)) { - $name = $this->uniqueName(dirname($path), $name, '-', false); - } - } - - if ($name !== '' && is_file($src)) { - $result = $this->_joinPath(dirname($path), $name); - - if (! ftp_put($this->connect, $result, $src, FTP_BINARY)) { - $this->rmdirRecursive($dir); - return false; - } - } else { - $dstDir = $this->_dirname($path); - $result = array(); - if (is_dir($src) && $name) { - $target = $this->_joinPath($dstDir, $name); - $_stat = $this->_stat($target); - if ($_stat) { - if (! $this->options['copyJoin']) { - if ($_stat['mime'] === 'directory') { - $this->delTree($target); - } else { - $this->_unlink($target); - } - $_stat = false; - } else { - $dstDir = $target; - } - } - if (! $_stat && (!$dstDir = $this->_mkdir($dstDir, $name))) { - $this->rmdirRecursive($dir); - return false; - } - $result[] = $dstDir; - } - foreach($filesToProcess as $name) { - $name = rtrim($name, DIRECTORY_SEPARATOR); - $src = $dir . DIRECTORY_SEPARATOR . $name; - if (is_dir($src)) { - $p = dirname($name); - if ($p === '.') { - $p = ''; - } - $name = basename($name); - $target = $this->_joinPath($this->_joinPath($dstDir, $p), $name); - $_stat = $this->_stat($target); - if ($_stat) { - if (! $this->options['copyJoin']) { - if ($_stat['mime'] === 'directory') { - $this->delTree($target); - } else { - $this->_unlink($target); - } - $_stat = false; - } - } - if (! $_stat && (!$target = $this->_mkdir($this->_joinPath($dstDir, $p), $name))) { - $this->rmdirRecursive($dir); - return false; - } - } else { - $target = $this->_joinPath($dstDir, $name); - if (! ftp_put($this->connect, $target, $src, FTP_BINARY)) { - $this->rmdirRecursive($dir); - return false; - } - } - $result[] = $target; - } - if (!$result) { - $this->rmdirRecursive($dir); - return false; - } - } - - is_dir($dir) && $this->rmdirRecursive($dir); - - $this->clearcache(); - return $result? $result : false; - } - - /** - * Create archive and return its path - * - * @param string $dir target dir - * @param array $files files names list - * @param string $name archive name - * @param array $arc archiver options - * @return string|bool - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - */ - protected function _archive($dir, $files, $name, $arc) - { - // get current directory - $cwd = getcwd(); - - $tmpDir = $this->tempDir(); - if (!$tmpDir) { - return false; - } - - //download data - if (!$this->ftp_download_files($dir, $files, $tmpDir)) { - //cleanup - $this->rmdirRecursive($tmpDir); - return false; - } - - $remoteArchiveFile = false; - if ($path = $this->makeArchive($tmpDir, $files, $name, $arc)) { - $remoteArchiveFile = $this->_joinPath($dir, $name); - if (!ftp_put($this->connect, $remoteArchiveFile, $path, FTP_BINARY)) { - $remoteArchiveFile = false; - } - } - - //cleanup - if(!$this->rmdirRecursive($tmpDir)) { - return false; - } - - return $remoteArchiveFile; - } - - /** - * Create writable temporary directory and return path to it. - * @return string path to the new temporary directory or false in case of error. - */ - private function tempDir() - { - $tempPath = tempnam($this->tmp, 'elFinder'); - if (!$tempPath) { - $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); - return false; - } - $success = unlink($tempPath); - if (!$success) { - $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); - return false; - } - $success = mkdir($tempPath, 0700, true); - if (!$success) { - $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); - return false; - } - return $tempPath; - } - - /** - * Gets an array of absolute remote FTP paths of files and - * folders in $remote_directory omitting symbolic links. - * - * @param $remote_directory string remote FTP path to scan for file and folders recursively - * @param $targets array Array of target item. `null` is to get all of items - * @return array of elements each of which is an array of two elements: - * - */ - protected function ftp_scan_dir($remote_directory, $targets = null) - { - $buff = $this->ftpRawList($remote_directory); - $items = array(); - if ($targets && is_array($targets)) { - $targets = array_flip($targets); - } else { - $targets = false; - } - foreach ($buff as $str) { - $info = preg_split("/\s+/", $str, 9); - if (!isset($this->ftpOsUnix)) { - $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); - } - if (!$this->ftpOsUnix) { - $info = $this->normalizeRawWindows($str); - } - $type = substr($info[0], 0, 1); - $name = trim($info[8]); - if ($name !== '.' && $name !== '..' && (!$targets || isset($targets[$name]))) { - switch ($type) { - case 'l' : //omit symbolic links - case 'd' : - $remote_file_path = $this->_joinPath($remote_directory, $name); - $item = array(); - $item['path'] = $remote_file_path; - $item['type'] = 'd'; // normal file - $items[] = $item; - $items = array_merge($items, $this->ftp_scan_dir($remote_file_path)); - break; - default: - $remote_file_path = $this->_joinPath($remote_directory, $name); - $item = array(); - $item['path'] = $remote_file_path; - $item['type'] = 'f'; // normal file - $items[] = $item; - } - } - } - return $items; - } - - /** - * Downloads specified files from remote directory - * if there is a directory among files it is downloaded recursively (omitting symbolic links). - * - * @param $remote_directory string remote FTP path to a source directory to download from. - * @param array $files list of files to download from remote directory. - * @param $dest_local_directory string destination folder to store downloaded files. - * @return bool true on success and false on failure. - */ - private function ftp_download_files($remote_directory, array $files, $dest_local_directory) - { - $contents = $this->ftp_scan_dir($remote_directory, $files); - if (!isset($contents)) { - $this->setError(elFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); - return false; - } - $remoteDirLen = strlen($remote_directory); - foreach ($contents as $item) { - $relative_path = substr($item['path'], $remoteDirLen); - $local_path = $dest_local_directory . DIRECTORY_SEPARATOR . $relative_path; - switch ($item['type']) { - case 'd': - $success = mkdir($local_path); - break; - case 'f': - $success = ftp_get($this->connect, $local_path, $item['path'], FTP_BINARY); - break; - default: - $success = true; - } - if (!$success) { - $this->setError(elFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); - return false; - } - } - return true; - } - - /** - * Delete local directory recursively. - * @param $dirPath string to directory to be erased. - * @return bool true on success and false on failure. - * @throws Exception - */ - private function deleteDir($dirPath) - { - if (!is_dir($dirPath)) { - $success = unlink($dirPath); - } else { - $success = true; - foreach (array_reverse(elFinderVolumeFTP::listFilesInDirectory($dirPath, false)) as $path) { - $path = $dirPath . DIRECTORY_SEPARATOR . $path; - if(is_link($path)) { - unlink($path); - } else if (is_dir($path)) { - $success = rmdir($path); - } else { - $success = unlink($path); - } - if (!$success) { - break; - } - } - if($success) { - $success = rmdir($dirPath); - } - } - if(!$success) { - $this->setError(elFinder::ERROR_RM, $dirPath); - return false; - } - return $success; - } - - /** - * Returns array of strings containing all files and folders in the specified local directory. - * @param $dir - * @param $omitSymlinks - * @param string $prefix - * @return array array of files and folders names relative to the $path - * or an empty array if the directory $path is empty, - *
- * false if $path is not a directory or does not exist. - * @throws Exception - * @internal param string $path path to directory to scan. - */ - private static function listFilesInDirectory($dir, $omitSymlinks, $prefix = '') - { - if (!is_dir($dir)) { - return false; - } - $excludes = array(".",".."); - $result = array(); - $files = self::localScandir($dir); - if(!$files) { - return array(); - } - foreach($files as $file) { - if(!in_array($file, $excludes)) { - $path = $dir.DIRECTORY_SEPARATOR.$file; - if(is_link($path)) { - if($omitSymlinks) { - continue; - } else { - $result[] = $prefix.$file; - } - } else if(is_dir($path)) { - $result[] = $prefix.$file.DIRECTORY_SEPARATOR; - $subs = elFinderVolumeFTP::listFilesInDirectory($path, $omitSymlinks, $prefix.$file.DIRECTORY_SEPARATOR); - if($subs) { - $result = array_merge($result, $subs); - } - - } else { - $result[] = $prefix.$file; - } - } - } - return $result; - } +class elFinderVolumeFTP extends elFinderVolumeDriver +{ + + /** + * Driver id + * Must be started from letter and contains [a-z0-9] + * Used as part of volume id + * + * @var string + **/ + protected $driverId = 'f'; + + /** + * FTP Connection Instance + * + * @var resource a FTP stream + **/ + protected $connect = null; + + /** + * Directory for tmp files + * If not set driver will try to use tmbDir as tmpDir + * + * @var string + **/ + protected $tmpPath = ''; + + /** + * Last FTP error message + * + * @var string + **/ + protected $ftpError = ''; + + /** + * FTP server output list as ftp on linux + * + * @var bool + **/ + protected $ftpOsUnix; + + /** + * FTP LIST command option + * + * @var string + */ + protected $ftpListOption = '-al'; + + + /** + * Is connected server Pure FTPd? + * + * @var bool + */ + protected $isPureFtpd = false; + + /** + * Is connected server with FTPS? + * + * @var bool + */ + protected $isFTPS = false; + + /** + * Tmp folder path + * + * @var string + **/ + protected $tmp = ''; + + /** + * FTP command `MLST` support + * + * @var bool + */ + private $MLSTsupprt = false; + + /** + * Calling cacheDir() target path with non-MLST + * + * @var string + */ + private $cacheDirTarget = ''; + + /** + * Constructor + * Extend options with required fields + * + * @author Dmitry (dio) Levashov + * @author Cem (DiscoFever) + */ + public function __construct() + { + $opts = array( + 'host' => 'localhost', + 'user' => '', + 'pass' => '', + 'port' => 21, + 'mode' => 'passive', + 'ssl' => false, + 'path' => '/', + 'timeout' => 20, + 'owner' => true, + 'tmbPath' => '', + 'tmpPath' => '', + 'separator' => '/', + 'dirMode' => 0755, + 'fileMode' => 0644, + 'rootCssClass' => 'elfinder-navbar-root-ftp', + 'ftpListOption' => '-al', + ); + $this->options = array_merge($this->options, $opts); + $this->options['mimeDetect'] = 'internal'; + } + + /** + * Prepare + * Call from elFinder::netmout() before volume->mount() + * + * @param $options + * + * @return array volume root options + * @author Naoki Sawada + */ + public function netmountPrepare($options) + { + if (!empty($_REQUEST['encoding']) && iconv('UTF-8', $_REQUEST['encoding'], '') !== false) { + $options['encoding'] = $_REQUEST['encoding']; + if (!empty($_REQUEST['locale']) && setlocale(LC_ALL, $_REQUEST['locale'])) { + setlocale(LC_ALL, elFinder::$locale); + $options['locale'] = $_REQUEST['locale']; + } + } + if (!empty($_REQUEST['FTPS'])) { + $options['ssl'] = true; + } + $options['statOwner'] = true; + $options['allowChmodReadOnly'] = true; + $options['acceptedName'] = '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#'; + return $options; + } + + /*********************************************************************/ + /* INIT AND CONFIGURE */ + /*********************************************************************/ + + /** + * Prepare FTP connection + * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn + * + * @return bool + * @author Dmitry (dio) Levashov + * @author Cem (DiscoFever) + **/ + protected function init() + { + if (!$this->options['host'] + || !$this->options['port']) { + return $this->setError('Required options undefined.'); + } + + if (!$this->options['user']) { + $this->options['user'] = 'anonymous'; + $this->options['pass'] = ''; + } + if (!$this->options['path']) { + $this->options['path'] = '/'; + } + + // make ney mount key + $this->netMountKey = md5(join('-', array('ftp', $this->options['host'], $this->options['port'], $this->options['path'], $this->options['user']))); + + if (!function_exists('ftp_connect')) { + return $this->setError('FTP extension not loaded.'); + } + + // remove protocol from host + $scheme = parse_url($this->options['host'], PHP_URL_SCHEME); + + if ($scheme) { + $this->options['host'] = substr($this->options['host'], strlen($scheme) + 3); + } + + // normalize root path + $this->root = $this->options['path'] = $this->_normpath($this->options['path']); + + if (empty($this->options['alias'])) { + $this->options['alias'] = $this->options['user'] . '@' . $this->options['host']; + // $num = elFinder::$volumesCnt-1; + // $this->options['alias'] = $this->root == '/' || $this->root == '.' ? 'FTP folder '.$num : basename($this->root); + } + + $this->rootName = $this->options['alias']; + $this->options['separator'] = '/'; + + if (is_null($this->options['syncChkAsTs'])) { + $this->options['syncChkAsTs'] = true; + } + + if (isset($this->options['ftpListOption'])) { + $this->ftpListOption = $this->options['ftpListOption']; + } + + return $this->connect(); + + } + + + /** + * Configure after successfull mount. + * + * @return void + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function configure() + { + parent::configure(); + + if (!empty($this->options['tmpPath'])) { + if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) { + $this->tmp = $this->options['tmpPath']; + } + } + if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) { + $this->tmp = $tmp; + } + + // fallback of $this->tmp + if (!$this->tmp && $this->tmbPathWritable) { + $this->tmp = $this->tmbPath; + } + + if (!$this->tmp) { + $this->disabled[] = 'mkfile'; + $this->disabled[] = 'paste'; + $this->disabled[] = 'duplicate'; + $this->disabled[] = 'upload'; + $this->disabled[] = 'edit'; + $this->disabled[] = 'archive'; + $this->disabled[] = 'extract'; + } + + // echo $this->tmp; + + } + + /** + * Connect to ftp server + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function connect() + { + $withSSL = empty($this->options['ssl']) ? '' : ' with SSL'; + if ($withSSL) { + if (!function_exists('ftp_ssl_connect') || !($this->connect = ftp_ssl_connect($this->options['host'], $this->options['port'], $this->options['timeout']))) { + return $this->setError('Unable to connect to FTP server ' . $this->options['host'] . $withSSL); + } + $this->isFTPS = true; + } else { + if (!($this->connect = ftp_connect($this->options['host'], $this->options['port'], $this->options['timeout']))) { + return $this->setError('Unable to connect to FTP server ' . $this->options['host']); + } + } + if (!ftp_login($this->connect, $this->options['user'], $this->options['pass'])) { + $this->umount(); + return $this->setError('Unable to login into ' . $this->options['host'] . $withSSL); + } + + // try switch utf8 mode + if ($this->encoding) { + ftp_raw($this->connect, 'OPTS UTF8 OFF'); + } else { + ftp_raw($this->connect, 'OPTS UTF8 ON'); + } + + $help = ftp_raw($this->connect, 'HELP'); + $this->isPureFtpd = stripos(implode(' ', $help), 'Pure-FTPd') !== false; + + if (!$this->isPureFtpd) { + // switch off extended passive mode - may be usefull for some servers + // this command, for pure-ftpd, doesn't work and takes a timeout in some pure-ftpd versions + ftp_raw($this->connect, 'epsv4 off'); + } + // enter passive mode if required + $pasv = ($this->options['mode'] == 'passive'); + if (!ftp_pasv($this->connect, $pasv)) { + if ($pasv) { + $this->options['mode'] = 'active'; + } + } + + // enter root folder + if (!ftp_chdir($this->connect, $this->root) + || $this->root != ftp_pwd($this->connect)) { + $this->umount(); + return $this->setError('Unable to open root folder.'); + } + + // check for MLST support + $features = ftp_raw($this->connect, 'FEAT'); + if (!is_array($features)) { + $this->umount(); + return $this->setError('Server does not support command FEAT.'); + } + + foreach ($features as $feat) { + if (strpos(trim($feat), 'MLST') === 0) { + $this->MLSTsupprt = true; + break; + } + } + + return true; + } + + /** + * Call ftp_rawlist with option prefix + * + * @param string $path + * + * @return array + */ + protected function ftpRawList($path) + { + if ($this->isPureFtpd) { + $path = str_replace(' ', '\ ', $path); + } + if ($this->ftpListOption) { + $path = $this->ftpListOption . ' ' . $path; + } + $res = ftp_rawlist($this->connect, $path); + if ($res === false) { + $res = array(); + } + return $res; + } + + /*********************************************************************/ + /* FS API */ + /*********************************************************************/ + + /** + * Close opened connection + * + * @return void + * @author Dmitry (dio) Levashov + **/ + public function umount() + { + $this->connect && ftp_close($this->connect); + } + + + /** + * Parse line from ftp_rawlist() output and return file stat (array) + * + * @param string $raw line from ftp_rawlist() output + * @param $base + * @param bool $nameOnly + * + * @return array + * @author Dmitry Levashov + */ + protected function parseRaw($raw, $base, $nameOnly = false) + { + static $now; + static $lastyear; + + if (!$now) { + $now = time(); + $lastyear = date('Y') - 1; + } + + $info = preg_split("/\s+/", $raw, 9); + $stat = array(); + + if (!isset($this->ftpOsUnix)) { + $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); + } + if (!$this->ftpOsUnix) { + $info = $this->normalizeRawWindows($raw); + } + + if (count($info) < 9 || $info[8] == '.' || $info[8] == '..') { + return false; + } + + $name = $info[8]; + + if (preg_match('|(.+)\-\>(.+)|', $name, $m)) { + $name = trim($m[1]); + // check recursive processing + if ($this->cacheDirTarget && $this->_joinPath($base, $name) !== $this->cacheDirTarget) { + return array(); + } + if (!$nameOnly) { + $target = trim($m[2]); + if (substr($target, 0, 1) !== $this->separator) { + $target = $this->getFullPath($target, $base); + } + $target = $this->_normpath($target); + $stat['name'] = $name; + $stat['target'] = $target; + return $stat; + } + } + + if ($nameOnly) { + return array('name' => $name); + } + + if (is_numeric($info[5]) && !$info[6] && !$info[7]) { + // by normalizeRawWindows() + $stat['ts'] = $info[5]; + } else { + $stat['ts'] = strtotime($info[5] . ' ' . $info[6] . ' ' . $info[7]); + if ($stat['ts'] && $stat['ts'] > $now && strpos($info[7], ':') !== false) { + $stat['ts'] = strtotime($info[5] . ' ' . $info[6] . ' ' . $lastyear . ' ' . $info[7]); + } + if (empty($stat['ts'])) { + $stat['ts'] = strtotime($info[6] . ' ' . $info[5] . ' ' . $info[7]); + if ($stat['ts'] && $stat['ts'] > $now && strpos($info[7], ':') !== false) { + $stat['ts'] = strtotime($info[6] . ' ' . $info[5] . ' ' . $lastyear . ' ' . $info[7]); + } + } + } + + if ($this->options['statOwner']) { + $stat['owner'] = $info[2]; + $stat['group'] = $info[3]; + $stat['perm'] = substr($info[0], 1); + // + // if not exists owner in LS ftp ==> isowner = true + // if is defined as option : 'owner' => true isowner = true + // + // if exist owner in LS ftp and 'owner' => False isowner = result of owner(file) == user(logged with ftp) + // + $stat['isowner'] = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true; + } + + $owner_computed = isset($stat['isowner']) ? $stat['isowner'] : $this->options['owner']; + $perm = $this->parsePermissions($info[0], $owner_computed); + $stat['name'] = $name; + $stat['mime'] = substr(strtolower($info[0]), 0, 1) == 'd' ? 'directory' : $this->mimetype($stat['name'], true); + $stat['size'] = $stat['mime'] == 'directory' ? 0 : $info[4]; + $stat['read'] = $perm['read']; + $stat['write'] = $perm['write']; + + return $stat; + } + + /** + * Normalize MS-DOS style FTP LIST Raw line + * + * @param string $raw line from FTP LIST (MS-DOS style) + * + * @return array + * @author Naoki Sawada + **/ + protected function normalizeRawWindows($raw) + { + $info = array_pad(array(), 9, ''); + $item = preg_replace('#\s+#', ' ', trim($raw), 3); + list($date, $time, $size, $name) = explode(' ', $item, 4); + $format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i'; + $dateObj = DateTime::createFromFormat($format, $date . $time); + $info[5] = strtotime($dateObj->format('Y-m-d H:i')); + $info[8] = $name; + if ($size === '') { + $info[4] = 0; + $info[0] = 'drwxr-xr-x'; + } else { + $info[4] = (int)$size; + $info[0] = '-rw-r--r--'; + } + return $info; + } + + /** + * Parse permissions string. Return array(read => true/false, write => true/false) + * + * @param string $perm permissions string 'rwx' + 'rwx' + 'rwx' + * ^ ^ ^ + * | | +-> others + * | +---------> group + * +-----------------> owner + * The isowner parameter is computed by the caller. + * If the owner parameter in the options is true, the user is the actual owner of all objects even if che user used in the ftp Login + * is different from the file owner id. + * If the owner parameter is false to understand if the user is the file owner we compare the ftp user with the file owner id. + * @param Boolean $isowner . Tell if the current user is the owner of the object. + * + * @return string + * @author Dmitry (dio) Levashov + * @author Ugo Vierucci + */ + protected function parsePermissions($perm, $isowner = true) + { + $res = array(); + $parts = array(); + for ($i = 0, $l = strlen($perm); $i < $l; $i++) { + $parts[] = substr($perm, $i, 1); + } + + $read = ($isowner && $parts[1] == 'r') || $parts[4] == 'r' || $parts[7] == 'r'; + + return array( + 'read' => $parts[0] == 'd' ? $read && (($isowner && $parts[3] == 'x') || $parts[6] == 'x' || $parts[9] == 'x') : $read, + 'write' => ($isowner && $parts[2] == 'w') || $parts[5] == 'w' || $parts[8] == 'w' + ); + } + + /** + * Cache dir contents + * + * @param string $path dir path + * + * @return void + * @author Dmitry Levashov + **/ + protected function cacheDir($path) + { + $this->dirsCache[$path] = array(); + $hasDir = false; + + $list = array(); + $encPath = $this->convEncIn($path); + foreach ($this->ftpRawList($encPath) as $raw) { + if (($stat = $this->parseRaw($raw, $encPath))) { + $list[] = $stat; + } + } + $list = $this->convEncOut($list); + $prefix = ($path === $this->separator) ? $this->separator : $path . $this->separator; + $targets = array(); + foreach ($list as $stat) { + $p = $prefix . $stat['name']; + if (isset($stat['target'])) { + // stat later + $targets[$stat['name']] = $stat['target']; + } else { + $stat = $this->updateCache($p, $stat); + if (empty($stat['hidden'])) { + if (!$hasDir && $stat['mime'] === 'directory') { + $hasDir = true; + } + $this->dirsCache[$path][] = $p; + } + } + } + // stat link targets + foreach ($targets as $name => $target) { + $stat = array(); + $stat['name'] = $name; + $p = $prefix . $name; + $cacheDirTarget = $this->cacheDirTarget; + $this->cacheDirTarget = $this->convEncIn($target, true); + if ($tstat = $this->stat($target)) { + $stat['size'] = $tstat['size']; + $stat['alias'] = $target; + $stat['thash'] = $tstat['hash']; + $stat['mime'] = $tstat['mime']; + $stat['read'] = $tstat['read']; + $stat['write'] = $tstat['write']; + + if (isset($tstat['ts'])) { + $stat['ts'] = $tstat['ts']; + } + if (isset($tstat['owner'])) { + $stat['owner'] = $tstat['owner']; + } + if (isset($tstat['group'])) { + $stat['group'] = $tstat['group']; + } + if (isset($tstat['perm'])) { + $stat['perm'] = $tstat['perm']; + } + if (isset($tstat['isowner'])) { + $stat['isowner'] = $tstat['isowner']; + } + } else { + + $stat['mime'] = 'symlink-broken'; + $stat['read'] = false; + $stat['write'] = false; + $stat['size'] = 0; + + } + $this->cacheDirTarget = $cacheDirTarget; + $stat = $this->updateCache($p, $stat); + if (empty($stat['hidden'])) { + if (!$hasDir && $stat['mime'] === 'directory') { + $hasDir = true; + } + $this->dirsCache[$path][] = $p; + } + } + + if (isset($this->sessionCache['subdirs'])) { + $this->sessionCache['subdirs'][$path] = $hasDir; + } + } + + /** + * Return ftp transfer mode for file + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function ftpMode($path) + { + return strpos($this->mimetype($path), 'text/') === 0 ? FTP_ASCII : FTP_BINARY; + } + + /*********************** paths/urls *************************/ + + /** + * Return parent directory path + * + * @param string $path file path + * + * @return string + * @author Naoki Sawada + **/ + protected function _dirname($path) + { + $parts = explode($this->separator, trim($path, $this->separator)); + array_pop($parts); + return $this->separator . join($this->separator, $parts); + } + + /** + * Return file name + * + * @param string $path file path + * + * @return string + * @author Naoki Sawada + **/ + protected function _basename($path) + { + $parts = explode($this->separator, trim($path, $this->separator)); + return array_pop($parts); + } + + /** + * Join dir name and file name and retur full path + * + * @param string $dir + * @param string $name + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _joinPath($dir, $name) + { + return rtrim($dir, $this->separator) . $this->separator . $name; + } + + /** + * Return normalized path, this works the same as os.path.normpath() in Python + * + * @param string $path path + * + * @return string + * @author Troex Nevelin + **/ + protected function _normpath($path) + { + if (empty($path)) { + $path = '.'; + } + // path must be start with / + $path = preg_replace('|^\.\/?|', $this->separator, $path); + $path = preg_replace('/^([^\/])/', "/$1", $path); + + if ($path[0] === $this->separator) { + $initial_slashes = true; + } else { + $initial_slashes = false; + } + + if (($initial_slashes) + && (strpos($path, '//') === 0) + && (strpos($path, '///') === false)) { + $initial_slashes = 2; + } + + $initial_slashes = (int)$initial_slashes; + + $comps = explode($this->separator, $path); + $new_comps = array(); + foreach ($comps as $comp) { + if (in_array($comp, array('', '.'))) { + continue; + } + + if (($comp != '..') + || (!$initial_slashes && !$new_comps) + || ($new_comps && (end($new_comps) == '..'))) { + array_push($new_comps, $comp); + } elseif ($new_comps) { + array_pop($new_comps); + } + } + $comps = $new_comps; + $path = implode($this->separator, $comps); + if ($initial_slashes) { + $path = str_repeat($this->separator, $initial_slashes) . $path; + } + + return $path ? $path : '.'; + } + + /** + * Return file path related to root dir + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _relpath($path) + { + if ($path === $this->root) { + return ''; + } else { + if (strpos($path, $this->root) === 0) { + return ltrim(substr($path, strlen($this->root)), $this->separator); + } else { + // for link + return $path; + } + } + } + + /** + * Convert path related to root dir into real path + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _abspath($path) + { + if ($path === $this->separator) { + return $this->root; + } else { + if ($path[0] === $this->separator) { + // for link + return $path; + } else { + return $this->_joinPath($this->root, $path); + } + } + } + + /** + * Return fake path started from root dir + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _path($path) + { + return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path)); + } + + /** + * Return true if $path is children of $parent + * + * @param string $path path to check + * @param string $parent parent path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _inpath($path, $parent) + { + return $path == $parent || strpos($path, rtrim($parent, $this->separator) . $this->separator) === 0; + } + + /***************** file stat ********************/ + /** + * Return stat for given path. + * Stat contains following fields: + * - (int) size file size in b. required + * - (int) ts file modification time in unix time. required + * - (string) mime mimetype. required for folders, others - optionally + * - (bool) read read permissions. required + * - (bool) write write permissions. required + * - (bool) locked is object locked. optionally + * - (bool) hidden is object hidden. optionally + * - (string) alias for symlinks - link target path relative to root path. optionally + * - (string) target for symlinks - link target path. optionally + * If file does not exists - returns empty array or false. + * + * @param string $path file path + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + protected function _stat($path) + { + $outPath = $this->convEncOut($path); + if (isset($this->cache[$outPath])) { + return $this->convEncIn($this->cache[$outPath]); + } else { + $this->convEncIn(); + } + if (!$this->MLSTsupprt) { + if ($path === $this->root) { + $res = array( + 'name' => $this->root, + 'mime' => 'directory', + 'dirs' => $this->_subdirs($path) + ); + if ($this->isMyReload()) { + $ts = 0; + foreach ($this->ftpRawList($path) as $str) { + if (($stat = $this->parseRaw($str, $path))) { + if (!empty($stat['ts'])) { + $ts = max($ts, $stat['ts']); + } + } + } + if ($ts) { + $res['ts'] = $ts; + } + } + return $res; + } + // stat of system root + if ($path === $this->separator) { + $res = array( + 'name' => $this->separator, + 'mime' => 'directory', + 'dirs' => 1 + ); + $this->cache[$outPath] = $res; + return $res; + } + + $pPath = $this->_dirname($path); + if ($this->_inPath($pPath, $this->root)) { + $outPPpath = $this->convEncOut($pPath); + if (!isset($this->dirsCache[$outPPpath])) { + $parentSubdirs = null; + if (isset($this->sessionCache['subdirs']) && isset($this->sessionCache['subdirs'][$outPPpath])) { + $parentSubdirs = $this->sessionCache['subdirs'][$outPPpath]; + } + $this->cacheDir($outPPpath); + if ($parentSubdirs) { + $this->sessionCache['subdirs'][$outPPpath] = $parentSubdirs; + } + } + } + + $stat = $this->convEncIn(isset($this->cache[$outPath]) ? $this->cache[$outPath] : array()); + if (!$this->mounted) { + // dispose incomplete cache made by calling `stat` by 'startPath' option + $this->cache = array(); + } + return $stat; + } + $raw = ftp_raw($this->connect, 'MLST ' . $path); + if (is_array($raw) && count($raw) > 1 && substr(trim($raw[0]), 0, 1) == 2) { + $parts = explode(';', trim($raw[1])); + array_pop($parts); + $parts = array_map('strtolower', $parts); + $stat = array(); + $mode = ''; + foreach ($parts as $part) { + + list($key, $val) = explode('=', $part, 2); + + switch ($key) { + case 'type': + if (strpos($val, 'dir') !== false) { + $stat['mime'] = 'directory'; + } else if (strpos($val, 'link') !== false) { + $stat['mime'] = 'symlink'; + break(2); + } else { + $stat['mime'] = $this->mimetype($path); + } + break; + + case 'size': + $stat['size'] = $val; + break; + + case 'modify': + $ts = mktime(intval(substr($val, 8, 2)), intval(substr($val, 10, 2)), intval(substr($val, 12, 2)), intval(substr($val, 4, 2)), intval(substr($val, 6, 2)), substr($val, 0, 4)); + $stat['ts'] = $ts; + break; + + case 'unix.mode': + $mode = strval($val); + break; + + case 'unix.uid': + $stat['owner'] = $val; + break; + + case 'unix.gid': + $stat['group'] = $val; + break; + + case 'perm': + $val = strtolower($val); + $stat['read'] = (int)preg_match('/e|l|r/', $val); + $stat['write'] = (int)preg_match('/w|m|c/', $val); + if (!preg_match('/f|d/', $val)) { + $stat['locked'] = 1; + } + break; + } + } + + if (empty($stat['mime'])) { + return array(); + } + + // do not use MLST to get stat of symlink + if ($stat['mime'] === 'symlink') { + $this->MLSTsupprt = false; + $res = $this->_stat($path); + $this->MLSTsupprt = true; + return $res; + } + + if ($stat['mime'] === 'directory') { + $stat['size'] = 0; + } + + if ($mode) { + $stat['perm'] = ''; + if ($mode[0] === '0') { + $mode = substr($mode, 1); + } + + $perm = array(); + for ($i = 0; $i <= 2; $i++) { + $perm[$i] = array(false, false, false); + $n = isset($mode[$i]) ? $mode[$i] : 0; + + if ($n - 4 >= 0) { + $perm[$i][0] = true; + $n = $n - 4; + $stat['perm'] .= 'r'; + } else { + $stat['perm'] .= '-'; + } + + if ($n - 2 >= 0) { + $perm[$i][1] = true; + $n = $n - 2; + $stat['perm'] .= 'w'; + } else { + $stat['perm'] .= '-'; + } + + if ($n - 1 == 0) { + $perm[$i][2] = true; + $stat['perm'] .= 'x'; + } else { + $stat['perm'] .= '-'; + } + } + + $stat['perm'] = trim($stat['perm']); + // + // if not exists owner in LS ftp ==> isowner = true + // if is defined as option : 'owner' => true isowner = true + // + // if exist owner in LS ftp and 'owner' => False isowner = result of owner(file) == user(logged with ftp) + + $owner_computed = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true; + + $read = ($owner_computed && $perm[0][0]) || $perm[1][0] || $perm[2][0]; + + $stat['read'] = $stat['mime'] == 'directory' ? $read && (($owner_computed && $perm[0][2]) || $perm[1][2] || $perm[2][2]) : $read; + $stat['write'] = ($owner_computed && $perm[0][1]) || $perm[1][1] || $perm[2][1]; + + if ($this->options['statOwner']) { + $stat['isowner'] = $owner_computed; + } else { + unset($stat['owner'], $stat['group'], $stat['perm']); + } + } + + return $stat; + + } + + return array(); + } + + /** + * Return true if path is dir and has at least one childs directory + * + * @param string $path dir path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _subdirs($path) + { + + foreach ($this->ftpRawList($path) as $str) { + $info = preg_split('/\s+/', $str, 9); + if (!isset($this->ftpOsUnix)) { + $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); + } + if (!$this->ftpOsUnix) { + $info = $this->normalizeRawWindows($str); + } + $name = isset($info[8]) ? trim($info[8]) : ''; + if ($name && $name !== '.' && $name !== '..' && substr(strtolower($info[0]), 0, 1) === 'd') { + return true; + } + } + return false; + } + + /** + * Return object width and height + * Ususaly used for images, but can be realize for video etc... + * + * @param string $path file path + * @param string $mime file mime type + * + * @return string|false + * @throws ImagickException + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function _dimensions($path, $mime) + { + $ret = false; + if ($imgsize = $this->getImageSize($path, $mime)) { + $ret = array('dim' => $imgsize['dimensions']); + if (!empty($imgsize['url'])) { + $ret['url'] = $imgsize['url']; + } + } + return $ret; + } + + /******************** file/dir content *********************/ + + /** + * Return files list in directory. + * + * @param string $path dir path + * + * @return array + * @author Dmitry (dio) Levashov + * @author Cem (DiscoFever) + **/ + protected function _scandir($path) + { + $files = array(); + + foreach ($this->ftpRawList($path) as $str) { + if (($stat = $this->parseRaw($str, $path, true))) { + $files[] = $this->_joinPath($path, $stat['name']); + } + } + + return $files; + } + + /** + * Open file and return file pointer + * + * @param string $path file path + * @param string $mode + * + * @return false|resource + * @throws elFinderAbortException + * @internal param bool $write open file for writing + * @author Dmitry (dio) Levashov + */ + protected function _fopen($path, $mode = 'rb') + { + // try ftp stream wrapper + if ($this->options['mode'] === 'passive' && ini_get('allow_url_fopen')) { + $url = ($this->isFTPS ? 'ftps' : 'ftp') . '://' . $this->options['user'] . ':' . $this->options['pass'] . '@' . $this->options['host'] . ':' . $this->options['port'] . $path; + if (strtolower($mode[0]) === 'w') { + $context = stream_context_create(array('ftp' => array('overwrite' => true))); + $fp = fopen($url, $mode, false, $context); + } else { + $fp = fopen($url, $mode); + } + if ($fp) { + return $fp; + } + } + + if ($this->tmp) { + $local = $this->getTempFile($path); + $fp = fopen($local, 'wb'); + $ret = ftp_nb_fget($this->connect, $fp, $path, FTP_BINARY); + while ($ret === FTP_MOREDATA) { + elFinder::extendTimeLimit(); + $ret = ftp_nb_continue($this->connect); + } + if ($ret === FTP_FINISHED) { + fclose($fp); + $fp = fopen($local, $mode); + return $fp; + } + fclose($fp); + is_file($local) && unlink($local); + } + + return false; + } + + /** + * Close opened file + * + * @param resource $fp file pointer + * @param string $path + * + * @return void + * @author Dmitry (dio) Levashov + */ + protected function _fclose($fp, $path = '') + { + is_resource($fp) && fclose($fp); + if ($path) { + unlink($this->getTempFile($path)); + } + } + + /******************** file/dir manipulations *************************/ + + /** + * Create dir and return created dir path or false on failed + * + * @param string $path parent dir path + * @param string $name new directory name + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + protected function _mkdir($path, $name) + { + $path = $this->_joinPath($path, $name); + if (ftp_mkdir($this->connect, $path) === false) { + return false; + } + + $this->options['dirMode'] && ftp_chmod($this->connect, $this->options['dirMode'], $path); + return $path; + } + + /** + * Create file and return it's path or false on failed + * + * @param string $path parent dir path + * @param string $name new file name + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + protected function _mkfile($path, $name) + { + if ($this->tmp) { + $path = $this->_joinPath($path, $name); + $local = $this->getTempFile(); + $res = touch($local) && ftp_put($this->connect, $path, $local, FTP_ASCII); + unlink($local); + return $res ? $path : false; + } + return false; + } + + /** + * Create symlink. FTP driver does not support symlinks. + * + * @param string $target link target + * @param string $path symlink path + * @param string $name + * + * @return bool + * @author Dmitry (dio) Levashov + */ + protected function _symlink($target, $path, $name) + { + return false; + } + + /** + * Copy file into another file + * + * @param string $source source file path + * @param string $targetDir target directory path + * @param string $name new file name + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _copy($source, $targetDir, $name) + { + $res = false; + + if ($this->tmp) { + $local = $this->getTempFile(); + $target = $this->_joinPath($targetDir, $name); + + if (ftp_get($this->connect, $local, $source, FTP_BINARY) + && ftp_put($this->connect, $target, $local, $this->ftpMode($target))) { + $res = $target; + } + unlink($local); + } + + return $res; + } + + /** + * Move file into another parent dir. + * Return new file path or false. + * + * @param string $source source file path + * @param $targetDir + * @param string $name file name + * + * @return bool|string + * @internal param string $target target dir path + * @author Dmitry (dio) Levashov + */ + protected function _move($source, $targetDir, $name) + { + $target = $this->_joinPath($targetDir, $name); + return ftp_rename($this->connect, $source, $target) ? $target : false; + } + + /** + * Remove file + * + * @param string $path file path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _unlink($path) + { + return ftp_delete($this->connect, $path); + } + + /** + * Remove dir + * + * @param string $path dir path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _rmdir($path) + { + return ftp_rmdir($this->connect, $path); + } + + /** + * Create new file and write into it from file pointer. + * Return new file path or false on error. + * + * @param resource $fp file pointer + * @param string $dir target dir path + * @param string $name file name + * @param array $stat file stat (required by some virtual fs) + * + * @return bool|string + * @author Dmitry (dio) Levashov + **/ + protected function _save($fp, $dir, $name, $stat) + { + $path = $this->_joinPath($dir, $name); + return ftp_fput($this->connect, $path, $fp, $this->ftpMode($path)) + ? $path + : false; + } + + /** + * Get file contents + * + * @param string $path file path + * + * @return string|false + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function _getContents($path) + { + $contents = ''; + if (($fp = $this->_fopen($path))) { + while (!feof($fp)) { + $contents .= fread($fp, 8192); + } + $this->_fclose($fp, $path); + return $contents; + } + return false; + } + + /** + * Write a string to a file + * + * @param string $path file path + * @param string $content new file content + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _filePutContents($path, $content) + { + $res = false; + + if ($this->tmp) { + $local = $this->getTempFile(); + + if (file_put_contents($local, $content, LOCK_EX) !== false + && ($fp = fopen($local, 'rb'))) { + $file = $this->stat($this->convEncOut($path, false)); + if (!empty($file['thash'])) { + $path = $this->decode($file['thash']); + } + clearstatcache(); + $res = ftp_fput($this->connect, $path, $fp, $this->ftpMode($path)); + fclose($fp); + } + file_exists($local) && unlink($local); + } + + return $res; + } + + /** + * Detect available archivers + * + * @return void + * @throws elFinderAbortException + */ + protected function _checkArchivers() + { + $this->archivers = $this->getArchivers(); + return; + } + + /** + * chmod availability + * + * @param string $path + * @param string $mode + * + * @return bool + */ + protected function _chmod($path, $mode) + { + $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode)); + return ftp_chmod($this->connect, $modeOct, $path); + } + + /** + * Extract files from archive + * + * @param string $path archive path + * @param array $arc archiver command and arguments (same as in $this->archivers) + * + * @return true + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + */ + protected function _extract($path, $arc) + { + $dir = $this->tempDir(); + if (!$dir) { + return false; + } + + $basename = $this->_basename($path); + $localPath = $dir . DIRECTORY_SEPARATOR . $basename; + + if (!ftp_get($this->connect, $localPath, $path, FTP_BINARY)) { + //cleanup + $this->rmdirRecursive($dir); + return false; + } + + $this->unpackArchive($localPath, $arc); + + $this->archiveSize = 0; + + // find symlinks and check extracted items + $checkRes = $this->checkExtractItems($dir); + if ($checkRes['symlinks']) { + $this->rmdirRecursive($dir); + return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS))); + } + $this->archiveSize = $checkRes['totalSize']; + if ($checkRes['rmNames']) { + foreach ($checkRes['rmNames'] as $name) { + $this->addError(elFinder::ERROR_SAVE, $name); + } + } + + $filesToProcess = self::listFilesInDirectory($dir, true); + + // no files - extract error ? + if (empty($filesToProcess)) { + $this->rmdirRecursive($dir); + return false; + } + + // check max files size + if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { + $this->rmdirRecursive($dir); + return $this->setError(elFinder::ERROR_ARC_MAXSIZE); + } + + $extractTo = $this->extractToNewdir; // 'auto', ture or false + + // archive contains one item - extract in archive dir + $name = ''; + $src = $dir . DIRECTORY_SEPARATOR . $filesToProcess[0]; + if (($extractTo === 'auto' || !$extractTo) && count($filesToProcess) === 1 && is_file($src)) { + $name = $filesToProcess[0]; + } else if ($extractTo === 'auto' || $extractTo) { + // for several files - create new directory + // create unique name for directory + $src = $dir; + $splits = elFinder::splitFileExtention(basename($path)); + $name = $splits[0]; + $test = $this->_joinPath(dirname($path), $name); + if ($this->stat($test)) { + $name = $this->uniqueName(dirname($path), $name, '-', false); + } + } + + if ($name !== '' && is_file($src)) { + $result = $this->_joinPath(dirname($path), $name); + + if (!ftp_put($this->connect, $result, $src, FTP_BINARY)) { + $this->rmdirRecursive($dir); + return false; + } + } else { + $dstDir = $this->_dirname($path); + $result = array(); + if (is_dir($src) && $name) { + $target = $this->_joinPath($dstDir, $name); + $_stat = $this->_stat($target); + if ($_stat) { + if (!$this->options['copyJoin']) { + if ($_stat['mime'] === 'directory') { + $this->delTree($target); + } else { + $this->_unlink($target); + } + $_stat = false; + } else { + $dstDir = $target; + } + } + if (!$_stat && (!$dstDir = $this->_mkdir($dstDir, $name))) { + $this->rmdirRecursive($dir); + return false; + } + $result[] = $dstDir; + } + foreach ($filesToProcess as $name) { + $name = rtrim($name, DIRECTORY_SEPARATOR); + $src = $dir . DIRECTORY_SEPARATOR . $name; + if (is_dir($src)) { + $p = dirname($name); + if ($p === '.') { + $p = ''; + } + $name = basename($name); + $target = $this->_joinPath($this->_joinPath($dstDir, $p), $name); + $_stat = $this->_stat($target); + if ($_stat) { + if (!$this->options['copyJoin']) { + if ($_stat['mime'] === 'directory') { + $this->delTree($target); + } else { + $this->_unlink($target); + } + $_stat = false; + } + } + if (!$_stat && (!$target = $this->_mkdir($this->_joinPath($dstDir, $p), $name))) { + $this->rmdirRecursive($dir); + return false; + } + } else { + $target = $this->_joinPath($dstDir, $name); + if (!ftp_put($this->connect, $target, $src, FTP_BINARY)) { + $this->rmdirRecursive($dir); + return false; + } + } + $result[] = $target; + } + if (!$result) { + $this->rmdirRecursive($dir); + return false; + } + } + + is_dir($dir) && $this->rmdirRecursive($dir); + + $this->clearcache(); + return $result ? $result : false; + } + + /** + * Create archive and return its path + * + * @param string $dir target dir + * @param array $files files names list + * @param string $name archive name + * @param array $arc archiver options + * + * @return string|bool + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + */ + protected function _archive($dir, $files, $name, $arc) + { + // get current directory + $cwd = getcwd(); + + $tmpDir = $this->tempDir(); + if (!$tmpDir) { + return false; + } + + //download data + if (!$this->ftp_download_files($dir, $files, $tmpDir)) { + //cleanup + $this->rmdirRecursive($tmpDir); + return false; + } + + $remoteArchiveFile = false; + if ($path = $this->makeArchive($tmpDir, $files, $name, $arc)) { + $remoteArchiveFile = $this->_joinPath($dir, $name); + if (!ftp_put($this->connect, $remoteArchiveFile, $path, FTP_BINARY)) { + $remoteArchiveFile = false; + } + } + + //cleanup + if (!$this->rmdirRecursive($tmpDir)) { + return false; + } + + return $remoteArchiveFile; + } + + /** + * Create writable temporary directory and return path to it. + * + * @return string path to the new temporary directory or false in case of error. + */ + private function tempDir() + { + $tempPath = tempnam($this->tmp, 'elFinder'); + if (!$tempPath) { + $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); + return false; + } + $success = unlink($tempPath); + if (!$success) { + $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); + return false; + } + $success = mkdir($tempPath, 0700, true); + if (!$success) { + $this->setError(elFinder::ERROR_CREATING_TEMP_DIR, $this->tmp); + return false; + } + return $tempPath; + } + + /** + * Gets an array of absolute remote FTP paths of files and + * folders in $remote_directory omitting symbolic links. + * + * @param $remote_directory string remote FTP path to scan for file and folders recursively + * @param $targets array Array of target item. `null` is to get all of items + * + * @return array of elements each of which is an array of two elements: + * + */ + protected function ftp_scan_dir($remote_directory, $targets = null) + { + $buff = $this->ftpRawList($remote_directory); + $items = array(); + if ($targets && is_array($targets)) { + $targets = array_flip($targets); + } else { + $targets = false; + } + foreach ($buff as $str) { + $info = preg_split("/\s+/", $str, 9); + if (!isset($this->ftpOsUnix)) { + $this->ftpOsUnix = !preg_match('/\d/', substr($info[0], 0, 1)); + } + if (!$this->ftpOsUnix) { + $info = $this->normalizeRawWindows($str); + } + $type = substr($info[0], 0, 1); + $name = trim($info[8]); + if ($name !== '.' && $name !== '..' && (!$targets || isset($targets[$name]))) { + switch ($type) { + case 'l' : //omit symbolic links + case 'd' : + $remote_file_path = $this->_joinPath($remote_directory, $name); + $item = array(); + $item['path'] = $remote_file_path; + $item['type'] = 'd'; // normal file + $items[] = $item; + $items = array_merge($items, $this->ftp_scan_dir($remote_file_path)); + break; + default: + $remote_file_path = $this->_joinPath($remote_directory, $name); + $item = array(); + $item['path'] = $remote_file_path; + $item['type'] = 'f'; // normal file + $items[] = $item; + } + } + } + return $items; + } + + /** + * Downloads specified files from remote directory + * if there is a directory among files it is downloaded recursively (omitting symbolic links). + * + * @param $remote_directory string remote FTP path to a source directory to download from. + * @param array $files list of files to download from remote directory. + * @param $dest_local_directory string destination folder to store downloaded files. + * + * @return bool true on success and false on failure. + */ + private function ftp_download_files($remote_directory, array $files, $dest_local_directory) + { + $contents = $this->ftp_scan_dir($remote_directory, $files); + if (!isset($contents)) { + $this->setError(elFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); + return false; + } + $remoteDirLen = strlen($remote_directory); + foreach ($contents as $item) { + $relative_path = substr($item['path'], $remoteDirLen); + $local_path = $dest_local_directory . DIRECTORY_SEPARATOR . $relative_path; + switch ($item['type']) { + case 'd': + $success = mkdir($local_path); + break; + case 'f': + $success = ftp_get($this->connect, $local_path, $item['path'], FTP_BINARY); + break; + default: + $success = true; + } + if (!$success) { + $this->setError(elFinder::ERROR_FTP_DOWNLOAD_FILE, $remote_directory); + return false; + } + } + return true; + } + + /** + * Delete local directory recursively. + * + * @param $dirPath string to directory to be erased. + * + * @return bool true on success and false on failure. + * @throws Exception + */ + private function deleteDir($dirPath) + { + if (!is_dir($dirPath)) { + $success = unlink($dirPath); + } else { + $success = true; + foreach (array_reverse(elFinderVolumeFTP::listFilesInDirectory($dirPath, false)) as $path) { + $path = $dirPath . DIRECTORY_SEPARATOR . $path; + if (is_link($path)) { + unlink($path); + } else if (is_dir($path)) { + $success = rmdir($path); + } else { + $success = unlink($path); + } + if (!$success) { + break; + } + } + if ($success) { + $success = rmdir($dirPath); + } + } + if (!$success) { + $this->setError(elFinder::ERROR_RM, $dirPath); + return false; + } + return $success; + } + + /** + * Returns array of strings containing all files and folders in the specified local directory. + * + * @param $dir + * @param $omitSymlinks + * @param string $prefix + * + * @return array array of files and folders names relative to the $path + * or an empty array if the directory $path is empty, + *
+ * false if $path is not a directory or does not exist. + * @throws Exception + * @internal param string $path path to directory to scan. + */ + private static function listFilesInDirectory($dir, $omitSymlinks, $prefix = '') + { + if (!is_dir($dir)) { + return false; + } + $excludes = array(".", ".."); + $result = array(); + $files = self::localScandir($dir); + if (!$files) { + return array(); + } + foreach ($files as $file) { + if (!in_array($file, $excludes)) { + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (is_link($path)) { + if ($omitSymlinks) { + continue; + } else { + $result[] = $prefix . $file; + } + } else if (is_dir($path)) { + $result[] = $prefix . $file . DIRECTORY_SEPARATOR; + $subs = elFinderVolumeFTP::listFilesInDirectory($path, $omitSymlinks, $prefix . $file . DIRECTORY_SEPARATOR); + if ($subs) { + $result = array_merge($result, $subs); + } + + } else { + $result[] = $prefix . $file; + } + } + } + return $result; + } } // END class diff --git a/php/elFinderVolumeGoogleDrive.class.php b/php/elFinderVolumeGoogleDrive.class.php index fe0c7ca6d..e7d25ab9f 100644 --- a/php/elFinderVolumeGoogleDrive.class.php +++ b/php/elFinderVolumeGoogleDrive.class.php @@ -91,7 +91,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver /** * Current token expires - * + * * @var integer **/ private $expires; @@ -160,7 +160,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $paths = explode('/', $path); $id = array_pop($paths); if ($paths) { - $parent = '/'.implode('/', $paths); + $parent = '/' . implode('/', $paths); $pid = array_pop($paths); } else { $rootid = ($this->root === '/') ? 'root' : trim($this->root, '/'); @@ -188,9 +188,9 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $result = []; $pageToken = null; $parameters = [ - 'fields' => self::FETCHFIELDS_LIST, - 'pageSize' => 1000, - 'spaces' => 'drive', + 'fields' => self::FETCHFIELDS_LIST, + 'pageSize' => 1000, + 'spaces' => 'drive', ]; if (is_array($opts)) { @@ -244,7 +244,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param array $raw line from ftp_rawlist() output * * @return array - * * @author Dmitry Levashov **/ protected function _gd_parseRaw($raw) @@ -262,7 +261,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $stat['size'] = 0; } else { $stat['mime'] = $raw['mimeType'] == 'image/bmp' ? 'image/x-ms-bmp' : $raw['mimeType']; - $stat['size'] = (int) $raw['size']; + $stat['size'] = (int)$raw['size']; if ($size = $raw->getImageMediaMetadata()) { $stat['width'] = $size['width']; $stat['height'] = $size['height']; @@ -273,7 +272,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver if ($this->options['useGoogleTmb']) { if (isset($raw['thumbnailLink'])) { if ($published) { - $stat['tmb'] = 'drive.google.com/thumbnail?authuser=0&sz=s'.$this->options['tmbSize'].'&id='.$raw['id']; + $stat['tmb'] = 'drive.google.com/thumbnail?authuser=0&sz=s' . $this->options['tmbSize'] . '&id=' . $raw['id']; } else { $stat['tmb'] = substr($raw['thumbnailLink'], 8); // remove "https://" } @@ -317,7 +316,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver protected function _gd_getDirectoryData($usecache = true) { if ($usecache) { - $cache = $this->session->get($this->id.$this->netMountKey, []); + $cache = $this->session->get($this->id . $this->netMountKey, []); if ($cache) { $this->parents = $cache['parents']; $this->names = $cache['names']; @@ -337,8 +336,8 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $data = []; $opts = [ - 'fields' => 'files(id, name, parents)', - 'q' => sprintf('trashed=false and mimeType="%s"', self::DIRMIME), + 'fields' => 'files(id, name, parents)', + 'q' => sprintf('trashed=false and mimeType="%s"', self::DIRMIME), ]; $res = $this->_gd_query($opts); foreach ($res as $raw) { @@ -359,10 +358,10 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $data['root'] = $data[$root]; } $this->directories = $data; - $this->session->set($this->id.$this->netMountKey, [ - 'parents' => $this->parents, - 'names' => $this->names, - 'directories' => $this->directories, + $this->session->set($this->id . $this->netMountKey, [ + 'parents' => $this->parents, + 'names' => $this->names, + 'directories' => $this->directories, ]); } @@ -394,6 +393,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Get ID based path from item ID. * * @param string $id + * * @return array */ protected function _gd_getMountPaths($id) @@ -404,7 +404,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } list($pid) = explode('/', $id, 2); $path = $id; - if ('/'.$pid === $this->root) { + if ('/' . $pid === $this->root) { $root = true; } elseif (!isset($this->parents[$pid])) { $root = true; @@ -412,12 +412,12 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } $res = []; if ($root) { - if ($this->root === '/' || strpos('/'.$path, $this->root) === 0) { - $res = [(strpos($path, '/') === false) ? '/' : ('/'.$path)]; + if ($this->root === '/' || strpos('/' . $path, $this->root) === 0) { + $res = [(strpos($path, '/') === false) ? '/' : ('/' . $path)]; } } else { foreach ($this->parents[$pid] as $p) { - $_p = $p.'/'.$path; + $_p = $p . '/' . $path; $res = array_merge($res, $this->_gd_getMountPaths($_p)); } } @@ -480,7 +480,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver protected function _gd_getDownloadUrl($file) { if (strpos($file->mimeType, 'application/vnd.google-apps.') !== 0) { - return 'https://www.googleapis.com/drive/v3/files/'.$file->getId().'?alt=media'; + return 'https://www.googleapis.com/drive/v3/files/' . $file->getId() . '?alt=media'; } else { $mimeMap = $this->options['appsExportMap']; if (isset($mimeMap[$file->getMimeType()])) { @@ -490,7 +490,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } $mime = rawurlencode($mime); - return 'https://www.googleapis.com/drive/v3/files/'.$file->getId().'/export?mimeType='.$mime; + return 'https://www.googleapis.com/drive/v3/files/' . $file->getId() . '/export?mimeType=' . $mime; } return false; @@ -500,6 +500,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Get thumbnail from GoogleDrive.com. * * @param string $path + * * @return string | boolean */ protected function _gd_getThumbnail($path) @@ -508,7 +509,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver try { $contents = $this->service->files->get($itemId, [ - 'alt' => 'media', + 'alt' => 'media', ]); $contents = $contents->getBody()->detach(); rewind($contents); @@ -611,7 +612,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Call from elFinder::netmout() before volume->mount(). * * @return array - * * @author Naoki Sawada * @author Raja Sharma updating for GoogleDrive **/ @@ -682,7 +682,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } $callback = $options['url'] - .'?cmd=netmount&protocol=googledrive&host=1'; + . '?cmd=netmount&protocol=googledrive&host=1'; $client->setRedirectUri($callback); if (!$aToken && empty($_GET['code'])) { @@ -693,9 +693,9 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } $url = $client->createAuthUrl(); - $html = ''; + $html = ''; $html .= ''; if (empty($options['pass']) && $options['host'] !== '1') { $options['pass'] = 'return'; @@ -705,7 +705,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } else { $out = [ 'node' => $options['id'], - 'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "'.str_replace($html, '"', '\\"').'", "error" : "'.elFinder::ERROR_ACCESS_DENIED.'"}', + 'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "' . str_replace($html, '"', '\\"') . '", "error" : "' . elFinder::ERROR_ACCESS_DENIED . '"}', 'bind' => 'netmount', ]; @@ -744,11 +744,11 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $folders = ['root' => $rootObj->getName()] + $folders; $folders = json_encode($folders); $expires = empty($aToken['refresh_token']) ? $aToken['created'] + $aToken['expires_in'] - 30 : 0; - $json = '{"protocol": "googledrive", "mode": "done", "folders": '.$folders.', "expires": '.$expires.'}'; + $json = '{"protocol": "googledrive", "mode": "done", "folders": ' . $folders . ', "expires": ' . $expires . '}'; $options['pass'] = 'return'; $html = 'Google.com'; $html .= ''; $this->session->set('GoogleDriveAuthParams', $options); @@ -758,7 +758,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } catch (Exception $e) { $this->session->remove('GoogleDriveAuthParams')->remove('GoogleDriveTokens'); if (empty($options['pass'])) { - return ['exit' => true, 'body' => '{msg:'.elFinder::ERROR_ACCESS_DENIED.'}'.' '.$e->getMessage()]; + return ['exit' => true, 'body' => '{msg:' . elFinder::ERROR_ACCESS_DENIED . '}' . ' ' . $e->getMessage()]; } else { return ['exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]]; } @@ -799,18 +799,19 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * * @param $netVolumes * @param $key + * * @return bool */ public function netunmount($netVolumes, $key) { if (!$this->options['useGoogleTmb']) { - if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/').DIRECTORY_SEPARATOR.$this->netMountKey.'*.png')) { + if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->netMountKey . '*.png')) { foreach ($tmbs as $file) { unlink($file); } } } - $this->session->remove($this->id.$this->netMountKey); + $this->session->remove($this->id . $this->netMountKey); return true; } @@ -828,9 +829,9 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver { list($parentId, $name) = $this->_gd_splitPath($path); $opts = [ - 'q' => sprintf('trashed=false and "%s" in parents and name="%s"', $parentId, $name), - 'fields' => self::FETCHFIELDS_LIST, - ]; + 'q' => sprintf('trashed=false and "%s" in parents and name="%s"', $parentId, $name), + 'fields' => self::FETCHFIELDS_LIST, + ]; $srcFile = $this->_gd_query($opts); return empty($srcFile) ? false : $this->_gd_parseRaw($srcFile[0]); @@ -845,7 +846,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn. * * @return bool - * * @author Dmitry (dio) Levashov * @author Cem (DiscoFever) **/ @@ -889,7 +889,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $tmp = $this->getTempPath(); } if ($tmp) { - $aTokenFile = $tmp.DIRECTORY_SEPARATOR.md5($this->options['client_id'].$this->options['refresh_token']).'.gtoken'; + $aTokenFile = $tmp . DIRECTORY_SEPARATOR . md5($this->options['client_id'] . $this->options['refresh_token']) . '.gtoken'; if (is_file($aTokenFile)) { $this->options['access_token'] = json_decode(file_get_contents($aTokenFile), true); } @@ -948,7 +948,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $this->service = new \Google_Service_Drive($client); } - $this->netMountKey = md5($aToken.'-'.$this->options['path']); + $this->netMountKey = md5($aToken . '-' . $this->options['path']); } catch (InvalidArgumentException $e) { $errors[] = $e->getMessage(); } catch (Google_Service_Exception $e) { @@ -956,7 +956,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } if (!$this->service) { - $this->session->remove($this->id.$this->netMountKey); + $this->session->remove($this->id . $this->netMountKey); if ($aTokenFile) { unlink($aTokenFile); } @@ -1044,9 +1044,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path dir path * * @return array - * * @author Dmitry Levashov - * */ protected function cacheDir($path) { @@ -1062,17 +1060,17 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $res = $this->_gd_query($opts); - $mountPath = $this->_normpath($path.'/'); + $mountPath = $this->_normpath($path . '/'); if ($res) { foreach ($res as $raw) { if ($stat = $this->_gd_parseRaw($raw)) { - $stat = $this->updateCache($mountPath.$raw->id, $stat); - if (empty($stat['hidden']) && $path !== $mountPath.$raw->id) { + $stat = $this->updateCache($mountPath . $raw->id, $stat); + if (empty($stat['hidden']) && $path !== $mountPath . $raw->id) { if (!$hasDir && $stat['mime'] === 'directory') { $hasDir = true; } - $this->dirsCache[$path][] = $mountPath.$raw->id; + $this->dirsCache[$path][] = $mountPath . $raw->id; } } } @@ -1089,11 +1087,10 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Recursive files search. * * @param string $path dir path - * @param string $q search string - * @param array $mimes + * @param string $q search string + * @param array $mimes * * @return array - * * @throws elFinderAbortException * @author Naoki Sawada */ @@ -1106,26 +1103,26 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver list(, $itemId) = $this->_gd_splitPath($path); - $path = $this->_normpath($path.'/'); + $path = $this->_normpath($path . '/'); $result = []; $query = ''; if ($itemId !== 'root') { $dirs = array_merge([$itemId], $this->_gd_getDirectories($itemId)); - $query = '(\''.implode('\' in parents or \'', $dirs).'\' in parents)'; + $query = '(\'' . implode('\' in parents or \'', $dirs) . '\' in parents)'; } $tmp = []; if (!$mimes) { foreach (explode(' ', $q) as $_v) { - $tmp[] = 'fullText contains \''.str_replace('\'', '\\\'', $_v).'\''; + $tmp[] = 'fullText contains \'' . str_replace('\'', '\\\'', $_v) . '\''; } - $query .= ($query ? ' and ' : '').implode(' and ', $tmp); + $query .= ($query ? ' and ' : '') . implode(' and ', $tmp); } else { foreach ($mimes as $_v) { - $tmp[] = 'mimeType contains \''.str_replace('\'', '\\\'', $_v).'\''; + $tmp[] = 'mimeType contains \'' . str_replace('\'', '\\\'', $_v) . '\''; } - $query .= ($query ? ' and ' : '').'('.implode(' or ', $tmp).')'; + $query .= ($query ? ' and ' : '') . '(' . implode(' or ', $tmp) . ')'; } $opts = [ @@ -1145,14 +1142,14 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver foreach ($parents as $parent) { $paths = $this->_gd_getMountPaths($parent); foreach ($paths as $path) { - $path = ($path === '') ? '/' : (rtrim($path, '/').'/'); - if (!isset($this->cache[$path.$raw->id])) { - $stat = $this->updateCache($path.$raw->id, $stat); + $path = ($path === '') ? '/' : (rtrim($path, '/') . '/'); + if (!isset($this->cache[$path . $raw->id])) { + $stat = $this->updateCache($path . $raw->id, $stat); } else { - $stat = $this->cache[$path.$raw->id]; + $stat = $this->cache[$path . $raw->id]; } if (empty($stat['hidden'])) { - $stat['path'] = $this->_path($path).$stat['name']; + $stat['path'] = $this->_path($path) . $stat['name']; $result[] = $stat; } } @@ -1173,7 +1170,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $name new file name (optionaly) * * @return string|false - * * @author Dmitry (dio) Levashov * @author Naoki Sawada **/ @@ -1193,7 +1189,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $res = $this->_gd_query($opts); foreach ($res as $raw) { - $raw['mimeType'] == self::DIRMIME ? $this->copy($src.'/'.$raw['id'], $path, $raw['name']) : $this->_copy($src.'/'.$raw['id'], $path, $raw['name']); + $raw['mimeType'] == self::DIRMIME ? $this->copy($src . '/' . $raw['id'], $path, $raw['name']) : $this->_copy($src . '/' . $raw['id'], $path, $raw['name']); } $ret = $this->_joinPath($dst, $itemId); @@ -1215,12 +1211,11 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver /** * Remove file/ recursive remove dir. * - * @param string $path file path - * @param bool $force try to remove even if file locked + * @param string $path file path + * @param bool $force try to remove even if file locked + * @param bool $recursive * - * @param bool $recursive * @return bool - * * @throws elFinderAbortException * @author Dmitry (dio) Levashov * @author Naoki Sawada @@ -1259,9 +1254,9 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Create thumnbnail and return it's URL on success. * * @param string $path file path - * @param $stat - * @return string|false + * @param $stat * + * @return string|false * @throws ImagickException * @throws elFinderAbortException * @author Dmitry (dio) Levashov @@ -1274,7 +1269,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } $name = $this->tmbname($stat); - $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; + $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; // copy image into tmbPath so some drivers does not store files on local fs if (!$data = $this->_gd_getThumbnail($path)) { @@ -1330,12 +1325,11 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param array $stat file stat * * @return string - * * @author Dmitry (dio) Levashov **/ protected function tmbname($stat) { - return $this->netMountKey.$stat['iid'].$stat['ts'].'.png'; + return $this->netMountKey . $stat['iid'] . $stat['ts'] . '.png'; } /** @@ -1346,12 +1340,11 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param array $options options array * * @return bool|string - * * @author Naoki Sawada */ public function getContentUrl($hash, $options = []) { - if (! empty($options['onetime']) && $this->options['onetimeUrl']) { + if (!empty($options['onetime']) && $this->options['onetimeUrl']) { return parent::getContentUrl($hash, $options); } if (!empty($options['temporary'])) { @@ -1397,7 +1390,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _dirname($path) @@ -1413,7 +1405,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _basename($path) @@ -1430,12 +1421,11 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $name * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _joinPath($dir, $name) { - return $this->_normpath($dir.'/'.$name); + return $this->_normpath($dir . '/' . $name); } /** @@ -1444,7 +1434,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path path * * @return string - * * @author Troex Nevelin **/ protected function _normpath($path) @@ -1452,7 +1441,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver if (DIRECTORY_SEPARATOR !== '/') { $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); } - $path = '/'.ltrim($path, '/'); + $path = '/' . ltrim($path, '/'); return $path; } @@ -1463,7 +1452,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _relpath($path) @@ -1477,7 +1465,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _abspath($path) @@ -1491,7 +1478,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _path($path) @@ -1506,7 +1492,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $names[] = isset($this->names[$_p]) ? $this->names[$_p] : $_p; } - return $this->rootName.implode('/', $names); + return $this->rootName . implode('/', $names); } /** @@ -1516,12 +1502,11 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $parent parent path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _inpath($path, $parent) { - return $path == $parent || strpos($path, $parent.'/') === 0; + return $path == $parent || strpos($path, $parent . '/') === 0; } /***************** file stat ********************/ @@ -1537,13 +1522,11 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * - (bool) hidden is object hidden. optionally * - (string) alias for symlinks - link target path relative to root path. optionally * - (string) target for symlinks - link target path. optionally. - * * If file does not exists - returns empty array or false. * * @param string $path file path * * @return array|false - * * @author Dmitry (dio) Levashov **/ protected function _stat($path) @@ -1565,7 +1548,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path dir path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _subdirs($path) @@ -1586,7 +1568,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $mime file mime type * * @return string - * * @throws ImagickException * @throws elFinderAbortException * @author Dmitry (dio) Levashov @@ -1600,7 +1581,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver if ($file = $this->_gd_getFile($path)) { if (isset($file['imageMediaMetadata'])) { - $ret = array('dim' => $file['imageMediaMetadata']['width'].'x'.$file['imageMediaMetadata']['height']); + $ret = array('dim' => $file['imageMediaMetadata']['width'] . 'x' . $file['imageMediaMetadata']['height']); if (func_num_args() > 2) { $args = func_get_arg(2); } else { @@ -1613,7 +1594,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver if (min(($tmbSize / $srcSize[0]), ($tmbSize / $srcSize[1])) < 1) { if ($this->_gd_isPublished($file)) { $tmbSize = strval($tmbSize); - $ret['url'] = 'https://drive.google.com/thumbnail?authuser=0&sz=s'.$tmbSize.'&id='.$file['id']; + $ret['url'] = 'https://drive.google.com/thumbnail?authuser=0&sz=s' . $tmbSize . '&id=' . $file['id']; } elseif ($subImgLink = $this->getSubstituteImgLink(elFinder::$currentArgs['target'], $srcSize)) { $ret['url'] = $subImgLink; } @@ -1634,7 +1615,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path dir path * * @return array - * * @author Dmitry (dio) Levashov * @author Cem (DiscoFever) **/ @@ -1652,7 +1632,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param bool $write open file for writing * * @return resource|false - * * @author Dmitry (dio) Levashov **/ protected function _fopen($path, $mode = 'rb') @@ -1675,8 +1654,8 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver } if ($access_token) { $data = array( - 'target' => $dlurl, - 'headers' => array('Authorization: Bearer '.$access_token), + 'target' => $dlurl, + 'headers' => array('Authorization: Bearer ' . $access_token), ); return elFinder::getStreamByUrl($data); @@ -1694,7 +1673,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param resource $fp file pointer * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _fclose($fp, $path = '') @@ -1714,7 +1692,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $name new directory name * * @return string|bool - * * @author Dmitry (dio) Levashov **/ protected function _mkdir($path, $name) @@ -1741,7 +1718,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver return false; } } catch (Exception $e) { - return $this->setError('GoogleDrive error: '.$e->getMessage()); + return $this->setError('GoogleDrive error: ' . $e->getMessage()); } } @@ -1752,7 +1729,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $name new file name * * @return string|bool - * * @author Dmitry (dio) Levashov **/ protected function _mkfile($path, $name) @@ -1767,7 +1743,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path symlink path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _symlink($target, $path, $name) @@ -1783,7 +1758,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $name new file name * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _copy($source, $targetDir, $name) @@ -1805,7 +1779,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver return $itemId; } catch (Exception $e) { - return $this->setError('GoogleDrive error: '.$e->getMessage()); + return $this->setError('GoogleDrive error: ' . $e->getMessage()); } return true; @@ -1820,13 +1794,12 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $name file name * * @return string|bool - * * @author Dmitry (dio) Levashov **/ protected function _move($source, $targetDir, $name) { list($removeParents, $itemId) = $this->_gd_splitPath($source); - $target = $this->_normpath($targetDir.'/'.$itemId); + $target = $this->_normpath($targetDir . '/' . $itemId); try { //moving and renaming a file or directory $files = new \Google_Service_Drive_DriveFile(); @@ -1842,7 +1815,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $this->_gd_getDirectoryData(false); } } catch (Exception $e) { - return $this->setError('GoogleDrive error: '.$e->getMessage()); + return $this->setError('GoogleDrive error: ' . $e->getMessage()); } return $target; @@ -1854,7 +1827,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path file path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _unlink($path) @@ -1867,7 +1839,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $opts = ['removeParents' => $pid]; $this->service->files->update($itemId, $files, $opts); } catch (Exception $e) { - return $this->setError('GoogleDrive error: '.$e->getMessage()); + return $this->setError('GoogleDrive error: ' . $e->getMessage()); } return true; @@ -1879,7 +1851,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path dir path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _rmdir($path) @@ -1894,19 +1865,18 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Create new file and write into it from file pointer. * Return new file path or false on error. * - * @param resource $fp file pointer - * @param $path - * @param string $name file name - * @param array $stat file stat (required by some virtual fs) + * @param resource $fp file pointer + * @param $path + * @param string $name file name + * @param array $stat file stat (required by some virtual fs) * * @return bool|string - * * @author Dmitry (dio) Levashov */ protected function _save($fp, $path, $name, $stat) { if ($name !== '') { - $path .= '/'.$name; + $path .= '/' . $name; } list($parentId, $itemId, $parent) = $this->_gd_splitPath($path); if ($name === '') { @@ -2019,7 +1989,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver return false; } } catch (Exception $e) { - return $this->setError('GoogleDrive error: '.$e->getMessage()); + return $this->setError('GoogleDrive error: ' . $e->getMessage()); } } @@ -2029,7 +1999,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $path file path * * @return string|false - * * @author Dmitry (dio) Levashov **/ protected function _getContents($path) @@ -2042,9 +2011,9 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver $contents = $this->service->files->get($itemId, [ 'alt' => 'media', ]); - $contents = (string) $contents->getBody(); + $contents = (string)$contents->getBody(); } catch (Exception $e) { - return $this->setError('GoogleDrive error: '.$e->getMessage()); + return $this->setError('GoogleDrive error: ' . $e->getMessage()); } return $contents; @@ -2057,7 +2026,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param string $content new file content * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _filePutContents($path, $content) @@ -2066,7 +2034,7 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver if ($local = $this->getTempFile($path)) { if (file_put_contents($local, $content, LOCK_EX) !== false - && ($fp = fopen($local, 'rb'))) { + && ($fp = fopen($local, 'rb'))) { clearstatcache(); $res = $this->_save($fp, $path, '', []); fclose($fp); @@ -2100,10 +2068,9 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Unpack archive. * * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) + * @param array $arc archiver command and arguments (same as in $this->archivers) * * @return void - * * @author Dmitry (dio) Levashov * @author Alexey Sukhotin */ @@ -2117,10 +2084,9 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * Extract files from archive. * * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) + * @param array $arc archiver command and arguments (same as in $this->archivers) * * @return void - * * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin */ @@ -2138,7 +2104,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver * @param array $arc archiver options * * @return string|bool - * * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin **/ diff --git a/php/elFinderVolumeGroup.class.php b/php/elFinderVolumeGroup.class.php index b9145dd61..38e7e54d5 100644 --- a/php/elFinderVolumeGroup.class.php +++ b/php/elFinderVolumeGroup.class.php @@ -5,279 +5,311 @@ * * @author Naoki Sawada **/ -class elFinderVolumeGroup extends elFinderVolumeDriver { - - /** - * Driver id - * Must be started from letter and contains [a-z0-9] - * Used as part of volume id - * - * @var string - **/ - protected $driverId = 'g'; - - - /** - * Constructor - * Extend options with required fields - */ - public function __construct() { - $this->options['type'] = 'group'; - $this->options['path'] = '/'; - $this->options['dirUrlOwn'] = true; - $this->options['syncMinMs'] = 0; - $this->options['tmbPath'] = ''; - $this->options['disabled'] = array( - 'archive', - 'copy', - 'cut', - 'duplicate', - 'edit', - 'empty', - 'extract', - 'getfile', - 'mkdir', - 'mkfile', - 'paste', - 'resize', - 'rm', - 'upload' - ); - } - - /*********************************************************************/ - /* FS API */ - /*********************************************************************/ +class elFinderVolumeGroup extends elFinderVolumeDriver +{ - /*********************** paths/urls *************************/ - - /** - * @inheritdoc - **/ - protected function _dirname($path) { - return '/'; - } + /** + * Driver id + * Must be started from letter and contains [a-z0-9] + * Used as part of volume id + * + * @var string + **/ + protected $driverId = 'g'; - /** - * {@inheritDoc} - **/ - protected function _basename($path) { - return ''; - } - /** - * {@inheritDoc} - **/ - protected function _joinPath($dir, $name) { - return '/' . $name; - } - - /** - * {@inheritDoc} - **/ - protected function _normpath($path) { - return '/'; - } - - /** - * {@inheritDoc} - **/ - protected function _relpath($path) { - return '/'; - } - - /** - * {@inheritDoc} - **/ - protected function _abspath($path) { - return '/'; - } - - /** - * {@inheritDoc} - **/ - protected function _path($path) { - return '/'; - } - - /** - * {@inheritDoc} - **/ - protected function _inpath($path, $parent) { - return false; - } - - - - /***************** file stat ********************/ + /** + * Constructor + * Extend options with required fields + */ + public function __construct() + { + $this->options['type'] = 'group'; + $this->options['path'] = '/'; + $this->options['dirUrlOwn'] = true; + $this->options['syncMinMs'] = 0; + $this->options['tmbPath'] = ''; + $this->options['disabled'] = array( + 'archive', + 'copy', + 'cut', + 'duplicate', + 'edit', + 'empty', + 'extract', + 'getfile', + 'mkdir', + 'mkfile', + 'paste', + 'resize', + 'rm', + 'upload' + ); + } - /** - * {@inheritDoc} - **/ - protected function _stat($path) { - if ($path === '/') { - return array( - 'size' => 0, - 'ts' => 0, - 'mime' => 'directory', - 'read' => true, - 'write' => false, - 'locked' => true, - 'hidden' => false, - 'dirs' => 0 - ); - } - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _subdirs($path) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _dimensions($path, $mime) { - return false; - } - /******************** file/dir content *********************/ - - /** - * {@inheritDoc} - **/ - protected function readlink($path) { - return null; - } - - /** - * {@inheritDoc} - **/ - protected function _scandir($path) { - return array(); - } + /*********************************************************************/ + /* FS API */ + /*********************************************************************/ - /** - * {@inheritDoc} - **/ - protected function _fopen($path, $mode='rb') { - return false; - } + /*********************** paths/urls *************************/ - /** - * {@inheritDoc} - **/ - protected function _fclose($fp, $path='') { - return true; - } - - /******************** file/dir manipulations *************************/ - - /** - * {@inheritDoc} - **/ - protected function _mkdir($path, $name) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _mkfile($path, $name) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _symlink($source, $targetDir, $name) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _copy($source, $targetDir, $name) { - return false; - } + /** + * @inheritdoc + **/ + protected function _dirname($path) + { + return '/'; + } - /** - * {@inheritDoc} - **/ - protected function _move($source, $targetDir, $name) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _unlink($path) { - return false; - } + /** + * {@inheritDoc} + **/ + protected function _basename($path) + { + return ''; + } - /** - * {@inheritDoc} - **/ - protected function _rmdir($path) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _save($fp, $dir, $name, $stat) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _getContents($path) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _filePutContents($path, $content) { - return false; - } + /** + * {@inheritDoc} + **/ + protected function _joinPath($dir, $name) + { + return '/' . $name; + } - /** - * {@inheritDoc} - **/ - protected function _checkArchivers() { - return; - } + /** + * {@inheritDoc} + **/ + protected function _normpath($path) + { + return '/'; + } - /** - * {@inheritDoc} - **/ - protected function _chmod($path, $mode) { - return false; - } + /** + * {@inheritDoc} + **/ + protected function _relpath($path) + { + return '/'; + } - /** - * {@inheritDoc} - **/ - protected function _findSymlinks($path) { - return false; - } + /** + * {@inheritDoc} + **/ + protected function _abspath($path) + { + return '/'; + } - /** - * {@inheritDoc} - **/ - protected function _extract($path, $arc) { - return false; - } - - /** - * {@inheritDoc} - **/ - protected function _archive($dir, $files, $name, $arc) { - return false; - } + /** + * {@inheritDoc} + **/ + protected function _path($path) + { + return '/'; + } + + /** + * {@inheritDoc} + **/ + protected function _inpath($path, $parent) + { + return false; + } + + + + /***************** file stat ********************/ + + /** + * {@inheritDoc} + **/ + protected function _stat($path) + { + if ($path === '/') { + return array( + 'size' => 0, + 'ts' => 0, + 'mime' => 'directory', + 'read' => true, + 'write' => false, + 'locked' => true, + 'hidden' => false, + 'dirs' => 0 + ); + } + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _subdirs($path) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _dimensions($path, $mime) + { + return false; + } + /******************** file/dir content *********************/ + + /** + * {@inheritDoc} + **/ + protected function readlink($path) + { + return null; + } + + /** + * {@inheritDoc} + **/ + protected function _scandir($path) + { + return array(); + } + + /** + * {@inheritDoc} + **/ + protected function _fopen($path, $mode = 'rb') + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _fclose($fp, $path = '') + { + return true; + } + + /******************** file/dir manipulations *************************/ + + /** + * {@inheritDoc} + **/ + protected function _mkdir($path, $name) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _mkfile($path, $name) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _symlink($source, $targetDir, $name) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _copy($source, $targetDir, $name) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _move($source, $targetDir, $name) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _unlink($path) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _rmdir($path) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _save($fp, $dir, $name, $stat) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _getContents($path) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _filePutContents($path, $content) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _checkArchivers() + { + return; + } + + /** + * {@inheritDoc} + **/ + protected function _chmod($path, $mode) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _findSymlinks($path) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _extract($path, $arc) + { + return false; + } + + /** + * {@inheritDoc} + **/ + protected function _archive($dir, $files, $name, $arc) + { + return false; + } } diff --git a/php/elFinderVolumeLocalFileSystem.class.php b/php/elFinderVolumeLocalFileSystem.class.php index f513808b5..b6534bb89 100644 --- a/php/elFinderVolumeLocalFileSystem.class.php +++ b/php/elFinderVolumeLocalFileSystem.class.php @@ -2,23 +2,27 @@ // Implement similar functionality in PHP 5.2 or 5.3 // http://php.net/manual/class.recursivecallbackfilteriterator.php#110974 -if (! class_exists('RecursiveCallbackFilterIterator', false)) { - class RecursiveCallbackFilterIterator extends RecursiveFilterIterator { - private $callback; +if (!class_exists('RecursiveCallbackFilterIterator', false)) { + class RecursiveCallbackFilterIterator extends RecursiveFilterIterator + { + private $callback; - public function __construct ( RecursiveIterator $iterator, $callback ) { - $this->callback = $callback; - parent::__construct($iterator); - } - - public function accept () { - return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator()); - } - - public function getChildren () { - return new self($this->getInnerIterator()->getChildren(), $this->callback); - } - } + public function __construct(RecursiveIterator $iterator, $callback) + { + $this->callback = $callback; + parent::__construct($iterator); + } + + public function accept() + { + return call_user_func($this->callback, parent::current(), parent::key(), parent::getInnerIterator()); + } + + public function getChildren() + { + return new self($this->getInnerIterator()->getChildren(), $this->callback); + } + } } /** @@ -27,1278 +31,1361 @@ if (! class_exists('RecursiveCallbackFilterIterator', false)) { * @author Dmitry (dio) Levashov * @author Troex Nevelin **/ -class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver { - - /** - * Driver id - * Must be started from letter and contains [a-z0-9] - * Used as part of volume id - * - * @var string - **/ - protected $driverId = 'l'; - - /** - * Required to count total archive files size - * - * @var int - **/ - protected $archiveSize = 0; +class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver +{ - /** - * Is checking stat owner - * - * @var boolean - */ - protected $statOwner = false; + /** + * Driver id + * Must be started from letter and contains [a-z0-9] + * Used as part of volume id + * + * @var string + **/ + protected $driverId = 'l'; - /** - * Path to quarantine directory - * - * @var string - */ - private $quarantine; + /** + * Required to count total archive files size + * + * @var int + **/ + protected $archiveSize = 0; - /** - * Constructor - * Extend options with required fields - * - * @author Dmitry (dio) Levashov - */ - public function __construct() { - $this->options['alias'] = ''; // alias to replace root dir name - $this->options['dirMode'] = 0755; // new dirs mode - $this->options['fileMode'] = 0644; // new files mode - $this->options['quarantine'] = '.quarantine'; // quarantine folder name - required to check archive (must be hidden) - $this->options['rootCssClass'] = 'elfinder-navbar-root-local'; - $this->options['followSymLinks'] = true; - $this->options['detectDirIcon'] = ''; // file name that is detected as a folder icon e.g. '.diricon.png' - $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload' - $this->options['substituteImg'] = true; // support substitute image with dim command - $this->options['statCorrector'] = null; // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}` - } - - /*********************************************************************/ - /* INIT AND CONFIGURE */ - /*********************************************************************/ - - /** - * Prepare driver before mount volume. - * Return true if volume is ready. - * - * @return bool - **/ - protected function init() { - // Normalize directory separator for windows - if (DIRECTORY_SEPARATOR !== '/') { - foreach(array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) { - if (!empty($this->options[$key])) { - $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]); - } - } - // PHP >= 7.1 Supports UTF-8 path on Windows - if (version_compare(PHP_VERSION, '7.1', '>=')) { - $this->options['encoding'] = ''; - $this->options['locale'] = ''; - } - } - if (!$cwd = getcwd()) { - return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().'); - } - // detect systemRoot - if (!isset($this->options['systemRoot'])) { - if ($cwd[0] === $this->separator || $this->root[0] === $this->separator) { - $this->systemRoot = $this->separator; - } else if (preg_match('/^([a-zA-Z]:'.preg_quote($this->separator, '/').')/', $this->root, $m)) { - $this->systemRoot = $m[1]; - } else if (preg_match('/^([a-zA-Z]:'.preg_quote($this->separator, '/').')/', $cwd, $m)) { - $this->systemRoot = $m[1]; - } - } - $this->root = $this->getFullPath($this->root, $cwd); - if (!empty($this->options['startPath'])) { - $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root); - } - - if (is_null($this->options['syncChkAsTs'])) { - $this->options['syncChkAsTs'] = true; - } - if (is_null($this->options['syncCheckFunc'])) { - $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify'); - } - // check 'statCorrector' - if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) { - $this->options['statCorrector'] = null; - } + /** + * Is checking stat owner + * + * @var boolean + */ + protected $statOwner = false; - return true; - } + /** + * Path to quarantine directory + * + * @var string + */ + private $quarantine; - /** - * Configure after successfull mount. - * - * @return void - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function configure() { - $root = $this->stat($this->root); - - // chek thumbnails path - if ($this->options['tmbPath']) { - $this->options['tmbPath'] = strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false - // tmb path set as dirname under root dir - ? $this->_abspath($this->options['tmbPath']) - // tmb path as full path - : $this->_normpath($this->options['tmbPath']); - } + /** + * Constructor + * Extend options with required fields + * + * @author Dmitry (dio) Levashov + */ + public function __construct() + { + $this->options['alias'] = ''; // alias to replace root dir name + $this->options['dirMode'] = 0755; // new dirs mode + $this->options['fileMode'] = 0644; // new files mode + $this->options['quarantine'] = '.quarantine'; // quarantine folder name - required to check archive (must be hidden) + $this->options['rootCssClass'] = 'elfinder-navbar-root-local'; + $this->options['followSymLinks'] = true; + $this->options['detectDirIcon'] = ''; // file name that is detected as a folder icon e.g. '.diricon.png' + $this->options['keepTimestamp'] = array('copy', 'move'); // keep timestamp at inner filesystem allowed 'copy', 'move' and 'upload' + $this->options['substituteImg'] = true; // support substitute image with dim command + $this->options['statCorrector'] = null; // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}` + } - parent::configure(); - - // set $this->tmp by options['tmpPath'] - $this->tmp = ''; - if (!empty($this->options['tmpPath'])) { - if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) { - $this->tmp = $this->options['tmpPath']; - } - } - if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) { - $this->tmp = $tmp; - } - - // if no thumbnails url - try detect it - if ($root['read'] && !$this->tmbURL && $this->URL) { - if (strpos($this->tmbPath, $this->root) === 0) { - $this->tmbURL = $this->URL.str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root)+1)); - if (preg_match("|[^/?&=]$|", $this->tmbURL)) { - $this->tmbURL .= '/'; - } - } - } + /*********************************************************************/ + /* INIT AND CONFIGURE */ + /*********************************************************************/ - // check quarantine dir - $this->quarantine = ''; - if (!empty($this->options['quarantine'])) { - if (is_dir($this->options['quarantine'])) { - if (is_writable($this->options['quarantine'])) { - $this->quarantine = $this->options['quarantine']; - } - $this->options['quarantine'] = ''; - } else { - $this->quarantine = $this->_abspath($this->options['quarantine']); - if ((!is_dir($this->quarantine) && !mkdir($this->quarantine)) || !is_writable($this->quarantine)) { - $this->options['quarantine'] = $this->quarantine = ''; - } - } - } - - if (!$this->quarantine) { - if (!$this->tmp) { - $this->archivers['extract'] = array(); - $this->disabled[] = 'extract'; - } else { - $this->quarantine = $this->tmp; - } - } - - if ($this->options['quarantine']) { - $this->attributes[] = array( - 'pattern' => '~^'.preg_quote(DIRECTORY_SEPARATOR.$this->options['quarantine']).'$~', - 'read' => false, - 'write' => false, - 'locked' => true, - 'hidden' => true - ); - } - - if (! empty($this->options['keepTimestamp'])) { - $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']); - } + /** + * Prepare driver before mount volume. + * Return true if volume is ready. + * + * @return bool + **/ + protected function init() + { + // Normalize directory separator for windows + if (DIRECTORY_SEPARATOR !== '/') { + foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) { + if (!empty($this->options[$key])) { + $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]); + } + } + // PHP >= 7.1 Supports UTF-8 path on Windows + if (version_compare(PHP_VERSION, '7.1', '>=')) { + $this->options['encoding'] = ''; + $this->options['locale'] = ''; + } + } + if (!$cwd = getcwd()) { + return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().'); + } + // detect systemRoot + if (!isset($this->options['systemRoot'])) { + if ($cwd[0] === $this->separator || $this->root[0] === $this->separator) { + $this->systemRoot = $this->separator; + } else if (preg_match('/^([a-zA-Z]:' . preg_quote($this->separator, '/') . ')/', $this->root, $m)) { + $this->systemRoot = $m[1]; + } else if (preg_match('/^([a-zA-Z]:' . preg_quote($this->separator, '/') . ')/', $cwd, $m)) { + $this->systemRoot = $m[1]; + } + } + $this->root = $this->getFullPath($this->root, $cwd); + if (!empty($this->options['startPath'])) { + $this->options['startPath'] = $this->getFullPath($this->options['startPath'], $this->root); + } - $this->statOwner = (!empty($this->options['statOwner'])); - } + if (is_null($this->options['syncChkAsTs'])) { + $this->options['syncChkAsTs'] = true; + } + if (is_null($this->options['syncCheckFunc'])) { + $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify'); + } + // check 'statCorrector' + if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) { + $this->options['statCorrector'] = null; + } - /** - * Long pooling sync checker - * This function require server command `inotifywait` - * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php - * - * @param string $path - * @param int $standby - * @param number $compare - * @return number|bool - * @throws elFinderAbortException - */ - public function localFileSystemInotify($path, $standby, $compare) { - if (isset($this->sessionCache['localFileSystemInotify_disable'])) { - return false; - } - $path = realpath($path); - $mtime = filemtime($path); - if (! $mtime) { - return false; - } - if ($mtime != $compare) { - return $mtime; - } - $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH')? ELFINER_INOTIFYWAIT_PATH : 'inotifywait'; - $standby = max(1, intval($standby)); - $cmd = $inotifywait.' '.escapeshellarg($path).' -t '.$standby.' -e moved_to,moved_from,move,close_write,delete,delete_self'; - $this->procExec($cmd , $o, $r); - if ($r === 0) { - // changed - clearstatcache(); - if (file_exists($path)) { - $mtime = filemtime($path); // error on busy? - return $mtime? $mtime : time(); - } else { - // target was removed - return 0; - } - } else if ($r === 2) { - // not changed (timeout) - return $compare; - } - // error - // cache to $_SESSION - $this->sessionCache['localFileSystemInotify_disable'] = true; - $this->session->set($this->id, $this->sessionCache); - return false; - } - - /*********************************************************************/ - /* FS API */ - /*********************************************************************/ + return true; + } - /*********************** paths/urls *************************/ - - /** - * Return parent directory path - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _dirname($path) { - return dirname($path); - } + /** + * Configure after successfull mount. + * + * @return void + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function configure() + { + $root = $this->stat($this->root); - /** - * Return file name - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _basename($path) { - return basename($path); - } + // chek thumbnails path + if ($this->options['tmbPath']) { + $this->options['tmbPath'] = strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false + // tmb path set as dirname under root dir + ? $this->_abspath($this->options['tmbPath']) + // tmb path as full path + : $this->_normpath($this->options['tmbPath']); + } - /** - * Join dir name and file name and retur full path - * - * @param string $dir - * @param string $name - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _joinPath($dir, $name) { - return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name; - } - - /** - * Return normalized path, this works the same as os.path.normpath() in Python - * - * @param string $path path - * @return string - * @author Troex Nevelin - **/ - protected function _normpath($path) { - if (empty($path)) { - return '.'; - } - - $changeSep = (DIRECTORY_SEPARATOR !== '/'); - if ($changeSep) { - $drive = ''; - if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) { - $drive = $m[1]; - $path = $m[2]? $m[2] : '/'; - } - $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); - } + parent::configure(); - if (strpos($path, '/') === 0) { - $initial_slashes = true; - } else { - $initial_slashes = false; - } - - if (($initial_slashes) - && (strpos($path, '//') === 0) - && (strpos($path, '///') === false)) { - $initial_slashes = 2; - } - - $initial_slashes = (int) $initial_slashes; + // set $this->tmp by options['tmpPath'] + $this->tmp = ''; + if (!empty($this->options['tmpPath'])) { + if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], 0755, true)) && is_writable($this->options['tmpPath'])) { + $this->tmp = $this->options['tmpPath']; + } + } + if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) { + $this->tmp = $tmp; + } - $comps = explode('/', $path); - $new_comps = array(); - foreach ($comps as $comp) { - if (in_array($comp, array('', '.'))) { - continue; - } - - if (($comp != '..') - || (!$initial_slashes && !$new_comps) - || ($new_comps && (end($new_comps) == '..'))) { - array_push($new_comps, $comp); - } elseif ($new_comps) { - array_pop($new_comps); - } - } - $comps = $new_comps; - $path = implode('/', $comps); - if ($initial_slashes) { - $path = str_repeat('/', $initial_slashes) . $path; - } - - if ($changeSep) { - $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path); - } - - return $path ? $path : '.'; - } - - /** - * Return file path related to root dir - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _relpath($path) { - if ($path === $this->root) { - return ''; - } else { - if (strpos($path, $this->root) === 0) { - return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR); - } else { - // for link - return $path; - } - } - } - - /** - * Convert path related to root dir into real path - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _abspath($path) { - if ($path === DIRECTORY_SEPARATOR) { - return $this->root; - } else { - if ($path[0] === DIRECTORY_SEPARATOR) { - // for link - return $path; - } else { - return $this->_joinPath($this->root, $path); - } - } - } - - /** - * Return fake path started from root dir - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _path($path) { - return $this->rootName.($path == $this->root ? '' : $this->separator.$this->_relpath($path)); - } - - /** - * Return true if $path is children of $parent - * - * @param string $path path to check - * @param string $parent parent path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _inpath($path, $parent) { - $cwd = getcwd(); - $real_path = $this->getFullPath($path, $cwd); - $real_parent = $this->getFullPath($parent, $cwd); - if ($real_path && $real_parent) { - return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR) === 0; - } - return false; - } - - - - /***************** file stat ********************/ + // if no thumbnails url - try detect it + if ($root['read'] && !$this->tmbURL && $this->URL) { + if (strpos($this->tmbPath, $this->root) === 0) { + $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1)); + if (preg_match("|[^/?&=]$|", $this->tmbURL)) { + $this->tmbURL .= '/'; + } + } + } - /** - * Return stat for given path. - * Stat contains following fields: - * - (int) size file size in b. required - * - (int) ts file modification time in unix time. required - * - (string) mime mimetype. required for folders, others - optionally - * - (bool) read read permissions. required - * - (bool) write write permissions. required - * - (bool) locked is object locked. optionally - * - (bool) hidden is object hidden. optionally - * - (string) alias for symlinks - link target path relative to root path. optionally - * - (string) target for symlinks - link target path. optionally - * - * If file does not exists - returns empty array or false. - * - * @param string $path file path - * @return array|false - * @author Dmitry (dio) Levashov - **/ - protected function _stat($path) { - $stat = array(); + // check quarantine dir + $this->quarantine = ''; + if (!empty($this->options['quarantine'])) { + if (is_dir($this->options['quarantine'])) { + if (is_writable($this->options['quarantine'])) { + $this->quarantine = $this->options['quarantine']; + } + $this->options['quarantine'] = ''; + } else { + $this->quarantine = $this->_abspath($this->options['quarantine']); + if ((!is_dir($this->quarantine) && !mkdir($this->quarantine)) || !is_writable($this->quarantine)) { + $this->options['quarantine'] = $this->quarantine = ''; + } + } + } - if (!file_exists($path) && !is_link($path)) { - return $stat; - } + if (!$this->quarantine) { + if (!$this->tmp) { + $this->archivers['extract'] = array(); + $this->disabled[] = 'extract'; + } else { + $this->quarantine = $this->tmp; + } + } - //Verifies the given path is the root or is inside the root. Prevents directory traveral. - if (!$this->_inpath($path, $this->root)) { - return $stat; - } + if ($this->options['quarantine']) { + $this->attributes[] = array( + 'pattern' => '~^' . preg_quote(DIRECTORY_SEPARATOR . $this->options['quarantine']) . '$~', + 'read' => false, + 'write' => false, + 'locked' => true, + 'hidden' => true + ); + } - $stat['isowner'] = false; - $linkreadable = false; - if ($path != $this->root && is_link($path)) { - if (! $this->options['followSymLinks']) { - return array(); - } - if (!($target = $this->readlink($path)) - || $target == $path) { - if (is_null($target)) { - $stat = array(); - return $stat; - } else { - $stat['mime'] = 'symlink-broken'; - $target = readlink($path); - $lstat = lstat($path); - $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']); - $linkreadable = !empty($ostat['isowner']); - } - } - $stat['alias'] = $this->_path($target); - $stat['target'] = $target; - } + if (!empty($this->options['keepTimestamp'])) { + $this->options['keepTimestamp'] = array_flip($this->options['keepTimestamp']); + } - $readable = is_readable($path); - - if ($readable) { - $size = sprintf('%u', filesize($path)); - $stat['ts'] = filemtime($path); - if ($this->statOwner) { - $fstat = stat($path); - $uid = $fstat['uid']; - $gid = $fstat['gid']; - $stat['perm'] = substr((string)decoct($fstat['mode']), -4); - $stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); - } - } + $this->statOwner = (!empty($this->options['statOwner'])); + } - if (($dir = is_dir($path)) && $this->options['detectDirIcon']) { - $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon']; - if ($this->URL && file_exists($favicon)) { - $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1)); - } - } - - if (!isset($stat['mime'])) { - $stat['mime'] = $dir ? 'directory' : $this->mimetype($path); - } - //logical rights first - $stat['read'] = ($linkreadable || $readable)? null : false; - $stat['write'] = is_writable($path)? null : false; + /** + * Long pooling sync checker + * This function require server command `inotifywait` + * If `inotifywait` need full path, Please add `define('ELFINER_INOTIFYWAIT_PATH', '/PATH_TO/inotifywait');` into connector.php + * + * @param string $path + * @param int $standby + * @param number $compare + * + * @return number|bool + * @throws elFinderAbortException + */ + public function localFileSystemInotify($path, $standby, $compare) + { + if (isset($this->sessionCache['localFileSystemInotify_disable'])) { + return false; + } + $path = realpath($path); + $mtime = filemtime($path); + if (!$mtime) { + return false; + } + if ($mtime != $compare) { + return $mtime; + } + $inotifywait = defined('ELFINER_INOTIFYWAIT_PATH') ? ELFINER_INOTIFYWAIT_PATH : 'inotifywait'; + $standby = max(1, intval($standby)); + $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self'; + $this->procExec($cmd, $o, $r); + if ($r === 0) { + // changed + clearstatcache(); + if (file_exists($path)) { + $mtime = filemtime($path); // error on busy? + return $mtime ? $mtime : time(); + } else { + // target was removed + return 0; + } + } else if ($r === 2) { + // not changed (timeout) + return $compare; + } + // error + // cache to $_SESSION + $this->sessionCache['localFileSystemInotify_disable'] = true; + $this->session->set($this->id, $this->sessionCache); + return false; + } - if (is_null($stat['read'])) { - if ($dir) { - $stat['size'] = 0; - } else if (isset($size)) { - $stat['size'] = $size; - } - } - - if ($this->options['statCorrector']) { - call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this)); - } + /*********************************************************************/ + /* FS API */ + /*********************************************************************/ - return $stat; - } - - /** - * Get stat `owner`, `group` and `isowner` by `uid` and `gid` - * Sub-fuction of _stat() and _scandir() - * - * @param integer $uid - * @param integer $gid - * @return array stat - */ - protected function getOwnerStat($uid, $gid) { - static $names = null; - static $phpuid = null; - - if (is_null($names)) { - $names = array('uid' => array(), 'gid' =>array()); - } - if (is_null($phpuid)) { - if (is_callable('posix_getuid')) { - $phpuid = posix_getuid(); - } else { - $phpuid = 0; - } - } - - $stat = array(); - - if ($uid) { - $stat['isowner'] = ($phpuid == $uid); - if (isset($names['uid'][$uid])) { - $stat['owner'] = $names['uid'][$uid]; - } else if (is_callable('posix_getpwuid')) { - $pwuid = posix_getpwuid($uid); - $stat['owner'] = $names['uid'][$uid] = $pwuid['name']; - } else { - $stat['owner'] = $names['uid'][$uid] = $uid; - } - } - if ($gid) { - if (isset($names['gid'][$gid])) { - $stat['group'] = $names['gid'][$gid]; - } else if (is_callable('posix_getgrgid')) { - $grgid = posix_getgrgid($gid); - $stat['group'] = $names['gid'][$gid] = $grgid['name']; - } else { - $stat['group'] = $names['gid'][$gid] = $gid; - } - } - - return $stat; - } + /*********************** paths/urls *************************/ - /** - * Return true if path is dir and has at least one childs directory - * - * @param string $path dir path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _subdirs($path) { + /** + * Return parent directory path + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _dirname($path) + { + return dirname($path); + } - $dirs = false; - if (is_dir($path) && is_readable($path)) { - if (class_exists('FilesystemIterator', false)) { - $dirItr = new ParentIterator( - new RecursiveDirectoryIterator($path, - FilesystemIterator::SKIP_DOTS | - FilesystemIterator::CURRENT_AS_SELF | - (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS')? - RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0) - ) - ); - $dirItr->rewind(); - if ($dirItr->hasChildren()) { - $dirs = true; - $name = $dirItr->getSubPathName(); - while($dirItr->valid()) { - if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) { - $dirs = false; - $dirItr->next(); - $name = $dirItr->getSubPathName(); - continue; - } - $dirs = true; - break; - } - } - } else { - $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?')); - return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR); - } - } - return $dirs; - } - - /** - * Return object width and height - * Usualy used for images, but can be realize for video etc... - * - * @param string $path file path - * @param string $mime file mime type - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _dimensions($path, $mime) { - clearstatcache(); - return strpos($mime, 'image') === 0 && is_readable($path) && ($s = getimagesize($path)) !== false - ? $s[0].'x'.$s[1] - : false; - } - /******************** file/dir content *********************/ - - /** - * Return symlink target file - * - * @param string $path link path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function readlink($path) { - if (!($target = readlink($path))) { - return null; - } + /** + * Return file name + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _basename($path) + { + return basename($path); + } - if (strpos($target, $this->systemRoot) !== 0) { - $target = $this->_joinPath(dirname($path), $target); - } + /** + * Join dir name and file name and retur full path + * + * @param string $dir + * @param string $name + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _joinPath($dir, $name) + { + return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name; + } - if (!file_exists($target)) { - return false; - } - - return $target; - } + /** + * Return normalized path, this works the same as os.path.normpath() in Python + * + * @param string $path path + * + * @return string + * @author Troex Nevelin + **/ + protected function _normpath($path) + { + if (empty($path)) { + return '.'; + } - /** - * Return files list in directory. - * - * @param string $path dir path - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function _scandir($path) { - elFinder::checkAborted(); - $files = array(); - $cache = array(); - $dirWritable = is_writable($path); - $dirItr = array(); - $followSymLinks = $this->options['followSymLinks']; - try { - $dirItr = new DirectoryIterator($path); - } catch (UnexpectedValueException $e) {} - - foreach ($dirItr as $file) { - try { - if ($file->isDot()) { continue; } - - $files[] = $fpath = $file->getPathname(); - - $br = false; - $stat = array(); - - $stat['isowner'] = false; - $linkreadable = false; - if ($file->isLink()) { - if (! $followSymLinks) { continue; } - if (!($target = $this->readlink($fpath)) - || $target == $fpath) { - if (is_null($target)) { - $stat = array(); - $br = true; - } else { - $_path = $fpath; - $stat['mime'] = 'symlink-broken'; - $target = readlink($_path); - $lstat = lstat($_path); - $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']); - $linkreadable = !empty($ostat['isowner']); - $dir = false; - $stat['alias'] = $this->_path($target); - $stat['target'] = $target; - } - } else { - $dir = is_dir($target); - $stat['alias'] = $this->_path($target); - $stat['target'] = $target; - $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']); - } - } else { - if (($dir = $file->isDir()) && $this->options['detectDirIcon']) { - $path = $file->getPathname(); - $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon']; - if ($this->URL && file_exists($favicon)) { - $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1)); - } - } - $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath); - } - $size = sprintf('%u', $file->getSize()); - $stat['ts'] = $file->getMTime(); - if (!$br) { - if ($this->statOwner && !$linkreadable) { - $uid = $file->getOwner(); - $gid = $file->getGroup(); - $stat['perm'] = substr((string)decoct($file->getPerms()), -4); - $stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); - } - - //logical rights first - $stat['read'] = ($linkreadable || $file->isReadable())? null : false; - $stat['write'] = $file->isWritable()? null : false; - $stat['locked'] = $dirWritable? null : true; - - if (is_null($stat['read'])) { - $stat['size'] = $dir ? 0 : $size; - } - - if ($this->options['statCorrector']) { - call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this)); - } - } - - $cache[] = array($fpath, $stat); - } catch (RuntimeException $e) { - continue; - } - } - - if ($cache) { - $cache = $this->convEncOut($cache, false); - foreach($cache as $d) { - $this->updateCache($d[0], $d[1]); - } - } - - return $files; - } + $changeSep = (DIRECTORY_SEPARATOR !== '/'); + if ($changeSep) { + $drive = ''; + if (preg_match('/^([a-zA-Z]:)(.*)/', $path, $m)) { + $drive = $m[1]; + $path = $m[2] ? $m[2] : '/'; + } + $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); + } - /** - * Open file and return file pointer - * - * @param string $path file path - * @param string $mode - * @return false|resource - * @internal param bool $write open file for writing - * @author Dmitry (dio) Levashov - */ - protected function _fopen($path, $mode='rb') { - return fopen($path, $mode); - } + if (strpos($path, '/') === 0) { + $initial_slashes = true; + } else { + $initial_slashes = false; + } - /** - * Close opened file - * - * @param resource $fp file pointer - * @param string $path - * @return bool - * @author Dmitry (dio) Levashov - */ - protected function _fclose($fp, $path='') { - return (is_resource($fp) && fclose($fp)); - } - - /******************** file/dir manipulations *************************/ - - /** - * Create dir and return created dir path or false on failed - * - * @param string $path parent dir path - * @param string $name new directory name - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - protected function _mkdir($path, $name) { - $path = $this->_joinPath($path, $name); + if (($initial_slashes) + && (strpos($path, '//') === 0) + && (strpos($path, '///') === false)) { + $initial_slashes = 2; + } - if (mkdir($path)) { - chmod($path, $this->options['dirMode']); - return $path; - } + $initial_slashes = (int)$initial_slashes; - return false; - } - - /** - * Create file and return it's path or false on failed - * - * @param string $path parent dir path - * @param string $name new file name - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - protected function _mkfile($path, $name) { - $path = $this->_joinPath($path, $name); - - if (($fp = fopen($path, 'w'))) { - fclose($fp); - chmod($path, $this->options['fileMode']); - return $path; - } - return false; - } - - /** - * Create symlink - * - * @param string $source file to link to - * @param string $targetDir folder to create link in - * @param string $name symlink name - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _symlink($source, $targetDir, $name) { - return symlink($source, $this->_joinPath($targetDir, $name)); - } - - /** - * Copy file into another file - * - * @param string $source source file path - * @param string $targetDir target directory path - * @param string $name new file name - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _copy($source, $targetDir, $name) { - $mtime = filemtime($source); - $target = $this->_joinPath($targetDir, $name); - if ($ret = copy($source, $target)) { - isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime); - } - return $ret; - } + $comps = explode('/', $path); + $new_comps = array(); + foreach ($comps as $comp) { + if (in_array($comp, array('', '.'))) { + continue; + } - /** - * Move file into another parent dir. - * Return new file path or false. - * - * @param string $source source file path - * @param $targetDir - * @param string $name file name - * @return bool|string - * @internal param string $target target dir path - * @author Dmitry (dio) Levashov - */ - protected function _move($source, $targetDir, $name) { - $mtime = filemtime($source); - $target = $this->_joinPath($targetDir, $name); - if ($ret = rename($source, $target) ? $target : false) { - isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime); - } - return $ret; - } - - /** - * Remove file - * - * @param string $path file path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _unlink($path) { - return is_file($path) && unlink($path); - } + if (($comp != '..') + || (!$initial_slashes && !$new_comps) + || ($new_comps && (end($new_comps) == '..'))) { + array_push($new_comps, $comp); + } elseif ($new_comps) { + array_pop($new_comps); + } + } + $comps = $new_comps; + $path = implode('/', $comps); + if ($initial_slashes) { + $path = str_repeat('/', $initial_slashes) . $path; + } - /** - * Remove dir - * - * @param string $path dir path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _rmdir($path) { - return rmdir($path); - } - - /** - * Create new file and write into it from file pointer. - * Return new file path or false on error. - * - * @param resource $fp file pointer - * @param string $dir target dir path - * @param string $name file name - * @param array $stat file stat (required by some virtual fs) - * @return bool|string - * @author Dmitry (dio) Levashov - **/ - protected function _save($fp, $dir, $name, $stat) { - $path = $this->_joinPath($dir, $name); + if ($changeSep) { + $path = $drive . str_replace('/', DIRECTORY_SEPARATOR, $path); + } - $meta = stream_get_meta_data($fp); - $uri = isset($meta['uri'])? $meta['uri'] : ''; - if ($uri && ! preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) { - fclose($fp); - $mtime = filemtime($uri); - $isCmdPaste = ($this->ARGS['cmd'] === 'paste'); - $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut'])); - if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) { - return false; - } - // keep timestamp on upload - if ($mtime && $this->ARGS['cmd'] === 'upload') { - touch($path, isset($this->options['keepTimestamp']['upload'])? $mtime : time()); - } - } else { - if (file_put_contents($path, $fp, LOCK_EX) === false) { - return false; - } - } - - chmod($path, $this->options['fileMode']); - return $path; - } - - /** - * Get file contents - * - * @param string $path file path - * @return string|false - * @author Dmitry (dio) Levashov - **/ - protected function _getContents($path) { - return file_get_contents($path); - } - - /** - * Write a string to a file - * - * @param string $path file path - * @param string $content new file content - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _filePutContents($path, $content) { - return (file_put_contents($path, $content, LOCK_EX) !== false); - } + return $path ? $path : '.'; + } - /** - * Detect available archivers - * - * @return void - * @throws elFinderAbortException - */ - protected function _checkArchivers() { - $this->archivers = $this->getArchivers(); - return; - } + /** + * Return file path related to root dir + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _relpath($path) + { + if ($path === $this->root) { + return ''; + } else { + if (strpos($path, $this->root) === 0) { + return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR); + } else { + // for link + return $path; + } + } + } - /** - * chmod availability - * - * @param string $path - * @param string $mode - * @return bool - */ - protected function _chmod($path, $mode) { - $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o",$mode)); - return chmod($path, $modeOct); - } + /** + * Convert path related to root dir into real path + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _abspath($path) + { + if ($path === DIRECTORY_SEPARATOR) { + return $this->root; + } else { + if ($path[0] === DIRECTORY_SEPARATOR) { + // for link + return $path; + } else { + return $this->_joinPath($this->root, $path); + } + } + } - /** - * Recursive symlinks search - * - * @param string $path file/dir path - * @return bool - * @throws Exception - * @author Dmitry (dio) Levashov - */ - protected function _findSymlinks($path) { - return self::localFindSymlinks($path); - } + /** + * Return fake path started from root dir + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _path($path) + { + return $this->rootName . ($path == $this->root ? '' : $this->separator . $this->_relpath($path)); + } - /** - * Extract files from archive - * - * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) - * @return array|string|boolean - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - */ - protected function _extract($path, $arc) { - - if ($this->quarantine) { + /** + * Return true if $path is children of $parent + * + * @param string $path path to check + * @param string $parent parent path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _inpath($path, $parent) + { + $cwd = getcwd(); + $real_path = $this->getFullPath($path, $cwd); + $real_parent = $this->getFullPath($parent, $cwd); + if ($real_path && $real_parent) { + return $real_path === $real_parent || strpos($real_path, rtrim($real_parent, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) === 0; + } + return false; + } - $dir = $this->quarantine.DIRECTORY_SEPARATOR.md5(basename($path).mt_rand()); - $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction')? '' : $dir.DIRECTORY_SEPARATOR.basename($path); - - if (!mkdir($dir)) { - return false; - } - - // insurance unexpected shutdown - register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir)); - - chmod($dir, 0777); - - // copy in quarantine - if (!is_readable($path) || ($archive && !copy($path, $archive))) { - return false; - } - - // extract in quarantine - $this->unpackArchive($path, $arc, $archive? true : $dir); - - // get files list - try { - $ls = self::localScandir($dir); - } catch (Exception $e) { - return false; - } - // no files - extract error ? - if (empty($ls)) { - return false; - } - - $this->archiveSize = 0; - - // find symlinks and check extracted items - $checkRes = $this->checkExtractItems($dir); - if ($checkRes['symlinks']) { - self::localRmdirRecursive($dir); - return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS))); - } - $this->archiveSize = $checkRes['totalSize']; - if ($checkRes['rmNames']) { - foreach($checkRes['rmNames'] as $name) { - $this->addError(elFinder::ERROR_SAVE, $name); - } - } - - // check max files size - if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { - $this->delTree($dir); - return $this->setError(elFinder::ERROR_ARC_MAXSIZE); - } - - $extractTo = $this->extractToNewdir; // 'auto', ture or false - - // archive contains one item - extract in archive dir - $name = ''; - $src = $dir.DIRECTORY_SEPARATOR.$ls[0]; - if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) { - $name = $ls[0]; - } else if ($extractTo === 'auto' || $extractTo) { - // for several files - create new directory - // create unique name for directory - $src = $dir; - $splits = elFinder::splitFileExtention(basename($path)); - $name = $splits[0]; - $test = dirname($path).DIRECTORY_SEPARATOR.$name; - if (file_exists($test) || is_link($test)) { - $name = $this->uniqueName(dirname($path), $name, '-', false); - } - } - - if ($name !== '') { - $result = dirname($path).DIRECTORY_SEPARATOR.$name; - if (! rename($src, $result)) { - $this->delTree($dir); - return false; - } - } else { - $dstDir = dirname($path); - $result = array(); - foreach($ls as $name) { - $target = $dstDir.DIRECTORY_SEPARATOR.$name; - if (self::localMoveRecursive($dir.DIRECTORY_SEPARATOR.$name, $target, true, $this->options['copyJoin'])) { - $result[] = $target; - } - } - if (!$result) { - $this->delTree($dir); - return false; - } - } - - is_dir($dir) && $this->delTree($dir); - - return (is_array($result) || file_exists($result)) ? $result : false; - } - //TODO: Add return statement here - return false; - } + /***************** file stat ********************/ - /** - * Create archive and return its path - * - * @param string $dir target dir - * @param array $files files names list - * @param string $name archive name - * @param array $arc archiver options - * @return string|bool - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - */ - protected function _archive($dir, $files, $name, $arc) { - return $this->makeArchive($dir, $files, $name, $arc); - } - - /******************** Over write functions *************************/ - - /** - * File path of local server side work file path - * - * @param string $path - * @return string - * @author Naoki Sawada - */ - protected function getWorkFile($path) { - return $path; - } + /** + * Return stat for given path. + * Stat contains following fields: + * - (int) size file size in b. required + * - (int) ts file modification time in unix time. required + * - (string) mime mimetype. required for folders, others - optionally + * - (bool) read read permissions. required + * - (bool) write write permissions. required + * - (bool) locked is object locked. optionally + * - (bool) hidden is object hidden. optionally + * - (string) alias for symlinks - link target path relative to root path. optionally + * - (string) target for symlinks - link target path. optionally + * If file does not exists - returns empty array or false. + * + * @param string $path file path + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + protected function _stat($path) + { + $stat = array(); - /** - * Delete dirctory trees - * - * @param string $localpath path need convert encoding to server encoding - * @return boolean - * @throws elFinderAbortException - * @author Naoki Sawada - */ - protected function delTree($localpath) { - return $this->rmdirRecursive($localpath); - } + if (!file_exists($path) && !is_link($path)) { + return $stat; + } - /** - * Return fileinfo based on filename - * For item ID based path file system - * Please override if needed on each drivers - * - * @param string $path file cache - * @return array|boolean false - */ - protected function isNameExists($path) { - $exists = file_exists($this->convEncIn($path)); - // restore locale - $this->convEncOut(); - return $exists? $this->stat($path) : false; - } + //Verifies the given path is the root or is inside the root. Prevents directory traveral. + if (!$this->_inpath($path, $this->root)) { + return $stat; + } - /******************** Over write (Optimized) functions *************************/ + $stat['isowner'] = false; + $linkreadable = false; + if ($path != $this->root && is_link($path)) { + if (!$this->options['followSymLinks']) { + return array(); + } + if (!($target = $this->readlink($path)) + || $target == $path) { + if (is_null($target)) { + $stat = array(); + return $stat; + } else { + $stat['mime'] = 'symlink-broken'; + $target = readlink($path); + $lstat = lstat($path); + $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']); + $linkreadable = !empty($ostat['isowner']); + } + } + $stat['alias'] = $this->_path($target); + $stat['target'] = $target; + } - /** - * Recursive files search - * - * @param string $path dir path - * @param string $q search string - * @param array $mimes - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - * @author Naoki Sawada - */ - protected function doSearch($path, $q, $mimes) { - if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || ! class_exists('FilesystemIterator', false)) { - // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch() - return parent::doSearch($path, $q, $mimes); - } - - $result = array(); - - $timeout = $this->options['searchTimeout']? $this->searchStart + $this->options['searchTimeout'] : 0; - if ($timeout && $timeout < time()) { - $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); - return $result; - } - elFinder::extendTimeLimit($this->options['searchTimeout'] + 30); - - $match = array(); - try { - $iterator = new RecursiveIteratorIterator( - new RecursiveCallbackFilterIterator( - new RecursiveDirectoryIterator($path, - FilesystemIterator::KEY_AS_PATHNAME | - FilesystemIterator::SKIP_DOTS | - ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks'])? - RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0) - ), - array($this, 'localFileSystemSearchIteratorFilter') - ), - RecursiveIteratorIterator::SELF_FIRST, - RecursiveIteratorIterator::CATCH_GET_CHILD - ); - foreach ($iterator as $key => $node) { - if ($timeout && ($this->error || $timeout < time())) { - !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath))); - break; - } - if ($node->isDir()) { - if ($this->stripos($node->getFilename(), $q) !== false) { - $match[] = $key; - } - } else { - $match[] = $key; - } - } - } catch (Exception $e) {} - - if ($match) { - foreach($match as $p) { - if ($timeout && ($this->error || $timeout < time())) { - !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p)))); - break; - } - - $stat = $this->stat($p); - - if (!$stat) { // invalid links - continue; - } - - if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) { - continue; - } - - if ((!$mimes || $stat['mime'] !== 'directory')) { - $stat['path'] = $this->path($stat['hash']); - if ($this->URL && !isset($stat['url'])) { - $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1)); - $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path)); - } - - $result[] = $stat; - } - } - } - - return $result; - } + $readable = is_readable($path); - /******************** Original local functions ************************ - * @param $file - * @param $key - * @param $iterator - * @return bool - */ + if ($readable) { + $size = sprintf('%u', filesize($path)); + $stat['ts'] = filemtime($path); + if ($this->statOwner) { + $fstat = stat($path); + $uid = $fstat['uid']; + $gid = $fstat['gid']; + $stat['perm'] = substr((string)decoct($fstat['mode']), -4); + $stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); + } + } + + if (($dir = is_dir($path)) && $this->options['detectDirIcon']) { + $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon']; + if ($this->URL && file_exists($favicon)) { + $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1)); + } + } + + if (!isset($stat['mime'])) { + $stat['mime'] = $dir ? 'directory' : $this->mimetype($path); + } + //logical rights first + $stat['read'] = ($linkreadable || $readable) ? null : false; + $stat['write'] = is_writable($path) ? null : false; + + if (is_null($stat['read'])) { + if ($dir) { + $stat['size'] = 0; + } else if (isset($size)) { + $stat['size'] = $size; + } + } + + if ($this->options['statCorrector']) { + call_user_func_array($this->options['statCorrector'], array(&$stat, $path, $this->statOwner, $this)); + } + + return $stat; + } + + /** + * Get stat `owner`, `group` and `isowner` by `uid` and `gid` + * Sub-fuction of _stat() and _scandir() + * + * @param integer $uid + * @param integer $gid + * + * @return array stat + */ + protected function getOwnerStat($uid, $gid) + { + static $names = null; + static $phpuid = null; + + if (is_null($names)) { + $names = array('uid' => array(), 'gid' => array()); + } + if (is_null($phpuid)) { + if (is_callable('posix_getuid')) { + $phpuid = posix_getuid(); + } else { + $phpuid = 0; + } + } + + $stat = array(); + + if ($uid) { + $stat['isowner'] = ($phpuid == $uid); + if (isset($names['uid'][$uid])) { + $stat['owner'] = $names['uid'][$uid]; + } else if (is_callable('posix_getpwuid')) { + $pwuid = posix_getpwuid($uid); + $stat['owner'] = $names['uid'][$uid] = $pwuid['name']; + } else { + $stat['owner'] = $names['uid'][$uid] = $uid; + } + } + if ($gid) { + if (isset($names['gid'][$gid])) { + $stat['group'] = $names['gid'][$gid]; + } else if (is_callable('posix_getgrgid')) { + $grgid = posix_getgrgid($gid); + $stat['group'] = $names['gid'][$gid] = $grgid['name']; + } else { + $stat['group'] = $names['gid'][$gid] = $gid; + } + } + + return $stat; + } + + /** + * Return true if path is dir and has at least one childs directory + * + * @param string $path dir path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _subdirs($path) + { + + $dirs = false; + if (is_dir($path) && is_readable($path)) { + if (class_exists('FilesystemIterator', false)) { + $dirItr = new ParentIterator( + new RecursiveDirectoryIterator($path, + FilesystemIterator::SKIP_DOTS | + FilesystemIterator::CURRENT_AS_SELF | + (defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') ? + RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0) + ) + ); + $dirItr->rewind(); + if ($dirItr->hasChildren()) { + $dirs = true; + $name = $dirItr->getSubPathName(); + while ($dirItr->valid()) { + if (!$this->attr($path . DIRECTORY_SEPARATOR . $name, 'read', null, true)) { + $dirs = false; + $dirItr->next(); + $name = $dirItr->getSubPathName(); + continue; + } + $dirs = true; + break; + } + } + } else { + $path = strtr($path, array('[' => '\\[', ']' => '\\]', '*' => '\\*', '?' => '\\?')); + return (bool)glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR); + } + } + return $dirs; + } + + /** + * Return object width and height + * Usualy used for images, but can be realize for video etc... + * + * @param string $path file path + * @param string $mime file mime type + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _dimensions($path, $mime) + { + clearstatcache(); + return strpos($mime, 'image') === 0 && is_readable($path) && ($s = getimagesize($path)) !== false + ? $s[0] . 'x' . $s[1] + : false; + } + /******************** file/dir content *********************/ + + /** + * Return symlink target file + * + * @param string $path link path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function readlink($path) + { + if (!($target = readlink($path))) { + return null; + } + + if (strpos($target, $this->systemRoot) !== 0) { + $target = $this->_joinPath(dirname($path), $target); + } + + if (!file_exists($target)) { + return false; + } + + return $target; + } + + /** + * Return files list in directory. + * + * @param string $path dir path + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function _scandir($path) + { + elFinder::checkAborted(); + $files = array(); + $cache = array(); + $dirWritable = is_writable($path); + $dirItr = array(); + $followSymLinks = $this->options['followSymLinks']; + try { + $dirItr = new DirectoryIterator($path); + } catch (UnexpectedValueException $e) { + } + + foreach ($dirItr as $file) { + try { + if ($file->isDot()) { + continue; + } + + $files[] = $fpath = $file->getPathname(); + + $br = false; + $stat = array(); + + $stat['isowner'] = false; + $linkreadable = false; + if ($file->isLink()) { + if (!$followSymLinks) { + continue; + } + if (!($target = $this->readlink($fpath)) + || $target == $fpath) { + if (is_null($target)) { + $stat = array(); + $br = true; + } else { + $_path = $fpath; + $stat['mime'] = 'symlink-broken'; + $target = readlink($_path); + $lstat = lstat($_path); + $ostat = $this->getOwnerStat($lstat['uid'], $lstat['gid']); + $linkreadable = !empty($ostat['isowner']); + $dir = false; + $stat['alias'] = $this->_path($target); + $stat['target'] = $target; + } + } else { + $dir = is_dir($target); + $stat['alias'] = $this->_path($target); + $stat['target'] = $target; + $stat['mime'] = $dir ? 'directory' : $this->mimetype($stat['alias']); + } + } else { + if (($dir = $file->isDir()) && $this->options['detectDirIcon']) { + $path = $file->getPathname(); + $favicon = $path . DIRECTORY_SEPARATOR . $this->options['detectDirIcon']; + if ($this->URL && file_exists($favicon)) { + $stat['icon'] = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($favicon, strlen($this->root) + 1)); + } + } + $stat['mime'] = $dir ? 'directory' : $this->mimetype($fpath); + } + $size = sprintf('%u', $file->getSize()); + $stat['ts'] = $file->getMTime(); + if (!$br) { + if ($this->statOwner && !$linkreadable) { + $uid = $file->getOwner(); + $gid = $file->getGroup(); + $stat['perm'] = substr((string)decoct($file->getPerms()), -4); + $stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); + } + + //logical rights first + $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false; + $stat['write'] = $file->isWritable() ? null : false; + $stat['locked'] = $dirWritable ? null : true; + + if (is_null($stat['read'])) { + $stat['size'] = $dir ? 0 : $size; + } + + if ($this->options['statCorrector']) { + call_user_func_array($this->options['statCorrector'], array(&$stat, $fpath, $this->statOwner, $this)); + } + } + + $cache[] = array($fpath, $stat); + } catch (RuntimeException $e) { + continue; + } + } + + if ($cache) { + $cache = $this->convEncOut($cache, false); + foreach ($cache as $d) { + $this->updateCache($d[0], $d[1]); + } + } + + return $files; + } + + /** + * Open file and return file pointer + * + * @param string $path file path + * @param string $mode + * + * @return false|resource + * @internal param bool $write open file for writing + * @author Dmitry (dio) Levashov + */ + protected function _fopen($path, $mode = 'rb') + { + return fopen($path, $mode); + } + + /** + * Close opened file + * + * @param resource $fp file pointer + * @param string $path + * + * @return bool + * @author Dmitry (dio) Levashov + */ + protected function _fclose($fp, $path = '') + { + return (is_resource($fp) && fclose($fp)); + } + + /******************** file/dir manipulations *************************/ + + /** + * Create dir and return created dir path or false on failed + * + * @param string $path parent dir path + * @param string $name new directory name + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + protected function _mkdir($path, $name) + { + $path = $this->_joinPath($path, $name); + + if (mkdir($path)) { + chmod($path, $this->options['dirMode']); + return $path; + } + + return false; + } + + /** + * Create file and return it's path or false on failed + * + * @param string $path parent dir path + * @param string $name new file name + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + protected function _mkfile($path, $name) + { + $path = $this->_joinPath($path, $name); + + if (($fp = fopen($path, 'w'))) { + fclose($fp); + chmod($path, $this->options['fileMode']); + return $path; + } + return false; + } + + /** + * Create symlink + * + * @param string $source file to link to + * @param string $targetDir folder to create link in + * @param string $name symlink name + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _symlink($source, $targetDir, $name) + { + return symlink($source, $this->_joinPath($targetDir, $name)); + } + + /** + * Copy file into another file + * + * @param string $source source file path + * @param string $targetDir target directory path + * @param string $name new file name + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _copy($source, $targetDir, $name) + { + $mtime = filemtime($source); + $target = $this->_joinPath($targetDir, $name); + if ($ret = copy($source, $target)) { + isset($this->options['keepTimestamp']['copy']) && $mtime && touch($target, $mtime); + } + return $ret; + } + + /** + * Move file into another parent dir. + * Return new file path or false. + * + * @param string $source source file path + * @param $targetDir + * @param string $name file name + * + * @return bool|string + * @internal param string $target target dir path + * @author Dmitry (dio) Levashov + */ + protected function _move($source, $targetDir, $name) + { + $mtime = filemtime($source); + $target = $this->_joinPath($targetDir, $name); + if ($ret = rename($source, $target) ? $target : false) { + isset($this->options['keepTimestamp']['move']) && $mtime && touch($target, $mtime); + } + return $ret; + } + + /** + * Remove file + * + * @param string $path file path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _unlink($path) + { + return is_file($path) && unlink($path); + } + + /** + * Remove dir + * + * @param string $path dir path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _rmdir($path) + { + return rmdir($path); + } + + /** + * Create new file and write into it from file pointer. + * Return new file path or false on error. + * + * @param resource $fp file pointer + * @param string $dir target dir path + * @param string $name file name + * @param array $stat file stat (required by some virtual fs) + * + * @return bool|string + * @author Dmitry (dio) Levashov + **/ + protected function _save($fp, $dir, $name, $stat) + { + $path = $this->_joinPath($dir, $name); + + $meta = stream_get_meta_data($fp); + $uri = isset($meta['uri']) ? $meta['uri'] : ''; + if ($uri && !preg_match('#^[a-zA-Z0-9]+://#', $uri) && !is_link($uri)) { + fclose($fp); + $mtime = filemtime($uri); + $isCmdPaste = ($this->ARGS['cmd'] === 'paste'); + $isCmdCopy = ($isCmdPaste && empty($this->ARGS['cut'])); + if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) { + return false; + } + // keep timestamp on upload + if ($mtime && $this->ARGS['cmd'] === 'upload') { + touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time()); + } + } else { + if (file_put_contents($path, $fp, LOCK_EX) === false) { + return false; + } + } + + chmod($path, $this->options['fileMode']); + return $path; + } + + /** + * Get file contents + * + * @param string $path file path + * + * @return string|false + * @author Dmitry (dio) Levashov + **/ + protected function _getContents($path) + { + return file_get_contents($path); + } + + /** + * Write a string to a file + * + * @param string $path file path + * @param string $content new file content + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _filePutContents($path, $content) + { + return (file_put_contents($path, $content, LOCK_EX) !== false); + } + + /** + * Detect available archivers + * + * @return void + * @throws elFinderAbortException + */ + protected function _checkArchivers() + { + $this->archivers = $this->getArchivers(); + return; + } + + /** + * chmod availability + * + * @param string $path + * @param string $mode + * + * @return bool + */ + protected function _chmod($path, $mode) + { + $modeOct = is_string($mode) ? octdec($mode) : octdec(sprintf("%04o", $mode)); + return chmod($path, $modeOct); + } + + /** + * Recursive symlinks search + * + * @param string $path file/dir path + * + * @return bool + * @throws Exception + * @author Dmitry (dio) Levashov + */ + protected function _findSymlinks($path) + { + return self::localFindSymlinks($path); + } + + /** + * Extract files from archive + * + * @param string $path archive path + * @param array $arc archiver command and arguments (same as in $this->archivers) + * + * @return array|string|boolean + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + */ + protected function _extract($path, $arc) + { + + if ($this->quarantine) { + + $dir = $this->quarantine . DIRECTORY_SEPARATOR . md5(basename($path) . mt_rand()); + $archive = (isset($arc['toSpec']) || $arc['cmd'] === 'phpfunction') ? '' : $dir . DIRECTORY_SEPARATOR . basename($path); + + if (!mkdir($dir)) { + return false; + } + + // insurance unexpected shutdown + register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir)); + + chmod($dir, 0777); + + // copy in quarantine + if (!is_readable($path) || ($archive && !copy($path, $archive))) { + return false; + } + + // extract in quarantine + $this->unpackArchive($path, $arc, $archive ? true : $dir); + + // get files list + try { + $ls = self::localScandir($dir); + } catch (Exception $e) { + return false; + } + + // no files - extract error ? + if (empty($ls)) { + return false; + } + + $this->archiveSize = 0; + + // find symlinks and check extracted items + $checkRes = $this->checkExtractItems($dir); + if ($checkRes['symlinks']) { + self::localRmdirRecursive($dir); + return $this->setError(array_merge($this->error, array(elFinder::ERROR_ARC_SYMLINKS))); + } + $this->archiveSize = $checkRes['totalSize']; + if ($checkRes['rmNames']) { + foreach ($checkRes['rmNames'] as $name) { + $this->addError(elFinder::ERROR_SAVE, $name); + } + } + + // check max files size + if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { + $this->delTree($dir); + return $this->setError(elFinder::ERROR_ARC_MAXSIZE); + } + + $extractTo = $this->extractToNewdir; // 'auto', ture or false + + // archive contains one item - extract in archive dir + $name = ''; + $src = $dir . DIRECTORY_SEPARATOR . $ls[0]; + if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) { + $name = $ls[0]; + } else if ($extractTo === 'auto' || $extractTo) { + // for several files - create new directory + // create unique name for directory + $src = $dir; + $splits = elFinder::splitFileExtention(basename($path)); + $name = $splits[0]; + $test = dirname($path) . DIRECTORY_SEPARATOR . $name; + if (file_exists($test) || is_link($test)) { + $name = $this->uniqueName(dirname($path), $name, '-', false); + } + } + + if ($name !== '') { + $result = dirname($path) . DIRECTORY_SEPARATOR . $name; + + if (!rename($src, $result)) { + $this->delTree($dir); + return false; + } + } else { + $dstDir = dirname($path); + $result = array(); + foreach ($ls as $name) { + $target = $dstDir . DIRECTORY_SEPARATOR . $name; + if (self::localMoveRecursive($dir . DIRECTORY_SEPARATOR . $name, $target, true, $this->options['copyJoin'])) { + $result[] = $target; + } + } + if (!$result) { + $this->delTree($dir); + return false; + } + } + + is_dir($dir) && $this->delTree($dir); + + return (is_array($result) || file_exists($result)) ? $result : false; + } + //TODO: Add return statement here + return false; + } + + /** + * Create archive and return its path + * + * @param string $dir target dir + * @param array $files files names list + * @param string $name archive name + * @param array $arc archiver options + * + * @return string|bool + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + */ + protected function _archive($dir, $files, $name, $arc) + { + return $this->makeArchive($dir, $files, $name, $arc); + } + + /******************** Over write functions *************************/ + + /** + * File path of local server side work file path + * + * @param string $path + * + * @return string + * @author Naoki Sawada + */ + protected function getWorkFile($path) + { + return $path; + } + + /** + * Delete dirctory trees + * + * @param string $localpath path need convert encoding to server encoding + * + * @return boolean + * @throws elFinderAbortException + * @author Naoki Sawada + */ + protected function delTree($localpath) + { + return $this->rmdirRecursive($localpath); + } + + /** + * Return fileinfo based on filename + * For item ID based path file system + * Please override if needed on each drivers + * + * @param string $path file cache + * + * @return array|boolean false + */ + protected function isNameExists($path) + { + $exists = file_exists($this->convEncIn($path)); + // restore locale + $this->convEncOut(); + return $exists ? $this->stat($path) : false; + } + + /******************** Over write (Optimized) functions *************************/ + + /** + * Recursive files search + * + * @param string $path dir path + * @param string $q search string + * @param array $mimes + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + * @author Naoki Sawada + */ + protected function doSearch($path, $q, $mimes) + { + if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) { + // has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch() + return parent::doSearch($path, $q, $mimes); + } + + $result = array(); + + $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0; + if ($timeout && $timeout < time()) { + $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); + return $result; + } + elFinder::extendTimeLimit($this->options['searchTimeout'] + 30); + + $match = array(); + try { + $iterator = new RecursiveIteratorIterator( + new RecursiveCallbackFilterIterator( + new RecursiveDirectoryIterator($path, + FilesystemIterator::KEY_AS_PATHNAME | + FilesystemIterator::SKIP_DOTS | + ((defined('RecursiveDirectoryIterator::FOLLOW_SYMLINKS') && $this->options['followSymLinks']) ? + RecursiveDirectoryIterator::FOLLOW_SYMLINKS : 0) + ), + array($this, 'localFileSystemSearchIteratorFilter') + ), + RecursiveIteratorIterator::SELF_FIRST, + RecursiveIteratorIterator::CATCH_GET_CHILD + ); + foreach ($iterator as $key => $node) { + if ($timeout && ($this->error || $timeout < time())) { + !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($node->getPath))); + break; + } + if ($node->isDir()) { + if ($this->stripos($node->getFilename(), $q) !== false) { + $match[] = $key; + } + } else { + $match[] = $key; + } + } + } catch (Exception $e) { + } + + if ($match) { + foreach ($match as $p) { + if ($timeout && ($this->error || $timeout < time())) { + !$this->error && $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode(dirname($p)))); + break; + } + + $stat = $this->stat($p); + + if (!$stat) { // invalid links + continue; + } + + if (!empty($stat['hidden']) || !$this->mimeAccepted($stat['mime'], $mimes)) { + continue; + } + + if ((!$mimes || $stat['mime'] !== 'directory')) { + $stat['path'] = $this->path($stat['hash']); + if ($this->URL && !isset($stat['url'])) { + $_path = str_replace(DIRECTORY_SEPARATOR, '/', substr($p, strlen($this->root) + 1)); + $stat['url'] = $this->URL . str_replace('%2F', '/', rawurlencode($_path)); + } + + $result[] = $stat; + } + } + } + + return $result; + } + + /******************** Original local functions ************************ + * + * @param $file + * @param $key + * @param $iterator + * + * @return bool + */ + + public function localFileSystemSearchIteratorFilter($file, $key, $iterator) + { + /* @var FilesystemIterator $file */ + /* @var RecursiveDirectoryIterator $iterator */ + $name = $file->getFilename(); + if ($this->doSearchCurrentQuery['excludes']) { + foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) { + if ($this->stripos($name, $exclude) !== false) { + return false; + } + } + } + if ($iterator->hasChildren()) { + if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) { + return false; + } + return (bool)$this->attr($key, 'read', null, true); + } + return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false) ? false : true; + } - public function localFileSystemSearchIteratorFilter($file, $key, $iterator) { - /* @var FilesystemIterator $file */ - /* @var RecursiveDirectoryIterator $iterator */ - $name = $file->getFilename(); - if ($this->doSearchCurrentQuery['excludes']) { - foreach($this->doSearchCurrentQuery['excludes'] as $exclude) { - if ($this->stripos($name, $exclude) !== false) { - return false; - } - } - } - if ($iterator->hasChildren()) { - if ($this->options['searchExDirReg'] && preg_match($this->options['searchExDirReg'], $key)) { - return false; - } - return (bool)$this->attr($key, 'read', null, true); - } - return ($this->stripos($name, $this->doSearchCurrentQuery['q']) === false)? false : true; - } - } // END class diff --git a/php/elFinderVolumeMySQL.class.php b/php/elFinderVolumeMySQL.class.php index 2f06bb6fd..7239596e2 100644 --- a/php/elFinderVolumeMySQL.class.php +++ b/php/elFinderVolumeMySQL.class.php @@ -5,924 +5,999 @@ * * @author Dmitry (dio) Levashov **/ -class elFinderVolumeMySQL extends elFinderVolumeDriver { - - /** - * Driver id - * Must be started from letter and contains [a-z0-9] - * Used as part of volume id - * - * @var string - **/ - protected $driverId = 'm'; - - /** - * Database object - * - * @var mysqli - **/ - protected $db = null; - - /** - * Tables to store files - * - * @var string - **/ - protected $tbf = ''; - - /** - * Directory for tmp files - * If not set driver will try to use tmbDir as tmpDir - * - * @var string - **/ - protected $tmpPath = ''; - - /** - * Numbers of sql requests (for debug) - * - * @var int - **/ - protected $sqlCnt = 0; - - /** - * Last db error message - * - * @var string - **/ - protected $dbError = ''; +class elFinderVolumeMySQL extends elFinderVolumeDriver +{ - /** - * This root has parent id - * - * @var boolean - */ - protected $rootHasParent = false; + /** + * Driver id + * Must be started from letter and contains [a-z0-9] + * Used as part of volume id + * + * @var string + **/ + protected $driverId = 'm'; - /** - * Constructor - * Extend options with required fields - * - * @author Dmitry (dio) Levashov - */ - public function __construct() { - $opts = array( - 'host' => 'localhost', - 'user' => '', - 'pass' => '', - 'db' => '', - 'port' => null, - 'socket' => null, - 'files_table' => 'elfinder_file', - 'tmbPath' => '', - 'tmpPath' => '', - 'rootCssClass' => 'elfinder-navbar-root-sql', - 'noSessionCache' => array('hasdirs') - ); - $this->options = array_merge($this->options, $opts); - $this->options['mimeDetect'] = 'internal'; - } - - /*********************************************************************/ - /* INIT AND CONFIGURE */ - /*********************************************************************/ - - /** - * Prepare driver before mount volume. - * Connect to db, check required tables and fetch root path - * - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function init() { - - if (!($this->options['host'] || $this->options['socket']) - || !$this->options['user'] - || !$this->options['pass'] - || !$this->options['db'] - || !$this->options['path'] - || !$this->options['files_table']) { - return false; - } - - - $this->db = new mysqli($this->options['host'], $this->options['user'], $this->options['pass'], $this->options['db'], $this->options['port'], $this->options['socket']); - if ($this->db->connect_error || mysqli_connect_error()) { - return false; - } - - $this->db->set_charset('utf8'); + /** + * Database object + * + * @var mysqli + **/ + protected $db = null; - if ($res = $this->db->query('SHOW TABLES')) { - while ($row = $res->fetch_array()) { - if ($row[0] == $this->options['files_table']) { - $this->tbf = $this->options['files_table']; - break; - } - } - } + /** + * Tables to store files + * + * @var string + **/ + protected $tbf = ''; - if (!$this->tbf) { - return false; - } + /** + * Directory for tmp files + * If not set driver will try to use tmbDir as tmpDir + * + * @var string + **/ + protected $tmpPath = ''; - $this->updateCache($this->options['path'], $this->_stat($this->options['path'])); + /** + * Numbers of sql requests (for debug) + * + * @var int + **/ + protected $sqlCnt = 0; - // enable command archive - $this->options['useRemoteArchive'] = true; + /** + * Last db error message + * + * @var string + **/ + protected $dbError = ''; - return true; - } + /** + * This root has parent id + * + * @var boolean + */ + protected $rootHasParent = false; + + /** + * Constructor + * Extend options with required fields + * + * @author Dmitry (dio) Levashov + */ + public function __construct() + { + $opts = array( + 'host' => 'localhost', + 'user' => '', + 'pass' => '', + 'db' => '', + 'port' => null, + 'socket' => null, + 'files_table' => 'elfinder_file', + 'tmbPath' => '', + 'tmpPath' => '', + 'rootCssClass' => 'elfinder-navbar-root-sql', + 'noSessionCache' => array('hasdirs') + ); + $this->options = array_merge($this->options, $opts); + $this->options['mimeDetect'] = 'internal'; + } + + /*********************************************************************/ + /* INIT AND CONFIGURE */ + /*********************************************************************/ + + /** + * Prepare driver before mount volume. + * Connect to db, check required tables and fetch root path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function init() + { + + if (!($this->options['host'] || $this->options['socket']) + || !$this->options['user'] + || !$this->options['pass'] + || !$this->options['db'] + || !$this->options['path'] + || !$this->options['files_table']) { + return false; + } - /** - * Set tmp path - * - * @return void - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function configure() { - parent::configure(); + $this->db = new mysqli($this->options['host'], $this->options['user'], $this->options['pass'], $this->options['db'], $this->options['port'], $this->options['socket']); + if ($this->db->connect_error || mysqli_connect_error()) { + return false; + } - if (($tmp = $this->options['tmpPath'])) { - if (!file_exists($tmp)) { - if (mkdir($tmp)) { - chmod($tmp, $this->options['tmbPathMode']); - } - } - - $this->tmpPath = is_dir($tmp) && is_writable($tmp) ? $tmp : false; - } - if (!$this->tmpPath && ($tmp = elFinder::getStaticVar('commonTempPath'))) { - $this->tmpPath = $tmp; - } - - // fallback of $this->tmp - if (!$this->tmpPath && $this->tmbPathWritable) { - $this->tmpPath = $this->tmbPath; - } + $this->db->set_charset('utf8'); - $this->mimeDetect = 'internal'; - } - - /** - * Close connection - * - * @return void - * @author Dmitry (dio) Levashov - **/ - public function umount() { - $this->db->close(); - } - - /** - * Return debug info for client - * - * @return array - * @author Dmitry (dio) Levashov - **/ - public function debug() { - $debug = parent::debug(); - $debug['sqlCount'] = $this->sqlCnt; - if ($this->dbError) { - $debug['dbError'] = $this->dbError; - } - return $debug; - } + if ($res = $this->db->query('SHOW TABLES')) { + while ($row = $res->fetch_array()) { + if ($row[0] == $this->options['files_table']) { + $this->tbf = $this->options['files_table']; + break; + } + } + } - /** - * Perform sql query and return result. - * Increase sqlCnt and save error if occured - * - * @param string $sql query - * @return bool|mysqli_result - * @author Dmitry (dio) Levashov - */ - protected function query($sql) { - $this->sqlCnt++; - $res = $this->db->query($sql); - if (!$res) { - $this->dbError = $this->db->error; - } - return $res; - } + if (!$this->tbf) { + return false; + } - /** - * Create empty object with required mimetype - * - * @param string $path parent dir path - * @param string $name object name - * @param string $mime mime type - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function make($path, $name, $mime) { - $sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`, `locked`, `hidden`, `width`, `height`) VALUES (\'%s\', \'%s\', 0, %d, \'%s\', \'\', \'%d\', \'%d\', \'%d\', \'%d\', 0, 0)'; - $sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write'], $this->defaults['locked'], $this->defaults['hidden']); - // echo $sql; - return $this->query($sql) && $this->db->affected_rows > 0; - } + $this->updateCache($this->options['path'], $this->_stat($this->options['path'])); - /*********************************************************************/ - /* FS API */ - /*********************************************************************/ - - /** - * Cache dir contents - * - * @param string $path dir path - * @return string - * @author Dmitry Levashov - **/ - protected function cacheDir($path) { - $this->dirsCache[$path] = array(); + // enable command archive + $this->options['useRemoteArchive'] = true; - $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs - FROM '.$this->tbf.' AS f - LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime=\'directory\' - WHERE f.parent_id=\''.$path.'\' + return true; + } + + + /** + * Set tmp path + * + * @return void + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function configure() + { + parent::configure(); + + if (($tmp = $this->options['tmpPath'])) { + if (!file_exists($tmp)) { + if (mkdir($tmp)) { + chmod($tmp, $this->options['tmbPathMode']); + } + } + + $this->tmpPath = is_dir($tmp) && is_writable($tmp) ? $tmp : false; + } + if (!$this->tmpPath && ($tmp = elFinder::getStaticVar('commonTempPath'))) { + $this->tmpPath = $tmp; + } + + // fallback of $this->tmp + if (!$this->tmpPath && $this->tmbPathWritable) { + $this->tmpPath = $this->tmbPath; + } + + $this->mimeDetect = 'internal'; + } + + /** + * Close connection + * + * @return void + * @author Dmitry (dio) Levashov + **/ + public function umount() + { + $this->db->close(); + } + + /** + * Return debug info for client + * + * @return array + * @author Dmitry (dio) Levashov + **/ + public function debug() + { + $debug = parent::debug(); + $debug['sqlCount'] = $this->sqlCnt; + if ($this->dbError) { + $debug['dbError'] = $this->dbError; + } + return $debug; + } + + /** + * Perform sql query and return result. + * Increase sqlCnt and save error if occured + * + * @param string $sql query + * + * @return bool|mysqli_result + * @author Dmitry (dio) Levashov + */ + protected function query($sql) + { + $this->sqlCnt++; + $res = $this->db->query($sql); + if (!$res) { + $this->dbError = $this->db->error; + } + return $res; + } + + /** + * Create empty object with required mimetype + * + * @param string $path parent dir path + * @param string $name object name + * @param string $mime mime type + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function make($path, $name, $mime) + { + $sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`, `locked`, `hidden`, `width`, `height`) VALUES (\'%s\', \'%s\', 0, %d, \'%s\', \'\', \'%d\', \'%d\', \'%d\', \'%d\', 0, 0)'; + $sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write'], $this->defaults['locked'], $this->defaults['hidden']); + // echo $sql; + return $this->query($sql) && $this->db->affected_rows > 0; + } + + /*********************************************************************/ + /* FS API */ + /*********************************************************************/ + + /** + * Cache dir contents + * + * @param string $path dir path + * + * @return string + * @author Dmitry Levashov + **/ + protected function cacheDir($path) + { + $this->dirsCache[$path] = array(); + + $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs + FROM ' . $this->tbf . ' AS f + LEFT JOIN ' . $this->tbf . ' AS ch ON ch.parent_id=f.id AND ch.mime=\'directory\' + WHERE f.parent_id=\'' . $path . '\' GROUP BY f.id, ch.id'; - - $res = $this->query($sql); - if ($res) { - while ($row = $res->fetch_assoc()) { - $id = $row['id']; - if ($row['parent_id'] && $id != $this->root) { - $row['phash'] = $this->encode($row['parent_id']); - } - - if ($row['mime'] == 'directory') { - unset($row['width']); - unset($row['height']); - $row['size'] = 0; - } else { - unset($row['dirs']); - } - - unset($row['id']); - unset($row['parent_id']); - - - - if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) { - $this->dirsCache[$path][] = $id; - } - } - } - - return $this->dirsCache[$path]; - } - /** - * Return array of parents paths (ids) - * - * @param int $path file path (id) - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function getParents($path) { - $parents = array(); + $res = $this->query($sql); + if ($res) { + while ($row = $res->fetch_assoc()) { + $id = $row['id']; + if ($row['parent_id'] && $id != $this->root) { + $row['phash'] = $this->encode($row['parent_id']); + } - while ($path) { - if ($file = $this->stat($path)) { - array_unshift($parents, $path); - $path = isset($file['phash']) ? $this->decode($file['phash']) : false; - } - } - - if (count($parents)) { - array_pop($parents); - } - return $parents; - } + if ($row['mime'] == 'directory') { + unset($row['width']); + unset($row['height']); + $row['size'] = 0; + } else { + unset($row['dirs']); + } - /** - * Return correct file path for LOAD_FILE method - * - * @param string $path file path (id) - * @return string - * @author Troex Nevelin - **/ - protected function loadFilePath($path) { - $realPath = realpath($path); - if (DIRECTORY_SEPARATOR == '\\') { // windows - $realPath = str_replace('\\', '\\\\', $realPath); - } - return $this->db->real_escape_string($realPath); - } + unset($row['id']); + unset($row['parent_id']); - /** - * Recursive files search - * - * @param string $path dir path - * @param string $q search string - * @param array $mimes - * @return array - * @throws elFinderAbortException - * @author Dmitry (dio) Levashov - */ - protected function doSearch($path, $q, $mimes) { - if (!empty($this->doSearchCurrentQuery['matchMethod'])) { - // has custom match method use elFinderVolumeDriver::doSearch() - return parent::doSearch($path, $q, $mimes); - } - $dirs = array(); - $timeout = $this->options['searchTimeout']? $this->searchStart + $this->options['searchTimeout'] : 0; - - if ($path != $this->root || $this->rootHasParent) { - $dirs = $inpath = array(intval($path)); - while($inpath) { - $in = '('.join(',', $inpath).')'; - $inpath = array(); - $sql = 'SELECT f.id FROM %s AS f WHERE f.parent_id IN '.$in.' AND `mime` = \'directory\''; - $sql = sprintf($sql, $this->tbf); - if ($res = $this->query($sql)) { - $_dir = array(); - while ($dat = $res->fetch_assoc()) { - $inpath[] = $dat['id']; - } - $dirs = array_merge($dirs, $inpath); - } - } - } + if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) { + $this->dirsCache[$path][] = $id; + } + } + } - $result = array(); - - if ($mimes) { - $whrs = array(); - foreach($mimes as $mime) { - if (strpos($mime, '/') === false) { - $whrs[] = sprintf('f.mime LIKE \'%s/%%\'', $this->db->real_escape_string($mime)); - } else { - $whrs[] = sprintf('f.mime = \'%s\'', $this->db->real_escape_string($mime)); - } - } - $whr = join(' OR ', $whrs); - } else { - $whr = sprintf('f.name LIKE \'%%%s%%\'', $this->db->real_escape_string($q)); - } - if ($dirs) { - $whr = '(' . $whr . ') AND (`parent_id` IN (' . join(',', $dirs) . '))'; - } - - $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, 0 AS dirs + return $this->dirsCache[$path]; + } + + /** + * Return array of parents paths (ids) + * + * @param int $path file path (id) + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function getParents($path) + { + $parents = array(); + + while ($path) { + if ($file = $this->stat($path)) { + array_unshift($parents, $path); + $path = isset($file['phash']) ? $this->decode($file['phash']) : false; + } + } + + if (count($parents)) { + array_pop($parents); + } + return $parents; + } + + /** + * Return correct file path for LOAD_FILE method + * + * @param string $path file path (id) + * + * @return string + * @author Troex Nevelin + **/ + protected function loadFilePath($path) + { + $realPath = realpath($path); + if (DIRECTORY_SEPARATOR == '\\') { // windows + $realPath = str_replace('\\', '\\\\', $realPath); + } + return $this->db->real_escape_string($realPath); + } + + /** + * Recursive files search + * + * @param string $path dir path + * @param string $q search string + * @param array $mimes + * + * @return array + * @throws elFinderAbortException + * @author Dmitry (dio) Levashov + */ + protected function doSearch($path, $q, $mimes) + { + if (!empty($this->doSearchCurrentQuery['matchMethod'])) { + // has custom match method use elFinderVolumeDriver::doSearch() + return parent::doSearch($path, $q, $mimes); + } + + $dirs = array(); + $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0; + + if ($path != $this->root || $this->rootHasParent) { + $dirs = $inpath = array(intval($path)); + while ($inpath) { + $in = '(' . join(',', $inpath) . ')'; + $inpath = array(); + $sql = 'SELECT f.id FROM %s AS f WHERE f.parent_id IN ' . $in . ' AND `mime` = \'directory\''; + $sql = sprintf($sql, $this->tbf); + if ($res = $this->query($sql)) { + $_dir = array(); + while ($dat = $res->fetch_assoc()) { + $inpath[] = $dat['id']; + } + $dirs = array_merge($dirs, $inpath); + } + } + } + + $result = array(); + + if ($mimes) { + $whrs = array(); + foreach ($mimes as $mime) { + if (strpos($mime, '/') === false) { + $whrs[] = sprintf('f.mime LIKE \'%s/%%\'', $this->db->real_escape_string($mime)); + } else { + $whrs[] = sprintf('f.mime = \'%s\'', $this->db->real_escape_string($mime)); + } + } + $whr = join(' OR ', $whrs); + } else { + $whr = sprintf('f.name LIKE \'%%%s%%\'', $this->db->real_escape_string($q)); + } + if ($dirs) { + $whr = '(' . $whr . ') AND (`parent_id` IN (' . join(',', $dirs) . '))'; + } + + $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, 0 AS dirs FROM %s AS f WHERE %s'; - - $sql = sprintf($sql, $this->tbf, $whr); - - if (($res = $this->query($sql))) { - while ($row = $res->fetch_assoc()) { - if ($timeout && $timeout < time()) { - $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); - break; - } - - if (!$this->mimeAccepted($row['mime'], $mimes)) { - continue; - } - $id = $row['id']; - if ($id == $this->root) { - continue; - } - if ($row['parent_id'] && $id != $this->root) { - $row['phash'] = $this->encode($row['parent_id']); - } - $row['path'] = $this->_path($id); - if ($row['mime'] == 'directory') { - unset($row['width']); - unset($row['height']); - } else { - unset($row['dirs']); - } + $sql = sprintf($sql, $this->tbf, $whr); - unset($row['id']); - unset($row['parent_id']); + if (($res = $this->query($sql))) { + while ($row = $res->fetch_assoc()) { + if ($timeout && $timeout < time()) { + $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->path($this->encode($path))); + break; + } - if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) { - $result[] = $stat; - } - } - } - return $result; - } + if (!$this->mimeAccepted($row['mime'], $mimes)) { + continue; + } + $id = $row['id']; + if ($id == $this->root) { + continue; + } + if ($row['parent_id'] && $id != $this->root) { + $row['phash'] = $this->encode($row['parent_id']); + } + $row['path'] = $this->_path($id); + + if ($row['mime'] == 'directory') { + unset($row['width']); + unset($row['height']); + } else { + unset($row['dirs']); + } + + unset($row['id']); + unset($row['parent_id']); + + if (($stat = $this->updateCache($id, $row)) && empty($stat['hidden'])) { + $result[] = $stat; + } + } + } + return $result; + } - /*********************** paths/urls *************************/ - - /** - * Return parent directory path - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _dirname($path) { - return ($stat = $this->stat($path)) ? (!empty($stat['phash']) ? $this->decode($stat['phash']) : $this->root) : false; - } + /*********************** paths/urls *************************/ - /** - * Return file name - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _basename($path) { - return ($stat = $this->stat($path)) ? $stat['name'] : false; - } + /** + * Return parent directory path + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _dirname($path) + { + return ($stat = $this->stat($path)) ? (!empty($stat['phash']) ? $this->decode($stat['phash']) : $this->root) : false; + } - /** - * Join dir name and file name and return full path - * - * @param string $dir - * @param string $name - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _joinPath($dir, $name) { - $sql = 'SELECT id FROM '.$this->tbf.' WHERE parent_id=\''.$dir.'\' AND name=\''.$this->db->real_escape_string($name).'\''; + /** + * Return file name + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _basename($path) + { + return ($stat = $this->stat($path)) ? $stat['name'] : false; + } - if (($res = $this->query($sql)) && ($r = $res->fetch_assoc())) { - $this->updateCache($r['id'], $this->_stat($r['id'])); - return $r['id']; - } - return -1; - } - - /** - * Return normalized path, this works the same as os.path.normpath() in Python - * - * @param string $path path - * @return string - * @author Troex Nevelin - **/ - protected function _normpath($path) { - return $path; - } - - /** - * Return file path related to root dir - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _relpath($path) { - return $path; - } - - /** - * Convert path related to root dir into real path - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _abspath($path) { - return $path; - } - - /** - * Return fake path started from root dir - * - * @param string $path file path - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _path($path) { - if (($file = $this->stat($path)) == false) { - return ''; - } - - $parentsIds = $this->getParents($path); - $path = ''; - foreach ($parentsIds as $id) { - $dir = $this->stat($id); - $path .= $dir['name'].$this->separator; - } - return $path.$file['name']; - } - - /** - * Return true if $path is children of $parent - * - * @param string $path path to check - * @param string $parent parent path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _inpath($path, $parent) { - return $path == $parent - ? true - : in_array($parent, $this->getParents($path)); - } - - /***************** file stat ********************/ - /** - * Return stat for given path. - * Stat contains following fields: - * - (int) size file size in b. required - * - (int) ts file modification time in unix time. required - * - (string) mime mimetype. required for folders, others - optionally - * - (bool) read read permissions. required - * - (bool) write write permissions. required - * - (bool) locked is object locked. optionally - * - (bool) hidden is object hidden. optionally - * - (string) alias for symlinks - link target path relative to root path. optionally - * - (string) target for symlinks - link target path. optionally - * - * If file does not exists - returns empty array or false. - * - * @param string $path file path - * @return array|false - * @author Dmitry (dio) Levashov - **/ - protected function _stat($path) { - $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs - FROM '.$this->tbf.' AS f - LEFT JOIN '.$this->tbf.' AS ch ON ch.parent_id=f.id AND ch.mime=\'directory\' - WHERE f.id=\''.$path.'\' + /** + * Join dir name and file name and return full path + * + * @param string $dir + * @param string $name + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _joinPath($dir, $name) + { + $sql = 'SELECT id FROM ' . $this->tbf . ' WHERE parent_id=\'' . $dir . '\' AND name=\'' . $this->db->real_escape_string($name) . '\''; + + if (($res = $this->query($sql)) && ($r = $res->fetch_assoc())) { + $this->updateCache($r['id'], $this->_stat($r['id'])); + return $r['id']; + } + return -1; + } + + /** + * Return normalized path, this works the same as os.path.normpath() in Python + * + * @param string $path path + * + * @return string + * @author Troex Nevelin + **/ + protected function _normpath($path) + { + return $path; + } + + /** + * Return file path related to root dir + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _relpath($path) + { + return $path; + } + + /** + * Convert path related to root dir into real path + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _abspath($path) + { + return $path; + } + + /** + * Return fake path started from root dir + * + * @param string $path file path + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _path($path) + { + if (($file = $this->stat($path)) == false) { + return ''; + } + + $parentsIds = $this->getParents($path); + $path = ''; + foreach ($parentsIds as $id) { + $dir = $this->stat($id); + $path .= $dir['name'] . $this->separator; + } + return $path . $file['name']; + } + + /** + * Return true if $path is children of $parent + * + * @param string $path path to check + * @param string $parent parent path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _inpath($path, $parent) + { + return $path == $parent + ? true + : in_array($parent, $this->getParents($path)); + } + + /***************** file stat ********************/ + /** + * Return stat for given path. + * Stat contains following fields: + * - (int) size file size in b. required + * - (int) ts file modification time in unix time. required + * - (string) mime mimetype. required for folders, others - optionally + * - (bool) read read permissions. required + * - (bool) write write permissions. required + * - (bool) locked is object locked. optionally + * - (bool) hidden is object hidden. optionally + * - (string) alias for symlinks - link target path relative to root path. optionally + * - (string) target for symlinks - link target path. optionally + * If file does not exists - returns empty array or false. + * + * @param string $path file path + * + * @return array|false + * @author Dmitry (dio) Levashov + **/ + protected function _stat($path) + { + $sql = 'SELECT f.id, f.parent_id, f.name, f.size, f.mtime AS ts, f.mime, f.read, f.write, f.locked, f.hidden, f.width, f.height, IF(ch.id, 1, 0) AS dirs + FROM ' . $this->tbf . ' AS f + LEFT JOIN ' . $this->tbf . ' AS ch ON ch.parent_id=f.id AND ch.mime=\'directory\' + WHERE f.id=\'' . $path . '\' GROUP BY f.id, ch.id'; - $res = $this->query($sql); - - if ($res) { - $stat = $res->fetch_assoc(); - if ($stat['id'] == $this->root) { - $this->rootHasParent = true; - $stat['parent_id'] = ''; - } - if ($stat['parent_id']) { - $stat['phash'] = $this->encode($stat['parent_id']); - } - if ($stat['mime'] == 'directory') { - unset($stat['width']); - unset($stat['height']); - $stat['size'] = 0; - } else { - if (!$stat['mime']) { - unset($stat['mime']); - } - unset($stat['dirs']); - } - unset($stat['id']); - unset($stat['parent_id']); - return $stat; - - } - return array(); - } - - /** - * Return true if path is dir and has at least one childs directory - * - * @param string $path dir path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _subdirs($path) { - return ($stat = $this->stat($path)) && isset($stat['dirs']) ? $stat['dirs'] : false; - } - - /** - * Return object width and height - * Usualy used for images, but can be realize for video etc... - * - * @param string $path file path - * @param string $mime file mime type - * @return string - * @author Dmitry (dio) Levashov - **/ - protected function _dimensions($path, $mime) { - return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'].'x'.$stat['height'] : ''; - } - - /******************** file/dir content *********************/ - - /** - * Return files list in directory. - * - * @param string $path dir path - * @return array - * @author Dmitry (dio) Levashov - **/ - protected function _scandir($path) { - return isset($this->dirsCache[$path]) - ? $this->dirsCache[$path] - : $this->cacheDir($path); - } - - /** - * Open file and return file pointer - * - * @param string $path file path - * @param string $mode open file mode (ignored in this driver) - * @return resource|false - * @author Dmitry (dio) Levashov - **/ - protected function _fopen($path, $mode='rb') { - $fp = $this->tmbPath - ? fopen($this->getTempFile($path), 'w+') - : $this->tmpfile(); - - - if ($fp) { - if (($res = $this->query('SELECT content FROM '.$this->tbf.' WHERE id=\''.$path.'\'')) - && ($r = $res->fetch_assoc())) { - fwrite($fp, $r['content']); - rewind($fp); - return $fp; - } else { - $this->_fclose($fp, $path); - } - } - - return false; - } + $res = $this->query($sql); - /** - * Close opened file - * - * @param resource $fp file pointer - * @param string $path - * @return void - * @author Dmitry (dio) Levashov - */ - protected function _fclose($fp, $path='') { - is_resource($fp) && fclose($fp); - if ($path) { - $file = $this->getTempFile($path); - is_file($file) && unlink($file); - } - } - - /******************** file/dir manipulations *************************/ - - /** - * Create dir and return created dir path or false on failed - * - * @param string $path parent dir path - * @param string $name new directory name - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - protected function _mkdir($path, $name) { - return $this->make($path, $name, 'directory') ? $this->_joinPath($path, $name) : false; - } - - /** - * Create file and return it's path or false on failed - * - * @param string $path parent dir path - * @param string $name new file name - * @return string|bool - * @author Dmitry (dio) Levashov - **/ - protected function _mkfile($path, $name) { - return $this->make($path, $name, '') ? $this->_joinPath($path, $name) : false; - } + if ($res) { + $stat = $res->fetch_assoc(); + if ($stat['id'] == $this->root) { + $this->rootHasParent = true; + $stat['parent_id'] = ''; + } + if ($stat['parent_id']) { + $stat['phash'] = $this->encode($stat['parent_id']); + } + if ($stat['mime'] == 'directory') { + unset($stat['width']); + unset($stat['height']); + $stat['size'] = 0; + } else { + if (!$stat['mime']) { + unset($stat['mime']); + } + unset($stat['dirs']); + } + unset($stat['id']); + unset($stat['parent_id']); + return $stat; - /** - * Create symlink. FTP driver does not support symlinks. - * - * @param string $target link target - * @param string $path symlink path - * @param string $name - * @return bool - * @author Dmitry (dio) Levashov - */ - protected function _symlink($target, $path, $name) { - return false; - } - - /** - * Copy file into another file - * - * @param string $source source file path - * @param string $targetDir target directory path - * @param string $name new file name - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _copy($source, $targetDir, $name) { - $this->clearcache(); - $id = $this->_joinPath($targetDir, $name); + } + return array(); + } - $sql = $id > 0 - ? sprintf('REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) (SELECT %d, %d, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d)', $this->tbf, $id, $this->_dirname($id), $this->tbf, $source) - : sprintf('INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) SELECT %d, \'%s\', content, size, %d, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d', $this->tbf, $targetDir, $this->db->real_escape_string($name), time(), $this->tbf, $source); + /** + * Return true if path is dir and has at least one childs directory + * + * @param string $path dir path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _subdirs($path) + { + return ($stat = $this->stat($path)) && isset($stat['dirs']) ? $stat['dirs'] : false; + } - return $this->query($sql); - } + /** + * Return object width and height + * Usualy used for images, but can be realize for video etc... + * + * @param string $path file path + * @param string $mime file mime type + * + * @return string + * @author Dmitry (dio) Levashov + **/ + protected function _dimensions($path, $mime) + { + return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'] . 'x' . $stat['height'] : ''; + } - /** - * Move file into another parent dir. - * Return new file path or false. - * - * @param string $source source file path - * @param $targetDir - * @param string $name file name - * @return bool|string - * @internal param string $target target dir path - * @author Dmitry (dio) Levashov - */ - protected function _move($source, $targetDir, $name) { - $sql = 'UPDATE %s SET parent_id=%d, name=\'%s\' WHERE id=%d LIMIT 1'; - $sql = sprintf($sql, $this->tbf, $targetDir, $this->db->real_escape_string($name), $source); - return $this->query($sql) && $this->db->affected_rows > 0 ? $source : false; - } - - /** - * Remove file - * - * @param string $path file path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _unlink($path) { - return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime!=\'directory\' LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows; - } + /******************** file/dir content *********************/ - /** - * Remove dir - * - * @param string $path dir path - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _rmdir($path) { - return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime=\'directory\' LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows; - } + /** + * Return files list in directory. + * + * @param string $path dir path + * + * @return array + * @author Dmitry (dio) Levashov + **/ + protected function _scandir($path) + { + return isset($this->dirsCache[$path]) + ? $this->dirsCache[$path] + : $this->cacheDir($path); + } - /** - * undocumented function - * - * @param $path - * @param $fp - * @author Dmitry Levashov - */ - protected function _setContent($path, $fp) { - elFinder::rewind($fp); - $fstat = fstat($fp); - $size = $fstat['size']; - - - } - - /** - * Create new file and write into it from file pointer. - * Return new file path or false on error. - * - * @param resource $fp file pointer - * @param string $dir target dir path - * @param string $name file name - * @param array $stat file stat (required by some virtual fs) - * @return bool|string - * @author Dmitry (dio) Levashov - **/ - protected function _save($fp, $dir, $name, $stat) { - $this->clearcache(); - - $mime = $stat['mime']; - $w = !empty($stat['width']) ? $stat['width'] : 0; - $h = !empty($stat['height']) ? $stat['height'] : 0; - - $id = $this->_joinPath($dir, $name); - elFinder::rewind($fp); - $stat = fstat($fp); - $size = $stat['size']; - - if (($tmpfile = tempnam($this->tmpPath, $this->id))) { - if (($trgfp = fopen($tmpfile, 'wb')) == false) { - unlink($tmpfile); - } else { - while (!feof($fp)) { - fwrite($trgfp, fread($fp, 8192)); - } - fclose($trgfp); - chmod($tmpfile, 0644); - - $sql = $id > 0 - ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, \'%s\', LOAD_FILE(\'%s\'), %d, %d, \'%s\', %d, %d)' - : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, \'%s\', LOAD_FILE(\'%s\'), %d, %d, \'%s\', %d, %d)'; - $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->loadFilePath($tmpfile), $size, time(), $mime, $w, $h); + /** + * Open file and return file pointer + * + * @param string $path file path + * @param string $mode open file mode (ignored in this driver) + * + * @return resource|false + * @author Dmitry (dio) Levashov + **/ + protected function _fopen($path, $mode = 'rb') + { + $fp = $this->tmbPath + ? fopen($this->getTempFile($path), 'w+') + : $this->tmpfile(); - $res = $this->query($sql); - unlink($tmpfile); - - if ($res) { - return $id > 0 ? $id : $this->db->insert_id; - } - } - } - - $content = ''; - elFinder::rewind($fp); - while (!feof($fp)) { - $content .= fread($fp, 8192); - } - - $sql = $id > 0 - ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES ('.$id.', %d, \'%s\', \'%s\', %d, %d, \'%s\', %d, %d)' - : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, \'%s\', \'%s\', %d, %d, \'%s\', %d, %d)'; - $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->db->real_escape_string($content), $size, time(), $mime, $w, $h); - - unset($content); + if ($fp) { + if (($res = $this->query('SELECT content FROM ' . $this->tbf . ' WHERE id=\'' . $path . '\'')) + && ($r = $res->fetch_assoc())) { + fwrite($fp, $r['content']); + rewind($fp); + return $fp; + } else { + $this->_fclose($fp, $path); + } + } - if ($this->query($sql)) { - return $id > 0 ? $id : $this->db->insert_id; - } - - return false; - } - - /** - * Get file contents - * - * @param string $path file path - * @return string|false - * @author Dmitry (dio) Levashov - **/ - protected function _getContents($path) { - return ($res = $this->query(sprintf('SELECT content FROM %s WHERE id=%d', $this->tbf, $path))) && ($r = $res->fetch_assoc()) ? $r['content'] : false; - } - - /** - * Write a string to a file - * - * @param string $path file path - * @param string $content new file content - * @return bool - * @author Dmitry (dio) Levashov - **/ - protected function _filePutContents($path, $content) { - return $this->query(sprintf('UPDATE %s SET content=\'%s\', size=%d, mtime=%d WHERE id=%d LIMIT 1', $this->tbf, $this->db->real_escape_string($content), strlen($content), time(), $path)); - } + return false; + } - /** - * Detect available archivers - * - * @return void - **/ - protected function _checkArchivers() { - return; - } + /** + * Close opened file + * + * @param resource $fp file pointer + * @param string $path + * + * @return void + * @author Dmitry (dio) Levashov + */ + protected function _fclose($fp, $path = '') + { + is_resource($fp) && fclose($fp); + if ($path) { + $file = $this->getTempFile($path); + is_file($file) && unlink($file); + } + } - /** - * chmod implementation - * - * @param string $path - * @param string $mode - * @return bool - */ - protected function _chmod($path, $mode) { - return false; - } + /******************** file/dir manipulations *************************/ - /** - * Unpack archive - * - * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) - * @return void - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin - **/ - protected function _unpack($path, $arc) { - return; - } + /** + * Create dir and return created dir path or false on failed + * + * @param string $path parent dir path + * @param string $name new directory name + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + protected function _mkdir($path, $name) + { + return $this->make($path, $name, 'directory') ? $this->_joinPath($path, $name) : false; + } + + /** + * Create file and return it's path or false on failed + * + * @param string $path parent dir path + * @param string $name new file name + * + * @return string|bool + * @author Dmitry (dio) Levashov + **/ + protected function _mkfile($path, $name) + { + return $this->make($path, $name, '') ? $this->_joinPath($path, $name) : false; + } + + /** + * Create symlink. FTP driver does not support symlinks. + * + * @param string $target link target + * @param string $path symlink path + * @param string $name + * + * @return bool + * @author Dmitry (dio) Levashov + */ + protected function _symlink($target, $path, $name) + { + return false; + } + + /** + * Copy file into another file + * + * @param string $source source file path + * @param string $targetDir target directory path + * @param string $name new file name + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _copy($source, $targetDir, $name) + { + $this->clearcache(); + $id = $this->_joinPath($targetDir, $name); + + $sql = $id > 0 + ? sprintf('REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) (SELECT %d, %d, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d)', $this->tbf, $id, $this->_dirname($id), $this->tbf, $source) + : sprintf('INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height, `read`, `write`, `locked`, `hidden`) SELECT %d, \'%s\', content, size, %d, mime, width, height, `read`, `write`, `locked`, `hidden` FROM %s WHERE id=%d', $this->tbf, $targetDir, $this->db->real_escape_string($name), time(), $this->tbf, $source); + + return $this->query($sql); + } + + /** + * Move file into another parent dir. + * Return new file path or false. + * + * @param string $source source file path + * @param $targetDir + * @param string $name file name + * + * @return bool|string + * @internal param string $target target dir path + * @author Dmitry (dio) Levashov + */ + protected function _move($source, $targetDir, $name) + { + $sql = 'UPDATE %s SET parent_id=%d, name=\'%s\' WHERE id=%d LIMIT 1'; + $sql = sprintf($sql, $this->tbf, $targetDir, $this->db->real_escape_string($name), $source); + return $this->query($sql) && $this->db->affected_rows > 0 ? $source : false; + } + + /** + * Remove file + * + * @param string $path file path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _unlink($path) + { + return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime!=\'directory\' LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows; + } + + /** + * Remove dir + * + * @param string $path dir path + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _rmdir($path) + { + return $this->query(sprintf('DELETE FROM %s WHERE id=%d AND mime=\'directory\' LIMIT 1', $this->tbf, $path)) && $this->db->affected_rows; + } + + /** + * undocumented function + * + * @param $path + * @param $fp + * + * @author Dmitry Levashov + */ + protected function _setContent($path, $fp) + { + elFinder::rewind($fp); + $fstat = fstat($fp); + $size = $fstat['size']; + + + } + + /** + * Create new file and write into it from file pointer. + * Return new file path or false on error. + * + * @param resource $fp file pointer + * @param string $dir target dir path + * @param string $name file name + * @param array $stat file stat (required by some virtual fs) + * + * @return bool|string + * @author Dmitry (dio) Levashov + **/ + protected function _save($fp, $dir, $name, $stat) + { + $this->clearcache(); + + $mime = $stat['mime']; + $w = !empty($stat['width']) ? $stat['width'] : 0; + $h = !empty($stat['height']) ? $stat['height'] : 0; + + $id = $this->_joinPath($dir, $name); + elFinder::rewind($fp); + $stat = fstat($fp); + $size = $stat['size']; + + if (($tmpfile = tempnam($this->tmpPath, $this->id))) { + if (($trgfp = fopen($tmpfile, 'wb')) == false) { + unlink($tmpfile); + } else { + while (!feof($fp)) { + fwrite($trgfp, fread($fp, 8192)); + } + fclose($trgfp); + chmod($tmpfile, 0644); + + $sql = $id > 0 + ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES (' . $id . ', %d, \'%s\', LOAD_FILE(\'%s\'), %d, %d, \'%s\', %d, %d)' + : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, \'%s\', LOAD_FILE(\'%s\'), %d, %d, \'%s\', %d, %d)'; + $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->loadFilePath($tmpfile), $size, time(), $mime, $w, $h); + + $res = $this->query($sql); + unlink($tmpfile); + + if ($res) { + return $id > 0 ? $id : $this->db->insert_id; + } + } + } + + + $content = ''; + elFinder::rewind($fp); + while (!feof($fp)) { + $content .= fread($fp, 8192); + } + + $sql = $id > 0 + ? 'REPLACE INTO %s (id, parent_id, name, content, size, mtime, mime, width, height) VALUES (' . $id . ', %d, \'%s\', \'%s\', %d, %d, \'%s\', %d, %d)' + : 'INSERT INTO %s (parent_id, name, content, size, mtime, mime, width, height) VALUES (%d, \'%s\', \'%s\', %d, %d, \'%s\', %d, %d)'; + $sql = sprintf($sql, $this->tbf, $dir, $this->db->real_escape_string($name), $this->db->real_escape_string($content), $size, time(), $mime, $w, $h); + + unset($content); + + if ($this->query($sql)) { + return $id > 0 ? $id : $this->db->insert_id; + } + + return false; + } + + /** + * Get file contents + * + * @param string $path file path + * + * @return string|false + * @author Dmitry (dio) Levashov + **/ + protected function _getContents($path) + { + return ($res = $this->query(sprintf('SELECT content FROM %s WHERE id=%d', $this->tbf, $path))) && ($r = $res->fetch_assoc()) ? $r['content'] : false; + } + + /** + * Write a string to a file + * + * @param string $path file path + * @param string $content new file content + * + * @return bool + * @author Dmitry (dio) Levashov + **/ + protected function _filePutContents($path, $content) + { + return $this->query(sprintf('UPDATE %s SET content=\'%s\', size=%d, mtime=%d WHERE id=%d LIMIT 1', $this->tbf, $this->db->real_escape_string($content), strlen($content), time(), $path)); + } + + /** + * Detect available archivers + * + * @return void + **/ + protected function _checkArchivers() + { + return; + } + + /** + * chmod implementation + * + * @param string $path + * @param string $mode + * + * @return bool + */ + protected function _chmod($path, $mode) + { + return false; + } + + /** + * Unpack archive + * + * @param string $path archive path + * @param array $arc archiver command and arguments (same as in $this->archivers) + * + * @return void + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + **/ + protected function _unpack($path, $arc) + { + return; + } + + /** + * Extract files from archive + * + * @param string $path archive path + * @param array $arc archiver command and arguments (same as in $this->archivers) + * + * @return true + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + **/ + protected function _extract($path, $arc) + { + return false; + } + + /** + * Create archive and return its path + * + * @param string $dir target dir + * @param array $files files names list + * @param string $name archive name + * @param array $arc archiver options + * + * @return string|bool + * @author Dmitry (dio) Levashov, + * @author Alexey Sukhotin + **/ + protected function _archive($dir, $files, $name, $arc) + { + return false; + } - /** - * Extract files from archive - * - * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) - * @return true - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - **/ - protected function _extract($path, $arc) { - return false; - } - - /** - * Create archive and return its path - * - * @param string $dir target dir - * @param array $files files names list - * @param string $name archive name - * @param array $arc archiver options - * @return string|bool - * @author Dmitry (dio) Levashov, - * @author Alexey Sukhotin - **/ - protected function _archive($dir, $files, $name, $arc) { - return false; - } - } // END class diff --git a/php/elFinderVolumeOneDrive.class.php b/php/elFinderVolumeOneDrive.class.php index 5b1442503..8fb20f397 100644 --- a/php/elFinderVolumeOneDrive.class.php +++ b/php/elFinderVolumeOneDrive.class.php @@ -78,7 +78,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver /** * Current token expires - * + * * @var integer **/ private $expires; @@ -118,10 +118,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver /** * Obtains a new access token from OAuth. This token is valid for one hour. * - * @param $client_id - * @param $client_secret - * @param string $code The code returned by OneDrive after + * @param $client_id + * @param $client_secret + * @param string $code The code returned by OneDrive after * successful log in + * * @return object|string * @throws Exception Thrown if the redirect URI of this Client instance's * state is not set @@ -141,26 +142,26 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $curl = curl_init(); $fields = http_build_query( - array( - 'client_id' => $client_id, - 'redirect_uri' => elFinder::getConnectorUrl(), - 'client_secret' => $client_secret, - 'code' => $code, - 'grant_type' => 'authorization_code', - ) - ); + array( + 'client_id' => $client_id, + 'redirect_uri' => elFinder::getConnectorUrl(), + 'client_secret' => $client_secret, + 'code' => $code, + 'grant_type' => 'authorization_code', + ) + ); curl_setopt_array($curl, array( - // General options. - CURLOPT_RETURNTRANSFER => true, - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => $fields, + // General options. + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $fields, - CURLOPT_HTTPHEADER => array( - 'Content-Length: '.strlen($fields), - ), + CURLOPT_HTTPHEADER => array( + 'Content-Length: ' . strlen($fields), + ), - CURLOPT_URL => $url, + CURLOPT_URL => $url, )); $result = curl_exec($curl); @@ -168,7 +169,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if (false === $result) { if (curl_errno($curl)) { throw new \Exception('curl_setopt_array() failed: ' - .curl_error($curl)); + . curl_error($curl)); } else { throw new \Exception('curl_setopt_array(): empty response'); } @@ -181,9 +182,9 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver throw new \Exception('json_decode() failed'); } - return (object) array( - 'expires' => time() + $decoded->expires_in - 30, - 'data' => $decoded, + return (object)array( + 'expires' => time() + $decoded->expires_in - 30, + 'data' => $decoded, ); } @@ -217,22 +218,22 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $curl = curl_init(); curl_setopt_array($curl, array( - // General options. - CURLOPT_RETURNTRANSFER => true, - CURLOPT_POST => true, // i am sending post data - CURLOPT_POSTFIELDS => 'client_id='.urlencode($this->options['client_id']) - .'&client_secret='.urlencode($this->options['client_secret']) - .'&grant_type=refresh_token' - .'&refresh_token='.urlencode($token->data->refresh_token), + // General options. + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, // i am sending post data + CURLOPT_POSTFIELDS => 'client_id=' . urlencode($this->options['client_id']) + . '&client_secret=' . urlencode($this->options['client_secret']) + . '&grant_type=refresh_token' + . '&refresh_token=' . urlencode($token->data->refresh_token), - CURLOPT_URL => $url, - )); + CURLOPT_URL => $url, + )); $result = curl_exec($curl); if (!$result) { if (curl_errno($curl)) { - throw new \Exception('curl_setopt_array() failed: '.curl_error($curl)); + throw new \Exception('curl_setopt_array() failed: ' . curl_error($curl)); } else { throw new \Exception('curl_setopt_array(): empty response'); } @@ -249,10 +250,10 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver throw new \Exception(elFinder::ERROR_REAUTH_REQUIRE); } - $token = (object) array( - 'expires' => time() + $decoded->expires_in - 30, - 'data' => $decoded, - ); + $token = (object)array( + 'expires' => time() + $decoded->expires_in - 30, + 'data' => $decoded, + ); $this->session->set('OneDriveTokens', $token); $this->options['accessToken'] = json_encode($token); @@ -284,7 +285,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $paths = explode('/', trim($path, '/')); $id = array_pop($paths); if ($paths) { - $parent = '/'.implode('/', $paths); + $parent = '/' . implode('/', $paths); $pid = array_pop($paths); } else { $pid = 'root'; @@ -305,12 +306,12 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $curl = curl_init($url); $defaultOptions = array( - // General options. - CURLOPT_RETURNTRANSFER => true, - CURLOPT_HTTPHEADER => array( - 'Content-Type: application/json', - 'Authorization: Bearer '.$this->token->data->access_token, - ), + // General options. + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => array( + 'Content-Type: application/json', + 'Authorization: Bearer ' . $this->token->data->access_token, + ), ); curl_setopt_array($curl, $defaultOptions); @@ -322,8 +323,8 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Creates a base cURL object which is compatible with the OneDrive API. * * @param string $path The path of the API call (eg. me/skydrive) + * @param bool $contents * - * @param bool $contents * @return resource A compatible cURL object * @throws elFinderAbortException */ @@ -340,7 +341,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if (isset($result->value)) { $res = $result->value; unset($result->value); - $result = (array) $result; + $result = (array)$result; if (!empty($result['@odata.nextLink'])) { $nextRes = $this->_od_createCurl($result['@odata.nextLink'], false); if (is_array($nextRes)) { @@ -358,10 +359,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver /** * Drive query and fetchAll. * - * @param $itemId - * @param bool $fetch_self - * @param bool $recursive + * @param $itemId + * @param bool $fetch_self + * @param bool $recursive * @param array $options + * * @return object|array * @throws elFinderAbortException */ @@ -376,14 +378,14 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if ($fetch_self == true) { $path = $itemId; } else { - $path = $itemId.'/children'; + $path = $itemId . '/children'; } if (isset($options['query'])) { - $path .= '?'.http_build_query($options['query']); + $path .= '?' . http_build_query($options['query']); } - $url = self::API_URL.$path; + $url = self::API_URL . $path; $res = $this->_od_createCurl($url); if (!$fetch_self && $recursive && is_array($res)) { @@ -406,7 +408,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param object $raw line from ftp_rawlist() output * * @return array - * * @author Dmitry Levashov **/ protected function _od_parseRaw($raw) @@ -433,7 +434,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if (isset($raw->file->mimeType)) { $stat['mime'] = $raw->file->mimeType; } - $stat['size'] = (int) $raw->size; + $stat['size'] = (int)$raw->size; if (!$this->disabledGetUrl) { $stat['url'] = '1'; } @@ -476,6 +477,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Get thumbnail from OneDrive.com. * * @param string $path + * * @return string | boolean */ protected function _od_getThumbnail($path) @@ -483,7 +485,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver list(, $itemId) = $this->_od_splitPath($path); try { - $url = self::API_URL.$itemId.'/thumbnails/0/medium/content'; + $url = self::API_URL . $itemId . '/thumbnails/0/medium/content'; return $this->_od_createCurl($url, $contents = true); } catch (Exception $e) { @@ -513,9 +515,9 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver // create upload session if ($itemId) { - $url = self::API_URL.$itemId.'/createUploadSession'; + $url = self::API_URL . $itemId . '/createUploadSession'; } else { - $url = self::API_URL.$parentId.':/'.rawurlencode($name).':/createUploadSession'; + $url = self::API_URL . $parentId . ':/' . rawurlencode($name) . ':/createUploadSession'; } $curl = $this->_od_prepareCurl($url); curl_setopt_array($curl, array( @@ -530,7 +532,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver throw new Exception($sess->error->message); } $next = strlen($send); - $range = '0-'.($next - 1).'/'.$size; + $range = '0-' . ($next - 1) . '/' . $size; } else { throw new Exception('API response can not be obtained.'); } @@ -550,8 +552,8 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver CURLOPT_RETURNTRANSFER => true, CURLOPT_INFILE => $putFp, CURLOPT_HTTPHEADER => array( - 'Content-Length: '.strlen($send), - 'Content-Range: bytes '.$range, + 'Content-Length: ' . strlen($send), + 'Content-Range: bytes ' . $range, ), ); curl_setopt_array($curl, $options); @@ -573,7 +575,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver throw new Exception('Data can not be acquired from the source.'); } $next += strlen($send); - $range = $_next.'-'.($next - 1).'/'.$size; + $range = $_next . '-' . ($next - 1) . '/' . $size; $retry = 0; } else { if (++$retry > 3) { @@ -593,7 +595,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver throw new Exception('An error occurred in the uploadSession API call.'); } } catch (Exception $e) { - return $this->setError('OneDrive error: '.$e->getMessage()); + return $this->setError('OneDrive error: ' . $e->getMessage()); } } @@ -642,7 +644,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Call from elFinder::netmout() before volume->mount(). * * @return array - * * @author Naoki Sawada * @author Raja Sharma updating for OneDrive **/ @@ -677,27 +678,27 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver try { // Obtain the token using the code received by the OneDrive API $this->session->set('OneDriveTokens', - $this->_od_obtainAccessToken($options['client_id'], $options['client_secret'], $_GET['code'])); + $this->_od_obtainAccessToken($options['client_id'], $options['client_secret'], $_GET['code'])); $out = array( - 'node' => $options['id'], - 'json' => '{"protocol": "onedrive", "mode": "done", "reset": 1}', - 'bind' => 'netmount', + 'node' => $options['id'], + 'json' => '{"protocol": "onedrive", "mode": "done", "reset": 1}', + 'bind' => 'netmount', ); return array('exit' => 'callback', 'out' => $out); } catch (Exception $e) { $out = array( - 'node' => $options['id'], - 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED.' '.$e->getMessage())), + 'node' => $options['id'], + 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED . ' ' . $e->getMessage())), ); return array('exit' => 'callback', 'out' => $out); } } elseif (!empty($_GET['error'])) { $out = array( - 'node' => $options['id'], - 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED)), + 'node' => $options['id'], + 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED)), ); return array('exit' => 'callback', 'out' => $out); @@ -737,17 +738,17 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET; foreach ($this->ARGS as $k => $v) { if (!in_array($k, $innerKeys)) { - $cdata .= '&'.$k.'='.rawurlencode($v); + $cdata .= '&' . $k . '=' . rawurlencode($v); } } if (empty($options['url'])) { $options['url'] = elFinder::getConnectorUrl(); } $callback = $options['url'] - .'?cmd=netmount&protocol=onedrive&host=onedrive.com&user=init&pass=return&node='.$options['id'].$cdata; + . '?cmd=netmount&protocol=onedrive&host=onedrive.com&user=init&pass=return&node=' . $options['id'] . $cdata; try { - $this->session->set('OneDriveTokens', (object) array('token' => null)); + $this->session->set('OneDriveTokens', (object)array('token' => null)); $offline = ''; // Gets a log in URL with sufficient privileges from the OneDrive API @@ -755,21 +756,21 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $offline = ' offline_access'; } - $redirect_uri = $options['url'].'/netmount/onedrive/1'; + $redirect_uri = $options['url'] . '/netmount/onedrive/1'; $url = self::AUTH_URL - .'?client_id='.urlencode($options['client_id']) - .'&scope='.urlencode('files.readwrite.all'.$offline) - .'&response_type=code' - .'&redirect_uri='.urlencode($redirect_uri); + . '?client_id=' . urlencode($options['client_id']) + . '&scope=' . urlencode('files.readwrite.all' . $offline) + . '&response_type=code' + . '&redirect_uri=' . urlencode($redirect_uri); - $url .= '&oauth_callback='.rawurlencode($callback); + $url .= '&oauth_callback=' . rawurlencode($callback); } catch (Exception $e) { return array('exit' => true, 'body' => '{msg:errAccess}'); } - $html = ''; + $html = ''; $html .= ''; return array('exit' => true, 'body' => $html); @@ -790,11 +791,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $folders = ['root' => 'My OneDrive'] + $folders; $folders = json_encode($folders); - $expires = empty($this->token->data->refresh_token) ? (int) $this->token->expires : 0; - $json = '{"protocol": "onedrive", "mode": "done", "folders": '.$folders.', "expires": '.$expires.'}'; + $expires = empty($this->token->data->refresh_token) ? (int)$this->token->expires : 0; + $json = '{"protocol": "onedrive", "mode": "done", "folders": ' . $folders . ', "expires": ' . $expires . '}'; $html = 'OneDrive.com'; $html .= ''; return array('exit' => true, 'body' => $html); @@ -828,7 +829,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver */ public function netunmount($netVolumes, $key) { - if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/').DIRECTORY_SEPARATOR.$this->tmbPrefix.'*.png')) { + if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->tmbPrefix . '*.png')) { foreach ($tmbs as $file) { unlink($file); } @@ -861,7 +862,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn. * * @return bool - * * @throws elFinderAbortException * @author Dmitry (dio) Levashov * @author Cem (DiscoFever) @@ -882,7 +882,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver return $this->setError($e->getMessage()); } - $this->expires = empty($this->token->data->refresh_token) ? (int) $this->token->expires : 0; + $this->expires = empty($this->token->data->refresh_token) ? (int)$this->token->expires : 0; if (empty($options['netkey'])) { // make net mount key @@ -903,12 +903,12 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if (empty($this->options['alias'])) { $this->options['alias'] = ($this->options['path'] === '/') ? $this->options['root'] : - $this->_od_query(basename($this->options['path']), $fetch_self = true)->name.'@OneDrive'; + $this->_od_query(basename($this->options['path']), $fetch_self = true)->name . '@OneDrive'; } $this->rootName = $this->options['alias']; - $this->tmbPrefix = 'onedrive'.base_convert($this->netMountKey, 10, 32); + $this->tmbPrefix = 'onedrive' . base_convert($this->netMountKey, 10, 32); if (!empty($this->options['tmpPath'])) { if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) { @@ -976,8 +976,8 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver { list($pid, $name) = $this->_od_splitPath($path); - $raw = $this->_od_query($pid.'/children/'.rawurlencode($name), true); - return $raw? $this->_od_parseRaw($raw) : false; + $raw = $this->_od_query($pid . '/children/' . rawurlencode($name), true); + return $raw ? $this->_od_parseRaw($raw) : false; } /** @@ -1024,12 +1024,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Copy file/recursive copy dir only in current volume. * Return new file path or false. * - * @param string $src source path - * @param string $dst destination dir path + * @param string $src source path + * @param string $dst destination dir path * @param string $name new file name (optionaly) * * @return string|false - * * @throws elFinderAbortException * @author Dmitry (dio) Levashov * @author Naoki Sawada @@ -1056,11 +1055,10 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver /** * Remove file/ recursive remove dir. * - * @param string $path file path - * @param bool $force try to remove even if file locked + * @param string $path file path + * @param bool $force try to remove even if file locked * * @return bool - * * @throws elFinderAbortException * @author Dmitry (dio) Levashov * @author Naoki Sawada @@ -1099,9 +1097,9 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Create thumnbnail and return it's URL on success. * * @param string $path file path - * @param $stat - * @return string|false + * @param $stat * + * @return string|false * @throws ImagickException * @throws elFinderAbortException * @author Dmitry (dio) Levashov @@ -1135,7 +1133,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver } $name = $this->tmbname($stat); - $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; + $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; // copy image into tmbPath so some drivers does not store files on local fs if (!$data = $this->_od_getThumbnail($path)) { @@ -1191,12 +1189,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param array $stat file stat * * @return string - * * @author Dmitry (dio) Levashov **/ protected function tmbname($stat) { - return $this->tmbPrefix.$stat['rev'].$stat['ts'].'.png'; + return $this->tmbPrefix . $stat['rev'] . $stat['ts'] . '.png'; } /** @@ -1206,12 +1203,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param array $options options * * @return string - * * @author Naoki Sawada **/ public function getContentUrl($hash, $options = array()) { - if (! empty($options['onetime']) && $this->options['onetimeUrl']) { + if (!empty($options['onetime']) && $this->options['onetimeUrl']) { return parent::getContentUrl($hash, $options); } if (!empty($options['temporary'])) { @@ -1227,8 +1223,8 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver list(, $itemId) = $this->_od_splitPath($path); try { - $url = self::API_URL.$itemId.'/createLink'; - $data = (object) array( + $url = self::API_URL . $itemId . '/createLink'; + $data = (object)array( 'type' => 'embed', 'scope' => 'anonymous', ); @@ -1244,7 +1240,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $result = json_decode($result); if (isset($result->link)) { list(, $res) = explode('?', $result->link->webUrl); - $res = 'https://onedrive.live.com/download.aspx?'.$res; + $res = 'https://onedrive.live.com/download.aspx?' . $res; } } } catch (Exception $e) { @@ -1263,7 +1259,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _dirname($path) @@ -1279,7 +1274,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _basename($path) @@ -1296,7 +1290,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $name * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _joinPath($dir, $name) @@ -1305,7 +1298,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $dir = ''; } - return $this->_normpath($dir.'/'.$name); + return $this->_normpath($dir . '/' . $name); } /** @@ -1314,7 +1307,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path path * * @return string - * * @author Troex Nevelin **/ protected function _normpath($path) @@ -1322,7 +1314,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if (DIRECTORY_SEPARATOR !== '/') { $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); } - $path = '/'.ltrim($path, '/'); + $path = '/' . ltrim($path, '/'); return $path; } @@ -1333,7 +1325,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _relpath($path) @@ -1347,7 +1338,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _abspath($path) @@ -1361,12 +1351,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path file path * * @return string - * * @author Dmitry (dio) Levashov **/ protected function _path($path) { - return $this->rootName.$this->_normpath(substr($path, strlen($this->root))); + return $this->rootName . $this->_normpath(substr($path, strlen($this->root))); } /** @@ -1376,12 +1365,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $parent parent path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _inpath($path, $parent) { - return $path == $parent || strpos($path, $parent.'/') === 0; + return $path == $parent || strpos($path, $parent . '/') === 0; } /***************** file stat ********************/ @@ -1397,13 +1385,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * - (bool) hidden is object hidden. optionally * - (string) alias for symlinks - link target path relative to root path. optionally * - (string) target for symlinks - link target path. optionally. - * * If file does not exists - returns empty array or false. * * @param string $path file path * * @return array|false - * * @author Dmitry (dio) Levashov **/ protected function _stat($path) @@ -1425,7 +1411,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path dir path * * @return bool - * * @throws elFinderAbortException * @author Dmitry (dio) Levashov */ @@ -1433,7 +1418,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver { list(, $itemId) = $this->_od_splitPath($path); - return (bool) $this->_od_query($itemId, false, false, array( + return (bool)$this->_od_query($itemId, false, false, array( 'query' => array( 'top' => 1, 'select' => 'id', @@ -1450,7 +1435,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $mime file mime type * * @return string - * * @throws elFinderAbortException * @author Dmitry (dio) Levashov */ @@ -1478,19 +1462,19 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver ), ); if ($tmbSize) { - $tmb = 'c'.$tmbSize.'x'.$tmbSize; - $options['query']['expand'] = 'thumbnails(select='.$tmb.')'; + $tmb = 'c' . $tmbSize . 'x' . $tmbSize; + $options['query']['expand'] = 'thumbnails(select=' . $tmb . ')'; } $raw = $this->_od_query($itemId, true, false, $options); if ($raw && $img = $raw->image) { if (isset($img->width) && isset($img->height)) { - $ret = array('dim' => $img->width.'x'.$img->height); + $ret = array('dim' => $img->width . 'x' . $img->height); if ($tmbSize) { $srcSize = explode('x', $ret['dim']); if (min(($tmbSize / $srcSize[0]), ($tmbSize / $srcSize[1])) < 1) { if (!empty($raw->thumbnails)) { - $tmbArr = (array) $raw->thumbnails[0]; + $tmbArr = (array)$raw->thumbnails[0]; if (!empty($tmbArr[$tmb]->url)) { $ret['url'] = $tmbArr[$tmb]->url; } @@ -1507,7 +1491,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if ($size = @getimagesize($work)) { $cache['width'] = $size[0]; $cache['height'] = $size[1]; - $ret = $size[0].'x'.$size[1]; + $ret = $size[0] . 'x' . $size[1]; } } is_file($work) && @unlink($work); @@ -1523,7 +1507,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path dir path * * @return array - * * @throws elFinderAbortException * @author Dmitry (dio) Levashov * @author Cem (DiscoFever) @@ -1542,7 +1525,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param bool $write open file for writing * * @return resource|false - * * @author Dmitry (dio) Levashov **/ protected function _fopen($path, $mode = 'rb') @@ -1550,8 +1532,8 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if ($mode === 'rb' || $mode === 'r') { list(, $itemId) = $this->_od_splitPath($path); $data = array( - 'target' => self::API_URL.$itemId.'/content', - 'headers' => array('Authorization: Bearer '.$this->token->data->access_token), + 'target' => self::API_URL . $itemId . '/content', + 'headers' => array('Authorization: Bearer ' . $this->token->data->access_token), ); return elFinder::getStreamByUrl($data); @@ -1566,7 +1548,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param resource $fp file pointer * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _fclose($fp, $path = '') @@ -1586,7 +1567,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $name new directory name * * @return string|bool - * * @author Dmitry (dio) Levashov **/ protected function _mkdir($path, $name) @@ -1596,13 +1576,13 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver try { $properties = array( - 'name' => (string) $name, - 'folder' => (object) array(), + 'name' => (string)$name, + 'folder' => (object)array(), ); - $data = (object) $properties; + $data = (object)$properties; - $url = self::API_URL.$parentId.'/children'; + $url = self::API_URL . $parentId . '/children'; $curl = $this->_od_prepareCurl($url); @@ -1618,7 +1598,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver return $this->_joinPath($path, $folder->id); } catch (Exception $e) { - return $this->setError('OneDrive error: '.$e->getMessage()); + return $this->setError('OneDrive error: ' . $e->getMessage()); } } @@ -1629,7 +1609,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $name new file name * * @return string|bool - * * @author Dmitry (dio) Levashov **/ protected function _mkfile($path, $name) @@ -1644,7 +1623,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path symlink path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _symlink($target, $path, $name) @@ -1660,7 +1638,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $name new file name * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _copy($source, $targetDir, $name) @@ -1672,24 +1649,24 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver list(, $parentId) = $this->_od_splitPath($targetDir); list(, $itemId) = $this->_od_splitPath($source); - $url = self::API_URL.$itemId.'/copy'; + $url = self::API_URL . $itemId . '/copy'; $properties = array( - 'name' => (string) $name, + 'name' => (string)$name, ); if ($parentId === 'root') { - $properties['parentReference'] = (object) array('path' => '/drive/root:'); + $properties['parentReference'] = (object)array('path' => '/drive/root:'); } else { - $properties['parentReference'] = (object) array('id' => (string) $parentId); + $properties['parentReference'] = (object)array('id' => (string)$parentId); } - $data = (object) $properties; + $data = (object)$properties; $curl = $this->_od_prepareCurl($url); curl_setopt_array($curl, array( CURLOPT_POST => true, CURLOPT_HEADER => true, CURLOPT_HTTPHEADER => array( - 'Content-Type: application/json', - 'Authorization: Bearer '.$this->token->data->access_token, + 'Content-Type: application/json', + 'Authorization: Bearer ' . $this->token->data->access_token, 'Prefer: respond-async', ), CURLOPT_POSTFIELDS => json_encode($data), @@ -1716,7 +1693,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver break; } } elseif (isset($res->error)) { - return $this->setError('OneDrive error: '.$res->error->message); + return $this->setError('OneDrive error: ' . $res->error->message); } } } @@ -1731,7 +1708,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver return false; } catch (Exception $e) { - return $this->setError('OneDrive error: '.$e->getMessage()); + return $this->setError('OneDrive error: ' . $e->getMessage()); } return true; @@ -1742,11 +1719,10 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Return new file path or false. * * @param string $source source file path - * @param $targetDir - * @param string $name file name + * @param $targetDir + * @param string $name file name * * @return string|bool - * * @author Dmitry (dio) Levashov */ protected function _move($source, $targetDir, $name) @@ -1756,31 +1732,31 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver list($sourceParentId, $itemId, $srcParent) = $this->_od_splitPath($source); $properties = array( - 'name' => (string) $name, - ); + 'name' => (string)$name, + ); if ($targetParentId !== $sourceParentId) { - $properties['parentReference'] = (object) array('id' => (string) $targetParentId); + $properties['parentReference'] = (object)array('id' => (string)$targetParentId); } - $url = self::API_URL.$itemId; - $data = (object) $properties; + $url = self::API_URL . $itemId; + $data = (object)$properties; $curl = $this->_od_prepareCurl($url); curl_setopt_array($curl, array( - CURLOPT_CUSTOMREQUEST => 'PATCH', - CURLOPT_POSTFIELDS => json_encode($data), - )); + CURLOPT_CUSTOMREQUEST => 'PATCH', + CURLOPT_POSTFIELDS => json_encode($data), + )); $result = json_decode(curl_exec($curl)); curl_close($curl); if ($result && isset($result->id)) { - return $targetDir.'/'.$result->id; + return $targetDir . '/' . $result->id; } else { return false; } } catch (Exception $e) { - return $this->setError('OneDrive error: '.$e->getMessage()); + return $this->setError('OneDrive error: ' . $e->getMessage()); } return false; @@ -1792,7 +1768,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path file path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _unlink($path) @@ -1801,7 +1776,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver try { list(, $itemId) = $this->_od_splitPath($path); - $url = self::API_URL.$itemId; + $url = self::API_URL . $itemId; $curl = $this->_od_prepareCurl($url); curl_setopt_array($curl, array( @@ -1812,7 +1787,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver $result = curl_exec($curl); curl_close($curl); } catch (Exception $e) { - return $this->setError('OneDrive error: '.$e->getMessage()); + return $this->setError('OneDrive error: ' . $e->getMessage()); } return true; @@ -1824,7 +1799,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path dir path * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _rmdir($path) @@ -1836,13 +1810,12 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Create new file and write into it from file pointer. * Return new file path or false on error. * - * @param resource $fp file pointer - * @param $path - * @param string $name file name - * @param array $stat file stat (required by some virtual fs) + * @param resource $fp file pointer + * @param $path + * @param string $name file name + * @param array $stat file stat (required by some virtual fs) * * @return bool|string - * * @author Dmitry (dio) Levashov */ protected function _save($fp, $path, $name, $stat) @@ -1880,9 +1853,9 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver try { //Create or Update a file if ($itemId === '') { - $url = self::API_URL.$parentId.':/'.rawurlencode($name).':/content'; + $url = self::API_URL . $parentId . ':/' . rawurlencode($name) . ':/content'; } else { - $url = self::API_URL.$itemId.'/content'; + $url = self::API_URL . $itemId . '/content'; } $curl = $this->_od_prepareCurl(); @@ -1904,7 +1877,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver return $this->_joinPath($parent, $file->id); } catch (Exception $e) { - return $this->setError('OneDrive error: '.$e->getMessage()); + return $this->setError('OneDrive error: ' . $e->getMessage()); } } @@ -1914,7 +1887,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $path file path * * @return string|false - * * @author Dmitry (dio) Levashov **/ protected function _getContents($path) @@ -1923,10 +1895,10 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver try { list(, $itemId) = $this->_od_splitPath($path); - $url = self::API_URL.$itemId.'/content'; + $url = self::API_URL . $itemId . '/content'; $contents = $this->_od_createCurl($url, $contents = true); } catch (Exception $e) { - return $this->setError('OneDrive error: '.$e->getMessage()); + return $this->setError('OneDrive error: ' . $e->getMessage()); } return $contents; @@ -1939,7 +1911,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param string $content new file content * * @return bool - * * @author Dmitry (dio) Levashov **/ protected function _filePutContents($path, $content) @@ -1948,7 +1919,7 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver if ($local = $this->getTempFile($path)) { if (file_put_contents($local, $content, LOCK_EX) !== false - && ($fp = fopen($local, 'rb'))) { + && ($fp = fopen($local, 'rb'))) { clearstatcache(); $res = $this->_save($fp, $path, '', array()); fclose($fp); @@ -1982,10 +1953,9 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Unpack archive. * * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) + * @param array $arc archiver command and arguments (same as in $this->archivers) * * @return void - * * @author Dmitry (dio) Levashov * @author Alexey Sukhotin */ @@ -1999,10 +1969,9 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * Extract files from archive. * * @param string $path archive path - * @param array $arc archiver command and arguments (same as in $this->archivers) + * @param array $arc archiver command and arguments (same as in $this->archivers) * * @return void - * * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin */ @@ -2020,7 +1989,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver * @param array $arc archiver options * * @return string|bool - * * @author Dmitry (dio) Levashov, * @author Alexey Sukhotin **/ diff --git a/php/elFinderVolumeTrash.class.php b/php/elFinderVolumeTrash.class.php index 266138b61..5140e7db3 100644 --- a/php/elFinderVolumeTrash.class.php +++ b/php/elFinderVolumeTrash.class.php @@ -1,4 +1,5 @@ = 26){ - $bfh = unpack("vtype/Vsize", $filename_or_stream_or_binary); - if ($bfh["type"] == 0x4d42 && ($bfh["size"] == 0 || $bfh["size"] == strlen($filename_or_stream_or_binary))){ - return self::loadFromString($filename_or_stream_or_binary); - } - } - return self::loadFromFile($filename_or_stream_or_binary); - } - public static function loadFromFile($filename){ - $fp = fopen($filename, "rb"); - if ($fp === false){ - return false; - } +class elFinderLibGdBmp +{ + public static function load($filename_or_stream_or_binary) + { + if (is_resource($filename_or_stream_or_binary)) { + return self::loadFromStream($filename_or_stream_or_binary); + } else if (is_string($filename_or_stream_or_binary) && strlen($filename_or_stream_or_binary) >= 26) { + $bfh = unpack("vtype/Vsize", $filename_or_stream_or_binary); + if ($bfh["type"] == 0x4d42 && ($bfh["size"] == 0 || $bfh["size"] == strlen($filename_or_stream_or_binary))) { + return self::loadFromString($filename_or_stream_or_binary); + } + } + return self::loadFromFile($filename_or_stream_or_binary); + } - $bmp = self::loadFromStream($fp); + public static function loadFromFile($filename) + { + $fp = fopen($filename, "rb"); + if ($fp === false) { + return false; + } - fclose($fp); - return $bmp; - } + $bmp = self::loadFromStream($fp); - public static function loadFromString($str){ - //data scheme より古いバージョンから対応しているようなので php://memory を使う - $fp = fopen("php://memory", "r+b"); - if ($fp === false){ - return false; - } + fclose($fp); + return $bmp; + } - if (fwrite($fp, $str) != strlen($str)){ - fclose($fp); - return false; - } + public static function loadFromString($str) + { + //data scheme より古いバージョンから対応しているようなので php://memory を使う + $fp = fopen("php://memory", "r+b"); + if ($fp === false) { + return false; + } - if (fseek($fp, 0) === -1){ - fclose($fp); - return false; - } + if (fwrite($fp, $str) != strlen($str)) { + fclose($fp); + return false; + } - $bmp = self::loadFromStream($fp); + if (fseek($fp, 0) === -1) { + fclose($fp); + return false; + } - fclose($fp); - return $bmp; - } + $bmp = self::loadFromStream($fp); - public static function loadFromStream($stream){ - $buf = fread($stream, 14); //2+4+2+2+4 - if ($buf === false){ - return false; - } + fclose($fp); + return $bmp; + } - //シグネチャチェック - if ($buf[0] != 'B' || $buf[1] != 'M'){ - return false; - } + public static function loadFromStream($stream) + { + $buf = fread($stream, 14); //2+4+2+2+4 + if ($buf === false) { + return false; + } - $bitmap_file_header = unpack( - //BITMAPFILEHEADER構造体 - "vtype/". - "Vsize/". - "vreserved1/". - "vreserved2/". - "Voffbits", $buf - ); + //シグネチャチェック + if ($buf[0] != 'B' || $buf[1] != 'M') { + return false; + } - return self::loadFromStreamAndFileHeader($stream, $bitmap_file_header); - } + $bitmap_file_header = unpack( + //BITMAPFILEHEADER構造体 + "vtype/" . + "Vsize/" . + "vreserved1/" . + "vreserved2/" . + "Voffbits", $buf + ); - public static function loadFromStreamAndFileHeader($stream, array $bitmap_file_header){ - if ($bitmap_file_header["type"] != 0x4d42){ - return false; - } + return self::loadFromStreamAndFileHeader($stream, $bitmap_file_header); + } - //情報ヘッダサイズを元に形式を区別して読み込み - $buf = fread($stream, 4); - if ($buf === false){ - return false; - } - list(,$header_size) = unpack("V", $buf); + public static function loadFromStreamAndFileHeader($stream, array $bitmap_file_header) + { + if ($bitmap_file_header["type"] != 0x4d42) { + return false; + } + + //情報ヘッダサイズを元に形式を区別して読み込み + $buf = fread($stream, 4); + if ($buf === false) { + return false; + } + list(, $header_size) = unpack("V", $buf); - if ($header_size == 12){ - $buf = fread($stream, $header_size - 4); - if ($buf === false){ - return false; - } + if ($header_size == 12) { + $buf = fread($stream, $header_size - 4); + if ($buf === false) { + return false; + } - extract(unpack( - //BITMAPCOREHEADER構造体 - OS/2 Bitmap - "vwidth/". - "vheight/". - "vplanes/". - "vbit_count", $buf - )); - //飛んでこない分は 0 で初期化しておく - $clr_used = $clr_important = $alpha_mask = $compression = 0; + extract(unpack( + //BITMAPCOREHEADER構造体 - OS/2 Bitmap + "vwidth/" . + "vheight/" . + "vplanes/" . + "vbit_count", $buf + )); + //飛んでこない分は 0 で初期化しておく + $clr_used = $clr_important = $alpha_mask = $compression = 0; - //マスク類は初期化されないのでここで割り当てておく - $red_mask = 0x00ff0000; - $green_mask = 0x0000ff00; - $blue_mask = 0x000000ff; - } else if (124 < $header_size || $header_size < 40) { - //未知の形式 - return false; - } else { - //この時点で36バイト読めることまではわかっている - $buf = fread($stream, 36); //既に読んだ部分は除外しつつBITMAPINFOHEADERのサイズだけ読む - if ($buf === false){ - return false; - } + //マスク類は初期化されないのでここで割り当てておく + $red_mask = 0x00ff0000; + $green_mask = 0x0000ff00; + $blue_mask = 0x000000ff; + } else if (124 < $header_size || $header_size < 40) { + //未知の形式 + return false; + } else { + //この時点で36バイト読めることまではわかっている + $buf = fread($stream, 36); //既に読んだ部分は除外しつつBITMAPINFOHEADERのサイズだけ読む + if ($buf === false) { + return false; + } - //BITMAPINFOHEADER構造体 - Windows Bitmap - extract(unpack( - "Vwidth/". - "Vheight/". - "vplanes/". - "vbit_count/". - "Vcompression/". - "Vsize_image/". - "Vx_pels_per_meter/". - "Vy_pels_per_meter/". - "Vclr_used/". - "Vclr_important", $buf - )); - //負の整数を受け取る可能性があるものは自前で変換する - if ($width & 0x80000000){ $width = -(~$width & 0xffffffff) - 1; } - if ($height & 0x80000000){ $height = -(~$height & 0xffffffff) - 1; } - if ($x_pels_per_meter & 0x80000000){ $x_pels_per_meter = -(~$x_pels_per_meter & 0xffffffff) - 1; } - if ($y_pels_per_meter & 0x80000000){ $y_pels_per_meter = -(~$y_pels_per_meter & 0xffffffff) - 1; } + //BITMAPINFOHEADER構造体 - Windows Bitmap + extract(unpack( + "Vwidth/" . + "Vheight/" . + "vplanes/" . + "vbit_count/" . + "Vcompression/" . + "Vsize_image/" . + "Vx_pels_per_meter/" . + "Vy_pels_per_meter/" . + "Vclr_used/" . + "Vclr_important", $buf + )); + //負の整数を受け取る可能性があるものは自前で変換する + if ($width & 0x80000000) { + $width = -(~$width & 0xffffffff) - 1; + } + if ($height & 0x80000000) { + $height = -(~$height & 0xffffffff) - 1; + } + if ($x_pels_per_meter & 0x80000000) { + $x_pels_per_meter = -(~$x_pels_per_meter & 0xffffffff) - 1; + } + if ($y_pels_per_meter & 0x80000000) { + $y_pels_per_meter = -(~$y_pels_per_meter & 0xffffffff) - 1; + } - //ファイルによっては BITMAPINFOHEADER のサイズがおかしい(書き込み間違い?)ケースがある - //自分でファイルサイズを元に逆算することで回避できることもあるので再計算できそうなら正当性を調べる - //シークできないストリームの場合全体のファイルサイズは取得できないので、$bitmap_file_headerにサイズ申告がなければやらない - if ($bitmap_file_header["size"] != 0){ - $colorsize = $bit_count == 1 || $bit_count == 4 || $bit_count == 8 ? ($clr_used ? $clr_used : pow(2, $bit_count))<<2 : 0; - $bodysize = $size_image ? $size_image : ((($width * $bit_count + 31) >> 3) & ~3) * abs($height); - $calcsize = $bitmap_file_header["size"] - $bodysize - $colorsize - 14; + //ファイルによっては BITMAPINFOHEADER のサイズがおかしい(書き込み間違い?)ケースがある + //自分でファイルサイズを元に逆算することで回避できることもあるので再計算できそうなら正当性を調べる + //シークできないストリームの場合全体のファイルサイズは取得できないので、$bitmap_file_headerにサイズ申告がなければやらない + if ($bitmap_file_header["size"] != 0) { + $colorsize = $bit_count == 1 || $bit_count == 4 || $bit_count == 8 ? ($clr_used ? $clr_used : pow(2, $bit_count)) << 2 : 0; + $bodysize = $size_image ? $size_image : ((($width * $bit_count + 31) >> 3) & ~3) * abs($height); + $calcsize = $bitmap_file_header["size"] - $bodysize - $colorsize - 14; - //本来であれば一致するはずなのに合わない時は、値がおかしくなさそうなら(BITMAPV5HEADERの範囲内なら)計算して求めた値を採用する - if ($header_size < $calcsize && 40 <= $header_size && $header_size <= 124){ - $header_size = $calcsize; - } - } + //本来であれば一致するはずなのに合わない時は、値がおかしくなさそうなら(BITMAPV5HEADERの範囲内なら)計算して求めた値を採用する + if ($header_size < $calcsize && 40 <= $header_size && $header_size <= 124) { + $header_size = $calcsize; + } + } - //BITMAPV4HEADER や BITMAPV5HEADER の場合まだ読むべきデータが残っている可能性がある - if ($header_size - 40 > 0){ - $buf = fread($stream, $header_size - 40); - if ($buf === false){ - return false; - } + //BITMAPV4HEADER や BITMAPV5HEADER の場合まだ読むべきデータが残っている可能性がある + if ($header_size - 40 > 0) { + $buf = fread($stream, $header_size - 40); + if ($buf === false) { + return false; + } - extract(unpack( - //BITMAPV4HEADER構造体(Windows95以降) - //BITMAPV5HEADER構造体(Windows98/2000以降) - "Vred_mask/". - "Vgreen_mask/". - "Vblue_mask/". - "Valpha_mask", $buf . str_repeat("\x00", 120) - )); - } else { - $alpha_mask = $red_mask = $green_mask = $blue_mask = 0; - } + extract(unpack( + //BITMAPV4HEADER構造体(Windows95以降) + //BITMAPV5HEADER構造体(Windows98/2000以降) + "Vred_mask/" . + "Vgreen_mask/" . + "Vblue_mask/" . + "Valpha_mask", $buf . str_repeat("\x00", 120) + )); + } else { + $alpha_mask = $red_mask = $green_mask = $blue_mask = 0; + } - //パレットがないがカラーマスクもない時 - if ( - ($bit_count == 16 || $bit_count == 24 || $bit_count == 32)&& - $compression == 0 && - $red_mask == 0 && $green_mask == 0 && $blue_mask == 0 - ){ - //もしカラーマスクを所持していない場合は - //規定のカラーマスクを適用する - switch($bit_count){ - case 16: - $red_mask = 0x7c00; - $green_mask = 0x03e0; - $blue_mask = 0x001f; - break; - case 24: - case 32: - $red_mask = 0x00ff0000; - $green_mask = 0x0000ff00; - $blue_mask = 0x000000ff; - break; - } - } - } + //パレットがないがカラーマスクもない時 + if ( + ($bit_count == 16 || $bit_count == 24 || $bit_count == 32) && + $compression == 0 && + $red_mask == 0 && $green_mask == 0 && $blue_mask == 0 + ) { + //もしカラーマスクを所持していない場合は + //規定のカラーマスクを適用する + switch ($bit_count) { + case 16: + $red_mask = 0x7c00; + $green_mask = 0x03e0; + $blue_mask = 0x001f; + break; + case 24: + case 32: + $red_mask = 0x00ff0000; + $green_mask = 0x0000ff00; + $blue_mask = 0x000000ff; + break; + } + } + } - if ( - ($width == 0)|| - ($height == 0)|| - ($planes != 1)|| - (($alpha_mask & $red_mask ) != 0)|| - (($alpha_mask & $green_mask) != 0)|| - (($alpha_mask & $blue_mask ) != 0)|| - (($red_mask & $green_mask) != 0)|| - (($red_mask & $blue_mask ) != 0)|| - (($green_mask & $blue_mask ) != 0) - ){ - //不正な画像 - return false; - } + if ( + ($width == 0) || + ($height == 0) || + ($planes != 1) || + (($alpha_mask & $red_mask) != 0) || + (($alpha_mask & $green_mask) != 0) || + (($alpha_mask & $blue_mask) != 0) || + (($red_mask & $green_mask) != 0) || + (($red_mask & $blue_mask) != 0) || + (($green_mask & $blue_mask) != 0) + ) { + //不正な画像 + return false; + } - //BI_JPEG と BI_PNG の場合は jpeg/png がそのまま入ってるだけなのでそのまま取り出してデコードする - if ($compression == 4 || $compression == 5){ - $buf = stream_get_contents($stream, $size_image); - if ($buf === false){ - return false; - } - return imagecreatefromstring($buf); - } + //BI_JPEG と BI_PNG の場合は jpeg/png がそのまま入ってるだけなのでそのまま取り出してデコードする + if ($compression == 4 || $compression == 5) { + $buf = stream_get_contents($stream, $size_image); + if ($buf === false) { + return false; + } + return imagecreatefromstring($buf); + } - //画像本体の読み出し - //1行のバイト数 - $line_bytes = (($width * $bit_count + 31) >> 3) & ~3; - //全体の行数 - $lines = abs($height); - //y軸進行量(ボトムアップかトップダウンか) - $y = $height > 0 ? $lines-1 : 0; - $line_step = $height > 0 ? -1 : 1; + //画像本体の読み出し + //1行のバイト数 + $line_bytes = (($width * $bit_count + 31) >> 3) & ~3; + //全体の行数 + $lines = abs($height); + //y軸進行量(ボトムアップかトップダウンか) + $y = $height > 0 ? $lines - 1 : 0; + $line_step = $height > 0 ? -1 : 1; - //256色以下の画像か? - if ($bit_count == 1 || $bit_count == 4 || $bit_count == 8){ - $img = imagecreate($width, $lines); + //256色以下の画像か? + if ($bit_count == 1 || $bit_count == 4 || $bit_count == 8) { + $img = imagecreate($width, $lines); - //画像データの前にパレットデータがあるのでパレットを作成する - $palette_size = $header_size == 12 ? 3 : 4; //OS/2形式の場合は x に相当する箇所のデータは最初から確保されていない - $colors = $clr_used ? $clr_used : pow(2, $bit_count); //色数 - $palette = array(); - for($i = 0; $i < $colors; ++$i){ - $buf = fread($stream, $palette_size); - if ($buf === false){ - imagedestroy($img); - return false; - } - extract(unpack("Cb/Cg/Cr/Cx", $buf . "\x00")); - $palette[] = imagecolorallocate($img, $r, $g, $b); - } + //画像データの前にパレットデータがあるのでパレットを作成する + $palette_size = $header_size == 12 ? 3 : 4; //OS/2形式の場合は x に相当する箇所のデータは最初から確保されていない + $colors = $clr_used ? $clr_used : pow(2, $bit_count); //色数 + $palette = array(); + for ($i = 0; $i < $colors; ++$i) { + $buf = fread($stream, $palette_size); + if ($buf === false) { + imagedestroy($img); + return false; + } + extract(unpack("Cb/Cg/Cr/Cx", $buf . "\x00")); + $palette[] = imagecolorallocate($img, $r, $g, $b); + } - $shift_base = 8 - $bit_count; - $mask = ((1 << $bit_count) - 1) << $shift_base; + $shift_base = 8 - $bit_count; + $mask = ((1 << $bit_count) - 1) << $shift_base; - //圧縮されている場合とされていない場合でデコード処理が大きく変わる - if ($compression == 1 || $compression == 2){ - $x = 0; - $qrt_mod2 = $bit_count >> 2 & 1; - for(;;){ - //もし描写先が範囲外になっている場合デコード処理がおかしくなっているので抜ける - //変なデータが渡されたとしても最悪なケースで255回程度の無駄なので目を瞑る - if ($x < -1 || $x > $width || $y < -1 || $y > $height){ - imagedestroy($img); - return false; - } - $buf = fread($stream, 1); - if ($buf === false){ - imagedestroy($img); - return false; - } - switch($buf){ - case "\x00": - $buf = fread($stream, 1); - if ($buf === false){ - imagedestroy($img); - return false; - } - switch($buf){ - case "\x00": //EOL - $y += $line_step; - $x = 0; - break; - case "\x01": //EOB - $y = 0; - $x = 0; - break 3; - case "\x02": //MOV - $buf = fread($stream, 2); - if ($buf === false){ - imagedestroy($img); - return false; - } - list(,$xx, $yy) = unpack("C2", $buf); - $x += $xx; - $y += $yy * $line_step; - break; - default: //ABS - list(,$pixels) = unpack("C", $buf); - $bytes = ($pixels >> $qrt_mod2) + ($pixels & $qrt_mod2); - $buf = fread($stream, ($bytes + 1) & ~1); - if ($buf === false){ - imagedestroy($img); - return false; - } - for ($i = 0, $pos = 0; $i < $pixels; ++$i, ++$x, $pos += $bit_count){ - list(,$c) = unpack("C", $buf[$pos >> 3]); - $b = $pos & 0x07; - imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]); - } - break; - } - break; - default: - $buf2 = fread($stream, 1); - if ($buf2 === false){ - imagedestroy($img); - return false; - } - list(,$size, $c) = unpack("C2", $buf . $buf2); - for($i = 0, $pos = 0; $i < $size; ++$i, ++$x, $pos += $bit_count){ - $b = $pos & 0x07; - imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]); - } - break; - } - } - } else { - for ($line = 0; $line < $lines; ++$line, $y += $line_step){ - $buf = fread($stream, $line_bytes); - if ($buf === false){ - imagedestroy($img); - return false; - } + //圧縮されている場合とされていない場合でデコード処理が大きく変わる + if ($compression == 1 || $compression == 2) { + $x = 0; + $qrt_mod2 = $bit_count >> 2 & 1; + for (; ;) { + //もし描写先が範囲外になっている場合デコード処理がおかしくなっているので抜ける + //変なデータが渡されたとしても最悪なケースで255回程度の無駄なので目を瞑る + if ($x < -1 || $x > $width || $y < -1 || $y > $height) { + imagedestroy($img); + return false; + } + $buf = fread($stream, 1); + if ($buf === false) { + imagedestroy($img); + return false; + } + switch ($buf) { + case "\x00": + $buf = fread($stream, 1); + if ($buf === false) { + imagedestroy($img); + return false; + } + switch ($buf) { + case "\x00": //EOL + $y += $line_step; + $x = 0; + break; + case "\x01": //EOB + $y = 0; + $x = 0; + break 3; + case "\x02": //MOV + $buf = fread($stream, 2); + if ($buf === false) { + imagedestroy($img); + return false; + } + list(, $xx, $yy) = unpack("C2", $buf); + $x += $xx; + $y += $yy * $line_step; + break; + default: //ABS + list(, $pixels) = unpack("C", $buf); + $bytes = ($pixels >> $qrt_mod2) + ($pixels & $qrt_mod2); + $buf = fread($stream, ($bytes + 1) & ~1); + if ($buf === false) { + imagedestroy($img); + return false; + } + for ($i = 0, $pos = 0; $i < $pixels; ++$i, ++$x, $pos += $bit_count) { + list(, $c) = unpack("C", $buf[$pos >> 3]); + $b = $pos & 0x07; + imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]); + } + break; + } + break; + default: + $buf2 = fread($stream, 1); + if ($buf2 === false) { + imagedestroy($img); + return false; + } + list(, $size, $c) = unpack("C2", $buf . $buf2); + for ($i = 0, $pos = 0; $i < $size; ++$i, ++$x, $pos += $bit_count) { + $b = $pos & 0x07; + imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]); + } + break; + } + } + } else { + for ($line = 0; $line < $lines; ++$line, $y += $line_step) { + $buf = fread($stream, $line_bytes); + if ($buf === false) { + imagedestroy($img); + return false; + } - $pos = 0; - for ($x = 0; $x < $width; ++$x, $pos += $bit_count){ - list(,$c) = unpack("C", $buf[$pos >> 3]); - $b = $pos & 0x7; - imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]); - } - } - } - } else { - $img = imagecreatetruecolor($width, $lines); - imagealphablending($img, false); - if ($alpha_mask) - { - //αデータがあるので透過情報も保存できるように - imagesavealpha($img, true); - } + $pos = 0; + for ($x = 0; $x < $width; ++$x, $pos += $bit_count) { + list(, $c) = unpack("C", $buf[$pos >> 3]); + $b = $pos & 0x7; + imagesetpixel($img, $x, $y, $palette[($c & ($mask >> $b)) >> ($shift_base - $b)]); + } + } + } + } else { + $img = imagecreatetruecolor($width, $lines); + imagealphablending($img, false); + if ($alpha_mask) { + //αデータがあるので透過情報も保存できるように + imagesavealpha($img, true); + } - //x軸進行量 - $pixel_step = $bit_count >> 3; - $alpha_max = $alpha_mask ? 0x7f : 0x00; - $alpha_mask_r = $alpha_mask ? 1/$alpha_mask : 1; - $red_mask_r = $red_mask ? 1/$red_mask : 1; - $green_mask_r = $green_mask ? 1/$green_mask : 1; - $blue_mask_r = $blue_mask ? 1/$blue_mask : 1; + //x軸進行量 + $pixel_step = $bit_count >> 3; + $alpha_max = $alpha_mask ? 0x7f : 0x00; + $alpha_mask_r = $alpha_mask ? 1 / $alpha_mask : 1; + $red_mask_r = $red_mask ? 1 / $red_mask : 1; + $green_mask_r = $green_mask ? 1 / $green_mask : 1; + $blue_mask_r = $blue_mask ? 1 / $blue_mask : 1; - for ($line = 0; $line < $lines; ++$line, $y += $line_step){ - $buf = fread($stream, $line_bytes); - if ($buf === false){ - imagedestroy($img); - return false; - } + for ($line = 0; $line < $lines; ++$line, $y += $line_step) { + $buf = fread($stream, $line_bytes); + if ($buf === false) { + imagedestroy($img); + return false; + } - $pos = 0; - for ($x = 0; $x < $width; ++$x, $pos += $pixel_step){ - list(,$c) = unpack("V", substr($buf, $pos, $pixel_step). "\x00\x00"); - $a_masked = $c & $alpha_mask; - $r_masked = $c & $red_mask; - $g_masked = $c & $green_mask; - $b_masked = $c & $blue_mask; - $a = $alpha_max - ((($a_masked<<7) - $a_masked) * $alpha_mask_r); - $r = (($r_masked<<8) - $r_masked) * $red_mask_r; - $g = (($g_masked<<8) - $g_masked) * $green_mask_r; - $b = (($b_masked<<8) - $b_masked) * $blue_mask_r; - imagesetpixel($img, $x, $y, ($a<<24)|($r<<16)|($g<<8)|$b); - } - } - imagealphablending($img, true); //デフォルト値に戻しておく - } - return $img; - } + $pos = 0; + for ($x = 0; $x < $width; ++$x, $pos += $pixel_step) { + list(, $c) = unpack("V", substr($buf, $pos, $pixel_step) . "\x00\x00"); + $a_masked = $c & $alpha_mask; + $r_masked = $c & $red_mask; + $g_masked = $c & $green_mask; + $b_masked = $c & $blue_mask; + $a = $alpha_max - ((($a_masked << 7) - $a_masked) * $alpha_mask_r); + $r = (($r_masked << 8) - $r_masked) * $red_mask_r; + $g = (($g_masked << 8) - $g_masked) * $green_mask_r; + $b = (($b_masked << 8) - $b_masked) * $blue_mask_r; + imagesetpixel($img, $x, $y, ($a << 24) | ($r << 16) | ($g << 8) | $b); + } + } + imagealphablending($img, true); //デフォルト値に戻しておく + } + return $img; + } } diff --git a/php/plugins/AutoResize/plugin.php b/php/plugins/AutoResize/plugin.php index 228077612..f01cc864b 100644 --- a/php/plugins/AutoResize/plugin.php +++ b/php/plugins/AutoResize/plugin.php @@ -1,141 +1,144 @@ array( - * 'upload.presave' => array( - * 'Plugin.AutoResize.onUpLoadPreSave' - * ) - * ), - * // global configure (optional) - * 'plugin' => array( - * 'AutoResize' => array( - * 'enable' => true, // For control by volume driver - * 'maxWidth' => 1024, // Path to Water mark image - * 'maxHeight' => 1024, // Margin right pixel - * 'quality' => 95, // JPEG image save quality - * 'preserveExif' => false, // Preserve EXIF data (Imagick only) - * 'forceEffect' => false, // For change quality or make progressive JPEG of small images - * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) - * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * ) - * ), - * // each volume configure (optional) - * 'roots' => array( - * array( - * 'driver' => 'LocalFileSystem', - * 'path' => '/path/to/files/', - * 'URL' => 'http://localhost/to/files/' - * 'plugin' => array( - * 'AutoResize' => array( - * 'enable' => true, // For control by volume driver - * 'maxWidth' => 1024, // Path to Water mark image - * 'maxHeight' => 1024, // Margin right pixel - * 'quality' => 95, // JPEG image save quality - * 'preserveExif' => false, // Preserve EXIF data (Imagick only) - * 'forceEffect' => false, // For change quality or make progressive JPEG of small images - * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) - * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * ) - * ) - * ) - * ) - * ); + * $opts = array( + * 'bind' => array( + * 'upload.presave' => array( + * 'Plugin.AutoResize.onUpLoadPreSave' + * ) + * ), + * // global configure (optional) + * 'plugin' => array( + * 'AutoResize' => array( + * 'enable' => true, // For control by volume driver + * 'maxWidth' => 1024, // Path to Water mark image + * 'maxHeight' => 1024, // Margin right pixel + * 'quality' => 95, // JPEG image save quality + * 'preserveExif' => false, // Preserve EXIF data (Imagick only) + * 'forceEffect' => false, // For change quality or make progressive JPEG of small images + * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) + * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * ) + * ), + * // each volume configure (optional) + * 'roots' => array( + * array( + * 'driver' => 'LocalFileSystem', + * 'path' => '/path/to/files/', + * 'URL' => 'http://localhost/to/files/' + * 'plugin' => array( + * 'AutoResize' => array( + * 'enable' => true, // For control by volume driver + * 'maxWidth' => 1024, // Path to Water mark image + * 'maxHeight' => 1024, // Margin right pixel + * 'quality' => 95, // JPEG image save quality + * 'preserveExif' => false, // Preserve EXIF data (Imagick only) + * 'forceEffect' => false, // For change quality or make progressive JPEG of small images + * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) + * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * ) + * ) + * ) + * ) + * ); * * @package elfinder - * @author Naoki Sawada + * @author Naoki Sawada * @license New BSD */ -class elFinderPluginAutoResize extends elFinderPlugin { +class elFinderPluginAutoResize extends elFinderPlugin +{ - public function __construct($opts) { - $defaults = array( - 'enable' => true, // For control by volume driver - 'maxWidth' => 1024, // Path to Water mark image - 'maxHeight' => 1024, // Margin right pixel - 'quality' => 95, // JPEG image save quality - 'preserveExif' => false, // Preserve EXIF data (Imagick only) - 'forceEffect' => false, // For change quality or make progressive JPEG of small images - 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) - 'offDropWith' => null, // To disable it if it is dropped with pressing the meta key - // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - // In case of using any key, specify it as an array - 'disableWithContentSaveId' => true // Disable on URL upload with post data "contentSaveId" - ); + public function __construct($opts) + { + $defaults = array( + 'enable' => true, // For control by volume driver + 'maxWidth' => 1024, // Path to Water mark image + 'maxHeight' => 1024, // Margin right pixel + 'quality' => 95, // JPEG image save quality + 'preserveExif' => false, // Preserve EXIF data (Imagick only) + 'forceEffect' => false, // For change quality or make progressive JPEG of small images + 'targetType' => IMG_GIF | IMG_JPG | IMG_PNG | IMG_WBMP, // Target image formats ( bit-field ) + 'offDropWith' => null, // To disable it if it is dropped with pressing the meta key + // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + // In case of using any key, specify it as an array + 'disableWithContentSaveId' => true // Disable on URL upload with post data "contentSaveId" + ); - $this->opts = array_merge($defaults, $opts); + $this->opts = array_merge($defaults, $opts); - } + } - public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) { - $opts = $this->getCurrentOpts($volume); - - if (! $this->iaEnabled($opts, $elfinder)) { - return false; - } - - $imageType = null; - $srcImgInfo = null; - if (extension_loaded('fileinfo') && function_exists('mime_content_type')) { - $mime = mime_content_type($src); - if (substr($mime, 0, 5) !== 'image') { - return false; - } - } - if (extension_loaded('exif') && function_exists('exif_imagetype')) { - $imageType = exif_imagetype($src); - } else { - $srcImgInfo = getimagesize($src); - if ($srcImgInfo === false) { - return false; - } - $imageType = $srcImgInfo[2]; - } - - // check target image type - $imgTypes = array( - IMAGETYPE_GIF => IMG_GIF, - IMAGETYPE_JPEG => IMG_JPEG, - IMAGETYPE_PNG => IMG_PNG, - IMAGETYPE_BMP => IMG_WBMP, - IMAGETYPE_WBMP => IMG_WBMP - ); - if (! isset($imgTypes[$imageType]) || ! ($opts['targetType'] & $imgTypes[$imageType])) { - return false; - } - - if (! $srcImgInfo) { - $srcImgInfo = getimagesize($src); - } - - if ($opts['forceEffect'] || $srcImgInfo[0] > $opts['maxWidth'] || $srcImgInfo[1] > $opts['maxHeight']) { - return $this->resize($volume, $src, $srcImgInfo, $opts['maxWidth'], $opts['maxHeight'], $opts['quality'], $opts['preserveExif']); - } - - return false; - } - - private function resize($volume, $src, $srcImgInfo, $maxWidth, $maxHeight, $jpgQuality, $preserveExif) { - $zoom = min(($maxWidth/$srcImgInfo[0]),($maxHeight/$srcImgInfo[1])); - $width = round($srcImgInfo[0] * $zoom); - $height = round($srcImgInfo[1] * $zoom); - $unenlarge = true; - $checkAnimated = true; - - return $volume->imageUtil('resize', $src, compact('width', 'height', 'jpgQuality', 'preserveExif', 'unenlarge', 'checkAnimated')); - } + public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) + { + $opts = $this->getCurrentOpts($volume); + + if (!$this->iaEnabled($opts, $elfinder)) { + return false; + } + + $imageType = null; + $srcImgInfo = null; + if (extension_loaded('fileinfo') && function_exists('mime_content_type')) { + $mime = mime_content_type($src); + if (substr($mime, 0, 5) !== 'image') { + return false; + } + } + if (extension_loaded('exif') && function_exists('exif_imagetype')) { + $imageType = exif_imagetype($src); + } else { + $srcImgInfo = getimagesize($src); + if ($srcImgInfo === false) { + return false; + } + $imageType = $srcImgInfo[2]; + } + + // check target image type + $imgTypes = array( + IMAGETYPE_GIF => IMG_GIF, + IMAGETYPE_JPEG => IMG_JPEG, + IMAGETYPE_PNG => IMG_PNG, + IMAGETYPE_BMP => IMG_WBMP, + IMAGETYPE_WBMP => IMG_WBMP + ); + if (!isset($imgTypes[$imageType]) || !($opts['targetType'] & $imgTypes[$imageType])) { + return false; + } + + if (!$srcImgInfo) { + $srcImgInfo = getimagesize($src); + } + + if ($opts['forceEffect'] || $srcImgInfo[0] > $opts['maxWidth'] || $srcImgInfo[1] > $opts['maxHeight']) { + return $this->resize($volume, $src, $srcImgInfo, $opts['maxWidth'], $opts['maxHeight'], $opts['quality'], $opts['preserveExif']); + } + + return false; + } + + private function resize($volume, $src, $srcImgInfo, $maxWidth, $maxHeight, $jpgQuality, $preserveExif) + { + $zoom = min(($maxWidth / $srcImgInfo[0]), ($maxHeight / $srcImgInfo[1])); + $width = round($srcImgInfo[0] * $zoom); + $height = round($srcImgInfo[1] * $zoom); + $unenlarge = true; + $checkAnimated = true; + + return $volume->imageUtil('resize', $src, compact('width', 'height', 'jpgQuality', 'preserveExif', 'unenlarge', 'checkAnimated')); + } } diff --git a/php/plugins/AutoRotate/plugin.php b/php/plugins/AutoRotate/plugin.php index 84b76562b..95f10da7f 100644 --- a/php/plugins/AutoRotate/plugin.php +++ b/php/plugins/AutoRotate/plugin.php @@ -1,132 +1,135 @@ array( - * 'upload.presave' => array( - * 'Plugin.AutoRotate.onUpLoadPreSave' - * ) - * ), - * // global configure (optional) - * 'plugin' => array( - * 'AutoRotate' => array( - * 'enable' => true, // For control by volume driver - * 'quality' => 95, // JPEG image save quality - * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * ) - * ), - * // each volume configure (optional) - * 'roots' => array( - * array( - * 'driver' => 'LocalFileSystem', - * 'path' => '/path/to/files/', - * 'URL' => 'http://localhost/to/files/' - * 'plugin' => array( - * 'AutoRotate' => array( - * 'enable' => true, // For control by volume driver - * 'quality' => 95, // JPEG image save quality - * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * ) - * ) - * ) - * ) - * ); + * $opts = array( + * 'bind' => array( + * 'upload.presave' => array( + * 'Plugin.AutoRotate.onUpLoadPreSave' + * ) + * ), + * // global configure (optional) + * 'plugin' => array( + * 'AutoRotate' => array( + * 'enable' => true, // For control by volume driver + * 'quality' => 95, // JPEG image save quality + * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * ) + * ), + * // each volume configure (optional) + * 'roots' => array( + * array( + * 'driver' => 'LocalFileSystem', + * 'path' => '/path/to/files/', + * 'URL' => 'http://localhost/to/files/' + * 'plugin' => array( + * 'AutoRotate' => array( + * 'enable' => true, // For control by volume driver + * 'quality' => 95, // JPEG image save quality + * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * ) + * ) + * ) + * ) + * ); * * @package elfinder - * @author Naoki Sawada + * @author Naoki Sawada * @license New BSD */ -class elFinderPluginAutoRotate extends elFinderPlugin { +class elFinderPluginAutoRotate extends elFinderPlugin +{ - public function __construct($opts) { - $defaults = array( - 'enable' => true, // For control by volume driver - 'quality' => 95, // JPEG image save quality - 'offDropWith' => null, // To disable it if it is dropped with pressing the meta key - // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - // In case of using any key, specify it as an array - 'disableWithContentSaveId' => true // Disable on URL upload with post data "contentSaveId" - ); + public function __construct($opts) + { + $defaults = array( + 'enable' => true, // For control by volume driver + 'quality' => 95, // JPEG image save quality + 'offDropWith' => null, // To disable it if it is dropped with pressing the meta key + // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + // In case of using any key, specify it as an array + 'disableWithContentSaveId' => true // Disable on URL upload with post data "contentSaveId" + ); - $this->opts = array_merge($defaults, $opts); + $this->opts = array_merge($defaults, $opts); - } + } - public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) { - $opts = $this->getCurrentOpts($volume); - - if (! $this->iaEnabled($opts, $elfinder)) { - return false; - } - - $imageType = null; - $srcImgInfo = null; - if (extension_loaded('fileinfo') && function_exists('mime_content_type')) { - $mime = mime_content_type($src); - if (substr($mime, 0, 5) !== 'image') { - return false; - } - } - if (extension_loaded('exif') && function_exists('exif_imagetype')) { - $imageType = exif_imagetype($src); - } else { - $srcImgInfo = getimagesize($src); - if ($srcImgInfo === false) { - return false; - } - $imageType = $srcImgInfo[2]; - } - - // check target image type - if ($imageType !== IMAGETYPE_JPEG) { - return false; - } - - if (! $srcImgInfo) { - $srcImgInfo = getimagesize($src); - } - - return $this->rotate($volume, $src, $srcImgInfo, $opts['quality']); - } - - private function rotate($volume, $src, $srcImgInfo, $quality) { - if (! function_exists('exif_read_data')) { - return false; - } - $degree = 0; - $exif = exif_read_data($src); - if($exif && !empty($exif['Orientation'])) { - switch($exif['Orientation']) { - case 8: - $degree = 270; - break; - case 3: - $degree = 180; - break; - case 6: - $degree = 90; - break; - } - } - $opts = array( - 'degree' => $degree, - 'jpgQuality' => $quality, - 'checkAnimated' => true - ); - return $volume->imageUtil('rotate', $src, $opts); - } + public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) + { + $opts = $this->getCurrentOpts($volume); + + if (!$this->iaEnabled($opts, $elfinder)) { + return false; + } + + $imageType = null; + $srcImgInfo = null; + if (extension_loaded('fileinfo') && function_exists('mime_content_type')) { + $mime = mime_content_type($src); + if (substr($mime, 0, 5) !== 'image') { + return false; + } + } + if (extension_loaded('exif') && function_exists('exif_imagetype')) { + $imageType = exif_imagetype($src); + } else { + $srcImgInfo = getimagesize($src); + if ($srcImgInfo === false) { + return false; + } + $imageType = $srcImgInfo[2]; + } + + // check target image type + if ($imageType !== IMAGETYPE_JPEG) { + return false; + } + + if (!$srcImgInfo) { + $srcImgInfo = getimagesize($src); + } + + return $this->rotate($volume, $src, $srcImgInfo, $opts['quality']); + } + + private function rotate($volume, $src, $srcImgInfo, $quality) + { + if (!function_exists('exif_read_data')) { + return false; + } + $degree = 0; + $exif = exif_read_data($src); + if ($exif && !empty($exif['Orientation'])) { + switch ($exif['Orientation']) { + case 8: + $degree = 270; + break; + case 3: + $degree = 180; + break; + case 6: + $degree = 90; + break; + } + } + $opts = array( + 'degree' => $degree, + 'jpgQuality' => $quality, + 'checkAnimated' => true + ); + return $volume->imageUtil('rotate', $src, $opts); + } } diff --git a/php/plugins/Normalizer/plugin.php b/php/plugins/Normalizer/plugin.php index 3bab32075..002355db2 100644 --- a/php/plugins/Normalizer/plugin.php +++ b/php/plugins/Normalizer/plugin.php @@ -1,197 +1,200 @@ = 5.3.0, PECL intl >= 1.0.0) * or PEAR package "I18N_UnicodeNormalizer" - * * ex. binding, configure on connector options - * $opts = array( - * 'bind' => array( - * 'upload.pre mkdir.pre mkfile.pre rename.pre archive.pre ls.pre' => array( - * 'Plugin.Normalizer.cmdPreprocess' - * ), - * 'upload.presave' => array( - * 'Plugin.Normalizer.onUpLoadPreSave' - * ) - * ), - * // global configure (optional) - * 'plugin' => array( - * 'Normalizer' => array( - * 'enable' => true, - * 'nfc' => true, - * 'nfkc' => true, - * 'umlauts' => false, - * 'lowercase' => false, - * 'convmap' => array() - * ) - * ), - * // each volume configure (optional) - * 'roots' => array( - * array( - * 'driver' => 'LocalFileSystem', - * 'path' => '/path/to/files/', - * 'URL' => 'http://localhost/to/files/' - * 'plugin' => array( - * 'Normalizer' => array( - * 'enable' => true, - * 'nfc' => true, - * 'nfkc' => true, - * 'umlauts' => false, - * 'lowercase' => false, - * 'convmap' => array() - * ) - * ) - * ) - * ) - * ); + * $opts = array( + * 'bind' => array( + * 'upload.pre mkdir.pre mkfile.pre rename.pre archive.pre ls.pre' => array( + * 'Plugin.Normalizer.cmdPreprocess' + * ), + * 'upload.presave' => array( + * 'Plugin.Normalizer.onUpLoadPreSave' + * ) + * ), + * // global configure (optional) + * 'plugin' => array( + * 'Normalizer' => array( + * 'enable' => true, + * 'nfc' => true, + * 'nfkc' => true, + * 'umlauts' => false, + * 'lowercase' => false, + * 'convmap' => array() + * ) + * ), + * // each volume configure (optional) + * 'roots' => array( + * array( + * 'driver' => 'LocalFileSystem', + * 'path' => '/path/to/files/', + * 'URL' => 'http://localhost/to/files/' + * 'plugin' => array( + * 'Normalizer' => array( + * 'enable' => true, + * 'nfc' => true, + * 'nfkc' => true, + * 'umlauts' => false, + * 'lowercase' => false, + * 'convmap' => array() + * ) + * ) + * ) + * ) + * ); * * @package elfinder - * @author Naoki Sawada + * @author Naoki Sawada * @license New BSD */ class elFinderPluginNormalizer extends elFinderPlugin { - private $replaced = array(); - private $keyMap = array( - 'ls' => 'intersect', - 'upload' => 'renames', - 'mkdir' => array('name', 'dirs') - ); - - public function __construct($opts) { - $defaults = array( - 'enable' => true, // For control by volume driver - 'nfc' => true, // Canonical Decomposition followed by Canonical Composition - 'nfkc' => true, // Compatibility Decomposition followed by Canonical - 'umlauts' => false, // Convert umlauts with their closest 7 bit ascii equivalent - 'lowercase' => false, // Make chars lowercase - 'convmap' => array()// Convert map ('FROM' => 'TO') array - ); - - $this->opts = array_merge($defaults, $opts); - } - - public function cmdPreprocess($cmd, &$args, $elfinder, $volume) { - $opts = $this->getCurrentOpts($volume); - if (! $opts['enable']) { - return false; - } - $this->replaced[$cmd] = array(); - $key = (isset($this->keyMap[$cmd]))? $this->keyMap[$cmd] : 'name'; - - if (is_array($key)) { - $keys = $key; - } else { - $keys = array($key); - } - foreach($keys as $key) { - if (isset($args[$key])) { - if (is_array($args[$key])) { - foreach($args[$key] as $i => $name) { - if ($cmd === 'mkdir' && $key === 'dirs') { - // $name need '/' as prefix see #2607 - $name = '/' . ltrim($name, '/'); - $_names = explode('/', $name); - $_res = array(); - foreach($_names as $_name) { - $_res[] = $this->normalize($_name, $opts); - } - $this->replaced[$cmd][$name] = $args[$key][$i] = join('/', $_res); - } else { - $this->replaced[$cmd][$name] = $args[$key][$i] = $this->normalize($name, $opts); - } - } - } else if ($args[$key] !== '') { - $name = $args[$key]; - $this->replaced[$cmd][$name] = $args[$key] = $this->normalize($name, $opts); - } - } - } - if ($cmd === 'ls' || $cmd === 'mkdir') { - if (! empty($this->replaced[$cmd])) { - // un-regist for legacy settings - $elfinder->unbind($cmd, array($this, 'cmdPostprocess')); - $elfinder->bind($cmd, array($this, 'cmdPostprocess')); - } - } - return true; - } - - public function cmdPostprocess($cmd, &$result, $args, $elfinder, $volume) { - if ($cmd === 'ls') { - if (! empty($result['list']) && ! empty($this->replaced['ls'])) { - foreach($result['list'] as $hash => $name) { - if ($keys = array_keys($this->replaced['ls'], $name)) { - if (count($keys) === 1) { - $result['list'][$hash] = $keys[0]; - } else { - $result['list'][$hash] = $keys; - } - } - } - } - } else if ($cmd === 'mkdir') { - if (! empty($result['hashes']) && ! empty($this->replaced['mkdir'])) { - foreach($result['hashes'] as $name => $hash) { - if ($keys = array_keys($this->replaced['mkdir'], $name)) { - $result['hashes'][$keys[0]] = $hash; - } - } - } - } - } - - // NOTE: $thash is directory hash so it unneed to process at here - public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) { - $opts = $this->getCurrentOpts($volume); - if (! $opts['enable']) { - return false; - } - - $name = $this->normalize($name, $opts); - return true; - } - - protected function normalize($str, $opts) { - if ($opts['nfc'] || $opts['nfkc']) { - if (class_exists('Normalizer', false)) { - if ($opts['nfc'] && ! Normalizer::isNormalized($str, Normalizer::FORM_C)) - $str = Normalizer::normalize($str, Normalizer::FORM_C); - if ($opts['nfkc'] && ! Normalizer::isNormalized($str, Normalizer::FORM_KC)) - $str = Normalizer::normalize($str, Normalizer::FORM_KC); - } else { - if (! class_exists('I18N_UnicodeNormalizer', false)) { - include_once 'I18N/UnicodeNormalizer.php'; - } - if (class_exists('I18N_UnicodeNormalizer', false)) { - $normalizer = new I18N_UnicodeNormalizer(); - if ($opts['nfc']) - $str = $normalizer->normalize($str, 'NFC'); - if ($opts['nfkc']) - $str = $normalizer->normalize($str, 'NFKC'); - } - } - } - if ($opts['umlauts']) { - if (strpos($str = htmlentities($str, ENT_QUOTES, 'UTF-8'), '&') !== false) { - $str = html_entity_decode(preg_replace('~&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|tilde|uml);~i', '$1', $str), ENT_QUOTES, 'utf-8'); - } - } - if ($opts['convmap'] && is_array($opts['convmap'])) { - $str = strtr($str, $opts['convmap']); - } - if ($opts['lowercase']) { - if (function_exists('mb_strtolower')) { - $str = mb_strtolower($str, 'UTF-8'); - } else { - $str = strtolower($str); - } - } - return $str; - } + private $replaced = array(); + private $keyMap = array( + 'ls' => 'intersect', + 'upload' => 'renames', + 'mkdir' => array('name', 'dirs') + ); + + public function __construct($opts) + { + $defaults = array( + 'enable' => true, // For control by volume driver + 'nfc' => true, // Canonical Decomposition followed by Canonical Composition + 'nfkc' => true, // Compatibility Decomposition followed by Canonical + 'umlauts' => false, // Convert umlauts with their closest 7 bit ascii equivalent + 'lowercase' => false, // Make chars lowercase + 'convmap' => array()// Convert map ('FROM' => 'TO') array + ); + + $this->opts = array_merge($defaults, $opts); + } + + public function cmdPreprocess($cmd, &$args, $elfinder, $volume) + { + $opts = $this->getCurrentOpts($volume); + if (!$opts['enable']) { + return false; + } + $this->replaced[$cmd] = array(); + $key = (isset($this->keyMap[$cmd])) ? $this->keyMap[$cmd] : 'name'; + + if (is_array($key)) { + $keys = $key; + } else { + $keys = array($key); + } + foreach ($keys as $key) { + if (isset($args[$key])) { + if (is_array($args[$key])) { + foreach ($args[$key] as $i => $name) { + if ($cmd === 'mkdir' && $key === 'dirs') { + // $name need '/' as prefix see #2607 + $name = '/' . ltrim($name, '/'); + $_names = explode('/', $name); + $_res = array(); + foreach ($_names as $_name) { + $_res[] = $this->normalize($_name, $opts); + } + $this->replaced[$cmd][$name] = $args[$key][$i] = join('/', $_res); + } else { + $this->replaced[$cmd][$name] = $args[$key][$i] = $this->normalize($name, $opts); + } + } + } else if ($args[$key] !== '') { + $name = $args[$key]; + $this->replaced[$cmd][$name] = $args[$key] = $this->normalize($name, $opts); + } + } + } + if ($cmd === 'ls' || $cmd === 'mkdir') { + if (!empty($this->replaced[$cmd])) { + // un-regist for legacy settings + $elfinder->unbind($cmd, array($this, 'cmdPostprocess')); + $elfinder->bind($cmd, array($this, 'cmdPostprocess')); + } + } + return true; + } + + public function cmdPostprocess($cmd, &$result, $args, $elfinder, $volume) + { + if ($cmd === 'ls') { + if (!empty($result['list']) && !empty($this->replaced['ls'])) { + foreach ($result['list'] as $hash => $name) { + if ($keys = array_keys($this->replaced['ls'], $name)) { + if (count($keys) === 1) { + $result['list'][$hash] = $keys[0]; + } else { + $result['list'][$hash] = $keys; + } + } + } + } + } else if ($cmd === 'mkdir') { + if (!empty($result['hashes']) && !empty($this->replaced['mkdir'])) { + foreach ($result['hashes'] as $name => $hash) { + if ($keys = array_keys($this->replaced['mkdir'], $name)) { + $result['hashes'][$keys[0]] = $hash; + } + } + } + } + } + + // NOTE: $thash is directory hash so it unneed to process at here + public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) + { + $opts = $this->getCurrentOpts($volume); + if (!$opts['enable']) { + return false; + } + + $name = $this->normalize($name, $opts); + return true; + } + + protected function normalize($str, $opts) + { + if ($opts['nfc'] || $opts['nfkc']) { + if (class_exists('Normalizer', false)) { + if ($opts['nfc'] && !Normalizer::isNormalized($str, Normalizer::FORM_C)) + $str = Normalizer::normalize($str, Normalizer::FORM_C); + if ($opts['nfkc'] && !Normalizer::isNormalized($str, Normalizer::FORM_KC)) + $str = Normalizer::normalize($str, Normalizer::FORM_KC); + } else { + if (!class_exists('I18N_UnicodeNormalizer', false)) { + include_once 'I18N/UnicodeNormalizer.php'; + } + if (class_exists('I18N_UnicodeNormalizer', false)) { + $normalizer = new I18N_UnicodeNormalizer(); + if ($opts['nfc']) + $str = $normalizer->normalize($str, 'NFC'); + if ($opts['nfkc']) + $str = $normalizer->normalize($str, 'NFKC'); + } + } + } + if ($opts['umlauts']) { + if (strpos($str = htmlentities($str, ENT_QUOTES, 'UTF-8'), '&') !== false) { + $str = html_entity_decode(preg_replace('~&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|tilde|uml);~i', '$1', $str), ENT_QUOTES, 'utf-8'); + } + } + if ($opts['convmap'] && is_array($opts['convmap'])) { + $str = strtr($str, $opts['convmap']); + } + if ($opts['lowercase']) { + if (function_exists('mb_strtolower')) { + $str = mb_strtolower($str, 'UTF-8'); + } else { + $str = strtolower($str); + } + } + return $str; + } } diff --git a/php/plugins/Sanitizer/plugin.php b/php/plugins/Sanitizer/plugin.php index 64fa11191..f4122b4bf 100644 --- a/php/plugins/Sanitizer/plugin.php +++ b/php/plugins/Sanitizer/plugin.php @@ -1,153 +1,157 @@ array( - * 'upload.pre mkdir.pre mkfile.pre rename.pre archive.pre ls.pre' => array( - * 'Plugin.Sanitizer.cmdPreprocess' - * ), - * 'upload.presave' => array( - * 'Plugin.Sanitizer.onUpLoadPreSave' - * ) - * ), - * // global configure (optional) - * 'plugin' => array( - * 'Sanitizer' => array( - * 'enable' => true, - * 'targets' => array('\\','/',':','*','?','"','<','>','|'), // target chars - * 'replace' => '_', // replace to this - * 'callBack' => null // Or @callable sanitize function - * ) - * ), - * // each volume configure (optional) - * 'roots' => array( - * array( - * 'driver' => 'LocalFileSystem', - * 'path' => '/path/to/files/', - * 'URL' => 'http://localhost/to/files/' - * 'plugin' => array( - * 'Sanitizer' => array( - * 'enable' => true, - * 'targets' => array('\\','/',':','*','?','"','<','>','|'), // target chars - * 'replace' => '_', // replace to this - * 'callBack' => null // Or @callable sanitize function - * ) - * ) - * ) - * ) - * ); + * $opts = array( + * 'bind' => array( + * 'upload.pre mkdir.pre mkfile.pre rename.pre archive.pre ls.pre' => array( + * 'Plugin.Sanitizer.cmdPreprocess' + * ), + * 'upload.presave' => array( + * 'Plugin.Sanitizer.onUpLoadPreSave' + * ) + * ), + * // global configure (optional) + * 'plugin' => array( + * 'Sanitizer' => array( + * 'enable' => true, + * 'targets' => array('\\','/',':','*','?','"','<','>','|'), // target chars + * 'replace' => '_', // replace to this + * 'callBack' => null // Or @callable sanitize function + * ) + * ), + * // each volume configure (optional) + * 'roots' => array( + * array( + * 'driver' => 'LocalFileSystem', + * 'path' => '/path/to/files/', + * 'URL' => 'http://localhost/to/files/' + * 'plugin' => array( + * 'Sanitizer' => array( + * 'enable' => true, + * 'targets' => array('\\','/',':','*','?','"','<','>','|'), // target chars + * 'replace' => '_', // replace to this + * 'callBack' => null // Or @callable sanitize function + * ) + * ) + * ) + * ) + * ); * * @package elfinder - * @author Naoki Sawada + * @author Naoki Sawada * @license New BSD */ class elFinderPluginSanitizer extends elFinderPlugin { - private $replaced = array(); - private $keyMap = array( - 'ls' => 'intersect', - 'upload' => 'renames', - 'mkdir' => array('name', 'dirs') - ); + private $replaced = array(); + private $keyMap = array( + 'ls' => 'intersect', + 'upload' => 'renames', + 'mkdir' => array('name', 'dirs') + ); - public function __construct($opts) { - $defaults = array( - 'enable' => true, // For control by volume driver - 'targets' => array('\\','/',':','*','?','"','<','>','|'), // target chars - 'replace' => '_', // replace to this - 'callBack' => null // Or callable sanitize function - ); - $this->opts = array_merge($defaults, $opts); - } - - public function cmdPreprocess($cmd, &$args, $elfinder, $volume) { - $opts = $this->getCurrentOpts($volume); - if (! $opts['enable']) { - return false; - } - $this->replaced[$cmd] = array(); - $key = (isset($this->keyMap[$cmd]))? $this->keyMap[$cmd] : 'name'; - - if (is_array($key)) { - $keys = $key; - } else { - $keys = array($key); - } - foreach($keys as $key) { - if (isset($args[$key])) { - if (is_array($args[$key])) { - foreach($args[$key] as $i => $name) { - if ($cmd === 'mkdir' && $key === 'dirs') { - // $name need '/' as prefix see #2607 - $name = '/' . ltrim($name, '/'); - $_names = explode('/', $name); - $_res = array(); - foreach($_names as $_name) { - $_res[] = $this->sanitizeFileName($_name, $opts); - } - $this->replaced[$cmd][$name] = $args[$key][$i] = join('/', $_res); - } else { - $this->replaced[$cmd][$name] = $args[$key][$i] = $this->sanitizeFileName($name, $opts); - } - } - } else if ($args[$key] !== '') { - $name = $args[$key]; - $this->replaced[$cmd][$name] = $args[$key] = $this->sanitizeFileName($name, $opts); - } - } - } - if ($cmd === 'ls' || $cmd === 'mkdir') { - if (! empty($this->replaced[$cmd])) { - // un-regist for legacy settings - $elfinder->unbind($cmd, array($this, 'cmdPostprocess')); - $elfinder->bind($cmd, array($this, 'cmdPostprocess')); - } - } - return true; - } - - public function cmdPostprocess($cmd, &$result, $args, $elfinder, $volume) { - if ($cmd === 'ls') { - if (! empty($result['list']) && ! empty($this->replaced['ls'])) { - foreach($result['list'] as $hash => $name) { - if ($keys = array_keys($this->replaced['ls'], $name)) { - if (count($keys) === 1) { - $result['list'][$hash] = $keys[0]; - } else { - $result['list'][$hash] = $keys; - } - } - } - } - } else if ($cmd === 'mkdir') { - if (! empty($result['hashes']) && ! empty($this->replaced['mkdir'])) { - foreach($result['hashes'] as $name => $hash) { - if ($keys = array_keys($this->replaced['mkdir'], $name)) { - $result['hashes'][$keys[0]] = $hash; - } - } - } - } - } - - // NOTE: $thash is directory hash so it unneed to process at here - public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) { - $opts = $this->getCurrentOpts($volume); - if (! $opts['enable']) { - return false; - } - $name = $this->sanitizeFileName($name, $opts); - return true; - } - - protected function sanitizeFileName($filename, $opts) { - if(!empty($opts['callBack']) && is_callable($opts['callBack'])) { - return call_user_func_array($opts['callBack'], array($filename, $opts)); - } - return str_replace($opts['targets'], $opts['replace'], $filename); - } + public function __construct($opts) + { + $defaults = array( + 'enable' => true, // For control by volume driver + 'targets' => array('\\', '/', ':', '*', '?', '"', '<', '>', '|'), // target chars + 'replace' => '_', // replace to this + 'callBack' => null // Or callable sanitize function + ); + $this->opts = array_merge($defaults, $opts); + } + + public function cmdPreprocess($cmd, &$args, $elfinder, $volume) + { + $opts = $this->getCurrentOpts($volume); + if (!$opts['enable']) { + return false; + } + $this->replaced[$cmd] = array(); + $key = (isset($this->keyMap[$cmd])) ? $this->keyMap[$cmd] : 'name'; + + if (is_array($key)) { + $keys = $key; + } else { + $keys = array($key); + } + foreach ($keys as $key) { + if (isset($args[$key])) { + if (is_array($args[$key])) { + foreach ($args[$key] as $i => $name) { + if ($cmd === 'mkdir' && $key === 'dirs') { + // $name need '/' as prefix see #2607 + $name = '/' . ltrim($name, '/'); + $_names = explode('/', $name); + $_res = array(); + foreach ($_names as $_name) { + $_res[] = $this->sanitizeFileName($_name, $opts); + } + $this->replaced[$cmd][$name] = $args[$key][$i] = join('/', $_res); + } else { + $this->replaced[$cmd][$name] = $args[$key][$i] = $this->sanitizeFileName($name, $opts); + } + } + } else if ($args[$key] !== '') { + $name = $args[$key]; + $this->replaced[$cmd][$name] = $args[$key] = $this->sanitizeFileName($name, $opts); + } + } + } + if ($cmd === 'ls' || $cmd === 'mkdir') { + if (!empty($this->replaced[$cmd])) { + // un-regist for legacy settings + $elfinder->unbind($cmd, array($this, 'cmdPostprocess')); + $elfinder->bind($cmd, array($this, 'cmdPostprocess')); + } + } + return true; + } + + public function cmdPostprocess($cmd, &$result, $args, $elfinder, $volume) + { + if ($cmd === 'ls') { + if (!empty($result['list']) && !empty($this->replaced['ls'])) { + foreach ($result['list'] as $hash => $name) { + if ($keys = array_keys($this->replaced['ls'], $name)) { + if (count($keys) === 1) { + $result['list'][$hash] = $keys[0]; + } else { + $result['list'][$hash] = $keys; + } + } + } + } + } else if ($cmd === 'mkdir') { + if (!empty($result['hashes']) && !empty($this->replaced['mkdir'])) { + foreach ($result['hashes'] as $name => $hash) { + if ($keys = array_keys($this->replaced['mkdir'], $name)) { + $result['hashes'][$keys[0]] = $hash; + } + } + } + } + } + + // NOTE: $thash is directory hash so it unneed to process at here + public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) + { + $opts = $this->getCurrentOpts($volume); + if (!$opts['enable']) { + return false; + } + $name = $this->sanitizeFileName($name, $opts); + return true; + } + + protected function sanitizeFileName($filename, $opts) + { + if (!empty($opts['callBack']) && is_callable($opts['callBack'])) { + return call_user_func_array($opts['callBack'], array($filename, $opts)); + } + return str_replace($opts['targets'], $opts['replace'], $filename); + } } diff --git a/php/plugins/Watermark/plugin.php b/php/plugins/Watermark/plugin.php index ed4bf4d87..6d59a762c 100644 --- a/php/plugins/Watermark/plugin.php +++ b/php/plugins/Watermark/plugin.php @@ -1,420 +1,424 @@ array( - * 'upload.presave' => array( - * 'Plugin.Watermark.onUpLoadPreSave' - * ) - * ), - * // global configure (optional) - * 'plugin' => array( - * 'Watermark' => array( - * 'enable' => true, // For control by volume driver - * 'source' => 'logo.png', // Path to Water mark image - * 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) - * 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) - * 'marginX' => 5, // Margin horizontal pixel - * 'marginY' => 5, // Margin vertical pixel - * 'quality' => 95, // JPEG image save quality - * 'transparency' => 70, // Water mark image transparency ( other than PNG ) - * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) - * 'targetMinPixel' => 200, // Target image minimum pixel size - * 'interlace' => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field ) - * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * ) - * ), - * // each volume configure (optional) - * 'roots' => array( - * array( - * 'driver' => 'LocalFileSystem', - * 'path' => '/path/to/files/', - * 'URL' => 'http://localhost/to/files/' - * 'plugin' => array( - * 'Watermark' => array( - * 'enable' => true, // For control by volume driver - * 'source' => 'logo.png', // Path to Water mark image - * 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) -* 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) - * 'marginX' => 5, // Margin horizontal pixel - * 'marginY' => 5, // Margin vertical pixel - * 'quality' => 95, // JPEG image save quality - * 'transparency' => 70, // Water mark image transparency ( other than PNG ) - * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) - * 'targetMinPixel' => 200, // Target image minimum pixel size - * 'interlace' => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field ) - * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key - * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - * // In case of using any key, specify it as an array - * ) - * ) - * ) - * ) - * ); + * $opts = array( + * 'bind' => array( + * 'upload.presave' => array( + * 'Plugin.Watermark.onUpLoadPreSave' + * ) + * ), + * // global configure (optional) + * 'plugin' => array( + * 'Watermark' => array( + * 'enable' => true, // For control by volume driver + * 'source' => 'logo.png', // Path to Water mark image + * 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) + * 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) + * 'marginX' => 5, // Margin horizontal pixel + * 'marginY' => 5, // Margin vertical pixel + * 'quality' => 95, // JPEG image save quality + * 'transparency' => 70, // Water mark image transparency ( other than PNG ) + * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) + * 'targetMinPixel' => 200, // Target image minimum pixel size + * 'interlace' => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field ) + * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * ) + * ), + * // each volume configure (optional) + * 'roots' => array( + * array( + * 'driver' => 'LocalFileSystem', + * 'path' => '/path/to/files/', + * 'URL' => 'http://localhost/to/files/' + * 'plugin' => array( + * 'Watermark' => array( + * 'enable' => true, // For control by volume driver + * 'source' => 'logo.png', // Path to Water mark image + * 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) + * 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) + * 'marginX' => 5, // Margin horizontal pixel + * 'marginY' => 5, // Margin vertical pixel + * 'quality' => 95, // JPEG image save quality + * 'transparency' => 70, // Water mark image transparency ( other than PNG ) + * 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) + * 'targetMinPixel' => 200, // Target image minimum pixel size + * 'interlace' => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field ) + * 'offDropWith' => null, // Enabled by default. To disable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * 'onDropWith' => null // Disabled by default. To enable it if it is dropped with pressing the meta key + * // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + * // In case of using any key, specify it as an array + * ) + * ) + * ) + * ) + * ); * * @package elfinder - * @author Naoki Sawada + * @author Naoki Sawada * @license New BSD */ -class elFinderPluginWatermark extends elFinderPlugin { +class elFinderPluginWatermark extends elFinderPlugin +{ - private $watermarkImgInfo = null; + private $watermarkImgInfo = null; - public function __construct($opts) { - $defaults = array( - 'enable' => true, // For control by volume driver - 'source' => 'logo.png', // Path to Water mark image - 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) - 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) - 'marginX' => 5, // Margin horizontal pixel - 'marginY' => 5, // Margin vertical pixel - 'quality' => 95, // JPEG image save quality - 'transparency' => 70, // Water mark image transparency ( other than PNG ) - 'targetType' => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field ) - 'targetMinPixel' => 200, // Target image minimum pixel size - 'interlace' => IMG_GIF|IMG_JPG, // Set interlacebit image formats ( bit-field ) - 'offDropWith' => null, // To disable it if it is dropped with pressing the meta key - // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value - // In case of using any key, specify it as an array - 'marginRight' => 0, // Deprecated - marginX should be used - 'marginBottom' => 0, // Deprecated - marginY should be used - 'disableWithContentSaveId' => true // Disable on URL upload with post data "contentSaveId" - ); + public function __construct($opts) + { + $defaults = array( + 'enable' => true, // For control by volume driver + 'source' => 'logo.png', // Path to Water mark image + 'ratio' => 0.2, // Ratio to original image (ratio > 0 and ratio <= 1) + 'position' => 'RB', // Position L(eft)/C(enter)/R(ight) and T(op)/M(edium)/B(ottom) + 'marginX' => 5, // Margin horizontal pixel + 'marginY' => 5, // Margin vertical pixel + 'quality' => 95, // JPEG image save quality + 'transparency' => 70, // Water mark image transparency ( other than PNG ) + 'targetType' => IMG_GIF | IMG_JPG | IMG_PNG | IMG_WBMP, // Target image formats ( bit-field ) + 'targetMinPixel' => 200, // Target image minimum pixel size + 'interlace' => IMG_GIF | IMG_JPG, // Set interlacebit image formats ( bit-field ) + 'offDropWith' => null, // To disable it if it is dropped with pressing the meta key + // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value + // In case of using any key, specify it as an array + 'marginRight' => 0, // Deprecated - marginX should be used + 'marginBottom' => 0, // Deprecated - marginY should be used + 'disableWithContentSaveId' => true // Disable on URL upload with post data "contentSaveId" + ); - $this->opts = array_merge($defaults, $opts); + $this->opts = array_merge($defaults, $opts); - } + } - public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) { - $opts = $this->getCurrentOpts($volume); - - if (! $this->iaEnabled($opts, $elfinder)) { - return false; - } - - $imageType = null; - $srcImgInfo = null; - if (extension_loaded('fileinfo') && function_exists('mime_content_type')) { - $mime = mime_content_type($src); - if (substr($mime, 0, 5) !== 'image') { - return false; - } - } - if (extension_loaded('exif') && function_exists('exif_imagetype')) { - $imageType = exif_imagetype($src); - } else { - $srcImgInfo = getimagesize($src); - if ($srcImgInfo === false) { - return false; - } - $imageType = $srcImgInfo[2]; - } - - // check target image type - $imgTypes = array( - IMAGETYPE_GIF => IMG_GIF, - IMAGETYPE_JPEG => IMG_JPEG, - IMAGETYPE_PNG => IMG_PNG, - IMAGETYPE_BMP => IMG_WBMP, - IMAGETYPE_WBMP => IMG_WBMP - ); - if (! isset($imgTypes[$imageType]) || ! ($opts['targetType'] & $imgTypes[$imageType])) { - return false; - } - - // check Animation Gif - if ($imageType === IMAGETYPE_GIF && elFinder::isAnimationGif($src)) { - return false; - } - // check Animation Png - if ($imageType === IMAGETYPE_PNG && elFinder::isAnimationPng($src)) { - return false; - } - // check water mark image - if (! file_exists($opts['source'])) { - $opts['source'] = dirname(__FILE__) . "/" . $opts['source']; - } - if (is_readable($opts['source'])) { - $watermarkImgInfo = getimagesize($opts['source']); - if (! $watermarkImgInfo) { - return false; - } - } else { - return false; - } - - if (! $srcImgInfo) { - $srcImgInfo = getimagesize($src); - } - - $watermark = $opts['source']; - $quality = $opts['quality']; - $transparency = $opts['transparency']; + public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume) + { + $opts = $this->getCurrentOpts($volume); - // check target image size - if ($opts['targetMinPixel'] > 0 && $opts['targetMinPixel'] > min($srcImgInfo[0], $srcImgInfo[1])) { - return false; - } - - $watermark_width = $watermarkImgInfo[0]; - $watermark_height = $watermarkImgInfo[1]; + if (!$this->iaEnabled($opts, $elfinder)) { + return false; + } - // Specified as a ratio to the image size - if ($opts['ratio'] && $opts['ratio'] > 0 && $opts['ratio'] <= 1) { - $maxW = $srcImgInfo[0] * $opts['ratio'] - ($opts['marginX'] * 2); - $maxH = $srcImgInfo[1] * $opts['ratio'] - ($opts['marginY'] * 2); - $dx = $dy = 0; - if (($maxW >= $watermarkImgInfo[0] && $maxH >= $watermarkImgInfo[0]) || ($maxW <= $watermarkImgInfo[0] && $maxH <= $watermarkImgInfo[0])) { - $dx = abs($srcImgInfo[0] - $watermarkImgInfo[0]); - $dy = abs($srcImgInfo[1] - $watermarkImgInfo[1]); - } else if ($maxW < $watermarkImgInfo[0]) { - $dx = -1; - } else { - $dy = -1; - } - if ($dx < $dy) { - $ww = $maxW; - $wh = $watermarkImgInfo[1] * ($ww / $watermarkImgInfo[0]); - } else { - $wh = $maxH; - $ww = $watermarkImgInfo[0] * ($wh / $watermarkImgInfo[1]); - } - $watermarkImgInfo[0] = $ww; - $watermarkImgInfo[1] = $wh; - } else { - $opts['ratio'] = null; - } + $imageType = null; + $srcImgInfo = null; + if (extension_loaded('fileinfo') && function_exists('mime_content_type')) { + $mime = mime_content_type($src); + if (substr($mime, 0, 5) !== 'image') { + return false; + } + } + if (extension_loaded('exif') && function_exists('exif_imagetype')) { + $imageType = exif_imagetype($src); + } else { + $srcImgInfo = getimagesize($src); + if ($srcImgInfo === false) { + return false; + } + $imageType = $srcImgInfo[2]; + } - $opts['position'] = strtoupper($opts['position']); - - // Set vertical position - if (strpos($opts['position'], 'T') !== false) { - // Top - $dest_x = $opts['marginX']; - } else if (strpos($opts['position'], 'M') !== false) { - // Middle - $dest_x = ($srcImgInfo[0] - $watermarkImgInfo[0]) / 2; - } else { - // Bottom - $dest_x = $srcImgInfo[0] - $watermarkImgInfo[0] - max($opts['marginBottom'], $opts['marginX']); - } + // check target image type + $imgTypes = array( + IMAGETYPE_GIF => IMG_GIF, + IMAGETYPE_JPEG => IMG_JPEG, + IMAGETYPE_PNG => IMG_PNG, + IMAGETYPE_BMP => IMG_WBMP, + IMAGETYPE_WBMP => IMG_WBMP + ); + if (!isset($imgTypes[$imageType]) || !($opts['targetType'] & $imgTypes[$imageType])) { + return false; + } - // Set horizontal position - if (strpos($opts['position'], 'L') !== false) { - // Left - $dest_y = $opts['marginY']; - } else if (strpos($opts['position'], 'C') !== false) { - // Middle - $dest_y = ($srcImgInfo[1] - $watermarkImgInfo[1]) / 2; - } else { - // Right - $dest_y = $srcImgInfo[1] - $watermarkImgInfo[1] - max($opts['marginRight'], $opts['marginY']); - } + // check Animation Gif + if ($imageType === IMAGETYPE_GIF && elFinder::isAnimationGif($src)) { + return false; + } + // check Animation Png + if ($imageType === IMAGETYPE_PNG && elFinder::isAnimationPng($src)) { + return false; + } + // check water mark image + if (!file_exists($opts['source'])) { + $opts['source'] = dirname(__FILE__) . "/" . $opts['source']; + } + if (is_readable($opts['source'])) { + $watermarkImgInfo = getimagesize($opts['source']); + if (!$watermarkImgInfo) { + return false; + } + } else { + return false; + } - - // check interlace - $opts['interlace'] = ($opts['interlace'] & $imgTypes[$imageType]); - - if (class_exists('Imagick', false)) { - return $this->watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts); - } else { - elFinder::expandMemoryForGD(array($watermarkImgInfo, $srcImgInfo)); - return $this->watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts); - } - } - - private function watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts) { - - try { - - // Open the original image - $img = new Imagick($src); - - // Open the watermark - $watermark = new Imagick($watermark); - - // zoom - if ($opts['ratio']) { - $watermark->scaleImage($watermarkImgInfo[0], $watermarkImgInfo[1]); - } + if (!$srcImgInfo) { + $srcImgInfo = getimagesize($src); + } - // Set transparency - if (strtoupper($watermark->getImageFormat()) !== 'PNG') { - $watermark->setImageOpacity($transparency/100); - } - - // Overlay the watermark on the original image - $img->compositeImage($watermark, imagick::COMPOSITE_OVER, $dest_x, $dest_y); - - // Set quality - if (strtoupper($img->getImageFormat()) === 'JPEG') { - $img->setImageCompression(imagick::COMPRESSION_JPEG); - $img->setCompressionQuality($quality); - } - - // set interlace - $opts['interlace'] && $img->setInterlaceScheme(Imagick::INTERLACE_PLANE); - - $result = $img->writeImage($src); - - $img->clear(); - $img->destroy(); - $watermark->clear(); - $watermark->destroy(); - - return $result ? true : false; - } catch (Exception $e) { - $ermsg = $e->getMessage(); - $ermsg && trigger_error($ermsg); - return false; - } - } - - private function watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts) { - - $watermark_width = $watermarkImgInfo[0]; - $watermark_height = $watermarkImgInfo[1]; + $watermark = $opts['source']; + $quality = $opts['quality']; + $transparency = $opts['transparency']; - $ermsg = ''; - switch ($watermarkImgInfo['mime']) { - case 'image/gif': - if (imagetypes() & IMG_GIF) { - $oWatermarkImg = imagecreatefromgif($watermark); - } else { - $ermsg = 'GIF images are not supported as watermark image'; - } - break; - case 'image/jpeg': - if (imagetypes() & IMG_JPG) { - $oWatermarkImg = imagecreatefromjpeg($watermark) ; - } else { - $ermsg = 'JPEG images are not supported as watermark image'; - } - break; - case 'image/png': - if (imagetypes() & IMG_PNG) { - $oWatermarkImg = imagecreatefrompng($watermark) ; - } else { - $ermsg = 'PNG images are not supported as watermark image'; - } - break; - case 'image/wbmp': - if (imagetypes() & IMG_WBMP) { - $oWatermarkImg = imagecreatefromwbmp($watermark); - } else { - $ermsg = 'WBMP images are not supported as watermark image'; - } - break; - default: - $oWatermarkImg = false; - $ermsg = $watermarkImgInfo['mime'].' images are not supported as watermark image'; - break; - } - + // check target image size + if ($opts['targetMinPixel'] > 0 && $opts['targetMinPixel'] > min($srcImgInfo[0], $srcImgInfo[1])) { + return false; + } - if (! $ermsg) { - // zoom - if ($opts['ratio']) { - $tmpImg = imagecreatetruecolor($watermarkImgInfo[0], $watermarkImgInfo[1]); - imagealphablending($tmpImg, false); - imagesavealpha($tmpImg, true); - imagecopyresampled($tmpImg, $oWatermarkImg, 0, 0, 0, 0, $watermarkImgInfo[0], $watermarkImgInfo[1], imagesx($oWatermarkImg), imagesy($oWatermarkImg)); - imageDestroy($oWatermarkImg); - $oWatermarkImg = $tmpImg; - $tmpImg = null; - } + $watermark_width = $watermarkImgInfo[0]; + $watermark_height = $watermarkImgInfo[1]; - switch ($srcImgInfo['mime']) { - case 'image/gif': - if (imagetypes() & IMG_GIF) { - $oSrcImg = imagecreatefromgif($src); - } else { - $ermsg = 'GIF images are not supported as source image'; - } - break; - case 'image/jpeg': - if (imagetypes() & IMG_JPG) { - $oSrcImg = imagecreatefromjpeg($src) ; - } else { - $ermsg = 'JPEG images are not supported as source image'; - } - break; - case 'image/png': - if (imagetypes() & IMG_PNG) { - $oSrcImg = imagecreatefrompng($src) ; - } else { - $ermsg = 'PNG images are not supported as source image'; - } - break; - case 'image/wbmp': - if (imagetypes() & IMG_WBMP) { - $oSrcImg = imagecreatefromwbmp($src); - } else { - $ermsg = 'WBMP images are not supported as source image'; - } - break; - default: - $oSrcImg = false; - $ermsg = $srcImgInfo['mime'].' images are not supported as source image'; - break; - } - } - - if ($ermsg || false === $oSrcImg || false === $oWatermarkImg) { - $ermsg && trigger_error($ermsg); - return false; - } - - if ($srcImgInfo['mime'] === 'image/png') { - if (function_exists('imagecolorallocatealpha')) { - $bg = imagecolorallocatealpha($oSrcImg, 255, 255, 255, 127); - imagefill($oSrcImg, 0, 0 , $bg); - } - } - - if ($watermarkImgInfo['mime'] === 'image/png') { - imagecopy($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height); - } else { - imagecopymerge($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $transparency); - } - - // set interlace - $opts['interlace'] && imageinterlace($oSrcImg, true); - - switch ($srcImgInfo['mime']) { - case 'image/gif': - imagegif($oSrcImg, $src); - break; - case 'image/jpeg': - imagejpeg($oSrcImg, $src, $quality); - break; - case 'image/png': - if (function_exists('imagesavealpha') && function_exists('imagealphablending')) { - imagealphablending($oSrcImg, false); - imagesavealpha($oSrcImg, true); - } - imagepng($oSrcImg, $src); - break; - case 'image/wbmp': - imagewbmp($oSrcImg, $src); - break; - } + // Specified as a ratio to the image size + if ($opts['ratio'] && $opts['ratio'] > 0 && $opts['ratio'] <= 1) { + $maxW = $srcImgInfo[0] * $opts['ratio'] - ($opts['marginX'] * 2); + $maxH = $srcImgInfo[1] * $opts['ratio'] - ($opts['marginY'] * 2); + $dx = $dy = 0; + if (($maxW >= $watermarkImgInfo[0] && $maxH >= $watermarkImgInfo[0]) || ($maxW <= $watermarkImgInfo[0] && $maxH <= $watermarkImgInfo[0])) { + $dx = abs($srcImgInfo[0] - $watermarkImgInfo[0]); + $dy = abs($srcImgInfo[1] - $watermarkImgInfo[1]); + } else if ($maxW < $watermarkImgInfo[0]) { + $dx = -1; + } else { + $dy = -1; + } + if ($dx < $dy) { + $ww = $maxW; + $wh = $watermarkImgInfo[1] * ($ww / $watermarkImgInfo[0]); + } else { + $wh = $maxH; + $ww = $watermarkImgInfo[0] * ($wh / $watermarkImgInfo[1]); + } + $watermarkImgInfo[0] = $ww; + $watermarkImgInfo[1] = $wh; + } else { + $opts['ratio'] = null; + } - imageDestroy($oSrcImg); - imageDestroy($oWatermarkImg); - - return true; - } + $opts['position'] = strtoupper($opts['position']); + + // Set vertical position + if (strpos($opts['position'], 'T') !== false) { + // Top + $dest_x = $opts['marginX']; + } else if (strpos($opts['position'], 'M') !== false) { + // Middle + $dest_x = ($srcImgInfo[0] - $watermarkImgInfo[0]) / 2; + } else { + // Bottom + $dest_x = $srcImgInfo[0] - $watermarkImgInfo[0] - max($opts['marginBottom'], $opts['marginX']); + } + + // Set horizontal position + if (strpos($opts['position'], 'L') !== false) { + // Left + $dest_y = $opts['marginY']; + } else if (strpos($opts['position'], 'C') !== false) { + // Middle + $dest_y = ($srcImgInfo[1] - $watermarkImgInfo[1]) / 2; + } else { + // Right + $dest_y = $srcImgInfo[1] - $watermarkImgInfo[1] - max($opts['marginRight'], $opts['marginY']); + } + + + // check interlace + $opts['interlace'] = ($opts['interlace'] & $imgTypes[$imageType]); + + if (class_exists('Imagick', false)) { + return $this->watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts); + } else { + elFinder::expandMemoryForGD(array($watermarkImgInfo, $srcImgInfo)); + return $this->watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts); + } + } + + private function watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $opts) + { + + try { + + // Open the original image + $img = new Imagick($src); + + // Open the watermark + $watermark = new Imagick($watermark); + + // zoom + if ($opts['ratio']) { + $watermark->scaleImage($watermarkImgInfo[0], $watermarkImgInfo[1]); + } + + // Set transparency + if (strtoupper($watermark->getImageFormat()) !== 'PNG') { + $watermark->setImageOpacity($transparency / 100); + } + + // Overlay the watermark on the original image + $img->compositeImage($watermark, imagick::COMPOSITE_OVER, $dest_x, $dest_y); + + // Set quality + if (strtoupper($img->getImageFormat()) === 'JPEG') { + $img->setImageCompression(imagick::COMPRESSION_JPEG); + $img->setCompressionQuality($quality); + } + + // set interlace + $opts['interlace'] && $img->setInterlaceScheme(Imagick::INTERLACE_PLANE); + + $result = $img->writeImage($src); + + $img->clear(); + $img->destroy(); + $watermark->clear(); + $watermark->destroy(); + + return $result ? true : false; + } catch (Exception $e) { + $ermsg = $e->getMessage(); + $ermsg && trigger_error($ermsg); + return false; + } + } + + private function watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo, $opts) + { + + $watermark_width = $watermarkImgInfo[0]; + $watermark_height = $watermarkImgInfo[1]; + + $ermsg = ''; + switch ($watermarkImgInfo['mime']) { + case 'image/gif': + if (imagetypes() & IMG_GIF) { + $oWatermarkImg = imagecreatefromgif($watermark); + } else { + $ermsg = 'GIF images are not supported as watermark image'; + } + break; + case 'image/jpeg': + if (imagetypes() & IMG_JPG) { + $oWatermarkImg = imagecreatefromjpeg($watermark); + } else { + $ermsg = 'JPEG images are not supported as watermark image'; + } + break; + case 'image/png': + if (imagetypes() & IMG_PNG) { + $oWatermarkImg = imagecreatefrompng($watermark); + } else { + $ermsg = 'PNG images are not supported as watermark image'; + } + break; + case 'image/wbmp': + if (imagetypes() & IMG_WBMP) { + $oWatermarkImg = imagecreatefromwbmp($watermark); + } else { + $ermsg = 'WBMP images are not supported as watermark image'; + } + break; + default: + $oWatermarkImg = false; + $ermsg = $watermarkImgInfo['mime'] . ' images are not supported as watermark image'; + break; + } + + + if (!$ermsg) { + // zoom + if ($opts['ratio']) { + $tmpImg = imagecreatetruecolor($watermarkImgInfo[0], $watermarkImgInfo[1]); + imagealphablending($tmpImg, false); + imagesavealpha($tmpImg, true); + imagecopyresampled($tmpImg, $oWatermarkImg, 0, 0, 0, 0, $watermarkImgInfo[0], $watermarkImgInfo[1], imagesx($oWatermarkImg), imagesy($oWatermarkImg)); + imageDestroy($oWatermarkImg); + $oWatermarkImg = $tmpImg; + $tmpImg = null; + } + + switch ($srcImgInfo['mime']) { + case 'image/gif': + if (imagetypes() & IMG_GIF) { + $oSrcImg = imagecreatefromgif($src); + } else { + $ermsg = 'GIF images are not supported as source image'; + } + break; + case 'image/jpeg': + if (imagetypes() & IMG_JPG) { + $oSrcImg = imagecreatefromjpeg($src); + } else { + $ermsg = 'JPEG images are not supported as source image'; + } + break; + case 'image/png': + if (imagetypes() & IMG_PNG) { + $oSrcImg = imagecreatefrompng($src); + } else { + $ermsg = 'PNG images are not supported as source image'; + } + break; + case 'image/wbmp': + if (imagetypes() & IMG_WBMP) { + $oSrcImg = imagecreatefromwbmp($src); + } else { + $ermsg = 'WBMP images are not supported as source image'; + } + break; + default: + $oSrcImg = false; + $ermsg = $srcImgInfo['mime'] . ' images are not supported as source image'; + break; + } + } + + if ($ermsg || false === $oSrcImg || false === $oWatermarkImg) { + $ermsg && trigger_error($ermsg); + return false; + } + + if ($srcImgInfo['mime'] === 'image/png') { + if (function_exists('imagecolorallocatealpha')) { + $bg = imagecolorallocatealpha($oSrcImg, 255, 255, 255, 127); + imagefill($oSrcImg, 0, 0, $bg); + } + } + + if ($watermarkImgInfo['mime'] === 'image/png') { + imagecopy($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height); + } else { + imagecopymerge($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $transparency); + } + + // set interlace + $opts['interlace'] && imageinterlace($oSrcImg, true); + + switch ($srcImgInfo['mime']) { + case 'image/gif': + imagegif($oSrcImg, $src); + break; + case 'image/jpeg': + imagejpeg($oSrcImg, $src, $quality); + break; + case 'image/png': + if (function_exists('imagesavealpha') && function_exists('imagealphablending')) { + imagealphablending($oSrcImg, false); + imagesavealpha($oSrcImg, true); + } + imagepng($oSrcImg, $src); + break; + case 'image/wbmp': + imagewbmp($oSrcImg, $src); + break; + } + + imageDestroy($oSrcImg); + imageDestroy($oWatermarkImg); + + return true; + } }